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