[node operator] helpers: find node vpc IP and check if node is control-plane node

Signed-off-by: Malte Poll <mp@edgeless.systems>
This commit is contained in:
Malte Poll 2022-07-18 16:37:59 +02:00 committed by Malte Poll
parent de9fa37f63
commit bef2bcc4a9
2 changed files with 87 additions and 5 deletions

View File

@ -1,12 +1,15 @@
package node package node
import ( import (
"errors"
"regexp" "regexp"
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1" updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
) )
const controlPlaneRoleLabel = "node-role.kubernetes.io/control-plane"
var reservedHostRegex = regexp.MustCompile(`^(.+\.|)(kubernetes|k8s)\.io(/.*)?$`) var reservedHostRegex = regexp.MustCompile(`^(.+\.|)(kubernetes|k8s)\.io(/.*)?$`)
// Ready checks if a kubernetes node has the `NodeReady` condition set to true. // 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 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. // 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. // 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. // if the node is not found, nil is returned.

View File

@ -5,8 +5,9 @@ import (
updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1" updatev1alpha1 "github.com/edgelesssys/constellation/operators/constellation-node-operator/api/v1alpha1"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1" 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) { 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) { func TestFindPending(t *testing.T) {
testCases := map[string]struct { testCases := map[string]struct {
pendingNodes []updatev1alpha1.PendingNode pendingNodes []updatev1alpha1.PendingNode
@ -66,7 +129,7 @@ func TestFindPending(t *testing.T) {
"node is not in pending nodes list": { "node is not in pending nodes list": {
pendingNodes: pendingNodes, pendingNodes: pendingNodes,
node: &corev1.Node{ node: &corev1.Node{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "doesnotexist", Name: "doesnotexist",
}, },
}, },
@ -74,7 +137,7 @@ func TestFindPending(t *testing.T) {
"pending node is leaving": { "pending node is leaving": {
pendingNodes: pendingNodes, pendingNodes: pendingNodes,
node: &corev1.Node{ node: &corev1.Node{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "leavingnode", Name: "leavingnode",
}, },
}, },
@ -82,7 +145,7 @@ func TestFindPending(t *testing.T) {
"pending node is not ready": { "pending node is not ready": {
pendingNodes: pendingNodes, pendingNodes: pendingNodes,
node: &corev1.Node{ node: &corev1.Node{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "unreadynode", Name: "unreadynode",
}, },
}, },
@ -90,7 +153,7 @@ func TestFindPending(t *testing.T) {
"pending node is found": { "pending node is found": {
pendingNodes: pendingNodes, pendingNodes: pendingNodes,
node: &corev1.Node{ node: &corev1.Node{
ObjectMeta: v1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: "joiningnode", Name: "joiningnode",
}, },
}, },