blob: 05b628f7460a1fdb630df8a6bb45532ff339068d [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
29func (d machine) ID() shepherd.ProviderID {
30 return d.ProviderID
31}
32
33func (d machine) Addr() netip.Addr {
34 return d.Address
35}
36
37func (d machine) State() shepherd.State {
38 return shepherd.StatePossiblyUsed
39}
40
41func (p *provider) ListMachines(ctx context.Context) ([]shepherd.Machine, error) {
42 machines := make([]shepherd.Machine, 0, len(p.machines))
43 for _, m := range p.machines {
44 machines = append(machines, m)
45 }
46
47 return machines, nil
48}
49
50func (p *provider) GetMachine(ctx context.Context, id shepherd.ProviderID) (shepherd.Machine, error) {
51 // If the provided machine is not inside our known machines,
52 // bail-out early as this is unsupported.
53 if _, ok := p.machines[id]; !ok {
54 return nil, fmt.Errorf("unknown provided machine requested")
55 }
56
57 return p.machines[id], nil
58}
59
60func (p *provider) CreateMachine(ctx context.Context, session *bmdb.Session, request shepherd.CreateMachineRequest) (shepherd.Machine, error) {
61 if request.UnusedMachine == nil {
62 return nil, fmt.Errorf("parameter UnusedMachine is missing")
63 }
64
65 //TODO: Do we just trust the implementation to be correct?
66 m, ok := request.UnusedMachine.(machine)
67 if !ok {
68 return nil, fmt.Errorf("invalid type for parameter UnusedMachine")
69 }
70
71 if err := p.assimilate(ctx, session, m); err != nil {
72 klog.Errorf("Failed to provision machine %s: %v", m.ProviderID, err)
73 return nil, err
74 }
75
76 return m, nil
77}
78
79func (p *provider) assimilate(ctx context.Context, sess *bmdb.Session, machine machine) error {
80 return sess.Transact(ctx, func(q *model.Queries) error {
81 // Create a new machine record within BMDB.
82 m, err := q.NewMachine(ctx)
83 if err != nil {
84 return fmt.Errorf("while creating a new machine record in BMDB: %w", err)
85 }
86
87 // Link the new machine with the device, and tag it "provided".
88 addParams := model.MachineAddProvidedParams{
89 MachineID: m.MachineID,
90 ProviderID: string(machine.ProviderID),
91 Provider: p.providerType,
92 }
93 klog.Infof("Setting \"provided\" tag (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, addParams.ProviderID, addParams.Provider)
94 if err := q.MachineAddProvided(ctx, addParams); err != nil {
95 return fmt.Errorf("while tagging machine active: %w", err)
96 }
97
98 upParams := model.MachineUpdateProviderStatusParams{
99 ProviderID: string(machine.ProviderID),
100 Provider: p.providerType,
101 ProviderIpAddress: sql.NullString{
102 String: machine.Address.String(),
103 Valid: true,
104 },
105 ProviderLocation: sql.NullString{
106 String: machine.Location,
107 Valid: machine.Location != "",
108 },
109 ProviderStatus: model.NullProviderStatus{
110 ProviderStatus: model.ProviderStatusUnknown,
111 Valid: true,
112 },
113 }
114
115 klog.Infof("Setting \"provided\" tag status parameter (ID: %s, PID: %s, Provider: %s).", addParams.MachineID, upParams.ProviderID, upParams.Provider)
116 if err := q.MachineUpdateProviderStatus(ctx, upParams); err != nil {
117 return fmt.Errorf("while setting machine params: %w", err)
118 }
119
120 return nil
121 })
122}
123
124func (p *provider) Type() model.Provider {
125 return p.providerType
126}