From fad478f608a62b0f572ab0679ebcbe165c9d8ebf Mon Sep 17 00:00:00 2001 From: Angus Lees <gus@inodes.org> Date: Thu, 18 May 2017 16:48:29 +1000 Subject: [PATCH] Attempt to topologically sort resources before updating The goal is to make a best-effort attempt at reducing the number of "crash-restart" loops required to bring up a group of interdependent resources. The current implementation is very simple and just sorts into 3 tiers: - Namespace, ThirdPartyResource, StorageClass - everything else - Pods or similar (Pod/Job/Deployment/DaemonSet/StatefulSet) --- cmd/update.go | 4 ++++ utils/sort.go | 59 ++++++++++++++++++++++++++++++++++++++++++++++ utils/sort_test.go | 34 ++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 utils/sort.go create mode 100644 utils/sort_test.go diff --git a/cmd/update.go b/cmd/update.go index 8ffc485d..655afe59 100644 --- a/cmd/update.go +++ b/cmd/update.go @@ -10,6 +10,8 @@ import ( "k8s.io/client-go/pkg/api/errors" "k8s.io/client-go/pkg/runtime" "k8s.io/client-go/pkg/util/diff" + + "github.com/ksonnet/kubecfg/utils" ) const ( @@ -47,6 +49,8 @@ var updateCmd = &cobra.Command{ return err } + utils.SortDepFirst(objs) + for _, obj := range objs { desc := fmt.Sprintf("%s/%s", obj.GetKind(), fqName(obj)) glog.Info("Updating ", desc) diff --git a/utils/sort.go b/utils/sort.go new file mode 100644 index 00000000..554f4c24 --- /dev/null +++ b/utils/sort.go @@ -0,0 +1,59 @@ +package utils + +import ( + "sort" + + "k8s.io/client-go/pkg/api/unversioned" + "k8s.io/client-go/pkg/runtime" +) + +var ( + gkNamespace = unversioned.GroupKind{Group: "", Kind: "Namespace"} + gkTpr = unversioned.GroupKind{Group: "extensions", Kind: "ThirdPartyResource"} + gkStorageClass = unversioned.GroupKind{Group: "storage.k8s.io", Kind: "StorageClass"} + + gkPod = unversioned.GroupKind{Group: "", Kind: "Pod"} + gkJob = unversioned.GroupKind{Group: "batch", Kind: "Job"} + gkDeployment = unversioned.GroupKind{Group: "extensions", Kind: "Deployment"} + gkDaemonSet = unversioned.GroupKind{Group: "extensions", Kind: "DaemonSet"} + gkStatefulSet = unversioned.GroupKind{Group: "apps", Kind: "StatefulSet"} +) + +// These kinds all start pods. +// TODO: expand this list. +func isPodOrSimilar(gk unversioned.GroupKind) bool { + return gk == gkPod || + gk == gkJob || + gk == gkDeployment || + gk == gkDaemonSet || + gk == gkStatefulSet +} + +// Arbitrary numbers used to do a simple topological sort of resources. +// TODO: expand this list. +func depTier(o *runtime.Unstructured) int { + gk := o.GroupVersionKind().GroupKind() + if gk == gkNamespace || gk == gkTpr || gk == gkStorageClass { + return 10 + } else if isPodOrSimilar(gk) { + return 100 + } else { + return 50 + } +} + +type dependentObjects []*runtime.Unstructured + +func (l dependentObjects) Len() int { return len(l) } +func (l dependentObjects) Swap(i, j int) { l[i], l[j] = l[j], l[i] } +func (l dependentObjects) Less(i, j int) bool { + return depTier(l[i]) < depTier(l[j]) +} + +// SortDepFirst *best-effort* sorts the objects so that known +// dependencies appear earlier in the list. The idea is to prevent +// *some* of the "crash-restart" loops when creating inter-dependent +// resources. +func SortDepFirst(objs []*runtime.Unstructured) { + sort.Sort(dependentObjects(objs)) +} diff --git a/utils/sort_test.go b/utils/sort_test.go new file mode 100644 index 00000000..c579badc --- /dev/null +++ b/utils/sort_test.go @@ -0,0 +1,34 @@ +package utils + +import ( + "testing" + + "k8s.io/client-go/pkg/runtime" +) + +func newObj(apiVersion, kind string) *runtime.Unstructured { + return &runtime.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": apiVersion, + "kind": kind, + }, + } +} + +func TestSort(t *testing.T) { + objs := []*runtime.Unstructured{ + newObj("extensions/v1beta1", "Deployment"), + newObj("v1", "ConfigMap"), + newObj("v1", "Namespace"), + newObj("v1", "Service"), + } + + SortDepFirst(objs) + + if objs[0].GetKind() != "Namespace" { + t.Error("Namespace should be sorted first") + } + if objs[3].GetKind() != "Deployment" { + t.Error("Deployment should be sorted after other objects") + } +} -- GitLab