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/shepherd.go b/cloud/shepherd/shepherd.go
new file mode 100644
index 0000000..3504eb7
--- /dev/null
+++ b/cloud/shepherd/shepherd.go
@@ -0,0 +1,119 @@
+package shepherd
+
+import (
+	"context"
+	"fmt"
+	"net/netip"
+
+	"source.monogon.dev/cloud/bmaas/bmdb"
+	"source.monogon.dev/cloud/bmaas/bmdb/model"
+)
+
+var ErrMachineNotFound = fmt.Errorf("machine not found")
+var ErrNotImplemented = fmt.Errorf("not implemented")
+
+// ProviderID is an opaque unique identifier for a machine within a single
+// provider instance. It is generated by the Provider and usually the same
+// as the ID of the machine within the system that the Provider managed.
+// The Shepherd (and BMaaS in general) requires these IDs to be unique
+// within a provider and stable.
+type ProviderID string
+
+const InvalidProviderID ProviderID = "invalid"
+
+// IsValid reports whether the ProviderID is valid.
+func (p ProviderID) IsValid() bool {
+	return p != InvalidProviderID
+}
+
+// State defines in which state the machine is.
+// See the different states for more information.
+type State int
+
+const (
+	// StateUndefined is used as a placeholder to prevent that the default
+	// value can create any type of bad behaviour.
+	StateUndefined State = iota
+	// StatePossiblyUsed defines the state where a machine is possibly used,
+	// this is a state for use in stateless providers where the shepherd has
+	// to check against the bmdb if Machine.ID is already provisioned or not.
+	// These machines must have a valid ID and Addr.
+	StatePossiblyUsed
+	// StateKnownUnused defines the state where a machine is know to be free,
+	// e.g. a hardware reservation at equinix. These machines may not have an
+	// ID or Addr.
+	StateKnownUnused
+	// StateKnownUsed defines the state where a machine is known to be used,
+	// e.g. a deployed machine that is in use. These machines must have a
+	// valid ID and Addr.
+	StateKnownUsed
+)
+
+func (s State) String() string {
+	switch s {
+	case StateUndefined:
+		return "Undefined"
+	case StateKnownUnused:
+		return "KnownUnused"
+	case StateKnownUsed:
+		return "KnownUsed"
+	case StatePossiblyUsed:
+		return "PossiblyUsed"
+	default:
+		return fmt.Sprintf("<invalid value %d>", s)
+	}
+}
+
+type Machine interface {
+	// ID returns the provider id, see ProviderID for more information.
+	ID() ProviderID
+	// Addr returns the machines ip address that is reachable from the
+	// shepherd. It is used to connect to the machine via SSH to execute
+	// all takeover tasks, etc.
+	Addr() netip.Addr
+	// State returns the state in which the machine is
+	State() State
+}
+
+type CreateMachineRequest struct {
+	// UnusedMachine resembles a machine to use as deployment target.
+	UnusedMachine Machine
+}
+
+// Provider is the interface that is used to abstract the interaction between
+// the shepherd and machine providers like Equinix. All methods inside this
+// interface must not be called concurrently.
+type Provider interface {
+	// ListMachines returns all existing machines for a provider. Machines
+	// that are still in the state of being created by CreateMachine should
+	// not be returned.
+	ListMachines(context.Context) ([]Machine, error)
+
+	// GetMachine returns an existing machine for a provider. Machines
+	// that are still in the state of being created by CreateMachine should
+	// not be returned. If a there are no machines found after these filters
+	// an error should be returned.
+	GetMachine(context.Context, ProviderID) (Machine, error)
+
+	// CreateMachine creates a new machine with the given parameters and
+	// returns the created instance. The provider is required to create the
+	// entry into the machine table and MachineProvided tag. If there are no
+	// more machines avaiable, an error should be returned.
+	CreateMachine(context.Context, *bmdb.Session, CreateMachineRequest) (Machine, error)
+
+	// Type returns the value that represents this provider inside the database.
+	Type() model.Provider
+}
+
+type Recoverer interface {
+	Provider
+
+	// RebootMachine tries to bring a machine back from the dead by e.g. rebooting
+	RebootMachine(context.Context, ProviderID) error
+
+	// ReinstallMachine should reinstall the given machine and if the provider
+	// does not support reinstallation, the function should return an error
+	// stating this. If reinstalled, the installed tag should be updated to
+	// allow the reconcile loop to restart the takeover process.
+	ReinstallMachine(context.Context, ProviderID) error
+}