github.com/argoproj/argo-cd@v1.8.7/test/e2e/helm_test.go (about) 1 package e2e 2 3 import ( 4 "context" 5 "fmt" 6 "io/ioutil" 7 "net" 8 "net/http" 9 "os" 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/pkg/apis/application/v1alpha1" 20 "github.com/argoproj/argo-cd/test/e2e/fixture" 21 . "github.com/argoproj/argo-cd/test/e2e/fixture" 22 . "github.com/argoproj/argo-cd/test/e2e/fixture/app" 23 "github.com/argoproj/argo-cd/test/e2e/fixture/repos" 24 . "github.com/argoproj/argo-cd/util/errors" 25 "github.com/argoproj/argo-cd/util/settings" 26 ) 27 28 func TestHelmHooksAreCreated(t *testing.T) { 29 Given(t). 30 Path("hook"). 31 When(). 32 PatchFile("hook.yaml", `[{"op": "replace", "path": "/metadata/annotations", "value": {"helm.sh/hook": "pre-install"}}]`). 33 Create(). 34 Sync(). 35 Then(). 36 Expect(OperationPhaseIs(OperationSucceeded)). 37 Expect(HealthIs(health.HealthStatusHealthy)). 38 Expect(SyncStatusIs(SyncStatusCodeSynced)). 39 Expect(ResourceResultIs(ResourceResult{Version: "v1", Kind: "Pod", Namespace: DeploymentNamespace(), Name: "hook", Message: "pod/hook created", HookType: HookTypePreSync, HookPhase: OperationSucceeded, SyncPhase: SyncPhasePreSync})) 40 } 41 42 // make sure we treat Helm weights as a sync wave 43 func TestHelmHookWeight(t *testing.T) { 44 Given(t). 45 Path("hook"). 46 When(). 47 // this create a weird hook, that runs during sync - but before the pod, and because it'll fail - the pod will never be created 48 PatchFile("hook.yaml", `[ 49 {"op": "replace", "path": "/metadata/annotations", "value": {"argocd.argoproj.io/hook": "Sync", "helm.sh/hook-weight": "-1"}}, 50 {"op": "replace", "path": "/spec/containers/0/command/0", "value": "false"} 51 ]`). 52 Create(). 53 IgnoreErrors(). 54 Sync(). 55 Then(). 56 Expect(OperationPhaseIs(OperationFailed)). 57 Expect(ResourceResultNumbering(1)) 58 } 59 60 // make sure that execute the delete policy 61 func TestHelmHookDeletePolicy(t *testing.T) { 62 Given(t). 63 Path("hook"). 64 When(). 65 PatchFile("hook.yaml", `[{"op": "add", "path": "/metadata/annotations/helm.sh~1hook-delete-policy", "value": "hook-succeeded"}]`). 66 Create(). 67 Sync(). 68 Then(). 69 Expect(OperationPhaseIs(OperationSucceeded)). 70 Expect(ResourceResultNumbering(2)). 71 Expect(NotPod(func(p v1.Pod) bool { return p.Name == "hook" })) 72 } 73 74 func TestDeclarativeHelm(t *testing.T) { 75 Given(t). 76 Path("helm"). 77 When(). 78 Declarative("declarative-apps/app.yaml"). 79 Sync(). 80 Then(). 81 Expect(OperationPhaseIs(OperationSucceeded)). 82 Expect(HealthIs(health.HealthStatusHealthy)). 83 Expect(SyncStatusIs(SyncStatusCodeSynced)) 84 } 85 86 func TestDeclarativeHelmInvalidValuesFile(t *testing.T) { 87 Given(t). 88 Path("helm"). 89 When(). 90 Declarative("declarative-apps/invalid-helm.yaml"). 91 Then(). 92 Expect(HealthIs(health.HealthStatusHealthy)). 93 Expect(SyncStatusIs(SyncStatusCodeUnknown)). 94 Expect(Condition(ApplicationConditionComparisonError, "does-not-exist-values.yaml: no such file or directory")) 95 } 96 97 func TestHelmRepo(t *testing.T) { 98 Given(t). 99 CustomCACertAdded(). 100 HelmRepoAdded("custom-repo"). 101 RepoURLType(RepoURLTypeHelm). 102 Chart("helm"). 103 Revision("1.0.0"). 104 When(). 105 Create(). 106 Then(). 107 When(). 108 Sync(). 109 Then(). 110 Expect(OperationPhaseIs(OperationSucceeded)). 111 Expect(HealthIs(health.HealthStatusHealthy)). 112 Expect(SyncStatusIs(SyncStatusCodeSynced)) 113 } 114 115 func TestHelmValues(t *testing.T) { 116 Given(t). 117 Path("helm"). 118 When(). 119 AddFile("foo.yml", ""). 120 Create(). 121 AppSet("--values", "foo.yml"). 122 Then(). 123 And(func(app *Application) { 124 assert.Equal(t, []string{"foo.yml"}, app.Spec.Source.Helm.ValueFiles) 125 }) 126 } 127 128 func TestHelmValuesMultipleUnset(t *testing.T) { 129 Given(t). 130 Path("helm"). 131 When(). 132 AddFile("foo.yml", ""). 133 AddFile("baz.yml", ""). 134 Create(). 135 AppSet("--values", "foo.yml", "--values", "baz.yml"). 136 Then(). 137 And(func(app *Application) { 138 assert.NotNil(t, app.Spec.Source.Helm) 139 assert.Equal(t, []string{"foo.yml", "baz.yml"}, app.Spec.Source.Helm.ValueFiles) 140 }). 141 When(). 142 AppUnSet("--values", "foo.yml"). 143 Then(). 144 And(func(app *Application) { 145 assert.NotNil(t, app.Spec.Source.Helm) 146 assert.Equal(t, []string{"baz.yml"}, app.Spec.Source.Helm.ValueFiles) 147 }). 148 When(). 149 AppUnSet("--values", "baz.yml"). 150 Then(). 151 And(func(app *Application) { 152 assert.Nil(t, app.Spec.Source.Helm) 153 }) 154 } 155 156 func TestHelmValuesLiteralFileLocal(t *testing.T) { 157 Given(t). 158 Path("helm"). 159 When(). 160 Create(). 161 AppSet("--values-literal-file", "testdata/helm/baz.yaml"). 162 Then(). 163 And(func(app *Application) { 164 data, err := ioutil.ReadFile("testdata/helm/baz.yaml") 165 if err != nil { 166 panic(err) 167 } 168 assert.Equal(t, string(data), app.Spec.Source.Helm.Values) 169 }). 170 When(). 171 AppUnSet("--values-literal"). 172 Then(). 173 And(func(app *Application) { 174 assert.Nil(t, app.Spec.Source.Helm) 175 }) 176 } 177 178 func TestHelmValuesLiteralFileRemote(t *testing.T) { 179 sentinel := "a: b" 180 serve := func(c chan<- string) { 181 // listen on first available dynamic (unprivileged) port 182 listener, err := net.Listen("tcp", ":0") 183 if err != nil { 184 panic(err) 185 } 186 187 // send back the address so that it can be used 188 c <- listener.Addr().String() 189 http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 190 // return the sentinel text at root URL 191 fmt.Fprint(w, sentinel) 192 }) 193 194 panic(http.Serve(listener, nil)) 195 } 196 c := make(chan string, 1) 197 198 // run a local webserver to test data retrieval 199 go serve(c) 200 address := <-c 201 t.Logf("Listening at address: %s", address) 202 203 Given(t). 204 Path("helm"). 205 When(). 206 Create(). 207 AppSet("--values-literal-file", "http://"+address). 208 Then(). 209 And(func(app *Application) { 210 assert.Equal(t, "a: b", app.Spec.Source.Helm.Values) 211 }). 212 When(). 213 AppUnSet("--values-literal"). 214 Then(). 215 And(func(app *Application) { 216 assert.Nil(t, app.Spec.Source.Helm) 217 }) 218 } 219 220 func TestHelmCrdHook(t *testing.T) { 221 Given(t). 222 Path("helm-crd"). 223 When(). 224 Create(). 225 Sync(). 226 Then(). 227 Expect(OperationPhaseIs(OperationSucceeded)). 228 Expect(HealthIs(health.HealthStatusHealthy)). 229 Expect(SyncStatusIs(SyncStatusCodeSynced)). 230 Expect(ResourceResultNumbering(2)) 231 } 232 233 func TestHelmReleaseName(t *testing.T) { 234 Given(t). 235 Path("helm"). 236 When(). 237 Create(). 238 AppSet("--release-name", "foo"). 239 Then(). 240 And(func(app *Application) { 241 assert.Equal(t, "foo", app.Spec.Source.Helm.ReleaseName) 242 }) 243 } 244 245 func TestHelmSet(t *testing.T) { 246 Given(t). 247 Path("helm"). 248 When(). 249 Create(). 250 AppSet("--helm-set", "foo=bar", "--helm-set", "foo=baz", "--helm-set", "app=$ARGOCD_APP_NAME"). 251 Then(). 252 And(func(app *Application) { 253 assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz"}, {Name: "app", Value: "$ARGOCD_APP_NAME"}}, app.Spec.Source.Helm.Parameters) 254 }) 255 } 256 257 func TestHelmSetString(t *testing.T) { 258 Given(t). 259 Path("helm"). 260 When(). 261 Create(). 262 AppSet("--helm-set-string", "foo=bar", "--helm-set-string", "foo=baz", "--helm-set-string", "app=$ARGOCD_APP_NAME"). 263 Then(). 264 And(func(app *Application) { 265 assert.Equal(t, []HelmParameter{{Name: "foo", Value: "baz", ForceString: true}, {Name: "app", Value: "$ARGOCD_APP_NAME", ForceString: true}}, app.Spec.Source.Helm.Parameters) 266 }) 267 } 268 269 func TestHelmSetFile(t *testing.T) { 270 Given(t). 271 Path("helm"). 272 When(). 273 Create(). 274 AppSet("--helm-set-file", "foo=bar.yaml", "--helm-set-file", "foo=baz.yaml"). 275 Then(). 276 And(func(app *Application) { 277 assert.Equal(t, []HelmFileParameter{{Name: "foo", Path: "baz.yaml"}}, app.Spec.Source.Helm.FileParameters) 278 }) 279 } 280 281 // ensure we can use envsubst in "set" variables 282 func TestHelmSetEnv(t *testing.T) { 283 Given(t). 284 Path("helm-values"). 285 When(). 286 Create(). 287 AppSet("--helm-set", "foo=$ARGOCD_APP_NAME"). 288 Sync(). 289 Then(). 290 Expect(OperationPhaseIs(OperationSucceeded)). 291 Expect(SyncStatusIs(SyncStatusCodeSynced)). 292 And(func(app *Application) { 293 assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string)) 294 }) 295 } 296 297 func TestHelmSetStringEnv(t *testing.T) { 298 Given(t). 299 Path("helm-values"). 300 When(). 301 Create(). 302 AppSet("--helm-set-string", "foo=$ARGOCD_APP_NAME"). 303 Sync(). 304 Then(). 305 Expect(OperationPhaseIs(OperationSucceeded)). 306 Expect(SyncStatusIs(SyncStatusCodeSynced)). 307 And(func(app *Application) { 308 assert.Equal(t, Name(), FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", "-o", "jsonpath={.data.foo}")).(string)) 309 }) 310 } 311 312 // make sure kube-version gets passed down to resources 313 func TestKubeVersion(t *testing.T) { 314 Given(t). 315 Path("helm-kube-version"). 316 When(). 317 Create(). 318 Sync(). 319 Then(). 320 Expect(SyncStatusIs(SyncStatusCodeSynced)). 321 And(func(app *Application) { 322 kubeVersion := FailOnErr(Run(".", "kubectl", "-n", DeploymentNamespace(), "get", "cm", "my-map", 323 "-o", "jsonpath={.data.kubeVersion}")).(string) 324 // Capabilities.KubeVersion defaults to 1.9.0, we assume here you are running a later version 325 assert.Equal(t, GetVersions().ServerVersion.Format("v%s.%s.0"), kubeVersion) 326 }) 327 } 328 329 func TestHelmValuesHiddenDirectory(t *testing.T) { 330 Given(t). 331 Path(".hidden-helm"). 332 When(). 333 AddFile("foo.yaml", ""). 334 Create(). 335 AppSet("--values", "foo.yaml"). 336 Sync(). 337 Then(). 338 Expect(OperationPhaseIs(OperationSucceeded)). 339 Expect(HealthIs(health.HealthStatusHealthy)). 340 Expect(SyncStatusIs(SyncStatusCodeSynced)) 341 } 342 343 func TestHelmWithDependencies(t *testing.T) { 344 testHelmWithDependencies(t, "helm-with-dependencies", false) 345 } 346 347 func TestHelm2WithDependencies(t *testing.T) { 348 testHelmWithDependencies(t, "helm2-with-dependencies", false) 349 } 350 351 func TestHelmWithDependenciesLegacyRepo(t *testing.T) { 352 testHelmWithDependencies(t, "helm2-with-dependencies", false) 353 } 354 355 func testHelmWithDependencies(t *testing.T, chartPath string, legacyRepo bool) { 356 ctx := Given(t). 357 CustomCACertAdded(). 358 // these are slow tests 359 Timeout(30) 360 if legacyRepo { 361 ctx.And(func() { 362 FailOnErr(fixture.Run("", "kubectl", "create", "secret", "generic", "helm-repo", 363 "-n", fixture.ArgoCDNamespace, 364 fmt.Sprintf("--from-file=certSecret=%s", repos.CertPath), 365 fmt.Sprintf("--from-file=keySecret=%s", repos.CertKeyPath), 366 fmt.Sprintf("--from-literal=username=%s", GitUsername), 367 fmt.Sprintf("--from-literal=password=%s", GitPassword), 368 )) 369 FailOnErr(fixture.KubeClientset.CoreV1().Secrets(fixture.ArgoCDNamespace).Patch(context.Background(), 370 "helm-repo", types.MergePatchType, []byte(`{"metadata": { "labels": {"e2e.argoproj.io": "true"} }}`), metav1.PatchOptions{})) 371 372 fixture.SetHelmRepos(settings.HelmRepoCredentials{ 373 URL: RepoURL(RepoURLTypeHelm), 374 Name: "custom-repo", 375 KeySecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "keySecret"}, 376 CertSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "certSecret"}, 377 UsernameSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "username"}, 378 PasswordSecret: &v1.SecretKeySelector{LocalObjectReference: v1.LocalObjectReference{Name: "helm-repo"}, Key: "password"}, 379 }) 380 }) 381 } else { 382 ctx = ctx.HelmRepoAdded("custom-repo") 383 } 384 385 ctx.Path(chartPath). 386 When(). 387 Create(). 388 Sync(). 389 Then(). 390 Expect(SyncStatusIs(SyncStatusCodeSynced)) 391 } 392 393 func TestHelm3CRD(t *testing.T) { 394 Given(t). 395 Path("helm3-crd"). 396 When(). 397 Create(). 398 Sync(). 399 Then(). 400 Expect(SyncStatusIs(SyncStatusCodeSynced)). 401 Expect(ResourceSyncStatusIs("CustomResourceDefinition", "crontabs.stable.example.com", SyncStatusCodeSynced)) 402 } 403 404 func TestHelmRepoDiffLocal(t *testing.T) { 405 helmTmp, err := ioutil.TempDir("", "argocd-helm-repo-diff-local-test") 406 assert.NoError(t, err) 407 Given(t). 408 CustomCACertAdded(). 409 HelmRepoAdded("custom-repo"). 410 RepoURLType(RepoURLTypeHelm). 411 Chart("helm"). 412 Revision("1.0.0"). 413 When(). 414 Create(). 415 Then(). 416 When(). 417 Sync(). 418 Then(). 419 Expect(OperationPhaseIs(OperationSucceeded)). 420 Expect(HealthIs(health.HealthStatusHealthy)). 421 Expect(SyncStatusIs(SyncStatusCodeSynced)). 422 And(func(app *Application) { 423 _ = os.Setenv("XDG_CONFIG_HOME", helmTmp) 424 FailOnErr(Run("", "helm", "repo", "add", "custom-repo", RepoURL(RepoURLTypeHelm), 425 "--username", GitUsername, 426 "--password", GitPassword, 427 "--cert-file", "../fixture/certs/argocd-test-client.crt", 428 "--key-file", "../fixture/certs/argocd-test-client.key", 429 "--ca-file", "../fixture/certs/argocd-test-ca.crt", 430 )) 431 diffOutput := FailOnErr(RunCli("app", "diff", app.Name, "--local", "testdata/helm")).(string) 432 assert.Empty(t, diffOutput) 433 }) 434 }