metropolis/roleserver: expose cluster credentials to external users

This will be used by the terminal console to access information about
the node ID and CA fingerprint.

Change-Id: Ia9ff6ab1b5b903415b8275d6b4156ba176bbbf1b
Reviewed-on: https://review.monogon.dev/c/monogon/+/3369
Reviewed-by: Leopold Schabel <leo@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/node/core/roleserve/roleserve.go b/metropolis/node/core/roleserve/roleserve.go
index f03a83b..3bcc48d 100644
--- a/metropolis/node/core/roleserve/roleserve.go
+++ b/metropolis/node/core/roleserve/roleserve.go
@@ -82,11 +82,11 @@
 
 	KubernetesStatus      memory.Value[*KubernetesStatus]
 	bootstrapData         memory.Value[*BootstrapData]
-	localRoles            memory.Value[*cpb.NodeRoles]
+	LocalRoles            memory.Value[*cpb.NodeRoles]
 	podNetwork            memory.Value[*clusternet.Prefixes]
 	clusterDirectorySaved memory.Value[bool]
 	localControlPlane     memory.Value[*localControlPlane]
-	CuratorConnection     memory.Value[*curatorConnection]
+	CuratorConnection     memory.Value[*CuratorConnection]
 
 	controlPlane *workerControlPlane
 	statusPush   *workerStatusPush
@@ -108,7 +108,7 @@
 		storageRoot: s.StorageRoot,
 
 		bootstrapData: &s.bootstrapData,
-		localRoles:    &s.localRoles,
+		localRoles:    &s.LocalRoles,
 		resolver:      s.Resolver,
 
 		localControlPlane: &s.localControlPlane,
@@ -133,7 +133,7 @@
 		network:     s.Network,
 		storageRoot: s.StorageRoot,
 
-		localRoles:        &s.localRoles,
+		localRoles:        &s.LocalRoles,
 		localControlPlane: &s.localControlPlane,
 		curatorConnection: &s.CuratorConnection,
 
@@ -145,7 +145,7 @@
 		storageRoot:       s.StorageRoot,
 		curatorConnection: &s.CuratorConnection,
 
