cloud: split shepherd up
Change-Id: I8e386d9eaaf17543743e1e8a37a8d71426910d59
Reviewed-on: https://review.monogon.dev/c/monogon/+/2213
Reviewed-by: Serge Bazanski <serge@monogon.tech>
Tested-by: Jenkins CI
diff --git a/cloud/shepherd/provider/equinix/fakequinix_test.go b/cloud/shepherd/provider/equinix/fakequinix_test.go
new file mode 100644
index 0000000..bd0df4a
--- /dev/null
+++ b/cloud/shepherd/provider/equinix/fakequinix_test.go
@@ -0,0 +1,221 @@
+package main
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "sync"
+
+ "github.com/google/uuid"
+ "github.com/packethost/packngo"
+)
+
+// fakequinix implements a wrapngo.Client for testing. It starts out with a
+// number of made up hardware reservations, and allows for creating devices and
+// SSH keys.
+type fakequinix struct {
+ mu sync.Mutex
+
+ pid string
+ devices map[string]*packngo.Device
+ reservations map[string]*packngo.HardwareReservation
+ sshKeys map[string]*packngo.SSHKey
+ reboots map[string]int
+}
+
+// newFakequinix makes a fakequinix with a given fake project ID and number of
+// hardware reservations to create.
+func newFakequinix(pid string, numReservations int) *fakequinix {
+ f := fakequinix{
+ pid: pid,
+ devices: make(map[string]*packngo.Device),
+ reservations: make(map[string]*packngo.HardwareReservation),
+ sshKeys: make(map[string]*packngo.SSHKey),
+ reboots: make(map[string]int),
+ }
+
+ for i := 0; i < numReservations; i++ {
+ uid := uuid.New()
+ f.reservations[uid.String()] = &packngo.HardwareReservation{
+ ID: uid.String(),
+ ShortID: uid.String(),
+ Provisionable: true,
+ }
+ }
+
+ return &f
+}
+
+func (f *fakequinix) notFound() error {
+ return &packngo.ErrorResponse{
+ Response: &http.Response{
+ StatusCode: http.StatusNotFound,
+ },
+ }
+}
+
+func (f *fakequinix) GetDevice(_ context.Context, pid, did string, _ *packngo.ListOptions) (*packngo.Device, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ val := f.devices[did]
+ if val == nil {
+ return nil, f.notFound()
+ }
+ return val, nil
+}
+
+func (f *fakequinix) ListDevices(_ context.Context, pid string) ([]packngo.Device, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ if pid != f.pid {
+ return nil, nil
+ }
+ var res []packngo.Device
+ for _, dev := range f.devices {
+ res = append(res, *dev)
+ }
+ return res, nil
+}
+
+func (f *fakequinix) UpdateDevice(ctx context.Context, id string, r *packngo.DeviceUpdateRequest) (*packngo.Device, error) {
+ return nil, fmt.Errorf("not implemented")
+}
+
+// MoveReservation is not implemented in fakequinix
+func (f *fakequinix) MoveReservation(_ context.Context, hardwareReservationDID, projectID string) (*packngo.HardwareReservation, error) {
+ return nil, &packngo.ErrorResponse{
+ Response: &http.Response{
+ StatusCode: http.StatusNotImplemented,
+ },
+ }
+}
+
+func (f *fakequinix) DeleteDevice(_ context.Context, id string) error {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ if _, ok := f.devices[id]; !ok {
+ return f.notFound()
+ }
+
+ delete(f.devices, id)
+
+ return nil
+}
+
+func (f *fakequinix) CreateDevice(_ context.Context, request *packngo.DeviceCreateRequest) (*packngo.Device, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ rid := request.HardwareReservationID
+ res := f.reservations[rid]
+ if res == nil {
+ return nil, f.notFound()
+ }
+ if res.Device != nil {
+ return nil, f.notFound()
+ }
+
+ dev := &packngo.Device{
+ ID: uuid.New().String(),
+ State: "active",
+ HardwareReservation: &packngo.HardwareReservation{
+ ID: rid,
+ },
+ Network: []*packngo.IPAddressAssignment{
+ {
+ IpAddressCommon: packngo.IpAddressCommon{
+ Public: true,
+ Address: "1.2.3.4",
+ },
+ },
+ },
+ Facility: &packngo.Facility{
+ Code: "wad",
+ },
+ Hostname: request.Hostname,
+ OS: &packngo.OS{
+ Name: request.OS,
+ Slug: request.OS,
+ },
+ }
+ res.Device = dev
+ res.Provisionable = false
+
+ f.devices[dev.ID] = dev
+ return dev, nil
+}
+
+func (f *fakequinix) ListReservations(_ context.Context, pid string) ([]packngo.HardwareReservation, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ var res []packngo.HardwareReservation
+ for _, r := range f.reservations {
+ res = append(res, *r)
+ }
+
+ return res, nil
+}
+
+func (f *fakequinix) ListSSHKeys(_ context.Context) ([]packngo.SSHKey, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ var res []packngo.SSHKey
+ for _, key := range f.sshKeys {
+ res = append(res, *key)
+ }
+
+ return res, nil
+}
+
+func (f *fakequinix) CreateSSHKey(_ context.Context, req *packngo.SSHKeyCreateRequest) (*packngo.SSHKey, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ for _, k := range f.sshKeys {
+ if k.Key == req.Key {
+ return nil, f.notFound()
+ }
+ if k.Label == req.Label {
+ return nil, f.notFound()
+ }
+ }
+
+ uid := uuid.New().String()
+ f.sshKeys[uid] = &packngo.SSHKey{
+ ID: uid,
+ Label: req.Label,
+ Key: req.Key,
+ }
+
+ return f.sshKeys[uid], nil
+}
+
+func (f *fakequinix) UpdateSSHKey(_ context.Context, kid string, req *packngo.SSHKeyUpdateRequest) (*packngo.SSHKey, error) {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ key := f.sshKeys[kid]
+ if key == nil {
+ return nil, f.notFound()
+ }
+ key.Key = *req.Key
+
+ return key, nil
+}
+
+func (f *fakequinix) RebootDevice(_ context.Context, did string) error {
+ f.mu.Lock()
+ defer f.mu.Unlock()
+
+ f.reboots[did]++
+
+ return nil
+}
+
+func (f *fakequinix) Close() {
+}