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