Merge pull request #7499 from bacongobbler/fix-7416

ref(helm): sort hooks by kind
pull/7806/head
Matthew Fisher 5 years ago committed by GitHub
commit 193850a9e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -38,7 +38,8 @@ func (cfg *Configuration) execHook(rl *release.Release, hook release.HookEvent,
} }
} }
sort.Sort(hookByWeight(executingHooks)) // hooke are pre-ordered by kind, so keep order stable
sort.Stable(hookByWeight(executingHooks))
for _, h := range executingHooks { for _, h := range executingHooks {
// Set default delete policy to before-hook-creation // Set default delete policy to before-hook-creation

@ -16,7 +16,11 @@ limitations under the License.
package releaseutil package releaseutil
import "sort" import (
"sort"
"helm.sh/helm/v3/pkg/release"
)
// KindSortOrder is an ordering of Kinds. // KindSortOrder is an ordering of Kinds.
type KindSortOrder []string type KindSortOrder []string
@ -99,46 +103,42 @@ var UninstallOrder KindSortOrder = []string{
"Namespace", "Namespace",
} }
// sortByKind does an in-place sort of manifests by Kind. // sort manifests by kind.
// //
// Results are sorted by 'ordering', keeping order of items with equal kind/priority // Results are sorted by 'ordering', keeping order of items with equal kind/priority
func sortByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { func sortManifestsByKind(manifests []Manifest, ordering KindSortOrder) []Manifest {
ks := newKindSorter(manifests, ordering) sort.SliceStable(manifests, func(i, j int) bool {
sort.Stable(ks) return lessByKind(manifests[i], manifests[j], manifests[i].Head.Kind, manifests[j].Head.Kind, ordering)
return ks.manifests })
}
type kindSorter struct { return manifests
ordering map[string]int
manifests []Manifest
} }
func newKindSorter(m []Manifest, s KindSortOrder) *kindSorter { // sort hooks by kind, using an out-of-place sort to preserve the input parameters.
o := make(map[string]int, len(s)) //
for v, k := range s { // Results are sorted by 'ordering', keeping order of items with equal kind/priority
o[k] = v func sortHooksByKind(hooks []*release.Hook, ordering KindSortOrder) []*release.Hook {
} h := hooks
sort.SliceStable(h, func(i, j int) bool {
return lessByKind(h[i], h[j], h[i].Kind, h[j].Kind, ordering)
})
return &kindSorter{ return h
manifests: m,
ordering: o,
}
} }
func (k *kindSorter) Len() int { return len(k.manifests) } func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o KindSortOrder) bool {
ordering := make(map[string]int, len(o))
func (k *kindSorter) Swap(i, j int) { k.manifests[i], k.manifests[j] = k.manifests[j], k.manifests[i] } for v, k := range o {
ordering[k] = v
}
func (k *kindSorter) Less(i, j int) bool { first, aok := ordering[kindA]
a := k.manifests[i] second, bok := ordering[kindB]
b := k.manifests[j]
first, aok := k.ordering[a.Head.Kind]
second, bok := k.ordering[b.Head.Kind]
if !aok && !bok { if !aok && !bok {
// if both are unknown then sort alphabetically by kind, keep original order if same kind // if both are unknown then sort alphabetically by kind, keep original order if same kind
if a.Head.Kind != b.Head.Kind { if kindA != kindB {
return a.Head.Kind < b.Head.Kind return kindA < kindB
} }
return first < second return first < second
} }

@ -19,6 +19,8 @@ package releaseutil
import ( import (
"bytes" "bytes"
"testing" "testing"
"helm.sh/helm/v3/pkg/release"
) )
func TestKindSorter(t *testing.T) { func TestKindSorter(t *testing.T) {
@ -175,12 +177,18 @@ func TestKindSorter(t *testing.T) {
t.Fatalf("Expected %d names in order, got %d", want, got) t.Fatalf("Expected %d names in order, got %d", want, got)
} }
defer buf.Reset() defer buf.Reset()
for _, r := range sortByKind(manifests, test.order) { orig := manifests
for _, r := range sortManifestsByKind(manifests, test.order) {
buf.WriteString(r.Name) buf.WriteString(r.Name)
} }
if got := buf.String(); got != test.expected { if got := buf.String(); got != test.expected {
t.Errorf("Expected %q, got %q", test.expected, got) t.Errorf("Expected %q, got %q", test.expected, got)
} }
for i, manifest := range orig {
if manifest != manifests[i] {
t.Fatal("Expected input to sortManifestsByKind to stay the same")
}
}
}) })
} }
} }
@ -236,7 +244,7 @@ func TestKindSorterKeepOriginalOrder(t *testing.T) {
var buf bytes.Buffer var buf bytes.Buffer
t.Run(test.description, func(t *testing.T) { t.Run(test.description, func(t *testing.T) {
defer buf.Reset() defer buf.Reset()
for _, r := range sortByKind(manifests, test.order) { for _, r := range sortManifestsByKind(manifests, test.order) {
buf.WriteString(r.Name) buf.WriteString(r.Name)
} }
if got := buf.String(); got != test.expected { if got := buf.String(); got != test.expected {
@ -257,7 +265,7 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) {
} }
manifests := []Manifest{unknown, namespace} manifests := []Manifest{unknown, namespace}
sortByKind(manifests, InstallOrder) manifests = sortManifestsByKind(manifests, InstallOrder)
expectedOrder := []Manifest{namespace, unknown} expectedOrder := []Manifest{namespace, unknown}
for i, manifest := range manifests { for i, manifest := range manifests {
@ -266,3 +274,54 @@ func TestKindSorterNamespaceAgainstUnknown(t *testing.T) {
} }
} }
} }
// test hook sorting with a small subset of kinds, since it uses the same algorithm as sortManifestsByKind
func TestKindSorterForHooks(t *testing.T) {
hooks := []*release.Hook{
{
Name: "i",
Kind: "ClusterRole",
},
{
Name: "j",
Kind: "ClusterRoleBinding",
},
{
Name: "c",
Kind: "LimitRange",
},
{
Name: "a",
Kind: "Namespace",
},
}
for _, test := range []struct {
description string
order KindSortOrder
expected string
}{
{"install", InstallOrder, "acij"},
{"uninstall", UninstallOrder, "jica"},
} {
var buf bytes.Buffer
t.Run(test.description, func(t *testing.T) {
if got, want := len(test.expected), len(hooks); got != want {
t.Fatalf("Expected %d names in order, got %d", want, got)
}
defer buf.Reset()
orig := hooks
for _, r := range sortHooksByKind(hooks, test.order) {
buf.WriteString(r.Name)
}
for i, hook := range orig {
if hook != hooks[i] {
t.Fatal("Expected input to sortHooksByKind to stay the same")
}
}
if got := buf.String(); got != test.expected {
t.Errorf("Expected %q, got %q", test.expected, got)
}
})
}
}

@ -108,7 +108,7 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering
} }
} }
return result.hooks, sortByKind(result.generic, ordering), nil return sortHooksByKind(result.hooks, ordering), sortManifestsByKind(result.generic, ordering), nil
} }
// sort takes a manifestFile object which may contain multiple resource definition // sort takes a manifestFile object which may contain multiple resource definition

@ -219,7 +219,7 @@ metadata:
} }
} }
sorted = sortByKind(sorted, InstallOrder) sorted = sortManifestsByKind(sorted, InstallOrder)
for i, m := range generic { for i, m := range generic {
if m.Content != sorted[i].Content { if m.Content != sorted[i].Content {
t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content) t.Errorf("Expected %q, got %q", m.Content, sorted[i].Content)

Loading…
Cancel
Save