Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Append --force-update for specific helm versions. #1494

Merged
merged 3 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.14

require (
github.com/Azure/azure-sdk-for-go v35.0.0+incompatible // indirect
github.com/Masterminds/semver v1.4.2
github.com/Masterminds/semver/v3 v3.1.0
github.com/Masterminds/sprig/v3 v3.1.0
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a
github.com/go-test/deep v1.0.3
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY=
github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
Expand Down Expand Up @@ -1108,6 +1109,7 @@ google.golang.org/api v0.21.0 h1:zS+Q/CJJnVlXpXQVIz+lH0ZT2lBuT2ac7XD8Y/3w6hY=
google.golang.org/api v0.26.0 h1:VJZ8h6E8ip82FRpQl848c5vAadxlTXrUh8RzQzSRm08=
google.golang.org/api v0.30.0 h1:yfrXXP61wVuLb0vBcG6qaOoIoqYEzOQS8jum51jkv2w=
google.golang.org/api v0.31.0 h1:1w5Sz/puhxFo9lTtip2n47k7toB/U2nCqOKNHd3Yrbo=
google.golang.org/api v0.32.0 h1:Le77IccnTqEa8ryp9wIpX5W3zYm7Gf9LhOp9PHcwFts=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
Expand Down
2 changes: 1 addition & 1 deletion pkg/app/app_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2462,7 +2462,7 @@ func (helm *mockHelmExec) GetVersion() helmexec.Version {
return helmexec.Version{}
}

func (helm *mockHelmExec) IsVersionAtLeast(major int, minor int, patch int) bool {
func (helm *mockHelmExec) IsVersionAtLeast(versionStr string) bool {
return false
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/app/mocks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func (helm *noCallHelmExec) GetVersion() helmexec.Version {
return helmexec.Version{}
}

func (helm *noCallHelmExec) IsVersionAtLeast(major int, minor int, patch int) bool {
func (helm *noCallHelmExec) IsVersionAtLeast(versionStr string) bool {
helm.doPanic()
return false
}
16 changes: 9 additions & 7 deletions pkg/exectest/helm.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"
"sync"

"github.com/Masterminds/semver/v3"
"github.com/roboll/helmfile/pkg/helmexec"
)

Expand All @@ -30,7 +31,7 @@ type Helm struct {
Diffed []Release
FailOnUnexpectedDiff bool
FailOnUnexpectedList bool
Version *helmexec.Version
Version *semver.Version

UpdateDepsCallbacks map[string]func(string) error

Expand Down Expand Up @@ -163,19 +164,20 @@ func (helm *Helm) IsHelm3() bool {
}

func (helm *Helm) GetVersion() helmexec.Version {
if helm.Version != nil {
return *helm.Version
return helmexec.Version{
Major: int(helm.Version.Major()),
Minor: int(helm.Version.Minor()),
Patch: int(helm.Version.Patch()),
}

return helmexec.Version{}
}

func (helm *Helm) IsVersionAtLeast(major int, minor int, patch int) bool {
func (helm *Helm) IsVersionAtLeast(versionStr string) bool {
if helm.Version == nil {
return false
}

return helm.Version.Major >= major && minor >= helm.Version.Minor && patch >= helm.Version.Patch
ver := semver.MustParse(versionStr)
return helm.Version.Equal(ver) || helm.Version.GreaterThan(ver)
}

func (helm *Helm) sync(m *sync.Mutex, f func()) {
Expand Down
72 changes: 33 additions & 39 deletions pkg/helmexec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"

"github.com/Masterminds/semver/v3"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
Expand All @@ -22,7 +22,7 @@ type decryptedSecret struct {

type execer struct {
helmBinary string
version Version
version semver.Version
runner Runner
logger *zap.SugaredLogger
kubeContext string
Expand All @@ -48,51 +48,33 @@ func NewLogger(writer io.Writer, logLevel string) *zap.SugaredLogger {
return zap.New(core).Sugar()
}

// versionRegex matches versions like v1.1.1 and v1.1
var versionRegex = regexp.MustCompile("v(?P<major>\\d+)\\.(?P<minor>\\d+)(?:\\.(?P<patch>\\d+))?")

func parseHelmVersion(versionStr string) (Version, error) {
func parseHelmVersion(versionStr string) (semver.Version, error) {
if len(versionStr) == 0 {
return Version{}, nil
return semver.Version{}, nil
}

matches := versionRegex.FindStringSubmatch(versionStr)
if len(matches) == 0 {
return Version{}, fmt.Errorf("error parsing helm verion '%s'", versionStr)
}
result := make(map[string]string)
for i, name := range versionRegex.SubexpNames() {
result[name] = matches[i]
}
versionStr = strings.TrimLeft(versionStr, "Client: ")
versionStr = strings.TrimRight(versionStr, "\n")

// We ignore errors because regex matches only integers
// If any of the parts does not exist - default "0" will be used
major, _ := strconv.Atoi(result["major"])
minor, _ := strconv.Atoi(result["minor"])
patch, _ := strconv.Atoi(result["patch"])
ver, err := semver.NewVersion(versionStr)
if err != nil {
return semver.Version{}, fmt.Errorf("error parsing helm verion '%s'", versionStr)
}

// Support explicit helm3 opt-in via environment variable
if os.Getenv("HELMFILE_HELM3") != "" && major < 3 {
return Version{
Major: 3,
Minor: 0,
Patch: 0,
}, nil
if os.Getenv("HELMFILE_HELM3") != "" && ver.Major() < 3 {
return *semver.MustParse("v3.0.0"), nil
}

return Version{
Major: major,
Minor: minor,
Patch: patch,
}, nil
return *ver, nil
}

func getHelmVersion(helmBinary string, runner Runner) (Version, error) {
func getHelmVersion(helmBinary string, runner Runner) (semver.Version, error) {

// Autodetect from `helm verison`
bytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil)
if err != nil {
return Version{}, fmt.Errorf("error determining helm version: %w", err)
return semver.Version{}, fmt.Errorf("error determining helm version: %w", err)
}

return parseHelmVersion(string(bytes))
Expand Down Expand Up @@ -130,9 +112,16 @@ func (helm *execer) AddRepo(name, repository, cafile, certfile, keyfile, usernam
return fmt.Errorf("empty field name")
}
args = append(args, "repo", "add", name, repository)
if helm.IsHelm3() && helm.IsVersionAtLeast(3, 3, 2) {
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)
}

if certfile != "" && keyfile != "" {
args = append(args, "--cert-file", certfile, "--key-file", keyfile)
}
Expand Down Expand Up @@ -394,13 +383,18 @@ func (helm *execer) write(out []byte) {
}

func (helm *execer) IsHelm3() bool {
return helm.version.Major == 3
return helm.version.Major() == 3
}

func (helm *execer) GetVersion() Version {
return helm.version
return Version{
Major: int(helm.version.Major()),
Minor: int(helm.version.Minor()),
Patch: int(helm.version.Patch()),
}
}

func (helm *execer) IsVersionAtLeast(major int, minor int, patch int) bool {
return helm.version.Major >= major && helm.version.Minor >= minor && helm.version.Patch >= patch
func (helm *execer) IsVersionAtLeast(versionStr string) bool {
ver := semver.MustParse(versionStr)
return helm.version.Equal(ver) || helm.version.GreaterThan(ver)
}
75 changes: 24 additions & 51 deletions pkg/helmexec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"reflect"
"testing"

"github.com/Masterminds/semver/v3"
"go.uber.org/zap"
)

Expand Down Expand Up @@ -71,6 +72,26 @@ func Test_SetHelmBinary(t *testing.T) {
}
}

func Test_AddRepo_Helm_3_3_2(t *testing.T) {
var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug")
helm := &execer{
helmBinary: "helm",
version: *semver.MustParse("3.3.2"),
logger: logger,
kubeContext: "dev",
runner: &mockRunner{},
}
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:
`
if buffer.String() != expected {
t.Errorf("helmexec.AddRepo()\nactual = %v\nexpect = %v", buffer.String(), expected)
}
}

func Test_AddRepo(t *testing.T) {
var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug")
Expand Down Expand Up @@ -557,63 +578,15 @@ func Test_GetVersion(t *testing.T) {
func Test_IsVersionAtLeast(t *testing.T) {
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
helm := New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
if !helm.IsVersionAtLeast(2, 1, 0) {
if !helm.IsVersionAtLeast("2.1.0") {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 not atleast 2.1")
}

if helm.IsVersionAtLeast(2, 19, 0) {
if helm.IsVersionAtLeast("2.19.0") {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 2.19")
}

if helm.IsVersionAtLeast(3, 2, 0) {
if helm.IsVersionAtLeast("3.2.0") {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 3.2")
}

}

func Test_parseHelmVersion(t *testing.T) {
tests := []struct {
ver string
want Version
wantErr bool
}{
{
ver: "v1.2.3",
want: Version{
Major: 1,
Minor: 2,
Patch: 3,
},
wantErr: false,
},
{
ver: "v1.2",
want: Version{
Major: 1,
Minor: 2,
Patch: 0,
},
wantErr: false,
},
{
ver: "v1",
wantErr: true,
},
{
ver: "1.1.1",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.ver, func(t *testing.T) {
got, err := parseHelmVersion(tt.ver)
if (err != nil) != tt.wantErr {
t.Errorf("parseHelmVersion() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("parseHelmVersion() got = %v, want %v", got, tt.want)
}
})
}
}
2 changes: 1 addition & 1 deletion pkg/helmexec/helmexec.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ type Interface interface {
DecryptSecret(context HelmContext, name string, flags ...string) (string, error)
IsHelm3() bool
GetVersion() Version
IsVersionAtLeast(major int, minor int, patch int) bool
IsVersionAtLeast(versionStr string) bool
}

type DependencyUpdater interface {
Expand Down
13 changes: 7 additions & 6 deletions pkg/state/chart_dependency.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,19 @@ package state

import (
"fmt"
"github.com/Masterminds/semver"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"

"github.com/Masterminds/semver/v3"
goversion "github.com/hashicorp/go-version"
"github.com/r3labs/diff"
"github.com/roboll/helmfile/pkg/app/version"
"github.com/roboll/helmfile/pkg/helmexec"
"go.uber.org/zap"
"gopkg.in/yaml.v2"
"io/ioutil"
"os"
"path/filepath"
"sort"
"strings"
)

type ChartMeta struct {
Expand Down
2 changes: 1 addition & 1 deletion pkg/state/state.go
Original file line number Diff line number Diff line change
Expand Up @@ -2069,7 +2069,7 @@ func (st *HelmState) flagsForUpgrade(helm helmexec.Interface, release *ReleaseSp

if release.CreateNamespace != nil && *release.CreateNamespace ||
release.CreateNamespace == nil && (st.HelmDefaults.CreateNamespace == nil || *st.HelmDefaults.CreateNamespace) {
if helm.IsVersionAtLeast(3, 2, 0) {
if helm.IsVersionAtLeast("3.2.0") {
flags = append(flags, "--create-namespace")
} else if release.CreateNamespace != nil || st.HelmDefaults.CreateNamespace != nil {
// createNamespace was set explicitly, but not running supported version of helm - error
Expand Down