ContainerImage.Pinniped/internal/controller/secrets/secrets_test.go

297 lines
9.3 KiB
Go
Raw Permalink Normal View History

// Copyright 2023 the Pinniped contributors. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
package secrets
import (
"bytes"
"context"
"errors"
"fmt"
"testing"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
kubeinformers "k8s.io/client-go/informers"
kubernetesfake "k8s.io/client-go/kubernetes/fake"
kubetesting "k8s.io/client-go/testing"
"go.pinniped.dev/internal/controllerlib"
"go.pinniped.dev/internal/plog"
"go.pinniped.dev/internal/testutil"
)
func TestNewServiceAccountTokenCleanupController(t *testing.T) {
namespace := "a-namespace"
legacySecretName := "a-secret"
observableWithInformerOption := testutil.NewObservableWithInformerOption()
secretsInformer := kubeinformers.NewSharedInformerFactory(nil, 0).Core().V1().Secrets()
var log bytes.Buffer
_ = NewServiceAccountTokenCleanupController(
namespace,
legacySecretName,
nil, // not needed for this test
secretsInformer,
observableWithInformerOption.WithInformer,
plog.TestLogger(t, &log),
)
secretsInformerFilter := observableWithInformerOption.GetFilterForInformer(secretsInformer)
legacySecret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: legacySecretName, Namespace: namespace}, Type: corev1.SecretTypeServiceAccountToken}
wrongName := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "wrongName", Namespace: namespace}, Type: corev1.SecretTypeServiceAccountToken}
wrongType := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "wrongType", Namespace: namespace}, Type: "other-type"}
wrongNamespace := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "wrongNamespace", Namespace: "wrong-namespace"}, Type: corev1.SecretTypeServiceAccountToken}
wrongObject := &corev1.ConfigMap{ObjectMeta: metav1.ObjectMeta{Name: "config-map", Namespace: namespace}}
require.False(t, secretsInformerFilter.Add(wrongName))
require.False(t, secretsInformerFilter.Update(wrongName, wrongNamespace))
require.False(t, secretsInformerFilter.Update(wrongNamespace, wrongName))
require.False(t, secretsInformerFilter.Delete(wrongName))
require.False(t, secretsInformerFilter.Add(wrongObject))
require.False(t, secretsInformerFilter.Update(wrongObject, wrongNamespace))
require.False(t, secretsInformerFilter.Update(wrongNamespace, wrongObject))
require.False(t, secretsInformerFilter.Delete(wrongObject))
require.False(t, secretsInformerFilter.Add(wrongNamespace))
require.False(t, secretsInformerFilter.Update(wrongNamespace, wrongObject))
require.False(t, secretsInformerFilter.Update(wrongObject, wrongNamespace))
require.False(t, secretsInformerFilter.Delete(wrongNamespace))
require.False(t, secretsInformerFilter.Add(wrongType))
require.False(t, secretsInformerFilter.Update(wrongType, wrongNamespace))
require.False(t, secretsInformerFilter.Update(wrongNamespace, wrongType))
require.False(t, secretsInformerFilter.Delete(wrongType))
require.True(t, secretsInformerFilter.Add(legacySecret))
require.True(t, secretsInformerFilter.Update(legacySecret, wrongNamespace))
require.True(t, secretsInformerFilter.Update(wrongNamespace, legacySecret))
require.True(t, secretsInformerFilter.Delete(legacySecret))
}
func TestSync(t *testing.T) {
kubeAPIClient := kubernetesfake.NewSimpleClientset()
kubeInformerClient := kubernetesfake.NewSimpleClientset()
kubeInformers := kubeinformers.NewSharedInformerFactory(
kubeInformerClient,
0,
)
namespace := "some-namespace"
secretToDelete := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-to-delete",
Namespace: namespace,
},
Type: corev1.SecretTypeServiceAccountToken,
}
secretWithWrongName := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "wrong-name",
Namespace: namespace,
},
Type: corev1.SecretTypeServiceAccountToken,
}
secretWithWrongType := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-to-leave-alone",
Namespace: namespace,
},
}
secretWithWrongNamespace := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-to-delete",
Namespace: "other",
},
Type: corev1.SecretTypeServiceAccountToken,
}
require.NoError(t, kubeAPIClient.Tracker().Add(secretToDelete))
require.NoError(t, kubeInformerClient.Tracker().Add(secretToDelete))
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongName))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongName))
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongType))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongType))
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongNamespace))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongNamespace))
var log bytes.Buffer
controller := NewServiceAccountTokenCleanupController(
namespace,
"secret-to-delete",
kubeAPIClient,
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
plog.TestLogger(t, &log),
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Must start informers before calling TestRunSynchronously().
kubeInformers.Start(ctx.Done())
controllerlib.TestRunSynchronously(t, controller)
err := controllerlib.TestSync(t, controller, controllerlib.Context{
Context: ctx,
})
require.NoError(t, err)
t.Log(log.String())
expectedActions := []kubetesting.Action{kubetesting.NewDeleteAction(
schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "secrets",
},
namespace,
"secret-to-delete",
)}
actualActions := kubeAPIClient.Actions()
require.Equal(t, expectedActions, actualActions)
}
func TestSync_NoSecretToDelete(t *testing.T) {
kubeAPIClient := kubernetesfake.NewSimpleClientset()
kubeInformerClient := kubernetesfake.NewSimpleClientset()
kubeInformers := kubeinformers.NewSharedInformerFactory(
kubeInformerClient,
0,
)
namespace := "some-namespace"
secretWithWrongName := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "wrong-name",
Namespace: namespace,
},
Type: corev1.SecretTypeServiceAccountToken,
}
secretWithWrongType := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-to-leave-alone",
Namespace: namespace,
},
}
secretWithWrongNamespace := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "secret-to-delete",
Namespace: "other",
},
Type: corev1.SecretTypeServiceAccountToken,
}
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongName))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongName))
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongType))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongType))
require.NoError(t, kubeAPIClient.Tracker().Add(secretWithWrongNamespace))
require.NoError(t, kubeInformerClient.Tracker().Add(secretWithWrongNamespace))
var log bytes.Buffer
controller := NewServiceAccountTokenCleanupController(
namespace,
"secret-to-delete",
kubeAPIClient,
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
plog.TestLogger(t, &log),
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Must start informers before calling TestRunSynchronously().
kubeInformers.Start(ctx.Done())
controllerlib.TestRunSynchronously(t, controller)
err := controllerlib.TestSync(t, controller, controllerlib.Context{
Context: ctx,
})
require.NoError(t, err)
t.Log(log.String())
actualActions := kubeAPIClient.Actions()
require.Empty(t, actualActions)
}
func TestSync_ReturnsAPIErrors(t *testing.T) {
kubeAPIClient := kubernetesfake.NewSimpleClientset()
kubeInformerClient := kubernetesfake.NewSimpleClientset()
kubeInformers := kubeinformers.NewSharedInformerFactory(
kubeInformerClient,
0,
)
errorMessage := "error from API client"
kubeAPIClient.PrependReactor(
"delete",
"secrets",
func(a kubetesting.Action) (bool, runtime.Object, error) {
return true, nil, errors.New(errorMessage)
},
)
namespace := "some-namespace"
var log bytes.Buffer
legacySecretName := "secret-to-delete"
secretToDelete := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: legacySecretName,
Namespace: namespace,
},
Type: corev1.SecretTypeServiceAccountToken,
}
require.NoError(t, kubeAPIClient.Tracker().Add(secretToDelete))
require.NoError(t, kubeInformerClient.Tracker().Add(secretToDelete))
controller := NewServiceAccountTokenCleanupController(
namespace,
legacySecretName,
kubeAPIClient,
kubeInformers.Core().V1().Secrets(),
controllerlib.WithInformer,
plog.TestLogger(t, &log),
)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Must start informers before calling TestRunSynchronously().
kubeInformers.Start(ctx.Done())
controllerlib.TestRunSynchronously(t, controller)
err := controllerlib.TestSync(t, controller, controllerlib.Context{
Context: ctx,
})
require.ErrorContains(t, err, fmt.Sprintf("unable to delete secret %s in namespace %s: %s", legacySecretName, namespace, errorMessage))
t.Log(log.String())
expectedActions := []kubetesting.Action{kubetesting.NewDeleteAction(
schema.GroupVersionResource{
Group: "",
Version: "v1",
Resource: "secrets",
},
namespace,
legacySecretName,
)}
actualActions := kubeAPIClient.Actions()
require.Equal(t, expectedActions, actualActions)
}