mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 18:43:34 +00:00
Changes to accommodate client-go changes and kube vendor update
to v1.18.0 Signed-off-by: Humble Chirammal <hchiramm@redhat.com>
This commit is contained in:
committed by
mergify[bot]
parent
4c96ad3c85
commit
34fc1d847e
54
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/BUILD
generated
vendored
Normal file
54
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/BUILD
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"node_affinity.go",
|
||||
"normalize_score.go",
|
||||
"spread.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/fields:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/listers/apps/v1:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/listers/core/v1:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"node_affinity_test.go",
|
||||
"normalize_score_test.go",
|
||||
"spread_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
78
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/node_affinity.go
generated
vendored
Normal file
78
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/node_affinity.go
generated
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
Copyright 2019 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 helper
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
)
|
||||
|
||||
// PodMatchesNodeSelectorAndAffinityTerms checks whether the pod is schedulable onto nodes according to
|
||||
// the requirements in both NodeAffinity and nodeSelector.
|
||||
func PodMatchesNodeSelectorAndAffinityTerms(pod *v1.Pod, node *v1.Node) bool {
|
||||
// Check if node.Labels match pod.Spec.NodeSelector.
|
||||
if len(pod.Spec.NodeSelector) > 0 {
|
||||
selector := labels.SelectorFromSet(pod.Spec.NodeSelector)
|
||||
if !selector.Matches(labels.Set(node.Labels)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// 1. nil NodeSelector matches all nodes (i.e. does not filter out any nodes)
|
||||
// 2. nil []NodeSelectorTerm (equivalent to non-nil empty NodeSelector) matches no nodes
|
||||
// 3. zero-length non-nil []NodeSelectorTerm matches no nodes also, just for simplicity
|
||||
// 4. nil []NodeSelectorRequirement (equivalent to non-nil empty NodeSelectorTerm) matches no nodes
|
||||
// 5. zero-length non-nil []NodeSelectorRequirement matches no nodes also, just for simplicity
|
||||
// 6. non-nil empty NodeSelectorRequirement is not allowed
|
||||
nodeAffinityMatches := true
|
||||
affinity := pod.Spec.Affinity
|
||||
if affinity != nil && affinity.NodeAffinity != nil {
|
||||
nodeAffinity := affinity.NodeAffinity
|
||||
// if no required NodeAffinity requirements, will do no-op, means select all nodes.
|
||||
// TODO: Replace next line with subsequent commented-out line when implement RequiredDuringSchedulingRequiredDuringExecution.
|
||||
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
|
||||
// if nodeAffinity.RequiredDuringSchedulingRequiredDuringExecution == nil && nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
// Match node selector for requiredDuringSchedulingRequiredDuringExecution.
|
||||
// TODO: Uncomment this block when implement RequiredDuringSchedulingRequiredDuringExecution.
|
||||
// if nodeAffinity.RequiredDuringSchedulingRequiredDuringExecution != nil {
|
||||
// nodeSelectorTerms := nodeAffinity.RequiredDuringSchedulingRequiredDuringExecution.NodeSelectorTerms
|
||||
// klog.V(10).Infof("Match for RequiredDuringSchedulingRequiredDuringExecution node selector terms %+v", nodeSelectorTerms)
|
||||
// nodeAffinityMatches = nodeMatchesNodeSelectorTerms(node, nodeSelectorTerms)
|
||||
// }
|
||||
|
||||
// Match node selector for requiredDuringSchedulingIgnoredDuringExecution.
|
||||
if nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
nodeSelectorTerms := nodeAffinity.RequiredDuringSchedulingIgnoredDuringExecution.NodeSelectorTerms
|
||||
nodeAffinityMatches = nodeAffinityMatches && nodeMatchesNodeSelectorTerms(node, nodeSelectorTerms)
|
||||
}
|
||||
|
||||
}
|
||||
return nodeAffinityMatches
|
||||
}
|
||||
|
||||
// nodeMatchesNodeSelectorTerms checks if a node's labels satisfy a list of node selector terms,
|
||||
// terms are ORed, and an empty list of terms will match nothing.
|
||||
func nodeMatchesNodeSelectorTerms(node *v1.Node, nodeSelectorTerms []v1.NodeSelectorTerm) bool {
|
||||
return v1helper.MatchNodeSelectorTerms(nodeSelectorTerms, node.Labels, fields.Set{
|
||||
"metadata.name": node.Name,
|
||||
})
|
||||
}
|
54
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/normalize_score.go
generated
vendored
Normal file
54
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/normalize_score.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
Copyright 2019 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 helper
|
||||
|
||||
import (
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
)
|
||||
|
||||
// DefaultNormalizeScore generates a Normalize Score function that can normalize the
|
||||
// scores to [0, maxPriority]. If reverse is set to true, it reverses the scores by
|
||||
// subtracting it from maxPriority.
|
||||
func DefaultNormalizeScore(maxPriority int64, reverse bool, scores framework.NodeScoreList) *framework.Status {
|
||||
var maxCount int64
|
||||
for i := range scores {
|
||||
if scores[i].Score > maxCount {
|
||||
maxCount = scores[i].Score
|
||||
}
|
||||
}
|
||||
|
||||
if maxCount == 0 {
|
||||
if reverse {
|
||||
for i := range scores {
|
||||
scores[i].Score = maxPriority
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range scores {
|
||||
score := scores[i].Score
|
||||
|
||||
score = maxPriority * score / maxCount
|
||||
if reverse {
|
||||
score = maxPriority - score
|
||||
}
|
||||
|
||||
scores[i].Score = score
|
||||
}
|
||||
return nil
|
||||
}
|
95
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/spread.go
generated
vendored
Normal file
95
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper/spread.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
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 helper
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
appslisters "k8s.io/client-go/listers/apps/v1"
|
||||
corelisters "k8s.io/client-go/listers/core/v1"
|
||||
)
|
||||
|
||||
// DefaultSelector returns a selector deduced from the Services, Replication
|
||||
// Controllers, Replica Sets, and Stateful Sets matching the given pod.
|
||||
func DefaultSelector(pod *v1.Pod, sl corelisters.ServiceLister, cl corelisters.ReplicationControllerLister, rsl appslisters.ReplicaSetLister, ssl appslisters.StatefulSetLister) labels.Selector {
|
||||
labelSet := make(labels.Set)
|
||||
// Since services, RCs, RSs and SSs match the pod, they won't have conflicting
|
||||
// labels. Merging is safe.
|
||||
|
||||
if services, err := GetPodServices(sl, pod); err == nil {
|
||||
for _, service := range services {
|
||||
labelSet = labels.Merge(labelSet, service.Spec.Selector)
|
||||
}
|
||||
}
|
||||
|
||||
if rcs, err := cl.GetPodControllers(pod); err == nil {
|
||||
for _, rc := range rcs {
|
||||
labelSet = labels.Merge(labelSet, rc.Spec.Selector)
|
||||
}
|
||||
}
|
||||
|
||||
selector := labels.NewSelector()
|
||||
if len(labelSet) != 0 {
|
||||
selector = labelSet.AsSelector()
|
||||
}
|
||||
|
||||
if rss, err := rsl.GetPodReplicaSets(pod); err == nil {
|
||||
for _, rs := range rss {
|
||||
if other, err := metav1.LabelSelectorAsSelector(rs.Spec.Selector); err == nil {
|
||||
if r, ok := other.Requirements(); ok {
|
||||
selector = selector.Add(r...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if sss, err := ssl.GetPodStatefulSets(pod); err == nil {
|
||||
for _, ss := range sss {
|
||||
if other, err := metav1.LabelSelectorAsSelector(ss.Spec.Selector); err == nil {
|
||||
if r, ok := other.Requirements(); ok {
|
||||
selector = selector.Add(r...)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selector
|
||||
}
|
||||
|
||||
// GetPodServices gets the services that have the selector that match the labels on the given pod.
|
||||
func GetPodServices(sl corelisters.ServiceLister, pod *v1.Pod) ([]*v1.Service, error) {
|
||||
allServices, err := sl.Services(pod.Namespace).List(labels.Everything())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var services []*v1.Service
|
||||
for i := range allServices {
|
||||
service := allServices[i]
|
||||
if service.Spec.Selector == nil {
|
||||
// services with nil selectors match nothing, not everything.
|
||||
continue
|
||||
}
|
||||
selector := labels.Set(service.Spec.Selector).AsSelectorPreValidated()
|
||||
if selector.Matches(labels.Set(pod.Labels)) {
|
||||
services = append(services, service)
|
||||
}
|
||||
}
|
||||
|
||||
return services, nil
|
||||
}
|
45
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity/BUILD
generated
vendored
Normal file
45
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity/BUILD
generated
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["node_affinity.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/scheduler/framework/plugins/helper:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["node_affinity_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/core:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/internal/cache:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
119
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity/node_affinity.go
generated
vendored
Normal file
119
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeaffinity/node_affinity.go
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
Copyright 2019 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 nodeaffinity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
pluginhelper "k8s.io/kubernetes/pkg/scheduler/framework/plugins/helper"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
// NodeAffinity is a plugin that checks if a pod node selector matches the node label.
|
||||
type NodeAffinity struct {
|
||||
handle framework.FrameworkHandle
|
||||
}
|
||||
|
||||
var _ framework.FilterPlugin = &NodeAffinity{}
|
||||
var _ framework.ScorePlugin = &NodeAffinity{}
|
||||
|
||||
const (
|
||||
// Name is the name of the plugin used in the plugin registry and configurations.
|
||||
Name = "NodeAffinity"
|
||||
|
||||
// ErrReason for node affinity/selector not matching.
|
||||
ErrReason = "node(s) didn't match node selector"
|
||||
)
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (pl *NodeAffinity) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// Filter invoked at the filter extension point.
|
||||
func (pl *NodeAffinity) Filter(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) *framework.Status {
|
||||
node := nodeInfo.Node()
|
||||
if node == nil {
|
||||
return framework.NewStatus(framework.Error, "node not found")
|
||||
}
|
||||
if !pluginhelper.PodMatchesNodeSelectorAndAffinityTerms(pod, node) {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Score invoked at the Score extension point.
|
||||
func (pl *NodeAffinity) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := pl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
|
||||
}
|
||||
|
||||
node := nodeInfo.Node()
|
||||
if node == nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
|
||||
}
|
||||
|
||||
affinity := pod.Spec.Affinity
|
||||
|
||||
var count int64
|
||||
// A nil element of PreferredDuringSchedulingIgnoredDuringExecution matches no objects.
|
||||
// An element of PreferredDuringSchedulingIgnoredDuringExecution that refers to an
|
||||
// empty PreferredSchedulingTerm matches all objects.
|
||||
if affinity != nil && affinity.NodeAffinity != nil && affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution != nil {
|
||||
// Match PreferredDuringSchedulingIgnoredDuringExecution term by term.
|
||||
for i := range affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution {
|
||||
preferredSchedulingTerm := &affinity.NodeAffinity.PreferredDuringSchedulingIgnoredDuringExecution[i]
|
||||
if preferredSchedulingTerm.Weight == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: Avoid computing it for all nodes if this becomes a performance problem.
|
||||
nodeSelector, err := v1helper.NodeSelectorRequirementsAsSelector(preferredSchedulingTerm.Preference.MatchExpressions)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, err.Error())
|
||||
}
|
||||
|
||||
if nodeSelector.Matches(labels.Set(node.Labels)) {
|
||||
count += int64(preferredSchedulingTerm.Weight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return count, nil
|
||||
}
|
||||
|
||||
// NormalizeScore invoked after scoring all nodes.
|
||||
func (pl *NodeAffinity) NormalizeScore(ctx context.Context, state *framework.CycleState, pod *v1.Pod, scores framework.NodeScoreList) *framework.Status {
|
||||
return pluginhelper.DefaultNormalizeScore(framework.MaxNodeScore, false, scores)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (pl *NodeAffinity) ScoreExtensions() framework.ScoreExtensions {
|
||||
return pl
|
||||
}
|
||||
|
||||
// New initializes a new plugin and returns it.
|
||||
func New(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &NodeAffinity{handle: h}, nil
|
||||
}
|
40
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename/BUILD
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["node_name.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["node_name_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
],
|
||||
)
|
65
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename/node_name.go
generated
vendored
Normal file
65
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodename/node_name.go
generated
vendored
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
Copyright 2019 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 nodename
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
// NodeName is a plugin that checks if a pod spec node name matches the current node.
|
||||
type NodeName struct{}
|
||||
|
||||
var _ framework.FilterPlugin = &NodeName{}
|
||||
|
||||
const (
|
||||
// Name is the name of the plugin used in the plugin registry and configurations.
|
||||
Name = "NodeName"
|
||||
|
||||
// ErrReason returned when node name doesn't match.
|
||||
ErrReason = "node(s) didn't match the requested hostname"
|
||||
)
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (pl *NodeName) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// Filter invoked at the filter extension point.
|
||||
func (pl *NodeName) Filter(ctx context.Context, _ *framework.CycleState, pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) *framework.Status {
|
||||
if nodeInfo.Node() == nil {
|
||||
return framework.NewStatus(framework.Error, "node not found")
|
||||
}
|
||||
if !Fits(pod, nodeInfo) {
|
||||
return framework.NewStatus(framework.UnschedulableAndUnresolvable, ErrReason)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fits actually checks if the pod fits the node.
|
||||
func Fits(pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) bool {
|
||||
return len(pod.Spec.NodeName) == 0 || pod.Spec.NodeName == nodeInfo.Node().Name
|
||||
}
|
||||
|
||||
// New initializes a new plugin and returns it.
|
||||
func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &NodeName{}, nil
|
||||
}
|
40
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports/BUILD
generated
vendored
Normal file
40
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports/BUILD
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["node_ports.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["node_ports_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/diff:go_default_library",
|
||||
],
|
||||
)
|
134
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports/node_ports.go
generated
vendored
Normal file
134
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/nodeports/node_ports.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
/*
|
||||
Copyright 2019 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 nodeports
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
"k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
// NodePorts is a plugin that checks if a node has free ports for the requested pod ports.
|
||||
type NodePorts struct{}
|
||||
|
||||
var _ framework.FilterPlugin = &NodePorts{}
|
||||
|
||||
const (
|
||||
// Name is the name of the plugin used in the plugin registry and configurations.
|
||||
Name = "NodePorts"
|
||||
|
||||
// preFilterStateKey is the key in CycleState to NodePorts pre-computed data.
|
||||
// Using the name of the plugin will likely help us avoid collisions with other plugins.
|
||||
preFilterStateKey = "PreFilter" + Name
|
||||
|
||||
// ErrReason when node ports aren't available.
|
||||
ErrReason = "node(s) didn't have free ports for the requested pod ports"
|
||||
)
|
||||
|
||||
type preFilterState []*v1.ContainerPort
|
||||
|
||||
// Clone the prefilter state.
|
||||
func (s preFilterState) Clone() framework.StateData {
|
||||
// The state is not impacted by adding/removing existing pods, hence we don't need to make a deep copy.
|
||||
return s
|
||||
}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (pl *NodePorts) Name() string {
|
||||
return Name
|
||||
}
|
||||
|
||||
// getContainerPorts returns the used host ports of Pods: if 'port' was used, a 'port:true' pair
|
||||
// will be in the result; but it does not resolve port conflict.
|
||||
func getContainerPorts(pods ...*v1.Pod) []*v1.ContainerPort {
|
||||
ports := []*v1.ContainerPort{}
|
||||
for _, pod := range pods {
|
||||
for j := range pod.Spec.Containers {
|
||||
container := &pod.Spec.Containers[j]
|
||||
for k := range container.Ports {
|
||||
ports = append(ports, &container.Ports[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
return ports
|
||||
}
|
||||
|
||||
// PreFilter invoked at the prefilter extension point.
|
||||
func (pl *NodePorts) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
|
||||
s := getContainerPorts(pod)
|
||||
cycleState.Write(preFilterStateKey, preFilterState(s))
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreFilterExtensions do not exist for this plugin.
|
||||
func (pl *NodePorts) PreFilterExtensions() framework.PreFilterExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPreFilterState(cycleState *framework.CycleState) (preFilterState, error) {
|
||||
c, err := cycleState.Read(preFilterStateKey)
|
||||
if err != nil {
|
||||
// preFilterState doesn't exist, likely PreFilter wasn't invoked.
|
||||
return nil, fmt.Errorf("error reading %q from cycleState: %v", preFilterStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(preFilterState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%+v convert to nodeports.preFilterState error", c)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Filter invoked at the filter extension point.
|
||||
func (pl *NodePorts) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) *framework.Status {
|
||||
wantPorts, err := getPreFilterState(cycleState)
|
||||
if err != nil {
|
||||
return framework.NewStatus(framework.Error, err.Error())
|
||||
}
|
||||
|
||||
fits := fitsPorts(wantPorts, nodeInfo)
|
||||
if !fits {
|
||||
return framework.NewStatus(framework.Unschedulable, ErrReason)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Fits checks if the pod fits the node.
|
||||
func Fits(pod *v1.Pod, nodeInfo *nodeinfo.NodeInfo) bool {
|
||||
return fitsPorts(getContainerPorts(pod), nodeInfo)
|
||||
}
|
||||
|
||||
func fitsPorts(wantPorts []*v1.ContainerPort, nodeInfo *nodeinfo.NodeInfo) bool {
|
||||
// try to see whether existingPorts and wantPorts will conflict or not
|
||||
existingPorts := nodeInfo.UsedPorts()
|
||||
for _, cp := range wantPorts {
|
||||
if existingPorts.CheckConflict(cp.HostIP, string(cp.Protocol), cp.HostPort) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// New initializes a new plugin and returns it.
|
||||
func New(_ *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &NodePorts{}, nil
|
||||
}
|
73
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/BUILD
generated
vendored
Normal file
73
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/BUILD
generated
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"balanced_allocation.go",
|
||||
"fit.go",
|
||||
"least_allocated.go",
|
||||
"most_allocated.go",
|
||||
"requested_to_capacity_ratio.go",
|
||||
"resource_allocation.go",
|
||||
"resource_limits.go",
|
||||
"test_util.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/scheduler/apis/config:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//pkg/scheduler/util:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"balanced_allocation_test.go",
|
||||
"fit_test.go",
|
||||
"least_allocated_test.go",
|
||||
"most_allocated_test.go",
|
||||
"requested_to_capacity_ratio_test.go",
|
||||
"resource_limits_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/features:go_default_library",
|
||||
"//pkg/scheduler/framework/v1alpha1:go_default_library",
|
||||
"//pkg/scheduler/internal/cache:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/util/feature:go_default_library",
|
||||
"//staging/src/k8s.io/component-base/featuregate/testing:go_default_library",
|
||||
"//vendor/github.com/stretchr/testify/assert:go_default_library",
|
||||
],
|
||||
)
|
120
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go
generated
vendored
Normal file
120
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/balanced_allocation.go
generated
vendored
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
)
|
||||
|
||||
// BalancedAllocation is a score plugin that calculates the difference between the cpu and memory fraction
|
||||
// of capacity, and prioritizes the host based on how close the two metrics are to each other.
|
||||
type BalancedAllocation struct {
|
||||
handle framework.FrameworkHandle
|
||||
resourceAllocationScorer
|
||||
}
|
||||
|
||||
var _ = framework.ScorePlugin(&BalancedAllocation{})
|
||||
|
||||
// BalancedAllocationName is the name of the plugin used in the plugin registry and configurations.
|
||||
const BalancedAllocationName = "NodeResourcesBalancedAllocation"
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (ba *BalancedAllocation) Name() string {
|
||||
return BalancedAllocationName
|
||||
}
|
||||
|
||||
// Score invoked at the score extension point.
|
||||
func (ba *BalancedAllocation) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := ba.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
|
||||
}
|
||||
|
||||
// ba.score favors nodes with balanced resource usage rate.
|
||||
// It should **NOT** be used alone, and **MUST** be used together
|
||||
// with NodeResourcesLeastAllocated plugin. It calculates the difference between the cpu and memory fraction
|
||||
// of capacity, and prioritizes the host based on how close the two metrics are to each other.
|
||||
// Detail: score = 10 - variance(cpuFraction,memoryFraction,volumeFraction)*10. The algorithm is partly inspired by:
|
||||
// "Wei Huang et al. An Energy Efficient Virtual Machine Placement Algorithm with Balanced
|
||||
// Resource Utilization"
|
||||
return ba.score(pod, nodeInfo)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (ba *BalancedAllocation) ScoreExtensions() framework.ScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewBalancedAllocation initializes a new plugin and returns it.
|
||||
func NewBalancedAllocation(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &BalancedAllocation{
|
||||
handle: h,
|
||||
resourceAllocationScorer: resourceAllocationScorer{
|
||||
BalancedAllocationName,
|
||||
balancedResourceScorer,
|
||||
defaultRequestedRatioResources,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// todo: use resource weights in the scorer function
|
||||
func balancedResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
|
||||
cpuFraction := fractionOfCapacity(requested[v1.ResourceCPU], allocable[v1.ResourceCPU])
|
||||
memoryFraction := fractionOfCapacity(requested[v1.ResourceMemory], allocable[v1.ResourceMemory])
|
||||
// This to find a node which has most balanced CPU, memory and volume usage.
|
||||
if cpuFraction >= 1 || memoryFraction >= 1 {
|
||||
// if requested >= capacity, the corresponding host should never be preferred.
|
||||
return 0
|
||||
}
|
||||
|
||||
if includeVolumes && utilfeature.DefaultFeatureGate.Enabled(features.BalanceAttachedNodeVolumes) && allocatableVolumes > 0 {
|
||||
volumeFraction := float64(requestedVolumes) / float64(allocatableVolumes)
|
||||
if volumeFraction >= 1 {
|
||||
// if requested >= capacity, the corresponding host should never be preferred.
|
||||
return 0
|
||||
}
|
||||
// Compute variance for all the three fractions.
|
||||
mean := (cpuFraction + memoryFraction + volumeFraction) / float64(3)
|
||||
variance := float64((((cpuFraction - mean) * (cpuFraction - mean)) + ((memoryFraction - mean) * (memoryFraction - mean)) + ((volumeFraction - mean) * (volumeFraction - mean))) / float64(3))
|
||||
// Since the variance is between positive fractions, it will be positive fraction. 1-variance lets the
|
||||
// score to be higher for node which has least variance and multiplying it with 10 provides the scaling
|
||||
// factor needed.
|
||||
return int64((1 - variance) * float64(framework.MaxNodeScore))
|
||||
}
|
||||
|
||||
// Upper and lower boundary of difference between cpuFraction and memoryFraction are -1 and 1
|
||||
// respectively. Multiplying the absolute value of the difference by 10 scales the value to
|
||||
// 0-10 with 0 representing well balanced allocation and 10 poorly balanced. Subtracting it from
|
||||
// 10 leads to the score which also scales from 0 to 10 while 10 representing well balanced.
|
||||
diff := math.Abs(cpuFraction - memoryFraction)
|
||||
return int64((1 - diff) * float64(framework.MaxNodeScore))
|
||||
}
|
||||
|
||||
func fractionOfCapacity(requested, capacity int64) float64 {
|
||||
if capacity == 0 {
|
||||
return 1
|
||||
}
|
||||
return float64(requested) / float64(capacity)
|
||||
}
|
267
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/fit.go
generated
vendored
Normal file
267
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/fit.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
var _ framework.PreFilterPlugin = &Fit{}
|
||||
var _ framework.FilterPlugin = &Fit{}
|
||||
|
||||
const (
|
||||
// FitName is the name of the plugin used in the plugin registry and configurations.
|
||||
FitName = "NodeResourcesFit"
|
||||
|
||||
// preFilterStateKey is the key in CycleState to NodeResourcesFit pre-computed data.
|
||||
// Using the name of the plugin will likely help us avoid collisions with other plugins.
|
||||
preFilterStateKey = "PreFilter" + FitName
|
||||
)
|
||||
|
||||
// Fit is a plugin that checks if a node has sufficient resources.
|
||||
type Fit struct {
|
||||
ignoredResources sets.String
|
||||
}
|
||||
|
||||
// FitArgs holds the args that are used to configure the plugin.
|
||||
type FitArgs struct {
|
||||
// IgnoredResources is the list of resources that NodeResources fit filter
|
||||
// should ignore.
|
||||
IgnoredResources []string `json:"ignoredResources,omitempty"`
|
||||
}
|
||||
|
||||
// preFilterState computed at PreFilter and used at Filter.
|
||||
type preFilterState struct {
|
||||
schedulernodeinfo.Resource
|
||||
}
|
||||
|
||||
// Clone the prefilter state.
|
||||
func (s *preFilterState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (f *Fit) Name() string {
|
||||
return FitName
|
||||
}
|
||||
|
||||
// computePodResourceRequest returns a schedulernodeinfo.Resource that covers the largest
|
||||
// width in each resource dimension. Because init-containers run sequentially, we collect
|
||||
// the max in each dimension iteratively. In contrast, we sum the resource vectors for
|
||||
// regular containers since they run simultaneously.
|
||||
//
|
||||
// If Pod Overhead is specified and the feature gate is set, the resources defined for Overhead
|
||||
// are added to the calculated Resource request sum
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// Pod:
|
||||
// InitContainers
|
||||
// IC1:
|
||||
// CPU: 2
|
||||
// Memory: 1G
|
||||
// IC2:
|
||||
// CPU: 2
|
||||
// Memory: 3G
|
||||
// Containers
|
||||
// C1:
|
||||
// CPU: 2
|
||||
// Memory: 1G
|
||||
// C2:
|
||||
// CPU: 1
|
||||
// Memory: 1G
|
||||
//
|
||||
// Result: CPU: 3, Memory: 3G
|
||||
func computePodResourceRequest(pod *v1.Pod) *preFilterState {
|
||||
result := &preFilterState{}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
result.Add(container.Resources.Requests)
|
||||
}
|
||||
|
||||
// take max_resource(sum_pod, any_init_container)
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
result.SetMaxResource(container.Resources.Requests)
|
||||
}
|
||||
|
||||
// If Overhead is being utilized, add to the total requests for the pod
|
||||
if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
|
||||
result.Add(pod.Spec.Overhead)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// PreFilter invoked at the prefilter extension point.
|
||||
func (f *Fit) PreFilter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod) *framework.Status {
|
||||
cycleState.Write(preFilterStateKey, computePodResourceRequest(pod))
|
||||
return nil
|
||||
}
|
||||
|
||||
// PreFilterExtensions returns prefilter extensions, pod add and remove.
|
||||
func (f *Fit) PreFilterExtensions() framework.PreFilterExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPreFilterState(cycleState *framework.CycleState) (*preFilterState, error) {
|
||||
c, err := cycleState.Read(preFilterStateKey)
|
||||
if err != nil {
|
||||
// preFilterState doesn't exist, likely PreFilter wasn't invoked.
|
||||
return nil, fmt.Errorf("error reading %q from cycleState: %v", preFilterStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(*preFilterState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%+v convert to NodeResourcesFit.preFilterState error", c)
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// Filter invoked at the filter extension point.
|
||||
// Checks if a node has sufficient resources, such as cpu, memory, gpu, opaque int resources etc to run a pod.
|
||||
// It returns a list of insufficient resources, if empty, then the node has all the resources requested by the pod.
|
||||
func (f *Fit) Filter(ctx context.Context, cycleState *framework.CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *framework.Status {
|
||||
s, err := getPreFilterState(cycleState)
|
||||
if err != nil {
|
||||
return framework.NewStatus(framework.Error, err.Error())
|
||||
}
|
||||
|
||||
insufficientResources := fitsRequest(s, nodeInfo, f.ignoredResources)
|
||||
|
||||
if len(insufficientResources) != 0 {
|
||||
// We will keep all failure reasons.
|
||||
failureReasons := make([]string, 0, len(insufficientResources))
|
||||
for _, r := range insufficientResources {
|
||||
failureReasons = append(failureReasons, r.Reason)
|
||||
}
|
||||
return framework.NewStatus(framework.Unschedulable, failureReasons...)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// InsufficientResource describes what kind of resource limit is hit and caused the pod to not fit the node.
|
||||
type InsufficientResource struct {
|
||||
ResourceName v1.ResourceName
|
||||
// We explicitly have a parameter for reason to avoid formatting a message on the fly
|
||||
// for common resources, which is expensive for cluster autoscaler simulations.
|
||||
Reason string
|
||||
Requested int64
|
||||
Used int64
|
||||
Capacity int64
|
||||
}
|
||||
|
||||
// Fits checks if node have enough resources to host the pod.
|
||||
func Fits(pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo, ignoredExtendedResources sets.String) []InsufficientResource {
|
||||
return fitsRequest(computePodResourceRequest(pod), nodeInfo, ignoredExtendedResources)
|
||||
}
|
||||
|
||||
func fitsRequest(podRequest *preFilterState, nodeInfo *schedulernodeinfo.NodeInfo, ignoredExtendedResources sets.String) []InsufficientResource {
|
||||
insufficientResources := make([]InsufficientResource, 0, 4)
|
||||
|
||||
allowedPodNumber := nodeInfo.AllowedPodNumber()
|
||||
if len(nodeInfo.Pods())+1 > allowedPodNumber {
|
||||
insufficientResources = append(insufficientResources, InsufficientResource{
|
||||
v1.ResourcePods,
|
||||
"Too many pods",
|
||||
1,
|
||||
int64(len(nodeInfo.Pods())),
|
||||
int64(allowedPodNumber),
|
||||
})
|
||||
}
|
||||
|
||||
if ignoredExtendedResources == nil {
|
||||
ignoredExtendedResources = sets.NewString()
|
||||
}
|
||||
|
||||
if podRequest.MilliCPU == 0 &&
|
||||
podRequest.Memory == 0 &&
|
||||
podRequest.EphemeralStorage == 0 &&
|
||||
len(podRequest.ScalarResources) == 0 {
|
||||
return insufficientResources
|
||||
}
|
||||
|
||||
allocatable := nodeInfo.AllocatableResource()
|
||||
if allocatable.MilliCPU < podRequest.MilliCPU+nodeInfo.RequestedResource().MilliCPU {
|
||||
insufficientResources = append(insufficientResources, InsufficientResource{
|
||||
v1.ResourceCPU,
|
||||
"Insufficient cpu",
|
||||
podRequest.MilliCPU,
|
||||
nodeInfo.RequestedResource().MilliCPU,
|
||||
allocatable.MilliCPU,
|
||||
})
|
||||
}
|
||||
if allocatable.Memory < podRequest.Memory+nodeInfo.RequestedResource().Memory {
|
||||
insufficientResources = append(insufficientResources, InsufficientResource{
|
||||
v1.ResourceMemory,
|
||||
"Insufficient memory",
|
||||
podRequest.Memory,
|
||||
nodeInfo.RequestedResource().Memory,
|
||||
allocatable.Memory,
|
||||
})
|
||||
}
|
||||
if allocatable.EphemeralStorage < podRequest.EphemeralStorage+nodeInfo.RequestedResource().EphemeralStorage {
|
||||
insufficientResources = append(insufficientResources, InsufficientResource{
|
||||
v1.ResourceEphemeralStorage,
|
||||
"Insufficient ephemeral-storage",
|
||||
podRequest.EphemeralStorage,
|
||||
nodeInfo.RequestedResource().EphemeralStorage,
|
||||
allocatable.EphemeralStorage,
|
||||
})
|
||||
}
|
||||
|
||||
for rName, rQuant := range podRequest.ScalarResources {
|
||||
if v1helper.IsExtendedResourceName(rName) {
|
||||
// If this resource is one of the extended resources that should be
|
||||
// ignored, we will skip checking it.
|
||||
if ignoredExtendedResources.Has(string(rName)) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if allocatable.ScalarResources[rName] < rQuant+nodeInfo.RequestedResource().ScalarResources[rName] {
|
||||
insufficientResources = append(insufficientResources, InsufficientResource{
|
||||
rName,
|
||||
fmt.Sprintf("Insufficient %v", rName),
|
||||
podRequest.ScalarResources[rName],
|
||||
nodeInfo.RequestedResource().ScalarResources[rName],
|
||||
allocatable.ScalarResources[rName],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return insufficientResources
|
||||
}
|
||||
|
||||
// NewFit initializes a new plugin and returns it.
|
||||
func NewFit(plArgs *runtime.Unknown, _ framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
args := &FitArgs{}
|
||||
if err := framework.DecodeInto(plArgs, args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fit := &Fit{}
|
||||
fit.ignoredResources = sets.NewString(args.IgnoredResources...)
|
||||
return fit, nil
|
||||
}
|
99
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/least_allocated.go
generated
vendored
Normal file
99
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/least_allocated.go
generated
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
)
|
||||
|
||||
// LeastAllocated is a score plugin that favors nodes with fewer allocation requested resources based on requested resources.
|
||||
type LeastAllocated struct {
|
||||
handle framework.FrameworkHandle
|
||||
resourceAllocationScorer
|
||||
}
|
||||
|
||||
var _ = framework.ScorePlugin(&LeastAllocated{})
|
||||
|
||||
// LeastAllocatedName is the name of the plugin used in the plugin registry and configurations.
|
||||
const LeastAllocatedName = "NodeResourcesLeastAllocated"
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (la *LeastAllocated) Name() string {
|
||||
return LeastAllocatedName
|
||||
}
|
||||
|
||||
// Score invoked at the score extension point.
|
||||
func (la *LeastAllocated) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := la.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
|
||||
}
|
||||
|
||||
// la.score favors nodes with fewer requested resources.
|
||||
// It calculates the percentage of memory and CPU requested by pods scheduled on the node, and
|
||||
// prioritizes based on the minimum of the average of the fraction of requested to capacity.
|
||||
//
|
||||
// Details:
|
||||
// (cpu((capacity-sum(requested))*10/capacity) + memory((capacity-sum(requested))*10/capacity))/2
|
||||
return la.score(pod, nodeInfo)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (la *LeastAllocated) ScoreExtensions() framework.ScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewLeastAllocated initializes a new plugin and returns it.
|
||||
func NewLeastAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &LeastAllocated{
|
||||
handle: h,
|
||||
resourceAllocationScorer: resourceAllocationScorer{
|
||||
LeastAllocatedName,
|
||||
leastResourceScorer,
|
||||
defaultRequestedRatioResources,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func leastResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
|
||||
var nodeScore, weightSum int64
|
||||
for resource, weight := range defaultRequestedRatioResources {
|
||||
resourceScore := leastRequestedScore(requested[resource], allocable[resource])
|
||||
nodeScore += resourceScore * weight
|
||||
weightSum += weight
|
||||
}
|
||||
return nodeScore / weightSum
|
||||
}
|
||||
|
||||
// The unused capacity is calculated on a scale of 0-10
|
||||
// 0 being the lowest priority and 10 being the highest.
|
||||
// The more unused resources the higher the score is.
|
||||
func leastRequestedScore(requested, capacity int64) int64 {
|
||||
if capacity == 0 {
|
||||
return 0
|
||||
}
|
||||
if requested > capacity {
|
||||
return 0
|
||||
}
|
||||
|
||||
return ((capacity - requested) * int64(framework.MaxNodeScore)) / capacity
|
||||
}
|
102
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/most_allocated.go
generated
vendored
Normal file
102
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/most_allocated.go
generated
vendored
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
)
|
||||
|
||||
// MostAllocated is a score plugin that favors nodes with high allocation based on requested resources.
|
||||
type MostAllocated struct {
|
||||
handle framework.FrameworkHandle
|
||||
resourceAllocationScorer
|
||||
}
|
||||
|
||||
var _ = framework.ScorePlugin(&MostAllocated{})
|
||||
|
||||
// MostAllocatedName is the name of the plugin used in the plugin registry and configurations.
|
||||
const MostAllocatedName = "NodeResourcesMostAllocated"
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (ma *MostAllocated) Name() string {
|
||||
return MostAllocatedName
|
||||
}
|
||||
|
||||
// Score invoked at the Score extension point.
|
||||
func (ma *MostAllocated) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := ma.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil || nodeInfo.Node() == nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v, node is nil: %v", nodeName, err, nodeInfo.Node() == nil))
|
||||
}
|
||||
|
||||
// ma.score favors nodes with most requested resources.
|
||||
// It calculates the percentage of memory and CPU requested by pods scheduled on the node, and prioritizes
|
||||
// based on the maximum of the average of the fraction of requested to capacity.
|
||||
// Details: (cpu(10 * sum(requested) / capacity) + memory(10 * sum(requested) / capacity)) / 2
|
||||
return ma.score(pod, nodeInfo)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (ma *MostAllocated) ScoreExtensions() framework.ScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewMostAllocated initializes a new plugin and returns it.
|
||||
func NewMostAllocated(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &MostAllocated{
|
||||
handle: h,
|
||||
resourceAllocationScorer: resourceAllocationScorer{
|
||||
MostAllocatedName,
|
||||
mostResourceScorer,
|
||||
defaultRequestedRatioResources,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func mostResourceScorer(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
|
||||
var nodeScore, weightSum int64
|
||||
for resource, weight := range defaultRequestedRatioResources {
|
||||
resourceScore := mostRequestedScore(requested[resource], allocable[resource])
|
||||
nodeScore += resourceScore * weight
|
||||
weightSum += weight
|
||||
}
|
||||
return (nodeScore / weightSum)
|
||||
|
||||
}
|
||||
|
||||
// The used capacity is calculated on a scale of 0-10
|
||||
// 0 being the lowest priority and 10 being the highest.
|
||||
// The more resources are used the higher the score is. This function
|
||||
// is almost a reversed version of least_requested_priority.calculateUnusedScore
|
||||
// (10 - calculateUnusedScore). The main difference is in rounding. It was added to
|
||||
// keep the final formula clean and not to modify the widely used (by users
|
||||
// in their default scheduling policies) calculateUsedScore.
|
||||
func mostRequestedScore(requested, capacity int64) int64 {
|
||||
if capacity == 0 {
|
||||
return 0
|
||||
}
|
||||
if requested > capacity {
|
||||
return 0
|
||||
}
|
||||
|
||||
return (requested * framework.MaxNodeScore) / capacity
|
||||
}
|
219
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/requested_to_capacity_ratio.go
generated
vendored
Normal file
219
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/requested_to_capacity_ratio.go
generated
vendored
Normal file
@ -0,0 +1,219 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
)
|
||||
|
||||
const (
|
||||
// RequestedToCapacityRatioName is the name of this plugin.
|
||||
RequestedToCapacityRatioName = "RequestedToCapacityRatio"
|
||||
minUtilization = 0
|
||||
maxUtilization = 100
|
||||
minScore = 0
|
||||
maxScore = framework.MaxNodeScore
|
||||
)
|
||||
|
||||
// RequestedToCapacityRatioArgs holds the args that are used to configure the plugin.
|
||||
type RequestedToCapacityRatioArgs struct {
|
||||
config.RequestedToCapacityRatioArguments
|
||||
}
|
||||
|
||||
type functionShape []functionShapePoint
|
||||
|
||||
type functionShapePoint struct {
|
||||
// Utilization is function argument.
|
||||
utilization int64
|
||||
// Score is function value.
|
||||
score int64
|
||||
}
|
||||
|
||||
// NewRequestedToCapacityRatio initializes a new plugin and returns it.
|
||||
func NewRequestedToCapacityRatio(plArgs *runtime.Unknown, handle framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
args := &config.RequestedToCapacityRatioArguments{}
|
||||
if err := framework.DecodeInto(plArgs, args); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
shape := make([]functionShapePoint, 0, len(args.Shape))
|
||||
for _, point := range args.Shape {
|
||||
shape = append(shape, functionShapePoint{
|
||||
utilization: int64(point.Utilization),
|
||||
// MaxCustomPriorityScore may diverge from the max score used in the scheduler and defined by MaxNodeScore,
|
||||
// therefore we need to scale the score returned by requested to capacity ratio to the score range
|
||||
// used by the scheduler.
|
||||
score: int64(point.Score) * (framework.MaxNodeScore / config.MaxCustomPriorityScore),
|
||||
})
|
||||
}
|
||||
|
||||
if err := validateFunctionShape(shape); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resourceToWeightMap := make(resourceToWeightMap)
|
||||
for _, resource := range args.Resources {
|
||||
resourceToWeightMap[v1.ResourceName(resource.Name)] = resource.Weight
|
||||
if resource.Weight == 0 {
|
||||
// Apply the default weight.
|
||||
resourceToWeightMap[v1.ResourceName(resource.Name)] = 1
|
||||
}
|
||||
}
|
||||
if len(args.Resources) == 0 {
|
||||
// If no resources specified, used the default set.
|
||||
resourceToWeightMap = defaultRequestedRatioResources
|
||||
}
|
||||
|
||||
return &RequestedToCapacityRatio{
|
||||
handle: handle,
|
||||
resourceAllocationScorer: resourceAllocationScorer{
|
||||
RequestedToCapacityRatioName,
|
||||
buildRequestedToCapacityRatioScorerFunction(shape, resourceToWeightMap),
|
||||
resourceToWeightMap,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RequestedToCapacityRatio is a score plugin that allow users to apply bin packing
|
||||
// on core resources like CPU, Memory as well as extended resources like accelerators.
|
||||
type RequestedToCapacityRatio struct {
|
||||
handle framework.FrameworkHandle
|
||||
resourceAllocationScorer
|
||||
}
|
||||
|
||||
var _ framework.ScorePlugin = &RequestedToCapacityRatio{}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (pl *RequestedToCapacityRatio) Name() string {
|
||||
return RequestedToCapacityRatioName
|
||||
}
|
||||
|
||||
// Score invoked at the score extension point.
|
||||
func (pl *RequestedToCapacityRatio) Score(ctx context.Context, _ *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := pl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v", nodeName, err))
|
||||
}
|
||||
return pl.score(pod, nodeInfo)
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (pl *RequestedToCapacityRatio) ScoreExtensions() framework.ScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateFunctionShape(shape functionShape) error {
|
||||
if len(shape) == 0 {
|
||||
return fmt.Errorf("at least one point must be specified")
|
||||
}
|
||||
|
||||
for i := 1; i < len(shape); i++ {
|
||||
if shape[i-1].utilization >= shape[i].utilization {
|
||||
return fmt.Errorf("utilization values must be sorted. Utilization[%d]==%d >= Utilization[%d]==%d", i-1, shape[i-1].utilization, i, shape[i].utilization)
|
||||
}
|
||||
}
|
||||
|
||||
for i, point := range shape {
|
||||
if point.utilization < minUtilization {
|
||||
return fmt.Errorf("utilization values must not be less than %d. Utilization[%d]==%d", minUtilization, i, point.utilization)
|
||||
}
|
||||
if point.utilization > maxUtilization {
|
||||
return fmt.Errorf("utilization values must not be greater than %d. Utilization[%d]==%d", maxUtilization, i, point.utilization)
|
||||
}
|
||||
if point.score < minScore {
|
||||
return fmt.Errorf("score values must not be less than %d. Score[%d]==%d", minScore, i, point.score)
|
||||
}
|
||||
if int64(point.score) > maxScore {
|
||||
return fmt.Errorf("score values not be greater than %d. Score[%d]==%d", maxScore, i, point.score)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateResourceWeightMap(resourceToWeightMap resourceToWeightMap) error {
|
||||
if len(resourceToWeightMap) == 0 {
|
||||
return fmt.Errorf("resourceToWeightMap cannot be nil")
|
||||
}
|
||||
|
||||
for resource, weight := range resourceToWeightMap {
|
||||
if weight < 1 {
|
||||
return fmt.Errorf("resource %s weight %d must not be less than 1", string(resource), weight)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func buildRequestedToCapacityRatioScorerFunction(scoringFunctionShape functionShape, resourceToWeightMap resourceToWeightMap) func(resourceToValueMap, resourceToValueMap, bool, int, int) int64 {
|
||||
rawScoringFunction := buildBrokenLinearFunction(scoringFunctionShape)
|
||||
err := validateResourceWeightMap(resourceToWeightMap)
|
||||
if err != nil {
|
||||
klog.Error(err)
|
||||
}
|
||||
resourceScoringFunction := func(requested, capacity int64) int64 {
|
||||
if capacity == 0 || requested > capacity {
|
||||
return rawScoringFunction(maxUtilization)
|
||||
}
|
||||
|
||||
return rawScoringFunction(maxUtilization - (capacity-requested)*maxUtilization/capacity)
|
||||
}
|
||||
return func(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64 {
|
||||
var nodeScore, weightSum int64
|
||||
for resource, weight := range resourceToWeightMap {
|
||||
resourceScore := resourceScoringFunction(requested[resource], allocable[resource])
|
||||
if resourceScore > 0 {
|
||||
nodeScore += resourceScore * weight
|
||||
weightSum += weight
|
||||
}
|
||||
}
|
||||
if weightSum == 0 {
|
||||
return 0
|
||||
}
|
||||
return int64(math.Round(float64(nodeScore) / float64(weightSum)))
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a function which is built using linear segments. Segments are defined via shape array.
|
||||
// Shape[i].utilization slice represents points on "utilization" axis where different segments meet.
|
||||
// Shape[i].score represents function values at meeting points.
|
||||
//
|
||||
// function f(p) is defined as:
|
||||
// shape[0].score for p < f[0].utilization
|
||||
// shape[i].score for p == shape[i].utilization
|
||||
// shape[n-1].score for p > shape[n-1].utilization
|
||||
// and linear between points (p < shape[i].utilization)
|
||||
func buildBrokenLinearFunction(shape functionShape) func(int64) int64 {
|
||||
return func(p int64) int64 {
|
||||
for i := 0; i < len(shape); i++ {
|
||||
if p <= int64(shape[i].utilization) {
|
||||
if i == 0 {
|
||||
return shape[0].score
|
||||
}
|
||||
return shape[i-1].score + (shape[i].score-shape[i-1].score)*(p-shape[i-1].utilization)/(shape[i].utilization-shape[i-1].utilization)
|
||||
}
|
||||
}
|
||||
return shape[len(shape)-1].score
|
||||
}
|
||||
}
|
135
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go
generated
vendored
Normal file
135
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/resource_allocation.go
generated
vendored
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
Copyright 2017 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 noderesources
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
||||
"k8s.io/klog"
|
||||
v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
"k8s.io/kubernetes/pkg/features"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
schedutil "k8s.io/kubernetes/pkg/scheduler/util"
|
||||
)
|
||||
|
||||
// resourceToWeightMap contains resource name and weight.
|
||||
type resourceToWeightMap map[v1.ResourceName]int64
|
||||
|
||||
// defaultRequestedRatioResources is used to set default requestToWeight map for CPU and memory
|
||||
var defaultRequestedRatioResources = resourceToWeightMap{v1.ResourceMemory: 1, v1.ResourceCPU: 1}
|
||||
|
||||
// resourceAllocationScorer contains information to calculate resource allocation score.
|
||||
type resourceAllocationScorer struct {
|
||||
Name string
|
||||
scorer func(requested, allocable resourceToValueMap, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64
|
||||
resourceToWeightMap resourceToWeightMap
|
||||
}
|
||||
|
||||
// resourceToValueMap contains resource name and score.
|
||||
type resourceToValueMap map[v1.ResourceName]int64
|
||||
|
||||
// score will use `scorer` function to calculate the score.
|
||||
func (r *resourceAllocationScorer) score(
|
||||
pod *v1.Pod,
|
||||
nodeInfo *schedulernodeinfo.NodeInfo) (int64, *framework.Status) {
|
||||
node := nodeInfo.Node()
|
||||
if node == nil {
|
||||
return 0, framework.NewStatus(framework.Error, "node not found")
|
||||
}
|
||||
if r.resourceToWeightMap == nil {
|
||||
return 0, framework.NewStatus(framework.Error, "resources not found")
|
||||
}
|
||||
requested := make(resourceToValueMap, len(r.resourceToWeightMap))
|
||||
allocatable := make(resourceToValueMap, len(r.resourceToWeightMap))
|
||||
for resource := range r.resourceToWeightMap {
|
||||
allocatable[resource], requested[resource] = calculateResourceAllocatableRequest(nodeInfo, pod, resource)
|
||||
}
|
||||
var score int64
|
||||
|
||||
// Check if the pod has volumes and this could be added to scorer function for balanced resource allocation.
|
||||
if len(pod.Spec.Volumes) >= 0 && utilfeature.DefaultFeatureGate.Enabled(features.BalanceAttachedNodeVolumes) && nodeInfo.TransientInfo != nil {
|
||||
score = r.scorer(requested, allocatable, true, nodeInfo.TransientInfo.TransNodeInfo.RequestedVolumes, nodeInfo.TransientInfo.TransNodeInfo.AllocatableVolumesCount)
|
||||
} else {
|
||||
score = r.scorer(requested, allocatable, false, 0, 0)
|
||||
}
|
||||
if klog.V(10) {
|
||||
if len(pod.Spec.Volumes) >= 0 && utilfeature.DefaultFeatureGate.Enabled(features.BalanceAttachedNodeVolumes) && nodeInfo.TransientInfo != nil {
|
||||
klog.Infof(
|
||||
"%v -> %v: %v, map of allocatable resources %v, map of requested resources %v , allocatable volumes %d, requested volumes %d, score %d",
|
||||
pod.Name, node.Name, r.Name,
|
||||
allocatable, requested, nodeInfo.TransientInfo.TransNodeInfo.AllocatableVolumesCount,
|
||||
nodeInfo.TransientInfo.TransNodeInfo.RequestedVolumes,
|
||||
score,
|
||||
)
|
||||
} else {
|
||||
klog.Infof(
|
||||
"%v -> %v: %v, map of allocatable resources %v, map of requested resources %v ,score %d,",
|
||||
pod.Name, node.Name, r.Name,
|
||||
allocatable, requested, score,
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return score, nil
|
||||
}
|
||||
|
||||
// calculateResourceAllocatableRequest returns resources Allocatable and Requested values
|
||||
func calculateResourceAllocatableRequest(nodeInfo *schedulernodeinfo.NodeInfo, pod *v1.Pod, resource v1.ResourceName) (int64, int64) {
|
||||
allocatable := nodeInfo.AllocatableResource()
|
||||
requested := nodeInfo.RequestedResource()
|
||||
podRequest := calculatePodResourceRequest(pod, resource)
|
||||
switch resource {
|
||||
case v1.ResourceCPU:
|
||||
return allocatable.MilliCPU, (nodeInfo.NonZeroRequest().MilliCPU + podRequest)
|
||||
case v1.ResourceMemory:
|
||||
return allocatable.Memory, (nodeInfo.NonZeroRequest().Memory + podRequest)
|
||||
|
||||
case v1.ResourceEphemeralStorage:
|
||||
return allocatable.EphemeralStorage, (requested.EphemeralStorage + podRequest)
|
||||
default:
|
||||
if v1helper.IsScalarResourceName(resource) {
|
||||
return allocatable.ScalarResources[resource], (requested.ScalarResources[resource] + podRequest)
|
||||
}
|
||||
}
|
||||
if klog.V(10) {
|
||||
klog.Infof("requested resource %v not considered for node score calculation",
|
||||
resource,
|
||||
)
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// calculatePodResourceRequest returns the total non-zero requests. If Overhead is defined for the pod and the
|
||||
// PodOverhead feature is enabled, the Overhead is added to the result.
|
||||
func calculatePodResourceRequest(pod *v1.Pod, resource v1.ResourceName) int64 {
|
||||
var podRequest int64
|
||||
for i := range pod.Spec.Containers {
|
||||
container := &pod.Spec.Containers[i]
|
||||
value := schedutil.GetNonzeroRequestForResource(resource, &container.Resources.Requests)
|
||||
podRequest += value
|
||||
}
|
||||
|
||||
// If Overhead is being utilized, add to the total requests for the pod
|
||||
if pod.Spec.Overhead != nil && utilfeature.DefaultFeatureGate.Enabled(features.PodOverhead) {
|
||||
if quantity, found := pod.Spec.Overhead[resource]; found {
|
||||
podRequest += quantity.Value()
|
||||
}
|
||||
}
|
||||
return podRequest
|
||||
}
|
161
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/resource_limits.go
generated
vendored
Normal file
161
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/resource_limits.go
generated
vendored
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
framework "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1"
|
||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
// ResourceLimits is a score plugin that increases score of input node by 1 if the node satisfies
|
||||
// input pod's resource limits
|
||||
type ResourceLimits struct {
|
||||
handle framework.FrameworkHandle
|
||||
}
|
||||
|
||||
var _ = framework.PreScorePlugin(&ResourceLimits{})
|
||||
var _ = framework.ScorePlugin(&ResourceLimits{})
|
||||
|
||||
const (
|
||||
// ResourceLimitsName is the name of the plugin used in the plugin registry and configurations.
|
||||
ResourceLimitsName = "NodeResourceLimits"
|
||||
|
||||
// preScoreStateKey is the key in CycleState to NodeResourceLimits pre-computed data.
|
||||
// Using the name of the plugin will likely help us avoid collisions with other plugins.
|
||||
preScoreStateKey = "PreScore" + ResourceLimitsName
|
||||
)
|
||||
|
||||
// preScoreState computed at PreScore and used at Score.
|
||||
type preScoreState struct {
|
||||
podResourceRequest *schedulernodeinfo.Resource
|
||||
}
|
||||
|
||||
// Clone the preScore state.
|
||||
func (s *preScoreState) Clone() framework.StateData {
|
||||
return s
|
||||
}
|
||||
|
||||
// Name returns name of the plugin. It is used in logs, etc.
|
||||
func (rl *ResourceLimits) Name() string {
|
||||
return ResourceLimitsName
|
||||
}
|
||||
|
||||
// PreScore builds and writes cycle state used by Score and NormalizeScore.
|
||||
func (rl *ResourceLimits) PreScore(
|
||||
pCtx context.Context,
|
||||
cycleState *framework.CycleState,
|
||||
pod *v1.Pod,
|
||||
nodes []*v1.Node,
|
||||
) *framework.Status {
|
||||
if len(nodes) == 0 {
|
||||
// No nodes to score.
|
||||
return nil
|
||||
}
|
||||
|
||||
if rl.handle.SnapshotSharedLister() == nil {
|
||||
return framework.NewStatus(framework.Error, fmt.Sprintf("empty shared lister"))
|
||||
}
|
||||
s := &preScoreState{
|
||||
podResourceRequest: getResourceLimits(pod),
|
||||
}
|
||||
cycleState.Write(preScoreStateKey, s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func getPodResource(cycleState *framework.CycleState) (*schedulernodeinfo.Resource, error) {
|
||||
c, err := cycleState.Read(preScoreStateKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error reading %q from cycleState: %v", preScoreStateKey, err)
|
||||
}
|
||||
|
||||
s, ok := c.(*preScoreState)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%+v convert to ResourceLimits.preScoreState error", c)
|
||||
}
|
||||
return s.podResourceRequest, nil
|
||||
}
|
||||
|
||||
// Score invoked at the Score extension point.
|
||||
// The "score" returned in this function is the matching number of pods on the `nodeName`.
|
||||
// Currently works as follows:
|
||||
// If a node does not publish its allocatable resources (cpu and memory both), the node score is not affected.
|
||||
// If a pod does not specify its cpu and memory limits both, the node score is not affected.
|
||||
// If one or both of cpu and memory limits of the pod are satisfied, the node is assigned a score of 1.
|
||||
// Rationale of choosing the lowest score of 1 is that this is mainly selected to break ties between nodes that have
|
||||
// same scores assigned by one of least and most requested priority functions.
|
||||
func (rl *ResourceLimits) Score(ctx context.Context, state *framework.CycleState, pod *v1.Pod, nodeName string) (int64, *framework.Status) {
|
||||
nodeInfo, err := rl.handle.SnapshotSharedLister().NodeInfos().Get(nodeName)
|
||||
if err != nil || nodeInfo.Node() == nil {
|
||||
return 0, framework.NewStatus(framework.Error, fmt.Sprintf("getting node %q from Snapshot: %v, node is nil: %v", nodeName, err, nodeInfo.Node() == nil))
|
||||
}
|
||||
allocatableResources := nodeInfo.AllocatableResource()
|
||||
podLimits, err := getPodResource(state)
|
||||
if err != nil {
|
||||
return 0, framework.NewStatus(framework.Error, err.Error())
|
||||
}
|
||||
|
||||
cpuScore := computeScore(podLimits.MilliCPU, allocatableResources.MilliCPU)
|
||||
memScore := computeScore(podLimits.Memory, allocatableResources.Memory)
|
||||
|
||||
score := int64(0)
|
||||
if cpuScore == 1 || memScore == 1 {
|
||||
score = 1
|
||||
}
|
||||
return score, nil
|
||||
}
|
||||
|
||||
// ScoreExtensions of the Score plugin.
|
||||
func (rl *ResourceLimits) ScoreExtensions() framework.ScoreExtensions {
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewResourceLimits initializes a new plugin and returns it.
|
||||
func NewResourceLimits(_ *runtime.Unknown, h framework.FrameworkHandle) (framework.Plugin, error) {
|
||||
return &ResourceLimits{handle: h}, nil
|
||||
}
|
||||
|
||||
// getResourceLimits computes resource limits for input pod.
|
||||
// The reason to create this new function is to be consistent with other
|
||||
// priority functions because most or perhaps all priority functions work
|
||||
// with schedulernodeinfo.Resource.
|
||||
func getResourceLimits(pod *v1.Pod) *schedulernodeinfo.Resource {
|
||||
result := &schedulernodeinfo.Resource{}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
result.Add(container.Resources.Limits)
|
||||
}
|
||||
|
||||
// take max_resource(sum_pod, any_init_container)
|
||||
for _, container := range pod.Spec.InitContainers {
|
||||
result.SetMaxResource(container.Resources.Limits)
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// computeScore returns 1 if limit value is less than or equal to allocatable
|
||||
// value, otherwise it returns 0.
|
||||
func computeScore(limit, allocatable int64) int64 {
|
||||
if limit != 0 && allocatable != 0 && limit <= allocatable {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
55
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/test_util.go
generated
vendored
Normal file
55
vendor/k8s.io/kubernetes/pkg/scheduler/framework/plugins/noderesources/test_util.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
Copyright 2019 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 noderesources
|
||||
|
||||
import (
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func makeNode(node string, milliCPU, memory int64) *v1.Node {
|
||||
return &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: node},
|
||||
Status: v1.NodeStatus{
|
||||
Capacity: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
|
||||
},
|
||||
Allocatable: v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func makeNodeWithExtendedResource(node string, milliCPU, memory int64, extendedResource map[string]int64) *v1.Node {
|
||||
resourceList := make(map[v1.ResourceName]resource.Quantity)
|
||||
for res, quantity := range extendedResource {
|
||||
resourceList[v1.ResourceName(res)] = *resource.NewQuantity(quantity, resource.DecimalSI)
|
||||
}
|
||||
resourceList[v1.ResourceCPU] = *resource.NewMilliQuantity(milliCPU, resource.DecimalSI)
|
||||
resourceList[v1.ResourceMemory] = *resource.NewQuantity(memory, resource.BinarySI)
|
||||
return &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: node},
|
||||
Status: v1.NodeStatus{
|
||||
Capacity: resourceList,
|
||||
Allocatable: resourceList,
|
||||
},
|
||||
}
|
||||
}
|
70
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/BUILD
generated
vendored
Normal file
70
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/BUILD
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cycle_state.go",
|
||||
"framework.go",
|
||||
"interface.go",
|
||||
"metrics_recorder.go",
|
||||
"registry.go",
|
||||
"waiting_pods_map.go",
|
||||
],
|
||||
importpath = "k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//pkg/controller/volume/scheduling:go_default_library",
|
||||
"//pkg/scheduler/apis/config:go_default_library",
|
||||
"//pkg/scheduler/listers:go_default_library",
|
||||
"//pkg/scheduler/metrics:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//pkg/scheduler/util:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/json:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/informers:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/kubernetes:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/util/workqueue:go_default_library",
|
||||
"//staging/src/k8s.io/component-base/metrics:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = [
|
||||
"cycle_state_test.go",
|
||||
"framework_test.go",
|
||||
"interface_test.go",
|
||||
"registry_test.go",
|
||||
],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//pkg/scheduler/apis/config:go_default_library",
|
||||
"//pkg/scheduler/metrics:go_default_library",
|
||||
"//pkg/scheduler/nodeinfo:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_golang/prometheus:go_default_library",
|
||||
"//vendor/github.com/prometheus/client_model/go:go_default_library",
|
||||
],
|
||||
)
|
130
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/cycle_state.go
generated
vendored
Normal file
130
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/cycle_state.go
generated
vendored
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
Copyright 2019 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 v1alpha1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// NotFound is the not found error message.
|
||||
NotFound = "not found"
|
||||
)
|
||||
|
||||
// StateData is a generic type for arbitrary data stored in CycleState.
|
||||
type StateData interface {
|
||||
// Clone is an interface to make a copy of StateData. For performance reasons,
|
||||
// clone should make shallow copies for members (e.g., slices or maps) that are not
|
||||
// impacted by PreFilter's optional AddPod/RemovePod methods.
|
||||
Clone() StateData
|
||||
}
|
||||
|
||||
// StateKey is the type of keys stored in CycleState.
|
||||
type StateKey string
|
||||
|
||||
// CycleState provides a mechanism for plugins to store and retrieve arbitrary data.
|
||||
// StateData stored by one plugin can be read, altered, or deleted by another plugin.
|
||||
// CycleState does not provide any data protection, as all plugins are assumed to be
|
||||
// trusted.
|
||||
type CycleState struct {
|
||||
mx sync.RWMutex
|
||||
storage map[StateKey]StateData
|
||||
// if recordPluginMetrics is true, PluginExecutionDuration will be recorded for this cycle.
|
||||
recordPluginMetrics bool
|
||||
}
|
||||
|
||||
// NewCycleState initializes a new CycleState and returns its pointer.
|
||||
func NewCycleState() *CycleState {
|
||||
return &CycleState{
|
||||
storage: make(map[StateKey]StateData),
|
||||
}
|
||||
}
|
||||
|
||||
// ShouldRecordPluginMetrics returns whether PluginExecutionDuration metrics should be recorded.
|
||||
func (c *CycleState) ShouldRecordPluginMetrics() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.recordPluginMetrics
|
||||
}
|
||||
|
||||
// SetRecordPluginMetrics sets recordPluginMetrics to the given value.
|
||||
func (c *CycleState) SetRecordPluginMetrics(flag bool) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
c.recordPluginMetrics = flag
|
||||
}
|
||||
|
||||
// Clone creates a copy of CycleState and returns its pointer. Clone returns
|
||||
// nil if the context being cloned is nil.
|
||||
func (c *CycleState) Clone() *CycleState {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
copy := NewCycleState()
|
||||
for k, v := range c.storage {
|
||||
copy.Write(k, v.Clone())
|
||||
}
|
||||
return copy
|
||||
}
|
||||
|
||||
// Read retrieves data with the given "key" from CycleState. If the key is not
|
||||
// present an error is returned.
|
||||
// This function is not thread safe. In multi-threaded code, lock should be
|
||||
// acquired first.
|
||||
func (c *CycleState) Read(key StateKey) (StateData, error) {
|
||||
if v, ok := c.storage[key]; ok {
|
||||
return v, nil
|
||||
}
|
||||
return nil, errors.New(NotFound)
|
||||
}
|
||||
|
||||
// Write stores the given "val" in CycleState with the given "key".
|
||||
// This function is not thread safe. In multi-threaded code, lock should be
|
||||
// acquired first.
|
||||
func (c *CycleState) Write(key StateKey, val StateData) {
|
||||
c.storage[key] = val
|
||||
}
|
||||
|
||||
// Delete deletes data with the given key from CycleState.
|
||||
// This function is not thread safe. In multi-threaded code, lock should be
|
||||
// acquired first.
|
||||
func (c *CycleState) Delete(key StateKey) {
|
||||
delete(c.storage, key)
|
||||
}
|
||||
|
||||
// Lock acquires CycleState lock.
|
||||
func (c *CycleState) Lock() {
|
||||
c.mx.Lock()
|
||||
}
|
||||
|
||||
// Unlock releases CycleState lock.
|
||||
func (c *CycleState) Unlock() {
|
||||
c.mx.Unlock()
|
||||
}
|
||||
|
||||
// RLock acquires CycleState read lock.
|
||||
func (c *CycleState) RLock() {
|
||||
c.mx.RLock()
|
||||
}
|
||||
|
||||
// RUnlock releases CycleState read lock.
|
||||
func (c *CycleState) RUnlock() {
|
||||
c.mx.RUnlock()
|
||||
}
|
918
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/framework.go
generated
vendored
Normal file
918
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/framework.go
generated
vendored
Normal file
@ -0,0 +1,918 @@
|
||||
/*
|
||||
Copyright 2019 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 v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/util/workqueue"
|
||||
"k8s.io/klog"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/scheduling"
|
||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
schedulerlisters "k8s.io/kubernetes/pkg/scheduler/listers"
|
||||
"k8s.io/kubernetes/pkg/scheduler/metrics"
|
||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
schedutil "k8s.io/kubernetes/pkg/scheduler/util"
|
||||
)
|
||||
|
||||
const (
|
||||
// Filter is the name of the filter extension point.
|
||||
Filter = "Filter"
|
||||
// Specifies the maximum timeout a permit plugin can return.
|
||||
maxTimeout time.Duration = 15 * time.Minute
|
||||
preFilter = "PreFilter"
|
||||
preFilterExtensionAddPod = "PreFilterExtensionAddPod"
|
||||
preFilterExtensionRemovePod = "PreFilterExtensionRemovePod"
|
||||
preScore = "PreScore"
|
||||
score = "Score"
|
||||
scoreExtensionNormalize = "ScoreExtensionNormalize"
|
||||
preBind = "PreBind"
|
||||
bind = "Bind"
|
||||
postBind = "PostBind"
|
||||
reserve = "Reserve"
|
||||
unreserve = "Unreserve"
|
||||
permit = "Permit"
|
||||
)
|
||||
|
||||
// framework is the component responsible for initializing and running scheduler
|
||||
// plugins.
|
||||
type framework struct {
|
||||
registry Registry
|
||||
snapshotSharedLister schedulerlisters.SharedLister
|
||||
waitingPods *waitingPodsMap
|
||||
pluginNameToWeightMap map[string]int
|
||||
queueSortPlugins []QueueSortPlugin
|
||||
preFilterPlugins []PreFilterPlugin
|
||||
filterPlugins []FilterPlugin
|
||||
preScorePlugins []PreScorePlugin
|
||||
scorePlugins []ScorePlugin
|
||||
reservePlugins []ReservePlugin
|
||||
preBindPlugins []PreBindPlugin
|
||||
bindPlugins []BindPlugin
|
||||
postBindPlugins []PostBindPlugin
|
||||
unreservePlugins []UnreservePlugin
|
||||
permitPlugins []PermitPlugin
|
||||
|
||||
clientSet clientset.Interface
|
||||
informerFactory informers.SharedInformerFactory
|
||||
volumeBinder scheduling.SchedulerVolumeBinder
|
||||
|
||||
metricsRecorder *metricsRecorder
|
||||
|
||||
// Indicates that RunFilterPlugins should accumulate all failed statuses and not return
|
||||
// after the first failure.
|
||||
runAllFilters bool
|
||||
}
|
||||
|
||||
// extensionPoint encapsulates desired and applied set of plugins at a specific extension
|
||||
// point. This is used to simplify iterating over all extension points supported by the
|
||||
// framework.
|
||||
type extensionPoint struct {
|
||||
// the set of plugins to be configured at this extension point.
|
||||
plugins *config.PluginSet
|
||||
// a pointer to the slice storing plugins implementations that will run at this
|
||||
// extension point.
|
||||
slicePtr interface{}
|
||||
}
|
||||
|
||||
func (f *framework) getExtensionPoints(plugins *config.Plugins) []extensionPoint {
|
||||
return []extensionPoint{
|
||||
{plugins.PreFilter, &f.preFilterPlugins},
|
||||
{plugins.Filter, &f.filterPlugins},
|
||||
{plugins.Reserve, &f.reservePlugins},
|
||||
{plugins.PreScore, &f.preScorePlugins},
|
||||
{plugins.Score, &f.scorePlugins},
|
||||
{plugins.PreBind, &f.preBindPlugins},
|
||||
{plugins.Bind, &f.bindPlugins},
|
||||
{plugins.PostBind, &f.postBindPlugins},
|
||||
{plugins.Unreserve, &f.unreservePlugins},
|
||||
{plugins.Permit, &f.permitPlugins},
|
||||
{plugins.QueueSort, &f.queueSortPlugins},
|
||||
}
|
||||
}
|
||||
|
||||
type frameworkOptions struct {
|
||||
clientSet clientset.Interface
|
||||
informerFactory informers.SharedInformerFactory
|
||||
snapshotSharedLister schedulerlisters.SharedLister
|
||||
metricsRecorder *metricsRecorder
|
||||
volumeBinder scheduling.SchedulerVolumeBinder
|
||||
runAllFilters bool
|
||||
}
|
||||
|
||||
// Option for the framework.
|
||||
type Option func(*frameworkOptions)
|
||||
|
||||
// WithClientSet sets clientSet for the scheduling framework.
|
||||
func WithClientSet(clientSet clientset.Interface) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.clientSet = clientSet
|
||||
}
|
||||
}
|
||||
|
||||
// WithInformerFactory sets informer factory for the scheduling framework.
|
||||
func WithInformerFactory(informerFactory informers.SharedInformerFactory) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.informerFactory = informerFactory
|
||||
}
|
||||
}
|
||||
|
||||
// WithSnapshotSharedLister sets the SharedLister of the snapshot.
|
||||
func WithSnapshotSharedLister(snapshotSharedLister schedulerlisters.SharedLister) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.snapshotSharedLister = snapshotSharedLister
|
||||
}
|
||||
}
|
||||
|
||||
// WithRunAllFilters sets the runAllFilters flag, which means RunFilterPlugins accumulates
|
||||
// all failure Statuses.
|
||||
func WithRunAllFilters(runAllFilters bool) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.runAllFilters = runAllFilters
|
||||
}
|
||||
}
|
||||
|
||||
// withMetricsRecorder is only used in tests.
|
||||
func withMetricsRecorder(recorder *metricsRecorder) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.metricsRecorder = recorder
|
||||
}
|
||||
}
|
||||
|
||||
// WithVolumeBinder sets volume binder for the scheduling framework.
|
||||
func WithVolumeBinder(binder scheduling.SchedulerVolumeBinder) Option {
|
||||
return func(o *frameworkOptions) {
|
||||
o.volumeBinder = binder
|
||||
}
|
||||
}
|
||||
|
||||
var defaultFrameworkOptions = frameworkOptions{
|
||||
metricsRecorder: newMetricsRecorder(1000, time.Second),
|
||||
}
|
||||
|
||||
var _ Framework = &framework{}
|
||||
|
||||
// NewFramework initializes plugins given the configuration and the registry.
|
||||
func NewFramework(r Registry, plugins *config.Plugins, args []config.PluginConfig, opts ...Option) (Framework, error) {
|
||||
options := defaultFrameworkOptions
|
||||
for _, opt := range opts {
|
||||
opt(&options)
|
||||
}
|
||||
|
||||
f := &framework{
|
||||
registry: r,
|
||||
snapshotSharedLister: options.snapshotSharedLister,
|
||||
pluginNameToWeightMap: make(map[string]int),
|
||||
waitingPods: newWaitingPodsMap(),
|
||||
clientSet: options.clientSet,
|
||||
informerFactory: options.informerFactory,
|
||||
volumeBinder: options.volumeBinder,
|
||||
metricsRecorder: options.metricsRecorder,
|
||||
runAllFilters: options.runAllFilters,
|
||||
}
|
||||
if plugins == nil {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
// get needed plugins from config
|
||||
pg := f.pluginsNeeded(plugins)
|
||||
|
||||
pluginConfig := make(map[string]*runtime.Unknown, 0)
|
||||
for i := range args {
|
||||
name := args[i].Name
|
||||
if _, ok := pluginConfig[name]; ok {
|
||||
return nil, fmt.Errorf("repeated config for plugin %s", name)
|
||||
}
|
||||
pluginConfig[name] = &args[i].Args
|
||||
}
|
||||
|
||||
pluginsMap := make(map[string]Plugin)
|
||||
var totalPriority int64
|
||||
for name, factory := range r {
|
||||
// initialize only needed plugins.
|
||||
if _, ok := pg[name]; !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
p, err := factory(pluginConfig[name], f)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error initializing plugin %q: %v", name, err)
|
||||
}
|
||||
pluginsMap[name] = p
|
||||
|
||||
// a weight of zero is not permitted, plugins can be disabled explicitly
|
||||
// when configured.
|
||||
f.pluginNameToWeightMap[name] = int(pg[name].Weight)
|
||||
if f.pluginNameToWeightMap[name] == 0 {
|
||||
f.pluginNameToWeightMap[name] = 1
|
||||
}
|
||||
// Checks totalPriority against MaxTotalScore to avoid overflow
|
||||
if int64(f.pluginNameToWeightMap[name])*MaxNodeScore > MaxTotalScore-totalPriority {
|
||||
return nil, fmt.Errorf("total score of Score plugins could overflow")
|
||||
}
|
||||
totalPriority += int64(f.pluginNameToWeightMap[name]) * MaxNodeScore
|
||||
}
|
||||
|
||||
for _, e := range f.getExtensionPoints(plugins) {
|
||||
if err := updatePluginList(e.slicePtr, e.plugins, pluginsMap); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Verifying the score weights again since Plugin.Name() could return a different
|
||||
// value from the one used in the configuration.
|
||||
for _, scorePlugin := range f.scorePlugins {
|
||||
if f.pluginNameToWeightMap[scorePlugin.Name()] == 0 {
|
||||
return nil, fmt.Errorf("score plugin %q is not configured with weight", scorePlugin.Name())
|
||||
}
|
||||
}
|
||||
|
||||
if len(f.queueSortPlugins) == 0 {
|
||||
return nil, fmt.Errorf("no queue sort plugin is enabled")
|
||||
}
|
||||
if len(f.queueSortPlugins) > 1 {
|
||||
return nil, fmt.Errorf("only one queue sort plugin can be enabled")
|
||||
}
|
||||
if len(f.bindPlugins) == 0 {
|
||||
return nil, fmt.Errorf("at least one bind plugin is needed")
|
||||
}
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func updatePluginList(pluginList interface{}, pluginSet *config.PluginSet, pluginsMap map[string]Plugin) error {
|
||||
if pluginSet == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
plugins := reflect.ValueOf(pluginList).Elem()
|
||||
pluginType := plugins.Type().Elem()
|
||||
set := sets.NewString()
|
||||
for _, ep := range pluginSet.Enabled {
|
||||
pg, ok := pluginsMap[ep.Name]
|
||||
if !ok {
|
||||
return fmt.Errorf("%s %q does not exist", pluginType.Name(), ep.Name)
|
||||
}
|
||||
|
||||
if !reflect.TypeOf(pg).Implements(pluginType) {
|
||||
return fmt.Errorf("plugin %q does not extend %s plugin", ep.Name, pluginType.Name())
|
||||
}
|
||||
|
||||
if set.Has(ep.Name) {
|
||||
return fmt.Errorf("plugin %q already registered as %q", ep.Name, pluginType.Name())
|
||||
}
|
||||
|
||||
set.Insert(ep.Name)
|
||||
|
||||
newPlugins := reflect.Append(plugins, reflect.ValueOf(pg))
|
||||
plugins.Set(newPlugins)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// QueueSortFunc returns the function to sort pods in scheduling queue
|
||||
func (f *framework) QueueSortFunc() LessFunc {
|
||||
if f == nil {
|
||||
// If framework is nil, simply keep their order unchanged.
|
||||
// NOTE: this is primarily for tests.
|
||||
return func(_, _ *PodInfo) bool { return false }
|
||||
}
|
||||
|
||||
if len(f.queueSortPlugins) == 0 {
|
||||
panic("No QueueSort plugin is registered in the framework.")
|
||||
}
|
||||
|
||||
// Only one QueueSort plugin can be enabled.
|
||||
return f.queueSortPlugins[0].Less
|
||||
}
|
||||
|
||||
// RunPreFilterPlugins runs the set of configured PreFilter plugins. It returns
|
||||
// *Status and its code is set to non-success if any of the plugins returns
|
||||
// anything but Success. If a non-success status is returned, then the scheduling
|
||||
// cycle is aborted.
|
||||
func (f *framework) RunPreFilterPlugins(ctx context.Context, state *CycleState, pod *v1.Pod) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(preFilter, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.preFilterPlugins {
|
||||
status = f.runPreFilterPlugin(ctx, pl, state, pod)
|
||||
if !status.IsSuccess() {
|
||||
if status.IsUnschedulable() {
|
||||
msg := fmt.Sprintf("rejected by %q at prefilter: %v", pl.Name(), status.Message())
|
||||
klog.V(4).Infof(msg)
|
||||
return NewStatus(status.Code(), msg)
|
||||
}
|
||||
msg := fmt.Sprintf("error while running %q prefilter plugin for pod %q: %v", pl.Name(), pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPreFilterPlugin(ctx context.Context, pl PreFilterPlugin, state *CycleState, pod *v1.Pod) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.PreFilter(ctx, state, pod)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.PreFilter(ctx, state, pod)
|
||||
f.metricsRecorder.observePluginDurationAsync(preFilter, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunPreFilterExtensionAddPod calls the AddPod interface for the set of configured
|
||||
// PreFilter plugins. It returns directly if any of the plugins return any
|
||||
// status other than Success.
|
||||
func (f *framework) RunPreFilterExtensionAddPod(
|
||||
ctx context.Context,
|
||||
state *CycleState,
|
||||
podToSchedule *v1.Pod,
|
||||
podToAdd *v1.Pod,
|
||||
nodeInfo *schedulernodeinfo.NodeInfo,
|
||||
) (status *Status) {
|
||||
for _, pl := range f.preFilterPlugins {
|
||||
if pl.PreFilterExtensions() == nil {
|
||||
continue
|
||||
}
|
||||
status = f.runPreFilterExtensionAddPod(ctx, pl, state, podToSchedule, podToAdd, nodeInfo)
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("error while running AddPod for plugin %q while scheduling pod %q: %v",
|
||||
pl.Name(), podToSchedule.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPreFilterExtensionAddPod(ctx context.Context, pl PreFilterPlugin, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.PreFilterExtensions().AddPod(ctx, state, podToSchedule, podToAdd, nodeInfo)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.PreFilterExtensions().AddPod(ctx, state, podToSchedule, podToAdd, nodeInfo)
|
||||
f.metricsRecorder.observePluginDurationAsync(preFilterExtensionAddPod, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunPreFilterExtensionRemovePod calls the RemovePod interface for the set of configured
|
||||
// PreFilter plugins. It returns directly if any of the plugins return any
|
||||
// status other than Success.
|
||||
func (f *framework) RunPreFilterExtensionRemovePod(
|
||||
ctx context.Context,
|
||||
state *CycleState,
|
||||
podToSchedule *v1.Pod,
|
||||
podToRemove *v1.Pod,
|
||||
nodeInfo *schedulernodeinfo.NodeInfo,
|
||||
) (status *Status) {
|
||||
for _, pl := range f.preFilterPlugins {
|
||||
if pl.PreFilterExtensions() == nil {
|
||||
continue
|
||||
}
|
||||
status = f.runPreFilterExtensionRemovePod(ctx, pl, state, podToSchedule, podToRemove, nodeInfo)
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("error while running RemovePod for plugin %q while scheduling pod %q: %v",
|
||||
pl.Name(), podToSchedule.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPreFilterExtensionRemovePod(ctx context.Context, pl PreFilterPlugin, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.PreFilterExtensions().RemovePod(ctx, state, podToSchedule, podToAdd, nodeInfo)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.PreFilterExtensions().RemovePod(ctx, state, podToSchedule, podToAdd, nodeInfo)
|
||||
f.metricsRecorder.observePluginDurationAsync(preFilterExtensionRemovePod, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunFilterPlugins runs the set of configured Filter plugins for pod on
|
||||
// the given node. If any of these plugins doesn't return "Success", the
|
||||
// given node is not suitable for running pod.
|
||||
// Meanwhile, the failure message and status are set for the given node.
|
||||
func (f *framework) RunFilterPlugins(
|
||||
ctx context.Context,
|
||||
state *CycleState,
|
||||
pod *v1.Pod,
|
||||
nodeInfo *schedulernodeinfo.NodeInfo,
|
||||
) PluginToStatus {
|
||||
var firstFailedStatus *Status
|
||||
statuses := make(PluginToStatus)
|
||||
for _, pl := range f.filterPlugins {
|
||||
pluginStatus := f.runFilterPlugin(ctx, pl, state, pod, nodeInfo)
|
||||
if len(statuses) == 0 {
|
||||
firstFailedStatus = pluginStatus
|
||||
}
|
||||
if !pluginStatus.IsSuccess() {
|
||||
if !pluginStatus.IsUnschedulable() {
|
||||
// Filter plugins are not supposed to return any status other than
|
||||
// Success or Unschedulable.
|
||||
firstFailedStatus = NewStatus(Error, fmt.Sprintf("running %q filter plugin for pod %q: %v", pl.Name(), pod.Name, pluginStatus.Message()))
|
||||
return map[string]*Status{pl.Name(): firstFailedStatus}
|
||||
}
|
||||
statuses[pl.Name()] = pluginStatus
|
||||
if !f.runAllFilters {
|
||||
// Exit early if we don't need to run all filters.
|
||||
return statuses
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return statuses
|
||||
}
|
||||
|
||||
func (f *framework) runFilterPlugin(ctx context.Context, pl FilterPlugin, state *CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.Filter(ctx, state, pod, nodeInfo)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.Filter(ctx, state, pod, nodeInfo)
|
||||
f.metricsRecorder.observePluginDurationAsync(Filter, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunPreScorePlugins runs the set of configured pre-score plugins. If any
|
||||
// of these plugins returns any status other than "Success", the given pod is rejected.
|
||||
func (f *framework) RunPreScorePlugins(
|
||||
ctx context.Context,
|
||||
state *CycleState,
|
||||
pod *v1.Pod,
|
||||
nodes []*v1.Node,
|
||||
) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(preScore, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.preScorePlugins {
|
||||
status = f.runPreScorePlugin(ctx, pl, state, pod, nodes)
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("error while running %q prescore plugin for pod %q: %v", pl.Name(), pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPreScorePlugin(ctx context.Context, pl PreScorePlugin, state *CycleState, pod *v1.Pod, nodes []*v1.Node) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.PreScore(ctx, state, pod, nodes)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.PreScore(ctx, state, pod, nodes)
|
||||
f.metricsRecorder.observePluginDurationAsync(preScore, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunScorePlugins runs the set of configured scoring plugins. It returns a list that
|
||||
// stores for each scoring plugin name the corresponding NodeScoreList(s).
|
||||
// It also returns *Status, which is set to non-success if any of the plugins returns
|
||||
// a non-success status.
|
||||
func (f *framework) RunScorePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) (ps PluginToNodeScores, status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(score, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
pluginToNodeScores := make(PluginToNodeScores, len(f.scorePlugins))
|
||||
for _, pl := range f.scorePlugins {
|
||||
pluginToNodeScores[pl.Name()] = make(NodeScoreList, len(nodes))
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
errCh := schedutil.NewErrorChannel()
|
||||
|
||||
// Run Score method for each node in parallel.
|
||||
workqueue.ParallelizeUntil(ctx, 16, len(nodes), func(index int) {
|
||||
for _, pl := range f.scorePlugins {
|
||||
nodeName := nodes[index].Name
|
||||
s, status := f.runScorePlugin(ctx, pl, state, pod, nodeName)
|
||||
if !status.IsSuccess() {
|
||||
errCh.SendErrorWithCancel(fmt.Errorf(status.Message()), cancel)
|
||||
return
|
||||
}
|
||||
pluginToNodeScores[pl.Name()][index] = NodeScore{
|
||||
Name: nodeName,
|
||||
Score: int64(s),
|
||||
}
|
||||
}
|
||||
})
|
||||
if err := errCh.ReceiveError(); err != nil {
|
||||
msg := fmt.Sprintf("error while running score plugin for pod %q: %v", pod.Name, err)
|
||||
klog.Error(msg)
|
||||
return nil, NewStatus(Error, msg)
|
||||
}
|
||||
|
||||
// Run NormalizeScore method for each ScorePlugin in parallel.
|
||||
workqueue.ParallelizeUntil(ctx, 16, len(f.scorePlugins), func(index int) {
|
||||
pl := f.scorePlugins[index]
|
||||
nodeScoreList := pluginToNodeScores[pl.Name()]
|
||||
if pl.ScoreExtensions() == nil {
|
||||
return
|
||||
}
|
||||
status := f.runScoreExtension(ctx, pl, state, pod, nodeScoreList)
|
||||
if !status.IsSuccess() {
|
||||
err := fmt.Errorf("normalize score plugin %q failed with error %v", pl.Name(), status.Message())
|
||||
errCh.SendErrorWithCancel(err, cancel)
|
||||
return
|
||||
}
|
||||
})
|
||||
if err := errCh.ReceiveError(); err != nil {
|
||||
msg := fmt.Sprintf("error while running normalize score plugin for pod %q: %v", pod.Name, err)
|
||||
klog.Error(msg)
|
||||
return nil, NewStatus(Error, msg)
|
||||
}
|
||||
|
||||
// Apply score defaultWeights for each ScorePlugin in parallel.
|
||||
workqueue.ParallelizeUntil(ctx, 16, len(f.scorePlugins), func(index int) {
|
||||
pl := f.scorePlugins[index]
|
||||
// Score plugins' weight has been checked when they are initialized.
|
||||
weight := f.pluginNameToWeightMap[pl.Name()]
|
||||
nodeScoreList := pluginToNodeScores[pl.Name()]
|
||||
|
||||
for i, nodeScore := range nodeScoreList {
|
||||
// return error if score plugin returns invalid score.
|
||||
if nodeScore.Score > int64(MaxNodeScore) || nodeScore.Score < int64(MinNodeScore) {
|
||||
err := fmt.Errorf("score plugin %q returns an invalid score %v, it should in the range of [%v, %v] after normalizing", pl.Name(), nodeScore.Score, MinNodeScore, MaxNodeScore)
|
||||
errCh.SendErrorWithCancel(err, cancel)
|
||||
return
|
||||
}
|
||||
nodeScoreList[i].Score = nodeScore.Score * int64(weight)
|
||||
}
|
||||
})
|
||||
if err := errCh.ReceiveError(); err != nil {
|
||||
msg := fmt.Sprintf("error while applying score defaultWeights for pod %q: %v", pod.Name, err)
|
||||
klog.Error(msg)
|
||||
return nil, NewStatus(Error, msg)
|
||||
}
|
||||
|
||||
return pluginToNodeScores, nil
|
||||
}
|
||||
|
||||
func (f *framework) runScorePlugin(ctx context.Context, pl ScorePlugin, state *CycleState, pod *v1.Pod, nodeName string) (int64, *Status) {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.Score(ctx, state, pod, nodeName)
|
||||
}
|
||||
startTime := time.Now()
|
||||
s, status := pl.Score(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(score, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return s, status
|
||||
}
|
||||
|
||||
func (f *framework) runScoreExtension(ctx context.Context, pl ScorePlugin, state *CycleState, pod *v1.Pod, nodeScoreList NodeScoreList) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.ScoreExtensions().NormalizeScore(ctx, state, pod, nodeScoreList)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.ScoreExtensions().NormalizeScore(ctx, state, pod, nodeScoreList)
|
||||
f.metricsRecorder.observePluginDurationAsync(scoreExtensionNormalize, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunPreBindPlugins runs the set of configured prebind plugins. It returns a
|
||||
// failure (bool) if any of the plugins returns an error. It also returns an
|
||||
// error containing the rejection message or the error occurred in the plugin.
|
||||
func (f *framework) RunPreBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(preBind, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.preBindPlugins {
|
||||
status = f.runPreBindPlugin(ctx, pl, state, pod, nodeName)
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("error while running %q prebind plugin for pod %q: %v", pl.Name(), pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPreBindPlugin(ctx context.Context, pl PreBindPlugin, state *CycleState, pod *v1.Pod, nodeName string) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.PreBind(ctx, state, pod, nodeName)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.PreBind(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(preBind, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunBindPlugins runs the set of configured bind plugins until one returns a non `Skip` status.
|
||||
func (f *framework) RunBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(bind, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
if len(f.bindPlugins) == 0 {
|
||||
return NewStatus(Skip, "")
|
||||
}
|
||||
for _, bp := range f.bindPlugins {
|
||||
status = f.runBindPlugin(ctx, bp, state, pod, nodeName)
|
||||
if status != nil && status.Code() == Skip {
|
||||
continue
|
||||
}
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("plugin %q failed to bind pod \"%v/%v\": %v", bp.Name(), pod.Namespace, pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
return status
|
||||
}
|
||||
return status
|
||||
}
|
||||
|
||||
func (f *framework) runBindPlugin(ctx context.Context, bp BindPlugin, state *CycleState, pod *v1.Pod, nodeName string) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return bp.Bind(ctx, state, pod, nodeName)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := bp.Bind(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(bind, bp.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunPostBindPlugins runs the set of configured postbind plugins.
|
||||
func (f *framework) RunPostBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(postBind, Success.String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.postBindPlugins {
|
||||
f.runPostBindPlugin(ctx, pl, state, pod, nodeName)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *framework) runPostBindPlugin(ctx context.Context, pl PostBindPlugin, state *CycleState, pod *v1.Pod, nodeName string) {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
pl.PostBind(ctx, state, pod, nodeName)
|
||||
return
|
||||
}
|
||||
startTime := time.Now()
|
||||
pl.PostBind(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(postBind, pl.Name(), nil, metrics.SinceInSeconds(startTime))
|
||||
}
|
||||
|
||||
// RunReservePlugins runs the set of configured reserve plugins. If any of these
|
||||
// plugins returns an error, it does not continue running the remaining ones and
|
||||
// returns the error. In such case, pod will not be scheduled.
|
||||
func (f *framework) RunReservePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(reserve, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.reservePlugins {
|
||||
status = f.runReservePlugin(ctx, pl, state, pod, nodeName)
|
||||
if !status.IsSuccess() {
|
||||
msg := fmt.Sprintf("error while running %q reserve plugin for pod %q: %v", pl.Name(), pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runReservePlugin(ctx context.Context, pl ReservePlugin, state *CycleState, pod *v1.Pod, nodeName string) *Status {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.Reserve(ctx, state, pod, nodeName)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status := pl.Reserve(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(reserve, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status
|
||||
}
|
||||
|
||||
// RunUnreservePlugins runs the set of configured unreserve plugins.
|
||||
func (f *framework) RunUnreservePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(unreserve, Success.String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
for _, pl := range f.unreservePlugins {
|
||||
f.runUnreservePlugin(ctx, pl, state, pod, nodeName)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *framework) runUnreservePlugin(ctx context.Context, pl UnreservePlugin, state *CycleState, pod *v1.Pod, nodeName string) {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
pl.Unreserve(ctx, state, pod, nodeName)
|
||||
return
|
||||
}
|
||||
startTime := time.Now()
|
||||
pl.Unreserve(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(unreserve, pl.Name(), nil, metrics.SinceInSeconds(startTime))
|
||||
}
|
||||
|
||||
// RunPermitPlugins runs the set of configured permit plugins. If any of these
|
||||
// plugins returns a status other than "Success" or "Wait", it does not continue
|
||||
// running the remaining plugins and returns an error. Otherwise, if any of the
|
||||
// plugins returns "Wait", then this function will create and add waiting pod
|
||||
// to a map of currently waiting pods and return status with "Wait" code.
|
||||
// Pod will remain waiting pod for the minimum duration returned by the permit plugins.
|
||||
func (f *framework) RunPermitPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) (status *Status) {
|
||||
startTime := time.Now()
|
||||
defer func() {
|
||||
metrics.FrameworkExtensionPointDuration.WithLabelValues(permit, status.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
}()
|
||||
pluginsWaitTime := make(map[string]time.Duration)
|
||||
statusCode := Success
|
||||
for _, pl := range f.permitPlugins {
|
||||
status, timeout := f.runPermitPlugin(ctx, pl, state, pod, nodeName)
|
||||
if !status.IsSuccess() {
|
||||
if status.IsUnschedulable() {
|
||||
msg := fmt.Sprintf("rejected pod %q by permit plugin %q: %v", pod.Name, pl.Name(), status.Message())
|
||||
klog.V(4).Infof(msg)
|
||||
return NewStatus(status.Code(), msg)
|
||||
}
|
||||
if status.Code() == Wait {
|
||||
// Not allowed to be greater than maxTimeout.
|
||||
if timeout > maxTimeout {
|
||||
timeout = maxTimeout
|
||||
}
|
||||
pluginsWaitTime[pl.Name()] = timeout
|
||||
statusCode = Wait
|
||||
} else {
|
||||
msg := fmt.Sprintf("error while running %q permit plugin for pod %q: %v", pl.Name(), pod.Name, status.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
if statusCode == Wait {
|
||||
waitingPod := newWaitingPod(pod, pluginsWaitTime)
|
||||
f.waitingPods.add(waitingPod)
|
||||
msg := fmt.Sprintf("one or more plugins asked to wait and no plugin rejected pod %q", pod.Name)
|
||||
klog.V(4).Infof(msg)
|
||||
return NewStatus(Wait, msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *framework) runPermitPlugin(ctx context.Context, pl PermitPlugin, state *CycleState, pod *v1.Pod, nodeName string) (*Status, time.Duration) {
|
||||
if !state.ShouldRecordPluginMetrics() {
|
||||
return pl.Permit(ctx, state, pod, nodeName)
|
||||
}
|
||||
startTime := time.Now()
|
||||
status, timeout := pl.Permit(ctx, state, pod, nodeName)
|
||||
f.metricsRecorder.observePluginDurationAsync(permit, pl.Name(), status, metrics.SinceInSeconds(startTime))
|
||||
return status, timeout
|
||||
}
|
||||
|
||||
// WaitOnPermit will block, if the pod is a waiting pod, until the waiting pod is rejected or allowed.
|
||||
func (f *framework) WaitOnPermit(ctx context.Context, pod *v1.Pod) (status *Status) {
|
||||
waitingPod := f.waitingPods.get(pod.UID)
|
||||
if waitingPod == nil {
|
||||
return nil
|
||||
}
|
||||
defer f.waitingPods.remove(pod.UID)
|
||||
klog.V(4).Infof("pod %q waiting on permit", pod.Name)
|
||||
|
||||
startTime := time.Now()
|
||||
s := <-waitingPod.s
|
||||
metrics.PermitWaitDuration.WithLabelValues(s.Code().String()).Observe(metrics.SinceInSeconds(startTime))
|
||||
|
||||
if !s.IsSuccess() {
|
||||
if s.IsUnschedulable() {
|
||||
msg := fmt.Sprintf("pod %q rejected while waiting on permit: %v", pod.Name, s.Message())
|
||||
klog.V(4).Infof(msg)
|
||||
return NewStatus(s.Code(), msg)
|
||||
}
|
||||
msg := fmt.Sprintf("error received while waiting on permit for pod %q: %v", pod.Name, s.Message())
|
||||
klog.Error(msg)
|
||||
return NewStatus(Error, msg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SnapshotSharedLister returns the scheduler's SharedLister of the latest NodeInfo
|
||||
// snapshot. The snapshot is taken at the beginning of a scheduling cycle and remains
|
||||
// unchanged until a pod finishes "Reserve". There is no guarantee that the information
|
||||
// remains unchanged after "Reserve".
|
||||
func (f *framework) SnapshotSharedLister() schedulerlisters.SharedLister {
|
||||
return f.snapshotSharedLister
|
||||
}
|
||||
|
||||
// IterateOverWaitingPods acquires a read lock and iterates over the WaitingPods map.
|
||||
func (f *framework) IterateOverWaitingPods(callback func(WaitingPod)) {
|
||||
f.waitingPods.iterate(callback)
|
||||
}
|
||||
|
||||
// GetWaitingPod returns a reference to a WaitingPod given its UID.
|
||||
func (f *framework) GetWaitingPod(uid types.UID) WaitingPod {
|
||||
if wp := f.waitingPods.get(uid); wp != nil {
|
||||
return wp
|
||||
}
|
||||
return nil // Returning nil instead of *waitingPod(nil).
|
||||
}
|
||||
|
||||
// RejectWaitingPod rejects a WaitingPod given its UID.
|
||||
func (f *framework) RejectWaitingPod(uid types.UID) {
|
||||
waitingPod := f.waitingPods.get(uid)
|
||||
if waitingPod != nil {
|
||||
waitingPod.Reject("removed")
|
||||
}
|
||||
}
|
||||
|
||||
// HasFilterPlugins returns true if at least one filter plugin is defined.
|
||||
func (f *framework) HasFilterPlugins() bool {
|
||||
return len(f.filterPlugins) > 0
|
||||
}
|
||||
|
||||
// HasScorePlugins returns true if at least one score plugin is defined.
|
||||
func (f *framework) HasScorePlugins() bool {
|
||||
return len(f.scorePlugins) > 0
|
||||
}
|
||||
|
||||
// ListPlugins returns a map of extension point name to plugin names configured at each extension
|
||||
// point. Returns nil if no plugins where configred.
|
||||
func (f *framework) ListPlugins() map[string][]config.Plugin {
|
||||
m := make(map[string][]config.Plugin)
|
||||
|
||||
for _, e := range f.getExtensionPoints(&config.Plugins{}) {
|
||||
plugins := reflect.ValueOf(e.slicePtr).Elem()
|
||||
extName := plugins.Type().Elem().Name()
|
||||
var cfgs []config.Plugin
|
||||
for i := 0; i < plugins.Len(); i++ {
|
||||
name := plugins.Index(i).Interface().(Plugin).Name()
|
||||
p := config.Plugin{Name: name}
|
||||
if extName == "ScorePlugin" {
|
||||
// Weights apply only to score plugins.
|
||||
p.Weight = int32(f.pluginNameToWeightMap[name])
|
||||
}
|
||||
cfgs = append(cfgs, p)
|
||||
}
|
||||
if len(cfgs) > 0 {
|
||||
m[extName] = cfgs
|
||||
}
|
||||
}
|
||||
if len(m) > 0 {
|
||||
return m
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ClientSet returns a kubernetes clientset.
|
||||
func (f *framework) ClientSet() clientset.Interface {
|
||||
return f.clientSet
|
||||
}
|
||||
|
||||
// SharedInformerFactory returns a shared informer factory.
|
||||
func (f *framework) SharedInformerFactory() informers.SharedInformerFactory {
|
||||
return f.informerFactory
|
||||
}
|
||||
|
||||
// VolumeBinder returns the volume binder used by scheduler.
|
||||
func (f *framework) VolumeBinder() scheduling.SchedulerVolumeBinder {
|
||||
return f.volumeBinder
|
||||
}
|
||||
|
||||
func (f *framework) pluginsNeeded(plugins *config.Plugins) map[string]config.Plugin {
|
||||
pgMap := make(map[string]config.Plugin)
|
||||
|
||||
if plugins == nil {
|
||||
return pgMap
|
||||
}
|
||||
|
||||
find := func(pgs *config.PluginSet) {
|
||||
if pgs == nil {
|
||||
return
|
||||
}
|
||||
for _, pg := range pgs.Enabled {
|
||||
pgMap[pg.Name] = pg
|
||||
}
|
||||
}
|
||||
for _, e := range f.getExtensionPoints(plugins) {
|
||||
find(e.plugins)
|
||||
}
|
||||
return pgMap
|
||||
}
|
525
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/interface.go
generated
vendored
Normal file
525
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/interface.go
generated
vendored
Normal file
@ -0,0 +1,525 @@
|
||||
/*
|
||||
Copyright 2019 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.
|
||||
*/
|
||||
|
||||
// This file defines the scheduling framework plugin interfaces.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/informers"
|
||||
clientset "k8s.io/client-go/kubernetes"
|
||||
"k8s.io/kubernetes/pkg/controller/volume/scheduling"
|
||||
"k8s.io/kubernetes/pkg/scheduler/apis/config"
|
||||
schedulerlisters "k8s.io/kubernetes/pkg/scheduler/listers"
|
||||
schedulernodeinfo "k8s.io/kubernetes/pkg/scheduler/nodeinfo"
|
||||
)
|
||||
|
||||
// NodeScoreList declares a list of nodes and their scores.
|
||||
type NodeScoreList []NodeScore
|
||||
|
||||
// NodeScore is a struct with node name and score.
|
||||
type NodeScore struct {
|
||||
Name string
|
||||
Score int64
|
||||
}
|
||||
|
||||
// PluginToNodeScores declares a map from plugin name to its NodeScoreList.
|
||||
type PluginToNodeScores map[string]NodeScoreList
|
||||
|
||||
// NodeToStatusMap declares map from node name to its status.
|
||||
type NodeToStatusMap map[string]*Status
|
||||
|
||||
// Code is the Status code/type which is returned from plugins.
|
||||
type Code int
|
||||
|
||||
// These are predefined codes used in a Status.
|
||||
const (
|
||||
// Success means that plugin ran correctly and found pod schedulable.
|
||||
// NOTE: A nil status is also considered as "Success".
|
||||
Success Code = iota
|
||||
// Error is used for internal plugin errors, unexpected input, etc.
|
||||
Error
|
||||
// Unschedulable is used when a plugin finds a pod unschedulable. The scheduler might attempt to
|
||||
// preempt other pods to get this pod scheduled. Use UnschedulableAndUnresolvable to make the
|
||||
// scheduler skip preemption.
|
||||
// The accompanying status message should explain why the pod is unschedulable.
|
||||
Unschedulable
|
||||
// UnschedulableAndUnresolvable is used when a (pre-)filter plugin finds a pod unschedulable and
|
||||
// preemption would not change anything. Plugins should return Unschedulable if it is possible
|
||||
// that the pod can get scheduled with preemption.
|
||||
// The accompanying status message should explain why the pod is unschedulable.
|
||||
UnschedulableAndUnresolvable
|
||||
// Wait is used when a permit plugin finds a pod scheduling should wait.
|
||||
Wait
|
||||
// Skip is used when a bind plugin chooses to skip binding.
|
||||
Skip
|
||||
)
|
||||
|
||||
// This list should be exactly the same as the codes iota defined above in the same order.
|
||||
var codes = []string{"Success", "Error", "Unschedulable", "UnschedulableAndUnresolvable", "Wait", "Skip"}
|
||||
|
||||
func (c Code) String() string {
|
||||
return codes[c]
|
||||
}
|
||||
|
||||
const (
|
||||
// MaxNodeScore is the maximum score a Score plugin is expected to return.
|
||||
MaxNodeScore int64 = 100
|
||||
|
||||
// MinNodeScore is the minimum score a Score plugin is expected to return.
|
||||
MinNodeScore int64 = 0
|
||||
|
||||
// MaxTotalScore is the maximum total score.
|
||||
MaxTotalScore int64 = math.MaxInt64
|
||||
)
|
||||
|
||||
// Status indicates the result of running a plugin. It consists of a code and a
|
||||
// message. When the status code is not `Success`, the reasons should explain why.
|
||||
// NOTE: A nil Status is also considered as Success.
|
||||
type Status struct {
|
||||
code Code
|
||||
reasons []string
|
||||
}
|
||||
|
||||
// Code returns code of the Status.
|
||||
func (s *Status) Code() Code {
|
||||
if s == nil {
|
||||
return Success
|
||||
}
|
||||
return s.code
|
||||
}
|
||||
|
||||
// Message returns a concatenated message on reasons of the Status.
|
||||
func (s *Status) Message() string {
|
||||
if s == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.Join(s.reasons, ", ")
|
||||
}
|
||||
|
||||
// Reasons returns reasons of the Status.
|
||||
func (s *Status) Reasons() []string {
|
||||
return s.reasons
|
||||
}
|
||||
|
||||
// AppendReason appends given reason to the Status.
|
||||
func (s *Status) AppendReason(reason string) {
|
||||
s.reasons = append(s.reasons, reason)
|
||||
}
|
||||
|
||||
// IsSuccess returns true if and only if "Status" is nil or Code is "Success".
|
||||
func (s *Status) IsSuccess() bool {
|
||||
return s.Code() == Success
|
||||
}
|
||||
|
||||
// IsUnschedulable returns true if "Status" is Unschedulable (Unschedulable or UnschedulableAndUnresolvable).
|
||||
func (s *Status) IsUnschedulable() bool {
|
||||
code := s.Code()
|
||||
return code == Unschedulable || code == UnschedulableAndUnresolvable
|
||||
}
|
||||
|
||||
// AsError returns nil if the status is a success; otherwise returns an "error" object
|
||||
// with a concatenated message on reasons of the Status.
|
||||
func (s *Status) AsError() error {
|
||||
if s.IsSuccess() {
|
||||
return nil
|
||||
}
|
||||
return errors.New(s.Message())
|
||||
}
|
||||
|
||||
// NewStatus makes a Status out of the given arguments and returns its pointer.
|
||||
func NewStatus(code Code, reasons ...string) *Status {
|
||||
return &Status{
|
||||
code: code,
|
||||
reasons: reasons,
|
||||
}
|
||||
}
|
||||
|
||||
// PluginToStatus maps plugin name to status. Currently used to identify which Filter plugin
|
||||
// returned which status.
|
||||
type PluginToStatus map[string]*Status
|
||||
|
||||
// Merge merges the statuses in the map into one. The resulting status code have the following
|
||||
// precedence: Error, UnschedulableAndUnresolvable, Unschedulable.
|
||||
func (p PluginToStatus) Merge() *Status {
|
||||
if len(p) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
finalStatus := NewStatus(Success)
|
||||
var hasError, hasUnschedulableAndUnresolvable, hasUnschedulable bool
|
||||
for _, s := range p {
|
||||
if s.Code() == Error {
|
||||
hasError = true
|
||||
} else if s.Code() == UnschedulableAndUnresolvable {
|
||||
hasUnschedulableAndUnresolvable = true
|
||||
} else if s.Code() == Unschedulable {
|
||||
hasUnschedulable = true
|
||||
}
|
||||
finalStatus.code = s.Code()
|
||||
for _, r := range s.reasons {
|
||||
finalStatus.AppendReason(r)
|
||||
}
|
||||
}
|
||||
|
||||
if hasError {
|
||||
finalStatus.code = Error
|
||||
} else if hasUnschedulableAndUnresolvable {
|
||||
finalStatus.code = UnschedulableAndUnresolvable
|
||||
} else if hasUnschedulable {
|
||||
finalStatus.code = Unschedulable
|
||||
}
|
||||
return finalStatus
|
||||
}
|
||||
|
||||
// WaitingPod represents a pod currently waiting in the permit phase.
|
||||
type WaitingPod interface {
|
||||
// GetPod returns a reference to the waiting pod.
|
||||
GetPod() *v1.Pod
|
||||
// GetPendingPlugins returns a list of pending permit plugin's name.
|
||||
GetPendingPlugins() []string
|
||||
// Allow declares the waiting pod is allowed to be scheduled by plugin pluginName.
|
||||
// If this is the last remaining plugin to allow, then a success signal is delivered
|
||||
// to unblock the pod.
|
||||
Allow(pluginName string)
|
||||
// Reject declares the waiting pod unschedulable.
|
||||
Reject(msg string)
|
||||
}
|
||||
|
||||
// Plugin is the parent type for all the scheduling framework plugins.
|
||||
type Plugin interface {
|
||||
Name() string
|
||||
}
|
||||
|
||||
// PodInfo is a wrapper to a Pod with additional information for purposes such as tracking
|
||||
// the timestamp when it's added to the queue or recording per-pod metrics.
|
||||
type PodInfo struct {
|
||||
Pod *v1.Pod
|
||||
// The time pod added to the scheduling queue.
|
||||
Timestamp time.Time
|
||||
// Number of schedule attempts before successfully scheduled.
|
||||
// It's used to record the # attempts metric.
|
||||
Attempts int
|
||||
// The time when the pod is added to the queue for the first time. The pod may be added
|
||||
// back to the queue multiple times before it's successfully scheduled.
|
||||
// It shouldn't be updated once initialized. It's used to record the e2e scheduling
|
||||
// latency for a pod.
|
||||
InitialAttemptTimestamp time.Time
|
||||
}
|
||||
|
||||
// DeepCopy returns a deep copy of the PodInfo object.
|
||||
func (podInfo *PodInfo) DeepCopy() *PodInfo {
|
||||
return &PodInfo{
|
||||
Pod: podInfo.Pod.DeepCopy(),
|
||||
Timestamp: podInfo.Timestamp,
|
||||
Attempts: podInfo.Attempts,
|
||||
InitialAttemptTimestamp: podInfo.InitialAttemptTimestamp,
|
||||
}
|
||||
}
|
||||
|
||||
// LessFunc is the function to sort pod info
|
||||
type LessFunc func(podInfo1, podInfo2 *PodInfo) bool
|
||||
|
||||
// QueueSortPlugin is an interface that must be implemented by "QueueSort" plugins.
|
||||
// These plugins are used to sort pods in the scheduling queue. Only one queue sort
|
||||
// plugin may be enabled at a time.
|
||||
type QueueSortPlugin interface {
|
||||
Plugin
|
||||
// Less are used to sort pods in the scheduling queue.
|
||||
Less(*PodInfo, *PodInfo) bool
|
||||
}
|
||||
|
||||
// PreFilterExtensions is an interface that is included in plugins that allow specifying
|
||||
// callbacks to make incremental updates to its supposedly pre-calculated
|
||||
// state.
|
||||
type PreFilterExtensions interface {
|
||||
// AddPod is called by the framework while trying to evaluate the impact
|
||||
// of adding podToAdd to the node while scheduling podToSchedule.
|
||||
AddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status
|
||||
// RemovePod is called by the framework while trying to evaluate the impact
|
||||
// of removing podToRemove from the node while scheduling podToSchedule.
|
||||
RemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToRemove *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status
|
||||
}
|
||||
|
||||
// PreFilterPlugin is an interface that must be implemented by "prefilter" plugins.
|
||||
// These plugins are called at the beginning of the scheduling cycle.
|
||||
type PreFilterPlugin interface {
|
||||
Plugin
|
||||
// PreFilter is called at the beginning of the scheduling cycle. All PreFilter
|
||||
// plugins must return success or the pod will be rejected.
|
||||
PreFilter(ctx context.Context, state *CycleState, p *v1.Pod) *Status
|
||||
// PreFilterExtensions returns a PreFilterExtensions interface if the plugin implements one,
|
||||
// or nil if it does not. A Pre-filter plugin can provide extensions to incrementally
|
||||
// modify its pre-processed info. The framework guarantees that the extensions
|
||||
// AddPod/RemovePod will only be called after PreFilter, possibly on a cloned
|
||||
// CycleState, and may call those functions more than once before calling
|
||||
// Filter again on a specific node.
|
||||
PreFilterExtensions() PreFilterExtensions
|
||||
}
|
||||
|
||||
// FilterPlugin is an interface for Filter plugins. These plugins are called at the
|
||||
// filter extension point for filtering out hosts that cannot run a pod.
|
||||
// This concept used to be called 'predicate' in the original scheduler.
|
||||
// These plugins should return "Success", "Unschedulable" or "Error" in Status.code.
|
||||
// However, the scheduler accepts other valid codes as well.
|
||||
// Anything other than "Success" will lead to exclusion of the given host from
|
||||
// running the pod.
|
||||
type FilterPlugin interface {
|
||||
Plugin
|
||||
// Filter is called by the scheduling framework.
|
||||
// All FilterPlugins should return "Success" to declare that
|
||||
// the given node fits the pod. If Filter doesn't return "Success",
|
||||
// please refer scheduler/algorithm/predicates/error.go
|
||||
// to set error message.
|
||||
// For the node being evaluated, Filter plugins should look at the passed
|
||||
// nodeInfo reference for this particular node's information (e.g., pods
|
||||
// considered to be running on the node) instead of looking it up in the
|
||||
// NodeInfoSnapshot because we don't guarantee that they will be the same.
|
||||
// For example, during preemption, we may pass a copy of the original
|
||||
// nodeInfo object that has some pods removed from it to evaluate the
|
||||
// possibility of preempting them to schedule the target pod.
|
||||
Filter(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status
|
||||
}
|
||||
|
||||
// PreScorePlugin is an interface for Pre-score plugin. Pre-score is an
|
||||
// informational extension point. Plugins will be called with a list of nodes
|
||||
// that passed the filtering phase. A plugin may use this data to update internal
|
||||
// state or to generate logs/metrics.
|
||||
type PreScorePlugin interface {
|
||||
Plugin
|
||||
// PreScore is called by the scheduling framework after a list of nodes
|
||||
// passed the filtering phase. All prescore plugins must return success or
|
||||
// the pod will be rejected
|
||||
PreScore(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) *Status
|
||||
}
|
||||
|
||||
// ScoreExtensions is an interface for Score extended functionality.
|
||||
type ScoreExtensions interface {
|
||||
// NormalizeScore is called for all node scores produced by the same plugin's "Score"
|
||||
// method. A successful run of NormalizeScore will update the scores list and return
|
||||
// a success status.
|
||||
NormalizeScore(ctx context.Context, state *CycleState, p *v1.Pod, scores NodeScoreList) *Status
|
||||
}
|
||||
|
||||
// ScorePlugin is an interface that must be implemented by "score" plugins to rank
|
||||
// nodes that passed the filtering phase.
|
||||
type ScorePlugin interface {
|
||||
Plugin
|
||||
// Score is called on each filtered node. It must return success and an integer
|
||||
// indicating the rank of the node. All scoring plugins must return success or
|
||||
// the pod will be rejected.
|
||||
Score(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (int64, *Status)
|
||||
|
||||
// ScoreExtensions returns a ScoreExtensions interface if it implements one, or nil if does not.
|
||||
ScoreExtensions() ScoreExtensions
|
||||
}
|
||||
|
||||
// ReservePlugin is an interface for Reserve plugins. These plugins are called
|
||||
// at the reservation point. These are meant to update the state of the plugin.
|
||||
// This concept used to be called 'assume' in the original scheduler.
|
||||
// These plugins should return only Success or Error in Status.code. However,
|
||||
// the scheduler accepts other valid codes as well. Anything other than Success
|
||||
// will lead to rejection of the pod.
|
||||
type ReservePlugin interface {
|
||||
Plugin
|
||||
// Reserve is called by the scheduling framework when the scheduler cache is
|
||||
// updated.
|
||||
Reserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
|
||||
}
|
||||
|
||||
// PreBindPlugin is an interface that must be implemented by "prebind" plugins.
|
||||
// These plugins are called before a pod being scheduled.
|
||||
type PreBindPlugin interface {
|
||||
Plugin
|
||||
// PreBind is called before binding a pod. All prebind plugins must return
|
||||
// success or the pod will be rejected and won't be sent for binding.
|
||||
PreBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
|
||||
}
|
||||
|
||||
// PostBindPlugin is an interface that must be implemented by "postbind" plugins.
|
||||
// These plugins are called after a pod is successfully bound to a node.
|
||||
type PostBindPlugin interface {
|
||||
Plugin
|
||||
// PostBind is called after a pod is successfully bound. These plugins are
|
||||
// informational. A common application of this extension point is for cleaning
|
||||
// up. If a plugin needs to clean-up its state after a pod is scheduled and
|
||||
// bound, PostBind is the extension point that it should register.
|
||||
PostBind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string)
|
||||
}
|
||||
|
||||
// UnreservePlugin is an interface for Unreserve plugins. This is an informational
|
||||
// extension point. If a pod was reserved and then rejected in a later phase, then
|
||||
// un-reserve plugins will be notified. Un-reserve plugins should clean up state
|
||||
// associated with the reserved Pod.
|
||||
type UnreservePlugin interface {
|
||||
Plugin
|
||||
// Unreserve is called by the scheduling framework when a reserved pod was
|
||||
// rejected in a later phase.
|
||||
Unreserve(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string)
|
||||
}
|
||||
|
||||
// PermitPlugin is an interface that must be implemented by "permit" plugins.
|
||||
// These plugins are called before a pod is bound to a node.
|
||||
type PermitPlugin interface {
|
||||
Plugin
|
||||
// Permit is called before binding a pod (and before prebind plugins). Permit
|
||||
// plugins are used to prevent or delay the binding of a Pod. A permit plugin
|
||||
// must return success or wait with timeout duration, or the pod will be rejected.
|
||||
// The pod will also be rejected if the wait timeout or the pod is rejected while
|
||||
// waiting. Note that if the plugin returns "wait", the framework will wait only
|
||||
// after running the remaining plugins given that no other plugin rejects the pod.
|
||||
Permit(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) (*Status, time.Duration)
|
||||
}
|
||||
|
||||
// BindPlugin is an interface that must be implemented by "bind" plugins. Bind
|
||||
// plugins are used to bind a pod to a Node.
|
||||
type BindPlugin interface {
|
||||
Plugin
|
||||
// Bind plugins will not be called until all pre-bind plugins have completed. Each
|
||||
// bind plugin is called in the configured order. A bind plugin may choose whether
|
||||
// or not to handle the given Pod. If a bind plugin chooses to handle a Pod, the
|
||||
// remaining bind plugins are skipped. When a bind plugin does not handle a pod,
|
||||
// it must return Skip in its Status code. If a bind plugin returns an Error, the
|
||||
// pod is rejected and will not be bound.
|
||||
Bind(ctx context.Context, state *CycleState, p *v1.Pod, nodeName string) *Status
|
||||
}
|
||||
|
||||
// Framework manages the set of plugins in use by the scheduling framework.
|
||||
// Configured plugins are called at specified points in a scheduling context.
|
||||
type Framework interface {
|
||||
FrameworkHandle
|
||||
// QueueSortFunc returns the function to sort pods in scheduling queue
|
||||
QueueSortFunc() LessFunc
|
||||
|
||||
// RunPreFilterPlugins runs the set of configured prefilter plugins. It returns
|
||||
// *Status and its code is set to non-success if any of the plugins returns
|
||||
// anything but Success. If a non-success status is returned, then the scheduling
|
||||
// cycle is aborted.
|
||||
RunPreFilterPlugins(ctx context.Context, state *CycleState, pod *v1.Pod) *Status
|
||||
|
||||
// RunFilterPlugins runs the set of configured filter plugins for pod on
|
||||
// the given node. Note that for the node being evaluated, the passed nodeInfo
|
||||
// reference could be different from the one in NodeInfoSnapshot map (e.g., pods
|
||||
// considered to be running on the node could be different). For example, during
|
||||
// preemption, we may pass a copy of the original nodeInfo object that has some pods
|
||||
// removed from it to evaluate the possibility of preempting them to
|
||||
// schedule the target pod.
|
||||
RunFilterPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) PluginToStatus
|
||||
|
||||
// RunPreFilterExtensionAddPod calls the AddPod interface for the set of configured
|
||||
// PreFilter plugins. It returns directly if any of the plugins return any
|
||||
// status other than Success.
|
||||
RunPreFilterExtensionAddPod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status
|
||||
|
||||
// RunPreFilterExtensionRemovePod calls the RemovePod interface for the set of configured
|
||||
// PreFilter plugins. It returns directly if any of the plugins return any
|
||||
// status other than Success.
|
||||
RunPreFilterExtensionRemovePod(ctx context.Context, state *CycleState, podToSchedule *v1.Pod, podToAdd *v1.Pod, nodeInfo *schedulernodeinfo.NodeInfo) *Status
|
||||
|
||||
// RunPreScorePlugins runs the set of configured pre-score plugins. If any
|
||||
// of these plugins returns any status other than "Success", the given pod is rejected.
|
||||
RunPreScorePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) *Status
|
||||
|
||||
// RunScorePlugins runs the set of configured scoring plugins. It returns a map that
|
||||
// stores for each scoring plugin name the corresponding NodeScoreList(s).
|
||||
// It also returns *Status, which is set to non-success if any of the plugins returns
|
||||
// a non-success status.
|
||||
RunScorePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodes []*v1.Node) (PluginToNodeScores, *Status)
|
||||
|
||||
// RunPreBindPlugins runs the set of configured prebind plugins. It returns
|
||||
// *Status and its code is set to non-success if any of the plugins returns
|
||||
// anything but Success. If the Status code is "Unschedulable", it is
|
||||
// considered as a scheduling check failure, otherwise, it is considered as an
|
||||
// internal error. In either case the pod is not going to be bound.
|
||||
RunPreBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
|
||||
|
||||
// RunPostBindPlugins runs the set of configured postbind plugins.
|
||||
RunPostBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string)
|
||||
|
||||
// RunReservePlugins runs the set of configured reserve plugins. If any of these
|
||||
// plugins returns an error, it does not continue running the remaining ones and
|
||||
// returns the error. In such case, pod will not be scheduled.
|
||||
RunReservePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
|
||||
|
||||
// RunUnreservePlugins runs the set of configured unreserve plugins.
|
||||
RunUnreservePlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string)
|
||||
|
||||
// RunPermitPlugins runs the set of configured permit plugins. If any of these
|
||||
// plugins returns a status other than "Success" or "Wait", it does not continue
|
||||
// running the remaining plugins and returns an error. Otherwise, if any of the
|
||||
// plugins returns "Wait", then this function will create and add waiting pod
|
||||
// to a map of currently waiting pods and return status with "Wait" code.
|
||||
// Pod will remain waiting pod for the minimum duration returned by the permit plugins.
|
||||
RunPermitPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
|
||||
|
||||
// WaitOnPermit will block, if the pod is a waiting pod, until the waiting pod is rejected or allowed.
|
||||
WaitOnPermit(ctx context.Context, pod *v1.Pod) *Status
|
||||
|
||||
// RunBindPlugins runs the set of configured bind plugins. A bind plugin may choose
|
||||
// whether or not to handle the given Pod. If a bind plugin chooses to skip the
|
||||
// binding, it should return code=5("skip") status. Otherwise, it should return "Error"
|
||||
// or "Success". If none of the plugins handled binding, RunBindPlugins returns
|
||||
// code=5("skip") status.
|
||||
RunBindPlugins(ctx context.Context, state *CycleState, pod *v1.Pod, nodeName string) *Status
|
||||
|
||||
// HasFilterPlugins returns true if at least one filter plugin is defined.
|
||||
HasFilterPlugins() bool
|
||||
|
||||
// HasScorePlugins returns true if at least one score plugin is defined.
|
||||
HasScorePlugins() bool
|
||||
|
||||
// ListPlugins returns a map of extension point name to list of configured Plugins.
|
||||
ListPlugins() map[string][]config.Plugin
|
||||
}
|
||||
|
||||
// FrameworkHandle provides data and some tools that plugins can use. It is
|
||||
// passed to the plugin factories at the time of plugin initialization. Plugins
|
||||
// must store and use this handle to call framework functions.
|
||||
type FrameworkHandle interface {
|
||||
// SnapshotSharedLister returns listers from the latest NodeInfo Snapshot. The snapshot
|
||||
// is taken at the beginning of a scheduling cycle and remains unchanged until
|
||||
// a pod finishes "Permit" point. There is no guarantee that the information
|
||||
// remains unchanged in the binding phase of scheduling, so plugins in the binding
|
||||
// cycle (pre-bind/bind/post-bind/un-reserve plugin) should not use it,
|
||||
// otherwise a concurrent read/write error might occur, they should use scheduler
|
||||
// cache instead.
|
||||
SnapshotSharedLister() schedulerlisters.SharedLister
|
||||
|
||||
// IterateOverWaitingPods acquires a read lock and iterates over the WaitingPods map.
|
||||
IterateOverWaitingPods(callback func(WaitingPod))
|
||||
|
||||
// GetWaitingPod returns a waiting pod given its UID.
|
||||
GetWaitingPod(uid types.UID) WaitingPod
|
||||
|
||||
// RejectWaitingPod rejects a waiting pod given its UID.
|
||||
RejectWaitingPod(uid types.UID)
|
||||
|
||||
// ClientSet returns a kubernetes clientSet.
|
||||
ClientSet() clientset.Interface
|
||||
|
||||
SharedInformerFactory() informers.SharedInformerFactory
|
||||
|
||||
// VolumeBinder returns the volume binder used by scheduler.
|
||||
VolumeBinder() scheduling.SchedulerVolumeBinder
|
||||
}
|
101
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/metrics_recorder.go
generated
vendored
Normal file
101
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/metrics_recorder.go
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
Copyright 2019 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 v1alpha1
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
k8smetrics "k8s.io/component-base/metrics"
|
||||
"k8s.io/kubernetes/pkg/scheduler/metrics"
|
||||
)
|
||||
|
||||
// frameworkMetric is the data structure passed in the buffer channel between the main framework thread
|
||||
// and the metricsRecorder goroutine.
|
||||
type frameworkMetric struct {
|
||||
metric *k8smetrics.HistogramVec
|
||||
labelValues []string
|
||||
value float64
|
||||
}
|
||||
|
||||
// metricRecorder records framework metrics in a separate goroutine to avoid overhead in the critical path.
|
||||
type metricsRecorder struct {
|
||||
// bufferCh is a channel that serves as a metrics buffer before the metricsRecorder goroutine reports it.
|
||||
bufferCh chan *frameworkMetric
|
||||
// if bufferSize is reached, incoming metrics will be discarded.
|
||||
bufferSize int
|
||||
// how often the recorder runs to flush the metrics.
|
||||
interval time.Duration
|
||||
|
||||
// stopCh is used to stop the goroutine which periodically flushes metrics. It's currently only
|
||||
// used in tests.
|
||||
stopCh chan struct{}
|
||||
// isStoppedCh indicates whether the goroutine is stopped. It's used in tests only to make sure
|
||||
// the metric flushing goroutine is stopped so that tests can collect metrics for verification.
|
||||
isStoppedCh chan struct{}
|
||||
}
|
||||
|
||||
func newMetricsRecorder(bufferSize int, interval time.Duration) *metricsRecorder {
|
||||
recorder := &metricsRecorder{
|
||||
bufferCh: make(chan *frameworkMetric, bufferSize),
|
||||
bufferSize: bufferSize,
|
||||
interval: interval,
|
||||
stopCh: make(chan struct{}),
|
||||
isStoppedCh: make(chan struct{}),
|
||||
}
|
||||
go recorder.run()
|
||||
return recorder
|
||||
}
|
||||
|
||||
// observePluginDurationAsync observes the plugin_execution_duration_seconds metric.
|
||||
// The metric will be flushed to Prometheus asynchronously.
|
||||
func (r *metricsRecorder) observePluginDurationAsync(extensionPoint, pluginName string, status *Status, value float64) {
|
||||
newMetric := &frameworkMetric{
|
||||
metric: metrics.PluginExecutionDuration,
|
||||
labelValues: []string{pluginName, extensionPoint, status.Code().String()},
|
||||
value: value,
|
||||
}
|
||||
select {
|
||||
case r.bufferCh <- newMetric:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// run flushes buffered metrics into Prometheus every second.
|
||||
func (r *metricsRecorder) run() {
|
||||
for {
|
||||
select {
|
||||
case <-r.stopCh:
|
||||
close(r.isStoppedCh)
|
||||
return
|
||||
default:
|
||||
}
|
||||
r.flushMetrics()
|
||||
time.Sleep(r.interval)
|
||||
}
|
||||
}
|
||||
|
||||
// flushMetrics tries to clean up the bufferCh by reading at most bufferSize metrics.
|
||||
func (r *metricsRecorder) flushMetrics() {
|
||||
for i := 0; i < r.bufferSize; i++ {
|
||||
select {
|
||||
case m := <-r.bufferCh:
|
||||
m.metric.WithLabelValues(m.labelValues...).Observe(m.value)
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
80
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/registry.go
generated
vendored
Normal file
80
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/registry.go
generated
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
Copyright 2019 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 v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/json"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
// PluginFactory is a function that builds a plugin.
|
||||
type PluginFactory = func(configuration *runtime.Unknown, f FrameworkHandle) (Plugin, error)
|
||||
|
||||
// DecodeInto decodes configuration whose type is *runtime.Unknown to the interface into.
|
||||
func DecodeInto(configuration *runtime.Unknown, into interface{}) error {
|
||||
if configuration == nil || configuration.Raw == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch configuration.ContentType {
|
||||
// If ContentType is empty, it means ContentTypeJSON by default.
|
||||
case runtime.ContentTypeJSON, "":
|
||||
return json.Unmarshal(configuration.Raw, into)
|
||||
case runtime.ContentTypeYAML:
|
||||
return yaml.Unmarshal(configuration.Raw, into)
|
||||
default:
|
||||
return fmt.Errorf("not supported content type %s", configuration.ContentType)
|
||||
}
|
||||
}
|
||||
|
||||
// Registry is a collection of all available plugins. The framework uses a
|
||||
// registry to enable and initialize configured plugins.
|
||||
// All plugins must be in the registry before initializing the framework.
|
||||
type Registry map[string]PluginFactory
|
||||
|
||||
// Register adds a new plugin to the registry. If a plugin with the same name
|
||||
// exists, it returns an error.
|
||||
func (r Registry) Register(name string, factory PluginFactory) error {
|
||||
if _, ok := r[name]; ok {
|
||||
return fmt.Errorf("a plugin named %v already exists", name)
|
||||
}
|
||||
r[name] = factory
|
||||
return nil
|
||||
}
|
||||
|
||||
// Unregister removes an existing plugin from the registry. If no plugin with
|
||||
// the provided name exists, it returns an error.
|
||||
func (r Registry) Unregister(name string) error {
|
||||
if _, ok := r[name]; !ok {
|
||||
return fmt.Errorf("no plugin named %v exists", name)
|
||||
}
|
||||
delete(r, name)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Merge merges the provided registry to the current one.
|
||||
func (r Registry) Merge(in Registry) error {
|
||||
for name, factory := range in {
|
||||
if err := r.Register(name, factory); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
164
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/waiting_pods_map.go
generated
vendored
Normal file
164
vendor/k8s.io/kubernetes/pkg/scheduler/framework/v1alpha1/waiting_pods_map.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
/*
|
||||
Copyright 2019 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 v1alpha1
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
)
|
||||
|
||||
// waitingPodsMap a thread-safe map used to maintain pods waiting in the permit phase.
|
||||
type waitingPodsMap struct {
|
||||
pods map[types.UID]*waitingPod
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// newWaitingPodsMap returns a new waitingPodsMap.
|
||||
func newWaitingPodsMap() *waitingPodsMap {
|
||||
return &waitingPodsMap{
|
||||
pods: make(map[types.UID]*waitingPod),
|
||||
}
|
||||
}
|
||||
|
||||
// add a new WaitingPod to the map.
|
||||
func (m *waitingPodsMap) add(wp *waitingPod) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.pods[wp.GetPod().UID] = wp
|
||||
}
|
||||
|
||||
// remove a WaitingPod from the map.
|
||||
func (m *waitingPodsMap) remove(uid types.UID) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
delete(m.pods, uid)
|
||||
}
|
||||
|
||||
// get a WaitingPod from the map.
|
||||
func (m *waitingPodsMap) get(uid types.UID) *waitingPod {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.pods[uid]
|
||||
}
|
||||
|
||||
// iterate acquires a read lock and iterates over the WaitingPods map.
|
||||
func (m *waitingPodsMap) iterate(callback func(WaitingPod)) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
for _, v := range m.pods {
|
||||
callback(v)
|
||||
}
|
||||
}
|
||||
|
||||
// waitingPod represents a pod waiting in the permit phase.
|
||||
type waitingPod struct {
|
||||
pod *v1.Pod
|
||||
pendingPlugins map[string]*time.Timer
|
||||
s chan *Status
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
var _ WaitingPod = &waitingPod{}
|
||||
|
||||
// newWaitingPod returns a new waitingPod instance.
|
||||
func newWaitingPod(pod *v1.Pod, pluginsMaxWaitTime map[string]time.Duration) *waitingPod {
|
||||
wp := &waitingPod{
|
||||
pod: pod,
|
||||
// Allow() and Reject() calls are non-blocking. This property is guaranteed
|
||||
// by using non-blocking send to this channel. This channel has a buffer of size 1
|
||||
// to ensure that non-blocking send will not be ignored - possible situation when
|
||||
// receiving from this channel happens after non-blocking send.
|
||||
s: make(chan *Status, 1),
|
||||
}
|
||||
|
||||
wp.pendingPlugins = make(map[string]*time.Timer, len(pluginsMaxWaitTime))
|
||||
// The time.AfterFunc calls wp.Reject which iterates through pendingPlugins map. Acquire the
|
||||
// lock here so that time.AfterFunc can only execute after newWaitingPod finishes.
|
||||
wp.mu.Lock()
|
||||
defer wp.mu.Unlock()
|
||||
for k, v := range pluginsMaxWaitTime {
|
||||
plugin, waitTime := k, v
|
||||
wp.pendingPlugins[plugin] = time.AfterFunc(waitTime, func() {
|
||||
msg := fmt.Sprintf("rejected due to timeout after waiting %v at plugin %v",
|
||||
waitTime, plugin)
|
||||
wp.Reject(msg)
|
||||
})
|
||||
}
|
||||
|
||||
return wp
|
||||
}
|
||||
|
||||
// GetPod returns a reference to the waiting pod.
|
||||
func (w *waitingPod) GetPod() *v1.Pod {
|
||||
return w.pod
|
||||
}
|
||||
|
||||
// GetPendingPlugins returns a list of pending permit plugin's name.
|
||||
func (w *waitingPod) GetPendingPlugins() []string {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
plugins := make([]string, 0, len(w.pendingPlugins))
|
||||
for p := range w.pendingPlugins {
|
||||
plugins = append(plugins, p)
|
||||
}
|
||||
|
||||
return plugins
|
||||
}
|
||||
|
||||
// Allow declares the waiting pod is allowed to be scheduled by plugin pluginName.
|
||||
// If this is the last remaining plugin to allow, then a success signal is delivered
|
||||
// to unblock the pod.
|
||||
func (w *waitingPod) Allow(pluginName string) {
|
||||
w.mu.Lock()
|
||||
defer w.mu.Unlock()
|
||||
if timer, exist := w.pendingPlugins[pluginName]; exist {
|
||||
timer.Stop()
|
||||
delete(w.pendingPlugins, pluginName)
|
||||
}
|
||||
|
||||
// Only signal success status after all plugins have allowed
|
||||
if len(w.pendingPlugins) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// The select clause works as a non-blocking send.
|
||||
// If there is no receiver, it's a no-op (default case).
|
||||
select {
|
||||
case w.s <- NewStatus(Success, ""):
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
// Reject declares the waiting pod unschedulable.
|
||||
func (w *waitingPod) Reject(msg string) {
|
||||
w.mu.RLock()
|
||||
defer w.mu.RUnlock()
|
||||
for _, timer := range w.pendingPlugins {
|
||||
timer.Stop()
|
||||
}
|
||||
|
||||
// The select clause works as a non-blocking send.
|
||||
// If there is no receiver, it's a no-op (default case).
|
||||
select {
|
||||
case w.s <- NewStatus(Unschedulable, msg):
|
||||
default:
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user