github.com/argoproj/argo-cd/v2@v2.10.5/test/e2e/helm_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "fmt" 6 "net" 7 "net/http" 8 "os" 9 "strings" 10 "testing" 11 12 "github.com/argoproj/gitops-engine/pkg/health" 13 . "github.com/argoproj/gitops-engine/pkg/sync/common" 14 "github.com/stretchr/testify/assert" 15 v1 "k8s.io/api/core/v1" 16 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 17 "k8s.io/apimachinery/pkg/types" 18 19 . "github.com/argoproj/argo-cd/v2/pkg/apis/application/v1alpha1" 20 "github.com/argoproj/argo-cd/v2/test/e2e/fixture" 21 . "github.com/argoproj/argo-cd/v2/test/e2e/fixture" 22 . "github.com/argoproj/argo-cd/v2/test/e2e/fixture/app" 23 projectFixture "github.com/argoproj/argo-cd/v2/test/e2e/fixture/project" 24 "github.com/argoproj/argo-cd/v2/test/e2e/fixture/repos" 25 . "github.com/argoproj/argo-cd/v2/util/errors" 26 "github.com/argoproj/argo-cd/v2/util/settings" 27 ) 28 29 func TestHelmHooksAreCreated(t *testing.T) { 30 Given(t). 31 Path("hook"). 32 When(). 33 PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"helm.sh/hook": "pre-install"}}]`). 34 CreateApp(). 35 Sync(). 36 Then(). 37 Expect(OperationPhaseIs(OperationSucceeded)). 38 Expect(HealthIs(health.HealthStatusHealthy)). 39 Expect(SyncStatusIs(SyncStatusCodeSynced)). 40 Expect(ResourceResultIs(ResourceResult{Version: "v1", Kind: "Pod", Namespace: DeploymentNamespace(), Name: "hook", Message: "pod/hook created", HookType: HookTypePreSync, HookPhase: OperationSucceeded, SyncPhase: SyncPhasePreSync})) 41 } 42 43 // make sure we treat Helm weights as a sync wave 44 func TestHelmHookWeight(t *testing.T) { 45 Given(t). 46 Path("hook"). 47 When(). 48 // this create a weird hook, that runs during sync - but before the pod, and because it'll fail - the pod will never be created 49 PatchFile("hook.yaml", `[ 50 {"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "Sync", "helm.sh/hook-weight": "-1"}}, 51 {"op": "replace", "path": "/spec/containers/0/command/0", "value": "false"} 52 ]`). 53 CreateApp(). 54 IgnoreErrors(). 55 Sync(). 56 Then(). 57 Expect(OperationPhaseIs(OperationFailed)). 58 Expect(ResourceResultNumbering(1)) 59 } 60 61 // make sure that execute the delete policy 62 func TestHelmHookDeletePolicy(t *testing.T) { 63 Given(t). 64 Path("hook"). 65 When(). 66 PatchFile("hook.yaml", `[{"op": "add", "path": "/metadata/annotations/helm.sh~1hook-delete-policy", "value": "hook-succeeded"}]`). 67 CreateApp(). 68 Sync(). 69 Then(). 70 Expect(OperationPhaseIs(OperationSucceeded)). 71 Expect(ResourceResultNumbering(2)). 72 Expect(NotPod(func(p v1.Pod) bool { return p.Name == "hook" })) 73 } 74 75 func TestDeclarativeHelm(t *testing.T) { 76 Given(t). 77 Path("helm"). 78 When(). 79 Declarative("declarative-apps/app.yaml"). 80 Sync(). 81 Then(). 82 Expect(OperationPhaseIs(OperationSucceeded)). 83 Expect(HealthIs(health.HealthStatusHealthy)). 84 Expect(SyncStatusIs(SyncStatusCodeSynced)) 85 } 86 87 func TestDeclarativeHelmInvalidValuesFile(t *testing.T) { 88 Given(t). 89 Path("helm"). 90 When(). 91 Declarative("declarative-apps/invalid-helm.yaml"). 92 Then(). 93 Expect(HealthIs(health.HealthStatusHealthy)). 94 Expect(SyncStatusIs(SyncStatusCodeUnknown)). 95 Expect(Condition(ApplicationConditionComparisonError, "does-not-exist-values.yaml: no such file or directory")) 96 } 97 98 func TestHelmRepo(t *testing.T) { 99 SkipOnEnv(t, "HELM") 100 Given(t). 101 CustomCACertAdded(). 102 HelmRepoAdded("custom-repo"). 103 RepoURLType(RepoURLTypeHelm). 104 Chart("helm"). 105 Revision("1.0.0"). 106 When(). 107 CreateApp(). 108 Then(). 109 When(). 110 Sync(). 111 Then(). 112 Expect(OperationPhaseIs(OperationSucceeded)). 113 Expect(HealthIs(health.HealthStatusHealthy)). 114 Expect(SyncStatusIs(SyncStatusCodeSynced)) 115 } 116 117 func TestHelmValues(t *testing.T) { 118 Given(t). 119 Path("helm"). 120 When(). 121 AddFile("foo.yml", ""). 122 CreateApp(). 123 AppSet("--values", "foo.yml"). 124 Then(). 125 And(func(app *Application) { 126 assert.Equal(t, []string{"foo.yml"}, app.Spec.GetSource().Helm.ValueFiles) 127 }) 128 } 129 130 func TestHelmIgnoreMissingValueFiles(t *testing.T) { 131 Given(t). 132 Path("helm"). 133 When(). 134 Declarative("declarative-apps/invalid-helm.yaml"). 135 Then(). 136 And(func(app *Application) { 137 assert.Equal(t, []string{"does-not-exist-values.yaml"}, app.Spec.GetSource().Helm.ValueFiles) 138 assert.Equal(t, false, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) 139 }). 140 When(). 141 AppSet("--ignore-missing-value-files"). 142 Then(). 143 And(func(app *Application) { 144 assert.Equal(t, true, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) 145 }). 146 When(). 147 Sync(). 148 Then(). 149 Expect(OperationPhaseIs(OperationSucceeded)). 150 Expect(HealthIs(health.HealthStatusHealthy)). 151 Expect(SyncStatusIs(SyncStatusCodeSynced)). 152 When(). 153 AppUnSet("--ignore-missing-value-files"). 154 Then(). 155 And(func(app *Application) { 156 assert.Equal(t, false, app.Spec.GetSource().Helm.IgnoreMissingValueFiles) 157 }). 158 When(). 159 IgnoreErrors(). 160 Sync(). 161 Then(). 162 Expect(ErrorRegex("Error: open .*does-not-exist-values.yaml: no such file or directory", "")) 163 } 164 165 func TestHelmValuesMultipleUnset(t *testing.T) { 166 Given(t). 167 Path("helm"). 168 When(). 169 AddFile("foo.yml", ""). 170 AddFile("baz.yml", ""). 171 CreateApp(). 172 AppSet("--values", "foo.yml", "--values", "baz.yml"). 173 Then(). 174 And(func(app *Application) { 175 assert.NotNil(t, app.Spec.GetSource().Helm) 176 assert.Equal(t, []string{"foo.yml", "baz.yml"}, app.Spec.GetSource().Helm.ValueFiles) 177 }). 178 When(). 179 AppUnSet("--values", "foo.yml"). 180 Then(). 181 And(func(app *Application) { 182 assert.NotNil(t, app.Spec.GetSource().Helm) 183 assert.Equal(t, []string{"baz.yml"}, app.Spec.GetSource().Helm.ValueFiles) 184 }). 185 When(). 186 AppUnSet("--values", "baz.yml"). 187 Then(). 188 And(func(app *Application) { 189 assert.Nil(t, app.Spec.GetSource().Helm) 190 }) 191 } 192 193 func TestHelmValuesLiteralFileLocal(t *testing.T) { 194 Given(t). 195 Path("helm"). 196 When(). 197 CreateApp(). 198 AppSet("--values-literal-file", "testdata/helm/baz.yaml"). 199 Then(). 200 And(func(app *Application) { 201 data, err := os.ReadFile("testdata/helm/baz.yaml") 202 if err != nil { 203 panic(err) 204 } 205 assert.Equal(t, strings.TrimSuffix(string(data), "\n"), app.Spec.GetSource().Helm.ValuesString()) 206 }). 207 When(). 208 AppUnSet("--values-literal"). 209 Then(). 210 And(func(app *Application) { 211 assert.Nil(t, app.Spec.GetSource().Helm) 212 }) 213 } 214 215 func TestHelmValuesLiteralFileRemote(t *testing.T) { 216 sentinel := "a: b" 217 serve := func(c chan<- string) { 218 // listen on first available dynamic (unprivileged) port 219 listener, err := net.Listen("tcp", ":0") 220 if err != nil { 221 panic(err) 222 } 223 224 // send back the address so that it can be used 225 c <- listener.Addr().String() 226 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 227 // return the sentinel text at root URL 228 fmt.Fprint(w, sentinel) 229 }) 230 231 panic(http.Serve(listener, nil)) 232 } 233 c := make(chan string, 1) 234 235 // run a local webserver to test data retrieval 236 go serve(c) 237 address := <-c 238 t.Logf("Listening at address: %s", address) 239 240 Given(t). 241 Path("helm"). 242 When(). 243 CreateApp(). 244 AppSet("--values-literal-file", "http://"+address). 245 Then(). 246 And(func(app *Application) { 247 assert.Equal(t, "a: b", app.Spec.GetSource().Helm.ValuesString()) 248 }). 249 When(). 250 AppUnSet("--values-literal"). 251 Then(). 252 And(func(app *Application) { 253 assert.Nil(t, app.Spec.GetSource().Helm) 254 }) 255 } 256 257 func TestHelmCrdHook(t *testing.T) { 258 SkipOnEnv(t, "HELM") 259 Given(t). 260 Path("helm-crd"). 261 When(). 262 CreateApp(). 263 Sync(). 264 Then(). 265 Expect(OperationPhaseIs(OperationSucceeded)). 266 Expect(HealthIs(health.HealthStatusHealthy)). 267 Expect(SyncStatusIs(SyncStatusCodeSynced)). 268 Expect(ResourceResultNumbering(2)) 269 } 270 271 func TestHelmReleaseName(t *testing.T) { 272 Given(t). 273 Path("helm"). 274 When(). 275 CreateApp(). 276 AppSet("--release-name", "foo"). 277 Then(). 278 And(func(app *Application) { 279 assert.Equal(t, "foo", app.Spec.GetSource().Helm.ReleaseName) 280 }) 281 } 282 283 func TestHelmSet(t *testing.T) { 284 Given(t). 285 Path("helm"). 286 When(). 287 CreateApp(). 288 AppSet("--helm-set", "foo=bar", "--helm-set", "foo=baz", "--helm-set", "app=$ARGOCD_APP_NAME"). 289 Then(). 290 And(func(app *Application) { 291 assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz"}, {Name: "app", Value: "$ARGOCD_APP_NAME"}}, app.Spec.GetSource().Helm.Parameters) 292 }) 293 } 294 295 func TestHelmSetString(t *testing.T) { 296 Given(t). 297 Path("helm"). 298 When(). 299 CreateApp(). 300 AppSet("--helm-set-string", "foo=bar", "--helm-set-string", "foo=baz", "--helm-set-string", "app=$ARGOCD_APP_NAME"). 301 Then(). 302 And(func(app *Application) { 303 assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz", ForceString: true}, {Name: "app", Value: "$ARGOCD_APP_NAME", ForceString: true}}, app.Spec.GetSource().Helm.Parameters) 304 }) 305 } 306 307 func TestHelmSetFile(t *testing.T) { 308 Given(t). 309 Path("helm"). 310 When(). 311 CreateApp(). 312 AppSet("--helm-set-file", "foo=bar.yaml", "--helm-set-file", "foo=baz.yaml"). 313 Then(). 314 And(func(app *Application) { 315 assert.Equal(t, []HelmFileParameter{{Name: "foo", Path: "baz.yaml"}}, app.Spec.GetSource().Helm.FileParameters) 316 }) 317 } 318 319 // ensure we can use envsubst in "set" variables 320 func TestHelmSetEnv(t *testing.T) { 321 Given(t). 322 Path("helm-values"). 323 When(). 324 CreateApp(). 325 AppSet("--helm-set", "foo=$ARGOCD_APP_NAME"). 326 Sync(). 327 Then(). 328 Expect(OperationPhaseIs(OperationSucceeded)). 329 Expect(SyncStatusIs(SyncStatusCodeSynced)). 330 And(func(app *Application) { 331 assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string)) 332 }) 333 } 334 335 func TestHelmSetStringEnv(t *testing.T) { 336 Given(t). 337 Path("helm-values"). 338 When(). 339 CreateApp(). 340 AppSet("--helm-set-string", "foo=$ARGOCD_APP_NAME"). 341 Sync(). 342 Then(). 343 Expect(OperationPhaseIs(OperationSucceeded)). 344 Expect(SyncStatusIs(SyncStatusCodeSynced)). 345 And(func(app *Application) { 346 assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string)) 347 }) 348 } 349 350 // make sure kube-version gets passed down to resources 351 func TestKubeVersion(t *testing.T) { 352 SkipOnEnv(t, "HELM") 353 Given(t). 354 Path("helm-kube-version"). 355 When(). 356 CreateApp(). 357 Sync(). 358 Then(). 359 Expect(SyncStatusIs(SyncStatusCodeSynced)). 360 And(func(app *Application) { 361 kubeVersion := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", 362 "-o", "jsonpath={.data.kubeVersion}")).(string) 363 // Capabilities.KubeVersion defaults to 1.9.0, we assume here you are running a later version 364 assert.LessOrEqual(t, GetVersions().ServerVersion.Format("v%s.%s.0"), kubeVersion) 365 }) 366 } 367 368 func TestHelmValuesHiddenDirectory(t *testing.T) { 369 SkipOnEnv(t, "HELM") 370 Given(t). 371 Path(".hidden-helm"). 372 When(). 373 AddFile("foo.yaml", ""). 374 CreateApp(). 375 AppSet("--values", "foo.yaml"). 376 Sync(). 377 Then(). 378 Expect(OperationPhaseIs(OperationSucceeded)). 379 Expect(HealthIs(health.HealthStatusHealthy)). 380 Expect(SyncStatusIs(SyncStatusCodeSynced)) 381 } 382 383 func TestHelmWithDependencies(t *testing.T) { 384 SkipOnEnv(t, "HELM") 385 testHelmWithDependencies(t, "helm-with-dependencies", false) 386 } 387 388 func TestHelmWithMultipleDependencies(t *testing.T) { 389 SkipOnEnv(t, "HELM") 390 391 Given(t).Path("helm-with-multiple-dependencies"). 392 CustomCACertAdded(). 393 // these are slow tests 394 Timeout(30). 395 HelmHTTPSCredentialsUserPassAdded(). 396 HelmPassCredentials(). 397 When(). 398 CreateApp(). 399 Sync(). 400 Then(). 401 Expect(SyncStatusIs(SyncStatusCodeSynced)) 402 } 403 404 func TestHelmDependenciesPermissionDenied(t *testing.T) { 405 SkipOnEnv(t, "HELM") 406 407 projName := "argo-helm-project-denied" 408 projectFixture. 409 Given(t). 410 Name(projName). 411 Destination("*,*"). 412 When(). 413 Create(). 414 AddSource(RepoURL(RepoURLTypeFile)) 415 416 expectedErr := fmt.Sprintf("helm repos localhost:5000/myrepo are not permitted in project '%s'", projName) 417 GivenWithSameState(t). 418 Project(projName). 419 Path("helm-oci-with-dependencies"). 420 CustomCACertAdded(). 421 HelmHTTPSCredentialsUserPassAdded(). 422 HelmPassCredentials(). 423 When(). 424 IgnoreErrors(). 425 CreateApp(). 426 Then(). 427 Expect(Error("", expectedErr)) 428 429 expectedErr = fmt.Sprintf("helm repos https://localhost:9443/argo-e2e/testdata.git/helm-repo/local, https://localhost:9443/argo-e2e/testdata.git/helm-repo/local2 are not permitted in project '%s'", projName) 430 GivenWithSameState(t). 431 Project(projName). 432 Path("helm-with-multiple-dependencies-permission-denied"). 433 CustomCACertAdded(). 434 HelmHTTPSCredentialsUserPassAdded(). 435 HelmPassCredentials(). 436 When(). 437 IgnoreErrors(). 438 CreateApp(). 439 Then(). 440 Expect(Error("", expectedErr)) 441 } 442 443 func TestHelmWithDependenciesLegacyRepo(t *testing.T) { 444 SkipOnEnv(t, "HELM") 445 testHelmWithDependencies(t, "helm-with-dependencies", true) 446 } 447 448 func testHelmWithDependencies(t *testing.T, chartPath string, legacyRepo bool) { 449 ctx := Given(t). 450 CustomCACertAdded(). 451 // these are slow tests 452 Timeout(30). 453 HelmPassCredentials() 454 if legacyRepo { 455 ctx.And(func() { 456 FailOnErr(fixture.Run("", "kubectl", "create", "secret", "generic", "helm-repo", 457 "-n", fixture.TestNamespace(), 458 fmt.Sprintf("--from-file=certSecret=%s", repos.CertPath), 459 fmt.Sprintf("--from-file=keySecret=%s", repos.CertKeyPath), 460 fmt.Sprintf("--from-literal=username=%s", GitUsername), 461 fmt.Sprintf("--from-literal=password=%s", GitPassword), 462 )) 463 FailOnErr(fixture.KubeClientset.CoreV1().Secrets(fixture.TestNamespace()).Patch(context.Background(), 464 "helm-repo", types.MergePatchType, []byte(`{"metadata": { "labels": {"e2e.argoproj.io": "true"} }}`), metav1.PatchOptions{})) 465 466 fixture.SetHelmRepos(settings.HelmRepoCredentials{ 467 URL: RepoURL(RepoURLTypeHelm), 468 Name: "custom-repo", 469 KeySecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "keySecret"}, 470 CertSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "certSecret"}, 471 UsernameSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "username"}, 472 PasswordSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "password"}, 473 }) 474 }) 475 } else { 476 ctx = ctx.HelmRepoAdded("custom-repo") 477 } 478 479 helmVer := "" 480 481 ctx.Path(chartPath). 482 When(). 483 CreateApp("--helm-version", helmVer). 484 Sync(). 485 Then(). 486 Expect(SyncStatusIs(SyncStatusCodeSynced)) 487 } 488 489 func TestHelm3CRD(t *testing.T) { 490 SkipOnEnv(t, "HELM") 491 Given(t). 492 Path("helm3-crd"). 493 When(). 494 CreateApp(). 495 Sync(). 496 Then(). 497 Expect(SyncStatusIs(SyncStatusCodeSynced)). 498 Expect(ResourceSyncStatusIs("CustomResourceDefinition", "crontabs.stable.example.com", SyncStatusCodeSynced)) 499 } 500 501 func TestHelmRepoDiffLocal(t *testing.T) { 502 SkipOnEnv(t, "HELM") 503 helmTmp := t.TempDir() 504 Given(t). 505 CustomCACertAdded(). 506 HelmRepoAdded("custom-repo"). 507 RepoURLType(RepoURLTypeHelm). 508 Chart("helm"). 509 Revision("1.0.0"). 510 When(). 511 CreateApp(). 512 Then(). 513 When(). 514 Sync(). 515 Then(). 516 Expect(OperationPhaseIs(OperationSucceeded)). 517 Expect(HealthIs(health.HealthStatusHealthy)). 518 Expect(SyncStatusIs(SyncStatusCodeSynced)). 519 And(func(app *Application) { 520 _ = os.Setenv("XDG_CONFIG_HOME", helmTmp) 521 FailOnErr(Run("", "helm", "repo", "add", "custom-repo", GetEnvWithDefault("ARGOCD_E2E_HELM_SERVICE", RepoURL(RepoURLTypeHelm)), 522 "--username", GitUsername, 523 "--password", GitPassword, 524 "--cert-file", "../fixture/certs/argocd-test-client.crt", 525 "--key-file", "../fixture/certs/argocd-test-client.key", 526 "--ca-file", "../fixture/certs/argocd-test-ca.crt", 527 )) 528 diffOutput := FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/helm")).(string) 529 assert.Empty(t, diffOutput) 530 }) 531 } 532 533 func TestHelmOCIRegistry(t *testing.T) { 534 Given(t). 535 PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0"). 536 HelmOCIRepoAdded("myrepo"). 537 RepoURLType(RepoURLTypeHelmOCI). 538 Chart("helm-values"). 539 Revision("1.0.0"). 540 When(). 541 CreateApp(). 542 Then(). 543 When(). 544 Sync(). 545 Then(). 546 Expect(OperationPhaseIs(OperationSucceeded)). 547 Expect(HealthIs(health.HealthStatusHealthy)). 548 Expect(SyncStatusIs(SyncStatusCodeSynced)) 549 } 550 551 func TestGitWithHelmOCIRegistryDependencies(t *testing.T) { 552 Given(t). 553 PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0"). 554 HelmOCIRepoAdded("myrepo"). 555 Path("helm-oci-with-dependencies"). 556 When(). 557 CreateApp(). 558 Then(). 559 When(). 560 Sync(). 561 Then(). 562 Expect(OperationPhaseIs(OperationSucceeded)). 563 Expect(HealthIs(health.HealthStatusHealthy)). 564 Expect(SyncStatusIs(SyncStatusCodeSynced)) 565 } 566 567 func TestHelmOCIRegistryWithDependencies(t *testing.T) { 568 Given(t). 569 PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0"). 570 PushChartToOCIRegistry("helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0"). 571 HelmOCIRepoAdded("myrepo"). 572 RepoURLType(RepoURLTypeHelmOCI). 573 Chart("helm-oci-with-dependencies"). 574 Revision("1.0.0"). 575 When(). 576 CreateApp(). 577 Then(). 578 When(). 579 Sync(). 580 Then(). 581 Expect(OperationPhaseIs(OperationSucceeded)). 582 Expect(HealthIs(health.HealthStatusHealthy)). 583 Expect(SyncStatusIs(SyncStatusCodeSynced)) 584 } 585 586 func TestTemplatesGitWithHelmOCIDependencies(t *testing.T) { 587 Given(t). 588 PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0"). 589 HelmoOCICredentialsWithoutUserPassAdded(). 590 Path("helm-oci-with-dependencies"). 591 When(). 592 CreateApp(). 593 Then(). 594 When(). 595 Sync(). 596 Then(). 597 Expect(OperationPhaseIs(OperationSucceeded)). 598 Expect(HealthIs(health.HealthStatusHealthy)). 599 Expect(SyncStatusIs(SyncStatusCodeSynced)) 600 } 601 602 func TestTemplatesHelmOCIWithDependencies(t *testing.T) { 603 Given(t). 604 PushChartToOCIRegistry("helm-values", "helm-values", "1.0.0"). 605 PushChartToOCIRegistry("helm-oci-with-dependencies", "helm-oci-with-dependencies", "1.0.0"). 606 HelmoOCICredentialsWithoutUserPassAdded(). 607 RepoURLType(RepoURLTypeHelmOCI). 608 Chart("helm-oci-with-dependencies"). 609 Revision("1.0.0"). 610 When(). 611 CreateApp(). 612 Then(). 613 When(). 614 Sync(). 615 Then(). 616 Expect(OperationPhaseIs(OperationSucceeded)). 617 Expect(HealthIs(health.HealthStatusHealthy)). 618 Expect(SyncStatusIs(SyncStatusCodeSynced)) 619 }