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) {
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})
return nil
}

View File

@ -41,7 +41,7 @@ func (helm *noCallHelmExec) SetHelmBinary(bin string) {
helm.doPanic()
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()
return nil
}

View File

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

View File

@ -105,34 +105,46 @@ func (helm *execer) SetHelmBinary(bin string) {
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 out []byte
var err error
if name == "" && repository != "" {
helm.logger.Infof("empty field name\n")
return fmt.Errorf("empty field name")
}
args = append(args, "repo", "add", name, repository)
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)
// See https://github.com/helm/helm/pull/8777
if cons, err := semver.NewConstraint(">= 3.3.2, < 3.3.4"); err == nil {
if cons.Check(&helm.version) {
args = append(args, "--force-update")
// See https://github.com/helm/helm/pull/8777
if cons, err := semver.NewConstraint(">= 3.3.2, < 3.3.4"); err == nil {
if cons.Check(&helm.version) {
args = append(args, "--force-update")
}
} else {
panic(err)
}
} else {
panic(err)
}
if certfile != "" && keyfile != "" {
args = append(args, "--cert-file", certfile, "--key-file", keyfile)
if certfile != "" && keyfile != "" {
args = append(args, "--cert-file", certfile, "--key-file", keyfile)
}
if cafile != "" {
args = append(args, "--ca-file", cafile)
}
if username != "" && password != "" {
args = append(args, "--username", username, "--password", password)
}
helm.logger.Infof("Adding repo %v %v", name, repository)
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
}
if cafile != "" {
args = append(args, "--ca-file", cafile)
}
if username != "" && password != "" {
args = append(args, "--username", username, "--password", password)
}
helm.logger.Infof("Adding repo %v %v", name, repository)
out, err := helm.exec(args, map[string]string{})
helm.info(out)
return err
}
@ -370,6 +382,15 @@ func (helm *execer) exec(args []string, env map[string]string) ([]byte, error) {
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) {
if len(out) > 0 {
helm.logger.Infof("%s", out)

View File

@ -82,7 +82,7 @@ func Test_AddRepo_Helm_3_3_2(t *testing.T) {
kubeContext: "dev",
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/
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
logger := NewLogger(&buffer, "debug")
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/
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()
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/
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()
helm.AddRepo("myRepo", "https://repo.example.com/", "", "", "", "", "")
helm.AddRepo("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/:
@ -126,7 +126,25 @@ exec: helm --kube-context dev repo add myRepo https://repo.example.com/:
}
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/
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()
helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "")
helm.AddRepo("", "https://repo.example.com/", "", "", "", "", "", "")
expected = `empty field name
`
@ -482,7 +500,7 @@ func Test_LogLevels(t *testing.T) {
buffer.Reset()
logger := NewLogger(&buffer, logLevel)
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 {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
}

View File

@ -12,7 +12,7 @@ type Interface interface {
SetExtraArgs(args ...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
BuildDeps(name, chart string) error
UpdateDeps(chart string) error

View File

@ -157,6 +157,7 @@ type RepositorySpec struct {
KeyFile string `yaml:"keyFile,omitempty"`
Username string `yaml:"username,omitempty"`
Password string `yaml:"password,omitempty"`
Managed string `yaml:"managed,omitempty"`
}
// ReleaseSpec defines the structure of a helm release
@ -313,7 +314,7 @@ func (st *HelmState) ApplyOverrides(spec *ReleaseSpec) {
}
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
}
@ -355,7 +356,7 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
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
}

View File

@ -857,7 +857,18 @@ func TestHelmState_SyncRepos(t *testing.T) {
},
},
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",
@ -872,7 +883,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
},
},
helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "", "certfile", "keyfile", "", ""},
want: []string{"name", "http://example.com/", "", "certfile", "keyfile", "", "", ""},
},
{
name: "repository with ca file",
@ -886,7 +897,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
},
},
helm: &exectest.Helm{},
want: []string{"name", "http://example.com/", "cafile", "", "", "", ""},
want: []string{"name", "http://example.com/", "cafile", "", "", "", "", ""},
},
{
name: "repository with username and password",
@ -901,7 +912,7 @@ func TestHelmState_SyncRepos(t *testing.T) {
},
},
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 {