From 67ac62c2cd0248492e0dba5a3230c6ddc36d782e Mon Sep 17 00:00:00 2001 From: pamiel <14542297+pamiel@users.noreply.github.com> Date: Mon, 11 Feb 2019 23:07:00 +0100 Subject: [PATCH] Wait Deployment for sub chart of weight 0 and only if targeted --- main.go | 206 ++++++++++++++++++++--------------------- pkg/helm/helm.go | 70 +++++++------- pkg/kubectl/kubectl.go | 10 +- 3 files changed, 144 insertions(+), 142 deletions(-) diff --git a/main.go b/main.go index 7706755..87ff82f 100644 --- a/main.go +++ b/main.go @@ -28,26 +28,26 @@ import ( ) type sprayCmd struct { - chartName string - chartVersion string - targets []string - namespace string - resetValues bool - reuseValues bool - valueFiles []string - valuesSet string - force bool - dryRun bool - debug bool + chartName string + chartVersion string + targets []string + namespace string + resetValues bool + reuseValues bool + valueFiles []string + valuesSet string + force bool + dryRun bool + debug bool } // Dependency ... type Dependency struct { - Name string - Alias string - UsedName string - Targeted bool - Weight int + Name string + Alias string + UsedName string + Targeted bool + Weight int } var ( @@ -94,10 +94,10 @@ func newSprayCmd(args []string) *cobra.Command { p := &sprayCmd{} cmd := &cobra.Command{ - Use: "helm spray [CHART]", - Short: `Helm plugin to upgrade subcharts from an umbrella chart`, - Long: globalUsage, - SilenceUsage: true, + Use: "helm spray [CHART]", + Short: `Helm plugin to upgrade subcharts from an umbrella chart`, + Long: globalUsage, + SilenceUsage: true, RunE: func(cmd *cobra.Command, args []string) error { if len(args) != 1 { @@ -136,12 +136,12 @@ func newSprayCmd(args []string) *cobra.Command { f.BoolVar(&p.debug, "debug", false, "enable verbose output") f.Parse(args) - // When called through helm, debug mode is transmitted through the HELM_DEBUG envvar - if !p.debug { - if "1" == os.Getenv("HELM_DEBUG") { - p.debug = true - } - } + // When called through helm, debug mode is transmitted through the HELM_DEBUG envvar + if !p.debug { + if "1" == os.Getenv("HELM_DEBUG") { + p.debug = true + } + } return cmd @@ -170,30 +170,30 @@ func (p *sprayCmd) spray() error { dependencies := make([]Dependency, len(reqs.Dependencies)) for i, req := range reqs.Dependencies { - // Dependency name and alias + // Dependency name and alias dependencies[i].Name = req.Name dependencies[i].Alias = req.Alias - if req.Alias == "" { - dependencies[i].UsedName = dependencies[i].Name - } else { - dependencies[i].UsedName = dependencies[i].Alias - } - - // Is dependency targeted? If --target is specificed, it should match the name of the current dependency; - // if no --target is specified, then all dependencies are targeted - if len(p.targets) > 0 { - dependencies[i].Targeted = false - for j := range p.targets { - if p.targets[j] == dependencies[i].UsedName { - dependencies[i].Targeted = true - } - } - } else { - dependencies[i].Targeted = true - } - - // Get weight of the dependency. If no weight is specified, setting it to 0 - dependencies[i].Weight = 0 + if req.Alias == "" { + dependencies[i].UsedName = dependencies[i].Name + } else { + dependencies[i].UsedName = dependencies[i].Alias + } + + // Is dependency targeted? If --target is specificed, it should match the name of the current dependency; + // if no --target is specified, then all dependencies are targeted + if len(p.targets) > 0 { + dependencies[i].Targeted = false + for j := range p.targets { + if p.targets[j] == dependencies[i].UsedName { + dependencies[i].Targeted = true + } + } + } else { + dependencies[i].Targeted = true + } + + // Get weight of the dependency. If no weight is specified, setting it to 0 + dependencies[i].Weight = 0 depi, err := values.Table(dependencies[i].UsedName) if (err == nil && depi["weight"] != nil) { w64 := depi["weight"].(float64) @@ -206,72 +206,72 @@ func (p *sprayCmd) spray() error { } // For debug... - if p.debug { + if p.debug { for _, dependency := range dependencies { - if dependency.Alias == "" { - fmt.Printf("[spray] subchart: \"%s\" | targeted: %t | weight: %d\n", dependency.Name, dependency.Targeted, dependency.Weight) - } else { - fmt.Printf("[spray] subchart: \"%s\" (is alias of: \"%s\") | targeted: %t | weight: %d\n", dependency.Alias, dependency.Name, dependency.Targeted, dependency.Weight) - } + if dependency.Alias == "" { + fmt.Printf("[spray] subchart: \"%s\" | targeted: %t | weight: %d\n", dependency.Name, dependency.Targeted, dependency.Weight) + } else { + fmt.Printf("[spray] subchart: \"%s\" (is alias of: \"%s\") | targeted: %t | weight: %d\n", dependency.Alias, dependency.Name, dependency.Targeted, dependency.Weight) + } } - } + } for i := 0; i <= getMaxWeight(dependencies); i++ { - shouldWait := false + shouldWait := false - // Upgrade the current (targeted) Deployments, following the increasing weight + // Upgrade the current (targeted) Deployments, following the increasing weight for _, dependency := range dependencies { - if dependency.Targeted { - if dependency.Weight == i { - fmt.Println("[spray] upgrading release: \"" + dependency.UsedName + "\"...") - shouldWait = true - - // Add the ".enabled" flags to ensure that only the current chart is to be executed - valuesSet := "" - for _, dep := range dependencies { - if dep.UsedName == dependency.UsedName { - valuesSet = valuesSet + dep.UsedName + ".enabled=true," - } else { - valuesSet = valuesSet + dep.UsedName + ".enabled=false," - } - } - valuesSet = valuesSet + p.valuesSet - - // Upgrade the Deployment - helm.UpgradeWithValues(p.namespace, dependency.UsedName, dependency.UsedName, p.chartName, p.resetValues, p.reuseValues, p.valueFiles, valuesSet, p.force, p.dryRun, p.debug) - - if !p.dryRun { - status := helm.GetHelmStatus(dependency.UsedName) - if status != "DEPLOYED" { - os.Exit(1) - } - } - - fmt.Println("[spray] release: \"" + dependency.UsedName + "\" upgraded") - } - } + if dependency.Targeted { + if dependency.Weight == i { + fmt.Println("[spray] upgrading release: \"" + dependency.UsedName + "\"...") + shouldWait = true + + // Add the ".enabled" flags to ensure that only the current chart is to be executed + valuesSet := "" + for _, dep := range dependencies { + if dep.UsedName == dependency.UsedName { + valuesSet = valuesSet + dep.UsedName + ".enabled=true," + } else { + valuesSet = valuesSet + dep.UsedName + ".enabled=false," + } + } + valuesSet = valuesSet + p.valuesSet + + // Upgrade the Deployment + helm.UpgradeWithValues(p.namespace, dependency.UsedName, dependency.UsedName, p.chartName, p.resetValues, p.reuseValues, p.valueFiles, valuesSet, p.force, p.dryRun, p.debug) + + if !p.dryRun { + status := helm.GetHelmStatus(dependency.UsedName) + if status != "DEPLOYED" { + os.Exit(1) + } + } + + fmt.Println("[spray] release: \"" + dependency.UsedName + "\" upgraded") + } + } } - // Wait availability of the Deployment just upgraded - if shouldWait { - fmt.Println("[spray] waiting for Liveness and Readiness...") - - if !p.dryRun { - for _, dependency := range dependencies { - if i > 0 && dependency.Weight == i { - for { - if kubectl.IsDeploymentUpToDate(dependency.UsedName, p.namespace) { - break - } - time.Sleep(5 * time.Second) - } - } - } - } + // Wait availability of the Deployment just upgraded + if shouldWait { + fmt.Println("[spray] waiting for Liveness and Readiness...") + + if !p.dryRun { + for _, dependency := range dependencies { + if dependency.Weight == i && dependency.Targeted == true { + for { + if kubectl.IsDeploymentUpToDate(dependency.UsedName, p.namespace) { + break + } + time.Sleep(5 * time.Second) + } + } + } + } } } - fmt.Println("[spray] upgrade completed.") + fmt.Println("[spray] upgrade completed.") return nil } diff --git a/pkg/helm/helm.go b/pkg/helm/helm.go index af1717c..85c4867 100644 --- a/pkg/helm/helm.go +++ b/pkg/helm/helm.go @@ -97,39 +97,39 @@ func Delete(chart string, dryRun bool) { // UpgradeWithValues ... func UpgradeWithValues(namespace string, release string, chartName string, chartPath string, resetValues bool, reuseValues bool, valueFiles []string, valuesSet string, force bool, dryRun bool, debug bool) { var myargs []string = []string{"upgrade", "--install", release, chartPath, "--namespace", namespace, "--set", valuesSet} - for _, v := range valueFiles { - myargs = append(myargs, "-f") - myargs = append(myargs, v) - } + for _, v := range valueFiles { + myargs = append(myargs, "-f") + myargs = append(myargs, v) + } - if resetValues { - myargs = append(myargs, "--reset-values") - } + if resetValues { + myargs = append(myargs, "--reset-values") + } - if reuseValues { - myargs = append(myargs, "--reuse-values") - } + if reuseValues { + myargs = append(myargs, "--reuse-values") + } - if force { - myargs = append(myargs, "--force") - } + if force { + myargs = append(myargs, "--force") + } if dryRun { - myargs = append(myargs, "--dry-run") - } + myargs = append(myargs, "--dry-run") + } - if debug { - myargs = append(myargs, "--debug") - fmt.Printf("[spray] running helm command for \"%s\": %v\n", release, myargs) - } + if debug { + myargs = append(myargs, "--debug") + fmt.Printf("[spray] running helm command for \"%s\": %v\n", release, myargs) + } cmd := exec.Command("helm", myargs...) - if debug { - cmd.Stdout = os.Stdout - } else { - cmdOutput := &bytes.Buffer{} - cmd.Stdout = cmdOutput - } + if debug { + cmd.Stdout = os.Stdout + } else { + cmdOutput := &bytes.Buffer{} + cmd.Stdout = cmdOutput + } cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { @@ -148,18 +148,18 @@ func Upgrade(namespace string, chart string, chartPath string, valuesSet string, myargs = []string{"upgrade", "--install", "--namespace", namespace, "--set", chart + ".enabled=true," + valuesSet, chart, chartPath} } - if debug { - myargs = append(myargs, "--debug") - fmt.Printf("[spray] running command: %v\n", myargs) - } + if debug { + myargs = append(myargs, "--debug") + fmt.Printf("[spray] running command: %v\n", myargs) + } cmd := exec.Command("helm", myargs...) - if debug { - cmd.Stdout = os.Stdout - } else { - cmdOutput := &bytes.Buffer{} - cmd.Stdout = cmdOutput - } + if debug { + cmd.Stdout = os.Stdout + } else { + cmdOutput := &bytes.Buffer{} + cmd.Stdout = cmdOutput + } cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { diff --git a/pkg/kubectl/kubectl.go b/pkg/kubectl/kubectl.go index 268bf30..d68101c 100644 --- a/pkg/kubectl/kubectl.go +++ b/pkg/kubectl/kubectl.go @@ -14,7 +14,6 @@ package kubectl import ( "fmt" - "log" "os" "os/exec" ) @@ -90,7 +89,8 @@ func IsStatefulSetUpToDate(deployment string, namespace string) bool { func getDesired(k8stype string, namespace string, deployment string) string { desired, err := exec.Command("kubectl", "--namespace", namespace, "get", k8stype, deployment, "-o=jsonpath='{.spec.replicas}'").Output() if err != nil { - log.Fatal(err) + // Cannot make the difference between an error when calling kubectl and no corresponding resource found. Return "0" in any case. + return "0" } return string(desired) } @@ -98,7 +98,8 @@ func getDesired(k8stype string, namespace string, deployment string) string { func getReady(k8stype string, namespace string, deployment string) string { ready, err := exec.Command("kubectl", "--namespace", namespace, "get", k8stype, deployment, "-o=jsonpath='{.status.readyReplicas}'").Output() if err != nil { - log.Fatal(err) + // Cannot make the difference between an error when calling kubectl and no corresponding resource found. Return "0" in any case. + return "0" } return string(ready) } @@ -106,7 +107,8 @@ func getReady(k8stype string, namespace string, deployment string) string { func getUpdated(k8stype string, namespace string, deployment string) string { updated, err := exec.Command("kubectl", "--namespace", namespace, "get", k8stype, deployment, "-o=jsonpath='{.status.updatedReplicas}'").Output() if err != nil { - log.Fatal(err) + // Cannot make the difference between an error when calling kubectl and no corresponding resource found. Return "0" in any case. + return "0" } return string(updated) }