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",
|
Name: "output-dir",
|
||||||
Usage: "output directory to pass to helm template (helm template --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{
|
cli.IntFlag{
|
||||||
Name: "concurrency",
|
Name: "concurrency",
|
||||||
Value: 0,
|
Value: 0,
|
||||||
|
|
@ -552,6 +556,10 @@ func (c configImpl) OutputDir() string {
|
||||||
return c.c.String("output-dir")
|
return c.c.String("output-dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c configImpl) OutputDirTemplate() string {
|
||||||
|
return c.c.String("output-dir-template")
|
||||||
|
}
|
||||||
|
|
||||||
func (c configImpl) Validate() bool {
|
func (c configImpl) Validate() bool {
|
||||||
return c.c.Bool("validate")
|
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)
|
args := argparser.GetArgs(c.Args(), st)
|
||||||
opts := &state.TemplateOpts{
|
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)
|
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"
|
return "output/subdir"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c configImpl) OutputDirTemplate() string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func (c configImpl) Concurrency() int {
|
func (c configImpl) Concurrency() int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,6 +129,7 @@ type TemplateConfigProvider interface {
|
||||||
|
|
||||||
Values() []string
|
Values() []string
|
||||||
Set() []string
|
Set() []string
|
||||||
|
OutputDirTemplate() string
|
||||||
Validate() bool
|
Validate() bool
|
||||||
SkipDeps() bool
|
SkipDeps() bool
|
||||||
OutputDir() string
|
OutputDir() string
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
package state
|
package state
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
|
@ -16,6 +17,7 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"text/template"
|
||||||
|
|
||||||
"github.com/roboll/helmfile/pkg/environment"
|
"github.com/roboll/helmfile/pkg/environment"
|
||||||
"github.com/roboll/helmfile/pkg/event"
|
"github.com/roboll/helmfile/pkg/event"
|
||||||
|
|
@ -828,7 +830,8 @@ func (st *HelmState) PrepareCharts(helm helmexec.Interface, dir string, concurre
|
||||||
}
|
}
|
||||||
|
|
||||||
type TemplateOpts struct {
|
type TemplateOpts struct {
|
||||||
Set []string
|
Set []string
|
||||||
|
OutputDirTemplate string
|
||||||
}
|
}
|
||||||
|
|
||||||
type TemplateOpt interface{ Apply(*TemplateOpts) }
|
type TemplateOpt interface{ Apply(*TemplateOpts) }
|
||||||
|
|
@ -890,8 +893,8 @@ func (st *HelmState) TemplateReleases(helm helmexec.Interface, outputDir string,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(outputDir) > 0 {
|
if len(outputDir) > 0 || len(opts.OutputDirTemplate) > 0 {
|
||||||
releaseOutputDir, err := st.GenerateOutputDir(outputDir, release)
|
releaseOutputDir, err := st.GenerateOutputDir(outputDir, release, opts.OutputDirTemplate)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = append(errs, err)
|
errs = append(errs, err)
|
||||||
}
|
}
|
||||||
|
|
@ -2193,7 +2196,7 @@ func (hf *SubHelmfileSpec) UnmarshalYAML(unmarshal func(interface{}) error) erro
|
||||||
return nil
|
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
|
// 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
|
// 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
|
// 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 stateFileExtension = filepath.Ext(st.FilePath)
|
||||||
var stateFileName = st.FilePath[0 : len(st.FilePath)-len(stateFileExtension)]
|
var stateFileName = st.FilePath[0 : len(st.FilePath)-len(stateFileExtension)]
|
||||||
|
|
||||||
|
sha1sum := hex.EncodeToString(hasher.Sum(nil))[:8]
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
sb.WriteString(stateFileName)
|
sb.WriteString(stateFileName)
|
||||||
sb.WriteString("-")
|
sb.WriteString("-")
|
||||||
sb.WriteString(hex.EncodeToString(hasher.Sum(nil))[:8])
|
sb.WriteString(sha1sum)
|
||||||
sb.WriteString("-")
|
sb.WriteString("-")
|
||||||
sb.WriteString(release.Name)
|
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) {
|
func (st *HelmState) ToYaml() (string, error) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue