feat: `helmfile template --output-dir-template` for customizing output dirs (#1357)
This is useful for e.g. removing state file names and their hash values out of output dirs so that it can be used easily in a gitops setup. For example, `--output-dir-template mybasedir/{{.Release.Name}}` produces `mybasedir/RELEASE/CHART/templates/*.yaml` for each release in your helmfile.yaml.
This commit is contained in:
parent
31d0fce6fd
commit
df6489ab43
8
main.go
8
main.go
|
|
@ -235,6 +235,10 @@ func main() {
|
|||
Name: "output-dir",
|
||||
Usage: "output directory to pass to helm template (helm template --output-dir)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "output-dir-template",
|
||||
Usage: "go text template for generating the output directory. Default: {{ .OutputDir }}/{{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}-{{ .Release.Name}}",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "concurrency",
|
||||
Value: 0,
|
||||
|
|
@ -552,6 +556,10 @@ func (c configImpl) OutputDir() string {
|
|||
return c.c.String("output-dir")
|
||||
}
|
||||
|
||||
func (c configImpl) OutputDirTemplate() string {
|
||||
return c.c.String("output-dir-template")
|
||||
}
|
||||
|
||||
func (c configImpl) Validate() bool {
|
||||
return c.c.Bool("validate")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1282,7 +1282,8 @@ func (a *App) template(r *Run, c TemplateConfigProvider) (bool, []error) {
|
|||
|
||||
args := argparser.GetArgs(c.Args(), st)
|
||||
opts := &state.TemplateOpts{
|
||||
Set: c.Set(),
|
||||
Set: c.Set(),
|
||||
OutputDirTemplate: c.OutputDirTemplate(),
|
||||
}
|
||||
return subst.TemplateReleases(helm, c.OutputDir(), c.Values(), args, c.Concurrency(), c.Validate(), opts)
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -2178,6 +2178,10 @@ func (c configImpl) OutputDir() string {
|
|||
return "output/subdir"
|
||||
}
|
||||
|
||||
func (c configImpl) OutputDirTemplate() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (c configImpl) Concurrency() int {
|
||||
return 1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,7 @@ type TemplateConfigProvider interface {
|
|||
|
||||
Values() []string
|
||||
Set() []string
|
||||
OutputDirTemplate() string
|
||||
Validate() bool
|
||||
SkipDeps() bool
|
||||
OutputDir() string
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha1"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
|
|
@ -16,6 +17,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"text/template"
|
||||
|
||||
"github.com/roboll/helmfile/pkg/environment"
|
||||
"github.com/roboll/helmfile/pkg/event"
|
||||
|
|
@ -828,7 +830,8 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
|||
}
|
||||
|
||||
type TemplateOpts struct {
|
||||
Set []string
|
||||
Set []string
|
||||
OutputDirTemplate string
|
||||
}
|
||||
|
||||
type TemplateOpt interface{ Apply(*TemplateOpts) }
|
||||
|
|
@ -890,8 +893,8 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
|
|||
}
|
||||
}
|
||||
|
||||
if len(outputDir) > 0 {
|
||||
releaseOutputDir, err := st.GenerateOutputDir(outputDir, release)
|
||||
if len(outputDir) > 0 || len(opts.OutputDirTemplate) > 0 {
|
||||
releaseOutputDir, err := st.GenerateOutputDir(outputDir, release, opts.OutputDirTemplate)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
|
|
@ -2193,7 +2196,7 @@ func (hf *SubHelmfileSpec) UnmarshalYAML(unmarshal func(interface{}) error) erro
|
|||
return nil
|
||||
}
|
||||
|
||||
func (st *HelmState) GenerateOutputDir(outputDir string, release *ReleaseSpec) (string, error) {
|
||||
func (st *HelmState) GenerateOutputDir(outputDir string, release *ReleaseSpec, outputDirTemplate string) (string, error) {
|
||||
// get absolute path of state file to generate a hash
|
||||
// use this hash to write helm output in a specific directory by state file and release name
|
||||
// ie. in a directory named stateFileName-stateFileHash-releaseName
|
||||
|
|
@ -2208,14 +2211,53 @@ func (st *HelmState) GenerateOutputDir(outputDir string, release *ReleaseSpec) (
|
|||
var stateFileExtension = filepath.Ext(st.FilePath)
|
||||
var stateFileName = st.FilePath[0 : len(st.FilePath)-len(stateFileExtension)]
|
||||
|
||||
sha1sum := hex.EncodeToString(hasher.Sum(nil))[:8]
|
||||
|
||||
var sb strings.Builder
|
||||
sb.WriteString(stateFileName)
|
||||
sb.WriteString("-")
|
||||
sb.WriteString(hex.EncodeToString(hasher.Sum(nil))[:8])
|
||||
sb.WriteString(sha1sum)
|
||||
sb.WriteString("-")
|
||||
sb.WriteString(release.Name)
|
||||
|
||||
return path.Join(outputDir, sb.String()), nil
|
||||
if outputDirTemplate == "" {
|
||||
outputDirTemplate = filepath.Join("{{ .OutputDir }}", "{{ .State.BaseName }}-{{ .State.AbsPathSHA1 }}-{{ .Release.Name}}")
|
||||
}
|
||||
|
||||
t, err := template.New("output-dir").Parse(outputDirTemplate)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("parsing output-dir templmate")
|
||||
}
|
||||
|
||||
buf := &bytes.Buffer{}
|
||||
|
||||
type state struct {
|
||||
BaseName string
|
||||
Path string
|
||||
AbsPath string
|
||||
AbsPathSHA1 string
|
||||
}
|
||||
|
||||
data := struct {
|
||||
OutputDir string
|
||||
State state
|
||||
Release *ReleaseSpec
|
||||
}{
|
||||
OutputDir: outputDir,
|
||||
State: state{
|
||||
BaseName: stateFileName,
|
||||
Path: st.FilePath,
|
||||
AbsPath: stateAbsPath,
|
||||
AbsPathSHA1: sha1sum,
|
||||
},
|
||||
Release: release,
|
||||
}
|
||||
|
||||
if err := t.Execute(buf, data); err != nil {
|
||||
return "", fmt.Errorf("executing output-dir template: %w", err)
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func (st *HelmState) ToYaml() (string, error) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue