github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_k8ssecret_test.go (about)

     1  // Copyright (c) 2021, 2022, 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 mcagent
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	asserts "github.com/stretchr/testify/assert"
    13  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    14  	"github.com/verrazzano/verrazzano/application-operator/constants"
    15  	clusterstest "github.com/verrazzano/verrazzano/application-operator/controllers/clusters/test"
    16  	constants2 "github.com/verrazzano/verrazzano/pkg/constants"
    17  	"go.uber.org/zap"
    18  	corev1 "k8s.io/api/core/v1"
    19  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    20  	"k8s.io/apimachinery/pkg/runtime"
    21  	"k8s.io/apimachinery/pkg/types"
    22  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    23  )
    24  
    25  // TestCreateSecretOneMCAppConfig tests the synchronization method for the following use case.
    26  // GIVEN a request to sync Secret objects with a single MultiClusterApplicationConfiguration object
    27  //
    28  //	containing two secrets
    29  //
    30  // WHEN the new object exists
    31  // THEN ensure that the Secret objects are created
    32  func TestCreateSecretOneMCAppConfig(t *testing.T) {
    33  	assert := asserts.New(t)
    34  	log := zap.S().With("test")
    35  
    36  	// Test data
    37  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
    38  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
    39  
    40  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
    41  	assert.NoError(err, "failed to read sample data for Secret")
    42  
    43  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
    44  	assert.NoError(err, "failed to read sample data for Secret")
    45  
    46  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig, &testSecret1, &testSecret2).Build()
    47  
    48  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).Build()
    49  
    50  	// Make the request
    51  	s := &Syncer{
    52  		AdminClient:        adminClient,
    53  		LocalClient:        localClient,
    54  		Log:                log,
    55  		ManagedClusterName: testClusterName,
    56  		Context:            context.TODO(),
    57  	}
    58  
    59  	err = s.syncSecretObjects(testMCAppConfigNamespace)
    60  
    61  	// Validate the results
    62  	assert.NoError(err)
    63  
    64  	// Verify the associated secrets got created on local cluster
    65  	secret := &corev1.Secret{}
    66  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
    67  	assert.NoError(err)
    68  	assert.Equal(4, len(secret.Labels))
    69  	assertCommonLabels(assert, secret, "unit-mcappconfig")
    70  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
    71  
    72  	secret = &corev1.Secret{}
    73  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
    74  	assert.NoError(err)
    75  	assert.Equal(3, len(secret.Labels))
    76  	assertCommonLabels(assert, secret, "unit-mcappconfig")
    77  }
    78  
    79  // TestCreateSecretTwoMCAppConfigs tests the synchronization method for the following use case.
    80  // GIVEN a request to sync Secret objects with two MultiClusterApplicationConfiguration objects
    81  //
    82  //	and one of the secrets is shared by two MultiClusterApplicationConfiguration objects
    83  //
    84  // WHEN the new object exists
    85  // THEN ensure that the Secret objects are created
    86  func TestCreateSecretTwoMCAppConfigs(t *testing.T) {
    87  	assert := asserts.New(t)
    88  	log := zap.S().With("test")
    89  
    90  	// Test data
    91  	testMCAppConfig1, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
    92  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
    93  
    94  	testMCAppConfig2, err := getSampleMCAppConfig("testdata/multicluster-appconfig2.yaml")
    95  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
    96  
    97  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
    98  	assert.NoError(err, "failed to read sample data for Secret")
    99  
   100  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
   101  	assert.NoError(err, "failed to read sample data for Secret")
   102  
   103  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig1, &testMCAppConfig2, &testSecret1, &testSecret2).Build()
   104  
   105  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).Build()
   106  
   107  	// Make the request
   108  	s := &Syncer{
   109  		AdminClient:        adminClient,
   110  		LocalClient:        localClient,
   111  		Log:                log,
   112  		ManagedClusterName: testClusterName,
   113  		Context:            context.TODO(),
   114  	}
   115  
   116  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   117  
   118  	// Validate the results
   119  	assert.NoError(err)
   120  
   121  	// Verify the associated secrets got created on local cluster
   122  	secret := &corev1.Secret{}
   123  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   124  	assert.NoError(err)
   125  	assert.Equal(4, len(secret.Labels))
   126  	assertCommonLabels(assert, secret, "unit-mcappconfig,unit-mcappconfig2")
   127  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
   128  
   129  	secret = &corev1.Secret{}
   130  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   131  	assert.NoError(err)
   132  	assert.Equal(3, len(secret.Labels))
   133  	assert.Contains(secret.Labels[managedClusterLabel], testClusterName, "secret label did not match")
   134  	assert.Contains(secret.Labels[mcAppConfigsLabel], "unit-mcappconfig", "secret label did not match")
   135  	assert.Contains(secret.Labels[constants2.VerrazzanoManagedLabelKey], constants.LabelVerrazzanoManagedDefault, "secret label did not match")
   136  }
   137  
   138  // TestChangePlacement tests the synchronization method for the following use case.
   139  // GIVEN a request to move a MultiClusterApplicationConfiguration object
   140  //
   141  //	from one cluster to another
   142  //
   143  // WHEN the new object exists
   144  // THEN ensure that the Secret objects are created and then removed
   145  func TestChangePlacement(t *testing.T) {
   146  	assert := asserts.New(t)
   147  	log := zap.S().With("test")
   148  
   149  	// Test data
   150  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   151  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   152  
   153  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
   154  	assert.NoError(err, "failed to read sample data for Secret")
   155  
   156  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
   157  	assert.NoError(err, "failed to read sample data for Secret")
   158  
   159  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig, &testSecret1, &testSecret2).Build()
   160  
   161  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).Build()
   162  
   163  	// Make the request
   164  	s := &Syncer{
   165  		AdminClient:        adminClient,
   166  		LocalClient:        localClient,
   167  		Log:                log,
   168  		ManagedClusterName: testClusterName,
   169  		Context:            context.TODO(),
   170  	}
   171  
   172  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   173  	assert.NoError(err)
   174  
   175  	// Verify the associated secrets got created on local cluster
   176  	secret := &corev1.Secret{}
   177  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   178  	assert.NoError(err)
   179  	assert.Equal(4, len(secret.Labels))
   180  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   181  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
   182  
   183  	secret = &corev1.Secret{}
   184  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   185  	assert.NoError(err)
   186  	assert.Equal(3, len(secret.Labels))
   187  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   188  
   189  	testMCAppConfig.Spec.Placement.Clusters[0].Name = "managed2"
   190  	err = s.AdminClient.Update(s.Context, &testMCAppConfig)
   191  	assert.NoError(err)
   192  
   193  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   194  	assert.NoError(err)
   195  
   196  	// Check that secrets have been deleted on the local cluster since the placement has changed
   197  	secret = &corev1.Secret{}
   198  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   199  	assert.True(apierrors.IsNotFound(err))
   200  
   201  	secret = &corev1.Secret{}
   202  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   203  	assert.True(apierrors.IsNotFound(err))
   204  }
   205  
   206  // TestDeleteSecret tests the deletion of secrets for the following use case.
   207  // GIVEN a request to delete a MultiClusterApplicationConfiguration object
   208  //
   209  //	containing two secrets that are not shared
   210  //
   211  // WHEN the MultiClusterApplicationConfiguration object is deleted
   212  // THEN ensure that the Secret objects are deleted
   213  func TestDeleteSecret(t *testing.T) {
   214  	assert := asserts.New(t)
   215  	log := zap.S().With("test")
   216  
   217  	// Test data
   218  	testMCAppConfig, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   219  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   220  
   221  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
   222  	assert.NoError(err, "failed to read sample data for Secret")
   223  
   224  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
   225  	assert.NoError(err, "failed to read sample data for Secret")
   226  
   227  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig, &testSecret1, &testSecret2).Build()
   228  
   229  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).Build()
   230  
   231  	// Make the request
   232  	s := &Syncer{
   233  		AdminClient:        adminClient,
   234  		LocalClient:        localClient,
   235  		Log:                log,
   236  		ManagedClusterName: testClusterName,
   237  		Context:            context.TODO(),
   238  	}
   239  
   240  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   241  	assert.NoError(err)
   242  
   243  	// Verify the associated secrets got created on local cluster
   244  	secret := &corev1.Secret{}
   245  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   246  	assert.NoError(err)
   247  	assert.Equal(4, len(secret.Labels))
   248  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
   249  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   250  
   251  	secret = &corev1.Secret{}
   252  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   253  	assert.NoError(err)
   254  	assert.Equal(3, len(secret.Labels))
   255  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   256  
   257  	// Delete the MultiClusterApplicationConfigurarion object from the admin cluster
   258  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig)
   259  	assert.NoError(err)
   260  
   261  	// sync
   262  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   263  	assert.NoError(err)
   264  
   265  	// Check that secrets have been deleted on the local cluster
   266  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   267  	assert.True(apierrors.IsNotFound(err))
   268  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   269  	assert.True(apierrors.IsNotFound(err))
   270  }
   271  
   272  // TestDeleteSecretSharedSecret tests the deletion of secrets for the following use case.
   273  // GIVEN a request to delete a MultiClusterApplicationConfiguration object
   274  //
   275  //	containing a secret that is shared by another MultiClusterApplicationConfiguration object
   276  //
   277  // WHEN the MultiClusterApplicationConfiguration object is deleted
   278  // THEN ensure that the shared secret is not deleted and the verrazzano.io/mc-app-configs label is updated to reflect
   279  //
   280  //	the deleted MultiClusterApplicationConfiguration object
   281  func TestDeleteSecretSharedSecret(t *testing.T) {
   282  	assert := asserts.New(t)
   283  	log := zap.S().With("test")
   284  
   285  	// Test data
   286  	testMCAppConfig1, err := getSampleMCAppConfig("testdata/multicluster-appconfig.yaml")
   287  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   288  
   289  	testMCAppConfig2, err := getSampleMCAppConfig("testdata/multicluster-appconfig2.yaml")
   290  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   291  
   292  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
   293  	assert.NoError(err, "failed to read sample data for Secret")
   294  
   295  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
   296  	assert.NoError(err, "failed to read sample data for Secret")
   297  
   298  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig1, &testMCAppConfig2, &testSecret1, &testSecret2).Build()
   299  
   300  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).Build()
   301  
   302  	// Make the request
   303  	s := &Syncer{
   304  		AdminClient:        adminClient,
   305  		LocalClient:        localClient,
   306  		Log:                log,
   307  		ManagedClusterName: testClusterName,
   308  		Context:            context.TODO(),
   309  	}
   310  
   311  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   312  	assert.NoError(err)
   313  
   314  	// Verify the associated secrets got created on local cluster
   315  	secret := &corev1.Secret{}
   316  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   317  	assert.NoError(err)
   318  	assert.Equal(4, len(secret.Labels))
   319  	assertCommonLabels(assert, secret, "unit-mcappconfig,unit-mcappconfig2")
   320  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
   321  
   322  	secret = &corev1.Secret{}
   323  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   324  	assert.NoError(err)
   325  	assert.Equal(3, len(secret.Labels))
   326  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   327  
   328  	// Delete the MultiClusterApplicationConfigurarion object from the admin cluster
   329  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig1)
   330  	assert.NoError(err)
   331  
   332  	// sync
   333  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   334  	assert.NoError(err)
   335  
   336  	// shared secret1 should not have been deleted on the local cluster
   337  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   338  	assert.NoError(err)
   339  
   340  	// label for secret1 should have been updated
   341  	assert.Contains(secret.Labels[mcAppConfigsLabel], "unit-mcappconfig2", "secret label did not match")
   342  
   343  	// secret2 should have been deleted on the local cluster
   344  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   345  	assert.True(apierrors.IsNotFound(err))
   346  }
   347  
   348  // TestDeleteSecretExtra tests the deletion of secrets for the following use case.
   349  // GIVEN a request to delete a MultiClusterApplicationConfiguration object
   350  //
   351  //	containing a secret and there is also a secret not part of the MultiClusterApplicationConfiguration object
   352  //
   353  // WHEN the MultiClusterApplicationConfiguration object is deleted
   354  // THEN ensure that only the secret in the MultiClusterApplicationConfiguration object is deleted
   355  func TestDeleteSecretExtra(t *testing.T) {
   356  	assert := asserts.New(t)
   357  	log := zap.S().With("test")
   358  
   359  	// Test data
   360  	testMCAppConfig2, err := getSampleMCAppConfig("testdata/multicluster-appconfig2.yaml")
   361  	assert.NoError(err, "failed to read sample data for MultiClusterApplicationConfiguration")
   362  
   363  	testSecret1, err := getSampleSecret("testdata/secret1.yaml")
   364  	assert.NoError(err, "failed to read sample data for Secret")
   365  
   366  	testSecret2, err := getSampleSecret("testdata/secret2.yaml")
   367  	assert.NoError(err, "failed to read sample data for Secret")
   368  
   369  	adminClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testMCAppConfig2, &testSecret1).Build()
   370  
   371  	localClient := fake.NewClientBuilder().WithScheme(newTestScheme()).WithObjects(&testSecret2).Build()
   372  
   373  	// Make the request
   374  	s := &Syncer{
   375  		AdminClient:        adminClient,
   376  		LocalClient:        localClient,
   377  		Log:                log,
   378  		ManagedClusterName: testClusterName,
   379  		Context:            context.TODO(),
   380  	}
   381  
   382  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   383  	assert.NoError(err)
   384  
   385  	// Verify the associated secrets got created on local cluster
   386  	secret := &corev1.Secret{}
   387  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   388  	assert.NoError(err)
   389  	assert.Equal(4, len(secret.Labels))
   390  	assertCommonLabels(assert, secret, "unit-mcappconfig")
   391  	assert.Contains(secret.Labels["label1"], "test1", "secret label did not match")
   392  
   393  	secret = &corev1.Secret{}
   394  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   395  	assert.NoError(err)
   396  	assert.Equal(0, len(secret.Labels))
   397  
   398  	// Delete the MultiClusterApplicationConfigurarion object from the admin cluster
   399  	err = s.AdminClient.Delete(s.Context, &testMCAppConfig2)
   400  	assert.NoError(err)
   401  
   402  	// sync
   403  	err = s.syncSecretObjects(testMCAppConfigNamespace)
   404  	assert.NoError(err)
   405  
   406  	// secret1 should have been deleted on the local cluster
   407  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret1.Name, Namespace: testSecret1.Namespace}, secret)
   408  	assert.True(apierrors.IsNotFound(err))
   409  	// secret2 should not have been deleted on the local cluster
   410  	err = s.LocalClient.Get(s.Context, types.NamespacedName{Name: testSecret2.Name, Namespace: testSecret2.Namespace}, secret)
   411  	assert.NoError(err)
   412  }
   413  
   414  // getSampleSecret returns a sample secret object
   415  func getSampleSecret(filePath string) (corev1.Secret, error) {
   416  	secret := corev1.Secret{}
   417  	sampleSecretFile, err := filepath.Abs(filePath)
   418  	if err != nil {
   419  		return secret, err
   420  	}
   421  
   422  	rawResource, err := clusterstest.ReadYaml2Json(sampleSecretFile)
   423  	if err != nil {
   424  		return secret, err
   425  	}
   426  
   427  	err = json.Unmarshal(rawResource, &secret)
   428  	return secret, err
   429  }
   430  
   431  // assert labels common to all K8S secrets synced to managed cluster
   432  func assertCommonLabels(assert *asserts.Assertions, secret *corev1.Secret, appConfigs string) {
   433  	assert.Contains(secret.Labels[managedClusterLabel], testClusterName, "secret label did not match")
   434  	assert.Contains(secret.Labels[mcAppConfigsLabel], appConfigs, "secret label did not match")
   435  	assert.Contains(secret.Labels[constants2.VerrazzanoManagedLabelKey], constants.LabelVerrazzanoManagedDefault, "secret label did not match")
   436  }
   437  
   438  func newTestScheme() *runtime.Scheme {
   439  	scheme := runtime.NewScheme()
   440  	clustersv1alpha1.AddToScheme(scheme)
   441  	corev1.AddToScheme(scheme)
   442  	return scheme
   443  }