github.com/verrazzano/verrazzano@v1.7.0/application-operator/mcagent/mcagent_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 "fmt" 9 "testing" 10 11 "github.com/golang/mock/gomock" 12 v1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" 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 "github.com/verrazzano/verrazzano/application-operator/controllers/clusters" 17 "github.com/verrazzano/verrazzano/application-operator/mocks" 18 clustersapi "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 19 vzconstants "github.com/verrazzano/verrazzano/pkg/constants" 20 "github.com/verrazzano/verrazzano/pkg/mcconstants" 21 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 22 "go.uber.org/zap" 23 corev1 "k8s.io/api/core/v1" 24 networkingv1 "k8s.io/api/networking/v1" 25 apiextv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 26 "k8s.io/apimachinery/pkg/api/errors" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime/schema" 29 "k8s.io/apimachinery/pkg/types" 30 "k8s.io/apimachinery/pkg/version" 31 "k8s.io/client-go/discovery" 32 fakediscovery "k8s.io/client-go/discovery/fake" 33 clientgotesting "k8s.io/client-go/testing" 34 "sigs.k8s.io/controller-runtime/pkg/client" 35 "sigs.k8s.io/controller-runtime/pkg/reconcile" 36 ) 37 38 var validSecret = corev1.Secret{ 39 ObjectMeta: metav1.ObjectMeta{ 40 Name: constants.MCAgentSecret, 41 Namespace: constants.VerrazzanoSystemNamespace, 42 }, 43 Data: map[string][]byte{constants.ClusterNameData: []byte("cluster1"), mcconstants.KubeconfigKey: []byte("kubeconfig")}, 44 } 45 46 const testManagedPrometheusHost = "prometheus" 47 const testManagedThanosQueryStoreAPIHost = "thanos-query-store.example.com" 48 const testVZVersion = "dummy-verrazzano-version" 49 50 var testK8sVersion = &version.Info{ 51 GitVersion: "v1.26.3", 52 } 53 54 func fakeDiscoveryClientFunc() (discovery.DiscoveryInterface, error) { 55 discoveryClient := fakediscovery.FakeDiscovery{ 56 Fake: &clientgotesting.Fake{}, 57 FakedServerVersion: testK8sVersion, 58 } 59 return &discoveryClient, nil 60 } 61 62 // TestReconcileAgentSecretDeleted tests agent thread when the registration secret is deleted 63 // GIVEN a request to process the agent loop 64 // WHEN the agent secret has been deleted 65 // THEN ensure that there are no calls to get VerrazzanoProject resources 66 func TestReconcileAgentSecretDeleted(t *testing.T) { 67 assert := asserts.New(t) 68 log := zap.S().With("test") 69 70 // Managed cluster mocks 71 mcMocker := gomock.NewController(t) 72 mcMock := mocks.NewMockClient(mcMocker) 73 74 // Admin cluster mocks 75 adminMocker := gomock.NewController(t) 76 adminMock := mocks.NewMockClient(adminMocker) 77 78 // Override createAdminClient to return the mock 79 originalAdminClientFunc := getAdminClientFunc 80 defer func() { 81 getAdminClientFunc = originalAdminClientFunc 82 }() 83 getAdminClientFunc = func(secret *corev1.Secret) (client.Client, error) { 84 return adminMock, nil 85 } 86 87 // Managed Cluster - expect call to get the agent secret. 88 expectAgentSecretNotFound(mcMock) 89 90 // Do not expect any further calls because the agent secret no longer exists 91 92 // Make the request 93 r := &Reconciler{ 94 Client: mcMock, 95 Log: log, 96 Scheme: newTestScheme(), 97 AgentChannel: nil, 98 } 99 100 _, err := r.Reconcile(context.TODO(), reconcile.Request{NamespacedName: types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.MCAgentSecret}}) 101 102 // Validate the results 103 // asserts.Equal(t, false, s.AgentSecretFound) 104 // asserts.Equal(t, false, s.AgentSecretValid) 105 106 adminMocker.Finish() 107 mcMocker.Finish() 108 assert.NoError(err) 109 } 110 111 func expectAgentSecretNotFound(mock *mocks.MockClient) { 112 mock.EXPECT(). 113 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.MCAgentSecret}, gomock.Not(gomock.Nil()), gomock.Any()). 114 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 115 return errors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, name.Name) 116 }) 117 } 118 119 // TestValidateSecret tests secret validation function 120 func TestValidateSecret(t *testing.T) { 121 assert := asserts.New(t) 122 123 // Valid secret 124 err := validateAgentSecret(&validSecret) 125 assert.NoError(err) 126 127 // A secret without a cluster name 128 invalidSecret := validSecret 129 invalidSecret.Data = map[string][]byte{mcconstants.KubeconfigKey: []byte("kubeconfig")} 130 err = validateAgentSecret(&invalidSecret) 131 assert.Error(err) 132 assert.Contains(err.Error(), fmt.Sprintf("missing the required field %s", constants.ClusterNameData)) 133 134 // A secret without a kubeconfig 135 invalidSecret.Data = map[string][]byte{constants.ClusterNameData: []byte("cluster1")} 136 err = validateAgentSecret(&invalidSecret) 137 assert.Error(err) 138 assert.Contains(err.Error(), fmt.Sprintf("missing the required field %s", mcconstants.KubeconfigKey)) 139 } 140 141 // Test_getEnvValue tests getEnvValue 142 // GIVEN a request for a specified ENV name 143 // WHEN the env array contains such an env 144 // THEN returns the env value, empty string if not found 145 func Test_getEnvValue(t *testing.T) { 146 container := corev1.Container{} 147 container.Env = []corev1.EnvVar{} 148 asserts.Equal(t, "", getEnvValue(&[]corev1.Container{container}, registrationSecretVersion), "expected cluster name") 149 container.Env = []corev1.EnvVar{ 150 { 151 Name: registrationSecretVersion, 152 Value: "version1", 153 }, 154 } 155 asserts.Equal(t, "version1", getEnvValue(&[]corev1.Container{container}, registrationSecretVersion), "expected cluster name") 156 container.Env = []corev1.EnvVar{ 157 { 158 Name: "env1", 159 Value: "value1", 160 }, 161 { 162 Name: registrationSecretVersion, 163 Value: "version1", 164 }, 165 } 166 asserts.Equal(t, "version1", getEnvValue(&[]corev1.Container{container}, registrationSecretVersion), "expected cluster name") 167 } 168 169 // Test_getEnvValue tests updateEnvValue 170 // GIVEN a request for a specified ENV name/value 171 // WHEN the env array contains such an env 172 // THEN updates its value, append the env name/value if not found 173 func Test_updateEnvValue(t *testing.T) { 174 var testEnvs []corev1.EnvVar 175 newValue := "version2" 176 newEnvs := updateEnvValue(testEnvs, registrationSecretVersion, newValue) 177 asserts.Equal(t, registrationSecretVersion, newEnvs[0].Name, "expected env") 178 asserts.Equal(t, newValue, newEnvs[0].Value, "expected env value") 179 testEnvs = []corev1.EnvVar{ 180 { 181 Name: registrationSecretVersion, 182 Value: "version1", 183 }, 184 } 185 newValue = "version2" 186 newEnvs = updateEnvValue(testEnvs, registrationSecretVersion, newValue) 187 asserts.Equal(t, registrationSecretVersion, newEnvs[0].Name, "expected env") 188 asserts.Equal(t, newValue, newEnvs[0].Value, "expected env value") 189 testEnvs = []corev1.EnvVar{ 190 { 191 Name: "env1", 192 Value: "value1", 193 }, 194 { 195 Name: registrationSecretVersion, 196 Value: "version1", 197 }, 198 } 199 newEnvs = updateEnvValue(testEnvs, registrationSecretVersion, newValue) 200 asserts.Equal(t, registrationSecretVersion, newEnvs[1].Name, "expected env") 201 asserts.Equal(t, newValue, newEnvs[1].Value, "expected env value") 202 } 203 204 // TestReconcile_AgentSecretPresenceAndValidity 205 // GIVEN a request to reconcile the agent 206 // WHEN no new VerrazzanoProjects resources exists 207 // THEN all the expected K8S calls are made and that there are no calls to sync any multi-cluster resources 208 // for various cases of agent secret presence and validity, and agent state configmap presence 209 func TestReconcile_AgentSecretPresenceAndValidity(t *testing.T) { 210 assert := asserts.New(t) 211 req := reconcile.Request{NamespacedName: types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.MCAgentSecret}} 212 defer setDiscoveryClientFunc(getDiscoveryClientFunc) 213 setDiscoveryClientFunc(fakeDiscoveryClientFunc) 214 type fields struct { 215 AgentSecretFound bool 216 AgentSecretValid bool 217 AgentStateConfigMapFound bool 218 } 219 tests := []struct { 220 name string 221 fields fields 222 wantErr bool 223 }{ 224 {"agent secret found not valid", fields{AgentSecretFound: true, AgentSecretValid: false, AgentStateConfigMapFound: false}, true}, 225 {"agent secret not found - no op expected", fields{AgentSecretFound: false, AgentSecretValid: false, AgentStateConfigMapFound: false}, false}, 226 {"agent secret found and valid but no configmap - should create configmap", fields{AgentSecretFound: true, AgentSecretValid: true, AgentStateConfigMapFound: false}, false}, 227 {"agent secret found and valid and configmap exists", fields{AgentSecretFound: true, AgentSecretValid: true, AgentStateConfigMapFound: true}, false}, 228 } 229 for _, tt := range tests { 230 t.Run(tt.name, func(t *testing.T) { 231 232 // Managed cluster mocks 233 mcMocker := gomock.NewController(t) 234 mcMock := mocks.NewMockClient(mcMocker) 235 236 // admin cluster mocks 237 adminMocker := gomock.NewController(t) 238 adminMock := mocks.NewMockClient(adminMocker) 239 adminStatusMock := mocks.NewMockStatusWriter(adminMocker) 240 241 // Override createAdminClient to return the mock 242 originalAdminClientFunc := getAdminClientFunc 243 defer func() { 244 getAdminClientFunc = originalAdminClientFunc 245 }() 246 getAdminClientFunc = func(secret *corev1.Secret) (client.Client, error) { 247 return adminMock, nil 248 } 249 250 secretToUse := validSecret 251 if !tt.fields.AgentSecretValid { 252 secretToUse.Data = map[string][]byte{} 253 } 254 if tt.fields.AgentSecretFound { 255 expectAgentSecretFound(mcMock, secretToUse) 256 } else { 257 expectAgentSecretNotFound(mcMock) 258 } 259 if tt.fields.AgentSecretFound && tt.fields.AgentSecretValid { 260 expectGetMCNamespace(mcMock) 261 clusterName := string(secretToUse.Data[constants.ClusterNameData]) 262 if tt.fields.AgentStateConfigMapFound { 263 // Managed Cluster - expect call to get the agent state config map at the beginning of reconcile 264 expectAgentStateConfigMapFound(mcMock, clusterName) 265 } else { 266 expectAgentStateConfigMapNotFound(mcMock) 267 expectAgentStateConfigMapCreated(mcMock, clusterName) 268 } 269 expectAllCallsNoApps(adminMock, mcMock, adminStatusMock, clusterName, assert) 270 } 271 r := Reconciler{ 272 Client: mcMock, 273 Scheme: newTestScheme(), 274 Log: zap.S(), 275 } 276 277 _, err := r.Reconcile(context.TODO(), req) 278 if !tt.wantErr { 279 asserts.NoError(t, err) 280 } else { 281 asserts.NotNil(t, err) 282 } 283 adminMocker.Finish() 284 mcMocker.Finish() 285 }) 286 } 287 } 288 289 // Test_discardStatusMessages tests the discardStatusMessages function 290 func Test_discardStatusMessages(t *testing.T) { 291 statusUpdateChan := make(chan clusters.StatusUpdateMessage, 12) 292 for i := 0; i < 10; i++ { 293 statusUpdateChan <- clusters.StatusUpdateMessage{} 294 } 295 discardStatusMessages(statusUpdateChan) 296 297 asserts.Equal(t, 0, len(statusUpdateChan)) 298 } 299 300 func expectAdminVMCStatusUpdateFailure(adminMock *mocks.MockClient, vmcName types.NamespacedName, adminStatusMock *mocks.MockStatusWriter, assert *asserts.Assertions) { 301 adminMock.EXPECT(). 302 Get(gomock.Any(), vmcName, gomock.Not(gomock.Nil()), gomock.Any()). 303 Return(errors.NewNotFound(schema.GroupResource{Group: "clusters.verrazzano.io", Resource: "VerrazzanoManagedCluster"}, vmcName.Name)) 304 } 305 306 func expectAdminVMCStatusUpdateSuccess(adminMock *mocks.MockClient, vmcName types.NamespacedName, adminStatusMock *mocks.MockStatusWriter, assert *asserts.Assertions) { 307 expectGetVMC(adminMock, vmcName, "") 308 adminMock.EXPECT().Status().Return(adminStatusMock) 309 adminStatusMock.EXPECT(). 310 Update(gomock.Any(), gomock.AssignableToTypeOf(&clustersapi.VerrazzanoManagedCluster{}), gomock.Any()). 311 DoAndReturn(func(ctx context.Context, vmc *clustersapi.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 312 assert.Equal(vmcName.Namespace, vmc.Namespace) 313 assert.Equal(vmcName.Name, vmc.Name) 314 assert.NotNil(vmc.Status) 315 assert.NotNil(vmc.Status.LastAgentConnectTime) 316 assert.NotNil(vmc.Status.APIUrl) 317 assert.Equal(testManagedPrometheusHost, vmc.Status.PrometheusHost) 318 assert.Equal(testManagedThanosQueryStoreAPIHost, vmc.Status.ThanosQueryStore) 319 assert.Equal(testK8sVersion.String(), vmc.Status.Kubernetes.Version) 320 assert.Equal(testVZVersion, vmc.Status.Verrazzano.Version) 321 return nil 322 }) 323 } 324 325 func expectCASyncSuccess(localMock, adminMock *mocks.MockClient, assert *asserts.Assertions, testClusterName string) { 326 localRegistrationSecret := types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.MCRegistrationSecret} 327 adminCASecret := types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: constants.VerrazzanoLocalCABundleSecret} 328 adminRegSecret := types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: getRegistrationSecretName(testClusterName)} 329 localIngressTLSSecret := types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: vzconstants.VerrazzanoIngressTLSSecret} 330 331 // Managed Cluster - expect call to get the verrazzano-tls-ca. Return not found since it 332 // is ok for it to be not present. 333 localMock.EXPECT(). 334 Get(gomock.Any(), types.NamespacedName{Namespace: vzconstants.VerrazzanoSystemNamespace, Name: vzconstants.PrivateCABundle}, gomock.Not(gomock.Nil()), gomock.Any()). 335 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 336 return errors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, name.Name) 337 }) 338 adminMock.EXPECT(). 339 Get(gomock.Any(), adminCASecret, gomock.Not(gomock.Nil()), gomock.Any()). 340 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 341 secret.Name = adminCASecret.Name 342 secret.Namespace = adminCASecret.Namespace 343 return nil 344 }) 345 adminMock.EXPECT(). 346 Get(gomock.Any(), adminRegSecret, gomock.Not(gomock.Nil()), gomock.Any()). 347 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 348 secret.Name = adminRegSecret.Name 349 secret.Namespace = adminRegSecret.Namespace 350 return nil 351 }) 352 localMock.EXPECT(). 353 Get(gomock.Any(), localRegistrationSecret, gomock.Not(gomock.Nil()), gomock.Any()). 354 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 355 secret.Name = localRegistrationSecret.Name 356 secret.Namespace = localRegistrationSecret.Namespace 357 return nil 358 }) 359 localMock.EXPECT(). 360 Get(gomock.Any(), localIngressTLSSecret, gomock.Not(gomock.Nil()), gomock.Any()). 361 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 362 secret.Name = localIngressTLSSecret.Name 363 secret.Namespace = localIngressTLSSecret.Namespace 364 secret.Data = map[string][]byte{mcconstants.CaCrtKey: []byte("somekey")} 365 return nil 366 }) 367 368 vmcName := types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: testClusterName} 369 clusterCASecret := "clusterCASecret" 370 expectGetVMC(adminMock, vmcName, clusterCASecret) 371 adminClusterCASecret := types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: clusterCASecret} 372 adminMock.EXPECT(). 373 Get(gomock.Any(), adminClusterCASecret, gomock.Not(gomock.Nil()), gomock.Any()). 374 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 375 secret.Name = adminClusterCASecret.Name 376 secret.Namespace = adminClusterCASecret.Namespace 377 // make the value equal to the managed cluster CA - we are not looking for updates to this secret 378 secret.Data = map[string][]byte{keyCaCrtNoDot: []byte("somekey")} 379 return nil 380 }) 381 } 382 383 // TestSyncer_updateVMCStatus tests updateVMCStatus method 384 // GIVEN updateVMCStatus is called 385 // WHEN the status update of VMC on admin cluster succeeds 386 // THEN updateVMCStatus returns nil error 387 // GIVEN updateVMCStatus is called 388 // WHEN the status update of VMC on admin cluster fails 389 // THEN updateVMCStatus returns a non-nil error 390 func TestSyncer_updateVMCStatus(t *testing.T) { 391 assert := asserts.New(t) 392 log := zap.S().With("test") 393 394 // Admin cluster mocks 395 adminMocker := gomock.NewController(t) 396 adminMock := mocks.NewMockClient(adminMocker) 397 adminStatusMock := mocks.NewMockStatusWriter(adminMocker) 398 localClientMock := mocks.NewMockClient(adminMocker) 399 400 fakeDiscoveryClient, err := fakeDiscoveryClientFunc() 401 assert.Nil(err) 402 403 s := &Syncer{ 404 AdminClient: adminMock, 405 Log: log, 406 ManagedClusterName: "my-test-cluster", 407 LocalClient: localClientMock, 408 LocalDiscoveryClient: fakeDiscoveryClient, 409 } 410 vmcName := types.NamespacedName{Name: s.ManagedClusterName, Namespace: constants.VerrazzanoMultiClusterNamespace} 411 412 expectGetAPIServerURLCalled(localClientMock) 413 expectGetPrometheusHostCalled(localClientMock) 414 expectGetThanosQueryHostCalled(localClientMock) 415 expectGetWorkloadVZVersionCalled(localClientMock) 416 // Mock the success of status updates and assert that updateVMCStatus returns nil error 417 expectAdminVMCStatusUpdateSuccess(adminMock, vmcName, adminStatusMock, assert) 418 assert.Nil(s.updateVMCStatus()) 419 420 // Mock the failure of status updates and assert that updateVMCStatus returns non-nil error 421 expectAdminVMCStatusUpdateFailure(adminMock, vmcName, adminStatusMock, assert) 422 assert.NotNil(s.updateVMCStatus()) 423 424 adminMocker.Finish() 425 } 426 427 func expectGetWorkloadVZVersionCalled(mock *mocks.MockClient) { 428 // Expect a call to list the Verrazzanos. 429 mock.EXPECT(). 430 List(gomock.Any(), &v1beta1.VerrazzanoList{}, gomock.Any()). 431 DoAndReturn(func(ctx context.Context, vzList *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 432 vzList.Items = []v1beta1.Verrazzano{ 433 { 434 Status: v1beta1.VerrazzanoStatus{ 435 Version: testVZVersion, 436 }, 437 }, 438 } 439 return nil 440 }) 441 } 442 443 func expectGetAPIServerURLCalled(mock *mocks.MockClient) { 444 // Expect a call to get the console ingress and return the ingress. 445 mock.EXPECT(). 446 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.VzConsoleIngress}, gomock.Not(gomock.Nil()), gomock.Any()). 447 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 448 ingress.TypeMeta = metav1.TypeMeta{ 449 APIVersion: "networking.k8s.io/v1", 450 Kind: "ingress"} 451 ingress.ObjectMeta = metav1.ObjectMeta{ 452 Namespace: name.Namespace, 453 Name: name.Name} 454 ingress.Spec.Rules = []networkingv1.IngressRule{{ 455 Host: "console", 456 }} 457 return nil 458 }) 459 } 460 461 func expectGetPrometheusHostCalled(mock *mocks.MockClient) { 462 // Expect a call to get the prometheus ingress and return the host. 463 expectGetIngress(mock, constants.VerrazzanoSystemNamespace, constants.VzPrometheusIngress, testManagedPrometheusHost) 464 } 465 466 func expectGetThanosQueryHostCalled(mock *mocks.MockClient) { 467 // Expect a call to get the Thanos query ingress and return the host. 468 expectGetIngress(mock, constants.VerrazzanoSystemNamespace, vzconstants.ThanosQueryStoreIngress, testManagedThanosQueryStoreAPIHost) 469 } 470 471 // Expects a call to get an ingress with the given name and namespace, and returns an ingress with the specified 472 // ingressHost 473 func expectGetIngress(mock *mocks.MockClient, ingressNamespace string, ingressName string, ingressHost string) { 474 mock.EXPECT(). 475 Get(gomock.Any(), types.NamespacedName{Namespace: ingressNamespace, Name: ingressName}, gomock.Not(gomock.Nil()), gomock.Any()). 476 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 477 ingress.TypeMeta = metav1.TypeMeta{ 478 APIVersion: "networking.k8s.io/v1", 479 Kind: "ingress"} 480 ingress.ObjectMeta = metav1.ObjectMeta{ 481 Namespace: name.Namespace, 482 Name: name.Name} 483 ingress.Spec.Rules = []networkingv1.IngressRule{{ 484 Host: ingressHost, 485 }} 486 return nil 487 }) 488 } 489 490 func expectGetManifestSecretNotFound(mock *mocks.MockClient, clusterName string) { 491 mock.EXPECT(). 492 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: getManifestSecretName(clusterName)}, gomock.Not(gomock.Nil()), gomock.Any()). 493 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 494 return errors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, name.Name) 495 }) 496 } 497 498 func expectListNamespacesReturnNone(mock *mocks.MockClient) { 499 mock.EXPECT(). 500 List(gomock.Any(), &corev1.NamespaceList{}, gomock.Any()). 501 DoAndReturn(func(ctx context.Context, list *corev1.NamespaceList, opts ...client.ListOption) error { 502 return nil 503 }) 504 505 } 506 507 func expectListProjectsReturnNone(mock *mocks.MockClient) { 508 mock.EXPECT(). 509 List(gomock.Any(), &clustersv1alpha1.VerrazzanoProjectList{}, gomock.Any()). 510 DoAndReturn(func(ctx context.Context, list *clustersv1alpha1.VerrazzanoProjectList, opts ...client.ListOption) error { 511 return nil 512 }) 513 } 514 515 func expectAgentStateConfigMapNotFound(mock *mocks.MockClient) { 516 // called once for reading the existing data, and another time during update 517 mock.EXPECT(). 518 Get(gomock.Any(), mcAgentStateConfigMapName, gomock.Not(gomock.Nil()), gomock.Any()). 519 DoAndReturn(func(ctx context.Context, name types.NamespacedName, cm *corev1.ConfigMap, opts ...client.GetOption) error { 520 return errors.NewNotFound(schema.GroupResource{Group: "", Resource: "ConfigMap"}, name.Name) 521 }) 522 } 523 524 func expectAgentStateConfigMapFound(mock *mocks.MockClient, clusterName string) { 525 // called at the beginning for reading the existing data, and another get to determine if update is needed 526 mock.EXPECT(). 527 Get(gomock.Any(), mcAgentStateConfigMapName, gomock.Not(gomock.Nil()), gomock.Any()). 528 DoAndReturn(func(ctx context.Context, name types.NamespacedName, cm *corev1.ConfigMap, opts ...client.GetOption) error { 529 cm.Name = mcAgentStateConfigMapName.Name 530 cm.Namespace = mcAgentStateConfigMapName.Namespace 531 cm.Data = map[string]string{} 532 if clusterName != "" { 533 cm.Data[constants.ClusterNameData] = clusterName 534 } 535 return nil 536 }).Times(2) 537 } 538 539 func expectGetMCNamespace(mock *mocks.MockClient) { 540 // expect a get for the verrazzano-mc namespace 541 mock.EXPECT(). 542 Get(gomock.Any(), types.NamespacedName{Name: mcAgentStateConfigMapName.Namespace}, gomock.Not(gomock.Nil()), gomock.Any()). 543 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ns *corev1.Namespace, opts ...client.GetOption) error { 544 ns.Name = name.Name 545 return nil 546 }) 547 } 548 549 func expectAgentStateConfigMapCreated(mock *mocks.MockClient, clusterName string) { 550 // expect a get where the configmap is not found 551 expectAgentStateConfigMapNotFound(mock) 552 553 // expect a create of the config map 554 mock.EXPECT(). 555 Create(gomock.Any(), gomock.AssignableToTypeOf(&corev1.ConfigMap{})). 556 DoAndReturn(func(ctx context.Context, cm *corev1.ConfigMap, opts ...client.GetOption) error { 557 cm.Name = mcAgentStateConfigMapName.Name 558 cm.Namespace = mcAgentStateConfigMapName.Namespace 559 if clusterName != "" { 560 cm.Data = map[string]string{constants.ClusterNameData: clusterName} 561 } 562 return nil 563 }) 564 } 565 566 func expectGetVMC(mock *mocks.MockClient, vmcName types.NamespacedName, caSecretName string) { 567 mock.EXPECT(). 568 Get(gomock.Any(), vmcName, gomock.Not(gomock.Nil()), gomock.Any()). 569 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vmc *clustersapi.VerrazzanoManagedCluster, opts ...client.GetOption) error { 570 vmc.DeletionTimestamp = nil 571 vmc.Name = vmcName.Name 572 vmc.Namespace = vmcName.Namespace 573 if caSecretName != "" { 574 vmc.Spec.CASecret = caSecretName 575 } 576 return nil 577 }) 578 } 579 580 func expectAgentSecretFound(mock *mocks.MockClient, secretToUse corev1.Secret) { 581 mock.EXPECT(). 582 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.MCAgentSecret}, gomock.Not(gomock.Nil()), gomock.Any()). 583 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 584 secret.ObjectMeta = secretToUse.ObjectMeta 585 secret.Data = secretToUse.Data 586 return nil 587 }) 588 } 589 590 func expectServiceAndPodMonitorsList(mock *mocks.MockClient, assert *asserts.Assertions) { 591 // Managed Cluster, expect call to list service monitors, return an empty list 592 mock.EXPECT(). 593 List(gomock.Any(), &v1.ServiceMonitorList{}, gomock.Any()). 594 DoAndReturn(func(ctx context.Context, list *v1.ServiceMonitorList, opts ...client.ListOption) error { 595 return nil 596 }) 597 // Managed Cluster, expect call to list pod monitors, return an empty list 598 mock.EXPECT(). 599 List(gomock.Any(), &v1.PodMonitorList{}, gomock.Any()). 600 DoAndReturn(func(ctx context.Context, list *v1.PodMonitorList, opts ...client.ListOption) error { 601 return nil 602 }) 603 604 } 605 606 // expectAllCallsNoApps expects all the calls that a reconcile of the MC agent would result in, if 607 // there are no applications or VerrazzanoProjects. It does not include the initial call to get the agent secret. 608 func expectAllCallsNoApps(adminMock *mocks.MockClient, mcMock *mocks.MockClient, adminStatusMock *mocks.MockStatusWriter, clusterName string, assert *asserts.Assertions) { 609 // Admin Cluster - expect a get followed by status update on VMC to record last agent connect time 610 vmcName := types.NamespacedName{Name: clusterName, Namespace: constants.VerrazzanoMultiClusterNamespace} 611 expectGetAPIServerURLCalled(mcMock) 612 expectGetPrometheusHostCalled(mcMock) 613 expectGetThanosQueryHostCalled(mcMock) 614 expectGetWorkloadVZVersionCalled(mcMock) 615 expectAdminVMCStatusUpdateSuccess(adminMock, vmcName, adminStatusMock, assert) 616 617 // Managed Cluster - expect call to get MC app config CRD - return exists 618 expectGetMCAppConfigCRD(mcMock) 619 620 // Admin Cluster - expect call to list VerrazzanoProject objects - return an empty list 621 expectListProjectsReturnNone(adminMock) 622 // Managed Cluster - expect call to list VerrazzanoProject objects - return an empty list 623 expectListProjectsReturnNone(mcMock) 624 625 expectServiceAndPodMonitorsList(mcMock, assert) 626 627 // Managed Cluster - expect call to list Namespace objects - return an empty list 628 expectListNamespacesReturnNone(mcMock) 629 630 expectCASyncSuccess(mcMock, adminMock, assert, "cluster1") 631 632 // expect the VMC to be retrieved to check for deletion 633 clusterCASecret := "clusterCASecret" 634 expectGetVMC(adminMock, vmcName, clusterCASecret) 635 expectGetManifestSecretNotFound(adminMock, clusterName) 636 } 637 638 func expectGetMCAppConfigCRD(mock *mocks.MockClient) { 639 mock.EXPECT(). 640 Get(gomock.Any(), types.NamespacedName{Name: mcAppConfCRDName}, gomock.Not(gomock.Nil()), gomock.Any()). 641 DoAndReturn(func(ctx context.Context, name types.NamespacedName, crd *apiextv1.CustomResourceDefinition, opts ...client.GetOption) error { 642 crd.Name = mcAppConfCRDName 643 return nil 644 }) 645 }