Support for createNamespace (#1226)

- createNamespace is a new attribute that can be added to helmDefaults
  or an individual release to enforce the creation of a release namespace
  during sync if the namespace does not exist. This leverages helm's
  (3.2+) --create-namespace flag for the install/upgrade command. If
  running helm < 3.2, the createNamespace attribute has no effect.

Resolves #891
Resolves #1140
This commit is contained in:
Craig Dunford
2020-04-26 10:41:40 +09:00
committed by GitHub
parent b1190508b2
commit eeb61e6174
9 changed files with 270 additions and 26 deletions
+55 -9
View File
@@ -6,6 +6,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"strconv"
"strings"
"sync"
@@ -21,7 +22,7 @@ type decryptedSecret struct {
type execer struct {
helmBinary string
isHelm3 bool
version Version
runner Runner
logger *zap.SugaredLogger
kubeContext string
@@ -47,25 +48,62 @@ func NewLogger(writer io.Writer, logLevel string) *zap.SugaredLogger {
return zap.New(core).Sugar()
}
func detectHelm3(helmBinary string, logger *zap.SugaredLogger, runner Runner) bool {
// Support explicit opt-in via environment variable
if os.Getenv("HELMFILE_HELM3") != "" {
return true
}
func getHelmVersion(helmBinary string, logger *zap.SugaredLogger, runner Runner) Version {
// Autodetect from `helm verison`
bytes, err := runner.Execute(helmBinary, []string{"version", "--client", "--short"}, nil)
if err != nil {
panic(err)
}
return strings.HasPrefix(string(bytes), "v3.")
if bytes == nil || len(bytes) == 0 {
return Version{}
}
re := regexp.MustCompile("v(?P<major>\\d+)\\.(?P<minor>\\d+)\\.(?P<patch>\\d+)")
matches := re.FindStringSubmatch(string(bytes))
result := make(map[string]string)
for i, name := range re.SubexpNames() {
result[name] = matches[i]
}
major, err := strconv.Atoi(result["major"])
if err != nil {
panic(err)
}
minor, err := strconv.Atoi(result["minor"])
if err != nil {
panic(err)
}
patch, err := strconv.Atoi(result["patch"])
if err != nil {
panic(err)
}
// Support explicit helm3 opt-in via environment variable
if os.Getenv("HELMFILE_HELM3") != "" && major < 3 {
return Version{
Major: 3,
Minor: 0,
Patch: 0,
}
}
return Version{
Major: major,
Minor: minor,
Patch: patch,
}
}
// New for running helm commands
func New(helmBinary string, logger *zap.SugaredLogger, kubeContext string, runner Runner) *execer {
return &execer{
helmBinary: helmBinary,
isHelm3: detectHelm3(helmBinary, logger, runner),
version: getHelmVersion(helmBinary, logger, runner),
logger: logger,
kubeContext: kubeContext,
runner: runner,
@@ -349,5 +387,13 @@ func (helm *execer) write(out []byte) {
}
func (helm *execer) IsHelm3() bool {
return helm.isHelm3
return helm.version.Major == 3
}
func (helm *execer) GetVersion() Version {
return helm.version
}
func (helm *execer) IsVersionAtLeast(major int, minor int) bool {
return helm.version.Major >= major && helm.version.Minor >= minor
}
+41
View File
@@ -528,4 +528,45 @@ func Test_IsHelm3(t *testing.T) {
if !helm.IsHelm3() {
t.Error("helmexec.IsHelm3() - Failed to detect Helm 3")
}
os.Setenv("HELMFILE_HELM3", "1")
helm2Runner = mockRunner{output: []byte("Client: v2.16.0+ge13bc94\n")}
helm = New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
if !helm.IsHelm3() {
t.Error("helmexec.IsHelm3() - Helm3 not detected when HELMFILE_HELM3 is set")
}
os.Setenv("HELMFILE_HELM3", "")
}
func Test_GetVersion(t *testing.T) {
helm2Runner := mockRunner{output: []byte("Client: v2.16.1+ge13bc94\n")}
helm := New("helm", NewLogger(os.Stdout, "info"), "dev", &helm2Runner)
ver := helm.GetVersion()
if ver.Major != 2 || ver.Minor != 16 || ver.Patch != 1 {
t.Error(fmt.Sprintf("helmexec.GetVersion - did not detect correct Helm2 version; it was: %+v", ver))
}
helm3Runner := mockRunner{output: []byte("v3.2.4+ge29ce2a\n")}
helm = New("helm", NewLogger(os.Stdout, "info"), "dev", &helm3Runner)
ver = helm.GetVersion()
if ver.Major != 3 || ver.Minor != 2 || ver.Patch != 4 {
t.Error(fmt.Sprintf("helmexec.GetVersion - did not detect correct Helm3 version; it was: %+v", ver))
}
}
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) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 not atleast 2.1")
}
if helm.IsVersionAtLeast(2, 19) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 2.19")
}
if helm.IsVersionAtLeast(3, 2) {
t.Error("helmexec.IsVersionAtLeast - 2.16.1 is atleast 3.2")
}
}
+9
View File
@@ -1,5 +1,12 @@
package helmexec
// Version represents the version of helm
type Version struct {
Major int
Minor int
Patch int
}
// Interface for executing helm commands
type Interface interface {
SetExtraArgs(args ...string)
@@ -20,6 +27,8 @@ type Interface interface {
List(context HelmContext, filter string, flags ...string) (string, error)
DecryptSecret(context HelmContext, name string, flags ...string) (string, error)
IsHelm3() bool
GetVersion() Version
IsVersionAtLeast(major int, minor int) bool
}
type DependencyUpdater interface {