/* Copyright (c) Edgeless Systems GmbH SPDX-License-Identifier: AGPL-3.0-only */ package node import ( "testing" updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/v2/api/v1alpha1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) func TestReady(t *testing.T) { testCases := map[string]struct { node corev1.Node wantReady bool }{ "node without status conditions": {}, "node with NodeReady set to false": { node: corev1.Node{ Status: corev1.NodeStatus{ Conditions: []corev1.NodeCondition{ {Type: corev1.NodeReady, Status: corev1.ConditionFalse}, }, }, }, }, "node with NodeReady set to unknown": { node: corev1.Node{ Status: corev1.NodeStatus{ Conditions: []corev1.NodeCondition{ {Type: corev1.NodeReady, Status: corev1.ConditionUnknown}, }, }, }, }, "node with NodeReady set to true": { node: corev1.Node{ Status: corev1.NodeStatus{ Conditions: []corev1.NodeCondition{ {Type: corev1.NodeReady, Status: corev1.ConditionTrue}, }, }, }, wantReady: true, }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) assert.Equal(tc.wantReady, Ready(&tc.node)) }) } } 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 node *corev1.Node wantPending *updatev1alpha1.PendingNode }{ "everything nil": {}, "node nil": { pendingNodes: pendingNodes, }, "node is not in pending nodes list": { pendingNodes: pendingNodes, node: &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "doesnotexist", }, }, }, "pending node is leaving": { pendingNodes: pendingNodes, node: &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "leavingnode", }, }, }, "pending node is not ready": { pendingNodes: pendingNodes, node: &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "unreadynode", }, }, }, "pending node is found": { pendingNodes: pendingNodes, node: &corev1.Node{ ObjectMeta: metav1.ObjectMeta{ Name: "joiningnode", }, }, wantPending: &pendingNodes[0], }, } for name, tc := range testCases { t.Run(name, func(t *testing.T) { assert := assert.New(t) pending := FindPending(tc.pendingNodes, tc.node) if tc.wantPending == nil { assert.Nil(pending) return } assert.Equal(*tc.wantPending, *pending) }) } } func TestFilterLabels(t *testing.T) { labels := map[string]string{ "key": "value", "app.kubernetes.io/component": "component", "app.kubernetes.io/created-by": "created-by", "app.kubernetes.io/instance": "instance", "app.kubernetes.io/managed-by": "managed-by", "app.kubernetes.io/name": "name", "app.kubernetes.io/part-of": "part-of", "app.kubernetes.io/version": "version", "kubernetes.io/arch": "arch", "kubernetes.io/os": "os", "beta.kubernetes.io/arch": "arch", "beta.kubernetes.io/os": "os", "kubernetes.io/hostname": "hostname", "kubernetes.io/change-cause": "change-cause", "kubernetes.io/description": "description", "node.kubernetes.io/instance-type": "instance-type", "failure-domain.beta.kubernetes.io/region": "region", "failure-domain.beta.kubernetes.io/zone": "zone", "topology.kubernetes.io/region": "region", "topology.kubernetes.io/zone": "zone", "node.kubernetes.io/windows-build": "windows-build", "node-role.kubernetes.io/control-plane": "control-plane", } wantFiltered := map[string]string{ "key": "value", } assert := assert.New(t) assert.Equal(wantFiltered, FilterLabels(labels)) } var pendingNodes = []updatev1alpha1.PendingNode{ { Spec: updatev1alpha1.PendingNodeSpec{ NodeName: "joiningnode", Goal: updatev1alpha1.NodeGoalJoin, }, Status: updatev1alpha1.PendingNodeStatus{ CSPNodeState: updatev1alpha1.NodeStateReady, }, }, { Spec: updatev1alpha1.PendingNodeSpec{ NodeName: "unreadynode", Goal: updatev1alpha1.NodeGoalJoin, }, Status: updatev1alpha1.PendingNodeStatus{ CSPNodeState: updatev1alpha1.NodeStateCreating, }, }, { Spec: updatev1alpha1.PendingNodeSpec{ NodeName: "leavingnode", Goal: updatev1alpha1.NodeGoalLeave, }, }, }