From 7e4bb365e6e7faf5520cf154a922786a1ea4e0dd Mon Sep 17 00:00:00 2001 From: Niklas Wagner Date: Wed, 24 Mar 2021 12:16:55 +0100 Subject: [PATCH] Allows ordering of Custom Resources Signed-off-by: Niklas Wagner --- pkg/releaseutil/kind_sorter.go | 46 +++++++++++++++++++++--------- pkg/releaseutil/manifest.go | 3 ++ pkg/releaseutil/manifest_sorter.go | 43 ++++++++++++++++++++++++++-- 3 files changed, 76 insertions(+), 16 deletions(-) diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index a340dfc29..89289c5de 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -108,12 +108,12 @@ var UninstallOrder KindSortOrder = []string{ // sort manifests by kind. // // Results are sorted by 'ordering', keeping order of items with equal kind/priority -func sortManifestsByKind(manifests []Manifest, ordering KindSortOrder) []Manifest { - sort.SliceStable(manifests, func(i, j int) bool { - return lessByKind(manifests[i], manifests[j], manifests[i].Head.Kind, manifests[j].Head.Kind, ordering) +func sortManifestsByKind(m []Manifest, ordering KindSortOrder) []Manifest { + sort.SliceStable(m, func(i, j int) bool { + return lessByKind(m[i], m[j], m[i].Head.Kind, m[j].Head.Kind, m[i].InstallBefore, m[j].InstallBefore, ordering) }) - return manifests + return m } // sort hooks by kind, using an out-of-place sort to preserve the input parameters. @@ -122,20 +122,15 @@ func sortManifestsByKind(manifests []Manifest, ordering KindSortOrder) []Manifes 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 lessByKind(h[i], h[j], h[i].Kind, h[j].Kind, nil, nil, ordering) }) return h } -func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o KindSortOrder) bool { - ordering := make(map[string]int, len(o)) - for v, k := range o { - ordering[k] = v - } - - first, aok := ordering[kindA] - second, bok := ordering[kindB] +func lessByKind(a interface{}, b interface{}, kindA string, kindB string, beforeA []string, beforeB []string, o KindSortOrder) bool { + first, aok := installOrderIndex(kindA, beforeA, o) + second, bok := installOrderIndex(kindB, beforeB, o) if !aok && !bok { // if both are unknown then sort alphabetically by kind, keep original order if same kind @@ -154,3 +149,28 @@ func lessByKind(a interface{}, b interface{}, kindA string, kindB string, o Kind // sort different kinds, keep original order if same priority return first < second } + +// installOrderIndex returns the lowest index number of all beforeKinds +func installOrderIndex(kind string, beforeKinds []string, o KindSortOrder) (int, bool) { + ordering := make(map[string]int, len(o)) + for v, k := range o { + ordering[k] = v + } + + orderIndex, foundIndex := ordering[kind] + + // reset orderIndex for unknown resources + if !foundIndex { + orderIndex = len(o) + } + + for _, kind := range beforeKinds { + i, ok := ordering[kind] + if ok && i < orderIndex { + foundIndex = true + // set orderIndex 1 BEFORE the actual index, so it get executed BEFORE it + orderIndex = i - 1 + } + } + return orderIndex, foundIndex +} diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index 0b04a4599..34dd16be0 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -23,6 +23,9 @@ import ( "strings" ) +// InstallOrderAnnotation the annotation that allows manipulating the install order of custom resources +const InstallOrderAnnotation = "helm.sh/install-before" + // SimpleHead defines what the structure of the head of a manifest file type SimpleHead struct { Version string `json:"apiVersion"` diff --git a/pkg/releaseutil/manifest_sorter.go b/pkg/releaseutil/manifest_sorter.go index e83414500..8f5f61263 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -32,9 +32,10 @@ import ( // Manifest represents a manifest file, which has a name and some content. type Manifest struct { - Name string - Content string - Head *SimpleHead + Name string + Content string + Head *SimpleHead + InstallBefore []string } // manifestFile represents a file that contains a manifest. @@ -155,6 +156,30 @@ func (file *manifestFile) sort(result *result) error { continue } + installBeforeKinds, ok := entry.Metadata.Annotations[InstallOrderAnnotation] + // InstallOrderAnnotation is only supported for unknown Kinds e.g. Custom Resources + if ok && isKnownKind(entry.Kind) { + log.Printf("info: %v annotation is not supported for Kind %v", InstallOrderAnnotation, entry.Kind) + } else if ok { + var installBefore []string + for _, kind := range strings.Split(installBeforeKinds, ",") { + kind = strings.TrimSpace(kind) + if isKnownKind(kind) { + installBefore = append(installBefore, kind) + } else { + log.Printf("info: skipping unknown install-before kind: %q", kind) + } + } + + result.generic = append(result.generic, Manifest{ + Name: file.path, + Content: m, + Head: &entry, + InstallBefore: installBefore, + }) + continue + } + hookTypes, ok := entry.Metadata.Annotations[release.HookAnnotation] if !ok { result.generic = append(result.generic, Manifest{ @@ -231,3 +256,15 @@ func operateAnnotationValues(entry SimpleHead, annotation string, operate func(p } } } + +// isKnownKind returns true if the given kind exists in the InstallOrder or UninstallOrder +func isKnownKind(kind string) bool { + knownKinds := append(InstallOrder, UninstallOrder...) + + for _, kk := range knownKinds { + if kk == kind { + return true + } + } + return false +}