diff --git a/Makefile b/Makefile index 16dd157..05bb4a4 100644 --- a/Makefile +++ b/Makefile @@ -43,6 +43,7 @@ build-client: generate $(GO) build -ldflags '$(LDFLAGS)' -o $(BINDIR)/$(MIXIN)$(FILE_EXT) ./cmd/$(MIXIN) generate: packr2 + $(GO) mod tidy $(GO) generate ./... HAS_PACKR2 := $(shell command -v packr2) diff --git a/README.md b/README.md index 72469fd..5e660fd 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Helm client ```yaml - helm3: - clientVersion: v3.1.2 + clientVersion: v3.2.1 ``` Repositories @@ -41,48 +41,48 @@ Install ```yaml install: -- helm3: - description: "Description of the command" - name: RELEASE_NAME - chart: STABLE_CHART_NAME - version: CHART_VERSION - namespace: NAMESPACE - replace: BOOL - devel: BOOL - wait: BOOL # default true - set: - VAR1: VALUE1 - VAR2: VALUE2 + - helm3: + description: "Description of the command" + name: RELEASE_NAME + chart: STABLE_CHART_NAME + version: CHART_VERSION + namespace: NAMESPACE + replace: BOOL + devel: BOOL + wait: BOOL # default true + set: + VAR1: VALUE1 + VAR2: VALUE2 ``` Upgrade ```yaml install: -- helm3: - description: "Description of the command" - name: RELEASE_NAME - chart: STABLE_CHART_NAME - version: CHART_VERSION - namespace: NAMESPACE - resetValues: BOOL - reuseValues: BOOL - wait: BOOL # default true - set: - VAR1: VALUE1 - VAR2: VALUE2 + - helm3: + description: "Description of the command" + name: RELEASE_NAME + chart: STABLE_CHART_NAME + version: CHART_VERSION + namespace: NAMESPACE + resetValues: BOOL + reuseValues: BOOL + wait: BOOL # default true + set: + VAR1: VALUE1 + VAR2: VALUE2 ``` Uninstall ```yaml uninstall: -- helm3: - description: "Description of command" - namespace: NAMESPACE - releases: - - RELEASE_NAME1 - - RELASE_NAME2 + - helm3: + description: "Description of command" + namespace: NAMESPACE + releases: + - RELEASE_NAME1 + - RELASE_NAME2 ``` #### Outputs @@ -91,9 +91,9 @@ The mixin supports saving secrets from Kuberentes as outputs. ```yaml outputs: - - name: NAME - secret: SECRET_NAME - key: SECRET_KEY + - name: NAME + secret: SECRET_NAME + key: SECRET_KEY ``` ### Examples @@ -102,51 +102,51 @@ Install ```yaml install: -- helm3: - description: "Install MySQL" - name: mydb - chart: stable/mysql - version: 0.10.2 - namespace: mydb - replace: true - set: - mysqlDatabase: wordpress - mysqlUser: wordpress - outputs: - - name: mysql-root-password - secret: mydb-mysql - key: mysql-root-password - - name: mysql-password - secret: mydb-mysql - key: mysql-password + - helm3: + description: "Install MySQL" + name: mydb + chart: stable/mysql + version: 0.10.2 + namespace: mydb + replace: true + set: + mysqlDatabase: wordpress + mysqlUser: wordpress + outputs: + - name: mysql-root-password + secret: mydb-mysql + key: mysql-root-password + - name: mysql-password + secret: mydb-mysql + key: mysql-password ``` Upgrade ```yaml upgrade: -- helm3: - description: "Upgrade MySQL" - name: porter-ci-mysql - chart: stable/mysql - version: 0.10.2 - wait: true - resetValues: true - reuseValues: false - set: - mysqlDatabase: mydb - mysqlUser: myuser - livenessProbe.initialDelaySeconds: 30 - persistence.enabled: true + - helm3: + description: "Upgrade MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2 + wait: true + resetValues: true + reuseValues: false + set: + mysqlDatabase: mydb + mysqlUser: myuser + livenessProbe.initialDelaySeconds: 30 + persistence.enabled: true ``` Uninstall ```yaml uninstall: -- helm3: - description: "Uninstall MySQL" - namespace: mydb - releases: - - mydb -``` \ No newline at end of file + - helm3: + description: "Uninstall MySQL" + namespace: mydb + releases: + - mydb +``` diff --git a/example/porter.yaml b/example/porter.yaml index d366b62..646d8d3 100644 --- a/example/porter.yaml +++ b/example/porter.yaml @@ -1,6 +1,6 @@ mixins: - helm3: - clientVersion: v3.1.2 + clientVersion: v3.2.1 repositories: stable: url: "https://kubernetes-charts.storage.googleapis.com" @@ -79,7 +79,7 @@ upgrade: uninstall: - helm3: description: "Uninstall MySQL" - purge: true + namespace: "{{ bundle.parameters.namespace }}" releases: - "{{ bundle.parameters.mysql-name }}" diff --git a/go.mod b/go.mod index ed8f055..476e95d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( cloud.google.com/go v0.55.0 // indirect get.porter.sh/porter v0.23.0-beta.1 github.com/Azure/go-autorest/autorest v0.10.0 // indirect + github.com/Masterminds/semver v1.5.0 github.com/PaesslerAG/gval v1.0.1 // indirect github.com/PuerkitoBio/goquery v1.5.1 // indirect github.com/ghodss/yaml v1.0.0 diff --git a/pkg/helm3/build.go b/pkg/helm3/build.go index 867b4c4..964c6b6 100644 --- a/pkg/helm3/build.go +++ b/pkg/helm3/build.go @@ -5,11 +5,14 @@ import ( "strings" "get.porter.sh/porter/pkg/exec/builder" + "github.com/Masterminds/semver" + "github.com/pkg/errors" yaml "gopkg.in/yaml.v2" ) -// These values may be referenced elsewhere (init.go), hence consts -var helmClientVersion string +// clientVersionConstraint represents the semver constraint for the Helm client version +// Currently, this mixin only supports Helm clients versioned v2.x.x +const clientVersionConstraint string = "^v3.x" // BuildInput represents stdin passed to the mixin for the build command. type BuildInput struct { @@ -55,8 +58,17 @@ func (m *Mixin) Build() error { return err } - if input.Config.ClientVersion != "" { - m.HelmClientVersion = input.Config.ClientVersion + suppliedClientVersion := input.Config.ClientVersion + if suppliedClientVersion != "" { + ok, err := validate(suppliedClientVersion, clientVersionConstraint) + if err != nil { + return err + } + if !ok { + return errors.Errorf("supplied clientVersion %q does not meet semver constraint %q", + suppliedClientVersion, clientVersionConstraint) + } + m.HelmClientVersion = suppliedClientVersion } // Install helm3 @@ -100,3 +112,18 @@ func GetAddRepositoryCommand(name, url, cafile, certfile, keyfile, username, pas return commandBuilder, nil } + +// validate validates that the supplied clientVersion meets the supplied semver constraint +func validate(clientVersion, constraint string) (bool, error) { + c, err := semver.NewConstraint(constraint) + if err != nil { + return false, errors.Wrapf(err, "unable to parse version constraint %q", constraint) + } + + v, err := semver.NewVersion(clientVersion) + if err != nil { + return false, errors.Wrapf(err, "supplied client version %q cannot be parsed as semver", clientVersion) + } + + return c.Check(v), nil +} diff --git a/pkg/helm3/build_test.go b/pkg/helm3/build_test.go index 0300c99..01972d9 100644 --- a/pkg/helm3/build_test.go +++ b/pkg/helm3/build_test.go @@ -67,4 +67,28 @@ RUN mv linux-amd64/helm /usr/local/bin/helm3` gotOutput := m.TestContext.GetOutput() assert.Equal(t, wantOutput, gotOutput) }) + + t.Run("build with a defined helm client version that does not meet the semver constraint", func(t *testing.T) { + + b, err := ioutil.ReadFile("testdata/build-input-with-unsupported-client-version.yaml") + require.NoError(t, err) + + m := NewTestMixin(t) + m.Debug = false + m.In = bytes.NewReader(b) + err = m.Build() + require.EqualError(t, err, `supplied clientVersion "v2.16.1" does not meet semver constraint "^v3.x"`) + }) + + t.Run("build with a defined helm client version that does not parse as valid semver", func(t *testing.T) { + + b, err := ioutil.ReadFile("testdata/build-input-with-invalid-client-version.yaml") + require.NoError(t, err) + + m := NewTestMixin(t) + m.Debug = false + m.In = bytes.NewReader(b) + err = m.Build() + require.EqualError(t, err, `supplied client version "v3.2.1.0" cannot be parsed as semver: Invalid Semantic Version`) + }) } diff --git a/pkg/helm3/helm3.go b/pkg/helm3/helm3.go index 9d4bf58..588d69c 100644 --- a/pkg/helm3/helm3.go +++ b/pkg/helm3/helm3.go @@ -16,7 +16,7 @@ import ( k8s "k8s.io/client-go/kubernetes" ) -const defaultHelmClientVersion string = "v3.1.2" +const defaultHelmClientVersion string = "v3.2.1" // Helm is the logic behind the helm mixin type Mixin struct { diff --git a/pkg/helm3/helpers.go b/pkg/helm3/helpers.go index 52d0f1c..47bc806 100644 --- a/pkg/helm3/helpers.go +++ b/pkg/helm3/helpers.go @@ -8,7 +8,7 @@ import ( testclient "k8s.io/client-go/kubernetes/fake" ) -const MockHelmClientVersion string = "v3.1.2" +const MockHelmClientVersion string = "v3.2.1" type TestMixin struct { *Mixin diff --git a/pkg/helm3/testdata/build-input-with-invalid-client-version.yaml b/pkg/helm3/testdata/build-input-with-invalid-client-version.yaml new file mode 100644 index 0000000..ef67caf --- /dev/null +++ b/pkg/helm3/testdata/build-input-with-invalid-client-version.yaml @@ -0,0 +1,8 @@ +config: + clientVersion: v3.2.1.0 +install: + - helm3: + description: "Install MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2 diff --git a/pkg/helm3/testdata/build-input-with-supported-client-version.yaml b/pkg/helm3/testdata/build-input-with-supported-client-version.yaml new file mode 100644 index 0000000..ae7e5a4 --- /dev/null +++ b/pkg/helm3/testdata/build-input-with-supported-client-version.yaml @@ -0,0 +1,8 @@ +config: + clientVersion: v3.2.1 +install: + - helm3: + description: "Install MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2 diff --git a/pkg/helm3/testdata/build-input-with-unsupported-client-version.yaml b/pkg/helm3/testdata/build-input-with-unsupported-client-version.yaml new file mode 100644 index 0000000..a15dd33 --- /dev/null +++ b/pkg/helm3/testdata/build-input-with-unsupported-client-version.yaml @@ -0,0 +1,8 @@ +config: + clientVersion: v2.16.1 +install: + - helm3: + description: "Install MySQL" + name: porter-ci-mysql + chart: stable/mysql + version: 0.10.2 diff --git a/pkg/helm3/testdata/build-input-with-version.yaml b/pkg/helm3/testdata/build-input-with-version.yaml index 6e88844..c627866 100644 --- a/pkg/helm3/testdata/build-input-with-version.yaml +++ b/pkg/helm3/testdata/build-input-with-version.yaml @@ -1,5 +1,5 @@ config: - version: v3.1.2 + version: v3.2.1 install: - helm3: description: "Install MySQL"