blob: 003fd5efe205eeaa7e10c68c155ae9c785e59213 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
2// SPDX-License-Identifier: Apache-2.0
3
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +02004package manager
5
6import (
7 "context"
8 "fmt"
9 "net/netip"
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +010010 "sync"
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020011 "time"
12
13 "github.com/google/uuid"
14 "k8s.io/klog/v2"
15
16 "source.monogon.dev/cloud/bmaas/bmdb"
17 "source.monogon.dev/cloud/bmaas/bmdb/model"
18 "source.monogon.dev/cloud/shepherd"
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010019 "source.monogon.dev/go/net/ssh"
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020020)
21
22type dummyMachine struct {
23 id shepherd.ProviderID
24 addr netip.Addr
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010025 availability shepherd.Availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020026 agentStarted bool
27}
28
Tim Windelschmidtfdd87ab2023-12-07 18:03:21 +010029func (dm *dummyMachine) Failed() bool {
30 return false
31}
32
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020033func (dm *dummyMachine) ID() shepherd.ProviderID {
34 return dm.id
35}
36
37func (dm *dummyMachine) Addr() netip.Addr {
38 return dm.addr
39}
40
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +010041func (dm *dummyMachine) Availability() shepherd.Availability {
42 return dm.availability
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020043}
44
45type dummySSHClient struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010046 ssh.Client
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020047 dp *dummyProvider
48}
49
50type dummySSHConnection struct {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010051 ssh.Connection
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020052 m *dummyMachine
53}
54
55func (dsc *dummySSHConnection) Execute(ctx context.Context, command string, stdin []byte) ([]byte, []byte, error) {
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010056 stdout, stderr, err := dsc.Connection.Execute(ctx, command, stdin)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020057 if err != nil {
58 return nil, nil, err
59 }
60
61 dsc.m.agentStarted = true
62 return stdout, stderr, nil
63}
64
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010065func (dsc *dummySSHClient) Dial(ctx context.Context, address string, timeout time.Duration) (ssh.Connection, error) {
66 conn, err := dsc.Client.Dial(ctx, address, timeout)
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020067 if err != nil {
68 return nil, err
69 }
70
71 addrPort := netip.MustParseAddrPort(address)
72 uid, err := uuid.FromBytes(addrPort.Addr().AsSlice())
73 if err != nil {
74 return nil, err
75 }
76
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +010077 dsc.dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020078 m := dsc.dp.machines[shepherd.ProviderID(uid.String())]
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +010079 dsc.dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020080 if m == nil {
81 return nil, fmt.Errorf("failed finding machine in map")
82 }
83
84 return &dummySSHConnection{conn, m}, nil
85}
86
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010087func (dp *dummyProvider) sshClient() ssh.Client {
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020088 return &dummySSHClient{
Tim Windelschmidt5f5f3302024-02-22 23:50:24 +010089 Client: &FakeSSHClient{},
90 dp: dp,
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020091 }
92}
93
94func newDummyProvider(cap int) *dummyProvider {
95 return &dummyProvider{
96 capacity: cap,
97 machines: make(map[shepherd.ProviderID]*dummyMachine),
98 }
99}
100
101type dummyProvider struct {
Tim Windelschmidt99e15112025-02-05 17:38:16 +0100102 capacity int
103 machines map[shepherd.ProviderID]*dummyMachine
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100104 muMachines sync.RWMutex
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200105}
106
107func (dp *dummyProvider) createDummyMachines(ctx context.Context, session *bmdb.Session, count int) ([]shepherd.Machine, error) {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100108 dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200109 if len(dp.machines)+count > dp.capacity {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100110 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200111 return nil, fmt.Errorf("no capacity left")
112 }
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100113 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200114
115 var machines []shepherd.Machine
116 for i := 0; i < count; i++ {
117 uid := uuid.Must(uuid.NewRandom())
118 m, err := dp.CreateMachine(ctx, session, shepherd.CreateMachineRequest{
119 UnusedMachine: &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100120 id: shepherd.ProviderID(uid.String()),
121 availability: shepherd.AvailabilityKnownUsed,
122 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200123 },
124 })
125 if err != nil {
126 return nil, err
127 }
128 machines = append(machines, m)
129 }
130
131 return machines, nil
132}
133
134func (dp *dummyProvider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
135 var machines []shepherd.Machine
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100136 dp.muMachines.RLock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200137 for _, m := range dp.machines {
138 machines = append(machines, m)
139 }
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100140 dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200141
142 unusedMachineCount := dp.capacity - len(machines)
143 for i := 0; i < unusedMachineCount; i++ {
144 uid := uuid.Must(uuid.NewRandom())
145 machines = append(machines, &dummyMachine{
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100146 id: shepherd.ProviderID(uid.String()),
147 availability: shepherd.AvailabilityKnownUnused,
148 addr: netip.AddrFrom16(uid),
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200149 })
150 }
151
152 return machines, nil
153}
154
155func (dp *dummyProvider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100156 dp.muMachines.RLock()
157 defer dp.muMachines.RUnlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200158 for _, m := range dp.machines {
159 if m.ID() == id {
160 return m, nil
161 }
162 }
163
164 return nil, shepherd.ErrMachineNotFound
165}
166
167func (dp *dummyProvider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
168 dm := request.UnusedMachine.(*dummyMachine)
169
170 err := session.Transact(ctx, func(q *model.Queries) error {
171 // Create a new machine record within BMDB.
172 m, err := q.NewMachine(ctx)
173 if err != nil {
174 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
175 }
176
177 p := model.MachineAddProvidedParams{
178 MachineID: m.MachineID,
179 ProviderID: string(dm.id),
180 Provider: dp.Type(),
181 }
182 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", p.MachineID, p.ProviderID, p.Provider)
183 if err := q.MachineAddProvided(ctx, p); err != nil {
184 return fmt.Errorf("while tagging machine active: %w", err)
185 }
186 return nil
187 })
188
189 if err != nil {
190 return nil, err
191 }
192
Tim Windelschmidtc4dd0032024-02-19 13:13:31 +0100193 dm.availability = shepherd.AvailabilityKnownUsed
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100194 dp.muMachines.Lock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200195 dp.machines[dm.id] = dm
Tim Windelschmidt8eeae7b2024-12-09 22:52:55 +0100196 dp.muMachines.Unlock()
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +0200197
198 return dm, nil
199}
200
201func (dp *dummyProvider) Type() model.Provider {
202 return model.ProviderNone
203}