github.com/argoproj/argo-cd@v1.8.7/test/e2e/app_management_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"math/rand"
     7  	"os"
     8  	"path"
     9  	"reflect"
    10  	"regexp"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/argoproj/gitops-engine/pkg/diff"
    16  	"github.com/argoproj/gitops-engine/pkg/health"
    17  	. "github.com/argoproj/gitops-engine/pkg/sync/common"
    18  	"github.com/argoproj/gitops-engine/pkg/utils/kube"
    19  	"github.com/argoproj/pkg/errors"
    20  	log "github.com/sirupsen/logrus"
    21  	"github.com/stretchr/testify/assert"
    22  	"github.com/stretchr/testify/require"
    23  	v1 "k8s.io/api/core/v1"
    24  	networkingv1beta "k8s.io/api/networking/v1beta1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"k8s.io/apimachinery/pkg/runtime/schema"
    28  	"k8s.io/apimachinery/pkg/types"
    29  	"k8s.io/apimachinery/pkg/util/intstr"
    30  	"k8s.io/utils/pointer"
    31  
    32  	"github.com/argoproj/argo-cd/common"
    33  	applicationpkg "github.com/argoproj/argo-cd/pkg/apiclient/application"
    34  	repositorypkg "github.com/argoproj/argo-cd/pkg/apiclient/repository"
    35  	. "github.com/argoproj/argo-cd/pkg/apis/application/v1alpha1"
    36  	. "github.com/argoproj/argo-cd/test/e2e/fixture"
    37  	. "github.com/argoproj/argo-cd/test/e2e/fixture/app"
    38  	. "github.com/argoproj/argo-cd/util/argo"
    39  	. "github.com/argoproj/argo-cd/util/errors"
    40  	"github.com/argoproj/argo-cd/util/io"
    41  	"github.com/argoproj/argo-cd/util/settings"
    42  )
    43  
    44  const (
    45  	guestbookPath          = "guestbook"
    46  	guestbookPathLocal     = "./testdata/guestbook_local"
    47  	globalWithNoNameSpace  = "global-with-no-namesapce"
    48  	guestbookWithNamespace = "guestbook-with-namespace"
    49  )
    50  
    51  func TestSyncToUnsignedCommit(t *testing.T) {
    52  	Given(t).
    53  		Project("gpg").
    54  		Path(guestbookPath).
    55  		When().
    56  		IgnoreErrors().
    57  		Create().
    58  		Sync().
    59  		Then().
    60  		Expect(OperationPhaseIs(OperationError)).
    61  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
    62  		Expect(HealthIs(health.HealthStatusMissing))
    63  }
    64  
    65  func TestSyncToSignedCommitWithoutKnownKey(t *testing.T) {
    66  	Given(t).
    67  		Project("gpg").
    68  		Path(guestbookPath).
    69  		When().
    70  		AddSignedFile("test.yaml", "null").
    71  		IgnoreErrors().
    72  		Create().
    73  		Sync().
    74  		Then().
    75  		Expect(OperationPhaseIs(OperationError)).
    76  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
    77  		Expect(HealthIs(health.HealthStatusMissing))
    78  }
    79  
    80  func TestSyncToSignedCommitKeyWithKnownKey(t *testing.T) {
    81  	Given(t).
    82  		Project("gpg").
    83  		Path(guestbookPath).
    84  		GPGPublicKeyAdded().
    85  		Sleep(2).
    86  		When().
    87  		AddSignedFile("test.yaml", "null").
    88  		IgnoreErrors().
    89  		Create().
    90  		Sync().
    91  		Then().
    92  		Expect(OperationPhaseIs(OperationSucceeded)).
    93  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
    94  		Expect(HealthIs(health.HealthStatusHealthy))
    95  }
    96  
    97  func TestAppCreation(t *testing.T) {
    98  	ctx := Given(t)
    99  
   100  	ctx.
   101  		Path(guestbookPath).
   102  		When().
   103  		Create().
   104  		Then().
   105  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   106  		And(func(app *Application) {
   107  			assert.Equal(t, Name(), app.Name)
   108  			assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.Source.RepoURL)
   109  			assert.Equal(t, guestbookPath, app.Spec.Source.Path)
   110  			assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace)
   111  			assert.Equal(t, common.KubernetesInternalAPIServerAddr, app.Spec.Destination.Server)
   112  		}).
   113  		Expect(Event(EventReasonResourceCreated, "create")).
   114  		And(func(_ *Application) {
   115  			// app should be listed
   116  			output, err := RunCli("app", "list")
   117  			assert.NoError(t, err)
   118  			assert.Contains(t, output, Name())
   119  		}).
   120  		When().
   121  		// ensure that create is idempotent
   122  		Create().
   123  		Then().
   124  		Given().
   125  		Revision("master").
   126  		When().
   127  		// ensure that update replaces spec and merge labels and annotations
   128  		And(func() {
   129  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(context.Background(),
   130  				ctx.GetName(), types.MergePatchType, []byte(`{"metadata": {"labels": { "test": "label" }, "annotations": { "test": "annotation" }}}`), metav1.PatchOptions{}))
   131  		}).
   132  		Create("--upsert").
   133  		Then().
   134  		And(func(app *Application) {
   135  			assert.Equal(t, "label", app.Labels["test"])
   136  			assert.Equal(t, "annotation", app.Annotations["test"])
   137  			assert.Equal(t, "master", app.Spec.Source.TargetRevision)
   138  		})
   139  }
   140  
   141  // demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force"
   142  func TestImmutableChange(t *testing.T) {
   143  	text := FailOnErr(Run(".", "kubectl", "get", "service", "-n", "kube-system", "kube-dns", "-o", "jsonpath={.spec.clusterIP}")).(string)
   144  	parts := strings.Split(text, ".")
   145  	n := rand.Intn(254)
   146  	ip1 := fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n)
   147  	ip2 := fmt.Sprintf("%s.%s.%s.%d", parts[0], parts[1], parts[2], n+1)
   148  	Given(t).
   149  		Path("service").
   150  		When().
   151  		Create().
   152  		PatchFile("service.yaml", fmt.Sprintf(`[{"op": "add", "path": "/spec/clusterIP", "value": "%s"}]`, ip1)).
   153  		Sync().
   154  		Then().
   155  		Expect(OperationPhaseIs(OperationSucceeded)).
   156  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   157  		Expect(HealthIs(health.HealthStatusHealthy)).
   158  		When().
   159  		PatchFile("service.yaml", fmt.Sprintf(`[{"op": "add", "path": "/spec/clusterIP", "value": "%s"}]`, ip2)).
   160  		IgnoreErrors().
   161  		Sync().
   162  		DoNotIgnoreErrors().
   163  		Then().
   164  		Expect(OperationPhaseIs(OperationFailed)).
   165  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   166  		Expect(ResourceResultNumbering(1)).
   167  		Expect(ResourceResultIs(ResourceResult{
   168  			Kind:      "Service",
   169  			Version:   "v1",
   170  			Namespace: DeploymentNamespace(),
   171  			Name:      "my-service",
   172  			SyncPhase: "Sync",
   173  			Status:    "SyncFailed",
   174  			HookPhase: "Failed",
   175  			Message:   fmt.Sprintf(`Service "my-service" is invalid: spec.clusterIP: Invalid value: "%s": field is immutable`, ip2),
   176  		})).
   177  		// now we can do this will a force
   178  		Given().
   179  		Force().
   180  		When().
   181  		Sync().
   182  		Then().
   183  		Expect(OperationPhaseIs(OperationSucceeded)).
   184  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   185  		Expect(HealthIs(health.HealthStatusHealthy))
   186  }
   187  
   188  func TestInvalidAppProject(t *testing.T) {
   189  	Given(t).
   190  		Path(guestbookPath).
   191  		Project("does-not-exist").
   192  		When().
   193  		IgnoreErrors().
   194  		Create().
   195  		Then().
   196  		Expect(Error("", "application references project does-not-exist which does not exist"))
   197  }
   198  
   199  func TestAppDeletion(t *testing.T) {
   200  	Given(t).
   201  		Path(guestbookPath).
   202  		When().
   203  		Create().
   204  		Then().
   205  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   206  		When().
   207  		Delete(true).
   208  		Then().
   209  		Expect(DoesNotExist()).
   210  		Expect(Event(EventReasonResourceDeleted, "delete"))
   211  
   212  	output, err := RunCli("app", "list")
   213  	assert.NoError(t, err)
   214  	assert.NotContains(t, output, Name())
   215  }
   216  
   217  func TestAppLabels(t *testing.T) {
   218  	Given(t).
   219  		Path("config-map").
   220  		When().
   221  		Create("-l", "foo=bar").
   222  		Then().
   223  		And(func(app *Application) {
   224  			assert.Contains(t, FailOnErr(RunCli("app", "list")), Name())
   225  			assert.Contains(t, FailOnErr(RunCli("app", "list", "-l", "foo=bar")), Name())
   226  			assert.NotContains(t, FailOnErr(RunCli("app", "list", "-l", "foo=rubbish")), Name())
   227  		}).
   228  		Given().
   229  		// remove both name and replace labels means nothing will sync
   230  		Name("").
   231  		When().
   232  		IgnoreErrors().
   233  		Sync("-l", "foo=rubbish").
   234  		DoNotIgnoreErrors().
   235  		Then().
   236  		Expect(Error("", "no apps match selector foo=rubbish")).
   237  		// check we can update the app and it is then sync'd
   238  		Given().
   239  		When().
   240  		Sync("-l", "foo=bar")
   241  }
   242  
   243  func TestTrackAppStateAndSyncApp(t *testing.T) {
   244  	Given(t).
   245  		Path(guestbookPath).
   246  		When().
   247  		Create().
   248  		Sync().
   249  		Then().
   250  		Expect(OperationPhaseIs(OperationSucceeded)).
   251  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   252  		Expect(HealthIs(health.HealthStatusHealthy)).
   253  		Expect(Success(fmt.Sprintf("Service     %s  guestbook-ui  Synced ", DeploymentNamespace()))).
   254  		Expect(Success(fmt.Sprintf("apps   Deployment  %s  guestbook-ui  Synced", DeploymentNamespace()))).
   255  		Expect(Event(EventReasonResourceUpdated, "sync")).
   256  		And(func(app *Application) {
   257  			assert.NotNil(t, app.Status.OperationState.SyncResult)
   258  		})
   259  }
   260  
   261  func TestAppRollbackSuccessful(t *testing.T) {
   262  	Given(t).
   263  		Path(guestbookPath).
   264  		When().
   265  		Create().
   266  		Sync().
   267  		Then().
   268  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   269  		And(func(app *Application) {
   270  			assert.NotEmpty(t, app.Status.Sync.Revision)
   271  		}).
   272  		And(func(app *Application) {
   273  			appWithHistory := app.DeepCopy()
   274  			appWithHistory.Status.History = []RevisionHistory{{
   275  				ID:         1,
   276  				Revision:   app.Status.Sync.Revision,
   277  				DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-1 * time.Minute)},
   278  				Source:     app.Spec.Source,
   279  			}, {
   280  				ID:         2,
   281  				Revision:   "cdb",
   282  				DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-2 * time.Minute)},
   283  				Source:     app.Spec.Source,
   284  			}}
   285  			patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{})
   286  			assert.NoError(t, err)
   287  
   288  			app, err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
   289  			assert.NoError(t, err)
   290  
   291  			// sync app and make sure it reaches InSync state
   292  			_, err = RunCli("app", "rollback", app.Name, "1")
   293  			assert.NoError(t, err)
   294  
   295  		}).
   296  		Expect(Event(EventReasonOperationStarted, "rollback")).
   297  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   298  		And(func(app *Application) {
   299  			assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status)
   300  			assert.NotNil(t, app.Status.OperationState.SyncResult)
   301  			assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources))
   302  			assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase)
   303  			assert.Equal(t, 3, len(app.Status.History))
   304  		})
   305  }
   306  
   307  func TestComparisonFailsIfClusterNotAdded(t *testing.T) {
   308  	Given(t).
   309  		Path(guestbookPath).
   310  		DestServer("https://not-registered-cluster/api").
   311  		When().
   312  		IgnoreErrors().
   313  		Create().
   314  		Then().
   315  		Expect(DoesNotExist())
   316  }
   317  
   318  func TestCannotSetInvalidPath(t *testing.T) {
   319  	Given(t).
   320  		Path(guestbookPath).
   321  		When().
   322  		Create().
   323  		IgnoreErrors().
   324  		AppSet("--path", "garbage").
   325  		Then().
   326  		Expect(Error("", "app path does not exist"))
   327  }
   328  
   329  func TestManipulateApplicationResources(t *testing.T) {
   330  	Given(t).
   331  		Path(guestbookPath).
   332  		When().
   333  		Create().
   334  		Sync().
   335  		Then().
   336  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   337  		And(func(app *Application) {
   338  			manifests, err := RunCli("app", "manifests", app.Name, "--source", "live")
   339  			assert.NoError(t, err)
   340  			resources, err := kube.SplitYAML([]byte(manifests))
   341  			assert.NoError(t, err)
   342  
   343  			index := -1
   344  			for i := range resources {
   345  				if resources[i].GetKind() == kube.DeploymentKind {
   346  					index = i
   347  					break
   348  				}
   349  			}
   350  
   351  			assert.True(t, index > -1)
   352  
   353  			deployment := resources[index]
   354  
   355  			closer, client, err := ArgoCDClientset.NewApplicationClient()
   356  			assert.NoError(t, err)
   357  			defer io.Close(closer)
   358  
   359  			_, err = client.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{
   360  				Name:         &app.Name,
   361  				Group:        deployment.GroupVersionKind().Group,
   362  				Kind:         deployment.GroupVersionKind().Kind,
   363  				Version:      deployment.GroupVersionKind().Version,
   364  				Namespace:    deployment.GetNamespace(),
   365  				ResourceName: deployment.GetName(),
   366  			})
   367  			assert.NoError(t, err)
   368  		}).
   369  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync))
   370  }
   371  
   372  func assetSecretDataHidden(t *testing.T, manifest string) {
   373  	secret, err := UnmarshalToUnstructured(manifest)
   374  	assert.NoError(t, err)
   375  
   376  	_, hasStringData, err := unstructured.NestedMap(secret.Object, "stringData")
   377  	assert.NoError(t, err)
   378  	assert.False(t, hasStringData)
   379  
   380  	secretData, hasData, err := unstructured.NestedMap(secret.Object, "data")
   381  	assert.NoError(t, err)
   382  	assert.True(t, hasData)
   383  	for _, v := range secretData {
   384  		assert.Regexp(t, regexp.MustCompile(`[*]*`), v)
   385  	}
   386  	var lastAppliedConfigAnnotation string
   387  	annotations := secret.GetAnnotations()
   388  	if annotations != nil {
   389  		lastAppliedConfigAnnotation = annotations[v1.LastAppliedConfigAnnotation]
   390  	}
   391  	if lastAppliedConfigAnnotation != "" {
   392  		assetSecretDataHidden(t, lastAppliedConfigAnnotation)
   393  	}
   394  }
   395  
   396  func TestAppWithSecrets(t *testing.T) {
   397  	closer, client, err := ArgoCDClientset.NewApplicationClient()
   398  	assert.NoError(t, err)
   399  	defer io.Close(closer)
   400  
   401  	Given(t).
   402  		Path("secrets").
   403  		When().
   404  		Create().
   405  		Sync().
   406  		Then().
   407  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   408  		And(func(app *Application) {
   409  			res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
   410  				Namespace:    app.Spec.Destination.Namespace,
   411  				Kind:         kube.SecretKind,
   412  				Group:        "",
   413  				Name:         &app.Name,
   414  				Version:      "v1",
   415  				ResourceName: "test-secret",
   416  			})).(*applicationpkg.ApplicationResourceResponse)
   417  			assetSecretDataHidden(t, res.Manifest)
   418  
   419  			manifests, err := client.GetManifests(context.Background(), &applicationpkg.ApplicationManifestQuery{Name: &app.Name})
   420  			errors.CheckError(err)
   421  
   422  			for _, manifest := range manifests.Manifests {
   423  				assetSecretDataHidden(t, manifest)
   424  			}
   425  
   426  			diffOutput := FailOnErr(RunCli("app", "diff", app.Name)).(string)
   427  			assert.Empty(t, diffOutput)
   428  
   429  			// make sure resource update error does not print secret details
   430  			_, err = RunCli("app", "patch-resource", "test-app-with-secrets", "--resource-name", "test-secret",
   431  				"--kind", "Secret", "--patch", `{"op": "add", "path": "/data", "value": "hello"}'`,
   432  				"--patch-type", "application/json-patch+json")
   433  			require.Error(t, err)
   434  			assert.Contains(t, err.Error(), fmt.Sprintf("failed to patch Secret %s/test-secret", DeploymentNamespace()))
   435  			assert.NotContains(t, err.Error(), "username")
   436  			assert.NotContains(t, err.Error(), "password")
   437  
   438  			// patch secret and make sure app is out of sync and diff detects the change
   439  			FailOnErr(KubeClientset.CoreV1().Secrets(DeploymentNamespace()).Patch(context.Background(),
   440  				"test-secret", types.JSONPatchType, []byte(`[
   441  	{"op": "remove", "path": "/data/username"},
   442  	{"op": "add", "path": "/stringData", "value": {"password": "foo"}}
   443  ]`), metav1.PatchOptions{}))
   444  		}).
   445  		When().
   446  		Refresh(RefreshTypeNormal).
   447  		Then().
   448  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   449  		And(func(app *Application) {
   450  			diffOutput, err := RunCli("app", "diff", app.Name)
   451  			assert.Error(t, err)
   452  			assert.Contains(t, diffOutput, "username: ++++++++")
   453  			assert.Contains(t, diffOutput, "password: ++++++++++++")
   454  
   455  			// local diff should ignore secrets
   456  			diffOutput = FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/secrets")).(string)
   457  			assert.Empty(t, diffOutput)
   458  
   459  			// ignore missing field and make sure diff shows no difference
   460  			app.Spec.IgnoreDifferences = []ResourceIgnoreDifferences{{
   461  				Kind: kube.SecretKind, JSONPointers: []string{"/data"},
   462  			}}
   463  			FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, Spec: app.Spec}))
   464  		}).
   465  		When().
   466  		Refresh(RefreshTypeNormal).
   467  		Then().
   468  		Expect(OperationPhaseIs(OperationSucceeded)).
   469  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   470  		And(func(app *Application) {
   471  			diffOutput := FailOnErr(RunCli("app", "diff", app.Name)).(string)
   472  			assert.Empty(t, diffOutput)
   473  		}).
   474  		// verify not committed secret also ignore during diffing
   475  		When().
   476  		WriteFile("secret3.yaml", `
   477  apiVersion: v1
   478  kind: Secret
   479  metadata:
   480    name: test-secret3
   481  stringData:
   482    username: test-username`).
   483  		Then().
   484  		And(func(app *Application) {
   485  			diffOutput := FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/secrets")).(string)
   486  			assert.Empty(t, diffOutput)
   487  		})
   488  }
   489  
   490  func TestResourceDiffing(t *testing.T) {
   491  	Given(t).
   492  		Path(guestbookPath).
   493  		When().
   494  		Create().
   495  		Sync().
   496  		Then().
   497  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   498  		And(func(app *Application) {
   499  			// Patch deployment
   500  			_, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(context.Background(),
   501  				"guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`), metav1.PatchOptions{})
   502  			assert.NoError(t, err)
   503  		}).
   504  		When().
   505  		Refresh(RefreshTypeNormal).
   506  		Then().
   507  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   508  		And(func(app *Application) {
   509  			diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
   510  			assert.Error(t, err)
   511  			assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace()))
   512  		}).
   513  		Given().
   514  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {
   515  			IgnoreDifferences: OverrideIgnoreDiff{JSONPointers: []string{"/spec/template/spec/containers/0/image"}},
   516  		}}).
   517  		When().
   518  		Refresh(RefreshTypeNormal).
   519  		Then().
   520  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   521  		And(func(app *Application) {
   522  			diffOutput, err := RunCli("app", "diff", app.Name, "--local", "testdata/guestbook")
   523  			assert.NoError(t, err)
   524  			assert.Empty(t, diffOutput)
   525  		})
   526  }
   527  
   528  func TestCRDs(t *testing.T) {
   529  	testEdgeCasesApplicationResources(t, "crd-creation", health.HealthStatusHealthy)
   530  }
   531  
   532  func TestKnownTypesInCRDDiffing(t *testing.T) {
   533  	dummiesGVR := schema.GroupVersionResource{Group: "argoproj.io", Version: "v1alpha1", Resource: "dummies"}
   534  
   535  	Given(t).
   536  		Path("crd-creation").
   537  		When().Create().Sync().Then().
   538  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)).
   539  		When().
   540  		And(func() {
   541  			dummyResIf := DynamicClientset.Resource(dummiesGVR).Namespace(DeploymentNamespace())
   542  			patchData := []byte(`{"spec":{"requests": {"cpu": "2"}}}`)
   543  			FailOnErr(dummyResIf.Patch(context.Background(), "dummy-crd-instance", types.MergePatchType, patchData, metav1.PatchOptions{}))
   544  		}).Refresh(RefreshTypeNormal).
   545  		Then().
   546  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   547  		When().
   548  		And(func() {
   549  			SetResourceOverrides(map[string]ResourceOverride{
   550  				"argoproj.io/Dummy": {
   551  					KnownTypeFields: []KnownTypeField{{
   552  						Field: "spec.requests",
   553  						Type:  "core/v1/ResourceList",
   554  					}},
   555  				},
   556  			})
   557  		}).
   558  		Refresh(RefreshTypeNormal).
   559  		Then().
   560  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   561  }
   562  
   563  func TestDuplicatedResources(t *testing.T) {
   564  	testEdgeCasesApplicationResources(t, "duplicated-resources", health.HealthStatusHealthy)
   565  }
   566  
   567  func TestConfigMap(t *testing.T) {
   568  	testEdgeCasesApplicationResources(t, "config-map", health.HealthStatusHealthy, "my-map  Synced                configmap/my-map created")
   569  }
   570  
   571  func TestFailedConversion(t *testing.T) {
   572  	if os.Getenv("ARGOCD_E2E_K3S") == "true" {
   573  		t.SkipNow()
   574  	}
   575  	defer func() {
   576  		FailOnErr(Run("", "kubectl", "delete", "apiservice", "v1beta1.metrics.k8s.io"))
   577  	}()
   578  
   579  	testEdgeCasesApplicationResources(t, "failed-conversion", health.HealthStatusProgressing)
   580  }
   581  
   582  func testEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode health.HealthStatusCode, message ...string) {
   583  	expect := Given(t).
   584  		Path(appPath).
   585  		When().
   586  		Create().
   587  		Sync().
   588  		Then().
   589  		Expect(OperationPhaseIs(OperationSucceeded)).
   590  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   591  	for i := range message {
   592  		expect = expect.Expect(Success(message[i]))
   593  	}
   594  	expect.
   595  		Expect(HealthIs(statusCode)).
   596  		And(func(app *Application) {
   597  			diffOutput, err := RunCli("app", "diff", app.Name, "--local", path.Join("testdata", appPath))
   598  			assert.Empty(t, diffOutput)
   599  			assert.NoError(t, err)
   600  		})
   601  }
   602  
   603  func TestKsonnetApp(t *testing.T) {
   604  	Given(t).
   605  		Path("ksonnet").
   606  		Env("prod").
   607  		// Null out dest server to verify that destination is inferred from ksonnet app
   608  		Parameter("guestbook-ui=image=gcr.io/heptio-images/ks-guestbook-demo:0.1").
   609  		DestServer("").
   610  		When().
   611  		Create().
   612  		Sync().
   613  		Then().
   614  		And(func(app *Application) {
   615  			closer, client, err := ArgoCDClientset.NewRepoClient()
   616  			assert.NoError(t, err)
   617  			defer io.Close(closer)
   618  
   619  			details, err := client.GetAppDetails(context.Background(), &repositorypkg.RepoAppDetailsQuery{
   620  				Source: &app.Spec.Source,
   621  			})
   622  			assert.NoError(t, err)
   623  
   624  			serviceType := ""
   625  			for _, param := range details.Ksonnet.Parameters {
   626  				if param.Name == "type" && param.Component == "guestbook-ui" {
   627  					serviceType = param.Value
   628  				}
   629  			}
   630  			assert.Equal(t, serviceType, "LoadBalancer")
   631  		})
   632  }
   633  
   634  const actionsConfig = `discovery.lua: return { sample = {} }
   635  definitions:
   636  - name: sample
   637    action.lua: |
   638      obj.metadata.labels.sample = 'test'
   639      return obj`
   640  
   641  func TestResourceAction(t *testing.T) {
   642  	Given(t).
   643  		Path(guestbookPath).
   644  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}).
   645  		When().
   646  		Create().
   647  		Sync().
   648  		Then().
   649  		And(func(app *Application) {
   650  
   651  			closer, client, err := ArgoCDClientset.NewApplicationClient()
   652  			assert.NoError(t, err)
   653  			defer io.Close(closer)
   654  
   655  			actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{
   656  				Name:         &app.Name,
   657  				Group:        "apps",
   658  				Kind:         "Deployment",
   659  				Version:      "v1",
   660  				Namespace:    DeploymentNamespace(),
   661  				ResourceName: "guestbook-ui",
   662  			})
   663  			assert.NoError(t, err)
   664  			assert.Equal(t, []ResourceAction{{Name: "sample", Disabled: false}}, actions.Actions)
   665  
   666  			_, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name,
   667  				Group:        "apps",
   668  				Kind:         "Deployment",
   669  				Version:      "v1",
   670  				Namespace:    DeploymentNamespace(),
   671  				ResourceName: "guestbook-ui",
   672  				Action:       "sample",
   673  			})
   674  			assert.NoError(t, err)
   675  
   676  			deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
   677  			assert.NoError(t, err)
   678  
   679  			assert.Equal(t, "test", deployment.Labels["sample"])
   680  		})
   681  }
   682  
   683  func TestSyncResourceByLabel(t *testing.T) {
   684  	Given(t).
   685  		Path(guestbookPath).
   686  		When().
   687  		Create().
   688  		Sync().
   689  		Then().
   690  		And(func(app *Application) {
   691  			_, _ = RunCli("app", "sync", app.Name, "--label", fmt.Sprintf("app.kubernetes.io/instance=%s", app.Name))
   692  		}).
   693  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   694  		And(func(app *Application) {
   695  			_, err := RunCli("app", "sync", app.Name, "--label", "this-label=does-not-exist")
   696  			assert.Error(t, err)
   697  			assert.Contains(t, err.Error(), "level=fatal")
   698  		})
   699  }
   700  
   701  func TestLocalManifestSync(t *testing.T) {
   702  	Given(t).
   703  		Path(guestbookPath).
   704  		When().
   705  		Create().
   706  		Sync().
   707  		Then().
   708  		And(func(app *Application) {
   709  			res, _ := RunCli("app", "manifests", app.Name)
   710  			assert.Contains(t, res, "containerPort: 80")
   711  			assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
   712  		}).
   713  		Given().
   714  		LocalPath(guestbookPathLocal).
   715  		When().
   716  		Sync().
   717  		Then().
   718  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   719  		And(func(app *Application) {
   720  			res, _ := RunCli("app", "manifests", app.Name)
   721  			assert.Contains(t, res, "containerPort: 81")
   722  			assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.3")
   723  		}).
   724  		Given().
   725  		LocalPath("").
   726  		When().
   727  		Sync().
   728  		Then().
   729  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   730  		And(func(app *Application) {
   731  			res, _ := RunCli("app", "manifests", app.Name)
   732  			assert.Contains(t, res, "containerPort: 80")
   733  			assert.Contains(t, res, "image: gcr.io/heptio-images/ks-guestbook-demo:0.2")
   734  		})
   735  }
   736  
   737  func TestLocalSync(t *testing.T) {
   738  	Given(t).
   739  		// we've got to use Helm as this uses kubeVersion
   740  		Path("helm").
   741  		When().
   742  		Create().
   743  		Then().
   744  		And(func(app *Application) {
   745  			FailOnErr(RunCli("app", "sync", app.Name, "--local", "testdata/helm"))
   746  		})
   747  }
   748  
   749  func TestNoLocalSyncWithAutosyncEnabled(t *testing.T) {
   750  	Given(t).
   751  		Path(guestbookPath).
   752  		When().
   753  		Create().
   754  		Sync().
   755  		Then().
   756  		And(func(app *Application) {
   757  			_, err := RunCli("app", "set", app.Name, "--sync-policy", "automated")
   758  			assert.NoError(t, err)
   759  
   760  			_, err = RunCli("app", "sync", app.Name, "--local", guestbookPathLocal)
   761  			assert.Error(t, err)
   762  		})
   763  }
   764  
   765  func TestLocalSyncDryRunWithAutosyncEnabled(t *testing.T) {
   766  	Given(t).
   767  		Path(guestbookPath).
   768  		When().
   769  		Create().
   770  		Sync().
   771  		Then().
   772  		And(func(app *Application) {
   773  			_, err := RunCli("app", "set", app.Name, "--sync-policy", "automated")
   774  			assert.NoError(t, err)
   775  
   776  			appBefore := app.DeepCopy()
   777  			_, err = RunCli("app", "sync", app.Name, "--dry-run", "--local", guestbookPathLocal)
   778  			assert.NoError(t, err)
   779  
   780  			appAfter := app.DeepCopy()
   781  			assert.True(t, reflect.DeepEqual(appBefore, appAfter))
   782  		})
   783  }
   784  
   785  func TestSyncAsync(t *testing.T) {
   786  	Given(t).
   787  		Path(guestbookPath).
   788  		Async(true).
   789  		When().
   790  		Create().
   791  		Sync().
   792  		Then().
   793  		Expect(Success("")).
   794  		Expect(OperationPhaseIs(OperationSucceeded)).
   795  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   796  }
   797  
   798  func TestPermissions(t *testing.T) {
   799  	EnsureCleanState(t)
   800  	appName := Name()
   801  	_, err := RunCli("proj", "create", "test")
   802  	assert.NoError(t, err)
   803  
   804  	// make sure app cannot be created without permissions in project
   805  	_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
   806  		"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
   807  	assert.Error(t, err)
   808  	sourceError := fmt.Sprintf("application repo %s is not permitted in project 'test'", RepoURL(RepoURLTypeFile))
   809  	destinationError := fmt.Sprintf("application destination {%s %s} is not permitted in project 'test'", common.KubernetesInternalAPIServerAddr, DeploymentNamespace())
   810  
   811  	assert.Contains(t, err.Error(), sourceError)
   812  	assert.Contains(t, err.Error(), destinationError)
   813  
   814  	proj, err := AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Get(context.Background(), "test", metav1.GetOptions{})
   815  	assert.NoError(t, err)
   816  
   817  	proj.Spec.Destinations = []ApplicationDestination{{Server: "*", Namespace: "*"}}
   818  	proj.Spec.SourceRepos = []string{"*"}
   819  	proj, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
   820  	assert.NoError(t, err)
   821  
   822  	// make sure controller report permissions issues in conditions
   823  	_, err = RunCli("app", "create", appName, "--repo", RepoURL(RepoURLTypeFile),
   824  		"--path", guestbookPath, "--project", "test", "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
   825  	assert.NoError(t, err)
   826  	defer func() {
   827  		err = AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Delete(context.Background(), appName, metav1.DeleteOptions{})
   828  		assert.NoError(t, err)
   829  	}()
   830  
   831  	proj.Spec.Destinations = []ApplicationDestination{}
   832  	proj.Spec.SourceRepos = []string{}
   833  	_, err = AppClientset.ArgoprojV1alpha1().AppProjects(ArgoCDNamespace).Update(context.Background(), proj, metav1.UpdateOptions{})
   834  	assert.NoError(t, err)
   835  	time.Sleep(1 * time.Second)
   836  	closer, client, err := ArgoCDClientset.NewApplicationClient()
   837  	assert.NoError(t, err)
   838  	defer io.Close(closer)
   839  
   840  	refresh := string(RefreshTypeNormal)
   841  	app, err := client.Get(context.Background(), &applicationpkg.ApplicationQuery{Name: &appName, Refresh: &refresh})
   842  	assert.NoError(t, err)
   843  
   844  	destinationErrorExist := false
   845  	sourceErrorExist := false
   846  	for i := range app.Status.Conditions {
   847  		if strings.Contains(app.Status.Conditions[i].Message, destinationError) {
   848  			destinationErrorExist = true
   849  		}
   850  		if strings.Contains(app.Status.Conditions[i].Message, sourceError) {
   851  			sourceErrorExist = true
   852  		}
   853  	}
   854  	assert.True(t, destinationErrorExist)
   855  	assert.True(t, sourceErrorExist)
   856  }
   857  
   858  // make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false
   859  func TestSyncOptionPruneFalse(t *testing.T) {
   860  	Given(t).
   861  		Path("two-nice-pods").
   862  		When().
   863  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`).
   864  		Create().
   865  		Sync().
   866  		Then().
   867  		Expect(OperationPhaseIs(OperationSucceeded)).
   868  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   869  		When().
   870  		DeleteFile("pod-1.yaml").
   871  		Refresh(RefreshTypeHard).
   872  		IgnoreErrors().
   873  		Sync().
   874  		Then().
   875  		Expect(OperationPhaseIs(OperationSucceeded)).
   876  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   877  		Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync))
   878  }
   879  
   880  // make sure that if we have an invalid manifest, we can add it if we disable validation, we get a server error rather than a client error
   881  func TestSyncOptionValidateFalse(t *testing.T) {
   882  
   883  	// k3s does not validate at all, so this test does not work
   884  	if os.Getenv("ARGOCD_E2E_K3S") == "true" {
   885  		t.SkipNow()
   886  	}
   887  
   888  	Given(t).
   889  		Path("crd-validation").
   890  		When().
   891  		Create().
   892  		Then().
   893  		Expect(Success("")).
   894  		When().
   895  		IgnoreErrors().
   896  		Sync().
   897  		Then().
   898  		// client error
   899  		Expect(Error("error validating data", "")).
   900  		When().
   901  		PatchFile("deployment.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Validate=false"}}]`).
   902  		Sync().
   903  		Then().
   904  		// server error
   905  		Expect(Error("Error from server", ""))
   906  }
   907  
   908  // make sure that, if we have a resource that needs pruning, but we're ignoring it, the app is in-sync
   909  func TestCompareOptionIgnoreExtraneous(t *testing.T) {
   910  	Given(t).
   911  		Prune(false).
   912  		Path("two-nice-pods").
   913  		When().
   914  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/compare-options": "IgnoreExtraneous"}}]`).
   915  		Create().
   916  		Sync().
   917  		Then().
   918  		Expect(OperationPhaseIs(OperationSucceeded)).
   919  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   920  		When().
   921  		DeleteFile("pod-1.yaml").
   922  		Refresh(RefreshTypeHard).
   923  		Then().
   924  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   925  		And(func(app *Application) {
   926  			assert.Len(t, app.Status.Resources, 2)
   927  			statusByName := map[string]SyncStatusCode{}
   928  			for _, r := range app.Status.Resources {
   929  				statusByName[r.Name] = r.Status
   930  			}
   931  			assert.Equal(t, SyncStatusCodeOutOfSync, statusByName["pod-1"])
   932  			assert.Equal(t, SyncStatusCodeSynced, statusByName["pod-2"])
   933  		}).
   934  		When().
   935  		Sync().
   936  		Then().
   937  		Expect(OperationPhaseIs(OperationSucceeded)).
   938  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   939  }
   940  
   941  func TestSelfManagedApps(t *testing.T) {
   942  
   943  	Given(t).
   944  		Path("self-managed-app").
   945  		When().
   946  		PatchFile("resources.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/spec/source/repoURL", "value": "%s"}]`, RepoURL(RepoURLTypeFile))).
   947  		Create().
   948  		Sync().
   949  		Then().
   950  		Expect(OperationPhaseIs(OperationSucceeded)).
   951  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   952  		And(func(a *Application) {
   953  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
   954  			defer cancel()
   955  
   956  			reconciledCount := 0
   957  			var lastReconciledAt *metav1.Time
   958  			for event := range ArgoCDClientset.WatchApplicationWithRetry(ctx, a.Name, a.ResourceVersion) {
   959  				reconciledAt := event.Application.Status.ReconciledAt
   960  				if reconciledAt == nil {
   961  					reconciledAt = &metav1.Time{}
   962  				}
   963  				if lastReconciledAt != nil && !lastReconciledAt.Equal(reconciledAt) {
   964  					reconciledCount = reconciledCount + 1
   965  				}
   966  				lastReconciledAt = reconciledAt
   967  			}
   968  
   969  			assert.True(t, reconciledCount < 3, "Application was reconciled too many times")
   970  		})
   971  }
   972  
   973  func TestExcludedResource(t *testing.T) {
   974  	Given(t).
   975  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}).
   976  		Path(guestbookPath).
   977  		ResourceFilter(settings.ResourcesFilter{
   978  			ResourceExclusions: []settings.FilteredResource{{Kinds: []string{kube.DeploymentKind}}},
   979  		}).
   980  		When().
   981  		Create().
   982  		Sync().
   983  		Refresh(RefreshTypeNormal).
   984  		Then().
   985  		Expect(Condition(ApplicationConditionExcludedResourceWarning, "Resource apps/Deployment guestbook-ui is excluded in the settings"))
   986  }
   987  
   988  func TestRevisionHistoryLimit(t *testing.T) {
   989  	Given(t).
   990  		Path("config-map").
   991  		When().
   992  		Create().
   993  		Sync().
   994  		Then().
   995  		Expect(OperationPhaseIs(OperationSucceeded)).
   996  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   997  		And(func(app *Application) {
   998  			assert.Len(t, app.Status.History, 1)
   999  		}).
  1000  		When().
  1001  		AppSet("--revision-history-limit", "1").
  1002  		Sync().
  1003  		Then().
  1004  		Expect(OperationPhaseIs(OperationSucceeded)).
  1005  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1006  		And(func(app *Application) {
  1007  			assert.Len(t, app.Status.History, 1)
  1008  		})
  1009  }
  1010  
  1011  func TestOrphanedResource(t *testing.T) {
  1012  	Given(t).
  1013  		ProjectSpec(AppProjectSpec{
  1014  			SourceRepos:       []string{"*"},
  1015  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1016  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)},
  1017  		}).
  1018  		Path(guestbookPath).
  1019  		When().
  1020  		Create().
  1021  		Sync().
  1022  		Then().
  1023  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1024  		Expect(NoConditions()).
  1025  		When().
  1026  		And(func() {
  1027  			FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
  1028  				ObjectMeta: metav1.ObjectMeta{
  1029  					Name: "orphaned-configmap",
  1030  				},
  1031  			}, metav1.CreateOptions{}))
  1032  		}).
  1033  		Refresh(RefreshTypeNormal).
  1034  		Then().
  1035  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1036  		And(func(app *Application) {
  1037  			output, err := RunCli("app", "resources", app.Name)
  1038  			assert.NoError(t, err)
  1039  			assert.Contains(t, output, "orphaned-configmap")
  1040  		}).
  1041  		Given().
  1042  		ProjectSpec(AppProjectSpec{
  1043  			SourceRepos:       []string{"*"},
  1044  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1045  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}},
  1046  		}).
  1047  		When().
  1048  		Refresh(RefreshTypeNormal).
  1049  		Then().
  1050  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1051  		And(func(app *Application) {
  1052  			output, err := RunCli("app", "resources", app.Name)
  1053  			assert.NoError(t, err)
  1054  			assert.Contains(t, output, "orphaned-configmap")
  1055  		}).
  1056  		Given().
  1057  		ProjectSpec(AppProjectSpec{
  1058  			SourceRepos:       []string{"*"},
  1059  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1060  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}},
  1061  		}).
  1062  		When().
  1063  		Refresh(RefreshTypeNormal).
  1064  		Then().
  1065  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1066  		Expect(NoConditions()).
  1067  		And(func(app *Application) {
  1068  			output, err := RunCli("app", "resources", app.Name)
  1069  			assert.NoError(t, err)
  1070  			assert.NotContains(t, output, "orphaned-configmap")
  1071  		}).
  1072  		Given().
  1073  		ProjectSpec(AppProjectSpec{
  1074  			SourceRepos:       []string{"*"},
  1075  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1076  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}},
  1077  		}).
  1078  		When().
  1079  		Refresh(RefreshTypeNormal).
  1080  		Then().
  1081  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1082  		Expect(NoConditions()).
  1083  		And(func(app *Application) {
  1084  			output, err := RunCli("app", "resources", app.Name)
  1085  			assert.NoError(t, err)
  1086  			assert.NotContains(t, output, "orphaned-configmap")
  1087  		}).
  1088  		Given().
  1089  		ProjectSpec(AppProjectSpec{
  1090  			SourceRepos:       []string{"*"},
  1091  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1092  			OrphanedResources: nil,
  1093  		}).
  1094  		When().
  1095  		Refresh(RefreshTypeNormal).
  1096  		Then().
  1097  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1098  		Expect(NoConditions())
  1099  }
  1100  
  1101  func TestNotPermittedResources(t *testing.T) {
  1102  	ctx := Given(t)
  1103  
  1104  	ingress := &networkingv1beta.Ingress{
  1105  		ObjectMeta: metav1.ObjectMeta{
  1106  			Name: "sample-ingress",
  1107  			Labels: map[string]string{
  1108  				common.LabelKeyAppInstance: ctx.GetName(),
  1109  			},
  1110  		},
  1111  		Spec: networkingv1beta.IngressSpec{
  1112  			Rules: []networkingv1beta.IngressRule{{
  1113  				IngressRuleValue: networkingv1beta.IngressRuleValue{
  1114  					HTTP: &networkingv1beta.HTTPIngressRuleValue{
  1115  						Paths: []networkingv1beta.HTTPIngressPath{{
  1116  							Path: "/",
  1117  							Backend: networkingv1beta.IngressBackend{
  1118  								ServiceName: "guestbook-ui",
  1119  								ServicePort: intstr.IntOrString{Type: intstr.Int, IntVal: 80},
  1120  							},
  1121  						}},
  1122  					},
  1123  				},
  1124  			}},
  1125  		},
  1126  	}
  1127  	defer func() {
  1128  		log.Infof("Ingress 'sample-ingress' deleted from %s", ArgoCDNamespace)
  1129  		CheckError(KubeClientset.NetworkingV1beta1().Ingresses(ArgoCDNamespace).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{}))
  1130  	}()
  1131  
  1132  	svc := &v1.Service{
  1133  		ObjectMeta: metav1.ObjectMeta{
  1134  			Name: "guestbook-ui",
  1135  			Labels: map[string]string{
  1136  				common.LabelKeyAppInstance: ctx.GetName(),
  1137  			},
  1138  		},
  1139  		Spec: v1.ServiceSpec{
  1140  			Ports: []v1.ServicePort{{
  1141  				Port:       80,
  1142  				TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 80},
  1143  			}},
  1144  			Selector: map[string]string{
  1145  				"app": "guestbook-ui",
  1146  			},
  1147  		},
  1148  	}
  1149  
  1150  	ctx.ProjectSpec(AppProjectSpec{
  1151  		SourceRepos:  []string{"*"},
  1152  		Destinations: []ApplicationDestination{{Namespace: DeploymentNamespace(), Server: "*"}},
  1153  		NamespaceResourceBlacklist: []metav1.GroupKind{
  1154  			{Group: "", Kind: "Service"},
  1155  		}}).
  1156  		And(func() {
  1157  			FailOnErr(KubeClientset.NetworkingV1beta1().Ingresses(ArgoCDNamespace).Create(context.Background(), ingress, metav1.CreateOptions{}))
  1158  			FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{}))
  1159  		}).
  1160  		Path(guestbookPath).
  1161  		When().
  1162  		Create().
  1163  		Then().
  1164  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
  1165  		And(func(app *Application) {
  1166  			statusByKind := make(map[string]ResourceStatus)
  1167  			for _, res := range app.Status.Resources {
  1168  				statusByKind[res.Kind] = res
  1169  			}
  1170  			_, hasIngress := statusByKind[kube.IngressKind]
  1171  			assert.False(t, hasIngress, "Ingress is prohibited not managed object and should be even visible to user")
  1172  			serviceStatus := statusByKind[kube.ServiceKind]
  1173  			assert.Equal(t, serviceStatus.Status, SyncStatusCodeUnknown, "Service is prohibited managed resource so should be set to Unknown")
  1174  			deploymentStatus := statusByKind[kube.DeploymentKind]
  1175  			assert.Equal(t, deploymentStatus.Status, SyncStatusCodeOutOfSync)
  1176  		}).
  1177  		When().
  1178  		Delete(true).
  1179  		Then().
  1180  		Expect(DoesNotExist())
  1181  
  1182  	// Make sure prohibited resources are not deleted during application deletion
  1183  	FailOnErr(KubeClientset.NetworkingV1beta1().Ingresses(ArgoCDNamespace).Get(context.Background(), "sample-ingress", metav1.GetOptions{}))
  1184  	FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}))
  1185  }
  1186  
  1187  func TestSyncWithInfos(t *testing.T) {
  1188  	expectedInfo := make([]*Info, 2)
  1189  	expectedInfo[0] = &Info{Name: "name1", Value: "val1"}
  1190  	expectedInfo[1] = &Info{Name: "name2", Value: "val2"}
  1191  
  1192  	Given(t).
  1193  		Path(guestbookPath).
  1194  		When().
  1195  		Create().
  1196  		Then().
  1197  		And(func(app *Application) {
  1198  			_, err := RunCli("app", "sync", app.Name,
  1199  				"--info", fmt.Sprintf("%s=%s", expectedInfo[0].Name, expectedInfo[0].Value),
  1200  				"--info", fmt.Sprintf("%s=%s", expectedInfo[1].Name, expectedInfo[1].Value))
  1201  			assert.NoError(t, err)
  1202  		}).
  1203  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1204  		And(func(app *Application) {
  1205  			assert.ElementsMatch(t, app.Status.OperationState.Operation.Info, expectedInfo)
  1206  		})
  1207  }
  1208  
  1209  //Given: argocd app create does not provide --dest-namespace
  1210  //       Manifest contains resource console which does not require namespace
  1211  //Expect: no app.Status.Conditions
  1212  func TestCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) {
  1213  	Given(t).
  1214  		Path(globalWithNoNameSpace).
  1215  		When().
  1216  		CreateWithNoNameSpace().
  1217  		Then().
  1218  		And(func(app *Application) {
  1219  			time.Sleep(500 * time.Millisecond)
  1220  			app, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{})
  1221  			assert.NoError(t, err)
  1222  			assert.Len(t, app.Status.Conditions, 0)
  1223  		})
  1224  }
  1225  
  1226  //Given: argocd app create does not provide --dest-namespace
  1227  //       Manifest contains resource deployment, and service which requires namespace
  1228  //       Deployment and service do not have namespace in manifest
  1229  //Expect: app.Status.Conditions for deployment ans service which does not have namespace in manifest
  1230  func TestCreateAppWithNoNameSpaceWhenRequired(t *testing.T) {
  1231  	Given(t).
  1232  		Path(guestbookPath).
  1233  		When().
  1234  		CreateWithNoNameSpace().
  1235  		Then().
  1236  		And(func(app *Application) {
  1237  			var updatedApp *Application
  1238  			for i := 0; i < 3; i++ {
  1239  				obj, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{})
  1240  				assert.NoError(t, err)
  1241  				updatedApp = obj
  1242  				if len(updatedApp.Status.Conditions) > 0 {
  1243  					break
  1244  				}
  1245  				time.Sleep(500 * time.Millisecond)
  1246  			}
  1247  			assert.Len(t, updatedApp.Status.Conditions, 2)
  1248  			assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError)
  1249  			assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError)
  1250  		})
  1251  }
  1252  
  1253  //Given: argocd app create does not provide --dest-namespace
  1254  //       Manifest contains resource deployment, and service which requires namespace
  1255  //       Some deployment and service has namespace in manifest
  1256  //       Some deployment and service does not have namespace in manifest
  1257  //Expect: app.Status.Conditions for deployment and service which does not have namespace in manifest
  1258  func TestCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) {
  1259  	Given(t).
  1260  		Path(guestbookWithNamespace).
  1261  		When().
  1262  		CreateWithNoNameSpace().
  1263  		Then().
  1264  		And(func(app *Application) {
  1265  			var updatedApp *Application
  1266  			for i := 0; i < 3; i++ {
  1267  				obj, err := AppClientset.ArgoprojV1alpha1().Applications(ArgoCDNamespace).Get(context.Background(), app.Name, metav1.GetOptions{})
  1268  				assert.NoError(t, err)
  1269  				updatedApp = obj
  1270  				if len(updatedApp.Status.Conditions) > 0 {
  1271  					break
  1272  				}
  1273  				time.Sleep(500 * time.Millisecond)
  1274  			}
  1275  			assert.Len(t, updatedApp.Status.Conditions, 2)
  1276  			assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError)
  1277  			assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError)
  1278  		})
  1279  }
  1280  
  1281  func TestListResource(t *testing.T) {
  1282  	Given(t).
  1283  		ProjectSpec(AppProjectSpec{
  1284  			SourceRepos:       []string{"*"},
  1285  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1286  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.BoolPtr(true)},
  1287  		}).
  1288  		Path(guestbookPath).
  1289  		When().
  1290  		Create().
  1291  		Sync().
  1292  		Then().
  1293  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1294  		Expect(NoConditions()).
  1295  		When().
  1296  		And(func() {
  1297  			FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
  1298  				ObjectMeta: metav1.ObjectMeta{
  1299  					Name: "orphaned-configmap",
  1300  				},
  1301  			}, metav1.CreateOptions{}))
  1302  		}).
  1303  		Refresh(RefreshTypeNormal).
  1304  		Then().
  1305  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1306  		And(func(app *Application) {
  1307  			output, err := RunCli("app", "resources", app.Name)
  1308  			assert.NoError(t, err)
  1309  			assert.Contains(t, output, "orphaned-configmap")
  1310  			assert.Contains(t, output, "guestbook-ui")
  1311  		}).
  1312  		And(func(app *Application) {
  1313  			output, err := RunCli("app", "resources", app.Name, "--orphaned=true")
  1314  			assert.NoError(t, err)
  1315  			assert.Contains(t, output, "orphaned-configmap")
  1316  			assert.NotContains(t, output, "guestbook-ui")
  1317  		}).
  1318  		And(func(app *Application) {
  1319  			output, err := RunCli("app", "resources", app.Name, "--orphaned=false")
  1320  			assert.NoError(t, err)
  1321  			assert.NotContains(t, output, "orphaned-configmap")
  1322  			assert.Contains(t, output, "guestbook-ui")
  1323  		}).
  1324  		Given().
  1325  		ProjectSpec(AppProjectSpec{
  1326  			SourceRepos:       []string{"*"},
  1327  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1328  			OrphanedResources: nil,
  1329  		}).
  1330  		When().
  1331  		Refresh(RefreshTypeNormal).
  1332  		Then().
  1333  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1334  		Expect(NoConditions())
  1335  }
  1336  
  1337  // Given application is set with --sync-option CreateNamespace=true
  1338  //       application --dest-namespace does not exist
  1339  // Verity application --dest-namespace is created
  1340  //        application sync successful
  1341  //        when application is deleted, --dest-namespace is not deleted
  1342  func TestNamespaceAutoCreation(t *testing.T) {
  1343  	updatedNamespace := getNewNamespace(t)
  1344  	defer func() {
  1345  		_, err := Run("", "kubectl", "delete", "namespace", updatedNamespace)
  1346  		assert.NoError(t, err)
  1347  	}()
  1348  	Given(t).
  1349  		Timeout(30).
  1350  		Path("guestbook").
  1351  		When().
  1352  		Create("--sync-option", "CreateNamespace=true").
  1353  		Then().
  1354  		And(func(app *Application) {
  1355  			//Make sure the namespace we are about to update to does not exist
  1356  			_, err := Run("", "kubectl", "get", "namespace", updatedNamespace)
  1357  			assert.Error(t, err)
  1358  			assert.Contains(t, err.Error(), "not found")
  1359  		}).
  1360  		When().
  1361  		AppSet("--dest-namespace", updatedNamespace).
  1362  		Sync().
  1363  		Then().
  1364  		Expect(Success("")).
  1365  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1366  		Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1367  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)).
  1368  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)).
  1369  		When().
  1370  		Delete(true).
  1371  		Then().
  1372  		Expect(Success("")).
  1373  		And(func(app *Application) {
  1374  			//Verify delete app does not delete the namespace auto created
  1375  			output, err := Run("", "kubectl", "get", "namespace", updatedNamespace)
  1376  			assert.NoError(t, err)
  1377  			assert.Contains(t, output, updatedNamespace)
  1378  		})
  1379  }
  1380  
  1381  func TestFailedSyncWithRetry(t *testing.T) {
  1382  	Given(t).
  1383  		Path("hook").
  1384  		When().
  1385  		PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "PreSync"}}]`).
  1386  		// make hook fail
  1387  		PatchFile("hook.yaml", `[{"op": "replace", "path": "/spec/containers/0/command", "value": ["false"]}]`).
  1388  		Create().
  1389  		IgnoreErrors().
  1390  		Sync("--retry-limit=1", "--retry-backoff-duration=1s").
  1391  		Then().
  1392  		Expect(OperationPhaseIs(OperationFailed)).
  1393  		Expect(OperationMessageContains("retried 1 times"))
  1394  }
  1395  
  1396  func TestCreateDisableValidation(t *testing.T) {
  1397  	Given(t).
  1398  		Path("baddir").
  1399  		When().
  1400  		Create("--validate=false").
  1401  		Then().
  1402  		And(func(app *Application) {
  1403  			_, err := RunCli("app", "create", app.Name, "--upsert", "--validate=false", "--repo", RepoURL(RepoURLTypeFile),
  1404  				"--path", "baddir2", "--project", app.Spec.Project, "--dest-server", common.KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
  1405  			assert.NoError(t, err)
  1406  		}).
  1407  		When().
  1408  		AppSet("--path", "baddir3", "--validate=false")
  1409  
  1410  }
  1411  
  1412  func TestCreateFromPartialFile(t *testing.T) {
  1413  	partialApp :=
  1414  		`spec:
  1415    syncPolicy:
  1416      automated: {prune: true }`
  1417  
  1418  	path := "helm-values"
  1419  	Given(t).
  1420  		When().
  1421  		// app should be auto-synced once created
  1422  		CreateFromPartialFile(partialApp, "--path", path, "--helm-set", "foo=foo").
  1423  		Then().
  1424  		Expect(Success("")).
  1425  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1426  		Expect(NoConditions()).
  1427  		And(func(app *Application) {
  1428  			assert.Equal(t, path, app.Spec.Source.Path)
  1429  			assert.Equal(t, []HelmParameter{{Name: "foo", Value: "foo"}}, app.Spec.Source.Helm.Parameters)
  1430  		})
  1431  }
  1432  func TestAppCreationWithExclude(t *testing.T) {
  1433  	Given(t).
  1434  		Path("app-exclusions").
  1435  		When().
  1436  		Create("--directory-exclude", "**/.*", "--directory-recurse").
  1437  		Sync().
  1438  		Then().
  1439  		Expect(OperationPhaseIs(OperationSucceeded)).
  1440  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1441  		Expect(HealthIs(health.HealthStatusHealthy))
  1442  }
  1443  
  1444  // Ensure actions work when using a resource action that modifies status and/or spec
  1445  func TestCRDStatusSubresourceAction(t *testing.T) {
  1446  	actions := `
  1447  discovery.lua: |
  1448    actions = {}
  1449    actions["update-spec"] = {["disabled"] = false}
  1450    actions["update-status"] = {["disabled"] = false}
  1451    actions["update-both"] = {["disabled"] = false}
  1452    return actions
  1453  definitions:
  1454  - name: update-both
  1455    action.lua: |
  1456      obj.spec = {}
  1457      obj.spec.foo = "update-both"
  1458      obj.status = {}
  1459      obj.status.bar = "update-both"
  1460      return obj
  1461  - name: update-spec
  1462    action.lua: |
  1463      obj.spec = {}
  1464      obj.spec.foo = "update-spec"
  1465      return obj
  1466  - name: update-status
  1467    action.lua: |
  1468      obj.status = {}
  1469      obj.status.bar = "update-status"
  1470      return obj
  1471  `
  1472  	Given(t).
  1473  		Path("crd-subresource").
  1474  		And(func() {
  1475  			SetResourceOverrides(map[string]ResourceOverride{
  1476  				"argoproj.io/StatusSubResource": {
  1477  					Actions: actions,
  1478  				},
  1479  				"argoproj.io/NonStatusSubResource": {
  1480  					Actions: actions,
  1481  				},
  1482  			})
  1483  		}).
  1484  		When().Create().Sync().Then().
  1485  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1486  		When().
  1487  		Then().
  1488  		// tests resource actions on a CRD using status subresource
  1489  		And(func(app *Application) {
  1490  			_, err := RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-both")
  1491  			assert.NoError(t, err)
  1492  			text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  1493  			assert.Equal(t, "update-both", text)
  1494  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  1495  			assert.Equal(t, "update-both", text)
  1496  
  1497  			_, err = RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-spec")
  1498  			assert.NoError(t, err)
  1499  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  1500  			assert.Equal(t, "update-spec", text)
  1501  
  1502  			_, err = RunCli("app", "actions", "run", app.Name, "--kind", "StatusSubResource", "update-status")
  1503  			assert.NoError(t, err)
  1504  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  1505  			assert.Equal(t, "update-status", text)
  1506  		}).
  1507  		// tests resource actions on a CRD *not* using status subresource
  1508  		And(func(app *Application) {
  1509  			_, err := RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-both")
  1510  			assert.NoError(t, err)
  1511  			text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  1512  			assert.Equal(t, "update-both", text)
  1513  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  1514  			assert.Equal(t, "update-both", text)
  1515  
  1516  			_, err = RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-spec")
  1517  			assert.NoError(t, err)
  1518  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  1519  			assert.Equal(t, "update-spec", text)
  1520  
  1521  			_, err = RunCli("app", "actions", "run", app.Name, "--kind", "NonStatusSubResource", "update-status")
  1522  			assert.NoError(t, err)
  1523  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  1524  			assert.Equal(t, "update-status", text)
  1525  		})
  1526  }
  1527  
  1528  func TestAppWaitOperationInProgress(t *testing.T) {
  1529  	Given(t).
  1530  		And(func() {
  1531  			SetResourceOverrides(map[string]ResourceOverride{
  1532  				"batch/Job": {
  1533  					HealthLua: `return { status = 'Running' }`,
  1534  				},
  1535  				"apps/Deployment": {
  1536  					HealthLua: `return { status = 'Suspended' }`,
  1537  				},
  1538  			})
  1539  		}).
  1540  		Async(true).
  1541  		Path("hook-and-deployment").
  1542  		When().
  1543  		Create().
  1544  		Sync().
  1545  		Then().
  1546  		// stuck in running state
  1547  		Expect(OperationPhaseIs(OperationRunning)).
  1548  		When().
  1549  		And(func() {
  1550  			_, err := RunCli("app", "wait", Name(), "--suspended")
  1551  			errors.CheckError(err)
  1552  		})
  1553  }