diff --git a/README.md b/README.md index f45cbde..0cc9513 100644 --- a/README.md +++ b/README.md @@ -8,10 +8,24 @@ appropriate helm command based on which action it is included within: `install`, ### Install or Upgrade -``` +```shell porter mixin install helm ``` +### Configure mixin repositories + +```yaml +- helm: + repositories: + stable: + url: "https://kubernetes-charts.storage.googleapis.com" + cafile: "path/to/cafile" + certfile: "path/to/certfile" + keyfile: "path/to/keyfile" + username: "username" + password: "password" +``` + ### Mixin Syntax Install diff --git a/pkg/helm/build.go b/pkg/helm/build.go index 0b57158..538c6a8 100644 --- a/pkg/helm/build.go +++ b/pkg/helm/build.go @@ -2,6 +2,10 @@ package helm import ( "fmt" + "strings" + + "get.porter.sh/porter/pkg/exec/builder" + yaml "gopkg.in/yaml.v2" ) // These values may be referenced elsewhere (init.go), hence consts @@ -29,8 +33,86 @@ const getKubectl string = `RUN apt-get update && \ mv kubectl /usr/local/bin && \ chmod a+x /usr/local/bin/kubectl` +// BuildInput represents stdin passed to the mixin for the build command. +type BuildInput struct { + Config MixinConfig +} + +// MixinConfig represents configuration that can be set on the helm mixin in porter.yaml +// mixins: +// - helm: +// repositories: +// stable: +// url: "https://kubernetes-charts.storage.googleapis.com" +// cafile: "path/to/cafile" +// certfile: "path/to/certfile" +// keyfile: "path/to/keyfile" +// username: "username" +// password: "password" +type MixinConfig struct { + Repositories map[string]Repository +} + +type Repository struct { + URL string `yaml:"url,omitempty"` + Cafile string `yaml:"cafile,omitempty"` + Certfile string `yaml:"certfile,omitempty"` + Keyfile string `yaml:"keyfile,omitempty"` + Username string `yaml:"username,omitempty"` + Password string `yaml:"password,omitempty"` +} + func (m *Mixin) Build() error { + + // Create new Builder. + var input BuildInput + err := builder.LoadAction(m.Context, "", func(contents []byte) (interface{}, error) { + err := yaml.Unmarshal(contents, &input) + return &input, err + }) + if err != nil { + return err + } + + // Define helm fmt.Fprintf(m.Out, getHelm, helmDownloadURL) + + // Define kubectl fmt.Fprintf(m.Out, getKubectl, kubeVersion) + + // Go through repositories + for name, repo := range input.Config.Repositories { + + commandValue, err := GetAddRepositoryCommand(name, repo.URL, repo.Cafile, repo.Certfile, repo.Keyfile, repo.Username, repo.Password) + if err != nil && m.Debug { + fmt.Fprintf(m.Err, "DEBUG: addition of repository failed: %s\n", err.Error()) + } else { + fmt.Fprintf(m.Out, strings.Join(commandValue, " ")) + } + } + return nil } + +func GetAddRepositoryCommand(name, url, cafile, certfile, keyfile, username, password string) (commandValue []string, err error) { + + var commandBuilder []string + + if url == "" { + return commandBuilder, fmt.Errorf("repository url must be supplied") + } + + commandBuilder = append(commandBuilder, "\nRUN", "helm", "repo", "add", name, url) + + if certfile != "" && keyfile != "" { + commandBuilder = append(commandBuilder, "--cert-file", certfile, "--key-file", keyfile) + } + if cafile != "" { + commandBuilder = append(commandBuilder, "--ca-file", cafile) + } + if username != "" && password != "" { + commandBuilder = append(commandBuilder, "--username", username, "--password", password) + } + + return commandBuilder, nil +} diff --git a/pkg/helm/build_test.go b/pkg/helm/build_test.go index 64e2ceb..dff5c81 100644 --- a/pkg/helm/build_test.go +++ b/pkg/helm/build_test.go @@ -1,6 +1,8 @@ package helm import ( + "bytes" + "io/ioutil" "testing" "github.com/stretchr/testify/assert" @@ -13,7 +15,7 @@ func TestMixin_Build(t *testing.T) { err := m.Build() require.NoError(t, err) - wantOutput := `RUN apt-get update && \ + buildOutput := `RUN apt-get update && \ apt-get install -y curl && \ curl -o helm.tgz https://get.helm.sh/helm-v2.15.2-linux-amd64.tar.gz && \ tar -xzf helm.tgz && \ @@ -26,6 +28,34 @@ RUN apt-get update && \ mv kubectl /usr/local/bin && \ chmod a+x /usr/local/bin/kubectl` - gotOutput := m.TestContext.GetOutput() - assert.Equal(t, wantOutput, gotOutput) + t.Run("build with a valid config", func(t *testing.T) { + b, err := ioutil.ReadFile("testdata/build-input-with-valid-config.yaml") + require.NoError(t, err) + + m := NewTestMixin(t) + m.Debug = false + m.In = bytes.NewReader(b) + + err = m.Build() + require.NoError(t, err, "build failed") + + wantOutput := buildOutput + "\nRUN helm repo add stable kubernetes-charts --username username --password password" + + gotOutput := m.TestContext.GetOutput() + assert.Equal(t, wantOutput, gotOutput) + }) + + t.Run("build with invalid config", func(t *testing.T) { + b, err := ioutil.ReadFile("testdata/build-input-with-invalid-config.yaml") + require.NoError(t, err) + + m := NewTestMixin(t) + m.Debug = false + m.In = bytes.NewReader(b) + + err = m.Build() + require.NoError(t, err, "build failed") + gotOutput := m.TestContext.GetOutput() + assert.Equal(t, buildOutput, gotOutput) + }) } diff --git a/pkg/helm/testdata/build-input-with-invalid-config.yaml b/pkg/helm/testdata/build-input-with-invalid-config.yaml new file mode 100644 index 0000000..d86f09b --- /dev/null +++ b/pkg/helm/testdata/build-input-with-invalid-config.yaml @@ -0,0 +1,9 @@ +config: + repositories: + stable: +install: + - helm: + description: "Install MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2 diff --git a/pkg/helm/testdata/build-input-with-valid-config.yaml b/pkg/helm/testdata/build-input-with-valid-config.yaml new file mode 100644 index 0000000..a9734f0 --- /dev/null +++ b/pkg/helm/testdata/build-input-with-valid-config.yaml @@ -0,0 +1,12 @@ +config: + repositories: + stable: + url: "kubernetes-charts" + username: "username" + password: "password" +install: + - helm: + description: "Install MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2