Skip to content

Commit

Permalink
feat(*) add key helm.sh/hook-delete-policy to hook annotation
Browse files Browse the repository at this point in the history
When "helm.sh/hook-delete-policy: hook-succeeded" is provided in a hook's annotation, Tiller will automatically delete the hook after the hook is succeeded. When "helm.sh/hook-delete-policy: hook-failed" is provided in a hook's annotation, Tiller will automatically delete the hook after the hook is failed.

Closes helm#1769
  • Loading branch information
DockerZK committed Aug 24, 2017
1 parent a2323f8 commit 734b124
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 46 deletions.
6 changes: 6 additions & 0 deletions _proto/hapi/release/hook.proto
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ message Hook {
RELEASE_TEST_SUCCESS = 9;
RELEASE_TEST_FAILURE = 10;
}
enum DeletePolicy {
SUCCEEDED = 0;
FAILED = 1;
}
string name = 1;
// Kind is the Kubernetes kind.
string kind = 2;
Expand All @@ -48,4 +52,6 @@ message Hook {
google.protobuf.Timestamp last_run = 6;
// Weight indicates the sort order for execution among similar Hook type
int32 weight = 7;
// DeletePolicies are the policies that indicate when to delete the hook
repeated DeletePolicy delete_policies = 8;
}
18 changes: 14 additions & 4 deletions docs/charts_hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,16 +87,16 @@ in the future.) It is considered good practice to add a hook weight, and set it
to `0` if weight is not important.


### Hook resources are unmanaged
### Hook resources are not managed with correponding releases

The resources that a hook creates are not tracked or managed as part of the
release. Once Tiller verifies that the hook has reached its ready state, it
will leave the hook resource alone.

Practically speaking, this means that if you create resources in a hook, you
cannot rely upon `helm delete` to remove the resources. To destroy such
resources, you need to write code to perform this operation in a `pre-delete`
or `post-delete` hook.
resources, you need to either write code to perform this operation in a `pre-delete`
or `post-delete` hook or add `"helm.sh/hook-delete-policy"` annotation to the hook template file.

## Writing a Hook

Expand All @@ -122,6 +122,7 @@ metadata:
# job is considered part of the release.
"helm.sh/hook": post-install
"helm.sh/hook-weight": "-5"
"helm/hook-delete-policy": hook-succeeded
spec:
template:
metadata:
Expand Down Expand Up @@ -160,7 +161,7 @@ and a config map as a pre-install hook.
When subcharts declare hooks, those are also evaluated. There is no way
for a top-level chart to disable the hooks declared by subcharts.

It is also possible to define a weight for a hook which will help build a
It is possible to define a weight for a hook which will help build a
deterministic executing order. Weights are defined using the following annotation:

```
Expand All @@ -172,3 +173,12 @@ Hook weights can be positive or negative numbers but must be represented as
strings. When Tiller starts the execution cycle of hooks of a particular Kind it
will sort those hooks in ascending order.

It is also possible to define policies that determine when to delete corresponding hook resources. Hook deletion policies are defined using the following annotation:

```
annotations:
"helm.sh/hook-delete-policy": hook-succeeded
```

When using `"helm.sh/hook-delete-policy"` annoation, you can choose its value from `"hook-succeeded"` and `"hook-failed"`. The value `"hook-succeeded"` specifies Tiller should delete the hook after the hook is successfully excuted, while the value `"hook-failed"`specifies Tiller should delete the hook if the hook is failed during execuation.

9 changes: 9 additions & 0 deletions pkg/hooks/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ const HookAnno = "helm.sh/hook"
// HookWeightAnno is the label name for a hook weight
const HookWeightAnno = "helm.sh/hook-weight"

// HookDeleteAnno is the label name for the delete policy for a hook
const HookDeleteAnno = "helm.sh/hook-delete-policy"

// Types of hooks
const (
PreInstall = "pre-install"
Expand All @@ -40,6 +43,12 @@ const (
ReleaseTestFailure = "test-failure"
)

// Type of policy for deleting the hook
const (
HookSucceeded = "hook-succeeded"
HookFailed = "hook-failed"
)

// FilterTestHooks filters the list of hooks are returns only testing hooks.
func FilterTestHooks(hooks []*release.Hook) []*release.Hook {
testHooks := []*release.Hook{}
Expand Down
94 changes: 59 additions & 35 deletions pkg/proto/hapi/release/hook.pb.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

42 changes: 36 additions & 6 deletions pkg/tiller/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ var events = map[string]release.Hook_Event{
hooks.ReleaseTestFailure: release.Hook_RELEASE_TEST_FAILURE,
}

// deletePolices represents a mapping between the key in the annotation for label deleting policy and its real meaning
var deletePolices = map[string]release.Hook_DeletePolicy{
hooks.HookSucceeded: release.Hook_SUCCEEDED,
hooks.HookFailed: release.Hook_FAILED,
}

// manifest represents a manifest file, which has a name and some content.
type manifest struct {
name string
Expand Down Expand Up @@ -113,6 +119,13 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort
// annotations:
// helm.sh/hook: pre-install
//
// To determine the policy to delete the hook, it looks for a YAML structure like this:
//
// kind: SomeKind
// apiVersion: v1
// metadata:
// annotations:
// helm.sh/hook-delete-policy: hook-succeeded
func (file *manifestFile) sort(result *result) error {
for _, m := range file.entries {
var entry util.SimpleHead
Expand Down Expand Up @@ -149,12 +162,13 @@ func (file *manifestFile) sort(result *result) error {
hw := calculateHookWeight(entry)

h := &release.Hook{
Name: entry.Metadata.Name,
Kind: entry.Kind,
Path: file.path,
Manifest: m,
Events: []release.Hook_Event{},
Weight: hw,
Name: entry.Metadata.Name,
Kind: entry.Kind,
Path: file.path,
Manifest: m,
Events: []release.Hook_Event{},
Weight: hw,
DeletePolicies: []release.Hook_DeletePolicy{},
}

isKnownHook := false
Expand All @@ -173,6 +187,22 @@ func (file *manifestFile) sort(result *result) error {
}

result.hooks = append(result.hooks, h)

isKnownDeletePolices := false
dps, ok := entry.Metadata.Annotations[hooks.HookDeleteAnno]
if ok {
for _, dp := range strings.Split(dps, ",") {
dp = strings.ToLower(strings.TrimSpace(dp))
p, exist := deletePolices[dp]
if exist {
isKnownDeletePolices = true
h.DeletePolicies = append(h.DeletePolicies, p)
}
}
if !isKnownDeletePolices {
log.Printf("info: skipping unknown hook delete policy: %q", dps)
}
}
}

return nil
Expand Down
40 changes: 39 additions & 1 deletion pkg/tiller/release_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"

"k8s.io/helm/pkg/chartutil"
"k8s.io/helm/pkg/hooks"
"k8s.io/helm/pkg/proto/hapi/chart"
"k8s.io/helm/pkg/proto/hapi/release"
"k8s.io/helm/pkg/proto/hapi/services"
Expand Down Expand Up @@ -347,12 +348,36 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
b.WriteString(h.Manifest)
if err := kubeCli.WatchUntilReady(namespace, b, timeout, false); err != nil {
s.Log("warning: Release %s %s %s could not complete: %s", name, hook, h.Path, err)
// If a hook is failed, checkout the annotation of the hook to determine whether the hook should be deleted
// under failed condition. If so, then clear the corresponding resource object in the hook
if hookShouldBeDeleted(h, hooks.HookFailed) {
b.Reset()
b.WriteString(h.Manifest)
s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookFailed)
if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil {
s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete)
return errHookDelete
}
}
return err
}
h.LastRun = timeconv.Now()
}

s.Log("hooks complete for %s %s", hook, name)
// If all hooks are succeeded, checkout the annotation of each hook to determine whether the hook should be deleted
// under succeeded condition. If so, then clear the corresponding resource object in each hook
for _, h := range executingHooks {
b := bytes.NewBufferString(h.Manifest)
if hookShouldBeDeleted(h, hooks.HookSucceeded) {
s.Log("deleting %s hook %s for release %s due to %q policy", hook, h.Name, name, hooks.HookSucceeded)
if errHookDelete := kubeCli.Delete(namespace, b); errHookDelete != nil {
s.Log("warning: Release %s %s %S could not be deleted: %s", name, hook, h.Path, errHookDelete)
return errHookDelete
}
}
h.LastRun = timeconv.Now()
}

return nil
}

Expand All @@ -373,3 +398,16 @@ func validateReleaseName(releaseName string) error {

return nil
}

// hookShouldBeDeleted determines whether the defined hook deletion policy matches the hook deletion polices
// supported by helm. If so, mark the hook as one should be deleted.
func hookShouldBeDeleted(hook *release.Hook, policy string) bool {
if dp, ok := deletePolices[policy]; ok {
for _, v := range hook.DeletePolicies {
if dp == v {
return true
}
}
}
return false
}

0 comments on commit 734b124

Please sign in to comment.