-		localRoles: &s.localRoles,
+		localRoles: &s.LocalRoles,
 	}
 
 	s.nodeMgmt = &workerNodeMgmt{
@@ -171,7 +171,7 @@
 
 	s.metrics = &workerMetrics{
 		curatorConnection: &s.CuratorConnection,
-		localRoles:        &s.localRoles,
+		localRoles:        &s.LocalRoles,
 		localControlplane: &s.localControlPlane,
 	}
 
diff --git a/metropolis/node/core/roleserve/values.go b/metropolis/node/core/roleserve/values.go
index 91510ae..34c743b 100644
--- a/metropolis/node/core/roleserve/values.go
+++ b/metropolis/node/core/roleserve/values.go
@@ -42,28 +42,28 @@
 //
 // This structure should also be used by roleserver runnables that simply wish to
 // access the node's credentials.
-type curatorConnection struct {
-	credentials *identity.NodeCredentials
+type CuratorConnection struct {
+	Credentials *identity.NodeCredentials
 	resolver    *resolver.Resolver
 	conn        *grpc.ClientConn
 }
 
-func newCuratorConnection(creds *identity.NodeCredentials, res *resolver.Resolver) *curatorConnection {
+func newCuratorConnection(creds *identity.NodeCredentials, res *resolver.Resolver) *CuratorConnection {
 	c := rpc.NewAuthenticatedCredentials(creds.TLSCredentials(), rpc.WantRemoteCluster(creds.ClusterCA()))
 	conn, err := grpc.Dial(resolver.MetropolisControlAddress, grpc.WithTransportCredentials(c), grpc.WithResolvers(res))
 	if err != nil {
 		// TOOD(q3k): triple check that Dial will not fail
 		panic(err)
 	}
-	return &curatorConnection{
-		credentials: creds,
+	return &CuratorConnection{
+		Credentials: creds,
 		resolver:    res,
 		conn:        conn,
 	}
 }
 
-func (c *curatorConnection) nodeID() string {
-	return identity.NodeID(c.credentials.PublicKey())
+func (c *CuratorConnection) nodeID() string {
+	return identity.NodeID(c.Credentials.PublicKey())
 }
 
 // KubernetesStatus is an Event Value structure populated by a running
diff --git a/metropolis/node/core/roleserve/worker_clusternet.go b/metropolis/node/core/roleserve/worker_clusternet.go
index a2db859..6665b4a 100644
--- a/metropolis/node/core/roleserve/worker_clusternet.go
+++ b/metropolis/node/core/roleserve/worker_clusternet.go
@@ -17,7 +17,7 @@
 	storageRoot *localstorage.Root
 
 	// curatorConnection will be read
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 	// podNetwork will be read.
 	podNetwork *memory.Value[*clusternet.Prefixes]
 	network    *network.Service
diff --git a/metropolis/node/core/roleserve/worker_controlplane.go b/metropolis/node/core/roleserve/worker_controlplane.go
index dbae49f..3ba4293 100644
--- a/metropolis/node/core/roleserve/worker_controlplane.go
+++ b/metropolis/node/core/roleserve/worker_controlplane.go
@@ -49,7 +49,7 @@
 	// localControlPlane will be written.
 	localControlPlane *memory.Value[*localControlPlane]
 	// curatorConnection will be written.
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 }
 
 // controlPlaneStartup is used internally to provide a reduced (as in MapReduce)
@@ -62,7 +62,7 @@
 	// bootstrap is set if this node should bootstrap consensus. It contains all
 	// data required to perform this bootstrap step.
 	bootstrap *BootstrapData
-	existing  *curatorConnection
+	existing  *CuratorConnection
 }
 
 // changed informs the Control Plane launcher whether two different
@@ -95,21 +95,21 @@
 	// which is okay as long as the entire tree restarts simultaneously (which we
 	// ensure via RunGroup).
 	bootstrapDataC := make(chan *BootstrapData)
-	curatorConnectionC := make(chan *curatorConnection)
+	curatorConnectionC := make(chan *CuratorConnection)
 	rolesC := make(chan *cpb.NodeRoles)
 
 	supervisor.RunGroup(ctx, map[string]supervisor.Runnable{
 		// Plain conversion from Event Value to channel.
 		"map-bootstrap-data": event.Pipe[*BootstrapData](s.bootstrapData, bootstrapDataC),
 		// Plain conversion from Event Value to channel.
-		"map-curator-connection": event.Pipe[*curatorConnection](s.curatorConnection, curatorConnectionC),
+		"map-curator-connection": event.Pipe[*CuratorConnection](s.curatorConnection, curatorConnectionC),
 		// Plain conversion from Event Value to channel.
 		"map-roles": event.Pipe[*cpb.NodeRoles](s.localRoles, rolesC),
 		// Provide config from above.
 		"reduce-config": func(ctx context.Context) error {
 			supervisor.Signal(ctx, supervisor.SignalHealthy)
 			var lr *cpb.NodeRoles
-			var cc *curatorConnection
+			var cc *CuratorConnection
 			var bd *BootstrapData
 			for {
 				select {
@@ -197,7 +197,7 @@
 						consensusConfig: &consensus.Config{
 							Data:           &s.storageRoot.Data.Etcd,
 							Ephemeral:      &s.storageRoot.Ephemeral.Consensus,
-							NodePrivateKey: cc.credentials.TLSCredentials().PrivateKey.(ed25519.PrivateKey),
+							NodePrivateKey: cc.Credentials.TLSCredentials().PrivateKey.(ed25519.PrivateKey),
 							JoinCluster: &consensus.JoinCluster{
 								CACertificate:   caCert,
 								NodeCertificate: peerCert,
@@ -320,14 +320,14 @@
 				if startup.existing == nil {
 					panic("no existing curator connection but not bootstrapping either")
 				}
-				if startup.existing.credentials == nil {
+				if startup.existing.Credentials == nil {
 					panic("no existing.credentials but not bootstrapping either")
 				}
 
 				// Use already existing credentials, and pass over already known curators (as
 				// we're not the only node, and we'd like downstream consumers to be able to
 				// keep connecting to existing curators in case the local one fails).
-				creds = startup.existing.credentials
+				creds = startup.existing.Credentials
 			}
 
 			// Start curator.
diff --git a/metropolis/node/core/roleserve/worker_heartbeat.go b/metropolis/node/core/roleserve/worker_heartbeat.go
index 7a77cb9..b543334 100644
--- a/metropolis/node/core/roleserve/worker_heartbeat.go
+++ b/metropolis/node/core/roleserve/worker_heartbeat.go
@@ -19,7 +19,7 @@
 	network *network.Service
 
 	// curatorConnection will be read.
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 }
 
 func (s *workerHeartbeat) run(ctx context.Context) error {
diff --git a/metropolis/node/core/roleserve/worker_hostsfile.go b/metropolis/node/core/roleserve/worker_hostsfile.go
index 7beff2f..54e2f49 100644
--- a/metropolis/node/core/roleserve/worker_hostsfile.go
+++ b/metropolis/node/core/roleserve/worker_hostsfile.go
@@ -21,7 +21,7 @@
 
 	// network will be read. It provides data about the local node's address.
 	network           *network.Service
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 
 	// clusterDirectorySaved will be written. A value of true indicates that the
 	// cluster directory has been successfully written at least once to the ESP.
diff --git a/metropolis/node/core/roleserve/worker_kubernetes.go b/metropolis/node/core/roleserve/worker_kubernetes.go
index b50fe20..7b6038d 100644
--- a/metropolis/node/core/roleserve/worker_kubernetes.go
+++ b/metropolis/node/core/roleserve/worker_kubernetes.go
@@ -33,7 +33,7 @@
 
 	localRoles        *memory.Value[*cpb.NodeRoles]
 	localControlPlane *memory.Value[*localControlPlane]
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 	kubernetesStatus  *memory.Value[*KubernetesStatus]
 	podNetwork        *memory.Value[*clusternet.Prefixes]
 }
@@ -69,14 +69,14 @@
 	var startupV memory.Value[*kubernetesStartup]
 
 	localControlPlaneC := make(chan *localControlPlane)
-	curatorConnectionC := make(chan *curatorConnection)
+	curatorConnectionC := make(chan *CuratorConnection)
 	rolesC := make(chan *cpb.NodeRoles)
 
 	supervisor.RunGroup(ctx, map[string]supervisor.Runnable{
 		// Plain conversion from Event Value to channel.
 		"map-local-control-plane": event.Pipe[*localControlPlane](s.localControlPlane, localControlPlaneC),
 		// Plain conversion from Event Value to channel.
-		"map-curator-connection": event.Pipe[*curatorConnection](s.curatorConnection, curatorConnectionC),
+		"map-curator-connection": event.Pipe[*CuratorConnection](s.curatorConnection, curatorConnectionC),
 		// Plain conversion from Event Value to channel.
 		"map-roles": event.Pipe[*cpb.NodeRoles](s.localRoles, rolesC),
 		// Provide config from the above.
@@ -84,7 +84,7 @@
 			supervisor.Signal(ctx, supervisor.SignalHealthy)
 			var lr *cpb.NodeRoles
 			var lcp *localControlPlane
-			var cc *curatorConnection
+			var cc *CuratorConnection
 			for {
 				select {
 				case <-ctx.Done():
@@ -97,7 +97,7 @@
 					startupV.Set(&kubernetesStartup{
 						roles:   lr,
 						lcp:     lcp,
-						node:    cc.credentials,
+						node:    cc.Credentials,
 						curator: ipb.NewCuratorClient(cc.conn),
 					})
 				}
diff --git a/metropolis/node/core/roleserve/worker_metrics.go b/metropolis/node/core/roleserve/worker_metrics.go
index e2a78b8..8db7b4f 100644
--- a/metropolis/node/core/roleserve/worker_metrics.go
+++ b/metropolis/node/core/roleserve/worker_metrics.go
@@ -17,7 +17,7 @@
 // sub-processes), and a forwarding service that lets external users access them
 // over HTTPS using the Cluster CA.
 type workerMetrics struct {
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 	localRoles        *memory.Value[*cpb.NodeRoles]
 	localControlplane *memory.Value[*localControlPlane]
 }
@@ -34,7 +34,7 @@
 	supervisor.Logger(ctx).Infof("Got curator connection, starting...")
 
 	svc := metrics.Service{
-		Credentials: cc.credentials,
+		Credentials: cc.Credentials,
 		Discovery: metrics.Discovery{
 			Curator: ipb.NewCuratorClient(cc.conn),
 		},
diff --git a/metropolis/node/core/roleserve/worker_nodemgmt.go b/metropolis/node/core/roleserve/worker_nodemgmt.go
index 52f1086..11f8faa 100644
--- a/metropolis/node/core/roleserve/worker_nodemgmt.go
+++ b/metropolis/node/core/roleserve/worker_nodemgmt.go
@@ -11,7 +11,7 @@
 )
 
 type workerNodeMgmt struct {
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 	logTree           *logtree.LogTree
 	updateService     *update.Service
 }
@@ -27,7 +27,7 @@
 
 	supervisor.Logger(ctx).Infof("Got cluster membership, starting...")
 	srv := mgmt.Service{
-		NodeCredentials: cc.credentials,
+		NodeCredentials: cc.Credentials,
 		LogTree:         s.logTree,
 		UpdateService:   s.updateService,
 	}
diff --git a/metropolis/node/core/roleserve/worker_rolefetch.go b/metropolis/node/core/roleserve/worker_rolefetch.go
index 726fc69..304451b 100644
--- a/metropolis/node/core/roleserve/worker_rolefetch.go
+++ b/metropolis/node/core/roleserve/worker_rolefetch.go
@@ -19,7 +19,7 @@
 // the node is HOME and cluster credentials / curator access is available.
 type workerRoleFetch struct {
 	storageRoot       *localstorage.Root
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 
 	// localRoles will be written.
 	localRoles *memory.Value[*cpb.NodeRoles]
diff --git a/metropolis/node/core/roleserve/worker_statuspush.go b/metropolis/node/core/roleserve/worker_statuspush.go
index 0cbc442..c6987c3 100644
--- a/metropolis/node/core/roleserve/worker_statuspush.go
+++ b/metropolis/node/core/roleserve/worker_statuspush.go
@@ -25,7 +25,7 @@
 	// localControlPlane will be read
 	localControlPlane *memory.Value[*localControlPlane]
 	// curatorConnection will be read.
-	curatorConnection *memory.Value[*curatorConnection]
+	curatorConnection *memory.Value[*CuratorConnection]
 	// clusterDirectorySaved will be read.
 	clusterDirectorySaved *memory.Value[bool]
 }
@@ -36,7 +36,7 @@
 	// address of the node, or empty if none. Retrieved from network service.
 	address           chan string
 	localControlPlane chan *localControlPlane
-	curatorConnection chan *curatorConnection
+	curatorConnection chan *CuratorConnection
 }
 
 // workerStatusPushLoop runs the main loop acting on data received from
@@ -107,7 +107,7 @@
 func (s *workerStatusPush) run(ctx context.Context) error {
 	chans := workerStatusPushChannels{
 		address:           make(chan string),
-		curatorConnection: make(chan *curatorConnection),
+		curatorConnection: make(chan *CuratorConnection),
 		localControlPlane: make(chan *localControlPlane),
 	}
 
@@ -137,7 +137,7 @@
 		}
 	})
 	supervisor.Run(ctx, "pipe-local-control-plane", event.Pipe[*localControlPlane](s.localControlPlane, chans.localControlPlane))
-	supervisor.Run(ctx, "pipe-curator-connection", event.Pipe[*curatorConnection](s.curatorConnection, chans.curatorConnection))
+	supervisor.Run(ctx, "pipe-curator-connection", event.Pipe[*CuratorConnection](s.curatorConnection, chans.curatorConnection))
 
 	supervisor.Signal(ctx, supervisor.SignalHealthy)
 	return workerStatusPushLoop(ctx, &chans)
diff --git a/metropolis/node/core/roleserve/worker_statuspush_test.go b/metropolis/node/core/roleserve/worker_statuspush_test.go
index 39222bd..760d97a 100644
--- a/metropolis/node/core/roleserve/worker_statuspush_test.go
+++ b/metropolis/node/core/roleserve/worker_statuspush_test.go
@@ -69,7 +69,7 @@
 	chans := workerStatusPushChannels{
 		address:           make(chan string),
 		localControlPlane: make(chan *localControlPlane),
-		curatorConnection: make(chan *curatorConnection),
+		curatorConnection: make(chan *CuratorConnection),
 	}
 
 	go supervisor.TestHarness(t, func(ctx context.Context) error {
@@ -104,8 +104,8 @@
 	nodeID := eph.Nodes[0].ID()
 
 	// Actual test code starts here.
-	chans.curatorConnection <- &curatorConnection{
-		credentials: eph.Nodes[0],
+	chans.curatorConnection <- &CuratorConnection{
+		Credentials: eph.Nodes[0],
 		conn:        cl,
 	}
 	cur.expectReports(t, nil)