From bef2bcc4a98964fe8c3f81f1b51a0bb56c2fbf90 Mon Sep 17 00:00:00 2001 From: Malte Poll Date: Mon, 18 Jul 2022 16:37:59 +0200 Subject: [PATCH] [node operator] helpers: find node vpc IP and check if node is control-plane node Signed-off-by: Malte Poll --- .../internal/node/node.go | 19 +++++ .../internal/node/node_test.go | 73 +++++++++++++++++-- 2 files changed, 87 insertions(+), 5 deletions(-) diff --git a/operators/constellation-node-operator/internal/node/node.go b/operators/constellation-node-operator/internal/node/node.go index 138dd74f6..182ae4441 100644 --- a/operators/constellation-node-operator/internal/node/node.go +++ b/operators/constellation-node-operator/internal/node/node.go @@ -1,12 +1,15 @@ package node import ( + "errors" "regexp" updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1" corev1 "k8s.io/api/core/v1" ) +const controlPlaneRoleLabel = "node-role.kubernetes.io/control-plane" + var reservedHostRegex = regexp.MustCompile(`^(.+\.|)(kubernetes|k8s)\.io(/.*)?$`) // Ready checks if a kubernetes node has the `NodeReady` condition set to true. @@ -19,6 +22,22 @@ func Ready(node *corev1.Node) bool { return false } +// VPCIP returns the VPC IP of a node. +func VPCIP(node *corev1.Node) (string, error) { + for _, addr := range node.Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + return addr.Address, nil + } + } + return "", errors.New("no VPC IP found") +} + +// IsControlPlaneNode returns true if the node is a control plane node. +func IsControlPlaneNode(node *corev1.Node) bool { + _, ok := node.Labels[controlPlaneRoleLabel] + return ok +} + // FindPending searches for a pending node that matches a node. // The pending node has to have the goal to join the cluster and be reported as ready be the CSP. // if the node is not found, nil is returned. diff --git a/operators/constellation-node-operator/internal/node/node_test.go b/operators/constellation-node-operator/internal/node/node_test.go index b3619f887..291cd1fdf 100644 --- a/operators/constellation-node-operator/internal/node/node_test.go +++ b/operators/constellation-node-operator/internal/node/node_test.go @@ -5,8 +5,9 @@ import ( updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestReady(t *testing.T) { @@ -53,6 +54,68 @@ func TestReady(t *testing.T) { } } +func TestIsControlPlaneNode(t *testing.T) { + testCases := map[string]struct { + node corev1.Node + wantControlPlane bool + }{ + "node without labels": {}, + "node with control-plane role label": { + node: corev1.Node{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{controlPlaneRoleLabel: ""}, + }, + }, + wantControlPlane: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + assert.Equal(tc.wantControlPlane, IsControlPlaneNode(&tc.node)) + }) + } +} + +func TestVPCIP(t *testing.T) { + testCases := map[string]struct { + node corev1.Node + wantVPCIP string + wantErr bool + }{ + "node without addresses": { + wantErr: true, + }, + "node with multiple addresses": { + node: corev1.Node{ + Status: corev1.NodeStatus{ + Addresses: []corev1.NodeAddress{ + {Type: corev1.NodeExternalIP, Address: "external"}, + {Type: corev1.NodeInternalIP, Address: "192.0.2.1"}, + {Type: corev1.NodeInternalIP, Address: "second-internal"}, + }, + }, + }, + wantVPCIP: "192.0.2.1", + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + gotIP, err := VPCIP(&tc.node) + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.Equal(tc.wantVPCIP, gotIP) + }) + } +} + func TestFindPending(t *testing.T) { testCases := map[string]struct { pendingNodes []updatev1alpha1.PendingNode @@ -66,7 +129,7 @@ func TestFindPending(t *testing.T) { "node is not in pending nodes list": { pendingNodes: pendingNodes, node: &corev1.Node{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "doesnotexist", }, }, @@ -74,7 +137,7 @@ func TestFindPending(t *testing.T) { "pending node is leaving": { pendingNodes: pendingNodes, node: &corev1.Node{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "leavingnode", }, }, @@ -82,7 +145,7 @@ func TestFindPending(t *testing.T) { "pending node is not ready": { pendingNodes: pendingNodes, node: &corev1.Node{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "unreadynode", }, }, @@ -90,7 +153,7 @@ func TestFindPending(t *testing.T) { "pending node is found": { pendingNodes: pendingNodes, node: &corev1.Node{ - ObjectMeta: v1.ObjectMeta{ + ObjectMeta: metav1.ObjectMeta{ Name: "joiningnode", }, },