github.com/verrazzano/verrazzano@v1.7.0/application-operator/controllers/cohworkload/coherenceworkload_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 cohworkload
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"strings"
    10  	"testing"
    11  
    12  	"github.com/go-logr/logr"
    13  	"github.com/prometheus/client_golang/prometheus/testutil"
    14  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    15  	"github.com/verrazzano/verrazzano/pkg/log/vzlog"
    16  
    17  	oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1"
    18  	"github.com/crossplane/oam-kubernetes-runtime/apis/core"
    19  	oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
    20  	"github.com/crossplane/oam-kubernetes-runtime/pkg/oam"
    21  	"github.com/golang/mock/gomock"
    22  	asserts "github.com/stretchr/testify/assert"
    23  	vzapi "github.com/verrazzano/verrazzano/application-operator/apis/oam/v1alpha1"
    24  	"github.com/verrazzano/verrazzano/application-operator/controllers/logging"
    25  	"github.com/verrazzano/verrazzano/application-operator/controllers/metricstrait"
    26  	"github.com/verrazzano/verrazzano/application-operator/metricsexporter"
    27  	"github.com/verrazzano/verrazzano/application-operator/mocks"
    28  	vzstring "github.com/verrazzano/verrazzano/pkg/string"
    29  	"go.uber.org/zap"
    30  	istionet "istio.io/api/networking/v1alpha3"
    31  	istioclient "istio.io/client-go/pkg/apis/networking/v1alpha3"
    32  	v1 "k8s.io/api/apps/v1"
    33  	corev1 "k8s.io/api/core/v1"
    34  	k8serrors "k8s.io/apimachinery/pkg/api/errors"
    35  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    36  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    37  	"k8s.io/apimachinery/pkg/runtime"
    38  	k8sschema "k8s.io/apimachinery/pkg/runtime/schema"
    39  	"k8s.io/apimachinery/pkg/types"
    40  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    41  	ctrl "sigs.k8s.io/controller-runtime"
    42  	"sigs.k8s.io/controller-runtime/pkg/client"
    43  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    44  )
    45  
    46  const namespace = "unit-test-namespace"
    47  const coherenceAPIVersion = "coherence.oracle.com/v1"
    48  const coherenceKind = "Coherence"
    49  const testRestartVersion = "new-restart"
    50  const loggingTrait = `
    51  {
    52  	"apiVersion": "oam.verrazzano.io/v1alpha1",
    53  	"kind": "LoggingTrait",
    54  	"name": "my-logging-trait"
    55  }
    56  `
    57  
    58  var specJvmArgsFields = []string{specField, jvmField, argsField}
    59  
    60  // TestReconcilerSetupWithManager test the creation of the logging scope reconciler.
    61  // GIVEN a controller implementation
    62  // WHEN the controller is created
    63  // THEN verify no error is returned
    64  func TestReconcilerSetupWithManager(t *testing.T) {
    65  	assert := asserts.New(t)
    66  
    67  	var mocker *gomock.Controller
    68  	var mgr *mocks.MockManager
    69  	var cli *mocks.MockClient
    70  	var scheme *runtime.Scheme
    71  	var reconciler Reconciler
    72  	var err error
    73  
    74  	mocker = gomock.NewController(t)
    75  	mgr = mocks.NewMockManager(mocker)
    76  	cli = mocks.NewMockClient(mocker)
    77  	scheme = runtime.NewScheme()
    78  	_ = vzapi.AddToScheme(scheme)
    79  	metricsReconciler := &metricstrait.Reconciler{Client: cli, Scheme: scheme, Scraper: "verrazzano-system/vmi-system-prometheus-0"}
    80  	reconciler = Reconciler{Client: cli, Scheme: scheme, Metrics: metricsReconciler}
    81  	mgr.EXPECT().GetControllerOptions().AnyTimes()
    82  	mgr.EXPECT().GetScheme().Return(scheme)
    83  	mgr.EXPECT().GetLogger().Return(logr.Discard())
    84  	mgr.EXPECT().SetFields(gomock.Any()).Return(nil).AnyTimes()
    85  	mgr.EXPECT().Add(gomock.Any()).Return(nil).AnyTimes()
    86  	err = reconciler.SetupWithManager(mgr)
    87  	mocker.Finish()
    88  	assert.NoError(err)
    89  }
    90  
    91  // TestReconcileCreateCoherence tests the basic happy path of reconciling a VerrazzanoCoherenceWorkload. We
    92  // expect to write out a Coherence CR but we aren't adding logging or any other scopes or traits.
    93  // GIVEN a VerrazzanoCoherenceWorkload resource is created
    94  // WHEN the controller Reconcile function is called
    95  // THEN expect a Coherence CR to be written
    96  func TestReconcileCreateCoherence(t *testing.T) {
    97  	appConfigName := "unit-test-app-config"
    98  	componentName := "unit-test-component"
    99  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   100  
   101  	tests := []struct {
   102  		name                  string
   103  		coherenceJSON         string
   104  		expectedSvcLabelCount int
   105  	}{
   106  		{name: "Coherence with no ports", coherenceJSON: `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`, expectedSvcLabelCount: 0},
   107  
   108  		{name: "Coherence with ports, no services",
   109  			coherenceJSON:         `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3,"ports":[{"name": "http","port":"8080"}]}}`,
   110  			expectedSvcLabelCount: len(labels)},
   111  
   112  		{name: "Coherence with port services, no existing service labels",
   113  			coherenceJSON:         `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3,"ports":[{"name": "http","port":"8080", "service":{"enabled": true}}]}}`,
   114  			expectedSvcLabelCount: len(labels)},
   115  		{name: "Coherence with ports services, with existing labels",
   116  			coherenceJSON:         `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3,"ports":[{"name": "http","port":"8080", "service":{"enabled": true, "labels": {"l1": "val1", "l2": "val2"}}}]}}`,
   117  			expectedSvcLabelCount: len(labels) + 2},
   118  	}
   119  	for _, tt := range tests {
   120  		t.Run(tt.name, func(t *testing.T) {
   121  			assert := asserts.New(t)
   122  
   123  			var mocker = gomock.NewController(t)
   124  			var cli = mocks.NewMockClient(mocker)
   125  			mockStatus := mocks.NewMockStatusWriter(mocker)
   126  
   127  			// expect call to fetch existing coherence StatefulSet
   128  			cli.EXPECT().
   129  				Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   130  				DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   131  					return k8serrors.NewNotFound(k8sschema.GroupResource{}, "test")
   132  				})
   133  			// expect a call to fetch the VerrazzanoCoherenceWorkload
   134  			cli.EXPECT().
   135  				Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
   136  				DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   137  					// coherenceJSON := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
   138  					// coherenceJSON := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3,"ports":[{"name": "http","port":"8080"}]}}`
   139  					workload.Spec.Template = runtime.RawExtension{Raw: []byte(tt.coherenceJSON)}
   140  					workload.ObjectMeta.Labels = labels
   141  					workload.APIVersion = vzapi.SchemeGroupVersion.String()
   142  					workload.Kind = "VerrazzanoCoherenceWorkload"
   143  					workload.Namespace = namespace
   144  					workload.ObjectMeta.Generation = 2
   145  					workload.Status.LastGeneration = "1"
   146  					return nil
   147  				})
   148  			// expect a call to list the FLUENTD config maps
   149  			cli.EXPECT().
   150  				List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   151  				DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   152  					// return no resources
   153  					return nil
   154  				})
   155  			// no config maps found, so expect a call to create a config map with our parsing rules
   156  			cli.EXPECT().
   157  				Create(gomock.Any(), gomock.Any(), gomock.Any()).
   158  				DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   159  					assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   160  					return nil
   161  				})
   162  			cli.EXPECT().
   163  				Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   164  				DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   165  					component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   166  					appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   167  					return nil
   168  				})
   169  			// expect a call to get the application configuration for the workload
   170  			cli.EXPECT().
   171  				Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   172  				DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   173  					appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
   174  					return nil
   175  				})
   176  			// expect a call to attempt to get the Coherence CR - return not found
   177  			cli.EXPECT().
   178  				Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
   179  				DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
   180  					return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
   181  				})
   182  			// expect a call to create the Coherence CR
   183  			cli.EXPECT().
   184  				Create(gomock.Any(), gomock.Any(), gomock.Any()).
   185  				DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
   186  					assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   187  					assert.Equal(coherenceKind, u.GetKind())
   188  
   189  					// make sure the OAM component and app name labels were copied to the spec
   190  					specLabels, _, _ := unstructured.NestedStringMap(u.Object, specLabelsFields...)
   191  					assert.Equal(labels, specLabels)
   192  
   193  					// make sure the OAM component and app name labels were copied to the service section
   194  					// of every port in the spec
   195  					ports, _, _ := unstructured.NestedSlice(u.Object, specPortsField...)
   196  					for _, port := range ports {
   197  						p := port.(map[string]interface{})
   198  						svc := p[specPortSvcFieldName].(map[string]interface{})
   199  						assert.NotNil(svc, "ports in Coherence should contain service field")
   200  						svcLbls := svc["labels"].(map[string]interface{})
   201  						assert.NotNilf(svc, "service labels in Coherence object port %s should not be nil", p["name"])
   202  						assert.Equalf(tt.expectedSvcLabelCount, len(svcLbls), "expected %d svc labels in port but got %d", tt.expectedSvcLabelCount, len(svcLbls))
   203  						for expectedKey, expectedVal := range labels {
   204  							assert.Equalf(expectedVal, svcLbls[expectedKey], "expected service for port %s to contain label %s=%s", p["name"], expectedKey, expectedVal)
   205  						}
   206  					}
   207  
   208  					// make sure sidecar.istio.io/inject annotation was added
   209  					annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
   210  					assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
   211  					return nil
   212  				})
   213  			// expect a call to get the namespace for the Coherence resource
   214  			cli.EXPECT().
   215  				Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   216  				DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   217  					namespace.Name = "test-namespace"
   218  					return nil
   219  				})
   220  
   221  			// expect a call to status update
   222  			cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   223  			// expect a call to update the status upgrade version
   224  			mockStatus.EXPECT().
   225  				Update(gomock.Any(), gomock.Any(), gomock.Any()).
   226  				DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   227  					return nil
   228  				})
   229  
   230  			// create a request and reconcile it
   231  			request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   232  			reconciler := newReconciler(cli)
   233  			result, err := reconciler.Reconcile(context.TODO(), request)
   234  
   235  			mocker.Finish()
   236  			assert.NoError(err)
   237  			assert.Equal(false, result.Requeue)
   238  		})
   239  	}
   240  }
   241  
   242  // TestReconcileCreateCoherenceWithLogging tests the happy path of reconciling a VerrazzanoCoherenceWorkload with
   243  // an attached logging scope. We expect to write out a Coherence CR with the FLUENTD sidecar and
   244  // additional JVM args set.
   245  // GIVEN a VerrazzanoCoherenceWorkload resource is created with a logging scope
   246  // WHEN the controller Reconcile function is called
   247  // THEN expect a Coherence CR to be written
   248  func TestReconcileCreateCoherenceWithLogging(t *testing.T) {
   249  
   250  	assert := asserts.New(t)
   251  
   252  	var mocker = gomock.NewController(t)
   253  	var cli = mocks.NewMockClient(mocker)
   254  	mockStatus := mocks.NewMockStatusWriter(mocker)
   255  
   256  	appConfigName := "unit-test-app-config"
   257  	componentName := "unit-test-component"
   258  	fluentdImage := "unit-test-image:latest"
   259  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   260  	// set the Fluentd image which is obtained via env then reset at end of test
   261  	initialDefaultFluentdImage := logging.DefaultFluentdImage
   262  	logging.DefaultFluentdImage = fluentdImage
   263  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
   264  
   265  	// expect call to fetch existing coherence StatefulSet
   266  	cli.EXPECT().
   267  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   268  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   269  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "test")
   270  		})
   271  
   272  	// expect a call to fetch the VerrazzanoCoherenceWorkload
   273  	cli.EXPECT().
   274  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
   275  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   276  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
   277  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
   278  			workload.ObjectMeta.Labels = labels
   279  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
   280  			workload.Kind = "VerrazzanoCoherenceWorkload"
   281  			workload.Namespace = namespace
   282  			workload.ObjectMeta.Generation = 2
   283  			workload.Status.LastGeneration = "1"
   284  			return nil
   285  		})
   286  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
   287  	cli.EXPECT().
   288  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   289  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   290  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   291  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   292  			return nil
   293  		})
   294  	// expect a call to list the FLUENTD config maps
   295  	cli.EXPECT().
   296  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   297  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   298  			// return no resources
   299  			return nil
   300  		})
   301  	// no config maps found, so expect a call to create a config map with our parsing rules
   302  	cli.EXPECT().
   303  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   304  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   305  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   306  			return nil
   307  		})
   308  	// expect a call to get the application configuration for the workload
   309  	cli.EXPECT().
   310  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   311  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   312  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
   313  			return nil
   314  		})
   315  	// expect a call to attempt to get the Coherence CR - return not found
   316  	cli.EXPECT().
   317  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
   318  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
   319  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
   320  		})
   321  	// expect a call to create the Coherence CR
   322  	cli.EXPECT().
   323  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   324  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
   325  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   326  			assert.Equal(coherenceKind, u.GetKind())
   327  
   328  			// make sure JVM args were added
   329  			jvmArgs, _, _ := unstructured.NestedSlice(u.Object, specJvmArgsFields...)
   330  			assert.Equal(additionalJvmArgs, jvmArgs)
   331  
   332  			// make sure side car was added
   333  			sideCars, _, _ := unstructured.NestedSlice(u.Object, specField, "sideCars")
   334  			assert.Equal(1, len(sideCars))
   335  			// assert correct Fluentd image
   336  			assert.Equal(fluentdImage, sideCars[0].(map[string]interface{})["image"])
   337  
   338  			// make sure sidecar.istio.io/inject annotation was added
   339  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
   340  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
   341  			return nil
   342  		})
   343  	// expect a call to get the namespace for the Coherence resource
   344  	cli.EXPECT().
   345  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   346  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   347  			return nil
   348  		})
   349  
   350  	// expect a call to status update
   351  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   352  	// expect a call to update the status upgrade version
   353  	mockStatus.EXPECT().
   354  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   355  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   356  			return nil
   357  		})
   358  
   359  	// create a request and reconcile it
   360  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   361  	reconciler := newReconciler(cli)
   362  	result, err := reconciler.Reconcile(context.TODO(), request)
   363  
   364  	mocker.Finish()
   365  	assert.NoError(err)
   366  	assert.Equal(false, result.Requeue)
   367  }
   368  
   369  // TestReconcileCreateCoherenceWithLogging tests the happy path of reconciling a VerrazzanoCoherenceWorkload with
   370  // an attached logging scope. We expect to write out a Coherence CR with the FLUENTD sidecar and
   371  // additional JVM args set.
   372  // GIVEN a VerrazzanoCoherenceWorkload resource is created with a logging scope
   373  // WHEN the controller Reconcile function is called
   374  // THEN expect a Coherence CR to be written
   375  func TestReconcileCreateCoherenceWithCustomLogging(t *testing.T) {
   376  
   377  	assert := asserts.New(t)
   378  
   379  	var mocker = gomock.NewController(t)
   380  	var cli = mocks.NewMockClient(mocker)
   381  	mockStatus := mocks.NewMockStatusWriter(mocker)
   382  
   383  	appConfigName := "unit-test-app-config"
   384  	componentName := "unit-test-component"
   385  	fluentdImage := "unit-test-image:latest"
   386  	workloadName := "unit-test-verrazzano-coherence-workload"
   387  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   388  	// set the Fluentd image which is obtained via env then reset at end of test
   389  	initialDefaultFluentdImage := logging.DefaultFluentdImage
   390  	logging.DefaultFluentdImage = fluentdImage
   391  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
   392  
   393  	// expect call to fetch existing coherence StatefulSet
   394  	cli.EXPECT().
   395  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   396  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   397  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "test")
   398  		})
   399  
   400  	// expect a call to fetch the VerrazzanoCoherenceWorkload
   401  	cli.EXPECT().
   402  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: workloadName}, gomock.Not(gomock.Nil()), gomock.Any()).
   403  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   404  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
   405  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
   406  			workload.ObjectMeta.Labels = labels
   407  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
   408  			workload.Kind = "VerrazzanoCoherenceWorkload"
   409  			workload.Namespace = namespace
   410  			workload.Name = workloadName
   411  			workload.ObjectMeta.Generation = 2
   412  			workload.Status.LastGeneration = "1"
   413  			workload.OwnerReferences = []metav1.OwnerReference{
   414  				{
   415  					UID: types.UID(namespace),
   416  				},
   417  			}
   418  			return nil
   419  		})
   420  	// expect a call to list the logging traits
   421  	cli.EXPECT().
   422  		List(gomock.Any(), &vzapi.LoggingTraitList{TypeMeta: metav1.TypeMeta{Kind: "LoggingTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}}, gomock.Not(gomock.Nil())).
   423  		DoAndReturn(func(ctx context.Context, loggingTraitList *vzapi.LoggingTraitList, inNamespace client.InNamespace) error {
   424  			loggingTraitList.Items = []vzapi.LoggingTrait{
   425  				{
   426  					ObjectMeta: metav1.ObjectMeta{
   427  						OwnerReferences: []metav1.OwnerReference{
   428  							{
   429  								UID: types.UID(namespace),
   430  							},
   431  						},
   432  					},
   433  					Spec: vzapi.LoggingTraitSpec{
   434  						WorkloadReference: oamrt.TypedReference{
   435  							Name: workloadName,
   436  						},
   437  					},
   438  				},
   439  			}
   440  			return nil
   441  		})
   442  	// expect a call to get the application configuration for the workload
   443  	cli.EXPECT().
   444  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   445  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   446  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{
   447  				{
   448  					ComponentName: componentName,
   449  					Traits: []oamcore.ComponentTrait{
   450  						{
   451  							Trait: runtime.RawExtension{
   452  								Raw: []byte(strings.ReplaceAll(strings.ReplaceAll(loggingTrait, " ", ""), "\n", "")),
   453  							},
   454  						},
   455  					},
   456  				},
   457  			}
   458  			return nil
   459  		})
   460  	// expect a call to get the ConfigMap for logging - return not found
   461  	cli.EXPECT().
   462  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: loggingNamePart + "-unit-test-cluster-coherence"}), gomock.Not(gomock.Nil()), gomock.Any()).
   463  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, configMap *corev1.ConfigMap, opt ...client.GetOption) error {
   464  			return k8serrors.NewNotFound(k8sschema.GroupResource{
   465  				Group:    "",
   466  				Resource: "ConfigMap",
   467  			},
   468  				"logging-stdout-unit-test-cluster-coherence")
   469  		})
   470  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
   471  	cli.EXPECT().
   472  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   473  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   474  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   475  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   476  			return nil
   477  		})
   478  	// expect a call to list the FLUENTD config maps
   479  	cli.EXPECT().
   480  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   481  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   482  			// return no resources
   483  			return nil
   484  		})
   485  	// Define expected ConfigMap
   486  	data := make(map[string]string)
   487  	data["custom.conf"] = ""
   488  	customLoggingConfigMap := &corev1.ConfigMap{
   489  		TypeMeta: metav1.TypeMeta{
   490  			Kind:       "",
   491  			APIVersion: "",
   492  		},
   493  		ObjectMeta: metav1.ObjectMeta{
   494  			Name:              loggingNamePart + "-unit-test-cluster-coherence",
   495  			Namespace:         namespace,
   496  			CreationTimestamp: metav1.Time{},
   497  			OwnerReferences: []metav1.OwnerReference{
   498  				{
   499  					APIVersion:         "oam.verrazzano.io/v1alpha1",
   500  					Kind:               "VerrazzanoCoherenceWorkload",
   501  					Name:               "unit-test-verrazzano-coherence-workload",
   502  					UID:                "",
   503  					Controller:         newTrue(),
   504  					BlockOwnerDeletion: newTrue(),
   505  				},
   506  			},
   507  		},
   508  		Data: data,
   509  	}
   510  	// expect a call to create the custom logging config map
   511  	cli.EXPECT().
   512  		Create(gomock.Any(), customLoggingConfigMap, gomock.Any()).
   513  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   514  			return nil
   515  		})
   516  	// no config maps found, so expect a call to create a config map with our parsing rules
   517  	cli.EXPECT().
   518  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   519  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   520  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   521  			return nil
   522  		})
   523  	// expect a call to attempt to get the Coherence CR - return not found
   524  	cli.EXPECT().
   525  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
   526  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
   527  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
   528  		})
   529  	// expect a call to create the Coherence CR
   530  	cli.EXPECT().
   531  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   532  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
   533  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   534  			assert.Equal(coherenceKind, u.GetKind())
   535  
   536  			// make sure the OAM component and app name labels were copied
   537  			specLabels, _, _ := unstructured.NestedStringMap(u.Object, specLabelsFields...)
   538  			assert.Equal(labels, specLabels)
   539  
   540  			// make sure sidecar.istio.io/inject annotation was added
   541  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
   542  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
   543  			return nil
   544  		})
   545  	// expect a call to get the namespace for the Coherence resource
   546  	cli.EXPECT().
   547  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   548  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   549  			return nil
   550  		})
   551  
   552  	// expect a call to status update
   553  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   554  	// expect a call to update the status upgrade version
   555  	mockStatus.EXPECT().
   556  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   557  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   558  			return nil
   559  		})
   560  
   561  	// create a request and reconcile it
   562  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   563  	reconciler := newReconciler(cli)
   564  	result, err := reconciler.Reconcile(context.TODO(), request)
   565  
   566  	mocker.Finish()
   567  	assert.NoError(err)
   568  	assert.Equal(false, result.Requeue)
   569  }
   570  
   571  // TestReconcileCreateCoherenceWithLogging tests the happy path of reconciling a VerrazzanoCoherenceWorkload with
   572  // an attached logging scope. We expect to write out a Coherence CR with the FLUENTD sidecar and
   573  // additional JVM args set.
   574  // GIVEN a VerrazzanoCoherenceWorkload resource is created with a logging scope
   575  // WHEN the controller Reconcile function is called
   576  // THEN expect a Coherence CR to be written
   577  func TestReconcileCreateCoherenceWithCustomLoggingConfigMapExists(t *testing.T) {
   578  
   579  	assert := asserts.New(t)
   580  
   581  	var mocker = gomock.NewController(t)
   582  	var cli = mocks.NewMockClient(mocker)
   583  	mockStatus := mocks.NewMockStatusWriter(mocker)
   584  
   585  	appConfigName := "unit-test-app-config"
   586  	componentName := "unit-test-component"
   587  	fluentdImage := "unit-test-image:latest"
   588  	workloadName := "unit-test-verrazzano-coherence-workload"
   589  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   590  	// set the Fluentd image which is obtained via env then reset at end of test
   591  	initialDefaultFluentdImage := logging.DefaultFluentdImage
   592  	logging.DefaultFluentdImage = fluentdImage
   593  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
   594  
   595  	// expect call to fetch existing coherence StatefulSet
   596  	cli.EXPECT().
   597  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   598  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   599  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "test")
   600  		})
   601  
   602  	// expect a call to fetch the VerrazzanoCoherenceWorkload
   603  	cli.EXPECT().
   604  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: workloadName}, gomock.Not(gomock.Nil()), gomock.Any()).
   605  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   606  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
   607  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
   608  			workload.ObjectMeta.Labels = labels
   609  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
   610  			workload.Kind = "VerrazzanoCoherenceWorkload"
   611  			workload.Namespace = namespace
   612  			workload.Name = workloadName
   613  			workload.ObjectMeta.Generation = 2
   614  			workload.Status.LastGeneration = "1"
   615  			workload.OwnerReferences = []metav1.OwnerReference{
   616  				{
   617  					UID: types.UID(namespace),
   618  				},
   619  			}
   620  			return nil
   621  		})
   622  	// expect a call to list the logging traits
   623  	cli.EXPECT().
   624  		List(gomock.Any(), &vzapi.LoggingTraitList{TypeMeta: metav1.TypeMeta{Kind: "LoggingTrait", APIVersion: "oam.verrazzano.io/v1alpha1"}}, gomock.Not(gomock.Nil())).
   625  		DoAndReturn(func(ctx context.Context, loggingTraitList *vzapi.LoggingTraitList, inNamespace client.InNamespace) error {
   626  			loggingTraitList.Items = []vzapi.LoggingTrait{
   627  				{
   628  					ObjectMeta: metav1.ObjectMeta{
   629  						OwnerReferences: []metav1.OwnerReference{
   630  							{
   631  								UID: types.UID(namespace),
   632  							},
   633  						},
   634  					},
   635  					Spec: vzapi.LoggingTraitSpec{
   636  						WorkloadReference: oamrt.TypedReference{
   637  							Name: workloadName,
   638  						},
   639  					},
   640  				},
   641  			}
   642  			return nil
   643  		})
   644  	// expect a call to get the application configuration for the workload
   645  	cli.EXPECT().
   646  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   647  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   648  
   649  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{
   650  				{
   651  					ComponentName: componentName,
   652  					Traits: []oamcore.ComponentTrait{
   653  						{
   654  							Trait: runtime.RawExtension{
   655  								Raw: []byte(strings.ReplaceAll(strings.ReplaceAll(loggingTrait, " ", ""), "\n", "")),
   656  							},
   657  						},
   658  					},
   659  				},
   660  			}
   661  			return nil
   662  		})
   663  	// expect a call to get the ConfigMap for logging - return not found
   664  	cli.EXPECT().
   665  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: loggingNamePart + "-unit-test-cluster-coherence"}), gomock.Not(gomock.Nil()), gomock.Any()).
   666  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, configMap *corev1.ConfigMap, opt ...client.GetOption) error {
   667  			return nil
   668  		})
   669  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
   670  	cli.EXPECT().
   671  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   672  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   673  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   674  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   675  			return nil
   676  		})
   677  	// expect a call to list the FLUENTD config maps
   678  	cli.EXPECT().
   679  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   680  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   681  			// return no resources
   682  			return nil
   683  		})
   684  	// no config maps found, so expect a call to create a config map with our parsing rules
   685  	cli.EXPECT().
   686  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   687  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   688  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   689  			return nil
   690  		})
   691  	// expect a call to attempt to get the Coherence CR - return not found
   692  	cli.EXPECT().
   693  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
   694  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
   695  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
   696  		})
   697  	// expect a call to create the Coherence CR
   698  	cli.EXPECT().
   699  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   700  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
   701  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   702  			assert.Equal(coherenceKind, u.GetKind())
   703  
   704  			// make sure the OAM component and app name labels were copied
   705  			specLabels, _, _ := unstructured.NestedStringMap(u.Object, specLabelsFields...)
   706  			assert.Equal(labels, specLabels)
   707  
   708  			// make sure sidecar.istio.io/inject annotation was added
   709  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
   710  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
   711  			return nil
   712  		})
   713  	// expect a call to get the namespace for the Coherence resource
   714  	cli.EXPECT().
   715  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   716  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   717  			return nil
   718  		})
   719  
   720  	// expect a call to status update
   721  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   722  	// expect a call to update the status upgrade version
   723  	mockStatus.EXPECT().
   724  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   725  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   726  			return nil
   727  		})
   728  
   729  	// create a request and reconcile it
   730  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   731  	reconciler := newReconciler(cli)
   732  	result, err := reconciler.Reconcile(context.TODO(), request)
   733  
   734  	mocker.Finish()
   735  	assert.NoError(err)
   736  	assert.Equal(false, result.Requeue)
   737  }
   738  
   739  // TestReconcileUpdateFluentdImage tests reconciling a VerrazzanoCoherenceWorkload when the Fluentd image
   740  // in the Coherence sidecar is old and a new image is available. This should result in the latest Fluentd
   741  // image being pulled from the env and replaced in the sidecar
   742  // GIVEN a VerrazzanoCoherenceWorkload resource that is using an old Fluentd image
   743  // WHEN the controller Reconcile function is called
   744  // THEN the Fluentd image should be replaced in the Fluentd sidecar
   745  func TestReconcileUpdateFluentdImage(t *testing.T) {
   746  
   747  	assert := asserts.New(t)
   748  
   749  	var mocker = gomock.NewController(t)
   750  	var cli = mocks.NewMockClient(mocker)
   751  	mockStatus := mocks.NewMockStatusWriter(mocker)
   752  
   753  	appConfigName := "unit-test-app-config"
   754  	componentName := "unit-test-component"
   755  	fluentdImage := "unit-test-image:latest"
   756  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   757  
   758  	// set the Fluentd image which is obtained via env then reset at end of test
   759  	initialDefaultFluentdImage := logging.DefaultFluentdImage
   760  	logging.DefaultFluentdImage = fluentdImage
   761  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
   762  
   763  	// expect call to fetch existing coherence StatefulSet
   764  	cli.EXPECT().
   765  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   766  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   767  			// return nil error because Coherence StatefulSet exists
   768  			return nil
   769  		})
   770  	// expect a call to fetch the VerrazzanoCoherenceWorkload
   771  	cli.EXPECT().
   772  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
   773  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   774  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
   775  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
   776  			workload.ObjectMeta.Labels = labels
   777  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
   778  			workload.Kind = "VerrazzanoCoherenceWorkload"
   779  			workload.Namespace = namespace
   780  			workload.ObjectMeta.Generation = 2
   781  			workload.Status.LastGeneration = "1"
   782  			return nil
   783  		})
   784  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
   785  	cli.EXPECT().
   786  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   787  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   788  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   789  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   790  			return nil
   791  		})
   792  	// expect a call to list the FLUENTD config maps
   793  	cli.EXPECT().
   794  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   795  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   796  			// return no resources
   797  			return nil
   798  		})
   799  	// no config maps found, so expect a call to create a config map with our parsing rules
   800  	cli.EXPECT().
   801  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   802  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   803  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   804  			return nil
   805  		})
   806  	// expect a call to attempt to get the Coherence CR
   807  	cli.EXPECT().
   808  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   809  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, u *unstructured.Unstructured, opt ...client.GetOption) error {
   810  			// set the old Fluentd image on the returned obj
   811  			containers, _, _ := unstructured.NestedSlice(u.Object, "spec", "sideCars")
   812  			unstructured.SetNestedField(containers[0].(map[string]interface{}), "unit-test-image:existing", "image")
   813  			unstructured.SetNestedSlice(u.Object, containers, "spec", "sideCars")
   814  			// return nil error because Coherence StatefulSet exists
   815  			return nil
   816  		})
   817  	// expect a call to update the Coherence CR
   818  	cli.EXPECT().
   819  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   820  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.UpdateOption) error {
   821  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   822  			assert.Equal(coherenceKind, u.GetKind())
   823  
   824  			// make sure JVM args were added
   825  			jvmArgs, _, _ := unstructured.NestedSlice(u.Object, specJvmArgsFields...)
   826  			assert.Equal(additionalJvmArgs, jvmArgs)
   827  
   828  			// make sure side car was added
   829  			sideCars, _, _ := unstructured.NestedSlice(u.Object, specField, "sideCars")
   830  			assert.Equal(1, len(sideCars))
   831  			// assert correct Fluentd image
   832  			assert.Equal(fluentdImage, sideCars[0].(map[string]interface{})["image"])
   833  
   834  			// make sure sidecar.istio.io/inject annotation was added
   835  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
   836  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
   837  			return nil
   838  		})
   839  	// expect a call to get the application configuration for the workload
   840  	cli.EXPECT().
   841  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   842  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   843  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
   844  			return nil
   845  		})
   846  	// expect a call to get the namespace for the Coherence resource
   847  	cli.EXPECT().
   848  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   849  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   850  			return nil
   851  		})
   852  
   853  	// expect a call to status update
   854  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   855  	mockStatus.EXPECT().
   856  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   857  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   858  			return nil
   859  		})
   860  
   861  	// create a request and reconcile it
   862  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   863  	reconciler := newReconciler(cli)
   864  	result, err := reconciler.Reconcile(context.TODO(), request)
   865  
   866  	mocker.Finish()
   867  	assert.NoError(err)
   868  	assert.Equal(false, result.Requeue)
   869  }
   870  
   871  // TestReconcileUpdateCR tests reconciling a VerrazzanoCoherenceWorkload when the Coherence
   872  // CR already exists. We expect the CR to be updated.
   873  // GIVEN a VerrazzanoCoherenceWorkload resource is updated
   874  // WHEN the controller Reconcile function is called and the Coherence CR already exists
   875  // THEN the Coherence CR is updated
   876  func TestReconcileUpdateCR(t *testing.T) {
   877  
   878  	assert := asserts.New(t)
   879  
   880  	var mocker = gomock.NewController(t)
   881  	var cli = mocks.NewMockClient(mocker)
   882  	mockStatus := mocks.NewMockStatusWriter(mocker)
   883  
   884  	appConfigName := "unit-test-app-config"
   885  	componentName := "unit-test-component"
   886  	existingFluentdImage := "unit-test-image:existing"
   887  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
   888  	containers := []corev1.Container{{Name: logging.FluentdStdoutSidecarName, Image: existingFluentdImage}}
   889  
   890  	// simulate the "replicas" field changing to 3
   891  	replicasFromWorkload := int64(3)
   892  
   893  	// expect call to fetch existing coherence StatefulSet
   894  	cli.EXPECT().
   895  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
   896  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
   897  			coherence.Spec.Template.Spec.Containers = containers
   898  			// return nil error because Coherence StatefulSet exists
   899  			return nil
   900  		})
   901  	// expect a call to fetch the OAM application configuration
   902  	cli.EXPECT().
   903  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   904  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   905  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
   906  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
   907  			return nil
   908  		})
   909  	// expect a call to fetch the VerrazzanoCoherenceWorkload
   910  	cli.EXPECT().
   911  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
   912  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
   913  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":` + fmt.Sprint(replicasFromWorkload) + `}}`
   914  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
   915  			workload.ObjectMeta.Labels = labels
   916  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
   917  			workload.Kind = "VerrazzanoCoherenceWorkload"
   918  			workload.Namespace = namespace
   919  			workload.ObjectMeta.Generation = 2
   920  			workload.Status.LastGeneration = "1"
   921  			return nil
   922  		})
   923  	// expect a call to list the FLUENTD config maps
   924  	cli.EXPECT().
   925  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
   926  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
   927  			// return no resources
   928  			return nil
   929  		})
   930  	// no config maps found, so expect a call to create a config map with our parsing rules
   931  	cli.EXPECT().
   932  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
   933  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
   934  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
   935  			return nil
   936  		})
   937  	// expect a call to get the application configuration for the workload
   938  	cli.EXPECT().
   939  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
   940  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
   941  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
   942  			return nil
   943  		})
   944  	// expect a call to attempt to get the Coherence CR and return an existing resource
   945  	cli.EXPECT().
   946  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
   947  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
   948  			// note this resource has a "replicas" field currently set to 1
   949  			spec := map[string]interface{}{"replicas": int64(1)}
   950  			return unstructured.SetNestedField(u.Object, spec, specField)
   951  		})
   952  	// expect a call to update the Coherence CR
   953  	cli.EXPECT().
   954  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   955  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.UpdateOption) error {
   956  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
   957  			assert.Equal(coherenceKind, u.GetKind())
   958  
   959  			// make sure the replicas field is set to the correct value (from our workload spec)
   960  			spec, _, _ := unstructured.NestedMap(u.Object, specField)
   961  			assert.Equal(replicasFromWorkload, spec["replicas"])
   962  			return nil
   963  		})
   964  	// expect a call to get the namespace for the Coherence resource
   965  	cli.EXPECT().
   966  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
   967  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
   968  			return nil
   969  		})
   970  
   971  	// expect a call to status update
   972  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
   973  	// expect a call to update the status upgrade version
   974  	mockStatus.EXPECT().
   975  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
   976  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
   977  			return nil
   978  		})
   979  
   980  	// create a request and reconcile it
   981  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
   982  	reconciler := newReconciler(cli)
   983  	result, err := reconciler.Reconcile(context.TODO(), request)
   984  
   985  	mocker.Finish()
   986  	assert.NoError(err)
   987  	assert.Equal(false, result.Requeue)
   988  }
   989  
   990  // TestReconcileWithLoggingWithJvmArgs tests the happy path of reconciling a VerrazzanoCoherenceWorkload with
   991  // an attached logging scope and the Coherence spec as existing JVM args. We expect to write out a Coherence CR
   992  // with the FLUENTD sidecar and a JVM args list that has the user-specified args and the args we add
   993  // for logging.
   994  // GIVEN a VerrazzanoCoherenceWorkload resource is created with a logging scope and JVM args
   995  // WHEN the controller Reconcile function is called
   996  // THEN expect a Coherence CR to be written with the combined JVM args
   997  func TestReconcileWithLoggingWithJvmArgs(t *testing.T) {
   998  	assert := asserts.New(t)
   999  	var mocker = gomock.NewController(t)
  1000  	var cli = mocks.NewMockClient(mocker)
  1001  	mockStatus := mocks.NewMockStatusWriter(mocker)
  1002  
  1003  	appConfigName := "unit-test-app-config"
  1004  	componentName := "unit-test-component"
  1005  	fluentdImage := "unit-test-image:latest"
  1006  	existingJvmArg := "-Dcoherence.test=unit-test"
  1007  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
  1008  
  1009  	// set the Fluentd image which is obtained via env then reset at end of test
  1010  	initialDefaultFluentdImage := logging.DefaultFluentdImage
  1011  	logging.DefaultFluentdImage = fluentdImage
  1012  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
  1013  
  1014  	// expect call to fetch existing coherence StatefulSet
  1015  	cli.EXPECT().
  1016  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1017  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
  1018  			// return nil error because Coherence StatefulSet exists
  1019  			return nil
  1020  		})
  1021  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
  1022  	cli.EXPECT().
  1023  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1024  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1025  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
  1026  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
  1027  			return nil
  1028  		})
  1029  	// expect a call to fetch the VerrazzanoCoherenceWorkload
  1030  	cli.EXPECT().
  1031  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1032  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
  1033  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"jvm":{"args": ["` + existingJvmArg + `"]}}}`
  1034  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
  1035  			workload.ObjectMeta.Labels = labels
  1036  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
  1037  			workload.Kind = "VerrazzanoCoherenceWorkload"
  1038  			workload.Namespace = namespace
  1039  			workload.ObjectMeta.Generation = 2
  1040  			workload.Status.LastGeneration = "1"
  1041  			return nil
  1042  		})
  1043  	// expect a call to list the FLUENTD config maps
  1044  	cli.EXPECT().
  1045  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
  1046  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
  1047  			// return no resources
  1048  			return nil
  1049  		})
  1050  	// no config maps found, so expect a call to create a config map with our parsing rules
  1051  	cli.EXPECT().
  1052  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1053  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
  1054  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
  1055  			return nil
  1056  		})
  1057  	// expect a call to get the application configuration for the workload
  1058  	cli.EXPECT().
  1059  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1060  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1061  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
  1062  			return nil
  1063  		})
  1064  	// expect a call to attempt to get the Coherence CR - return not found
  1065  	cli.EXPECT().
  1066  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
  1067  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
  1068  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
  1069  		})
  1070  	// expect a call to create the Coherence CR
  1071  	cli.EXPECT().
  1072  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1073  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
  1074  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
  1075  			assert.Equal(coherenceKind, u.GetKind())
  1076  
  1077  			// make sure JVM args were added and that the existing arg is still present
  1078  			jvmArgs, _, _ := unstructured.NestedStringSlice(u.Object, specJvmArgsFields...)
  1079  			assert.Equal(len(additionalJvmArgs)+1, len(jvmArgs))
  1080  			assert.True(vzstring.SliceContainsString(jvmArgs, existingJvmArg))
  1081  
  1082  			// make sure side car was added
  1083  			sideCars, _, _ := unstructured.NestedSlice(u.Object, specField, "sideCars")
  1084  			assert.Equal(1, len(sideCars))
  1085  
  1086  			// make sure sidecar.istio.io/inject annotation was added
  1087  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
  1088  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false"})
  1089  			return nil
  1090  		})
  1091  	// expect a call to get the namespace for the Coherence resource
  1092  	cli.EXPECT().
  1093  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
  1094  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
  1095  			return nil
  1096  		})
  1097  
  1098  	// expect a call to status update
  1099  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
  1100  	// expect a call to update the status upgrade version
  1101  	mockStatus.EXPECT().
  1102  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
  1103  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
  1104  			return nil
  1105  		})
  1106  
  1107  	// create a request and reconcile it
  1108  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
  1109  	reconciler := newReconciler(cli)
  1110  	result, err := reconciler.Reconcile(context.TODO(), request)
  1111  
  1112  	mocker.Finish()
  1113  	assert.NoError(err)
  1114  	assert.Equal(false, result.Requeue)
  1115  }
  1116  
  1117  // TestReconcileErrorOnCreate tests reconciling a VerrazzanoCoherenceWorkload and an
  1118  // error occurs attempting to create the Coherence CR.
  1119  // GIVEN a VerrazzanoCoherenceWorkload resource is created
  1120  // WHEN the controller Reconcile function is called and there is an error creating the Coherence CR
  1121  // THEN expect an error to be returned
  1122  func TestReconcileErrorOnCreate(t *testing.T) {
  1123  	assert := asserts.New(t)
  1124  	var mocker = gomock.NewController(t)
  1125  	var cli = mocks.NewMockClient(mocker)
  1126  
  1127  	appConfigName := "unit-test-app-config"
  1128  	componentName := "unit-test-component"
  1129  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
  1130  
  1131  	// expect call to fetch existing coherence StatefulSet
  1132  	cli.EXPECT().
  1133  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1134  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
  1135  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "test")
  1136  		})
  1137  	// expect a call to fetch the OAM application configuration
  1138  	cli.EXPECT().
  1139  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1140  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1141  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
  1142  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
  1143  			return nil
  1144  		})
  1145  	// expect a call to fetch the VerrazzanoCoherenceWorkload
  1146  	cli.EXPECT().
  1147  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1148  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
  1149  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
  1150  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
  1151  			workload.ObjectMeta.Labels = labels
  1152  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
  1153  			workload.Kind = "VerrazzanoCoherenceWorkload"
  1154  			workload.Namespace = namespace
  1155  			workload.ObjectMeta.Generation = 2
  1156  			workload.Status.LastGeneration = "1"
  1157  			return nil
  1158  		})
  1159  	// expect a call to list the FLUENTD config maps
  1160  	cli.EXPECT().
  1161  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
  1162  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
  1163  			// return no resources
  1164  			return nil
  1165  		})
  1166  	// no config maps found, so expect a call to create a config map with our parsing rules
  1167  	cli.EXPECT().
  1168  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1169  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
  1170  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
  1171  			return nil
  1172  		})
  1173  	// expect a call to get the application configuration for the workload
  1174  	cli.EXPECT().
  1175  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1176  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1177  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
  1178  			return nil
  1179  		})
  1180  	// expect a call to attempt to get the Coherence CR - return not found
  1181  	cli.EXPECT().
  1182  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: "unit-test-cluster"}), gomock.Not(gomock.Nil()), gomock.Any()).
  1183  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, u *unstructured.Unstructured, opt ...client.GetOption) error {
  1184  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
  1185  		})
  1186  	// expect a call to create the Coherence CR and return an error
  1187  	cli.EXPECT().
  1188  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1189  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.CreateOption) error {
  1190  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
  1191  			assert.Equal(coherenceKind, u.GetKind())
  1192  			return k8serrors.NewBadRequest("An error has occurred")
  1193  		})
  1194  
  1195  	// create a request and reconcile it
  1196  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
  1197  	reconciler := newReconciler(cli)
  1198  	result, err := reconciler.Reconcile(context.TODO(), request)
  1199  
  1200  	mocker.Finish()
  1201  	assert.Nil(err)
  1202  	assert.True(result.Requeue)
  1203  }
  1204  
  1205  // TestReconcileWorkloadNotFound tests reconciling a VerrazzanoCoherenceWorkload when the workload
  1206  // cannot be fetched. This happens when the workload has been deleted by the OAM runtime.
  1207  // GIVEN a VerrazzanoCoherenceWorkload resource has been deleted
  1208  // WHEN the controller Reconcile function is called and we attempt to fetch the workload
  1209  // THEN return success from the controller as there is nothing more to do
  1210  func TestReconcileWorkloadNotFound(t *testing.T) {
  1211  	assert := asserts.New(t)
  1212  
  1213  	var mocker = gomock.NewController(t)
  1214  	var cli = mocks.NewMockClient(mocker)
  1215  
  1216  	// expect a call to fetch the VerrazzanoCoherenceWorkload
  1217  	cli.EXPECT().
  1218  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1219  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
  1220  			return k8serrors.NewNotFound(k8sschema.GroupResource{}, "")
  1221  		})
  1222  
  1223  	// create a request and reconcile it
  1224  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
  1225  	reconciler := newReconciler(cli)
  1226  	result, err := reconciler.Reconcile(context.TODO(), request)
  1227  
  1228  	mocker.Finish()
  1229  	assert.NoError(err)
  1230  	assert.Equal(false, result.Requeue)
  1231  }
  1232  
  1233  // TestReconcileFetchWorkloadError tests reconciling a VerrazzanoCoherenceWorkload when the workload
  1234  // cannot be fetched due to an unexpected error.
  1235  // GIVEN a VerrazzanoCoherenceWorkload resource has been created
  1236  // WHEN the controller Reconcile function is called and we attempt to fetch the workload and get an error
  1237  // THEN return the error
  1238  func TestReconcileFetchWorkloadError(t *testing.T) {
  1239  	assert := asserts.New(t)
  1240  
  1241  	var mocker = gomock.NewController(t)
  1242  	var cli = mocks.NewMockClient(mocker)
  1243  
  1244  	// expect a call to fetch the VerrazzanoCoherenceWorkload
  1245  	cli.EXPECT().
  1246  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1247  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
  1248  			return k8serrors.NewBadRequest("An error has occurred")
  1249  		})
  1250  
  1251  	// create a request and reconcile it
  1252  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
  1253  	reconciler := newReconciler(cli)
  1254  	result, err := reconciler.Reconcile(context.TODO(), request)
  1255  
  1256  	mocker.Finish()
  1257  	assert.Nil(err)
  1258  	assert.True(result.Requeue)
  1259  }
  1260  
  1261  // TestCreateUpdateDestinationRuleCreate tests creation of a destination rule
  1262  // GIVEN the destination rule does not exist
  1263  // WHEN the controller createOrUpdateDestinationRule function is called
  1264  // THEN expect no error to be returned and destination rule is created
  1265  func TestCreateUpdateDestinationRuleCreate(t *testing.T) {
  1266  	assert := asserts.New(t)
  1267  
  1268  	var mocker = gomock.NewController(t)
  1269  	var cli = mocks.NewMockClient(mocker)
  1270  
  1271  	// Expect a call to get a destination rule and return that it is not found.
  1272  	cli.EXPECT().
  1273  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1274  		Return(k8serrors.NewNotFound(k8sschema.GroupResource{Group: "test-space", Resource: "DestinationRule"}, "test-space-myapp-dr"))
  1275  
  1276  	// Expect a call to get the appconfig resource to set the owner reference
  1277  	cli.EXPECT().
  1278  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1279  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, app *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1280  			app.TypeMeta = metav1.TypeMeta{
  1281  				APIVersion: "core.oam.dev/v1alpha2",
  1282  				Kind:       "ApplicationConfiguration",
  1283  			}
  1284  			return nil
  1285  		})
  1286  
  1287  	// Expect a call to create the destinationrule and return success
  1288  	cli.EXPECT().
  1289  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1290  		DoAndReturn(func(ctx context.Context, dr *istioclient.DestinationRule, opts ...client.CreateOption) error {
  1291  			assert.Equal(destinationRuleKind, dr.Kind)
  1292  			assert.Equal(destinationRuleAPIVersion, dr.APIVersion)
  1293  			assert.Equal("*.test-namespace.svc.cluster.local", dr.Spec.Host)
  1294  			assert.Equal(istionet.ClientTLSSettings_ISTIO_MUTUAL, dr.Spec.TrafficPolicy.Tls.Mode)
  1295  			assert.Equal(uint32(coherenceExtendPort), dr.Spec.TrafficPolicy.PortLevelSettings[0].Port.Number)
  1296  			assert.Equal(istionet.ClientTLSSettings_DISABLE, dr.Spec.TrafficPolicy.PortLevelSettings[0].Tls.Mode)
  1297  			assert.Equal(1, len(dr.OwnerReferences))
  1298  			assert.Equal("ApplicationConfiguration", dr.OwnerReferences[0].Kind)
  1299  			assert.Equal("core.oam.dev/v1alpha2", dr.OwnerReferences[0].APIVersion)
  1300  			return nil
  1301  		})
  1302  
  1303  	scheme := runtime.NewScheme()
  1304  	istioclient.AddToScheme(scheme)
  1305  	core.AddToScheme(scheme)
  1306  	vzapi.AddToScheme(scheme)
  1307  	reconciler := Reconciler{Client: cli, Scheme: scheme}
  1308  
  1309  	namespaceLabels := make(map[string]string)
  1310  	namespaceLabels["istio-injection"] = "enabled"
  1311  	workloadLabels := make(map[string]string)
  1312  	workloadLabels["app.oam.dev/name"] = "test-app"
  1313  	err := reconciler.createOrUpdateDestinationRule(context.Background(), vzlog.DefaultLogger(), "test-namespace", namespaceLabels, workloadLabels)
  1314  	mocker.Finish()
  1315  	assert.NoError(err)
  1316  }
  1317  
  1318  // TestCreateUpdateDestinationRuleUpdate tests update of a destination rule
  1319  // GIVEN the destination rule exist
  1320  // WHEN the controller createOrUpdateDestinationRule function is called
  1321  // THEN expect no error to be returned and destination rule is updated
  1322  func TestCreateUpdateDestinationRuleUpdate(t *testing.T) {
  1323  	assert := asserts.New(t)
  1324  
  1325  	var mocker = gomock.NewController(t)
  1326  	var cli = mocks.NewMockClient(mocker)
  1327  
  1328  	// Expect a call to get a destination rule and return that it was found.
  1329  	cli.EXPECT().
  1330  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1331  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, dr *istioclient.DestinationRule, opt ...client.GetOption) error {
  1332  			dr.TypeMeta = metav1.TypeMeta{
  1333  				APIVersion: destinationRuleAPIVersion,
  1334  				Kind:       destinationRuleKind}
  1335  			return nil
  1336  		})
  1337  
  1338  	// Expect a call to get the appconfig resource to set the owner reference
  1339  	cli.EXPECT().
  1340  		Get(gomock.Any(), types.NamespacedName{Namespace: "test-namespace", Name: "test-app"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1341  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, app *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1342  			app.TypeMeta = metav1.TypeMeta{
  1343  				APIVersion: "core.oam.dev/v1alpha2",
  1344  				Kind:       "ApplicationConfiguration",
  1345  			}
  1346  			return nil
  1347  		})
  1348  
  1349  	// Expect a call to update the destinationrule and return success
  1350  	cli.EXPECT().
  1351  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
  1352  		DoAndReturn(func(ctx context.Context, dr *istioclient.DestinationRule, opts ...client.UpdateOption) error {
  1353  			assert.Equal(destinationRuleKind, dr.Kind)
  1354  			assert.Equal(destinationRuleAPIVersion, dr.APIVersion)
  1355  			assert.Equal("*.test-namespace.svc.cluster.local", dr.Spec.Host)
  1356  			assert.Equal(istionet.ClientTLSSettings_ISTIO_MUTUAL, dr.Spec.TrafficPolicy.Tls.Mode)
  1357  			assert.Equal(uint32(coherenceExtendPort), dr.Spec.TrafficPolicy.PortLevelSettings[0].Port.Number)
  1358  			assert.Equal(istionet.ClientTLSSettings_DISABLE, dr.Spec.TrafficPolicy.PortLevelSettings[0].Tls.Mode)
  1359  			assert.Equal(1, len(dr.OwnerReferences))
  1360  			assert.Equal("ApplicationConfiguration", dr.OwnerReferences[0].Kind)
  1361  			assert.Equal("core.oam.dev/v1alpha2", dr.OwnerReferences[0].APIVersion)
  1362  			return nil
  1363  		})
  1364  
  1365  	scheme := runtime.NewScheme()
  1366  	istioclient.AddToScheme(scheme)
  1367  	core.AddToScheme(scheme)
  1368  	vzapi.AddToScheme(scheme)
  1369  	reconciler := Reconciler{Client: cli, Scheme: scheme}
  1370  
  1371  	namespaceLabels := make(map[string]string)
  1372  	namespaceLabels["istio-injection"] = "enabled"
  1373  	workloadLabels := make(map[string]string)
  1374  	workloadLabels["app.oam.dev/name"] = "test-app"
  1375  	err := reconciler.createOrUpdateDestinationRule(context.Background(), vzlog.DefaultLogger(), "test-namespace", namespaceLabels, workloadLabels)
  1376  	mocker.Finish()
  1377  	assert.NoError(err)
  1378  }
  1379  
  1380  // TestCreateUpdateDestinationRuleNoOamLabel tests failure when no OAM label found
  1381  // GIVEN no app.oam.dev/name label specified
  1382  // WHEN the controller createOrUpdateDestinationRule function is called
  1383  // THEN expect an error to be returned
  1384  func TestCreateUpdateDestinationRuleNoOamLabel(t *testing.T) {
  1385  	assert := asserts.New(t)
  1386  
  1387  	reconciler := Reconciler{}
  1388  	namespaceLabels := make(map[string]string)
  1389  	namespaceLabels["istio-injection"] = "enabled"
  1390  	workloadLabels := make(map[string]string)
  1391  	err := reconciler.createOrUpdateDestinationRule(context.Background(), vzlog.DefaultLogger(), "test-namespace", namespaceLabels, workloadLabels)
  1392  	assert.Equal("OAM app name label missing from metadata, unable to generate destination rule name", err.Error())
  1393  }
  1394  
  1395  // TestCreateUpdateDestinationRuleNoIstioLabel tests failure when no istio label found
  1396  // GIVEN no istio-injection label specified
  1397  // WHEN the controller createOrUpdateDestinationRule function is called
  1398  // THEN expect an error to be returned
  1399  func TestCreateUpdateDestinationRuleNoLabel(t *testing.T) {
  1400  	assert := asserts.New(t)
  1401  
  1402  	reconciler := Reconciler{}
  1403  	namespaceLabels := make(map[string]string)
  1404  	workloadLabels := make(map[string]string)
  1405  	err := reconciler.createOrUpdateDestinationRule(context.Background(), vzlog.DefaultLogger(), "test-namespace", namespaceLabels, workloadLabels)
  1406  	assert.NoError(err)
  1407  }
  1408  
  1409  // newScheme creates a new scheme that includes this package's object to use for testing
  1410  func newScheme() *runtime.Scheme {
  1411  	scheme := runtime.NewScheme()
  1412  	vzapi.AddToScheme(scheme)
  1413  	return scheme
  1414  }
  1415  
  1416  // newReconciler creates a new reconciler for testing
  1417  // c - The K8s client to inject into the reconciler
  1418  func newReconciler(c client.Client) Reconciler {
  1419  	scheme := newScheme()
  1420  	metricsReconciler := &metricstrait.Reconciler{Client: c, Scheme: scheme, Scraper: "verrazzano-system/vmi-system-prometheus-0"}
  1421  	return Reconciler{
  1422  		Client:  c,
  1423  		Log:     zap.S().With("test"),
  1424  		Scheme:  scheme,
  1425  		Metrics: metricsReconciler,
  1426  	}
  1427  }
  1428  
  1429  // newRequest creates a new reconciler request for testing
  1430  // namespace - The namespace to use in the request
  1431  // name - The name to use in the request
  1432  func newRequest(namespace string, name string) ctrl.Request {
  1433  	return ctrl.Request{
  1434  		NamespacedName: types.NamespacedName{
  1435  			Namespace: namespace,
  1436  			Name:      name,
  1437  		},
  1438  	}
  1439  }
  1440  
  1441  // Used for bool in struct literal
  1442  func newTrue() *bool {
  1443  	b := true
  1444  	return &b
  1445  }
  1446  
  1447  func getUnstructuredConfigMapList() *unstructured.UnstructuredList {
  1448  	unstructuredConfigMapList := &unstructured.UnstructuredList{}
  1449  	unstructuredConfigMapList.SetAPIVersion("v1")
  1450  	unstructuredConfigMapList.SetKind("ConfigMap")
  1451  	return unstructuredConfigMapList
  1452  }
  1453  
  1454  // TestReconcileRestart tests reconciling a VerrazzanoCoherenceWorkload with the restart-version specified in its annotations.
  1455  // This should result in restart-version annotation written to the Coherence CR.
  1456  // GIVEN a VerrazzanoCoherenceWorkload resource
  1457  // WHEN the controller Reconcile function is called and the restart-version is specified in annotations
  1458  // THEN the restart-version annotation written  to the Coherence CR
  1459  func TestReconcileRestart(t *testing.T) {
  1460  	assert := asserts.New(t)
  1461  
  1462  	var mocker = gomock.NewController(t)
  1463  	var cli = mocks.NewMockClient(mocker)
  1464  	mockStatus := mocks.NewMockStatusWriter(mocker)
  1465  
  1466  	appConfigName := "unit-test-app-config"
  1467  	componentName := "unit-test-component"
  1468  	fluentdImage := "unit-test-image:latest"
  1469  	labels := map[string]string{oam.LabelAppComponent: componentName, oam.LabelAppName: appConfigName}
  1470  	annotations := map[string]string{vzconst.RestartVersionAnnotation: testRestartVersion}
  1471  
  1472  	// set the Fluentd image which is obtained via env then reset at end of test
  1473  	initialDefaultFluentdImage := logging.DefaultFluentdImage
  1474  	logging.DefaultFluentdImage = fluentdImage
  1475  	defer func() { logging.DefaultFluentdImage = initialDefaultFluentdImage }()
  1476  
  1477  	// expect call to fetch existing coherence StatefulSet
  1478  	cli.EXPECT().
  1479  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1480  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, coherence *v1.StatefulSet, opt ...client.GetOption) error {
  1481  			// return nil error because Coherence StatefulSet exists
  1482  			return nil
  1483  		})
  1484  	// expect a call to fetch the VerrazzanoCoherenceWorkload
  1485  	cli.EXPECT().
  1486  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-verrazzano-coherence-workload"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1487  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, workload *vzapi.VerrazzanoCoherenceWorkload, opt ...client.GetOption) error {
  1488  			json := `{"metadata":{"name":"unit-test-cluster"},"spec":{"replicas":3}}`
  1489  			workload.Spec.Template = runtime.RawExtension{Raw: []byte(json)}
  1490  			workload.ObjectMeta.Labels = labels
  1491  			workload.ObjectMeta.Annotations = annotations
  1492  			workload.APIVersion = vzapi.SchemeGroupVersion.String()
  1493  			workload.Kind = "VerrazzanoCoherenceWorkload"
  1494  			workload.Namespace = namespace
  1495  			workload.ObjectMeta.Generation = 2
  1496  			workload.Status.LastGeneration = "1"
  1497  			return nil
  1498  		})
  1499  	// expect a call to fetch the OAM application configuration (and the component has an attached logging scope)
  1500  	cli.EXPECT().
  1501  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1502  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1503  			component := oamcore.ApplicationConfigurationComponent{ComponentName: componentName}
  1504  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{component}
  1505  			return nil
  1506  		})
  1507  	// expect a call to list the FLUENTD config maps
  1508  	cli.EXPECT().
  1509  		List(gomock.Any(), getUnstructuredConfigMapList(), gomock.Any()).
  1510  		DoAndReturn(func(ctx context.Context, list *unstructured.UnstructuredList, opts ...client.ListOption) error {
  1511  			// return no resources
  1512  			return nil
  1513  		})
  1514  	// no config maps found, so expect a call to create a config map with our parsing rules
  1515  	cli.EXPECT().
  1516  		Create(gomock.Any(), gomock.Any(), gomock.Any()).
  1517  		DoAndReturn(func(ctx context.Context, configMap *corev1.ConfigMap, opts ...client.CreateOption) error {
  1518  			assert.Equal(strings.Join(strings.Split(cohFluentdParsingRules, "{{ .CAFile}}"), ""), configMap.Data["fluentd.conf"])
  1519  			return nil
  1520  		})
  1521  	// expect a call to attempt to get the Coherence CR
  1522  	cli.EXPECT().
  1523  		Get(gomock.Any(), types.NamespacedName{Namespace: namespace, Name: "unit-test-cluster"}, gomock.Not(gomock.Nil()), gomock.Any()).
  1524  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, u *unstructured.Unstructured, opt ...client.GetOption) error {
  1525  			// set the old Fluentd image on the returned obj
  1526  			containers, _, _ := unstructured.NestedSlice(u.Object, "spec", "sideCars")
  1527  			unstructured.SetNestedField(containers[0].(map[string]interface{}), "unit-test-image:existing", "image")
  1528  			unstructured.SetNestedSlice(u.Object, containers, "spec", "sideCars")
  1529  			// return nil error because Coherence StatefulSet exists
  1530  			return nil
  1531  		})
  1532  	// expect a call to update the Coherence CR
  1533  	cli.EXPECT().
  1534  		Update(gomock.Any(), gomock.AssignableToTypeOf(&unstructured.Unstructured{}), gomock.Any()).
  1535  		DoAndReturn(func(ctx context.Context, u *unstructured.Unstructured, opts ...client.UpdateOption) error {
  1536  			assert.Equal(coherenceAPIVersion, u.GetAPIVersion())
  1537  			assert.Equal(coherenceKind, u.GetKind())
  1538  
  1539  			// make sure JVM args were added
  1540  			jvmArgs, _, _ := unstructured.NestedSlice(u.Object, specJvmArgsFields...)
  1541  			assert.Equal(additionalJvmArgs, jvmArgs)
  1542  
  1543  			// make sure side car was added
  1544  			sideCars, _, _ := unstructured.NestedSlice(u.Object, specField, "sideCars")
  1545  			assert.Equal(1, len(sideCars))
  1546  			// assert correct Fluentd image
  1547  			assert.Equal(fluentdImage, sideCars[0].(map[string]interface{})["image"])
  1548  
  1549  			// make sure sidecar.istio.io/inject annotation was added
  1550  			annotations, _, _ := unstructured.NestedStringMap(u.Object, specAnnotationsFields...)
  1551  			assert.Equal(annotations, map[string]string{"sidecar.istio.io/inject": "false", vzconst.RestartVersionAnnotation: testRestartVersion})
  1552  			return nil
  1553  		})
  1554  	// expect a call to get the application configuration for the workload
  1555  	cli.EXPECT().
  1556  		Get(gomock.Any(), gomock.Eq(types.NamespacedName{Namespace: namespace, Name: appConfigName}), gomock.Not(gomock.Nil()), gomock.Any()).
  1557  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamcore.ApplicationConfiguration, opt ...client.GetOption) error {
  1558  			appConfig.Spec.Components = []oamcore.ApplicationConfigurationComponent{{ComponentName: componentName}}
  1559  			return nil
  1560  		})
  1561  	// expect a call to get the namespace for the Coherence resource
  1562  	cli.EXPECT().
  1563  		Get(gomock.Any(), gomock.Eq(client.ObjectKey{Namespace: "", Name: namespace}), gomock.Not(gomock.Nil()), gomock.Any()).
  1564  		DoAndReturn(func(ctx context.Context, key client.ObjectKey, namespace *corev1.Namespace, opt ...client.GetOption) error {
  1565  			return nil
  1566  		})
  1567  
  1568  	// expect a call to status update
  1569  	cli.EXPECT().Status().Return(mockStatus).AnyTimes()
  1570  	// expect a call to update the status upgrade version
  1571  	mockStatus.EXPECT().
  1572  		Update(gomock.Any(), gomock.Any(), gomock.Any()).
  1573  		DoAndReturn(func(ctx context.Context, workload *vzapi.VerrazzanoCoherenceWorkload, opts ...client.UpdateOption) error {
  1574  			return nil
  1575  		})
  1576  
  1577  	// create a request and reconcile it
  1578  	request := newRequest(namespace, "unit-test-verrazzano-coherence-workload")
  1579  	reconciler := newReconciler(cli)
  1580  	result, err := reconciler.Reconcile(context.TODO(), request)
  1581  
  1582  	mocker.Finish()
  1583  	assert.NoError(err)
  1584  	assert.Equal(false, result.Requeue)
  1585  }
  1586  
  1587  // TestReconcileKubeSystem tests to make sure we do not reconcile
  1588  // Any resource that belong to the kube-system namespace
  1589  func TestReconcileKubeSystem(t *testing.T) {
  1590  
  1591  	assert := asserts.New(t)
  1592  
  1593  	var mocker = gomock.NewController(t)
  1594  	var cli = mocks.NewMockClient(mocker)
  1595  
  1596  	// create a request and reconcile it
  1597  	request := newRequest(vzconst.KubeSystem, "unit-test-verrazzano-helidon-workload")
  1598  	reconciler := newReconciler(cli)
  1599  	result, err := reconciler.Reconcile(context.TODO(), request)
  1600  
  1601  	mocker.Finish()
  1602  	assert.Nil(err)
  1603  	assert.True(result.IsZero())
  1604  }
  1605  
  1606  // TestReconcileFailed tests to make sure the failure metric is being exposed
  1607  func TestReconcileFailed(t *testing.T) {
  1608  	testAppConfigName := "unit-test-app-config"
  1609  	testNamespace := "test-ns"
  1610  
  1611  	scheme := k8scheme.Scheme
  1612  	assert := asserts.New(t)
  1613  	clientBuilder := fake.NewClientBuilder().WithScheme(scheme).Build()
  1614  	// Create a request and reconcile it
  1615  	reconciler := newReconciler(clientBuilder)
  1616  	request := newRequest(testNamespace, testAppConfigName)
  1617  	reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.CohworkloadReconcileError)
  1618  	assert.NoError(err)
  1619  	// Expect a call to fetch the error
  1620  	reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get())
  1621  	reconciler.Reconcile(context.TODO(), request)
  1622  	reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get())
  1623  	assert.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1)
  1624  }