blob: d1c6361956bd7d2e67462271d8002962992480cd [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"
15)
16
17type dummyMachine struct {
18 id shepherd.ProviderID
19 addr netip.Addr
20 state shepherd.State
21 agentStarted bool
22}
23
24func (dm *dummyMachine) ID() shepherd.ProviderID {
25 return dm.id
26}
27
28func (dm *dummyMachine) Addr() netip.Addr {
29 return dm.addr
30}
31
32func (dm *dummyMachine) State() shepherd.State {
33 return dm.state
34}
35
36type dummySSHClient struct {
37 SSHClient
38 dp *dummyProvider
39}
40
41type dummySSHConnection struct {
42 SSHConnection
43 m *dummyMachine
44}
45
46func (dsc *dummySSHConnection) Execute(ctx context.Context, command string, stdin []byte) ([]byte, []byte, error) {
47 stdout, stderr, err := dsc.SSHConnection.Execute(ctx, command, stdin)
48 if err != nil {
49 return nil, nil, err
50 }
51
52 dsc.m.agentStarted = true
53 return stdout, stderr, nil
54}
55
56func (dsc *dummySSHClient) Dial(ctx context.Context, address string, timeout time.Duration) (SSHConnection, error) {
57 conn, err := dsc.SSHClient.Dial(ctx, address, timeout)
58 if err != nil {
59 return nil, err
60 }
61
62 addrPort := netip.MustParseAddrPort(address)
63 uid, err := uuid.FromBytes(addrPort.Addr().AsSlice())
64 if err != nil {
65 return nil, err
66 }
67
68 m := dsc.dp.machines[shepherd.ProviderID(uid.String())]
69 if m == nil {
70 return nil, fmt.Errorf("failed finding machine in map")
71 }
72
73 return &dummySSHConnection{conn, m}, nil
74}
75
76func (dp *dummyProvider) sshClient() SSHClient {
77 return &dummySSHClient{
78 SSHClient: &FakeSSHClient{},
79 dp: dp,
80 }
81}
82
83func newDummyProvider(cap int) *dummyProvider {
84 return &dummyProvider{
85 capacity: cap,
86 machines: make(map[shepherd.ProviderID]*dummyMachine),
87 }
88}
89
90type dummyProvider struct {
91 capacity int
92 machines map[shepherd.ProviderID]*dummyMachine
93}
94
95func (dp *dummyProvider) createDummyMachines(ctx context.Context, session *bmdb.Session, count int) ([]shepherd.Machine, error) {
96 if len(dp.machines)+count > dp.capacity {
97 return nil, fmt.Errorf("no capacity left")
98 }
99
100 var machines []shepherd.Machine
101 for i := 0; i < count; i++ {
102 uid := uuid.Must(uuid.NewRandom())
103 m, err := dp.CreateMachine(ctx, session, shepherd.CreateMachineRequest{
104 UnusedMachine: &dummyMachine{
105 id: shepherd.ProviderID(uid.String()),
106 state: shepherd.StateKnownUsed,
107 addr: netip.AddrFrom16(uid),
108 },
109 })
110 if err != nil {
111 return nil, err
112 }
113 machines = append(machines, m)
114 }
115
116 return machines, nil
117}
118
119func (dp *dummyProvider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
120 var machines []shepherd.Machine
121 for _, m := range dp.machines {
122 machines = append(machines, m)
123 }
124
125 unusedMachineCount := dp.capacity - len(machines)
126 for i := 0; i < unusedMachineCount; i++ {
127 uid := uuid.Must(uuid.NewRandom())
128 machines = append(machines, &dummyMachine{
129 id: shepherd.ProviderID(uid.String()),
130 state: shepherd.StateKnownUnused,
131 addr: netip.AddrFrom16(uid),
132 })
133 }
134
135 return machines, nil
136}
137
138func (dp *dummyProvider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
139 for _, m := range dp.machines {
140 if m.ID() == id {
141 return m, nil
142 }
143 }
144
145 return nil, shepherd.ErrMachineNotFound
146}
147
148func (dp *dummyProvider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
149 dm := request.UnusedMachine.(*dummyMachine)
150
151 err := session.Transact(ctx, func(q *model.Queries) error {
152 // Create a new machine record within BMDB.
153 m, err := q.NewMachine(ctx)
154 if err != nil {
155 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
156 }
157
158 p := model.MachineAddProvidedParams{
159 MachineID: m.MachineID,
160 ProviderID: string(dm.id),
161 Provider: dp.Type(),
162 }
163 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", p.MachineID, p.ProviderID, p.Provider)
164 if err := q.MachineAddProvided(ctx, p); err != nil {
165 return fmt.Errorf("while tagging machine active: %w", err)
166 }
167 return nil
168 })
169
170 if err != nil {
171 return nil, err
172 }
173
174 dm.state = shepherd.StateKnownUsed
175 dp.machines[dm.id] = dm
176
177 return dm, nil
178}
179
180func (dp *dummyProvider) Type() model.Provider {
181 return model.ProviderNone
182}