Stabilize helmfile-diff output (#1619)
`helmfile-diff` sorts multiple and concurrent helm-diff outputs and stabilizes writes to stdout. It's required to use the stdout from helmfile-diff to detect if there was another change(s) between 2 points in time. For example, terraform-provider-helmfile runs a helmfile-diff on `terraform plan` and another on `terraform apply`. `terraform`, by design, fails when helmfile-diff outputs were not equivalent. Stabilized helmfile-diff output rescues that.
This commit is contained in:
parent
28e7ebb4a4
commit
1c7b872476
|
|
@ -1,6 +1,7 @@
|
||||||
package helmexec
|
package helmexec
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
)
|
)
|
||||||
|
|
@ -10,6 +11,7 @@ type HelmContext struct {
|
||||||
TillerNamespace string
|
TillerNamespace string
|
||||||
HistoryMax int
|
HistoryMax int
|
||||||
WorkerIndex int
|
WorkerIndex int
|
||||||
|
Writer io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (context *HelmContext) GetTillerlessArgs(helm *execer) []string {
|
func (context *HelmContext) GetTillerlessArgs(helm *execer) []string {
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ func (helm *execer) SyncRelease(context HelmContext, name, chart string, flags .
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := helm.exec(append(append(preArgs, "upgrade", "--install", "--reset-values", name, chart), flags...), env)
|
out, err := helm.exec(append(append(preArgs, "upgrade", "--install", "--reset-values", name, chart), flags...), env)
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -192,7 +192,7 @@ func (helm *execer) ReleaseStatus(context HelmContext, name string, flags ...str
|
||||||
preArgs := context.GetTillerlessArgs(helm)
|
preArgs := context.GetTillerlessArgs(helm)
|
||||||
env := context.getTillerlessEnv()
|
env := context.getTillerlessEnv()
|
||||||
out, err := helm.exec(append(append(preArgs, "status", name), flags...), env)
|
out, err := helm.exec(append(append(preArgs, "status", name), flags...), env)
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +219,7 @@ func (helm *execer) List(context HelmContext, filter string, flags ...string) (s
|
||||||
lines = lines[1:]
|
lines = lines[1:]
|
||||||
out = []byte(strings.Join(lines, "\n"))
|
out = []byte(strings.Join(lines, "\n"))
|
||||||
}
|
}
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return string(out), err
|
return string(out), err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,12 +317,16 @@ func (helm *execer) TemplateRelease(name string, chart string, flags ...string)
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := helm.exec(append(args, flags...), map[string]string{})
|
out, err := helm.exec(append(args, flags...), map[string]string{})
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (helm *execer) DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error {
|
func (helm *execer) DiffRelease(context HelmContext, name, chart string, suppressDiff bool, flags ...string) error {
|
||||||
helm.logger.Infof("Comparing release=%v, chart=%v", name, chart)
|
if context.Writer != nil {
|
||||||
|
fmt.Fprintf(context.Writer, "Comparing release=%v, chart=%v\n", name, chart)
|
||||||
|
} else {
|
||||||
|
helm.logger.Infof("Comparing release=%v, chart=%v", name, chart)
|
||||||
|
}
|
||||||
preArgs := context.GetTillerlessArgs(helm)
|
preArgs := context.GetTillerlessArgs(helm)
|
||||||
env := context.getTillerlessEnv()
|
env := context.getTillerlessEnv()
|
||||||
out, err := helm.exec(append(append(preArgs, "diff", "upgrade", "--reset-values", "--allow-unreleased", name, chart), flags...), env)
|
out, err := helm.exec(append(append(preArgs, "diff", "upgrade", "--reset-values", "--allow-unreleased", name, chart), flags...), env)
|
||||||
|
|
@ -340,13 +344,13 @@ func (helm *execer) DiffRelease(context HelmContext, name, chart string, suppres
|
||||||
case ExitError:
|
case ExitError:
|
||||||
if e.ExitStatus() == 2 {
|
if e.ExitStatus() == 2 {
|
||||||
if !(suppressDiff) {
|
if !(suppressDiff) {
|
||||||
helm.write(out)
|
helm.write(context.Writer, out)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if !(suppressDiff) {
|
} else if !(suppressDiff) {
|
||||||
helm.write(out)
|
helm.write(context.Writer, out)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -354,7 +358,7 @@ func (helm *execer) DiffRelease(context HelmContext, name, chart string, suppres
|
||||||
func (helm *execer) Lint(name, chart string, flags ...string) error {
|
func (helm *execer) Lint(name, chart string, flags ...string) error {
|
||||||
helm.logger.Infof("Linting release=%v, chart=%v", name, chart)
|
helm.logger.Infof("Linting release=%v, chart=%v", name, chart)
|
||||||
out, err := helm.exec(append([]string{"lint", chart}, flags...), map[string]string{})
|
out, err := helm.exec(append([]string{"lint", chart}, flags...), map[string]string{})
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -370,7 +374,7 @@ func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...str
|
||||||
preArgs := context.GetTillerlessArgs(helm)
|
preArgs := context.GetTillerlessArgs(helm)
|
||||||
env := context.getTillerlessEnv()
|
env := context.getTillerlessEnv()
|
||||||
out, err := helm.exec(append(append(preArgs, "delete", name), flags...), env)
|
out, err := helm.exec(append(append(preArgs, "delete", name), flags...), env)
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -380,7 +384,7 @@ func (helm *execer) TestRelease(context HelmContext, name string, flags ...strin
|
||||||
env := context.getTillerlessEnv()
|
env := context.getTillerlessEnv()
|
||||||
args := []string{"test", name}
|
args := []string{"test", name}
|
||||||
out, err := helm.exec(append(append(preArgs, args...), flags...), env)
|
out, err := helm.exec(append(append(preArgs, args...), flags...), env)
|
||||||
helm.write(out)
|
helm.write(nil, out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -413,9 +417,12 @@ func (helm *execer) info(out []byte) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (helm *execer) write(out []byte) {
|
func (helm *execer) write(w io.Writer, out []byte) {
|
||||||
if len(out) > 0 {
|
if len(out) > 0 {
|
||||||
fmt.Printf("%s\n", out)
|
if w == nil {
|
||||||
|
w = os.Stdout
|
||||||
|
}
|
||||||
|
fmt.Fprintf(w, "%s\n", out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1418,7 +1418,9 @@ func (st *HelmState) LintReleases(helm helmexec.Interface, additionalValues []st
|
||||||
}
|
}
|
||||||
|
|
||||||
type diffResult struct {
|
type diffResult struct {
|
||||||
err *ReleaseError
|
release *ReleaseSpec
|
||||||
|
err *ReleaseError
|
||||||
|
buf *bytes.Buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
type diffPrepareResult struct {
|
type diffPrepareResult struct {
|
||||||
|
|
@ -1600,6 +1602,14 @@ func (st *HelmState) createHelmContext(spec *ReleaseSpec, workerIndex int) helme
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) createHelmContextWithWriter(spec *ReleaseSpec, w io.Writer) helmexec.HelmContext {
|
||||||
|
ctx := st.createHelmContext(spec, 0)
|
||||||
|
|
||||||
|
ctx.Writer = w
|
||||||
|
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
type DiffOpts struct {
|
type DiffOpts struct {
|
||||||
Context int
|
Context int
|
||||||
NoColor bool
|
NoColor bool
|
||||||
|
|
@ -1617,7 +1627,13 @@ func (o *DiffOpts) Apply(opts *DiffOpts) {
|
||||||
type DiffOpt interface{ Apply(*DiffOpts) }
|
type DiffOpt interface{ Apply(*DiffOpts) }
|
||||||
|
|
||||||
// DiffReleases wrapper for executing helm diff on the releases
|
// DiffReleases wrapper for executing helm diff on the releases
|
||||||
// It returns releases that had any changes
|
// It returns releases that had any changes, and errors if any.
|
||||||
|
//
|
||||||
|
// This function has responsibility to stabilize the order of writes to stdout from multiple concurrent helm-diff runs.
|
||||||
|
// It's required to use the stdout from helmfile-diff to detect if there was another change(s) between 2 points in time.
|
||||||
|
// For example, terraform-provider-helmfile runs a helmfile-diff on `terraform plan` and another on `terraform apply`.
|
||||||
|
// `terraform`, by design, fails when helmfile-diff outputs were not equivalent.
|
||||||
|
// Stabilized helmfile-diff output rescues that.
|
||||||
func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode, includeTests, suppressSecrets, suppressDiff, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) {
|
func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []string, workerLimit int, detailedExitCode, includeTests, suppressSecrets, suppressDiff, triggerCleanupEvents bool, opt ...DiffOpt) ([]ReleaseSpec, []error) {
|
||||||
opts := &DiffOpts{}
|
opts := &DiffOpts{}
|
||||||
for _, o := range opt {
|
for _, o := range opt {
|
||||||
|
|
@ -1644,6 +1660,7 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
||||||
results := make(chan diffResult, len(preps))
|
results := make(chan diffResult, len(preps))
|
||||||
|
|
||||||
rs := []ReleaseSpec{}
|
rs := []ReleaseSpec{}
|
||||||
|
outputs := map[string]*bytes.Buffer{}
|
||||||
errs := []error{}
|
errs := []error{}
|
||||||
|
|
||||||
// The exit code returned by helm-diff when it detected any changes
|
// The exit code returned by helm-diff when it detected any changes
|
||||||
|
|
@ -1662,19 +1679,20 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
||||||
for prep := range jobQueue {
|
for prep := range jobQueue {
|
||||||
flags := prep.flags
|
flags := prep.flags
|
||||||
release := prep.release
|
release := prep.release
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
if prep.upgradeDueToSkippedDiff {
|
if prep.upgradeDueToSkippedDiff {
|
||||||
results <- diffResult{&ReleaseError{ReleaseSpec: release, err: nil, Code: HelmDiffExitCodeChanged}}
|
results <- diffResult{release, &ReleaseError{ReleaseSpec: release, err: nil, Code: HelmDiffExitCodeChanged}, buf}
|
||||||
} else if err := helm.DiffRelease(st.createHelmContext(release, workerIndex), release.Name, normalizeChart(st.basePath, release.Chart), suppressDiff, flags...); err != nil {
|
} else if err := helm.DiffRelease(st.createHelmContextWithWriter(release, buf), release.Name, normalizeChart(st.basePath, release.Chart), suppressDiff, flags...); err != nil {
|
||||||
switch e := err.(type) {
|
switch e := err.(type) {
|
||||||
case helmexec.ExitError:
|
case helmexec.ExitError:
|
||||||
// Propagate any non-zero exit status from the external command like `helm` that is failed under the hood
|
// Propagate any non-zero exit status from the external command like `helm` that is failed under the hood
|
||||||
results <- diffResult{&ReleaseError{release, err, e.ExitStatus()}}
|
results <- diffResult{release, &ReleaseError{release, err, e.ExitStatus()}, buf}
|
||||||
default:
|
default:
|
||||||
results <- diffResult{&ReleaseError{release, err, 0}}
|
results <- diffResult{release, &ReleaseError{release, err, 0}, buf}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// diff succeeded, found no changes
|
// diff succeeded, found no changes
|
||||||
results <- diffResult{}
|
results <- diffResult{release, nil, buf}
|
||||||
}
|
}
|
||||||
|
|
||||||
if triggerCleanupEvents {
|
if triggerCleanupEvents {
|
||||||
|
|
@ -1693,10 +1711,18 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
||||||
rs = append(rs, *res.err.ReleaseSpec)
|
rs = append(rs, *res.err.ReleaseSpec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputs[ReleaseToID(res.release)] = res.buf
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
for _, p := range preps {
|
||||||
|
if stdout, ok := outputs[ReleaseToID(p.release)]; ok {
|
||||||
|
fmt.Print(stdout.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return rs, errs
|
return rs, errs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue