...

Source file src/sigs.k8s.io/kustomize/api/internal/builtins/SortOrderTransformer.go

Documentation: sigs.k8s.io/kustomize/api/internal/builtins

     1  // Code generated by pluginator on SortOrderTransformer; DO NOT EDIT.
     2  // pluginator {(devel)  unknown   }
     3  
     4  package builtins
     5  
     6  import (
     7  	"sort"
     8  	"strings"
     9  
    10  	"sigs.k8s.io/kustomize/api/resmap"
    11  	"sigs.k8s.io/kustomize/api/resource"
    12  	"sigs.k8s.io/kustomize/api/types"
    13  	"sigs.k8s.io/kustomize/kyaml/errors"
    14  	"sigs.k8s.io/kustomize/kyaml/resid"
    15  	"sigs.k8s.io/yaml"
    16  )
    17  
    18  // Sort the resources using a customizable ordering based of Kind.
    19  // Defaults to the ordering of the GVK struct, which puts cluster-wide basic
    20  // resources with no dependencies (like Namespace, StorageClass, etc.) first,
    21  // and resources with a high number of dependencies
    22  // (like ValidatingWebhookConfiguration) last.
    23  type SortOrderTransformerPlugin struct {
    24  	SortOptions *types.SortOptions `json:"sortOptions,omitempty" yaml:"sortOptions,omitempty"`
    25  }
    26  
    27  func (p *SortOrderTransformerPlugin) Config(
    28  	_ *resmap.PluginHelpers, c []byte) error {
    29  	return errors.WrapPrefixf(yaml.Unmarshal(c, p), "Failed to unmarshal SortOrderTransformer config")
    30  }
    31  
    32  func (p *SortOrderTransformerPlugin) applyDefaults() {
    33  	// Default to FIFO sort, aka no-op.
    34  	if p.SortOptions == nil {
    35  		p.SortOptions = &types.SortOptions{
    36  			Order: types.FIFOSortOrder,
    37  		}
    38  	}
    39  
    40  	// If legacy sort is selected and no options are given, default to
    41  	// hardcoded order.
    42  	if p.SortOptions.Order == types.LegacySortOrder && p.SortOptions.LegacySortOptions == nil {
    43  		p.SortOptions.LegacySortOptions = &types.LegacySortOptions{
    44  			OrderFirst: defaultOrderFirst,
    45  			OrderLast:  defaultOrderLast,
    46  		}
    47  	}
    48  }
    49  
    50  func (p *SortOrderTransformerPlugin) validate() error {
    51  	// Check valid values for SortOrder
    52  	if p.SortOptions.Order != types.FIFOSortOrder && p.SortOptions.Order != types.LegacySortOrder {
    53  		return errors.Errorf("the field 'sortOptions.order' must be one of [%s, %s]",
    54  			types.FIFOSortOrder, types.LegacySortOrder)
    55  	}
    56  
    57  	// Validate that the only options set are the ones corresponding to the
    58  	// selected sort order.
    59  	if p.SortOptions.Order == types.FIFOSortOrder &&
    60  		p.SortOptions.LegacySortOptions != nil {
    61  		return errors.Errorf("the field 'sortOptions.legacySortOptions' is"+
    62  			" set but the selected sort order is '%v', not 'legacy'",
    63  			p.SortOptions.Order)
    64  	}
    65  	return nil
    66  }
    67  
    68  func (p *SortOrderTransformerPlugin) Transform(m resmap.ResMap) (err error) {
    69  	p.applyDefaults()
    70  	err = p.validate()
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// Sort
    76  	if p.SortOptions.Order == types.LegacySortOrder {
    77  		s := newLegacyIDSorter(m.AllIds(), p.SortOptions.LegacySortOptions)
    78  		sort.Sort(s)
    79  		err = applyOrdering(m, s.resids)
    80  		if err != nil {
    81  			return err
    82  		}
    83  	}
    84  	return nil
    85  }
    86  
    87  // applyOrdering takes resources (given in ResMap) and a desired ordering given
    88  // as a sequence of ResIds, and updates the ResMap's resources to match the
    89  // ordering.
    90  func applyOrdering(m resmap.ResMap, ordering []resid.ResId) error {
    91  	var err error
    92  	resources := make([]*resource.Resource, m.Size())
    93  	// Clear and refill with the correct order
    94  	for i, id := range ordering {
    95  		resources[i], err = m.GetByCurrentId(id)
    96  		if err != nil {
    97  			return errors.WrapPrefixf(err, "expected match for sorting")
    98  		}
    99  	}
   100  	m.Clear()
   101  	for _, r := range resources {
   102  		err = m.Append(r)
   103  		if err != nil {
   104  			return errors.WrapPrefixf(err, "SortOrderTransformer: Failed to append to resources")
   105  		}
   106  	}
   107  	return nil
   108  }
   109  
   110  // Code for legacy sorting.
   111  // Legacy sorting is a "fixed" order sorting maintained for backwards
   112  // compatibility.
   113  
   114  // legacyIDSorter sorts resources based on two priority lists:
   115  // - orderFirst: Resources that should be placed in the start, in the given order.
   116  // - orderLast: Resources that should be placed in the end, in the given order.
   117  type legacyIDSorter struct {
   118  	// resids only stores the metadata of the object. This is an optimization as
   119  	// it's expensive to compute these again and again during ordering.
   120  	resids     []resid.ResId
   121  	typeOrders map[string]int
   122  }
   123  
   124  func newLegacyIDSorter(
   125  	resids []resid.ResId,
   126  	options *types.LegacySortOptions) *legacyIDSorter {
   127  	// Precalculate a resource ranking based on the priority lists.
   128  	var typeOrders = func() map[string]int {
   129  		m := map[string]int{}
   130  		for i, n := range options.OrderFirst {
   131  			m[n] = -len(options.OrderFirst) + i
   132  		}
   133  		for i, n := range options.OrderLast {
   134  			m[n] = 1 + i
   135  		}
   136  		return m
   137  	}()
   138  	return &legacyIDSorter{
   139  		resids:     resids,
   140  		typeOrders: typeOrders,
   141  	}
   142  }
   143  
   144  var _ sort.Interface = legacyIDSorter{}
   145  
   146  func (a legacyIDSorter) Len() int { return len(a.resids) }
   147  func (a legacyIDSorter) Swap(i, j int) {
   148  	a.resids[i], a.resids[j] = a.resids[j], a.resids[i]
   149  }
   150  func (a legacyIDSorter) Less(i, j int) bool {
   151  	if !a.resids[i].Gvk.Equals(a.resids[j].Gvk) {
   152  		return gvkLessThan(a.resids[i].Gvk, a.resids[j].Gvk, a.typeOrders)
   153  	}
   154  	return legacyResIDSortString(a.resids[i]) < legacyResIDSortString(a.resids[j])
   155  }
   156  
   157  func gvkLessThan(gvk1, gvk2 resid.Gvk, typeOrders map[string]int) bool {
   158  	index1 := typeOrders[gvk1.Kind]
   159  	index2 := typeOrders[gvk2.Kind]
   160  	if index1 != index2 {
   161  		return index1 < index2
   162  	}
   163  	return legacyGVKSortString(gvk1) < legacyGVKSortString(gvk2)
   164  }
   165  
   166  // legacyGVKSortString returns a string representation of given GVK used for
   167  // stable sorting.
   168  func legacyGVKSortString(x resid.Gvk) string {
   169  	legacyNoGroup := "~G"
   170  	legacyNoVersion := "~V"
   171  	legacyNoKind := "~K"
   172  	legacyFieldSeparator := "_"
   173  
   174  	g := x.Group
   175  	if g == "" {
   176  		g = legacyNoGroup
   177  	}
   178  	v := x.Version
   179  	if v == "" {
   180  		v = legacyNoVersion
   181  	}
   182  	k := x.Kind
   183  	if k == "" {
   184  		k = legacyNoKind
   185  	}
   186  	return strings.Join([]string{g, v, k}, legacyFieldSeparator)
   187  }
   188  
   189  // legacyResIDSortString returns a string representation of given ResID used for
   190  // stable sorting.
   191  func legacyResIDSortString(id resid.ResId) string {
   192  	legacyNoNamespace := "~X"
   193  	legacyNoName := "~N"
   194  	legacySeparator := "|"
   195  
   196  	ns := id.Namespace
   197  	if ns == "" {
   198  		ns = legacyNoNamespace
   199  	}
   200  	nm := id.Name
   201  	if nm == "" {
   202  		nm = legacyNoName
   203  	}
   204  	return strings.Join(
   205  		[]string{id.Gvk.String(), ns, nm}, legacySeparator)
   206  }
   207  
   208  // DO NOT CHANGE!
   209  // Final legacy ordering provided as a default by kustomize.
   210  // Originally an attempt to apply resources in the correct order, an effort
   211  // which later proved impossible as not all types are known beforehand.
   212  // See: https://github.com/kubernetes-sigs/kustomize/issues/3913
   213  var defaultOrderFirst = []string{ //nolint:gochecknoglobals
   214  	"Namespace",
   215  	"ResourceQuota",
   216  	"StorageClass",
   217  	"CustomResourceDefinition",
   218  	"ServiceAccount",
   219  	"PodSecurityPolicy",
   220  	"Role",
   221  	"ClusterRole",
   222  	"RoleBinding",
   223  	"ClusterRoleBinding",
   224  	"ConfigMap",
   225  	"Secret",
   226  	"Endpoints",
   227  	"Service",
   228  	"LimitRange",
   229  	"PriorityClass",
   230  	"PersistentVolume",
   231  	"PersistentVolumeClaim",
   232  	"Deployment",
   233  	"StatefulSet",
   234  	"CronJob",
   235  	"PodDisruptionBudget",
   236  }
   237  var defaultOrderLast = []string{ //nolint:gochecknoglobals
   238  	"MutatingWebhookConfiguration",
   239  	"ValidatingWebhookConfiguration",
   240  }
   241  
   242  func NewSortOrderTransformerPlugin() resmap.TransformerPlugin {
   243  	return &SortOrderTransformerPlugin{}
   244  }
   245  

View as plain text