Add global hooks (#1301)
Changes: * Add global hooks * Add top level hooks field to yaml spec * Add functions for global prepare and cleanup events * Call global prepare and cleanup events in withPreparedCharts function * Update README * Add helmfileCommand variable to withPreparedCharts Pass the information on what helmfileCommand has been run down from the top level functions through withReposAndPreparedCharts and withPreparedCharts.
This commit is contained in:
parent
0f86cc9b87
commit
f16d96bc8f
13
README.md
13
README.md
|
|
@ -983,6 +983,19 @@ Now, replace `echo` with any command you like, and rewrite `args` that actually
|
||||||
For templating, imagine that you created a hook that generates a helm chart on-the-fly by running an external tool like ksonnet, kustomize, or your own template engine.
|
For templating, imagine that you created a hook that generates a helm chart on-the-fly by running an external tool like ksonnet, kustomize, or your own template engine.
|
||||||
It will allow you to write your helm releases with any language you like, while still leveraging goodies provided by helm.
|
It will allow you to write your helm releases with any language you like, while still leveraging goodies provided by helm.
|
||||||
|
|
||||||
|
### Global Hooks
|
||||||
|
In contrast to the per release hooks mentioned above these are run only once at the very beginning and end of the execution of a helmfile command and only the `prepare` and `cleanup` hooks are available respectively.
|
||||||
|
|
||||||
|
They use the same syntax as per release hooks, but at the top level of your helmfile:
|
||||||
|
``` yaml
|
||||||
|
hooks:
|
||||||
|
- events: ["prepare", "cleanup"]
|
||||||
|
showlogs: true
|
||||||
|
command: "echo"
|
||||||
|
args: ["{{`{{.Environment.Name}}`}}", "{{`{{.Release.Name}}`}}", "{{`{{.HelmfileCommand}}`}}\
|
||||||
|
"]
|
||||||
|
```
|
||||||
|
|
||||||
### Helmfile + Kustomize
|
### Helmfile + Kustomize
|
||||||
|
|
||||||
Do you prefer `kustomize` to write and organize your Kubernetes apps, but still want to leverage helm's useful features
|
Do you prefer `kustomize` to write and organize your Kubernetes apps, but still want to leverage helm's useful features
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ func Init(app *App) *App {
|
||||||
|
|
||||||
func (a *App) Deps(c DepsConfigProvider) error {
|
func (a *App) Deps(c DepsConfigProvider) error {
|
||||||
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
||||||
prepErrs := run.withReposAndPreparedCharts(false, c.SkipRepos(), func() {
|
prepErrs := run.withReposAndPreparedCharts(false, c.SkipRepos(), "deps", func() {
|
||||||
errs = run.Deps(c)
|
errs = run.Deps(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -113,7 +113,7 @@ func (a *App) Deps(c DepsConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Repos(c ReposConfigProvider) error {
|
func (a *App) Repos(c ReposConfigProvider) error {
|
||||||
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "repos", func() {
|
||||||
errs = run.Repos(c)
|
errs = run.Repos(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -127,7 +127,7 @@ func (a *App) Repos(c ReposConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
|
func (a *App) DeprecatedSyncCharts(c DeprecatedChartsConfigProvider) error {
|
||||||
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "charts", func() {
|
||||||
errs = run.DeprecatedSyncCharts(c)
|
errs = run.DeprecatedSyncCharts(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -153,7 +153,7 @@ func (a *App) Diff(c DiffConfigProvider) error {
|
||||||
|
|
||||||
var errs []error
|
var errs []error
|
||||||
|
|
||||||
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), func() {
|
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), "diff", func() {
|
||||||
msg, matched, affected, errs = run.Diff(c)
|
msg, matched, affected, errs = run.Diff(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -204,7 +204,7 @@ func (a *App) Diff(c DiffConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Template(c TemplateConfigProvider) error {
|
func (a *App) Template(c TemplateConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||||
prepErrs := run.withReposAndPreparedCharts(true, c.SkipDeps(), func() {
|
prepErrs := run.withReposAndPreparedCharts(true, c.SkipDeps(), "template", func() {
|
||||||
ok, errs = a.template(run, c)
|
ok, errs = a.template(run, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -216,7 +216,7 @@ func (a *App) Template(c TemplateConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Lint(c LintConfigProvider) error {
|
func (a *App) Lint(c LintConfigProvider) error {
|
||||||
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
||||||
prepErrs := run.withReposAndPreparedCharts(true, c.SkipDeps(), func() {
|
prepErrs := run.withReposAndPreparedCharts(true, c.SkipDeps(), "lint", func() {
|
||||||
errs = run.Lint(c)
|
errs = run.Lint(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -228,7 +228,7 @@ func (a *App) Lint(c LintConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Sync(c SyncConfigProvider) error {
|
func (a *App) Sync(c SyncConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||||
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), func() {
|
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), "sync", func() {
|
||||||
ok, errs = a.sync(run, c)
|
ok, errs = a.sync(run, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -248,7 +248,7 @@ func (a *App) Apply(c ApplyConfigProvider) error {
|
||||||
opts = append(opts, SetRetainValuesFiles(c.RetainValuesFiles()))
|
opts = append(opts, SetRetainValuesFiles(c.RetainValuesFiles()))
|
||||||
|
|
||||||
err := a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
err := a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||||
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), func() {
|
prepErrs := run.withReposAndPreparedCharts(false, c.SkipDeps(), "apply", func() {
|
||||||
matched, updated, es := a.apply(run, c)
|
matched, updated, es := a.apply(run, c)
|
||||||
|
|
||||||
mut.Lock()
|
mut.Lock()
|
||||||
|
|
@ -278,7 +278,7 @@ func (a *App) Apply(c ApplyConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Status(c StatusesConfigProvider) error {
|
func (a *App) Status(c StatusesConfigProvider) error {
|
||||||
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
return a.ForEachStateFiltered(func(run *Run) (errs []error) {
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "status", func() {
|
||||||
errs = run.Status(c)
|
errs = run.Status(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -292,7 +292,7 @@ func (a *App) Status(c StatusesConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Delete(c DeleteConfigProvider) error {
|
func (a *App) Delete(c DeleteConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "delete", func() {
|
||||||
ok, errs = a.delete(run, c.Purge(), c)
|
ok, errs = a.delete(run, c.Purge(), c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -306,7 +306,7 @@ func (a *App) Delete(c DeleteConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) Destroy(c DestroyConfigProvider) error {
|
func (a *App) Destroy(c DestroyConfigProvider) error {
|
||||||
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
return a.ForEachState(func(run *Run) (ok bool, errs []error) {
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "destroy", func() {
|
||||||
ok, errs = a.delete(run, true, c)
|
ok, errs = a.delete(run, true, c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -326,7 +326,7 @@ func (a *App) Test(c TestConfigProvider) error {
|
||||||
"or set helm.sh/hook-delete-policy\n")
|
"or set helm.sh/hook-delete-policy\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
err := run.withPreparedCharts(false, func() {
|
err := run.withPreparedCharts(false, "test", func() {
|
||||||
errs = run.Test(c)
|
errs = run.Test(c)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
@ -340,7 +340,7 @@ func (a *App) Test(c TestConfigProvider) error {
|
||||||
|
|
||||||
func (a *App) PrintState(c StateConfigProvider) error {
|
func (a *App) PrintState(c StateConfigProvider) error {
|
||||||
return a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) (errs []error) {
|
return a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) (errs []error) {
|
||||||
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, func() {
|
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, "build", func() {
|
||||||
state, err := st.ToYaml()
|
state, err := st.ToYaml()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errs = []error{err}
|
errs = []error{err}
|
||||||
|
|
@ -363,7 +363,7 @@ func (a *App) ListReleases(c ListConfigProvider) error {
|
||||||
var releases []*HelmRelease
|
var releases []*HelmRelease
|
||||||
|
|
||||||
err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) []error {
|
err := a.VisitDesiredStatesWithReleasesFiltered(a.FileOrDir, func(st *state.HelmState) []error {
|
||||||
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, func() {
|
err := NewRun(st, nil, NewContext()).withPreparedCharts(false, "list", func() {
|
||||||
|
|
||||||
//var releases m
|
//var releases m
|
||||||
for _, r := range st.Releases {
|
for _, r := range st.Releases {
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ func (r *Run) askForConfirmation(msg string) bool {
|
||||||
return AskForConfirmation(msg)
|
return AskForConfirmation(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Run) withReposAndPreparedCharts(forceDownload bool, skipRepos bool, f func()) []error {
|
func (r *Run) withReposAndPreparedCharts(forceDownload bool, skipRepos bool, helmfileCommand string, f func()) []error {
|
||||||
if !skipRepos {
|
if !skipRepos {
|
||||||
ctx := r.ctx
|
ctx := r.ctx
|
||||||
if errs := ctx.SyncReposOnce(r.state, r.helm); errs != nil && len(errs) > 0 {
|
if errs := ctx.SyncReposOnce(r.state, r.helm); errs != nil && len(errs) > 0 {
|
||||||
|
|
@ -40,14 +40,14 @@ func (r *Run) withReposAndPreparedCharts(forceDownload bool, skipRepos bool, f f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := r.withPreparedCharts(forceDownload, f); err != nil {
|
if err := r.withPreparedCharts(forceDownload, helmfileCommand, f); err != nil {
|
||||||
return []error{err}
|
return []error{err}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Run) withPreparedCharts(forceDownload bool, f func()) error {
|
func (r *Run) withPreparedCharts(forceDownload bool, helmfileCommand string, f func()) error {
|
||||||
if r.ReleaseToChart != nil {
|
if r.ReleaseToChart != nil {
|
||||||
panic("Run.PrepareCharts can be called only once")
|
panic("Run.PrepareCharts can be called only once")
|
||||||
}
|
}
|
||||||
|
|
@ -59,7 +59,11 @@ func (r *Run) withPreparedCharts(forceDownload bool, f func()) error {
|
||||||
}
|
}
|
||||||
defer os.RemoveAll(dir)
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
releaseToChart, errs := state.PrepareCharts(r.helm, r.state, dir, 2, "template", forceDownload)
|
if _, err = r.state.TriggerGlobalPrepareEvent(helmfileCommand); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseToChart, errs := state.PrepareCharts(r.helm, r.state, dir, 2, helmfileCommand, forceDownload)
|
||||||
|
|
||||||
if len(errs) > 0 {
|
if len(errs) > 0 {
|
||||||
return fmt.Errorf("%v", errs)
|
return fmt.Errorf("%v", errs)
|
||||||
|
|
@ -75,7 +79,9 @@ func (r *Run) withPreparedCharts(forceDownload bool, f func()) error {
|
||||||
|
|
||||||
f()
|
f()
|
||||||
|
|
||||||
return nil
|
_, err = r.state.TriggerGlobalCleanupEvent(helmfileCommand)
|
||||||
|
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Run) Deps(c DepsConfigProvider) []error {
|
func (r *Run) Deps(c DepsConfigProvider) []error {
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,9 @@ type HelmState struct {
|
||||||
Selectors []string `yaml:"-"`
|
Selectors []string `yaml:"-"`
|
||||||
ApiVersions []string `yaml:"apiVersions,omitempty"`
|
ApiVersions []string `yaml:"apiVersions,omitempty"`
|
||||||
|
|
||||||
|
// Hooks is a list of extension points paired with operations, that are executed in specific points of the lifecycle of releases defined in helmfile
|
||||||
|
Hooks []event.Hook `yaml:"hooks,omitempty"`
|
||||||
|
|
||||||
Templates map[string]TemplateSpec `yaml:"templates"`
|
Templates map[string]TemplateSpec `yaml:"templates"`
|
||||||
|
|
||||||
Env environment.Environment `yaml:"-"`
|
Env environment.Environment `yaml:"-"`
|
||||||
|
|
@ -1441,6 +1444,30 @@ func (st *HelmState) PrepareReleases(helm helmexec.Interface, helmfileCommand st
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) TriggerGlobalPrepareEvent(helmfileCommand string) (bool, error) {
|
||||||
|
return st.triggerGlobalReleaseEvent("prepare", nil, helmfileCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) TriggerGlobalCleanupEvent(helmfileCommand string) (bool, error) {
|
||||||
|
return st.triggerGlobalReleaseEvent("cleanup", nil, helmfileCommand)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *HelmState) triggerGlobalReleaseEvent(evt string, evtErr error, helmfileCmd string) (bool, error) {
|
||||||
|
bus := &event.Bus{
|
||||||
|
Hooks: st.Hooks,
|
||||||
|
StateFilePath: st.FilePath,
|
||||||
|
BasePath: st.basePath,
|
||||||
|
Namespace: st.OverrideNamespace,
|
||||||
|
Env: st.Env,
|
||||||
|
Logger: st.logger,
|
||||||
|
ReadFile: st.readFile,
|
||||||
|
}
|
||||||
|
data := map[string]interface{}{
|
||||||
|
"HelmfileCommand": helmfileCmd,
|
||||||
|
}
|
||||||
|
return bus.Trigger(evt, evtErr, data)
|
||||||
|
}
|
||||||
|
|
||||||
func (st *HelmState) triggerPrepareEvent(r *ReleaseSpec, helmfileCommand string) (bool, error) {
|
func (st *HelmState) triggerPrepareEvent(r *ReleaseSpec, helmfileCommand string) (bool, error) {
|
||||||
return st.triggerReleaseEvent("prepare", nil, r, helmfileCommand)
|
return st.triggerReleaseEvent("prepare", nil, r, helmfileCommand)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue