github.com/verrazzano/verrazzano@v1.7.1/application-operator/controllers/appconfig/appconfig_controller_test.go (about)

     1  // Copyright (c) 2021, 2023, Oracle and/or its affiliates.
     2  // Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl.
     3  
     4  package appconfig
     5  
     6  import (
     7  	"context"
     8  	"fmt"
     9  	"testing"
    10  
    11  	oamrt "github.com/crossplane/crossplane-runtime/apis/common/v1"
    12  	oamcore "github.com/crossplane/oam-kubernetes-runtime/apis/core"
    13  	oamv1 "github.com/crossplane/oam-kubernetes-runtime/apis/core/v1alpha2"
    14  	"github.com/golang/mock/gomock"
    15  	"github.com/prometheus/client_golang/prometheus/testutil"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/verrazzano/verrazzano/application-operator/metricsexporter"
    18  	"github.com/verrazzano/verrazzano/application-operator/mocks"
    19  	vzconst "github.com/verrazzano/verrazzano/pkg/constants"
    20  	"go.uber.org/zap"
    21  	appsv1 "k8s.io/api/apps/v1"
    22  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	"k8s.io/apimachinery/pkg/types"
    26  	k8scheme "k8s.io/client-go/kubernetes/scheme"
    27  	ctrl "sigs.k8s.io/controller-runtime"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    30  )
    31  
    32  const (
    33  	testNamespace         = "test-ns"
    34  	testAppConfigName     = "test-appconfig"
    35  	testComponentName     = "test-component"
    36  	testNewRestartVersion = "test-new-restart"
    37  	testDeploymentName    = "test-deployment"
    38  	testStatefulSetName   = "test-statefulset"
    39  	testDaemonSetName     = "test-daemonset"
    40  )
    41  
    42  // newScheme creates a new scheme that includes this package's object to use for testing
    43  func newScheme() *runtime.Scheme {
    44  	scheme := runtime.NewScheme()
    45  	_ = oamcore.AddToScheme(scheme)
    46  	return scheme
    47  }
    48  
    49  // newReconciler creates a new reconciler for testing
    50  func newReconciler(c client.Client) Reconciler {
    51  	return Reconciler{
    52  		Client: c,
    53  		Log:    zap.S().With("test"),
    54  		Scheme: newScheme(),
    55  	}
    56  }
    57  
    58  // newRequest creates a new reconciler request for testing
    59  func newRequest(namespace string, name string) ctrl.Request {
    60  	return ctrl.Request{
    61  		NamespacedName: types.NamespacedName{
    62  			Namespace: namespace,
    63  			Name:      name,
    64  		},
    65  	}
    66  }
    67  
    68  // newAppConfig creates a minimal ApplicationConfiguration struct
    69  func newAppConfig() *oamv1.ApplicationConfiguration {
    70  	return &oamv1.ApplicationConfiguration{
    71  		ObjectMeta: v1.ObjectMeta{
    72  			Name:        testAppConfigName,
    73  			Namespace:   testNamespace,
    74  			Annotations: make(map[string]string),
    75  		},
    76  	}
    77  }
    78  
    79  func TestReconcileApplicationConfigurationNotFound(t *testing.T) {
    80  
    81  	assertions := assert.New(t)
    82  	_ = oamcore.AddToScheme(k8scheme.Scheme)
    83  	c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
    84  
    85  	reconciler := newReconciler(c)
    86  	request := newRequest(testNamespace, testAppConfigName)
    87  
    88  	_, err := reconciler.Reconcile(context.TODO(), request)
    89  	assertions.NoError(err)
    90  }
    91  func TestReconcileNoRestartVersion(t *testing.T) {
    92  
    93  	assertions := assert.New(t)
    94  	_ = oamcore.AddToScheme(k8scheme.Scheme)
    95  	c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
    96  
    97  	reconciler := newReconciler(c)
    98  	request := newRequest(testNamespace, testAppConfigName)
    99  
   100  	err := c.Create(context.TODO(), newAppConfig())
   101  	assertions.NoError(err)
   102  
   103  	_, err = reconciler.Reconcile(context.TODO(), request)
   104  	assertions.NoError(err)
   105  }
   106  
   107  func TestReconcileRestartVersion(t *testing.T) {
   108  
   109  	assertions := assert.New(t)
   110  	_ = oamcore.AddToScheme(k8scheme.Scheme)
   111  	c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
   112  
   113  	reconciler := newReconciler(c)
   114  	request := newRequest(testNamespace, testAppConfigName)
   115  
   116  	appConfig := newAppConfig()
   117  	appConfig.Annotations[vzconst.RestartVersionAnnotation] = "1"
   118  	err := c.Create(context.TODO(), appConfig)
   119  	assertions.NoError(err)
   120  
   121  	_, err = reconciler.Reconcile(context.TODO(), request)
   122  	assertions.NoError(err)
   123  
   124  	err = c.Get(context.TODO(), request.NamespacedName, appConfig)
   125  	assertions.NoError(err)
   126  }
   127  
   128  func TestReconcileEmptyRestartVersion(t *testing.T) {
   129  
   130  	assertions := assert.New(t)
   131  	_ = oamcore.AddToScheme(k8scheme.Scheme)
   132  	c := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
   133  
   134  	reconciler := newReconciler(c)
   135  	request := newRequest(testNamespace, testAppConfigName)
   136  
   137  	appConfig := newAppConfig()
   138  	appConfig.Annotations[vzconst.RestartVersionAnnotation] = ""
   139  	err := c.Create(context.TODO(), appConfig)
   140  	assertions.NoError(err)
   141  
   142  	_, err = reconciler.Reconcile(context.TODO(), request)
   143  	assertions.NoError(err)
   144  
   145  	err = c.Get(context.TODO(), request.NamespacedName, appConfig)
   146  	assertions.NoError(err)
   147  }
   148  
   149  func TestReconcileRestartWeblogic(t *testing.T) {
   150  
   151  	assertions := assert.New(t)
   152  
   153  	var mocker = gomock.NewController(t)
   154  	var cli = mocks.NewMockClient(mocker)
   155  
   156  	// expect a call to fetch the ApplicationConfiguration
   157  	cli.EXPECT().
   158  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   159  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   160  			appConfig.Namespace = testNamespace
   161  			appConfig.Name = testAppConfigName
   162  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   163  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   164  				ComponentName: testComponentName,
   165  				Reference: oamrt.TypedReference{
   166  					APIVersion: "v1",
   167  					Kind:       vzconst.VerrazzanoWebLogicWorkloadKind,
   168  					Name:       testComponentName,
   169  				},
   170  			}}
   171  			return nil
   172  		})
   173  
   174  	// expect a call to fetch the workload
   175  	cli.EXPECT().
   176  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   177  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   178  			return nil
   179  		}).Times(2)
   180  
   181  	// expect a call to update the workload
   182  	cli.EXPECT().
   183  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   184  		DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error {
   185  			return nil
   186  		})
   187  
   188  	// create a request and reconcile it
   189  	request := newRequest(testNamespace, testAppConfigName)
   190  	reconciler := newReconciler(cli)
   191  	result, err := reconciler.Reconcile(context.TODO(), request)
   192  
   193  	mocker.Finish()
   194  	assertions.NoError(err)
   195  	assertions.Equal(false, result.Requeue)
   196  }
   197  
   198  func TestReconcileRestartCoherence(t *testing.T) {
   199  
   200  	assertions := assert.New(t)
   201  
   202  	var mocker = gomock.NewController(t)
   203  	var cli = mocks.NewMockClient(mocker)
   204  
   205  	// expect a call to fetch the ApplicationConfiguration
   206  	cli.EXPECT().
   207  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   208  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   209  			appConfig.Namespace = testNamespace
   210  			appConfig.Name = testAppConfigName
   211  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   212  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   213  				ComponentName: testComponentName,
   214  				Reference: oamrt.TypedReference{
   215  					APIVersion: "v1",
   216  					Kind:       vzconst.VerrazzanoCoherenceWorkloadKind,
   217  					Name:       testComponentName,
   218  				},
   219  			}}
   220  			return nil
   221  		})
   222  
   223  	// expect a call to fetch the workload
   224  	cli.EXPECT().
   225  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   226  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   227  			return nil
   228  		}).Times(2)
   229  
   230  	// expect a call to update the workload
   231  	cli.EXPECT().
   232  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   233  		DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error {
   234  			return nil
   235  		})
   236  
   237  	// create a request and reconcile it
   238  	request := newRequest(testNamespace, testAppConfigName)
   239  	reconciler := newReconciler(cli)
   240  	result, err := reconciler.Reconcile(context.TODO(), request)
   241  
   242  	mocker.Finish()
   243  	assertions.NoError(err)
   244  	assertions.Equal(false, result.Requeue)
   245  }
   246  
   247  func TestReconcileRestartHelidon(t *testing.T) {
   248  
   249  	assertions := assert.New(t)
   250  
   251  	var mocker = gomock.NewController(t)
   252  	var cli = mocks.NewMockClient(mocker)
   253  
   254  	// expect a call to fetch the ApplicationConfiguration
   255  	cli.EXPECT().
   256  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   257  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   258  			appConfig.Namespace = testNamespace
   259  			appConfig.Name = testAppConfigName
   260  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   261  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   262  				ComponentName: testComponentName,
   263  				Reference: oamrt.TypedReference{
   264  					APIVersion: "v1",
   265  					Kind:       vzconst.VerrazzanoHelidonWorkloadKind,
   266  					Name:       testComponentName,
   267  				},
   268  			}}
   269  			return nil
   270  		})
   271  
   272  	// Expect a call to fetch the workload
   273  	cli.EXPECT().
   274  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   275  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   276  			return nil
   277  		}).Times(2)
   278  
   279  	// expect a call to update the workload
   280  	cli.EXPECT().
   281  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   282  		DoAndReturn(func(ctx context.Context, component *unstructured.Unstructured, options ...client.UpdateOption) error {
   283  			return nil
   284  		})
   285  
   286  	// create a request and reconcile it
   287  	request := newRequest(testNamespace, testAppConfigName)
   288  	reconciler := newReconciler(cli)
   289  	result, err := reconciler.Reconcile(context.TODO(), request)
   290  
   291  	mocker.Finish()
   292  	assertions.NoError(err)
   293  	assertions.Equal(false, result.Requeue)
   294  }
   295  
   296  func TestReconcileDeploymentRestart(t *testing.T) {
   297  
   298  	assertions := assert.New(t)
   299  
   300  	var mocker = gomock.NewController(t)
   301  	var cli = mocks.NewMockClient(mocker)
   302  
   303  	// expect a call to fetch the ApplicationConfiguration
   304  	cli.EXPECT().
   305  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   306  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   307  			appConfig.Namespace = testNamespace
   308  			appConfig.Name = testAppConfigName
   309  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   310  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   311  				ComponentName: testDeploymentName,
   312  				Reference: oamrt.TypedReference{
   313  					APIVersion: "v1",
   314  					Kind:       vzconst.DeploymentWorkloadKind,
   315  					Name:       testDeploymentName,
   316  				},
   317  			}}
   318  			return nil
   319  		})
   320  
   321  	// expect a call to fetch the workload
   322  	cli.EXPECT().
   323  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   324  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   325  			return nil
   326  		})
   327  
   328  	// expect a call to fetch the deployment
   329  	cli.EXPECT().
   330  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()).
   331  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error {
   332  			deploy.Name = testDeploymentName
   333  			deploy.Namespace = testNamespace
   334  			return nil
   335  		})
   336  	// expect a call to fetch the deployment
   337  	cli.EXPECT().
   338  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()).
   339  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error {
   340  			deploy.Name = testDeploymentName
   341  			deploy.Namespace = testNamespace
   342  			return nil
   343  		})
   344  	// expect a call to update the deployment
   345  	cli.EXPECT().
   346  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   347  		DoAndReturn(func(ctx context.Context, deploy *appsv1.Deployment, options ...client.UpdateOption) error {
   348  			assertions.Equal(testNewRestartVersion, deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation])
   349  			return nil
   350  		})
   351  	// create a request and reconcile it
   352  	request := newRequest(testNamespace, testAppConfigName)
   353  	reconciler := newReconciler(cli)
   354  	result, err := reconciler.Reconcile(context.TODO(), request)
   355  
   356  	mocker.Finish()
   357  	assertions.NoError(err)
   358  	assertions.Equal(false, result.Requeue)
   359  }
   360  
   361  func TestFailedReconcileDeploymentRestart(t *testing.T) {
   362  
   363  	assertions := assert.New(t)
   364  
   365  	var mocker = gomock.NewController(t)
   366  	var cli = mocks.NewMockClient(mocker)
   367  
   368  	// expect a call to fetch the ApplicationConfiguration
   369  	cli.EXPECT().
   370  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   371  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   372  			appConfig.Namespace = testNamespace
   373  			appConfig.Name = testAppConfigName
   374  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   375  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   376  				ComponentName: testDeploymentName,
   377  				Reference: oamrt.TypedReference{
   378  					APIVersion: "v1",
   379  					Kind:       vzconst.DeploymentWorkloadKind,
   380  					Name:       testDeploymentName,
   381  				},
   382  			}}
   383  			return nil
   384  		})
   385  
   386  	// expect a call to fetch the workload
   387  	cli.EXPECT().
   388  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   389  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   390  			return nil
   391  		})
   392  
   393  	// expect a call to fetch the deployment
   394  	cli.EXPECT().
   395  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()).
   396  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error {
   397  			return fmt.Errorf("Could not return %s in namespace %s", testDeploymentName, testNamespace)
   398  		})
   399  
   400  	// create a request and reconcile it
   401  	request := newRequest(testNamespace, testAppConfigName)
   402  	reconciler := newReconciler(cli)
   403  	result, err := reconciler.Reconcile(context.TODO(), request)
   404  
   405  	mocker.Finish()
   406  	assertions.NoError(err)
   407  	assertions.Equal(true, result.Requeue)
   408  }
   409  
   410  func TestReconcileDeploymentNoRestart(t *testing.T) {
   411  
   412  	assertions := assert.New(t)
   413  
   414  	var mocker = gomock.NewController(t)
   415  	var cli = mocks.NewMockClient(mocker)
   416  
   417  	// expect a call to fetch the ApplicationConfiguration
   418  	cli.EXPECT().
   419  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   420  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   421  			appConfig.Namespace = testNamespace
   422  			appConfig.Name = testAppConfigName
   423  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   424  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   425  				ComponentName: testDeploymentName,
   426  				Reference: oamrt.TypedReference{
   427  					APIVersion: "v1",
   428  					Kind:       vzconst.DeploymentWorkloadKind,
   429  					Name:       testDeploymentName,
   430  				},
   431  			}}
   432  			return nil
   433  		})
   434  
   435  	// expect a call to fetch the workload
   436  	cli.EXPECT().
   437  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   438  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   439  			return nil
   440  		})
   441  
   442  	// expect a call to fetch the deployment
   443  	cli.EXPECT().
   444  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()).
   445  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error {
   446  			deploy.Name = testDeploymentName
   447  			deploy.Namespace = testNamespace
   448  			deploy.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   449  			deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   450  			return nil
   451  		})
   452  	// expect a call to fetch the deployment
   453  	cli.EXPECT().
   454  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDeploymentName}, gomock.Not(gomock.Nil()), gomock.Any()).
   455  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, deploy *appsv1.Deployment, opt ...client.GetOption) error {
   456  			deploy.Name = testDeploymentName
   457  			deploy.Namespace = testNamespace
   458  			deploy.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   459  			deploy.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   460  			return nil
   461  		})
   462  
   463  	// create a request and reconcile it
   464  	request := newRequest(testNamespace, testAppConfigName)
   465  	reconciler := newReconciler(cli)
   466  	result, err := reconciler.Reconcile(context.TODO(), request)
   467  
   468  	mocker.Finish()
   469  	assertions.NoError(err)
   470  	assertions.Equal(false, result.Requeue)
   471  }
   472  
   473  func TestReconcileDaemonSetRestartDaemonSet(t *testing.T) {
   474  
   475  	assertions := assert.New(t)
   476  
   477  	var mocker = gomock.NewController(t)
   478  	var cli = mocks.NewMockClient(mocker)
   479  
   480  	// expect a call to fetch the ApplicationConfiguration
   481  	cli.EXPECT().
   482  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   483  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   484  			appConfig.Namespace = testNamespace
   485  			appConfig.Name = testAppConfigName
   486  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   487  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   488  				ComponentName: testDaemonSetName,
   489  				Reference: oamrt.TypedReference{
   490  					APIVersion: "v1",
   491  					Kind:       vzconst.DaemonSetWorkloadKind,
   492  					Name:       testDaemonSetName,
   493  				},
   494  			}}
   495  			return nil
   496  		})
   497  
   498  	// expect a call to fetch the workload
   499  	cli.EXPECT().
   500  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   501  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   502  			return nil
   503  		})
   504  	// expect a call to fetch the daemonset
   505  	cli.EXPECT().
   506  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   507  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error {
   508  			daemonset.Name = testDaemonSetName
   509  			daemonset.Namespace = testNamespace
   510  			return nil
   511  		})
   512  	// expect a call to fetch the daemonset
   513  	cli.EXPECT().
   514  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   515  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error {
   516  			daemonset.Name = testDaemonSetName
   517  			daemonset.Namespace = testNamespace
   518  			return nil
   519  		})
   520  	// expect a call to update the daemonset
   521  	cli.EXPECT().
   522  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   523  		DoAndReturn(func(ctx context.Context, daemonset *appsv1.DaemonSet, options ...client.UpdateOption) error {
   524  			assertions.Equal(testNewRestartVersion, daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation])
   525  			return nil
   526  		})
   527  
   528  	// create a request and reconcile it
   529  	request := newRequest(testNamespace, testAppConfigName)
   530  	reconciler := newReconciler(cli)
   531  	result, err := reconciler.Reconcile(context.TODO(), request)
   532  
   533  	mocker.Finish()
   534  	assertions.NoError(err)
   535  	assertions.Equal(false, result.Requeue)
   536  }
   537  
   538  func TestReconcileDaemonSetNoRestartDaemonSet(t *testing.T) {
   539  
   540  	assertions := assert.New(t)
   541  
   542  	var mocker = gomock.NewController(t)
   543  	var cli = mocks.NewMockClient(mocker)
   544  
   545  	// expect a call to fetch the ApplicationConfiguration
   546  	cli.EXPECT().
   547  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   548  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   549  			appConfig.Namespace = testNamespace
   550  			appConfig.Name = testAppConfigName
   551  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   552  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   553  				ComponentName: testDaemonSetName,
   554  				Reference: oamrt.TypedReference{
   555  					APIVersion: "v1",
   556  					Kind:       vzconst.DaemonSetWorkloadKind,
   557  					Name:       testDaemonSetName,
   558  				},
   559  			}}
   560  			return nil
   561  		})
   562  
   563  	// expect a call to fetch the workload
   564  	cli.EXPECT().
   565  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   566  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   567  			return nil
   568  		})
   569  	// expect a call to fetch the daemonset
   570  	cli.EXPECT().
   571  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   572  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error {
   573  			daemonset.Name = testDaemonSetName
   574  			daemonset.Namespace = testNamespace
   575  			daemonset.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   576  			daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   577  			return nil
   578  		})
   579  	// expect a call to fetch the daemonset
   580  	cli.EXPECT().
   581  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testDaemonSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   582  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, daemonset *appsv1.DaemonSet, opt ...client.GetOption) error {
   583  			daemonset.Name = testDaemonSetName
   584  			daemonset.Namespace = testNamespace
   585  			daemonset.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   586  			daemonset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   587  			return nil
   588  		})
   589  
   590  	// create a request and reconcile it
   591  	request := newRequest(testNamespace, testAppConfigName)
   592  	reconciler := newReconciler(cli)
   593  	result, err := reconciler.Reconcile(context.TODO(), request)
   594  
   595  	mocker.Finish()
   596  	assertions.NoError(err)
   597  	assertions.Equal(false, result.Requeue)
   598  }
   599  
   600  func TestReconcileStatefulSetRestart(t *testing.T) {
   601  
   602  	assertions := assert.New(t)
   603  
   604  	var mocker = gomock.NewController(t)
   605  	var cli = mocks.NewMockClient(mocker)
   606  
   607  	// expect a call to fetch the ApplicationConfiguration
   608  	cli.EXPECT().
   609  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   610  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   611  			appConfig.Namespace = testNamespace
   612  			appConfig.Name = testAppConfigName
   613  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   614  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   615  				ComponentName: testStatefulSetName,
   616  				Reference: oamrt.TypedReference{
   617  					APIVersion: "v1",
   618  					Kind:       vzconst.StatefulSetWorkloadKind,
   619  					Name:       testStatefulSetName,
   620  				},
   621  			}}
   622  			return nil
   623  		})
   624  
   625  	// expect a call to fetch the workload
   626  	cli.EXPECT().
   627  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil())).
   628  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   629  			return nil
   630  		})
   631  	// expect a call to fetch the statefulset
   632  	cli.EXPECT().
   633  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   634  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error {
   635  			statefulset.Name = testStatefulSetName
   636  			statefulset.Namespace = testNamespace
   637  			return nil
   638  		})
   639  	// expect a call to fetch the statefulset
   640  	cli.EXPECT().
   641  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   642  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error {
   643  			statefulset.Name = testStatefulSetName
   644  			statefulset.Namespace = testNamespace
   645  			return nil
   646  		})
   647  	// expect a call to update the statefulset
   648  	cli.EXPECT().
   649  		Update(gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   650  		DoAndReturn(func(ctx context.Context, statefulset *appsv1.StatefulSet, options ...client.UpdateOption) error {
   651  			assertions.Equal(testNewRestartVersion, statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation])
   652  			return nil
   653  		})
   654  
   655  	// create a request and reconcile it
   656  	request := newRequest(testNamespace, testAppConfigName)
   657  	reconciler := newReconciler(cli)
   658  	result, err := reconciler.Reconcile(context.TODO(), request)
   659  
   660  	mocker.Finish()
   661  	assertions.NoError(err)
   662  	assertions.Equal(false, result.Requeue)
   663  }
   664  
   665  func TestReconcileStatefulSetNoRestart(t *testing.T) {
   666  
   667  	assertions := assert.New(t)
   668  
   669  	var mocker = gomock.NewController(t)
   670  	var cli = mocks.NewMockClient(mocker)
   671  
   672  	// expect a call to fetch the ApplicationConfiguration
   673  	cli.EXPECT().
   674  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testAppConfigName}, gomock.Not(gomock.Nil()), gomock.Any()).
   675  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, appConfig *oamv1.ApplicationConfiguration, opt ...client.GetOption) error {
   676  			appConfig.Namespace = testNamespace
   677  			appConfig.Name = testAppConfigName
   678  			appConfig.Annotations = map[string]string{vzconst.RestartVersionAnnotation: testNewRestartVersion}
   679  			appConfig.Status.Workloads = []oamv1.WorkloadStatus{{
   680  				ComponentName: testStatefulSetName,
   681  				Reference: oamrt.TypedReference{
   682  					APIVersion: "v1",
   683  					Kind:       vzconst.StatefulSetWorkloadKind,
   684  					Name:       testStatefulSetName,
   685  				},
   686  			}}
   687  			return nil
   688  		})
   689  
   690  	// expect a call to fetch the workload
   691  	cli.EXPECT().
   692  		Get(gomock.Any(), gomock.Any(), gomock.Not(gomock.Nil()), gomock.Any()).
   693  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, component *unstructured.Unstructured, opt ...client.GetOption) error {
   694  			return nil
   695  		})
   696  	// expect a call to fetch the statefulset
   697  	cli.EXPECT().
   698  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   699  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error {
   700  			statefulset.Name = testStatefulSetName
   701  			statefulset.Namespace = testNamespace
   702  			statefulset.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   703  			statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   704  			return nil
   705  		})
   706  	// expect a call to fetch the statefulset
   707  	cli.EXPECT().
   708  		Get(gomock.Any(), types.NamespacedName{Namespace: testNamespace, Name: testStatefulSetName}, gomock.Not(gomock.Nil()), gomock.Any()).
   709  		DoAndReturn(func(ctx context.Context, name types.NamespacedName, statefulset *appsv1.StatefulSet, opt ...client.GetOption) error {
   710  			statefulset.Name = testStatefulSetName
   711  			statefulset.Namespace = testNamespace
   712  			statefulset.Spec.Template.ObjectMeta.Annotations = make(map[string]string)
   713  			statefulset.Spec.Template.ObjectMeta.Annotations[vzconst.RestartVersionAnnotation] = testNewRestartVersion
   714  			return nil
   715  		})
   716  
   717  	// create a request and reconcile it
   718  	request := newRequest(testNamespace, testAppConfigName)
   719  	reconciler := newReconciler(cli)
   720  	result, err := reconciler.Reconcile(context.TODO(), request)
   721  
   722  	mocker.Finish()
   723  	assertions.NoError(err)
   724  	assertions.Equal(false, result.Requeue)
   725  }
   726  
   727  // TestReconcileKubeSystem tests to make sure we do not reconcile
   728  // Any resource that belong to the kube-system namespace
   729  func TestReconcileKubeSystem(t *testing.T) {
   730  
   731  	assertions := assert.New(t)
   732  	mocker := gomock.NewController(t)
   733  	cli := mocks.NewMockClient(mocker)
   734  
   735  	// create a request and reconcile it
   736  	request := newRequest(vzconst.KubeSystem, testAppConfigName)
   737  	reconciler := newReconciler(cli)
   738  	result, err := reconciler.Reconcile(context.TODO(), request)
   739  
   740  	// Validate the results
   741  	mocker.Finish()
   742  	assertions.Nil(err)
   743  	assertions.True(result.IsZero())
   744  }
   745  
   746  // TestReconcileFailed tests to make sure the failure metric is being exposed
   747  func TestReconcileFailed(t *testing.T) {
   748  
   749  	assertions := assert.New(t)
   750  	clientBuilder := fake.NewClientBuilder().WithScheme(k8scheme.Scheme).Build()
   751  	// Create a request and reconcile it
   752  	reconciler := newReconciler(clientBuilder)
   753  	request := newRequest(testNamespace, testAppConfigName)
   754  	reconcileerrorCounterObject, err := metricsexporter.GetSimpleCounterMetric(metricsexporter.AppconfigReconcileError)
   755  	assertions.NoError(err)
   756  	// Expect a call to fetch the error
   757  	reconcileFailedCounterBefore := testutil.ToFloat64(reconcileerrorCounterObject.Get())
   758  	reconcileerrorCounterObject.Get().Inc()
   759  	reconciler.Reconcile(context.TODO(), request)
   760  	reconcileFailedCounterAfter := testutil.ToFloat64(reconcileerrorCounterObject.Get())
   761  	assertions.Equal(reconcileFailedCounterBefore, reconcileFailedCounterAfter-1)
   762  }