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

     1  // Copyright (c) 2021, 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 mcagent
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"path/filepath"
    10  	"testing"
    11  
    12  	"github.com/golang/mock/gomock"
    13  	asserts "github.com/stretchr/testify/assert"
    14  	clustersv1alpha1 "github.com/verrazzano/verrazzano/application-operator/apis/clusters/v1alpha1"
    15  	clusterstest "github.com/verrazzano/verrazzano/application-operator/controllers/clusters/test"
    16  	"github.com/verrazzano/verrazzano/application-operator/mocks"
    17  	"go.uber.org/zap"
    18  	"k8s.io/apimachinery/pkg/api/errors"
    19  	"k8s.io/apimachinery/pkg/runtime/schema"
    20  	"k8s.io/apimachinery/pkg/types"
    21  	"sigs.k8s.io/controller-runtime/pkg/client"
    22  )
    23  
    24  const testClusterName = "managed1"
    25  const testMCSecretNamespace = "unit-mcsecret-namespace" //nolint:gosec //#gosec G101
    26  const testMCSecretName = "unit-mcsecret"
    27  
    28  var mcSecretTestLabels = map[string]string{"label1": "test1"}
    29  var mcSecretTestUpdatedLabels = map[string]string{"label1": "test1updated"}
    30  
    31  // TestCreateMCSecret tests the synchronization method for the following use case.
    32  // GIVEN a request to sync MultiClusterSecret objects
    33  // WHEN the a new object exists
    34  // THEN ensure that the MultiClusterSecret is created.
    35  func TestCreateMCSecret(t *testing.T) {
    36  	assert := asserts.New(t)
    37  	log := zap.S().With("test")
    38  
    39  	// Managed cluster mocks
    40  	mcMocker := gomock.NewController(t)
    41  	mcMock := mocks.NewMockClient(mcMocker)
    42  
    43  	// Admin cluster mocks
    44  	adminMocker := gomock.NewController(t)
    45  	adminMock := mocks.NewMockClient(adminMocker)
    46  
    47  	// Test data
    48  	testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
    49  	assert.NoError(err, "failed to get sample secret data")
    50  
    51  	// Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object
    52  	adminMock.EXPECT().
    53  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
    54  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
    55  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
    56  			mcSecretList.Items = append(mcSecretList.Items, testMCSecret)
    57  			return nil
    58  		})
    59  
    60  	// Managed Cluster - expect call to get a MultiClusterSecret secret from the list returned by the admin cluster
    61  	//                   Return the resource does not exist
    62  	mcMock.EXPECT().
    63  		Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()).
    64  		Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "MultiClusterSecret"}, testMCSecretName))
    65  
    66  	// Managed Cluster - expect call to create a MultiClusterSecret
    67  	mcMock.EXPECT().
    68  		Create(gomock.Any(), gomock.Any()).
    69  		DoAndReturn(func(ctx context.Context, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.CreateOption) error {
    70  			assert.Equal(testMCSecretNamespace, mcSecret.Namespace, "mcsecret namespace did not match")
    71  			assert.Equal(testMCSecretName, mcSecret.Name, "mcsecret name did not match")
    72  			assert.Equal(mcSecretTestLabels, mcSecret.Labels, "mcsecret labels did not match")
    73  			assert.Equal(testClusterName, mcSecret.Spec.Placement.Clusters[0].Name, "mcsecret does not contain expected placement")
    74  			assert.Equal([]byte("verrazzano"), mcSecret.Spec.Template.Data["username"], "mcsecret does not contain expected template data")
    75  			assert.Equal("test-stringdata", mcSecret.Spec.Template.StringData["test"], "mcsecret does not contain expected string data")
    76  			return nil
    77  		})
    78  
    79  	// Managed Cluster - expect call to list MultiClusterSecret objects - return same list as admin cluster
    80  	mcMock.EXPECT().
    81  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
    82  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
    83  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
    84  			mcSecretList.Items = append(mcSecretList.Items, testMCSecret)
    85  			return nil
    86  		})
    87  
    88  	// Make the request
    89  	s := &Syncer{
    90  		AdminClient:        adminMock,
    91  		LocalClient:        mcMock,
    92  		Log:                log,
    93  		ManagedClusterName: testClusterName,
    94  		Context:            context.TODO(),
    95  	}
    96  	err = s.syncMCSecretObjects(testMCSecretNamespace)
    97  
    98  	// Validate the results
    99  	adminMocker.Finish()
   100  	mcMocker.Finish()
   101  	assert.NoError(err)
   102  }
   103  
   104  // TestUpdateMCSecret tests the synchronization method for the following use case.
   105  // GIVEN a request to sync MultiClusterSecret objects
   106  // WHEN the a object exists
   107  // THEN ensure that the MultiClusterSecret is updated.
   108  func TestUpdateMCSecret(t *testing.T) {
   109  	assert := asserts.New(t)
   110  	log := zap.S().With("test")
   111  
   112  	// Managed cluster mocks
   113  	mcMocker := gomock.NewController(t)
   114  	mcMock := mocks.NewMockClient(mcMocker)
   115  
   116  	// Admin cluster mocks
   117  	adminMocker := gomock.NewController(t)
   118  	adminMock := mocks.NewMockClient(adminMocker)
   119  
   120  	// Test data
   121  	testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
   122  	assert.NoError(err, "failed to get sample secret data")
   123  	testMCSecretUpdate, err := getSampleMCSecret("testdata/multicluster-secret-update.yaml")
   124  	assert.NoError(err, "failed to get sample secret data")
   125  
   126  	// Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object
   127  	adminMock.EXPECT().
   128  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   129  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   130  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   131  			mcSecretList.Items = append(mcSecretList.Items, testMCSecretUpdate)
   132  			return nil
   133  		})
   134  
   135  	// Managed Cluster - expect call to get a MultiClusterSecret secret from the list returned by the admin cluster
   136  	//                   Return the resource with some values different than what the admin cluster returned
   137  	mcMock.EXPECT().
   138  		Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()).
   139  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.GetOption) error {
   140  			testMCSecret.DeepCopyInto(mcSecret)
   141  			return nil
   142  		})
   143  
   144  	// Managed Cluster - expect call to update a MultiClusterSecret
   145  	//                   Verify request had the updated values
   146  	mcMock.EXPECT().
   147  		Update(gomock.Any(), gomock.Any()).
   148  		DoAndReturn(func(ctx context.Context, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.UpdateOption) error {
   149  			assert.Equal(testMCSecretNamespace, mcSecret.Namespace, "mcsecret namespace did not match")
   150  			assert.Equal(testMCSecretName, mcSecret.Name, "mcsecret name did not match")
   151  			assert.Equal(mcSecretTestUpdatedLabels, mcSecret.Labels, "mcsecret labels did not match")
   152  			assert.Equal("test-stringdata2", mcSecret.Spec.Template.StringData["test"], "mcsecret does not contain expected string data")
   153  			assert.Equal([]byte("test"), mcSecret.Spec.Template.Data["username"], "mcsecret does not contain expected data")
   154  			return nil
   155  		})
   156  
   157  	// Managed Cluster - expect call to list MultiClusterSecret objects - return same list as admin cluster
   158  	mcMock.EXPECT().
   159  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   160  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   161  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   162  			mcSecretList.Items = append(mcSecretList.Items, testMCSecret)
   163  			return nil
   164  		})
   165  
   166  	// Make the request
   167  	s := &Syncer{
   168  		AdminClient:        adminMock,
   169  		LocalClient:        mcMock,
   170  		Log:                log,
   171  		ManagedClusterName: testClusterName,
   172  		Context:            context.TODO(),
   173  	}
   174  	err = s.syncMCSecretObjects(testMCSecretNamespace)
   175  
   176  	// Validate the results
   177  	adminMocker.Finish()
   178  	mcMocker.Finish()
   179  	assert.NoError(err)
   180  }
   181  
   182  // TestMCSecretPlacement tests the synchronization method for the following use case.
   183  // GIVEN a request to sync MultiClusterSecret objects
   184  // WHEN the a object exists that is not targeted for the cluster
   185  // THEN ensure that the MultiClusterSecret is not created or updated
   186  func TestMCSecretPlacement(t *testing.T) {
   187  	assert := asserts.New(t)
   188  	log := zap.S().With("test")
   189  
   190  	// Managed cluster mocks
   191  	mcMocker := gomock.NewController(t)
   192  	mcMock := mocks.NewMockClient(mcMocker)
   193  
   194  	// Admin cluster mocks
   195  	adminMocker := gomock.NewController(t)
   196  	adminMock := mocks.NewMockClient(adminMocker)
   197  
   198  	// Test data
   199  	adminMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
   200  	if err != nil {
   201  		assert.NoError(err, "failed to read sample data for MultiClusterSecret")
   202  	}
   203  	adminMCSecret.Spec.Placement.Clusters[0].Name = "managed2"
   204  	localMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
   205  	if err != nil {
   206  		assert.NoError(err, "failed to read sample data for MultiClusterSecret")
   207  	}
   208  
   209  	// Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object
   210  	adminMock.EXPECT().
   211  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   212  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   213  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   214  			mcSecretList.Items = append(mcSecretList.Items, adminMCSecret)
   215  			return nil
   216  		})
   217  
   218  	// Managed Cluster - expect call to list MultiClusterSecret objects - return list including a currently locally placed secret
   219  	mcMock.EXPECT().
   220  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   221  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   222  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   223  			mcSecretList.Items = append(mcSecretList.Items, localMCSecret)
   224  			return nil
   225  		})
   226  
   227  	// Managed Cluster - expect a call to delete a MultiClusterSecret object
   228  	mcMock.EXPECT().
   229  		Delete(gomock.Any(), gomock.Eq(&localMCSecret), gomock.Any()).
   230  		Return(nil)
   231  
   232  	// Make the request
   233  	s := &Syncer{
   234  		AdminClient:        adminMock,
   235  		LocalClient:        mcMock,
   236  		Log:                log,
   237  		ManagedClusterName: testClusterName,
   238  		Context:            context.TODO(),
   239  	}
   240  	err = s.syncMCSecretObjects(testMCSecretNamespace)
   241  
   242  	// Validate the results
   243  	adminMocker.Finish()
   244  	mcMocker.Finish()
   245  	assert.NoError(err)
   246  }
   247  
   248  // TestDeleteMCSecret tests the synchronization method for the following use case.
   249  // GIVEN a request to sync MultiClusterSecret objects
   250  // WHEN the object exists on the local cluster but not on the admin cluster
   251  // THEN ensure that the MultiClusterSecret is deleted.
   252  func TestDeleteMCSecret(t *testing.T) {
   253  	assert := asserts.New(t)
   254  	log := zap.S().With("test")
   255  
   256  	// Managed cluster mocks
   257  	mcMocker := gomock.NewController(t)
   258  	mcMock := mocks.NewMockClient(mcMocker)
   259  
   260  	// Admin cluster mocks
   261  	adminMocker := gomock.NewController(t)
   262  	adminMock := mocks.NewMockClient(adminMocker)
   263  
   264  	// Test data
   265  	testMCSecret, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
   266  	if err != nil {
   267  		assert.NoError(err, "failed to read sample data for MultiClusterSecret")
   268  	}
   269  	testMCSecretOrphan, err := getSampleMCSecret("testdata/multicluster-secret.yaml")
   270  	if err != nil {
   271  		assert.NoError(err, "failed to read sample data for MultiClusterSecret")
   272  	}
   273  	testMCSecretOrphan.Name = "orphaned-resource"
   274  
   275  	// Admin Cluster - expect call to list MultiClusterSecret objects - return list with one object
   276  	adminMock.EXPECT().
   277  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   278  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   279  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   280  			mcSecretList.Items = append(mcSecretList.Items, testMCSecret)
   281  			return nil
   282  		})
   283  
   284  	// Managed Cluster - expect call to get a MultiClusterSecret from the list returned by the admin cluster
   285  	//                   Return the resource
   286  	mcMock.EXPECT().
   287  		Get(gomock.Any(), types.NamespacedName{Namespace: testMCSecretNamespace, Name: testMCSecretName}, gomock.Not(gomock.Nil()), gomock.Any()).
   288  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, mcSecret *clustersv1alpha1.MultiClusterSecret, opts ...client.GetOption) error {
   289  			testMCSecret.DeepCopyInto(mcSecret)
   290  			return nil
   291  		})
   292  
   293  	// Managed Cluster - expect call to list MultiClusterSecret objects - return list including an orphaned object
   294  	mcMock.EXPECT().
   295  		List(gomock.Any(), &clustersv1alpha1.MultiClusterSecretList{}, gomock.Not(gomock.Nil())).
   296  		DoAndReturn(func(ctx context.Context, mcSecretList *clustersv1alpha1.MultiClusterSecretList, listOptions *client.ListOptions) error {
   297  			assert.Equal(testMCSecretNamespace, listOptions.Namespace, "list request did not have expected namespace")
   298  			mcSecretList.Items = append(mcSecretList.Items, testMCSecret)
   299  			mcSecretList.Items = append(mcSecretList.Items, testMCSecretOrphan)
   300  			return nil
   301  		})
   302  
   303  	// Managed Cluster - expect a call to delete a MultiClusterSecret object
   304  	mcMock.EXPECT().
   305  		Delete(gomock.Any(), gomock.Eq(&testMCSecretOrphan), gomock.Any()).
   306  		Return(nil)
   307  
   308  	// Make the request
   309  	s := &Syncer{
   310  		AdminClient:        adminMock,
   311  		LocalClient:        mcMock,
   312  		Log:                log,
   313  		ManagedClusterName: testClusterName,
   314  		Context:            context.TODO(),
   315  	}
   316  	err = s.syncMCSecretObjects(testMCSecretNamespace)
   317  
   318  	// Validate the results
   319  	adminMocker.Finish()
   320  	mcMocker.Finish()
   321  	assert.NoError(err)
   322  }
   323  
   324  // getSampleMCSecret creates and returns a sample MultiClusterSecret used in tests
   325  func getSampleMCSecret(filePath string) (clustersv1alpha1.MultiClusterSecret, error) {
   326  	mcSecret := clustersv1alpha1.MultiClusterSecret{}
   327  	sampleSecretFile, err := filepath.Abs(filePath)
   328  	if err != nil {
   329  		return mcSecret, err
   330  	}
   331  
   332  	rawMcSecret, err := clusterstest.ReadYaml2Json(sampleSecretFile)
   333  	if err != nil {
   334  		return mcSecret, err
   335  	}
   336  
   337  	err = json.Unmarshal(rawMcSecret, &mcSecret)
   338  	return mcSecret, err
   339  }