package object import ( "slices" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/cli-utils/pkg/object" "sigs.k8s.io/controller-runtime/pkg/client" ) type kindOrder struct { First []string Last []string } // reconcileOrder holds the list of Kubernetes native kinds based on the order // they are reconciled in, for coarse dependency ordering on apply. var reconcileOrder = kindOrder{ First: []string{ "CustomResourceDefinition", "Namespace", "ResourceQuota", "StorageClass", "ServiceAccount", "PodSecurityPolicy", "Role", "ClusterRole", "RoleBinding", "ClusterRoleBinding", "ConfigMap", "Secret", "Service", "LimitRange", "PriorityClass", "Deployment", "StatefulSet", "CronJob", "PodDisruptionBudget", }, Last: []string{ "MutatingWebhookConfiguration", "ValidatingWebhookConfiguration", }, } // Sort does an in-place sort of objects. func Sort[O client.Object](objs []O) { slices.SortFunc(objs, LessThan) } // Sort does an in-place sort of object metadata. func SortObjMeta(objMetas object.ObjMetadataSet) { slices.SortFunc(objMetas, lessThan) } // LessThan returns true if i < j. func LessThan[O client.Object](i, j O) int { return lessThan( object.ObjMetadata{ GroupKind: i.GetObjectKind().GroupVersionKind().GroupKind(), Namespace: i.GetNamespace(), Name: i.GetName(), }, object.ObjMetadata{ GroupKind: j.GetObjectKind().GroupVersionKind().GroupKind(), Namespace: j.GetNamespace(), Name: j.GetName(), }, ) } func lessThan(i, j object.ObjMetadata) int { if !equals(i.GroupKind, j.GroupKind) { cmp, resultFound := compareGroupKind(i.GroupKind, j.GroupKind) if resultFound { return cmp } } // If we tied, compare Namespace + Name for deterministic output if i.Namespace != j.Namespace { if i.Namespace < j.Namespace { return -1 } return 1 } if i.Name < j.Name { return -1 } if i.Name > j.Name { return 1 } return 0 } func compareGroupKind(i, j schema.GroupKind) (int, bool) { idxI, idxJ := getIndexByKind(i.Kind), getIndexByKind(j.Kind) if idxI != idxJ { return idxI - idxJ, true } if i.Group != j.Group { if i.Group < j.Group { return -1, true } return 1, true } if i.Kind < j.Kind { return -1, true } if i.Kind > j.Kind { return 1, true } return 0, false // need to do more checks to determine which is less than } func equals(i, j schema.GroupKind) bool { return i.Group == j.Group && i.Kind == j.Kind } func computeKind2index() map[string]int { // Based on cli-utils/pkg/ordering/sort.go // An attempt to order things to help k8s, e.g. a Service should come before // things that refer to it, Namespace should be first. // In some cases order is specified to provide determinism. kind2indexResult := make(map[string]int, len(reconcileOrder.First)+len(reconcileOrder.Last)) for i, n := range reconcileOrder.First { kind2indexResult[n] = -len(reconcileOrder.First) + i } for i, n := range reconcileOrder.Last { kind2indexResult[n] = 1 + i } return kind2indexResult } // getIndexByKind returns the index of the kind respecting the order func getIndexByKind(kind string) int { return computeKind2index()[kind] }