feat: `helmfile build --embed-values` to embed release values and secrets into the output (#1436)
This commit is contained in:
parent
5ca7ce15bc
commit
0fc0869671
11
main.go
11
main.go
|
|
@ -467,7 +467,12 @@ func main() {
|
||||||
{
|
{
|
||||||
Name: "build",
|
Name: "build",
|
||||||
Usage: "output compiled helmfile state(s) as YAML",
|
Usage: "output compiled helmfile state(s) as YAML",
|
||||||
Flags: []cli.Flag{},
|
Flags: []cli.Flag{
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "embed-values",
|
||||||
|
Usage: "Read all the values files for every release and embed into the output helmfile.yaml",
|
||||||
|
},
|
||||||
|
},
|
||||||
Action: action(func(run *app.App, c configImpl) error {
|
Action: action(func(run *app.App, c configImpl) error {
|
||||||
return run.PrintState(c)
|
return run.PrintState(c)
|
||||||
}),
|
}),
|
||||||
|
|
@ -674,6 +679,10 @@ func (c configImpl) Context() int {
|
||||||
return c.c.Int("context")
|
return c.c.Int("context")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c configImpl) EmbedValues() bool {
|
||||||
|
return c.c.Bool("embed-values")
|
||||||
|
}
|
||||||
|
|
||||||
func (c configImpl) Logger() *zap.SugaredLogger {
|
func (c configImpl) Logger() *zap.SugaredLogger {
|
||||||
return c.c.App.Metadata["logger"].(*zap.SugaredLogger)
|
return c.c.App.Metadata["logger"].(*zap.SugaredLogger)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -387,12 +387,35 @@ func (a *App) PrintState(c StateConfigProvider) error {
|
||||||
err := run.withPreparedCharts("build", state.ChartPrepareOptions{
|
err := run.withPreparedCharts("build", state.ChartPrepareOptions{
|
||||||
SkipRepos: true,
|
SkipRepos: true,
|
||||||
}, func() {
|
}, func() {
|
||||||
state, err := run.state.ToYaml()
|
if c.EmbedValues() {
|
||||||
|
for i := range run.state.Releases {
|
||||||
|
r := run.state.Releases[i]
|
||||||
|
|
||||||
|
values, err := run.state.LoadYAMLForEmbedding(r.Values, r.MissingFileHandler, r.ValuesPathPrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = []error{err}
|
errs = []error{err}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Printf("---\n# Source: %s\n\n%+v", run.state.FilePath, state)
|
|
||||||
|
run.state.Releases[i].Values = values
|
||||||
|
|
||||||
|
secrets, err := run.state.LoadYAMLForEmbedding(r.Secrets, r.MissingFileHandler, r.ValuesPathPrefix)
|
||||||
|
if err != nil {
|
||||||
|
errs = []error{err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
run.state.Releases[i].Secrets = secrets
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stateYaml, err := run.state.ToYaml()
|
||||||
|
if err != nil {
|
||||||
|
errs = []error{err}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("---\n# Source: %s\n\n%+v", run.state.FilePath, stateYaml)
|
||||||
|
|
||||||
errs = []error{}
|
errs = []error{}
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -2272,6 +2272,10 @@ func (c configImpl) Concurrency() int {
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c configImpl) EmbedValues() bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func (c configImpl) Output() string {
|
func (c configImpl) Output() string {
|
||||||
return c.output
|
return c.output
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,6 +144,7 @@ type StatusesConfigProvider interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
type StateConfigProvider interface {
|
type StateConfigProvider interface {
|
||||||
|
EmbedValues() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type concurrencyConfig interface {
|
type concurrencyConfig interface {
|
||||||
|
|
|
||||||
|
|
@ -133,13 +133,16 @@ func (r ReleaseSpec) ExecuteTemplateExpressions(renderer *tmpl.FileRenderer) (*R
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, ts := range result.Secrets {
|
for i, t := range result.Secrets {
|
||||||
|
switch ts := t.(type) {
|
||||||
|
case string:
|
||||||
s, err := renderer.RenderTemplateContentToBuffer([]byte(ts))
|
s, err := renderer.RenderTemplateContentToBuffer([]byte(ts))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed executing template expressions in release \"%s\".secrets[%d] = \"%s\": %v", r.Name, i, ts, err)
|
return nil, fmt.Errorf("failed executing template expressions in release \"%s\".secrets[%d] = \"%s\": %v", r.Name, i, ts, err)
|
||||||
}
|
}
|
||||||
result.Secrets[i] = s.String()
|
result.Secrets[i] = s.String()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if len(result.SetValuesTemplate) > 0 {
|
if len(result.SetValuesTemplate) > 0 {
|
||||||
for i, val := range result.SetValuesTemplate {
|
for i, val := range result.SetValuesTemplate {
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ type ReleaseSpec struct {
|
||||||
Namespace string `yaml:"namespace,omitempty"`
|
Namespace string `yaml:"namespace,omitempty"`
|
||||||
Labels map[string]string `yaml:"labels,omitempty"`
|
Labels map[string]string `yaml:"labels,omitempty"`
|
||||||
Values []interface{} `yaml:"values,omitempty"`
|
Values []interface{} `yaml:"values,omitempty"`
|
||||||
Secrets []string `yaml:"secrets,omitempty"`
|
Secrets []interface{} `yaml:"secrets,omitempty"`
|
||||||
SetValues []SetValue `yaml:"set,omitempty"`
|
SetValues []SetValue `yaml:"set,omitempty"`
|
||||||
|
|
||||||
ValuesTemplate []interface{} `yaml:"valuesTemplate,omitempty"`
|
ValuesTemplate []interface{} `yaml:"valuesTemplate,omitempty"`
|
||||||
|
|
@ -2262,11 +2262,41 @@ func (st *HelmState) generateVanillaValuesFiles(release *ReleaseSpec) ([]string,
|
||||||
func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
|
func (st *HelmState) generateSecretValuesFiles(helm helmexec.Interface, release *ReleaseSpec, workerIndex int) ([]string, error) {
|
||||||
var generatedFiles []string
|
var generatedFiles []string
|
||||||
|
|
||||||
for _, value := range release.Secrets {
|
for _, v := range release.Secrets {
|
||||||
paths, skip, err := st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value)
|
var (
|
||||||
|
paths []string
|
||||||
|
skip bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
switch value := v.(type) {
|
||||||
|
case string:
|
||||||
|
paths, skip, err = st.storage().resolveFile(release.MissingFileHandler, "secrets", release.ValuesPathPrefix+value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
bs, err := yaml.Marshal(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
path, err := ioutil.TempFile(os.TempDir(), "helmfile-embdedded-secrets-*.yaml.enc")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
_ = path.Close()
|
||||||
|
defer func() {
|
||||||
|
_ = os.Remove(path.Name())
|
||||||
|
}()
|
||||||
|
|
||||||
|
if err := ioutil.WriteFile(path.Name(), bs, 0644); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
paths = []string{path.Name()}
|
||||||
|
}
|
||||||
|
|
||||||
if skip {
|
if skip {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -2546,3 +2576,42 @@ func (st *HelmState) ToYaml() (string, error) {
|
||||||
return string(result), nil
|
return string(result), nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) LoadYAMLForEmbedding(entries []interface{}, missingFileHandler *string, pathPrefix string) ([]interface{}, error) {
|
||||||
|
var result []interface{}
|
||||||
|
|
||||||
|
for _, v := range entries {
|
||||||
|
switch t := v.(type) {
|
||||||
|
case string:
|
||||||
|
var values map[string]interface{}
|
||||||
|
|
||||||
|
paths, skip, err := st.storage().resolveFile(missingFileHandler, "values", pathPrefix+t)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if skip {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(paths) > 1 {
|
||||||
|
return nil, fmt.Errorf("glob patterns in release values and secrets is not supported yet. please submit a feature request if necessary")
|
||||||
|
}
|
||||||
|
yamlOrTemplatePath := paths[0]
|
||||||
|
|
||||||
|
yamlBytes, err := st.RenderValuesFileToBytes(yamlOrTemplatePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to render values files \"%s\": %v", t, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := yaml.Unmarshal(yamlBytes, &values); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, values)
|
||||||
|
default:
|
||||||
|
result = append(result, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ func TestHelmState_executeTemplates(t *testing.T) {
|
||||||
Name: "test-app",
|
Name: "test-app",
|
||||||
Namespace: "test-namespace-{{ .Release.Name }}",
|
Namespace: "test-namespace-{{ .Release.Name }}",
|
||||||
ValuesTemplate: []interface{}{"config/{{ .Environment.Name }}/{{ .Release.Name }}/values.yaml"},
|
ValuesTemplate: []interface{}{"config/{{ .Environment.Name }}/{{ .Release.Name }}/values.yaml"},
|
||||||
Secrets: []string{"config/{{ .Environment.Name }}/{{ .Release.Name }}/secrets.yaml"},
|
Secrets: []interface{}{"config/{{ .Environment.Name }}/{{ .Release.Name }}/secrets.yaml"},
|
||||||
Labels: map[string]string{"id": "{{ .Release.Name }}"},
|
Labels: map[string]string{"id": "{{ .Release.Name }}"},
|
||||||
},
|
},
|
||||||
want: ReleaseSpec{
|
want: ReleaseSpec{
|
||||||
|
|
@ -45,7 +45,7 @@ func TestHelmState_executeTemplates(t *testing.T) {
|
||||||
Name: "test-app",
|
Name: "test-app",
|
||||||
Namespace: "test-namespace-test-app",
|
Namespace: "test-namespace-test-app",
|
||||||
Values: []interface{}{"config/test_env/test-app/values.yaml"},
|
Values: []interface{}{"config/test_env/test-app/values.yaml"},
|
||||||
Secrets: []string{"config/test_env/test-app/secrets.yaml"},
|
Secrets: []interface{}{"config/test_env/test-app/secrets.yaml"},
|
||||||
Labels: map[string]string{"id": "test-app"},
|
Labels: map[string]string{"id": "test-app"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue