blob: 42b6348c7df59442973d316a4fd40e0da4b97fdb [file] [log] [blame]
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +02001package main
2
3import (
4 "context"
5 "database/sql"
6 "fmt"
7 "net/netip"
8
9 "k8s.io/klog/v2"
10
11 "source.monogon.dev/cloud/bmaas/bmdb"
12 "source.monogon.dev/cloud/bmaas/bmdb/model"
13 "source.monogon.dev/cloud/shepherd"
14)
15
16// provider represents a shepherd.Provider that works entirely on a
17// static device list. It requires a provider type and a device list.
18type provider struct {
19 providerType model.Provider
20 machines map[shepherd.ProviderID]machine
21}
22
23type machine struct {
24 ProviderID shepherd.ProviderID `json:"ID"`
25 Address netip.Addr `json:"Addr"`
26 Location string `json:"Location"`
27}
28
Tim Windelschmidtfdd87ab2023-12-07 18:03:21 +010029func (d machine) Failed() bool {
30 return false
31}
32
Tim Windelschmidtb6308cd2023-10-10 21:19:03 +020033func (d machine) ID() shepherd.ProviderID {
34 return d.ProviderID
35}
36
37func (d machine) Addr() netip.Addr {
38 return d.Address
39}
40
41func (d machine) State() shepherd.State {
42 return shepherd.StatePossiblyUsed
43}
44
45func (p *provider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
46 machines := make([]shepherd.Machine, 0, len(p.machines))
47 for _, m := range p.machines {
48 machines = append(machines, m)
49 }
50
51 return machines, nil
52}
53
54func (p *provider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
55 // If the provided machine is not inside our known machines,
56 // bail-out early as this is unsupported.
57 if _, ok := p.machines[id]; !ok {
58 return nil, fmt.Errorf("unknown provided machine requested")
59 }
60
61 return p.machines[id], nil
62}
63
64func (p *provider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
65 if request.UnusedMachine == nil {
66 return nil, fmt.Errorf("parameter UnusedMachine is missing")
67 }
68
69 //TODO: Do we just trust the implementation to be correct?
70 m, ok := request.UnusedMachine.(machine)
71 if !ok {
72 return nil, fmt.Errorf("invalid type for parameter UnusedMachine")
73 }
74
75 if err := p.assimilate(ctx, session, m); err != nil {
76 klog.Errorf("Failed to provision machine %s: %v", m.ProviderID, err)
77 return nil, err
78 }
79
80 return m, nil
81}
82
83func (p *provider) assimilate(ctx context.Context, sess *bmdb.Session, machine machine) error {
84 return sess.Transact(ctx, func(q *model.Queries) error {
85 // Create a new machine record within BMDB.
86 m, err := q.NewMachine(ctx)
87 if err != nil {
88 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
89 }
90
91 // Link the new machine with the device, and tag it "provided".
92 addParams := model.MachineAddProvidedParams{
93 MachineID: m.MachineID,
94 ProviderID: string(machine.ProviderID),
95 Provider: p.providerType,
96 }
97 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, addParams.ProviderID, addParams.Provider)
98 if err := q.MachineAddProvided(ctx, addParams); err != nil {
99 return fmt.Errorf("while tagging machine active: %w", err)
100 }
101
102 upParams := model.MachineUpdateProviderStatusParams{
103 ProviderID: string(machine.ProviderID),
104 Provider: p.providerType,
105 ProviderIpAddress: sql.NullString{
106 String: machine.Address.String(),
107 Valid: true,
108 },
109 ProviderLocation: sql.NullString{
110 String: machine.Location,
111 Valid: machine.Location != "",
112 },
113 ProviderStatus: model.NullProviderStatus{
114 ProviderStatus: model.ProviderStatusUnknown,
115 Valid: true,
116 },
117 }
118
119 klog.Infof("Setting \"provided\" tag status parameter (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, upParams.ProviderID, upParams.Provider)
120 if err := q.MachineUpdateProviderStatus(ctx, upParams); err != nil {
121 return fmt.Errorf("while setting machine params: %w", err)
122 }
123
124 return nil
125 })
126}
127
128func (p *provider) Type() model.Provider {
129 return p.providerType
130}