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 }