github.com/argoproj/argo-cd/v3@v3.2.1/test/e2e/sync_with_impersonate_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/argoproj/argo-cd/v3/test/e2e/fixture/project"
     9  
    10  	"github.com/stretchr/testify/require"
    11  	rbacv1 "k8s.io/api/rbac/v1"
    12  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/types"
    14  
    15  	"github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
    16  	"github.com/argoproj/argo-cd/v3/test/e2e/fixture"
    17  	. "github.com/argoproj/argo-cd/v3/test/e2e/fixture/app"
    18  )
    19  
    20  const (
    21  	WaitDuration    = time.Second
    22  	TimeoutDuration = time.Second * 3
    23  )
    24  
    25  func TestSyncWithFeatureDisabled(t *testing.T) {
    26  	Given(t).
    27  		Path("guestbook").
    28  		When().
    29  		WithImpersonationDisabled().
    30  		CreateFromFile(func(app *v1alpha1.Application) {
    31  			app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
    32  		}).
    33  		Then().
    34  		// With the impersonation feature disabled, Application sync should continue to use
    35  		// the control plane service account for the sync operation and the sync should succeed.
    36  		ExpectConsistently(SyncStatusIs(v1alpha1.SyncStatusCodeSynced), WaitDuration, TimeoutDuration).
    37  		Expect(OperationMessageContains("successfully synced"))
    38  }
    39  
    40  func TestSyncWithNoDestinationServiceAccountsInProject(t *testing.T) {
    41  	Given(t).
    42  		Path("guestbook").
    43  		When().
    44  		CreateFromFile(func(app *v1alpha1.Application) {
    45  			app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
    46  		}).
    47  		WithImpersonationEnabled("", nil).
    48  		Then().
    49  		// With the impersonation feature enabled, Application sync must fail
    50  		// when there are no destination service accounts configured in AppProject
    51  		ExpectConsistently(SyncStatusIs(v1alpha1.SyncStatusCodeOutOfSync), WaitDuration, TimeoutDuration).
    52  		Expect(OperationMessageContains("failed to find a matching service account to impersonate"))
    53  }
    54  
    55  func TestSyncWithImpersonateWithSyncServiceAccount(t *testing.T) {
    56  	projectName := "sync-test-project"
    57  	serviceAccountName := "test-account"
    58  
    59  	projectCtx := project.Given(t)
    60  	appCtx := Given(t)
    61  
    62  	projectCtx.
    63  		Name(projectName).
    64  		SourceNamespaces([]string{"*"}).
    65  		SourceRepositories([]string{"*"}).
    66  		Destination("*,*").
    67  		DestinationServiceAccounts(
    68  			[]string{
    69  				fmt.Sprintf("%s,%s,%s", "*", fixture.DeploymentNamespace(), serviceAccountName),
    70  				fmt.Sprintf("%s,%s,%s", v1alpha1.KubernetesInternalAPIServerAddr, fixture.DeploymentNamespace(), "missing-serviceAccount"),
    71  			}).
    72  		When().
    73  		Create().
    74  		Then().
    75  		Expect()
    76  
    77  	appCtx.
    78  		SetTrackingMethod("annotation").
    79  		Path("guestbook").
    80  		When().
    81  		WithImpersonationEnabled(serviceAccountName, []rbacv1.PolicyRule{
    82  			{
    83  				APIGroups: []string{"apps", ""},
    84  				Resources: []string{"deployments"},
    85  				Verbs:     []string{"*"},
    86  			},
    87  			{
    88  				APIGroups: []string{""},
    89  				Resources: []string{"services"},
    90  				Verbs:     []string{"*"},
    91  			},
    92  		}).
    93  		CreateFromFile(func(app *v1alpha1.Application) {
    94  			app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
    95  			app.Spec.Project = projectName
    96  		}).
    97  		Then().
    98  		// With the impersonation feature enabled, Application sync should succeed
    99  		// as there is a valid match found in the available destination service accounts configured in AppProject
   100  		ExpectConsistently(SyncStatusIs(v1alpha1.SyncStatusCodeSynced), WaitDuration, TimeoutDuration).
   101  		Expect(OperationMessageContains("successfully synced"))
   102  }
   103  
   104  func TestSyncWithMissingServiceAccount(t *testing.T) {
   105  	projectName := "false-test-project"
   106  	serviceAccountName := "test-account"
   107  
   108  	projectCtx := project.Given(t)
   109  	appCtx := Given(t)
   110  
   111  	projectCtx.
   112  		Name(projectName).
   113  		SourceNamespaces([]string{"*"}).
   114  		SourceRepositories([]string{"*"}).
   115  		Destination("*,*").
   116  		DestinationServiceAccounts(
   117  			[]string{
   118  				fmt.Sprintf("%s,%s,%s", v1alpha1.KubernetesInternalAPIServerAddr, fixture.DeploymentNamespace(), "missing-serviceAccount"),
   119  				fmt.Sprintf("%s,%s,%s", "*", fixture.DeploymentNamespace(), serviceAccountName),
   120  			}).
   121  		When().
   122  		Create().
   123  		Then().
   124  		Expect()
   125  
   126  	appCtx.
   127  		SetTrackingMethod("annotation").
   128  		Path("guestbook").
   129  		When().
   130  		WithImpersonationEnabled(serviceAccountName, []rbacv1.PolicyRule{
   131  			{
   132  				APIGroups: []string{"apps", ""},
   133  				Resources: []string{"deployments"},
   134  				Verbs:     []string{"*"},
   135  			},
   136  			{
   137  				APIGroups: []string{""},
   138  				Resources: []string{"services"},
   139  				Verbs:     []string{"*"},
   140  			},
   141  		}).
   142  		CreateFromFile(func(app *v1alpha1.Application) {
   143  			app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
   144  			app.Spec.Project = projectName
   145  		}).
   146  		Then().
   147  		// With the impersonation feature enabled, Application sync must fail
   148  		// when there is a valid match found in the available destination service accounts configured in AppProject,
   149  		// but the matching service account is missing.
   150  		ExpectConsistently(SyncStatusIs(v1alpha1.SyncStatusCodeOutOfSync), WaitDuration, TimeoutDuration).
   151  		Expect(OperationMessageContains("one or more objects failed to apply"))
   152  }
   153  
   154  func TestSyncWithValidSAButDisallowedDestination(t *testing.T) {
   155  	projectName := "negation-test-project"
   156  	serviceAccountName := "test-account"
   157  
   158  	projectCtx := project.Given(t)
   159  	appCtx := Given(t)
   160  
   161  	projectCtx.
   162  		Name(projectName).
   163  		SourceNamespaces([]string{"*"}).
   164  		SourceRepositories([]string{"*"}).
   165  		Destination("*,*").
   166  		DestinationServiceAccounts(
   167  			[]string{
   168  				fmt.Sprintf("%s,%s,%s", "*", fixture.DeploymentNamespace(), serviceAccountName),
   169  			}).
   170  		When().
   171  		Create().
   172  		Then().
   173  		Expect()
   174  
   175  	appCtx.
   176  		SetTrackingMethod("annotation").
   177  		Path("guestbook").
   178  		When().
   179  		WithImpersonationEnabled(serviceAccountName, []rbacv1.PolicyRule{
   180  			{
   181  				APIGroups: []string{"apps", ""},
   182  				Resources: []string{"deployments"},
   183  				Verbs:     []string{"*"},
   184  			},
   185  			{
   186  				APIGroups: []string{""},
   187  				Resources: []string{"services"},
   188  				Verbs:     []string{"*"},
   189  			},
   190  		}).
   191  		CreateFromFile(func(app *v1alpha1.Application) {
   192  			app.Spec.SyncPolicy = &v1alpha1.SyncPolicy{Automated: &v1alpha1.SyncPolicyAutomated{}}
   193  			app.Spec.Project = projectName
   194  		}).
   195  		Then().
   196  		Expect(SyncStatusIs(v1alpha1.SyncStatusCodeSynced)).
   197  		When().
   198  		And(func() {
   199  			// Patch destination to disallow target destination namespace
   200  			patch := []byte(fmt.Sprintf(`{"spec": {"destinations": [{"namespace": %q}]}}`, "!"+fixture.DeploymentNamespace()))
   201  
   202  			_, err := fixture.AppClientset.ArgoprojV1alpha1().AppProjects(fixture.TestNamespace()).Patch(t.Context(), projectName, types.MergePatchType, patch, metav1.PatchOptions{})
   203  			require.NoError(t, err)
   204  		}).
   205  		Refresh(v1alpha1.RefreshTypeNormal).
   206  		Then().
   207  		// With the impersonation feature enabled, Application sync must fail
   208  		// as there is a valid match found in the available destination service accounts configured in AppProject
   209  		// but the destination namespace is now disallowed.
   210  		ExpectConsistently(SyncStatusIs(v1alpha1.SyncStatusCodeUnknown), WaitDuration, TimeoutDuration)
   211  }