// Code generated by pluginator on SortOrderTransformer; DO NOT EDIT. // pluginator {(devel) unknown } package builtins import ( "sort" "strings" "sigs.k8s.io/kustomize/api/resmap" "sigs.k8s.io/kustomize/api/resource" "sigs.k8s.io/kustomize/api/types" "sigs.k8s.io/kustomize/kyaml/errors" "sigs.k8s.io/kustomize/kyaml/resid" "sigs.k8s.io/yaml" ) // Sort the resources using a customizable ordering based of Kind. // Defaults to the ordering of the GVK struct, which puts cluster-wide basic // resources with no dependencies (like Namespace, StorageClass, etc.) first, // and resources with a high number of dependencies // (like ValidatingWebhookConfiguration) last. type SortOrderTransformerPlugin struct { SortOptions *types.SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"` } func (p *SortOrderTransformerPlugin) Config( _ *resmap.PluginHelpers, c []byte) error { return errors.WrapPrefixf(yaml.Unmarshal(c, p), "Failed to unmarshal SortOrderTransformer config") } func (p *SortOrderTransformerPlugin) applyDefaults() { // Default to FIFO sort, aka no-op. if p.SortOptions == nil { p.SortOptions = &types.SortOptions{ Order: types.FIFOSortOrder, } } // If legacy sort is selected and no options are given, default to // hardcoded order. if p.SortOptions.Order == types.LegacySortOrder && p.SortOptions.LegacySortOptions == nil { p.SortOptions.LegacySortOptions = &types.LegacySortOptions{ OrderFirst: defaultOrderFirst, OrderLast: defaultOrderLast, } } } func (p *SortOrderTransformerPlugin) validate() error { // Check valid values for SortOrder if p.SortOptions.Order != types.FIFOSortOrder && p.SortOptions.Order != types.LegacySortOrder { return errors.Errorf("the field 'sortOptions.order' must be one of [%s, %s]", types.FIFOSortOrder, types.LegacySortOrder) } // Validate that the only options set are the ones corresponding to the // selected sort order. if p.SortOptions.Order == types.FIFOSortOrder && p.SortOptions.LegacySortOptions != nil { return errors.Errorf("the field 'sortOptions.legacySortOptions' is"+ " set but the selected sort order is '%v', not 'legacy'", p.SortOptions.Order) } return nil } func (p *SortOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) { p.applyDefaults() err = p.validate() if err != nil { return err } // Sort if p.SortOptions.Order == types.LegacySortOrder { s := newLegacyIDSorter(m.AllIds(), p.SortOptions.LegacySortOptions) sort.Sort(s) err = applyOrdering(m, s.resids) if err != nil { return err } } return nil } // applyOrdering takes resources (given in ResMap) and a desired ordering given // as a sequence of ResIds, and updates the ResMap's resources to match the // ordering. func applyOrdering(m resmap.ResMap, ordering []resid.ResId) error { var err error resources := make([]*resource.Resource, m.Size()) // Clear and refill with the correct order for i, id := range ordering { resources[i], err = m.GetByCurrentId(id) if err != nil { return errors.WrapPrefixf(err, "expected match for sorting") } } m.Clear() for _, r := range resources { err = m.Append(r) if err != nil { return errors.WrapPrefixf(err, "SortOrderTransformer: Failed to append to resources") } } return nil } // Code for legacy sorting. // Legacy sorting is a "fixed" order sorting maintained for backwards // compatibility. // legacyIDSorter sorts resources based on two priority lists: // - orderFirst: Resources that should be placed in the start, in the given order. // - orderLast: Resources that should be placed in the end, in the given order. type legacyIDSorter struct { // resids only stores the metadata of the object. This is an optimization as // it's expensive to compute these again and again during ordering. resids []resid.ResId typeOrders map[string]int } func newLegacyIDSorter( resids []resid.ResId, options *types.LegacySortOptions) *legacyIDSorter { // Precalculate a resource ranking based on the priority lists. var typeOrders = func() map[string]int { m := map[string]int{} for i, n := range options.OrderFirst { m[n] = -len(options.OrderFirst) + i } for i, n := range options.OrderLast { m[n] = 1 + i } return m }() return &legacyIDSorter{ resids: resids, typeOrders: typeOrders, } } var _ sort.Interface = legacyIDSorter{} func (a legacyIDSorter) Len() int { return len(a.resids) } func (a legacyIDSorter) Swap(i, j int) { a.resids[i], a.resids[j] = a.resids[j], a.resids[i] } func (a legacyIDSorter) Less(i, j int) bool { if !a.resids[i].Gvk.Equals(a.resids[j].Gvk) { return gvkLessThan(a.resids[i].Gvk, a.resids[j].Gvk, a.typeOrders) } return legacyResIDSortString(a.resids[i]) < legacyResIDSortString(a.resids[j]) } func gvkLessThan(gvk1, gvk2 resid.Gvk, typeOrders map[string]int) bool { index1 := typeOrders[gvk1.Kind] index2 := typeOrders[gvk2.Kind] if index1 != index2 { return index1 < index2 } return legacyGVKSortString(gvk1) < legacyGVKSortString(gvk2) } // legacyGVKSortString returns a string representation of given GVK used for // stable sorting. func legacyGVKSortString(x resid.Gvk) string { legacyNoGroup := "~G" legacyNoVersion := "~V" legacyNoKind := "~K" legacyFieldSeparator := "_" g := x.Group if g == "" { g = legacyNoGroup } v := x.Version if v == "" { v = legacyNoVersion } k := x.Kind if k == "" { k = legacyNoKind } return strings.Join([]string{g, v, k}, legacyFieldSeparator) } // legacyResIDSortString returns a string representation of given ResID used for // stable sorting. func legacyResIDSortString(id resid.ResId) string { legacyNoNamespace := "~X" legacyNoName := "~N" legacySeparator := "|" ns := id.Namespace if ns == "" { ns = legacyNoNamespace } nm := id.Name if nm == "" { nm = legacyNoName } return strings.Join( []string{id.Gvk.String(), ns, nm}, legacySeparator) } // DO NOT CHANGE! // Final legacy ordering provided as a default by kustomize. // Originally an attempt to apply resources in the correct order, an effort // which later proved impossible as not all types are known beforehand. // See: https://github.com/kubernetes-sigs/kustomize/issues/3913 var defaultOrderFirst = []string{ //nolint:gochecknoglobals "Namespace", "ResourceQuota", "StorageClass", "CustomResourceDefinition", "ServiceAccount", "PodSecurityPolicy", "Role", "ClusterRole", "RoleBinding", "ClusterRoleBinding", "ConfigMap", "Secret", "Endpoints", "Service", "LimitRange", "PriorityClass", "PersistentVolume", "PersistentVolumeClaim", "Deployment", "StatefulSet", "CronJob", "PodDisruptionBudget", } var defaultOrderLast = []string{ //nolint:gochecknoglobals "MutatingWebhookConfiguration", "ValidatingWebhookConfiguration", } func NewSortOrderTransformerPlugin() resmap.TransformerPlugin { return &SortOrderTransformerPlugin{} }