github.com/verrazzano/verrazzano@v1.7.0/cluster-operator/controllers/vmc/sync_thanos_test.go (about) 1 // Copyright (c) 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 "context" 8 "fmt" 9 "testing" 10 11 "github.com/stretchr/testify/assert" 12 clustersv1alpha1 "github.com/verrazzano/verrazzano/cluster-operator/apis/clusters/v1alpha1" 13 vzconst "github.com/verrazzano/verrazzano/pkg/constants" 14 "github.com/verrazzano/verrazzano/pkg/log/vzlog" 15 "github.com/verrazzano/verrazzano/pkg/metricsutils" 16 "github.com/verrazzano/verrazzano/platform-operator/apis/verrazzano/v1beta1" 17 "github.com/verrazzano/verrazzano/platform-operator/constants" 18 "github.com/verrazzano/verrazzano/platform-operator/controllers/verrazzano/component/thanos" 19 istionet "istio.io/api/networking/v1beta1" 20 istioclinet "istio.io/client-go/pkg/apis/networking/v1beta1" 21 appsv1 "k8s.io/api/apps/v1" 22 corev1 "k8s.io/api/core/v1" 23 k8sapiext "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 24 k8serrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/runtime" 27 "k8s.io/apimachinery/pkg/types" 28 "sigs.k8s.io/controller-runtime/pkg/client" 29 "sigs.k8s.io/controller-runtime/pkg/client/fake" 30 "sigs.k8s.io/yaml" 31 ) 32 33 type addRemoveSyncThanosTestType struct { 34 name string 35 clusterNumToCheck int 36 numClusters int 37 expectError bool 38 expectNumHosts int 39 changedHost *string 40 useValidCM bool 41 } 42 43 func TestAddThanosHostIfNotPresent(t *testing.T) { 44 vmcPrefix := "cluster" 45 host := "test-host" 46 newHost := "altered-host" 47 tests := []addRemoveSyncThanosTestType{ 48 {"no existing VMC", 1, 0, false, 1, nil, true}, 49 {"VMC already exists", 2, 2, false, 2, nil, true}, 50 {"VMC already exists host changed", 2, 2, false, 2, &newHost, true}, 51 {"VMC does not exist", 3, 2, false, 3, nil, true}, 52 {"existing ConfigMap is malformed", 1, 0, false, 1, nil, false}, 53 } 54 for _, tt := range tests { 55 t.Run(tt.name, func(t *testing.T) { 56 log := vzlog.DefaultLogger() 57 ctx := context.TODO() 58 effectiveHost := host 59 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 60 makeThanosConfigMapWithExistingHosts(t, tt.useValidCM, tt.numClusters, toGrpcTarget(effectiveHost), vmcPrefix), 61 makeThanosEnabledVerrazzano(), 62 ).Build() 63 r := &VerrazzanoManagedClusterReconciler{ 64 Client: cli, 65 log: log, 66 } 67 vmcName := fmt.Sprintf("%s%d", vmcPrefix, tt.clusterNumToCheck) 68 if tt.changedHost != nil { 69 effectiveHost = *tt.changedHost 70 } 71 err := r.addThanosHostIfNotPresent(ctx, effectiveHost, vmcName) 72 if tt.expectError { 73 assert.Error(t, err, "Expected error") 74 } else { 75 clusterShouldExist := true 76 assertThanosEndpointsConfigMap(ctx, t, cli, tt.expectNumHosts, toGrpcTarget(effectiveHost), vmcName, clusterShouldExist) 77 } 78 }) 79 } 80 } 81 82 func TestRemoveThanosHostFromConfigMap(t *testing.T) { 83 vmcPrefix := "cluster" 84 hostName := toGrpcTarget("test-host") 85 tests := []addRemoveSyncThanosTestType{ 86 {"no existing hosts", 0, 0, false, 0, nil, true}, 87 {"host already exists", 2, 2, false, 1, nil, true}, 88 {"host does not exist", 3, 2, false, 2, nil, true}, 89 } 90 for _, tt := range tests { 91 t.Run(tt.name, func(t *testing.T) { 92 log := vzlog.DefaultLogger() 93 ctx := context.TODO() 94 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 95 makeThanosConfigMapWithExistingHosts(t, tt.useValidCM, tt.numClusters, hostName, vmcPrefix), 96 makeThanosEnabledVerrazzano(), 97 ).Build() 98 r := &VerrazzanoManagedClusterReconciler{ 99 Client: cli, 100 log: log, 101 } 102 vmcName := fmt.Sprintf("%s%d", vmcPrefix, tt.clusterNumToCheck) 103 err := r.removeThanosHostFromConfigMap(ctx, vmcName, log) 104 if tt.expectError { 105 assert.Error(t, err, "Expected error") 106 } else { 107 clusterShouldExist := false 108 assertThanosEndpointsConfigMap(ctx, t, cli, tt.expectNumHosts, hostName, vmcName, clusterShouldExist) 109 } 110 }) 111 } 112 } 113 114 // TestSyncThanosQuery tests the syncThanosQuery function which is the top level entry point 115 func TestSyncThanosQuery(t *testing.T) { 116 hostName := "test-host" 117 vmcPrefix := "cluster" 118 tests := []struct { 119 name string 120 vmcStatus *clustersv1alpha1.VerrazzanoManagedClusterStatus 121 expectedConfigMapHosts int 122 numClusters int 123 clusterToSync int 124 clusterShouldExistInCM bool 125 prometheusConfig *corev1.Secret 126 }{ 127 {"VMC status empty", nil, 1, 1, 1, false, nil}, 128 {"VMC status has no Thanos host", 129 &clustersv1alpha1.VerrazzanoManagedClusterStatus{APIUrl: "someurl"}, 130 1, 131 1, 132 1, 133 false, 134 nil, 135 }, 136 {"VMC status has existing VMC", 137 &clustersv1alpha1.VerrazzanoManagedClusterStatus{APIUrl: "someurl", ThanosQueryStore: hostName}, 138 2, 139 2, 140 1, 141 true, // new host already exists in query endpoints configmap, should still exist 142 nil, 143 }, 144 {"VMC status has non-existing Thanos host", 145 &clustersv1alpha1.VerrazzanoManagedClusterStatus{APIUrl: "someurl", ThanosQueryStore: hostName}, 146 3, 147 2, 148 3, 149 true, // new host should be added to query endpoints configmap 150 nil, 151 }, 152 } 153 for _, tt := range tests { 154 t.Run(tt.name, func(t *testing.T) { 155 log := vzlog.DefaultLogger() 156 ctx := context.TODO() 157 var vmcStatus clustersv1alpha1.VerrazzanoManagedClusterStatus 158 thanosHost := "" 159 if tt.vmcStatus != nil { 160 vmcStatus = *tt.vmcStatus 161 thanosHost = vmcStatus.ThanosQueryStore 162 } 163 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ 164 ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("%s%d", vmcPrefix, tt.clusterToSync), Namespace: constants.VerrazzanoMultiClusterNamespace}, 165 Status: vmcStatus, 166 } 167 cliBuilder := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 168 makeThanosConfigMapWithExistingHosts(t, true, tt.numClusters, thanosHost, vmcPrefix), 169 makeThanosEnabledVerrazzano(), 170 &k8sapiext.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: serviceEntryCRDName}}, 171 &k8sapiext.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: destinationRuleCRDName}}, 172 ) 173 if tt.prometheusConfig != nil { 174 cliBuilder = cliBuilder.WithObjects(tt.prometheusConfig) 175 } 176 cli := cliBuilder.Build() 177 r := &VerrazzanoManagedClusterReconciler{ 178 Client: cli, 179 log: log, 180 } 181 err := r.syncThanosQuery(ctx, vmc) 182 assert.NoError(t, err) 183 assertThanosEndpointsConfigMap(ctx, t, cli, tt.expectedConfigMapHosts, toGrpcTarget(thanosHost), vmc.Name, tt.clusterShouldExistInCM) 184 if tt.clusterShouldExistInCM { 185 assertThanosServiceEntry(t, r, vmc.Name, hostName, thanosGrpcIngressPort) 186 assertThanosDestinationRule(t, r, vmc.Name, hostName, thanosGrpcIngressPort) 187 } 188 if tt.prometheusConfig != nil { 189 assertAdditionalScrapeConfigRemoved(t, r, vmc.Name) 190 } 191 }) 192 } 193 } 194 195 // TestDeleteClusterThanosEndpoint tests the deleteClusterThanosEndpoint function. 196 func TestDeleteClusterThanosEndpoint(t *testing.T) { 197 vmcPrefix := "managed" 198 const managedClusterName = "managed1" 199 const hostName = "thanos-query.example.com" 200 host := toGrpcTarget(hostName) 201 202 vmcStatus := clustersv1alpha1.VerrazzanoManagedClusterStatus{APIUrl: "someurl", ThanosQueryStore: hostName} 203 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ 204 ObjectMeta: metav1.ObjectMeta{Name: managedClusterName, Namespace: constants.VerrazzanoMultiClusterNamespace}, 205 Status: vmcStatus, 206 } 207 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 208 makeThanosConfigMapWithExistingHosts(t, true, 1, host, vmcPrefix), 209 makeThanosEnabledVerrazzano(), 210 &k8sapiext.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: serviceEntryCRDName}}, 211 &k8sapiext.CustomResourceDefinition{ObjectMeta: metav1.ObjectMeta{Name: destinationRuleCRDName}}, 212 &corev1.Secret{ 213 ObjectMeta: metav1.ObjectMeta{ 214 Name: constants.PromManagedClusterCACertsSecretName, 215 Namespace: constants.VerrazzanoMonitoringNamespace, 216 }, 217 Data: map[string][]byte{ 218 "ca-" + managedClusterName: []byte("ca-cert-1"), 219 "ca-some-other-managed-cluster": []byte("ca-cert-2"), 220 }, 221 }, 222 ).Build() 223 224 r := &VerrazzanoManagedClusterReconciler{ 225 Client: cli, 226 log: vzlog.DefaultLogger(), 227 } 228 229 // first sync to update endpoint configmap, add CA cert volume and volume mount, create ServiceEntry and 230 // DestinationRule 231 err := r.syncThanosQuery(context.TODO(), vmc) 232 assert.NoError(t, err) 233 234 assertThanosServiceEntry(t, r, vmc.Name, hostName, thanosGrpcIngressPort) 235 assertThanosDestinationRule(t, r, vmc.Name, hostName, thanosGrpcIngressPort) 236 237 // make sure the volume annotations have been added to the deployment 238 queryDeploy := &appsv1.Deployment{} 239 err = cli.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: thanosQueryDeployName}, queryDeploy) 240 assert.NoError(t, err) 241 242 assert.Contains(t, queryDeploy.Spec.Template.ObjectMeta.Annotations, istioVolumeAnnotation) 243 assert.Contains(t, queryDeploy.Spec.Template.ObjectMeta.Annotations, istioVolumeMountAnnotation) 244 245 // GIVEN we have sync'ed a managed cluster Thanos endpoint 246 // WHEN we call deleteClusterThanosEndpoint 247 // THEN the resources we created during sync are cleaned up 248 err = r.syncThanosQueryEndpointDelete(context.TODO(), vmc) 249 assert.NoError(t, err) 250 251 // ServiceEntry and DestinationRule should be gone 252 se := &istioclinet.ServiceEntry{} 253 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, se) 254 assert.True(t, k8serrors.IsNotFound(err)) 255 256 dr := &istioclinet.DestinationRule{} 257 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, dr) 258 assert.True(t, k8serrors.IsNotFound(err)) 259 } 260 261 func makeThanosTestScheme() *runtime.Scheme { 262 scheme := runtime.NewScheme() 263 v1beta1.AddToScheme(scheme) 264 corev1.AddToScheme(scheme) 265 appsv1.AddToScheme(scheme) 266 istioclinet.AddToScheme(scheme) 267 k8sapiext.AddToScheme(scheme) 268 return scheme 269 } 270 271 func makeThanosEnabledVerrazzano() *v1beta1.Verrazzano { 272 trueVal := true 273 return &v1beta1.Verrazzano{ 274 Spec: v1beta1.VerrazzanoSpec{ 275 Components: v1beta1.ComponentSpec{ 276 Thanos: &v1beta1.ThanosComponent{Enabled: &trueVal}, 277 }, 278 }, 279 } 280 } 281 282 func makeThanosConfigMapWithExistingHosts(t *testing.T, useValidConfigMap bool, numClusters int, host, vmcPrefix string) *corev1.ConfigMap { 283 var yamlExistingHostInfo []byte 284 var err error 285 if useValidConfigMap { 286 existingHostInfo := []*thanosServiceDiscovery{} 287 for i := 1; i <= numClusters; i++ { 288 existingHostInfo = append(existingHostInfo, &thanosServiceDiscovery{ 289 Targets: []string{host}, 290 Labels: map[string]string{ 291 verrazzanoManagedLabel: fmt.Sprintf("%s%d", vmcPrefix, i), 292 }, 293 }) 294 } 295 yamlExistingHostInfo, err = yaml.Marshal(existingHostInfo) 296 assert.NoError(t, err) 297 } else { 298 yamlExistingHostInfo = []byte("- targets: garbledTextHere") 299 } 300 return &corev1.ConfigMap{ 301 ObjectMeta: metav1.ObjectMeta{Namespace: thanos.ComponentNamespace, Name: ThanosManagedClusterEndpointsConfigMap}, 302 Data: map[string]string{ 303 serviceDiscoveryKey: string(yamlExistingHostInfo), 304 }, 305 } 306 } 307 308 func assertThanosEndpointsConfigMap(ctx context.Context, t *testing.T, cli client.WithWatch, expectNumHosts int, host, vmcName string, vmcShoudExist bool) { 309 modifiedConfigMap := &corev1.ConfigMap{} 310 err := cli.Get(ctx, types.NamespacedName{Namespace: thanos.ComponentNamespace, Name: ThanosManagedClusterEndpointsConfigMap}, modifiedConfigMap) 311 assert.NoError(t, err) 312 var modifiedContent []*thanosServiceDiscovery 313 err = yaml.Unmarshal([]byte(modifiedConfigMap.Data[serviceDiscoveryKey]), &modifiedContent) 314 assert.NoError(t, err) 315 assert.Len(t, modifiedContent, expectNumHosts, "Expected %d service discovery entries", expectNumHosts) 316 if vmcShoudExist { 317 for _, sd := range modifiedContent { 318 if val, ok := sd.Labels[verrazzanoManagedLabel]; ok && val == vmcName { 319 assert.Equal(t, host, sd.Targets[0]) 320 return 321 } 322 } 323 assert.Fail(t, fmt.Sprintf("Failed to find Service Discovery for VMC %s", vmcName)) 324 } 325 } 326 327 // TestCreateServiceEntry tests ServiceEntry creation scenarios. 328 func TestCreateServiceEntry(t *testing.T) { 329 const managedClusterName = "managed-cluster" 330 const host = "thanos-query.example.com" 331 const port = uint32(443) 332 333 log := vzlog.DefaultLogger() 334 335 // GIVEN the CRD for Istio ServiceEntry does not exist in the cluster 336 // WHEN the createOrUpdateServiceEntry function is called 337 // THEN the call does not return an error and no ServiceEntry is created 338 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects().Build() 339 r := &VerrazzanoManagedClusterReconciler{ 340 Client: cli, 341 log: log, 342 } 343 344 err := r.createOrUpdateServiceEntry(managedClusterName, host, port) 345 assert.NoError(t, err) 346 347 se := &istioclinet.ServiceEntry{} 348 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, se) 349 assert.True(t, k8serrors.IsNotFound(err)) 350 351 // GIVEN the CRD for Istio ServiceEntry exists in the cluster 352 // AND the ServiceEntry does not exist 353 // WHEN the createOrUpdateServiceEntry function is called 354 // THEN the call does not return an error and a ServiceEntry is created 355 cli = fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 356 &k8sapiext.CustomResourceDefinition{ 357 ObjectMeta: metav1.ObjectMeta{ 358 Name: serviceEntryCRDName, 359 }, 360 }, 361 ).Build() 362 r = &VerrazzanoManagedClusterReconciler{ 363 Client: cli, 364 log: log, 365 } 366 367 err = r.createOrUpdateServiceEntry(managedClusterName, host, port) 368 assert.NoError(t, err) 369 assertThanosServiceEntry(t, r, managedClusterName, host, port) 370 } 371 372 // TestUpdateServiceEntry tests ServiceEntry update scenarios. 373 func TestUpdateServiceEntry(t *testing.T) { 374 const managedClusterName = "managed-cluster" 375 const host = "thanos-query.example.com" 376 const port = uint32(443) 377 378 log := vzlog.DefaultLogger() 379 380 // GIVEN the ServiceEntry exists 381 // WHEN the createOrUpdateServiceEntry function is called 382 // THEN the call does not return an error and the ServiceEntry is updated 383 se := &istioclinet.ServiceEntry{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName, Namespace: constants.VerrazzanoMonitoringNamespace}} 384 populateServiceEntry(se, "bad-bad-host", port) 385 386 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 387 &k8sapiext.CustomResourceDefinition{ 388 ObjectMeta: metav1.ObjectMeta{ 389 Name: serviceEntryCRDName, 390 }, 391 }, 392 se, 393 ).Build() 394 r := &VerrazzanoManagedClusterReconciler{ 395 Client: cli, 396 log: log, 397 } 398 399 err := r.createOrUpdateServiceEntry(managedClusterName, host, port) 400 assert.NoError(t, err) 401 402 assertThanosServiceEntry(t, r, managedClusterName, host, port) 403 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, se) 404 assert.NoError(t, err) 405 assert.Contains(t, se.Spec.Hosts, host) 406 assert.Equal(t, se.Spec.Resolution, istionet.ServiceEntry_DNS) 407 assert.Equal(t, se.Spec.Ports[0].Number, port) 408 assert.Equal(t, se.Spec.Ports[0].TargetPort, port) 409 assert.Equal(t, se.Spec.Ports[0].Protocol, "GRPC") 410 } 411 412 // TestDeleteServiceEntry tests ServiceEntry deletion scenarios. 413 func TestDeleteServiceEntry(t *testing.T) { 414 const managedClusterName = "managed-cluster" 415 const host = "thanos-query.example.com" 416 const port = uint32(443) 417 418 log := vzlog.DefaultLogger() 419 420 // GIVEN the CRD for Istio ServiceEntry does not exist in the cluster 421 // WHEN the deleteServiceEntry function is called 422 // THEN the call does not return an error 423 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects().Build() 424 r := &VerrazzanoManagedClusterReconciler{ 425 Client: cli, 426 log: log, 427 } 428 429 err := r.deleteServiceEntry(managedClusterName) 430 assert.NoError(t, err) 431 432 // GIVEN the ServiceEntry exists 433 // WHEN the deleteServiceEntry function is called 434 // THEN the call does not return an error and the ServiceEntry is deleted 435 se := &istioclinet.ServiceEntry{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName, Namespace: constants.VerrazzanoMonitoringNamespace}} 436 populateServiceEntry(se, host, port) 437 438 cli = fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 439 &k8sapiext.CustomResourceDefinition{ 440 ObjectMeta: metav1.ObjectMeta{ 441 Name: serviceEntryCRDName, 442 }, 443 }, 444 se, 445 ).Build() 446 r = &VerrazzanoManagedClusterReconciler{ 447 Client: cli, 448 log: log, 449 } 450 451 err = r.deleteServiceEntry(managedClusterName) 452 assert.NoError(t, err) 453 454 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, se) 455 assert.True(t, k8serrors.IsNotFound(err)) 456 457 // GIVEN the ServiceEntry does not exist 458 // WHEN the deleteServiceEntry function is called 459 // THEN the call does not return an error 460 err = r.deleteServiceEntry(managedClusterName) 461 assert.NoError(t, err) 462 } 463 464 // TestCreateDestinationRule tests DestinationRule creation scenarios. 465 func TestCreateDestinationRule(t *testing.T) { 466 const managedClusterName = "managed-cluster" 467 const host = "thanos-query.example.com" 468 const port = uint32(443) 469 470 log := vzlog.DefaultLogger() 471 472 // GIVEN the CRD for Istio DestinationRule does not exist in the cluster 473 // WHEN the createOrUpdateDestinationRule function is called 474 // THEN the call does not return an error and no DestinationRule is created 475 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects().Build() 476 r := &VerrazzanoManagedClusterReconciler{ 477 Client: cli, 478 log: log, 479 } 480 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName}} 481 482 err := r.createOrUpdateDestinationRule(vmc, host, port) 483 assert.NoError(t, err) 484 485 dr := &istioclinet.DestinationRule{} 486 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, dr) 487 assert.True(t, k8serrors.IsNotFound(err)) 488 489 // GIVEN the CRD for Istio DestinationRule exists in the cluster 490 // AND the DestinationRule does not exist 491 // WHEN the createOrUpdateDestinationRule function is called 492 // THEN the call does not return an error and a DestinationRule is created 493 cli = fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 494 &k8sapiext.CustomResourceDefinition{ 495 ObjectMeta: metav1.ObjectMeta{ 496 Name: destinationRuleCRDName, 497 }, 498 }, 499 ).Build() 500 r = &VerrazzanoManagedClusterReconciler{ 501 Client: cli, 502 log: log, 503 } 504 505 err = r.createOrUpdateDestinationRule(vmc, host, port) 506 assert.NoError(t, err) 507 508 assertThanosDestinationRule(t, r, managedClusterName, host, port) 509 } 510 511 // TestUpdateDestinationRule tests DestinationRule update scenarios. 512 func TestUpdateDestinationRule(t *testing.T) { 513 const managedClusterName = "managed-cluster" 514 const host = "thanos-query.example.com" 515 const port = uint32(thanosGrpcIngressPort) 516 517 log := vzlog.DefaultLogger() 518 519 // GIVEN the DestinationRule exists 520 // WHEN the createOrUpdateDestinationRule function is called 521 // THEN the call does not return an error and the DestinationRule is updated 522 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName}} 523 dr := &istioclinet.DestinationRule{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName, Namespace: constants.VerrazzanoMonitoringNamespace}} 524 populateDestinationRule(dr, "bad-bad-host", port, vmc) 525 526 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 527 &k8sapiext.CustomResourceDefinition{ 528 ObjectMeta: metav1.ObjectMeta{ 529 Name: destinationRuleCRDName, 530 }, 531 }, 532 dr, 533 ).Build() 534 r := &VerrazzanoManagedClusterReconciler{ 535 Client: cli, 536 log: log, 537 } 538 539 err := r.createOrUpdateDestinationRule(vmc, host, port) 540 assert.NoError(t, err) 541 542 assertThanosDestinationRule(t, r, managedClusterName, host, port) 543 } 544 545 // TestDeleteDestinationRule tests DestinationRule deletion scenarios. 546 func TestDeleteDestinationRule(t *testing.T) { 547 const managedClusterName = "managed-cluster" 548 const host = "thanos-query.example.com" 549 const port = uint32(443) 550 551 log := vzlog.DefaultLogger() 552 553 // GIVEN the CRD for Istio DestinationRule does not exist in the cluster 554 // WHEN the deleteDestinationRule function is called 555 // THEN the call does not return an error 556 cli := fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects().Build() 557 r := &VerrazzanoManagedClusterReconciler{ 558 Client: cli, 559 log: log, 560 } 561 562 err := r.deleteDestinationRule(managedClusterName) 563 assert.NoError(t, err) 564 565 // GIVEN the DestinationRule exists 566 // WHEN the deleteDestinationRule function is called 567 // THEN the call does not return an error and the DestinationRule is deleted 568 vmc := &clustersv1alpha1.VerrazzanoManagedCluster{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName}} 569 dr := &istioclinet.DestinationRule{ObjectMeta: metav1.ObjectMeta{Name: managedClusterName, Namespace: constants.VerrazzanoMonitoringNamespace}} 570 populateDestinationRule(dr, host, port, vmc) 571 572 cli = fake.NewClientBuilder().WithScheme(makeThanosTestScheme()).WithRuntimeObjects( 573 &k8sapiext.CustomResourceDefinition{ 574 ObjectMeta: metav1.ObjectMeta{ 575 Name: destinationRuleCRDName, 576 }, 577 }, 578 dr, 579 ).Build() 580 r = &VerrazzanoManagedClusterReconciler{ 581 Client: cli, 582 log: log, 583 } 584 585 err = r.deleteDestinationRule(managedClusterName) 586 assert.NoError(t, err) 587 588 err = r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, dr) 589 assert.True(t, k8serrors.IsNotFound(err)) 590 591 // GIVEN the DestinationRule does not exist 592 // WHEN the deleteDestinationRule function is called 593 // THEN the call does not return an error 594 err = r.deleteDestinationRule(managedClusterName) 595 assert.NoError(t, err) 596 } 597 598 func assertThanosServiceEntry(t *testing.T, r *VerrazzanoManagedClusterReconciler, managedClusterName string, host string, port uint32) { 599 se := &istioclinet.ServiceEntry{} 600 err := r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: managedClusterName}, se) 601 assert.NoError(t, err) 602 assert.Contains(t, se.Spec.Hosts, host) 603 assert.Equal(t, se.Spec.Resolution, istionet.ServiceEntry_DNS) 604 assert.Equal(t, se.Spec.Ports[0].Number, port) 605 assert.Equal(t, se.Spec.Ports[0].TargetPort, port) 606 assert.Equal(t, se.Spec.Ports[0].Protocol, "GRPC") 607 } 608 609 func assertThanosDestinationRule(t *testing.T, r *VerrazzanoManagedClusterReconciler, clusterName string, hostName string, portNum uint32) { 610 dr := &istioclinet.DestinationRule{} 611 err := r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: clusterName}, dr) 612 assert.NoError(t, err) 613 assert.Equal(t, dr.Spec.Host, hostName) 614 assert.Equal(t, dr.Spec.TrafficPolicy.PortLevelSettings[0].Port.Number, portNum) 615 assert.Equal(t, dr.Spec.TrafficPolicy.PortLevelSettings[0].Tls.Mode, istionet.ClientTLSSettings_SIMPLE) 616 assert.Equal(t, dr.Spec.TrafficPolicy.PortLevelSettings[0].Tls.Sni, hostName) 617 } 618 619 func assertAdditionalScrapeConfigRemoved(t *testing.T, r *VerrazzanoManagedClusterReconciler, vmcName string) { 620 sec := &corev1.Secret{} 621 err := r.Client.Get(context.TODO(), client.ObjectKey{Namespace: constants.VerrazzanoMonitoringNamespace, Name: vzconst.PromAdditionalScrapeConfigsSecretName}, sec) 622 assert.NoError(t, err) 623 data, ok := sec.Data[vzconst.PromAdditionalScrapeConfigsSecretKey] 624 assert.True(t, ok, "Additional scrape configs key not found in secret") 625 assert.NotEmpty(t, data) 626 scrapeConfigContainer, err := metricsutils.ParseScrapeConfig(string(data)) 627 assert.NoError(t, err) 628 assert.Negative(t, metricsutils.FindScrapeJob(scrapeConfigContainer, vmcName)) 629 }