metropolis/node/kubernetes: synchronize metropolis node labels to kubernetes

This extends the labelmaker to manage Kubernetes node labels mirrored
from Metropolis node labels.

Note that currently there is no way to edit a ClusterConfiguration at
cluster runtime, but this will come in a future CL.

Change-Id: If7dbc3796085a8b85c1b5b2a181bcb1cee3d1db4
Reviewed-on: https://review.monogon.dev/c/monogon/+/3469
Reviewed-by: Jan Schär <jan@monogon.tech>
Tested-by: Jenkins CI
diff --git a/metropolis/test/e2e/suites/kubernetes/run_test.go b/metropolis/test/e2e/suites/kubernetes/run_test.go
index f68f0a4..cf33126 100644
--- a/metropolis/test/e2e/suites/kubernetes/run_test.go
+++ b/metropolis/test/e2e/suites/kubernetes/run_test.go
@@ -73,6 +73,11 @@
 		InitialClusterConfiguration: &cpb.ClusterConfiguration{
 			TpmMode:               cpb.ClusterConfiguration_TPM_MODE_DISABLED,
 			StorageSecurityPolicy: cpb.ClusterConfiguration_STORAGE_SECURITY_POLICY_NEEDS_INSECURE,
+			KubernetesConfig: &cpb.ClusterConfiguration_KubernetesConfig{
+				NodeLabelsToSynchronize: []*cpb.ClusterConfiguration_KubernetesConfig_NodeLabelsToSynchronize{
+					{Regexp: `^test\.monogon\.dev/`},
+				},
+			},
 		},
 	}
 	cluster, err := mlaunch.LaunchCluster(ctx, clusterOptions)
@@ -106,7 +111,13 @@
 			return nil
 		}
 		return common.Labels(node.Labels).Filter(func(k, v string) bool {
-			return strings.HasPrefix(k, "node-role.kubernetes.io/")
+			if strings.HasPrefix(k, "node-role.kubernetes.io/") {
+				return true
+			}
+			if strings.HasPrefix(k, "test.monogon.dev/") {
+				return true
+			}
+			return false
 		})
 	}
 
@@ -175,6 +186,30 @@
 		}
 		return nil
 	})
+
+	// Add Metropolis node label, ensure it gets reflected on the Kubernetes node.
+	_, err = mgmt.UpdateNodeLabels(ctx, &apb.UpdateNodeLabelsRequest{
+		Node: &apb.UpdateNodeLabelsRequest_Id{
+			Id: cluster.NodeIDs[1],
+		},
+		Upsert: []*apb.UpdateNodeLabelsRequest_Pair{
+			{Key: "test.monogon.dev/foo", Value: "bar"},
+		},
+	})
+
+	util.MustTestEventual(t, "Metropolis labels added", ctx, time.Second*5, func(ctx context.Context) error {
+		if err != nil {
+			t.Fatalf("Could not add label to node: %v", err)
+		}
+		want := common.Labels{
+			"node-role.kubernetes.io/KubernetesWorker": "",
+			"test.monogon.dev/foo":                     "bar",
+		}
+		if labels := getLabelsForNode(cluster.NodeIDs[1]); !want.Equals(labels) {
+			return fmt.Errorf("Node %s should have labels %s, has %s", cluster.NodeIDs[1], want, labels)
+		}
+		return nil
+	})
 }
 
 // TestE2EKubernetes exercises the Kubernetes functionality of Metropolis.