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

     1  package argocd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	corev1 "k8s.io/api/core/v1"
    11  	v1 "k8s.io/api/rbac/v1"
    12  	"k8s.io/apimachinery/pkg/api/errors"
    13  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    14  	"k8s.io/apimachinery/pkg/runtime"
    15  	"k8s.io/apimachinery/pkg/types"
    16  	"sigs.k8s.io/controller-runtime/pkg/client"
    17  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    18  
    19  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    20  	"github.com/argoproj-labs/argocd-operator/common"
    21  )
    22  
    23  func TestReconcileArgoCD_reconcileRole(t *testing.T) {
    24  	logf.SetLogger(ZapLogger(true))
    25  	a := makeTestArgoCD()
    26  
    27  	resObjs := []client.Object{a}
    28  	subresObjs := []client.Object{a}
    29  	runtimeObjs := []runtime.Object{}
    30  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
    31  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
    32  	r := makeTestReconciler(cl, sch)
    33  
    34  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
    35  	assert.NoError(t, createNamespace(r, "newNamespaceTest", a.Namespace))
    36  
    37  	workloadIdentifier := common.ArgoCDApplicationControllerComponent
    38  	expectedRules := policyRuleForApplicationController()
    39  	_, err := r.reconcileRole(workloadIdentifier, expectedRules, a)
    40  	assert.NoError(t, err)
    41  
    42  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
    43  	reconciledRole := &v1.Role{}
    44  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole))
    45  	assert.Equal(t, expectedRules, reconciledRole.Rules)
    46  
    47  	// check if roles are created for the new namespace as well
    48  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newNamespaceTest"}, reconciledRole))
    49  	assert.Equal(t, expectedRules, reconciledRole.Rules)
    50  
    51  	// update reconciledRole policy rules to RedisHa policy rules
    52  	reconciledRole.Rules = policyRuleForRedisHa(r.Client)
    53  	assert.NoError(t, r.Client.Update(context.TODO(), reconciledRole))
    54  
    55  	// Check if the RedisHa policy rules are overwritten to Application Controller
    56  	// policy rules by the reconciler
    57  	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
    58  	assert.NoError(t, err)
    59  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole))
    60  	assert.Equal(t, expectedRules, reconciledRole.Rules)
    61  }
    62  func TestReconcileArgoCD_reconcileRole_for_new_namespace(t *testing.T) {
    63  	logf.SetLogger(ZapLogger(true))
    64  	a := makeTestArgoCD()
    65  
    66  	resObjs := []client.Object{a}
    67  	subresObjs := []client.Object{a}
    68  	runtimeObjs := []runtime.Object{}
    69  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
    70  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
    71  	r := makeTestReconciler(cl, sch)
    72  
    73  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
    74  	assert.NoError(t, createNamespace(r, "newNamespaceTest", a.Namespace))
    75  
    76  	// only 1 role for the Argo CD instance namespace will be created
    77  	expectedNumberOfRoles := 1
    78  	// check no dexServer role is created for the new namespace with managed-by label
    79  	workloadIdentifier := common.ArgoCDDexServerComponent
    80  	expectedRoleNamespace := a.Namespace
    81  	expectedDexServerRules := policyRuleForDexServer()
    82  	dexRoles, err := r.reconcileRole(workloadIdentifier, expectedDexServerRules, a)
    83  	assert.NoError(t, err)
    84  	assert.Equal(t, expectedNumberOfRoles, len(dexRoles))
    85  	assert.Equal(t, expectedRoleNamespace, dexRoles[0].ObjectMeta.Namespace)
    86  	// check no redisHa role is created for the new namespace with managed-by label
    87  	workloadIdentifier = common.ArgoCDRedisHAComponent
    88  	expectedRedisHaRules := policyRuleForRedisHa(r.Client)
    89  	redisHaRoles, err := r.reconcileRole(workloadIdentifier, expectedRedisHaRules, a)
    90  	assert.NoError(t, err)
    91  	assert.Equal(t, expectedNumberOfRoles, len(redisHaRoles))
    92  	assert.Equal(t, expectedRoleNamespace, redisHaRoles[0].ObjectMeta.Namespace)
    93  	// check no redis role is created for the new namespace with managed-by label
    94  	workloadIdentifier = common.ArgoCDRedisComponent
    95  	expectedRedisRules := policyRuleForRedis(r.Client)
    96  	redisRoles, err := r.reconcileRole(workloadIdentifier, expectedRedisRules, a)
    97  	assert.NoError(t, err)
    98  	assert.Equal(t, expectedNumberOfRoles, len(redisRoles))
    99  	assert.Equal(t, expectedRoleNamespace, redisRoles[0].ObjectMeta.Namespace)
   100  }
   101  
   102  func TestReconcileArgoCD_reconcileClusterRole(t *testing.T) {
   103  	logf.SetLogger(ZapLogger(true))
   104  	a := makeTestArgoCD()
   105  
   106  	resObjs := []client.Object{a}
   107  	subresObjs := []client.Object{a}
   108  	runtimeObjs := []runtime.Object{}
   109  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   110  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   111  	r := makeTestReconciler(cl, sch)
   112  
   113  	workloadIdentifier := common.ArgoCDApplicationControllerComponent
   114  	clusterRoleName := GenerateUniqueResourceName(workloadIdentifier, a)
   115  	expectedRules := policyRuleForApplicationController()
   116  	_, err := r.reconcileClusterRole(workloadIdentifier, expectedRules, a)
   117  	assert.NoError(t, err)
   118  
   119  	// cluster role should not be created
   120  	//assert.ErrorContains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{}), "not found")
   121  	//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
   122  	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{}))
   123  	assert.Contains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, &v1.ClusterRole{}).Error(), "not found")
   124  
   125  	t.Setenv("ARGOCD_CLUSTER_CONFIG_NAMESPACES", a.Namespace)
   126  	_, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a)
   127  	assert.NoError(t, err)
   128  
   129  	reconciledClusterRole := &v1.ClusterRole{}
   130  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole))
   131  	assert.Equal(t, expectedRules, reconciledClusterRole.Rules)
   132  
   133  	// update reconciledRole policy rules to RedisHa policy rules
   134  	reconciledClusterRole.Rules = policyRuleForRedisHa(r.Client)
   135  	assert.NoError(t, r.Client.Update(context.TODO(), reconciledClusterRole))
   136  
   137  	// Check if the RedisHa policy rules are overwritten to Application Controller
   138  	// policy rules for cluster role by the reconciler
   139  	_, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a)
   140  	assert.NoError(t, err)
   141  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole))
   142  	assert.Equal(t, expectedRules, reconciledClusterRole.Rules)
   143  
   144  	// Check if the CLuster Role gets deleted
   145  	os.Unsetenv("ARGOCD_CLUSTER_CONFIG_NAMESPACES")
   146  	_, err = r.reconcileClusterRole(workloadIdentifier, expectedRules, a)
   147  	assert.NoError(t, err)
   148  	//assert.ErrorContains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole), "not found")
   149  	//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
   150  	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole))
   151  	assert.Contains(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: clusterRoleName}, reconciledClusterRole).Error(), "not found")
   152  }
   153  
   154  func TestReconcileArgoCD_reconcileRoleForApplicationSourceNamespaces(t *testing.T) {
   155  	logf.SetLogger(ZapLogger(true))
   156  	sourceNamespace := "newNamespaceTest"
   157  	a := makeTestArgoCD()
   158  	a.Spec = argoproj.ArgoCDSpec{
   159  		SourceNamespaces: []string{
   160  			sourceNamespace,
   161  		},
   162  		ApplicationSet: &argoproj.ArgoCDApplicationSet{
   163  			SourceNamespaces: []string{"tmp"},
   164  		},
   165  	}
   166  
   167  	resObjs := []client.Object{a}
   168  	subresObjs := []client.Object{a}
   169  	runtimeObjs := []runtime.Object{}
   170  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   171  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   172  	r := makeTestReconciler(cl, sch)
   173  
   174  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   175  	assert.NoError(t, createNamespaceManagedByClusterArgoCDLabel(r, sourceNamespace, a.Namespace))
   176  
   177  	workloadIdentifier := common.ArgoCDServerComponent
   178  	expectedRules := policyRuleForServerApplicationSourceNamespaces()
   179  	err := r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a)
   180  	assert.NoError(t, err)
   181  
   182  	expectedName := getRoleNameForApplicationSourceNamespaces(sourceNamespace, a)
   183  	reconciledRole := &v1.Role{}
   184  
   185  	// check if roles are created for the new namespace
   186  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole))
   187  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   188  
   189  	// check if appset rules are added for server-role when new appset namespace is added
   190  	a.Spec = argoproj.ArgoCDSpec{
   191  		SourceNamespaces: []string{
   192  			sourceNamespace,
   193  		},
   194  		ApplicationSet: &argoproj.ArgoCDApplicationSet{
   195  			SourceNamespaces: []string{"tmp", sourceNamespace},
   196  		},
   197  	}
   198  	err = r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a)
   199  	assert.NoError(t, err)
   200  	reconciledRole = &v1.Role{}
   201  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole))
   202  	assert.Equal(t, append(expectedRules, policyRuleForServerApplicationSetSourceNamespaces()...), reconciledRole.Rules)
   203  
   204  	// check if appset rules are removed for server-role when appset namespace is removed from the list
   205  	a.Spec = argoproj.ArgoCDSpec{
   206  		SourceNamespaces: []string{
   207  			sourceNamespace,
   208  		},
   209  		ApplicationSet: &argoproj.ArgoCDApplicationSet{
   210  			SourceNamespaces: []string{"tmp"},
   211  		},
   212  	}
   213  	err = r.reconcileRoleForApplicationSourceNamespaces(workloadIdentifier, expectedRules, a)
   214  	assert.NoError(t, err)
   215  	reconciledRole = &v1.Role{}
   216  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, reconciledRole))
   217  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   218  }
   219  
   220  func TestReconcileArgoCD_RoleHooks(t *testing.T) {
   221  	defer resetHooks()()
   222  	a := makeTestArgoCD()
   223  
   224  	resObjs := []client.Object{a}
   225  	subresObjs := []client.Object{a}
   226  	runtimeObjs := []runtime.Object{}
   227  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   228  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   229  	r := makeTestReconciler(cl, sch)
   230  
   231  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   232  	Register(testRoleHook)
   233  
   234  	roles, err := r.reconcileRole(common.ArgoCDApplicationControllerComponent, []v1.PolicyRule{}, a)
   235  	role := roles[0]
   236  	assert.NoError(t, err)
   237  	assert.Equal(t, role.Rules, testRules())
   238  
   239  	roles, err = r.reconcileRole("test", []v1.PolicyRule{}, a)
   240  	role = roles[0]
   241  	assert.NoError(t, err)
   242  	assert.Equal(t, role.Rules, []v1.PolicyRule{})
   243  }
   244  
   245  func TestReconcileArgoCD_reconcileRole_custom_role(t *testing.T) {
   246  	logf.SetLogger(ZapLogger(true))
   247  	a := makeTestArgoCD()
   248  
   249  	resObjs := []client.Object{a}
   250  	subresObjs := []client.Object{a}
   251  	runtimeObjs := []runtime.Object{}
   252  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   253  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   254  	r := makeTestReconciler(cl, sch)
   255  
   256  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   257  	assert.NoError(t, createNamespace(r, "namespace-custom-role", a.Namespace))
   258  
   259  	workloadIdentifier := "argocd-application-controller"
   260  	expectedRules := policyRuleForApplicationController()
   261  	_, err := r.reconcileRole(workloadIdentifier, expectedRules, a)
   262  	assert.NoError(t, err)
   263  
   264  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
   265  	reconciledRole := &v1.Role{}
   266  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole))
   267  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   268  
   269  	// check if roles are created for the new namespace as well
   270  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "namespace-custom-role"}, reconciledRole))
   271  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   272  
   273  	// set the custom role as env variable
   274  	t.Setenv(common.ArgoCDControllerClusterRoleEnvName, "custom-role")
   275  
   276  	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
   277  	assert.NoError(t, err)
   278  
   279  	// check if the default cluster roles are removed
   280  	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole)
   281  	if err == nil || !errors.IsNotFound(err) {
   282  		t.Fatal(err)
   283  	}
   284  
   285  	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "namespace-custom-role"}, reconciledRole)
   286  	if err == nil || !errors.IsNotFound(err) {
   287  		t.Fatal(err)
   288  	}
   289  }
   290  
   291  // This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated
   292  // or remains terminating may be because of some resources in the namespace not getting deleted.
   293  func TestReconcileRoles_ManagedTerminatingNamespace(t *testing.T) {
   294  
   295  	a := makeTestArgoCD()
   296  
   297  	resObjs := []client.Object{a}
   298  	subresObjs := []client.Object{a}
   299  	runtimeObjs := []runtime.Object{}
   300  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   301  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   302  	r := makeTestReconciler(cl, sch)
   303  
   304  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   305  
   306  	// Create a managed namespace
   307  	assert.NoError(t, createNamespace(r, "managedNS", a.Namespace))
   308  
   309  	workloadIdentifier := common.ArgoCDApplicationControllerComponent
   310  	expectedRules := policyRuleForApplicationController()
   311  	_, err := r.reconcileRole(workloadIdentifier, expectedRules, a)
   312  	assert.NoError(t, err)
   313  
   314  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
   315  	reconciledRole := &v1.Role{}
   316  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, reconciledRole))
   317  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   318  
   319  	// Check if roles are created for the new namespace as well
   320  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, reconciledRole))
   321  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   322  
   323  	// Create a configmap with an invalid finalizer in the "managedNS".
   324  	configMap := &corev1.ConfigMap{
   325  		ObjectMeta: metav1.ObjectMeta{
   326  			Name:      "dummy",
   327  			Namespace: "managedNS",
   328  			Finalizers: []string{
   329  				"nonexistent.finalizer/dummy",
   330  			},
   331  		},
   332  	}
   333  	assert.NoError(t, r.Client.Create(
   334  		context.TODO(), configMap))
   335  
   336  	// Delete the newNamespaceTest ns.
   337  	// Verify that operator should not reconcile back to create the roles in terminating ns.
   338  	newNS := &corev1.Namespace{}
   339  	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
   340  	r.Client.Delete(context.TODO(), newNS)
   341  
   342  	// Verify that the namespace exists and is in terminating state.
   343  	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
   344  	assert.NotEqual(t, newNS.DeletionTimestamp, nil)
   345  
   346  	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
   347  	assert.NoError(t, err)
   348  
   349  	// Verify that the roles are deleted
   350  	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole)
   351  	assert.ErrorContains(t, err, "not found")
   352  
   353  	// Create another managed namespace
   354  	assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace))
   355  
   356  	// Check if roles are created for the new namespace as well
   357  	_, err = r.reconcileRole(workloadIdentifier, expectedRules, a)
   358  	assert.NoError(t, err)
   359  
   360  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, reconciledRole))
   361  	assert.Equal(t, expectedRules, reconciledRole.Rules)
   362  }