Commit 9665db7d authored by Matt Butcher's avatar Matt Butcher Committed by GitHub
Browse files

Merge pull request #2157 from jchauncey/hook-weights

feat(hooks): Adds weighted hooks
parents 613f7e0d fe575009
main Release add-codeql dependabot/go_modules/github.com/docker/distribution-2.8.2incompatible dependabot/go_modules/github.com/lib/pq-1.10.9 dependabot/go_modules/github.com/rubenv/sql-migrate-1.4.0 dependabot/go_modules/golang.org/x/crypto-0.9.0 dependabot/go_modules/golang.org/x/term-0.8.0 dependabot/go_modules/k8s.io/klog/v2-2.100.1 dev-v2 feat-v3/event-emitter-lua release-2.10 release-2.11 release-2.12 release-2.13 release-2.14 release-2.15 release-2.16 release-2.17 release-2.3 release-2.4 release-2.5 release-2.6 release-2.7 release-2.8 release-2.9 release-3.0 release-3.1 release-3.10 release-3.11 release-3.12 release-3.2 release-3.3 release-3.4 release-3.5 release-3.6 release-3.6.1 release-3.6.2 release-3.7 release-3.8 release-3.9 release-v3.0.0-beta.4 v3.12.0 v3.12.0-rc.1 v3.12.0-dev.1 v3.11.3 v3.11.2 v3.11.1 v3.11.0 v3.11.0-rc.2 v3.11.0-rc.1 v3.10.3 v3.10.2 v3.10.1 v3.10.0 v3.10.0-rc.1 v3.9.4 v3.9.3 v3.9.2 v3.9.1 v3.9.0 v3.9.0-rc.1 v3.8.2 v3.8.1 v3.8.0 v3.8.0-rc.2 v3.8.0-rc.1 v3.7.2 v3.7.1 v3.7.0 v3.7.0-rc.3 v3.7.0-rc.2 v3.7.0-rc.1 v3.6.3 v3.6.2 v3.6.1 v3.6.0 v3.6.0-rc.1 v3.5.4 v3.5.3 v3.5.2 v3.5.1 v3.5.0 v3.5.0-rc.2 v3.5.0-rc.1 v3.4.2 v3.4.1 v3.4.0 v3.4.0-rc.1 v3.3.4 v3.3.3 v3.3.2 v3.3.1 v3.3.0 v3.3.0-rc.2 v3.3.0-rc.1 v3.2.4 v3.2.3 v3.2.2 v3.2.1 v3.2.0 v3.2.0-rc.1 v3.1.3 v3.1.2 v3.1.1 v3.1.0 v3.1.0-rc.3 v3.1.0-rc.2 v3.1.0-rc.1 v3.0.3 v3.0.2 v3.0.1 v3.0.0 v3.0.0-rc.4 v3.0.0-rc.3 v3.0.0-rc.2 v3.0.0-rc.1 v3.0.0-beta.5 v3.0.0-beta.4 v3.0.0-beta.3 v3.0.0-beta.2 v3.0.0-beta.1 v3.0.0-alpha.2 v3.0.0-alpha.1 v2.17.0 v2.17.0-rc.1 v2.16.12 v2.16.11 v2.16.10 v2.16.9 v2.16.8 v2.16.7 v2.16.6 v2.16.5 v2.16.4 v2.16.3 v2.16.2 v2.16.1 v2.16.0 v2.16.0-rc.2 v2.16.0-rc.1 v2.15.2 v2.15.1 v2.15.0 v2.15.0-rc.2 v2.15.0-rc.1 v2.14.3 v2.14.2 v2.14.1 v2.14.0 v2.14.0-rc.2 v2.14.0-rc.1 v2.13.1 v2.13.1-rc.1 v2.13.0 v2.13.0-rc.2 v2.13.0-rc.1 v2.12.3 v2.12.2 v2.12.1 v2.12.0 v2.12.0-rc.2 v2.12.0-rc.1 v2.11.0 v2.11.0-rc.4 v2.11.0-rc.3 v2.11.0-rc.2 v2.11.0-rc.1 v2.10.0 v2.10.0-rc.3 v2.10.0-rc.2 v2.10.0-rc.1 v2.9.1 v2.9.0 v2.9.0-rc5 v2.9.0-rc4 v2.9.0-rc3 v2.9.0-rc2 v2.9.0-rc1 v2.8.2 v2.8.2-rc1 v2.8.1 v2.8.0 v2.8.0-rc.1 v2.7.2 v2.7.1 v2.7.0 v2.7.0-rc1 v2.6.2 v2.6.1 v2.6.0 v2.5.1 v2.5.0 v2.4.2 v2.4.1 v2.4.0 v2.3.1 v2.3.0
No related merge requests found
Showing with 167 additions and 13 deletions
+167 -13
...@@ -58,16 +58,18 @@ hooks, the lifecycle is altered like this: ...@@ -58,16 +58,18 @@ hooks, the lifecycle is altered like this:
1. User runs `helm install foo` 1. User runs `helm install foo`
2. Chart is loaded into Tiller 2. Chart is loaded into Tiller
3. After some verification, Tiller renders the `foo` templates 3. After some verification, Tiller renders the `foo` templates
4. Tiller executes the `pre-install` hook (loading hook resources into 4. Tiller prepares to execute the `pre-install` hooks (loading hook resources into
Kubernetes) Kubernetes)
5. Tiller waits until the hook is "Ready" 5. Tiller sorts hooks by weight (assigning a weight of 0 by default) and by name for those hooks with the same weight in ascending order.
6. Tiller loads the resulting resources into Kubernetes. Note that if the `--wait` 6. Tiller then loads the hook with the lowest weight first (negative to positive)
7. Tiller waits until the hook is "Ready"
8. Tiller loads the resulting resources into Kubernetes. Note that if the `--wait`
flag is set, Tiller will wait until all resources are in a ready state flag is set, Tiller will wait until all resources are in a ready state
and will not run the `post-install` hook until they are ready. and will not run the `post-install` hook until they are ready.
7. Tiller executes the `post-install` hook (loading hook resources) 9. Tiller executes the `post-install` hook (loading hook resources)
8. Tiller waits until the hook is "Ready" 10. Tiller waits until the hook is "Ready"
9. Tiller returns the release name (and other data) to the client 11. Tiller returns the release name (and other data) to the client
10. The client exits 12. The client exits
What does it mean to wait until a hook is ready? This depends on the What does it mean to wait until a hook is ready? This depends on the
resource declared in the hook. If the resources is a `Job` kind, Tiller resource declared in the hook. If the resources is a `Job` kind, Tiller
...@@ -114,6 +116,7 @@ metadata: ...@@ -114,6 +116,7 @@ metadata:
# This is what defines this resource as a hook. Without this line, the # This is what defines this resource as a hook. Without this line, the
# job is considered part of the release. # job is considered part of the release.
"helm.sh/hook": post-install "helm.sh/hook": post-install
"helm.sh/hook-weight": "-5"
spec: spec:
template: template:
metadata: metadata:
...@@ -154,3 +157,12 @@ When subcharts declare hooks, those are also evaluated. There is no way ...@@ -154,3 +157,12 @@ When subcharts declare hooks, those are also evaluated. There is no way
for a top-level chart to disable the hooks declared by subcharts. And for a top-level chart to disable the hooks declared by subcharts. And
again, there is no guaranteed ordering. again, there is no guaranteed ordering.
It is also possible to define a weight for a hook which will help build a deterministic executing order. Weights are defined using the following annotation:
```
annotations:
"helm.sh/hook-weight": "5"
```
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.
...@@ -23,6 +23,9 @@ import ( ...@@ -23,6 +23,9 @@ import (
// HookAnno is the label name for a hook // HookAnno is the label name for a hook
const HookAnno = "helm.sh/hook" const HookAnno = "helm.sh/hook"
// HookWeightAnno is the label name for a hook weight
const HookWeightAnno = "helm.sh/hook-weight"
// Types of hooks // Types of hooks
const ( const (
PreInstall = "pre-install" PreInstall = "pre-install"
......
...@@ -100,6 +100,8 @@ type Hook struct { ...@@ -100,6 +100,8 @@ type Hook struct {
Events []Hook_Event `protobuf:"varint,5,rep,packed,name=events,enum=hapi.release.Hook_Event" json:"events,omitempty"` Events []Hook_Event `protobuf:"varint,5,rep,packed,name=events,enum=hapi.release.Hook_Event" json:"events,omitempty"`
// LastRun indicates the date/time this was last run. // LastRun indicates the date/time this was last run.
LastRun *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=last_run,json=lastRun" json:"last_run,omitempty"` LastRun *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=last_run,json=lastRun" json:"last_run,omitempty"`
// Weight indicates the sort order for execution among similar Hook types
Weight int `protobuf:"bytes,7,opt,name=weight" json:"weight,omitempty"`
} }
func (m *Hook) Reset() { *m = Hook{} } func (m *Hook) Reset() { *m = Hook{} }
......
/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tiller
import (
"sort"
"k8s.io/helm/pkg/proto/hapi/release"
)
// sortByHookWeight does an in-place sort of hooks by their supplied weight.
func sortByHookWeight(hooks []*release.Hook) []*release.Hook {
hs := newHookWeightSorter(hooks)
sort.Sort(hs)
return hs.hooks
}
type hookWeightSorter struct {
hooks []*release.Hook
}
func newHookWeightSorter(h []*release.Hook) *hookWeightSorter {
return &hookWeightSorter{
hooks: h,
}
}
func (hs *hookWeightSorter) Len() int { return len(hs.hooks) }
func (hs *hookWeightSorter) Swap(i, j int) {
hs.hooks[i], hs.hooks[j] = hs.hooks[j], hs.hooks[i]
}
func (hs *hookWeightSorter) Less(i, j int) bool {
if hs.hooks[i].Weight == hs.hooks[j].Weight {
return hs.hooks[i].Name < hs.hooks[j].Name
}
return hs.hooks[i].Weight < hs.hooks[j].Weight
}
/*
Copyright 2017 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package tiller
import (
"testing"
"k8s.io/helm/pkg/proto/hapi/release"
)
func TestHookSorter(t *testing.T) {
hooks := []*release.Hook{
{
Name: "g",
Kind: "pre-install",
Weight: 99,
},
{
Name: "f",
Kind: "pre-install",
Weight: 3,
},
{
Name: "b",
Kind: "pre-install",
Weight: -3,
},
{
Name: "e",
Kind: "pre-install",
Weight: 3,
},
{
Name: "a",
Kind: "pre-install",
Weight: -10,
},
{
Name: "c",
Kind: "pre-install",
Weight: 0,
},
{
Name: "d",
Kind: "pre-install",
Weight: 3,
},
}
res := sortByHookWeight(hooks)
got := ""
expect := "abcdefg"
for _, r := range res {
got += r.Name
}
if got != expect {
t.Errorf("Expected %q, got %q", expect, got)
}
}
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"fmt" "fmt"
"log" "log"
"path" "path"
"strconv"
"strings" "strings"
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
...@@ -109,12 +110,20 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort ...@@ -109,12 +110,20 @@ func sortManifests(files map[string]string, apis chartutil.VersionSet, sort Sort
generic = append(generic, manifest{name: n, content: c, head: &sh}) generic = append(generic, manifest{name: n, content: c, head: &sh})
continue continue
} }
hws, _ := sh.Metadata.Annotations[hooks.HookWeightAnno]
hw, err := strconv.Atoi(hws)
if err != nil {
hw = 0
}
h := &release.Hook{ h := &release.Hook{
Name: sh.Metadata.Name, Name: sh.Metadata.Name,
Kind: sh.Kind, Kind: sh.Kind,
Path: n, Path: n,
Manifest: c, Manifest: c,
Events: []release.Hook_Event{}, Events: []release.Hook_Event{},
Weight: hw,
} }
isHook := false isHook := false
......
...@@ -934,17 +934,18 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin ...@@ -934,17 +934,18 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
} }
log.Printf("Executing %s hooks for %s", hook, name) log.Printf("Executing %s hooks for %s", hook, name)
executingHooks := []*release.Hook{}
for _, h := range hs { for _, h := range hs {
found := false
for _, e := range h.Events { for _, e := range h.Events {
if e == code { if e == code {
found = true executingHooks = append(executingHooks, h)
} }
} }
// If this doesn't implement the hook, skip it. }
if !found {
continue executingHooks = sortByHookWeight(executingHooks)
}
for _, h := range executingHooks {
b := bytes.NewBufferString(h.Manifest) b := bytes.NewBufferString(h.Manifest)
if err := kubeCli.Create(namespace, b, timeout, false); err != nil { if err := kubeCli.Create(namespace, b, timeout, false); err != nil {
...@@ -960,6 +961,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin ...@@ -960,6 +961,7 @@ func (s *ReleaseServer) execHook(hs []*release.Hook, name, namespace, hook strin
} }
h.LastRun = timeconv.Now() h.LastRun = timeconv.Now()
} }
log.Printf("Hooks complete for %s %s", hook, name) log.Printf("Hooks complete for %s %s", hook, name)
return nil return nil
} }
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment