feat: `helmfile template --skip-cleanup` (#1570)

* feat: `helmfile template --skip-cleanup`

Resolves #1517
This commit is contained in:
Yusuke Kuoka 2020-11-06 09:23:49 +09:00 committed by GitHub
parent 5a15b65b94
commit 88884b68dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 64 additions and 10 deletions

View File

@ -256,6 +256,10 @@ func main() {
Name: "skip-deps", Name: "skip-deps",
Usage: "skip running `helm repo update` and `helm dependency build`", Usage: "skip running `helm repo update` and `helm dependency build`",
}, },
cli.BoolFlag{
Name: "skip-cleanup",
Usage: "Stop cleaning up temporary values generated by helmfile and helm-secrets. Useful for debugging. Don't use in production for security",
},
}, },
Action: action(func(run *app.App, c configImpl) error { Action: action(func(run *app.App, c configImpl) error {
return run.Template(c) return run.Template(c)

View File

@ -218,6 +218,9 @@ func (a *App) Diff(c DiffConfigProvider) error {
} }
func (a *App) Template(c TemplateConfigProvider) error { func (a *App) Template(c TemplateConfigProvider) error {
opts := []LoadOption{SetRetainValuesFiles(c.SkipCleanup())}
return a.ForEachState(func(run *Run) (ok bool, errs []error) { return a.ForEachState(func(run *Run) (ok bool, errs []error) {
// `helm template` in helm v2 does not support local chart. // `helm template` in helm v2 does not support local chart.
// So, we set forceDownload=true for helm v2 only // So, we set forceDownload=true for helm v2 only
@ -233,7 +236,7 @@ func (a *App) Template(c TemplateConfigProvider) error {
} }
return return
}) }, opts...)
} }
func (a *App) WriteValues(c WriteValuesConfigProvider) error { func (a *App) WriteValues(c WriteValuesConfigProvider) error {
@ -1437,6 +1440,7 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
Set: c.Set(), Set: c.Set(),
IncludeCRDs: c.IncludeCRDs(), IncludeCRDs: c.IncludeCRDs(),
OutputDirTemplate: c.OutputDirTemplate(), OutputDirTemplate: c.OutputDirTemplate(),
SkipCleanup: c.SkipCleanup(),
} }
return subst.TemplateReleases(helm, c.OutputDir(), c.Values(), args, c.Concurrency(), c.Validate(), opts) return subst.TemplateReleases(helm, c.OutputDir(), c.Values(), args, c.Concurrency(), c.Validate(), opts)
})) }))

View File

@ -2241,6 +2241,7 @@ type configImpl struct {
set []string set []string
output string output string
includeCRDs bool includeCRDs bool
skipCleanup bool
skipDeps bool skipDeps bool
} }
@ -2264,6 +2265,10 @@ func (c configImpl) Validate() bool {
return true return true
} }
func (c configImpl) SkipCleanup() bool {
return c.skipCleanup
}
func (c configImpl) SkipDeps() bool { func (c configImpl) SkipDeps() bool {
return c.skipDeps return c.skipDeps
} }

View File

