github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/metricstrait/metricstrait_controller_test.go (about)

     1  // Copyright (c) 2020, 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 metricstrait
     5  
     6  import (
     7  	"context"
     8  	"encoding/json"
     9  	"fmt"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
    14  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    15  	"k8s.io/apimachinery/pkg/api/errors"
    16  	"k8s.io/apimachinery/pkg/conversion"
    17  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    18  
    19  	promoperapi "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1"
    20  	"github.com/verrazzano/verrazzano/application-operator/constants"
    21  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    22  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    23  
    24  	oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1"
    25  	oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
    26  	"github.com/go-logr/logr"
    27  	"github.com/golang/mock/gomock"
    28  	asserts "github.com/stretchr/testify/assert"
    29  	vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1"
    30  	"github.com/verrazzano/verrazzano/application-operator/mocks"
    31  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    32  	"go.uber.org/zap"
    33  	k8sapps "k8s.io/api/apps/v1"
    34  	k8score "k8s.io/api/core/v1"
    35  	k8smeta "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/types"
    39  	ctrl "sigs.k8s.io/controller-runtime"
    40  	"sigs.k8s.io/controller-runtime/pkg/client"
    41  )
    42  
    43  const (
    44  	foo         = "foo"
    45  	bar         = "bar"
    46  	getError    = "get error"
    47  	updateError = "update error"
    48  )
    49  
    50  type erroringGetClient struct {
    51  	client.Client
    52  }
    53  
    54  type erroringUpdateClient struct {
    55  	client.Client
    56  }
    57  
    58  func (eg *erroringGetClient) Get(_ context.Context, _ client.ObjectKey, _ client.Object, _ ...client.GetOption) error {
    59  	return errors.NewTooManyRequests(getError, 0)
    60  }
    61  
    62  func (eu *erroringUpdateClient) Update(_ context.Context, _ client.Object, _ ...client.UpdateOption) error {
    63  	return errors.NewTooManyRequests(updateError, 0)
    64  }
    65  
    66  // TestReconcilerSetupWithManager test the creation of the metrics trait reconciler.
    67  // GIVEN a controller implementation
    68  // WHEN the controller is created
    69  // THEN verify no error is returned
    70  func TestReconcilerSetupWithManager(t *testing.T) {
    71  	assert := asserts.New(t)
    72  
    73  	var mocker *gomock.Controller
    74  	var mgr *mocks.MockManager
    75  	var cli *mocks.MockClient
    76  	var scheme *runtime.Scheme
    77  	var reconciler Reconciler
    78  	var err error
    79  
    80  	mocker = gomock.NewController(t)
    81  	mgr = mocks.NewMockManager(mocker)
    82  	cli = mocks.NewMockClient(mocker)
    83  	scheme = runtime.NewScheme()
    84  	_ = vzapi.AddToScheme(scheme)
    85  	reconciler = Reconciler{Client: cli, Scheme: scheme, Scraper: "istio-system/prometheus"}
    86  	mgr.EXPECT().GetControllerOptions().AnyTimes()
    87  	mgr.EXPECT().GetScheme().Return(scheme)
    88  	mgr.EXPECT().GetLogger().Return(logr.Discard())
    89  	mgr.EXPECT().SetFields(gomock.Any()).Return(nil).AnyTimes()
    90  	mgr.EXPECT().Add(gomock.Any()).Return(nil).AnyTimes()
    91  	err = reconciler.SetupWithManager(mgr)
    92  	mocker.Finish()
    93  	assert.NoError(err)
    94  }
    95  
    96  // TestMetricsTraitCreatedForContainerizedWorkload tests the creation of a metrics trait related to a containerized workload.
    97  // GIVEN a metrics trait that has been created
    98  // AND the metrics trait is related to a containerized workload
    99  // WHEN the metrics trait Reconcile method is invoked
   100  // THEN verify that metrics trait finalizer is added
   101  // AND verify that pod annotations are updated
   102  // AND verify that the scraper configmap is updated
   103  // AND verify that the scraper pod is restarted
   104  func TestMetricsTraitCreatedForContainerizedWorkload(t *testing.T) {
   105  	assert := asserts.New(t)
   106  
   107  	c := containerizedWorkloadClient(false, false, false)
   108  
   109  	// Create and make the request
   110  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   111  
   112  	reconciler := newMetricsTraitReconciler(c)
   113  	result, err := reconciler.Reconcile(context.TODO(), request)
   114  
   115  	// Validate the results
   116  	assert.NoError(err)
   117  	assert.Equal(true, result.Requeue)
   118  	assert.Equal(time.Duration(0), result.RequeueAfter)
   119  	trait := vzapi.MetricsTrait{}
   120  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   121  	assert.NoError(err)
   122  	assert.Equal("test-namespace", trait.Namespace)
   123  	assert.Equal("test-trait-name", trait.Name)
   124  	assert.Len(trait.Finalizers, 1)
   125  	assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0])
   126  }
   127  
   128  // TestMetricsTraitCreatedForVerrazzanoWorkload tests the creation of a metrics trait related to a Verrazzano workload.
   129  // The Verrazzano workload contains the real workload so we need to unwrap it.
   130  // GIVEN a metrics trait that has been created
   131  // AND the metrics trait is related to a Verrazzano workload
   132  // WHEN the metrics trait Reconcile method is invoked
   133  // THEN the contained workload should be unwrapped
   134  // AND verify that metrics trait finalizer is added
   135  // AND verify that pod annotations are updated
   136  // AND verify that the scraper configmap is updated
   137  // AND verify that the scraper pod is restarted
   138  func TestMetricsTraitCreatedForVerrazzanoWorkload(t *testing.T) {
   139  	assert := asserts.New(t)
   140  	mocker := gomock.NewController(t)
   141  
   142  	c := cohWorkloadClient(false, -1)
   143  
   144  	// Create and make the request
   145  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   146  
   147  	reconciler := newMetricsTraitReconciler(c)
   148  	result, err := reconciler.Reconcile(context.TODO(), request)
   149  
   150  	// Validate the results
   151  	mocker.Finish()
   152  	assert.NoError(err)
   153  	assert.Equal(true, result.Requeue)
   154  	assert.Equal(time.Duration(0), result.RequeueAfter)
   155  	trait := vzapi.MetricsTrait{}
   156  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   157  	assert.NoError(err)
   158  	assert.Equal("test-namespace", trait.Namespace)
   159  	assert.Equal("test-trait-name", trait.Name)
   160  	assert.Len(trait.Finalizers, 1)
   161  	assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0])
   162  }
   163  
   164  // TestMetricsTraitCreatedForDeploymentWorkload tests the creation of a metrics trait related to a native Kubernetes Deployment workload.
   165  // GIVEN a metrics trait that has been created
   166  // AND the metrics trait is related to a k8s deployment workload
   167  // WHEN the metrics trait Reconcile method is invoked
   168  // THEN verify that metrics trait finalizer is added
   169  // AND verify that pod annotations are updated
   170  // AND verify that the scraper configmap is updated
   171  // AND verify that the scraper pod is restarted
   172  func TestMetricsTraitCreatedForDeploymentWorkload(t *testing.T) {
   173  	assert := asserts.New(t)
   174  	mocker := gomock.NewController(t)
   175  
   176  	c := deploymentWorkloadClient(false)
   177  
   178  	// Create and make the request
   179  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   180  
   181  	reconciler := newMetricsTraitReconciler(c)
   182  	result, err := reconciler.Reconcile(context.TODO(), request)
   183  
   184  	// Validate the results
   185  	mocker.Finish()
   186  	assert.NoError(err)
   187  	assert.Equal(true, result.Requeue)
   188  	assert.Equal(time.Duration(0), result.RequeueAfter)
   189  	trait := vzapi.MetricsTrait{}
   190  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   191  	assert.NoError(err)
   192  	assert.Equal("test-namespace", trait.Namespace)
   193  	assert.Equal("test-trait-name", trait.Name)
   194  	assert.Len(trait.Finalizers, 1)
   195  	assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0])
   196  }
   197  
   198  // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload.
   199  // GIVEN a metrics trait with a non-zero deletion time
   200  // WHEN the metrics trait Reconcile method is invoked
   201  // THEN verify that metrics trait finalizer is removed
   202  // AND verify that pod annotations are cleaned up
   203  // AND verify that the scraper configmap is cleanup up
   204  // AND verify that the scraper pod is restarted
   205  func TestMetricsTraitDeletedForContainerizedWorkload(t *testing.T) {
   206  	assert := asserts.New(t)
   207  
   208  	c := containerizedWorkloadClient(true, false, false)
   209  
   210  	// Create and make the request
   211  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   212  	reconciler := newMetricsTraitReconciler(c)
   213  	result, err := reconciler.Reconcile(context.TODO(), request)
   214  
   215  	// Validate the results
   216  	assert.NoError(err)
   217  	assert.Equal(true, result.Requeue)
   218  	assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0)
   219  	trait := vzapi.MetricsTrait{}
   220  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   221  	assert.NoError(err)
   222  	assert.Len(trait.Finalizers, 0)
   223  }
   224  
   225  // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload.
   226  // GIVEN a metrics trait with a non-zero deletion time
   227  // GIVEN the related deployment resource no longer exists
   228  // WHEN the metrics trait Reconcile method is invoked
   229  // THEN verify that metrics trait finalizer is removed
   230  // AND verify that the scraper configmap is cleanup up
   231  // AND verify that the scraper pod is restarted
   232  // AND verify that the finalizer is removed
   233  func TestMetricsTraitDeletedForContainerizedWorkloadWhenDeploymentDeleted(t *testing.T) {
   234  	assert := asserts.New(t)
   235  
   236  	c := containerizedWorkloadClient(true, true, false)
   237  
   238  	// Create and make the request
   239  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   240  	reconciler := newMetricsTraitReconciler(c)
   241  	result, err := reconciler.Reconcile(context.TODO(), request)
   242  
   243  	// Validate the results
   244  	assert.NoError(err)
   245  	assert.Equal(true, result.Requeue)
   246  	assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0)
   247  	trait := vzapi.MetricsTrait{}
   248  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   249  	assert.NoError(err)
   250  	assert.Len(trait.Finalizers, 0)
   251  }
   252  
   253  // TestMetricsTraitDeletedForContainerizedWorkload tests deletion of a metrics trait related to a containerized workload.
   254  // GIVEN a metrics trait with a non-zero deletion time
   255  // WHEN the metrics trait Reconcile method is invoked
   256  // THEN verify that metrics trait finalizer is removed
   257  // AND verify that pod annotations are cleaned up
   258  // AND verify that the scraper configmap is cleanup up
   259  // AND verify that the scraper pod is restarted
   260  func TestMetricsTraitDeletedForDeploymentWorkload(t *testing.T) {
   261  	assert := asserts.New(t)
   262  
   263  	c := deploymentWorkloadClient(true)
   264  
   265  	// Create and make the request
   266  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   267  	reconciler := newMetricsTraitReconciler(c)
   268  	result, err := reconciler.Reconcile(context.TODO(), request)
   269  
   270  	// Validate the results
   271  	assert.NoError(err)
   272  	assert.Equal(true, result.Requeue)
   273  	assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0)
   274  	trait := vzapi.MetricsTrait{}
   275  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   276  	assert.NoError(err)
   277  	assert.Len(trait.Finalizers, 0)
   278  }
   279  
   280  // TestFetchTraitError tests a failure to fetch the trait during reconcile.
   281  // GIVEN a valid new metrics trait
   282  // WHEN the metrics trait Reconcile method is invoked
   283  // AND a failure occurs fetching the metrics trait
   284  // THEN verify the metrics trait finalizer is added
   285  // AND verify the error is propigated to the caller
   286  func TestFetchTraitError(t *testing.T) {
   287  	assert := asserts.New(t)
   288  
   289  	scheme := k8scheme.Scheme
   290  	_ = vzapi.AddToScheme(scheme)
   291  
   292  	c := fake.NewClientBuilder().WithScheme(scheme).Build()
   293  
   294  	// Create and make the request
   295  	reconciler := newMetricsTraitReconciler(c)
   296  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   297  	result, err := reconciler.Reconcile(context.TODO(), request)
   298  
   299  	// Validate the results
   300  	assert.Nil(err)
   301  	assert.Equal(false, result.Requeue)
   302  }
   303  
   304  // TestWorkloadFetchError tests failing to fetch the workload during reconcile.
   305  // GIVEN a valid new metrics trait
   306  // WHEN the the metrics trait Reconcile method is invoked
   307  // AND a failure occurs fetching the metrics trait
   308  // THEN verify the metrics trait finalizer is added
   309  // AND verify the error is propigated to the caller
   310  func TestWorkloadFetchError(t *testing.T) {
   311  	assert := asserts.New(t)
   312  
   313  	scheme := k8scheme.Scheme
   314  	_ = vzapi.AddToScheme(scheme)
   315  
   316  	c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
   317  		&vzapi.MetricsTrait{
   318  			TypeMeta: k8smeta.TypeMeta{
   319  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
   320  				Kind:       vzapi.MetricsTraitKind,
   321  			},
   322  			ObjectMeta: k8smeta.ObjectMeta{
   323  				Namespace: "test-namespace",
   324  				Name:      "test-trait-name",
   325  				Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
   326  			},
   327  			Spec: vzapi.MetricsTraitSpec{
   328  				WorkloadReference: oamrt.TypedReference{
   329  					APIVersion: oamcore.SchemeGroupVersion.Identifier(),
   330  					Kind:       oamcore.ContainerizedWorkloadKind,
   331  					Name:       "test-workload-name",
   332  				},
   333  			},
   334  		},
   335  	).Build()
   336  
   337  	// Create and make the request
   338  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   339  	reconciler := newMetricsTraitReconciler(c)
   340  	result, err := reconciler.Reconcile(context.TODO(), request)
   341  
   342  	// Validate the results
   343  	assert.NotNil(err)
   344  	assert.Equal(true, result.Requeue)
   345  }
   346  
   347  // TestDeploymentUpdateError testing failing to update a workload child deployment during reconcile.
   348  // GIVEN a metrics trait that has been updated
   349  // WHEN the metrics trait Reconcile method is invoked
   350  // AND an error occurs updating the scraper deployment
   351  // THEN verify an error is recorded in the status
   352  func TestDeploymentUpdateError(t *testing.T) {
   353  	assert := asserts.New(t)
   354  	mocker := gomock.NewController(t)
   355  	mock := mocks.NewMockClient(mocker)
   356  	mockStatus := mocks.NewMockStatusWriter(mocker)
   357  	testDeployment := k8sapps.Deployment{
   358  		TypeMeta: k8smeta.TypeMeta{
   359  			APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
   360  			Kind:       "Deployment",
   361  		},
   362  		ObjectMeta: k8smeta.ObjectMeta{
   363  			Name:              "test-deployment-name",
   364  			Namespace:         "test-namespace",
   365  			CreationTimestamp: k8smeta.Now(),
   366  			OwnerReferences: []k8smeta.OwnerReference{{
   367  				APIVersion: oamcore.SchemeGroupVersion.Identifier(),
   368  				Kind:       oamcore.ContainerizedWorkloadKind,
   369  				Name:       "test-workload-name",
   370  				UID:        "test-workload-uid"}}}}
   371  	// Expect a call to get the trait resource.
   372  	mock.EXPECT().
   373  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}, gomock.Not(gomock.Nil()), gomock.Any()).
   374  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, trait *vzapi.MetricsTrait, opts ...client.GetOption) error {
   375  			trait.TypeMeta = k8smeta.TypeMeta{
   376  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
   377  				Kind:       vzapi.MetricsTraitKind}
   378  			trait.ObjectMeta = k8smeta.ObjectMeta{
   379  				Namespace: name.Namespace,
   380  				Name:      name.Name,
   381  				Labels: map[string]string{
   382  					oam.LabelAppName:      "test-app",
   383  					oam.LabelAppComponent: "test-comp",
   384  				}}
   385  			trait.Spec.WorkloadReference = oamrt.TypedReference{
   386  				APIVersion: oamcore.SchemeGroupVersion.Identifier(),
   387  				Kind:       oamcore.ContainerizedWorkloadKind,
   388  				Name:       "test-workload-name"}
   389  			return nil
   390  		}).Times(2)
   391  	// Expect a call to update the trait resource with a finalizer.
   392  	mock.EXPECT().
   393  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   394  		DoAndReturn(func(ctx context.Context, trait *vzapi.MetricsTrait, opts ...client.UpdateOption) error {
   395  			assert.Equal("test-namespace", trait.Namespace)
   396  			assert.Equal("test-trait-name", trait.Name)
   397  			assert.Len(trait.Finalizers, 1)
   398  			assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0])
   399  			return nil
   400  		})
   401  	// Expect a call to get the containerized workload resource
   402  	mock.EXPECT().
   403  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-workload-name"}, gomock.Not(gomock.Nil()), gomock.Any()).
   404  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *unstructured.Unstructured, opts ...client.GetOption) error {
   405  			workload.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind)
   406  			workload.SetNamespace(name.Namespace)
   407  			workload.SetName(name.Name)
   408  			workload.SetUID("test-workload-uid")
   409  			return nil
   410  		}).Times(2)
   411  	// Expect a call to get the prometheus configuration.
   412  	mock.EXPECT().
   413  		Get(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
   414  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deployment *k8sapps.Deployment, opts ...client.GetOption) error {
   415  			assert.Equal("istio-system", name.Namespace)
   416  			assert.Equal("prometheus", name.Name)
   417  			deployment.APIVersion = k8sapps.SchemeGroupVersion.Identifier()
   418  			deployment.Kind = deploymentKind
   419  			deployment.Namespace = name.Namespace
   420  			deployment.Name = name.Name
   421  			return nil
   422  		})
   423  	// Expect a call to get the containerized workload resource definition
   424  	mock.EXPECT().
   425  		Get(gomock.Any(), types.NamespacedName{Namespace: "", Name: "containerizedworkloads.core.oam.dev"}, gomock.Not(gomock.Nil()), gomock.Any()).
   426  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workloadDef *oamcore.WorkloadDefinition, opts ...client.GetOption) error {
   427  			workloadDef.Namespace = name.Namespace
   428  			workloadDef.Name = name.Name
   429  			workloadDef.Spec.ChildResourceKinds = []oamcore.ChildResourceKind{
   430  				{APIVersion: "apps/v1", Kind: "Deployment", Selector: nil},
   431  				{APIVersion: "v1", Kind: "Service", Selector: nil},
   432  			}
   433  			return nil
   434  		})
   435  	// Expect a call to list the child Deployment resources of the containerized workload definition
   436  	mock.EXPECT().
   437  		List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   438  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   439  			assert.True(list.GetKind() == deploymentKind || list.GetKind() == serviceKind)
   440  			if list.GetKind() == deploymentKind {
   441  				return appendAsUnstructured(list, testDeployment)
   442  			}
   443  			return nil
   444  		}).Times(2)
   445  	// Expect a call to get the deployment definition
   446  	mock.EXPECT().
   447  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-deployment-name"}, gomock.Not(gomock.Nil()), gomock.Any()).
   448  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deployment *k8sapps.Deployment, opts ...client.GetOption) error {
   449  			deployment.ObjectMeta = testDeployment.ObjectMeta
   450  			deployment.Spec = testDeployment.Spec
   451  			return nil
   452  		})
   453  	// Expect a call to get the Replicasets for deployment
   454  	mock.EXPECT().
   455  		List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   456  		DoAndReturn(func(ctx context.Context, list *k8sapps.ReplicaSetList, opts ...client.ListOption) error {
   457  			list.Items = []k8sapps.ReplicaSet{{ObjectMeta: k8smeta.ObjectMeta{Name: "test-rs", OwnerReferences: []k8smeta.OwnerReference{{APIVersion: testDeployment.APIVersion, Kind: testDeployment.Kind, Name: testDeployment.Name}}}, TypeMeta: k8smeta.TypeMeta{Kind: "ReplicSet", APIVersion: "v1"}}}
   458  			return nil
   459  		})
   460  	// Expect a call to get the Podlist for deployment
   461  	mock.EXPECT().
   462  		List(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   463  		DoAndReturn(func(ctx context.Context, list *k8score.PodList, opts ...client.ListOption) error {
   464  			list.Items = []k8score.Pod{{ObjectMeta: k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{APIVersion: "v1", Kind: "ReplicSet", Name: "test-rs"}}}}}
   465  			return nil
   466  		})
   467  	// Expect a call to get the Pod to update
   468  	mock.EXPECT().
   469  		Get(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   470  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, pod *k8score.Pod, opts ...client.GetOption) error {
   471  			pod.ObjectMeta = k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{APIVersion: "v1", Kind: "ReplicSet", Name: "test-rs"}}}
   472  			pod.CreationTimestamp = k8smeta.Now()
   473  			return nil
   474  		})
   475  	// Expect a call to update the child with annotations but return an error.
   476  	mock.EXPECT().Update(gomock.Any(), gomock.Any(), gomock.Any()).Return(fmt.Errorf("test-error"))
   477  	// Expect a call to get the status writer and return a mock.
   478  	mock.EXPECT().Status().Return(mockStatus).AnyTimes()
   479  	// Expect a call to update the status of the ingress trait.
   480  	// The status should include the error updating the deployment
   481  	mockStatus.EXPECT().
   482  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   483  		DoAndReturn(func(ctx context.Context, trait *vzapi.MetricsTrait, opts ...client.UpdateOption) error {
   484  			assert.Len(trait.Status.Conditions, 1)
   485  			assert.Equal(oamrt.ReasonReconcileError, trait.Status.Conditions[0].Reason)
   486  			return nil
   487  		})
   488  
   489  	// Create and make the request
   490  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   491  
   492  	reconciler := newMetricsTraitReconciler(mock)
   493  	result, err := reconciler.Reconcile(context.TODO(), request)
   494  
   495  	// Validate the results
   496  	mocker.Finish()
   497  	assert.NoError(err)
   498  	assert.Equal(true, result.Requeue)
   499  	assert.Equal(time.Duration(0), result.RequeueAfter)
   500  }
   501  
   502  // TestUnsupportedWorkloadType tests a metrics trait with an unsupported workload type
   503  // GIVEN a metrics trait has an unsupported workload type of ConfigMap
   504  // WHEN the metrics trait Reconcile method is invoked
   505  // THEN verify the trait is deleted
   506  func TestUnsupportedWorkloadType(t *testing.T) {
   507  	assert := asserts.New(t)
   508  
   509  	scheme := k8scheme.Scheme
   510  	_ = vzapi.AddToScheme(scheme)
   511  
   512  	workload := unstructured.Unstructured{}
   513  	workload.SetAPIVersion("fakeAPIVersion")
   514  	workload.SetKind("fakeKind")
   515  	workload.SetName("test-workload-name")
   516  	workload.SetNamespace("test-namespace")
   517  
   518  	c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
   519  		&vzapi.MetricsTrait{
   520  			TypeMeta: k8smeta.TypeMeta{
   521  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
   522  				Kind:       vzapi.MetricsTraitKind,
   523  			},
   524  			ObjectMeta: k8smeta.ObjectMeta{
   525  				Namespace: "test-namespace",
   526  				Name:      "test-trait-name",
   527  				Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
   528  			},
   529  			Spec: vzapi.MetricsTraitSpec{
   530  				WorkloadReference: oamrt.TypedReference{
   531  					APIVersion: "fakeAPIVersion",
   532  					Kind:       "fakeKind",
   533  					Name:       "test-workload-name",
   534  				},
   535  			},
   536  		},
   537  		&workload,
   538  	).Build()
   539  
   540  	// Create and make the request
   541  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   542  
   543  	reconciler := newMetricsTraitReconciler(c)
   544  	result, err := reconciler.Reconcile(context.TODO(), request)
   545  
   546  	// Validate the results
   547  	assert.NoError(err)
   548  	assert.Equal(false, result.Requeue)
   549  }
   550  
   551  // TestMetricsTraitCreatedForWLSWorkload tests creation of a metrics trait related to a WLS workload.
   552  // GIVEN a metrics trait that has been created
   553  // WHEN the metrics trait Reconcile method is invoked
   554  // THEN verify that metrics trait finalizer is added
   555  // AND verify that pod annotations are updated
   556  // AND verify that the scraper configmap is updated
   557  // AND verify that the scraper pod is restarted
   558  func TestMetricsTraitCreatedForWLSWorkload(t *testing.T) {
   559  	assert := asserts.New(t)
   560  
   561  	c := wlsWorkloadClient(false)
   562  
   563  	// Create and make the request
   564  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   565  	reconciler := newMetricsTraitReconciler(c)
   566  	result, err := reconciler.Reconcile(context.TODO(), request)
   567  
   568  	// Validate the results
   569  	assert.NoError(err)
   570  	assert.Equal(true, result.Requeue)
   571  	assert.Equal(time.Duration(0), result.RequeueAfter)
   572  }
   573  
   574  // TestMetricsTraitDeletedForWLSWorkload tests reconciling a deleted metrics trait related to a WLS workload.
   575  // GIVEN a metrics trait with a non-zero deletion time
   576  // WHEN the metrics trait Reconcile method is invoked
   577  // THEN verify that metrics trait finalizer is removed
   578  // AND verify that pod annotations are cleaned up
   579  // AND verify that the scraper configmap is cleanup up
   580  // AND verify that the scraper pod is restarted
   581  func TestMetricsTraitDeletedForWLSWorkload(t *testing.T) {
   582  	assert := asserts.New(t)
   583  
   584  	c := wlsWorkloadClient(true)
   585  
   586  	// Create and make the request
   587  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   588  	reconciler := newMetricsTraitReconciler(c)
   589  	result, err := reconciler.Reconcile(context.TODO(), request)
   590  
   591  	// Validate the results
   592  	assert.NoError(err)
   593  	assert.Equal(true, result.Requeue)
   594  	assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0)
   595  }
   596  
   597  // TestMetricsTraitCreatedWithMultiplePorts tests the creation of a metrics trait related to a Coherence workload.
   598  // GIVEN a metrics trait that has been created that specifies multiple metrics ports
   599  // AND the metrics trait is related to a Coherence workload
   600  // WHEN the metrics trait Reconcile method is invoked
   601  // THEN verify that metrics trait finalizer is added
   602  // AND verify that pod annotations are updated
   603  // AND verify that the scraper configmap is updated
   604  // AND verify that the scraper pod is restarted
   605  func TestMetricsTraitCreatedWithMultiplePorts(t *testing.T) {
   606  	assert := asserts.New(t)
   607  
   608  	c := cohWorkloadClient(false, -1, 8080, 8081, 8082)
   609  
   610  	// Create and make the request
   611  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   612  
   613  	reconciler := newMetricsTraitReconciler(c)
   614  	result, err := reconciler.Reconcile(context.TODO(), request)
   615  
   616  	// Validate the results
   617  	assert.NoError(err)
   618  	assert.Equal(true, result.Requeue)
   619  	assert.Equal(time.Duration(0), result.RequeueAfter)
   620  }
   621  
   622  // TestMetricsTraitCreatedWithMultiplePortsAndPort tests the creation of a metrics trait related to a Coherence workload.
   623  // GIVEN a metrics trait that has been created that specifies multiple metrics ports and a single port
   624  // AND the metrics trait is related to a Coherence workload
   625  // WHEN the metrics trait Reconcile method is invoked
   626  // THEN verify that metrics trait finalizer is added
   627  // AND verify that pod annotations are updated
   628  // AND verify that the scraper configmap is updated
   629  // AND verify that the scraper pod is restarted
   630  func TestMetricsTraitCreatedWithMultiplePortsAndPort(t *testing.T) {
   631  	assert := asserts.New(t)
   632  
   633  	c := cohWorkloadClient(false, 8080, 8081, 8082, 8083)
   634  
   635  	// Create and make the request
   636  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   637  
   638  	reconciler := newMetricsTraitReconciler(c)
   639  	result, err := reconciler.Reconcile(context.TODO(), request)
   640  
   641  	// Validate the results
   642  	assert.NoError(err)
   643  	assert.Equal(true, result.Requeue)
   644  	assert.Equal(time.Duration(0), result.RequeueAfter)
   645  }
   646  
   647  // TestMetricsTraitDeletedForCOHWorkload tests deletion of a metrics trait related to a coherence workload.
   648  // GIVEN a metrics trait with a non-zero deletion time
   649  // WHEN the metrics trait Reconcile method is invoked
   650  // THEN verify that metrics trait finalizer is removed
   651  // AND verify that pod annotations are cleaned up
   652  // AND verify that the scraper configmap is cleanup up
   653  // AND verify that the scraper pod is restarted
   654  func TestMetricsTraitDeletedForCOHWorkload(t *testing.T) {
   655  	assert := asserts.New(t)
   656  
   657  	c := cohWorkloadClient(true, -1)
   658  
   659  	// Create and make the request
   660  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   661  	reconciler := newMetricsTraitReconciler(c)
   662  	result, err := reconciler.Reconcile(context.TODO(), request)
   663  
   664  	// Validate the results
   665  	assert.NoError(err)
   666  	assert.Equal(true, result.Requeue)
   667  	assert.GreaterOrEqual(result.RequeueAfter.Seconds(), 45.0)
   668  }
   669  
   670  // TestUseHTTPSForScrapeTargetFalseConditions tests that false is returned for the following conditions
   671  // GIVEN a unlabeled Istio namespace or a workload of kind VerrazzanoCoherenceWorkload or a workload of kind Coherence
   672  // WHEN the useHttpsForScrapeTarget method is invoked
   673  // THEN verify that the false boolean value is returned since all those conditions require an http scrape target
   674  func TestUseHTTPSForScrapeTargetFalseConditions(t *testing.T) {
   675  	assert := asserts.New(t)
   676  	mocker := gomock.NewController(t)
   677  	mock := mocks.NewMockClient(mocker)
   678  
   679  	mtrait := vzapi.MetricsTrait{
   680  		TypeMeta: k8smeta.TypeMeta{
   681  			Kind: "VerrazzanoCoherenceWorkload",
   682  		},
   683  	}
   684  
   685  	testNamespace := k8score.Namespace{
   686  		TypeMeta: k8smeta.TypeMeta{
   687  			Kind: "Namespace",
   688  		},
   689  		ObjectMeta: k8smeta.ObjectMeta{
   690  			Name: "test-namespace",
   691  		},
   692  	}
   693  
   694  	// Expect a call to get the namespace definition
   695  	mock.EXPECT().
   696  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   697  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, namespace *k8score.Namespace, opts ...client.GetOption) error {
   698  			namespace.TypeMeta = testNamespace.TypeMeta
   699  			namespace.ObjectMeta = testNamespace.ObjectMeta
   700  			return nil
   701  		})
   702  
   703  	mtrait.Spec.WorkloadReference.Kind = "VerrazzanoCoherenceWorkload"
   704  	https, _ := useHTTPSForScrapeTarget(context.TODO(), nil, &mtrait)
   705  	// Expect https to be false for scrape target of Kind VerrazzanoCoherenceWorkload
   706  	assert.False(https, "Expected https to be false for Workload of Kind VerrazzanoCoherenceWorkload")
   707  
   708  	mtrait.Spec.WorkloadReference.Kind = "Coherence"
   709  	https, _ = useHTTPSForScrapeTarget(context.TODO(), nil, &mtrait)
   710  	// Expect https to be false for scrape target of Kind Coherence
   711  	assert.False(https, "Expected https to be false for Workload of Kind Coherence")
   712  
   713  	reconciler := newMetricsTraitReconciler(mock)
   714  
   715  	mtrait.Spec.WorkloadReference.Kind = ""
   716  	https, _ = useHTTPSForScrapeTarget(context.TODO(), reconciler.Client, &mtrait)
   717  	// Expect https to be false for namespaces NOT labeled for istio-injection
   718  	assert.False(https, "Expected https to be false for namespace NOT labeled for istio injection")
   719  	mocker.Finish()
   720  }
   721  
   722  // TestUseHTTPSForScrapeTargetTrueCondition tests that true is returned for namespaces marked for Istio injection
   723  // GIVEN a labeled Istio namespace
   724  // WHEN the useHttpsForScrapeTarget method is invoked
   725  // THEN verify that the true boolean value is returned since pods in Istio namespaces require an https scrape target because of MTLS
   726  func TestUseHTTPSForScrapeTargetTrueCondition(t *testing.T) {
   727  	assert := asserts.New(t)
   728  	mocker := gomock.NewController(t)
   729  	mock := mocks.NewMockClient(mocker)
   730  
   731  	mtrait := vzapi.MetricsTrait{
   732  		TypeMeta: k8smeta.TypeMeta{
   733  			Kind: "ContainerizedWorkload",
   734  		},
   735  	}
   736  
   737  	testNamespace := k8score.Namespace{
   738  		TypeMeta: k8smeta.TypeMeta{
   739  			Kind: "Namespace",
   740  		},
   741  		ObjectMeta: k8smeta.ObjectMeta{
   742  			Name: "test-namespace",
   743  		},
   744  	}
   745  
   746  	// Expect a call to get the namespace definition
   747  	mock.EXPECT().
   748  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   749  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, namespace *k8score.Namespace, opts ...client.GetOption) error {
   750  			namespace.TypeMeta = testNamespace.TypeMeta
   751  			namespace.ObjectMeta = testNamespace.ObjectMeta
   752  			return nil
   753  		})
   754  
   755  	reconciler := newMetricsTraitReconciler(mock)
   756  
   757  	labels := map[string]string{
   758  		"istio-injection": "enabled",
   759  	}
   760  	testNamespace.ObjectMeta.Labels = labels
   761  	https, _ := useHTTPSForScrapeTarget(context.TODO(), reconciler.Client, &mtrait)
   762  	// Expect https to be true for namespaces labeled for istio-injection
   763  	assert.True(https, "Expected https to be true for namespaces labeled for Istio injection")
   764  	mocker.Finish()
   765  }
   766  
   767  // newMetricsTraitReconciler creates a new reconciler for testing
   768  // cli - The Kerberos client to inject into the reconciler
   769  func newMetricsTraitReconciler(cli client.Client) Reconciler {
   770  	scheme := runtime.NewScheme()
   771  	vzapi.AddToScheme(scheme)
   772  	reconciler := Reconciler{
   773  		Client:  cli,
   774  		Log:     zap.S(),
   775  		Scheme:  scheme,
   776  		Scraper: "istio-system/prometheus",
   777  	}
   778  	return reconciler
   779  }
   780  
   781  // convertToUnstructured converts an object to an Unstructured version
   782  // object - The object to convert to Unstructured
   783  func convertToUnstructured(object interface{}) (unstructured.Unstructured, error) {
   784  	bytes, err := json.Marshal(object)
   785  	if err != nil {
   786  		return unstructured.Unstructured{}, err
   787  	}
   788  	var u map[string]interface{}
   789  	json.Unmarshal(bytes, &u)
   790  	return unstructured.Unstructured{Object: u}, nil
   791  }
   792  
   793  // appendAsUnstructured appends an object to the list after converting it to an Unstructured
   794  // list - The list to append to.
   795  // object - The object to convert to Unstructured and append to the list
   796  func appendAsUnstructured(list *unstructured.UnstructuredList, object interface{}) error {
   797  	u, err := convertToUnstructured(object)
   798  	if err != nil {
   799  		return err
   800  	}
   801  	list.Items = append(list.Items, u)
   802  	return nil
   803  }
   804  
   805  // TestReconcileKubeSystem tests to make sure we do not reconcile
   806  // Any resource that belong to the kube-system namespace
   807  func TestReconcileKubeSystem(t *testing.T) {
   808  	assert := asserts.New(t)
   809  
   810  	var mocker = gomock.NewController(t)
   811  	var cli = mocks.NewMockClient(mocker)
   812  
   813  	// create a request and reconcile it
   814  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: vzconst.KubeSystem, Name: "test-trait-name"}}
   815  	reconciler := newMetricsTraitReconciler(cli)
   816  	result, err := reconciler.Reconcile(context.TODO(), request)
   817  
   818  	// Validate the results
   819  	mocker.Finish()
   820  	assert.Nil(err)
   821  	assert.True(result.IsZero())
   822  }
   823  
   824  // TestMetricsTraitDisabledForContainerizedWorkload tests the creation of a metrics trait related to a containerized workload.
   825  // GIVEN a metrics trait that has been disabled
   826  // WHEN the metrics trait Reconcile method is invoked
   827  // THEN verify that metrics trait finalizer is added
   828  // AND verify that the scraper configmap is updated with the scrape job being removed
   829  func TestMetricsTraitDisabledForContainerizedWorkload(t *testing.T) {
   830  	assert := asserts.New(t)
   831  
   832  	c := containerizedWorkloadClient(false, false, true)
   833  
   834  	// Create and make the request
   835  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   836  	reconciler := newMetricsTraitReconciler(c)
   837  	result, err := reconciler.Reconcile(context.TODO(), request)
   838  	// Validate the results
   839  	assert.NoError(err)
   840  	assert.Equal(true, result.Requeue)
   841  }
   842  
   843  // TestLegacyPrometheusScraper tests the case where the scraper is the default (legacy) VMO Prometheus.
   844  func TestLegacyPrometheusScraper(t *testing.T) {
   845  	assert := asserts.New(t)
   846  
   847  	// GIVEN a containerized workload and the reconciler scraper is configured to use the default (legacy) Prometheus scraper
   848  	//  WHEN we reconcile metrics traits
   849  	//  THEN the trait is updated with a finalizer and a ServiceMonitor has been created
   850  	c := containerizedWorkloadClient(false, false, false)
   851  
   852  	// Create and make the request
   853  	request := ctrl.Request{NamespacedName: types.NamespacedName{Namespace: "test-namespace", Name: "test-trait-name"}}
   854  
   855  	reconciler := newMetricsTraitReconciler(c)
   856  	reconciler.Scraper = constants.DefaultScraperName
   857  
   858  	result, err := reconciler.Reconcile(context.TODO(), request)
   859  
   860  	// Validate the results
   861  	assert.NoError(err)
   862  	assert.Equal(true, result.Requeue)
   863  	assert.True(result.RequeueAfter > 0)
   864  
   865  	trait := vzapi.MetricsTrait{}
   866  	err = c.Get(context.TODO(), types.NamespacedName{Name: "test-trait-name", Namespace: "test-namespace"}, &trait)
   867  	assert.NoError(err)
   868  	assert.Equal("test-namespace", trait.Namespace)
   869  	assert.Equal("test-trait-name", trait.Name)
   870  	assert.Len(trait.Finalizers, 1)
   871  	assert.Equal("metricstrait.finalizers.verrazzano.io", trait.Finalizers[0])
   872  
   873  	monitor := &promoperapi.ServiceMonitor{}
   874  	err = c.Get(context.TODO(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app-test-namespace-test-comp"}, monitor)
   875  	assert.NoError(err)
   876  }
   877  
   878  // TestCreateScrapeConfigFromTrait tests the createScrapeConfigFromTrait func calls
   879  func TestCreateScrapeConfigFromTrait(t *testing.T) {
   880  	scheme := k8scheme.Scheme
   881  	_ = promoperapi.AddToScheme(scheme)
   882  	_ = vzapi.AddToScheme(scheme)
   883  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
   884  	c := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&k8score.Namespace{}).Build()
   885  	testWorkLoad := unstructured.Unstructured{}
   886  	testWorkLoad.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind)
   887  
   888  	testWLSWorkload := unstructured.Unstructured{}
   889  	testWLSWorkload.SetAPIVersion("weblogic.oracle")
   890  	testWLSWorkload.SetKind("Domain")
   891  	tests := []struct {
   892  		name        string
   893  		trait       vzapi.MetricsTrait
   894  		workload    *unstructured.Unstructured
   895  		wantErr     bool
   896  		errContains string
   897  		isConfigNil bool
   898  		secret      *k8score.Secret
   899  	}{
   900  		// GIVEN a metricstrait and workload with default values
   901  		// WHEN createScrapeConfigFromTrait method is called
   902  		// THEN a non nil scrape config is created and no error is returned
   903  		{
   904  			"Create scrape config from a trait correctly",
   905  			vzapi.MetricsTrait{
   906  				ObjectMeta: k8smeta.ObjectMeta{
   907  					Labels: map[string]string{
   908  						oam.LabelAppName:      foo,
   909  						oam.LabelAppComponent: bar,
   910  					},
   911  				},
   912  			},
   913  			&testWorkLoad,
   914  			false,
   915  			"",
   916  			false,
   917  			nil,
   918  		},
   919  		// GIVEN a metricstrait missing app name label
   920  		// WHEN createScrapeConfigFromTrait is called
   921  		// THEN an error is returned
   922  		{
   923  			"Trait missing app name label",
   924  			vzapi.MetricsTrait{
   925  				ObjectMeta: k8smeta.ObjectMeta{
   926  					Labels: map[string]string{
   927  						oam.LabelAppComponent: bar,
   928  					},
   929  				},
   930  			},
   931  			nil,
   932  			true,
   933  			"metrics trait missing application name label",
   934  			true,
   935  			nil,
   936  		},
   937  		// GIVEN a metricstrait with missing component label
   938  		// WHEN createScrapeConfigFromTrait is called
   939  		// THEN an error is returned
   940  		{
   941  			"Trait missing component label",
   942  			vzapi.MetricsTrait{
   943  				ObjectMeta: k8smeta.ObjectMeta{
   944  					Labels: map[string]string{
   945  						oam.LabelAppName: foo,
   946  					},
   947  				},
   948  			},
   949  			&testWorkLoad,
   950  			true,
   951  			"metrics trait missing component name label",
   952  			true,
   953  			nil,
   954  		},
   955  		// GIVEN a nil workload and correct metricstrait
   956  		// WHEN createScrapeConfigFromTrait is called
   957  		// THEN no error is returned along with a nil config
   958  		{
   959  			"Nil workload",
   960  			vzapi.MetricsTrait{
   961  				ObjectMeta: k8smeta.ObjectMeta{
   962  					Labels: map[string]string{
   963  						oam.LabelAppName:      foo,
   964  						oam.LabelAppComponent: bar,
   965  					},
   966  				},
   967  			},
   968  			nil,
   969  			false,
   970  			"",
   971  			true,
   972  			nil,
   973  		},
   974  		// GIVEN a wls workload and correct metricstrait
   975  		// WHEN createScrapeConfigFromTrait is called
   976  		// THEN no error is returned along with a non nil config
   977  		{
   978  			"WLS Workload",
   979  			vzapi.MetricsTrait{
   980  				ObjectMeta: k8smeta.ObjectMeta{
   981  					Labels: map[string]string{
   982  						oam.LabelAppName:      foo,
   983  						oam.LabelAppComponent: bar,
   984  					},
   985  				},
   986  			},
   987  			&testWLSWorkload,
   988  			false,
   989  			"",
   990  			false,
   991  			nil,
   992  		},
   993  		{
   994  			"Basic auth",
   995  			vzapi.MetricsTrait{
   996  				ObjectMeta: k8smeta.ObjectMeta{
   997  					Labels: map[string]string{
   998  						oam.LabelAppName:      foo,
   999  						oam.LabelAppComponent: bar,
  1000  					},
  1001  				},
  1002  			},
  1003  			&testWLSWorkload,
  1004  			false,
  1005  			"",
  1006  			false,
  1007  			&k8score.Secret{
  1008  				Data: map[string][]byte{
  1009  					"username": []byte(foo),
  1010  					"password": []byte(bar),
  1011  				},
  1012  			},
  1013  		},
  1014  	}
  1015  
  1016  	for _, tt := range tests {
  1017  		t.Run(tt.name, func(t *testing.T) {
  1018  			_, job, err := createScrapeConfigFromTrait(context.Background(), &tt.trait, 0, tt.secret, tt.workload, c)
  1019  			if tt.wantErr {
  1020  				asserts.ErrorContains(t, err, tt.errContains)
  1021  			} else {
  1022  				asserts.NoError(t, err)
  1023  				if tt.isConfigNil {
  1024  					asserts.Nil(t, job)
  1025  				} else {
  1026  					asserts.NotNil(t, job)
  1027  					asserts.True(t, job.Exists("scheme"))
  1028  					asserts.Equal(t, tt.secret != nil, job.Exists(basicAuthLabel))
  1029  				}
  1030  			}
  1031  		})
  1032  	}
  1033  }
  1034  
  1035  // TestRemovedTraitReferencesFromOwner tests the removedTraitReferencesFromOwner reconcile method
  1036  func TestRemovedTraitReferencesFromOwner(t *testing.T) {
  1037  	testTrait := vzapi.MetricsTrait{
  1038  		ObjectMeta: k8smeta.ObjectMeta{
  1039  			Labels: map[string]string{
  1040  				oam.LabelAppName:      foo,
  1041  				oam.LabelAppComponent: bar,
  1042  			},
  1043  		},
  1044  	}
  1045  	testTrait.APIVersion = foo
  1046  	testTrait.Kind = bar
  1047  	ttro := runtime.Object(&testTrait)
  1048  	var scope conversion.Scope
  1049  	testTraitRawExtension := &runtime.RawExtension{}
  1050  	asserts.NoError(t, runtime.Convert_runtime_Object_To_runtime_RawExtension(&ttro, testTraitRawExtension, scope))
  1051  
  1052  	scheme := k8scheme.Scheme
  1053  	_ = vzapi.AddToScheme(scheme)
  1054  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1055  	cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1056  		&k8score.Namespace{},
  1057  		&oamcore.ApplicationConfiguration{
  1058  			ObjectMeta: k8smeta.ObjectMeta{
  1059  				Name: foo,
  1060  			},
  1061  			Spec: oamcore.ApplicationConfigurationSpec{
  1062  				Components: []oamcore.ApplicationConfigurationComponent{
  1063  					{
  1064  						ComponentName: bar,
  1065  						Traits: []oamcore.ComponentTrait{
  1066  							{
  1067  								Trait: *testTraitRawExtension,
  1068  							},
  1069  						},
  1070  					},
  1071  				},
  1072  			},
  1073  		},
  1074  	).Build()
  1075  
  1076  	badGetClient := &erroringGetClient{cli}
  1077  	badUpdateClient := &erroringUpdateClient{cli}
  1078  
  1079  	tests := []struct {
  1080  		name            string
  1081  		client          client.Client
  1082  		wantErr         bool
  1083  		errContains     string
  1084  		operationResult controllerutil.OperationResult
  1085  	}{
  1086  		// GIVEN a scenario where client is unable to make Get call to the api server
  1087  		// WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler
  1088  		// then the method returns an error
  1089  		{
  1090  			"Unsuccessful get",
  1091  			badGetClient,
  1092  			true,
  1093  			getError,
  1094  			controllerutil.OperationResultNone,
  1095  		},
  1096  		// GIVEN a scenario where client is unable to make Update call to the api server
  1097  		// WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler
  1098  		// then the method returns an error
  1099  		{
  1100  			"Unsuccessful Update",
  1101  			badUpdateClient,
  1102  			true,
  1103  			updateError,
  1104  			controllerutil.OperationResultNone,
  1105  		},
  1106  		// GIVEN an appconfig with the given metricstrait as a component
  1107  		// WHEN a call is made to removedTraitReferencesFromOwner func by the reconciler
  1108  		// THEN the appconfig is updated to remove the trait from the appconfig
  1109  		{
  1110  			"Successful update",
  1111  			cli,
  1112  			false,
  1113  			"",
  1114  			controllerutil.OperationResultUpdated,
  1115  		},
  1116  	}
  1117  
  1118  	for _, tt := range tests {
  1119  		t.Run(tt.name, func(t *testing.T) {
  1120  			reconciler := newMetricsTraitReconciler(tt.client)
  1121  			_, op, err := reconciler.removedTraitReferencesFromOwner(context.Background(), &k8smeta.OwnerReference{Name: foo}, &testTrait, vzlog.DefaultLogger())
  1122  			asserts.Equal(t, op, tt.operationResult)
  1123  			if tt.wantErr {
  1124  				asserts.ErrorContains(t, err, tt.errContains)
  1125  			} else {
  1126  				asserts.NoError(t, err)
  1127  			}
  1128  		})
  1129  	}
  1130  }
  1131  
  1132  // TestDeleteServiceMonitor tests the deleteServiceMonitor func call
  1133  func TestDeleteServiceMonitor(t *testing.T) {
  1134  	scheme := k8scheme.Scheme
  1135  	_ = vzapi.AddToScheme(scheme)
  1136  	_ = promoperapi.AddToScheme(scheme)
  1137  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1138  	tests := []struct {
  1139  		name   string
  1140  		trait  vzapi.MetricsTrait
  1141  		result controllerutil.OperationResult
  1142  	}{
  1143  		// GIVEN a metricstrait
  1144  		// WHEN the trait is enabled and not scheduled for deletion
  1145  		// THEN the ServiceMonitor resource is not deleted
  1146  		{
  1147  			"Trait is enabled",
  1148  			vzapi.MetricsTrait{
  1149  				Spec: vzapi.MetricsTraitSpec{
  1150  					Enabled: getBoolPtr(true),
  1151  				},
  1152  			},
  1153  			controllerutil.OperationResultNone,
  1154  		},
  1155  		// GIVEN a metricstrait
  1156  		// WHEN the trait is scheduled for deletion
  1157  		// THEN the associated ServiceMonitor is deleted
  1158  		{
  1159  			"Successful Delete",
  1160  			vzapi.MetricsTrait{
  1161  				ObjectMeta: k8smeta.ObjectMeta{
  1162  					DeletionTimestamp: &k8smeta.Time{Time: time.Now()},
  1163  				},
  1164  			},
  1165  			controllerutil.OperationResultUpdated,
  1166  		},
  1167  		// GIVEN a metricstrait
  1168  		// WHEN the trait is disabled for deletion
  1169  		// THEN the associated ServiceMonitor is deleted
  1170  		{
  1171  			"Trait is disabled",
  1172  			vzapi.MetricsTrait{
  1173  				Spec: vzapi.MetricsTraitSpec{
  1174  					Enabled: getBoolPtr(false),
  1175  				},
  1176  			},
  1177  			controllerutil.OperationResultUpdated,
  1178  		},
  1179  		{
  1180  			"Enabled but scheduled for deletion",
  1181  			vzapi.MetricsTrait{
  1182  				ObjectMeta: k8smeta.ObjectMeta{
  1183  					DeletionTimestamp: &k8smeta.Time{Time: time.Now()},
  1184  				},
  1185  				Spec: vzapi.MetricsTraitSpec{
  1186  					Enabled: getBoolPtr(true),
  1187  				},
  1188  			},
  1189  			controllerutil.OperationResultUpdated,
  1190  		},
  1191  	}
  1192  	for _, tt := range tests {
  1193  		t.Run(tt.name, func(t *testing.T) {
  1194  			cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1195  				&promoperapi.ServiceMonitor{
  1196  					ObjectMeta: k8smeta.ObjectMeta{
  1197  						Name:      foo,
  1198  						Namespace: bar,
  1199  					},
  1200  				},
  1201  				&k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: bar}},
  1202  			).Build()
  1203  			reconciler := newMetricsTraitReconciler(cli)
  1204  			res, _ := reconciler.deleteServiceMonitor(context.TODO(), bar, foo, &tt.trait, vzlog.DefaultLogger())
  1205  			asserts.Equal(t, tt.result, res)
  1206  		})
  1207  	}
  1208  }
  1209  
  1210  // TestUpdateRelatedStatefulSet tests the updateRelatedStatefulSet func call
  1211  // GIVEN metricstrait, workload and child resources
  1212  // WHEN updateRelatedPod func call is made by the reconciler
  1213  // THEN the related workload StatefulSets are mutated
  1214  func TestUpdateRelatedStatefulSet(t *testing.T) {
  1215  	scheme := runtime.NewScheme()
  1216  	_ = k8score.AddToScheme(scheme)
  1217  	_ = k8sapps.AddToScheme(scheme)
  1218  	port := 443
  1219  	path := foo
  1220  	trait := vzapi.MetricsTrait{
  1221  		Spec: vzapi.MetricsTraitSpec{
  1222  			Port: &port,
  1223  			Path: &path,
  1224  		},
  1225  	}
  1226  	child := unstructured.Unstructured{}
  1227  	child.SetAPIVersion(foo)
  1228  	child.SetKind(bar)
  1229  	child.SetName(foo)
  1230  	child.SetKind(bar)
  1231  	cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1232  		&k8sapps.StatefulSet{
  1233  			TypeMeta:   k8smeta.TypeMeta{APIVersion: child.GetAPIVersion(), Kind: child.GetKind()},
  1234  			ObjectMeta: k8smeta.ObjectMeta{Namespace: child.GetNamespace(), Name: child.GetName(), CreationTimestamp: k8smeta.Now()},
  1235  		},
  1236  		&k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: child.GetNamespace()}},
  1237  		&k8score.Pod{ObjectMeta: k8smeta.ObjectMeta{OwnerReferences: []k8smeta.OwnerReference{{Kind: child.GetKind(), APIVersion: child.GetAPIVersion(), Name: child.GetName()}}}},
  1238  	).Build()
  1239  	reconciler0 := newMetricsTraitReconciler(cli)
  1240  	_, res0, err0 := reconciler0.updateRelatedStatefulSet(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger())
  1241  	asserts.Equal(t, res0, controllerutil.OperationResultUpdated)
  1242  	asserts.NoError(t, err0)
  1243  
  1244  	badGetClient := &erroringGetClient{cli}
  1245  	reconciler1 := newMetricsTraitReconciler(badGetClient)
  1246  	_, res1, err1 := reconciler1.updateRelatedStatefulSet(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger())
  1247  	asserts.Equal(t, res1, controllerutil.OperationResultNone)
  1248  	asserts.ErrorContains(t, err1, getError)
  1249  }
  1250  
  1251  // TestUpdateRelatedPod tests the updateRelatedPod func call
  1252  // GIVEN metricstrait, workload and child resources
  1253  // WHEN updateRelatedPod func call is made by the reconciler
  1254  // THEN the related workload pods are mutated
  1255  func TestUpdateRelatedPod(t *testing.T) {
  1256  	scheme := runtime.NewScheme()
  1257  	_ = k8score.AddToScheme(scheme)
  1258  
  1259  	port := 443
  1260  	path := foo
  1261  	trait := vzapi.MetricsTrait{
  1262  		Spec: vzapi.MetricsTraitSpec{
  1263  			Port: &port,
  1264  			Path: &path,
  1265  		},
  1266  	}
  1267  
  1268  	child := unstructured.Unstructured{}
  1269  	child.SetAPIVersion(foo)
  1270  	child.SetKind(bar)
  1271  	child.SetName(foo)
  1272  	child.SetKind(bar)
  1273  
  1274  	cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1275  		&k8score.Pod{
  1276  			TypeMeta:   k8smeta.TypeMeta{APIVersion: child.GetAPIVersion(), Kind: child.GetKind()},
  1277  			ObjectMeta: k8smeta.ObjectMeta{Namespace: child.GetNamespace(), Name: child.GetName(), CreationTimestamp: k8smeta.Now()},
  1278  		},
  1279  		&k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: child.GetNamespace()}},
  1280  	).Build()
  1281  
  1282  	reconciler0 := newMetricsTraitReconciler(cli)
  1283  	_, res0, err0 := reconciler0.updateRelatedPod(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger())
  1284  	asserts.Equal(t, res0, controllerutil.OperationResultUpdated)
  1285  	asserts.NoError(t, err0)
  1286  
  1287  	badGetClient := &erroringGetClient{cli}
  1288  	reconciler1 := newMetricsTraitReconciler(badGetClient)
  1289  
  1290  	_, res1, err1 := reconciler1.updateRelatedPod(context.Background(), &trait, nil, nil, &child, vzlog.DefaultLogger())
  1291  	asserts.Equal(t, res1, controllerutil.OperationResultNone)
  1292  	asserts.ErrorContains(t, err1, getError)
  1293  }
  1294  
  1295  // TestUpdatePrometheusScraperConfigMap tests the updatePrometheusScraperConfigMap func call
  1296  func TestUpdatePrometheusScraperConfigMap(t *testing.T) {
  1297  	scheme := runtime.NewScheme()
  1298  	_ = k8sapps.AddToScheme(scheme)
  1299  	_ = vzapi.AddToScheme(scheme)
  1300  	_ = k8score.AddToScheme(scheme)
  1301  
  1302  	port := 443
  1303  	path := foo
  1304  	testWorkLoad := unstructured.Unstructured{}
  1305  	testWorkLoad.SetGroupVersionKind(oamcore.ContainerizedWorkloadGroupVersionKind)
  1306  
  1307  	testTrait := vzapi.MetricsTrait{
  1308  		ObjectMeta: k8smeta.ObjectMeta{
  1309  			Labels: map[string]string{
  1310  				oam.LabelAppName:      foo,
  1311  				oam.LabelAppComponent: bar,
  1312  			},
  1313  		},
  1314  		Spec: vzapi.MetricsTraitSpec{Port: &port, Path: &path},
  1315  	}
  1316  
  1317  	testDeployment0 := k8sapps.Deployment{
  1318  		ObjectMeta: k8smeta.ObjectMeta{Name: foo},
  1319  		Spec: k8sapps.DeploymentSpec{
  1320  			Template: k8score.PodTemplateSpec{
  1321  				Spec: k8score.PodSpec{
  1322  					Volumes: []k8score.Volume{
  1323  						{
  1324  							Name: "config-volume",
  1325  							VolumeSource: k8score.VolumeSource{
  1326  								ConfigMap: &k8score.ConfigMapVolumeSource{LocalObjectReference: k8score.LocalObjectReference{Name: foo}},
  1327  							},
  1328  						},
  1329  					},
  1330  				},
  1331  			},
  1332  		},
  1333  	}
  1334  
  1335  	// Deployment with an empty volume spec
  1336  	testDeployment1 := testDeployment0
  1337  	testDeployment1.Spec.Template.Spec.Volumes = []k8score.Volume{}
  1338  
  1339  	cli := fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1340  		&k8score.ConfigMap{
  1341  			ObjectMeta: k8smeta.ObjectMeta{Name: foo},
  1342  		},
  1343  		// &k8score.Namespace{ObjectMeta: k8smeta.ObjectMeta{Name: bar}},
  1344  		&k8score.Namespace{},
  1345  	).Build()
  1346  	badGetClient := &erroringGetClient{cli}
  1347  	badUpdateClient := &erroringUpdateClient{cli}
  1348  
  1349  	tests := []struct {
  1350  		name        string
  1351  		client      client.Client
  1352  		deployment  k8sapps.Deployment
  1353  		wantErr     bool
  1354  		errContains string
  1355  	}{
  1356  		// GIVEN a call to update the scraper ConfigMap
  1357  		// WHEN the resources are correctly configured
  1358  		// THEN the ConfigMap is updated with the scraper config
  1359  		{
  1360  			"Successful update",
  1361  			cli,
  1362  			testDeployment0,
  1363  			false,
  1364  			"",
  1365  		},
  1366  		// GIVEN a call to update the scraper config
  1367  		// WHEN the client is unable to make a Get call
  1368  		// THEN an error is returned
  1369  		{
  1370  			"Unsuccessful Get",
  1371  			badGetClient,
  1372  			testDeployment0,
  1373  			true,
  1374  			getError,
  1375  		},
  1376  		// GIVEN a call to update the scraper config
  1377  		// WHEN the client is unable to make an Update call
  1378  		// THEN an error is returned
  1379  		{
  1380  			"Unsuccessful Update",
  1381  			badUpdateClient,
  1382  			testDeployment0,
  1383  			true,
  1384  			updateError,
  1385  		},
  1386  		// GIVEN a call to update the scraper config
  1387  		// WHEN the deployment does not contain the ConfigMap name
  1388  		// THEN an error is returned
  1389  		{
  1390  			"Missing cm name",
  1391  			cli,
  1392  			testDeployment1,
  1393  			true,
  1394  			"failed to find Prometheus configmap name from deployment",
  1395  		},
  1396  	}
  1397  
  1398  	for _, tt := range tests {
  1399  		t.Run(tt.name, func(t *testing.T) {
  1400  			reconciler := newMetricsTraitReconciler(tt.client)
  1401  			_, res, err := reconciler.updatePrometheusScraperConfigMap(context.Background(), &testTrait, &testWorkLoad, nil, &tt.deployment, vzlog.DefaultLogger())
  1402  			if tt.wantErr {
  1403  				asserts.ErrorContains(t, err, tt.errContains)
  1404  			} else {
  1405  				asserts.Equal(t, controllerutil.OperationResultUpdated, res)
  1406  				asserts.NoError(t, err)
  1407  				cm := k8score.ConfigMap{}
  1408  				asserts.NoError(t, tt.client.Get(context.Background(), types.NamespacedName{Name: foo}, &cm))
  1409  				_, ok := cm.Data[prometheusConfigKey]
  1410  				asserts.True(t, ok)
  1411  			}
  1412  		})
  1413  	}
  1414  }
  1415  
  1416  // deploymentWorkloadClient returns a fake client with a containerized workload target in the trait
  1417  func containerizedWorkloadClient(deleting, deploymentDeleted, traitDisabled bool) client.WithWatch {
  1418  	scheme := k8scheme.Scheme
  1419  	_ = promoperapi.AddToScheme(scheme)
  1420  	_ = vzapi.AddToScheme(scheme)
  1421  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1422  
  1423  	testNamespace := "test-namespace"
  1424  	testWorkloadName := "test-workload-name"
  1425  	testWorkloadUID := types.UID("test-workload-uid")
  1426  
  1427  	trait := vzapi.MetricsTrait{
  1428  		TypeMeta: k8smeta.TypeMeta{
  1429  			APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1430  			Kind:       vzapi.MetricsTraitKind,
  1431  		},
  1432  		ObjectMeta: k8smeta.ObjectMeta{
  1433  			Namespace: testNamespace,
  1434  			Name:      "test-trait-name",
  1435  			Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
  1436  		},
  1437  		Spec: vzapi.MetricsTraitSpec{
  1438  			WorkloadReference: oamrt.TypedReference{
  1439  				APIVersion: oamcore.SchemeGroupVersion.Identifier(),
  1440  				Kind:       oamcore.ContainerizedWorkloadKind,
  1441  				Name:       testWorkloadName,
  1442  			},
  1443  		},
  1444  	}
  1445  	if deleting {
  1446  		trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()}
  1447  	}
  1448  	trueVal := true
  1449  	if traitDisabled {
  1450  		trait.Spec.Enabled = &trueVal
  1451  	}
  1452  
  1453  	objects := []client.Object{
  1454  		&k8sapps.Deployment{
  1455  			TypeMeta: k8smeta.TypeMeta{
  1456  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1457  				Kind:       "Deployment",
  1458  			},
  1459  			ObjectMeta: k8smeta.ObjectMeta{
  1460  				Name:      "prometheus",
  1461  				Namespace: "istio-system",
  1462  			},
  1463  		},
  1464  		&k8score.Service{
  1465  			TypeMeta: k8smeta.TypeMeta{
  1466  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1467  				Kind:       "Service",
  1468  			},
  1469  			ObjectMeta: k8smeta.ObjectMeta{
  1470  				Name:      "test-service",
  1471  				Namespace: testNamespace,
  1472  			},
  1473  		},
  1474  		&trait,
  1475  		&oamcore.ContainerizedWorkload{
  1476  			TypeMeta: k8smeta.TypeMeta{
  1477  				APIVersion: oamcore.SchemeGroupVersion.Identifier(),
  1478  				Kind:       oamcore.ContainerizedWorkloadKind,
  1479  			},
  1480  			ObjectMeta: k8smeta.ObjectMeta{
  1481  				Namespace: testNamespace,
  1482  				Name:      testWorkloadName,
  1483  				UID:       testWorkloadUID,
  1484  			},
  1485  		},
  1486  		&oamcore.WorkloadDefinition{
  1487  			ObjectMeta: k8smeta.ObjectMeta{
  1488  				Name: "containerizedworkloads.core.oam.dev",
  1489  			},
  1490  			Spec: oamcore.WorkloadDefinitionSpec{
  1491  				ChildResourceKinds: []oamcore.ChildResourceKind{
  1492  					{APIVersion: "apps/v1", Kind: "Deployment", Selector: nil},
  1493  					{APIVersion: "v1", Kind: "Service", Selector: nil},
  1494  				},
  1495  			},
  1496  		},
  1497  		&k8score.Namespace{
  1498  			ObjectMeta: k8smeta.ObjectMeta{
  1499  				Name: "test-namespace",
  1500  			},
  1501  		},
  1502  	}
  1503  
  1504  	if !deploymentDeleted {
  1505  		objects = append(objects, &k8sapps.Deployment{
  1506  			TypeMeta: k8smeta.TypeMeta{
  1507  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1508  				Kind:       "Deployment",
  1509  			},
  1510  			ObjectMeta: k8smeta.ObjectMeta{
  1511  				Name:              "test-deployment-name",
  1512  				Namespace:         testNamespace,
  1513  				CreationTimestamp: k8smeta.Now(),
  1514  				OwnerReferences: []k8smeta.OwnerReference{{
  1515  					APIVersion: oamcore.SchemeGroupVersion.Identifier(),
  1516  					Kind:       oamcore.ContainerizedWorkloadKind,
  1517  					Name:       testWorkloadName,
  1518  					UID:        testWorkloadUID},
  1519  				},
  1520  			},
  1521  		})
  1522  	}
  1523  
  1524  	return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
  1525  }
  1526  
  1527  // deploymentWorkloadClient returns a fake client with a deployment target in the trait
  1528  func deploymentWorkloadClient(deleting bool) client.WithWatch {
  1529  	scheme := k8scheme.Scheme
  1530  	_ = promoperapi.AddToScheme(scheme)
  1531  	_ = vzapi.AddToScheme(scheme)
  1532  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1533  
  1534  	testNamespace := "test-namespace"
  1535  	testWorkloadName := "test-workload-name"
  1536  
  1537  	trait := vzapi.MetricsTrait{
  1538  		TypeMeta: k8smeta.TypeMeta{
  1539  			APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1540  			Kind:       vzapi.MetricsTraitKind,
  1541  		},
  1542  		ObjectMeta: k8smeta.ObjectMeta{
  1543  			Namespace: testNamespace,
  1544  			Name:      "test-trait-name",
  1545  			Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
  1546  		},
  1547  		Spec: vzapi.MetricsTraitSpec{
  1548  			WorkloadReference: oamrt.TypedReference{
  1549  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1550  				Kind:       "Deployment",
  1551  				Name:       testWorkloadName,
  1552  			},
  1553  		},
  1554  	}
  1555  	if deleting {
  1556  		trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()}
  1557  	}
  1558  
  1559  	return fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1560  		&k8sapps.Deployment{
  1561  			TypeMeta: k8smeta.TypeMeta{
  1562  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1563  				Kind:       "Deployment",
  1564  			},
  1565  			ObjectMeta: k8smeta.ObjectMeta{
  1566  				Name:      testWorkloadName,
  1567  				Namespace: testNamespace,
  1568  			},
  1569  		},
  1570  		&k8sapps.Deployment{
  1571  			TypeMeta: k8smeta.TypeMeta{
  1572  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1573  				Kind:       "Deployment",
  1574  			},
  1575  			ObjectMeta: k8smeta.ObjectMeta{
  1576  				Name:      "prometheus",
  1577  				Namespace: "istio-system",
  1578  			},
  1579  		},
  1580  		&k8score.Service{
  1581  			TypeMeta: k8smeta.TypeMeta{
  1582  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1583  				Kind:       "Service",
  1584  			},
  1585  			ObjectMeta: k8smeta.ObjectMeta{
  1586  				Name:      "test-service",
  1587  				Namespace: testNamespace,
  1588  			},
  1589  		},
  1590  		&trait,
  1591  		&oamcore.WorkloadDefinition{
  1592  			ObjectMeta: k8smeta.ObjectMeta{
  1593  				Name: "deployments.apps",
  1594  			},
  1595  			Spec: oamcore.WorkloadDefinitionSpec{
  1596  				ChildResourceKinds: []oamcore.ChildResourceKind{
  1597  					{APIVersion: "v1", Kind: "Service", Selector: nil},
  1598  				},
  1599  			},
  1600  		},
  1601  		&k8score.Namespace{
  1602  			ObjectMeta: k8smeta.ObjectMeta{
  1603  				Name: "test-namespace",
  1604  			},
  1605  		},
  1606  	).Build()
  1607  }
  1608  
  1609  // wlsWorkloadClient returns a fake client with a WebLogic Workload target in the trait
  1610  func wlsWorkloadClient(deleting bool) client.WithWatch {
  1611  	scheme := k8scheme.Scheme
  1612  	_ = promoperapi.AddToScheme(scheme)
  1613  	_ = vzapi.AddToScheme(scheme)
  1614  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1615  
  1616  	testNamespace := "test-namespace"
  1617  	testWorkloadName := "test-workload-name"
  1618  	testWorkloadUID := types.UID("test-workload-uid")
  1619  
  1620  	trait := vzapi.MetricsTrait{
  1621  		TypeMeta: k8smeta.TypeMeta{
  1622  			APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1623  			Kind:       vzapi.MetricsTraitKind,
  1624  		},
  1625  		ObjectMeta: k8smeta.ObjectMeta{
  1626  			Namespace: testNamespace,
  1627  			Name:      "test-trait-name",
  1628  			Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
  1629  		},
  1630  		Spec: vzapi.MetricsTraitSpec{
  1631  			WorkloadReference: oamrt.TypedReference{
  1632  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1633  				Kind:       vzconst.VerrazzanoWebLogicWorkloadKind,
  1634  				Name:       testWorkloadName,
  1635  			},
  1636  		},
  1637  	}
  1638  	if deleting {
  1639  		trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()}
  1640  	}
  1641  
  1642  	domain := unstructured.Unstructured{}
  1643  	domain.SetKind("Domain")
  1644  	domain.SetAPIVersion("weblogic.oracle/v8")
  1645  	domain.SetName("test-domain")
  1646  	domain.SetNamespace(testNamespace)
  1647  
  1648  	objects := []client.Object{
  1649  		&k8sapps.Deployment{
  1650  			TypeMeta: k8smeta.TypeMeta{
  1651  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1652  				Kind:       "Deployment",
  1653  			},
  1654  			ObjectMeta: k8smeta.ObjectMeta{
  1655  				Name:      "prometheus",
  1656  				Namespace: "istio-system",
  1657  			},
  1658  		},
  1659  		&k8score.Service{
  1660  			TypeMeta: k8smeta.TypeMeta{
  1661  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1662  				Kind:       "Service",
  1663  			},
  1664  			ObjectMeta: k8smeta.ObjectMeta{
  1665  				Name:      "test-service",
  1666  				Namespace: testNamespace,
  1667  			},
  1668  		},
  1669  		&trait,
  1670  		&vzapi.VerrazzanoWebLogicWorkload{
  1671  			TypeMeta: k8smeta.TypeMeta{
  1672  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1673  				Kind:       vzconst.VerrazzanoWebLogicWorkloadKind,
  1674  			},
  1675  			ObjectMeta: k8smeta.ObjectMeta{
  1676  				Namespace: testNamespace,
  1677  				Name:      testWorkloadName,
  1678  				UID:       testWorkloadUID,
  1679  			},
  1680  			Spec: vzapi.VerrazzanoWebLogicWorkloadSpec{
  1681  				Template: vzapi.VerrazzanoWebLogicWorkloadTemplate{
  1682  					APIVersion: "weblogic.oracle/v8",
  1683  					Metadata: runtime.RawExtension{
  1684  						Raw:    []byte(`{"name": "test-domain"}`),
  1685  						Object: &unstructured.Unstructured{},
  1686  					},
  1687  				},
  1688  			},
  1689  		},
  1690  		&oamcore.WorkloadDefinition{
  1691  			ObjectMeta: k8smeta.ObjectMeta{
  1692  				Name: "domains.weblogic.oracle",
  1693  			},
  1694  			Spec: oamcore.WorkloadDefinitionSpec{
  1695  				ChildResourceKinds: []oamcore.ChildResourceKind{
  1696  					{APIVersion: "apps/v1", Kind: "Deployment", Selector: nil},
  1697  					{APIVersion: "v1", Kind: "Service", Selector: nil},
  1698  				},
  1699  			},
  1700  		},
  1701  		&k8sapps.Deployment{
  1702  			TypeMeta: k8smeta.TypeMeta{
  1703  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1704  				Kind:       "Deployment",
  1705  			},
  1706  			ObjectMeta: k8smeta.ObjectMeta{
  1707  				Name:              "test-deployment-name",
  1708  				Namespace:         testNamespace,
  1709  				CreationTimestamp: k8smeta.Now(),
  1710  				OwnerReferences: []k8smeta.OwnerReference{{
  1711  					APIVersion: oamcore.SchemeGroupVersion.Identifier(),
  1712  					Kind:       oamcore.ContainerizedWorkloadKind,
  1713  					Name:       testWorkloadName,
  1714  					UID:        testWorkloadUID},
  1715  				},
  1716  			},
  1717  		},
  1718  		&k8score.Namespace{
  1719  			ObjectMeta: k8smeta.ObjectMeta{
  1720  				Name: "test-namespace",
  1721  			},
  1722  		},
  1723  		&domain,
  1724  	}
  1725  
  1726  	return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
  1727  }
  1728  
  1729  // cohWorkloadClient returns a fake client with a Coherence Workload target in the trait
  1730  func cohWorkloadClient(deleting bool, portNum int, ports ...int) client.WithWatch {
  1731  	scheme := k8scheme.Scheme
  1732  	_ = promoperapi.AddToScheme(scheme)
  1733  	_ = vzapi.AddToScheme(scheme)
  1734  	_ = oamcore.SchemeBuilder.AddToScheme(scheme)
  1735  
  1736  	testNamespace := "test-namespace"
  1737  	testWorkloadName := "test-workload-name"
  1738  	testWorkloadUID := types.UID("test-workload-uid")
  1739  
  1740  	coherence := unstructured.Unstructured{}
  1741  	coherence.SetNamespace(testNamespace)
  1742  	coherence.SetName("test-coherence")
  1743  	coherence.SetAPIVersion("coherence.oracle.com/v1")
  1744  	coherence.SetKind("Coherence")
  1745  
  1746  	trait := vzapi.MetricsTrait{
  1747  		TypeMeta: k8smeta.TypeMeta{
  1748  			APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1749  			Kind:       vzapi.MetricsTraitKind,
  1750  		},
  1751  		ObjectMeta: k8smeta.ObjectMeta{
  1752  			Namespace: testNamespace,
  1753  			Name:      "test-trait-name",
  1754  			Labels:    map[string]string{oam.LabelAppName: "test-app", oam.LabelAppComponent: "test-comp"},
  1755  		},
  1756  		Spec: vzapi.MetricsTraitSpec{
  1757  			WorkloadReference: oamrt.TypedReference{
  1758  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1759  				Kind:       vzconst.VerrazzanoCoherenceWorkloadKind,
  1760  				Name:       testWorkloadName,
  1761  			},
  1762  		},
  1763  	}
  1764  	if deleting {
  1765  		trait.DeletionTimestamp = &k8smeta.Time{Time: time.Now()}
  1766  	}
  1767  	if portNum >= 0 {
  1768  		trait.Spec.Port = &portNum
  1769  	}
  1770  	path := "/metrics"
  1771  	if len(ports) > 0 {
  1772  		for i := range ports {
  1773  			port := vzapi.PortSpec{Port: &ports[i], Path: &path}
  1774  			trait.Spec.Ports = append(trait.Spec.Ports, port)
  1775  		}
  1776  	}
  1777  
  1778  	return fake.NewClientBuilder().WithScheme(scheme).WithObjects(
  1779  		&k8sapps.Deployment{
  1780  			TypeMeta: k8smeta.TypeMeta{
  1781  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1782  				Kind:       "Deployment",
  1783  			},
  1784  			ObjectMeta: k8smeta.ObjectMeta{
  1785  				Name:              "test-deployment-name",
  1786  				Namespace:         testNamespace,
  1787  				CreationTimestamp: k8smeta.Now(),
  1788  				OwnerReferences: []k8smeta.OwnerReference{{
  1789  					APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1790  					Kind:       "Coherence",
  1791  					Name:       testWorkloadName,
  1792  					UID:        testWorkloadUID},
  1793  				},
  1794  			},
  1795  		},
  1796  		&k8sapps.Deployment{
  1797  			TypeMeta: k8smeta.TypeMeta{
  1798  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1799  				Kind:       "Deployment",
  1800  			},
  1801  			ObjectMeta: k8smeta.ObjectMeta{
  1802  				Name:      "prometheus",
  1803  				Namespace: "istio-system",
  1804  			},
  1805  		},
  1806  		&k8score.Service{
  1807  			TypeMeta: k8smeta.TypeMeta{
  1808  				APIVersion: k8sapps.SchemeGroupVersion.Identifier(),
  1809  				Kind:       "Service",
  1810  			},
  1811  			ObjectMeta: k8smeta.ObjectMeta{
  1812  				Name:      "test-service",
  1813  				Namespace: testNamespace,
  1814  			},
  1815  		},
  1816  		&trait,
  1817  		&vzapi.VerrazzanoCoherenceWorkload{
  1818  			TypeMeta: k8smeta.TypeMeta{
  1819  				APIVersion: vzapi.SchemeGroupVersion.Identifier(),
  1820  				Kind:       vzconst.VerrazzanoCoherenceWorkloadKind,
  1821  			},
  1822  			ObjectMeta: k8smeta.ObjectMeta{
  1823  				Namespace: testNamespace,
  1824  				Name:      testWorkloadName,
  1825  				UID:       testWorkloadUID,
  1826  			},
  1827  			Spec: vzapi.VerrazzanoCoherenceWorkloadSpec{
  1828  				Template: runtime.RawExtension{
  1829  					Raw:    []byte(`{"metadata":{"name": "test-coherence"}}`),
  1830  					Object: &unstructured.Unstructured{},
  1831  				},
  1832  			},
  1833  		},
  1834  		&coherence,
  1835  		&oamcore.WorkloadDefinition{
  1836  			ObjectMeta: k8smeta.ObjectMeta{
  1837  				Name: "coherences.coherence.oracle.com",
  1838  			},
  1839  			Spec: oamcore.WorkloadDefinitionSpec{
  1840  				ChildResourceKinds: []oamcore.ChildResourceKind{
  1841  					{APIVersion: "apps/v1", Kind: "Deployment", Selector: nil},
  1842  					{APIVersion: "v1", Kind: "Service", Selector: nil},
  1843  				},
  1844  			},
  1845  		},
  1846  	).Build()
  1847  }
  1848  
  1849  func getBoolPtr(b bool) *bool {
  1850  	return &b
  1851  }