blob: 05b628f7460a1fdb630df8a6bb45532ff339068d [file] [log] [blame] [edit]
package main
import (
"context"
"database/sql"
"fmt"
"net/netip"
"k8s.io/klog/v2"
"source.monogon.dev/cloud/bmaas/bmdb"
"source.monogon.dev/cloud/bmaas/bmdb/model"
"source.monogon.dev/cloud/shepherd"
)
// provider represents a shepherd.Provider that works entirely on a
// static device list. It requires a provider type and a device list.
type provider struct {
providerType model.Provider
machines map[shepherd.ProviderID]machine
}
type machine struct {
ProviderID shepherd.ProviderID `json:"ID"`
Address netip.Addr `json:"Addr"`
Location string `json:"Location"`
}
func (d machine) ID() shepherd.ProviderID {
return d.ProviderID
}
func (d machine) Addr() netip.Addr {
return d.Address
}
func (d machine) State() shepherd.State {
return shepherd.StatePossiblyUsed
}
func (p *provider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
machines := make([]shepherd.Machine, 0, len(p.machines))
for _, m := range p.machines {
machines = append(machines, m)
}
return machines, nil
}
func (p *provider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
// If the provided machine is not inside our known machines,
// bail-out early as this is unsupported.
if _, ok := p.machines[id]; !ok {
return nil, fmt.Errorf("unknown provided machine requested")
}
return p.machines[id], nil
}
func (p *provider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
if request.UnusedMachine == nil {
return nil, fmt.Errorf("parameter UnusedMachine is missing")
}
//TODO: Do we just trust the implementation to be correct?
m, ok := request.UnusedMachine.(machine)
if !ok {
return nil, fmt.Errorf("invalid type for parameter UnusedMachine")
}
if err := p.assimilate(ctx, session, m); err != nil {
klog.Errorf("Failed to provision machine %s: %v", m.ProviderID, err)
return nil, err
}
return m, nil
}
func (p *provider) assimilate(ctx context.Context, sess *bmdb.Session, machine machine) error {
return sess.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)
}
// Link the new machine with the device, and tag it "provided".
addParams := model.MachineAddProvidedParams{
MachineID: m.MachineID,
ProviderID: string(machine.ProviderID),
Provider: p.providerType,
}
klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, addParams.ProviderID, addParams.Provider)
if err := q.MachineAddProvided(ctx, addParams); err != nil {
return fmt.Errorf("while tagging machine active: %w", err)
}
upParams := model.MachineUpdateProviderStatusParams{
ProviderID: string(machine.ProviderID),
Provider: p.providerType,
ProviderIpAddress: sql.NullString{
String: machine.Address.String(),
Valid: true,
},
ProviderLocation: sql.NullString{
String: machine.Location,
Valid: machine.Location != "",
},
ProviderStatus: model.NullProviderStatus{
ProviderStatus: model.ProviderStatusUnknown,
Valid: true,
},
}
klog.Infof("Setting \"provided\" tag status parameter (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, upParams.ProviderID, upParams.Provider)
if err := q.MachineUpdateProviderStatus(ctx, upParams); err != nil {
return fmt.Errorf("while setting machine params: %w", err)
}
return nil
})
}
func (p *provider) Type() model.Provider {
return p.providerType
}