Replace godep with dep
This commit is contained in:
parent
1e7489927c
commit
bf5616c65b
14883 changed files with 3937406 additions and 361781 deletions
56
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/BUILD
generated
vendored
Normal file
56
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/BUILD
generated
vendored
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package(default_visibility = ["//visibility:public"])
|
||||
|
||||
load(
|
||||
"@io_bazel_rules_go//go:def.bzl",
|
||||
"go_library",
|
||||
"go_test",
|
||||
)
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = [
|
||||
"cache.go",
|
||||
"interface.go",
|
||||
"node_info.go",
|
||||
"util.go",
|
||||
],
|
||||
deps = [
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library",
|
||||
"//plugin/pkg/scheduler/util:go_default_library",
|
||||
"//vendor/github.com/golang/glog:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
|
||||
"//vendor/k8s.io/client-go/tools/cache:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["cache_test.go"],
|
||||
library = ":go_default_library",
|
||||
deps = [
|
||||
"//pkg/api/v1/helper:go_default_library",
|
||||
"//plugin/pkg/scheduler/algorithm/priorities/util:go_default_library",
|
||||
"//plugin/pkg/scheduler/util:go_default_library",
|
||||
"//vendor/k8s.io/api/core/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//vendor/k8s.io/apimachinery/pkg/labels:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
)
|
||||
392
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/cache.go
generated
vendored
Normal file
392
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/cache.go
generated
vendored
Normal file
|
|
@ -0,0 +1,392 @@
|
|||
/*
|
||||
Copyright 2015 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 schedulercache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/util/wait"
|
||||
|
||||
"github.com/golang/glog"
|
||||
)
|
||||
|
||||
var (
|
||||
cleanAssumedPeriod = 1 * time.Second
|
||||
)
|
||||
|
||||
// New returns a Cache implementation.
|
||||
// It automatically starts a go routine that manages expiration of assumed pods.
|
||||
// "ttl" is how long the assumed pod will get expired.
|
||||
// "stop" is the channel that would close the background goroutine.
|
||||
func New(ttl time.Duration, stop <-chan struct{}) Cache {
|
||||
cache := newSchedulerCache(ttl, cleanAssumedPeriod, stop)
|
||||
cache.run()
|
||||
return cache
|
||||
}
|
||||
|
||||
type schedulerCache struct {
|
||||
stop <-chan struct{}
|
||||
ttl time.Duration
|
||||
period time.Duration
|
||||
|
||||
// This mutex guards all fields within this cache struct.
|
||||
mu sync.Mutex
|
||||
// a set of assumed pod keys.
|
||||
// The key could further be used to get an entry in podStates.
|
||||
assumedPods map[string]bool
|
||||
// a map from pod key to podState.
|
||||
podStates map[string]*podState
|
||||
nodes map[string]*NodeInfo
|
||||
}
|
||||
|
||||
type podState struct {
|
||||
pod *v1.Pod
|
||||
// Used by assumedPod to determinate expiration.
|
||||
deadline *time.Time
|
||||
// Used to block cache from expiring assumedPod if binding still runs
|
||||
bindingFinished bool
|
||||
}
|
||||
|
||||
func newSchedulerCache(ttl, period time.Duration, stop <-chan struct{}) *schedulerCache {
|
||||
return &schedulerCache{
|
||||
ttl: ttl,
|
||||
period: period,
|
||||
stop: stop,
|
||||
|
||||
nodes: make(map[string]*NodeInfo),
|
||||
assumedPods: make(map[string]bool),
|
||||
podStates: make(map[string]*podState),
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) UpdateNodeNameToInfoMap(nodeNameToInfo map[string]*NodeInfo) error {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
for name, info := range cache.nodes {
|
||||
if current, ok := nodeNameToInfo[name]; !ok || current.generation != info.generation {
|
||||
nodeNameToInfo[name] = info.Clone()
|
||||
}
|
||||
}
|
||||
for name := range nodeNameToInfo {
|
||||
if _, ok := cache.nodes[name]; !ok {
|
||||
delete(nodeNameToInfo, name)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) List(selector labels.Selector) ([]*v1.Pod, error) {
|
||||
alwaysTrue := func(p *v1.Pod) bool { return true }
|
||||
return cache.FilteredList(alwaysTrue, selector)
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) FilteredList(podFilter PodFilter, selector labels.Selector) ([]*v1.Pod, error) {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
var pods []*v1.Pod
|
||||
for _, info := range cache.nodes {
|
||||
for _, pod := range info.pods {
|
||||
if podFilter(pod) && selector.Matches(labels.Set(pod.Labels)) {
|
||||
pods = append(pods, pod)
|
||||
}
|
||||
}
|
||||
}
|
||||
return pods, nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) AssumePod(pod *v1.Pod) error {
|
||||
key, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
if _, ok := cache.podStates[key]; ok {
|
||||
return fmt.Errorf("pod %v state wasn't initial but get assumed", key)
|
||||
}
|
||||
|
||||
cache.addPod(pod)
|
||||
ps := &podState{
|
||||
pod: pod,
|
||||
}
|
||||
cache.podStates[key] = ps
|
||||
cache.assumedPods[key] = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) FinishBinding(pod *v1.Pod) error {
|
||||
return cache.finishBinding(pod, time.Now())
|
||||
}
|
||||
|
||||
// finishBinding exists to make tests determinitistic by injecting now as an argument
|
||||
func (cache *schedulerCache) finishBinding(pod *v1.Pod, now time.Time) error {
|
||||
key, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
glog.V(5).Infof("Finished binding for pod %v. Can be expired.", key)
|
||||
currState, ok := cache.podStates[key]
|
||||
if ok && cache.assumedPods[key] {
|
||||
dl := now.Add(cache.ttl)
|
||||
currState.bindingFinished = true
|
||||
currState.deadline = &dl
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) ForgetPod(pod *v1.Pod) error {
|
||||
key, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
currState, ok := cache.podStates[key]
|
||||
if ok && currState.pod.Spec.NodeName != pod.Spec.NodeName {
|
||||
return fmt.Errorf("pod %v state was assumed on a different node", key)
|
||||
}
|
||||
|
||||
switch {
|
||||
// Only assumed pod can be forgotten.
|
||||
case ok && cache.assumedPods[key]:
|
||||
err := cache.removePod(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(cache.assumedPods, key)
|
||||
delete(cache.podStates, key)
|
||||
default:
|
||||
return fmt.Errorf("pod %v state wasn't assumed but get forgotten", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assumes that lock is already acquired.
|
||||
func (cache *schedulerCache) addPod(pod *v1.Pod) {
|
||||
n, ok := cache.nodes[pod.Spec.NodeName]
|
||||
if !ok {
|
||||
n = NewNodeInfo()
|
||||
cache.nodes[pod.Spec.NodeName] = n
|
||||
}
|
||||
n.AddPod(pod)
|
||||
}
|
||||
|
||||
// Assumes that lock is already acquired.
|
||||
func (cache *schedulerCache) updatePod(oldPod, newPod *v1.Pod) error {
|
||||
if err := cache.removePod(oldPod); err != nil {
|
||||
return err
|
||||
}
|
||||
cache.addPod(newPod)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Assumes that lock is already acquired.
|
||||
func (cache *schedulerCache) removePod(pod *v1.Pod) error {
|
||||
n := cache.nodes[pod.Spec.NodeName]
|
||||
if err := n.RemovePod(pod); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(n.pods) == 0 && n.node == nil {
|
||||
delete(cache.nodes, pod.Spec.NodeName)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) AddPod(pod *v1.Pod) error {
|
||||
key, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
currState, ok := cache.podStates[key]
|
||||
switch {
|
||||
case ok && cache.assumedPods[key]:
|
||||
if currState.pod.Spec.NodeName != pod.Spec.NodeName {
|
||||
// The pod was added to a different node than it was assumed to.
|
||||
glog.Warningf("Pod %v assumed to a different node than added to.", key)
|
||||
// Clean this up.
|
||||
cache.removePod(currState.pod)
|
||||
cache.addPod(pod)
|
||||
}
|
||||
delete(cache.assumedPods, key)
|
||||
cache.podStates[key].deadline = nil
|
||||
case !ok:
|
||||
// Pod was expired. We should add it back.
|
||||
cache.addPod(pod)
|
||||
ps := &podState{
|
||||
pod: pod,
|
||||
}
|
||||
cache.podStates[key] = ps
|
||||
default:
|
||||
return fmt.Errorf("pod was already in added state. Pod key: %v", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) UpdatePod(oldPod, newPod *v1.Pod) error {
|
||||
key, err := getPodKey(oldPod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
currState, ok := cache.podStates[key]
|
||||
switch {
|
||||
// An assumed pod won't have Update/Remove event. It needs to have Add event
|
||||
// before Update event, in which case the state would change from Assumed to Added.
|
||||
case ok && !cache.assumedPods[key]:
|
||||
if currState.pod.Spec.NodeName != newPod.Spec.NodeName {
|
||||
glog.Errorf("Pod %v updated on a different node than previously added to.", key)
|
||||
glog.Fatalf("Schedulercache is corrupted and can badly affect scheduling decisions")
|
||||
}
|
||||
if err := cache.updatePod(oldPod, newPod); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("pod %v state wasn't added but get updated", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) RemovePod(pod *v1.Pod) error {
|
||||
key, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
currState, ok := cache.podStates[key]
|
||||
switch {
|
||||
// An assumed pod won't have Delete/Remove event. It needs to have Add event
|
||||
// before Remove event, in which case the state would change from Assumed to Added.
|
||||
case ok && !cache.assumedPods[key]:
|
||||
if currState.pod.Spec.NodeName != pod.Spec.NodeName {
|
||||
glog.Errorf("Pod %v removed from a different node than previously added to.", key)
|
||||
glog.Fatalf("Schedulercache is corrupted and can badly affect scheduling decisions")
|
||||
}
|
||||
err := cache.removePod(currState.pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(cache.podStates, key)
|
||||
default:
|
||||
return fmt.Errorf("pod state wasn't added but get removed. Pod key: %v", key)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) AddNode(node *v1.Node) error {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
n, ok := cache.nodes[node.Name]
|
||||
if !ok {
|
||||
n = NewNodeInfo()
|
||||
cache.nodes[node.Name] = n
|
||||
}
|
||||
return n.SetNode(node)
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) UpdateNode(oldNode, newNode *v1.Node) error {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
n, ok := cache.nodes[newNode.Name]
|
||||
if !ok {
|
||||
n = NewNodeInfo()
|
||||
cache.nodes[newNode.Name] = n
|
||||
}
|
||||
return n.SetNode(newNode)
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) RemoveNode(node *v1.Node) error {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
n := cache.nodes[node.Name]
|
||||
if err := n.RemoveNode(node); err != nil {
|
||||
return err
|
||||
}
|
||||
// We remove NodeInfo for this node only if there aren't any pods on this node.
|
||||
// We can't do it unconditionally, because notifications about pods are delivered
|
||||
// in a different watch, and thus can potentially be observed later, even though
|
||||
// they happened before node removal.
|
||||
if len(n.pods) == 0 && n.node == nil {
|
||||
delete(cache.nodes, node.Name)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) run() {
|
||||
go wait.Until(cache.cleanupExpiredAssumedPods, cache.period, cache.stop)
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) cleanupExpiredAssumedPods() {
|
||||
cache.cleanupAssumedPods(time.Now())
|
||||
}
|
||||
|
||||
// cleanupAssumedPods exists for making test deterministic by taking time as input argument.
|
||||
func (cache *schedulerCache) cleanupAssumedPods(now time.Time) {
|
||||
cache.mu.Lock()
|
||||
defer cache.mu.Unlock()
|
||||
|
||||
// The size of assumedPods should be small
|
||||
for key := range cache.assumedPods {
|
||||
ps, ok := cache.podStates[key]
|
||||
if !ok {
|
||||
panic("Key found in assumed set but not in podStates. Potentially a logical error.")
|
||||
}
|
||||
if !ps.bindingFinished {
|
||||
glog.Warningf("Couldn't expire cache for pod %v/%v. Binding is still in progress.",
|
||||
ps.pod.Namespace, ps.pod.Name)
|
||||
continue
|
||||
}
|
||||
if now.After(*ps.deadline) {
|
||||
glog.Warningf("Pod %s/%s expired", ps.pod.Namespace, ps.pod.Name)
|
||||
if err := cache.expirePod(key, ps); err != nil {
|
||||
glog.Errorf("ExpirePod failed for %s: %v", key, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cache *schedulerCache) expirePod(key string, ps *podState) error {
|
||||
if err := cache.removePod(ps.pod); err != nil {
|
||||
return err
|
||||
}
|
||||
delete(cache.assumedPods, key)
|
||||
delete(cache.podStates, key)
|
||||
return nil
|
||||
}
|
||||
878
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/cache_test.go
generated
vendored
Normal file
878
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/cache_test.go
generated
vendored
Normal file
|
|
@ -0,0 +1,878 @@
|
|||
/*
|
||||
Copyright 2015 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 schedulercache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
|
||||
schedutil "k8s.io/kubernetes/plugin/pkg/scheduler/util"
|
||||
)
|
||||
|
||||
func deepEqualWithoutGeneration(t *testing.T, testcase int, actual, expected *NodeInfo) {
|
||||
// Ignore generation field.
|
||||
if actual != nil {
|
||||
actual.generation = 0
|
||||
}
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("#%d: node info get=%s, want=%s", testcase, actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAssumePodScheduled tests that after a pod is assumed, its information is aggregated
|
||||
// on node level.
|
||||
func TestAssumePodScheduled(t *testing.T) {
|
||||
nodeName := "node"
|
||||
testPods := []*v1.Pod{
|
||||
makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test-1", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test-2", "200m", "1Ki", "", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
makeBasePod(t, nodeName, "test-nonzero", "", "", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test", "100m", "500", "oir-foo:3", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test-2", "200m", "1Ki", "oir-foo:5", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
makeBasePod(t, nodeName, "test", "100m", "500", "random-invalid-oir-key:100", []v1.ContainerPort{{}}),
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
pods []*v1.Pod
|
||||
|
||||
wNodeInfo *NodeInfo
|
||||
}{{
|
||||
pods: []*v1.Pod{testPods[0]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[0]},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
},
|
||||
}, {
|
||||
pods: []*v1.Pod{testPods[1], testPods[2]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 300,
|
||||
Memory: 1524,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 300,
|
||||
Memory: 1524,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[1], testPods[2]},
|
||||
usedPorts: map[int]bool{80: true, 8080: true},
|
||||
},
|
||||
}, { // test non-zero request
|
||||
pods: []*v1.Pod{testPods[3]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 0,
|
||||
Memory: 0,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: priorityutil.DefaultMilliCpuRequest,
|
||||
Memory: priorityutil.DefaultMemoryRequest,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[3]},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
},
|
||||
}, {
|
||||
pods: []*v1.Pod{testPods[4]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
ExtendedResources: map[v1.ResourceName]int64{"pod.alpha.kubernetes.io/opaque-int-resource-oir-foo": 3},
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[4]},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
},
|
||||
}, {
|
||||
pods: []*v1.Pod{testPods[4], testPods[5]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 300,
|
||||
Memory: 1524,
|
||||
ExtendedResources: map[v1.ResourceName]int64{"pod.alpha.kubernetes.io/opaque-int-resource-oir-foo": 8},
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 300,
|
||||
Memory: 1524,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[4], testPods[5]},
|
||||
usedPorts: map[int]bool{80: true, 8080: true},
|
||||
},
|
||||
}, {
|
||||
pods: []*v1.Pod{testPods[6]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[6]},
|
||||
usedPorts: map[int]bool{},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(time.Second, time.Second, nil)
|
||||
for _, pod := range tt.pods {
|
||||
if err := cache.AssumePod(pod); err != nil {
|
||||
t.Fatalf("AssumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo)
|
||||
|
||||
for _, pod := range tt.pods {
|
||||
if err := cache.ForgetPod(pod); err != nil {
|
||||
t.Fatalf("ForgetPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
if cache.nodes[nodeName] != nil {
|
||||
t.Errorf("NodeInfo should be cleaned for %s", nodeName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type testExpirePodStruct struct {
|
||||
pod *v1.Pod
|
||||
assumedTime time.Time
|
||||
}
|
||||
|
||||
func assumeAndFinishBinding(cache *schedulerCache, pod *v1.Pod, assumedTime time.Time) error {
|
||||
if err := cache.AssumePod(pod); err != nil {
|
||||
return err
|
||||
}
|
||||
return cache.finishBinding(pod, assumedTime)
|
||||
}
|
||||
|
||||
// TestExpirePod tests that assumed pods will be removed if expired.
|
||||
// The removal will be reflected in node info.
|
||||
func TestExpirePod(t *testing.T) {
|
||||
nodeName := "node"
|
||||
testPods := []*v1.Pod{
|
||||
makeBasePod(t, nodeName, "test-1", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test-2", "200m", "1Ki", "", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
}
|
||||
now := time.Now()
|
||||
ttl := 10 * time.Second
|
||||
tests := []struct {
|
||||
pods []*testExpirePodStruct
|
||||
cleanupTime time.Time
|
||||
|
||||
wNodeInfo *NodeInfo
|
||||
}{{ // assumed pod would expires
|
||||
pods: []*testExpirePodStruct{
|
||||
{pod: testPods[0], assumedTime: now},
|
||||
},
|
||||
cleanupTime: now.Add(2 * ttl),
|
||||
wNodeInfo: nil,
|
||||
}, { // first one would expire, second one would not.
|
||||
pods: []*testExpirePodStruct{
|
||||
{pod: testPods[0], assumedTime: now},
|
||||
{pod: testPods[1], assumedTime: now.Add(3 * ttl / 2)},
|
||||
},
|
||||
cleanupTime: now.Add(2 * ttl),
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[1]},
|
||||
usedPorts: map[int]bool{80: false, 8080: true},
|
||||
},
|
||||
}}
|
||||
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
|
||||
for _, pod := range tt.pods {
|
||||
if err := assumeAndFinishBinding(cache, pod.pod, pod.assumedTime); err != nil {
|
||||
t.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
// pods that have assumedTime + ttl < cleanupTime will get expired and removed
|
||||
cache.cleanupAssumedPods(tt.cleanupTime)
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddPodWillConfirm tests that a pod being Add()ed will be confirmed if assumed.
|
||||
// The pod info should still exist after manually expiring unconfirmed pods.
|
||||
func TestAddPodWillConfirm(t *testing.T) {
|
||||
nodeName := "node"
|
||||
now := time.Now()
|
||||
ttl := 10 * time.Second
|
||||
|
||||
testPods := []*v1.Pod{
|
||||
makeBasePod(t, nodeName, "test-1", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test-2", "200m", "1Ki", "", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
}
|
||||
tests := []struct {
|
||||
podsToAssume []*v1.Pod
|
||||
podsToAdd []*v1.Pod
|
||||
|
||||
wNodeInfo *NodeInfo
|
||||
}{{ // two pod were assumed at same time. But first one is called Add() and gets confirmed.
|
||||
podsToAssume: []*v1.Pod{testPods[0], testPods[1]},
|
||||
podsToAdd: []*v1.Pod{testPods[0]},
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[0]},
|
||||
usedPorts: map[int]bool{80: true, 8080: false},
|
||||
},
|
||||
}}
|
||||
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
for _, podToAssume := range tt.podsToAssume {
|
||||
if err := assumeAndFinishBinding(cache, podToAssume, now); err != nil {
|
||||
t.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
for _, podToAdd := range tt.podsToAdd {
|
||||
if err := cache.AddPod(podToAdd); err != nil {
|
||||
t.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
cache.cleanupAssumedPods(now.Add(2 * ttl))
|
||||
// check after expiration. confirmed pods shouldn't be expired.
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddPodAfterExpiration tests that a pod being Add()ed will be added back if expired.
|
||||
func TestAddPodAfterExpiration(t *testing.T) {
|
||||
nodeName := "node"
|
||||
ttl := 10 * time.Second
|
||||
basePod := makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}})
|
||||
tests := []struct {
|
||||
pod *v1.Pod
|
||||
|
||||
wNodeInfo *NodeInfo
|
||||
}{{
|
||||
pod: basePod,
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{basePod},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
},
|
||||
}}
|
||||
|
||||
now := time.Now()
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
if err := assumeAndFinishBinding(cache, tt.pod, now); err != nil {
|
||||
t.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
cache.cleanupAssumedPods(now.Add(2 * ttl))
|
||||
// It should be expired and removed.
|
||||
n := cache.nodes[nodeName]
|
||||
if n != nil {
|
||||
t.Errorf("#%d: expecting nil node info, but get=%v", i, n)
|
||||
}
|
||||
if err := cache.AddPod(tt.pod); err != nil {
|
||||
t.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
// check after expiration. confirmed pods shouldn't be expired.
|
||||
n = cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdatePod tests that a pod will be updated if added before.
|
||||
func TestUpdatePod(t *testing.T) {
|
||||
nodeName := "node"
|
||||
ttl := 10 * time.Second
|
||||
testPods := []*v1.Pod{
|
||||
makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test", "200m", "1Ki", "", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
}
|
||||
tests := []struct {
|
||||
podsToAssume []*v1.Pod
|
||||
podsToAdd []*v1.Pod
|
||||
podsToUpdate []*v1.Pod
|
||||
|
||||
wNodeInfo []*NodeInfo
|
||||
}{{ // add a pod and then update it twice
|
||||
podsToAdd: []*v1.Pod{testPods[0]},
|
||||
podsToUpdate: []*v1.Pod{testPods[0], testPods[1], testPods[0]},
|
||||
wNodeInfo: []*NodeInfo{{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[1]},
|
||||
usedPorts: map[int]bool{8080: true},
|
||||
}, {
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[0]},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
}},
|
||||
}}
|
||||
|
||||
for _, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
for _, podToAdd := range tt.podsToAdd {
|
||||
if err := cache.AddPod(podToAdd); err != nil {
|
||||
t.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tt.podsToUpdate {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
if err := cache.UpdatePod(tt.podsToUpdate[i-1], tt.podsToUpdate[i]); err != nil {
|
||||
t.Fatalf("UpdatePod failed: %v", err)
|
||||
}
|
||||
// check after expiration. confirmed pods shouldn't be expired.
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo[i-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestExpireAddUpdatePod test the sequence that a pod is expired, added, then updated
|
||||
func TestExpireAddUpdatePod(t *testing.T) {
|
||||
nodeName := "node"
|
||||
ttl := 10 * time.Second
|
||||
testPods := []*v1.Pod{
|
||||
makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}}),
|
||||
makeBasePod(t, nodeName, "test", "200m", "1Ki", "", []v1.ContainerPort{{HostPort: 8080}}),
|
||||
}
|
||||
tests := []struct {
|
||||
podsToAssume []*v1.Pod
|
||||
podsToAdd []*v1.Pod
|
||||
podsToUpdate []*v1.Pod
|
||||
|
||||
wNodeInfo []*NodeInfo
|
||||
}{{ // Pod is assumed, expired, and added. Then it would be updated twice.
|
||||
podsToAssume: []*v1.Pod{testPods[0]},
|
||||
podsToAdd: []*v1.Pod{testPods[0]},
|
||||
podsToUpdate: []*v1.Pod{testPods[0], testPods[1], testPods[0]},
|
||||
wNodeInfo: []*NodeInfo{{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 200,
|
||||
Memory: 1024,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[1]},
|
||||
usedPorts: map[int]bool{8080: true},
|
||||
}, {
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{testPods[0]},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
}},
|
||||
}}
|
||||
|
||||
now := time.Now()
|
||||
for _, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
for _, podToAssume := range tt.podsToAssume {
|
||||
if err := assumeAndFinishBinding(cache, podToAssume, now); err != nil {
|
||||
t.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
cache.cleanupAssumedPods(now.Add(2 * ttl))
|
||||
|
||||
for _, podToAdd := range tt.podsToAdd {
|
||||
if err := cache.AddPod(podToAdd); err != nil {
|
||||
t.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
for i := range tt.podsToUpdate {
|
||||
if i == 0 {
|
||||
continue
|
||||
}
|
||||
if err := cache.UpdatePod(tt.podsToUpdate[i-1], tt.podsToUpdate[i]); err != nil {
|
||||
t.Fatalf("UpdatePod failed: %v", err)
|
||||
}
|
||||
// check after expiration. confirmed pods shouldn't be expired.
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo[i-1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestRemovePod tests after added pod is removed, its information should also be subtracted.
|
||||
func TestRemovePod(t *testing.T) {
|
||||
nodeName := "node"
|
||||
basePod := makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}})
|
||||
tests := []struct {
|
||||
pod *v1.Pod
|
||||
wNodeInfo *NodeInfo
|
||||
}{{
|
||||
pod: basePod,
|
||||
wNodeInfo: &NodeInfo{
|
||||
requestedResource: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
nonzeroRequest: &Resource{
|
||||
MilliCPU: 100,
|
||||
Memory: 500,
|
||||
},
|
||||
allocatableResource: &Resource{},
|
||||
pods: []*v1.Pod{basePod},
|
||||
usedPorts: map[int]bool{80: true},
|
||||
},
|
||||
}}
|
||||
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(time.Second, time.Second, nil)
|
||||
if err := cache.AddPod(tt.pod); err != nil {
|
||||
t.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
n := cache.nodes[nodeName]
|
||||
deepEqualWithoutGeneration(t, i, n, tt.wNodeInfo)
|
||||
|
||||
if err := cache.RemovePod(tt.pod); err != nil {
|
||||
t.Fatalf("RemovePod failed: %v", err)
|
||||
}
|
||||
|
||||
n = cache.nodes[nodeName]
|
||||
if n != nil {
|
||||
t.Errorf("#%d: expecting pod deleted and nil node info, get=%s", i, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestForgetPod(t *testing.T) {
|
||||
nodeName := "node"
|
||||
basePod := makeBasePod(t, nodeName, "test", "100m", "500", "", []v1.ContainerPort{{HostPort: 80}})
|
||||
tests := []struct {
|
||||
pods []*v1.Pod
|
||||
}{{
|
||||
pods: []*v1.Pod{basePod},
|
||||
}}
|
||||
now := time.Now()
|
||||
ttl := 10 * time.Second
|
||||
|
||||
for i, tt := range tests {
|
||||
cache := newSchedulerCache(ttl, time.Second, nil)
|
||||
for _, pod := range tt.pods {
|
||||
if err := assumeAndFinishBinding(cache, pod, now); err != nil {
|
||||
t.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
for _, pod := range tt.pods {
|
||||
if err := cache.ForgetPod(pod); err != nil {
|
||||
t.Fatalf("ForgetPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
cache.cleanupAssumedPods(now.Add(2 * ttl))
|
||||
if n := cache.nodes[nodeName]; n != nil {
|
||||
t.Errorf("#%d: expecting pod deleted and nil node info, get=%s", i, n)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getResourceRequest returns the resource request of all containers in Pods;
|
||||
// excuding initContainers.
|
||||
func getResourceRequest(pod *v1.Pod) v1.ResourceList {
|
||||
result := &Resource{}
|
||||
for _, container := range pod.Spec.Containers {
|
||||
result.Add(container.Resources.Requests)
|
||||
}
|
||||
|
||||
return result.ResourceList()
|
||||
}
|
||||
|
||||
// buildNodeInfo creates a NodeInfo by simulating node operations in cache.
|
||||
func buildNodeInfo(node *v1.Node, pods []*v1.Pod) *NodeInfo {
|
||||
expected := NewNodeInfo()
|
||||
|
||||
// Simulate SetNode.
|
||||
expected.node = node
|
||||
expected.allocatableResource = NewResource(node.Status.Allocatable)
|
||||
expected.taints = node.Spec.Taints
|
||||
expected.generation++
|
||||
|
||||
for _, pod := range pods {
|
||||
// Simulate AddPod
|
||||
expected.pods = append(expected.pods, pod)
|
||||
expected.requestedResource.Add(getResourceRequest(pod))
|
||||
expected.nonzeroRequest.Add(getResourceRequest(pod))
|
||||
expected.usedPorts = schedutil.GetUsedPorts(pod)
|
||||
expected.generation++
|
||||
}
|
||||
|
||||
return expected
|
||||
}
|
||||
|
||||
// TestNodeOperators tests node operations of cache, including add, update
|
||||
// and remove.
|
||||
func TestNodeOperators(t *testing.T) {
|
||||
// Test datas
|
||||
nodeName := "test-node"
|
||||
cpu_1 := resource.MustParse("1000m")
|
||||
mem_100m := resource.MustParse("100m")
|
||||
cpu_half := resource.MustParse("500m")
|
||||
mem_50m := resource.MustParse("50m")
|
||||
resourceFooName := "pod.alpha.kubernetes.io/opaque-int-resource-foo"
|
||||
resourceFoo := resource.MustParse("1")
|
||||
|
||||
tests := []struct {
|
||||
node *v1.Node
|
||||
pods []*v1.Pod
|
||||
}{
|
||||
{
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nodeName,
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Allocatable: v1.ResourceList{
|
||||
v1.ResourceCPU: cpu_1,
|
||||
v1.ResourceMemory: mem_100m,
|
||||
v1.ResourceName(resourceFooName): resourceFoo,
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{
|
||||
Key: "test-key",
|
||||
Value: "test-value",
|
||||
Effect: v1.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pods: []*v1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: cpu_half,
|
||||
v1.ResourceMemory: mem_50m,
|
||||
},
|
||||
},
|
||||
Ports: []v1.ContainerPort{
|
||||
{
|
||||
Name: "http",
|
||||
HostPort: 80,
|
||||
ContainerPort: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
node: &v1.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nodeName,
|
||||
},
|
||||
Status: v1.NodeStatus{
|
||||
Allocatable: v1.ResourceList{
|
||||
v1.ResourceCPU: cpu_1,
|
||||
v1.ResourceMemory: mem_100m,
|
||||
v1.ResourceName(resourceFooName): resourceFoo,
|
||||
},
|
||||
},
|
||||
Spec: v1.NodeSpec{
|
||||
Taints: []v1.Taint{
|
||||
{
|
||||
Key: "test-key",
|
||||
Value: "test-value",
|
||||
Effect: v1.TaintEffectPreferNoSchedule,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
pods: []*v1.Pod{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod1",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: cpu_half,
|
||||
v1.ResourceMemory: mem_50m,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "pod2",
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
NodeName: nodeName,
|
||||
Containers: []v1.Container{
|
||||
{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: v1.ResourceList{
|
||||
v1.ResourceCPU: cpu_half,
|
||||
v1.ResourceMemory: mem_50m,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
expected := buildNodeInfo(test.node, test.pods)
|
||||
node := test.node
|
||||
|
||||
cache := newSchedulerCache(time.Second, time.Second, nil)
|
||||
cache.AddNode(node)
|
||||
for _, pod := range test.pods {
|
||||
cache.AddPod(pod)
|
||||
}
|
||||
|
||||
// Case 1: the node was added into cache successfully.
|
||||
got, found := cache.nodes[node.Name]
|
||||
if !found {
|
||||
t.Errorf("Failed to find node %v in schedulercache.", node.Name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("Failed to add node into schedulercache:\n got: %+v \nexpected: %+v", got, expected)
|
||||
}
|
||||
|
||||
// Case 2: dump cached nodes successfully.
|
||||
cachedNodes := map[string]*NodeInfo{}
|
||||
cache.UpdateNodeNameToInfoMap(cachedNodes)
|
||||
newNode, found := cachedNodes[node.Name]
|
||||
if !found || len(cachedNodes) != 1 {
|
||||
t.Errorf("failed to dump cached nodes:\n got: %v \nexpected: %v", cachedNodes, cache.nodes)
|
||||
}
|
||||
if !reflect.DeepEqual(newNode, expected) {
|
||||
t.Errorf("Failed to clone node:\n got: %+v, \n expected: %+v", newNode, expected)
|
||||
}
|
||||
|
||||
// Case 3: update node attribute successfully.
|
||||
node.Status.Allocatable[v1.ResourceMemory] = mem_50m
|
||||
expected.allocatableResource.Memory = mem_50m.Value()
|
||||
expected.generation++
|
||||
cache.UpdateNode(nil, node)
|
||||
got, found = cache.nodes[node.Name]
|
||||
if !found {
|
||||
t.Errorf("Failed to find node %v in schedulercache after UpdateNode.", node.Name)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(got, expected) {
|
||||
t.Errorf("Failed to update node in schedulercache:\n got: %+v \nexpected: %+v", got, expected)
|
||||
}
|
||||
|
||||
// Case 4: the node can not be removed if pods is not empty.
|
||||
cache.RemoveNode(node)
|
||||
if _, found := cache.nodes[node.Name]; !found {
|
||||
t.Errorf("The node %v should not be removed if pods is not empty.", node.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkList1kNodes30kPods(b *testing.B) {
|
||||
cache := setupCacheOf1kNodes30kPods(b)
|
||||
b.ResetTimer()
|
||||
for n := 0; n < b.N; n++ {
|
||||
cache.List(labels.Everything())
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkExpire100Pods(b *testing.B) {
|
||||
benchmarkExpire(b, 100)
|
||||
}
|
||||
|
||||
func BenchmarkExpire1kPods(b *testing.B) {
|
||||
benchmarkExpire(b, 1000)
|
||||
}
|
||||
|
||||
func BenchmarkExpire10kPods(b *testing.B) {
|
||||
benchmarkExpire(b, 10000)
|
||||
}
|
||||
|
||||
func benchmarkExpire(b *testing.B, podNum int) {
|
||||
now := time.Now()
|
||||
for n := 0; n < b.N; n++ {
|
||||
b.StopTimer()
|
||||
cache := setupCacheWithAssumedPods(b, podNum, now)
|
||||
b.StartTimer()
|
||||
cache.cleanupAssumedPods(now.Add(2 * time.Second))
|
||||
}
|
||||
}
|
||||
|
||||
type testingMode interface {
|
||||
Fatalf(format string, args ...interface{})
|
||||
}
|
||||
|
||||
func makeBasePod(t testingMode, nodeName, objName, cpu, mem, oir string, ports []v1.ContainerPort) *v1.Pod {
|
||||
req := v1.ResourceList{}
|
||||
if cpu != "" {
|
||||
req = v1.ResourceList{
|
||||
v1.ResourceCPU: resource.MustParse(cpu),
|
||||
v1.ResourceMemory: resource.MustParse(mem),
|
||||
}
|
||||
if oir != "" {
|
||||
if len(strings.Split(oir, ":")) != 2 {
|
||||
t.Fatalf("Invalid OIR string")
|
||||
}
|
||||
var name v1.ResourceName
|
||||
if strings.Split(oir, ":")[0] != "random-invalid-oir-key" {
|
||||
name = v1helper.OpaqueIntResourceName(strings.Split(oir, ":")[0])
|
||||
} else {
|
||||
name = v1.ResourceName(strings.Split(oir, ":")[0])
|
||||
}
|
||||
quantity := resource.MustParse(strings.Split(oir, ":")[1])
|
||||
req[name] = quantity
|
||||
}
|
||||
}
|
||||
return &v1.Pod{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "node_info_cache_test",
|
||||
Name: objName,
|
||||
},
|
||||
Spec: v1.PodSpec{
|
||||
Containers: []v1.Container{{
|
||||
Resources: v1.ResourceRequirements{
|
||||
Requests: req,
|
||||
},
|
||||
Ports: ports,
|
||||
}},
|
||||
NodeName: nodeName,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func setupCacheOf1kNodes30kPods(b *testing.B) Cache {
|
||||
cache := newSchedulerCache(time.Second, time.Second, nil)
|
||||
for i := 0; i < 1000; i++ {
|
||||
nodeName := fmt.Sprintf("node-%d", i)
|
||||
for j := 0; j < 30; j++ {
|
||||
objName := fmt.Sprintf("%s-pod-%d", nodeName, j)
|
||||
pod := makeBasePod(b, nodeName, objName, "0", "0", "", nil)
|
||||
|
||||
if err := cache.AddPod(pod); err != nil {
|
||||
b.Fatalf("AddPod failed: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return cache
|
||||
}
|
||||
|
||||
func setupCacheWithAssumedPods(b *testing.B, podNum int, assumedTime time.Time) *schedulerCache {
|
||||
cache := newSchedulerCache(time.Second, time.Second, nil)
|
||||
for i := 0; i < podNum; i++ {
|
||||
nodeName := fmt.Sprintf("node-%d", i/10)
|
||||
objName := fmt.Sprintf("%s-pod-%d", nodeName, i%10)
|
||||
pod := makeBasePod(b, nodeName, objName, "0", "0", "", nil)
|
||||
|
||||
err := assumeAndFinishBinding(cache, pod, assumedTime)
|
||||
if err != nil {
|
||||
b.Fatalf("assumePod failed: %v", err)
|
||||
}
|
||||
}
|
||||
return cache
|
||||
}
|
||||
101
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/interface.go
generated
vendored
Normal file
101
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/interface.go
generated
vendored
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
Copyright 2015 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 schedulercache
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
)
|
||||
|
||||
type PodFilter func(*v1.Pod) bool
|
||||
|
||||
// Cache collects pods' information and provides node-level aggregated information.
|
||||
// It's intended for generic scheduler to do efficient lookup.
|
||||
// Cache's operations are pod centric. It does incremental updates based on pod events.
|
||||
// Pod events are sent via network. We don't have guaranteed delivery of all events:
|
||||
// We use Reflector to list and watch from remote.
|
||||
// Reflector might be slow and do a relist, which would lead to missing events.
|
||||
//
|
||||
// State Machine of a pod's events in scheduler's cache:
|
||||
//
|
||||
//
|
||||
// +-------------------------------------------+ +----+
|
||||
// | Add | | |
|
||||
// | | | | Update
|
||||
// + Assume Add v v |
|
||||
//Initial +--------> Assumed +------------+---> Added <--+
|
||||
// ^ + + | +
|
||||
// | | | | |
|
||||
// | | | Add | | Remove
|
||||
// | | | | |
|
||||
// | | | + |
|
||||
// +----------------+ +-----------> Expired +----> Deleted
|
||||
// Forget Expire
|
||||
//
|
||||
//
|
||||
// Note that an assumed pod can expire, because if we haven't received Add event notifying us
|
||||
// for a while, there might be some problems and we shouldn't keep the pod in cache anymore.
|
||||
//
|
||||
// Note that "Initial", "Expired", and "Deleted" pods do not actually exist in cache.
|
||||
// Based on existing use cases, we are making the following assumptions:
|
||||
// - No pod would be assumed twice
|
||||
// - A pod could be added without going through scheduler. In this case, we will see Add but not Assume event.
|
||||
// - If a pod wasn't added, it wouldn't be removed or updated.
|
||||
// - Both "Expired" and "Deleted" are valid end states. In case of some problems, e.g. network issue,
|
||||
// a pod might have changed its state (e.g. added and deleted) without delivering notification to the cache.
|
||||
type Cache interface {
|
||||
// AssumePod assumes a pod scheduled and aggregates the pod's information into its node.
|
||||
// The implementation also decides the policy to expire pod before being confirmed (receiving Add event).
|
||||
// After expiration, its information would be subtracted.
|
||||
AssumePod(pod *v1.Pod) error
|
||||
|
||||
// FinishBinding signals that cache for assumed pod can be expired
|
||||
FinishBinding(pod *v1.Pod) error
|
||||
|
||||
// ForgetPod removes an assumed pod from cache.
|
||||
ForgetPod(pod *v1.Pod) error
|
||||
|
||||
// AddPod either confirms a pod if it's assumed, or adds it back if it's expired.
|
||||
// If added back, the pod's information would be added again.
|
||||
AddPod(pod *v1.Pod) error
|
||||
|
||||
// UpdatePod removes oldPod's information and adds newPod's information.
|
||||
UpdatePod(oldPod, newPod *v1.Pod) error
|
||||
|
||||
// RemovePod removes a pod. The pod's information would be subtracted from assigned node.
|
||||
RemovePod(pod *v1.Pod) error
|
||||
|
||||
// AddNode adds overall information about node.
|
||||
AddNode(node *v1.Node) error
|
||||
|
||||
// UpdateNode updates overall information about node.
|
||||
UpdateNode(oldNode, newNode *v1.Node) error
|
||||
|
||||
// RemoveNode removes overall information about node.
|
||||
RemoveNode(node *v1.Node) error
|
||||
|
||||
// UpdateNodeNameToInfoMap updates the passed infoMap to the current contents of Cache.
|
||||
// The node info contains aggregated information of pods scheduled (including assumed to be)
|
||||
// on this node.
|
||||
UpdateNodeNameToInfoMap(infoMap map[string]*NodeInfo) error
|
||||
|
||||
// List lists all cached pods (including assumed ones).
|
||||
List(labels.Selector) ([]*v1.Pod, error)
|
||||
|
||||
// FilteredList returns all cached pods that pass the filter.
|
||||
FilteredList(filter PodFilter, selector labels.Selector) ([]*v1.Pod, error)
|
||||
}
|
||||
531
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/node_info.go
generated
vendored
Normal file
531
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/node_info.go
generated
vendored
Normal file
|
|
@ -0,0 +1,531 @@
|
|||
/*
|
||||
Copyright 2015 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 schedulercache
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
clientcache "k8s.io/client-go/tools/cache"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
priorityutil "k8s.io/kubernetes/plugin/pkg/scheduler/algorithm/priorities/util"
|
||||
"k8s.io/kubernetes/plugin/pkg/scheduler/util"
|
||||
)
|
||||
|
||||
var emptyResource = Resource{}
|
||||
|
||||
// NodeInfo is node level aggregated information.
|
||||
type NodeInfo struct {
|
||||
// Overall node information.
|
||||
node *v1.Node
|
||||
|
||||
pods []*v1.Pod
|
||||
podsWithAffinity []*v1.Pod
|
||||
usedPorts map[int]bool
|
||||
|
||||
// Total requested resource of all pods on this node.
|
||||
// It includes assumed pods which scheduler sends binding to apiserver but
|
||||
// didn't get it as scheduled yet.
|
||||
requestedResource *Resource
|
||||
nonzeroRequest *Resource
|
||||
// We store allocatedResources (which is Node.Status.Allocatable.*) explicitly
|
||||
// as int64, to avoid conversions and accessing map.
|
||||
allocatableResource *Resource
|
||||
|
||||
// Cached tains of the node for faster lookup.
|
||||
taints []v1.Taint
|
||||
taintsErr error
|
||||
|
||||
// Cached conditions of node for faster lookup.
|
||||
memoryPressureCondition v1.ConditionStatus
|
||||
diskPressureCondition v1.ConditionStatus
|
||||
|
||||
// Whenever NodeInfo changes, generation is bumped.
|
||||
// This is used to avoid cloning it if the object didn't change.
|
||||
generation int64
|
||||
}
|
||||
|
||||
// Resource is a collection of compute resource.
|
||||
type Resource struct {
|
||||
MilliCPU int64
|
||||
Memory int64
|
||||
NvidiaGPU int64
|
||||
EphemeralStorage int64
|
||||
// We store allowedPodNumber (which is Node.Status.Allocatable.Pods().Value())
|
||||
// explicitly as int, to avoid conversions and improve performance.
|
||||
AllowedPodNumber int
|
||||
ExtendedResources map[v1.ResourceName]int64
|
||||
HugePages map[v1.ResourceName]int64
|
||||
}
|
||||
|
||||
// New creates a Resource from ResourceList
|
||||
func NewResource(rl v1.ResourceList) *Resource {
|
||||
r := &Resource{}
|
||||
r.Add(rl)
|
||||
return r
|
||||
}
|
||||
|
||||
// Add adds ResourceList into Resource.
|
||||
func (r *Resource) Add(rl v1.ResourceList) {
|
||||
if r == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for rName, rQuant := range rl {
|
||||
switch rName {
|
||||
case v1.ResourceCPU:
|
||||
r.MilliCPU += rQuant.MilliValue()
|
||||
case v1.ResourceMemory:
|
||||
r.Memory += rQuant.Value()
|
||||
case v1.ResourceNvidiaGPU:
|
||||
r.NvidiaGPU += rQuant.Value()
|
||||
case v1.ResourcePods:
|
||||
r.AllowedPodNumber += int(rQuant.Value())
|
||||
case v1.ResourceEphemeralStorage:
|
||||
r.EphemeralStorage += rQuant.Value()
|
||||
default:
|
||||
if v1helper.IsExtendedResourceName(rName) {
|
||||
r.AddExtended(rName, rQuant.Value())
|
||||
}
|
||||
if v1helper.IsHugePageResourceName(rName) {
|
||||
r.AddHugePages(rName, rQuant.Value())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) ResourceList() v1.ResourceList {
|
||||
result := v1.ResourceList{
|
||||
v1.ResourceCPU: *resource.NewMilliQuantity(r.MilliCPU, resource.DecimalSI),
|
||||
v1.ResourceMemory: *resource.NewQuantity(r.Memory, resource.BinarySI),
|
||||
v1.ResourceNvidiaGPU: *resource.NewQuantity(r.NvidiaGPU, resource.DecimalSI),
|
||||
v1.ResourcePods: *resource.NewQuantity(int64(r.AllowedPodNumber), resource.BinarySI),
|
||||
v1.ResourceEphemeralStorage: *resource.NewQuantity(r.EphemeralStorage, resource.BinarySI),
|
||||
}
|
||||
for rName, rQuant := range r.ExtendedResources {
|
||||
result[rName] = *resource.NewQuantity(rQuant, resource.DecimalSI)
|
||||
}
|
||||
for rName, rQuant := range r.HugePages {
|
||||
result[rName] = *resource.NewQuantity(rQuant, resource.BinarySI)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (r *Resource) Clone() *Resource {
|
||||
res := &Resource{
|
||||
MilliCPU: r.MilliCPU,
|
||||
Memory: r.Memory,
|
||||
NvidiaGPU: r.NvidiaGPU,
|
||||
AllowedPodNumber: r.AllowedPodNumber,
|
||||
EphemeralStorage: r.EphemeralStorage,
|
||||
}
|
||||
if r.ExtendedResources != nil {
|
||||
res.ExtendedResources = make(map[v1.ResourceName]int64)
|
||||
for k, v := range r.ExtendedResources {
|
||||
res.ExtendedResources[k] = v
|
||||
}
|
||||
}
|
||||
if r.HugePages != nil {
|
||||
res.HugePages = make(map[v1.ResourceName]int64)
|
||||
for k, v := range r.HugePages {
|
||||
res.HugePages[k] = v
|
||||
}
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
func (r *Resource) AddExtended(name v1.ResourceName, quantity int64) {
|
||||
r.SetExtended(name, r.ExtendedResources[name]+quantity)
|
||||
}
|
||||
|
||||
func (r *Resource) SetExtended(name v1.ResourceName, quantity int64) {
|
||||
// Lazily allocate opaque integer resource map.
|
||||
if r.ExtendedResources == nil {
|
||||
r.ExtendedResources = map[v1.ResourceName]int64{}
|
||||
}
|
||||
r.ExtendedResources[name] = quantity
|
||||
}
|
||||
|
||||
func (r *Resource) AddHugePages(name v1.ResourceName, quantity int64) {
|
||||
r.SetHugePages(name, r.HugePages[name]+quantity)
|
||||
}
|
||||
|
||||
func (r *Resource) SetHugePages(name v1.ResourceName, quantity int64) {
|
||||
// Lazily allocate hugepages resource map.
|
||||
if r.HugePages == nil {
|
||||
r.HugePages = map[v1.ResourceName]int64{}
|
||||
}
|
||||
r.HugePages[name] = quantity
|
||||
}
|
||||
|
||||
// NewNodeInfo returns a ready to use empty NodeInfo object.
|
||||
// If any pods are given in arguments, their information will be aggregated in
|
||||
// the returned object.
|
||||
func NewNodeInfo(pods ...*v1.Pod) *NodeInfo {
|
||||
ni := &NodeInfo{
|
||||
requestedResource: &Resource{},
|
||||
nonzeroRequest: &Resource{},
|
||||
allocatableResource: &Resource{},
|
||||
generation: 0,
|
||||
usedPorts: make(map[int]bool),
|
||||
}
|
||||
for _, pod := range pods {
|
||||
ni.AddPod(pod)
|
||||
}
|
||||
return ni
|
||||
}
|
||||
|
||||
// Returns overall information about this node.
|
||||
func (n *NodeInfo) Node() *v1.Node {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return n.node
|
||||
}
|
||||
|
||||
// Pods return all pods scheduled (including assumed to be) on this node.
|
||||
func (n *NodeInfo) Pods() []*v1.Pod {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return n.pods
|
||||
}
|
||||
|
||||
func (n *NodeInfo) UsedPorts() map[int]bool {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return n.usedPorts
|
||||
}
|
||||
|
||||
// PodsWithAffinity return all pods with (anti)affinity constraints on this node.
|
||||
func (n *NodeInfo) PodsWithAffinity() []*v1.Pod {
|
||||
if n == nil {
|
||||
return nil
|
||||
}
|
||||
return n.podsWithAffinity
|
||||
}
|
||||
|
||||
func (n *NodeInfo) AllowedPodNumber() int {
|
||||
if n == nil || n.allocatableResource == nil {
|
||||
return 0
|
||||
}
|
||||
return n.allocatableResource.AllowedPodNumber
|
||||
}
|
||||
|
||||
func (n *NodeInfo) Taints() ([]v1.Taint, error) {
|
||||
if n == nil {
|
||||
return nil, nil
|
||||
}
|
||||
return n.taints, n.taintsErr
|
||||
}
|
||||
|
||||
func (n *NodeInfo) MemoryPressureCondition() v1.ConditionStatus {
|
||||
if n == nil {
|
||||
return v1.ConditionUnknown
|
||||
}
|
||||
return n.memoryPressureCondition
|
||||
}
|
||||
|
||||
func (n *NodeInfo) DiskPressureCondition() v1.ConditionStatus {
|
||||
if n == nil {
|
||||
return v1.ConditionUnknown
|
||||
}
|
||||
return n.diskPressureCondition
|
||||
}
|
||||
|
||||
// RequestedResource returns aggregated resource request of pods on this node.
|
||||
func (n *NodeInfo) RequestedResource() Resource {
|
||||
if n == nil {
|
||||
return emptyResource
|
||||
}
|
||||
return *n.requestedResource
|
||||
}
|
||||
|
||||
// NonZeroRequest returns aggregated nonzero resource request of pods on this node.
|
||||
func (n *NodeInfo) NonZeroRequest() Resource {
|
||||
if n == nil {
|
||||
return emptyResource
|
||||
}
|
||||
return *n.nonzeroRequest
|
||||
}
|
||||
|
||||
// AllocatableResource returns allocatable resources on a given node.
|
||||
func (n *NodeInfo) AllocatableResource() Resource {
|
||||
if n == nil {
|
||||
return emptyResource
|
||||
}
|
||||
return *n.allocatableResource
|
||||
}
|
||||
|
||||
func (n *NodeInfo) Clone() *NodeInfo {
|
||||
clone := &NodeInfo{
|
||||
node: n.node,
|
||||
requestedResource: n.requestedResource.Clone(),
|
||||
nonzeroRequest: n.nonzeroRequest.Clone(),
|
||||
allocatableResource: n.allocatableResource.Clone(),
|
||||
taintsErr: n.taintsErr,
|
||||
memoryPressureCondition: n.memoryPressureCondition,
|
||||
diskPressureCondition: n.diskPressureCondition,
|
||||
usedPorts: make(map[int]bool),
|
||||
generation: n.generation,
|
||||
}
|
||||
if len(n.pods) > 0 {
|
||||
clone.pods = append([]*v1.Pod(nil), n.pods...)
|
||||
}
|
||||
if len(n.usedPorts) > 0 {
|
||||
for k, v := range n.usedPorts {
|
||||
clone.usedPorts[k] = v
|
||||
}
|
||||
}
|
||||
if len(n.podsWithAffinity) > 0 {
|
||||
clone.podsWithAffinity = append([]*v1.Pod(nil), n.podsWithAffinity...)
|
||||
}
|
||||
if len(n.taints) > 0 {
|
||||
clone.taints = append([]v1.Taint(nil), n.taints...)
|
||||
}
|
||||
return clone
|
||||
}
|
||||
|
||||
// String returns representation of human readable format of this NodeInfo.
|
||||
func (n *NodeInfo) String() string {
|
||||
podKeys := make([]string, len(n.pods))
|
||||
for i, pod := range n.pods {
|
||||
podKeys[i] = pod.Name
|
||||
}
|
||||
return fmt.Sprintf("&NodeInfo{Pods:%v, RequestedResource:%#v, NonZeroRequest: %#v, UsedPort: %#v, AllocatableResource:%#v}",
|
||||
podKeys, n.requestedResource, n.nonzeroRequest, n.usedPorts, n.allocatableResource)
|
||||
}
|
||||
|
||||
func hasPodAffinityConstraints(pod *v1.Pod) bool {
|
||||
affinity := pod.Spec.Affinity
|
||||
return affinity != nil && (affinity.PodAffinity != nil || affinity.PodAntiAffinity != nil)
|
||||
}
|
||||
|
||||
// AddPod adds pod information to this NodeInfo.
|
||||
func (n *NodeInfo) AddPod(pod *v1.Pod) {
|
||||
res, non0_cpu, non0_mem := calculateResource(pod)
|
||||
n.requestedResource.MilliCPU += res.MilliCPU
|
||||
n.requestedResource.Memory += res.Memory
|
||||
n.requestedResource.NvidiaGPU += res.NvidiaGPU
|
||||
n.requestedResource.EphemeralStorage += res.EphemeralStorage
|
||||
if n.requestedResource.ExtendedResources == nil && len(res.ExtendedResources) > 0 {
|
||||
n.requestedResource.ExtendedResources = map[v1.ResourceName]int64{}
|
||||
}
|
||||
for rName, rQuant := range res.ExtendedResources {
|
||||
n.requestedResource.ExtendedResources[rName] += rQuant
|
||||
}
|
||||
if n.requestedResource.HugePages == nil && len(res.HugePages) > 0 {
|
||||
n.requestedResource.HugePages = map[v1.ResourceName]int64{}
|
||||
}
|
||||
for rName, rQuant := range res.HugePages {
|
||||
n.requestedResource.HugePages[rName] += rQuant
|
||||
}
|
||||
n.nonzeroRequest.MilliCPU += non0_cpu
|
||||
n.nonzeroRequest.Memory += non0_mem
|
||||
n.pods = append(n.pods, pod)
|
||||
if hasPodAffinityConstraints(pod) {
|
||||
n.podsWithAffinity = append(n.podsWithAffinity, pod)
|
||||
}
|
||||
|
||||
// Consume ports when pods added.
|
||||
n.updateUsedPorts(pod, true)
|
||||
|
||||
n.generation++
|
||||
}
|
||||
|
||||
// RemovePod subtracts pod information from this NodeInfo.
|
||||
func (n *NodeInfo) RemovePod(pod *v1.Pod) error {
|
||||
k1, err := getPodKey(pod)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for i := range n.podsWithAffinity {
|
||||
k2, err := getPodKey(n.podsWithAffinity[i])
|
||||
if err != nil {
|
||||
glog.Errorf("Cannot get pod key, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if k1 == k2 {
|
||||
// delete the element
|
||||
n.podsWithAffinity[i] = n.podsWithAffinity[len(n.podsWithAffinity)-1]
|
||||
n.podsWithAffinity = n.podsWithAffinity[:len(n.podsWithAffinity)-1]
|
||||
break
|
||||
}
|
||||
}
|
||||
for i := range n.pods {
|
||||
k2, err := getPodKey(n.pods[i])
|
||||
if err != nil {
|
||||
glog.Errorf("Cannot get pod key, err: %v", err)
|
||||
continue
|
||||
}
|
||||
if k1 == k2 {
|
||||
// delete the element
|
||||
n.pods[i] = n.pods[len(n.pods)-1]
|
||||
n.pods = n.pods[:len(n.pods)-1]
|
||||
// reduce the resource data
|
||||
res, non0_cpu, non0_mem := calculateResource(pod)
|
||||
|
||||
n.requestedResource.MilliCPU -= res.MilliCPU
|
||||
n.requestedResource.Memory -= res.Memory
|
||||
n.requestedResource.NvidiaGPU -= res.NvidiaGPU
|
||||
if len(res.ExtendedResources) > 0 && n.requestedResource.ExtendedResources == nil {
|
||||
n.requestedResource.ExtendedResources = map[v1.ResourceName]int64{}
|
||||
}
|
||||
for rName, rQuant := range res.ExtendedResources {
|
||||
n.requestedResource.ExtendedResources[rName] -= rQuant
|
||||
}
|
||||
if len(res.HugePages) > 0 && n.requestedResource.HugePages == nil {
|
||||
n.requestedResource.HugePages = map[v1.ResourceName]int64{}
|
||||
}
|
||||
for rName, rQuant := range res.HugePages {
|
||||
n.requestedResource.HugePages[rName] -= rQuant
|
||||
}
|
||||
n.nonzeroRequest.MilliCPU -= non0_cpu
|
||||
n.nonzeroRequest.Memory -= non0_mem
|
||||
|
||||
// Release ports when remove Pods.
|
||||
n.updateUsedPorts(pod, false)
|
||||
|
||||
n.generation++
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("no corresponding pod %s in pods of node %s", pod.Name, n.node.Name)
|
||||
}
|
||||
|
||||
func calculateResource(pod *v1.Pod) (res Resource, non0_cpu int64, non0_mem int64) {
|
||||
resPtr := &res
|
||||
for _, c := range pod.Spec.Containers {
|
||||
resPtr.Add(c.Resources.Requests)
|
||||
|
||||
non0_cpu_req, non0_mem_req := priorityutil.GetNonzeroRequests(&c.Resources.Requests)
|
||||
non0_cpu += non0_cpu_req
|
||||
non0_mem += non0_mem_req
|
||||
// No non-zero resources for GPUs or opaque resources.
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (n *NodeInfo) updateUsedPorts(pod *v1.Pod, used bool) {
|
||||
for j := range pod.Spec.Containers {
|
||||
container := &pod.Spec.Containers[j]
|
||||
for k := range container.Ports {
|
||||
podPort := &container.Ports[k]
|
||||
// "0" is explicitly ignored in PodFitsHostPorts,
|
||||
// which is the only function that uses this value.
|
||||
if podPort.HostPort != 0 {
|
||||
n.usedPorts[int(podPort.HostPort)] = used
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sets the overall node information.
|
||||
func (n *NodeInfo) SetNode(node *v1.Node) error {
|
||||
n.node = node
|
||||
|
||||
n.allocatableResource = NewResource(node.Status.Allocatable)
|
||||
|
||||
n.taints = node.Spec.Taints
|
||||
for i := range node.Status.Conditions {
|
||||
cond := &node.Status.Conditions[i]
|
||||
switch cond.Type {
|
||||
case v1.NodeMemoryPressure:
|
||||
n.memoryPressureCondition = cond.Status
|
||||
case v1.NodeDiskPressure:
|
||||
n.diskPressureCondition = cond.Status
|
||||
default:
|
||||
// We ignore other conditions.
|
||||
}
|
||||
}
|
||||
n.generation++
|
||||
return nil
|
||||
}
|
||||
|
||||
// Removes the overall information about the node.
|
||||
func (n *NodeInfo) RemoveNode(node *v1.Node) error {
|
||||
// We don't remove NodeInfo for because there can still be some pods on this node -
|
||||
// this is because notifications about pods are delivered in a different watch,
|
||||
// and thus can potentially be observed later, even though they happened before
|
||||
// node removal. This is handled correctly in cache.go file.
|
||||
n.node = nil
|
||||
n.allocatableResource = &Resource{}
|
||||
n.taints, n.taintsErr = nil, nil
|
||||
n.memoryPressureCondition = v1.ConditionUnknown
|
||||
n.diskPressureCondition = v1.ConditionUnknown
|
||||
n.generation++
|
||||
return nil
|
||||
}
|
||||
|
||||
// FilterOutPods receives a list of pods and filters out those whose node names
|
||||
// are equal to the node of this NodeInfo, but are not found in the pods of this NodeInfo.
|
||||
//
|
||||
// Preemption logic simulates removal of pods on a node by removing them from the
|
||||
// corresponding NodeInfo. In order for the simulation to work, we call this method
|
||||
// on the pods returned from SchedulerCache, so that predicate functions see
|
||||
// only the pods that are not removed from the NodeInfo.
|
||||
func (n *NodeInfo) FilterOutPods(pods []*v1.Pod) []*v1.Pod {
|
||||
node := n.Node()
|
||||
if node == nil {
|
||||
return pods
|
||||
}
|
||||
filtered := make([]*v1.Pod, 0, len(pods))
|
||||
for _, p := range pods {
|
||||
if p.Spec.NodeName == node.Name {
|
||||
// If pod is on the given node, add it to 'filtered' only if it is present in nodeInfo.
|
||||
podKey, _ := getPodKey(p)
|
||||
for _, np := range n.Pods() {
|
||||
npodkey, _ := getPodKey(np)
|
||||
if npodkey == podKey {
|
||||
filtered = append(filtered, p)
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
filtered = append(filtered, p)
|
||||
}
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
|
||||
// getPodKey returns the string key of a pod.
|
||||
func getPodKey(pod *v1.Pod) (string, error) {
|
||||
return clientcache.MetaNamespaceKeyFunc(pod)
|
||||
}
|
||||
|
||||
// Filter implements PodFilter interface. It returns false only if the pod node name
|
||||
// matches NodeInfo.node and the pod is not found in the pods list. Otherwise,
|
||||
// returns true.
|
||||
func (n *NodeInfo) Filter(pod *v1.Pod) bool {
|
||||
pFullName := util.GetPodFullName(pod)
|
||||
if pod.Spec.NodeName != n.node.Name {
|
||||
return true
|
||||
}
|
||||
for _, p := range n.pods {
|
||||
if util.GetPodFullName(p) == pFullName {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
39
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/util.go
generated
vendored
Normal file
39
vendor/k8s.io/kubernetes/plugin/pkg/scheduler/schedulercache/util.go
generated
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright 2015 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 schedulercache
|
||||
|
||||
import "k8s.io/api/core/v1"
|
||||
|
||||
// CreateNodeNameToInfoMap obtains a list of pods and pivots that list into a map where the keys are node names
|
||||
// and the values are the aggregated information for that node.
|
||||
func CreateNodeNameToInfoMap(pods []*v1.Pod, nodes []*v1.Node) map[string]*NodeInfo {
|
||||
nodeNameToInfo := make(map[string]*NodeInfo)
|
||||
for _, pod := range pods {
|
||||
nodeName := pod.Spec.NodeName
|
||||
if _, ok := nodeNameToInfo[nodeName]; !ok {
|
||||
nodeNameToInfo[nodeName] = NewNodeInfo()
|
||||
}
|
||||
nodeNameToInfo[nodeName].AddPod(pod)
|
||||
}
|
||||
for _, node := range nodes {
|
||||
if _, ok := nodeNameToInfo[node.Name]; !ok {
|
||||
nodeNameToInfo[node.Name] = NewNodeInfo()
|
||||
}
|
||||
nodeNameToInfo[node.Name].SetNode(node)
|
||||
}
|
||||
return nodeNameToInfo
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue