Support for azure acr helm repositories (#1526)

Adds a basic support for Helm repositories hosted on Azure Container Registry (not OCI but classic ones). Add a new field to RepositorySpec to state that is externally managed and runs the `az-cli` command instead of the helm one to manage the repository.
This commit is contained in:
Javier Palacios 2020-10-15 01:45:45 +02:00 committed by GitHub
parent 563fce4adf
commit 8f8669778c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 88 additions and 37 deletions

View File

@ -2418,7 +2418,7 @@ func (helm *mockHelmExec) SetExtraArgs(args ...string) {
func (helm *mockHelmExec) SetHelmBinary(bin string) { func (helm *mockHelmExec) SetHelmBinary(bin string) {
return return
} }
func (helm *mockHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error { func (helm *mockHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error {
helm.repos = append(helm.repos, mockRepo{Name: name}) helm.repos = append(helm.repos, mockRepo{Name: name})
return nil return nil
} }

View File

@ -41,7 +41,7 @@ func (helm *noCallHelmExec) SetHelmBinary(bin string) {
helm.doPanic() helm.doPanic()
return return
} }
func (helm *noCallHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error { func (helm *noCallHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error {
helm.doPanic() helm.doPanic()
return nil return nil
} }

View File

@ -82,8 +82,8 @@ func (helm *Helm) SetExtraArgs(args ...string) {
func (helm *Helm) SetHelmBinary(bin string) { func (helm *Helm) SetHelmBinary(bin string) {
return return
} }
func (helm *Helm) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error { func (helm *Helm) AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error {
helm.Repo = []string{name, repository, cafile, certfile, keyfile, username, password} helm.Repo = []string{name, repository, cafile, certfile, keyfile, username, password, managed}
return nil return nil
} }
func (helm *Helm) UpdateRepo() error { func (helm *Helm) UpdateRepo() error {

View File

@ -105,12 +105,19 @@ func (helm *execer) SetHelmBinary(bin string) {
helm.helmBinary = bin helm.helmBinary = bin
} }
func (helm *execer) AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error { func (helm *execer) AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error {
var args []string var args []string
var out []byte
var err error
if name == "" && repository != "" { if name == "" && repository != "" {
helm.logger.Infof("empty field name\n") helm.logger.Infof("empty field name\n")
return fmt.Errorf("empty field name") return fmt.Errorf("empty field name")
} }
switch managed {
case "acr":
helm.logger.Infof("Adding repo %v (acr)", name)
out, err = helm.azcli(name)
case "":
args = append(args, "repo", "add", name, repository) args = append(args, "repo", "add", name, repository)
// See https://github.com/helm/helm/pull/8777 // See https://github.com/helm/helm/pull/8777
@ -132,7 +139,12 @@ func (helm *execer) AddRepo(name, repository, cafile, certfile, keyfile, usernam
args = append(args, "--username", username, "--password", password) args = append(args, "--username", username, "--password", password)
} }
helm.logger.Infof("Adding repo %v %v", name, repository) helm.logger.Infof("Adding repo %v %v", name, repository)
out, err := helm.exec(args, map[string]string{}) out, err = helm.exec(args, map[string]string{})
default:
helm.logger.Errorf("ERROR: unknown type '%v' for repository %v", managed, name)
out = nil
err = nil
}
helm.info(out) helm.info(out)
return err return err
} }
@ -370,6 +382,15 @@ func (helm *execer) exec(args []string, env map[string]string) ([]byte, error) {
return bytes, err return bytes, err
} }
func (helm *execer) azcli(name string) ([]byte, error) {
cmdargs := append(strings.Split("acr helm repo add --name", " "), name)
cmd := fmt.Sprintf("exec: az %s", strings.Join(cmdargs, " "))
helm.logger.Debug(cmd)
bytes, err := helm.runner.Execute("az", cmdargs, map[string]string{})
helm.logger.Debugf("%s: %s", cmd, bytes)
return bytes, err
}
func (helm *execer) info(out []byte) { func (helm *execer) info(out []byte) {
if len(out) > 0 { if len(out) > 0 {
helm.logger.Infof("%s", out) helm.logger.Infof("%s", out)

View File

@ -82,7 +82,7 @@ func Test_AddRepo_Helm_3_3_2(t *testing.T) {
kubeContext: "dev", kubeContext: "dev",
runner: &mockRunner{}, runner: &mockRunner{},
} }
helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "") helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "")
expected := `Adding repo myRepo https://repo.example.com/ expected := `Adding repo myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem: exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --force-update --cert-file cert.pem --key-file key.pem:
@ -96,7 +96,7 @@ func Test_AddRepo(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug") logger := NewLogger(&buffer, "debug")
helm := MockExecer(logger, "dev") helm := MockExecer(logger, "dev")
helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "") helm.AddRepo("myRepo", "https://repo.example.com/", "", "cert.pem", "key.pem", "", "", "")
expected := `Adding repo myRepo https://repo.example.com/ expected := `Adding repo myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem: exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --cert-file cert.pem --key-file key.pem:
@ -106,7 +106,7 @@ exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --cert-f
} }
buffer.Reset() buffer.Reset()
helm.AddRepo("myRepo", "https://repo.example.com/", "ca.crt", "", "", "", "") helm.AddRepo("myRepo", "https://repo.example.com/", "ca.crt", "", "", "", "", "")
expected = `Adding repo myRepo https://repo.example.com/ expected = `Adding repo myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --ca-file ca.crt exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --ca-file ca.crt
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --ca-file ca.crt: exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --ca-file ca.crt:
@ -116,7 +116,7 @@ exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --ca-fil
} }
buffer.Reset() buffer.Reset()
helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "", "") helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "", "", "")
expected = `Adding repo myRepo https://repo.example.com/ expected = `Adding repo myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ exec: helm --kube-context dev repo add myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/: exec: helm --kube-context dev repo add myRepo https://repo.example.com/:
@ -126,7 +126,25 @@ exec: helm --kube-context dev repo add myRepo https://repo.example.com/:
} }
buffer.Reset() buffer.Reset()
helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password") helm.AddRepo("acrRepo", "", "", "", "", "", "", "acr")
expected = `Adding repo acrRepo (acr)
exec: az acr helm repo add --name acrRepo
exec: az acr helm repo add --name acrRepo:
`
if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
}
buffer.Reset()
helm.AddRepo("otherRepo", "", "", "", "", "", "", "unknown")
expected = `ERROR: unknown type 'unknown' for repository otherRepo
`
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 = `Adding repo myRepo https://repo.example.com/ expected = `Adding repo myRepo https://repo.example.com/
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --username example_user --password example_password exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --username example_user --password example_password
exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --username example_user --password example_password: exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --username example_user --password example_password:
@ -136,7 +154,7 @@ exec: helm --kube-context dev repo add myRepo https://repo.example.com/ --userna
} }
buffer.Reset() buffer.Reset()
helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "") helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "", "")
expected = `empty field name expected = `empty field name
` `
@ -482,7 +500,7 @@ func Test_LogLevels(t *testing.T) {
buffer.Reset() buffer.Reset()
logger := NewLogger(&buffer, logLevel) logger := NewLogger(&buffer, logLevel)
helm := MockExecer(logger, "") helm := MockExecer(logger, "")
helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password") helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "example_user", "example_password", "")
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)
} }

