blob: 4cdfb186603936064d860b0ac0440455e3984dda [file] [log] [blame]
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +02001package manager
2
3import (
4 "context"
5 "fmt"
6 "net/netip"
7 "time"
8
9 "github.com/google/uuid"
10 "k8s.io/klog/v2"
11
12 "source.monogon.dev/cloud/bmaas/bmdb"
13 "source.monogon.dev/cloud/bmaas/bmdb/model"
14 "source.monogon.dev/cloud/shepherd"
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010015 "source.monogon.dev/go/net/ssh"
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020016)
17
18type dummyMachine struct {
19 id shepherd.ProviderID
20 addr netip.Addr
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010021 availability shepherd.Availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020022 agentStarted bool
23}
24
Tim Windelschmidtfdd87ab2023-12-07 18:03:21 +010025func (dm *dummyMachine) Failed() bool {
26 return false
27}
28
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020029func (dm *dummyMachine) ID() shepherd.ProviderID {
30 return dm.id
31}
32
33func (dm *dummyMachine) Addr() netip.Addr {
34 return dm.addr
35}
36
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010037func (dm *dummyMachine) Availability() shepherd.Availability {
38 return dm.availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020039}
40
41type dummySSHClient struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010042 ssh.Client
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020043 dp *dummyProvider
44}
45
46type dummySSHConnection struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010047 ssh.Connection
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020048 m *dummyMachine
49}
50
51func (dsc *dummySSHConnection) Execute(ctx context.Context, command string, stdin []byte) ([]byte, []byte, error) {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010052 stdout, stderr, err := dsc.Connection.Execute(ctx, command, stdin)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020053 if err != nil {
54 return nil, nil, err
55 }
56
57 dsc.m.agentStarted = true
58 return stdout, stderr, nil
59}
60
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010061func (dsc *dummySSHClient) Dial(ctx context.Context, address string, timeout time.Duration) (ssh.Connection, error) {
62 conn, err := dsc.Client.Dial(ctx, address, timeout)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020063 if err != nil {
64 return nil, err
65 }
66
67 addrPort := netip.MustParseAddrPort(address)
68 uid, err := uuid.FromBytes(addrPort.Addr().AsSlice())
69 if err != nil {
70 return nil, err
71 }
72
73 m := dsc.dp.machines[shepherd.ProviderID(uid.String())]
74 if m == nil {
75 return nil, fmt.Errorf("failed finding machine in map")
76 }
77
78 return &dummySSHConnection{conn, m}, nil
79}
80
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010081func (dp *dummyProvider) sshClient() ssh.Client {
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020082 return &dummySSHClient{
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010083 Client: &FakeSSHClient{},
84 dp: dp,
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020085 }
86}
87
88func newDummyProvider(cap int) *dummyProvider {
89 return &dummyProvider{
90 capacity: cap,
91 machines: make(map[shepherd.ProviderID]*dummyMachine),
92 }
93}
94
95type dummyProvider struct {
96 capacity int
97 machines map[shepherd.ProviderID]*dummyMachine
98}
99
100func (dp *dummyProvider) createDummyMachines(ctx context.Context, session *bmdb.Session, count int) ([]shepherd.Machine, error) {
101 if len(dp.machines)+count > dp.capacity {
102 return nil, fmt.Errorf("no capacity left")
103 }
104
105 var machines []shepherd.Machine
106 for i := 0; i < count; i++ {
107 uid := uuid.Must(uuid.NewRandom())
108 m, err := dp.CreateMachine(ctx, session, shepherd.CreateMachineRequest{
109 UnusedMachine: &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100110 id: shepherd.ProviderID(uid.String()),
111 availability: shepherd.AvailabilityKnownUsed,
112 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200113 },
114 })
115 if err != nil {
116 return nil, err
117 }
118 machines = append(machines, m)
119 }
120
121 return machines, nil
122}
123
124func (dp *dummyProvider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
125 var machines []shepherd.Machine
126 for _, m := range dp.machines {
127 machines = append(machines, m)
128 }
129
130 unusedMachineCount := dp.capacity - len(machines)
131 for i := 0; i < unusedMachineCount; i++ {
132 uid := uuid.Must(uuid.NewRandom())
133 machines = append(machines, &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100134 id: shepherd.ProviderID(uid.String()),
135 availability: shepherd.AvailabilityKnownUnused,
136 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200137 })
138 }
139
140 return machines, nil
141}
142
143func (dp *dummyProvider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
144 for _, m := range dp.machines {
145 if m.ID() == id {
146 return m, nil
147 }
148 }
149
150 return nil, shepherd.ErrMachineNotFound
151}
152
153func (dp *dummyProvider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
154 dm := request.UnusedMachine.(*dummyMachine)
155
156 err := session.Transact(ctx, func(q *model.Queries) error {
157 // Create a new machine record within BMDB.
158 m, err := q.NewMachine(ctx)
159 if err != nil {
160 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
161 }
162
163 p := model.MachineAddProvidedParams{
164 MachineID: m.MachineID,
165 ProviderID: string(dm.id),
166 Provider: dp.Type(),
167 }
168 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", p.MachineID, p.ProviderID, p.Provider)
169 if err := q.MachineAddProvided(ctx, p); err != nil {
170 return fmt.Errorf("while tagging machine active: %w", err)
171 }
172 return nil
173 })
174
175 if err != nil {
176 return nil, err
177 }
178
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100179 dm.availability = shepherd.AvailabilityKnownUsed
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200180 dp.machines[dm.id] = dm
181
182 return dm, nil
183}
184
185func (dp *dummyProvider) Type() model.Provider {
186 return model.ProviderNone
187}