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