View File

@ -12,7 +12,7 @@ type Interface interface {
SetExtraArgs(args ...string) SetExtraArgs(args ...string)
SetHelmBinary(bin string) SetHelmBinary(bin string)
AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
UpdateRepo() error UpdateRepo() error
BuildDeps(name, chart string) error BuildDeps(name, chart string) error
UpdateDeps(chart string) error UpdateDeps(chart string) error

View File

@ -157,6 +157,7 @@ type RepositorySpec struct {
KeyFile string `yaml:"keyFile,omitempty"` KeyFile string `yaml:"keyFile,omitempty"`
Username string `yaml:"username,omitempty"` Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"` Password string `yaml:"password,omitempty"`
Managed string `yaml:"managed,omitempty"`
} }
// ReleaseSpec defines the structure of a helm release // ReleaseSpec defines the structure of a helm release
@ -313,7 +314,7 @@ func (st *HelmState) ApplyOverrides(spec *ReleaseSpec) {
} }
type RepoUpdater interface { type RepoUpdater interface {
AddRepo(name, repository, cafile, certfile, keyfile, username, password string) error AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
UpdateRepo() error UpdateRepo() error
} }
@ -355,7 +356,7 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
continue continue
} }
if err := helm.AddRepo(repo.Name, repo.URL, repo.CaFile, repo.CertFile, repo.KeyFile, repo.Username, repo.Password); err != nil { if err := helm.AddRepo(repo.Name, repo.URL, repo.CaFile, repo.CertFile, repo.KeyFile, repo.Username, repo.Password, repo.Managed); err != nil {
return nil, err return nil, err
} }

View File

@ -857,7 +857,18 @@ func TestHelmState_SyncRepos(t *testing.T) {
}, },
}, },
helm: &exectest.Helm{}, helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "", "", "", "", ""}, want: []string{"name", "http://example.com/", "", "", "", "", "", ""},
},
{
name: "ACR hosted repository",
repos: []RepositorySpec{
{
Name: "name",
Managed: "acr",
},
},
helm: &exectest.Helm{},
want: []string{"name", "", "", "", "", "", "", "acr"},
}, },
{ {
name: "repository with cert and key", name: "repository with cert and key",
@ -872,7 +883,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
}, },
}, },
helm: &exectest.Helm{}, helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "", "certfile", "keyfile", "", ""}, want: []string{"name", "http://example.com/", "", "certfile", "keyfile", "", "", ""},
}, },
{ {
name: "repository with ca file", name: "repository with ca file",
@ -886,7 +897,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
}, },
}, },
helm: &exectest.Helm{}, helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "cafile", "", "", "", ""}, want: []string{"name", "http://example.com/", "cafile", "", "", "", "", ""},
}, },
{ {
name: "repository with username and password", name: "repository with username and password",
@ -901,7 +912,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
}, },
}, },
helm: &exectest.Helm{}, helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "", "", "", "example_user", "example_password"}, want: []string{"name", "http://example.com/", "", "", "", "example_user", "example_password", ""},
}, },
} }
for i := range tests { for i := range tests {