treewide: delete cloud/ except cloud/agent

bmdb is no longer used in production and would be hard to maintain. The only non-stub provider is Equinix Metal, which is shutting down in 2026, and we are moving away from CockroachDB. Keep the agent, which we'll need for Monogon Cloud.

Change-Id: If8b35c3ac8cdeed96a2b1814c0de7607e8acec63
Reviewed-on: https://review.monogon.dev/c/monogon/+/4235
Tested-by: Jenkins CI
Reviewed-by: Leopold Schabel <leo@monogon.tech>
diff --git a/cloud/agent/BUILD.bazel b/cloud/agent/BUILD.bazel
index bfb2147..48e7d20 100644
--- a/cloud/agent/BUILD.bazel
+++ b/cloud/agent/BUILD.bazel
@@ -14,7 +14,6 @@
     visibility = ["//visibility:private"],
     deps = [
         "//cloud/agent/api",
-        "//cloud/bmaas/server/api",
         "//metropolis/node/core/devmgr",
         "//metropolis/node/core/network",
         "//osbase/blockdev",
diff --git a/cloud/agent/agent.go b/cloud/agent/agent.go
index 05b2daf..665351a 100644
--- a/cloud/agent/agent.go
+++ b/cloud/agent/agent.go
@@ -22,7 +22,6 @@
 	"google.golang.org/protobuf/proto"
 
 	apb "source.monogon.dev/cloud/agent/api"
-	bpb "source.monogon.dev/cloud/bmaas/server/api"
 
 	"source.monogon.dev/metropolis/node/core/devmgr"
 	"source.monogon.dev/metropolis/node/core/network"
@@ -103,32 +102,32 @@
 	if err != nil {
 		return fmt.Errorf("error creating BMaaS gRPC client: %w", err)
 	}
-	c := bpb.NewAgentCallbackClient(conn)
+	c := apb.NewAgentCallbackClient(conn)
 
 	supervisor.Signal(ctx, supervisor.SignalHealthy)
 
-	assembleHWReport := func() *bpb.AgentHardwareReport {
+	assembleHWReport := func() *apb.AgentHardwareReport {
 		report, warnings := gatherHWReport()
 		var warningStrings []string
 		for _, w := range warnings {
 			l.Warningf("Hardware Report Warning: %v", w)
 			warningStrings = append(warningStrings, w.Error())
 		}
-		return &bpb.AgentHardwareReport{
+		return &apb.AgentHardwareReport{
 			Report:  report,
 			Warning: warningStrings,
 		}
 	}
 
 	var sentFirstHeartBeat, hwReportSent bool
-	var installationReport *bpb.OSInstallationReport
+	var installationReport *apb.OSInstallationReport
 	var installationGeneration int64
 	b := backoff.NewExponentialBackOff()
 	// Never stop retrying, there is nothing else to do
 	b.MaxElapsedTime = 0
 	// Main heartbeat loop
 	for {
-		req := bpb.HeartbeatRequest{
+		req := apb.HeartbeatRequest{
 			MachineId: agentInit.TakeoverInit.MachineId,
 		}
 		if sentFirstHeartBeat && !hwReportSent {
@@ -167,21 +166,21 @@
 				// This installation request has already been attempted
 				continue
 			}
-			installationReport = &bpb.OSInstallationReport{
+			installationReport = &apb.OSInstallationReport{
 				Generation: res.InstallationRequest.Generation,
 			}
 			installCtx, cancel := context.WithTimeout(ctx, 15*time.Minute)
 			if err := install(installCtx, res.InstallationRequest, agentInit.NetworkConfig); err != nil {
 				l.Errorf("Installation failed: %v", err)
-				installationReport.Result = &bpb.OSInstallationReport_Error_{
-					Error: &bpb.OSInstallationReport_Error{
+				installationReport.Result = &apb.OSInstallationReport_Error_{
+					Error: &apb.OSInstallationReport_Error{
 						Error: err.Error(),
 					},
 				}
 			} else {
 				l.Info("Installation succeeded")
-				installationReport.Result = &bpb.OSInstallationReport_Success_{
-					Success: &bpb.OSInstallationReport_Success{},
+				installationReport.Result = &apb.OSInstallationReport_Success_{
+					Success: &apb.OSInstallationReport_Success{},
 				}
 			}
 			cancel()
diff --git a/cloud/agent/api/BUILD.bazel b/cloud/agent/api/BUILD.bazel
index 48139f2..45d30e4 100644
--- a/cloud/agent/api/BUILD.bazel
+++ b/cloud/agent/api/BUILD.bazel
@@ -9,6 +9,7 @@
         "PACKAGE_VERSION_SUFFIX",
         "MESSAGE_PASCAL_CASE",
         "ENUM_ZERO_VALUE_SUFFIX",
+        "SERVICE_SUFFIX",
     ],
     protos = [":api_proto"],
     use_rules = [
@@ -25,15 +26,22 @@
         "takeover.proto",
     ],
     visibility = ["//visibility:public"],
-    deps = ["//osbase/net/proto:proto_proto"],
+    deps = [
+        "//metropolis/proto/api:api_proto",
+        "//osbase/net/proto:proto_proto",
+    ],
 )
 
 go_proto_library(
     name = "api_go_proto",
+    compilers = ["@io_bazel_rules_go//proto:go_grpc"],
     importpath = "source.monogon.dev/cloud/agent/api",
     proto = ":api_proto",
     visibility = ["//visibility:public"],
-    deps = ["//osbase/net/proto"],
+    deps = [
+        "//metropolis/proto/api",
+        "//osbase/net/proto",
+    ],
 )
 
 go_library(
diff --git a/cloud/agent/api/agent.proto b/cloud/agent/api/agent.proto
index c2ac0f7..f62b2b0 100644
--- a/cloud/agent/api/agent.proto
+++ b/cloud/agent/api/agent.proto
@@ -2,17 +2,111 @@
 package cloud.agent.api;
 import "osbase/net/proto/net.proto";
 import "cloud/agent/api/takeover.proto";
+import "cloud/agent/api/hwreport.proto";
+import "metropolis/proto/api/configuration.proto";
+import "metropolis/proto/api/management.proto";
 option go_package = "source.monogon.dev/cloud/agent/api";
 
 // AgentInit contains initialization information passed to the agent from the 
 // initial takeover process.
 message AgentInit {
-  // Original takeover init message which contains data to contact the BMaaS
-  // service with.
+  // Original takeover init message which contains data to contact the API
+  // server with.
   TakeoverInit takeover_init = 1;
-  // The Ed25519 private key to connect to the BMaaS service.
+  // The Ed25519 private key to connect to the API server.
   bytes private_key = 2;
   // A network configuration in case automatic configuration does not work or is
   // not desired. If left unset, automatic configuration is used.
   osbase.net.proto.Net network_config = 3;
-}
\ No newline at end of file
+}
+
+// AgentCallback runs on the API Server and exposes a gRPC interface to agents
+// running on machines. These APIs are served over TLS using component-style
+// server certificates, but clients are authenticated using ephemeral
+// certificates proving ownership of an agent keypair.
+service AgentCallback {
+  // Heartbeat is called by agents repeatedly to upload a hardware report, signal
+  // liveness and retrieve actions to be performed on a host.
+  //
+  // This isn't a streaming RPC as the current server implementation actually
+  // isn't reactive, so it would have to do its own inner polling to create
+  // a stream of updates. To keep things simple, we instead let the agent decide
+  // on the cadence of updates it wants to keep up with.
+  rpc Heartbeat(HeartbeatRequest) returns (HeartbeatResponse);
+}
+
+message AgentHardwareReport {
+  cloud.agent.api.Node report = 1;
+  // List of human-readable warnings which occurred during hardware report
+  // generation.
+  repeated string warning = 2;
+}
+
+// OSInstallationReport is submitted from the agent to the API server after
+// successful OS installation.
+message OSInstallationReport {
+  // generation must be set to the same value as 'generation' in the
+  // OSInstallation request which triggered the OS installation
+  int64 generation = 1;
+
+  // Success is set by the agent when the installation request has been
+  // successfully fulfilled. It is currently empty but is specified as a
+  // message to allow it to be expanded in the future.
+  message Success {}
+  // Error is set by the agent when the installation request could not be
+  // fulfilled due to an error.
+  message Error {
+    // A human-readable message of what went wrong.
+    string error = 1;
+  }
+  oneof result {
+    Success success = 2;
+    Error error = 3;
+  }
+}
+
+message HeartbeatRequest {
+  // MachineID that this agent represents. Technically not necessary since
+  // keypairs between agents should be unique, but this provides an extra layer
+  // of protection against programming bugs.
+  string machine_id = 1;
+  // Optional hardware report to be upserted for this machine. An agent should
+  // submit one at least once after it's started, as early as it can.
+  AgentHardwareReport hardware_report = 2;
+  // Optional installation report sent to be upserted to this machine. An agent
+  // should submit one after it successfully installed an operating system for
+  // a given OSInstallationRequest.
+  OSInstallationReport installation_report = 3;
+}
+
+message MetropolisInstallationRequest {
+  reserved 1;
+  // Parameters for fetching the OS image to install.
+  metropolis.proto.api.OSImageRef os_image = 4;
+  // Node parameters to be supplied to the new node. Note that network_config
+  // is automatically filled out if coming from the takeover.
+  metropolis.proto.api.NodeParameters node_parameters = 2;
+  // Name of the block device to be used as the root device for the install.
+  // A list of block devices can be taken from the node hardware report.
+  string root_device = 3;
+}
+
+// OSInstallationRequest is provided to the agent API server.
+message OSInstallationRequest {
+  // generation is the 'version' of the OS installation request, and will always
+  // be incremented within the API when a new OS installation request is
+  // submitted. The agent must pipe this through to the OSInstallationReport to
+  // let the rest of the system know which OS installation request it actually
+  // fulfilled.
+  int64 generation = 1;
+  // Selects which operating system installation flow is used.
+  oneof type {
+    MetropolisInstallationRequest metropolis = 2;
+  }
+}
+
+message HeartbeatResponse {
+  // If set, the control plane is requesting the installation of an operating
+  // system.
+  OSInstallationRequest installation_request = 1;
+}
diff --git a/cloud/agent/e2e/BUILD.bazel b/cloud/agent/e2e/BUILD.bazel
index 7df7ad6..91f09ce 100644
--- a/cloud/agent/e2e/BUILD.bazel
+++ b/cloud/agent/e2e/BUILD.bazel
@@ -19,7 +19,6 @@
     },
     deps = [
         "//cloud/agent/api",
-        "//cloud/bmaas/server/api",
         "//metropolis/proto/api",
         "//osbase/oci",
         "//osbase/oci/registry",
diff --git a/cloud/agent/e2e/main_test.go b/cloud/agent/e2e/main_test.go
index 8dbce49..dc3108e 100644
--- a/cloud/agent/e2e/main_test.go
+++ b/cloud/agent/e2e/main_test.go
@@ -31,8 +31,8 @@
 	"google.golang.org/protobuf/proto"
 
 	apb "source.monogon.dev/cloud/agent/api"
-	bpb "source.monogon.dev/cloud/bmaas/server/api"
 	mpb "source.monogon.dev/metropolis/proto/api"
+
 	"source.monogon.dev/osbase/oci"
 	"source.monogon.dev/osbase/oci/registry"
 	"source.monogon.dev/osbase/pki"
@@ -63,13 +63,13 @@
 }
 
 type fakeServer struct {
-	hardwareReport      *bpb.AgentHardwareReport
-	installationRequest *bpb.OSInstallationRequest
-	installationReport  *bpb.OSInstallationReport
+	hardwareReport      *apb.AgentHardwareReport
+	installationRequest *apb.OSInstallationRequest
+	installationReport  *apb.OSInstallationReport
 }
 
-func (f *fakeServer) Heartbeat(ctx context.Context, req *bpb.HeartbeatRequest) (*bpb.HeartbeatResponse, error) {
-	var res bpb.HeartbeatResponse
+func (f *fakeServer) Heartbeat(ctx context.Context, req *apb.HeartbeatRequest) (*apb.HeartbeatResponse, error) {
+	var res apb.HeartbeatResponse
 	if req.HardwareReport != nil {
 		f.hardwareReport = req.HardwareReport
 	}
@@ -106,9 +106,9 @@
 		t.Fatal(err)
 	}
 
-	f.installationRequest = &bpb.OSInstallationRequest{
+	f.installationRequest = &apb.OSInstallationRequest{
 		Generation: 5,
-		Type: &bpb.OSInstallationRequest_Metropolis{Metropolis: &bpb.MetropolisInstallationRequest{
+		Type: &apb.OSInstallationRequest_Metropolis{Metropolis: &apb.MetropolisInstallationRequest{
 			OsImage: &mpb.OSImageRef{
 				Scheme:     "http",
 				Host:       registryAddr.String(),
@@ -169,7 +169,7 @@
 		Certificate: [][]byte{serverCert},
 		PrivateKey:  serverPrivKey,
 	})))
-	bpb.RegisterAgentCallbackServer(s, &f)
+	apb.RegisterAgentCallbackServer(s, &f)
 	grpcLis, err := net.Listen("tcp", "127.0.0.1:0")
 	if err != nil {
 		panic(err)
diff --git a/cloud/agent/install.go b/cloud/agent/install.go
index 6deffab..2ba9859 100644
--- a/cloud/agent/install.go
+++ b/cloud/agent/install.go
@@ -15,7 +15,7 @@
 	"github.com/cenkalti/backoff/v4"
 	"google.golang.org/protobuf/proto"
 
-	bpb "source.monogon.dev/cloud/bmaas/server/api"
+	apb "source.monogon.dev/cloud/agent/api"
 	npb "source.monogon.dev/osbase/net/proto"
 
 	"source.monogon.dev/osbase/blockdev"
@@ -32,16 +32,16 @@
 
 // install dispatches OSInstallationRequests to the appropriate installer
 // method
-func install(ctx context.Context, req *bpb.OSInstallationRequest, netConfig *npb.Net) error {
+func install(ctx context.Context, req *apb.OSInstallationRequest, netConfig *npb.Net) error {
 	switch reqT := req.Type.(type) {
-	case *bpb.OSInstallationRequest_Metropolis:
+	case *apb.OSInstallationRequest_Metropolis:
 		return installMetropolis(ctx, reqT.Metropolis, netConfig)
 	default:
 		return errors.New("unknown installation request type")
 	}
 }
 
-func installMetropolis(ctx context.Context, req *bpb.MetropolisInstallationRequest, netConfig *npb.Net) error {
+func installMetropolis(ctx context.Context, req *apb.MetropolisInstallationRequest, netConfig *npb.Net) error {
 	l := supervisor.Logger(ctx)
 	// Validate we are running via EFI.
 	if _, err := os.Stat("/sys/firmware/efi"); os.IsNotExist(err) {