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  }