github.com/argoproj/argo-cd/v2@v2.10.9/test/e2e/app_management_ns_test.go (about)

     1  package e2e
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"path"
     8  	"reflect"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/argoproj/gitops-engine/pkg/diff"
    13  	"github.com/argoproj/gitops-engine/pkg/health"
    14  	. "github.com/argoproj/gitops-engine/pkg/sync/common"
    15  	"github.com/argoproj/gitops-engine/pkg/utils/kube"
    16  	"github.com/argoproj/pkg/errors"
    17  	log "github.com/sirupsen/logrus"
    18  	"github.com/stretchr/testify/assert"
    19  	"github.com/stretchr/testify/require"
    20  	v1 "k8s.io/api/core/v1"
    21  	networkingv1 "k8s.io/api/networking/v1"
    22  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    23  	"k8s.io/apimachinery/pkg/runtime/schema"
    24  	"k8s.io/apimachinery/pkg/types"
    25  	"k8s.io/apimachinery/pkg/util/intstr"
    26  	"k8s.io/utils/pointer"
    27  
    28  	"github.com/argoproj/argo-cd/v2/common"
    29  	applicationpkg "github.com/argoproj/argo-cd/v2/pkg/apiclient/application"
    30  	. "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1"
    31  	"github.com/argoproj/argo-cd/v2/test/e2e/fixture"
    32  	. "github.com/argoproj/argo-cd/v2/test/e2e/fixture"
    33  	accountFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/account"
    34  	. "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app"
    35  	projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project"
    36  	repoFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos"
    37  	"github.com/argoproj/argo-cd/v2/test/e2e/testdata"
    38  
    39  	"github.com/argoproj/argo-cd/v2/pkg/apis/application"
    40  	. "github.com/argoproj/argo-cd/v2/util/argo"
    41  	. "github.com/argoproj/argo-cd/v2/util/errors"
    42  	"github.com/argoproj/argo-cd/v2/util/io"
    43  	"github.com/argoproj/argo-cd/v2/util/settings"
    44  )
    45  
    46  // This empty test is here only for clarity, to conform to logs rbac tests structure in account. This exact usecase is covered in the TestAppLogs test
    47  func TestNamespacedGetLogsAllowNoSwitch(t *testing.T) {
    48  }
    49  
    50  func TestNamespacedGetLogsDenySwitchOn(t *testing.T) {
    51  	SkipOnEnv(t, "OPENSHIFT")
    52  
    53  	accountFixture.Given(t).
    54  		Name("test").
    55  		When().
    56  		Create().
    57  		Login().
    58  		SetPermissions([]fixture.ACL{
    59  			{
    60  				Resource: "applications",
    61  				Action:   "create",
    62  				Scope:    "*",
    63  			},
    64  			{
    65  				Resource: "applications",
    66  				Action:   "get",
    67  				Scope:    "*",
    68  			},
    69  			{
    70  				Resource: "applications",
    71  				Action:   "sync",
    72  				Scope:    "*",
    73  			},
    74  			{
    75  				Resource: "projects",
    76  				Action:   "get",
    77  				Scope:    "*",
    78  			},
    79  		}, "app-creator")
    80  
    81  	ctx := GivenWithSameState(t)
    82  	ctx.SetAppNamespace(ArgoCDAppNamespace)
    83  	ctx.
    84  		Path("guestbook-logs").
    85  		SetTrackingMethod("annotation").
    86  		When().
    87  		CreateApp().
    88  		Sync().
    89  		SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "true").
    90  		Then().
    91  		Expect(HealthIs(health.HealthStatusHealthy)).
    92  		And(func(app *Application) {
    93  			_, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui")
    94  			assert.Error(t, err)
    95  			assert.Contains(t, err.Error(), "permission denied")
    96  		})
    97  }
    98  
    99  func TestNamespacedGetLogsAllowSwitchOnNS(t *testing.T) {
   100  	SkipOnEnv(t, "OPENSHIFT")
   101  
   102  	accountFixture.Given(t).
   103  		Name("test").
   104  		When().
   105  		Create().
   106  		Login().
   107  		SetPermissions([]fixture.ACL{
   108  			{
   109  				Resource: "applications",
   110  				Action:   "create",
   111  				Scope:    "*",
   112  			},
   113  			{
   114  				Resource: "applications",
   115  				Action:   "get",
   116  				Scope:    "*",
   117  			},
   118  			{
   119  				Resource: "applications",
   120  				Action:   "sync",
   121  				Scope:    "*",
   122  			},
   123  			{
   124  				Resource: "projects",
   125  				Action:   "get",
   126  				Scope:    "*",
   127  			},
   128  			{
   129  				Resource: "logs",
   130  				Action:   "get",
   131  				Scope:    "*",
   132  			},
   133  		}, "app-creator")
   134  
   135  	ctx := GivenWithSameState(t)
   136  	ctx.SetAppNamespace(AppNamespace())
   137  	ctx.
   138  		Path("guestbook-logs").
   139  		SetTrackingMethod("annotation").
   140  		When().
   141  		CreateApp().
   142  		Sync().
   143  		SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "true").
   144  		Then().
   145  		Expect(HealthIs(health.HealthStatusHealthy)).
   146  		And(func(app *Application) {
   147  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui")
   148  			assert.NoError(t, err)
   149  			assert.Contains(t, out, "Hi")
   150  		}).
   151  		And(func(app *Application) {
   152  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod")
   153  			assert.NoError(t, err)
   154  			assert.Contains(t, out, "Hi")
   155  		}).
   156  		And(func(app *Application) {
   157  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service")
   158  			assert.NoError(t, err)
   159  			assert.NotContains(t, out, "Hi")
   160  		})
   161  
   162  }
   163  
   164  func TestNamespacedGetLogsAllowSwitchOff(t *testing.T) {
   165  	SkipOnEnv(t, "OPENSHIFT")
   166  
   167  	accountFixture.Given(t).
   168  		Name("test").
   169  		When().
   170  		Create().
   171  		Login().
   172  		SetPermissions([]fixture.ACL{
   173  			{
   174  				Resource: "applications",
   175  				Action:   "create",
   176  				Scope:    "*",
   177  			},
   178  			{
   179  				Resource: "applications",
   180  				Action:   "get",
   181  				Scope:    "*",
   182  			},
   183  			{
   184  				Resource: "applications",
   185  				Action:   "sync",
   186  				Scope:    "*",
   187  			},
   188  			{
   189  				Resource: "projects",
   190  				Action:   "get",
   191  				Scope:    "*",
   192  			},
   193  		}, "app-creator")
   194  	ctx := GivenWithSameState(t)
   195  	ctx.SetAppNamespace(AppNamespace())
   196  	ctx.
   197  		Path("guestbook-logs").
   198  		SetTrackingMethod("annotation").
   199  		When().
   200  		CreateApp().
   201  		Sync().
   202  		SetParamInSettingConfigMap("server.rbac.log.enforce.enable", "false").
   203  		Then().
   204  		Expect(HealthIs(health.HealthStatusHealthy)).
   205  		And(func(app *Application) {
   206  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui")
   207  			assert.NoError(t, err)
   208  			assert.Contains(t, out, "Hi")
   209  		}).
   210  		And(func(app *Application) {
   211  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Pod")
   212  			assert.NoError(t, err)
   213  			assert.Contains(t, out, "Hi")
   214  		}).
   215  		And(func(app *Application) {
   216  			out, err := RunCliWithRetry(5, "app", "logs", ctx.AppQualifiedName(), "--kind", "Service")
   217  			assert.NoError(t, err)
   218  			assert.NotContains(t, out, "Hi")
   219  		})
   220  }
   221  
   222  func TestNamespacedSyncToUnsignedCommit(t *testing.T) {
   223  	SkipOnEnv(t, "GPG")
   224  	GivenWithNamespace(t, AppNamespace()).
   225  		SetTrackingMethod("annotation").
   226  		Project("gpg").
   227  		Path(guestbookPath).
   228  		When().
   229  		IgnoreErrors().
   230  		CreateApp().
   231  		Sync().
   232  		Then().
   233  		Expect(OperationPhaseIs(OperationError)).
   234  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   235  		Expect(HealthIs(health.HealthStatusMissing))
   236  }
   237  
   238  func TestNamespacedSyncToSignedCommitWKK(t *testing.T) {
   239  	SkipOnEnv(t, "GPG")
   240  	Given(t).
   241  		SetAppNamespace(AppNamespace()).
   242  		SetTrackingMethod("annotation").
   243  		Project("gpg").
   244  		Path(guestbookPath).
   245  		When().
   246  		AddSignedFile("test.yaml", "null").
   247  		IgnoreErrors().
   248  		CreateApp().
   249  		Sync().
   250  		Then().
   251  		Expect(OperationPhaseIs(OperationError)).
   252  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   253  		Expect(HealthIs(health.HealthStatusMissing))
   254  }
   255  
   256  func TestNamespacedSyncToSignedCommitKWKK(t *testing.T) {
   257  	SkipOnEnv(t, "GPG")
   258  	Given(t).
   259  		SetAppNamespace(AppNamespace()).
   260  		SetTrackingMethod("annotation").
   261  		Project("gpg").
   262  		Path(guestbookPath).
   263  		GPGPublicKeyAdded().
   264  		Sleep(2).
   265  		When().
   266  		AddSignedFile("test.yaml", "null").
   267  		IgnoreErrors().
   268  		CreateApp().
   269  		Sync().
   270  		Then().
   271  		Expect(OperationPhaseIs(OperationSucceeded)).
   272  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   273  		Expect(HealthIs(health.HealthStatusHealthy))
   274  }
   275  
   276  func TestNamespacedAppCreation(t *testing.T) {
   277  	ctx := Given(t)
   278  	ctx.
   279  		Path(guestbookPath).
   280  		SetTrackingMethod("annotation").
   281  		SetAppNamespace(AppNamespace()).
   282  		When().
   283  		CreateApp().
   284  		Then().
   285  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   286  		And(func(app *Application) {
   287  			assert.Equal(t, Name(), app.Name)
   288  			assert.Equal(t, AppNamespace(), app.Namespace)
   289  			assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.GetSource().RepoURL)
   290  			assert.Equal(t, guestbookPath, app.Spec.GetSource().Path)
   291  			assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace)
   292  			assert.Equal(t, KubernetesInternalAPIServerAddr, app.Spec.Destination.Server)
   293  		}).
   294  		Expect(NamespacedEvent(AppNamespace(), EventReasonResourceCreated, "create")).
   295  		And(func(app *Application) {
   296  			// app should be listed
   297  			output, err := RunCli("app", "list")
   298  			assert.NoError(t, err)
   299  			assert.Contains(t, output, ctx.AppQualifiedName())
   300  		}).
   301  		When().
   302  		// ensure that create is idempotent
   303  		CreateApp().
   304  		Then().
   305  		Given().
   306  		Revision("master").
   307  		When().
   308  		// ensure that update replaces spec and merge labels and annotations
   309  		And(func() {
   310  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(),
   311  				ctx.GetName(), types.MergePatchType, []byte(`{"metadata": {"labels": { "test": "label" }, "annotations": { "test": "annotation" }}}`), metav1.PatchOptions{}))
   312  		}).
   313  		CreateApp("--upsert").
   314  		Then().
   315  		And(func(app *Application) {
   316  			assert.Equal(t, "label", app.Labels["test"])
   317  			assert.Equal(t, "annotation", app.Annotations["test"])
   318  			assert.Equal(t, "master", app.Spec.GetSource().TargetRevision)
   319  		})
   320  }
   321  
   322  func TestNamespacedAppCreationWithoutForceUpdate(t *testing.T) {
   323  	ctx := Given(t)
   324  
   325  	ctx.
   326  		Path(guestbookPath).
   327  		SetTrackingMethod("annotation").
   328  		SetAppNamespace(AppNamespace()).
   329  		DestName("in-cluster").
   330  		When().
   331  		CreateApp().
   332  		Then().
   333  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   334  		And(func(app *Application) {
   335  			assert.Equal(t, ctx.AppName(), app.Name)
   336  			assert.Equal(t, AppNamespace(), app.Namespace)
   337  			assert.Equal(t, RepoURL(RepoURLTypeFile), app.Spec.GetSource().RepoURL)
   338  			assert.Equal(t, guestbookPath, app.Spec.GetSource().Path)
   339  			assert.Equal(t, DeploymentNamespace(), app.Spec.Destination.Namespace)
   340  			assert.Equal(t, "in-cluster", app.Spec.Destination.Name)
   341  		}).
   342  		Expect(NamespacedEvent(AppNamespace(), EventReasonResourceCreated, "create")).
   343  		And(func(_ *Application) {
   344  			// app should be listed
   345  			output, err := RunCli("app", "list")
   346  			assert.NoError(t, err)
   347  			assert.Contains(t, output, ctx.AppQualifiedName())
   348  		}).
   349  		When().
   350  		IgnoreErrors().
   351  		CreateApp().
   352  		Then().
   353  		Expect(Error("", "existing application spec is different, use upsert flag to force update"))
   354  }
   355  
   356  func TestNamespacedDeleteAppResource(t *testing.T) {
   357  	ctx := Given(t)
   358  
   359  	ctx.
   360  		Path(guestbookPath).
   361  		SetTrackingMethod("annotation").
   362  		SetAppNamespace(AppNamespace()).
   363  		When().
   364  		CreateApp().
   365  		Sync().
   366  		Then().
   367  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   368  		And(func(_ *Application) {
   369  			// app should be listed
   370  			if _, err := RunCli("app", "delete-resource", ctx.AppQualifiedName(), "--kind", "Service", "--resource-name", "guestbook-ui"); err != nil {
   371  				assert.NoError(t, err)
   372  			}
   373  		}).
   374  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   375  		Expect(HealthIs(health.HealthStatusMissing))
   376  }
   377  
   378  // demonstrate that we cannot use a standard sync when an immutable field is changed, we must use "force"
   379  func TestNamespacedImmutableChange(t *testing.T) {
   380  	SkipOnEnv(t, "OPENSHIFT")
   381  	Given(t).
   382  		Path("secrets").
   383  		SetTrackingMethod("annotation").
   384  		SetAppNamespace(AppNamespace()).
   385  		When().
   386  		CreateApp().
   387  		PatchFile("secrets.yaml", `[{"op": "add", "path": "/data/new-field", "value": "dGVzdA=="}, {"op": "add", "path": "/immutable", "value": true}]`).
   388  		Sync().
   389  		Then().
   390  		Expect(OperationPhaseIs(OperationSucceeded)).
   391  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   392  		Expect(HealthIs(health.HealthStatusHealthy)).
   393  		When().
   394  		PatchFile("secrets.yaml", `[{"op": "add", "path": "/data/new-field", "value": "dGVzdDI="}]`).
   395  		IgnoreErrors().
   396  		Sync().
   397  		DoNotIgnoreErrors().
   398  		Then().
   399  		Expect(OperationPhaseIs(OperationFailed)).
   400  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   401  		Expect(ResourceResultNumbering(1)).
   402  		Expect(ResourceResultMatches(ResourceResult{
   403  			Kind:      "Secret",
   404  			Version:   "v1",
   405  			Namespace: DeploymentNamespace(),
   406  			Name:      "test-secret",
   407  			SyncPhase: "Sync",
   408  			Status:    "SyncFailed",
   409  			HookPhase: "Failed",
   410  			Message:   `Secret "test-secret" is invalid`,
   411  		})).
   412  		// now we can do this will a force
   413  		Given().
   414  		Force().
   415  		When().
   416  		Sync().
   417  		Then().
   418  		Expect(OperationPhaseIs(OperationSucceeded)).
   419  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   420  		Expect(HealthIs(health.HealthStatusHealthy))
   421  }
   422  
   423  func TestNamespacedInvalidAppProject(t *testing.T) {
   424  	Given(t).
   425  		SetTrackingMethod("annotation").
   426  		Path(guestbookPath).
   427  		SetAppNamespace(AppNamespace()).
   428  		Project("does-not-exist").
   429  		When().
   430  		IgnoreErrors().
   431  		CreateApp().
   432  		Then().
   433  		// We're not allowed to infer whether the project exists based on this error message. Instead, we get a generic
   434  		// permission denied error.
   435  		Expect(Error("", "is not allowed"))
   436  }
   437  
   438  func TestNamespacedAppDeletion(t *testing.T) {
   439  	ctx := Given(t)
   440  	ctx.
   441  		Path(guestbookPath).
   442  		SetTrackingMethod("annotation").
   443  		SetAppNamespace(AppNamespace()).
   444  		When().
   445  		CreateApp().
   446  		Then().
   447  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   448  		When().
   449  		Delete(true).
   450  		Then().
   451  		Expect(DoesNotExist()).
   452  		Expect(NamespacedEvent(AppNamespace(), EventReasonResourceDeleted, "delete"))
   453  
   454  	output, err := RunCli("app", "list")
   455  	assert.NoError(t, err)
   456  	assert.NotContains(t, output, ctx.AppQualifiedName())
   457  }
   458  
   459  func TestNamespacedAppLabels(t *testing.T) {
   460  	ctx := Given(t)
   461  	ctx.
   462  		Path("config-map").
   463  		SetTrackingMethod("annotation").
   464  		SetAppNamespace(AppNamespace()).
   465  		When().
   466  		CreateApp("-l", "foo=bar").
   467  		Then().
   468  		And(func(app *Application) {
   469  			assert.Contains(t, FailOnErr(RunCli("app", "list")), ctx.AppQualifiedName())
   470  			assert.Contains(t, FailOnErr(RunCli("app", "list", "-l", "foo=bar")), ctx.AppQualifiedName())
   471  			assert.NotContains(t, FailOnErr(RunCli("app", "list", "-l", "foo=rubbish")), ctx.AppQualifiedName())
   472  		}).
   473  		Given().
   474  		// remove both name and replace labels means nothing will sync
   475  		Name("").
   476  		When().
   477  		IgnoreErrors().
   478  		Sync("-l", "foo=rubbish").
   479  		DoNotIgnoreErrors().
   480  		Then().
   481  		Expect(Error("", "No matching apps found for filter: selector foo=rubbish")).
   482  		// check we can update the app and it is then sync'd
   483  		Given().
   484  		When().
   485  		Sync("-l", "foo=bar")
   486  }
   487  
   488  func TestNamespacedTrackAppStateAndSyncApp(t *testing.T) {
   489  	Given(t).
   490  		Path(guestbookPath).
   491  		SetTrackingMethod("annotation").
   492  		SetAppNamespace(AppNamespace()).
   493  		When().
   494  		CreateApp().
   495  		Sync().
   496  		Then().
   497  		Expect(OperationPhaseIs(OperationSucceeded)).
   498  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   499  		Expect(HealthIs(health.HealthStatusHealthy)).
   500  		Expect(Success(fmt.Sprintf("Service     %s  guestbook-ui  Synced ", DeploymentNamespace()))).
   501  		Expect(Success(fmt.Sprintf("apps   Deployment  %s  guestbook-ui  Synced", DeploymentNamespace()))).
   502  		Expect(NamespacedEvent(AppNamespace(), EventReasonResourceUpdated, "sync")).
   503  		And(func(app *Application) {
   504  			assert.NotNil(t, app.Status.OperationState.SyncResult)
   505  		})
   506  }
   507  
   508  func TestNamespacedAppRollbackSuccessful(t *testing.T) {
   509  	ctx := Given(t)
   510  	ctx.
   511  		Path(guestbookPath).
   512  		SetTrackingMethod("annotation").
   513  		SetAppNamespace(AppNamespace()).
   514  		When().
   515  		CreateApp().
   516  		Sync().
   517  		Then().
   518  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   519  		And(func(app *Application) {
   520  			assert.NotEmpty(t, app.Status.Sync.Revision)
   521  		}).
   522  		And(func(app *Application) {
   523  			appWithHistory := app.DeepCopy()
   524  			appWithHistory.Status.History = []RevisionHistory{{
   525  				ID:         1,
   526  				Revision:   app.Status.Sync.Revision,
   527  				DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-1 * time.Minute)},
   528  				Source:     app.Spec.GetSource(),
   529  			}, {
   530  				ID:         2,
   531  				Revision:   "cdb",
   532  				DeployedAt: metav1.Time{Time: metav1.Now().UTC().Add(-2 * time.Minute)},
   533  				Source:     app.Spec.GetSource(),
   534  			}}
   535  			patch, _, err := diff.CreateTwoWayMergePatch(app, appWithHistory, &Application{})
   536  			require.NoError(t, err)
   537  			app, err = AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(), app.Name, types.MergePatchType, patch, metav1.PatchOptions{})
   538  			require.NoError(t, err)
   539  
   540  			// sync app and make sure it reaches InSync state
   541  			_, err = RunCli("app", "rollback", app.QualifiedName(), "1")
   542  			require.NoError(t, err)
   543  
   544  		}).
   545  		Expect(NamespacedEvent(AppNamespace(), EventReasonOperationStarted, "rollback")).
   546  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   547  		And(func(app *Application) {
   548  			assert.Equal(t, SyncStatusCodeSynced, app.Status.Sync.Status)
   549  			require.NotNil(t, app.Status.OperationState.SyncResult)
   550  			assert.Equal(t, 2, len(app.Status.OperationState.SyncResult.Resources))
   551  			assert.Equal(t, OperationSucceeded, app.Status.OperationState.Phase)
   552  			assert.Equal(t, 3, len(app.Status.History))
   553  		})
   554  }
   555  
   556  func TestNamespacedComparisonFailsIfClusterNotAdded(t *testing.T) {
   557  	Given(t).
   558  		Path(guestbookPath).
   559  		SetTrackingMethod("annotation").
   560  		SetAppNamespace(AppNamespace()).
   561  		DestServer("https://not-registered-cluster/api").
   562  		When().
   563  		IgnoreErrors().
   564  		CreateApp().
   565  		Then().
   566  		Expect(DoesNotExist())
   567  }
   568  
   569  func TestNamespacedCannotSetInvalidPath(t *testing.T) {
   570  	Given(t).
   571  		Path(guestbookPath).
   572  		SetTrackingMethod("annotation").
   573  		SetAppNamespace(AppNamespace()).
   574  		When().
   575  		CreateApp().
   576  		IgnoreErrors().
   577  		AppSet("--path", "garbage").
   578  		Then().
   579  		Expect(Error("", "app path does not exist"))
   580  }
   581  
   582  func TestNamespacedManipulateApplicationResources(t *testing.T) {
   583  	ctx := Given(t)
   584  	ctx.
   585  		Path(guestbookPath).
   586  		SetTrackingMethod("annotation").
   587  		SetAppNamespace(AppNamespace()).
   588  		When().
   589  		CreateApp().
   590  		Sync().
   591  		Then().
   592  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   593  		And(func(app *Application) {
   594  			manifests, err := RunCli("app", "manifests", ctx.AppQualifiedName(), "--source", "live")
   595  			assert.NoError(t, err)
   596  			resources, err := kube.SplitYAML([]byte(manifests))
   597  			assert.NoError(t, err)
   598  
   599  			index := -1
   600  			for i := range resources {
   601  				if resources[i].GetKind() == kube.DeploymentKind {
   602  					index = i
   603  					break
   604  				}
   605  			}
   606  			assert.True(t, index > -1)
   607  
   608  			deployment := resources[index]
   609  
   610  			closer, client, err := ArgoCDClientset.NewApplicationClient()
   611  			assert.NoError(t, err)
   612  			defer io.Close(closer)
   613  
   614  			_, err = client.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{
   615  				Name:         &app.Name,
   616  				AppNamespace: pointer.String(AppNamespace()),
   617  				Group:        pointer.String(deployment.GroupVersionKind().Group),
   618  				Kind:         pointer.String(deployment.GroupVersionKind().Kind),
   619  				Version:      pointer.String(deployment.GroupVersionKind().Version),
   620  				Namespace:    pointer.String(deployment.GetNamespace()),
   621  				ResourceName: pointer.String(deployment.GetName()),
   622  			})
   623  			assert.NoError(t, err)
   624  		}).
   625  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync))
   626  }
   627  
   628  func TestNamespacedAppWithSecrets(t *testing.T) {
   629  	closer, client, err := ArgoCDClientset.NewApplicationClient()
   630  	assert.NoError(t, err)
   631  	defer io.Close(closer)
   632  
   633  	ctx := Given(t)
   634  	ctx.
   635  		Path("secrets").
   636  		SetAppNamespace(AppNamespace()).
   637  		SetTrackingMethod("annotation").
   638  		When().
   639  		CreateApp().
   640  		Sync().
   641  		Then().
   642  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   643  		And(func(app *Application) {
   644  			res := FailOnErr(client.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
   645  				Namespace:    &app.Spec.Destination.Namespace,
   646  				AppNamespace: pointer.String(AppNamespace()),
   647  				Kind:         pointer.String(kube.SecretKind),
   648  				Group:        pointer.String(""),
   649  				Name:         &app.Name,
   650  				Version:      pointer.String("v1"),
   651  				ResourceName: pointer.String("test-secret"),
   652  			})).(*applicationpkg.ApplicationResourceResponse)
   653  			assetSecretDataHidden(t, res.GetManifest())
   654  
   655  			manifests, err := client.GetManifests(context.Background(), &applicationpkg.ApplicationManifestQuery{
   656  				Name:         &app.Name,
   657  				AppNamespace: pointer.String(AppNamespace()),
   658  			})
   659  			errors.CheckError(err)
   660  
   661  			for _, manifest := range manifests.Manifests {
   662  				assetSecretDataHidden(t, manifest)
   663  			}
   664  
   665  			diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName())).(string)
   666  			assert.Empty(t, diffOutput)
   667  
   668  			// make sure resource update error does not print secret details
   669  			_, err = RunCli("app", "patch-resource", ctx.AppQualifiedName(), "--resource-name", "test-secret",
   670  				"--kind", "Secret", "--patch", `{"op": "add", "path": "/data", "value": "hello"}'`,
   671  				"--patch-type", "application/json-patch+json")
   672  			require.Error(t, err)
   673  			assert.Contains(t, err.Error(), fmt.Sprintf("failed to patch Secret %s/test-secret", DeploymentNamespace()))
   674  			assert.NotContains(t, err.Error(), "username")
   675  			assert.NotContains(t, err.Error(), "password")
   676  
   677  			// patch secret and make sure app is out of sync and diff detects the change
   678  			FailOnErr(KubeClientset.CoreV1().Secrets(DeploymentNamespace()).Patch(context.Background(),
   679  				"test-secret", types.JSONPatchType, []byte(`[
   680  	{"op": "remove", "path": "/data/username"},
   681  	{"op": "add", "path": "/stringData", "value": {"password": "foo"}}
   682  ]`), metav1.PatchOptions{}))
   683  		}).
   684  		When().
   685  		Refresh(RefreshTypeNormal).
   686  		Then().
   687  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   688  		And(func(app *Application) {
   689  			diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName())
   690  			assert.Error(t, err)
   691  			assert.Contains(t, diffOutput, "username: ++++++++")
   692  			assert.Contains(t, diffOutput, "password: ++++++++++++")
   693  
   694  			// local diff should ignore secrets
   695  			diffOutput = FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/secrets")).(string)
   696  			assert.Empty(t, diffOutput)
   697  
   698  			// ignore missing field and make sure diff shows no difference
   699  			app.Spec.IgnoreDifferences = []ResourceIgnoreDifferences{{
   700  				Kind: kube.SecretKind, JSONPointers: []string{"/data"},
   701  			}}
   702  			FailOnErr(client.UpdateSpec(context.Background(), &applicationpkg.ApplicationUpdateSpecRequest{Name: &app.Name, AppNamespace: pointer.String(AppNamespace()), Spec: &app.Spec}))
   703  		}).
   704  		When().
   705  		Refresh(RefreshTypeNormal).
   706  		Then().
   707  		Expect(OperationPhaseIs(OperationSucceeded)).
   708  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   709  		And(func(app *Application) {
   710  			diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName())).(string)
   711  			assert.Empty(t, diffOutput)
   712  		}).
   713  		// verify not committed secret also ignore during diffing
   714  		When().
   715  		WriteFile("secret3.yaml", `
   716  apiVersion: v1
   717  kind: Secret
   718  metadata:
   719    name: test-secret3
   720  stringData:
   721    username: test-username`).
   722  		Then().
   723  		And(func(app *Application) {
   724  			diffOutput := FailOnErr(RunCli("app", "diff", ctx.AppQualifiedName(), "--local", "testdata/secrets")).(string)
   725  			assert.Empty(t, diffOutput)
   726  		})
   727  }
   728  
   729  func TestNamespacedResourceDiffing(t *testing.T) {
   730  	ctx := Given(t)
   731  	ctx.
   732  		Path(guestbookPath).
   733  		SetTrackingMethod("annotation").
   734  		SetAppNamespace(AppNamespace()).
   735  		When().
   736  		CreateApp().
   737  		Sync().
   738  		Then().
   739  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   740  		And(func(app *Application) {
   741  			// Patch deployment
   742  			_, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Patch(context.Background(),
   743  				"guestbook-ui", types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/template/spec/containers/0/image", "value": "test" }]`), metav1.PatchOptions{})
   744  			assert.NoError(t, err)
   745  		}).
   746  		When().
   747  		Refresh(RefreshTypeNormal).
   748  		Then().
   749  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   750  		And(func(app *Application) {
   751  			diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook")
   752  			assert.Error(t, err)
   753  			assert.Contains(t, diffOutput, fmt.Sprintf("===== apps/Deployment %s/guestbook-ui ======", DeploymentNamespace()))
   754  		}).
   755  		Given().
   756  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {
   757  			IgnoreDifferences: OverrideIgnoreDiff{JSONPointers: []string{"/spec/template/spec/containers/0/image"}},
   758  		}}).
   759  		When().
   760  		Refresh(RefreshTypeNormal).
   761  		Then().
   762  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   763  		And(func(app *Application) {
   764  			diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", "testdata/guestbook")
   765  			assert.NoError(t, err)
   766  			assert.Empty(t, diffOutput)
   767  		}).
   768  		Given().
   769  		When().
   770  		// Now we migrate from client-side apply to server-side apply
   771  		// This is necessary, as starting with kubectl 1.26, all previously
   772  		// client-side owned fields have ownership migrated to the manager from
   773  		// the first ssa.
   774  		// More details: https://github.com/kubernetes/kubectl/issues/1337
   775  		PatchApp(`[{
   776  			"op": "add",
   777  			"path": "/spec/syncPolicy",
   778  			"value": { "syncOptions": ["ServerSideApply=true"] }
   779  			}]`).
   780  		Sync().
   781  		And(func() {
   782  			output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-")
   783  			assert.NoError(t, err)
   784  			assert.Contains(t, output, "serverside-applied")
   785  		}).
   786  		Refresh(RefreshTypeNormal).
   787  		Then().
   788  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   789  		Given().
   790  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {
   791  			IgnoreDifferences: OverrideIgnoreDiff{
   792  				ManagedFieldsManagers: []string{"revision-history-manager"},
   793  				JSONPointers:          []string{"/spec/template/spec/containers/0/image"},
   794  			},
   795  		}}).
   796  		When().
   797  		Refresh(RefreshTypeNormal).
   798  		Then().
   799  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   800  		Given().
   801  		When().
   802  		Sync().
   803  		PatchApp(`[{
   804  			"op": "add",
   805  			"path": "/spec/syncPolicy",
   806  			"value": { "syncOptions": ["RespectIgnoreDifferences=true"] }
   807  			}]`).
   808  		And(func() {
   809  			deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
   810  			assert.NoError(t, err)
   811  			assert.Equal(t, int32(3), *deployment.Spec.RevisionHistoryLimit)
   812  		}).
   813  		And(func() {
   814  			output, err := RunWithStdin(testdata.SSARevisionHistoryDeployment, "", "kubectl", "apply", "-n", DeploymentNamespace(), "--server-side=true", "--field-manager=revision-history-manager", "--validate=false", "--force-conflicts", "-f", "-")
   815  			assert.NoError(t, err)
   816  			assert.Contains(t, output, "serverside-applied")
   817  		}).
   818  		Then().
   819  		When().Refresh(RefreshTypeNormal).
   820  		Then().
   821  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   822  		And(func(app *Application) {
   823  			deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
   824  			assert.NoError(t, err)
   825  			assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit)
   826  		}).
   827  		When().Sync().Then().Expect(SyncStatusIs(SyncStatusCodeSynced)).
   828  		And(func(app *Application) {
   829  			deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
   830  			assert.NoError(t, err)
   831  			assert.Equal(t, int32(1), *deployment.Spec.RevisionHistoryLimit)
   832  		})
   833  }
   834  
   835  // func TestCRDs(t *testing.T) {
   836  // 	testEdgeCasesApplicationResources(t, "crd-creation", health.HealthStatusHealthy)
   837  // }
   838  
   839  func TestNamespacedKnownTypesInCRDDiffing(t *testing.T) {
   840  	dummiesGVR := schema.GroupVersionResource{Group: application.Group, Version: "v1alpha1", Resource: "dummies"}
   841  
   842  	ctx := Given(t)
   843  	ctx.
   844  		Path("crd-creation").
   845  		SetTrackingMethod("annotation").
   846  		SetAppNamespace(AppNamespace()).
   847  		When().CreateApp().Sync().Then().
   848  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)).
   849  		When().
   850  		And(func() {
   851  			dummyResIf := DynamicClientset.Resource(dummiesGVR).Namespace(DeploymentNamespace())
   852  			patchData := []byte(`{"spec":{"cpu": "2"}}`)
   853  			FailOnErr(dummyResIf.Patch(context.Background(), "dummy-crd-instance", types.MergePatchType, patchData, metav1.PatchOptions{}))
   854  		}).Refresh(RefreshTypeNormal).
   855  		Then().
   856  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
   857  		When().
   858  		And(func() {
   859  			SetResourceOverrides(map[string]ResourceOverride{
   860  				"argoproj.io/Dummy": {
   861  					KnownTypeFields: []KnownTypeField{{
   862  						Field: "spec",
   863  						Type:  "core/v1/ResourceList",
   864  					}},
   865  				},
   866  			})
   867  		}).
   868  		Refresh(RefreshTypeNormal).
   869  		Then().
   870  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   871  }
   872  
   873  // TODO(jannfis): This somehow doesn't work -- I suspect tracking method
   874  // func TestNamespacedDuplicatedResources(t *testing.T) {
   875  // 	testNSEdgeCasesApplicationResources(t, "duplicated-resources", health.HealthStatusHealthy)
   876  // }
   877  
   878  func TestNamespacedConfigMap(t *testing.T) {
   879  	testNSEdgeCasesApplicationResources(t, "config-map", health.HealthStatusHealthy, "my-map  Synced                configmap/my-map created")
   880  }
   881  
   882  func testNSEdgeCasesApplicationResources(t *testing.T, appPath string, statusCode health.HealthStatusCode, message ...string) {
   883  	ctx := Given(t)
   884  	expect := ctx.
   885  		Path(appPath).
   886  		SetTrackingMethod("annotation").
   887  		SetAppNamespace(AppNamespace()).
   888  		When().
   889  		CreateApp().
   890  		Sync().
   891  		Then().
   892  		Expect(OperationPhaseIs(OperationSucceeded)).
   893  		Expect(SyncStatusIs(SyncStatusCodeSynced))
   894  	for i := range message {
   895  		expect = expect.Expect(Success(message[i]))
   896  	}
   897  	expect.
   898  		Expect(HealthIs(statusCode)).
   899  		And(func(app *Application) {
   900  			diffOutput, err := RunCli("app", "diff", ctx.AppQualifiedName(), "--local-repo-root", ".", "--local", path.Join("testdata", appPath))
   901  			assert.Empty(t, diffOutput)
   902  			assert.NoError(t, err)
   903  		})
   904  }
   905  
   906  // // We don't have tracking label in namespaced tests, thus we need a unique
   907  // // resource action that modifies annotations instead of labels.
   908  // const nsActionsConfig = `discovery.lua: return { sample = {} }
   909  // definitions:
   910  // - name: sample
   911  //   action.lua: |
   912  //     obj.metadata.annotations.sample = 'test'
   913  //     return obj`
   914  
   915  func TestNamespacedResourceAction(t *testing.T) {
   916  	ctx := Given(t)
   917  	ctx.
   918  		Path(guestbookPath).
   919  		SetTrackingMethod("annotation").
   920  		SetAppNamespace(AppNamespace()).
   921  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}).
   922  		When().
   923  		CreateApp().
   924  		Sync().
   925  		Then().
   926  		And(func(app *Application) {
   927  
   928  			closer, client, err := ArgoCDClientset.NewApplicationClient()
   929  			assert.NoError(t, err)
   930  			defer io.Close(closer)
   931  
   932  			actions, err := client.ListResourceActions(context.Background(), &applicationpkg.ApplicationResourceRequest{
   933  				Name:         &app.Name,
   934  				AppNamespace: pointer.String(AppNamespace()),
   935  				Group:        pointer.String("apps"),
   936  				Kind:         pointer.String("Deployment"),
   937  				Version:      pointer.String("v1"),
   938  				Namespace:    pointer.String(DeploymentNamespace()),
   939  				ResourceName: pointer.String("guestbook-ui"),
   940  			})
   941  			assert.NoError(t, err)
   942  			assert.Equal(t, []*ResourceAction{{Name: "sample", Disabled: false}}, actions.Actions)
   943  
   944  			_, err = client.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{Name: &app.Name,
   945  				Group:        pointer.String("apps"),
   946  				Kind:         pointer.String("Deployment"),
   947  				Version:      pointer.String("v1"),
   948  				Namespace:    pointer.String(DeploymentNamespace()),
   949  				ResourceName: pointer.String("guestbook-ui"),
   950  				Action:       pointer.String("sample"),
   951  				AppNamespace: pointer.String(AppNamespace()),
   952  			})
   953  			assert.NoError(t, err)
   954  
   955  			deployment, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
   956  			assert.NoError(t, err)
   957  
   958  			assert.Equal(t, "test", deployment.Labels["sample"])
   959  		})
   960  }
   961  
   962  func TestNamespacedSyncResourceByLabel(t *testing.T) {
   963  	ctx := Given(t)
   964  	ctx.
   965  		Path(guestbookPath).
   966  		SetTrackingMethod("annotation").
   967  		SetAppNamespace(AppNamespace()).
   968  		When().
   969  		CreateApp().
   970  		Sync().
   971  		Then().
   972  		And(func(app *Application) {
   973  			_, _ = RunCli("app", "sync", ctx.AppQualifiedName(), "--label", fmt.Sprintf("app.kubernetes.io/instance=%s", app.Name))
   974  		}).
   975  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
   976  		And(func(app *Application) {
   977  			_, err := RunCli("app", "sync", ctx.AppQualifiedName(), "--label", "this-label=does-not-exist")
   978  			assert.Error(t, err)
   979  			assert.Contains(t, err.Error(), "level=fatal")
   980  		})
   981  }
   982  
   983  func TestNamespacedLocalManifestSync(t *testing.T) {
   984  	ctx := Given(t)
   985  	ctx.
   986  		Path(guestbookPath).
   987  		SetTrackingMethod("annotation").
   988  		SetAppNamespace(AppNamespace()).
   989  		When().
   990  		CreateApp().
   991  		Sync().
   992  		Then().
   993  		And(func(app *Application) {
   994  			res, _ := RunCli("app", "manifests", ctx.AppQualifiedName())
   995  			assert.Contains(t, res, "containerPort: 80")
   996  			assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2")
   997  		}).
   998  		Given().
   999  		LocalPath(guestbookPathLocal).
  1000  		When().
  1001  		Sync("--local-repo-root", ".").
  1002  		Then().
  1003  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1004  		And(func(app *Application) {
  1005  			res, _ := RunCli("app", "manifests", ctx.AppQualifiedName())
  1006  			assert.Contains(t, res, "containerPort: 81")
  1007  			assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.3")
  1008  		}).
  1009  		Given().
  1010  		LocalPath("").
  1011  		When().
  1012  		Sync().
  1013  		Then().
  1014  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1015  		And(func(app *Application) {
  1016  			res, _ := RunCli("app", "manifests", ctx.AppQualifiedName())
  1017  			assert.Contains(t, res, "containerPort: 80")
  1018  			assert.Contains(t, res, "image: quay.io/argoprojlabs/argocd-e2e-container:0.2")
  1019  		})
  1020  }
  1021  
  1022  func TestNamespacedLocalSync(t *testing.T) {
  1023  	Given(t).
  1024  		// we've got to use Helm as this uses kubeVersion
  1025  		Path("helm").
  1026  		SetTrackingMethod("annotation").
  1027  		SetAppNamespace(AppNamespace()).
  1028  		When().
  1029  		CreateApp().
  1030  		Then().
  1031  		And(func(app *Application) {
  1032  			FailOnErr(RunCli("app", "sync", app.QualifiedName(), "--local", "testdata/helm"))
  1033  		})
  1034  }
  1035  
  1036  func TestNamespacedNoLocalSyncWithAutosyncEnabled(t *testing.T) {
  1037  	Given(t).
  1038  		Path(guestbookPath).
  1039  		SetTrackingMethod("annotation").
  1040  		SetAppNamespace(AppNamespace()).
  1041  		When().
  1042  		CreateApp().
  1043  		Sync().
  1044  		Then().
  1045  		And(func(app *Application) {
  1046  			_, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated")
  1047  			assert.NoError(t, err)
  1048  
  1049  			_, err = RunCli("app", "sync", app.QualifiedName(), "--local", guestbookPathLocal)
  1050  			assert.Error(t, err)
  1051  			assert.Contains(t, err.Error(), "Cannot use local sync")
  1052  		})
  1053  }
  1054  
  1055  func TestNamespacedLocalSyncDryRunWithASEnabled(t *testing.T) {
  1056  	Given(t).
  1057  		Path(guestbookPath).
  1058  		SetTrackingMethod("annotation").
  1059  		SetAppNamespace(AppNamespace()).
  1060  		When().
  1061  		CreateApp().
  1062  		Sync().
  1063  		Then().
  1064  		And(func(app *Application) {
  1065  			_, err := RunCli("app", "set", app.QualifiedName(), "--sync-policy", "automated")
  1066  			assert.NoError(t, err)
  1067  
  1068  			appBefore := app.DeepCopy()
  1069  			_, err = RunCli("app", "sync", app.QualifiedName(), "--dry-run", "--local-repo-root", ".", "--local", guestbookPathLocal)
  1070  			assert.NoError(t, err)
  1071  
  1072  			appAfter := app.DeepCopy()
  1073  			assert.True(t, reflect.DeepEqual(appBefore, appAfter))
  1074  		})
  1075  }
  1076  
  1077  func TestNamespacedSyncAsync(t *testing.T) {
  1078  	Given(t).
  1079  		Path(guestbookPath).
  1080  		SetTrackingMethod("annotation").
  1081  		SetAppNamespace(AppNamespace()).
  1082  		Async(true).
  1083  		When().
  1084  		CreateApp().
  1085  		Sync().
  1086  		Then().
  1087  		Expect(Success("")).
  1088  		Expect(OperationPhaseIs(OperationSucceeded)).
  1089  		Expect(SyncStatusIs(SyncStatusCodeSynced))
  1090  }
  1091  
  1092  // assertResourceActions verifies if view/modify resource actions are successful/failing for given application
  1093  func assertNSResourceActions(t *testing.T, appName string, successful bool) {
  1094  	assertError := func(err error, message string) {
  1095  		if successful {
  1096  			assert.NoError(t, err)
  1097  		} else {
  1098  			if assert.Error(t, err) {
  1099  				assert.Contains(t, err.Error(), message)
  1100  			}
  1101  		}
  1102  	}
  1103  
  1104  	closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
  1105  	defer io.Close(closer)
  1106  
  1107  	deploymentResource, err := KubeClientset.AppsV1().Deployments(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{})
  1108  	require.NoError(t, err)
  1109  
  1110  	logs, err := cdClient.PodLogs(context.Background(), &applicationpkg.ApplicationPodLogsQuery{
  1111  		Group:        pointer.String("apps"),
  1112  		Kind:         pointer.String("Deployment"),
  1113  		Name:         &appName,
  1114  		AppNamespace: pointer.String(AppNamespace()),
  1115  		Namespace:    pointer.String(DeploymentNamespace()),
  1116  		Container:    pointer.String(""),
  1117  		SinceSeconds: pointer.Int64(0),
  1118  		TailLines:    pointer.Int64(0),
  1119  		Follow:       pointer.Bool(false),
  1120  	})
  1121  	require.NoError(t, err)
  1122  	_, err = logs.Recv()
  1123  	assertError(err, "EOF")
  1124  
  1125  	expectedError := fmt.Sprintf("Deployment apps guestbook-ui not found as part of application %s", appName)
  1126  
  1127  	_, err = cdClient.ListResourceEvents(context.Background(), &applicationpkg.ApplicationResourceEventsQuery{
  1128  		Name:              &appName,
  1129  		AppNamespace:      pointer.String(AppNamespace()),
  1130  		ResourceName:      pointer.String("guestbook-ui"),
  1131  		ResourceNamespace: pointer.String(DeploymentNamespace()),
  1132  		ResourceUID:       pointer.String(string(deploymentResource.UID)),
  1133  	})
  1134  	assertError(err, fmt.Sprintf("%s not found as part of application %s", "guestbook-ui", appName))
  1135  
  1136  	_, err = cdClient.GetResource(context.Background(), &applicationpkg.ApplicationResourceRequest{
  1137  		Name:         &appName,
  1138  		AppNamespace: pointer.String(AppNamespace()),
  1139  		ResourceName: pointer.String("guestbook-ui"),
  1140  		Namespace:    pointer.String(DeploymentNamespace()),
  1141  		Version:      pointer.String("v1"),
  1142  		Group:        pointer.String("apps"),
  1143  		Kind:         pointer.String("Deployment"),
  1144  	})
  1145  	assertError(err, expectedError)
  1146  
  1147  	_, err = cdClient.RunResourceAction(context.Background(), &applicationpkg.ResourceActionRunRequest{
  1148  		Name:         &appName,
  1149  		AppNamespace: pointer.String(AppNamespace()),
  1150  		ResourceName: pointer.String("guestbook-ui"),
  1151  		Namespace:    pointer.String(DeploymentNamespace()),
  1152  		Version:      pointer.String("v1"),
  1153  		Group:        pointer.String("apps"),
  1154  		Kind:         pointer.String("Deployment"),
  1155  		Action:       pointer.String("restart"),
  1156  	})
  1157  	assertError(err, expectedError)
  1158  
  1159  	_, err = cdClient.DeleteResource(context.Background(), &applicationpkg.ApplicationResourceDeleteRequest{
  1160  		Name:         &appName,
  1161  		AppNamespace: pointer.String(AppNamespace()),
  1162  		ResourceName: pointer.String("guestbook-ui"),
  1163  		Namespace:    pointer.String(DeploymentNamespace()),
  1164  		Version:      pointer.String("v1"),
  1165  		Group:        pointer.String("apps"),
  1166  		Kind:         pointer.String("Deployment"),
  1167  	})
  1168  	assertError(err, expectedError)
  1169  }
  1170  
  1171  func TestNamespacedPermissions(t *testing.T) {
  1172  	appCtx := Given(t)
  1173  	projName := "argo-project"
  1174  	projActions := projectFixture.
  1175  		Given(t).
  1176  		Name(projName).
  1177  		SourceNamespaces([]string{AppNamespace()}).
  1178  		When().
  1179  		Create()
  1180  
  1181  	sourceError := fmt.Sprintf("application repo %s is not permitted in project 'argo-project'", RepoURL(RepoURLTypeFile))
  1182  	destinationError := fmt.Sprintf("application destination server '%s' and namespace '%s' do not match any of the allowed destinations in project 'argo-project'", KubernetesInternalAPIServerAddr, DeploymentNamespace())
  1183  
  1184  	appCtx.
  1185  		Path("guestbook-logs").
  1186  		SetTrackingMethod("annotation").
  1187  		SetAppNamespace(AppNamespace()).
  1188  		Project(projName).
  1189  		When().
  1190  		IgnoreErrors().
  1191  		// ensure app is not created if project permissions are missing
  1192  		CreateApp().
  1193  		Then().
  1194  		Expect(Error("", sourceError)).
  1195  		Expect(Error("", destinationError)).
  1196  		When().
  1197  		DoNotIgnoreErrors().
  1198  		// add missing permissions, create and sync app
  1199  		And(func() {
  1200  			projActions.AddDestination("*", "*")
  1201  			projActions.AddSource("*")
  1202  		}).
  1203  		CreateApp().
  1204  		Sync().
  1205  		Then().
  1206  		// make sure application resource actiions are successful
  1207  		And(func(app *Application) {
  1208  			assertNSResourceActions(t, app.Name, true)
  1209  		}).
  1210  		When().
  1211  		// remove projet permissions and "refresh" app
  1212  		And(func() {
  1213  			projActions.UpdateProject(func(proj *AppProject) {
  1214  				proj.Spec.Destinations = nil
  1215  				proj.Spec.SourceRepos = nil
  1216  			})
  1217  		}).
  1218  		Refresh(RefreshTypeNormal).
  1219  		Then().
  1220  		// ensure app resource tree is empty when source/destination permissions are missing
  1221  		Expect(Condition(ApplicationConditionInvalidSpecError, destinationError)).
  1222  		Expect(Condition(ApplicationConditionInvalidSpecError, sourceError)).
  1223  		And(func(app *Application) {
  1224  			closer, cdClient := ArgoCDClientset.NewApplicationClientOrDie()
  1225  			defer io.Close(closer)
  1226  			tree, err := cdClient.ResourceTree(context.Background(), &applicationpkg.ResourcesQuery{ApplicationName: &app.Name, AppNamespace: &app.Namespace})
  1227  			require.NoError(t, err)
  1228  			assert.Len(t, tree.Nodes, 0)
  1229  			assert.Len(t, tree.OrphanedNodes, 0)
  1230  		}).
  1231  		When().
  1232  		// add missing permissions but deny management of Deployment kind
  1233  		And(func() {
  1234  			projActions.
  1235  				AddDestination("*", "*").
  1236  				AddSource("*").
  1237  				UpdateProject(func(proj *AppProject) {
  1238  					proj.Spec.NamespaceResourceBlacklist = []metav1.GroupKind{{Group: "*", Kind: "Deployment"}}
  1239  				})
  1240  		}).
  1241  		Refresh(RefreshTypeNormal).
  1242  		Then().
  1243  		// make sure application resource actiions are failing
  1244  		And(func(app *Application) {
  1245  			assertNSResourceActions(t, app.Name, false)
  1246  		})
  1247  }
  1248  
  1249  func TestNamespacedPermissionWithScopedRepo(t *testing.T) {
  1250  	projName := "argo-project"
  1251  	fixture.EnsureCleanState(t)
  1252  	projectFixture.
  1253  		Given(t).
  1254  		Name(projName).
  1255  		SourceNamespaces([]string{AppNamespace()}).
  1256  		Destination("*,*").
  1257  		When().
  1258  		Create()
  1259  
  1260  	repoFixture.Given(t, true).
  1261  		When().
  1262  		Path(RepoURL(RepoURLTypeFile)).
  1263  		Project(projName).
  1264  		Create()
  1265  
  1266  	GivenWithSameState(t).
  1267  		Project(projName).
  1268  		RepoURLType(RepoURLTypeFile).
  1269  		Path("two-nice-pods").
  1270  		SetTrackingMethod("annotation").
  1271  		SetAppNamespace(AppNamespace()).
  1272  		When().
  1273  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`).
  1274  		CreateApp().
  1275  		Sync().
  1276  		Then().
  1277  		Expect(OperationPhaseIs(OperationSucceeded)).
  1278  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1279  		When().
  1280  		DeleteFile("pod-1.yaml").
  1281  		Refresh(RefreshTypeHard).
  1282  		IgnoreErrors().
  1283  		Sync().
  1284  		Then().
  1285  		Expect(OperationPhaseIs(OperationSucceeded)).
  1286  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
  1287  		Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync))
  1288  }
  1289  
  1290  func TestNamespacedPermissionDeniedWithScopedRepo(t *testing.T) {
  1291  	projName := "argo-project"
  1292  	projectFixture.
  1293  		Given(t).
  1294  		Name(projName).
  1295  		Destination("*,*").
  1296  		SourceNamespaces([]string{AppNamespace()}).
  1297  		When().
  1298  		Create()
  1299  
  1300  	repoFixture.Given(t, true).
  1301  		When().
  1302  		Path(RepoURL(RepoURLTypeFile)).
  1303  		Create()
  1304  
  1305  	GivenWithSameState(t).
  1306  		Project(projName).
  1307  		RepoURLType(RepoURLTypeFile).
  1308  		SetTrackingMethod("annotation").
  1309  		SetAppNamespace(AppNamespace()).
  1310  		Path("two-nice-pods").
  1311  		When().
  1312  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`).
  1313  		IgnoreErrors().
  1314  		CreateApp().
  1315  		Then().
  1316  		Expect(Error("", "is not permitted in project"))
  1317  
  1318  }
  1319  
  1320  // make sure that if we deleted a resource from the app, it is not pruned if annotated with Prune=false
  1321  func TestNamespacedSyncOptionPruneFalse(t *testing.T) {
  1322  	Given(t).
  1323  		Path("two-nice-pods").
  1324  		SetTrackingMethod("annotation").
  1325  		SetAppNamespace(AppNamespace()).
  1326  		When().
  1327  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Prune=false"}}]`).
  1328  		CreateApp().
  1329  		Sync().
  1330  		Then().
  1331  		Expect(OperationPhaseIs(OperationSucceeded)).
  1332  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1333  		When().
  1334  		DeleteFile("pod-1.yaml").
  1335  		Refresh(RefreshTypeHard).
  1336  		IgnoreErrors().
  1337  		Sync().
  1338  		Then().
  1339  		Expect(OperationPhaseIs(OperationSucceeded)).
  1340  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
  1341  		Expect(ResourceSyncStatusIs("Pod", "pod-1", SyncStatusCodeOutOfSync))
  1342  }
  1343  
  1344  // 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
  1345  func TestNamespacedSyncOptionValidateFalse(t *testing.T) {
  1346  
  1347  	Given(t).
  1348  		Path("crd-validation").
  1349  		SetTrackingMethod("annotation").
  1350  		SetAppNamespace(AppNamespace()).
  1351  		When().
  1352  		CreateApp().
  1353  		Then().
  1354  		Expect(Success("")).
  1355  		When().
  1356  		IgnoreErrors().
  1357  		Sync().
  1358  		Then().
  1359  		// client error. K8s API changed error message w/ 1.25, so for now, we need to check both
  1360  		Expect(ErrorRegex("error validating data|of type int32", "")).
  1361  		When().
  1362  		PatchFile("deployment.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Validate=false"}}]`).
  1363  		Sync().
  1364  		Then().
  1365  		// server error
  1366  		Expect(Error("cannot be handled as a Deployment", ""))
  1367  }
  1368  
  1369  // make sure that, if we have a resource that needs pruning, but we're ignoring it, the app is in-sync
  1370  func TestNamespacedCompareOptionIgnoreExtraneous(t *testing.T) {
  1371  	Given(t).
  1372  		Prune(false).
  1373  		SetTrackingMethod("annotation").
  1374  		SetAppNamespace(AppNamespace()).
  1375  		Path("two-nice-pods").
  1376  		When().
  1377  		PatchFile("pod-1.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/compare-options": "IgnoreExtraneous"}}]`).
  1378  		CreateApp().
  1379  		Sync().
  1380  		Then().
  1381  		Expect(OperationPhaseIs(OperationSucceeded)).
  1382  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1383  		When().
  1384  		DeleteFile("pod-1.yaml").
  1385  		Refresh(RefreshTypeHard).
  1386  		Then().
  1387  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1388  		And(func(app *Application) {
  1389  			assert.Len(t, app.Status.Resources, 2)
  1390  			statusByName := map[string]SyncStatusCode{}
  1391  			for _, r := range app.Status.Resources {
  1392  				statusByName[r.Name] = r.Status
  1393  			}
  1394  			assert.Equal(t, SyncStatusCodeOutOfSync, statusByName["pod-1"])
  1395  			assert.Equal(t, SyncStatusCodeSynced, statusByName["pod-2"])
  1396  		}).
  1397  		When().
  1398  		Sync().
  1399  		Then().
  1400  		Expect(OperationPhaseIs(OperationSucceeded)).
  1401  		Expect(SyncStatusIs(SyncStatusCodeSynced))
  1402  }
  1403  
  1404  func TestNamespacedSelfManagedApps(t *testing.T) {
  1405  
  1406  	Given(t).
  1407  		Path("self-managed-app").
  1408  		SetTrackingMethod("annotation").
  1409  		SetAppNamespace(AppNamespace()).
  1410  		When().
  1411  		PatchFile("resources.yaml", fmt.Sprintf(`[{"op": "replace", "path": "/spec/source/repoURL", "value": "%s"}]`, RepoURL(RepoURLTypeFile))).
  1412  		CreateApp().
  1413  		Sync().
  1414  		Then().
  1415  		Expect(OperationPhaseIs(OperationSucceeded)).
  1416  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1417  		And(func(a *Application) {
  1418  			ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
  1419  			defer cancel()
  1420  
  1421  			reconciledCount := 0
  1422  			var lastReconciledAt *metav1.Time
  1423  			for event := range ArgoCDClientset.WatchApplicationWithRetry(ctx, a.QualifiedName(), a.ResourceVersion) {
  1424  				reconciledAt := event.Application.Status.ReconciledAt
  1425  				if reconciledAt == nil {
  1426  					reconciledAt = &metav1.Time{}
  1427  				}
  1428  				if lastReconciledAt != nil && !lastReconciledAt.Equal(reconciledAt) {
  1429  					reconciledCount = reconciledCount + 1
  1430  				}
  1431  				lastReconciledAt = reconciledAt
  1432  			}
  1433  
  1434  			assert.True(t, reconciledCount < 3, "Application was reconciled too many times")
  1435  		})
  1436  }
  1437  
  1438  func TestNamespacedExcludedResource(t *testing.T) {
  1439  	Given(t).
  1440  		ResourceOverrides(map[string]ResourceOverride{"apps/Deployment": {Actions: actionsConfig}}).
  1441  		SetTrackingMethod("annotation").
  1442  		SetAppNamespace(AppNamespace()).
  1443  		Path(guestbookPath).
  1444  		ResourceFilter(settings.ResourcesFilter{
  1445  			ResourceExclusions: []settings.FilteredResource{{Kinds: []string{kube.DeploymentKind}}},
  1446  		}).
  1447  		When().
  1448  		CreateApp().
  1449  		Sync().
  1450  		Refresh(RefreshTypeNormal).
  1451  		Then().
  1452  		Expect(Condition(ApplicationConditionExcludedResourceWarning, "Resource apps/Deployment guestbook-ui is excluded in the settings"))
  1453  }
  1454  
  1455  func TestNamespacedRevisionHistoryLimit(t *testing.T) {
  1456  	Given(t).
  1457  		Path("config-map").
  1458  		SetTrackingMethod("annotation").
  1459  		SetAppNamespace(AppNamespace()).
  1460  		When().
  1461  		CreateApp().
  1462  		Sync().
  1463  		Then().
  1464  		Expect(OperationPhaseIs(OperationSucceeded)).
  1465  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1466  		And(func(app *Application) {
  1467  			assert.Len(t, app.Status.History, 1)
  1468  		}).
  1469  		When().
  1470  		AppSet("--revision-history-limit", "1").
  1471  		Sync().
  1472  		Then().
  1473  		Expect(OperationPhaseIs(OperationSucceeded)).
  1474  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1475  		And(func(app *Application) {
  1476  			assert.Len(t, app.Status.History, 1)
  1477  		})
  1478  }
  1479  
  1480  func TestNamespacedOrphanedResource(t *testing.T) {
  1481  	SkipOnEnv(t, "OPENSHIFT")
  1482  	Given(t).
  1483  		ProjectSpec(AppProjectSpec{
  1484  			SourceRepos:       []string{"*"},
  1485  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1486  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)},
  1487  			SourceNamespaces:  []string{AppNamespace()},
  1488  		}).
  1489  		SetTrackingMethod("annotation").
  1490  		SetAppNamespace(AppNamespace()).
  1491  		Path(guestbookPath).
  1492  		When().
  1493  		CreateApp().
  1494  		Sync().
  1495  		Then().
  1496  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1497  		Expect(NoConditions()).
  1498  		When().
  1499  		And(func() {
  1500  			FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
  1501  				ObjectMeta: metav1.ObjectMeta{
  1502  					Name: "orphaned-configmap",
  1503  				},
  1504  			}, metav1.CreateOptions{}))
  1505  		}).
  1506  		Refresh(RefreshTypeNormal).
  1507  		Then().
  1508  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1509  		And(func(app *Application) {
  1510  			output, err := RunCli("app", "resources", app.QualifiedName())
  1511  			assert.NoError(t, err)
  1512  			assert.Contains(t, output, "orphaned-configmap")
  1513  		}).
  1514  		Given().
  1515  		ProjectSpec(AppProjectSpec{
  1516  			SourceRepos:       []string{"*"},
  1517  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1518  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Group: "Test", Kind: "ConfigMap"}}},
  1519  			SourceNamespaces:  []string{AppNamespace()},
  1520  		}).
  1521  		When().
  1522  		Refresh(RefreshTypeNormal).
  1523  		Then().
  1524  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1525  		And(func(app *Application) {
  1526  			output, err := RunCli("app", "resources", app.QualifiedName())
  1527  			assert.NoError(t, err)
  1528  			assert.Contains(t, output, "orphaned-configmap")
  1529  		}).
  1530  		Given().
  1531  		ProjectSpec(AppProjectSpec{
  1532  			SourceRepos:       []string{"*"},
  1533  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1534  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap"}}},
  1535  			SourceNamespaces:  []string{AppNamespace()},
  1536  		}).
  1537  		When().
  1538  		Refresh(RefreshTypeNormal).
  1539  		Then().
  1540  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1541  		Expect(NoConditions()).
  1542  		And(func(app *Application) {
  1543  			output, err := RunCli("app", "resources", app.QualifiedName())
  1544  			assert.NoError(t, err)
  1545  			assert.NotContains(t, output, "orphaned-configmap")
  1546  		}).
  1547  		Given().
  1548  		ProjectSpec(AppProjectSpec{
  1549  			SourceRepos:       []string{"*"},
  1550  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1551  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true), Ignore: []OrphanedResourceKey{{Kind: "ConfigMap", Name: "orphaned-configmap"}}},
  1552  			SourceNamespaces:  []string{AppNamespace()},
  1553  		}).
  1554  		When().
  1555  		Refresh(RefreshTypeNormal).
  1556  		Then().
  1557  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1558  		Expect(NoConditions()).
  1559  		And(func(app *Application) {
  1560  			output, err := RunCli("app", "resources", app.QualifiedName())
  1561  			assert.NoError(t, err)
  1562  			assert.NotContains(t, output, "orphaned-configmap")
  1563  		}).
  1564  		Given().
  1565  		ProjectSpec(AppProjectSpec{
  1566  			SourceRepos:       []string{"*"},
  1567  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1568  			OrphanedResources: nil,
  1569  			SourceNamespaces:  []string{AppNamespace()},
  1570  		}).
  1571  		When().
  1572  		Refresh(RefreshTypeNormal).
  1573  		Then().
  1574  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1575  		Expect(NoConditions())
  1576  }
  1577  
  1578  func TestNamespacedNotPermittedResources(t *testing.T) {
  1579  	ctx := Given(t)
  1580  	ctx.SetAppNamespace(AppNamespace())
  1581  	pathType := networkingv1.PathTypePrefix
  1582  	ingress := &networkingv1.Ingress{
  1583  		ObjectMeta: metav1.ObjectMeta{
  1584  			Name: "sample-ingress",
  1585  			Annotations: map[string]string{
  1586  				common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:networking/Ingress:%s/sample-ingress", AppNamespace(), ctx.AppName(), DeploymentNamespace()),
  1587  			},
  1588  		},
  1589  		Spec: networkingv1.IngressSpec{
  1590  			Rules: []networkingv1.IngressRule{{
  1591  				IngressRuleValue: networkingv1.IngressRuleValue{
  1592  					HTTP: &networkingv1.HTTPIngressRuleValue{
  1593  						Paths: []networkingv1.HTTPIngressPath{{
  1594  							Path: "/",
  1595  							Backend: networkingv1.IngressBackend{
  1596  								Service: &networkingv1.IngressServiceBackend{
  1597  									Name: "guestbook-ui",
  1598  									Port: networkingv1.ServiceBackendPort{Number: 80},
  1599  								},
  1600  							},
  1601  							PathType: &pathType,
  1602  						}},
  1603  					},
  1604  				},
  1605  			}},
  1606  		},
  1607  	}
  1608  	defer func() {
  1609  		log.Infof("Ingress 'sample-ingress' deleted from %s", TestNamespace())
  1610  		CheckError(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Delete(context.Background(), "sample-ingress", metav1.DeleteOptions{}))
  1611  	}()
  1612  
  1613  	svc := &v1.Service{
  1614  		ObjectMeta: metav1.ObjectMeta{
  1615  			Name: "guestbook-ui",
  1616  			Annotations: map[string]string{
  1617  				common.AnnotationKeyAppInstance: fmt.Sprintf("%s_%s:Service:%s/guesbook-ui", TestNamespace(), ctx.AppQualifiedName(), DeploymentNamespace()),
  1618  			},
  1619  		},
  1620  		Spec: v1.ServiceSpec{
  1621  			Ports: []v1.ServicePort{{
  1622  				Port:       80,
  1623  				TargetPort: intstr.IntOrString{Type: intstr.Int, IntVal: 80},
  1624  			}},
  1625  			Selector: map[string]string{
  1626  				"app": "guestbook-ui",
  1627  			},
  1628  		},
  1629  	}
  1630  
  1631  	ctx.ProjectSpec(AppProjectSpec{
  1632  		SourceRepos:      []string{"*"},
  1633  		Destinations:     []ApplicationDestination{{Namespace: DeploymentNamespace(), Server: "*"}},
  1634  		SourceNamespaces: []string{AppNamespace()},
  1635  		NamespaceResourceBlacklist: []metav1.GroupKind{
  1636  			{Group: "", Kind: "Service"},
  1637  		}}).
  1638  		And(func() {
  1639  			FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Create(context.Background(), ingress, metav1.CreateOptions{}))
  1640  			FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Create(context.Background(), svc, metav1.CreateOptions{}))
  1641  		}).
  1642  		Path(guestbookPath).
  1643  		When().
  1644  		CreateApp().
  1645  		Then().
  1646  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
  1647  		And(func(app *Application) {
  1648  			statusByKind := make(map[string]ResourceStatus)
  1649  			for _, res := range app.Status.Resources {
  1650  				statusByKind[res.Kind] = res
  1651  			}
  1652  			_, hasIngress := statusByKind[kube.IngressKind]
  1653  			assert.False(t, hasIngress, "Ingress is prohibited not managed object and should be even visible to user")
  1654  			serviceStatus := statusByKind[kube.ServiceKind]
  1655  			assert.Equal(t, serviceStatus.Status, SyncStatusCodeUnknown, "Service is prohibited managed resource so should be set to Unknown")
  1656  			deploymentStatus := statusByKind[kube.DeploymentKind]
  1657  			assert.Equal(t, deploymentStatus.Status, SyncStatusCodeOutOfSync)
  1658  		}).
  1659  		When().
  1660  		Delete(true).
  1661  		Then().
  1662  		Expect(DoesNotExist())
  1663  
  1664  	// Make sure prohibited resources are not deleted during application deletion
  1665  	FailOnErr(KubeClientset.NetworkingV1().Ingresses(TestNamespace()).Get(context.Background(), "sample-ingress", metav1.GetOptions{}))
  1666  	FailOnErr(KubeClientset.CoreV1().Services(DeploymentNamespace()).Get(context.Background(), "guestbook-ui", metav1.GetOptions{}))
  1667  }
  1668  
  1669  func TestNamespacedSyncWithInfos(t *testing.T) {
  1670  	expectedInfo := make([]*Info, 2)
  1671  	expectedInfo[0] = &Info{Name: "name1", Value: "val1"}
  1672  	expectedInfo[1] = &Info{Name: "name2", Value: "val2"}
  1673  
  1674  	Given(t).
  1675  		SetAppNamespace(AppNamespace()).
  1676  		SetTrackingMethod("annotation").
  1677  		Path(guestbookPath).
  1678  		When().
  1679  		CreateApp().
  1680  		Then().
  1681  		And(func(app *Application) {
  1682  			_, err := RunCli("app", "sync", app.QualifiedName(),
  1683  				"--info", fmt.Sprintf("%s=%s", expectedInfo[0].Name, expectedInfo[0].Value),
  1684  				"--info", fmt.Sprintf("%s=%s", expectedInfo[1].Name, expectedInfo[1].Value))
  1685  			assert.NoError(t, err)
  1686  		}).
  1687  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1688  		And(func(app *Application) {
  1689  			assert.ElementsMatch(t, app.Status.OperationState.Operation.Info, expectedInfo)
  1690  		})
  1691  }
  1692  
  1693  // Given: argocd app create does not provide --dest-namespace
  1694  //
  1695  //	Manifest contains resource console which does not require namespace
  1696  //
  1697  // Expect: no app.Status.Conditions
  1698  func TestNamespacedCreateAppWithNoNameSpaceForGlobalResource(t *testing.T) {
  1699  	Given(t).
  1700  		SetAppNamespace(AppNamespace()).
  1701  		SetTrackingMethod("annotation").
  1702  		Path(globalWithNoNameSpace).
  1703  		When().
  1704  		CreateWithNoNameSpace().
  1705  		Then().
  1706  		And(func(app *Application) {
  1707  			time.Sleep(500 * time.Millisecond)
  1708  			app, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{})
  1709  			assert.NoError(t, err)
  1710  			assert.Len(t, app.Status.Conditions, 0)
  1711  		})
  1712  }
  1713  
  1714  // Given: argocd app create does not provide --dest-namespace
  1715  //
  1716  //	Manifest contains resource deployment, and service which requires namespace
  1717  //	Deployment and service do not have namespace in manifest
  1718  //
  1719  // Expect: app.Status.Conditions for deployment ans service which does not have namespace in manifest
  1720  func TestNamespacedCreateAppWithNoNameSpaceWhenRequired(t *testing.T) {
  1721  	Given(t).
  1722  		SetAppNamespace(AppNamespace()).
  1723  		SetTrackingMethod("annotation").
  1724  		Path(guestbookPath).
  1725  		When().
  1726  		CreateWithNoNameSpace().
  1727  		Refresh(RefreshTypeNormal).
  1728  		Then().
  1729  		And(func(app *Application) {
  1730  			updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{})
  1731  			require.NoError(t, err)
  1732  
  1733  			assert.Len(t, updatedApp.Status.Conditions, 2)
  1734  			assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError)
  1735  			assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError)
  1736  		})
  1737  }
  1738  
  1739  // Given: argocd app create does not provide --dest-namespace
  1740  //
  1741  //	Manifest contains resource deployment, and service which requires namespace
  1742  //	Some deployment and service has namespace in manifest
  1743  //	Some deployment and service does not have namespace in manifest
  1744  //
  1745  // Expect: app.Status.Conditions for deployment and service which does not have namespace in manifest
  1746  func TestNamespacedCreateAppWithNoNameSpaceWhenRequired2(t *testing.T) {
  1747  	Given(t).
  1748  		SetAppNamespace(AppNamespace()).
  1749  		SetTrackingMethod("annotation").
  1750  		Path(guestbookWithNamespace).
  1751  		When().
  1752  		CreateWithNoNameSpace().
  1753  		Refresh(RefreshTypeNormal).
  1754  		Then().
  1755  		And(func(app *Application) {
  1756  			updatedApp, err := AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Get(context.Background(), app.Name, metav1.GetOptions{})
  1757  			require.NoError(t, err)
  1758  
  1759  			assert.Len(t, updatedApp.Status.Conditions, 2)
  1760  			assert.Equal(t, updatedApp.Status.Conditions[0].Type, ApplicationConditionInvalidSpecError)
  1761  			assert.Equal(t, updatedApp.Status.Conditions[1].Type, ApplicationConditionInvalidSpecError)
  1762  		})
  1763  }
  1764  
  1765  func TestNamespacedListResource(t *testing.T) {
  1766  	SkipOnEnv(t, "OPENSHIFT")
  1767  	Given(t).
  1768  		SetAppNamespace(AppNamespace()).
  1769  		SetTrackingMethod("annotation").
  1770  		ProjectSpec(AppProjectSpec{
  1771  			SourceRepos:       []string{"*"},
  1772  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1773  			OrphanedResources: &OrphanedResourcesMonitorSettings{Warn: pointer.Bool(true)},
  1774  			SourceNamespaces:  []string{AppNamespace()},
  1775  		}).
  1776  		Path(guestbookPath).
  1777  		When().
  1778  		CreateApp().
  1779  		Sync().
  1780  		Then().
  1781  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1782  		Expect(NoConditions()).
  1783  		When().
  1784  		And(func() {
  1785  			FailOnErr(KubeClientset.CoreV1().ConfigMaps(DeploymentNamespace()).Create(context.Background(), &v1.ConfigMap{
  1786  				ObjectMeta: metav1.ObjectMeta{
  1787  					Name: "orphaned-configmap",
  1788  				},
  1789  			}, metav1.CreateOptions{}))
  1790  		}).
  1791  		Refresh(RefreshTypeNormal).
  1792  		Then().
  1793  		Expect(Condition(ApplicationConditionOrphanedResourceWarning, "Application has 1 orphaned resources")).
  1794  		And(func(app *Application) {
  1795  			output, err := RunCli("app", "resources", app.QualifiedName())
  1796  			assert.NoError(t, err)
  1797  			assert.Contains(t, output, "orphaned-configmap")
  1798  			assert.Contains(t, output, "guestbook-ui")
  1799  		}).
  1800  		And(func(app *Application) {
  1801  			output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=true")
  1802  			assert.NoError(t, err)
  1803  			assert.Contains(t, output, "orphaned-configmap")
  1804  			assert.NotContains(t, output, "guestbook-ui")
  1805  		}).
  1806  		And(func(app *Application) {
  1807  			output, err := RunCli("app", "resources", app.QualifiedName(), "--orphaned=false")
  1808  			assert.NoError(t, err)
  1809  			assert.NotContains(t, output, "orphaned-configmap")
  1810  			assert.Contains(t, output, "guestbook-ui")
  1811  		}).
  1812  		Given().
  1813  		ProjectSpec(AppProjectSpec{
  1814  			SourceRepos:       []string{"*"},
  1815  			Destinations:      []ApplicationDestination{{Namespace: "*", Server: "*"}},
  1816  			OrphanedResources: nil,
  1817  			SourceNamespaces:  []string{AppNamespace()},
  1818  		}).
  1819  		When().
  1820  		Refresh(RefreshTypeNormal).
  1821  		Then().
  1822  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  1823  		Expect(NoConditions())
  1824  }
  1825  
  1826  // Given application is set with --sync-option CreateNamespace=true
  1827  //
  1828  //		application --dest-namespace does not exist
  1829  //
  1830  //	    Verify application --dest-namespace is created
  1831  //
  1832  //		application sync successful
  1833  //		when application is deleted, --dest-namespace is not deleted
  1834  func TestNamespacedNamespaceAutoCreation(t *testing.T) {
  1835  	SkipOnEnv(t, "OPENSHIFT")
  1836  	updatedNamespace := getNewNamespace(t)
  1837  	defer func() {
  1838  		if !t.Skipped() {
  1839  			_, err := Run("", "kubectl", "delete", "namespace", updatedNamespace)
  1840  			assert.NoError(t, err)
  1841  		}
  1842  	}()
  1843  	Given(t).
  1844  		SetAppNamespace(AppNamespace()).
  1845  		SetTrackingMethod("annotation").
  1846  		Timeout(30).
  1847  		Path("guestbook").
  1848  		When().
  1849  		CreateApp("--sync-option", "CreateNamespace=true").
  1850  		Then().
  1851  		Expect(NoNamespace(updatedNamespace)).
  1852  		When().
  1853  		AppSet("--dest-namespace", updatedNamespace).
  1854  		Sync().
  1855  		Then().
  1856  		Expect(Success("")).
  1857  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1858  		Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1859  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)).
  1860  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)).
  1861  		When().
  1862  		Delete(true).
  1863  		Then().
  1864  		Expect(Success("")).
  1865  		And(func(app *Application) {
  1866  			// Verify delete app does not delete the namespace auto created
  1867  			output, err := Run("", "kubectl", "get", "namespace", updatedNamespace)
  1868  			assert.NoError(t, err)
  1869  			assert.Contains(t, output, updatedNamespace)
  1870  		})
  1871  }
  1872  
  1873  // Given application is set with --sync-option CreateNamespace=true
  1874  //
  1875  //		application --dest-namespace does not exist
  1876  //
  1877  //	    Verify application --dest-namespace is created with managedNamespaceMetadata
  1878  func TestNamespacedNamespaceAutoCreationWithMetadata(t *testing.T) {
  1879  	SkipOnEnv(t, "OPENSHIFT")
  1880  	updatedNamespace := getNewNamespace(t)
  1881  	defer func() {
  1882  		if !t.Skipped() {
  1883  			_, err := Run("", "kubectl", "delete", "namespace", updatedNamespace)
  1884  			assert.NoError(t, err)
  1885  		}
  1886  	}()
  1887  	ctx := Given(t)
  1888  	ctx.
  1889  		SetAppNamespace(AppNamespace()).
  1890  		SetTrackingMethod("annotation").
  1891  		Timeout(30).
  1892  		Path("guestbook").
  1893  		When().
  1894  		CreateFromFile(func(app *Application) {
  1895  			app.Spec.SyncPolicy = &SyncPolicy{
  1896  				SyncOptions: SyncOptions{"CreateNamespace=true"},
  1897  				ManagedNamespaceMetadata: &ManagedNamespaceMetadata{
  1898  					Labels:      map[string]string{"foo": "bar"},
  1899  					Annotations: map[string]string{"bar": "bat"},
  1900  				}}
  1901  		}).
  1902  		Then().
  1903  		Expect(NoNamespace(updatedNamespace)).
  1904  		When().
  1905  		AppSet("--dest-namespace", updatedNamespace).
  1906  		Sync().
  1907  		Then().
  1908  		Expect(Success("")).
  1909  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  1910  			assert.Empty(t, app.Status.Conditions)
  1911  
  1912  			delete(ns.Labels, "kubernetes.io/metadata.name")
  1913  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  1914  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  1915  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  1916  
  1917  			assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels)
  1918  			assert.Equal(t, map[string]string{"bar": "bat", "argocd.argoproj.io/sync-options": "ServerSideApply=true"}, ns.Annotations)
  1919  			assert.Equal(t, map[string]string{"foo": "bar"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Labels)
  1920  			assert.Equal(t, map[string]string{"bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations)
  1921  		})).
  1922  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1923  		Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  1924  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced)).
  1925  		When().
  1926  		And(func() {
  1927  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(),
  1928  				ctx.GetName(), types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/syncPolicy/managedNamespaceMetadata/labels", "value": {"new":"label"} }]`), metav1.PatchOptions{}))
  1929  		}).
  1930  		Sync().
  1931  		Then().
  1932  		Expect(Success("")).
  1933  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  1934  
  1935  			delete(ns.Labels, "kubernetes.io/metadata.name")
  1936  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  1937  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  1938  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  1939  
  1940  			assert.Equal(t, map[string]string{"new": "label"}, ns.Labels)
  1941  			assert.Equal(t, map[string]string{"bar": "bat", "argocd.argoproj.io/sync-options": "ServerSideApply=true"}, ns.Annotations)
  1942  			assert.Equal(t, map[string]string{"new": "label"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Labels)
  1943  			assert.Equal(t, map[string]string{"bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations)
  1944  		})).
  1945  		When().
  1946  		And(func() {
  1947  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(),
  1948  				ctx.GetName(), types.JSONPatchType, []byte(`[{ "op": "replace", "path": "/spec/syncPolicy/managedNamespaceMetadata/annotations", "value": {"new":"custom-annotation"} }]`), metav1.PatchOptions{}))
  1949  		}).
  1950  		Sync().
  1951  		Then().
  1952  		Expect(Success("")).
  1953  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  1954  			delete(ns.Labels, "kubernetes.io/metadata.name")
  1955  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  1956  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  1957  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  1958  
  1959  			assert.Equal(t, map[string]string{"new": "label"}, ns.Labels)
  1960  			assert.Equal(t, map[string]string{"new": "custom-annotation", "argocd.argoproj.io/sync-options": "ServerSideApply=true"}, ns.Annotations)
  1961  			assert.Equal(t, map[string]string{"new": "label"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Labels)
  1962  			assert.Equal(t, map[string]string{"new": "custom-annotation"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations)
  1963  		}))
  1964  }
  1965  
  1966  // Given application is set with --sync-option CreateNamespace=true
  1967  //
  1968  //		application --dest-namespace does not exist
  1969  //
  1970  //	    Verify application namespace manifest takes precedence over managedNamespaceMetadata
  1971  func TestNamespacedNamespaceAutoCreationWithMetadataAndNsManifest(t *testing.T) {
  1972  	SkipOnEnv(t, "OPENSHIFT")
  1973  	namespace := "guestbook-ui-with-namespace-manifest"
  1974  	defer func() {
  1975  		if !t.Skipped() {
  1976  			_, err := Run("", "kubectl", "delete", "namespace", namespace)
  1977  			assert.NoError(t, err)
  1978  		}
  1979  	}()
  1980  
  1981  	ctx := Given(t)
  1982  	ctx.
  1983  		SetAppNamespace(AppNamespace()).
  1984  		SetTrackingMethod("annotation").
  1985  		Timeout(30).
  1986  		Path("guestbook-with-namespace-manifest").
  1987  		When().
  1988  		CreateFromFile(func(app *Application) {
  1989  			app.Spec.SyncPolicy = &SyncPolicy{
  1990  				SyncOptions: SyncOptions{"CreateNamespace=true"},
  1991  				ManagedNamespaceMetadata: &ManagedNamespaceMetadata{
  1992  					Labels:      map[string]string{"foo": "bar", "abc": "123"},
  1993  					Annotations: map[string]string{"bar": "bat"},
  1994  				}}
  1995  		}).
  1996  		Then().
  1997  		Expect(NoNamespace(namespace)).
  1998  		When().
  1999  		AppSet("--dest-namespace", namespace).
  2000  		Sync().
  2001  		Then().
  2002  		Expect(Success("")).
  2003  		Expect(Namespace(namespace, func(app *Application, ns *v1.Namespace) {
  2004  			delete(ns.Labels, "kubernetes.io/metadata.name")
  2005  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  2006  			delete(ns.Labels, "kubectl.kubernetes.io/last-applied-configuration")
  2007  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  2008  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  2009  
  2010  			// The application namespace manifest takes precedence over what is in managedNamespaceMetadata
  2011  			assert.Equal(t, map[string]string{"test": "true"}, ns.Labels)
  2012  			assert.Equal(t, map[string]string{"foo": "bar", "something": "else"}, ns.Annotations)
  2013  		})).
  2014  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", namespace, health.HealthStatusHealthy)).
  2015  		Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", namespace, health.HealthStatusHealthy)).
  2016  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", namespace, SyncStatusCodeSynced))
  2017  }
  2018  
  2019  // Given application is set with --sync-option CreateNamespace=true
  2020  //
  2021  //		application --dest-namespace exists
  2022  //
  2023  //	    Verify application --dest-namespace is updated with managedNamespaceMetadata labels and annotations
  2024  func TestNamespacedNamespaceAutoCreationWithPreexistingNs(t *testing.T) {
  2025  	SkipOnEnv(t, "OPENSHIFT")
  2026  	updatedNamespace := getNewNamespace(t)
  2027  	defer func() {
  2028  		if !t.Skipped() {
  2029  			_, err := Run("", "kubectl", "delete", "namespace", updatedNamespace)
  2030  			assert.NoError(t, err)
  2031  		}
  2032  	}()
  2033  
  2034  	existingNs := `
  2035  apiVersion: v1
  2036  kind: Namespace
  2037  metadata:
  2038    name: %s
  2039    labels:
  2040      test: "true"
  2041    annotations:
  2042      something: "whatevs"		
  2043  `
  2044  	s := fmt.Sprintf(existingNs, updatedNamespace)
  2045  
  2046  	tmpFile, err := os.CreateTemp("", "")
  2047  	errors.CheckError(err)
  2048  	_, err = tmpFile.Write([]byte(s))
  2049  	errors.CheckError(err)
  2050  
  2051  	_, err = Run("", "kubectl", "apply", "-f", tmpFile.Name())
  2052  	assert.NoError(t, err)
  2053  
  2054  	ctx := Given(t)
  2055  	ctx.
  2056  		SetAppNamespace(AppNamespace()).
  2057  		SetTrackingMethod("annotation").
  2058  		Timeout(30).
  2059  		Path("guestbook").
  2060  		When().
  2061  		CreateFromFile(func(app *Application) {
  2062  			app.Spec.SyncPolicy = &SyncPolicy{
  2063  				SyncOptions: SyncOptions{"CreateNamespace=true"},
  2064  				ManagedNamespaceMetadata: &ManagedNamespaceMetadata{
  2065  					Labels:      map[string]string{"foo": "bar"},
  2066  					Annotations: map[string]string{"bar": "bat"},
  2067  				}}
  2068  		}).
  2069  		Then().
  2070  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  2071  			assert.Empty(t, app.Status.Conditions)
  2072  
  2073  			delete(ns.Labels, "kubernetes.io/metadata.name")
  2074  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  2075  
  2076  			assert.Equal(t, map[string]string{"test": "true"}, ns.Labels)
  2077  			assert.Equal(t, map[string]string{"something": "whatevs"}, ns.Annotations)
  2078  		})).
  2079  		When().
  2080  		AppSet("--dest-namespace", updatedNamespace).
  2081  		Sync().
  2082  		Then().
  2083  		Expect(Success("")).
  2084  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  2085  			assert.Empty(t, app.Status.Conditions)
  2086  
  2087  			delete(ns.Labels, "kubernetes.io/metadata.name")
  2088  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  2089  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  2090  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  2091  
  2092  			assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels)
  2093  			assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat"}, ns.Annotations)
  2094  		})).
  2095  		When().
  2096  		And(func() {
  2097  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(),
  2098  				ctx.GetName(), types.JSONPatchType, []byte(`[{ "op": "add", "path": "/spec/syncPolicy/managedNamespaceMetadata/annotations/something", "value": "hmm" }]`), metav1.PatchOptions{}))
  2099  		}).
  2100  		Sync().
  2101  		Then().
  2102  		Expect(Success("")).
  2103  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  2104  
  2105  			assert.Empty(t, app.Status.Conditions)
  2106  
  2107  			delete(ns.Labels, "kubernetes.io/metadata.name")
  2108  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  2109  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  2110  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  2111  
  2112  			assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels)
  2113  			assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "something": "hmm", "bar": "bat"}, ns.Annotations)
  2114  			assert.Equal(t, map[string]string{"something": "hmm", "bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations)
  2115  		})).
  2116  		When().
  2117  		And(func() {
  2118  			FailOnErr(AppClientset.ArgoprojV1alpha1().Applications(AppNamespace()).Patch(context.Background(),
  2119  				ctx.GetName(), types.JSONPatchType, []byte(`[{ "op": "remove", "path": "/spec/syncPolicy/managedNamespaceMetadata/annotations/something" }]`), metav1.PatchOptions{}))
  2120  		}).
  2121  		Sync().
  2122  		Then().
  2123  		Expect(Success("")).
  2124  		Expect(Namespace(updatedNamespace, func(app *Application, ns *v1.Namespace) {
  2125  
  2126  			assert.Empty(t, app.Status.Conditions)
  2127  
  2128  			delete(ns.Labels, "kubernetes.io/metadata.name")
  2129  			delete(ns.Labels, "argocd.argoproj.io/tracking-id")
  2130  			delete(ns.Annotations, "kubectl.kubernetes.io/last-applied-configuration")
  2131  			delete(ns.Annotations, "argocd.argoproj.io/tracking-id")
  2132  
  2133  			assert.Equal(t, map[string]string{"foo": "bar"}, ns.Labels)
  2134  			assert.Equal(t, map[string]string{"argocd.argoproj.io/sync-options": "ServerSideApply=true", "bar": "bat"}, ns.Annotations)
  2135  			assert.Equal(t, map[string]string{"bar": "bat"}, app.Spec.SyncPolicy.ManagedNamespaceMetadata.Annotations)
  2136  		})).
  2137  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  2138  		Expect(ResourceHealthWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, health.HealthStatusHealthy)).
  2139  		Expect(ResourceSyncStatusWithNamespaceIs("Deployment", "guestbook-ui", updatedNamespace, SyncStatusCodeSynced))
  2140  }
  2141  
  2142  func TestNamespacedFailedSyncWithRetry(t *testing.T) {
  2143  	Given(t).
  2144  		SetAppNamespace(AppNamespace()).
  2145  		SetTrackingMethod("annotation").
  2146  		Path("hook").
  2147  		When().
  2148  		PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "PreSync"}}]`).
  2149  		// make hook fail
  2150  		PatchFile("hook.yaml", `[{"op": "replace", "path": "/spec/containers/0/command", "value": ["false"]}]`).
  2151  		CreateApp().
  2152  		IgnoreErrors().
  2153  		Sync("--retry-limit=1", "--retry-backoff-duration=1s").
  2154  		Then().
  2155  		Expect(OperationPhaseIs(OperationFailed)).
  2156  		Expect(OperationMessageContains("retried 1 times"))
  2157  }
  2158  
  2159  func TestNamespacedCreateDisableValidation(t *testing.T) {
  2160  	Given(t).
  2161  		SetAppNamespace(AppNamespace()).
  2162  		SetTrackingMethod("annotation").
  2163  		Path("baddir").
  2164  		When().
  2165  		CreateApp("--validate=false").
  2166  		Then().
  2167  		And(func(app *Application) {
  2168  			_, err := RunCli("app", "create", app.QualifiedName(), "--upsert", "--validate=false", "--repo", RepoURL(RepoURLTypeFile),
  2169  				"--path", "baddir2", "--project", app.Spec.Project, "--dest-server", KubernetesInternalAPIServerAddr, "--dest-namespace", DeploymentNamespace())
  2170  			assert.NoError(t, err)
  2171  		}).
  2172  		When().
  2173  		AppSet("--path", "baddir3", "--validate=false")
  2174  
  2175  }
  2176  
  2177  func TestNamespacedCreateFromPartialFile(t *testing.T) {
  2178  	partialApp :=
  2179  		`metadata:
  2180    labels:
  2181      labels.local/from-file: file
  2182      labels.local/from-args: file
  2183    annotations:
  2184      annotations.local/from-file: file
  2185    finalizers:
  2186    - resources-finalizer.argocd.argoproj.io
  2187  spec:
  2188    syncPolicy:
  2189      automated:
  2190        prune: true
  2191  `
  2192  
  2193  	path := "helm-values"
  2194  	Given(t).
  2195  		SetAppNamespace(AppNamespace()).
  2196  		SetTrackingMethod("annotation").
  2197  		When().
  2198  		// app should be auto-synced once created
  2199  		CreateFromPartialFile(partialApp, "--path", path, "-l", "labels.local/from-args=args", "--helm-set", "foo=foo").
  2200  		Then().
  2201  		Expect(Success("")).
  2202  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2203  		Expect(NoConditions()).
  2204  		And(func(app *Application) {
  2205  			assert.Equal(t, map[string]string{"labels.local/from-file": "file", "labels.local/from-args": "args"}, app.ObjectMeta.Labels)
  2206  			assert.Equal(t, map[string]string{"annotations.local/from-file": "file"}, app.ObjectMeta.Annotations)
  2207  			assert.Equal(t, []string{"resources-finalizer.argocd.argoproj.io"}, app.ObjectMeta.Finalizers)
  2208  			assert.Equal(t, path, app.Spec.GetSource().Path)
  2209  			assert.Equal(t, []HelmParameter{{Name: "foo", Value: "foo"}}, app.Spec.GetSource().Helm.Parameters)
  2210  		})
  2211  }
  2212  
  2213  // Ensure actions work when using a resource action that modifies status and/or spec
  2214  func TestNamespacedCRDStatusSubresourceAction(t *testing.T) {
  2215  	actions := `
  2216  discovery.lua: |
  2217    actions = {}
  2218    actions["update-spec"] = {["disabled"] = false}
  2219    actions["update-status"] = {["disabled"] = false}
  2220    actions["update-both"] = {["disabled"] = false}
  2221    return actions
  2222  definitions:
  2223  - name: update-both
  2224    action.lua: |
  2225      obj.spec = {}
  2226      obj.spec.foo = "update-both"
  2227      obj.status = {}
  2228      obj.status.bar = "update-both"
  2229      return obj
  2230  - name: update-spec
  2231    action.lua: |
  2232      obj.spec = {}
  2233      obj.spec.foo = "update-spec"
  2234      return obj
  2235  - name: update-status
  2236    action.lua: |
  2237      obj.status = {}
  2238      obj.status.bar = "update-status"
  2239      return obj
  2240  `
  2241  	Given(t).
  2242  		SetAppNamespace(AppNamespace()).
  2243  		SetTrackingMethod("annotation").
  2244  		Path("crd-subresource").
  2245  		And(func() {
  2246  			SetResourceOverrides(map[string]ResourceOverride{
  2247  				"argoproj.io/StatusSubResource": {
  2248  					Actions: actions,
  2249  				},
  2250  				"argoproj.io/NonStatusSubResource": {
  2251  					Actions: actions,
  2252  				},
  2253  			})
  2254  		}).
  2255  		When().CreateApp().Sync().Then().
  2256  		Expect(OperationPhaseIs(OperationSucceeded)).Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2257  		When().
  2258  		Refresh(RefreshTypeNormal).
  2259  		Then().
  2260  		// tests resource actions on a CRD using status subresource
  2261  		And(func(app *Application) {
  2262  			_, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-both")
  2263  			assert.NoError(t, err)
  2264  			text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  2265  			assert.Equal(t, "update-both", text)
  2266  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  2267  			assert.Equal(t, "update-both", text)
  2268  
  2269  			_, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-spec")
  2270  			assert.NoError(t, err)
  2271  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  2272  			assert.Equal(t, "update-spec", text)
  2273  
  2274  			_, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "StatusSubResource", "update-status")
  2275  			assert.NoError(t, err)
  2276  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "statussubresources", "status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  2277  			assert.Equal(t, "update-status", text)
  2278  		}).
  2279  		// tests resource actions on a CRD *not* using status subresource
  2280  		And(func(app *Application) {
  2281  			_, err := RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-both")
  2282  			assert.NoError(t, err)
  2283  			text := FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  2284  			assert.Equal(t, "update-both", text)
  2285  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  2286  			assert.Equal(t, "update-both", text)
  2287  
  2288  			_, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-spec")
  2289  			assert.NoError(t, err)
  2290  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.spec.foo}")).(string)
  2291  			assert.Equal(t, "update-spec", text)
  2292  
  2293  			_, err = RunCli("app", "actions", "run", app.QualifiedName(), "--kind", "NonStatusSubResource", "update-status")
  2294  			assert.NoError(t, err)
  2295  			text = FailOnErr(Run(".", "kubectl", "-n", app.Spec.Destination.Namespace, "get", "nonstatussubresources", "non-status-subresource", "-o", "jsonpath={.status.bar}")).(string)
  2296  			assert.Equal(t, "update-status", text)
  2297  		})
  2298  }
  2299  
  2300  func TestNamespacedAppLogs(t *testing.T) {
  2301  	SkipOnEnv(t, "OPENSHIFT")
  2302  	Given(t).
  2303  		SetAppNamespace(AppNamespace()).
  2304  		SetTrackingMethod("annotation").
  2305  		Path("guestbook-logs").
  2306  		When().
  2307  		CreateApp().
  2308  		Sync().
  2309  		Then().
  2310  		Expect(HealthIs(health.HealthStatusHealthy)).
  2311  		And(func(app *Application) {
  2312  			out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Deployment", "--group", "", "--name", "guestbook-ui")
  2313  			assert.NoError(t, err)
  2314  			assert.Contains(t, out, "Hi")
  2315  		}).
  2316  		And(func(app *Application) {
  2317  			out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Pod")
  2318  			assert.NoError(t, err)
  2319  			assert.Contains(t, out, "Hi")
  2320  		}).
  2321  		And(func(app *Application) {
  2322  			out, err := RunCliWithRetry(5, "app", "logs", app.QualifiedName(), "--kind", "Service")
  2323  			assert.NoError(t, err)
  2324  			assert.NotContains(t, out, "Hi")
  2325  		})
  2326  }
  2327  
  2328  func TestNamespacedAppWaitOperationInProgress(t *testing.T) {
  2329  	Given(t).
  2330  		SetAppNamespace(AppNamespace()).
  2331  		SetTrackingMethod("annotation").
  2332  		And(func() {
  2333  			SetResourceOverrides(map[string]ResourceOverride{
  2334  				"batch/Job": {
  2335  					HealthLua: `return { status = 'Running' }`,
  2336  				},
  2337  				"apps/Deployment": {
  2338  					HealthLua: `return { status = 'Suspended' }`,
  2339  				},
  2340  			})
  2341  		}).
  2342  		Async(true).
  2343  		Path("hook-and-deployment").
  2344  		When().
  2345  		CreateApp().
  2346  		Sync().
  2347  		Then().
  2348  		// stuck in running state
  2349  		Expect(OperationPhaseIs(OperationRunning)).
  2350  		When().
  2351  		Then().
  2352  		And(func(app *Application) {
  2353  			_, err := RunCli("app", "wait", app.QualifiedName(), "--suspended")
  2354  			errors.CheckError(err)
  2355  		})
  2356  }
  2357  
  2358  func TestNamespacedSyncOptionReplace(t *testing.T) {
  2359  	Given(t).
  2360  		SetAppNamespace(AppNamespace()).
  2361  		SetTrackingMethod("annotation").
  2362  		Path("config-map").
  2363  		When().
  2364  		PatchFile("config-map.yaml", `[{"op": "add", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/sync-options": "Replace=true"}}]`).
  2365  		CreateApp().
  2366  		Sync().
  2367  		Then().
  2368  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2369  		And(func(app *Application) {
  2370  			assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created")
  2371  		}).
  2372  		When().
  2373  		Sync().
  2374  		Then().
  2375  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2376  		And(func(app *Application) {
  2377  			assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced")
  2378  		})
  2379  }
  2380  
  2381  func TestNamespacedSyncOptionReplaceFromCLI(t *testing.T) {
  2382  	Given(t).
  2383  		SetAppNamespace(AppNamespace()).
  2384  		SetTrackingMethod("annotation").
  2385  		Path("config-map").
  2386  		Replace().
  2387  		When().
  2388  		CreateApp().
  2389  		Sync().
  2390  		Then().
  2391  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2392  		And(func(app *Application) {
  2393  			assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map created")
  2394  		}).
  2395  		When().
  2396  		Sync().
  2397  		Then().
  2398  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2399  		And(func(app *Application) {
  2400  			assert.Equal(t, app.Status.OperationState.SyncResult.Resources[0].Message, "configmap/my-map replaced")
  2401  		})
  2402  }
  2403  
  2404  func TestNamespacedDiscoverNewCommit(t *testing.T) {
  2405  	var sha string
  2406  	Given(t).
  2407  		SetAppNamespace(AppNamespace()).
  2408  		SetTrackingMethod("annotation").
  2409  		Path("config-map").
  2410  		When().
  2411  		CreateApp().
  2412  		Sync().
  2413  		Then().
  2414  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2415  		And(func(app *Application) {
  2416  			sha = app.Status.Sync.Revision
  2417  			assert.NotEmpty(t, sha)
  2418  		}).
  2419  		When().
  2420  		PatchFile("config-map.yaml", `[{"op": "replace", "path": "/data/foo", "value": "hello"}]`).
  2421  		Then().
  2422  		Expect(SyncStatusIs(SyncStatusCodeSynced)).
  2423  		// make sure new commit is not discovered immediately after push
  2424  		And(func(app *Application) {
  2425  			assert.Equal(t, sha, app.Status.Sync.Revision)
  2426  		}).
  2427  		When().
  2428  		// make sure new commit is not discovered after refresh is requested
  2429  		Refresh(RefreshTypeNormal).
  2430  		Then().
  2431  		Expect(SyncStatusIs(SyncStatusCodeOutOfSync)).
  2432  		And(func(app *Application) {
  2433  			assert.NotEqual(t, sha, app.Status.Sync.Revision)
  2434  		})
  2435  }
  2436  
  2437  func TestNamespacedDisableManifestGeneration(t *testing.T) {
  2438  	Given(t).
  2439  		SetAppNamespace(AppNamespace()).
  2440  		SetTrackingMethod("annotation").
  2441  		Path("guestbook").
  2442  		When().
  2443  		CreateApp().
  2444  		Refresh(RefreshTypeHard).
  2445  		Then().
  2446  		And(func(app *Application) {
  2447  			assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeKustomize)
  2448  		}).
  2449  		When().
  2450  		And(func() {
  2451  			time.Sleep(3 * time.Second)
  2452  			SetEnableManifestGeneration(map[ApplicationSourceType]bool{
  2453  				ApplicationSourceTypeKustomize: false,
  2454  			})
  2455  		}).
  2456  		Refresh(RefreshTypeHard).
  2457  		Then().
  2458  		And(func(app *Application) {
  2459  			time.Sleep(1 * time.Second)
  2460  		}).
  2461  		And(func(app *Application) {
  2462  			assert.Equal(t, app.Status.SourceType, ApplicationSourceTypeDirectory)
  2463  		})
  2464  }
  2465  
  2466  func TestCreateAppInNotAllowedNamespace(t *testing.T) {
  2467  	ctx := Given(t)
  2468  	ctx.
  2469  		ProjectSpec(AppProjectSpec{
  2470  			SourceRepos:      []string{"*"},
  2471  			SourceNamespaces: []string{"default"},
  2472  			Destinations: []ApplicationDestination{
  2473  				{Namespace: "*", Server: "*"},
  2474  			},
  2475  		}).
  2476  		Path(guestbookPath).
  2477  		SetTrackingMethod("annotation").
  2478  		SetAppNamespace("default").
  2479  		When().
  2480  		IgnoreErrors().
  2481  		CreateApp().
  2482  		Then().
  2483  		Expect(DoesNotExist()).
  2484  		Expect(Error("", "namespace 'default' is not permitted"))
  2485  }