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

     1  package argocd
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  
     8  	"github.com/stretchr/testify/assert"
     9  	corev1 "k8s.io/api/core/v1"
    10  	rbacv1 "k8s.io/api/rbac/v1"
    11  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    12  	"k8s.io/apimachinery/pkg/runtime"
    13  	"k8s.io/apimachinery/pkg/types"
    14  	"sigs.k8s.io/controller-runtime/pkg/client"
    15  	logf "sigs.k8s.io/controller-runtime/pkg/log"
    16  
    17  	argoproj "github.com/argoproj-labs/argocd-operator/api/v1beta1"
    18  	"github.com/argoproj-labs/argocd-operator/common"
    19  )
    20  
    21  func TestReconcileArgoCD_reconcileRoleBinding(t *testing.T) {
    22  	logf.SetLogger(ZapLogger(true))
    23  	a := makeTestArgoCD()
    24  
    25  	resObjs := []client.Object{a}
    26  	subresObjs := []client.Object{a}
    27  	runtimeObjs := []runtime.Object{}
    28  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
    29  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
    30  	r := makeTestReconciler(cl, sch)
    31  
    32  	p := policyRuleForApplicationController()
    33  
    34  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
    35  	assert.NoError(t, createNamespace(r, "newTestNamespace", a.Namespace))
    36  
    37  	workloadIdentifier := common.ArgoCDApplicationControllerComponent
    38  
    39  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a))
    40  
    41  	roleBinding := &rbacv1.RoleBinding{}
    42  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
    43  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding))
    44  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding))
    45  
    46  	// update role reference and subject of the rolebinding
    47  	roleBinding.RoleRef.Name = "not-xrb"
    48  	roleBinding.Subjects[0].Name = "not-xrb"
    49  	assert.NoError(t, r.Client.Update(context.TODO(), roleBinding))
    50  
    51  	// try reconciling it again and verify if the changes are overwritten
    52  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a))
    53  
    54  	roleBinding = &rbacv1.RoleBinding{}
    55  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding))
    56  }
    57  
    58  func TestReconcileArgoCD_reconcileRoleBinding_for_new_namespace(t *testing.T) {
    59  	logf.SetLogger(ZapLogger(true))
    60  	a := makeTestArgoCD()
    61  
    62  	resObjs := []client.Object{a}
    63  	subresObjs := []client.Object{a}
    64  	runtimeObjs := []runtime.Object{}
    65  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
    66  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
    67  	r := makeTestReconciler(cl, sch)
    68  
    69  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
    70  	assert.NoError(t, createNamespace(r, "newTestNamespace", a.Namespace))
    71  
    72  	// check no dexServer rolebinding is created for the new namespace with managed-by label
    73  	roleBinding := &rbacv1.RoleBinding{}
    74  	workloadIdentifier := common.ArgoCDDexServerComponent
    75  	expectedDexServerRules := policyRuleForDexServer()
    76  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
    77  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedDexServerRules, a))
    78  	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding))
    79  
    80  	// check no redisHa rolebinding is created for the new namespace with managed-by label
    81  	workloadIdentifier = common.ArgoCDRedisHAComponent
    82  	expectedRedisHaRules := policyRuleForRedisHa(r.Client)
    83  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRedisHaRules, a))
    84  	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding))
    85  
    86  	// check no redis rolebinding is created for the new namespace with managed-by label
    87  	workloadIdentifier = common.ArgoCDRedisComponent
    88  	expectedRedisRules := policyRuleForRedis(r.Client)
    89  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRedisRules, a))
    90  	assert.Error(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "newTestNamespace"}, roleBinding))
    91  }
    92  
    93  // This test validates the behavior of the operator reconciliation when a managed namespace is not properly terminated
    94  // or remains terminating may be because of some resources in the namespace not getting deleted.
    95  func TestReconcileRoleBinding_for_Managed_Teminating_Namespace(t *testing.T) {
    96  	a := makeTestArgoCD()
    97  
    98  	resObjs := []client.Object{a}
    99  	subresObjs := []client.Object{a}
   100  	runtimeObjs := []runtime.Object{}
   101  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   102  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   103  	r := makeTestReconciler(cl, sch)
   104  
   105  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   106  	assert.NoError(t, createNamespace(r, "managedNS", a.Namespace))
   107  
   108  	// Verify role bindings are created for the new namespace with managed-by label
   109  	roleBinding := &rbacv1.RoleBinding{}
   110  	workloadIdentifier := common.ArgoCDApplicationControllerComponent
   111  	expectedRules := policyRuleForApplicationController()
   112  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
   113  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, expectedRules, a))
   114  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS"}, roleBinding))
   115  
   116  	// Create a configmap with an invalid finalizer in the "managedNS".
   117  	configMap := &corev1.ConfigMap{
   118  		ObjectMeta: metav1.ObjectMeta{
   119  			Name:      "dummy",
   120  			Namespace: "managedNS",
   121  			Finalizers: []string{
   122  				"nonexistent.finalizer/dummy",
   123  			},
   124  		},
   125  	}
   126  	assert.NoError(t, r.Client.Create(
   127  		context.TODO(), configMap))
   128  
   129  	// Delete the newNamespaceTest ns.
   130  	// Verify that operator should not reconcile back to create the roleBindings in terminating ns.
   131  	newNS := &corev1.Namespace{}
   132  	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
   133  	r.Client.Delete(context.TODO(), newNS)
   134  
   135  	// Verify that the namespace exists and is in terminating state.
   136  	r.Client.Get(context.TODO(), types.NamespacedName{Namespace: "managedNS", Name: "managedNS"}, newNS)
   137  	assert.NotEqual(t, newNS.DeletionTimestamp, nil)
   138  
   139  	err := r.reconcileRoleBinding(workloadIdentifier, expectedRules, a)
   140  	assert.NoError(t, err)
   141  
   142  	// Verify that the role bindings are deleted
   143  	err = r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding)
   144  	assert.ErrorContains(t, err, "not found")
   145  
   146  	// Create another managed namespace
   147  	assert.NoError(t, createNamespace(r, "managedNS2", a.Namespace))
   148  
   149  	// Check if roleBindings are created for the new namespace as well
   150  	err = r.reconcileRoleBinding(workloadIdentifier, expectedRules, a)
   151  	assert.NoError(t, err)
   152  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: "managedNS2"}, roleBinding))
   153  }
   154  
   155  func TestReconcileArgoCD_reconcileClusterRoleBinding(t *testing.T) {
   156  	logf.SetLogger(ZapLogger(true))
   157  	a := makeTestArgoCD()
   158  
   159  	resObjs := []client.Object{a}
   160  	subresObjs := []client.Object{a}
   161  	runtimeObjs := []runtime.Object{}
   162  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   163  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   164  	r := makeTestReconciler(cl, sch)
   165  
   166  	workloadIdentifier := "x"
   167  	expectedClusterRole := &rbacv1.ClusterRole{ObjectMeta: metav1.ObjectMeta{Name: workloadIdentifier}}
   168  
   169  	assert.NoError(t, r.reconcileClusterRoleBinding(workloadIdentifier, expectedClusterRole, a))
   170  
   171  	clusterRoleBinding := &rbacv1.ClusterRoleBinding{}
   172  	expectedName := fmt.Sprintf("%s-%s-%s", a.Name, a.Namespace, workloadIdentifier)
   173  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName}, clusterRoleBinding))
   174  
   175  	// update role reference and subject of the clusterrolebinding
   176  	clusterRoleBinding.RoleRef.Name = "not-x"
   177  	clusterRoleBinding.Subjects[0].Name = "not-x"
   178  	assert.NoError(t, r.Client.Update(context.TODO(), clusterRoleBinding))
   179  
   180  	// try reconciling it again and verify if the changes are overwritten
   181  	assert.NoError(t, r.reconcileClusterRoleBinding(workloadIdentifier, expectedClusterRole, a))
   182  
   183  	clusterRoleBinding = &rbacv1.ClusterRoleBinding{}
   184  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName}, clusterRoleBinding))
   185  }
   186  
   187  func TestReconcileArgoCD_reconcileRoleBinding_custom_role(t *testing.T) {
   188  	logf.SetLogger(ZapLogger(true))
   189  	a := makeTestArgoCD()
   190  
   191  	resObjs := []client.Object{a}
   192  	subresObjs := []client.Object{a}
   193  	runtimeObjs := []runtime.Object{}
   194  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   195  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   196  	r := makeTestReconciler(cl, sch)
   197  
   198  	p := policyRuleForApplicationController()
   199  
   200  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   201  
   202  	workloadIdentifier := "argocd-application-controller"
   203  	expectedName := fmt.Sprintf("%s-%s", a.Name, workloadIdentifier)
   204  
   205  	namespaceWithCustomRole := "namespace-with-custom-role"
   206  	assert.NoError(t, createNamespace(r, namespaceWithCustomRole, a.Namespace))
   207  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a))
   208  
   209  	// check if the default rolebindings are created
   210  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, &rbacv1.RoleBinding{}))
   211  
   212  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: namespaceWithCustomRole}, &rbacv1.RoleBinding{}))
   213  
   214  	checkForUpdatedRoleRef := func(t *testing.T, roleName, expectedName string) {
   215  		t.Helper()
   216  		expectedRoleRef := rbacv1.RoleRef{
   217  			APIGroup: rbacv1.GroupName,
   218  			Kind:     "ClusterRole",
   219  			Name:     roleName,
   220  		}
   221  		roleBinding := &rbacv1.RoleBinding{}
   222  		assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: a.Namespace}, roleBinding))
   223  		assert.Equal(t, roleBinding.RoleRef, expectedRoleRef)
   224  
   225  		assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: namespaceWithCustomRole}, roleBinding))
   226  		assert.Equal(t, roleBinding.RoleRef, expectedRoleRef)
   227  	}
   228  
   229  	t.Setenv(common.ArgoCDControllerClusterRoleEnvName, "custom-controller-role")
   230  	assert.NoError(t, r.reconcileRoleBinding(common.ArgoCDApplicationControllerComponent, p, a))
   231  
   232  	expectedName = fmt.Sprintf("%s-%s", a.Name, "argocd-application-controller")
   233  	checkForUpdatedRoleRef(t, "custom-controller-role", expectedName)
   234  
   235  	t.Setenv(common.ArgoCDServerClusterRoleEnvName, "custom-server-role")
   236  	assert.NoError(t, r.reconcileRoleBinding("argocd-server", p, a))
   237  
   238  	expectedName = fmt.Sprintf("%s-%s", a.Name, "argocd-server")
   239  	checkForUpdatedRoleRef(t, "custom-server-role", expectedName)
   240  }
   241  
   242  func TestReconcileArgoCD_reconcileRoleBinding_forSourceNamespaces(t *testing.T) {
   243  	logf.SetLogger(ZapLogger(true))
   244  	sourceNamespace := "newNamespaceTest"
   245  	a := makeTestArgoCD()
   246  	a.Spec = argoproj.ArgoCDSpec{
   247  		SourceNamespaces: []string{
   248  			sourceNamespace,
   249  		},
   250  	}
   251  
   252  	resObjs := []client.Object{a}
   253  	subresObjs := []client.Object{a}
   254  	runtimeObjs := []runtime.Object{}
   255  	sch := makeTestReconcilerScheme(argoproj.AddToScheme)
   256  	cl := makeTestReconcilerClient(sch, resObjs, subresObjs, runtimeObjs)
   257  	r := makeTestReconciler(cl, sch)
   258  
   259  	p := policyRuleForApplicationController()
   260  
   261  	assert.NoError(t, createNamespace(r, a.Namespace, ""))
   262  	assert.NoError(t, createNamespaceManagedByClusterArgoCDLabel(r, sourceNamespace, a.Namespace))
   263  
   264  	workloadIdentifier := common.ArgoCDServerComponent
   265  
   266  	assert.NoError(t, r.reconcileRoleBinding(workloadIdentifier, p, a))
   267  
   268  	roleBinding := &rbacv1.RoleBinding{}
   269  	expectedName := getRoleBindingNameForSourceNamespaces(a.Name, sourceNamespace)
   270  
   271  	assert.NoError(t, r.Client.Get(context.TODO(), types.NamespacedName{Name: expectedName, Namespace: sourceNamespace}, roleBinding))
   272  
   273  }