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
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
|
@ -10,6 +11,7 @@ type HelmContext struct {
|
|||
TillerNamespace string
|
||||
HistoryMax int
|
||||
WorkerIndex int
|
||||
Writer io.Writer
|
||||
}
|
||||
|
||||
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)
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ func (helm *execer) ReleaseStatus(context HelmContext, name string, flags ...str
|
|||
preArgs := context.GetTillerlessArgs(helm)
|
||||
env := context.getTillerlessEnv()
|
||||
out, err := helm.exec(append(append(preArgs, "status", name), flags...), env)
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -219,7 +219,7 @@ func (helm *execer) List(context HelmContext, filter string, flags ...string) (s
|
|||
lines = lines[1:]
|
||||
out = []byte(strings.Join(lines, "\n"))
|
||||
}
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
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{})
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
env := context.getTillerlessEnv()
|
||||
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:
|
||||
if e.ExitStatus() == 2 {
|
||||
if !(suppressDiff) {
|
||||
helm.write(out)
|
||||
helm.write(context.Writer, out)
|
||||
}
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else if !(suppressDiff) {
|
||||
helm.write(out)
|
||||
helm.write(context.Writer, out)
|
||||
}
|
||||
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 {
|
||||
helm.logger.Infof("Linting release=%v, chart=%v", name, chart)
|
||||
out, err := helm.exec(append([]string{"lint", chart}, flags...), map[string]string{})
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -370,7 +374,7 @@ func (helm *execer) DeleteRelease(context HelmContext, name string, flags ...str
|
|||
preArgs := context.GetTillerlessArgs(helm)
|
||||
env := context.getTillerlessEnv()
|
||||
out, err := helm.exec(append(append(preArgs, "delete", name), flags...), env)
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -380,7 +384,7 @@ func (helm *execer) TestRelease(context HelmContext, name string, flags ...strin
|
|||
env := context.getTillerlessEnv()
|
||||
args := []string{"test", name}
|
||||
out, err := helm.exec(append(append(preArgs, args...), flags...), env)
|
||||
helm.write(out)
|
||||
helm.write(nil, out)
|
||||
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 {
|
||||
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 {
|
||||
err *ReleaseError
|
||||
release *ReleaseSpec
|
||||
err *ReleaseError
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
|
||||
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 {
|
||||
Context int
|
||||
NoColor bool
|
||||
|
|
@ -1617,7 +1627,13 @@ func (o *DiffOpts) Apply(opts *DiffOpts) {
|
|||
type DiffOpt interface{ Apply(*DiffOpts) }
|
||||
|
||||
// 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) {
|
||||
opts := &DiffOpts{}
|
||||
for _, o := range opt {
|
||||
|
|
@ -1644,6 +1660,7 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
|||
results := make(chan diffResult, len(preps))
|
||||
|
||||
rs := []ReleaseSpec{}
|
||||
outputs := map[string]*bytes.Buffer{}
|
||||
errs := []error{}
|
||||
|
||||
// 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 {
|
||||
flags := prep.flags
|
||||
release := prep.release
|
||||
buf := &bytes.Buffer{}
|
||||
if prep.upgradeDueToSkippedDiff {
|
||||
results <- diffResult{&ReleaseError{ReleaseSpec: release, err: nil, Code: HelmDiffExitCodeChanged}}
|
||||
} else if err := helm.DiffRelease(st.createHelmContext(release, workerIndex), release.Name, normalizeChart(st.basePath, release.Chart), suppressDiff, flags...); err != nil {
|
||||
results <- diffResult{release, &ReleaseError{ReleaseSpec: release, err: nil, Code: HelmDiffExitCodeChanged}, buf}
|
||||
} else if err := helm.DiffRelease(st.createHelmContextWithWriter(release, buf), release.Name, normalizeChart(st.basePath, release.Chart), suppressDiff, flags...); err != nil {
|
||||
switch e := err.(type) {
|
||||
case helmexec.ExitError:
|
||||
// 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:
|
||||
results <- diffResult{&ReleaseError{release, err, 0}}
|
||||
results <- diffResult{release, &ReleaseError{release, err, 0}, buf}
|
||||
}
|
||||
} else {
|
||||
// diff succeeded, found no changes
|
||||
results <- diffResult{}
|
||||
results <- diffResult{release, nil, buf}
|
||||
}
|
||||
|
||||
if triggerCleanupEvents {
|
||||
|
|
@ -1693,10 +1711,18 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
|
|||
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
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue