github.com/argoproj-labs/argocd-operator@v0.10.0/controllers/argocd/util_test.go (about)

     1  package argocd
     2  
     3  import (
     4  	"context"
     5  	b64 "encoding/base64"
     6  	"reflect"
     7  	"strings"
     8  	"testing"
     9  	"time"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"sigs.k8s.io/controller-runtime/pkg/client"
    13  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    14  
    15  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    16  	"github.com/argoproj-labs/argocd-operator/common"
    17  	"github.com/argoproj-labs/argocd-operator/controllers/argoutil"
    18  
    19  	v1 "k8s.io/api/core/v1"
    20  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    21  	"k8s.io/apimachinery/pkg/runtime"
    22  	testclient "k8s.io/client-go/kubernetes/fake"
    23  )
    24  
    25  const (
    26  	dexTestImage          = "testing/dex:latest"
    27  	argoTestImage         = "testing/argocd:latest"
    28  	redisTestImage        = "testing/redis:latest"
    29  	redisHATestImage      = "testing/redis:latest-ha"
    30  	redisHAProxyTestImage = "testing/redis-ha-haproxy:latest-ha"
    31  )
    32  
    33  func parallelismLimit(n int32) argoCDOpt {
    34  	return func(a *argoproj.ArgoCD) {
    35  		a.Spec.Controller.ParallelismLimit = n
    36  	}
    37  }
    38  
    39  func logFormat(f string) argoCDOpt {
    40  	return func(a *argoproj.ArgoCD) {
    41  		a.Spec.Controller.LogFormat = f
    42  	}
    43  }
    44  
    45  func logLevel(l string) argoCDOpt {
    46  	return func(a *argoproj.ArgoCD) {
    47  		a.Spec.Controller.LogLevel = l
    48  	}
    49  }
    50  
    51  func appSync(s int) argoCDOpt {
    52  	return func(a *argoproj.ArgoCD) {
    53  		a.Spec.Controller.AppSync = &metav1.Duration{Duration: time.Second * time.Duration(s)}
    54  	}
    55  }
    56  
    57  var imageTests = []struct {
    58  	name      string
    59  	pre       func(t *testing.T)
    60  	opts      []argoCDOpt
    61  	want      string
    62  	imageFunc func(a *argoproj.ArgoCD) string
    63  }{
    64  	{
    65  		name:      "dex default configuration",
    66  		imageFunc: getDexContainerImage,
    67  		want:      argoutil.CombineImageTag(common.ArgoCDDefaultDexImage, common.ArgoCDDefaultDexVersion),
    68  	},
    69  	{
    70  		name:      "dex spec configuration",
    71  		imageFunc: getDexContainerImage,
    72  		want:      dexTestImage,
    73  		opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
    74  			a.Spec.SSO = &argoproj.ArgoCDSSOSpec{
    75  				Provider: argoproj.SSOProviderTypeDex,
    76  				Dex: &argoproj.ArgoCDDexSpec{
    77  					Image:   "testing/dex",
    78  					Version: "latest",
    79  				},
    80  			}
    81  		}},
    82  	},
    83  	{
    84  		name:      "dex env configuration",
    85  		imageFunc: getDexContainerImage,
    86  		want:      dexTestImage,
    87  		pre: func(t *testing.T) {
    88  			t.Setenv(common.ArgoCDDexImageEnvName, dexTestImage)
    89  		},
    90  	},
    91  	{
    92  		name:      "argo default configuration",
    93  		imageFunc: getArgoContainerImage,
    94  		want:      argoutil.CombineImageTag(common.ArgoCDDefaultArgoImage, common.ArgoCDDefaultArgoVersion),
    95  	},
    96  	{
    97  		name:      "argo spec configuration",
    98  		imageFunc: getArgoContainerImage,
    99  		want:      argoTestImage, opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
   100  			a.Spec.Image = "testing/argocd"
   101  			a.Spec.Version = "latest"
   102  		}},
   103  	},
   104  	{
   105  		name:      "argo env configuration",
   106  		imageFunc: getArgoContainerImage,
   107  		want:      argoTestImage,
   108  		pre: func(t *testing.T) {
   109  			t.Setenv(common.ArgoCDImageEnvName, argoTestImage)
   110  		},
   111  	},
   112  	{
   113  		name:      "redis default configuration",
   114  		imageFunc: getRedisContainerImage,
   115  		want:      argoutil.CombineImageTag(common.ArgoCDDefaultRedisImage, common.ArgoCDDefaultRedisVersion),
   116  	},
   117  	{
   118  		name:      "redis spec configuration",
   119  		imageFunc: getRedisContainerImage,
   120  		want:      redisTestImage,
   121  		opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
   122  			a.Spec.Redis.Image = "testing/redis"
   123  			a.Spec.Redis.Version = "latest"
   124  		}},
   125  	},
   126  	{
   127  		name:      "redis env configuration",
   128  		imageFunc: getRedisContainerImage,
   129  		want:      redisTestImage,
   130  		pre: func(t *testing.T) {
   131  			t.Setenv(common.ArgoCDRedisImageEnvName, redisTestImage)
   132  		},
   133  	},
   134  	{
   135  		name:      "redis ha default configuration",
   136  		imageFunc: getRedisHAContainerImage,
   137  		want: argoutil.CombineImageTag(
   138  			common.ArgoCDDefaultRedisImage,
   139  			common.ArgoCDDefaultRedisVersionHA),
   140  	},
   141  	{
   142  		name:      "redis ha spec configuration",
   143  		imageFunc: getRedisHAContainerImage,
   144  		want:      redisHATestImage,
   145  		opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
   146  			a.Spec.Redis.Image = "testing/redis"
   147  			a.Spec.Redis.Version = "latest-ha"
   148  		}},
   149  	},
   150  	{
   151  		name:      "redis ha env configuration",
   152  		imageFunc: getRedisHAContainerImage,
   153  		want:      redisHATestImage,
   154  		pre: func(t *testing.T) {
   155  			t.Setenv(common.ArgoCDRedisHAImageEnvName, redisHATestImage)
   156  		},
   157  	},
   158  	{
   159  		name:      "redis ha proxy default configuration",
   160  		imageFunc: getRedisHAProxyContainerImage,
   161  		want: argoutil.CombineImageTag(
   162  			common.ArgoCDDefaultRedisHAProxyImage,
   163  			common.ArgoCDDefaultRedisHAProxyVersion),
   164  	},
   165  	{
   166  		name:      "redis ha proxy spec configuration",
   167  		imageFunc: getRedisHAProxyContainerImage,
   168  		want:      redisHAProxyTestImage,
   169  		opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
   170  			a.Spec.HA.RedisProxyImage = "testing/redis-ha-haproxy"
   171  			a.Spec.HA.RedisProxyVersion = "latest-ha"
   172  		}},
   173  	},
   174  	{
   175  		name:      "redis ha proxy env configuration",
   176  		imageFunc: getRedisHAProxyContainerImage,
   177  		want:      redisHAProxyTestImage,
   178  		pre: func(t *testing.T) {
   179  			t.Setenv(common.ArgoCDRedisHAProxyImageEnvName, redisHAProxyTestImage)
   180  		},
   181  	},
   182  }
   183  
   184  func TestContainerImages_configuration(t *testing.T) {
   185  	for _, tt := range imageTests {
   186  		t.Run(tt.name, func(rt *testing.T) {
   187  			if tt.pre != nil {
   188  				tt.pre(rt)
   189  			}
   190  			a := makeTestArgoCD(tt.opts...)
   191  			image := tt.imageFunc(a)
   192  			if image != tt.want {
   193  				rt.Errorf("got %q, want %q", image, tt.want)
   194  			}
   195  		})
   196  	}
   197  }
   198  
   199  var argoServerURITests = []struct {
   200  	name         string
   201  	routeEnabled bool
   202  	opts         []argoCDOpt
   203  	want         string
   204  }{
   205  	{
   206  		name:         "test with no host name - default",
   207  		routeEnabled: false,
   208  		want:         "https://argocd-server",
   209  	},
   210  	{
   211  		name:         "test with external host name",
   212  		routeEnabled: false,
   213  		opts: []argoCDOpt{func(a *argoproj.ArgoCD) {
   214  			a.Spec.Server.Host = "test-host-name"
   215  		}},
   216  		want: "https://test-host-name",
   217  	},
   218  }
   219  
   220  func setRouteAPIFound(t *testing.T, routeEnabled bool) {
   221  	routeAPIEnabledTemp := routeAPIFound
   222  	t.Cleanup(func() {
   223  		routeAPIFound = routeAPIEnabledTemp
   224  	})
   225  	routeAPIFound = routeEnabled
   226  }
   227  
   228  func TestGetArgoServerURI(t *testing.T) {
   229  	for _, tt := range argoServerURITests {
   230  		t.Run(tt.name, func(t *testing.T) {
   231  			cr := makeTestArgoCD(tt.opts...)
   232  			r := &ReconcileArgoCD{}
   233  			setRouteAPIFound(t, tt.routeEnabled)
   234  			result := r.getArgoServerURI(cr)
   235  			if result != tt.want {
   236  				t.Errorf("%s test failed, got=%q want=%q", tt.name, result, tt.want)
   237  			}
   238  		})
   239  	}
   240  }
   241  
   242  func TestRemoveDeletionFinalizer(t *testing.T) {
   243  	t.Run("ArgoCD resource present", func(t *testing.T) {
   244  		a := makeTestArgoCD(addFinalizer(common.ArgoCDDeletionFinalizer))
   245  
   246  		resObjs := []client.Object{a}
   247  		subresObjs := []client.Object{a}
   248  		runtimeObjs := []runtime.Object{}
   249  		sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   250  		cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   251  		r := makeTestReconciler(cl, sch)
   252  
   253  		err := r.removeDeletionFinalizer(a)
   254  		assert.NoError(t, err)
   255  		if a.IsDeletionFinalizerPresent() {
   256  			t.Fatal("Expected deletion finalizer to be removed")
   257  		}
   258  	})
   259  	t.Run("ArgoCD resource absent", func(t *testing.T) {
   260  		a := makeTestArgoCD(addFinalizer(common.ArgoCDDeletionFinalizer))
   261  
   262  		resObjs := []client.Object{}
   263  		subresObjs := []client.Object{}
   264  		runtimeObjs := []runtime.Object{}
   265  		sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   266  		cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   267  		r := makeTestReconciler(cl, sch)
   268  
   269  		err := r.removeDeletionFinalizer(a)
   270  		assert.Error(t, err, `failed to remove deletion finalizer from argocd: argocds.argoproj.io "argocd" not found`)
   271  	})
   272  }
   273  
   274  func TestAddDeletionFinalizer(t *testing.T) {
   275  	t.Run("ArgoCD resource present", func(t *testing.T) {
   276  		a := makeTestArgoCD()
   277  
   278  		resObjs := []client.Object{a}
   279  		subresObjs := []client.Object{a}
   280  		runtimeObjs := []runtime.Object{}
   281  		sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   282  		cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   283  		r := makeTestReconciler(cl, sch)
   284  
   285  		err := r.addDeletionFinalizer(a)
   286  		assert.NoError(t, err)
   287  		if !a.IsDeletionFinalizerPresent() {
   288  			t.Fatal("Expected deletion finalizer to be added")
   289  		}
   290  	})
   291  	t.Run("ArgoCD resource absent", func(t *testing.T) {
   292  		a := makeTestArgoCD()
   293  
   294  		resObjs := []client.Object{}
   295  		subresObjs := []client.Object{}
   296  		runtimeObjs := []runtime.Object{}
   297  		sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   298  		cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   299  		r := makeTestReconciler(cl, sch)
   300  
   301  		err := r.addDeletionFinalizer(a)
   302  		assert.Error(t, err, `failed to add deletion finalizer for argocd: argocds.argoproj.io "argocd" not found`)
   303  	})
   304  }
   305  
   306  func TestArgoCDInstanceSelector(t *testing.T) {
   307  	t.Run("Selector for a Valid name", func(t *testing.T) {
   308  		validName := "argocd-server"
   309  		selector, err := argocdInstanceSelector(validName)
   310  		assert.NoError(t, err)
   311  		assert.Equal(t, selector.String(), "app.kubernetes.io/managed-by=argocd-server")
   312  	})
   313  	t.Run("Selector for an Invalid name", func(t *testing.T) {
   314  		invalidName := "argocd-*/"
   315  		selector, err := argocdInstanceSelector(invalidName)
   316  		//assert.ErrorContains(t, err, `failed to create a requirement for values[0][app.kubernetes.io/managed-by]: Invalid value: "argocd-*/`)
   317  		//TODO: https://github.com/stretchr/testify/pull/1022 introduced ErrorContains, but is not yet available in a tagged release. Revert to ErrorContains once this becomes available
   318  		assert.Error(t, err)
   319  		assert.Contains(t, err.Error(), `failed to create a requirement for values[0][app.kubernetes.io/managed-by]: Invalid value: "argocd-*/`)
   320  
   321  		assert.Equal(t, selector, nil)
   322  	})
   323  }
   324  
   325  func TestGetArgoApplicationControllerCommand(t *testing.T) {
   326  
   327  	defaultResult := []string{
   328  		"argocd-application-controller",
   329  		"--operation-processors",
   330  		"10",
   331  		"--redis",
   332  		"argocd-redis.argocd.svc.cluster.local:6379",
   333  		"--repo-server",
   334  		"argocd-repo-server.argocd.svc.cluster.local:8081",
   335  		"--status-processors",
   336  		"20",
   337  		"--kubectl-parallelism-limit",
   338  		"10",
   339  		"--loglevel",
   340  		"info",
   341  		"--logformat",
   342  		"text",
   343  	}
   344  
   345  	controllerProcesorsChangedResult := func(n string) []string {
   346  		return []string{
   347  			"argocd-application-controller",
   348  			"--operation-processors",
   349  			"10",
   350  			"--redis",
   351  			"argocd-redis.argocd.svc.cluster.local:6379",
   352  			"--repo-server",
   353  			"argocd-repo-server.argocd.svc.cluster.local:8081",
   354  			"--status-processors",
   355  			n,
   356  			"--kubectl-parallelism-limit",
   357  			"10",
   358  			"--loglevel",
   359  			"info",
   360  			"--logformat",
   361  			"text",
   362  		}
   363  	}
   364  
   365  	operationProcesorsChangedResult := func(n string) []string {
   366  		return []string{
   367  			"argocd-application-controller",
   368  			"--operation-processors",
   369  			n,
   370  			"--redis",
   371  			"argocd-redis.argocd.svc.cluster.local:6379",
   372  			"--repo-server",
   373  			"argocd-repo-server.argocd.svc.cluster.local:8081",
   374  			"--status-processors",
   375  			"20",
   376  			"--kubectl-parallelism-limit",
   377  			"10",
   378  			"--loglevel",
   379  			"info",
   380  			"--logformat",
   381  			"text",
   382  		}
   383  	}
   384  
   385  	parallelismLimitChangedResult := func(n string) []string {
   386  		return []string{
   387  			"argocd-application-controller",
   388  			"--operation-processors",
   389  			"10",
   390  			"--redis",
   391  			"argocd-redis.argocd.svc.cluster.local:6379",
   392  			"--repo-server",
   393  			"argocd-repo-server.argocd.svc.cluster.local:8081",
   394  			"--status-processors",
   395  			"20",
   396  			"--kubectl-parallelism-limit",
   397  			n,
   398  			"--loglevel",
   399  			"info",
   400  			"--logformat",
   401  			"text",
   402  		}
   403  	}
   404  
   405  	logFormatChangedResult := func(f string) []string {
   406  		return []string{
   407  			"argocd-application-controller",
   408  			"--operation-processors",
   409  			"10",
   410  			"--redis",
   411  			"argocd-redis.argocd.svc.cluster.local:6379",
   412  			"--repo-server",
   413  			"argocd-repo-server.argocd.svc.cluster.local:8081",
   414  			"--status-processors",
   415  			"20",
   416  			"--kubectl-parallelism-limit",
   417  			"10",
   418  			"--loglevel",
   419  			"info",
   420  			"--logformat",
   421  			f,
   422  		}
   423  	}
   424  
   425  	logLevelChangedResult := func(l string) []string {
   426  		return []string{
   427  			"argocd-application-controller",
   428  			"--operation-processors",
   429  			"10",
   430  			"--redis",
   431  			"argocd-redis.argocd.svc.cluster.local:6379",
   432  			"--repo-server",
   433  			"argocd-repo-server.argocd.svc.cluster.local:8081",
   434  			"--status-processors",
   435  			"20",
   436  			"--kubectl-parallelism-limit",
   437  			"10",
   438  			"--loglevel",
   439  			l,
   440  			"--logformat",
   441  			"text",
   442  		}
   443  	}
   444  
   445  	cmdTests := []struct {
   446  		name string
   447  		opts []argoCDOpt
   448  		want []string
   449  	}{
   450  		{
   451  			"defaults",
   452  			[]argoCDOpt{},
   453  			defaultResult,
   454  		},
   455  		{
   456  			"configured status processors",
   457  			[]argoCDOpt{controllerProcessors(30)},
   458  			controllerProcesorsChangedResult("30"),
   459  		},
   460  		{
   461  			"configured status processors to zero",
   462  			[]argoCDOpt{controllerProcessors(0)},
   463  			defaultResult,
   464  		},
   465  		{
   466  			"configured status processors to be between zero and default",
   467  			[]argoCDOpt{controllerProcessors(10)},
   468  			controllerProcesorsChangedResult("10"),
   469  		},
   470  		{
   471  			"configured operation processors",
   472  			[]argoCDOpt{operationProcessors(15)},
   473  			operationProcesorsChangedResult("15"),
   474  		},
   475  		{
   476  			"configured operation processors to zero",
   477  			[]argoCDOpt{operationProcessors(0)},
   478  			defaultResult,
   479  		},
   480  		{
   481  			"configured operation processors to be between zero and ten",
   482  			[]argoCDOpt{operationProcessors(5)},
   483  			operationProcesorsChangedResult("5"),
   484  		},
   485  		{
   486  			"configured parallelism limit",
   487  			[]argoCDOpt{parallelismLimit(30)},
   488  			parallelismLimitChangedResult("30"),
   489  		},
   490  		{
   491  			"configured parallelism limit to zero",
   492  			[]argoCDOpt{parallelismLimit(0)},
   493  			defaultResult,
   494  		},
   495  		{
   496  			"configured invalid logformat",
   497  			[]argoCDOpt{logFormat("arbitrary")},
   498  			defaultResult,
   499  		},
   500  		{
   501  			"configured json logformat",
   502  			[]argoCDOpt{logFormat("json")},
   503  			logFormatChangedResult("json"),
   504  		},
   505  		{
   506  			"configured text logformat",
   507  			[]argoCDOpt{logFormat("text")},
   508  			logFormatChangedResult("text"),
   509  		},
   510  		{
   511  			"configured invalid loglevel",
   512  			[]argoCDOpt{logLevel("arbitrary")},
   513  			defaultResult,
   514  		},
   515  		{
   516  			"configured debug loglevel",
   517  			[]argoCDOpt{logLevel("debug")},
   518  			logLevelChangedResult("debug"),
   519  		},
   520  		{
   521  			"configured info loglevel",
   522  			[]argoCDOpt{logLevel("info")},
   523  			logLevelChangedResult("info"),
   524  		},
   525  		{
   526  			"configured warn loglevel",
   527  			[]argoCDOpt{logLevel("warn")},
   528  			logLevelChangedResult("warn"),
   529  		},
   530  		{
   531  			"configured error loglevel",
   532  			[]argoCDOpt{logLevel("error")},
   533  			logLevelChangedResult("error"),
   534  		},
   535  	}
   536  
   537  	for _, tt := range cmdTests {
   538  		cr := makeTestArgoCD(tt.opts...)
   539  		cmd := getArgoApplicationControllerCommand(cr, false)
   540  
   541  		if !reflect.DeepEqual(cmd, tt.want) {
   542  			t.Fatalf("got %#v, want %#v", cmd, tt.want)
   543  		}
   544  	}
   545  }
   546  
   547  func TestGetArgoApplicationContainerEnv(t *testing.T) {
   548  
   549  	sync60s := []v1.EnvVar{
   550  		v1.EnvVar{Name: "HOME", Value: "/home/argocd", ValueFrom: (*v1.EnvVarSource)(nil)},
   551  		v1.EnvVar{Name: "ARGOCD_RECONCILIATION_TIMEOUT", Value: "60s", ValueFrom: (*v1.EnvVarSource)(nil)}}
   552  
   553  	cmdTests := []struct {
   554  		name string
   555  		opts []argoCDOpt
   556  		want []v1.EnvVar
   557  	}{
   558  		{
   559  			"configured apsync to 60s",
   560  			[]argoCDOpt{appSync(60)},
   561  			sync60s,
   562  		},
   563  	}
   564  
   565  	for _, tt := range cmdTests {
   566  		cr := makeTestArgoCD(tt.opts...)
   567  		env := getArgoControllerContainerEnv(cr)
   568  
   569  		if !reflect.DeepEqual(env, tt.want) {
   570  			t.Fatalf("got %#v, want %#v", env, tt.want)
   571  		}
   572  	}
   573  }
   574  
   575  func TestDeleteRBACsForNamespace(t *testing.T) {
   576  	a := makeTestArgoCD()
   577  	testClient := testclient.NewSimpleClientset()
   578  	testNameSpace := "testNameSpace"
   579  
   580  	role := newRole("xyz", policyRuleForApplicationController(), a)
   581  	role.Namespace = testNameSpace
   582  
   583  	// create role with label
   584  	_, err := testClient.RbacV1().Roles(testNameSpace).Create(context.TODO(), role, metav1.CreateOptions{})
   585  	assert.NoError(t, err)
   586  
   587  	role2 := newRole("abc", policyRuleForApplicationController(), a)
   588  	role2.Namespace = testNameSpace
   589  	role2.Labels = map[string]string{}
   590  
   591  	// create role without label
   592  	_, err = testClient.RbacV1().Roles(testNameSpace).Create(context.TODO(), role2, metav1.CreateOptions{})
   593  	assert.NoError(t, err)
   594  
   595  	roleBinding := newRoleBindingWithname("xyz", a)
   596  	roleBinding.Namespace = testNameSpace
   597  
   598  	// create roleBinding with label
   599  	_, err = testClient.RbacV1().RoleBindings(testNameSpace).Create(context.TODO(), roleBinding, metav1.CreateOptions{})
   600  	assert.NoError(t, err)
   601  
   602  	roleBinding2 := newRoleBindingWithname("abc", a)
   603  	roleBinding2.Namespace = testNameSpace
   604  	roleBinding2.Labels = map[string]string{}
   605  
   606  	// create RoleBinding without label
   607  	_, err = testClient.RbacV1().RoleBindings(testNameSpace).Create(context.TODO(), roleBinding2, metav1.CreateOptions{})
   608  	assert.NoError(t, err)
   609  
   610  	// run deleteRBACsForNamespace
   611  	assert.NoError(t, deleteRBACsForNamespace(testNameSpace, testClient))
   612  
   613  	// role with the label should be deleted
   614  	_, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), role.Name, metav1.GetOptions{})
   615  	//assert.ErrorContains(t, err, "not found")
   616  	//TODO: https://github.com/stretchr/testify/pull/1022 introduced ErrorContains, but is not yet available in a tagged release. Revert to ErrorContains once this becomes available
   617  	assert.Error(t, err)
   618  	assert.Contains(t, err.Error(), "not found")
   619  
   620  	// role without the label should still exists, no error
   621  	_, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), role2.Name, metav1.GetOptions{})
   622  	assert.NoError(t, err)
   623  
   624  	// roleBinding with the label should be deleted
   625  	_, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), roleBinding.Name, metav1.GetOptions{})
   626  	//assert.ErrorContains(t, err, "not found")
   627  	//TODO: https://github.com/stretchr/testify/pull/1022 introduced ErrorContains, but is not yet available in a tagged release. Revert to ErrorContains once this becomes available
   628  	assert.Error(t, err)
   629  	assert.Contains(t, err.Error(), "not found")
   630  
   631  	// roleBinding without the label should still exists, no error
   632  	_, err = testClient.RbacV1().Roles(testNameSpace).Get(context.TODO(), roleBinding2.Name, metav1.GetOptions{})
   633  	assert.NoError(t, err)
   634  
   635  }
   636  
   637  func TestRemoveManagedNamespaceFromClusterSecretAfterDeletion(t *testing.T) {
   638  	a := makeTestArgoCD()
   639  	testClient := testclient.NewSimpleClientset()
   640  	testNameSpace := "testNameSpace"
   641  
   642  	secret := argoutil.NewSecretWithSuffix(a, "xyz")
   643  	secret.Labels = map[string]string{common.ArgoCDSecretTypeLabel: "cluster"}
   644  	secret.Data = map[string][]byte{
   645  		"server":     []byte(common.ArgoCDDefaultServer),
   646  		"namespaces": []byte(strings.Join([]string{testNameSpace, "testNamespace2"}, ",")),
   647  	}
   648  
   649  	// create secret with the label
   650  	_, err := testClient.CoreV1().Secrets(a.Namespace).Create(context.TODO(), secret, metav1.CreateOptions{})
   651  	assert.NoError(t, err)
   652  
   653  	// run deleteManagedNamespaceFromClusterSecret
   654  	assert.NoError(t, deleteManagedNamespaceFromClusterSecret(a.Namespace, testNameSpace, testClient))
   655  
   656  	// secret should still exists with updated list of namespaces
   657  	s, err := testClient.CoreV1().Secrets(a.Namespace).Get(context.TODO(), secret.Name, metav1.GetOptions{})
   658  	assert.NoError(t, err)
   659  	assert.Equal(t, string(s.Data["namespaces"]), "testNamespace2")
   660  
   661  }
   662  
   663  func TestRemoveManagedByLabelFromNamespaces(t *testing.T) {
   664  	a := makeTestArgoCD()
   665  
   666  	resObjs := []client.Object{a}
   667  	subresObjs := []client.Object{a}
   668  	runtimeObjs := []runtime.Object{}
   669  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   670  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   671  	r := makeTestReconciler(cl, sch)
   672  
   673  	nsArgocd := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{
   674  		Name: a.Namespace,
   675  	}}
   676  	err := r.Client.Create(context.TODO(), nsArgocd)
   677  	assert.NoError(t, err)
   678  
   679  	ns := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{
   680  		Name: "testNamespace",
   681  		Labels: map[string]string{
   682  			common.ArgoCDManagedByLabel: a.Namespace,
   683  		}},
   684  	}
   685  
   686  	err = r.Client.Create(context.TODO(), ns)
   687  	assert.NoError(t, err)
   688  
   689  	ns2 := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{
   690  		Name: "testNamespace2",
   691  		Labels: map[string]string{
   692  			common.ArgoCDManagedByLabel: a.Namespace,
   693  		}},
   694  	}
   695  
   696  	err = r.Client.Create(context.TODO(), ns2)
   697  	assert.NoError(t, err)
   698  
   699  	ns3 := &v1.Namespace{ObjectMeta: metav1.ObjectMeta{
   700  		Name: "testNamespace3",
   701  		Labels: map[string]string{
   702  			common.ArgoCDManagedByLabel: "newNamespace",
   703  		}},
   704  	}
   705  
   706  	err = r.Client.Create(context.TODO(), ns3)
   707  	assert.NoError(t, err)
   708  
   709  	err = r.removeManagedByLabelFromNamespaces(a.Namespace)
   710  	assert.NoError(t, err)
   711  
   712  	nsList := &v1.NamespaceList{}
   713  	err = r.Client.List(context.TODO(), nsList)
   714  	assert.NoError(t, err)
   715  	for _, n := range nsList.Items {
   716  		if n.Name == ns3.Name {
   717  			_, ok := n.Labels[common.ArgoCDManagedByLabel]
   718  			assert.Equal(t, ok, true)
   719  			continue
   720  		}
   721  		_, ok := n.Labels[common.ArgoCDManagedByLabel]
   722  		assert.Equal(t, ok, false)
   723  	}
   724  }
   725  
   726  func TestSetManagedNamespaces(t *testing.T) {
   727  	a := makeTestArgoCD()
   728  
   729  	ns1 := v1.Namespace{
   730  		ObjectMeta: metav1.ObjectMeta{
   731  			Name: "test-namespace-1",
   732  			Labels: map[string]string{
   733  				common.ArgoCDManagedByLabel: testNamespace,
   734  			},
   735  		},
   736  	}
   737  
   738  	ns2 := v1.Namespace{
   739  		ObjectMeta: metav1.ObjectMeta{
   740  			Name: "test-namespace-2",
   741  			Labels: map[string]string{
   742  				common.ArgoCDManagedByLabel: testNamespace,
   743  			},
   744  		},
   745  	}
   746  
   747  	ns3 := v1.Namespace{
   748  		ObjectMeta: metav1.ObjectMeta{
   749  			Name: "test-namespace-3",
   750  			Labels: map[string]string{
   751  				common.ArgoCDManagedByLabel: "random-namespace",
   752  			},
   753  		},
   754  	}
   755  
   756  	ns4 := v1.Namespace{
   757  		ObjectMeta: metav1.ObjectMeta{
   758  			Name: "test-namespace-4",
   759  		},
   760  	}
   761  
   762  	resObjs := []client.Object{a, &ns1, &ns2, &ns3, &ns4}
   763  
   764  	subresObjs := []client.Object{a}
   765  	runtimeObjs := []runtime.Object{}
   766  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   767  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   768  	r := makeTestReconciler(cl, sch)
   769  
   770  	err := r.setManagedNamespaces(a)
   771  	assert.NoError(t, err)
   772  
   773  	assert.Equal(t, len(r.ManagedNamespaces.Items), 3)
   774  	for _, n := range r.ManagedNamespaces.Items {
   775  		if n.Labels[common.ArgoCDManagedByLabel] != testNamespace && n.Name != testNamespace {
   776  			t.Errorf("Expected namespace %s to be managed by Argo CD instance %s", n.Name, testNamespace)
   777  		}
   778  	}
   779  }
   780  
   781  func TestSetManagedSourceNamespaces(t *testing.T) {
   782  	a := makeTestArgoCD()
   783  	a.Spec = argoproj.ArgoCDSpec{
   784  		SourceNamespaces: []string{
   785  			"test-namespace-1",
   786  		},
   787  	}
   788  	ns1 := v1.Namespace{
   789  		ObjectMeta: metav1.ObjectMeta{
   790  			Name: "test-namespace-1",
   791  			Labels: map[string]string{
   792  				common.ArgoCDManagedByClusterArgoCDLabel: testNamespace,
   793  			},
   794  		},
   795  	}
   796  
   797  	resObjs := []client.Object{a, &ns1}
   798  	subresObjs := []client.Object{a}
   799  	runtimeObjs := []runtime.Object{}
   800  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   801  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   802  	r := makeTestReconciler(cl, sch)
   803  
   804  	err := r.setManagedSourceNamespaces(a)
   805  	assert.NoError(t, err)
   806  
   807  	assert.Equal(t, 1, len(r.ManagedSourceNamespaces))
   808  	assert.Contains(t, r.ManagedSourceNamespaces, "test-namespace-1")
   809  }
   810  
   811  func TestGetSourceNamespacesWithWildcardPatternNamespace(t *testing.T) {
   812  	a := makeTestArgoCD()
   813  	a.Spec = argoproj.ArgoCDSpec{
   814  		SourceNamespaces: []string{
   815  			"test*",
   816  		},
   817  	}
   818  	ns1 := v1.Namespace{
   819  		ObjectMeta: metav1.ObjectMeta{
   820  			Name: "test-namespace-1",
   821  		},
   822  	}
   823  
   824  	ns2 := v1.Namespace{
   825  		ObjectMeta: metav1.ObjectMeta{
   826  			Name: "test-namespace-2",
   827  		},
   828  	}
   829  	ns3 := v1.Namespace{
   830  		ObjectMeta: metav1.ObjectMeta{
   831  			Name: "other-namespace",
   832  		},
   833  	}
   834  
   835  	resObjs := []client.Object{a, &ns1, &ns2, &ns3}
   836  	subresObjs := []client.Object{a}
   837  	runtimeObjs := []runtime.Object{}
   838  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   839  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   840  	r := makeTestReconciler(cl, sch)
   841  
   842  	sourceNamespaces, err := r.getSourceNamespaces(a)
   843  	assert.NoError(t, err)
   844  	assert.Equal(t, 2, len(sourceNamespaces))
   845  	assert.Contains(t, sourceNamespaces, "test-namespace-1")
   846  	assert.Contains(t, sourceNamespaces, "test-namespace-2")
   847  	assert.NotContains(t, sourceNamespaces, "other-namespace")
   848  }
   849  
   850  func TestGetSourceNamespacesWithSpecificNamespace(t *testing.T) {
   851  	a := makeTestArgoCD()
   852  	a.Spec = argoproj.ArgoCDSpec{
   853  		SourceNamespaces: []string{
   854  			"test",
   855  		},
   856  	}
   857  	ns1 := v1.Namespace{
   858  		ObjectMeta: metav1.ObjectMeta{
   859  			Name: "test",
   860  		},
   861  	}
   862  	ns2 := v1.Namespace{
   863  		ObjectMeta: metav1.ObjectMeta{
   864  			Name: "test-namespace-1",
   865  		},
   866  	}
   867  	ns3 := v1.Namespace{
   868  		ObjectMeta: metav1.ObjectMeta{
   869  			Name: "other-namespace",
   870  		},
   871  	}
   872  
   873  	resObjs := []client.Object{a, &ns1, &ns2, &ns3}
   874  	subresObjs := []client.Object{a}
   875  	runtimeObjs := []runtime.Object{}
   876  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   877  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   878  	r := makeTestReconciler(cl, sch)
   879  
   880  	sourceNamespaces, err := r.getSourceNamespaces(a)
   881  	assert.NoError(t, err)
   882  	assert.Equal(t, 1, len(sourceNamespaces))
   883  	assert.Contains(t, sourceNamespaces, "test")
   884  	assert.NotContains(t, sourceNamespaces, "test-namespace-1")
   885  	assert.NotContains(t, sourceNamespaces, "other-namespace")
   886  }
   887  
   888  func TestGetSourceNamespacesWithMultipleSourceNamespaces(t *testing.T) {
   889  	a := makeTestArgoCD()
   890  	a.Spec = argoproj.ArgoCDSpec{
   891  		SourceNamespaces: []string{
   892  			"test*",
   893  			"dev*",
   894  		},
   895  	}
   896  	ns1 := v1.Namespace{
   897  		ObjectMeta: metav1.ObjectMeta{
   898  			Name: "test",
   899  		},
   900  	}
   901  	ns2 := v1.Namespace{
   902  		ObjectMeta: metav1.ObjectMeta{
   903  			Name: "test-namespace-1",
   904  		},
   905  	}
   906  	ns3 := v1.Namespace{
   907  		ObjectMeta: metav1.ObjectMeta{
   908  			Name: "dev-namespace-1",
   909  		},
   910  	}
   911  	ns4 := v1.Namespace{
   912  		ObjectMeta: metav1.ObjectMeta{
   913  			Name: "other-namespace",
   914  		},
   915  	}
   916  
   917  	resObjs := []client.Object{a, &ns1, &ns2, &ns3, &ns4}
   918  	subresObjs := []client.Object{a}
   919  	runtimeObjs := []runtime.Object{}
   920  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   921  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   922  	r := makeTestReconciler(cl, sch)
   923  
   924  	sourceNamespaces, err := r.getSourceNamespaces(a)
   925  	assert.NoError(t, err)
   926  	assert.Equal(t, 3, len(sourceNamespaces))
   927  	assert.Contains(t, sourceNamespaces, "test")
   928  	assert.Contains(t, sourceNamespaces, "test-namespace-1")
   929  	assert.Contains(t, sourceNamespaces, "dev-namespace-1")
   930  	assert.NotContains(t, sourceNamespaces, "other-namespace")
   931  }
   932  
   933  func TestGetSourceNamespacesWithWildCardNamespace(t *testing.T) {
   934  	a := makeTestArgoCD()
   935  	a.Spec = argoproj.ArgoCDSpec{
   936  		SourceNamespaces: []string{
   937  			"*",
   938  		},
   939  	}
   940  	ns1 := v1.Namespace{
   941  		ObjectMeta: metav1.ObjectMeta{
   942  			Name: "test-namespace-1",
   943  		},
   944  	}
   945  	ns2 := v1.Namespace{
   946  		ObjectMeta: metav1.ObjectMeta{
   947  			Name: "test-namespace-2",
   948  		},
   949  	}
   950  	ns3 := v1.Namespace{
   951  		ObjectMeta: metav1.ObjectMeta{
   952  			Name: "other-namespace",
   953  		},
   954  	}
   955  
   956  	resObjs := []client.Object{a, &ns1, &ns2, &ns3}
   957  	subresObjs := []client.Object{a}
   958  	runtimeObjs := []runtime.Object{}
   959  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   960  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   961  	r := makeTestReconciler(cl, sch)
   962  
   963  	sourceNamespaces, err := r.getSourceNamespaces(a)
   964  	assert.NoError(t, err)
   965  	assert.Equal(t, 3, len(sourceNamespaces))
   966  	assert.Contains(t, sourceNamespaces, "other-namespace")
   967  	assert.Contains(t, sourceNamespaces, "test-namespace-1")
   968  	assert.Contains(t, sourceNamespaces, "test-namespace-2")
   969  }
   970  
   971  func TestGenerateRandomString(t *testing.T) {
   972  
   973  	// verify the creation of unique strings
   974  	s1 := generateRandomString(20)
   975  	s2 := generateRandomString(20)
   976  	assert.NotEqual(t, s1, s2)
   977  
   978  	// verify length
   979  	a, _ := b64.URLEncoding.DecodeString(s1)
   980  	assert.Len(t, a, 20)
   981  
   982  	b, _ := b64.URLEncoding.DecodeString(s2)
   983  	assert.Len(t, b, 20)
   984  }
   985  
   986  func generateEncodedPEM(t *testing.T, host string) []byte {
   987  	key, err := argoutil.NewPrivateKey()
   988  	assert.NoError(t, err)
   989  
   990  	cert, err := argoutil.NewSelfSignedCACertificate("foo", key)
   991  	assert.NoError(t, err)
   992  
   993  	encoded := argoutil.EncodeCertificatePEM(cert)
   994  	return encoded
   995  }
   996  
   997  // TestReconcileArgoCD_reconcileDexOAuthClientSecret This test make sures that if dex is enabled a service account is created with token stored in a secret which is used for oauth
   998  func TestReconcileArgoCD_reconcileDexOAuthClientSecret(t *testing.T) {
   999  	logf.SetLogger(ZapLogger(true))
  1000  	a := makeTestArgoCD(func(ac *argoproj.ArgoCD) {
  1001  		ac.Spec.SSO = &argoproj.ArgoCDSSOSpec{
  1002  			Provider: argoproj.SSOProviderTypeDex,
  1003  			Dex: &argoproj.ArgoCDDexSpec{
  1004  				OpenShiftOAuth: true,
  1005  			},
  1006  		}
  1007  	})
  1008  
  1009  	resObjs := []client.Object{a}
  1010  	subresObjs := []client.Object{a}
  1011  	runtimeObjs := []runtime.Object{}
  1012  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
  1013  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
  1014  	r := makeTestReconciler(cl, sch)
  1015  
  1016  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
  1017  	_, err := r.reconcileServiceAccount(common.ArgoCDDefaultDexServiceAccountName, a)
  1018  	assert.NoError(t, err)
  1019  	_, err = r.getDexOAuthClientSecret(a)
  1020  	assert.NoError(t, err)
  1021  	sa := newServiceAccountWithName(common.ArgoCDDefaultDexServiceAccountName, a)
  1022  	assert.NoError(t, argoutil.FetchObject(r.Client, a.Namespace, sa.Name, sa))
  1023  	tokenExists := false
  1024  	for _, saSecret := range sa.Secrets {
  1025  		if strings.Contains(saSecret.Name, "dex-server-token") {
  1026  			tokenExists = true
  1027  		}
  1028  	}
  1029  	assert.True(t, tokenExists, "Dex is enabled but unable to create oauth client secret")
  1030  }