blob: c88c9962f1f6cd6c8c3361ba4e8902713d47c11c [file] [log] [blame]
package manager
import (
"context"
"fmt"
"net/netip"
"sync"
"time"
"github.com/google/uuid"
"k8s.io/klog/v2"
"source.monogon.dev/cloud/bmaas/bmdb"
"source.monogon.dev/cloud/bmaas/bmdb/model"
"source.monogon.dev/cloud/shepherd"
"source.monogon.dev/go/net/ssh"
)
type dummyMachine struct {
id shepherd.ProviderID
addr netip.Addr
availability shepherd.Availability
agentStarted bool
}
func (dm *dummyMachine) Failed() bool {
return false
}
func (dm *dummyMachine) ID() shepherd.ProviderID {
return dm.id
}
func (dm *dummyMachine) Addr() netip.Addr {
return dm.addr
}
func (dm *dummyMachine) Availability() shepherd.Availability {
return dm.availability
}
type dummySSHClient struct {
ssh.Client
dp *dummyProvider
}
type dummySSHConnection struct {
ssh.Connection
m *dummyMachine
}
func (dsc *dummySSHConnection) Execute(ctx context.Context, command string, stdin []byte) ([]byte, []byte, error) {
stdout, stderr, err := dsc.Connection.Execute(ctx, command, stdin)
if err != nil {
return nil, nil, err
}
dsc.m.agentStarted = true
return stdout, stderr, nil
}
func (dsc *dummySSHClient) Dial(ctx context.Context, address string, timeout time.Duration) (ssh.Connection, error) {
conn, err := dsc.Client.Dial(ctx, address, timeout)
if err != nil {
return nil, err
}
addrPort := netip.MustParseAddrPort(address)
uid, err := uuid.FromBytes(addrPort.Addr().AsSlice())
if err != nil {
return nil, err
}
dsc.dp.muMachines.RLock()
m := dsc.dp.machines[shepherd.ProviderID(uid.String())]
dsc.dp.muMachines.RUnlock()
if m == nil {
return nil, fmt.Errorf("failed finding machine in map")
}
return &dummySSHConnection{conn, m}, nil
}
func (dp *dummyProvider) sshClient() ssh.Client {
return &dummySSHClient{
Client: &FakeSSHClient{},
dp: dp,
}
}
func newDummyProvider(cap int) *dummyProvider {
return &dummyProvider{
capacity: cap,
machines: make(map[shepherd.ProviderID]*dummyMachine),
}
}
type dummyProvider struct {
capacity int
machines map[shepherd.ProviderID]*dummyMachine
muMachines sync.RWMutex
}
func (dp *dummyProvider) createDummyMachines(ctx context.Context, session *bmdb.Session, count int) ([]shepherd.Machine, error) {
dp.muMachines.RLock()
if len(dp.machines)+count > dp.capacity {
dp.muMachines.RUnlock()
return nil, fmt.Errorf("no capacity left")
}
dp.muMachines.RUnlock()
var machines []shepherd.Machine
for i := 0; i < count; i++ {
uid := uuid.Must(uuid.NewRandom())
m, err := dp.CreateMachine(ctx, session, shepherd.CreateMachineRequest{
UnusedMachine: &dummyMachine{
id: shepherd.ProviderID(uid.String()),
availability: shepherd.AvailabilityKnownUsed,
addr: netip.AddrFrom16(uid),
},
})
if err != nil {
return nil, err
}
machines = append(machines, m)
}
return machines, nil
}
func (dp *dummyProvider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
var machines []shepherd.Machine
dp.muMachines.RLock()
for _, m := range dp.machines {
machines = append(machines, m)
}
dp.muMachines.RUnlock()
unusedMachineCount := dp.capacity - len(machines)
for i := 0; i < unusedMachineCount; i++ {
uid := uuid.Must(uuid.NewRandom())
machines = append(machines, &dummyMachine{
id: shepherd.ProviderID(uid.String()),
availability: shepherd.AvailabilityKnownUnused,
addr: netip.AddrFrom16(uid),
})
}
return machines, nil
}
func (dp *dummyProvider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
dp.muMachines.RLock()
defer dp.muMachines.RUnlock()
for _, m := range dp.machines {
if m.ID() == id {
return m, nil
}
}
return nil, shepherd.ErrMachineNotFound
}
func (dp *dummyProvider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
dm := request.UnusedMachine.(*dummyMachine)
err := session.Transact(ctx, func(q *model.Queries) error {
// Create a new machine record within BMDB.
m, err := q.NewMachine(ctx)
if err != nil {
return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
}
p := model.MachineAddProvidedParams{
MachineID: m.MachineID,
ProviderID: string(dm.id),
Provider: dp.Type(),
}
klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", p.MachineID, p.ProviderID, p.Provider)
if err := q.MachineAddProvided(ctx, p); err != nil {
return fmt.Errorf("while tagging machine active: %w", err)
}
return nil
})
if err != nil {
return nil, err
}
dm.availability = shepherd.AvailabilityKnownUsed
dp.muMachines.Lock()
dp.machines[dm.id] = dm
dp.muMachines.Unlock()
return dm, nil
}
func (dp *dummyProvider) Type() model.Provider {
return model.ProviderNone
}