m/n/core/mgmt: implement node-local management service

Change-Id: I1e8a8ff46d1172e00f2d991ae3cc3af1929b6e4e
Reviewed-on: https://review.monogon.dev/c/monogon/+/1428
Tested-by: Jenkins CI
Reviewed-by: Lorenz Brun <lorenz@monogon.tech>
diff --git a/metropolis/node/core/mgmt/BUILD.bazel b/metropolis/node/core/mgmt/BUILD.bazel
new file mode 100644
index 0000000..41a25d2
--- /dev/null
+++ b/metropolis/node/core/mgmt/BUILD.bazel
@@ -0,0 +1,16 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library")
+
+go_library(
+    name = "mgmt",
+    srcs = ["mgmt.go"],
+    importpath = "source.monogon.dev/metropolis/node/core/mgmt",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//metropolis/node",
+        "//metropolis/node/core/identity",
+        "//metropolis/node/core/rpc",
+        "//metropolis/pkg/supervisor",
+        "//metropolis/proto/api",
+        "@org_golang_google_grpc//:go_default_library",
+    ],
+)
diff --git a/metropolis/node/core/mgmt/mgmt.go b/metropolis/node/core/mgmt/mgmt.go
new file mode 100644
index 0000000..5fa12a0
--- /dev/null
+++ b/metropolis/node/core/mgmt/mgmt.go
@@ -0,0 +1,46 @@
+// Package mgmt implements the node-local management service, a.k.a.
+// metropolis.proto.api.NodeManagement.
+package mgmt
+
+import (
+	"context"
+	"fmt"
+	"net"
+
+	"google.golang.org/grpc"
+
+	"source.monogon.dev/metropolis/node"
+	"source.monogon.dev/metropolis/node/core/identity"
+	"source.monogon.dev/metropolis/node/core/rpc"
+	"source.monogon.dev/metropolis/pkg/supervisor"
+
+	apb "source.monogon.dev/metropolis/proto/api"
+)
+
+type Service struct {
+	NodeCredentials *identity.NodeCredentials
+}
+
+func (s *Service) Run(ctx context.Context) error {
+	sec := rpc.ServerSecurity{
+		NodeCredentials: s.NodeCredentials,
+	}
+	logger := supervisor.MustSubLogger(ctx, "rpc")
+	opts := sec.GRPCOptions(logger)
+	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", node.NodeManagement))
+	if err != nil {
+		return fmt.Errorf("failed to listen on node management socket socket: %w", err)
+	}
+	defer lis.Close()
+
+	srv := grpc.NewServer(opts...)
+	apb.RegisterNodeManagementServer(srv, s)
+
+	runnable := supervisor.GRPCServer(srv, lis, false)
+	if err := supervisor.Run(ctx, "server", runnable); err != nil {
+		return fmt.Errorf("could not run server: %w", err)
+	}
+	supervisor.Signal(ctx, supervisor.SignalHealthy)
+	<-ctx.Done()
+	return ctx.Err()
+}