From 7975c64b890c91f8596a84148a331bf225eaa11d Mon Sep 17 00:00:00 2001 From: cedric lamoriniere Date: Sat, 27 Mar 2021 22:05:02 +0100 Subject: [PATCH] Implements NodeInfoProcessor Builder function The `nodeinfos.builder` package contains: * the `Register()` function use to register `nodeinfos.NodeInfoProcessor` implementation. * the `Build()` function use to instanciate the requested `nodeinfos.NodeInfoProcessor` implementation from `AutoscalerOptions`. --- .../config/autoscaling_options.go | 4 +- cluster-autoscaler/main.go | 18 ++++--- .../processors/nodeinfos/builder/build.go | 51 +++++++++++++++++++ .../node_info_with_podtemplates_processors.go | 12 ++++- ..._info_with_podtemplates_processors_test.go | 11 +++- 5 files changed, 84 insertions(+), 12 deletions(-) create mode 100644 cluster-autoscaler/processors/nodeinfos/builder/build.go diff --git a/cluster-autoscaler/config/autoscaling_options.go b/cluster-autoscaler/config/autoscaling_options.go index 5ecc2fa8774..021dc5e6351 100644 --- a/cluster-autoscaler/config/autoscaling_options.go +++ b/cluster-autoscaler/config/autoscaling_options.go @@ -161,6 +161,6 @@ type AutoscalingOptions struct { CordonNodeBeforeTerminate bool // DaemonSetEvictionForEmptyNodes is whether CA will gracefully terminate DaemonSet pods from empty nodes. DaemonSetEvictionForEmptyNodes bool - // ExtraDaemonsetsFromPodTemplates enable ExtraDaemonset Processor to consider specific PodTemplate as DaemonSet. - ExtraDaemonsetsFromPodTemplates bool + // NodeInfoProcessorName sets the type of the NodeInfo processor. Allowed value: podtemplates. + NodeInfoProcessorName string } diff --git a/cluster-autoscaler/main.go b/cluster-autoscaler/main.go index f52e1e564ce..39854de573e 100644 --- a/cluster-autoscaler/main.go +++ b/cluster-autoscaler/main.go @@ -43,7 +43,8 @@ import ( "k8s.io/autoscaler/cluster-autoscaler/metrics" ca_processors "k8s.io/autoscaler/cluster-autoscaler/processors" "k8s.io/autoscaler/cluster-autoscaler/processors/nodegroupset" - "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos/podtemplates" + nodeinfobuilder "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos/builder" + _ "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos/podtemplates" "k8s.io/autoscaler/cluster-autoscaler/simulator" "k8s.io/autoscaler/cluster-autoscaler/utils/errors" kube_util "k8s.io/autoscaler/cluster-autoscaler/utils/kubernetes" @@ -179,7 +180,7 @@ var ( clusterAPICloudConfigAuthoritative = flag.Bool("clusterapi-cloud-config-authoritative", false, "Treat the cloud-config flag authoritatively (do not fallback to using kubeconfig flag). ClusterAPI only") cordonNodeBeforeTerminate = flag.Bool("cordon-node-before-terminating", false, "Should CA cordon nodes before terminating during downscale process") daemonSetEvictionForEmptyNodes = flag.Bool("daemonset-eviction-for-empty-nodes", false, "DaemonSet pods will be gracefully terminated from empty nodes") - extraDaemonsetsFromPodTemplates = flag.Bool("extra-daemonsets-from-pod-templates", false, "Enable ExtraDaemonset Processor to consider specific PodTemplate as DaemonSet") + nodeInfoProcessorName = flag.String("node-info-processor", "", "node-info processor type. Available values: ["+strings.Join(nodeinfobuilder.GetAvailableNodeInfoProcessors(), ",")+"]") ) func createAutoscalingOptions() config.AutoscalingOptions { @@ -255,7 +256,7 @@ func createAutoscalingOptions() config.AutoscalingOptions { ClusterAPICloudConfigAuthoritative: *clusterAPICloudConfigAuthoritative, CordonNodeBeforeTerminate: *cordonNodeBeforeTerminate, DaemonSetEvictionForEmptyNodes: *daemonSetEvictionForEmptyNodes, - ExtraDaemonsetsFromPodTemplates: *extraDaemonsetsFromPodTemplates, + NodeInfoProcessorName: *nodeInfoProcessorName, } } @@ -330,9 +331,14 @@ func buildAutoscaler() (core.Autoscaler, error) { Comparator: nodeInfoComparatorBuilder(autoscalingOptions.BalancingExtraIgnoredLabels), } - // Enable PodTemplateListProcessor if needed - if autoscalingOptions.ExtraDaemonsetsFromPodTemplates { - opts.Processors.NodeInfoProcessor = podtemplates.NewNodeInfoWithPodTemplateProcessor(kubeClient) + // Enable a NodeInfoProcessorName if requested. + if autoscalingOptions.NodeInfoProcessorName != "" { + processor, err := nodeinfobuilder.Build(&opts) + if err != nil { + klog.Errorf("Unable to instanciate the requested NodeInfoProcessor, err: %w", err) + } else { + opts.Processors.NodeInfoProcessor = processor + } } // These metrics should be published only once. diff --git a/cluster-autoscaler/processors/nodeinfos/builder/build.go b/cluster-autoscaler/processors/nodeinfos/builder/build.go new file mode 100644 index 00000000000..4559e8a79d9 --- /dev/null +++ b/cluster-autoscaler/processors/nodeinfos/builder/build.go @@ -0,0 +1,51 @@ +/* +Copyright 2020 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package builder + +import ( + "fmt" + + "k8s.io/autoscaler/cluster-autoscaler/core" + "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos" +) + +var availableNodeInfoProcessors = map[string]BuilderFunc{} + +// Build returns a new nodeinfos.NodeInfoProcessor instance with the requested implementation +func Build(opts *core.AutoscalerOptions) (nodeinfos.NodeInfoProcessor, error) { + if buildFunc, found := availableNodeInfoProcessors[opts.AutoscalingOptions.NodeInfoProcessorName]; found { + return buildFunc(opts), nil + } + return nodeinfos.NewDefaultNodeInfoProcessor(), fmt.Errorf("NodeInfoProcessor %s not found", opts.AutoscalingOptions.NodeInfoProcessorName) +} + +// BuilderFunc corresponds to nodeinfos.NodeInfoProcessor +type BuilderFunc func(opts *core.AutoscalerOptions) nodeinfos.NodeInfoProcessor + +// Register used to register a nodeinfos.NodeInfoProcessor implementation. +func Register(name string, builderFunc BuilderFunc) { + availableNodeInfoProcessors[name] = builderFunc +} + +// GetAvailableNodeInfoProcessors returns the list of registered NodeInfoProcessor implementation. +func GetAvailableNodeInfoProcessors() []string { + output := make([]string, 0, len(availableNodeInfoProcessors)) + for key := range availableNodeInfoProcessors { + output = append(output, key) + } + return output +} diff --git a/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors.go b/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors.go index 91a0a923e3c..da864cebf8c 100644 --- a/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors.go +++ b/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors.go @@ -33,7 +33,9 @@ import ( "k8s.io/client-go/tools/cache" ca_context "k8s.io/autoscaler/cluster-autoscaler/context" + "k8s.io/autoscaler/cluster-autoscaler/core" "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos" + "k8s.io/autoscaler/cluster-autoscaler/processors/nodeinfos/builder" "k8s.io/autoscaler/cluster-autoscaler/simulator" "k8s.io/autoscaler/cluster-autoscaler/utils/errors" schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" @@ -44,16 +46,22 @@ const ( PodTemplateDaemonSetLabelKey = "cluster-autoscaler.kubernetes.io/daemonset-pod" // PodTemplateDaemonSetLabelValueTrue use as PodTemplateDaemonSetLabelKey label value. PodTemplateDaemonSetLabelValueTrue = "true" + // processorsName use to identify the PodTemplate NodeInfoProcessor + processorName = "podtemplates" ) +func init() { + builder.Register(processorName, NewNodeInfoWithPodTemplateProcessor) +} + // NewNodeInfoWithPodTemplateProcessor returns a default instance of NodeInfoProcessor. -func NewNodeInfoWithPodTemplateProcessor(kubeClient client.Interface) nodeinfos.NodeInfoProcessor { +func NewNodeInfoWithPodTemplateProcessor(opts *core.AutoscalerOptions) nodeinfos.NodeInfoProcessor { internalContext, cancelFunc := context.WithCancel(context.Background()) return &nodeInfoWithPodTemplateProcessor{ ctx: internalContext, cancelFunc: cancelFunc, - podTemplateLister: newPodTemplateLister(kubeClient, internalContext.Done()), + podTemplateLister: newPodTemplateLister(opts.KubeClient, internalContext.Done()), } } diff --git a/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors_test.go b/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors_test.go index 6fc758e9edd..792cc0f9467 100644 --- a/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors_test.go +++ b/cluster-autoscaler/processors/nodeinfos/podtemplates/node_info_with_podtemplates_processors_test.go @@ -38,12 +38,13 @@ import ( schedulerframework "k8s.io/kubernetes/pkg/scheduler/framework" ca_context "k8s.io/autoscaler/cluster-autoscaler/context" + "k8s.io/autoscaler/cluster-autoscaler/core" "k8s.io/autoscaler/cluster-autoscaler/simulator" ) func TestNewNodeInfoWithPodTemplateProcessor(t *testing.T) { - client := fake.NewSimpleClientset() - processor := NewNodeInfoWithPodTemplateProcessor(client) + opts := newtTestAutoscalerOptions() + processor := NewNodeInfoWithPodTemplateProcessor(opts) defer processor.CleanUp() } @@ -204,6 +205,12 @@ func Test_nodeInfoWithPodTemplateProcessor_Process(t *testing.T) { } } +func newtTestAutoscalerOptions() *core.AutoscalerOptions { + return &core.AutoscalerOptions{ + KubeClient: fake.NewSimpleClientset(), + } +} + func newTestDaemonSetLister(pts []*apiv1.PodTemplate) (v1lister.PodTemplateLister, error) { store := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) for _, pt := range pts {