blob: 3f6b1396bad4a492a0d017a4b176641a8e71b75e [file] [log] [blame]
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +02001package manager
2
3import (
4 "context"
5 "fmt"
6 "net/netip"
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +01007 "sync"
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +02008 "time"
9
10 "github.com/google/uuid"
11 "k8s.io/klog/v2"
12
13 "source.monogon.dev/cloud/bmaas/bmdb"
14 "source.monogon.dev/cloud/bmaas/bmdb/model"
15 "source.monogon.dev/cloud/shepherd"
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010016 "source.monogon.dev/go/net/ssh"
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020017)
18
19type dummyMachine struct {
20 id shepherd.ProviderID
21 addr netip.Addr
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010022 availability shepherd.Availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020023 agentStarted bool
24}
25
Tim Windelschmidtfdd87ab2023-12-07 18:03:21 +010026func (dm *dummyMachine) Failed() bool {
27 return false
28}
29
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020030func (dm *dummyMachine) ID() shepherd.ProviderID {
31 return dm.id
32}
33
34func (dm *dummyMachine) Addr() netip.Addr {
35 return dm.addr
36}
37
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010038func (dm *dummyMachine) Availability() shepherd.Availability {
39 return dm.availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020040}
41
42type dummySSHClient struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010043 ssh.Client
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020044 dp *dummyProvider
45}
46
47type dummySSHConnection struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010048 ssh.Connection
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020049 m *dummyMachine
50}
51
52func (dsc *dummySSHConnection) Execute(ctx context.Context, command string, stdin []byte) ([]byte, []byte, error) {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010053 stdout, stderr, err := dsc.Connection.Execute(ctx, command, stdin)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020054 if err != nil {
55 return nil, nil, err
56 }
57
58 dsc.m.agentStarted = true
59 return stdout, stderr, nil
60}
61
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010062func (dsc *dummySSHClient) Dial(ctx context.Context, address string, timeout time.Duration) (ssh.Connection, error) {
63 conn, err := dsc.Client.Dial(ctx, address, timeout)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020064 if err != nil {
65 return nil, err
66 }
67
68 addrPort := netip.MustParseAddrPort(address)
69 uid, err := uuid.FromBytes(addrPort.Addr().AsSlice())
70 if err != nil {
71 return nil, err
72 }
73
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +010074 dsc.dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020075 m := dsc.dp.machines[shepherd.ProviderID(uid.String())]
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +010076 dsc.dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020077 if m == nil {
78 return nil, fmt.Errorf("failed finding machine in map")
79 }
80
81 return &dummySSHConnection{conn, m}, nil
82}
83
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010084func (dp *dummyProvider) sshClient() ssh.Client {
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020085 return &dummySSHClient{
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010086 Client: &FakeSSHClient{},
87 dp: dp,
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020088 }
89}
90
91func newDummyProvider(cap int) *dummyProvider {
92 return &dummyProvider{
93 capacity: cap,
94 machines: make(map[shepherd.ProviderID]*dummyMachine),
95 }
96}
97
98type dummyProvider struct {
Tim Windelschmidt99e15112025-02-05 17:38:16 +010099 capacity int
100 machines map[shepherd.ProviderID]*dummyMachine
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100101 muMachines sync.RWMutex
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200102}
103
104func (dp *dummyProvider) createDummyMachines(ctx context.Context, session *bmdb.Session, count int) ([]shepherd.Machine, error) {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100105 dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200106 if len(dp.machines)+count > dp.capacity {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100107 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200108 return nil, fmt.Errorf("no capacity left")
109 }
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100110 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200111
112 var machines []shepherd.Machine
113 for i := 0; i < count; i++ {
114 uid := uuid.Must(uuid.NewRandom())
115 m, err := dp.CreateMachine(ctx, session, shepherd.CreateMachineRequest{
116 UnusedMachine: &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100117 id: shepherd.ProviderID(uid.String()),
118 availability: shepherd.AvailabilityKnownUsed,
119 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200120 },
121 })
122 if err != nil {
123 return nil, err
124 }
125 machines = append(machines, m)
126 }
127
128 return machines, nil
129}
130
131func (dp *dummyProvider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
132 var machines []shepherd.Machine
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100133 dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200134 for _, m := range dp.machines {
135 machines = append(machines, m)
136 }
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100137 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200138
139 unusedMachineCount := dp.capacity - len(machines)
140 for i := 0; i < unusedMachineCount; i++ {
141 uid := uuid.Must(uuid.NewRandom())
142 machines = append(machines, &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100143 id: shepherd.ProviderID(uid.String()),
144 availability: shepherd.AvailabilityKnownUnused,
145 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200146 })
147 }
148
149 return machines, nil
150}
151
152func (dp *dummyProvider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100153 dp.muMachines.RLock()
154 defer dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200155 for _, m := range dp.machines {
156 if m.ID() == id {
157 return m, nil
158 }
159 }
160
161 return nil, shepherd.ErrMachineNotFound
162}
163
164func (dp *dummyProvider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
165 dm := request.UnusedMachine.(*dummyMachine)
166
167 err := session.Transact(ctx, func(q *model.Queries) error {
168 // Create a new machine record within BMDB.
169 m, err := q.NewMachine(ctx)
170 if err != nil {
171 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
172 }
173
174 p := model.MachineAddProvidedParams{
175 MachineID: m.MachineID,
176 ProviderID: string(dm.id),
177 Provider: dp.Type(),
178 }
179 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", p.MachineID, p.ProviderID, p.Provider)
180 if err := q.MachineAddProvided(ctx, p); err != nil {
181 return fmt.Errorf("while tagging machine active: %w", err)
182 }
183 return nil
184 })
185
186 if err != nil {
187 return nil, err
188 }
189
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100190 dm.availability = shepherd.AvailabilityKnownUsed
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100191 dp.muMachines.Lock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200192 dp.machines[dm.id] = dm
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100193 dp.muMachines.Unlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200194
195 return dm, nil
196}
197
198func (dp *dummyProvider) Type() model.Provider {
199 return model.ProviderNone
200}