@ -134,6 +134,7 @@ type TemplateConfigProvider interface {
OutputDirTemplate() string OutputDirTemplate() string
Validate() bool Validate() bool
SkipDeps() bool SkipDeps() bool
SkipCleanup() bool
OutputDir() string OutputDir() string
IncludeCRDs() bool IncludeCRDs() bool

View File

@ -29,6 +29,7 @@ type execer struct {
extra []string extra []string
decryptedSecretMutex sync.Mutex decryptedSecretMutex sync.Mutex
decryptedSecrets map[string]*decryptedSecret decryptedSecrets map[string]*decryptedSecret
writeTempFile func([]byte) (string, error)
} }
func NewLogger(writer io.Writer, logLevel string) *zap.SugaredLogger { func NewLogger(writer io.Writer, logLevel string) *zap.SugaredLogger {
@ -278,16 +279,32 @@ func (helm *execer) DecryptSecret(context HelmContext, name string, flags ...str
defer secret.mutex.RUnlock() defer secret.mutex.RUnlock()
} }
tempFile := helm.writeTempFile
if tempFile == nil {
tempFile = func(content []byte) (string, error) {
tmpFile, err := ioutil.TempFile("", "secret") tmpFile, err := ioutil.TempFile("", "secret")
if err != nil { if err != nil {
return "", err return "", err
} }
_, err = tmpFile.Write(secret.bytes)
_, err = tmpFile.Write(content)
if err != nil { if err != nil {
return "", err return "", err
} }
return tmpFile.Name(), err return tmpFile.Name(), nil
}
}
tmpFileName, err := tempFile(secret.bytes)
if err != nil {
return "", err
}
helm.logger.Debugf("Decrypted %s into %s", absPath, tmpFileName)
return tmpFileName, err
} }
func (helm *execer) TemplateRelease(name string, chart string, flags ...string) error { func (helm *execer) TemplateRelease(name string, chart string, flags ...string) error {

View File

@ -3,6 +3,7 @@ package helmexec
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/google/go-cmp/cmp"
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
@ -257,6 +258,12 @@ func Test_DecryptSecret(t *testing.T) {
var buffer bytes.Buffer var buffer bytes.Buffer
logger := NewLogger(&buffer, "debug") logger := NewLogger(&buffer, "debug")
helm := MockExecer(logger, "dev") helm := MockExecer(logger, "dev")
tmpFilePath := "path/to/temp/file"
helm.writeTempFile = func(content []byte) (string, error) {
return tmpFilePath, nil
}
helm.DecryptSecret(HelmContext{}, "secretName") helm.DecryptSecret(HelmContext{}, "secretName")
cwd, err := filepath.Abs(".") cwd, err := filepath.Abs(".")
if err != nil { if err != nil {
@ -270,9 +277,10 @@ Decrypting secret %s/secretName
exec: helm --kube-context dev secrets dec %s/secretName exec: helm --kube-context dev secrets dec %s/secretName
Preparing to decrypt secret %s/secretName Preparing to decrypt secret %s/secretName
Found secret in cache %s/secretName Found secret in cache %s/secretName
`, cwd, cwd, cwd, cwd, cwd) Decrypted %s/secretName into path/to/temp/file
if buffer.String() != expected { `, cwd, cwd, cwd, cwd, cwd, cwd)
t.Errorf("helmexec.DecryptSecret()\nactual = %v\nexpect = %v", buffer.String(), expected) if d := cmp.Diff(expected, buffer.String()); d != "" {
t.Errorf("helmexec.DecryptSecret(): want (-), got (+):\n%s", d)
} }
} }

View File

@ -670,6 +670,10 @@ func (st *HelmState) SyncReleases(affectedReleases *AffectedReleases, helm helme
preps, prepErrs := st.prepareSyncReleases(helm, additionalValues, workerLimit, opts) preps, prepErrs := st.prepareSyncReleases(helm, additionalValues, workerLimit, opts)
defer func() { defer func() {
if opts.SkipCleanup {
return
}
for _, p := range preps { for _, p := range preps {
st.removeFiles(p.files) st.removeFiles(p.files)
} }
@ -1125,6 +1129,7 @@ func (st *HelmState) runHelmDepBuilds(helm helmexec.Interface, concurrency int,
type TemplateOpts struct { type TemplateOpts struct {
Set []string Set []string
SkipCleanup bool
OutputDirTemplate string OutputDirTemplate string
IncludeCRDs bool IncludeCRDs bool
} }
@ -1136,7 +1141,9 @@ func (o *TemplateOpts) Apply(opts *TemplateOpts) {
} }
// TemplateReleases wrapper for executing helm template on the releases // TemplateReleases wrapper for executing helm template on the releases
func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string, additionalValues []string, args []string, workerLimit int, validate bool, opt ...TemplateOpt) []error { func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string, additionalValues []string, args []string, workerLimit int,
validate bool, opt ...TemplateOpt) []error {
opts := &TemplateOpts{} opts := &TemplateOpts{}
for _, o := range opt { for _, o := range opt {
o.Apply(opts) o.Apply(opts)
@ -1156,6 +1163,10 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
flags, files, err := st.flagsForTemplate(helm, release, 0) flags, files, err := st.flagsForTemplate(helm, release, 0)
defer func() { defer func() {
if opts.SkipCleanup {
return
}
st.removeFiles(files) st.removeFiles(files)
}() }()
@ -1571,6 +1582,10 @@ func (st *HelmState) DiffReleases(helm helmexec.Interface, additionalValues []st
preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, includeTests, suppressSecrets, opts) preps, prepErrs := st.prepareDiffReleases(helm, additionalValues, workerLimit, detailedExitCode, includeTests, suppressSecrets, opts)
defer func() { defer func() {
if opts.SkipCleanup {
return
}
for _, p := range preps { for _, p := range preps {
st.removeFiles(p.files) st.removeFiles(p.files)
} }