Add basic repository authentication (#154)

* basic repository authentication via new `username` and `password` keys

* add warning to readme
This commit is contained in:
Dan O'Brien 2018-05-30 21:42:38 -04:00 committed by KUOKA Yusuke
parent 1768e5dea7
commit 1a4f342f25
6 changed files with 46 additions and 11 deletions

View File

@ -34,6 +34,8 @@ repositories:
url: http://roboll.io/charts url: http://roboll.io/charts
certFile: optional_client_cert certFile: optional_client_cert
keyFile: optional_client_key keyFile: optional_client_key
username: optional_username
password: optional_password
context: kube-context # kube-context (--kube-context) 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). 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`. 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. 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. If the environment variable is unset or empty, the template rendering will fail with an error message.
## Using environment variables ## 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 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. 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 ### diff
The `helmfile diff` sub-command executes the [helm-diff](https://github.com/databus23/helm-diff) plugin across all of The `helmfile diff` sub-command executes the [helm-diff](https://github.com/databus23/helm-diff) plugin across all of

View File

@ -30,12 +30,15 @@ func (helm *execer) SetExtraArgs(args ...string) {
helm.extra = args 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 var args []string
args = append(args, "repo", "add", name, repository) args = append(args, "repo", "add", name, repository)
if certfile != "" && keyfile != "" { if certfile != "" && keyfile != "" {
args = append(args, "--cert-file", certfile, "--key-file", 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...) out, err := helm.exec(args...)
helm.write(out) helm.write(out)
return err return err

View File

@ -59,18 +59,25 @@ func Test_SetExtraArgs(t *testing.T) {
func Test_AddRepo(t *testing.T) { func Test_AddRepo(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
helm := MockExecer(&buffer, "dev") 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" 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 { if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected) t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
} }
buffer.Reset() 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" expected = "exec: helm repo add myRepo https://repo.example.com/ --kube-context dev\n"
if buffer.String() != expected { if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", 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) { func Test_UpdateRepo(t *testing.T) {

View File

@ -4,7 +4,7 @@ package helmexec
type Interface interface { type Interface interface {
SetExtraArgs(args ...string) SetExtraArgs(args ...string)
AddRepo(name, repository, certfile, keyfile string) error AddRepo(name, repository, certfile, keyfile, username, password string) error
UpdateRepo() error UpdateRepo() error
UpdateDeps(chart string) error UpdateDeps(chart string) error
SyncRelease(name, chart string, flags ...string) error SyncRelease(name, chart string, flags ...string) error

View File

@ -37,6 +37,8 @@ type RepositorySpec struct {
URL string `yaml:"url"` URL string `yaml:"url"`
CertFile string `yaml:"certFile"` CertFile string `yaml:"certFile"`
KeyFile string `yaml:"keyFile"` KeyFile string `yaml:"keyFile"`
Username string `yaml:"username"`
Password string `yaml:"password"`
} }
// ReleaseSpec defines the structure of a helm release // ReleaseSpec defines the structure of a helm release
@ -152,7 +154,7 @@ func (state *HelmState) SyncRepos(helm helmexec.Interface) []error {
errs := []error{} errs := []error{}
for _, repo := range state.Repositories { 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) errs = append(errs, err)
} }
} }

View File

@ -521,8 +521,8 @@ func (helm *mockHelmExec) UpdateDeps(chart string) error {
func (helm *mockHelmExec) SetExtraArgs(args ...string) { func (helm *mockHelmExec) SetExtraArgs(args ...string) {
return return
} }
func (helm *mockHelmExec) AddRepo(name, repository, certfile, keyfile string) error { func (helm *mockHelmExec) AddRepo(name, repository, certfile, keyfile, username, password string) error {
helm.repo = []string{name, repository, certfile, keyfile} helm.repo = []string{name, repository, certfile, keyfile, username, password}
return nil return nil
} }
func (helm *mockHelmExec) UpdateRepo() error { func (helm *mockHelmExec) UpdateRepo() error {
@ -576,10 +576,12 @@ func TestHelmState_SyncRepos(t *testing.T) {
URL: "http://example.com/", URL: "http://example.com/",
CertFile: "", CertFile: "",
KeyFile: "", KeyFile: "",
Username: "",
Password: "",
}, },
}, },
helm: &mockHelmExec{}, helm: &mockHelmExec{},
want: []string{"name", "http://example.com/", "", ""}, want: []string{"name", "http://example.com/", "", "", "", ""},
}, },
{ {
name: "repository with cert and key", name: "repository with cert and key",
@ -589,10 +591,27 @@ func TestHelmState_SyncRepos(t *testing.T) {
URL: "http://example.com/", URL: "http://example.com/",
CertFile: "certfile", CertFile: "certfile",
KeyFile: "keyfile", KeyFile: "keyfile",
Username: "",
Password: "",
}, },
}, },
helm: &mockHelmExec{}, 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 { for _, tt := range tests {