mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-05-17 18:25:26 +00:00
This shaves of 14 seconds (one third) of the execution time for a
kustomization tree with 4000 documents, from 40.68s to 27.41s
0 0% 5.44% 18.42s 40.56% sigs.k8s.io/kustomize/api/krusty.(*Kustomizer).applySortOrder
0 0% 5.44% 18.40s 40.52% sigs.k8s.io/kustomize/api/internal/builtins.applyOrdering
before
(pprof) top20 -cum
Showing nodes accounting for 5.85s, 12.88% of 45.41s total
Dropped 622 nodes (cum <= 0.23s)
Showing top 20 nodes out of 157
flat flat% sum% cum cum%
0 0% 0% 40.68s 89.58% github.com/spf13/cobra.(*Command).Execute
0 0% 0% 40.68s 89.58% github.com/spf13/cobra.(*Command).ExecuteC
0 0% 0% 40.68s 89.58% github.com/spf13/cobra.(*Command).execute
0 0% 0% 40.68s 89.58% main.main
0 0% 0% 40.68s 89.58% runtime.main
0 0% 0% 40.68s 89.58% sigs.k8s.io/kustomize/kustomize/v5/commands/build.NewCmdBuild.func1
0 0% 0% 40.12s 88.35% sigs.k8s.io/kustomize/api/krusty.(*Kustomizer).Run
0.51s 1.12% 1.12% 33.20s 73.11% sigs.k8s.io/kustomize/api/resource.(*Resource).CurId
0 0% 1.12% 26.95s 59.35% sigs.k8s.io/kustomize/api/resmap.(*resWrangler).GetMatchingResourcesByCurrentId
0.35s 0.77% 1.89% 26.95s 59.35% sigs.k8s.io/kustomize/api/resmap.(*resWrangler).filteredById
0.07s 0.15% 2.05% 25.53s 56.22% sigs.k8s.io/kustomize/api/resmap.GetCurrentId
0 0% 2.05% 21.68s 47.74% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).MakeCustomizedResMap (inline)
0 0% 2.05% 21.68s 47.74% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).makeCustomizedResMap
0.54s 1.19% 3.24% 19.75s 43.49% sigs.k8s.io/kustomize/api/resource.(*Resource).GetGvk (inline)
1s 2.20% 5.44% 19.21s 42.30% sigs.k8s.io/kustomize/kyaml/resid.GvkFromNode
0 0% 5.44% 18.42s 40.56% sigs.k8s.io/kustomize/api/internal/builtins.(*SortOrderTransformerPlugin).Transform
0 0% 5.44% 18.42s 40.56% sigs.k8s.io/kustomize/api/krusty.(*Kustomizer).applySortOrder
0 0% 5.44% 18.40s 40.52% sigs.k8s.io/kustomize/api/internal/builtins.applyOrdering
0.87s 1.92% 7.36% 16.55s 36.45% sigs.k8s.io/kustomize/kyaml/yaml.visitMappingNodeFields
2.51s 5.53% 12.88% 15.68s 34.53% sigs.k8s.io/kustomize/kyaml/yaml.visitFieldsWhileTrue
after
(pprof) top20 -cum
Showing nodes accounting for 1.23s, 3.85% of 31.98s total
Dropped 584 nodes (cum <= 0.16s)
Showing top 20 nodes out of 184
flat flat% sum% cum cum%
0 0% 0% 27.41s 85.71% github.com/spf13/cobra.(*Command).Execute
0 0% 0% 27.41s 85.71% github.com/spf13/cobra.(*Command).ExecuteC
0 0% 0% 27.41s 85.71% github.com/spf13/cobra.(*Command).execute
0 0% 0% 27.41s 85.71% main.main
0 0% 0% 27.41s 85.71% runtime.main
0 0% 0% 27.41s 85.71% sigs.k8s.io/kustomize/kustomize/v5/commands/build.NewCmdBuild.func1
0 0% 0% 26.85s 83.96% sigs.k8s.io/kustomize/api/krusty.(*Kustomizer).Run
0 0% 0% 22.07s 69.01% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).MakeCustomizedResMap (inline)
0 0% 0% 22.07s 69.01% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).makeCustomizedResMap
0.38s 1.19% 1.19% 20.69s 64.70% sigs.k8s.io/kustomize/api/resource.(*Resource).CurId
0 0% 1.19% 13.64s 42.65% sigs.k8s.io/kustomize/api/resmap.(*resWrangler).Append
0 0% 1.19% 13.55s 42.37% sigs.k8s.io/kustomize/api/resmap.(*resWrangler).GetMatchingResourcesByCurrentId (inline)
0.12s 0.38% 1.56% 13.55s 42.37% sigs.k8s.io/kustomize/api/resmap.(*resWrangler).filteredById
0.01s 0.031% 1.59% 12.67s 39.62% sigs.k8s.io/kustomize/api/resmap.GetCurrentId
0.21s 0.66% 2.25% 12.49s 39.06% sigs.k8s.io/kustomize/api/resource.(*Resource).GetGvk (inline)
0.51s 1.59% 3.85% 12.28s 38.40% sigs.k8s.io/kustomize/kyaml/resid.GvkFromNode
0 0% 3.85% 11.52s 36.02% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).IgnoreLocal
0 0% 3.85% 10.53s 32.93% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).AccumulateTarget
0 0% 3.85% 10.53s 32.93% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).accumulateResources
0 0% 3.85% 10.53s 32.93% sigs.k8s.io/kustomize/api/internal/target.(*KustTarget).accumulateTarget
236 lines
6.7 KiB
Go
236 lines
6.7 KiB
Go
// 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.Resources(), p.SortOptions.LegacySortOptions)
|
|
sort.Sort(s)
|
|
|
|
// Clear the map and re-add the resources in the sorted order.
|
|
m.Clear()
|
|
for _, r := range s.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
|
|
// Initially, we sorted the metadata (ResId) of each object and then called GetByCurrentId on each to construct the final list.
|
|
// The problem is that GetByCurrentId is inefficient and does a linear scan in a list every time we do that.
|
|
// So instead, we sort resources alongside the ResIds.
|
|
resources []*resource.Resource
|
|
|
|
typeOrders map[string]int
|
|
}
|
|
|
|
func newLegacyIDSorter(
|
|
resources []*resource.Resource,
|
|
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
|
|
}()
|
|
|
|
ret := &legacyIDSorter{typeOrders: typeOrders}
|
|
for _, res := range resources {
|
|
ret.resids = append(ret.resids, res.CurId())
|
|
ret.resources = append(ret.resources, res)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
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]
|
|
a.resources[i], a.resources[j] = a.resources[j], a.resources[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{}
|
|
}
|