Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 20 additions & 4 deletions docs/src/HOOKS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ During execution, a module hook receives global values and module values. Module
| [onStartup](#onstartup)↗ | – | ✓ | On Addon-operator startup or module enablement |
| [beforeAll](#beforeall)↗ | ✓ | – | Before any modules are executed |
| [afterAll](#afterall)↗ | ✓ | – | After all modules are executed |
| [beforeHelm](#beforehelm)↗ | – | ✓ | Before executing `helm install` |
| [afterHelm](#afterhelm)↗ | – | ✓ | After executing `helm install` |
| [afterDeleteHelm](#afterdeletehelm)↗ | – | ✓ | After executing `helm delete` |
| [beforeHelm](#beforehelm)↗ | – | ✓ | Before executing `helm install` |
| [afterHelm](#afterhelm)↗ | – | ✓ | After executing `helm install` |
| [beforeDeleteHelm](#beforedeletehelm)↗ | – | ✓ | Before executing `helm delete` |
| [afterDeleteHelm](#afterdeletehelm)↗ | – | ✓ | After executing `helm delete` |
| [schedule](#schedule)↗ | ✓ | ✓ | Run on schedule |
| [kubernetes](#kubernetes)↗ | ✓ | ✓ | Run on event from Kubernetes |

Expand Down Expand Up @@ -101,6 +102,21 @@ Parameters:

- `ORDER` — an integer value that specifies an execution order. When added to the "main" queue, the hooks will be sorted by this value and then alphabetically by file name.

### beforeDeleteHelm

Example:

```yaml
configVersion: v1
beforeDeleteHelm: ORDER
```

Parameters:

- `ORDER` — an integer value that specifies an execution order. When added to the "main" queue, the hooks will be sorted by this value and then alphabetically by file name.

The `beforeDeleteHelm` hook runs **just before `helm uninstall`** for the module's release. It is a native, addon-operator-side replacement for a Helm chart-level `pre-delete` hook: the hook runs as part of the addon-operator process (with full access to the module's values, snapshots, patch collector and metrics) instead of inside a Job in the cluster. If the hook fails, `helm uninstall` and `afterDeleteHelm` are not executed and the module is not removed; the converge loop will retry with backoff. If the module has no Helm release at delete time, the hook is skipped (symmetric with skipping `helm uninstall`).

### afterDeleteHelm

Example:
Expand Down Expand Up @@ -143,7 +159,7 @@ The `$BINDING_CONTEXT_PATH` environment variable contains the path to a file wit

The binding context for `schedule` and `kubernetes` hooks contains additional fields, described in Shell-operator [documentation][shell-operator-binding-context].

`beforeAll` and `afterAll` global hooks and `beforeHelm`, `afterHelm`, and `afterDeleteHelm` module hooks are executed with the binding context that includes a `snapshots` field, which contains all Kubernetes objects that match hook's `kubernetes` bindings configurations.
`beforeAll` and `afterAll` global hooks and `beforeHelm`, `afterHelm`, `beforeDeleteHelm`, and `afterDeleteHelm` module hooks are executed with the binding context that includes a `snapshots` field, which contains all Kubernetes objects that match hook's `kubernetes` bindings configurations.

For example, a global hook with `kubernetes` and `beforeAll` bindings may have this configuration:

Expand Down
7 changes: 7 additions & 0 deletions docs/src/LIFECYCLE-STEPS.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ Startup steps:
- if module values are changed, restart 'module run'

<a name="module-delete"></a>6. 'module delete' for each disabled module
- if a Helm release exists, execute module hooks with 'beforeDeleteHelm' binding ordered by the ORDER value (see [beforeDeleteHelm](HOOKS.md#beforedeletehelm))
- input
- binding context ($BINDING_CONTEXT_PATH temporary file)
- `{"binding":"beforeDeleteHelm"}`
- extra field `"snapshots"` contains existed objects from all 'kubernetes' bindings of this hook
- config and values are layered the same way as for the other module-lifecycle hooks (see 'beforeHelm' / 'afterHelm' above)
- if any 'beforeDeleteHelm' hook fails, `helm delete --purge` and 'afterDeleteHelm' are NOT executed and the deletion is retried with backoff
- run `helm delete --purge`
- execute module hooks with 'afterDeleteHelm' binding ordered by the ORDER value (see [afterDeleteHelm](HOOKS.md#afterdeletehelm))
- input
Expand Down
4 changes: 3 additions & 1 deletion docs/src/LIFECYCLE.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ All other actions are handled in a single "main" queue:
- `beforeHelm`
- execution of `helm` commands
- `afterHelm`
- `beforeDeleteHelm` (on deactivation, before `helm delete`)
- `afterDeleteHelm` (on deactivation, after `helm delete`)

This document mainly describes modules. To get more information on hooks, see [HOOKS](HOOKS.md) document. To get a full view of how hooks, modules, [values](VALUES.md), binding contexts, and queues are interlinked, see [LIFECYCLE-STEPS](LIFECYCLE-STEPS.md) document.

Expand All @@ -61,7 +63,7 @@ After the launch the module would start responding to two types of events:
- `schedule` — events that are generated by the crontab scheduler built in the addon-operator;
- `kubernetes` — events within the cluster that API server announces to the Addon-operator.

When the module is deactivated, the Addon-operator launches command `helm delete --purge` and after the release deletion, the `afterDeleteHelm` hooks are executed.
When the module is deactivated, the Addon-operator runs `beforeDeleteHelm` hooks (only when a Helm release exists), launches `helm delete --purge`, and then runs `afterDeleteHelm` hooks. If any `beforeDeleteHelm` hook returns an error, `helm delete` and `afterDeleteHelm` are skipped and the deletion is retried with backoff.

All necessary hooks will be restarted if there are errors during the module activation or deactivation. For example, if an error occurred in the hook with `afterHelm` binding during the first module execution, then after a 5 seconds delay the `onStartup` and `beforeHelm` hooks are executed, the Helm chart is installed and then `afterHelm` hooks are executed.

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ require (
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
github.com/deckhouse/deckhouse/pkg/log v0.2.0
github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0
github.com/deckhouse/module-sdk v0.10.4
github.com/deckhouse/module-sdk v0.10.8-0.20260427102426-3fb05940aa17
github.com/dominikbraun/graph v0.23.0
github.com/ettle/strcase v0.2.0
github.com/flant/kube-client v1.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ github.com/deckhouse/deckhouse/pkg/log v0.2.0 h1:6tmZQLwNb1o/hP1gzJQBjcwfA/bubbg
github.com/deckhouse/deckhouse/pkg/log v0.2.0/go.mod h1:pbAxTSDcPmwyl3wwKDcEB3qdxHnRxqTV+J0K+sha8bw=
github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0 h1:xZvbKuexrSQGEw6CB4n3UC7XbOb9QNLbm8UhcGZ2R1I=
github.com/deckhouse/deckhouse/pkg/metrics-storage v0.3.0/go.mod h1:Rz++SzCLkFW03WGgftnn91TimGU2shiKb5S/YuxcBuE=
github.com/deckhouse/module-sdk v0.10.4 h1:1C61nXevgQiUnmv5Is+XP9Octu05XWKgjFi6gmL7miM=
github.com/deckhouse/module-sdk v0.10.4/go.mod h1:wpEKjpMUHZ4D5JGPfHChKGzBcOVyZnUN4sP0yTEaHdU=
github.com/deckhouse/module-sdk v0.10.8-0.20260427102426-3fb05940aa17 h1:tPvr/8bkgRLjx98jgiLykj9QTB2k4udjrdugHmgF9e4=
github.com/deckhouse/module-sdk v0.10.8-0.20260427102426-3fb05940aa17/go.mod h1:wpEKjpMUHZ4D5JGPfHChKGzBcOVyZnUN4sP0yTEaHdU=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
Expand Down
11 changes: 6 additions & 5 deletions pkg/hook/types/bindings.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ import (

// Additional binding types, specific to addon-operator
const (
BeforeHelm BindingType = "beforeHelm"
AfterHelm BindingType = "afterHelm"
AfterDeleteHelm BindingType = "afterDeleteHelm"
BeforeAll BindingType = "beforeAll"
AfterAll BindingType = "afterAll"
BeforeHelm BindingType = "beforeHelm"
AfterHelm BindingType = "afterHelm"
BeforeDeleteHelm BindingType = "beforeDeleteHelm"
AfterDeleteHelm BindingType = "afterDeleteHelm"
BeforeAll BindingType = "beforeAll"
AfterAll BindingType = "afterAll"
)
22 changes: 12 additions & 10 deletions pkg/module_manager/go_hook/go_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ type HookConfigLoader interface {
GetOnStartup() *float64
GetBeforeAll() *float64
GetAfterAll() *float64
GetBeforeDeleteHelm() *float64
GetAfterDeleteHelm() *float64
}

Expand Down Expand Up @@ -65,16 +66,17 @@ type HookConfig struct {
// OnStartup runs hook on module/global startup
// Attention! During the startup you don't have snapshots available
// use native KubeClient to fetch resources
OnStartup *OrderedConfig
OnBeforeHelm *OrderedConfig
OnAfterHelm *OrderedConfig
OnAfterDeleteHelm *OrderedConfig
OnBeforeAll *OrderedConfig
OnAfterAll *OrderedConfig
AllowFailure bool
Queue string
Settings *HookConfigSettings
Logger *log.Logger
OnStartup *OrderedConfig
OnBeforeHelm *OrderedConfig
OnAfterHelm *OrderedConfig
OnBeforeDeleteHelm *OrderedConfig
OnAfterDeleteHelm *OrderedConfig
OnBeforeAll *OrderedConfig
OnAfterAll *OrderedConfig
AllowFailure bool
Queue string
Settings *HookConfigSettings
Logger *log.Logger
}

type HookConfigSettings struct {
Expand Down
10 changes: 10 additions & 0 deletions pkg/module_manager/models/hooks/kind/batch_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,16 @@ func (h *BatchHook) GetAfterAll() *float64 {
return &res
}

func (h *BatchHook) GetBeforeDeleteHelm() *float64 {
if h.config == nil || h.config.OnBeforeDeleteHelm == nil {
return nil
}

res := float64(*h.config.OnBeforeDeleteHelm)

return &res
}

func (h *BatchHook) GetAfterDeleteHelm() *float64 {
if h.config == nil || h.config.OnAfterDeleteHelm == nil {
return nil
Expand Down
26 changes: 16 additions & 10 deletions pkg/module_manager/models/hooks/kind/batch_hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ echo '[{"configVersion":"v1",
"onStartup":10,
"beforeHelm":5,
"afterHelm":15,
"beforeDeleteHelm":20,
"afterDeleteHelm":25,
"metadata":{
"name":"main",
Expand Down Expand Up @@ -63,6 +64,7 @@ fi
assert.Equal(t, ptr.To(uint(10)), hook.OnStartup)
assert.Equal(t, ptr.To(uint(5)), hook.OnBeforeHelm)
assert.Equal(t, ptr.To(uint(15)), hook.OnAfterHelm)
assert.Equal(t, ptr.To(uint(20)), hook.OnBeforeDeleteHelm)
assert.Equal(t, ptr.To(uint(25)), hook.OnAfterDeleteHelm)
}

Expand All @@ -81,11 +83,12 @@ func Test_BatchHook_Config(t *testing.T) {
}

type wants struct {
err error
onStartup *float64
beforeHelm *float64
afterHelm *float64
afterDeleteHelm *float64
err error
onStartup *float64
beforeHelm *float64
afterHelm *float64
beforeDeleteHelm *float64
afterDeleteHelm *float64
}

tests := []struct {
Expand All @@ -105,6 +108,7 @@ func Test_BatchHook_Config(t *testing.T) {
"onStartup":10,
"beforeHelm":5,
"afterHelm":15,
"beforeDeleteHelm":20,
"afterDeleteHelm":25,
"metadata":{
"name":"main",
Expand All @@ -122,11 +126,12 @@ func Test_BatchHook_Config(t *testing.T) {
"jqFilter":".metadata.name"}]}]`,
},
wants: wants{
err: nil,
onStartup: ptr.To(10.0),
beforeHelm: ptr.To(5.0),
afterHelm: ptr.To(15.0),
afterDeleteHelm: ptr.To(25.0),
err: nil,
onStartup: ptr.To(10.0),
beforeHelm: ptr.To(5.0),
afterHelm: ptr.To(15.0),
beforeDeleteHelm: ptr.To(20.0),
afterDeleteHelm: ptr.To(25.0),
},
},
}
Expand Down Expand Up @@ -170,6 +175,7 @@ fi
assert.Equal(t, tt.wants.onStartup, bHook.GetOnStartup())
assert.Equal(t, tt.wants.beforeHelm, bHook.GetBeforeAll())
assert.Equal(t, tt.wants.afterHelm, bHook.GetAfterAll())
assert.Equal(t, tt.wants.beforeDeleteHelm, bHook.GetBeforeDeleteHelm())
assert.Equal(t, tt.wants.afterDeleteHelm, bHook.GetAfterDeleteHelm())
})
}
Expand Down
8 changes: 8 additions & 0 deletions pkg/module_manager/models/hooks/kind/gohook.go
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,14 @@ func (h *GoHook) GetAfterAll() *float64 {
return nil
}

func (h *GoHook) GetBeforeDeleteHelm() *float64 {
if h.config == nil || h.config.OnBeforeDeleteHelm == nil {
return nil
}

return &h.config.OnBeforeDeleteHelm.Order
}

func (h *GoHook) GetAfterDeleteHelm() *float64 {
if h.config == nil || h.config.OnAfterDeleteHelm == nil {
return nil
Expand Down
38 changes: 27 additions & 11 deletions pkg/module_manager/models/hooks/kind/shellhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,15 @@ func (sh *ShellHook) GetAfterAll() *float64 {
return nil
}

func (sh *ShellHook) GetBeforeDeleteHelm() *float64 {
res := ConvertFloatForBinding(sh.ScheduleConfig.BeforeDeleteHelm)
if res != nil {
return res
}

return nil
}

func (sh *ShellHook) GetAfterDeleteHelm() *float64 {
res := ConvertFloatForBinding(sh.ScheduleConfig.AfterDeleteHelm)
if res != nil {
Expand All @@ -380,9 +389,10 @@ type HookScheduleConfig struct {
BeforeAll *uint `json:"beforeAll" yaml:"beforeAll"`
AfterAll *uint `json:"afterAll" yaml:"afterAll"`
// embedded module
BeforeHelm *uint `json:"beforeHelm" yaml:"beforeHelm"`
AfterHelm *uint `json:"afterHelm" yaml:"afterHelm"`
AfterDeleteHelm *uint `json:"afterDeleteHelm" yaml:"afterDeleteHelm"`
BeforeHelm *uint `json:"beforeHelm" yaml:"beforeHelm"`
AfterHelm *uint `json:"afterHelm" yaml:"afterHelm"`
BeforeDeleteHelm *uint `json:"beforeDeleteHelm" yaml:"beforeDeleteHelm"`
AfterDeleteHelm *uint `json:"afterDeleteHelm" yaml:"afterDeleteHelm"`
}

func ConvertFloatForBinding(value *uint) *float64 {
Expand Down Expand Up @@ -433,30 +443,36 @@ func getModuleHookConfigSchema(version string) *spec.Schema {
schema := config.Schemas[version]
switch version {
case "v1":
// add beforeHelm, afterHelm and afterDeleteHelm properties
// add beforeHelm, afterHelm, beforeDeleteHelm and afterDeleteHelm properties
schema += `
beforeHelm:
type: integer
example: 10
example: 10
afterHelm:
type: integer
example: 10
example: 10
beforeDeleteHelm:
type: integer
example: 10
afterDeleteHelm:
type: integer
example: 10
example: 10
`
case "v0":
// add beforeHelm, afterHelm and afterDeleteHelm properties
// add beforeHelm, afterHelm, beforeDeleteHelm and afterDeleteHelm properties
schema += `
beforeHelm:
type: integer
example: 10
example: 10
afterHelm:
type: integer
example: 10
example: 10
beforeDeleteHelm:
type: integer
example: 10
afterDeleteHelm:
type: integer
example: 10
example: 10
`
}
config.Schemas[globalHookVersion] = schema
Expand Down
Loading
Loading