diff --git a/operators/constellation-node-operator/internal/controlplane/controlplane.go b/operators/constellation-node-operator/internal/controlplane/controlplane.go new file mode 100644 index 000000000..64b321dce --- /dev/null +++ b/operators/constellation-node-operator/internal/controlplane/controlplane.go @@ -0,0 +1,32 @@ +package controlplane + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + nodeutil "github.com/edgelesssys/constellation/operators/constellation-node-operator/internal/node" +) + +// ListControlPlaneIPs retrieves a list of VPC IPs for the control plane nodes from kubernetes. +func ListControlPlaneIPs(k8sClient client.Client) ([]string, error) { + var nodes corev1.NodeList + if err := k8sClient.List(context.TODO(), &nodes); err != nil { + return nil, fmt.Errorf("listing nodes: %w", err) + } + var ips []string + for _, node := range nodes.Items { + if !nodeutil.IsControlPlaneNode(&node) { + continue + } + for _, addr := range node.Status.Addresses { + if addr.Type == corev1.NodeInternalIP { + ips = append(ips, addr.Address) + } + } + } + + return ips, nil +} diff --git a/operators/constellation-node-operator/internal/controlplane/controlplane_test.go b/operators/constellation-node-operator/internal/controlplane/controlplane_test.go new file mode 100644 index 000000000..290889387 --- /dev/null +++ b/operators/constellation-node-operator/internal/controlplane/controlplane_test.go @@ -0,0 +1,77 @@ +package controlplane + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func TestListControlPlaneIPs(t *testing.T) { + testCases := map[string]struct { + nodes []corev1.Node + listErr error + wantIPs []string + wantErr bool + }{ + "listing works": { + nodes: []corev1.Node{ + { + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"node-role.kubernetes.io/control-plane": ""}, + }, + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{ + Type: corev1.NodeInternalIP, + Address: "192.0.2.1", + }}}, + }, + { + Status: corev1.NodeStatus{Addresses: []corev1.NodeAddress{{ + Type: corev1.NodeInternalIP, + Address: "192.0.2.2", + }}}, + }, + }, + wantIPs: []string{"192.0.2.1"}, + }, + "listing fails": { + listErr: errors.New("listing failed"), + wantErr: true, + }, + } + + for name, tc := range testCases { + t.Run(name, func(t *testing.T) { + assert := assert.New(t) + require := require.New(t) + + client := &stubClient{ + nodes: tc.nodes, + listErr: tc.listErr, + } + gotIPs, err := ListControlPlaneIPs(client) + if tc.wantErr { + assert.Error(err) + return + } + require.NoError(err) + assert.ElementsMatch(tc.wantIPs, gotIPs) + }) + } +} + +type stubClient struct { + nodes []corev1.Node + listErr error + client.Client +} + +func (c *stubClient) List(ctx context.Context, list client.ObjectList, opts ...client.ListOption) error { + list.(*corev1.NodeList).Items = c.nodes + return c.listErr +}