diff --git a/README.md b/README.md index 62728ecc..33b611fd 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,8 @@ repositories: url: http://roboll.io/charts certFile: optional_client_cert keyFile: optional_client_key + username: optional_username + password: optional_password context: kube-context # kube-context (--kube-context) @@ -76,8 +78,8 @@ releases: Helmfile uses [Go templates](https://godoc.org/text/template) for templating your helmfile.yaml. While go ships several built-in functions, we have added all of the functions in the [Sprig library](https://godoc.org/github.com/Masterminds/sprig). -We also added one special template function: `requiredEnv`. -The `required_env` function allows you to declare a particular environment variable as required for template rendering. +We also added one special template function: `requiredEnv`. +The `required_env` function allows you to declare a particular environment variable as required for template rendering. If the environment variable is unset or empty, the template rendering will fail with an error message. ## Using environment variables @@ -178,6 +180,8 @@ The `helmfile sync` sub-command sync your cluster state as described in your `he Under the covers, Helmfile executes `helm upgrade --install` for each `release` declared in the manifest, by optionally decrypting [secrets](#secrets) to be consumed as helm chart values. It also updates specified chart repositories and updates the dependencies of any referenced local charts. +For Helm 2.9+ you can use a username and password to authenticate to a remote repository. WARNING - repository password will be exposed unmasked in console using literal value or environment variable. + ### diff The `helmfile diff` sub-command executes the [helm-diff](https://github.com/databus23/helm-diff) plugin across all of diff --git a/helmexec/exec.go b/helmexec/exec.go index 1d9bff6b..a5f5bc9e 100644 --- a/helmexec/exec.go +++ b/helmexec/exec.go @@ -30,12 +30,15 @@ func (helm *execer) SetExtraArgs(args ...string) { helm.extra = args } -func (helm *execer) AddRepo(name, repository, certfile, keyfile string) error { +func (helm *execer) AddRepo(name, repository, certfile, keyfile, username, password string) error { var args []string args = append(args, "repo", "add", name, repository) if certfile != "" && keyfile != "" { args = append(args, "--cert-file", certfile, "--key-file", keyfile) } + if username != "" && password != "" { + args = append(args, "--username", username, "--password", password) + } out, err := helm.exec(args...) helm.write(out) return err diff --git a/helmexec/exec_test.go b/helmexec/exec_test.go index 2142e87e..f19f4a46 100644 --- a/helmexec/exec_test.go +++ b/helmexec/exec_test.go @@ -59,18 +59,25 @@ func Test_SetExtraArgs(t *testing.T) { func Test_AddRepo(t *testing.T) { var buffer bytes.Buffer helm := MockExecer(&buffer, "dev") - helm.AddRepo("myRepo", "https://repo.example.com/", "cert.pem", "key.pem") + helm.AddRepo("myRepo", "https://repo.example.com/", "cert.pem", "key.pem", "", "") expected := "exec: helm repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem --kube-context dev\n" if buffer.String() != expected { t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected) } buffer.Reset() - helm.AddRepo("myRepo", "https://repo.example.com/", "", "") + helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "") expected = "exec: helm repo add myRepo https://repo.example.com/ --kube-context dev\n" if buffer.String() != expected { t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected) } + + buffer.Reset() + helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "example_user", "example_password") + expected = "exec: helm repo add myRepo https://repo.example.com/ --username example_user --password example_password --kube-context dev\n" + if buffer.String() != expected { + t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected) + } } func Test_UpdateRepo(t *testing.T) { diff --git a/helmexec/helmexec.go b/helmexec/helmexec.go index 0b0a556b..38e5ab50 100644 --- a/helmexec/helmexec.go +++ b/helmexec/helmexec.go @@ -4,7 +4,7 @@ package helmexec type Interface interface { SetExtraArgs(args ...string) - AddRepo(name, repository, certfile, keyfile string) error + AddRepo(name, repository, certfile, keyfile, username, password string) error UpdateRepo() error UpdateDeps(chart string) error SyncRelease(name, chart string, flags ...string) error diff --git a/state/state.go b/state/state.go index 5d79c50f..38237212 100644 --- a/state/state.go +++ b/state/state.go @@ -37,6 +37,8 @@ type RepositorySpec struct { URL string `yaml:"url"` CertFile string `yaml:"certFile"` KeyFile string `yaml:"keyFile"` + Username string `yaml:"username"` + Password string `yaml:"password"` } // ReleaseSpec defines the structure of a helm release @@ -152,7 +154,7 @@ func (state *HelmState) SyncRepos(helm helmexec.Interface) []error { errs := []error{} for _, repo := range state.Repositories { - if err := helm.AddRepo(repo.Name, repo.URL, repo.CertFile, repo.KeyFile); err != nil { + if err := helm.AddRepo(repo.Name, repo.URL, repo.CertFile, repo.KeyFile, repo.Username, repo.Password); err != nil { errs = append(errs, err) } } diff --git a/state/state_test.go b/state/state_test.go index 7475d18a..f3a785ab 100644 --- a/state/state_test.go +++ b/state/state_test.go @@ -521,8 +521,8 @@ func (helm *mockHelmExec) UpdateDeps(chart string) error { func (helm *mockHelmExec) SetExtraArgs(args ...string) { return } -func (helm *mockHelmExec) AddRepo(name, repository, certfile, keyfile string) error { - helm.repo = []string{name, repository, certfile, keyfile} +func (helm *mockHelmExec) AddRepo(name, repository, certfile, keyfile, username, password string) error { + helm.repo = []string{name, repository, certfile, keyfile, username, password} return nil } func (helm *mockHelmExec) UpdateRepo() error { @@ -576,10 +576,12 @@ func TestHelmState_SyncRepos(t *testing.T) { URL: "http://example.com/", CertFile: "", KeyFile: "", + Username: "", + Password: "", }, }, helm: &mockHelmExec{}, - want: []string{"name", "http://example.com/", "", ""}, + want: []string{"name", "http://example.com/", "", "", "", ""}, }, { name: "repository with cert and key", @@ -589,10 +591,27 @@ func TestHelmState_SyncRepos(t *testing.T) { URL: "http://example.com/", CertFile: "certfile", KeyFile: "keyfile", + Username: "", + Password: "", }, }, helm: &mockHelmExec{}, - want: []string{"name", "http://example.com/", "certfile", "keyfile"}, + want: []string{"name", "http://example.com/", "certfile", "keyfile", "", ""}, + }, + { + name: "repository with username and password", + repos: []RepositorySpec{ + { + Name: "name", + URL: "http://example.com/", + CertFile: "", + KeyFile: "", + Username: "example_user", + Password: "example_password", + }, + }, + helm: &mockHelmExec{}, + want: []string{"name", "http://example.com/", "", "", "example_user", "example_password"}, }, } for _, tt := range tests {