github.com/verrazzano/verrazzano@v1.7.0/platform-operator/controllers/secrets/secrets_controller_test.go (about)

     1  // Copyright (c) 2022, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package secrets
     5  
     6  import (
     7  	"context"
     8  	"crypto/x509"
     9  	"fmt"
    10  	"testing"
    11  	"time"
    12  
    13  	cmutil "github.com/cert-manager/cert-manager/pkg/api/util"
    14  	certv1 "github.com/cert-manager/cert-manager/pkg/apis/certmanager/v1"
    15  	cmmeta "github.com/cert-manager/cert-manager/pkg/apis/meta/v1"
    16  	certv1fake "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/fake"
    17  	certv1client "github.com/cert-manager/cert-manager/pkg/client/clientset/versioned/typed/certmanager/v1"
    18  	"github.com/golang/mock/gomock"
    19  	"github.com/stretchr/testify/assert"
    20  	constants2 "github.com/verrazzano/verrazzano/pkg/constants"
    21  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    22  	vzapi "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1alpha1"
    23  	"github.com/verrazzano/verrazzano/platform-operator/constants"
    24  	cmcommonfake "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/certmanager/common/fake"
    25  	"github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/certmanager/issuer"
    26  	vzstatus "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/healthcheck"
    27  	"github.com/verrazzano/verrazzano/platform-operator/internal/config"
    28  	"github.com/verrazzano/verrazzano/platform-operator/mocks"
    29  	appsv1 "k8s.io/api/apps/v1"
    30  	corev1 "k8s.io/api/core/v1"
    31  	"k8s.io/apimachinery/pkg/api/errors"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/runtime"
    34  	"k8s.io/apimachinery/pkg/types"
    35  	ctrl "sigs.k8s.io/controller-runtime"
    36  	"sigs.k8s.io/controller-runtime/pkg/client"
    37  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    38  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    39  )
    40  
    41  var vzTLSSecret = types.NamespacedName{Name: constants.VerrazzanoIngressSecret, Namespace: constants.VerrazzanoSystemNamespace}
    42  var vzPrivateCABundleSecret = types.NamespacedName{Name: constants2.PrivateCABundle, Namespace: constants.VerrazzanoSystemNamespace}
    43  var additionalTLSSecret = types.NamespacedName{Name: "tls-ca-additional", Namespace: constants2.RancherSystemNamespace}
    44  var unwatchedSecret = types.NamespacedName{Name: "any-secret", Namespace: "any-namespace"}
    45  
    46  // TestReconcileConfiguredCASecret tests the Reconcile method
    47  // GIVEN a request to reconcile the secret configured in ClusterIssuer
    48  // WHEN the secret has changed
    49  // THEN verify all certificates managed by ClusterIssuer are rotated
    50  // THEN verify the verrazzano-system/verrazzano-tls-ca secret is updated with the changes
    51  // THEN verify the cattle-system/tls-ca secret is updated with the changes
    52  // THEN verify the verrazzano-mc/verrazzano-local-ca-bundle secret is updated with the changes
    53  func TestReconcileConfiguredCASecret(t *testing.T) {
    54  	const caCertCommonName = "verrazzano-root-ca"
    55  	asserts := assert.New(t)
    56  	config.TestProfilesDir = "../../manifests/profiles"
    57  	defer func() { config.TestProfilesDir = "" }()
    58  	scheme := newScheme()
    59  	vz := newVZ()
    60  
    61  	// Create a CA certificate
    62  	commonName := caCertCommonName + "-a23asdfa"
    63  	caIssuerCert := cmcommonfake.CreateFakeCertificate(commonName)
    64  	caSecret, caCert, err := newCertificateWithSecret("verrazzano-selfsigned-issuer", commonName, "verrazzano-ca-certificate", constants2.CertManagerNamespace, nil)
    65  	asserts.NoError(err)
    66  
    67  	// Create a leaf certificate signed by the CA
    68  	leaf1Secret, leaf1Cert, err := newCertificateWithSecret("verrazzano-cluster-issuer", "common-name", "tls-rancher-ingress", constants2.RancherSystemNamespace, caIssuerCert)
    69  	assert.NoError(t, err)
    70  	leaf1Secret.Data[constants2.CACertKey] = caSecret.Data[corev1.TLSCertKey]
    71  
    72  	// Create the verrazzano-tls-ca secret
    73  	v8oTLSCASecret := newCertSecret(constants2.PrivateCABundle, constants2.VerrazzanoSystemNamespace, constants2.CABundleKey, caSecret.Data[corev1.TLSCertKey])
    74  
    75  	// Create the Rancher tls-ca secret
    76  	cattleTLSSecret := newCertSecret(constants2.RancherTLSCA, constants2.RancherSystemNamespace, constants2.RancherTLSCAKey, caSecret.Data[corev1.TLSCertKey])
    77  
    78  	// Create the Rancher deployment
    79  	cattleDeployment := &appsv1.Deployment{
    80  		ObjectMeta: metav1.ObjectMeta{
    81  			Namespace: constants2.RancherSystemNamespace,
    82  			Name:      rancherDeploymentName,
    83  		},
    84  	}
    85  
    86  	// Create the multi-cluster namespace
    87  	multiClusterNamespace := &corev1.Namespace{
    88  		ObjectMeta: metav1.ObjectMeta{
    89  			Name: constants2.VerrazzanoMultiClusterNamespace,
    90  		},
    91  	}
    92  
    93  	// Create the multi-cluster verrazzano-local-ca-bundle secret
    94  	mcSecret := newCertSecret(constants.VerrazzanoLocalCABundleSecret, constants.VerrazzanoMultiClusterNamespace, mcCABundleKey, caSecret.Data[corev1.TLSCertKey])
    95  
    96  	// Simulate rotate of the CA cert
    97  	fakeIssuerCertBytes, err := cmcommonfake.CreateFakeCertBytes(commonName+"foo", nil)
    98  	assert.NoError(t, err)
    99  	caSecret.Data[corev1.TLSCertKey] = fakeIssuerCertBytes
   100  
   101  	// Fake ControllerRuntime client
   102  	fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(vz, caSecret, caCert, leaf1Secret, leaf1Cert,
   103  		v8oTLSCASecret, cattleTLSSecret, cattleDeployment, multiClusterNamespace, mcSecret).Build()
   104  	r := newSecretsReconciler(fakeClient)
   105  
   106  	// Fake Go client for the CertManager clientSet
   107  	cmClient := certv1fake.NewSimpleClientset(caCert, leaf1Cert)
   108  	defer issuer.ResetCMClientFunc()
   109  	issuer.SetCMClientFunc(func() (certv1client.CertmanagerV1Interface, error) {
   110  		return cmClient.CertmanagerV1(), nil
   111  	})
   112  
   113  	// First reconcile the change to the ClusterIssuer secret
   114  	request := newRequest(caSecret.Namespace, caSecret.Name)
   115  	result, err := r.Reconcile(context.TODO(), request)
   116  	asserts.NoError(err)
   117  	asserts.NotNil(result)
   118  
   119  	// Next reconcile the change to the verrazzano-tls-ca secret
   120  	request = newRequest(v8oTLSCASecret.Namespace, v8oTLSCASecret.Name)
   121  	result, err = r.Reconcile(context.TODO(), request)
   122  	asserts.NoError(err)
   123  	asserts.NotNil(result)
   124  
   125  	// Confirm the expected certificates were marked to be rotated
   126  	updatedCert, err := cmClient.CertmanagerV1().Certificates(leaf1Cert.Namespace).Get(context.TODO(), leaf1Cert.Name, metav1.GetOptions{})
   127  	asserts.NoError(err)
   128  	asserts.True(cmutil.CertificateHasCondition(updatedCert, certv1.CertificateCondition{
   129  		Type:   certv1.CertificateConditionIssuing,
   130  		Status: cmmeta.ConditionTrue,
   131  	}))
   132  
   133  	// Confirm the verrazzano-tls-ca secret got updated
   134  	secret := &corev1.Secret{}
   135  	err = fakeClient.Get(context.TODO(), types.NamespacedName{Namespace: v8oTLSCASecret.Namespace, Name: v8oTLSCASecret.Name}, secret)
   136  	asserts.NoError(err)
   137  	asserts.Equal(caSecret.Data[corev1.TLSCertKey], secret.Data[constants2.CABundleKey])
   138  
   139  	// Confirm the Rancher tls-ca secret got updated
   140  	secret = &corev1.Secret{}
   141  	err = fakeClient.Get(context.TODO(), types.NamespacedName{Namespace: cattleTLSSecret.Namespace, Name: cattleTLSSecret.Name}, secret)
   142  	asserts.NoError(err)
   143  	asserts.Equal(caSecret.Data[corev1.TLSCertKey], secret.Data[constants2.RancherTLSCAKey])
   144  
   145  	// Confirm the Rancher deployment was annotated to restart
   146  	deployment := &appsv1.Deployment{}
   147  	err = fakeClient.Get(context.TODO(), types.NamespacedName{Namespace: cattleDeployment.Namespace, Name: cattleDeployment.Name}, deployment)
   148  	asserts.NoError(err)
   149  	annotations := deployment.Spec.Template.ObjectMeta.Annotations
   150  	asserts.NotNil(annotations)
   151  	asserts.NotEmpty(annotations[constants2.VerrazzanoRestartAnnotation])
   152  
   153  	// Confirm the multi-cluster verrazzano-local-ca-bundle secret got updated
   154  	secret = &corev1.Secret{}
   155  	err = fakeClient.Get(context.TODO(), types.NamespacedName{Namespace: mcSecret.Namespace, Name: mcSecret.Name}, secret)
   156  	asserts.NoError(err)
   157  	asserts.Equal(caSecret.Data[corev1.TLSCertKey], secret.Data[mcCABundleKey])
   158  }
   159  
   160  // TestIgnoresOtherSecrets tests the Reconcile method for the following use case
   161  // GIVEN a request to reconcile the additional TLS secret or a secret other than verrazzano TLS secret
   162  // WHEN any conditions
   163  // THEN the request is ignored
   164  func TestIgnoresOtherSecrets(t *testing.T) {
   165  	tests := []struct {
   166  		secretName string
   167  		secretNS   string
   168  	}{
   169  		// Additional TLS secret no longer watched
   170  		{
   171  			secretName: additionalTLSSecret.Name,
   172  			secretNS:   additionalTLSSecret.Namespace,
   173  		},
   174  		// VZ TLS secret name in wrong NS
   175  		{
   176  			secretName: vzTLSSecret.Name,
   177  			secretNS:   additionalTLSSecret.Namespace,
   178  		},
   179  		// Additional TLS secret name in wrong NS
   180  		{
   181  			secretName: additionalTLSSecret.Name,
   182  			secretNS:   vzTLSSecret.Namespace,
   183  		},
   184  		// A totally different secret name and NS
   185  		{
   186  			secretName: unwatchedSecret.Name,
   187  			secretNS:   unwatchedSecret.Namespace,
   188  		},
   189  	}
   190  	for _, tt := range tests {
   191  		asserts := assert.New(t)
   192  		mocker := gomock.NewController(t)
   193  		mock := mocks.NewMockClient(mocker)
   194  
   195  		expectNothingForWrongSecret(mock)
   196  
   197  		// Create and make the request
   198  		request := newRequest(tt.secretNS, tt.secretName)
   199  		reconciler := newSecretsReconciler(mock)
   200  		result, err := reconciler.Reconcile(context.TODO(), request)
   201  
   202  		// Validate the results
   203  		mocker.Finish()
   204  		asserts.NoError(err)
   205  		asserts.NotNil(result)
   206  	}
   207  }
   208  
   209  // TestSecretReconciler tests the Reconciler method for the following use case
   210  // GIVEN a request to reconcile a Secret
   211  // WHEN the Secret is referenced in the Verrazzano CR under a component and is also present the CR namespace
   212  // THEN the ReconcilingGeneration of the target component is set to 1
   213  func TestSecretReconciler(t *testing.T) {
   214  	asserts := assert.New(t)
   215  	secret := testSecret
   216  	secret.Finalizers = append(secret.Finalizers, constants.OverridesFinalizer)
   217  	cli := fake.NewClientBuilder().WithObjects(&testVZ, &secret).WithScheme(newScheme()).Build()
   218  
   219  	config.TestProfilesDir = "../../manifests/profiles"
   220  	defer func() { config.TestProfilesDir = "" }()
   221  
   222  	request := newRequest(testNS, testSecretName)
   223  	reconciler := newSecretsReconciler(cli)
   224  	res0, err0 := reconciler.Reconcile(context.TODO(), request)
   225  
   226  	asserts.NoError(err0)
   227  	asserts.Empty(res0)
   228  
   229  	vz := vzapi.Verrazzano{}
   230  	err := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testVZName}, &vz)
   231  	asserts.NoError(err)
   232  	asserts.Equal(int64(1), vz.Status.Components["prometheus-operator"].ReconcilingGeneration)
   233  
   234  }
   235  
   236  // TestSecretRequeue tests the Reconcile method for the following use case
   237  // GIVEN a request to reconcile a Secret that qualifies as an override
   238  // WHEN the status of the Verrazzano CR is found without the Component Status details
   239  // THEN a requeue request is returned with an error
   240  func TestSecretRequeue(t *testing.T) {
   241  	asserts := assert.New(t)
   242  	vz := testVZ
   243  	vz.Status.Components = nil
   244  	asserts.Nil(vz.Status.Components)
   245  	secret := testSecret
   246  	secret.Finalizers = append(secret.Finalizers, constants.OverridesFinalizer)
   247  	cli := fake.NewClientBuilder().WithObjects(&vz, &secret).WithScheme(newScheme()).Build()
   248  
   249  	config.TestProfilesDir = "../../manifests/profiles"
   250  	defer func() { config.TestProfilesDir = "" }()
   251  
   252  	request0 := newRequest(testNS, testSecretName)
   253  	reconciler := newSecretsReconciler(cli)
   254  	res0, err0 := reconciler.Reconcile(context.TODO(), request0)
   255  
   256  	asserts.Error(err0)
   257  	asserts.Contains(err0.Error(), "Components not initialized")
   258  	asserts.Equal(true, res0.Requeue)
   259  }
   260  
   261  // TestAddFinalizer tests the Reconciler for the following use case
   262  // GIVEN a request to reconcile a Secret that qualifies as an override
   263  // WHEN the Secret is found without the overrides finalizer
   264  // THEN the overrides finalizer is added and we requeue without an error
   265  func TestAddFinalizer(t *testing.T) {
   266  	asserts := assert.New(t)
   267  	cli := fake.NewClientBuilder().WithObjects(&testVZ, &testSecret).WithScheme(newScheme()).Build()
   268  
   269  	config.TestProfilesDir = "../../manifests/profiles"
   270  	defer func() { config.TestProfilesDir = "" }()
   271  
   272  	request0 := newRequest(testNS, testSecretName)
   273  	reconciler := newSecretsReconciler(cli)
   274  	res0, err0 := reconciler.Reconcile(context.TODO(), request0)
   275  
   276  	asserts.NoError(err0)
   277  	asserts.Equal(true, res0.Requeue)
   278  
   279  	secret := corev1.Secret{}
   280  	err := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testSecretName}, &secret)
   281  	asserts.NoError(err)
   282  	asserts.True(controllerutil.ContainsFinalizer(&secret, constants.OverridesFinalizer))
   283  }
   284  
   285  // TestOtherFinalizers tests the Reconcile loop for the following use case
   286  // GIVEN a request to reconcile a Secret that qualifies as an override resource and is scheduled for deletion
   287  // WHEN the Secret is found with finalizers but the override finalizer is missing
   288  // THEN without updating the Verrazzano CR a requeue request is returned without an error
   289  func TestOtherFinalizers(t *testing.T) {
   290  	asserts := assert.New(t)
   291  	secret := testSecret
   292  	secret.Finalizers = append(secret.Finalizers, "test")
   293  	secret.DeletionTimestamp = &metav1.Time{Time: time.Now()}
   294  	cli := fake.NewClientBuilder().WithObjects(&testVZ, &secret).WithScheme(newScheme()).Build()
   295  
   296  	config.TestProfilesDir = "../../manifests/profiles"
   297  	defer func() { config.TestProfilesDir = "" }()
   298  
   299  	request0 := newRequest(testNS, testSecretName)
   300  	reconciler := newSecretsReconciler(cli)
   301  	res0, err0 := reconciler.Reconcile(context.TODO(), request0)
   302  
   303  	asserts.NoError(err0)
   304  	asserts.Equal(true, res0.Requeue)
   305  
   306  	vz := &vzapi.Verrazzano{}
   307  	err1 := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testVZName}, vz)
   308  	asserts.NoError(err1)
   309  	asserts.NotEqual(int64(1), vz.Status.Components["prometheus-operator"].ReconcilingGeneration)
   310  }
   311  
   312  // TestSecretNotFound tests the Reconcile method for the following use cases
   313  // GIVEN requests to reconcile a ConfigMap
   314  // WHEN the Secret is not found in the cluster
   315  // THEN Verrazzano is updated if it's listed as an override, otherwise the request is ignored
   316  func TestSecretNotFound(t *testing.T) {
   317  	tests := []struct {
   318  		nsn types.NamespacedName
   319  	}{
   320  		{
   321  			nsn: types.NamespacedName{Namespace: testNS, Name: testSecretName},
   322  		},
   323  		{
   324  			nsn: types.NamespacedName{Namespace: testNS, Name: "test"},
   325  		},
   326  	}
   327  
   328  	for i, tt := range tests {
   329  		asserts := assert.New(t)
   330  		cli := fake.NewClientBuilder().WithObjects(&testVZ).WithScheme(newScheme()).Build()
   331  		config.Set(config.OperatorConfig{CloudCredentialWatchEnabled: false})
   332  		config.TestProfilesDir = "../../manifests/profiles"
   333  		defer func() { config.TestProfilesDir = "" }()
   334  
   335  		request0 := newRequest(tt.nsn.Namespace, tt.nsn.Name)
   336  		reconciler := newSecretsReconciler(cli)
   337  		res0, err0 := reconciler.Reconcile(context.TODO(), request0)
   338  
   339  		asserts.NoError(err0)
   340  		asserts.Equal(false, res0.Requeue)
   341  
   342  		vz := &vzapi.Verrazzano{}
   343  		err1 := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testVZName}, vz)
   344  		asserts.NoError(err1)
   345  		if i == 0 {
   346  			asserts.Equal(int64(1), vz.Status.Components["prometheus-operator"].ReconcilingGeneration)
   347  		} else {
   348  			asserts.NotEqual(int64(1), vz.Status.Components["prometheus-operator"].ReconcilingGeneration)
   349  		}
   350  	}
   351  
   352  }
   353  
   354  // TestVerrazzanoResourcesNotFound tests the Reconcile method for the following use cases
   355  // GIVEN a request to reconcile
   356  // WHEN no verrazzano resources are found
   357  // THEN the secrets reconciler returns a result of ctrl.Result{}
   358  func TestVerrazzanoResourcesNotFound(t *testing.T) {
   359  	asserts := assert.New(t)
   360  	mocker := gomock.NewController(t)
   361  	mock := mocks.NewMockClient(mocker)
   362  
   363  	// Expect a call to get a list of verrazzano resources
   364  	mock.EXPECT().
   365  		List(gomock.Any(), gomock.Any(), gomock.Any()).
   366  		DoAndReturn(func(ctx context.Context, list runtime.Object, opts ...client.ListOption) error {
   367  			// return no resources
   368  			return nil
   369  		})
   370  
   371  	// Create and make the request
   372  	request := newRequest(vzTLSSecret.Namespace, vzTLSSecret.Name)
   373  	reconciler := newSecretsReconciler(mock)
   374  	result, err := reconciler.Reconcile(context.TODO(), request)
   375  
   376  	// Validate the results
   377  	mocker.Finish()
   378  	asserts.NoError(err)
   379  	asserts.NotNil(result)
   380  	asserts.Equal(ctrl.Result{}, result)
   381  }
   382  
   383  // TestVerrazzanoVerrazzanoResourceBeingDeleted tests the Reconcile method for the following use cases
   384  // GIVEN a request to reconcile
   385  // WHEN the verrazzano resource is marked for deletion
   386  // THEN the secrets reconciler returns a result of ctrl.Result{}
   387  func TestVerrazzanoVerrazzanoResourceBeingDeleted(t *testing.T) {
   388  	asserts := assert.New(t)
   389  	mocker := gomock.NewController(t)
   390  	mock := mocks.NewMockClient(mocker)
   391  
   392  	// Expect a call to get a list of verrazzano resources
   393  	mock.EXPECT().
   394  		List(gomock.Any(), gomock.Any(), gomock.Any()).
   395  		DoAndReturn(func(ctx context.Context, vzList *vzapi.VerrazzanoList, opts ...client.ListOption) error {
   396  			vzList.Items = []vzapi.Verrazzano{{
   397  				ObjectMeta: metav1.ObjectMeta{
   398  					Namespace:         constants.DefaultNamespace,
   399  					Name:              "verrazzano",
   400  					DeletionTimestamp: &metav1.Time{Time: time.Now()},
   401  				},
   402  			}}
   403  			return nil
   404  		})
   405  
   406  	// Create and make the request
   407  	request := newRequest(vzTLSSecret.Namespace, vzTLSSecret.Name)
   408  	reconciler := newSecretsReconciler(mock)
   409  	result, err := reconciler.Reconcile(context.TODO(), request)
   410  
   411  	// Validate the results
   412  	mocker.Finish()
   413  	asserts.NoError(err)
   414  	asserts.NotNil(result)
   415  	asserts.Equal(ctrl.Result{}, result)
   416  }
   417  
   418  // TestDeletion tests the Reconcile loop for the following use case
   419  // GIVEN a request to reconcile a Secret that qualifies as an override
   420  // WHEN we find that it is scheduled for deletion and contains overrides finalizer
   421  // THEN the override finalizer is removed from the Secret and Verrazzano CR is updated and request is returned without an error
   422  func TestDeletion(t *testing.T) {
   423  	asserts := assert.New(t)
   424  	secret := testSecret
   425  	secret.Finalizers = append(secret.Finalizers, constants.OverridesFinalizer)
   426  	secret.DeletionTimestamp = &metav1.Time{Time: time.Now()}
   427  	cli := fake.NewClientBuilder().WithObjects(&testVZ, &secret).WithScheme(newScheme()).Build()
   428  
   429  	config.TestProfilesDir = "../../manifests/profiles"
   430  	defer func() { config.TestProfilesDir = "" }()
   431  
   432  	request0 := newRequest(testNS, testSecretName)
   433  	reconciler := newSecretsReconciler(cli)
   434  	res0, err0 := reconciler.Reconcile(context.TODO(), request0)
   435  
   436  	asserts.NoError(err0)
   437  	asserts.Equal(false, res0.Requeue)
   438  
   439  	sec1 := &corev1.Secret{}
   440  	err1 := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testSecretName}, sec1)
   441  	asserts.True(errors.IsNotFound(err1))
   442  
   443  	vz := &vzapi.Verrazzano{}
   444  	err2 := cli.Get(context.TODO(), types.NamespacedName{Namespace: testNS, Name: testVZName}, vz)
   445  	asserts.NoError(err2)
   446  	asserts.Equal(int64(1), vz.Status.Components["prometheus-operator"].ReconcilingGeneration)
   447  }
   448  
   449  // TestSecretCall tests the reconcileInstallOverrideSecret for the following use case
   450  // GIVEN a request to reconcile a Secret
   451  // WHEN the request namespace matches the Verrazzano CR namespace
   452  // THEN expect a call to get the Secret
   453  func TestSecretCall(t *testing.T) {
   454  	asserts := assert.New(t)
   455  	mocker := gomock.NewController(t)
   456  	mock := mocks.NewMockClient(mocker)
   457  	mockStatus := mocks.NewMockStatusWriter(mocker)
   458  	asserts.NotNil(mockStatus)
   459  
   460  	config.TestProfilesDir = "../../manifests/profiles"
   461  	defer func() { config.TestProfilesDir = "" }()
   462  
   463  	expectGetSecretExists(mock, testNS, testSecretName)
   464  
   465  	request := newRequest(testNS, testSecretName)
   466  	reconciler := newSecretsReconciler(mock)
   467  	result, err := reconciler.reconcileInstallOverrideSecret(context.TODO(), request, &testVZ)
   468  	asserts.NoError(err)
   469  	mocker.Finish()
   470  	asserts.Equal(false, result.Requeue)
   471  	asserts.Equal(time.Duration(0), result.RequeueAfter)
   472  }
   473  
   474  // TestOtherNS tests the reconcileInstallOverrideSecret for the following use case
   475  // GIVEN a request to reconcile a Secret
   476  // WHEN the request namespace does not match with the CR namespace
   477  // THEN the request is ignored
   478  func TestOtherNS(t *testing.T) {
   479  	asserts := assert.New(t)
   480  	mocker := gomock.NewController(t)
   481  	mock := mocks.NewMockClient(mocker)
   482  	mockStatus := mocks.NewMockStatusWriter(mocker)
   483  	asserts.NotNil(mockStatus)
   484  
   485  	// Do not expect a call to get the Secret if it's a different namespace
   486  	mock.EXPECT().
   487  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).MaxTimes(0)
   488  
   489  	request := newRequest("test0", "test1")
   490  	reconciler := newSecretsReconciler(mock)
   491  	result, err := reconciler.reconcileInstallOverrideSecret(context.TODO(), request, &testVZ)
   492  	asserts.NoError(err)
   493  	mocker.Finish()
   494  	asserts.Equal(false, result.Requeue)
   495  	asserts.Equal(time.Duration(0), result.RequeueAfter)
   496  
   497  }
   498  
   499  // mock client request to get the secret
   500  func expectGetSecretExists(mock *mocks.MockClient, namespace string, name string) {
   501  	mock.EXPECT().
   502  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: name}, gomock.Not(gomock.Nil()), gomock.Any()).
   503  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error {
   504  			return nil
   505  		})
   506  }
   507  
   508  func expectNothingForWrongSecret(mock *mocks.MockClient) {
   509  
   510  	mock.EXPECT().
   511  		List(gomock.Any(), &vzapi.VerrazzanoList{}, gomock.Any()).
   512  		DoAndReturn(func(ctx context.Context, verrazzanoList *vzapi.VerrazzanoList, options ...client.ListOption) error {
   513  			return nil
   514  		})
   515  
   516  	// Expect no calls to get a secret
   517  	mock.EXPECT().
   518  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).MaxTimes(0)
   519  
   520  	// Expect no calls to get update
   521  	mock.EXPECT().
   522  		Update(gomock.Any(), gomock.Any(), gomock.Any()).MaxTimes(0)
   523  }
   524  
   525  // newScheme creates a new scheme that includes this package's object to use for testing
   526  func newScheme() *runtime.Scheme {
   527  	scheme := runtime.NewScheme()
   528  	_ = corev1.AddToScheme(scheme)
   529  	_ = vzapi.AddToScheme(scheme)
   530  	_ = appsv1.AddToScheme(scheme)
   531  	_ = certv1.AddToScheme(scheme)
   532  	return scheme
   533  }
   534  
   535  // newRequest creates a new reconciler request for testing
   536  // namespace - The namespace to use in the request
   537  // name - The name to use in the request
   538  func newRequest(namespace string, name string) ctrl.Request {
   539  	return ctrl.Request{
   540  		NamespacedName: types.NamespacedName{
   541  			Namespace: namespace,
   542  			Name:      name}}
   543  }
   544  
   545  // newSecretsReconciler creates a new reconciler for testing
   546  // c - The Kerberos client to inject into the reconciler
   547  func newSecretsReconciler(c client.Client) VerrazzanoSecretsReconciler {
   548  	scheme := newScheme()
   549  	reconciler := VerrazzanoSecretsReconciler{
   550  		Client:        c,
   551  		Scheme:        scheme,
   552  		log:           vzlog.DefaultLogger(),
   553  		StatusUpdater: &vzstatus.FakeVerrazzanoStatusUpdater{Client: c},
   554  	}
   555  	return reconciler
   556  }
   557  
   558  // newVZ - create a Verrazzano custom resource
   559  func newVZ() *vzapi.Verrazzano {
   560  	return &vzapi.Verrazzano{
   561  		ObjectMeta: metav1.ObjectMeta{
   562  			Name: "verrazzano",
   563  		},
   564  		Spec: vzapi.VerrazzanoSpec{
   565  			Components: vzapi.ComponentSpec{
   566  				ClusterIssuer: &vzapi.ClusterIssuerComponent{
   567  					IssuerConfig: vzapi.IssuerConfig{
   568  						CA: &vzapi.CAIssuer{
   569  							SecretName: constants2.DefaultVerrazzanoCASecretName,
   570  						},
   571  					},
   572  					ClusterResourceNamespace: constants2.CertManagerNamespace,
   573  				},
   574  			},
   575  		},
   576  	}
   577  }
   578  
   579  // newCertificateWithSecret - Create a new certificate and secret that is optionally signed by a parent
   580  func newCertificateWithSecret(issuerName string, commonName string, certName string, certNamespace string, parent *x509.Certificate) (*corev1.Secret, *certv1.Certificate, error) {
   581  	fakeIssuerCertBytes, err := cmcommonfake.CreateFakeCertBytes(commonName, parent)
   582  	if err != nil {
   583  		return nil, nil, err
   584  	}
   585  	secret := newCertSecret(fmt.Sprintf("%s-secret", certName), certNamespace, corev1.TLSCertKey, fakeIssuerCertBytes)
   586  	certificate := &certv1.Certificate{
   587  		ObjectMeta: metav1.ObjectMeta{
   588  			Name:      certName,
   589  			Namespace: certNamespace,
   590  		},
   591  		Spec: certv1.CertificateSpec{
   592  			CommonName: commonName,
   593  			IsCA:       true,
   594  			IssuerRef: cmmeta.ObjectReference{
   595  				Name: issuerName,
   596  			},
   597  			SecretName: secret.Name,
   598  		},
   599  	}
   600  
   601  	return secret, certificate, nil
   602  }
   603  
   604  func newCertSecret(name string, namespace string, certKey string, certBytes []byte) *corev1.Secret {
   605  	return &corev1.Secret{
   606  		TypeMeta: metav1.TypeMeta{},
   607  		ObjectMeta: metav1.ObjectMeta{
   608  			Name:      name,
   609  			Namespace: namespace,
   610  		},
   611  		Data: map[string][]byte{
   612  			certKey: certBytes,
   613  		},
   614  		Type: corev1.SecretTypeTLS,
   615  	}
   616  }