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