github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/vmc/vmc_controller_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 vmc 5 6 import ( 7 "bytes" 8 "context" 9 "fmt" 10 "io" 11 "net/http" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/Jeffail/gabs/v2" 17 "github.com/golang/mock/gomock" 18 "github.com/prometheus/client_golang/prometheus/testutil" 19 "github.com/stretchr/testify/assert" 20 "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 21 "github.com/verrazzano/verrazzano/pkg/constants" 22 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 23 "github.com/verrazzano/verrazzano/pkg/mcconstants" 24 "github.com/verrazzano/verrazzano/pkg/metricsutils" 25 "github.com/verrazzano/verrazzano/pkg/rancherutil" 26 "github.com/verrazzano/verrazzano/pkg/test/mockmatchers" 27 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 28 vpoconstants "github.com/verrazzano/verrazzano/platform-operator/constants" 29 "github.com/verrazzano/verrazzano/platform-operator/mocks" 30 corev1 "k8s.io/api/core/v1" 31 networkingv1 "k8s.io/api/networking/v1" 32 rbacv1 "k8s.io/api/rbac/v1" 33 apiv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 34 "k8s.io/apimachinery/pkg/api/errors" 35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 37 "k8s.io/apimachinery/pkg/runtime" 38 "k8s.io/apimachinery/pkg/runtime/schema" 39 "k8s.io/apimachinery/pkg/types" 40 "k8s.io/apimachinery/pkg/util/wait" 41 "k8s.io/client-go/rest" 42 ctrl "sigs.k8s.io/controller-runtime" 43 "sigs.k8s.io/controller-runtime/pkg/client" 44 ) 45 46 const apiVersion = "clusters.verrazzano.io/v1alpha1" 47 const kind = "VerrazzanoManagedCluster" 48 49 const ( 50 token = "tokenData" 51 testManagedCluster = "test" 52 rancherAgentRegistry = "ghcr.io" 53 rancherAgentImage = rancherAgentRegistry + "/verrazzano/rancher-agent:v1.0.0" 54 rancherAdminSecret = "rancher-admin-secret" 55 unitTestRancherClusterID = "unit-test-rancher-cluster-id" 56 ) 57 58 var ( 59 loginURLParts = strings.Split(loginPath, "?") 60 loginURIPath = loginURLParts[0] 61 loginQueryString = loginURLParts[1] 62 ) 63 64 const rancherManifestYAML = ` 65 apiVersion: apps/v1 66 kind: Deployment 67 metadata: 68 name: cattle-cluster-agent 69 namespace: cattle-system 70 spec: 71 template: 72 spec: 73 containers: 74 - name: cluster-register 75 image: ` + rancherAgentImage + ` 76 imagePullPolicy: IfNotPresent 77 ` 78 79 type AssertFn func(configMap *corev1.ConfigMap) error 80 type secretAssertFn func(secret *corev1.Secret) error 81 82 func init() { 83 // stub out keycloak client creation for these tests 84 createClient = func(r *VerrazzanoManagedClusterReconciler, vmc *v1alpha1.VerrazzanoManagedCluster) error { 85 return nil 86 } 87 } 88 89 // TestCreateVMC tests the Reconcile method for the following use case 90 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 91 // WHEN a VerrazzanoManagedCluster resource has been applied in a cluster where Rancher is enabled 92 // THEN ensure all the objects are created correctly 93 func TestCreateVMCRancherEnabled(t *testing.T) { 94 // with feature flag disabled (which triggers different asserts/mocks from enabled) 95 doTestCreateVMC(t, true) 96 } 97 98 // TestCreateVMC tests the Reconcile method for the following use case 99 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 100 // WHEN a VerrazzanoManagedCluster resource has been applied in a cluster where Rancher is DISABLED 101 // THEN ensure all the objects are created correctly 102 func TestCreateVMCRancherDisabled(t *testing.T) { 103 doTestCreateVMC(t, false) 104 } 105 106 func doTestCreateVMC(t *testing.T, rancherEnabled bool) { 107 // clear any cached user auth tokens when the test completes 108 defer rancherutil.DeleteStoredTokens() 109 110 namespace := constants.VerrazzanoMultiClusterNamespace 111 asserts := assert.New(t) 112 mocker := gomock.NewController(t) 113 mock := mocks.NewMockClient(mocker) 114 mockStatus := mocks.NewMockStatusWriter(mocker) 115 asserts.NotNil(mockStatus) 116 117 mockRequestSender := mocks.NewMockRequestSender(mocker) 118 savedRancherHTTPClient := rancherutil.RancherHTTPClient 119 defer func() { 120 rancherutil.RancherHTTPClient = savedRancherHTTPClient 121 }() 122 rancherutil.RancherHTTPClient = mockRequestSender 123 124 defer setConfigFunc(getConfigFunc) 125 setConfigFunc(fakeGetConfig) 126 127 caSecretExistsInVMC := true 128 expectVmcGetAndUpdate(t, mock, testManagedCluster, caSecretExistsInVMC, false) 129 expectSyncServiceAccount(t, mock, testManagedCluster, true) 130 expectSyncRoleBinding(t, mock, testManagedCluster, true) 131 // Agent secret sync checks depend on whether Rancher is enabled 132 expectSyncAgent(t, mock, testManagedCluster, rancherEnabled, false) 133 expectMockCallsForListingRancherUsers(mock) 134 expectSyncRegistration(t, mock, testManagedCluster, false) 135 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 136 expectRancherConfigK8sCalls(t, mock, false) 137 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 138 expectPushManifestRequests(t, mockRequestSender, mock) 139 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 140 expectThanosDelete(t, mock) 141 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 142 asserts.Len(configMap.Data, 2, "no data found") 143 asserts.NotEmpty(configMap.Data["ca-test"], "No cert entry found") 144 prometheusYaml := configMap.Data["prometheus.yml"] 145 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 146 147 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 148 if err != nil { 149 asserts.Fail("failed due to error %v", err) 150 } 151 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 152 return nil 153 }, func(secret *corev1.Secret) error { 154 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 155 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 156 if err != nil { 157 asserts.Fail("failed due to error %v", err) 158 } 159 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 160 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 161 return nil 162 }, func(secret *corev1.Secret) error { 163 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 164 return nil 165 }) 166 167 // expect status updated with condition Ready=true 168 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 169 170 // Create and make the request 171 request := newRequest(namespace, testManagedCluster) 172 reconciler := newVMCReconciler(mock) 173 result, err := reconciler.Reconcile(context.TODO(), request) 174 175 // Validate the results 176 mocker.Finish() 177 asserts.NoError(err) 178 asserts.Equal(true, result.Requeue) 179 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 180 } 181 182 // TestCreateVMC tests the Reconcile method for the following use case 183 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 184 // WHEN a VerrazzanoManagedCluster resource has been applied on a Verrazzano install configured with external ES 185 // THEN ensure all the objects are created 186 func TestCreateVMCWithExternalES(t *testing.T) { 187 // clear any cached user auth tokens when the test completes 188 defer rancherutil.DeleteStoredTokens() 189 190 namespace := constants.VerrazzanoMultiClusterNamespace 191 asserts := assert.New(t) 192 mocker := gomock.NewController(t) 193 mock := mocks.NewMockClient(mocker) 194 mockStatus := mocks.NewMockStatusWriter(mocker) 195 asserts.NotNil(mockStatus) 196 197 mockRequestSender := mocks.NewMockRequestSender(mocker) 198 savedRancherHTTPClient := rancherutil.RancherHTTPClient 199 defer func() { 200 rancherutil.RancherHTTPClient = savedRancherHTTPClient 201 }() 202 rancherutil.RancherHTTPClient = mockRequestSender 203 204 defer setConfigFunc(getConfigFunc) 205 setConfigFunc(fakeGetConfig) 206 207 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, false) 208 expectSyncServiceAccount(t, mock, testManagedCluster, true) 209 expectSyncRoleBinding(t, mock, testManagedCluster, true) 210 expectSyncAgent(t, mock, testManagedCluster, false, true) 211 expectMockCallsForListingRancherUsers(mock) 212 expectSyncRegistration(t, mock, testManagedCluster, true) 213 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 214 expectRancherConfigK8sCalls(t, mock, false) 215 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 216 expectPushManifestRequests(t, mockRequestSender, mock) 217 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 218 expectThanosDelete(t, mock) 219 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 220 asserts.Len(configMap.Data, 2, "no data found") 221 asserts.NotEmpty(configMap.Data["ca-test"], "No cert entry found") 222 prometheusYaml := configMap.Data["prometheus.yml"] 223 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 224 225 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 226 if err != nil { 227 asserts.Fail("failed due to error %v", err) 228 } 229 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 230 return nil 231 }, func(secret *corev1.Secret) error { 232 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 233 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 234 if err != nil { 235 asserts.Fail("failed due to error %v", err) 236 } 237 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 238 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 239 return nil 240 }, func(secret *corev1.Secret) error { 241 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 242 return nil 243 }) 244 245 // expect status updated with condition Ready=true 246 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 247 248 // Create and make the request 249 request := newRequest(namespace, testManagedCluster) 250 reconciler := newVMCReconciler(mock) 251 result, err := reconciler.Reconcile(context.TODO(), request) 252 253 // Validate the results 254 mocker.Finish() 255 asserts.NoError(err) 256 asserts.Equal(true, result.Requeue) 257 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 258 } 259 260 // TestCreateVMC tests the Reconcile method for the following use case 261 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource for an OCI DNS cluster 262 // WHEN a VerrazzanoManagedCluster resource has been applied 263 // THEN ensure all the objects are created 264 func TestCreateVMCOCIDNS(t *testing.T) { 265 // clear any cached user auth tokens when the test completes 266 defer rancherutil.DeleteStoredTokens() 267 268 namespace := "verrazzano-mc" 269 asserts := assert.New(t) 270 mocker := gomock.NewController(t) 271 mock := mocks.NewMockClient(mocker) 272 mockStatus := mocks.NewMockStatusWriter(mocker) 273 asserts.NotNil(mockStatus) 274 275 mockRequestSender := mocks.NewMockRequestSender(mocker) 276 savedRancherHTTPClient := rancherutil.RancherHTTPClient 277 defer func() { 278 rancherutil.RancherHTTPClient = savedRancherHTTPClient 279 }() 280 rancherutil.RancherHTTPClient = mockRequestSender 281 282 defer setConfigFunc(getConfigFunc) 283 setConfigFunc(fakeGetConfig) 284 285 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, false) 286 expectSyncServiceAccount(t, mock, testManagedCluster, true) 287 expectSyncRoleBinding(t, mock, testManagedCluster, true) 288 expectSyncAgent(t, mock, testManagedCluster, false, true) 289 expectMockCallsForListingRancherUsers(mock) 290 expectSyncRegistration(t, mock, testManagedCluster, false) 291 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 292 expectRancherConfigK8sCalls(t, mock, false) 293 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 294 expectPushManifestRequests(t, mockRequestSender, mock) 295 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 296 expectThanosDelete(t, mock) 297 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", true, "", func(configMap *corev1.ConfigMap) error { 298 asserts.Len(configMap.Data, 2, "no data found") 299 asserts.Empty(configMap.Data["ca-test"], "Cert entry found") 300 prometheusYaml := configMap.Data["prometheus.yml"] 301 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 302 303 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 304 if err != nil { 305 asserts.Fail("failed due to error %v", err) 306 } 307 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, false) 308 return nil 309 }, func(secret *corev1.Secret) error { 310 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 311 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 312 if err != nil { 313 asserts.Fail("failed due to error %v", err) 314 } 315 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 316 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, false) 317 return nil 318 }, func(secret *corev1.Secret) error { 319 asserts.Empty(secret.Data["ca-test"]) 320 return nil 321 }) 322 323 // expect status updated with condition Ready=true 324 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 325 326 // Create and make the request 327 request := newRequest(namespace, testManagedCluster) 328 reconciler := newVMCReconciler(mock) 329 result, err := reconciler.Reconcile(context.TODO(), request) 330 331 // Validate the results 332 mocker.Finish() 333 asserts.NoError(err) 334 asserts.Equal(true, result.Requeue) 335 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 336 } 337 338 // TestCreateVMCNoCACert tests the Reconcile method for the following use case 339 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 340 // WHEN a VerrazzanoManagedCluster resource has been applied with no CA Cert 341 // THEN ensure all the objects are created 342 func TestCreateVMCNoCACert(t *testing.T) { 343 // clear any cached user auth tokens when the test completes 344 defer rancherutil.DeleteStoredTokens() 345 346 namespace := constants.VerrazzanoMultiClusterNamespace 347 asserts := assert.New(t) 348 mocker := gomock.NewController(t) 349 mock := mocks.NewMockClient(mocker) 350 mockStatus := mocks.NewMockStatusWriter(mocker) 351 asserts.NotNil(mockStatus) 352 353 mockRequestSender := mocks.NewMockRequestSender(mocker) 354 savedRancherHTTPClient := rancherutil.RancherHTTPClient 355 defer func() { 356 rancherutil.RancherHTTPClient = savedRancherHTTPClient 357 }() 358 rancherutil.RancherHTTPClient = mockRequestSender 359 360 defer setConfigFunc(getConfigFunc) 361 setConfigFunc(fakeGetConfig) 362 363 expectVmcGetAndUpdate(t, mock, testManagedCluster, false, false) 364 expectSyncServiceAccount(t, mock, testManagedCluster, true) 365 expectSyncRoleBinding(t, mock, testManagedCluster, true) 366 expectSyncAgent(t, mock, testManagedCluster, false, true) 367 expectMockCallsForListingRancherUsers(mock) 368 expectSyncRegistration(t, mock, testManagedCluster, true) 369 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 370 expectRancherConfigK8sCalls(t, mock, true) 371 expectSyncCACertRancherHTTPCalls(t, mockRequestSender, "") 372 expectRancherConfigK8sCalls(t, mock, false) 373 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 374 expectPushManifestRequests(t, mockRequestSender, mock) 375 expectThanosDelete(t, mock) 376 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", false, getCaCrt(), func(configMap *corev1.ConfigMap) error { 377 asserts.Len(configMap.Data, 2, "no data found") 378 prometheusYaml := configMap.Data["prometheus.yml"] 379 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 380 381 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 382 if err != nil { 383 asserts.Fail("failed due to error %v", err) 384 } 385 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, false) 386 return nil 387 }, func(secret *corev1.Secret) error { 388 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 389 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 390 if err != nil { 391 asserts.Fail("failed due to error %v", err) 392 } 393 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 394 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, false) 395 return nil 396 }, func(secret *corev1.Secret) error { 397 asserts.Empty(secret.Data["ca-test"]) 398 return nil 399 }) 400 401 // expect status updated with condition Ready=true and ManagedCARetrieved condition is not set because we don't provide 402 // a non-zero length managed ca cert 403 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 404 405 // Create and make the request 406 request := newRequest(namespace, testManagedCluster) 407 reconciler := newVMCReconciler(mock) 408 result, err := reconciler.Reconcile(context.TODO(), request) 409 410 // Validate the results 411 mocker.Finish() 412 asserts.NoError(err) 413 asserts.Equal(true, result.Requeue) 414 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 415 } 416 417 // TestCreateVMCFetchCACertFromManagedCluster tests the Reconcile method for the following use case 418 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 419 // WHEN a VerrazzanoManagedCluster resource has been applied and the caSecret field is NOT empty 420 // THEN ensure that we fetch the CA cert secret from the managed cluster and populate the caSecret field 421 func TestCreateVMCFetchCACertFromManagedCluster(t *testing.T) { 422 // clear any cached user auth tokens when the test completes 423 defer rancherutil.DeleteStoredTokens() 424 425 namespace := constants.VerrazzanoMultiClusterNamespace 426 asserts := assert.New(t) 427 mocker := gomock.NewController(t) 428 mock := mocks.NewMockClient(mocker) 429 mockStatus := mocks.NewMockStatusWriter(mocker) 430 asserts.NotNil(mockStatus) 431 432 mockRequestSender := mocks.NewMockRequestSender(mocker) 433 savedRancherHTTPClient := rancherutil.RancherHTTPClient 434 defer func() { 435 rancherutil.RancherHTTPClient = savedRancherHTTPClient 436 }() 437 rancherutil.RancherHTTPClient = mockRequestSender 438 439 defer setConfigFunc(getConfigFunc) 440 setConfigFunc(fakeGetConfig) 441 442 expectVmcGetAndUpdate(t, mock, testManagedCluster, false, false) 443 expectSyncServiceAccount(t, mock, testManagedCluster, true) 444 expectSyncRoleBinding(t, mock, testManagedCluster, true) 445 expectSyncAgent(t, mock, testManagedCluster, true, false) 446 expectMockCallsForListingRancherUsers(mock) 447 expectSyncRegistration(t, mock, testManagedCluster, true) 448 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 449 expectSyncCACertRancherHTTPCalls(t, mockRequestSender, `{"data":{"ca.crt":"base64-ca-cert"}}`) 450 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, true) 451 expectRancherConfigK8sCalls(t, mock, false) 452 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 453 expectPushManifestRequests(t, mockRequestSender, mock) 454 expectThanosDelete(t, mock) 455 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 456 asserts.Len(configMap.Data, 2, "no data found") 457 prometheusYaml := configMap.Data["prometheus.yml"] 458 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 459 460 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 461 if err != nil { 462 asserts.Fail("failed due to error %v", err) 463 } 464 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 465 return nil 466 }, func(secret *corev1.Secret) error { 467 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 468 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 469 if err != nil { 470 asserts.Fail("failed due to error %v", err) 471 } 472 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 473 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 474 return nil 475 }, func(secret *corev1.Secret) error { 476 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 477 return nil 478 }) 479 480 // expect status updated with condition Ready=true and ManagedCARetrieved 481 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", true) 482 483 // Create and make the request 484 request := newRequest(namespace, testManagedCluster) 485 reconciler := newVMCReconciler(mock) 486 result, err := reconciler.Reconcile(context.TODO(), request) 487 488 // Validate the results 489 mocker.Finish() 490 asserts.NoError(err) 491 asserts.Equal(true, result.Requeue) 492 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 493 } 494 495 // TestCreateVMCWithExistingScrapeConfiguration tests the Reconcile method for the following use case 496 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 497 // WHEN a VerrazzanoManagedCluster resource has been applied and prometheus is already configured with a scrape config for the cluster 498 // THEN ensure all the objects are created 499 func TestCreateVMCWithExistingScrapeConfiguration(t *testing.T) { 500 // clear any cached user auth tokens when the test completes 501 defer rancherutil.DeleteStoredTokens() 502 503 namespace := "verrazzano-mc" 504 jobs := ` - ` + constants.PrometheusJobNameKey + `: cluster1 505 scrape_interval: 20s 506 scrape_timeout: 15s 507 scheme: http` 508 prometheusYaml := `global: 509 scrape_interval: 20s 510 scrape_timeout: 10s 511 evaluation_interval: 30s 512 scrape_configs: 513 ` + jobs 514 asserts := assert.New(t) 515 mocker := gomock.NewController(t) 516 mock := mocks.NewMockClient(mocker) 517 mockStatus := mocks.NewMockStatusWriter(mocker) 518 asserts.NotNil(mockStatus) 519 520 mockRequestSender := mocks.NewMockRequestSender(mocker) 521 savedRancherHTTPClient := rancherutil.RancherHTTPClient 522 defer func() { 523 rancherutil.RancherHTTPClient = savedRancherHTTPClient 524 }() 525 rancherutil.RancherHTTPClient = mockRequestSender 526 527 defer setConfigFunc(getConfigFunc) 528 setConfigFunc(fakeGetConfig) 529 530 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, false) 531 expectSyncServiceAccount(t, mock, testManagedCluster, true) 532 expectSyncRoleBinding(t, mock, testManagedCluster, true) 533 expectSyncAgent(t, mock, testManagedCluster, false, false) 534 expectMockCallsForListingRancherUsers(mock) 535 expectSyncRegistration(t, mock, testManagedCluster, false) 536 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 537 expectRancherConfigK8sCalls(t, mock, false) 538 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 539 expectPushManifestRequests(t, mockRequestSender, mock) 540 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 541 expectThanosDelete(t, mock) 542 expectSyncPrometheusScraper(mock, testManagedCluster, prometheusYaml, jobs, true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 543 544 // check for the modified entry 545 asserts.Len(configMap.Data, 2, "no data found") 546 asserts.NotEmpty(configMap.Data["ca-test"], "No cert entry found") 547 prometheusYaml := configMap.Data["prometheus.yml"] 548 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 549 550 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 551 if err != nil { 552 asserts.Fail("failed due to error %v", err) 553 } 554 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 555 return nil 556 }, func(secret *corev1.Secret) error { 557 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 558 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 559 if err != nil { 560 asserts.Fail("failed due to error %v", err) 561 } 562 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 563 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 564 return nil 565 }, func(secret *corev1.Secret) error { 566 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 567 return nil 568 }) 569 570 // expect status updated with condition Ready=true 571 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 572 573 // Create and make the request 574 request := newRequest(namespace, testManagedCluster) 575 reconciler := newVMCReconciler(mock) 576 result, err := reconciler.Reconcile(context.TODO(), request) 577 578 // Validate the results 579 mocker.Finish() 580 asserts.NoError(err) 581 asserts.Equal(true, result.Requeue) 582 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 583 } 584 585 // TestReplaceExistingScrapeConfiguration tests the Reconcile method for the following use case 586 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 587 // WHEN a VerrazzanoManagedCluster resource has been applied and prometheus is already configured with a scrape configuration for the same cluster 588 // THEN ensure all the objects are created (existing configuration is replaced) 589 func TestReplaceExistingScrapeConfiguration(t *testing.T) { 590 // clear any cached user auth tokens when the test completes 591 defer rancherutil.DeleteStoredTokens() 592 593 namespace := "verrazzano-mc" 594 jobs := ` - ` + constants.PrometheusJobNameKey + `: test 595 scrape_interval: 20s 596 scrape_timeout: 15s 597 scheme: http` 598 prometheusYaml := `global: 599 scrape_interval: 20s 600 scrape_timeout: 10s 601 evaluation_interval: 30s 602 scrape_configs: 603 ` + jobs 604 asserts := assert.New(t) 605 mocker := gomock.NewController(t) 606 mock := mocks.NewMockClient(mocker) 607 mockStatus := mocks.NewMockStatusWriter(mocker) 608 asserts.NotNil(mockStatus) 609 610 mockRequestSender := mocks.NewMockRequestSender(mocker) 611 savedRancherHTTPClient := rancherutil.RancherHTTPClient 612 defer func() { 613 rancherutil.RancherHTTPClient = savedRancherHTTPClient 614 }() 615 rancherutil.RancherHTTPClient = mockRequestSender 616 617 defer setConfigFunc(getConfigFunc) 618 setConfigFunc(fakeGetConfig) 619 620 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, false) 621 expectSyncServiceAccount(t, mock, testManagedCluster, true) 622 expectSyncRoleBinding(t, mock, testManagedCluster, true) 623 expectSyncAgent(t, mock, testManagedCluster, false, true) 624 expectMockCallsForListingRancherUsers(mock) 625 expectSyncRegistration(t, mock, testManagedCluster, false) 626 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, false, rancherManifestYAML) 627 expectRancherConfigK8sCalls(t, mock, false) 628 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 629 expectPushManifestRequests(t, mockRequestSender, mock) 630 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 631 expectThanosDelete(t, mock) 632 expectSyncPrometheusScraper(mock, testManagedCluster, prometheusYaml, jobs, true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 633 634 asserts.Len(configMap.Data, 2, "no data found") 635 asserts.NotNil(configMap.Data["ca-test"], "No cert entry found") 636 prometheusYaml := configMap.Data["prometheus.yml"] 637 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 638 639 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 640 if err != nil { 641 asserts.Fail("failed due to error %v", err) 642 } 643 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 644 return nil 645 }, func(secret *corev1.Secret) error { 646 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 647 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 648 if err != nil { 649 asserts.Fail("failed due to error %v", err) 650 } 651 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 652 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 653 return nil 654 }, func(secret *corev1.Secret) error { 655 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 656 return nil 657 }) 658 659 // expect status updated with condition Ready=true 660 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 661 662 // Create and make the request 663 request := newRequest(namespace, testManagedCluster) 664 reconciler := newVMCReconciler(mock) 665 result, err := reconciler.Reconcile(context.TODO(), request) 666 667 // Validate the results 668 mocker.Finish() 669 asserts.NoError(err) 670 asserts.Equal(true, result.Requeue) 671 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 672 } 673 674 // TestCreateVMC tests the Reconcile method for the following use case 675 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 676 // WHEN a VerrazzanoManagedCluster resource has been applied 677 // AND the cluster has already been registered with Rancher 678 // THEN ensure all the objects are created 679 func TestCreateVMCClusterAlreadyRegistered(t *testing.T) { 680 // clear any cached user auth tokens when the test completes 681 defer rancherutil.DeleteStoredTokens() 682 683 namespace := constants.VerrazzanoMultiClusterNamespace 684 asserts := assert.New(t) 685 mocker := gomock.NewController(t) 686 mock := mocks.NewMockClient(mocker) 687 mockStatus := mocks.NewMockStatusWriter(mocker) 688 asserts.NotNil(mockStatus) 689 690 mockRequestSender := mocks.NewMockRequestSender(mocker) 691 savedRancherHTTPClient := rancherutil.RancherHTTPClient 692 defer func() { 693 rancherutil.RancherHTTPClient = savedRancherHTTPClient 694 }() 695 rancherutil.RancherHTTPClient = mockRequestSender 696 697 defer setConfigFunc(getConfigFunc) 698 setConfigFunc(fakeGetConfig) 699 700 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, true) 701 expectSyncServiceAccount(t, mock, testManagedCluster, true) 702 expectSyncRoleBinding(t, mock, testManagedCluster, true) 703 expectSyncAgent(t, mock, testManagedCluster, false, true) 704 expectMockCallsForListingRancherUsers(mock) 705 expectSyncRegistration(t, mock, testManagedCluster, false) 706 expectSyncManifest(t, mock, mockStatus, mockRequestSender, testManagedCluster, true, rancherManifestYAML) 707 expectRancherConfigK8sCalls(t, mock, false) 708 expectMockCallsForCreateClusterRoleBindingTemplate(mock, unitTestRancherClusterID) 709 expectPushManifestRequests(t, mockRequestSender, mock) 710 expectSyncCACertRancherK8sCalls(t, mock, mockRequestSender, false) 711 expectThanosDelete(t, mock) 712 expectSyncPrometheusScraper(mock, testManagedCluster, "", "", true, getCaCrt(), func(configMap *corev1.ConfigMap) error { 713 asserts.Len(configMap.Data, 2, "no data found") 714 asserts.NotEmpty(configMap.Data["ca-test"], "No cert entry found") 715 prometheusYaml := configMap.Data["prometheus.yml"] 716 asserts.NotEmpty(prometheusYaml, "No prometheus config yaml found") 717 718 scrapeConfig, err := getScrapeConfig(prometheusYaml, testManagedCluster) 719 if err != nil { 720 asserts.Fail("failed due to error %v", err) 721 } 722 validateScrapeConfig(t, scrapeConfig, prometheusConfigBasePath, true) 723 return nil 724 }, func(secret *corev1.Secret) error { 725 scrapeConfigYaml := secret.Data[constants.PromAdditionalScrapeConfigsSecretKey] 726 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(scrapeConfigYaml)) 727 if err != nil { 728 asserts.Fail("failed due to error %v", err) 729 } 730 scrapeConfig := getJob(scrapeConfigs.Children(), testManagedCluster) 731 validateScrapeConfig(t, scrapeConfig, managedCertsBasePath, true) 732 return nil 733 }, func(secret *corev1.Secret) error { 734 asserts.NotEmpty(secret.Data["ca-test"], "Expected to find a managed cluster TLS cert") 735 return nil 736 }) 737 738 // expect status updated with condition Ready=true 739 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionTrue, "", false) 740 741 // Create and make the request 742 request := newRequest(namespace, testManagedCluster) 743 reconciler := newVMCReconciler(mock) 744 result, err := reconciler.Reconcile(context.TODO(), request) 745 746 // Validate the results 747 mocker.Finish() 748 asserts.NoError(err) 749 asserts.Equal(true, result.Requeue) 750 asserts.Equal(time.Duration(vpoconstants.ReconcileLoopRequeueInterval), result.RequeueAfter) 751 } 752 753 // TestCreateVMCSyncSvcAccountFailed tests the Reconcile method for the following use case 754 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 755 // WHEN syncing of service account fails 756 // THEN ensure that the VMC status is updated to Ready=false with an appropriate message 757 func TestCreateVMCSyncSvcAccountFailed(t *testing.T) { 758 // clear any cached user auth tokens when the test completes 759 defer rancherutil.DeleteStoredTokens() 760 761 namespace := constants.VerrazzanoMultiClusterNamespace 762 asserts := assert.New(t) 763 mocker := gomock.NewController(t) 764 mock := mocks.NewMockClient(mocker) 765 mockStatus := mocks.NewMockStatusWriter(mocker) 766 asserts.NotNil(mockStatus) 767 768 defer setConfigFunc(getConfigFunc) 769 setConfigFunc(fakeGetConfig) 770 771 expectVmcGetAndUpdate(t, mock, testManagedCluster, true, false) 772 expectSyncServiceAccount(t, mock, testManagedCluster, false) 773 774 // expect status updated with condition Ready=true 775 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionFalse, "failing syncServiceAccount", false) 776 777 errCount := testutil.ToFloat64(reconcileErrorCount) 778 779 // Create and make the request 780 request := newRequest(namespace, testManagedCluster) 781 reconciler := newVMCReconciler(mock) 782 result, err := reconciler.Reconcile(context.TODO(), request) 783 784 // Validate the results - there should have been no error returned for failing to sync svc account, but the reconcile 785 // error metric should have been incremented and the request should be requeued 786 mocker.Finish() 787 asserts.NoError(err) 788 asserts.Equal(true, result.Requeue) 789 asserts.NotEqual(time.Duration(0), result.RequeueAfter) 790 asserts.Equal(errCount+1, testutil.ToFloat64(reconcileErrorCount)) 791 } 792 793 // TestCreateVMCSyncRoleBindingFailed tests the Reconcile method for the following use case 794 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 795 // WHEN syncing of role binding fails 796 // THEN ensure that the VMC status is updated to Ready=false with an appropriate message 797 func TestCreateVMCSyncRoleBindingFailed(t *testing.T) { 798 // clear any cached user auth tokens when the test completes 799 defer rancherutil.DeleteStoredTokens() 800 801 namespace := constants.VerrazzanoMultiClusterNamespace 802 name := "test" 803 804 asserts := assert.New(t) 805 mocker := gomock.NewController(t) 806 mock := mocks.NewMockClient(mocker) 807 mockStatus := mocks.NewMockStatusWriter(mocker) 808 asserts.NotNil(mockStatus) 809 810 defer setConfigFunc(getConfigFunc) 811 setConfigFunc(fakeGetConfig) 812 813 expectVmcGetAndUpdate(t, mock, name, true, false) 814 expectSyncServiceAccount(t, mock, name, true) 815 expectSyncRoleBinding(t, mock, name, false) 816 817 // expect status updated with condition Ready=true 818 expectStatusUpdateReadyCondition(asserts, mock, mockStatus, corev1.ConditionFalse, "failing syncRoleBinding", false) 819 820 // Create and make the request 821 request := newRequest(namespace, name) 822 reconciler := newVMCReconciler(mock) 823 result, err := reconciler.Reconcile(context.TODO(), request) 824 825 // Validate the results - there should have been an error returned 826 mocker.Finish() 827 asserts.Nil(err) 828 asserts.Equal(true, result.Requeue) 829 asserts.NotEqual(time.Duration(0), result.RequeueAfter) 830 } 831 832 // TestDeleteVMC tests the Reconcile method for the following use case 833 // GIVEN a request to reconcile an VerrazzanoManagedCluster resource 834 // WHEN a VerrazzanoManagedCluster resource has been deleted 835 // THEN ensure the object is not processed 836 func TestDeleteVMC(t *testing.T) { 837 // clear any cached user auth tokens when the test completes 838 defer rancherutil.DeleteStoredTokens() 839 840 namespace := "verrazzano-install" 841 asserts := assert.New(t) 842 mocker := gomock.NewController(t) 843 mock := mocks.NewMockClient(mocker) 844 845 mockRequestSender := mocks.NewMockRequestSender(mocker) 846 savedRancherHTTPClient := rancherutil.RancherHTTPClient 847 defer func() { 848 rancherutil.RancherHTTPClient = savedRancherHTTPClient 849 }() 850 rancherutil.RancherHTTPClient = mockRequestSender 851 852 // Expect all of the calls when deleting a VMC 853 expectMockCallsForDelete(t, mock, namespace) 854 expectRancherGetAuthTokenHTTPCall(t, mockRequestSender) 855 expectThanosDelete(t, mock) 856 857 // Expect an API call to delete the Rancher cluster 858 mockRequestSender.EXPECT(). 859 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURIMethod("DELETE", clustersPath+"/"+unitTestRancherClusterID)). 860 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 861 r := io.NopCloser(bytes.NewReader([]byte(""))) 862 resp := &http.Response{ 863 StatusCode: http.StatusOK, 864 Body: r, 865 } 866 return resp, nil 867 }) 868 869 // Expect a call to update the VerrazzanoManagedCluster finalizer 870 mock.EXPECT(). 871 Update(gomock.Any(), gomock.Any(), gomock.Any()). 872 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 873 asserts.True(len(vmc.ObjectMeta.Finalizers) == 0, "Wrong number of finalizers") 874 return nil 875 }) 876 877 mock.EXPECT(). 878 List(gomock.Any(), &v1beta1.VerrazzanoList{}, gomock.Not(gomock.Nil())). 879 DoAndReturn(func(ctx context.Context, list *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 880 vz := v1beta1.Verrazzano{} 881 list.Items = append(list.Items, vz) 882 return nil 883 }) 884 885 // Create and make the request 886 request := newRequest(namespace, testManagedCluster) 887 reconciler := newVMCReconciler(mock) 888 result, err := reconciler.Reconcile(context.TODO(), request) 889 890 // Validate the results 891 mocker.Finish() 892 asserts.NoError(err) 893 asserts.Equal(false, result.Requeue) 894 asserts.Equal(time.Duration(0), result.RequeueAfter) 895 } 896 897 // TestDeleteVMCFailedDeletingRancherCluster tests deleting a VMC when there are errors attempting to 898 // delete the Rancher cluster. 899 func TestDeleteVMCFailedDeletingRancherCluster(t *testing.T) { 900 // clear any cached user auth tokens when the test completes 901 defer rancherutil.DeleteStoredTokens() 902 903 namespace := "verrazzano-install" 904 asserts := assert.New(t) 905 mocker := gomock.NewController(t) 906 mock := mocks.NewMockClient(mocker) 907 mockStatus := mocks.NewMockStatusWriter(mocker) 908 asserts.NotNil(mockStatus) 909 910 mockRequestSender := mocks.NewMockRequestSender(mocker) 911 savedRancherHTTPClient := rancherutil.RancherHTTPClient 912 defer func() { 913 rancherutil.RancherHTTPClient = savedRancherHTTPClient 914 }() 915 rancherutil.RancherHTTPClient = mockRequestSender 916 917 // GIVEN a VMC is being deleted 918 // WHEN we fail creating a Rancher API client that will be used to delete the cluster in Rancher 919 // THEN the appropriate status is set on the VMC and the finalizer is not removed 920 921 // Expect all of the calls when deleting a VMC 922 expectMockCallsForDelete(t, mock, namespace) 923 expectThanosDelete(t, mock) 924 925 // Expect an HTTP request to fetch the admin token from Rancher - return an error 926 mockRequestSender.EXPECT(). 927 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 928 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 929 asserts.Equal(loginQueryString, req.URL.RawQuery) 930 931 r := io.NopCloser(bytes.NewReader([]byte(""))) 932 resp := &http.Response{ 933 StatusCode: http.StatusBadRequest, 934 Body: r, 935 Request: &http.Request{Method: http.MethodDelete}, 936 } 937 return resp, nil 938 }) 939 940 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 941 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 942 return nil 943 }) 944 945 mock.EXPECT().Status().Return(mockStatus) 946 mockStatus.EXPECT(). 947 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 948 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 949 asserts.Equal(v1alpha1.DeleteFailed, vmc.Status.RancherRegistration.Status) 950 asserts.Equal("Failed to create Rancher API client", vmc.Status.RancherRegistration.Message) 951 return nil 952 }) 953 954 mock.EXPECT(). 955 List(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil())). 956 DoAndReturn(func(ctx context.Context, list *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 957 vz := v1beta1.Verrazzano{} 958 list.Items = append(list.Items, vz) 959 return nil 960 }) 961 962 // Create and make the request 963 request := newRequest(namespace, testManagedCluster) 964 reconciler := newVMCReconciler(mock) 965 result, err := reconciler.Reconcile(context.TODO(), request) 966 967 // Validate the results 968 mocker.Finish() 969 asserts.NoError(err) 970 asserts.Equal(true, result.Requeue) 971 972 // GIVEN a VMC is being deleted 973 // WHEN we fail attempting to delete the cluster in Rancher 974 // THEN the appropriate status is set on the VMC and the finalizer is not removed 975 976 mock = mocks.NewMockClient(mocker) 977 mockStatus = mocks.NewMockStatusWriter(mocker) 978 mockRequestSender = mocks.NewMockRequestSender(mocker) 979 rancherutil.RancherHTTPClient = mockRequestSender 980 981 // Expect all of the calls when deleting a VMC 982 expectMockCallsForDelete(t, mock, namespace) 983 expectRancherGetAuthTokenHTTPCall(t, mockRequestSender) 984 expectThanosDelete(t, mock) 985 986 // Expect an API call to delete the Rancher cluster - return an error 987 mockRequestSender.EXPECT(). 988 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURIMethod("DELETE", clustersPath+"/"+unitTestRancherClusterID)). 989 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 990 r := io.NopCloser(bytes.NewReader([]byte(""))) 991 resp := &http.Response{ 992 StatusCode: http.StatusConflict, 993 Body: r, 994 Request: &http.Request{Method: http.MethodDelete}, 995 } 996 return resp, nil 997 }) 998 999 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1000 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 1001 return nil 1002 }) 1003 1004 mock.EXPECT().Status().Return(mockStatus) 1005 mockStatus.EXPECT(). 1006 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1007 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1008 asserts.Equal(v1alpha1.DeleteFailed, vmc.Status.RancherRegistration.Status) 1009 asserts.Equal("Failed deleting cluster", vmc.Status.RancherRegistration.Message) 1010 return nil 1011 }) 1012 1013 mock.EXPECT(). 1014 List(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil())). 1015 DoAndReturn(func(ctx context.Context, list *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 1016 vz := v1beta1.Verrazzano{} 1017 list.Items = append(list.Items, vz) 1018 return nil 1019 }) 1020 1021 // Create and make the request 1022 request = newRequest(namespace, testManagedCluster) 1023 reconciler = newVMCReconciler(mock) 1024 result, err = reconciler.Reconcile(context.TODO(), request) 1025 1026 // Validate the results 1027 mocker.Finish() 1028 asserts.NoError(err) 1029 asserts.Equal(true, result.Requeue) 1030 } 1031 1032 // TestSyncManifestSecretFailRancherRegistration tests syncing the manifest secret 1033 // when Rancher registration fails 1034 // GIVEN a call to sync the manifest secret 1035 // WHEN Rancher registration fails 1036 // THEN the manifest secret is still created and syncManifestSecret returns no error 1037 func TestSyncManifestSecretFailRancherRegistration(t *testing.T) { 1038 // clear any cached user auth tokens when the test completes 1039 defer rancherutil.DeleteStoredTokens() 1040 1041 asserts := assert.New(t) 1042 mocker := gomock.NewController(t) 1043 mock := mocks.NewMockClient(mocker) 1044 mockStatus := mocks.NewMockStatusWriter(mocker) 1045 asserts.NotNil(mockStatus) 1046 1047 clusterName := "cluster1" 1048 caData := "ca" 1049 userData := "user" 1050 passwordData := "pw" 1051 kubeconfigData := "fakekubeconfig" 1052 urlData := "https://testhost:443" 1053 1054 // Expect a call to get the Agent secret 1055 mock.EXPECT(). 1056 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetAgentSecretName(clusterName)}, gomock.Not(gomock.Nil()), gomock.Any()). 1057 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1058 secret.Data = map[string][]byte{ 1059 mcconstants.KubeconfigKey: []byte(kubeconfigData), 1060 } 1061 return nil 1062 }) 1063 1064 // Expect a call to get the registration secret 1065 mock.EXPECT(). 1066 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetRegistrationSecretName(clusterName)}, gomock.Not(gomock.Nil()), gomock.Any()). 1067 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1068 secret.Data = map[string][]byte{ 1069 mcconstants.ManagedClusterNameKey: []byte(clusterName), 1070 mcconstants.CaCrtKey: []byte(caData), 1071 mcconstants.RegistrationUsernameKey: []byte(userData), 1072 mcconstants.RegistrationPasswordKey: []byte(passwordData), 1073 mcconstants.ESURLKey: []byte(urlData), 1074 } 1075 return nil 1076 }) 1077 1078 // Expect a call to get the manifest secret - return that it does not exist 1079 mock.EXPECT(). 1080 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetManifestSecretName(clusterName)}, gomock.Not(gomock.Nil()), gomock.Any()). 1081 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoMultiClusterNamespace, Resource: "Secret"}, GetManifestSecretName(clusterName))) 1082 1083 // Expect a call to get the Rancher ingress and return no spec rules, which will cause registration to fail 1084 mock.EXPECT(). 1085 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherIngressName}), gomock.Not(gomock.Nil()), gomock.Any()). 1086 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 1087 return nil 1088 }) 1089 1090 // Expect to get existing VMC for status update 1091 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: clusterName}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1092 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 1093 return nil 1094 }) 1095 1096 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.RancherSystemNamespace, Name: rancherAdminSecret}, gomock.AssignableToTypeOf(&corev1.Secret{}), gomock.Any()). 1097 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1098 secret.Data = map[string][]byte{ 1099 "password": []byte("super-secret"), 1100 } 1101 return nil 1102 }) 1103 1104 mock.EXPECT().Status().Return(mockStatus) 1105 mockStatus.EXPECT(). 1106 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1107 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1108 asserts.Equal(v1alpha1.RegistrationFailed, vmc.Status.RancherRegistration.Status) 1109 asserts.Contains(vmc.Status.RancherRegistration.Message, "Failed to create Rancher API client") 1110 return nil 1111 }) 1112 1113 // Expect a call to create the manifest secret 1114 mock.EXPECT(). 1115 Create(gomock.Any(), gomock.AssignableToTypeOf(&corev1.Secret{}), gomock.Any()). 1116 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 1117 data := secret.Data[mcconstants.YamlKey] 1118 asserts.NotZero(len(data), "Expected yaml data in manifest secret") 1119 return nil 1120 }) 1121 1122 // Expect a call to update the VerrazzanoManagedCluster kubeconfig secret testManagedCluster - return success 1123 mock.EXPECT(). 1124 Update(gomock.Any(), gomock.Any(), gomock.Any()). 1125 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1126 asserts.Equal(vmc.Spec.ManagedClusterManifestSecret, GetManifestSecretName(clusterName), "Manifest secret testManagedCluster did not match") 1127 return nil 1128 }) 1129 1130 // Create a reconciler and call the function to sync the manifest secret - the call to register the cluster with Rancher will 1131 // fail but the result of syncManifestSecret should be success 1132 vmc := v1alpha1.VerrazzanoManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: clusterName, Namespace: constants.VerrazzanoMultiClusterNamespace}} 1133 reconciler := newVMCReconciler(mock) 1134 reconciler.log = vzlog.DefaultLogger() 1135 1136 vzVMCWaitingForClusterID, err := reconciler.syncManifestSecret(context.TODO(), &vmc) 1137 1138 // Validate the results 1139 mocker.Finish() 1140 asserts.NoError(err) 1141 asserts.False(vzVMCWaitingForClusterID) 1142 } 1143 1144 // TestSyncManifestSecretEmptyRancherManifest tests syncing the manifest secret 1145 // when Rancher returns an empty registration manifest YAML string. 1146 // GIVEN a call to sync the manifest secret 1147 // WHEN Rancher returns an empty manifest 1148 // THEN the status is set to failed on the VMC with an appropriate message and the syncManifestSecret call returns an error 1149 func TestSyncManifestSecretEmptyRancherManifest(t *testing.T) { 1150 // clear any cached user auth tokens when the test completes 1151 defer rancherutil.DeleteStoredTokens() 1152 1153 asserts := assert.New(t) 1154 mocker := gomock.NewController(t) 1155 mock := mocks.NewMockClient(mocker) 1156 mockStatus := mocks.NewMockStatusWriter(mocker) 1157 asserts.NotNil(mockStatus) 1158 1159 mockRequestSender := mocks.NewMockRequestSender(mocker) 1160 savedRancherHTTPClient := rancherutil.RancherHTTPClient 1161 defer func() { 1162 rancherutil.RancherHTTPClient = savedRancherHTTPClient 1163 }() 1164 rancherutil.RancherHTTPClient = mockRequestSender 1165 1166 defer setConfigFunc(getConfigFunc) 1167 setConfigFunc(fakeGetConfig) 1168 1169 caData := "ca" 1170 userData := "user" 1171 passwordData := "pw" 1172 kubeconfigData := "fakekubeconfig" 1173 urlData := "https://testhost:443" 1174 1175 mock.EXPECT(). 1176 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetAgentSecretName(testManagedCluster)}, gomock.Not(gomock.Nil()), gomock.Any()). 1177 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1178 secret.Data = map[string][]byte{ 1179 mcconstants.KubeconfigKey: []byte(kubeconfigData), 1180 } 1181 return nil 1182 }) 1183 1184 // Expect a call to get the registration secret 1185 mock.EXPECT(). 1186 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetRegistrationSecretName(testManagedCluster)}, gomock.Not(gomock.Nil()), gomock.Any()). 1187 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1188 secret.Data = map[string][]byte{ 1189 mcconstants.ManagedClusterNameKey: []byte(testManagedCluster), 1190 mcconstants.CaCrtKey: []byte(caData), 1191 mcconstants.RegistrationUsernameKey: []byte(userData), 1192 mcconstants.RegistrationPasswordKey: []byte(passwordData), 1193 mcconstants.ESURLKey: []byte(urlData), 1194 } 1195 return nil 1196 }) 1197 1198 // Expect all the calls needed to register the cluster with Rancher - note we are passing an empty string for the Rancher manifest YAML 1199 // that will be returned when calling the Rancher API 1200 expectRegisterClusterWithRancher(t, mock, mockRequestSender, testManagedCluster, false, "") 1201 1202 // Expect to get existing VMC for status update 1203 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1204 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 1205 return nil 1206 }) 1207 1208 // Expect the Rancher registration status to be set appropriately 1209 mock.EXPECT().Status().Return(mockStatus) 1210 mockStatus.EXPECT(). 1211 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1212 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1213 asserts.Equal(v1alpha1.RegistrationFailed, vmc.Status.RancherRegistration.Status) 1214 asserts.Equal(unitTestRancherClusterID, vmc.Status.RancherRegistration.ClusterID) 1215 asserts.Equal("Empty Rancher manifest YAML", vmc.Status.RancherRegistration.Message) 1216 return nil 1217 }) 1218 1219 // Create a reconciler and call the function to sync the manifest secret 1220 vmc := v1alpha1.VerrazzanoManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: testManagedCluster, Namespace: constants.VerrazzanoMultiClusterNamespace}} 1221 reconciler := newVMCReconciler(mock) 1222 reconciler.log = vzlog.DefaultLogger() 1223 1224 vzVMCWaitingForClusterID, err := reconciler.syncManifestSecret(context.TODO(), &vmc) 1225 1226 // Validate the results 1227 mocker.Finish() 1228 asserts.ErrorContains(err, "Failed retrieving Rancher manifest, YAML is an empty string") 1229 asserts.False(vzVMCWaitingForClusterID) 1230 } 1231 1232 // TestRegisterClusterWithRancherK8sErrorCases tests errors cases using the Kubernetes 1233 // client when registering with Rancher. 1234 func TestRegisterClusterWithRancherK8sErrorCases(t *testing.T) { 1235 // clear any cached user auth tokens when the test completes 1236 defer rancherutil.DeleteStoredTokens() 1237 1238 asserts := assert.New(t) 1239 mocker := gomock.NewController(t) 1240 mock := mocks.NewMockClient(mocker) 1241 1242 // GIVEN a call to register a managed cluster with Rancher 1243 // WHEN the call to get the ingress host name returns no ingress rules 1244 // THEN the registration call returns an error 1245 1246 // Expect a call to get the ingress host name but there are no ingress rules 1247 mock.EXPECT(). 1248 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherIngressName}), gomock.Not(gomock.Nil()), gomock.Any()). 1249 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 1250 return nil 1251 }) 1252 1253 // Expect a call for the verrazzano cluser user secret 1254 mock.EXPECT(). 1255 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: constants.VerrazzanoClusterRancherName}, gomock.AssignableToTypeOf(&corev1.Secret{}), gomock.Any()). 1256 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1257 secret.Data = map[string][]byte{ 1258 "password": []byte("super-secret"), 1259 } 1260 return nil 1261 }) 1262 1263 rc, err := rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1264 1265 mocker.Finish() 1266 asserts.Error(err) 1267 asserts.Nil(rc) 1268 1269 // GIVEN a call to register a managed cluster with Rancher 1270 // WHEN the call to get the Rancher root CA cert secret fails 1271 // THEN the registration call returns an error 1272 mocker = gomock.NewController(t) 1273 mock = mocks.NewMockClient(mocker) 1274 1275 // Expect a call to get the ingress host name 1276 mock.EXPECT(). 1277 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherIngressName}), gomock.Not(gomock.Nil()), gomock.Any()). 1278 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 1279 rule := networkingv1.IngressRule{Host: "rancher.unit-test.com"} 1280 ingress.Spec.Rules = append(ingress.Spec.Rules, rule) 1281 return nil 1282 }) 1283 1284 // Expect a call to get the secret with the Rancher root CA cert but the call fails 1285 mock.EXPECT(). 1286 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherTLSSecret}), gomock.Not(gomock.Nil()), gomock.Any()). 1287 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1288 return errors.NewResourceExpired("something bad happened") 1289 }) 1290 1291 // Expect a call for the verrazzano cluser user secret 1292 mock.EXPECT(). 1293 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: constants.VerrazzanoClusterRancherName}, gomock.AssignableToTypeOf(&corev1.Secret{}), gomock.Any()). 1294 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1295 secret.Data = map[string][]byte{ 1296 "password": []byte("super-secret"), 1297 } 1298 return nil 1299 }) 1300 1301 rc, err = rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1302 1303 mocker.Finish() 1304 asserts.Error(err) 1305 asserts.Nil(rc) 1306 } 1307 1308 // TestRegisterClusterWithRancherHTTPErrorCases tests errors cases using the HTTP 1309 // client when registering with Rancher. 1310 func TestRegisterClusterWithRancherHTTPErrorCases(t *testing.T) { 1311 // clear any cached user auth tokens when the test completes 1312 defer rancherutil.DeleteStoredTokens() 1313 1314 asserts := assert.New(t) 1315 mocker := gomock.NewController(t) 1316 mock := mocks.NewMockClient(mocker) 1317 mockRequestSender := mocks.NewMockRequestSender(mocker) 1318 1319 savedRancherHTTPClient := rancherutil.RancherHTTPClient 1320 defer func() { 1321 rancherutil.RancherHTTPClient = savedRancherHTTPClient 1322 }() 1323 rancherutil.RancherHTTPClient = mockRequestSender 1324 1325 // GIVEN a call to register a managed cluster with Rancher 1326 // WHEN the call to get the Rancher admin token fails 1327 // THEN the registration call returns an error 1328 1329 // Expect all of the Kubernetes calls 1330 expectRancherConfigK8sCalls(t, mock, false) 1331 1332 // Expect an HTTP request to fetch the admin token from Rancher but the call fails 1333 mockRequestSender.EXPECT(). 1334 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 1335 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1336 r := io.NopCloser(bytes.NewReader([]byte{})) 1337 resp := &http.Response{ 1338 StatusCode: http.StatusUnauthorized, 1339 Body: r, 1340 Request: &http.Request{Method: http.MethodPost}, 1341 } 1342 return resp, nil 1343 }) 1344 1345 rc, err := rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1346 1347 mocker.Finish() 1348 asserts.Error(err) 1349 asserts.Nil(rc) 1350 rancherutil.DeleteStoredTokens() 1351 1352 // GIVEN a call to register a managed cluster with Rancher 1353 // WHEN the call to import the cluster into Rancher fails 1354 // THEN the registration call returns an error 1355 mocker = gomock.NewController(t) 1356 mock = mocks.NewMockClient(mocker) 1357 mockRequestSender = mocks.NewMockRequestSender(mocker) 1358 rancherutil.RancherHTTPClient = mockRequestSender 1359 1360 // Expect all of the Kubernetes calls 1361 expectRancherConfigK8sCalls(t, mock, false) 1362 1363 // Expect an HTTP request to fetch the admin token from Rancher 1364 mockRequestSender.EXPECT(). 1365 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 1366 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1367 r := io.NopCloser(bytes.NewReader([]byte(`{"token":"unit-test-token"}`))) 1368 resp := &http.Response{ 1369 StatusCode: http.StatusCreated, 1370 Body: r, 1371 Request: &http.Request{Method: http.MethodPost}, 1372 } 1373 return resp, nil 1374 }) 1375 1376 // Expect an HTTP request to import the cluster to Rancher but the call fails 1377 mockRequestSender.EXPECT(). 1378 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterPath)). 1379 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1380 r := io.NopCloser(bytes.NewReader([]byte{})) 1381 resp := &http.Response{ 1382 StatusCode: http.StatusConflict, 1383 Body: r, 1384 Request: &http.Request{Method: http.MethodPost}, 1385 } 1386 return resp, nil 1387 }) 1388 1389 rc, err = rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1390 asserts.NoError(err) 1391 1392 regYAML, _, err := RegisterManagedClusterWithRancher(rc, testManagedCluster, "", vzlog.DefaultLogger()) 1393 1394 mocker.Finish() 1395 asserts.Error(err) 1396 asserts.Empty(regYAML) 1397 rancherutil.DeleteStoredTokens() 1398 1399 // GIVEN a call to register a managed cluster with Rancher 1400 // WHEN the call to create the Rancher registration token fails 1401 // THEN the registration call returns an error 1402 mocker = gomock.NewController(t) 1403 mock = mocks.NewMockClient(mocker) 1404 mockRequestSender = mocks.NewMockRequestSender(mocker) 1405 rancherutil.RancherHTTPClient = mockRequestSender 1406 1407 // Expect all of the Kubernetes calls 1408 expectRancherConfigK8sCalls(t, mock, false) 1409 1410 // Expect an HTTP request to fetch the admin token from Rancher 1411 mockRequestSender.EXPECT(). 1412 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 1413 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1414 r := io.NopCloser(bytes.NewReader([]byte(`{"token":"unit-test-token"}`))) 1415 resp := &http.Response{ 1416 StatusCode: http.StatusCreated, 1417 Body: r, 1418 Request: &http.Request{Method: http.MethodPost}, 1419 } 1420 return resp, nil 1421 }) 1422 1423 // Expect an HTTP request to import the cluster to Rancher 1424 mockRequestSender.EXPECT(). 1425 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterPath)). 1426 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1427 r := io.NopCloser(bytes.NewReader([]byte(`{"id":"some-cluster"}`))) 1428 resp := &http.Response{ 1429 StatusCode: http.StatusCreated, 1430 Body: r, 1431 } 1432 return resp, nil 1433 }) 1434 1435 // Expect an HTTP request to get the registration token in Rancher for the clusterId 1436 mockRequestSender.EXPECT(). 1437 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterRegTokenPath)). 1438 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1439 asserts.Contains(clusterRegTokenPath, req.URL.Path) 1440 1441 _, err := io.ReadAll(req.Body) 1442 asserts.NoError(err) 1443 1444 r := io.NopCloser(bytes.NewReader([]byte(`{"data":[]}`))) 1445 resp := &http.Response{ 1446 StatusCode: http.StatusOK, 1447 Body: r, 1448 Request: &http.Request{Method: http.MethodGet}, 1449 } 1450 return resp, nil 1451 }) 1452 1453 // Expect an HTTP request to create the registration token in Rancher but the call fails 1454 mockRequestSender.EXPECT(). 1455 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterRegTokenPath)). 1456 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1457 r := io.NopCloser(bytes.NewReader([]byte{})) 1458 resp := &http.Response{ 1459 StatusCode: http.StatusBadRequest, 1460 Body: r, 1461 Request: &http.Request{Method: http.MethodPost}, 1462 } 1463 return resp, nil 1464 }) 1465 1466 rc, err = rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1467 asserts.NoError(err) 1468 1469 regYAML, _, err = RegisterManagedClusterWithRancher(rc, testManagedCluster, "", vzlog.DefaultLogger()) 1470 1471 mocker.Finish() 1472 asserts.Error(err) 1473 asserts.Empty(regYAML) 1474 rancherutil.DeleteStoredTokens() 1475 1476 // GIVEN a call to register a managed cluster with Rancher 1477 // WHEN the call to get the Rancher manifest YAML fails 1478 // THEN the registration call returns an error 1479 mocker = gomock.NewController(t) 1480 mock = mocks.NewMockClient(mocker) 1481 mockRequestSender = mocks.NewMockRequestSender(mocker) 1482 rancherutil.RancherHTTPClient = mockRequestSender 1483 1484 // Expect all of the Kubernetes calls 1485 expectRancherConfigK8sCalls(t, mock, false) 1486 1487 // Expect an HTTP request to fetch the admin token from Rancher 1488 mockRequestSender.EXPECT(). 1489 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 1490 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1491 r := io.NopCloser(bytes.NewReader([]byte(`{"token":"unit-test-token"}`))) 1492 resp := &http.Response{ 1493 StatusCode: http.StatusCreated, 1494 Body: r, 1495 Request: &http.Request{Method: http.MethodPost}, 1496 } 1497 return resp, nil 1498 }) 1499 1500 // Expect an HTTP request to import the cluster to Rancher 1501 mockRequestSender.EXPECT(). 1502 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterPath)). 1503 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1504 r := io.NopCloser(bytes.NewReader([]byte(`{"id":"some-cluster"}`))) 1505 resp := &http.Response{ 1506 StatusCode: http.StatusCreated, 1507 Body: r, 1508 } 1509 return resp, nil 1510 }) 1511 1512 // Expect an HTTP request to get the registration token in Rancher for the clusterId 1513 mockRequestSender.EXPECT(). 1514 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterRegTokenPath)). 1515 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1516 asserts.Contains(clusterRegTokenPath, req.URL.Path) 1517 1518 _, err := io.ReadAll(req.Body) 1519 asserts.NoError(err) 1520 1521 // return a response with the CRT 1522 r := io.NopCloser(bytes.NewReader([]byte(`{"data":[{"token":"manifest-token","state":"active","clusterId":"some-cluster"}]}`))) 1523 resp := &http.Response{ 1524 StatusCode: http.StatusOK, 1525 Body: r, 1526 Request: &http.Request{Method: http.MethodGet}, 1527 } 1528 return resp, nil 1529 }) 1530 1531 // Expect an HTTP request to fetch the manifest YAML from Rancher but the call fails 1532 mockRequestSender.EXPECT(). 1533 Do(gomock.Not(gomock.Nil()), gomock.Not(gomock.Nil())). 1534 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1535 r := io.NopCloser(bytes.NewReader([]byte{})) 1536 resp := &http.Response{ 1537 StatusCode: http.StatusUnsupportedMediaType, 1538 Body: r, 1539 Request: &http.Request{Method: http.MethodGet}, 1540 } 1541 return resp, nil 1542 }) 1543 1544 rc, err = rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1545 asserts.NoError(err) 1546 1547 regYAML, _, err = RegisterManagedClusterWithRancher(rc, testManagedCluster, "", vzlog.DefaultLogger()) 1548 1549 mocker.Finish() 1550 asserts.Error(err) 1551 asserts.Empty(regYAML) 1552 } 1553 1554 // GIVEN a call to register a managed cluster with Rancher 1555 // WHEN the call to get the admin token from Rancher fails 1556 // AND the error is retryable 1557 // THEN ensure that the request is retried 1558 func TestRegisterClusterWithRancherRetryRequest(t *testing.T) { 1559 // clear any cached user auth tokens when the test completes 1560 defer rancherutil.DeleteStoredTokens() 1561 1562 asserts := assert.New(t) 1563 mocker := gomock.NewController(t) 1564 mock := mocks.NewMockClient(mocker) 1565 mockRequestSender := mocks.NewMockRequestSender(mocker) 1566 1567 savedRancherHTTPClient := rancherutil.RancherHTTPClient 1568 defer func() { 1569 rancherutil.RancherHTTPClient = savedRancherHTTPClient 1570 }() 1571 rancherutil.RancherHTTPClient = mockRequestSender 1572 1573 // replace the retry configuration so all of the retries happen very quickly 1574 retrySteps := 3 1575 savedRetry := rancherutil.DefaultRetry 1576 defer func() { 1577 rancherutil.DefaultRetry = savedRetry 1578 }() 1579 rancherutil.DefaultRetry = wait.Backoff{ 1580 Steps: retrySteps, 1581 Duration: 1 * time.Millisecond, 1582 Factor: 1.0, 1583 Jitter: 0.1, 1584 } 1585 1586 // Expect all of the Kubernetes calls 1587 expectRancherConfigK8sCalls(t, mock, false) 1588 1589 // Expect an HTTP request to fetch the admin token from Rancher - return an error response and 1590 // the request should be retried for a total of "retrySteps" # of times 1591 mockRequestSender.EXPECT(). 1592 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 1593 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 1594 r := io.NopCloser(bytes.NewReader([]byte{})) 1595 resp := &http.Response{ 1596 StatusCode: http.StatusInternalServerError, 1597 Body: r, 1598 Request: &http.Request{Method: http.MethodPost}, 1599 } 1600 return resp, nil 1601 }).Times(retrySteps) 1602 1603 _, err := rancherutil.NewVerrazzanoClusterRancherConfig(mock, rancherutil.RancherIngressServiceHost(), vzlog.DefaultLogger()) 1604 1605 mocker.Finish() 1606 asserts.Error(err) 1607 } 1608 1609 // TestUpateStatus tests the updateStatus function 1610 func TestUpateStatus(t *testing.T) { 1611 // clear any cached user auth tokens when the test completes 1612 defer rancherutil.DeleteStoredTokens() 1613 1614 asserts := assert.New(t) 1615 mocker := gomock.NewController(t) 1616 mock := mocks.NewMockClient(mocker) 1617 mockStatus := mocks.NewMockStatusWriter(mocker) 1618 asserts.NotNil(mockStatus) 1619 1620 // Expect the requests for the existing VMC resource 1621 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1622 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 1623 return nil 1624 }).AnyTimes() 1625 1626 // GIVEN a VMC with a status state unset and the last agent connect time set 1627 // WHEN the updateStatus function is called 1628 // THEN the status state is updated to pending 1629 mock.EXPECT().Status().Return(mockStatus) 1630 mockStatus.EXPECT(). 1631 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1632 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1633 asserts.Equal(v1alpha1.StatePending, vmc.Status.State) 1634 return nil 1635 }) 1636 1637 vmc := v1alpha1.VerrazzanoManagedCluster{ 1638 ObjectMeta: metav1.ObjectMeta{ 1639 Name: testManagedCluster, 1640 Namespace: constants.VerrazzanoMultiClusterNamespace, 1641 }, 1642 Status: v1alpha1.VerrazzanoManagedClusterStatus{ 1643 LastAgentConnectTime: &metav1.Time{ 1644 Time: time.Now(), 1645 }, 1646 }, 1647 } 1648 reconciler := newVMCReconciler(mock) 1649 reconciler.log = vzlog.DefaultLogger() 1650 1651 err := reconciler.updateStatus(context.TODO(), &vmc) 1652 1653 // Validate the results 1654 mocker.Finish() 1655 asserts.NoError(err) 1656 1657 // GIVEN a VMC with a status state of pending and the last agent connect time set 1658 // WHEN the updateStatus function is called 1659 // THEN the status state is updated to active 1660 mock.EXPECT().Status().Return(mockStatus) 1661 mockStatus.EXPECT(). 1662 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1663 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1664 asserts.Equal(v1alpha1.StateActive, vmc.Status.State) 1665 return nil 1666 }) 1667 1668 err = reconciler.updateStatus(context.TODO(), &vmc) 1669 1670 // Validate the results 1671 mocker.Finish() 1672 asserts.NoError(err) 1673 1674 // GIVEN a VMC with a last agent connect time set in the past 1675 // WHEN the updateStatus function is called 1676 // THEN the status state is updated to inactive 1677 past := metav1.Unix(0, 0) 1678 vmc.Status.LastAgentConnectTime = &past 1679 1680 // Expect the Rancher registration status to be set appropriately 1681 mock.EXPECT().Status().Return(mockStatus) 1682 mockStatus.EXPECT(). 1683 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 1684 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1685 asserts.Equal(v1alpha1.StateInactive, vmc.Status.State) 1686 return nil 1687 }) 1688 1689 err = reconciler.updateStatus(context.TODO(), &vmc) 1690 1691 // Validate the results 1692 mocker.Finish() 1693 asserts.NoError(err) 1694 } 1695 1696 // newScheme creates a new scheme that includes this package's object to use for testing 1697 func newScheme() *runtime.Scheme { 1698 scheme := runtime.NewScheme() 1699 _ = corev1.AddToScheme(scheme) 1700 _ = v1alpha1.AddToScheme(scheme) 1701 return scheme 1702 } 1703 1704 // newRequest creates a new reconciler request for testing 1705 // namespace - The namespace to use in the request 1706 // testManagedCluster - The testManagedCluster to use in the request 1707 func newRequest(namespace string, name string) ctrl.Request { 1708 return ctrl.Request{ 1709 NamespacedName: types.NamespacedName{ 1710 Namespace: namespace, 1711 Name: name}} 1712 } 1713 1714 // newVMCReconciler creates a new reconciler for testing 1715 // c - The Kerberos client to inject into the reconciler 1716 func newVMCReconciler(c client.Client) VerrazzanoManagedClusterReconciler { 1717 scheme := newScheme() 1718 reconciler := VerrazzanoManagedClusterReconciler{ 1719 Client: c, 1720 Scheme: scheme} 1721 return reconciler 1722 } 1723 1724 func fakeGetConfig() (*rest.Config, error) { 1725 conf := rest.Config{ 1726 TLSClientConfig: rest.TLSClientConfig{ 1727 CAData: []byte("fakeCA"), 1728 }, 1729 } 1730 return &conf, nil 1731 } 1732 1733 // Expect syncRoleBinding related calls 1734 func expectSyncRoleBinding(t *testing.T, mock *mocks.MockClient, name string, succeed bool) { 1735 asserts := assert.New(t) 1736 1737 // Expect a call to get the RoleBinding - return that it does not exist 1738 mock.EXPECT(). 1739 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: generateManagedResourceName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1740 Return(errors.NewNotFound(schema.GroupResource{Group: "", Resource: "RoleBinding"}, generateManagedResourceName(name))) 1741 1742 // Expect a call to create the RoleBinding - return success or failure based on the succeed argument 1743 mock.EXPECT(). 1744 Create(gomock.Any(), gomock.Any(), gomock.Any()). 1745 DoAndReturn(func(ctx context.Context, binding *rbacv1.RoleBinding, opts ...client.CreateOption) error { 1746 if succeed { 1747 asserts.Equalf(generateManagedResourceName(name), binding.Name, "RoleBinding testManagedCluster did not match") 1748 asserts.Equalf(vpoconstants.MCClusterRole, binding.RoleRef.Name, "RoleBinding roleref did not match") 1749 asserts.Equalf(generateManagedResourceName(name), binding.Subjects[0].Name, "Subject did not match") 1750 asserts.Equalf(constants.VerrazzanoMultiClusterNamespace, binding.Subjects[0].Namespace, "Subject namespace did not match") 1751 return nil 1752 } 1753 return errors.NewInternalError(fmt.Errorf("failing syncRoleBinding")) 1754 }) 1755 } 1756 1757 // Expect syncServiceAccount related calls 1758 func expectSyncServiceAccount(t *testing.T, mock *mocks.MockClient, name string, succeed bool) { 1759 asserts := assert.New(t) 1760 1761 // Expect a call to get the ServiceAccount - return that it does not exist 1762 mock.EXPECT(). 1763 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: generateManagedResourceName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1764 Return(errors.NewNotFound(schema.GroupResource{Group: "", Resource: "ServiceAccount"}, generateManagedResourceName(name))) 1765 1766 // Expect a call to create the ServiceAccount - return success 1767 mock.EXPECT(). 1768 Create(gomock.Any(), gomock.Any(), gomock.Any()). 1769 DoAndReturn(func(ctx context.Context, serviceAccount *corev1.ServiceAccount, opts ...client.CreateOption) error { 1770 asserts.Equalf(constants.VerrazzanoMultiClusterNamespace, serviceAccount.Namespace, "ServiceAccount namespace did not match") 1771 asserts.Equalf(generateManagedResourceName(name), serviceAccount.Name, "ServiceAccount testManagedCluster did not match") 1772 return nil 1773 }) 1774 1775 // Expect a call to get the Token Secret - return that it does not exist 1776 mock.EXPECT(). 1777 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: generateManagedResourceName(name) + "-token"}, gomock.Not(gomock.Nil()), gomock.Any()). 1778 Return(errors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, generateManagedResourceName(name)+"-token")) 1779 1780 // Expect a call to create the Token Secret - return success 1781 mock.EXPECT(). 1782 Create(gomock.Any(), gomock.Any(), gomock.Any()). 1783 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 1784 asserts.Equalf(constants.VerrazzanoMultiClusterNamespace, secret.Namespace, "Secret namespace did not match") 1785 asserts.Equalf(generateManagedResourceName(name)+"-token", secret.Name, "Secret testManagedCluster did not match") 1786 return nil 1787 }) 1788 1789 // Expect a call to update the VerrazzanoManagedCluster service account name - return success or 1790 // failure depending on the succeed argument 1791 mock.EXPECT(). 1792 Update(gomock.Any(), gomock.Any(), gomock.Any()). 1793 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 1794 if succeed { 1795 asserts.Equal(vmc.Spec.ServiceAccount, generateManagedResourceName(name), "ServiceAccount testManagedCluster did not match") 1796 return nil 1797 } 1798 return errors.NewInternalError(fmt.Errorf("failing syncServiceAccount")) 1799 }) 1800 } 1801 1802 // Expect syncAgent related calls 1803 func expectSyncAgent(t *testing.T, mock *mocks.MockClient, name string, rancherEnabled bool, noSASecret bool) { 1804 saSecretName := "saSecret" 1805 rancherURL := "http://rancher-url" 1806 rancherCAData := "rancherCAData" 1807 userAPIServerURL := "https://testurl" 1808 if noSASecret { 1809 // Expect a call to get the ServiceAccount, return without the secret set 1810 mock.EXPECT(). 1811 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: generateManagedResourceName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1812 DoAndReturn(func(ctx context.Context, name types.NamespacedName, sa *corev1.ServiceAccount, opts ...client.GetOption) error { 1813 sa.Name = name.Name 1814 return nil 1815 }) 1816 // Set the secret name to the service account token created by the VMC controller 1817 saSecretName = generateManagedResourceName(name) + "-token" 1818 1819 } else { 1820 // Expect a call to get the ServiceAccount, return one with the secret testManagedCluster set 1821 mock.EXPECT(). 1822 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: generateManagedResourceName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1823 DoAndReturn(func(ctx context.Context, name types.NamespacedName, sa *corev1.ServiceAccount, opts ...client.GetOption) error { 1824 sa.Secrets = []corev1.ObjectReference{{ 1825 Name: saSecretName, 1826 }} 1827 return nil 1828 }) 1829 } 1830 1831 // Expect a call to list Verrazzanos and return a Verrazzano that has Rancher URL in status only 1832 // if rancherEnabled is true 1833 mock.EXPECT(). 1834 List(gomock.Any(), &v1beta1.VerrazzanoList{}, gomock.Not(gomock.Nil())). 1835 DoAndReturn(func(ctx context.Context, list *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 1836 var status v1beta1.VerrazzanoStatus 1837 if rancherEnabled { 1838 status = v1beta1.VerrazzanoStatus{ 1839 VerrazzanoInstance: &v1beta1.InstanceInfo{RancherURL: &rancherURL}, 1840 } 1841 } 1842 vz := v1beta1.Verrazzano{ 1843 Spec: v1beta1.VerrazzanoSpec{}, 1844 Status: status, 1845 } 1846 list.Items = append(list.Items, vz) 1847 return nil 1848 }) 1849 1850 // Expect a call to get the service token secret, return the secret with the token 1851 mock.EXPECT(). 1852 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: saSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 1853 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1854 secret.Data = map[string][]byte{ 1855 mcconstants.TokenKey: []byte(token), 1856 } 1857 return nil 1858 }) 1859 1860 if rancherEnabled { 1861 // Expect a call to get the tls-ca secret, return the secret as not found 1862 mock.EXPECT(). 1863 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.PrivateCABundle}, gomock.Not(gomock.Nil()), gomock.Any()). 1864 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoSystemNamespace, Resource: "Secret"}, constants.PrivateCABundle)) 1865 1866 // Expect a call to get the Rancher ingress tls secret, return the secret with the fields set 1867 mock.EXPECT(). 1868 Get(gomock.Any(), types.NamespacedName{Namespace: constants.RancherSystemNamespace, Name: rancherTLSSecret}, gomock.Not(gomock.Nil()), gomock.Any()). 1869 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1870 secret.Data = map[string][]byte{ 1871 mcconstants.CaCrtKey: []byte(rancherCAData), 1872 } 1873 return nil 1874 }) 1875 } else { 1876 // Expect a call to get the verrazzano-admin-cluster configmap 1877 mock.EXPECT(). 1878 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: vpoconstants.AdminClusterConfigMapName}, gomock.Not(gomock.Nil()), gomock.Any()). 1879 DoAndReturn(func(ctx context.Context, name types.NamespacedName, cm *corev1.ConfigMap, opts ...client.GetOption) error { 1880 cm.Data = map[string]string{ 1881 vpoconstants.ServerDataKey: userAPIServerURL, 1882 } 1883 return nil 1884 }) 1885 } 1886 1887 // Expect a call to get the Agent secret - return that it does not exist 1888 mock.EXPECT(). 1889 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetAgentSecretName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1890 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoMultiClusterNamespace, Resource: "Secret"}, GetAgentSecretName(name))) 1891 1892 // Expect a call to create the Agent secret 1893 mock.EXPECT(). 1894 Create(gomock.Any(), gomock.Any(), gomock.Any()). 1895 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 1896 adminKubeconfig := string(secret.Data[mcconstants.KubeconfigKey]) 1897 if rancherEnabled { 1898 assert.Contains(t, adminKubeconfig, "server: "+rancherURL) 1899 } else { 1900 assert.Contains(t, adminKubeconfig, "server: "+userAPIServerURL) 1901 } 1902 return nil 1903 }) 1904 } 1905 1906 // Expect syncRegistration related calls 1907 func expectSyncRegistration(t *testing.T, mock *mocks.MockClient, name string, externalES bool) { 1908 const vzEsURLData = "https://vz-testhost:443" 1909 const vzUserData = "vz-user" 1910 const vzPasswordData = "vz-pw" 1911 const vzCaData = "vz-ca" 1912 1913 const externalEsURLData = "https://external-testhost:443" 1914 const externalUserData = "external-user" 1915 const externalPasswordData = "external-pw" 1916 const externalCaData = "external-ca" 1917 1918 fluentdESURL := "http://verrazzano-authproxy-opensearch:8775" 1919 fluentdESSecret := "verrazzano" 1920 esSecret := constants.VerrazzanoESInternal 1921 if externalES { 1922 fluentdESURL = externalEsURLData 1923 fluentdESSecret = "some-external-es-secret" 1924 esSecret = fluentdESSecret 1925 } 1926 1927 asserts := assert.New(t) 1928 1929 // Expect a call to get the registration secret - return that it does not exist 1930 mock.EXPECT(). 1931 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetRegistrationSecretName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 1932 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoMultiClusterNamespace, Resource: "Secret"}, GetRegistrationSecretName(name))) 1933 1934 // Expect a call to list Verrazzanos - return the Verrazzano configured with fluentd 1935 mock.EXPECT(). 1936 List(gomock.Any(), &v1beta1.VerrazzanoList{}, gomock.Not(gomock.Nil())). 1937 DoAndReturn(func(ctx context.Context, list *v1beta1.VerrazzanoList, opts ...client.ListOption) error { 1938 vz := v1beta1.Verrazzano{ 1939 Spec: v1beta1.VerrazzanoSpec{ 1940 Components: v1beta1.ComponentSpec{ 1941 Fluentd: &v1beta1.FluentdComponent{ 1942 OpenSearchURL: fluentdESURL, 1943 OpenSearchSecret: fluentdESSecret, 1944 }, 1945 }, 1946 }, 1947 } 1948 list.Items = append(list.Items, vz) 1949 return nil 1950 }).Times(5) 1951 1952 // Expect a call to get the tls ingress and return the ingress. 1953 if !externalES { 1954 mock.EXPECT(). 1955 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: operatorOSIngress}, gomock.Not(gomock.Nil()), gomock.Any()). 1956 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 1957 ingress.TypeMeta = metav1.TypeMeta{ 1958 APIVersion: "networking.k8s.io/v1", 1959 Kind: "ingress"} 1960 ingress.ObjectMeta = metav1.ObjectMeta{ 1961 Namespace: name.Namespace, 1962 Name: name.Name} 1963 ingress.Spec.Rules = []networkingv1.IngressRule{{ 1964 Host: "vz-testhost", 1965 }} 1966 return nil 1967 }) 1968 } 1969 1970 // Expect a call to get the opensearch secret, return the secret with the fields set 1971 mock.EXPECT(). 1972 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: esSecret}, gomock.Not(gomock.Nil()), gomock.Any()). 1973 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1974 if externalES { 1975 secret.Data = map[string][]byte{ 1976 mcconstants.VerrazzanoUsernameKey: []byte(externalUserData), 1977 mcconstants.VerrazzanoPasswordKey: []byte(externalPasswordData), 1978 mcconstants.FluentdESCaBundleKey: []byte(externalCaData), 1979 } 1980 } else { 1981 secret.Data = map[string][]byte{ 1982 mcconstants.VerrazzanoUsernameKey: []byte(vzUserData), 1983 mcconstants.VerrazzanoPasswordKey: []byte(vzPasswordData), 1984 } 1985 } 1986 return nil 1987 }) 1988 1989 // Expect a call to get the tls secret, return the secret with the fields set 1990 mock.EXPECT(). 1991 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: "verrazzano-tls"}, gomock.Not(gomock.Nil()), gomock.Any()). 1992 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 1993 secret.Data = map[string][]byte{ 1994 mcconstants.CaCrtKey: []byte(vzCaData), 1995 } 1996 return nil 1997 }) 1998 1999 // Expect a call to get the tls-ca secret, return the secret as not found 2000 mock.EXPECT(). 2001 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.PrivateCABundle}, gomock.Not(gomock.Nil()), gomock.Any()). 2002 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoSystemNamespace, Resource: "Secret"}, constants.PrivateCABundle)) 2003 2004 // Expect a call to get the keycloak ingress and return the ingress. 2005 mock.EXPECT(). 2006 Get(gomock.Any(), types.NamespacedName{Namespace: "keycloak", Name: "keycloak"}, gomock.Not(gomock.Nil()), gomock.Any()). 2007 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 2008 ingress.TypeMeta = metav1.TypeMeta{ 2009 APIVersion: "networking.k8s.io/v1", 2010 Kind: "ingress"} 2011 ingress.ObjectMeta = metav1.ObjectMeta{ 2012 Namespace: name.Namespace, 2013 Name: name.Name} 2014 ingress.Spec.Rules = []networkingv1.IngressRule{{ 2015 Host: "keycloak", 2016 }} 2017 return nil 2018 }) 2019 2020 // Expect a call to get the dex ingress and return the ingress. 2021 mock.EXPECT(). 2022 Get(gomock.Any(), types.NamespacedName{Namespace: "verrazzano-auth", Name: "dex"}, gomock.Not(gomock.Nil()), gomock.Any()). 2023 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 2024 ingress.TypeMeta = metav1.TypeMeta{ 2025 APIVersion: "networking.k8s.io/v1", 2026 Kind: "ingress"} 2027 ingress.ObjectMeta = metav1.ObjectMeta{ 2028 Namespace: name.Namespace, 2029 Name: name.Name} 2030 ingress.Spec.Rules = []networkingv1.IngressRule{{ 2031 Host: "auth", 2032 }} 2033 return nil 2034 }) 2035 2036 // Expect a call to create the registration secret 2037 mock.EXPECT(). 2038 Create(gomock.Any(), gomock.Any(), gomock.Any()). 2039 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 2040 asserts.Equalf(testManagedCluster, string(secret.Data[mcconstants.ManagedClusterNameKey]), "Incorrect cluster testManagedCluster in Registration secret ") 2041 asserts.Equalf("https://keycloak", string(secret.Data[mcconstants.KeycloakURLKey]), "Incorrect admin ca bundle in Registration secret ") 2042 asserts.Equalf(vzCaData, string(secret.Data[mcconstants.AdminCaBundleKey]), "Incorrect admin ca bundle in Registration secret ") 2043 if externalES { 2044 asserts.Equalf(externalEsURLData, string(secret.Data[mcconstants.ESURLKey]), "Incorrect ES URL in Registration secret ") 2045 asserts.Equalf(externalCaData, string(secret.Data[mcconstants.ESCaBundleKey]), "Incorrect ES ca bundle in Registration secret ") 2046 asserts.Equalf(externalUserData, string(secret.Data[mcconstants.RegistrationUsernameKey]), "Incorrect ES user in Registration secret ") 2047 asserts.Equalf(externalPasswordData, string(secret.Data[mcconstants.RegistrationPasswordKey]), "Incorrect ES password in Registration secret ") 2048 } else { 2049 asserts.Equalf(vzEsURLData, string(secret.Data[mcconstants.ESURLKey]), "Incorrect ES URL in Registration secret ") 2050 asserts.Equalf(vzCaData, string(secret.Data[mcconstants.ESCaBundleKey]), "Incorrect ES ca bundle in Registration secret ") 2051 asserts.Equalf(vzUserData, string(secret.Data[mcconstants.RegistrationUsernameKey]), "Incorrect ES user in Registration secret ") 2052 asserts.Equalf(vzPasswordData, string(secret.Data[mcconstants.RegistrationPasswordKey]), "Incorrect ES password in Registration secret ") 2053 } 2054 return nil 2055 }) 2056 } 2057 2058 // Expect syncManifest related calls 2059 func expectSyncManifest(t *testing.T, mock *mocks.MockClient, mockStatus *mocks.MockStatusWriter, mockRequestSender *mocks.MockRequestSender, 2060 name string, clusterAlreadyRegistered bool, expectedRancherYAML string) { 2061 2062 asserts := assert.New(t) 2063 clusterName := "cluster1" 2064 caData := "ca" 2065 userData := "user" 2066 passwordData := "pw" 2067 kubeconfigData := "fakekubeconfig" 2068 urlData := "https://testhost:443" 2069 2070 // Expect a call to get the Agent secret 2071 mock.EXPECT(). 2072 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetAgentSecretName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 2073 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2074 secret.Data = map[string][]byte{ 2075 mcconstants.KubeconfigKey: []byte(kubeconfigData), 2076 } 2077 return nil 2078 }) 2079 2080 // Expect a call to get the registration secret 2081 mock.EXPECT(). 2082 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetRegistrationSecretName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 2083 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2084 secret.Data = map[string][]byte{ 2085 mcconstants.ManagedClusterNameKey: []byte(clusterName), 2086 mcconstants.CaCrtKey: []byte(caData), 2087 mcconstants.RegistrationUsernameKey: []byte(userData), 2088 mcconstants.RegistrationPasswordKey: []byte(passwordData), 2089 mcconstants.ESURLKey: []byte(urlData), 2090 } 2091 return nil 2092 }) 2093 2094 // Expect a call to get the manifest secret - return that it does not exist 2095 mock.EXPECT(). 2096 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: GetManifestSecretName(name)}, gomock.Not(gomock.Nil()), gomock.Any()). 2097 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoMultiClusterNamespace, Resource: "Secret"}, GetManifestSecretName(name))) 2098 2099 // Expect all the calls needed to register the cluster with Rancher 2100 expectRegisterClusterWithRancher(t, mock, mockRequestSender, name, clusterAlreadyRegistered, expectedRancherYAML) 2101 2102 // Expect to get existing VMC for status update 2103 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 2104 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 2105 return nil 2106 }) 2107 2108 mock.EXPECT().Status().Return(mockStatus) 2109 mockStatus.EXPECT(). 2110 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 2111 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 2112 asserts.Equal(v1alpha1.RegistrationCompleted, vmc.Status.RancherRegistration.Status) 2113 asserts.Equal(unitTestRancherClusterID, vmc.Status.RancherRegistration.ClusterID) 2114 asserts.Equal(fmt.Sprintf("Registration of managed cluster completed successfully for cluster test with ID %s", unitTestRancherClusterID), 2115 vmc.Status.RancherRegistration.Message) 2116 return nil 2117 }) 2118 2119 // Expect a call to create the manifest secret 2120 mock.EXPECT(). 2121 Create(gomock.Any(), gomock.Any(), gomock.Any()). 2122 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 2123 data := secret.Data[mcconstants.YamlKey] 2124 asserts.NotZero(len(data), "Expected yaml data in manifest secret") 2125 2126 // YAML should contain the Rancher manifest things 2127 yamlString := string(data) 2128 asserts.True(strings.Contains(yamlString, expectedRancherYAML), "Manifest YAML does not contain the correct Rancher resources") 2129 2130 return nil 2131 }) 2132 2133 // Expect a call to update the VerrazzanoManagedCluster kubeconfig secret testManagedCluster - return success 2134 mock.EXPECT(). 2135 Update(gomock.Any(), gomock.Any(), gomock.Any()). 2136 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 2137 asserts.Equal(vmc.Spec.ManagedClusterManifestSecret, GetManifestSecretName(name), "Manifest secret testManagedCluster did not match") 2138 return nil 2139 }) 2140 } 2141 2142 func expectPushManifestRequests(t *testing.T, mockRequestSender *mocks.MockRequestSender, mock *mocks.MockClient) { 2143 // expect a login request for the vz-cluster-reg user 2144 expectRancherGetAuthTokenHTTPCall(t, mockRequestSender) 2145 expectRancherConfigK8sCalls(t, mock, true) 2146 addVerrazzanoSystemNamespaceMock(mockRequestSender, unitTestRancherClusterID, true) 2147 2148 mockRequestSender.EXPECT(). 2149 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clustersPath+"/"+unitTestRancherClusterID)). 2150 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2151 var resp *http.Response 2152 r := io.NopCloser(bytes.NewReader([]byte(`{"state":"inactive", "agentImage":"test"}`))) 2153 resp = &http.Response{ 2154 StatusCode: http.StatusOK, 2155 Body: r, 2156 } 2157 return resp, nil 2158 }) 2159 } 2160 2161 func expectVmcGetAndUpdate(t *testing.T, mock *mocks.MockClient, name string, caSecretExists bool, rancherClusterAlreadyRegistered bool) { 2162 asserts := assert.New(t) 2163 labels := map[string]string{"label1": "test"} 2164 2165 // Expect a call to get the VerrazzanoManagedCluster resource. 2166 mock.EXPECT(). 2167 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: name}, gomock.Not(gomock.Nil()), gomock.Any()). 2168 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 2169 vmc.TypeMeta = metav1.TypeMeta{ 2170 APIVersion: apiVersion, 2171 Kind: kind} 2172 vmc.ObjectMeta = metav1.ObjectMeta{ 2173 Namespace: name.Namespace, 2174 Name: name.Name, 2175 Labels: labels} 2176 if caSecretExists { 2177 vmc.Spec = v1alpha1.VerrazzanoManagedClusterSpec{ 2178 CASecret: getCASecretName(name.Name), 2179 } 2180 } 2181 vmc.Status = v1alpha1.VerrazzanoManagedClusterStatus{ 2182 PrometheusHost: getPrometheusHost(), 2183 } 2184 if rancherClusterAlreadyRegistered { 2185 vmc.Status.RancherRegistration = v1alpha1.RancherRegistration{ 2186 Status: v1alpha1.RegistrationCompleted, 2187 ClusterID: unitTestRancherClusterID, 2188 } 2189 } 2190 return nil 2191 }) 2192 2193 // Expect a call to update the VerrazzanoManagedCluster finalizer 2194 mock.EXPECT(). 2195 Update(gomock.Any(), gomock.Any(), gomock.Any()). 2196 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 2197 asserts.True(len(vmc.ObjectMeta.Finalizers) == 1, "Wrong number of finalizers") 2198 asserts.Equal(finalizerName, vmc.ObjectMeta.Finalizers[0], "wrong finalizer") 2199 return nil 2200 }) 2201 2202 } 2203 2204 func expectSyncPrometheusScraper(mock *mocks.MockClient, vmcName string, prometheusYaml string, jobs string, caSecretExists bool, cacrtSecretData string, 2205 f AssertFn, additionalScrapeConfigsAssertFunc secretAssertFn, managedClusterCertsAssertFunc secretAssertFn) { 2206 const internalSecretPassword = "nRXlxXgMwN" //nolint:gosec //#gosec G101 2207 2208 if caSecretExists { 2209 // Expect a call to get the prometheus secret - return it 2210 mock.EXPECT(). 2211 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: getCASecretName(vmcName)}, gomock.Not(gomock.Nil()), gomock.Any()). 2212 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2213 2214 secret.Data = map[string][]byte{ 2215 "cacrt": []byte(cacrtSecretData), 2216 } 2217 return nil 2218 }) 2219 } 2220 2221 // Expect a call to get the Verrazzano Prometheus internal secret - return it 2222 mock.EXPECT(). 2223 Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.VerrazzanoPromInternal}, gomock.Not(gomock.Nil()), gomock.Any()). 2224 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2225 secret.Data = map[string][]byte{ 2226 mcconstants.VerrazzanoPasswordKey: []byte(internalSecretPassword), 2227 } 2228 return nil 2229 }) 2230 2231 // Expect a call to get the additional scrape config secret - return it 2232 mock.EXPECT(). 2233 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: constants.PromAdditionalScrapeConfigsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2234 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2235 secret.Data = map[string][]byte{ 2236 constants.PromAdditionalScrapeConfigsSecretKey: []byte(jobs), 2237 } 2238 return nil 2239 }) 2240 2241 // Expect a call to get the additional scrape config secret (we call controllerruntime.CreateOrUpdate so it fetches again) - return it 2242 mock.EXPECT(). 2243 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: constants.PromAdditionalScrapeConfigsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2244 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2245 secret.Data = map[string][]byte{ 2246 constants.PromAdditionalScrapeConfigsSecretKey: []byte(jobs), 2247 } 2248 return nil 2249 }) 2250 2251 // Expect a call to update the additional scrape config secret 2252 mock.EXPECT(). 2253 Update(gomock.Any(), gomock.Any(), gomock.Any()). 2254 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.UpdateOption) error { 2255 return additionalScrapeConfigsAssertFunc(secret) 2256 }) 2257 2258 // Expect a call to get the verrazzano-monitoring namespace 2259 mock.EXPECT(). 2260 Get(gomock.Any(), types.NamespacedName{Name: vpoconstants.VerrazzanoMonitoringNamespace}, gomock.Not(gomock.Nil()), gomock.Any()). 2261 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ns *corev1.Namespace, opts ...client.GetOption) error { 2262 ns.SetName(vpoconstants.VerrazzanoMonitoringNamespace) 2263 return nil 2264 }) 2265 2266 // Expect a call to get the managed cluster TLS certs secret - return NotFound error 2267 mock.EXPECT(). 2268 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: vpoconstants.PromManagedClusterCACertsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2269 Return(errors.NewNotFound(schema.GroupResource{Group: "", Resource: "Secret"}, vpoconstants.PromManagedClusterCACertsSecretName)) 2270 2271 // Expect a call to update the managed cluster TLS certs secret 2272 mock.EXPECT(). 2273 Create(gomock.Any(), gomock.Any(), gomock.Any()). 2274 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.UpdateOption) error { 2275 return managedClusterCertsAssertFunc(secret) 2276 }) 2277 2278 } 2279 2280 // expectRegisterClusterWithRancher asserts all of the expected calls on the Kubernetes client mock and the HTTP client mock 2281 func expectRegisterClusterWithRancher(t *testing.T, 2282 k8sMock *mocks.MockClient, 2283 requestSenderMock *mocks.MockRequestSender, 2284 clusterName string, 2285 clusterAlreadyRegistered bool, 2286 expectedRancherYAML string) { 2287 2288 expectRancherConfigK8sCalls(t, k8sMock, true) 2289 expectRegisterClusterWithRancherHTTPCalls(t, requestSenderMock, clusterName, clusterAlreadyRegistered, expectedRancherYAML) 2290 } 2291 2292 // expectRegisterClusterWithRancherHTTPCalls asserts all of the expected calls on the HTTP client mock 2293 func expectRegisterClusterWithRancherHTTPCalls(t *testing.T, requestSenderMock *mocks.MockRequestSender, clusterName string, clusterAlreadyRegistered bool, rancherManifestYAML string) { 2294 asserts := assert.New(t) 2295 2296 expectRancherGetAuthTokenHTTPCall(t, requestSenderMock) 2297 2298 if !clusterAlreadyRegistered { 2299 // Cluster is not already registered - we now only expect import to happen if the cluster is NOT already registered 2300 // Expect an HTTP request to import the cluster to Rancher 2301 requestSenderMock.EXPECT(). 2302 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterPath)). 2303 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2304 asserts.Equal(clusterPath, req.URL.Path) 2305 2306 var resp *http.Response 2307 r := io.NopCloser(bytes.NewReader([]byte(`{"id":"` + unitTestRancherClusterID + `"}`))) 2308 resp = &http.Response{ 2309 StatusCode: http.StatusCreated, 2310 Body: r, 2311 } 2312 return resp, nil 2313 }) 2314 } 2315 2316 manifestToken := "unit-test-manifest-token" 2317 2318 // Expect an HTTP request to get the registration token in Rancher 2319 requestSenderMock.EXPECT(). 2320 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterRegTokenPath)). 2321 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2322 asserts.Contains(clusterRegTokenPath, req.URL.Path) 2323 2324 // assert that the cluster ID in the request body is what we expect 2325 _, err := io.ReadAll(req.Body) 2326 asserts.NoError(err) 2327 2328 // return a response with the empty data, no CRT exists for this cluster 2329 r := io.NopCloser(bytes.NewReader([]byte(`{"data":[]}`))) 2330 resp := &http.Response{ 2331 StatusCode: http.StatusOK, 2332 Body: r, 2333 Request: &http.Request{Method: http.MethodGet}, 2334 } 2335 return resp, nil 2336 }) 2337 2338 // Expect an HTTP request to create the registration token in Rancher 2339 requestSenderMock.EXPECT(). 2340 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(clusterRegTokenPath)). 2341 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2342 asserts.Equal(clusterRegTokenPath, req.URL.Path) 2343 2344 // assert that the cluster ID in the request body is what we expect 2345 body, err := io.ReadAll(req.Body) 2346 asserts.NoError(err) 2347 jsonString, err := gabs.ParseJSON(body) 2348 asserts.NoError(err) 2349 clusterID, ok := jsonString.Path("clusterId").Data().(string) 2350 asserts.True(ok) 2351 asserts.Equal(unitTestRancherClusterID, clusterID) 2352 2353 // return a response with the manifest token 2354 r := io.NopCloser(bytes.NewReader([]byte(`{"token":"` + manifestToken + `"}`))) 2355 resp := &http.Response{ 2356 StatusCode: http.StatusCreated, 2357 Body: r, 2358 Request: &http.Request{Method: http.MethodPost}, 2359 } 2360 return resp, nil 2361 }) 2362 2363 // Expect an HTTP request to fetch the manifest YAML from Rancher 2364 requestSenderMock.EXPECT(). 2365 Do(gomock.Not(gomock.Nil()), gomock.Not(gomock.Nil())). 2366 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2367 asserts.Equal(manifestPath+manifestToken+"_"+unitTestRancherClusterID+".yaml", req.URL.Path) 2368 2369 r := io.NopCloser(bytes.NewReader([]byte(rancherManifestYAML))) 2370 resp := &http.Response{ 2371 StatusCode: http.StatusOK, 2372 Body: r, 2373 Request: &http.Request{Method: http.MethodGet}, 2374 } 2375 return resp, nil 2376 }) 2377 } 2378 2379 func expectRancherGetAuthTokenHTTPCall(t *testing.T, requestSenderMock *mocks.MockRequestSender) { 2380 asserts := assert.New(t) 2381 2382 // Expect an HTTP request to fetch the admin token from Rancher 2383 requestSenderMock.EXPECT(). 2384 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(loginURIPath)). 2385 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2386 asserts.Equal(loginQueryString, req.URL.RawQuery) 2387 2388 r := io.NopCloser(bytes.NewReader([]byte(`{"token":"unit-test-token"}`))) 2389 resp := &http.Response{ 2390 StatusCode: http.StatusCreated, 2391 Body: r, 2392 Request: &http.Request{Method: http.MethodPost}, 2393 } 2394 return resp, nil 2395 }).AnyTimes() 2396 2397 } 2398 2399 func expectThanosDelete(t *testing.T, mock *mocks.MockClient) { 2400 // Expect a call to get the Istio CRDs 2401 mock.EXPECT(). 2402 Get(gomock.Any(), gomock.Eq(client.ObjectKey{Name: destinationRuleCRDName}), gomock.AssignableToTypeOf(&apiv1.CustomResourceDefinition{}), gomock.Any()). 2403 DoAndReturn(func(ctx context.Context, key client.ObjectKey, crd *apiv1.CustomResourceDefinition, opts ...client.GetOption) error { 2404 return errors.NewNotFound(schema.GroupResource{}, "not found") 2405 }) 2406 mock.EXPECT(). 2407 Get(gomock.Any(), gomock.Eq(client.ObjectKey{Name: serviceEntryCRDName}), gomock.AssignableToTypeOf(&apiv1.CustomResourceDefinition{}), gomock.Any()). 2408 DoAndReturn(func(ctx context.Context, key client.ObjectKey, crd *apiv1.CustomResourceDefinition, opts ...client.GetOption) error { 2409 return errors.NewNotFound(schema.GroupResource{}, "not found") 2410 }) 2411 } 2412 2413 // expectSyncCACertRancherHTTPCalls asserts all of the expected calls on the HTTP client mock when sync'ing the managed cluster 2414 // CA cert secret 2415 func expectSyncCACertRancherHTTPCalls(t *testing.T, requestSenderMock *mocks.MockRequestSender, caCertSecretData string) { 2416 // Expect an HTTP request to fetch the managed cluster info from Rancher 2417 fetchClusterPath := fmt.Sprintf("/v3/clusters/%s", unitTestRancherClusterID) 2418 requestSenderMock.EXPECT(). 2419 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(fetchClusterPath)). 2420 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2421 2422 r := io.NopCloser(bytes.NewReader([]byte(`{"state":"active","agentImage":"test-image:1.0.0"}`))) 2423 resp := &http.Response{ 2424 StatusCode: http.StatusOK, 2425 Body: r, 2426 Request: &http.Request{Method: http.MethodGet}, 2427 } 2428 return resp, nil 2429 }) 2430 2431 // Expect an HTTP request to fetch the Rancher TLS additional CA secret from the managed cluster and return an HTTP 404 2432 managedClusterAdditionalTLSCAPath := fmt.Sprintf("/k8s/clusters/%s/api/v1/namespaces/cattle-system/secrets/tls-ca", unitTestRancherClusterID) 2433 requestSenderMock.EXPECT(). 2434 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(managedClusterAdditionalTLSCAPath)). 2435 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2436 r := io.NopCloser(bytes.NewReader([]byte{})) 2437 resp := &http.Response{ 2438 StatusCode: http.StatusNotFound, 2439 Body: r, 2440 Request: &http.Request{Method: http.MethodGet}, 2441 } 2442 return resp, nil 2443 }) 2444 2445 // Expect an HTTP request to fetch the Verrazzano system TLS CA secret from the managed cluster and return the secret 2446 managedClusterSystemCAPath := fmt.Sprintf("/k8s/clusters/%s/api/v1/namespaces/verrazzano-system/secrets/verrazzano-tls", unitTestRancherClusterID) 2447 requestSenderMock.EXPECT(). 2448 Do(gomock.Not(gomock.Nil()), mockmatchers.MatchesURI(managedClusterSystemCAPath)). 2449 DoAndReturn(func(httpClient *http.Client, req *http.Request) (*http.Response, error) { 2450 statusCode := http.StatusOK 2451 if len(caCertSecretData) == 0 { 2452 statusCode = http.StatusNotFound 2453 } 2454 r := io.NopCloser(bytes.NewReader([]byte(caCertSecretData))) 2455 resp := &http.Response{ 2456 StatusCode: statusCode, 2457 Body: r, 2458 Request: &http.Request{Method: http.MethodGet}, 2459 } 2460 return resp, nil 2461 }) 2462 } 2463 2464 // expectSyncCACertRancherK8sCalls asserts all of the expected calls on the Kubernetes client mock when sync'ing the managed cluster 2465 // CA cert secret 2466 func expectSyncCACertRancherK8sCalls(t *testing.T, k8sMock *mocks.MockClient, mockRequestSender *mocks.MockRequestSender, shouldSyncCACert bool) { 2467 asserts := assert.New(t) 2468 2469 if shouldSyncCACert { 2470 expectRancherConfigK8sCalls(t, k8sMock, true) 2471 2472 // Expect a call to get the CA cert secret for the managed cluster - return not found 2473 k8sMock.EXPECT(). 2474 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: "ca-secret-test"}), gomock.Not(gomock.Nil()), gomock.Any()). 2475 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoMultiClusterNamespace, Resource: "Secret"}, "ca-secret-test")) 2476 2477 // Expect a call to create the CA cert secret for the managed cluster 2478 k8sMock.EXPECT(). 2479 Create(gomock.Any(), gomock.Any(), gomock.Any()). 2480 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.CreateOption) error { 2481 data := secret.Data[caCertSecretKey] 2482 asserts.NotZero(len(data), "Expected data in CA cert secret") 2483 return nil 2484 }) 2485 2486 // Expect a call to update the VMC with ca secret name 2487 k8sMock.EXPECT(). 2488 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 2489 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 2490 asserts.Equal(vmc.Spec.CASecret, getCASecretName(vmc.Name), "CA secret name %s did not match", vmc.Spec.CASecret) 2491 return nil 2492 }) 2493 } 2494 } 2495 2496 // getScrapeConfig gets a representation of the vmc scrape configuration from the provided yaml 2497 func getScrapeConfig(prometheusYaml string, name string) (*gabs.Container, error) { 2498 cfg, err := parsePrometheusConfig(prometheusYaml) 2499 if err != nil { 2500 return nil, err 2501 } 2502 scrapeConfigs := cfg.Path(scrapeConfigsKey).Children() 2503 return getJob(scrapeConfigs, name), nil 2504 } 2505 2506 // getJob returns the scrape config job identified by the passed name from the slice of scrape configs 2507 func getJob(scrapeConfigs []*gabs.Container, name string) *gabs.Container { 2508 var job *gabs.Container 2509 for _, scrapeConfig := range scrapeConfigs { 2510 jobName := scrapeConfig.Search(constants.PrometheusJobNameKey).Data() 2511 if jobName == name { 2512 job = scrapeConfig 2513 break 2514 } 2515 } 2516 return job 2517 } 2518 2519 // getPrometheusHost returns the prometheus host for testManagedCluster 2520 func getPrometheusHost() string { 2521 return "prometheus.vmi.system.default.1.2.3.4.nip.io" 2522 } 2523 2524 // getPrometheusHost returns the prometheus host for testManagedCluster 2525 func getCaCrt() string { 2526 // this is fake data 2527 return " -----BEGIN CERTIFICATE-----\n" + 2528 " MIIBiDCCAS6gAwIBAgIBADAKBggqhkjOPQQDAjA7MRwwGgYDVQQKExNkeW5hbWlj\n" + 2529 " -----END CERTIFICATE-----" 2530 } 2531 2532 func expectStatusUpdateReadyCondition(asserts *assert.Assertions, mock *mocks.MockClient, mockStatus *mocks.MockStatusWriter, expectReady corev1.ConditionStatus, msg string, assertManagedCACondition bool) { 2533 mock.EXPECT().Get(gomock.Any(), types.NamespacedName{Namespace: constants.VerrazzanoMultiClusterNamespace, Name: testManagedCluster}, gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 2534 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 2535 return nil 2536 }) 2537 2538 mock.EXPECT().Status().Return(mockStatus) 2539 mockStatus.EXPECT(). 2540 Update(gomock.Any(), gomock.AssignableToTypeOf(&v1alpha1.VerrazzanoManagedCluster{}), gomock.Any()). 2541 DoAndReturn(func(ctx context.Context, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.UpdateOption) error { 2542 readyFound := false 2543 managedCaFound := false 2544 readyConditionCount := 0 2545 managedCaConditionCount := 0 2546 for _, condition := range vmc.Status.Conditions { 2547 if condition.Type == v1alpha1.ConditionReady { 2548 readyConditionCount++ 2549 if condition.Status == expectReady { 2550 readyFound = true 2551 asserts.Contains(condition.Message, msg) 2552 } 2553 } 2554 if condition.Type == v1alpha1.ConditionManagedCARetrieved { 2555 managedCaConditionCount++ 2556 if condition.Status == expectReady { 2557 managedCaFound = true 2558 // asserts.Contains(condition.Message, msg) 2559 } 2560 } 2561 } 2562 asserts.True(readyFound, "Expected Ready condition on VMC not found") 2563 asserts.Equal(1, readyConditionCount, "Found more than one Ready condition") 2564 if assertManagedCACondition { 2565 asserts.True(managedCaFound, "Expected ManagedCARetrieved condition on VMC not found") 2566 asserts.Equal(1, managedCaConditionCount, "Found more than one ManagedCARetrieved condition") 2567 } 2568 2569 return nil 2570 }) 2571 } 2572 2573 // validateScrapeConfig validates that a scrape config job has the expected field names and values 2574 func validateScrapeConfig(t *testing.T, scrapeConfig *gabs.Container, caBasePath string, expectTLSConfig bool) { 2575 asserts := assert.New(t) 2576 asserts.NotNil(scrapeConfig) 2577 asserts.Equal(getPrometheusHost(), 2578 scrapeConfig.Search("static_configs", "0", "targets", "0").Data(), "No host entry found") 2579 asserts.NotEmpty(scrapeConfig.Search("basic_auth", "password").Data(), "No password") 2580 2581 // assert that the verrazzano_cluster label is added in the static config 2582 asserts.Equal(testManagedCluster, scrapeConfig.Search( 2583 "static_configs", "0", "labels", "verrazzano_cluster").Data(), 2584 "Label verrazzano_cluster not set correctly in static_configs") 2585 2586 // assert that the VMC job relabels verrazzano_cluster label to the right value 2587 asserts.Equal("verrazzano_cluster", scrapeConfig.Search("metric_relabel_configs", "0", 2588 "target_label").Data(), 2589 "metric_relabel_configs entry to post-process verrazzano_cluster label does not have expected target_label value") 2590 asserts.Equal(testManagedCluster, scrapeConfig.Search("metric_relabel_configs", "0", 2591 "replacement").Data(), 2592 "metric_relabel_configs entry to post-process verrazzano_cluster label does not have right replacement value") 2593 schemePath := scrapeConfig.Path("scheme") 2594 tlsScrapeConfig := scrapeConfig.Search("tls_config", "ca_file") 2595 if expectTLSConfig { 2596 asserts.Equal("https", schemePath.Data(), "wrong scheme") 2597 assert.NotNil(t, tlsScrapeConfig) 2598 asserts.Equal(caBasePath+"ca-test", tlsScrapeConfig.Data(), "Wrong cert path") 2599 } else { 2600 assert.Nil(t, tlsScrapeConfig) 2601 asserts.Equal("https", schemePath.Data(), "wrong scheme") 2602 } 2603 } 2604 2605 // expectRancherConfigK8sCalls asserts all of the expected calls on the Kubernetes client mock 2606 // when creating a new Rancher config for the purpose of making http calls 2607 func expectRancherConfigK8sCalls(t *testing.T, k8sMock *mocks.MockClient, admin bool) { 2608 // Expect a call to get the ingress host name 2609 k8sMock.EXPECT(). 2610 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherIngressName}), gomock.Not(gomock.Nil()), gomock.Any()). 2611 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, ingress *networkingv1.Ingress, opts ...client.GetOption) error { 2612 rule := networkingv1.IngressRule{Host: "rancher.unit-test.com"} 2613 ingress.Spec.Rules = append(ingress.Spec.Rules, rule) 2614 return nil 2615 }) 2616 2617 // Expect a call to get the secret with the Rancher root CA cert 2618 k8sMock.EXPECT(). 2619 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: rancherNamespace, Name: rancherTLSSecret}), gomock.Not(gomock.Nil()), gomock.Any()). 2620 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2621 secret.Data = map[string][]byte{ 2622 "ca.crt": {}, 2623 } 2624 return nil 2625 }) 2626 2627 secNS := constants.VerrazzanoMultiClusterNamespace 2628 secName := constants.VerrazzanoClusterRancherName 2629 if admin { 2630 secNS = constants.RancherSystemNamespace 2631 secName = rancherAdminSecret 2632 } 2633 // Expect a call to get the admin secret 2634 k8sMock.EXPECT(). 2635 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: secNS, Name: secName}), gomock.Not(gomock.Nil()), gomock.Any()). 2636 DoAndReturn(func(ctx context.Context, nsName types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2637 secret.Data = map[string][]byte{ 2638 "password": []byte("super-secret"), 2639 } 2640 return nil 2641 }) 2642 2643 // Expect a call to get the Rancher additional CA secret 2644 k8sMock.EXPECT(). 2645 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: constants.VerrazzanoSystemNamespace, Name: constants.PrivateCABundle}), gomock.Not(gomock.Nil()), gomock.Any()). 2646 Return(errors.NewNotFound(schema.GroupResource{Group: constants.VerrazzanoSystemNamespace, Resource: "Secret"}, constants.PrivateCABundle)) 2647 } 2648 2649 // expectMockCallsForDelete mocks expectations for the VMC deletion scenario. These are the common mock expectations across 2650 // tests that exercise delete functionality. Individual tests add mock expectations after these to test various scenarios. 2651 func expectMockCallsForDelete(t *testing.T, mock *mocks.MockClient, namespace string) { 2652 asserts := assert.New(t) 2653 2654 // Expect a call to get the VerrazzanoManagedCluster resource. 2655 mock.EXPECT(). 2656 Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: testManagedCluster}, gomock.Not(gomock.Nil()), gomock.Any()). 2657 DoAndReturn(func(ctx context.Context, name types.NamespacedName, vmc *v1alpha1.VerrazzanoManagedCluster, opts ...client.GetOption) error { 2658 vmc.TypeMeta = metav1.TypeMeta{ 2659 APIVersion: apiVersion, 2660 Kind: kind} 2661 vmc.ObjectMeta = metav1.ObjectMeta{ 2662 Namespace: name.Namespace, 2663 Name: name.Name, 2664 DeletionTimestamp: &metav1.Time{Time: time.Now()}, 2665 Finalizers: []string{finalizerName}} 2666 vmc.Status = v1alpha1.VerrazzanoManagedClusterStatus{ 2667 PrometheusHost: getPrometheusHost(), 2668 RancherRegistration: v1alpha1.RancherRegistration{ 2669 ClusterID: unitTestRancherClusterID, 2670 }, 2671 } 2672 2673 return nil 2674 }) 2675 2676 jobs := ` - ` + constants.PrometheusJobNameKey + `: test 2677 scrape_interval: 20s 2678 scrape_timeout: 15s 2679 scheme: http 2680 - ` + constants.PrometheusJobNameKey + `: test2 2681 scrape_interval: 20s 2682 scrape_timeout: 15s 2683 scheme: http` 2684 2685 // Expect a call to get the additional scrape config secret - return it configured with the two scrape jobs 2686 mock.EXPECT(). 2687 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: constants.PromAdditionalScrapeConfigsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2688 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2689 secret.Data = map[string][]byte{ 2690 constants.PromAdditionalScrapeConfigsSecretKey: []byte(jobs), 2691 } 2692 return nil 2693 }) 2694 2695 // Expect a call to get the additional scrape config secret (we call controllerruntime.CreateOrUpdate so it fetches again) - return it 2696 mock.EXPECT(). 2697 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: constants.PromAdditionalScrapeConfigsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2698 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2699 secret.Data = map[string][]byte{ 2700 constants.PromAdditionalScrapeConfigsSecretKey: []byte(jobs), 2701 } 2702 return nil 2703 }) 2704 2705 // Expect a call to update the additional scrape config secret 2706 mock.EXPECT(). 2707 Update(gomock.Any(), gomock.Any(), gomock.Any()). 2708 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.UpdateOption) error { 2709 // validate that the scrape config for the managed cluster is no longer present 2710 scrapeConfigs, err := metricsutils.ParseScrapeConfig(string(secret.Data[constants.PromAdditionalScrapeConfigsSecretKey])) 2711 if err != nil { 2712 return err 2713 } 2714 asserts.Len(scrapeConfigs.Children(), 1, "Expected only one scrape config") 2715 scrapeJobName := scrapeConfigs.Children()[0].Search(constants.PrometheusJobNameKey).Data() 2716 asserts.Equal("test2", scrapeJobName) 2717 return nil 2718 }) 2719 2720 // Expect a call to get the verrazzano-monitoring namespace 2721 mock.EXPECT(). 2722 Get(gomock.Any(), types.NamespacedName{Name: vpoconstants.VerrazzanoMonitoringNamespace}, gomock.Not(gomock.Nil()), gomock.Any()). 2723 DoAndReturn(func(ctx context.Context, name types.NamespacedName, ns *corev1.Namespace, opts ...client.GetOption) error { 2724 ns.SetName(vpoconstants.VerrazzanoMonitoringNamespace) 2725 return nil 2726 }) 2727 2728 // Expect a call to get the managed cluster TLS certs secret - return it configured with two managed cluster certs 2729 mock.EXPECT(). 2730 Get(gomock.Any(), types.NamespacedName{Namespace: vpoconstants.VerrazzanoMonitoringNamespace, Name: vpoconstants.PromManagedClusterCACertsSecretName}, gomock.Not(gomock.Nil()), gomock.Any()). 2731 DoAndReturn(func(ctx context.Context, name types.NamespacedName, secret *corev1.Secret, opts ...client.GetOption) error { 2732 secret.Data = map[string][]byte{ 2733 "ca-test": []byte("ca-cert-1"), 2734 "ca-test2": []byte("ca-cert-1"), 2735 } 2736 return nil 2737 }) 2738 2739 // Expect a call to update the managed cluster TLS certs secret 2740 mock.EXPECT(). 2741 Update(gomock.Any(), gomock.Any(), gomock.Any()). 2742 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.UpdateOption) error { 2743 // validate that the cert for the cluster being deleted is no longer present 2744 asserts.Len(secret.Data, 1, "Expected only one managed cluster cert") 2745 asserts.Contains(secret.Data, "ca-test2", "Expected to find cert for managed cluster not being deleted") 2746 return nil 2747 }) 2748 2749 // Expect a call to delete the argocd cluster secret 2750 mock.EXPECT(). 2751 Delete(gomock.Any(), gomock.Any(), gomock.Any()). 2752 DoAndReturn(func(ctx context.Context, secret *corev1.Secret, opts ...client.UpdateOption) error { 2753 return nil 2754 }) 2755 2756 // Expect Rancher k8s calls to configure the API client 2757 expectRancherConfigK8sCalls(t, mock, true) 2758 } 2759 2760 func expectMockCallsForCreateClusterRoleBindingTemplate(mock *mocks.MockClient, clusterID string) { 2761 name := fmt.Sprintf("crtb-verrazzano-cluster-%s", clusterID) 2762 mock.EXPECT(). 2763 Get(gomock.Any(), gomock.Eq(types.NamespacedName{Name: name, Namespace: clusterID}), gomock.Not(gomock.Nil()), gomock.Any()). 2764 DoAndReturn(func(ctx context.Context, nsn types.NamespacedName, resource *unstructured.Unstructured, opts ...client.GetOption) error { 2765 data := resource.UnstructuredContent() 2766 data[ClusterRoleTemplateBindingAttributeClusterName] = clusterID 2767 data[ClusterRoleTemplateBindingAttributeUserName] = constants.VerrazzanoClusterRancherUsername 2768 data[ClusterRoleTemplateBindingAttributeRoleTemplateName] = constants.VerrazzanoClusterRancherName 2769 return nil 2770 }) 2771 } 2772 2773 func expectMockCallsForListingRancherUsers(mock *mocks.MockClient) { 2774 usersList := unstructured.UnstructuredList{} 2775 usersList.SetGroupVersionKind(schema.GroupVersionKind{ 2776 Group: APIGroupRancherManagement, 2777 Version: APIGroupVersionRancherManagement, 2778 Kind: UserListKind, 2779 }) 2780 mock.EXPECT(). 2781 List(gomock.Any(), &usersList, gomock.Not(gomock.Nil())). 2782 DoAndReturn(func(ctx context.Context, userList *unstructured.UnstructuredList, options *client.ListOptions) error { 2783 user := unstructured.Unstructured{} 2784 user.SetGroupVersionKind(schema.GroupVersionKind{ 2785 Group: APIGroupRancherManagement, 2786 Version: APIGroupVersionRancherManagement, 2787 Kind: UserKind, 2788 }) 2789 user.SetName(constants.VerrazzanoClusterRancherUsername) 2790 data := user.UnstructuredContent() 2791 data[UserUsernameAttribute] = constants.VerrazzanoClusterRancherUsername 2792 userList.Items = []unstructured.Unstructured{user} 2793 return nil 2794 }) 2795 }