Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: zarf init state #2556

Merged
merged 2 commits into from
May 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions src/pkg/cluster/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO
if err != nil {
return err
}
if len(nodeList.Items) == 0 {
return fmt.Errorf("cannot init Zarf state in empty cluster")
}
namespaceList, err := c.Clientset.CoreV1().Namespaces().List(ctx, metav1.ListOptions{})
if err != nil {
return err
Expand All @@ -90,6 +93,10 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO
}
// Mark existing namespaces as ignored for the zarf agent to prevent mutating resources we don't own.
for _, namespace := range namespaces.Items {
// Skip Zarf namespace if it already exists.
if namespace.Name == ZarfNamespaceName {
continue
}
spinner.Updatef("Marking existing namespace %s as ignored by Zarf Agent", namespace.Name)
if namespace.Labels == nil {
// Ensure label map exists to avoid nil panic
Expand All @@ -107,8 +114,22 @@ func (c *Cluster) InitZarfState(ctx context.Context, initOptions types.ZarfInitO
// Try to create the zarf namespace.
spinner.Updatef("Creating the Zarf namespace")
zarfNamespace := NewZarfManagedNamespace(ZarfNamespaceName)
if _, err := c.CreateNamespace(ctx, zarfNamespace); err != nil {
return fmt.Errorf("unable to create the zarf namespace: %w", err)
err = func() error {
_, err := c.Clientset.CoreV1().Namespaces().Create(ctx, zarfNamespace, metav1.CreateOptions{})
if err != nil && !kerrors.IsAlreadyExists(err) {
return fmt.Errorf("unable to create the Zarf namespace: %w", err)
}
if err == nil {
return nil
}
_, err = c.Clientset.CoreV1().Namespaces().Update(ctx, zarfNamespace, metav1.UpdateOptions{})
if err != nil {
return fmt.Errorf("unable to update the Zarf namespace: %w", err)
}
return nil
}()
if err != nil {
return err
}

// Wait up to 2 minutes for the default service account to be created.
Expand Down
165 changes: 164 additions & 1 deletion src/pkg/cluster/state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,179 @@
package cluster

import (
"context"
"fmt"
"testing"
"time"

"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes/fake"

"github.com/defenseunicorns/pkg/helpers"

"github.com/defenseunicorns/zarf/src/pkg/k8s"
"github.com/defenseunicorns/zarf/src/pkg/message"
"github.com/defenseunicorns/zarf/src/pkg/pki"
"github.com/defenseunicorns/zarf/src/types"
"github.com/stretchr/testify/require"
)

func TestInitZarfState(t *testing.T) {
tests := []struct {
name string
initOpts types.ZarfInitOptions
nodes []corev1.Node
namespaces []corev1.Namespace
secrets []corev1.Secret
expectedErr string
}{
{
name: "no nodes in cluster",
expectedErr: "cannot init Zarf state in empty cluster",
AustinAbro321 marked this conversation as resolved.
Show resolved Hide resolved
},
{
name: "no namespaces exist",
initOpts: types.ZarfInitOptions{},
nodes: []corev1.Node{
{
ObjectMeta: metav1.ObjectMeta{
Name: "node",
},
},
},
},
{
name: "namespaces exists",
nodes: []corev1.Node{
{
ObjectMeta: metav1.ObjectMeta{
Name: "node",
},
},
},
namespaces: []corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: "kube-system",
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "default",
},
},
},
},
{
name: "Zarf namespace exists",
nodes: []corev1.Node{
{
ObjectMeta: metav1.ObjectMeta{
Name: "node",
},
},
},
namespaces: []corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: ZarfNamespaceName,
},
},
},
},
{
name: "Zarf state exists",
nodes: []corev1.Node{
{
ObjectMeta: metav1.ObjectMeta{
Name: "node",
},
},
},
namespaces: []corev1.Namespace{
{
ObjectMeta: metav1.ObjectMeta{
Name: ZarfNamespaceName,
},
},
},
secrets: []corev1.Secret{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: ZarfNamespaceName,
Name: ZarfStateSecretName,
},
},
},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
cs := fake.NewSimpleClientset()
for _, node := range tt.nodes {
_, err := cs.CoreV1().Nodes().Create(ctx, &node, metav1.CreateOptions{})
require.NoError(t, err)
}
for _, namespace := range tt.namespaces {
_, err := cs.CoreV1().Namespaces().Create(ctx, &namespace, metav1.CreateOptions{})
require.NoError(t, err)
}
for _, secret := range tt.secrets {
_, err := cs.CoreV1().Secrets(secret.ObjectMeta.Namespace).Create(ctx, &secret, metav1.CreateOptions{})
require.NoError(t, err)
}
c := &Cluster{
&k8s.K8s{
Clientset: cs,
Log: func(string, ...any) {},
},
}

// Create default service account in Zarf namespace
go func() {
for {
time.Sleep(1 * time.Second)
ns, err := cs.CoreV1().Namespaces().Get(ctx, ZarfNamespaceName, metav1.GetOptions{})
if err != nil {
continue
}
sa := &corev1.ServiceAccount{
ObjectMeta: metav1.ObjectMeta{
Namespace: ns.Name,
Name: "default",
},
}
cs.CoreV1().ServiceAccounts(ns.Name).Create(ctx, sa, metav1.CreateOptions{})
lucasrod16 marked this conversation as resolved.
Show resolved Hide resolved
break
}
}()

err := c.InitZarfState(ctx, tt.initOpts)
if tt.expectedErr != "" {
require.EqualError(t, err, tt.expectedErr)
return
}
require.NoError(t, err)
zarfNs, err := cs.CoreV1().Namespaces().Get(ctx, ZarfNamespaceName, metav1.GetOptions{})
require.NoError(t, err)
require.Equal(t, map[string]string{"app.kubernetes.io/managed-by": "zarf"}, zarfNs.Labels)
_, err = cs.CoreV1().Secrets(zarfNs.Name).Get(ctx, ZarfStateSecretName, metav1.GetOptions{})
require.NoError(t, err)
for _, ns := range tt.namespaces {
if ns.Name == zarfNs.Name {
continue
}
ns, err := cs.CoreV1().Namespaces().Get(ctx, ns.Name, metav1.GetOptions{})
require.NoError(t, err)
require.Equal(t, map[string]string{k8s.AgentLabel: "ignore"}, ns.Labels)
}
})
}
}

// TODO: Change password gen method to make testing possible.
func TestMergeZarfStateRegistry(t *testing.T) {
t.Parallel()
Expand Down
Loading