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  }