package encryptionctl_test import ( "context" "os" "testing" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "github.com/stretchr/testify/suite" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" api "edge-infra.dev/pkg/edge/iam/api/v1alpha1" "edge-infra.dev/pkg/lib/fog" "edge-infra.dev/pkg/edge/iam/ctl/encryptionctl" "edge-infra.dev/pkg/edge/iam/ctl/providerctl" "edge-infra.dev/pkg/k8s/runtime/controller" "edge-infra.dev/test" "edge-infra.dev/test/framework" "edge-infra.dev/test/framework/k8s" "edge-infra.dev/test/framework/k8s/envtest" ) // test predicate when secret added // test updateStatus // we already test the updating of the databases in // pkg/edge/iam/crypto/encryption_key_rotation_test.go // so this just tests that the created secret triggers a reconciliation loop // bazel test pkg/edge/iam/ctl/encryptionctl:encryptionctl_test type Suite struct { *framework.Framework *k8s.K8s ctx context.Context timeout time.Duration tick time.Duration } func TestEncryptionReconcilerSetup(t *testing.T) { // set env variables os.Setenv("IAM_CLUSTER_ID", "123") os.Setenv("IAM_ENCRYPTION_ENABLED", "true") // setup testEnv := envtest.Setup() cfg, opts := controller.ProcessOptions(controller.WithCfg(testEnv.Config), controller.WithMetricsAddress("0")) opts.Scheme = createScheme() ctrl.SetLogger(fog.New()) mgr, err := ctrl.NewManager(cfg, opts) if err != nil { t.Errorf("unable to create manager: %v", err) } f := framework.New("providerctl").Component("providerctl") s := &Suite{ Framework: f, ctx: context.Background(), timeout: 10 * time.Second, tick: 50 * time.Millisecond, } // create provider reconciler providerRec := &providerctl.ProviderReconciler{ Name: "provider-controller", Client: mgr.GetClient(), Scheme: mgr.GetScheme(), } err = providerRec.SetupWithManager(mgr) test.NoError(err) // create encryption reconciler encryptionRec := &encryptionctl.EncryptionSecretReconciler{ Name: "encryption-secret-controller", Client: mgr.GetClient(), } err = encryptionRec.SetupWithManager(mgr) test.NoError(err) k := k8s.New(testEnv.Config, k8s.WithCtrlManager(mgr), k8s.WithKonfigKonnector()) s.K8s = k f.Register(k) suite.Run(t, s) t.Cleanup(func() { f.NoError(testEnv.Stop()) }) } func (s *Suite) TestEncryptionSecretReconciler() { // create edge-iam ns namespace := &corev1.Namespace{ TypeMeta: metav1.TypeMeta{ Kind: "Namespace", APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ Name: "edge-iam", }, } s.Require().NoError(s.Client.Create(s.ctx, namespace)) // create private-key-secret secret := &corev1.Secret{} secret.ObjectMeta = metav1.ObjectMeta{ Name: "private-key-secret", Namespace: "edge-iam", } secret.Data = map[string][]byte{ "private_key": []byte("key"), "private_key_id": []byte("key-id"), } s.Require().NoError(s.Client.Create(s.ctx, secret)) // create challenge-secret challengeSecret := &corev1.Secret{} challengeSecret.ObjectMeta = metav1.ObjectMeta{ Name: "challenge-secret", Namespace: "edge-iam", } challengeSecret.Data = map[string][]byte{ "secret": []byte("secret"), } s.Require().NoError(s.Client.Create(s.ctx, challengeSecret)) // create initial provider obj w/ v1 provider := createProviderObj("1", "1") s.Require().NoError(s.Client.Create(s.ctx, provider)) // then create secret that would be created by External Secrets encryptionSecret := getSecret() s.Require().NoError(s.Client.Create(s.ctx, encryptionSecret.DeepCopy())) // get provider to see if status is updated providerObj := &api.Provider{} s.Require().Eventually(func() bool { err := s.Client.Get(s.ctx, types.NamespacedName{ Name: "provider", Namespace: "edge-iam", }, providerObj) return err == nil }, s.timeout, s.tick, "expected provider object was never found") // confirm status has the correct message for _, status := range providerObj.Status.Conditions { if status.Reason == "EncryptionRotationSucceeded" { s.Require().Equal(status.Message, "successfully updated databases to version: 1") } } } func getSecret() corev1.Secret { secret := corev1.Secret{ TypeMeta: metav1.TypeMeta{ Kind: "Secret", APIVersion: "v1", }, ObjectMeta: metav1.ObjectMeta{ Name: "id-encryption-key-1", Namespace: "edge-iam", }, Data: map[string][]byte{ "key": []byte("my-32bit-super-extra-secret-key!"), }, } return secret } func createProviderObj(specVersion string, statusVersion string) *api.Provider { statusMessage := "successfully updated databases to version: " + statusVersion providerObj := &api.Provider{ TypeMeta: metav1.TypeMeta{ APIVersion: "iam.edge-infra.dev/v1alpha1", Kind: "Provider", }, ObjectMeta: metav1.ObjectMeta{ Name: "provider", Namespace: "edge-iam", }, Spec: api.ProviderSpec{ Encryption: api.EncryptionFields{ Version: specVersion, }, Issuer: "http://localhost:8080", Target: "kind", }, Status: api.ProviderStatus{ Conditions: []metav1.Condition{ { Type: "DatabaseUpdated", Status: metav1.ConditionTrue, Reason: "EncryptionRotationSucceeded", Message: statusMessage, }, }, }, } return providerObj } func createScheme() *runtime.Scheme { scheme := runtime.NewScheme() utilruntime.Must(api.AddToScheme(scheme)) utilruntime.Must(corev1.AddToScheme(scheme)) return scheme }