feat: `helmfile template --skip-cleanup` (#1570)
* feat: `helmfile template --skip-cleanup` Resolves #1517
This commit is contained in:
		
							parent
							
								
									5a15b65b94
								
							
						
					
					
						commit
						88884b68dc
					
				
							
								
								
									
										4
									
								
								main.go
								
								
								
								
							
							
						
						
									
										4
									
								
								main.go
								
								
								
								
							|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 		})) | 		})) | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	tmpFile, err := ioutil.TempFile("", "secret") | 	tempFile := helm.writeTempFile | ||||||
| 	if err != nil { | 
 | ||||||
| 		return "", err | 	if tempFile == nil { | ||||||
|  | 		tempFile = func(content []byte) (string, error) { | ||||||
|  | 			tmpFile, err := ioutil.TempFile("", "secret") | ||||||
|  | 			if err != nil { | ||||||
|  | 				return "", err | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			_, err = tmpFile.Write(content) | ||||||
|  | 			if err != nil { | ||||||
|  | 				return "", err | ||||||
|  | 			} | ||||||
|  | 
 | ||||||
|  | 			return tmpFile.Name(), nil | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 	_, err = tmpFile.Write(secret.bytes) | 
 | ||||||
|  | 	tmpFileName, err := tempFile(secret.bytes) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", err | 		return "", err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	return tmpFile.Name(), 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 { | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue