github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_project_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 vzstring "github.com/verrazzano/verrazzano/pkg/string" 19 "go.uber.org/zap" 20 corev1 "k8s.io/api/core/v1" 21 "k8s.io/apimachinery/pkg/api/errors" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/runtime/schema" 24 "k8s.io/apimachinery/pkg/types" 25 "sigs.k8s.io/controller-runtime/pkg/client" 26 ) 27 28 var testLabels = map[string]string{"label1": "test1", "label2": "test2"} 29 var testAnnotations = map[string]string{"annot1": "test1", "annot2": "test2"} 30 31 var testNamespace1 = clustersv1alpha1.NamespaceTemplate{ 32 Metadata: metav1.ObjectMeta{ 33 Name: "newNS1", 34 Labels: testLabels, 35 Annotations: testAnnotations, 36 }, 37 } 38 39 var testNamespace2 = clustersv1alpha1.NamespaceTemplate{ 40 Metadata: metav1.ObjectMeta{ 41 Name: "newNS2", 42 Labels: testLabels, 43 Annotations: testAnnotations, 44 }, 45 } 46 47 var testNamespace3 = clustersv1alpha1.NamespaceTemplate{ 48 Metadata: metav1.ObjectMeta{ 49 Name: "newNS3", 50 Labels: testLabels, 51 Annotations: testAnnotations, 52 }, 53 } 54 55 var testNamespace4 = clustersv1alpha1.NamespaceTemplate{ 56 Metadata: metav1.ObjectMeta{ 57 Name: "newNS4", 58 Labels: testLabels, 59 }, 60 } 61 62 // TestSyncer_syncVerrazzanoProjects tests the synchronization method for the following use case. 63 // GIVEN a request to sync VerrazzanoProject objects 64 // WHEN the a new object exists 65 // THEN ensure that the VerrazzanoProject is created. 66 func TestSyncer_syncVerrazzanoProjects(t *testing.T) { 67 const existingVP = "existingVP" 68 newNamespaces := []clustersv1alpha1.NamespaceTemplate{testNamespace1, testNamespace2} 69 70 type fields struct { 71 vpNamespace string 72 vpName string 73 nsList []clustersv1alpha1.NamespaceTemplate 74 clusters []clustersv1alpha1.Cluster 75 } 76 tests := []struct { 77 name string 78 fields fields 79 wantErr bool 80 }{ 81 { 82 "Update VP", 83 fields{ 84 constants.VerrazzanoMultiClusterNamespace, 85 existingVP, 86 newNamespaces, 87 []clustersv1alpha1.Cluster{{Name: testClusterName}}, 88 }, 89 false, 90 }, 91 { 92 "Create VP", 93 fields{ 94 constants.VerrazzanoMultiClusterNamespace, 95 "newVP", 96 newNamespaces, 97 []clustersv1alpha1.Cluster{{Name: testClusterName}}, 98 }, 99 false, 100 }, 101 } 102 for _, tt := range tests { 103 t.Run(tt.name, func(t *testing.T) { 104 assert := asserts.New(t) 105 log := zap.S().With("test") 106 107 // Managed cluster mocks 108 localMocker := gomock.NewController(t) 109 localMock := mocks.NewMockClient(localMocker) 110 111 // Admin cluster mocks 112 adminMocker := gomock.NewController(t) 113 adminMock := mocks.NewMockClient(adminMocker) 114 115 // Test data 116 testProj, err := getTestVerrazzanoProject(tt.fields.vpNamespace, tt.fields.vpName, tt.fields.nsList, tt.fields.clusters) 117 assert.NoError(err, "failed to get sample project") 118 119 // Admin Cluster - expect call to list VerrazzanoProject objects - return list with one object 120 adminMock.EXPECT(). 121 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 122 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, listOptions *client.ListOptions) error { 123 assert.Equal(constants.VerrazzanoMultiClusterNamespace, listOptions.Namespace) 124 list.Items = append(list.Items, testProj) 125 return nil 126 }) 127 128 if tt.fields.vpName == existingVP { 129 // Managed Cluster - expect call to get VerrazzanoProject 130 localMock.EXPECT(). 131 Get(gomock.Any(), types.NamespacedName{Namespace: tt.fields.vpNamespace, Name: tt.fields.vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 132 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.GetOption) error { 133 vp.Namespace = tt.fields.vpNamespace 134 vp.Name = tt.fields.vpName 135 vp.Spec.Template.Namespaces = []clustersv1alpha1.NamespaceTemplate{testNamespace1, testNamespace2, testNamespace3} 136 vp.Spec.Placement.Clusters = []clustersv1alpha1.Cluster{{Name: testClusterName}} 137 return nil 138 }) 139 140 // Managed Cluster - expect call to update a VerrazzanoProject 141 localMock.EXPECT(). 142 Update(gomock.Any(), gomock.Any()). 143 DoAndReturn(func(ctx context.Context, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.UpdateOption) error { 144 assert.Equal(tt.fields.vpNamespace, vp.Namespace, "VerrazzanoProject namespace did not match") 145 assert.Equal(tt.fields.vpName, vp.Name, "VerrazzanoProject name did not match") 146 assert.ElementsMatch(tt.fields.nsList, vp.Spec.Template.Namespaces) 147 return nil 148 }) 149 } else { 150 // Managed Cluster - expect call to get VerrazzanoProject 151 localMock.EXPECT(). 152 Get(gomock.Any(), types.NamespacedName{Namespace: tt.fields.vpNamespace, Name: tt.fields.vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 153 Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "VerrazzanoProject"}, tt.fields.vpName)) 154 155 // Managed Cluster - expect call to create a VerrazzanoProject 156 localMock.EXPECT(). 157 Create(gomock.Any(), gomock.Any()). 158 DoAndReturn(func(ctx context.Context, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.CreateOption) error { 159 assert.Equal(tt.fields.vpNamespace, vp.Namespace, "VerrazzanoProject namespace did not match") 160 assert.Equal(tt.fields.vpName, vp.Name, "VerrazzanoProject name did not match") 161 assert.ElementsMatch(tt.fields.nsList, vp.Spec.Template.Namespaces) 162 return nil 163 }) 164 } 165 166 // Managed Cluster - expect call to list VerrazzanoProject objects on the local cluster 167 localMock.EXPECT(). 168 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 169 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, listOptions *client.ListOptions) error { 170 assert.Equal(constants.VerrazzanoMultiClusterNamespace, listOptions.Namespace) 171 list.Items = append(list.Items, testProj) 172 return nil 173 }) 174 175 // Managed cluster - expect call to list Namespace objects - return list defined for this test run 176 localMock.EXPECT(). 177 List(gomock.Any(), &corev1.NamespaceList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 178 DoAndReturn(func(ctx context.Context, list *corev1.NamespaceList, listOptions *client.ListOptions) error { 179 for _, namespace := range tt.fields.nsList { 180 list.Items = append(list.Items, corev1.Namespace{ 181 ObjectMeta: namespace.Metadata, 182 Spec: namespace.Spec, 183 }) 184 } 185 return nil 186 }) 187 188 // Make the request 189 s := &Syncer{ 190 AdminClient: adminMock, 191 LocalClient: localMock, 192 Log: log, 193 ManagedClusterName: testClusterName, 194 Context: context.TODO(), 195 } 196 err = s.syncVerrazzanoProjects() 197 198 // Validate the results 199 adminMocker.Finish() 200 localMocker.Finish() 201 202 if (err != nil) != tt.wantErr { 203 t.Errorf("syncVerrazzanoProjects() error = %v, wantErr %v", err, tt.wantErr) 204 } 205 206 // Validate the namespace list that resulted from processing the VerrazzanoProject objects 207 assert.Equal(len(tt.fields.nsList), len(s.ProjectNamespaces), "number of expected namespaces did not match") 208 for _, namespace := range tt.fields.nsList { 209 assert.True(vzstring.SliceContainsString(s.ProjectNamespaces, namespace.Metadata.Name), "expected namespace not being watched") 210 } 211 }) 212 } 213 } 214 215 // TestDeleteVerrazzanoProject tests the synchronization method for the following use case. 216 // GIVEN a request to sync VerrazzanoProject objects 217 // WHEN the object exists on the local cluster but not on the admin cluster 218 // THEN ensure that the VerrazzanoProject is deleted. 219 func TestDeleteVerrazzanoProject(t *testing.T) { 220 type fields struct { 221 vpNamespace string 222 vpName string 223 nsList []clustersv1alpha1.NamespaceTemplate 224 clusters []clustersv1alpha1.Cluster 225 } 226 tests := []struct { 227 name string 228 fields fields 229 wantErr bool 230 }{ 231 { 232 "Orphaned VP", 233 fields{ 234 constants.VerrazzanoMultiClusterNamespace, 235 "TestVP", 236 []clustersv1alpha1.NamespaceTemplate{testNamespace1}, 237 []clustersv1alpha1.Cluster{{Name: testClusterName}}, 238 }, 239 false, 240 }, 241 } 242 for _, tt := range tests { 243 t.Run(tt.name, func(t *testing.T) { 244 assert := asserts.New(t) 245 log := zap.S().With("test") 246 247 // Managed cluster mocks 248 localMocker := gomock.NewController(t) 249 localMock := mocks.NewMockClient(localMocker) 250 251 // Admin cluster mocks 252 adminMocker := gomock.NewController(t) 253 adminMock := mocks.NewMockClient(adminMocker) 254 255 // Test data 256 testProj, err := getTestVerrazzanoProject(tt.fields.vpNamespace, tt.fields.vpName, tt.fields.nsList, tt.fields.clusters) 257 assert.NoError(err, "failed to get sample project") 258 testProjOrphan, err := getTestVerrazzanoProject(tt.fields.vpNamespace, tt.fields.vpName+"-orphan", tt.fields.nsList, tt.fields.clusters) 259 assert.NoError(err, "failed to get sample project") 260 261 // Admin Cluster - expect call to list VerrazzanoProject objects - return list with one object 262 adminMock.EXPECT(). 263 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 264 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, listOptions *client.ListOptions) error { 265 assert.Equal(constants.VerrazzanoMultiClusterNamespace, listOptions.Namespace) 266 list.Items = append(list.Items, testProj) 267 return nil 268 }) 269 270 // Managed Cluster - expect call to get VerrazzanoProject 271 localMock.EXPECT(). 272 Get(gomock.Any(), types.NamespacedName{Namespace: tt.fields.vpNamespace, Name: tt.fields.vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 273 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.GetOption) error { 274 vp.Namespace = tt.fields.vpNamespace 275 vp.Name = tt.fields.vpName 276 vp.Spec.Template.Namespaces = tt.fields.nsList 277 vp.Spec.Placement.Clusters = tt.fields.clusters 278 vp.Labels = testProj.Labels 279 vp.Annotations = testProj.Annotations 280 return nil 281 }) 282 283 // Managed Cluster - expect call to list VerrazzanoProject objects on the local cluster, return an object that 284 // does not exist on the admin cluster 285 localMock.EXPECT(). 286 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 287 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, opts ...*client.ListOptions) error { 288 list.Items = append(list.Items, testProj) 289 list.Items = append(list.Items, testProjOrphan) 290 return nil 291 }) 292 293 // Managed cluster - expect call to list Namespace objects - return list defined for this test run 294 localMock.EXPECT(). 295 List(gomock.Any(), &corev1.NamespaceList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 296 DoAndReturn(func(ctx context.Context, list *corev1.NamespaceList, listOptions *client.ListOptions) error { 297 for _, namespace := range tt.fields.nsList { 298 list.Items = append(list.Items, corev1.Namespace{ 299 ObjectMeta: namespace.Metadata, 300 Spec: namespace.Spec, 301 }) 302 } 303 return nil 304 }) 305 306 // Managed Cluster - expect a call to delete a VerrazzanoProject object 307 localMock.EXPECT(). 308 Delete(gomock.Any(), gomock.Eq(&testProjOrphan), gomock.Any()). 309 Return(nil) 310 311 // Make the request 312 s := &Syncer{ 313 AdminClient: adminMock, 314 LocalClient: localMock, 315 Log: log, 316 ManagedClusterName: testClusterName, 317 Context: context.TODO(), 318 } 319 err = s.syncVerrazzanoProjects() 320 321 // Validate the results 322 adminMocker.Finish() 323 localMocker.Finish() 324 325 if (err != nil) != tt.wantErr { 326 t.Errorf("syncVerrazzanoProjects() error = %v, wantErr %v", err, tt.wantErr) 327 } 328 329 // Validate the namespace list that resulted from processing the VerrazzanoProject objects 330 assert.Equal(len(tt.fields.nsList), len(s.ProjectNamespaces), "number of expected namespaces did not match") 331 for _, namespace := range tt.fields.nsList { 332 assert.True(vzstring.SliceContainsString(s.ProjectNamespaces, namespace.Metadata.Name), "expected namespace not being watched") 333 } 334 }) 335 } 336 } 337 338 // TestVerrazzanoProjectMulti tests the synchronization method for the following use case. 339 // GIVEN a request to sync multiple VerrazzanoProject objects 340 // WHEN the a new object exists 341 // THEN ensure that the list of namespaces to watch is correct 342 func TestVerrazzanoProjectMulti(t *testing.T) { 343 type fields struct { 344 vpNamespace string 345 vpName string 346 nsList []clustersv1alpha1.NamespaceTemplate 347 clusters []clustersv1alpha1.Cluster 348 } 349 tests := []struct { 350 name string 351 vp1Fields fields 352 vp2Fields fields 353 expectedNamespaces int 354 wantErr bool 355 }{ 356 { 357 "TwoVP", 358 fields{ 359 constants.VerrazzanoMultiClusterNamespace, 360 "newVP", 361 []clustersv1alpha1.NamespaceTemplate{testNamespace1, testNamespace2}, 362 []clustersv1alpha1.Cluster{{Name: testClusterName}}, 363 }, 364 fields{ 365 constants.VerrazzanoMultiClusterNamespace, 366 "newVP", 367 []clustersv1alpha1.NamespaceTemplate{testNamespace3, testNamespace4}, 368 []clustersv1alpha1.Cluster{{Name: testClusterName}}, 369 }, 370 4, 371 false, 372 }, 373 } 374 for _, tt := range tests { 375 t.Run(tt.name, func(t *testing.T) { 376 assert := asserts.New(t) 377 log := zap.S().With("test") 378 379 // Managed cluster mocks 380 localMocker := gomock.NewController(t) 381 localMock := mocks.NewMockClient(localMocker) 382 383 // Admin cluster mocks 384 adminMocker := gomock.NewController(t) 385 adminMock := mocks.NewMockClient(adminMocker) 386 387 // Test data 388 testProj1, err := getTestVerrazzanoProject(tt.vp1Fields.vpNamespace, tt.vp1Fields.vpName, tt.vp1Fields.nsList, tt.vp1Fields.clusters) 389 assert.NoError(err, "failed to get sample project") 390 testProj2, err := getTestVerrazzanoProject(tt.vp2Fields.vpNamespace, tt.vp2Fields.vpName, tt.vp2Fields.nsList, tt.vp2Fields.clusters) 391 assert.NoError(err, "failed to get sample project") 392 393 // Admin Cluster - expect call to list VerrazzanoProject objects - return list with two objects 394 adminMock.EXPECT(). 395 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 396 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, opts ...*client.ListOptions) error { 397 list.Items = append(list.Items, testProj1) 398 list.Items = append(list.Items, testProj2) 399 return nil 400 }) 401 402 if tt.vp1Fields.vpNamespace == constants.VerrazzanoMultiClusterNamespace { 403 // Managed Cluster - expect call to get VerrazzanoProject 404 localMock.EXPECT(). 405 Get(gomock.Any(), types.NamespacedName{Namespace: tt.vp1Fields.vpNamespace, Name: tt.vp1Fields.vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 406 Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "VerrazzanoProject"}, tt.vp1Fields.vpName)) 407 408 // Managed Cluster - expect call to create a VerrazzanoProject 409 localMock.EXPECT(). 410 Create(gomock.Any(), gomock.Any()). 411 DoAndReturn(func(ctx context.Context, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.CreateOption) error { 412 assert.Equal(tt.vp1Fields.vpNamespace, vp.Namespace, "VerrazzanoProject namespace did not match") 413 assert.Equal(tt.vp1Fields.vpName, vp.Name, "VerrazzanoProject name did not match") 414 assert.ElementsMatch(tt.vp1Fields.nsList, vp.Spec.Template.Namespaces) 415 return nil 416 }) 417 418 // Managed Cluster - expect call to get VerrazzanoProject 419 localMock.EXPECT(). 420 Get(gomock.Any(), types.NamespacedName{Namespace: tt.vp2Fields.vpNamespace, Name: tt.vp2Fields.vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 421 Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "VerrazzanoProject"}, tt.vp2Fields.vpName)) 422 423 // Managed Cluster - expect call to create a VerrazzanoProject 424 localMock.EXPECT(). 425 Create(gomock.Any(), gomock.Any()). 426 DoAndReturn(func(ctx context.Context, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.CreateOption) error { 427 assert.Equal(tt.vp2Fields.vpNamespace, vp.Namespace, "VerrazzanoProject namespace did not match") 428 assert.Equal(tt.vp2Fields.vpName, vp.Name, "VerrazzanoProject name did not match") 429 assert.ElementsMatch(tt.vp2Fields.nsList, vp.Spec.Template.Namespaces) 430 return nil 431 }) 432 433 // Managed Cluster - expect call to list VerrazzanoProject objects on the local cluster 434 localMock.EXPECT(). 435 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 436 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, opts ...*client.ListOptions) error { 437 list.Items = append(list.Items, testProj1) 438 list.Items = append(list.Items, testProj2) 439 return nil 440 }) 441 442 // Managed cluster - expect call to list Namespace objects - return list defined for this test run 443 localMock.EXPECT(). 444 List(gomock.Any(), &corev1.NamespaceList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 445 DoAndReturn(func(ctx context.Context, list *corev1.NamespaceList, listOptions *client.ListOptions) error { 446 for _, namespace := range tt.vp1Fields.nsList { 447 list.Items = append(list.Items, corev1.Namespace{ 448 ObjectMeta: namespace.Metadata, 449 Spec: namespace.Spec, 450 }) 451 } 452 for _, namespace := range tt.vp2Fields.nsList { 453 list.Items = append(list.Items, corev1.Namespace{ 454 ObjectMeta: namespace.Metadata, 455 Spec: namespace.Spec, 456 }) 457 } 458 return nil 459 }) 460 461 } 462 463 // Make the request 464 s := &Syncer{ 465 AdminClient: adminMock, 466 LocalClient: localMock, 467 Log: log, 468 ManagedClusterName: testClusterName, 469 Context: context.TODO(), 470 } 471 err = s.syncVerrazzanoProjects() 472 473 // Validate the results 474 adminMocker.Finish() 475 localMocker.Finish() 476 477 if (err != nil) != tt.wantErr { 478 t.Errorf("syncVerrazzanoProjects() error = %v, wantErr %v", err, tt.wantErr) 479 } 480 481 // Validate the namespace list that resulted from processing the VerrazzanoProject objects 482 assert.Equal(tt.expectedNamespaces, len(s.ProjectNamespaces), "number of expected namespaces did not match") 483 for _, namespace := range tt.vp1Fields.nsList { 484 assert.True(vzstring.SliceContainsString(s.ProjectNamespaces, namespace.Metadata.Name), "expected namespace not being watched") 485 } 486 for _, namespace := range tt.vp2Fields.nsList { 487 assert.True(vzstring.SliceContainsString(s.ProjectNamespaces, namespace.Metadata.Name), "expected namespace not being watched") 488 } 489 }) 490 } 491 } 492 493 // TestRemovePlacementVerrazzanoProject tests the synchronization method for the following use case. 494 // GIVEN a request to sync VerrazzanoProject objects 495 // WHEN the object exists on the local cluster but is no longer placed on the local cluster 496 // THEN ensure that the VerrazzanoProject is deleted. 497 func TestRemovePlacementVerrazzanoProject(t *testing.T) { 498 assert := asserts.New(t) 499 log := zap.S().With("test") 500 vpName := "test" 501 vpNamespace := constants.VerrazzanoMultiClusterNamespace 502 nsList := []clustersv1alpha1.NamespaceTemplate{testNamespace1, testNamespace2} 503 clusters := []clustersv1alpha1.Cluster{{Name: testClusterName}} 504 clustersUpdated := []clustersv1alpha1.Cluster{{Name: constants.DefaultClusterName}} 505 506 // Managed cluster mocks 507 localMocker := gomock.NewController(t) 508 localMock := mocks.NewMockClient(localMocker) 509 510 // Admin cluster mocks 511 adminMocker := gomock.NewController(t) 512 adminMock := mocks.NewMockClient(adminMocker) 513 514 // Test data 515 testProj, err := getTestVerrazzanoProject(vpNamespace, vpName, nsList, clusters) 516 assert.NoError(err, "failed to get sample project") 517 testProjUpdated, err := getTestVerrazzanoProject(vpNamespace, vpName, nsList, clustersUpdated) 518 assert.NoError(err, "failed to get sample project") 519 520 // Admin Cluster - expect call to list VerrazzanoProject objects - return list with one object 521 adminMock.EXPECT(). 522 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 523 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, listOptions *client.ListOptions) error { 524 assert.Equal(constants.VerrazzanoMultiClusterNamespace, listOptions.Namespace) 525 list.Items = append(list.Items, testProjUpdated) 526 return nil 527 }) 528 529 // Managed Cluster - expect call to get VerrazzanoProject 530 localMock.EXPECT(). 531 Get(gomock.Any(), types.NamespacedName{Namespace: vpNamespace, Name: vpName}, gomock.Not(gomock.Nil()), gomock.Any()). 532 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vp *clustersv1alpha1.VerrazzanoProject, opts ...client.GetOption) error { 533 testProj.DeepCopyInto(vp) 534 return nil 535 }) 536 537 // Managed cluster - expect call to list Namespace objects - return list defined for this test run 538 localMock.EXPECT(). 539 List(gomock.Any(), &corev1.NamespaceList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 540 DoAndReturn(func(ctx context.Context, list *corev1.NamespaceList, listOptions *client.ListOptions) error { 541 for _, namespace := range nsList { 542 list.Items = append(list.Items, corev1.Namespace{ 543 ObjectMeta: namespace.Metadata, 544 Spec: namespace.Spec, 545 }) 546 } 547 return nil 548 }) 549 550 // Managed Cluster - expect a call to delete a VerrazzanoProject object 551 localMock.EXPECT(). 552 Delete(gomock.Any(), gomock.Eq(&testProj), gomock.Any()). 553 Return(nil) 554 555 // Managed Cluster - expect call to list VerrazzanoProject objects on the local cluster, return an empty list 556 localMock.EXPECT(). 557 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.AssignableToTypeOf(&client.ListOptions{})). 558 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, opts ...*client.ListOptions) error { 559 return nil 560 }) 561 562 // Make the request 563 s := &Syncer{ 564 AdminClient: adminMock, 565 LocalClient: localMock, 566 Log: log, 567 ManagedClusterName: testClusterName, 568 Context: context.TODO(), 569 } 570 err = s.syncVerrazzanoProjects() 571 572 // Validate the results 573 adminMocker.Finish() 574 localMocker.Finish() 575 assert.NoError(err) 576 } 577 578 // getTestVerrazzanoProject creates and returns VerrazzanoProject used in tests 579 func getTestVerrazzanoProject(vpNamespace string, vpName string, nsNames []clustersv1alpha1.NamespaceTemplate, clusters []clustersv1alpha1.Cluster) (clustersv1alpha1.VerrazzanoProject, error) { 580 proj := clustersv1alpha1.VerrazzanoProject{} 581 templateFile, err := filepath.Abs("testdata/verrazzanoproject.yaml") 582 if err != nil { 583 return proj, err 584 } 585 586 // Convert template file to VerrazzanoProject struct 587 rawMcComp, err := clusterstest.ReadYaml2Json(templateFile) 588 if err != nil { 589 return proj, err 590 } 591 err = json.Unmarshal(rawMcComp, &proj) 592 593 // Populate the content 594 proj.Namespace = vpNamespace 595 proj.Name = vpName 596 proj.Spec.Template.Namespaces = nsNames 597 proj.Spec.Placement.Clusters = clusters 598 return proj, err 599 }