feat: added in oci repository flag and added helm methods to pull and export charts (#1629)
This commit is contained in:
parent
33880dab77
commit
2a71640095
|
|
@ -37,7 +37,7 @@ RUN set -x & \
|
||||||
chmod +x kubectl && \
|
chmod +x kubectl && \
|
||||||
mv kubectl /usr/local/bin/kubectl
|
mv kubectl /usr/local/bin/kubectl
|
||||||
|
|
||||||
RUN ["helm", "init", "--client-only"]
|
RUN ["helm", "init", "--client-only", "--stable-repo-url", "https://charts.helm.sh/stable"]
|
||||||
RUN helm plugin install https://github.com/databus23/helm-diff && \
|
RUN helm plugin install https://github.com/databus23/helm-diff && \
|
||||||
helm plugin install https://github.com/futuresimple/helm-secrets && \
|
helm plugin install https://github.com/futuresimple/helm-secrets && \
|
||||||
helm plugin install https://github.com/hypnoglow/helm-s3.git && \
|
helm plugin install https://github.com/hypnoglow/helm-s3.git && \
|
||||||
|
|
|
||||||
32
README.md
32
README.md
|
|
@ -54,13 +54,14 @@ repositories:
|
||||||
# helm-git powered repository: You can treat any Git repository as a charts repository
|
# helm-git powered repository: You can treat any Git repository as a charts repository
|
||||||
- name: polaris
|
- name: polaris
|
||||||
url: git+https://github.com/reactiveops/polaris@deploy/helm?ref=master
|
url: git+https://github.com/reactiveops/polaris@deploy/helm?ref=master
|
||||||
# Advanced configuration: You can setup basic or tls auth
|
# Advanced configuration: You can setup basic or tls auth and optionally enable helm OCI integration
|
||||||
- name: roboll
|
- name: roboll
|
||||||
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
|
username: optional_username
|
||||||
password: optional_password
|
password: optional_password
|
||||||
|
oci: true
|
||||||
# Advanced configuration: You can use a ca bundle to use an https repo
|
# Advanced configuration: You can use a ca bundle to use an https repo
|
||||||
# with a self-signed certificate
|
# with a self-signed certificate
|
||||||
- name: insecure
|
- name: insecure
|
||||||
|
|
@ -1308,6 +1309,35 @@ repositories:
|
||||||
url: https://<MyRegistry>.azurecr.io/helm/v1/repo
|
url: https://<MyRegistry>.azurecr.io/helm/v1/repo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## OCI Registries
|
||||||
|
|
||||||
|
In order to use OCI chart registries firstly they must be marked in the repository list as OCI enabled, e.g.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
repositories:
|
||||||
|
- name: myOCIRegistry
|
||||||
|
url: https://myregistry.azurecr.io
|
||||||
|
oci: true
|
||||||
|
```
|
||||||
|
|
||||||
|
Secondly the credentials for the OCI registry can either be specified within `helmfile.yaml` similar to
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
repositories:
|
||||||
|
- name: myOCIRegistry
|
||||||
|
url: https://myregistry.azurecr.io
|
||||||
|
oci: true
|
||||||
|
username: spongebob
|
||||||
|
password: squarepants
|
||||||
|
```
|
||||||
|
|
||||||
|
or for CI scenarios these can be sourced from the environment with the format `<registryName>_USERNAME` and `<registryName_PASSWORD>`, e.g.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
export MYOCIREGISTRY_USERNAME=spongebob
|
||||||
|
export MYOCIREGISTRY_PASSWORD=squarepants
|
||||||
|
```
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
We use:
|
We use:
|
||||||
|
|
|
||||||
4
go.sum
4
go.sum
|
|
@ -626,10 +626,6 @@ github.com/ulikunitz/xz v0.5.8 h1:ERv8V6GKqVi23rgu5cj9pVfVzJbOqAY2Ntl88O6c2nQ=
|
||||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||||
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
github.com/urfave/cli v1.22.5 h1:lNq9sAHXK2qfdI8W+GRItjCEkI+2oR4d+MEHy1CKXoU=
|
||||||
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/variantdev/chartify v0.4.9 h1:06foIMnJj31q/l1JZ+54anDLwqtP8zAOv5qVEn2IQhM=
|
|
||||||
github.com/variantdev/chartify v0.4.9/go.mod h1:jqlUJIzcrIVSfg8FC4g+IoC5WB83TBl8rnVVEv6g8MQ=
|
|
||||||
github.com/variantdev/chartify v0.5.0 h1:I6T6oobjLfYmwZ4dUjRsO9AdGKPCMtfzt0CXR0ovl9k=
|
|
||||||
github.com/variantdev/chartify v0.5.0/go.mod h1:jqlUJIzcrIVSfg8FC4g+IoC5WB83TBl8rnVVEv6g8MQ=
|
|
||||||
github.com/variantdev/chartify v0.6.0 h1:QQ00a8Vtuhk6F9jeTZJEXV2g0zRXhYG43xovWZrc3ac=
|
github.com/variantdev/chartify v0.6.0 h1:QQ00a8Vtuhk6F9jeTZJEXV2g0zRXhYG43xovWZrc3ac=
|
||||||
github.com/variantdev/chartify v0.6.0/go.mod h1:qF4XzQlkfH/6k2jAi1hLas+lK4zSCa8kY+r5JdmLA68=
|
github.com/variantdev/chartify v0.6.0/go.mod h1:qF4XzQlkfH/6k2jAi1hLas+lK4zSCa8kY+r5JdmLA68=
|
||||||
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363 h1:KrfQBEUn+wEOQ/6UIfoqRDvn+Q/wZridQ7t0G1vQqKE=
|
github.com/variantdev/dag v0.0.0-20191028002400-bb0b3c785363 h1:KrfQBEUn+wEOQ/6UIfoqRDvn+Q/wZridQ7t0G1vQqKE=
|
||||||
|
|
|
||||||
|
|
@ -2404,6 +2404,10 @@ type mockRunner struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mock *mockRunner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
||||||
return []byte{}, nil
|
return []byte{}, nil
|
||||||
}
|
}
|
||||||
|
|
@ -2441,6 +2445,14 @@ func (helm *mockHelmExec) TemplateRelease(name, chart string, flags ...string) e
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (helm *mockHelmExec) ChartPull(chart string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (helm *mockHelmExec) ChartExport(chart string, path string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (helm *mockHelmExec) UpdateDeps(chart string) error {
|
func (helm *mockHelmExec) UpdateDeps(chart string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -2462,6 +2474,9 @@ func (helm *mockHelmExec) AddRepo(name, repository, cafile, certfile, keyfile, u
|
||||||
func (helm *mockHelmExec) UpdateRepo() error {
|
func (helm *mockHelmExec) UpdateRepo() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (helm *mockHelmExec) RegistryLogin(name string, username string, password string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (helm *mockHelmExec) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
func (helm *mockHelmExec) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,14 @@ func (helm *noCallHelmExec) TemplateRelease(name, chart string, flags ...string)
|
||||||
helm.doPanic()
|
helm.doPanic()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (helm *noCallHelmExec) ChartPull(chart string, flags ...string) error {
|
||||||
|
helm.doPanic()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *noCallHelmExec) ChartExport(chart string, path string, flags ...string) error {
|
||||||
|
helm.doPanic()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (helm *noCallHelmExec) UpdateDeps(chart string) error {
|
func (helm *noCallHelmExec) UpdateDeps(chart string) error {
|
||||||
helm.doPanic()
|
helm.doPanic()
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -49,6 +56,10 @@ func (helm *noCallHelmExec) UpdateRepo() error {
|
||||||
helm.doPanic()
|
helm.doPanic()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (helm *noCallHelmExec) RegistryLogin(name string, username string, password string) error {
|
||||||
|
helm.doPanic()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (helm *noCallHelmExec) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
func (helm *noCallHelmExec) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
||||||
helm.doPanic()
|
helm.doPanic()
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package event
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -16,6 +17,10 @@ var logger = helmexec.NewLogger(os.Stdout, "warn")
|
||||||
type runner struct {
|
type runner struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *runner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
||||||
|
return []byte(""), nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *runner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
func (r *runner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
||||||
if cmd == "ng" {
|
if cmd == "ng" {
|
||||||
return nil, fmt.Errorf("cmd failed due to invalid cmd: %s", cmd)
|
return nil, fmt.Errorf("cmd failed due to invalid cmd: %s", cmd)
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,9 @@ func (helm *Helm) AddRepo(name, repository, cafile, certfile, keyfile, username,
|
||||||
func (helm *Helm) UpdateRepo() error {
|
func (helm *Helm) UpdateRepo() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (helm *Helm) RegistryLogin(name string, username string, password string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
func (helm *Helm) SyncRelease(context helmexec.HelmContext, name, chart string, flags ...string) error {
|
||||||
if strings.Contains(name, "error") {
|
if strings.Contains(name, "error") {
|
||||||
return errors.New("error")
|
return errors.New("error")
|
||||||
|
|
@ -158,7 +161,12 @@ func (helm *Helm) Lint(name, chart string, flags ...string) error {
|
||||||
func (helm *Helm) TemplateRelease(name, chart string, flags ...string) error {
|
func (helm *Helm) TemplateRelease(name, chart string, flags ...string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
func (helm *Helm) ChartPull(chart string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
func (helm *Helm) ChartExport(chart string, path string, flags ...string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
func (helm *Helm) IsHelm3() bool {
|
func (helm *Helm) IsHelm3() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package helmexec
|
package helmexec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
|
@ -72,13 +73,13 @@ func parseHelmVersion(versionStr string) (semver.Version, error) {
|
||||||
|
|
||||||
func getHelmVersion(helmBinary string, runner Runner) (semver.Version, error) {
|
func getHelmVersion(helmBinary string, runner Runner) (semver.Version, error) {
|
||||||
|
|
||||||
// Autodetect from `helm verison`
|
// Autodetect from `helm version`
|
||||||
bytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil)
|
outBytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return semver.Version{}, fmt.Errorf("error determining helm version: %w", err)
|
return semver.Version{}, fmt.Errorf("error determining helm version: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return parseHelmVersion(string(bytes))
|
return parseHelmVersion(string(outBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// New for running helm commands
|
// New for running helm commands
|
||||||
|
|
@ -157,6 +158,24 @@ func (helm *execer) UpdateRepo() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (helm *execer) RegistryLogin(repository string, username string, password string) error {
|
||||||
|
helm.logger.Info("Logging in to registry")
|
||||||
|
args := []string{
|
||||||
|
"registry",
|
||||||
|
"login",
|
||||||
|
repository,
|
||||||
|
"--username",
|
||||||
|
username,
|
||||||
|
"--password",
|
||||||
|
password,
|
||||||
|
}
|
||||||
|
buffer := bytes.Buffer{}
|
||||||
|
buffer.Write([]byte(fmt.Sprintf("%s\n", password)))
|
||||||
|
out, err := helm.execStdIn(args, map[string]string{"HELM_EXPERIMENTAL_OCI": "1"}, &buffer)
|
||||||
|
helm.info(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (helm *execer) BuildDeps(name, chart string) error {
|
func (helm *execer) BuildDeps(name, chart string) error {
|
||||||
helm.logger.Infof("Building dependency release=%v, chart=%v", name, chart)
|
helm.logger.Infof("Building dependency release=%v, chart=%v", name, chart)
|
||||||
out, err := helm.exec([]string{"dependency", "build", chart}, map[string]string{})
|
out, err := helm.exec([]string{"dependency", "build", chart}, map[string]string{})
|
||||||
|
|
@ -369,6 +388,20 @@ func (helm *execer) Fetch(chart string, flags ...string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (helm *execer) ChartPull(chart string, flags ...string) error {
|
||||||
|
helm.logger.Infof("Pulling %v", chart)
|
||||||
|
out, err := helm.exec(append([]string{"chart", "pull", chart}, flags...), map[string]string{"HELM_EXPERIMENTAL_OCI": "1"})
|
||||||
|
helm.info(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (helm *execer) ChartExport(chart string, path string, flags ...string) error {
|
||||||
|
helm.logger.Infof("Exporting %v", chart)
|
||||||
|
out, err := helm.exec(append([]string{"chart", "export", chart, "--destination", path}, flags...), map[string]string{"HELM_EXPERIMENTAL_OCI": "1"})
|
||||||
|
helm.info(out)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...string) error {
|
func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...string) error {
|
||||||
helm.logger.Infof("Deleting %v", name)
|
helm.logger.Infof("Deleting %v", name)
|
||||||
preArgs := context.GetTillerlessArgs(helm)
|
preArgs := context.GetTillerlessArgs(helm)
|
||||||
|
|
@ -398,17 +431,31 @@ func (helm *execer) exec(args []string, env map[string]string) ([]byte, error) {
|
||||||
}
|
}
|
||||||
cmd := fmt.Sprintf("exec: %s %s", helm.helmBinary, strings.Join(cmdargs, " "))
|
cmd := fmt.Sprintf("exec: %s %s", helm.helmBinary, strings.Join(cmdargs, " "))
|
||||||
helm.logger.Debug(cmd)
|
helm.logger.Debug(cmd)
|
||||||
bytes, err := helm.runner.Execute(helm.helmBinary, cmdargs, env)
|
outBytes, err := helm.runner.Execute(helm.helmBinary, cmdargs, env)
|
||||||
return bytes, err
|
return outBytes, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (helm *execer) execStdIn(args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
||||||
|
cmdargs := args
|
||||||
|
if len(helm.extra) > 0 {
|
||||||
|
cmdargs = append(cmdargs, helm.extra...)
|
||||||
|
}
|
||||||
|
if helm.kubeContext != "" {
|
||||||
|
cmdargs = append([]string{"--kube-context", helm.kubeContext}, cmdargs...)
|
||||||
|
}
|
||||||
|
cmd := fmt.Sprintf("exec: %s %s", helm.helmBinary, strings.Join(cmdargs, " "))
|
||||||
|
helm.logger.Debug(cmd)
|
||||||
|
outBytes, err := helm.runner.ExecuteStdIn(helm.helmBinary, cmdargs, env, stdin)
|
||||||
|
return outBytes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (helm *execer) azcli(name string) ([]byte, error) {
|
func (helm *execer) azcli(name string) ([]byte, error) {
|
||||||
cmdargs := append(strings.Split("acr helm repo add --name", " "), name)
|
cmdargs := append(strings.Split("acr helm repo add --name", " "), name)
|
||||||
cmd := fmt.Sprintf("exec: az %s", strings.Join(cmdargs, " "))
|
cmd := fmt.Sprintf("exec: az %s", strings.Join(cmdargs, " "))
|
||||||
helm.logger.Debug(cmd)
|
helm.logger.Debug(cmd)
|
||||||
bytes, err := helm.runner.Execute("az", cmdargs, map[string]string{})
|
outBytes, err := helm.runner.Execute("az", cmdargs, map[string]string{})
|
||||||
helm.logger.Debugf("%s: %s", cmd, bytes)
|
helm.logger.Debugf("%s: %s", cmd, outBytes)
|
||||||
return bytes, err
|
return outBytes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (helm *execer) info(out []byte) {
|
func (helm *execer) info(out []byte) {
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -21,6 +22,10 @@ type mockRunner struct {
|
||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (mock *mockRunner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
||||||
|
return mock.output, mock.err
|
||||||
|
}
|
||||||
|
|
||||||
func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
func (mock *mockRunner) Execute(cmd string, args []string, env map[string]string) ([]byte, error) {
|
||||||
return mock.output, mock.err
|
return mock.output, mock.err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,12 +14,15 @@ type Interface interface {
|
||||||
|
|
||||||
AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
|
AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
|
||||||
UpdateRepo() error
|
UpdateRepo() error
|
||||||
|
RegistryLogin(name string, username string, password string) error
|
||||||
BuildDeps(name, chart string) error
|
BuildDeps(name, chart string) error
|
||||||
UpdateDeps(chart string) error
|
UpdateDeps(chart string) error
|
||||||
SyncRelease(context HelmContext, name, chart string, flags ...string) error
|
SyncRelease(context HelmContext, name, chart string, flags ...string) error
|
||||||
DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error
|
DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error
|
||||||
TemplateRelease(name, chart string, flags ...string) error
|
TemplateRelease(name, chart string, flags ...string) error
|
||||||
Fetch(chart string, flags ...string) error
|
Fetch(chart string, flags ...string) error
|
||||||
|
ChartPull(chart string, flags ...string) error
|
||||||
|
ChartExport(chart string, path string, flags ...string) error
|
||||||
Lint(name, chart string, flags ...string) error
|
Lint(name, chart string, flags ...string) error
|
||||||
ReleaseStatus(context HelmContext, name string, flags ...string) error
|
ReleaseStatus(context HelmContext, name string, flags ...string) error
|
||||||
DeleteRelease(context HelmContext, name string, flags ...string) error
|
DeleteRelease(context HelmContext, name string, flags ...string) error
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ const (
|
||||||
// Runner interface for shell commands
|
// Runner interface for shell commands
|
||||||
type Runner interface {
|
type Runner interface {
|
||||||
Execute(cmd string, args []string, env map[string]string) ([]byte, error)
|
Execute(cmd string, args []string, env map[string]string) ([]byte, error)
|
||||||
|
ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ShellRunner implemention for shell commands
|
// ShellRunner implemention for shell commands
|
||||||
|
|
@ -41,6 +42,17 @@ func (shell ShellRunner) Execute(cmd string, args []string, env map[string]strin
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Execute a shell command
|
||||||
|
func (shell ShellRunner) ExecuteStdIn(cmd string, args []string, env map[string]string, stdin io.Reader) ([]byte, error) {
|
||||||
|
preparedCmd := exec.Command(cmd, args...)
|
||||||
|
preparedCmd.Dir = shell.Dir
|
||||||
|
preparedCmd.Env = mergeEnv(os.Environ(), env)
|
||||||
|
preparedCmd.Stdin = stdin
|
||||||
|
return Output(preparedCmd, &logWriterGenerator{
|
||||||
|
log: shell.Logger,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func Output(c *exec.Cmd, logWriterGenerators ...*logWriterGenerator) ([]byte, error) {
|
func Output(c *exec.Cmd, logWriterGenerators ...*logWriterGenerator) ([]byte, error) {
|
||||||
if c.Stdout != nil {
|
if c.Stdout != nil {
|
||||||
return nil, errors.New("exec: Stdout already set")
|
return nil, errors.New("exec: Stdout already set")
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
|
"github.com/hashicorp/go-getter/helper/url"
|
||||||
"github.com/imdario/mergo"
|
"github.com/imdario/mergo"
|
||||||
"github.com/variantdev/chartify"
|
"github.com/variantdev/chartify"
|
||||||
|
|
||||||
|
|
@ -162,6 +163,7 @@ type RepositorySpec struct {
|
||||||
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"`
|
Managed string `yaml:"managed,omitempty"`
|
||||||
|
OCI bool `yaml:"oci,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReleaseSpec defines the structure of a helm release
|
// ReleaseSpec defines the structure of a helm release
|
||||||
|
|
@ -336,6 +338,7 @@ func (st *HelmState) ApplyOverrides(spec *ReleaseSpec) {
|
||||||
type RepoUpdater interface {
|
type RepoUpdater interface {
|
||||||
AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
|
AddRepo(name, repository, cafile, certfile, keyfile, username, password string, managed string) error
|
||||||
UpdateRepo() error
|
UpdateRepo() error
|
||||||
|
RegistryLogin(name string, username string, password string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
// getRepositoriesToSync returns the names of repositories to be updated
|
// getRepositoriesToSync returns the names of repositories to be updated
|
||||||
|
|
@ -375,8 +378,18 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
|
||||||
if shouldSkip[repo.Name] {
|
if shouldSkip[repo.Name] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
var err error
|
||||||
|
if repo.OCI {
|
||||||
|
username, password := gatherOCIUsernamePassword(repo.Name, repo.Username, repo.Password)
|
||||||
|
if username == "" || password == "" {
|
||||||
|
return nil, fmt.Errorf("username and password are required fields for logging in to OCI registries with helm")
|
||||||
|
}
|
||||||
|
err = helm.RegistryLogin(repo.URL, username, password)
|
||||||
|
} else {
|
||||||
|
err = helm.AddRepo(repo.Name, repo.URL, repo.CaFile, repo.CertFile, repo.KeyFile, repo.Username, repo.Password, repo.Managed)
|
||||||
|
}
|
||||||
|
|
||||||
if err := helm.AddRepo(repo.Name, repo.URL, repo.CaFile, repo.CertFile, repo.KeyFile, repo.Username, repo.Password, repo.Managed); err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -386,6 +399,24 @@ func (st *HelmState) SyncRepos(helm RepoUpdater, shouldSkip map[string]bool) ([]
|
||||||
return updated, nil
|
return updated, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func gatherOCIUsernamePassword(repoName string, username string, password string) (string, string) {
|
||||||
|
var user, pass string
|
||||||
|
|
||||||
|
if username != "" {
|
||||||
|
user = username
|
||||||
|
} else if u := os.Getenv(fmt.Sprintf("%s_USERNAME", strings.ToUpper(repoName))); u != "" {
|
||||||
|
user = u
|
||||||
|
}
|
||||||
|
|
||||||
|
if password != "" {
|
||||||
|
pass = password
|
||||||
|
} else if p := os.Getenv(fmt.Sprintf("%s_PASSWORD", strings.ToUpper(repoName))); p != "" {
|
||||||
|
pass = p
|
||||||
|
}
|
||||||
|
|
||||||
|
return user, pass
|
||||||
|
}
|
||||||
|
|
||||||
type syncResult struct {
|
type syncResult struct {
|
||||||
errors []*ReleaseError
|
errors []*ReleaseError
|
||||||
}
|
}
|
||||||
|
|
@ -857,6 +888,20 @@ type chartPrepareResult struct {
|
||||||
chartFetchedByGoGetter bool
|
chartFetchedByGoGetter bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) GetRepositoryAndNameFromChartName(chartName string) (*RepositorySpec, string) {
|
||||||
|
chart := strings.Split(chartName, "/")
|
||||||
|
if len(chart) == 1 {
|
||||||
|
return nil, chartName
|
||||||
|
}
|
||||||
|
repo := chart[0]
|
||||||
|
for _, r := range st.Repositories {
|
||||||
|
if r.Name == repo {
|
||||||
|
return &r, strings.Join(chart[1:], "/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, chartName
|
||||||
|
}
|
||||||
|
|
||||||
// PrepareCharts creates temporary directories of charts.
|
// PrepareCharts creates temporary directories of charts.
|
||||||
//
|
//
|
||||||
// Each resulting "chart" can be one of the followings:
|
// Each resulting "chart" can be one of the followings:
|
||||||
|
|
@ -935,16 +980,21 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
||||||
|
|
||||||
chartName := release.Chart
|
chartName := release.Chart
|
||||||
|
|
||||||
isLocal := st.directoryExistsAt(normalizeChart(st.basePath, chartName))
|
|
||||||
|
|
||||||
chartPath, err := st.goGetterChart(chartName, release.Directory, release.ForceGoGetter)
|
chartPath, err := st.goGetterChart(chartName, release.Directory, release.ForceGoGetter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
results <- &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
results <- &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
chartFetchedByGoGetter := chartPath != chartName
|
chartFetchedByGoGetter := chartPath != chartName
|
||||||
|
|
||||||
|
isOCI, chartPath, err := st.getOCIChart(release, dir, helm)
|
||||||
|
if err != nil {
|
||||||
|
results <- &chartPrepareResult{err: fmt.Errorf("release %q: %w", release.Name, err)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isLocal := st.directoryExistsAt(normalizeChart(st.basePath, chartName))
|
||||||
|
|
||||||
chartification, clean, err := st.PrepareChartify(helm, release, chartPath, workerIndex)
|
chartification, clean, err := st.PrepareChartify(helm, release, chartPath, workerIndex)
|
||||||
defer clean()
|
defer clean()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -957,7 +1007,7 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
||||||
skipDepsGlobal := opts.SkipDeps
|
skipDepsGlobal := opts.SkipDeps
|
||||||
skipDepsRelease := release.SkipDeps != nil && *release.SkipDeps
|
skipDepsRelease := release.SkipDeps != nil && *release.SkipDeps
|
||||||
skipDepsDefault := release.SkipDeps == nil && st.HelmDefaults.SkipDeps
|
skipDepsDefault := release.SkipDeps == nil && st.HelmDefaults.SkipDeps
|
||||||
skipDeps := !isLocal || skipDepsGlobal || skipDepsRelease || skipDepsDefault
|
skipDeps := !isLocal || skipDepsGlobal || skipDepsRelease || skipDepsDefault || !isOCI
|
||||||
|
|
||||||
if chartification != nil {
|
if chartification != nil {
|
||||||
c := chartify.New(
|
c := chartify.New(
|
||||||
|
|
@ -2928,3 +2978,64 @@ func (st *HelmState) Reverse() {
|
||||||
st.Helmfiles[i], st.Helmfiles[j] = st.Helmfiles[j], st.Helmfiles[i]
|
st.Helmfiles[i], st.Helmfiles[j] = st.Helmfiles[j], st.Helmfiles[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) getOCIChart(release *ReleaseSpec, tempDir string, helm helmexec.Interface) (bool, string, error) {
|
||||||
|
|
||||||
|
isOCI := false
|
||||||
|
|
||||||
|
repo, name := st.GetRepositoryAndNameFromChartName(release.Chart)
|
||||||
|
if repo == nil {
|
||||||
|
return false, release.Chart, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if repo.OCI {
|
||||||
|
isOCI = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isOCI {
|
||||||
|
return isOCI, release.Chart, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
repoUrl, err := url.Parse(repo.URL)
|
||||||
|
if err != nil {
|
||||||
|
return isOCI, release.Chart, err
|
||||||
|
}
|
||||||
|
if repoUrl.Scheme == "" {
|
||||||
|
return isOCI, release.Chart, fmt.Errorf("unable to detect scheme - a valid url must be supplied for OCI registry %s", repo.URL)
|
||||||
|
}
|
||||||
|
|
||||||
|
chartVersion := "latest"
|
||||||
|
if release.Version != "" {
|
||||||
|
chartVersion = release.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
qualifiedChartName := fmt.Sprintf("%s/%s:%s", repoUrl.Host, name, chartVersion)
|
||||||
|
|
||||||
|
err = helm.ChartPull(qualifiedChartName)
|
||||||
|
if err != nil {
|
||||||
|
return isOCI, release.Chart, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pathElems := []string{
|
||||||
|
tempDir,
|
||||||
|
}
|
||||||
|
|
||||||
|
if release.Namespace != "" {
|
||||||
|
pathElems = append(pathElems, release.Namespace)
|
||||||
|
}
|
||||||
|
|
||||||
|
if release.KubeContext != "" {
|
||||||
|
pathElems = append(pathElems, release.KubeContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
pathElems = append(pathElems, release.Name, name, chartVersion)
|
||||||
|
|
||||||
|
dir := filepath.Join(pathElems...)
|
||||||
|
err = helm.ChartExport(qualifiedChartName, dir)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return isOCI, release.Chart, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return isOCI, filepath.Join(dir, name), nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ info "Using namespace: ${test_ns}"
|
||||||
if helm version --client 2>/dev/null | grep '"v2\.'; then
|
if helm version --client 2>/dev/null | grep '"v2\.'; then
|
||||||
helm_major_version=2
|
helm_major_version=2
|
||||||
info "Using Helm version: $(helm version --short --client | grep -o v.*$)"
|
info "Using Helm version: $(helm version --short --client | grep -o v.*$)"
|
||||||
${helm} init --wait --override spec.template.spec.automountServiceAccountToken=true
|
${helm} init --stable-repo-url https://charts.helm.sh/stable --wait --override spec.template.spec.automountServiceAccountToken=true
|
||||||
# helm v3
|
# helm v3
|
||||||
else
|
else
|
||||||
helm_major_version=3
|
helm_major_version=3
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue