diff --git a/pkg/action/resource_policy.go b/pkg/action/resource_policy.go index cfabdf7ba..494996964 100644 --- a/pkg/action/resource_policy.go +++ b/pkg/action/resource_policy.go @@ -31,7 +31,7 @@ const resourcePolicyAnno = "helm.sh/resource-policy" // during an uninstallRelease action. const keepPolicy = "keep" -func filterManifestsToKeep(manifests []releaseutil.Manifest) (keep, remaining []releaseutil.Manifest) { +func filterManifestsToKeep(manifests []*releaseutil.Manifest) (keep, remaining []*releaseutil.Manifest) { for _, m := range manifests { if m.Head.Metadata == nil || m.Head.Metadata.Annotations == nil || len(m.Head.Metadata.Annotations) == 0 { remaining = append(remaining, m) diff --git a/pkg/releaseutil/kind_sorter.go b/pkg/releaseutil/kind_sorter.go index a340dfc29..8e9eac65a 100644 --- a/pkg/releaseutil/kind_sorter.go +++ b/pkg/releaseutil/kind_sorter.go @@ -108,12 +108,13 @@ 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(manifests []*Manifest, ordering KindSortOrder) []*Manifest { + m := manifests + sort.SliceStable(m, func(i, j int) bool { + return lessByKind(m[i], m[j], m[i].Head.Kind, m[j].Head.Kind, ordering) }) - return manifests + return m } // sort hooks by kind, using an out-of-place sort to preserve the input parameters. diff --git a/pkg/releaseutil/kind_sorter_test.go b/pkg/releaseutil/kind_sorter_test.go index 71d355210..18a29a49a 100644 --- a/pkg/releaseutil/kind_sorter_test.go +++ b/pkg/releaseutil/kind_sorter_test.go @@ -24,7 +24,7 @@ import ( ) func TestKindSorter(t *testing.T) { - manifests := []Manifest{ + manifests := []*Manifest{ { Name: "E", Head: &SimpleHead{Kind: "SecretList"}, @@ -199,7 +199,7 @@ func TestKindSorter(t *testing.T) { // TestKindSorterKeepOriginalOrder verifies manifests of same kind are kept in original order func TestKindSorterKeepOriginalOrder(t *testing.T) { - manifests := []Manifest{ + manifests := []*Manifest{ { Name: "a", Head: &SimpleHead{Kind: "ClusterRole"}, @@ -259,19 +259,19 @@ func TestKindSorterKeepOriginalOrder(t *testing.T) { } func TestKindSorterNamespaceAgainstUnknown(t *testing.T) { - unknown := Manifest{ + unknown := &Manifest{ Name: "a", Head: &SimpleHead{Kind: "Unknown"}, } - namespace := Manifest{ + namespace := &Manifest{ Name: "b", Head: &SimpleHead{Kind: "Namespace"}, } - manifests := []Manifest{unknown, namespace} + manifests := []*Manifest{unknown, namespace} manifests = sortManifestsByKind(manifests, InstallOrder) - expectedOrder := []Manifest{namespace, unknown} + expectedOrder := []*Manifest{namespace, unknown} for i, manifest := range manifests { if expectedOrder[i].Name != manifest.Name { t.Errorf("Expected %s, got %s", expectedOrder[i].Name, manifest.Name) diff --git a/pkg/releaseutil/manifest.go b/pkg/releaseutil/manifest.go index 0b04a4599..36cc4c2f6 100644 --- a/pkg/releaseutil/manifest.go +++ b/pkg/releaseutil/manifest.go @@ -23,6 +23,9 @@ import ( "strings" ) +// OrderWeightAnnotation is the label name for a weight of a manifest +const OrderWeightAnnotation = "helm.sh/order-weight" + // 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..71513d073 100644 --- a/pkg/releaseutil/manifest_sorter.go +++ b/pkg/releaseutil/manifest_sorter.go @@ -34,6 +34,7 @@ import ( type Manifest struct { Name string Content string + Weight int Head *SimpleHead } @@ -47,7 +48,7 @@ type manifestFile struct { // result is an intermediate structure used during sorting. type result struct { hooks []*release.Hook - generic []Manifest + generic []*Manifest } // TODO: Refactor this out. It's here because naming conventions were not followed through. @@ -75,7 +76,7 @@ var events = map[string]release.HookEvent{ // // Files that do not parse into the expected format are simply placed into a map and // returned. -func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering KindSortOrder) ([]*release.Hook, []Manifest, error) { +func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering KindSortOrder) ([]*release.Hook, []*Manifest, error) { result := &result{} var sortedFilePaths []string @@ -107,8 +108,11 @@ func SortManifests(files map[string]string, apis chartutil.VersionSet, ordering return result.hooks, result.generic, err } } + sortedManifests := sortManifestsByKind(result.generic, ordering) + // manifests are ordered by kind, so keep order stable + sort.Stable(manifestByWeight(sortedManifests)) - return sortHooksByKind(result.hooks, ordering), sortManifestsByKind(result.generic, ordering), nil + return sortHooksByKind(result.hooks, ordering), sortedManifests, nil } // sort takes a manifestFile object which may contain multiple resource definition @@ -147,7 +151,7 @@ func (file *manifestFile) sort(result *result) error { } if !hasAnyAnnotation(entry) { - result.generic = append(result.generic, Manifest{ + result.generic = append(result.generic, &Manifest{ Name: file.path, Content: m, Head: &entry, @@ -157,9 +161,11 @@ func (file *manifestFile) sort(result *result) error { hookTypes, ok := entry.Metadata.Annotations[release.HookAnnotation] if !ok { - result.generic = append(result.generic, Manifest{ + ow := calculateOrderWeight(entry) + result.generic = append(result.generic, &Manifest{ Name: file.path, Content: m, + Weight: ow, Head: &entry, }) continue @@ -222,6 +228,18 @@ func calculateHookWeight(entry SimpleHead) int { return hw } +// calculateOrderWeight finds the weight in the hook weight annotation. +// +// If no weight is found, the assigned weight is 0 +func calculateOrderWeight(entry SimpleHead) int { + ows := entry.Metadata.Annotations[OrderWeightAnnotation] + ow, err := strconv.Atoi(ows) + if err != nil { + ow = 0 + } + return ow +} + // operateAnnotationValues finds the given annotation and runs the operate function with the value of that annotation func operateAnnotationValues(entry SimpleHead, annotation string, operate func(p string)) { if dps, ok := entry.Metadata.Annotations[annotation]; ok { @@ -231,3 +249,12 @@ func operateAnnotationValues(entry SimpleHead, annotation string, operate func(p } } } + +// manifestByWeight is a sorter for manifests +type manifestByWeight []*Manifest + +func (x manifestByWeight) Len() int { return len(x) } +func (x manifestByWeight) Swap(i, j int) { x[i], x[j] = x[j], x[i] } +func (x manifestByWeight) Less(i, j int) bool { + return x[i].Weight < x[j].Weight +} diff --git a/pkg/releaseutil/manifest_sorter_test.go b/pkg/releaseutil/manifest_sorter_test.go index 20d809317..2aed18615 100644 --- a/pkg/releaseutil/manifest_sorter_test.go +++ b/pkg/releaseutil/manifest_sorter_test.go @@ -193,7 +193,7 @@ metadata: } // Verify the sort order - sorted := []Manifest{} + sorted := []*Manifest{} for _, s := range data { manifests := SplitManifests(s.manifest) @@ -209,7 +209,7 @@ metadata: // only keep track of non-hook manifests if s.hooks[name] == nil { - another := Manifest{ + another := &Manifest{ Content: m, Name: name, Head: &sh,