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:
		
							parent
							
								
									563fce4adf
								
							
						
					
					
						commit
						8f8669778c
					
				|  | @ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
|  | @ -105,34 +105,46 @@ 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") | ||||||
| 	} | 	} | ||||||
| 	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
 | 		// See https://github.com/helm/helm/pull/8777
 | ||||||
| 	if cons, err := semver.NewConstraint(">= 3.3.2, < 3.3.4"); err == nil { | 		if cons, err := semver.NewConstraint(">= 3.3.2, < 3.3.4"); err == nil { | ||||||
| 		if cons.Check(&helm.version) { | 			if cons.Check(&helm.version) { | ||||||
| 			args = append(args, "--force-update") | 				args = append(args, "--force-update") | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			panic(err) | ||||||
| 		} | 		} | ||||||
| 	} else { |  | ||||||
| 		panic(err) |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	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 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) | 	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) | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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 { | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue