Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
89d6ec4b
Commit
89d6ec4b
authored
Oct 05, 2018
by
Darren Shepherd
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Remove EnableEquivalenceClassCache
parent
63d8d89d
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
4 additions
and
1622 deletions
+4
-1622
server.go
cmd/kube-scheduler/app/server.go
+0
-2
kube_features.go
pkg/features/kube_features.go
+0
-7
BUILD
pkg/scheduler/core/equivalence/BUILD
+0
-49
eqivalence.go
pkg/scheduler/core/equivalence/eqivalence.go
+0
-443
eqivalence_test.go
pkg/scheduler/core/equivalence/eqivalence_test.go
+0
-803
generic_scheduler.go
pkg/scheduler/core/generic_scheduler.go
+4
-40
factory.go
pkg/scheduler/factory/factory.go
+0
-254
scheduler.go
pkg/scheduler/scheduler.go
+0
-24
No files found.
cmd/kube-scheduler/app/server.go
View file @
89d6ec4b
...
...
@@ -187,7 +187,6 @@ func Run(cc schedulerserverconfig.CompletedConfig, stopCh <-chan struct{}) error
stopCh
,
scheduler
.
WithName
(
cc
.
ComponentConfig
.
SchedulerName
),
scheduler
.
WithHardPodAffinitySymmetricWeight
(
cc
.
ComponentConfig
.
HardPodAffinitySymmetricWeight
),
scheduler
.
WithEquivalenceClassCacheEnabled
(
cc
.
ComponentConfig
.
EnableContentionProfiling
),
scheduler
.
WithPreemptionDisabled
(
cc
.
ComponentConfig
.
DisablePreemption
),
scheduler
.
WithPercentageOfNodesToScore
(
cc
.
ComponentConfig
.
PercentageOfNodesToScore
),
scheduler
.
WithBindTimeoutSeconds
(
*
cc
.
ComponentConfig
.
BindTimeoutSeconds
))
...
...
@@ -356,7 +355,6 @@ func NewSchedulerConfig(s schedulerserverconfig.CompletedConfig) (*factory.Confi
PdbInformer
:
s
.
InformerFactory
.
Policy
()
.
V1beta1
()
.
PodDisruptionBudgets
(),
StorageClassInformer
:
storageClassInformer
,
HardPodAffinitySymmetricWeight
:
s
.
ComponentConfig
.
HardPodAffinitySymmetricWeight
,
EnableEquivalenceClassCache
:
utilfeature
.
DefaultFeatureGate
.
Enabled
(
features
.
EnableEquivalenceClassCache
),
DisablePreemption
:
s
.
ComponentConfig
.
DisablePreemption
,
PercentageOfNodesToScore
:
s
.
ComponentConfig
.
PercentageOfNodesToScore
,
BindTimeoutSeconds
:
*
s
.
ComponentConfig
.
BindTimeoutSeconds
,
...
...
pkg/features/kube_features.go
View file @
89d6ec4b
...
...
@@ -109,12 +109,6 @@ const (
// Add priority to pods. Priority affects scheduling and preemption of pods.
PodPriority
utilfeature
.
Feature
=
"PodPriority"
// owner: @resouer
// alpha: v1.8
//
// Enable equivalence class cache for scheduler.
EnableEquivalenceClassCache
utilfeature
.
Feature
=
"EnableEquivalenceClassCache"
// owner: @k82cn
// beta: v1.12
//
...
...
@@ -394,7 +388,6 @@ var defaultKubernetesFeatureGates = map[utilfeature.Feature]utilfeature.FeatureS
Sysctls
:
{
Default
:
true
,
PreRelease
:
utilfeature
.
Beta
},
PodShareProcessNamespace
:
{
Default
:
true
,
PreRelease
:
utilfeature
.
Beta
},
PodPriority
:
{
Default
:
true
,
PreRelease
:
utilfeature
.
Beta
},
EnableEquivalenceClassCache
:
{
Default
:
false
,
PreRelease
:
utilfeature
.
Alpha
},
TaintNodesByCondition
:
{
Default
:
true
,
PreRelease
:
utilfeature
.
Beta
},
MountPropagation
:
{
Default
:
true
,
PreRelease
:
utilfeature
.
GA
},
QOSReserved
:
{
Default
:
false
,
PreRelease
:
utilfeature
.
Alpha
},
...
...
pkg/scheduler/core/equivalence/BUILD
deleted
100644 → 0
View file @
63d8d89d
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
go_library(
name = "go_default_library",
srcs = ["eqivalence.go"],
importpath = "k8s.io/kubernetes/pkg/scheduler/core/equivalence",
visibility = ["//visibility:public"],
deps = [
"//pkg/features:go_default_library",
"//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/cache:go_default_library",
"//pkg/scheduler/metrics:go_default_library",
"//pkg/util/hash:go_default_library",
"//staging/src/k8s.io/api/core/v1: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",
],
)
go_test(
name = "go_default_test",
srcs = ["eqivalence_test.go"],
embed = [":go_default_library"],
deps = [
"//pkg/scheduler/algorithm:go_default_library",
"//pkg/scheduler/algorithm/predicates:go_default_library",
"//pkg/scheduler/cache: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/util/sets: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"],
)
pkg/scheduler/core/equivalence/eqivalence.go
deleted
100644 → 0
View file @
63d8d89d
/*
Copyright 2016 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 equivalence defines Pod equivalence classes and the equivalence class
// cache.
package
equivalence
import
(
"fmt"
"hash/fnv"
"sync"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/klog"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
schedulercache
"k8s.io/kubernetes/pkg/scheduler/cache"
"k8s.io/kubernetes/pkg/scheduler/metrics"
hashutil
"k8s.io/kubernetes/pkg/util/hash"
)
// nodeMap stores a *NodeCache for each node.
type
nodeMap
map
[
string
]
*
NodeCache
// Cache is a thread safe map saves and reuses the output of predicate functions,
// it uses node name as key to access those cached results.
//
// Internally, results are keyed by predicate name, and "equivalence
// class". (Equivalence class is defined in the `Class` type.) Saved results
// will be reused until an appropriate invalidation function is called.
type
Cache
struct
{
// NOTE(harry): Theoretically sync.Map has better performance in machine with 8+ CPUs, while
// the reality is lock contention in first level cache is rare.
mu
sync
.
RWMutex
nodeToCache
nodeMap
predicateIDMap
map
[
string
]
int
}
// NewCache create an empty equiv class cache.
func
NewCache
(
predicates
[]
string
)
*
Cache
{
predicateIDMap
:=
make
(
map
[
string
]
int
,
len
(
predicates
))
for
id
,
predicate
:=
range
predicates
{
predicateIDMap
[
predicate
]
=
id
}
return
&
Cache
{
nodeToCache
:
make
(
nodeMap
),
predicateIDMap
:
predicateIDMap
,
}
}
// NodeCache saves and reuses the output of predicate functions. Use RunPredicate to
// get or update the cached results. An appropriate Invalidate* function should
// be called when some predicate results are no longer valid.
//
// Internally, results are keyed by predicate name, and "equivalence
// class". (Equivalence class is defined in the `Class` type.) Saved results
// will be reused until an appropriate invalidation function is called.
//
// NodeCache objects are thread safe within the context of NodeCache,
type
NodeCache
struct
{
mu
sync
.
RWMutex
cache
predicateMap
// generation is current generation of node cache, incremented on node
// invalidation.
generation
uint64
// snapshotGeneration saves snapshot of generation of node cache.
snapshotGeneration
uint64
// predicateGenerations stores generation numbers for predicates, incremented on
// predicate invalidation. Created on first update. Use 0 if does not
// exist.
predicateGenerations
[]
uint64
// snapshotPredicateGenerations saves snapshot of generation numbers for predicates.
snapshotPredicateGenerations
[]
uint64
}
// newNodeCache returns an empty NodeCache.
func
newNodeCache
(
n
int
)
*
NodeCache
{
return
&
NodeCache
{
cache
:
make
(
predicateMap
,
n
),
predicateGenerations
:
make
([]
uint64
,
n
),
snapshotPredicateGenerations
:
make
([]
uint64
,
n
),
}
}
// Snapshot snapshots current generations of cache.
// NOTE: We snapshot generations of all node caches before using it and these
// operations are serialized, we can save snapshot as member of node cache
// itself.
func
(
c
*
Cache
)
Snapshot
()
{
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
for
_
,
n
:=
range
c
.
nodeToCache
{
n
.
mu
.
Lock
()
// snapshot predicate generations
copy
(
n
.
snapshotPredicateGenerations
,
n
.
predicateGenerations
)
// snapshot node generation
n
.
snapshotGeneration
=
n
.
generation
n
.
mu
.
Unlock
()
}
return
}
// GetNodeCache returns the existing NodeCache for given node if present. Otherwise,
// it creates the NodeCache and returns it.
// The boolean flag is true if the value was loaded, false if created.
func
(
c
*
Cache
)
GetNodeCache
(
name
string
)
(
nodeCache
*
NodeCache
,
exists
bool
)
{
c
.
mu
.
Lock
()
defer
c
.
mu
.
Unlock
()
if
nodeCache
,
exists
=
c
.
nodeToCache
[
name
];
!
exists
{
nodeCache
=
newNodeCache
(
len
(
c
.
predicateIDMap
))
c
.
nodeToCache
[
name
]
=
nodeCache
}
return
}
// LoadNodeCache returns the existing NodeCache for given node, nil if not
// present.
func
(
c
*
Cache
)
LoadNodeCache
(
node
string
)
*
NodeCache
{
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
return
c
.
nodeToCache
[
node
]
}
func
(
c
*
Cache
)
predicateKeysToIDs
(
predicateKeys
sets
.
String
)
[]
int
{
predicateIDs
:=
make
([]
int
,
0
,
len
(
predicateKeys
))
for
predicateKey
:=
range
predicateKeys
{
if
id
,
ok
:=
c
.
predicateIDMap
[
predicateKey
];
ok
{
predicateIDs
=
append
(
predicateIDs
,
id
)
}
else
{
klog
.
Errorf
(
"predicate key %q not found"
,
predicateKey
)
}
}
return
predicateIDs
}
// InvalidatePredicates clears all cached results for the given predicates.
func
(
c
*
Cache
)
InvalidatePredicates
(
predicateKeys
sets
.
String
)
{
if
len
(
predicateKeys
)
==
0
{
return
}
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
predicateIDs
:=
c
.
predicateKeysToIDs
(
predicateKeys
)
for
_
,
n
:=
range
c
.
nodeToCache
{
n
.
invalidatePreds
(
predicateIDs
)
}
klog
.
V
(
5
)
.
Infof
(
"Cache invalidation: node=*,predicates=%v"
,
predicateKeys
)
}
// InvalidatePredicatesOnNode clears cached results for the given predicates on one node.
func
(
c
*
Cache
)
InvalidatePredicatesOnNode
(
nodeName
string
,
predicateKeys
sets
.
String
)
{
if
len
(
predicateKeys
)
==
0
{
return
}
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
predicateIDs
:=
c
.
predicateKeysToIDs
(
predicateKeys
)
if
n
,
ok
:=
c
.
nodeToCache
[
nodeName
];
ok
{
n
.
invalidatePreds
(
predicateIDs
)
}
klog
.
V
(
5
)
.
Infof
(
"Cache invalidation: node=%s,predicates=%v"
,
nodeName
,
predicateKeys
)
}
// InvalidateAllPredicatesOnNode clears all cached results for one node.
func
(
c
*
Cache
)
InvalidateAllPredicatesOnNode
(
nodeName
string
)
{
c
.
mu
.
RLock
()
defer
c
.
mu
.
RUnlock
()
if
node
,
ok
:=
c
.
nodeToCache
[
nodeName
];
ok
{
node
.
invalidate
()
}
klog
.
V
(
5
)
.
Infof
(
"Cache invalidation: node=%s,predicates=*"
,
nodeName
)
}
// InvalidateCachedPredicateItemForPodAdd is a wrapper of
// InvalidateCachedPredicateItem for pod add case
// TODO: This does not belong with the equivalence cache implementation.
func
(
c
*
Cache
)
InvalidateCachedPredicateItemForPodAdd
(
pod
*
v1
.
Pod
,
nodeName
string
)
{
// MatchInterPodAffinity: we assume scheduler can make sure newly bound pod
// will not break the existing inter pod affinity. So we does not need to
// invalidate MatchInterPodAffinity when pod added.
//
// But when a pod is deleted, existing inter pod affinity may become invalid.
// (e.g. this pod was preferred by some else, or vice versa)
//
// NOTE: assumptions above will not stand when we implemented features like
// RequiredDuringSchedulingRequiredDuringExecutioc.
// NoDiskConflict: the newly scheduled pod fits to existing pods on this node,
// it will also fits to equivalence class of existing pods
// GeneralPredicates: will always be affected by adding a new pod
invalidPredicates
:=
sets
.
NewString
(
predicates
.
GeneralPred
)
// MaxPDVolumeCountPredicate: we check the volumes of pod to make decisioc.
for
_
,
vol
:=
range
pod
.
Spec
.
Volumes
{
if
vol
.
PersistentVolumeClaim
!=
nil
{
invalidPredicates
.
Insert
(
predicates
.
MaxEBSVolumeCountPred
,
predicates
.
MaxGCEPDVolumeCountPred
,
predicates
.
MaxAzureDiskVolumeCountPred
)
if
utilfeature
.
DefaultFeatureGate
.
Enabled
(
features
.
AttachVolumeLimit
)
{
invalidPredicates
.
Insert
(
predicates
.
MaxCSIVolumeCountPred
)
}
}
else
{
// We do not consider CSI volumes here because CSI
// volumes can not be used inline.
if
vol
.
AWSElasticBlockStore
!=
nil
{
invalidPredicates
.
Insert
(
predicates
.
MaxEBSVolumeCountPred
)
}
if
vol
.
GCEPersistentDisk
!=
nil
{
invalidPredicates
.
Insert
(
predicates
.
MaxGCEPDVolumeCountPred
)
}
if
vol
.
AzureDisk
!=
nil
{
invalidPredicates
.
Insert
(
predicates
.
MaxAzureDiskVolumeCountPred
)
}
}
}
c
.
InvalidatePredicatesOnNode
(
nodeName
,
invalidPredicates
)
}
// Class represents a set of pods which are equivalent from the perspective of
// the scheduler. i.e. the scheduler would make the same decision for any pod
// from the same class.
type
Class
struct
{
// Equivalence hash
hash
uint64
}
// NewClass returns the equivalence class for a given Pod. The returned Class
// objects will be equal for two Pods in the same class. nil values should not
// be considered equal to each other.
//
// NOTE: Make sure to compare types of Class and not *Class.
// TODO(misterikkit): Return error instead of nil *Class.
func
NewClass
(
pod
*
v1
.
Pod
)
*
Class
{
equivalencePod
:=
getEquivalencePod
(
pod
)
if
equivalencePod
!=
nil
{
hash
:=
fnv
.
New32a
()
hashutil
.
DeepHashObject
(
hash
,
equivalencePod
)
return
&
Class
{
hash
:
uint64
(
hash
.
Sum32
()),
}
}
return
nil
}
// predicateMap stores resultMaps with predicate ID as the key.
type
predicateMap
[]
resultMap
// resultMap stores PredicateResult with pod equivalence hash as the key.
type
resultMap
map
[
uint64
]
predicateResult
// predicateResult stores the output of a FitPredicate.
type
predicateResult
struct
{
Fit
bool
FailReasons
[]
algorithm
.
PredicateFailureReason
}
// RunPredicate returns a cached predicate result. In case of a cache miss, the predicate will be
// run and its results cached for the next call.
//
// NOTE: RunPredicate will not update the equivalence cache if generation does not match live version.
func
(
n
*
NodeCache
)
RunPredicate
(
pred
algorithm
.
FitPredicate
,
predicateKey
string
,
predicateID
int
,
pod
*
v1
.
Pod
,
meta
algorithm
.
PredicateMetadata
,
nodeInfo
*
schedulercache
.
NodeInfo
,
equivClass
*
Class
,
)
(
bool
,
[]
algorithm
.
PredicateFailureReason
,
error
)
{
if
nodeInfo
==
nil
||
nodeInfo
.
Node
()
==
nil
{
// This may happen during tests.
return
false
,
[]
algorithm
.
PredicateFailureReason
{},
fmt
.
Errorf
(
"nodeInfo is nil or node is invalid"
)
}
result
,
ok
:=
n
.
lookupResult
(
pod
.
GetName
(),
nodeInfo
.
Node
()
.
GetName
(),
predicateKey
,
predicateID
,
equivClass
.
hash
)
if
ok
{
return
result
.
Fit
,
result
.
FailReasons
,
nil
}
fit
,
reasons
,
err
:=
pred
(
pod
,
meta
,
nodeInfo
)
if
err
!=
nil
{
return
fit
,
reasons
,
err
}
n
.
updateResult
(
pod
.
GetName
(),
predicateKey
,
predicateID
,
fit
,
reasons
,
equivClass
.
hash
,
nodeInfo
)
return
fit
,
reasons
,
nil
}
// updateResult updates the cached result of a predicate.
func
(
n
*
NodeCache
)
updateResult
(
podName
,
predicateKey
string
,
predicateID
int
,
fit
bool
,
reasons
[]
algorithm
.
PredicateFailureReason
,
equivalenceHash
uint64
,
nodeInfo
*
schedulercache
.
NodeInfo
,
)
{
if
nodeInfo
==
nil
||
nodeInfo
.
Node
()
==
nil
{
// This may happen during tests.
metrics
.
EquivalenceCacheWrites
.
WithLabelValues
(
"discarded_bad_node"
)
.
Inc
()
return
}
predicateItem
:=
predicateResult
{
Fit
:
fit
,
FailReasons
:
reasons
,
}
n
.
mu
.
Lock
()
defer
n
.
mu
.
Unlock
()
if
(
n
.
snapshotGeneration
!=
n
.
generation
)
||
(
n
.
snapshotPredicateGenerations
[
predicateID
]
!=
n
.
predicateGenerations
[
predicateID
])
{
// Generation of node or predicate has been updated since we last took
// a snapshot, this indicates that we received a invalidation request
// during this time. Cache may be stale, skip update.
metrics
.
EquivalenceCacheWrites
.
WithLabelValues
(
"discarded_stale"
)
.
Inc
()
return
}
// If cached predicate map already exists, just update the predicate by key
if
predicates
:=
n
.
cache
[
predicateID
];
predicates
!=
nil
{
// maps in golang are references, no need to add them back
predicates
[
equivalenceHash
]
=
predicateItem
}
else
{
n
.
cache
[
predicateID
]
=
resultMap
{
equivalenceHash
:
predicateItem
,
}
}
n
.
predicateGenerations
[
predicateID
]
++
klog
.
V
(
5
)
.
Infof
(
"Cache update: node=%s, predicate=%s,pod=%s,value=%v"
,
nodeInfo
.
Node
()
.
Name
,
predicateKey
,
podName
,
predicateItem
)
}
// lookupResult returns cached predicate results and a bool saying whether a
// cache entry was found.
func
(
n
*
NodeCache
)
lookupResult
(
podName
,
nodeName
,
predicateKey
string
,
predicateID
int
,
equivalenceHash
uint64
,
)
(
value
predicateResult
,
ok
bool
)
{
n
.
mu
.
RLock
()
defer
n
.
mu
.
RUnlock
()
value
,
ok
=
n
.
cache
[
predicateID
][
equivalenceHash
]
if
ok
{
metrics
.
EquivalenceCacheHits
.
Inc
()
}
else
{
metrics
.
EquivalenceCacheMisses
.
Inc
()
}
return
value
,
ok
}
// invalidatePreds deletes cached predicates by given IDs.
func
(
n
*
NodeCache
)
invalidatePreds
(
predicateIDs
[]
int
)
{
n
.
mu
.
Lock
()
defer
n
.
mu
.
Unlock
()
for
_
,
predicateID
:=
range
predicateIDs
{
n
.
cache
[
predicateID
]
=
nil
n
.
predicateGenerations
[
predicateID
]
++
}
}
// invalidate invalidates node cache.
func
(
n
*
NodeCache
)
invalidate
()
{
n
.
mu
.
Lock
()
defer
n
.
mu
.
Unlock
()
n
.
cache
=
make
(
predicateMap
,
len
(
n
.
cache
))
n
.
generation
++
}
// equivalencePod is the set of pod attributes which must match for two pods to
// be considered equivalent for scheduling purposes. For correctness, this must
// include any Pod field which is used by a FitPredicate.
//
// NOTE: For equivalence hash to be formally correct, lists and maps in the
// equivalencePod should be normalized. (e.g. by sorting them) However, the vast
// majority of equivalent pod classes are expected to be created from a single
// pod template, so they will all have the same ordering.
type
equivalencePod
struct
{
Namespace
*
string
Labels
map
[
string
]
string
Affinity
*
v1
.
Affinity
Containers
[]
v1
.
Container
// See note about ordering
InitContainers
[]
v1
.
Container
// See note about ordering
NodeName
*
string
NodeSelector
map
[
string
]
string
Tolerations
[]
v1
.
Toleration
Volumes
[]
v1
.
Volume
// See note about ordering
}
// getEquivalencePod returns a normalized representation of a pod so that two
// "equivalent" pods will hash to the same value.
func
getEquivalencePod
(
pod
*
v1
.
Pod
)
*
equivalencePod
{
ep
:=
&
equivalencePod
{
Namespace
:
&
pod
.
Namespace
,
Labels
:
pod
.
Labels
,
Affinity
:
pod
.
Spec
.
Affinity
,
Containers
:
pod
.
Spec
.
Containers
,
InitContainers
:
pod
.
Spec
.
InitContainers
,
NodeName
:
&
pod
.
Spec
.
NodeName
,
NodeSelector
:
pod
.
Spec
.
NodeSelector
,
Tolerations
:
pod
.
Spec
.
Tolerations
,
Volumes
:
pod
.
Spec
.
Volumes
,
}
// DeepHashObject considers nil and empty slices to be different. Normalize them.
if
len
(
ep
.
Containers
)
==
0
{
ep
.
Containers
=
nil
}
if
len
(
ep
.
InitContainers
)
==
0
{
ep
.
InitContainers
=
nil
}
if
len
(
ep
.
Tolerations
)
==
0
{
ep
.
Tolerations
=
nil
}
if
len
(
ep
.
Volumes
)
==
0
{
ep
.
Volumes
=
nil
}
// Normalize empty maps also.
if
len
(
ep
.
Labels
)
==
0
{
ep
.
Labels
=
nil
}
if
len
(
ep
.
NodeSelector
)
==
0
{
ep
.
NodeSelector
=
nil
}
// TODO(misterikkit): Also normalize nested maps and slices.
return
ep
}
pkg/scheduler/core/equivalence/eqivalence_test.go
deleted
100644 → 0
View file @
63d8d89d
/*
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
equivalence
import
(
"errors"
"reflect"
"testing"
"k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
schedulercache
"k8s.io/kubernetes/pkg/scheduler/cache"
)
// makeBasicPod returns a Pod object with many of the fields populated.
func
makeBasicPod
(
name
string
)
*
v1
.
Pod
{
isController
:=
true
return
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
name
,
Namespace
:
"test-ns"
,
Labels
:
map
[
string
]
string
{
"app"
:
"web"
,
"env"
:
"prod"
},
OwnerReferences
:
[]
metav1
.
OwnerReference
{
{
APIVersion
:
"v1"
,
Kind
:
"ReplicationController"
,
Name
:
"rc"
,
UID
:
"123"
,
Controller
:
&
isController
,
},
},
},
Spec
:
v1
.
PodSpec
{
Affinity
:
&
v1
.
Affinity
{
NodeAffinity
:
&
v1
.
NodeAffinity
{
RequiredDuringSchedulingIgnoredDuringExecution
:
&
v1
.
NodeSelector
{
NodeSelectorTerms
:
[]
v1
.
NodeSelectorTerm
{
{
MatchExpressions
:
[]
v1
.
NodeSelectorRequirement
{
{
Key
:
"failure-domain.beta.kubernetes.io/zone"
,
Operator
:
"Exists"
,
},
},
},
},
},
},
PodAffinity
:
&
v1
.
PodAffinity
{
RequiredDuringSchedulingIgnoredDuringExecution
:
[]
v1
.
PodAffinityTerm
{
{
LabelSelector
:
&
metav1
.
LabelSelector
{
MatchLabels
:
map
[
string
]
string
{
"app"
:
"db"
}},
TopologyKey
:
"kubernetes.io/hostname"
,
},
},
},
PodAntiAffinity
:
&
v1
.
PodAntiAffinity
{
RequiredDuringSchedulingIgnoredDuringExecution
:
[]
v1
.
PodAffinityTerm
{
{
LabelSelector
:
&
metav1
.
LabelSelector
{
MatchLabels
:
map
[
string
]
string
{
"app"
:
"web"
}},
TopologyKey
:
"kubernetes.io/hostname"
,
},
},
},
},
InitContainers
:
[]
v1
.
Container
{
{
Name
:
"init-pause"
,
Image
:
"gcr.io/google_containers/pause"
,
Resources
:
v1
.
ResourceRequirements
{
Limits
:
v1
.
ResourceList
{
"cpu"
:
resource
.
MustParse
(
"1"
),
"mem"
:
resource
.
MustParse
(
"100Mi"
),
},
},
},
},
Containers
:
[]
v1
.
Container
{
{
Name
:
"pause"
,
Image
:
"gcr.io/google_containers/pause"
,
Resources
:
v1
.
ResourceRequirements
{
Limits
:
v1
.
ResourceList
{
"cpu"
:
resource
.
MustParse
(
"1"
),
"mem"
:
resource
.
MustParse
(
"100Mi"
),
},
},
VolumeMounts
:
[]
v1
.
VolumeMount
{
{
Name
:
"nfs"
,
MountPath
:
"/srv/data"
,
},
},
},
},
NodeSelector
:
map
[
string
]
string
{
"node-type"
:
"awesome"
},
Tolerations
:
[]
v1
.
Toleration
{
{
Effect
:
"NoSchedule"
,
Key
:
"experimental"
,
Operator
:
"Exists"
,
},
},
Volumes
:
[]
v1
.
Volume
{
{
VolumeSource
:
v1
.
VolumeSource
{
PersistentVolumeClaim
:
&
v1
.
PersistentVolumeClaimVolumeSource
{
ClaimName
:
"someEBSVol1"
,
},
},
},
{
VolumeSource
:
v1
.
VolumeSource
{
PersistentVolumeClaim
:
&
v1
.
PersistentVolumeClaimVolumeSource
{
ClaimName
:
"someEBSVol2"
,
},
},
},
{
Name
:
"nfs"
,
VolumeSource
:
v1
.
VolumeSource
{
NFS
:
&
v1
.
NFSVolumeSource
{
Server
:
"nfs.corp.example.com"
,
},
},
},
},
},
}
}
type
predicateItemType
struct
{
fit
bool
reasons
[]
algorithm
.
PredicateFailureReason
}
// mockPredicate provides an algorithm.FitPredicate with pre-set return values.
type
mockPredicate
struct
{
fit
bool
reasons
[]
algorithm
.
PredicateFailureReason
err
error
callCount
int
}
func
(
p
*
mockPredicate
)
predicate
(
*
v1
.
Pod
,
algorithm
.
PredicateMetadata
,
*
schedulercache
.
NodeInfo
)
(
bool
,
[]
algorithm
.
PredicateFailureReason
,
error
)
{
p
.
callCount
++
return
p
.
fit
,
p
.
reasons
,
p
.
err
}
func
TestRunPredicate
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
pred
mockPredicate
expectFit
,
expectCacheHit
,
expectCacheWrite
bool
expectedReasons
[]
algorithm
.
PredicateFailureReason
expectedError
string
}{
{
name
:
"pod fits/cache hit"
,
pred
:
mockPredicate
{},
expectFit
:
true
,
expectCacheHit
:
true
,
expectCacheWrite
:
false
,
},
{
name
:
"pod fits/cache miss"
,
pred
:
mockPredicate
{
fit
:
true
},
expectFit
:
true
,
expectCacheHit
:
false
,
expectCacheWrite
:
true
,
},
{
name
:
"pod doesn't fit/cache miss"
,
pred
:
mockPredicate
{
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrFakePredicate
}},
expectFit
:
false
,
expectCacheHit
:
false
,
expectCacheWrite
:
true
,
expectedReasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrFakePredicate
},
},
{
name
:
"pod doesn't fit/cache hit"
,
pred
:
mockPredicate
{},
expectFit
:
false
,
expectCacheHit
:
true
,
expectCacheWrite
:
false
,
expectedReasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrFakePredicate
},
},
{
name
:
"predicate error"
,
pred
:
mockPredicate
{
err
:
errors
.
New
(
"This is expected"
)},
expectFit
:
false
,
expectCacheHit
:
false
,
expectCacheWrite
:
false
,
expectedError
:
"This is expected"
,
},
}
predicatesOrdering
:=
[]
string
{
"testPredicate"
}
predicateID
:=
0
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
node
:=
schedulercache
.
NewNodeInfo
()
testNode
:=
&
v1
.
Node
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"n1"
}}
node
.
SetNode
(
testNode
)
pod
:=
&
v1
.
Pod
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
"p1"
}}
meta
:=
algorithm
.
EmptyPredicateMetadataProducer
(
nil
,
nil
)
// Initialize and populate equivalence class cache.
ecache
:=
NewCache
(
predicatesOrdering
)
ecache
.
Snapshot
()
nodeCache
,
_
:=
ecache
.
GetNodeCache
(
testNode
.
Name
)
equivClass
:=
NewClass
(
pod
)
if
test
.
expectCacheHit
{
nodeCache
.
updateResult
(
pod
.
Name
,
"testPredicate"
,
predicateID
,
test
.
expectFit
,
test
.
expectedReasons
,
equivClass
.
hash
,
node
)
}
fit
,
reasons
,
err
:=
nodeCache
.
RunPredicate
(
test
.
pred
.
predicate
,
"testPredicate"
,
predicateID
,
pod
,
meta
,
node
,
equivClass
)
if
err
!=
nil
{
if
err
.
Error
()
!=
test
.
expectedError
{
t
.
Errorf
(
"Expected error %v but got %v"
,
test
.
expectedError
,
err
)
}
}
else
if
len
(
test
.
expectedError
)
>
0
{
t
.
Errorf
(
"Expected error %v but got nil"
,
test
.
expectedError
)
}
if
fit
&&
!
test
.
expectFit
{
t
.
Errorf
(
"pod should not fit"
)
}
if
!
fit
&&
test
.
expectFit
{
t
.
Errorf
(
"pod should fit"
)
}
if
len
(
reasons
)
!=
len
(
test
.
expectedReasons
)
{
t
.
Errorf
(
"Expected failures: %v but got %v"
,
test
.
expectedReasons
,
reasons
)
}
else
{
for
i
,
reason
:=
range
reasons
{
if
reason
!=
test
.
expectedReasons
[
i
]
{
t
.
Errorf
(
"Expected failures: %v but got %v"
,
test
.
expectedReasons
,
reasons
)
break
}
}
}
if
test
.
expectCacheHit
&&
test
.
pred
.
callCount
!=
0
{
t
.
Errorf
(
"Predicate should not be called"
)
}
if
!
test
.
expectCacheHit
&&
test
.
pred
.
callCount
==
0
{
t
.
Errorf
(
"Predicate should be called"
)
}
_
,
ok
:=
nodeCache
.
lookupResult
(
pod
.
Name
,
node
.
Node
()
.
Name
,
"testPredicate"
,
predicateID
,
equivClass
.
hash
)
if
!
ok
&&
test
.
expectCacheWrite
{
t
.
Errorf
(
"Cache write should happen"
)
}
if
!
test
.
expectCacheHit
&&
test
.
expectCacheWrite
&&
!
ok
{
t
.
Errorf
(
"Cache write should happen"
)
}
if
!
test
.
expectCacheHit
&&
!
test
.
expectCacheWrite
&&
ok
{
t
.
Errorf
(
"Cache write should not happen"
)
}
})
}
}
func
TestUpdateResult
(
t
*
testing
.
T
)
{
predicatesOrdering
:=
[]
string
{
"GeneralPredicates"
}
tests
:=
[]
struct
{
name
string
pod
string
predicateKey
string
predicateID
int
nodeName
string
fit
bool
reasons
[]
algorithm
.
PredicateFailureReason
equivalenceHash
uint64
expectPredicateMap
bool
expectCacheItem
predicateResult
}{
{
name
:
"test 1"
,
pod
:
"testPod"
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
nodeName
:
"node1"
,
fit
:
true
,
equivalenceHash
:
123
,
expectPredicateMap
:
false
,
expectCacheItem
:
predicateResult
{
Fit
:
true
,
},
},
{
name
:
"test 2"
,
pod
:
"testPod"
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
nodeName
:
"node2"
,
fit
:
false
,
equivalenceHash
:
123
,
expectPredicateMap
:
true
,
expectCacheItem
:
predicateResult
{
Fit
:
false
,
},
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
node
:=
schedulercache
.
NewNodeInfo
()
testNode
:=
&
v1
.
Node
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
test
.
nodeName
}}
node
.
SetNode
(
testNode
)
// Initialize and populate equivalence class cache.
ecache
:=
NewCache
(
predicatesOrdering
)
nodeCache
,
_
:=
ecache
.
GetNodeCache
(
testNode
.
Name
)
if
test
.
expectPredicateMap
{
predicateItem
:=
predicateResult
{
Fit
:
true
,
}
nodeCache
.
cache
[
test
.
predicateID
]
=
resultMap
{
test
.
equivalenceHash
:
predicateItem
,
}
}
nodeCache
.
updateResult
(
test
.
pod
,
test
.
predicateKey
,
test
.
predicateID
,
test
.
fit
,
test
.
reasons
,
test
.
equivalenceHash
,
node
,
)
cachedMapItem
:=
nodeCache
.
cache
[
test
.
predicateID
]
if
cachedMapItem
==
nil
{
t
.
Errorf
(
"can't find expected cache item: %v"
,
test
.
expectCacheItem
)
}
else
{
if
!
reflect
.
DeepEqual
(
cachedMapItem
[
test
.
equivalenceHash
],
test
.
expectCacheItem
)
{
t
.
Errorf
(
"expected cached item: %v, but got: %v"
,
test
.
expectCacheItem
,
cachedMapItem
[
test
.
equivalenceHash
])
}
}
})
}
}
// slicesEqual wraps reflect.DeepEqual, but returns true when comparing nil and empty slice.
func
slicesEqual
(
a
,
b
[]
algorithm
.
PredicateFailureReason
)
bool
{
if
len
(
a
)
==
0
&&
len
(
b
)
==
0
{
return
true
}
return
reflect
.
DeepEqual
(
a
,
b
)
}
func
TestLookupResult
(
t
*
testing
.
T
)
{
predicatesOrdering
:=
[]
string
{
"GeneralPredicates"
}
tests
:=
[]
struct
{
name
string
podName
string
nodeName
string
predicateKey
string
predicateID
int
equivalenceHashForUpdatePredicate
uint64
equivalenceHashForCalPredicate
uint64
cachedItem
predicateItemType
expectedPredicateKeyMiss
bool
expectedEquivalenceHashMiss
bool
expectedPredicateItem
predicateItemType
}{
{
name
:
"test 1"
,
podName
:
"testPod"
,
nodeName
:
"node1"
,
equivalenceHashForUpdatePredicate
:
123
,
equivalenceHashForCalPredicate
:
123
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
expectedPredicateKeyMiss
:
true
,
expectedPredicateItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{},
},
},
{
name
:
"test 2"
,
podName
:
"testPod"
,
nodeName
:
"node2"
,
equivalenceHashForUpdatePredicate
:
123
,
equivalenceHashForCalPredicate
:
123
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
cachedItem
:
predicateItemType
{
fit
:
true
,
},
expectedPredicateKeyMiss
:
false
,
expectedPredicateItem
:
predicateItemType
{
fit
:
true
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{},
},
},
{
name
:
"test 3"
,
podName
:
"testPod"
,
nodeName
:
"node3"
,
equivalenceHashForUpdatePredicate
:
123
,
equivalenceHashForCalPredicate
:
123
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
expectedPredicateKeyMiss
:
false
,
expectedPredicateItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
},
{
name
:
"test 4"
,
podName
:
"testPod"
,
nodeName
:
"node4"
,
equivalenceHashForUpdatePredicate
:
123
,
equivalenceHashForCalPredicate
:
456
,
predicateKey
:
"GeneralPredicates"
,
predicateID
:
0
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
expectedPredicateKeyMiss
:
false
,
expectedEquivalenceHashMiss
:
true
,
expectedPredicateItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{},
},
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
testNode
:=
&
v1
.
Node
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
test
.
nodeName
}}
// Initialize and populate equivalence class cache.
ecache
:=
NewCache
(
predicatesOrdering
)
nodeCache
,
_
:=
ecache
.
GetNodeCache
(
testNode
.
Name
)
node
:=
schedulercache
.
NewNodeInfo
()
node
.
SetNode
(
testNode
)
// set cached item to equivalence cache
nodeCache
.
updateResult
(
test
.
podName
,
test
.
predicateKey
,
test
.
predicateID
,
test
.
cachedItem
.
fit
,
test
.
cachedItem
.
reasons
,
test
.
equivalenceHashForUpdatePredicate
,
node
,
)
// if we want to do invalid, invalid the cached item
if
test
.
expectedPredicateKeyMiss
{
predicateKeys
:=
sets
.
NewString
()
predicateKeys
.
Insert
(
test
.
predicateKey
)
ecache
.
InvalidatePredicatesOnNode
(
test
.
nodeName
,
predicateKeys
)
}
// calculate predicate with equivalence cache
result
,
ok
:=
nodeCache
.
lookupResult
(
test
.
podName
,
test
.
nodeName
,
test
.
predicateKey
,
test
.
predicateID
,
test
.
equivalenceHashForCalPredicate
,
)
fit
,
reasons
:=
result
.
Fit
,
result
.
FailReasons
// returned invalid should match expectedPredicateKeyMiss or expectedEquivalenceHashMiss
if
test
.
equivalenceHashForUpdatePredicate
!=
test
.
equivalenceHashForCalPredicate
{
if
ok
&&
test
.
expectedEquivalenceHashMiss
{
t
.
Errorf
(
"Failed: %s, expected (equivalence hash) cache miss"
,
test
.
name
)
}
if
!
ok
&&
!
test
.
expectedEquivalenceHashMiss
{
t
.
Errorf
(
"Failed: %s, expected (equivalence hash) cache hit"
,
test
.
name
)
}
}
else
{
if
ok
&&
test
.
expectedPredicateKeyMiss
{
t
.
Errorf
(
"Failed: %s, expected (predicate key) cache miss"
,
test
.
name
)
}
if
!
ok
&&
!
test
.
expectedPredicateKeyMiss
{
t
.
Errorf
(
"Failed: %s, expected (predicate key) cache hit"
,
test
.
name
)
}
}
// returned predicate result should match expected predicate item
if
fit
!=
test
.
expectedPredicateItem
.
fit
{
t
.
Errorf
(
"Failed: %s, expected fit: %v, but got: %v"
,
test
.
name
,
test
.
cachedItem
.
fit
,
fit
)
}
if
!
slicesEqual
(
reasons
,
test
.
expectedPredicateItem
.
reasons
)
{
t
.
Errorf
(
"Failed: %s, expected reasons: %v, but got: %v"
,
test
.
name
,
test
.
expectedPredicateItem
.
reasons
,
reasons
)
}
})
}
}
func
TestGetEquivalenceHash
(
t
*
testing
.
T
)
{
pod1
:=
makeBasicPod
(
"pod1"
)
pod2
:=
makeBasicPod
(
"pod2"
)
pod3
:=
makeBasicPod
(
"pod3"
)
pod3
.
Spec
.
Volumes
=
[]
v1
.
Volume
{
{
VolumeSource
:
v1
.
VolumeSource
{
PersistentVolumeClaim
:
&
v1
.
PersistentVolumeClaimVolumeSource
{
ClaimName
:
"someEBSVol111"
,
},
},
},
}
pod4
:=
makeBasicPod
(
"pod4"
)
pod4
.
Spec
.
Volumes
=
[]
v1
.
Volume
{
{
VolumeSource
:
v1
.
VolumeSource
{
PersistentVolumeClaim
:
&
v1
.
PersistentVolumeClaimVolumeSource
{
ClaimName
:
"someEBSVol222"
,
},
},
},
}
pod5
:=
makeBasicPod
(
"pod5"
)
pod5
.
Spec
.
Volumes
=
[]
v1
.
Volume
{}
pod6
:=
makeBasicPod
(
"pod6"
)
pod6
.
Spec
.
Volumes
=
nil
pod7
:=
makeBasicPod
(
"pod7"
)
pod7
.
Spec
.
NodeSelector
=
nil
pod8
:=
makeBasicPod
(
"pod8"
)
pod8
.
Spec
.
NodeSelector
=
make
(
map
[
string
]
string
)
type
podInfo
struct
{
pod
*
v1
.
Pod
hashIsValid
bool
}
tests
:=
[]
struct
{
name
string
podInfoList
[]
podInfo
isEquivalent
bool
}{
{
name
:
"pods with everything the same except name"
,
podInfoList
:
[]
podInfo
{
{
pod
:
pod1
,
hashIsValid
:
true
},
{
pod
:
pod2
,
hashIsValid
:
true
},
},
isEquivalent
:
true
,
},
{
name
:
"pods that only differ in their PVC volume sources"
,
podInfoList
:
[]
podInfo
{
{
pod
:
pod3
,
hashIsValid
:
true
},
{
pod
:
pod4
,
hashIsValid
:
true
},
},
isEquivalent
:
false
,
},
{
name
:
"pods that have no volumes, but one uses nil and one uses an empty slice"
,
podInfoList
:
[]
podInfo
{
{
pod
:
pod5
,
hashIsValid
:
true
},
{
pod
:
pod6
,
hashIsValid
:
true
},
},
isEquivalent
:
true
,
},
{
name
:
"pods that have no NodeSelector, but one uses nil and one uses an empty map"
,
podInfoList
:
[]
podInfo
{
{
pod
:
pod7
,
hashIsValid
:
true
},
{
pod
:
pod8
,
hashIsValid
:
true
},
},
isEquivalent
:
true
,
},
}
var
(
targetPodInfo
podInfo
targetHash
uint64
)
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
for
i
,
podInfo
:=
range
test
.
podInfoList
{
testPod
:=
podInfo
.
pod
eclassInfo
:=
NewClass
(
testPod
)
if
eclassInfo
==
nil
&&
podInfo
.
hashIsValid
{
t
.
Errorf
(
"Failed: pod %v is expected to have valid hash"
,
testPod
)
}
if
eclassInfo
!=
nil
{
// NOTE(harry): the first element will be used as target so
// this logic can't verify more than two inequivalent pods
if
i
==
0
{
targetHash
=
eclassInfo
.
hash
targetPodInfo
=
podInfo
}
else
{
if
targetHash
!=
eclassInfo
.
hash
{
if
test
.
isEquivalent
{
t
.
Errorf
(
"Failed: pod: %v is expected to be equivalent to: %v"
,
testPod
,
targetPodInfo
.
pod
)
}
}
}
}
}
})
}
}
func
TestInvalidateCachedPredicateItemOfAllNodes
(
t
*
testing
.
T
)
{
testPredicate
:=
"GeneralPredicates"
testPredicateID
:=
0
predicatesOrdering
:=
[]
string
{
testPredicate
}
// tests is used to initialize all nodes
tests
:=
[]
struct
{
name
string
podName
string
nodeName
string
equivalenceHashForUpdatePredicate
uint64
cachedItem
predicateItemType
}{
{
name
:
"hash predicate 123 not fits host ports"
,
podName
:
"testPod"
,
nodeName
:
"node1"
,
equivalenceHashForUpdatePredicate
:
123
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
,
},
},
},
{
name
:
"hash predicate 456 not fits host ports"
,
podName
:
"testPod"
,
nodeName
:
"node2"
,
equivalenceHashForUpdatePredicate
:
456
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
,
},
},
},
{
name
:
"hash predicate 123 fits"
,
podName
:
"testPod"
,
nodeName
:
"node3"
,
equivalenceHashForUpdatePredicate
:
123
,
cachedItem
:
predicateItemType
{
fit
:
true
,
},
},
}
ecache
:=
NewCache
(
predicatesOrdering
)
for
_
,
test
:=
range
tests
{
node
:=
schedulercache
.
NewNodeInfo
()
testNode
:=
&
v1
.
Node
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
test
.
nodeName
}}
node
.
SetNode
(
testNode
)
nodeCache
,
_
:=
ecache
.
GetNodeCache
(
testNode
.
Name
)
// set cached item to equivalence cache
nodeCache
.
updateResult
(
test
.
podName
,
testPredicate
,
testPredicateID
,
test
.
cachedItem
.
fit
,
test
.
cachedItem
.
reasons
,
test
.
equivalenceHashForUpdatePredicate
,
node
,
)
}
// invalidate cached predicate for all nodes
ecache
.
InvalidatePredicates
(
sets
.
NewString
(
testPredicate
))
// there should be no cached predicate any more
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
if
nodeCache
,
exist
:=
ecache
.
nodeToCache
[
test
.
nodeName
];
exist
{
if
cache
:=
nodeCache
.
cache
[
testPredicateID
];
cache
!=
nil
{
t
.
Errorf
(
"Failed: cached item for predicate key: %v on node: %v should be invalidated"
,
testPredicate
,
test
.
nodeName
)
}
}
})
}
}
func
TestInvalidateAllCachedPredicateItemOfNode
(
t
*
testing
.
T
)
{
testPredicate
:=
"GeneralPredicates"
testPredicateID
:=
0
predicatesOrdering
:=
[]
string
{
testPredicate
}
// tests is used to initialize all nodes
tests
:=
[]
struct
{
name
string
podName
string
nodeName
string
equivalenceHashForUpdatePredicate
uint64
cachedItem
predicateItemType
}{
{
name
:
"hash predicate 123 not fits host ports"
,
podName
:
"testPod"
,
nodeName
:
"node1"
,
equivalenceHashForUpdatePredicate
:
123
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
},
{
name
:
"hash predicate 456 not fits host ports"
,
podName
:
"testPod"
,
nodeName
:
"node2"
,
equivalenceHashForUpdatePredicate
:
456
,
cachedItem
:
predicateItemType
{
fit
:
false
,
reasons
:
[]
algorithm
.
PredicateFailureReason
{
predicates
.
ErrPodNotFitsHostPorts
},
},
},
{
name
:
"hash predicate 123 fits host ports"
,
podName
:
"testPod"
,
nodeName
:
"node3"
,
equivalenceHashForUpdatePredicate
:
123
,
cachedItem
:
predicateItemType
{
fit
:
true
,
},
},
}
ecache
:=
NewCache
(
predicatesOrdering
)
for
_
,
test
:=
range
tests
{
node
:=
schedulercache
.
NewNodeInfo
()
testNode
:=
&
v1
.
Node
{
ObjectMeta
:
metav1
.
ObjectMeta
{
Name
:
test
.
nodeName
}}
node
.
SetNode
(
testNode
)
nodeCache
,
_
:=
ecache
.
GetNodeCache
(
testNode
.
Name
)
// set cached item to equivalence cache
nodeCache
.
updateResult
(
test
.
podName
,
testPredicate
,
testPredicateID
,
test
.
cachedItem
.
fit
,
test
.
cachedItem
.
reasons
,
test
.
equivalenceHashForUpdatePredicate
,
node
,
)
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
oldNodeCache
,
_
:=
ecache
.
GetNodeCache
(
test
.
nodeName
)
oldGeneration
:=
oldNodeCache
.
generation
// invalidate cached predicate for all nodes
ecache
.
InvalidateAllPredicatesOnNode
(
test
.
nodeName
)
if
n
,
_
:=
ecache
.
GetNodeCache
(
test
.
nodeName
);
oldGeneration
==
n
.
generation
{
t
.
Errorf
(
"Failed: cached item for node: %v should be invalidated"
,
test
.
nodeName
)
}
})
}
}
func
BenchmarkEquivalenceHash
(
b
*
testing
.
B
)
{
pod
:=
makeBasicPod
(
"test"
)
for
i
:=
0
;
i
<
b
.
N
;
i
++
{
getEquivalencePod
(
pod
)
}
}
pkg/scheduler/core/generic_scheduler.go
View file @
89d6ec4b
...
...
@@ -40,7 +40,6 @@ import (
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
schedulerapi
"k8s.io/kubernetes/pkg/scheduler/api"
schedulercache
"k8s.io/kubernetes/pkg/scheduler/cache"
"k8s.io/kubernetes/pkg/scheduler/core/equivalence"
schedulerinternalcache
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
internalqueue
"k8s.io/kubernetes/pkg/scheduler/internal/queue"
"k8s.io/kubernetes/pkg/scheduler/metrics"
...
...
@@ -97,7 +96,6 @@ func (f *FitError) Error() string {
type
genericScheduler
struct
{
cache
schedulerinternalcache
.
Cache
equivalenceCache
*
equivalence
.
Cache
schedulingQueue
internalqueue
.
SchedulingQueue
predicates
map
[
string
]
algorithm
.
FitPredicate
priorityMetaProducer
algorithm
.
PriorityMetadataProducer
...
...
@@ -117,18 +115,6 @@ type genericScheduler struct {
// snapshot snapshots equivalane cache and node infos for all fit and priority
// functions.
func
(
g
*
genericScheduler
)
snapshot
()
error
{
// IMPORTANT NOTE: We must snapshot equivalence cache before snapshotting
// scheduler cache, otherwise stale data may be written into equivalence
// cache, e.g.
// 1. snapshot cache
// 2. event arrives, updating cache and invalidating predicates or whole node cache
// 3. snapshot ecache
// 4. evaludate predicates
// 5. stale result will be written to ecache
if
g
.
equivalenceCache
!=
nil
{
g
.
equivalenceCache
.
Snapshot
()
}
// Used for all fit and priority funcs.
return
g
.
cache
.
UpdateNodeNameToInfoMap
(
g
.
cachedNodeInfoMap
)
}
...
...
@@ -400,7 +386,6 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v
var
(
predicateResultLock
sync
.
Mutex
filteredLen
int32
equivClass
*
equivalence
.
Class
)
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
...
...
@@ -408,26 +393,15 @@ func (g *genericScheduler) findNodesThatFit(pod *v1.Pod, nodes []*v1.Node) ([]*v
// We can use the same metadata producer for all nodes.
meta
:=
g
.
predicateMetaProducer
(
pod
,
g
.
cachedNodeInfoMap
)
if
g
.
equivalenceCache
!=
nil
{
// getEquivalenceClassInfo will return immediately if no equivalence pod found
equivClass
=
equivalence
.
NewClass
(
pod
)
}
checkNode
:=
func
(
i
int
)
{
var
nodeCache
*
equivalence
.
NodeCache
nodeName
:=
g
.
cache
.
NodeTree
()
.
Next
()
if
g
.
equivalenceCache
!=
nil
{
nodeCache
=
g
.
equivalenceCache
.
LoadNodeCache
(
nodeName
)
}
fits
,
failedPredicates
,
err
:=
podFitsOnNode
(
pod
,
meta
,
g
.
cachedNodeInfoMap
[
nodeName
],
g
.
predicates
,
nodeCache
,
g
.
schedulingQueue
,
g
.
alwaysCheckAllPredicates
,
equivClass
,
)
if
err
!=
nil
{
predicateResultLock
.
Lock
()
...
...
@@ -536,13 +510,10 @@ func podFitsOnNode(
meta
algorithm
.
PredicateMetadata
,
info
*
schedulercache
.
NodeInfo
,
predicateFuncs
map
[
string
]
algorithm
.
FitPredicate
,
nodeCache
*
equivalence
.
NodeCache
,
queue
internalqueue
.
SchedulingQueue
,
alwaysCheckAllPredicates
bool
,
equivClass
*
equivalence
.
Class
,
)
(
bool
,
[]
algorithm
.
PredicateFailureReason
,
error
)
{
var
(
eCacheAvailable
bool
failedPredicates
[]
algorithm
.
PredicateFailureReason
)
...
...
@@ -576,8 +547,7 @@ func podFitsOnNode(
// Bypass eCache if node has any nominated pods.
// TODO(bsalamat): consider using eCache and adding proper eCache invalidations
// when pods are nominated or their nominations change.
eCacheAvailable
=
equivClass
!=
nil
&&
nodeCache
!=
nil
&&
!
podsAdded
for
predicateID
,
predicateKey
:=
range
predicates
.
Ordering
()
{
for
_
,
predicateKey
:=
range
predicates
.
Ordering
()
{
var
(
fit
bool
reasons
[]
algorithm
.
PredicateFailureReason
...
...
@@ -585,11 +555,7 @@ func podFitsOnNode(
)
//TODO (yastij) : compute average predicate restrictiveness to export it as Prometheus metric
if
predicate
,
exist
:=
predicateFuncs
[
predicateKey
];
exist
{
if
eCacheAvailable
{
fit
,
reasons
,
err
=
nodeCache
.
RunPredicate
(
predicate
,
predicateKey
,
predicateID
,
pod
,
metaToUse
,
nodeInfoToUse
,
equivClass
)
}
else
{
fit
,
reasons
,
err
=
predicate
(
pod
,
metaToUse
,
nodeInfoToUse
)
}
fit
,
reasons
,
err
=
predicate
(
pod
,
metaToUse
,
nodeInfoToUse
)
if
err
!=
nil
{
return
false
,
[]
algorithm
.
PredicateFailureReason
{},
err
}
...
...
@@ -1018,7 +984,7 @@ func selectVictimsOnNode(
// that we should check is if the "pod" is failing to schedule due to pod affinity
// failure.
// TODO(bsalamat): Consider checking affinity to lower priority pods if feasible with reasonable performance.
if
fits
,
_
,
err
:=
podFitsOnNode
(
pod
,
meta
,
nodeInfoCopy
,
fitPredicates
,
nil
,
queue
,
false
,
nil
);
!
fits
{
if
fits
,
_
,
err
:=
podFitsOnNode
(
pod
,
meta
,
nodeInfoCopy
,
fitPredicates
,
queue
,
false
);
!
fits
{
if
err
!=
nil
{
klog
.
Warningf
(
"Encountered error while selecting victims on node %v: %v"
,
nodeInfo
.
Node
()
.
Name
,
err
)
}
...
...
@@ -1032,7 +998,7 @@ func selectVictimsOnNode(
violatingVictims
,
nonViolatingVictims
:=
filterPodsWithPDBViolation
(
potentialVictims
.
Items
,
pdbs
)
reprievePod
:=
func
(
p
*
v1
.
Pod
)
bool
{
addPod
(
p
)
fits
,
_
,
_
:=
podFitsOnNode
(
pod
,
meta
,
nodeInfoCopy
,
fitPredicates
,
nil
,
queue
,
false
,
nil
)
fits
,
_
,
_
:=
podFitsOnNode
(
pod
,
meta
,
nodeInfoCopy
,
fitPredicates
,
queue
,
false
)
if
!
fits
{
removePod
(
p
)
victims
=
append
(
victims
,
p
)
...
...
@@ -1148,7 +1114,6 @@ func podPassesBasicChecks(pod *v1.Pod, pvcLister corelisters.PersistentVolumeCla
// NewGenericScheduler creates a genericScheduler object.
func
NewGenericScheduler
(
cache
schedulerinternalcache
.
Cache
,
eCache
*
equivalence
.
Cache
,
podQueue
internalqueue
.
SchedulingQueue
,
predicates
map
[
string
]
algorithm
.
FitPredicate
,
predicateMetaProducer
algorithm
.
PredicateMetadataProducer
,
...
...
@@ -1164,7 +1129,6 @@ func NewGenericScheduler(
)
algorithm
.
ScheduleAlgorithm
{
return
&
genericScheduler
{
cache
:
cache
,
equivalenceCache
:
eCache
,
schedulingQueue
:
podQueue
,
predicates
:
predicates
,
predicateMetaProducer
:
predicateMetaProducer
,
...
...
pkg/scheduler/factory/factory.go
View file @
89d6ec4b
...
...
@@ -51,7 +51,6 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
podutil
"k8s.io/kubernetes/pkg/api/v1/pod"
"k8s.io/kubernetes/pkg/apis/core/helper"
"k8s.io/kubernetes/pkg/features"
kubeletapis
"k8s.io/kubernetes/pkg/kubelet/apis"
"k8s.io/kubernetes/pkg/scheduler/algorithm"
...
...
@@ -59,7 +58,6 @@ import (
schedulerapi
"k8s.io/kubernetes/pkg/scheduler/api"
"k8s.io/kubernetes/pkg/scheduler/api/validation"
"k8s.io/kubernetes/pkg/scheduler/core"
"k8s.io/kubernetes/pkg/scheduler/core/equivalence"
schedulerinternalcache
"k8s.io/kubernetes/pkg/scheduler/internal/cache"
cachedebugger
"k8s.io/kubernetes/pkg/scheduler/internal/cache/debugger"
internalqueue
"k8s.io/kubernetes/pkg/scheduler/internal/queue"
...
...
@@ -97,9 +95,6 @@ type Config struct {
// It is expected that changes made via SchedulerCache will be observed
// by NodeLister and Algorithm.
SchedulerCache
schedulerinternalcache
.
Cache
// Ecache is used for optimistically invalid affected cache items after
// successfully binding a pod
Ecache
*
equivalence
.
Cache
NodeLister
algorithm
.
NodeLister
Algorithm
algorithm
.
ScheduleAlgorithm
GetBinder
func
(
pod
*
v1
.
Pod
)
Binder
...
...
@@ -219,12 +214,6 @@ type configFactory struct {
// HardPodAffinitySymmetricWeight represents the weight of implicit PreferredDuringScheduling affinity rule, in the range 0-100.
hardPodAffinitySymmetricWeight
int32
// Equivalence class cache
equivalencePodCache
*
equivalence
.
Cache
// Enable equivalence class cache
enableEquivalenceClassCache
bool
// Handles volume binding decisions
volumeBinder
*
volumebinder
.
VolumeBinder
...
...
@@ -253,7 +242,6 @@ type ConfigFactoryArgs struct {
PdbInformer
policyinformers
.
PodDisruptionBudgetInformer
StorageClassInformer
storageinformers
.
StorageClassInformer
HardPodAffinitySymmetricWeight
int32
EnableEquivalenceClassCache
bool
DisablePreemption
bool
PercentageOfNodesToScore
int32
BindTimeoutSeconds
int64
...
...
@@ -291,7 +279,6 @@ func NewConfigFactory(args *ConfigFactoryArgs) Configurator {
StopEverything
:
stopEverything
,
schedulerName
:
args
.
SchedulerName
,
hardPodAffinitySymmetricWeight
:
args
.
HardPodAffinitySymmetricWeight
,
enableEquivalenceClassCache
:
args
.
EnableEquivalenceClassCache
,
disablePreemption
:
args
.
DisablePreemption
,
percentageOfNodesToScore
:
args
.
PercentageOfNodesToScore
,
}
...
...
@@ -480,14 +467,6 @@ func (c *configFactory) skipPodUpdate(pod *v1.Pod) bool {
}
func
(
c
*
configFactory
)
onPvAdd
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
pv
,
ok
:=
obj
.
(
*
v1
.
PersistentVolume
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolume: %v"
,
obj
)
return
}
c
.
invalidatePredicatesForPv
(
pv
)
}
// Pods created when there are no PVs available will be stuck in
// unschedulable queue. But unbound PVs created for static provisioning and
// delay binding storage class are skipped in PV controller dynamic
...
...
@@ -498,19 +477,6 @@ func (c *configFactory) onPvAdd(obj interface{}) {
}
func
(
c
*
configFactory
)
onPvUpdate
(
old
,
new
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
newPV
,
ok
:=
new
.
(
*
v1
.
PersistentVolume
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolume: %v"
,
new
)
return
}
oldPV
,
ok
:=
old
.
(
*
v1
.
PersistentVolume
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolume: %v"
,
old
)
return
}
c
.
invalidatePredicatesForPvUpdate
(
oldPV
,
newPV
)
}
// Scheduler.bindVolumesWorker may fail to update assumed pod volume
// bindings due to conflicts if PVs are updated by PV controller or other
// parties, then scheduler will add pod back to unschedulable queue. We
...
...
@@ -534,7 +500,6 @@ func (c *configFactory) invalidatePredicatesForPvUpdate(oldPV, newPV *v1.Persist
break
}
}
c
.
equivalencePodCache
.
InvalidatePredicates
(
invalidPredicates
)
}
// isZoneRegionLabel check if given key of label is zone or region label.
...
...
@@ -543,24 +508,6 @@ func isZoneRegionLabel(k string) bool {
}
func
(
c
*
configFactory
)
onPvDelete
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
var
pv
*
v1
.
PersistentVolume
switch
t
:=
obj
.
(
type
)
{
case
*
v1
.
PersistentVolume
:
pv
=
t
case
cache
.
DeletedFinalStateUnknown
:
var
ok
bool
pv
,
ok
=
t
.
Obj
.
(
*
v1
.
PersistentVolume
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolume: %v"
,
t
.
Obj
)
return
}
default
:
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolume: %v"
,
t
)
return
}
c
.
invalidatePredicatesForPv
(
pv
)
}
}
func
(
c
*
configFactory
)
invalidatePredicatesForPv
(
pv
*
v1
.
PersistentVolume
)
{
...
...
@@ -595,19 +542,9 @@ func (c *configFactory) invalidatePredicatesForPv(pv *v1.PersistentVolume) {
// Add/delete impacts the available PVs to choose from
invalidPredicates
.
Insert
(
predicates
.
CheckVolumeBindingPred
)
}
c
.
equivalencePodCache
.
InvalidatePredicates
(
invalidPredicates
)
}
func
(
c
*
configFactory
)
onPvcAdd
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
pvc
,
ok
:=
obj
.
(
*
v1
.
PersistentVolumeClaim
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolumeClaim: %v"
,
obj
)
return
}
c
.
invalidatePredicatesForPvc
(
pvc
)
}
c
.
podQueue
.
MoveAllToActiveQueue
()
}
...
...
@@ -616,41 +553,10 @@ func (c *configFactory) onPvcUpdate(old, new interface{}) {
return
}
if
c
.
enableEquivalenceClassCache
{
newPVC
,
ok
:=
new
.
(
*
v1
.
PersistentVolumeClaim
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolumeClaim: %v"
,
new
)
return
}
oldPVC
,
ok
:=
old
.
(
*
v1
.
PersistentVolumeClaim
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolumeClaim: %v"
,
old
)
return
}
c
.
invalidatePredicatesForPvcUpdate
(
oldPVC
,
newPVC
)
}
c
.
podQueue
.
MoveAllToActiveQueue
()
}
func
(
c
*
configFactory
)
onPvcDelete
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
var
pvc
*
v1
.
PersistentVolumeClaim
switch
t
:=
obj
.
(
type
)
{
case
*
v1
.
PersistentVolumeClaim
:
pvc
=
t
case
cache
.
DeletedFinalStateUnknown
:
var
ok
bool
pvc
,
ok
=
t
.
Obj
.
(
*
v1
.
PersistentVolumeClaim
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolumeClaim: %v"
,
t
.
Obj
)
return
}
default
:
klog
.
Errorf
(
"cannot convert to *v1.PersistentVolumeClaim: %v"
,
t
)
return
}
c
.
invalidatePredicatesForPvc
(
pvc
)
}
}
func
(
c
*
configFactory
)
invalidatePredicatesForPvc
(
pvc
*
v1
.
PersistentVolumeClaim
)
{
...
...
@@ -670,7 +576,6 @@ func (c *configFactory) invalidatePredicatesForPvc(pvc *v1.PersistentVolumeClaim
// Add/delete impacts the available PVs to choose from
invalidPredicates
.
Insert
(
predicates
.
CheckVolumeBindingPred
)
}
c
.
equivalencePodCache
.
InvalidatePredicates
(
invalidPredicates
)
}
func
(
c
*
configFactory
)
invalidatePredicatesForPvcUpdate
(
old
,
new
*
v1
.
PersistentVolumeClaim
)
{
...
...
@@ -688,8 +593,6 @@ func (c *configFactory) invalidatePredicatesForPvcUpdate(old, new *v1.Persistent
invalidPredicates
.
Insert
(
predicates
.
MaxCSIVolumeCountPred
)
}
}
c
.
equivalencePodCache
.
InvalidatePredicates
(
invalidPredicates
)
}
func
(
c
*
configFactory
)
onStorageClassAdd
(
obj
interface
{})
{
...
...
@@ -711,24 +614,6 @@ func (c *configFactory) onStorageClassAdd(obj interface{}) {
}
func
(
c
*
configFactory
)
onStorageClassDelete
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
var
sc
*
storagev1
.
StorageClass
switch
t
:=
obj
.
(
type
)
{
case
*
storagev1
.
StorageClass
:
sc
=
t
case
cache
.
DeletedFinalStateUnknown
:
var
ok
bool
sc
,
ok
=
t
.
Obj
.
(
*
storagev1
.
StorageClass
)
if
!
ok
{
klog
.
Errorf
(
"cannot convert to *storagev1.StorageClass: %v"
,
t
.
Obj
)
return
}
default
:
klog
.
Errorf
(
"cannot convert to *storagev1.StorageClass: %v"
,
t
)
return
}
c
.
invalidatePredicatesForStorageClass
(
sc
)
}
}
func
(
c
*
configFactory
)
invalidatePredicatesForStorageClass
(
sc
*
storagev1
.
StorageClass
)
{
...
...
@@ -741,33 +626,17 @@ func (c *configFactory) invalidatePredicatesForStorageClass(sc *storagev1.Storag
invalidPredicates
.
Insert
(
predicates
.
NoVolumeZoneConflictPred
)
}
}
c
.
equivalencePodCache
.
InvalidatePredicates
(
invalidPredicates
)
}
func
(
c
*
configFactory
)
onServiceAdd
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
c
.
equivalencePodCache
.
InvalidatePredicates
(
serviceAffinitySet
)
}
c
.
podQueue
.
MoveAllToActiveQueue
()
}
func
(
c
*
configFactory
)
onServiceUpdate
(
oldObj
interface
{},
newObj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
// TODO(resouer) We may need to invalidate this for specified group of pods only
oldService
:=
oldObj
.
(
*
v1
.
Service
)
newService
:=
newObj
.
(
*
v1
.
Service
)
if
!
reflect
.
DeepEqual
(
oldService
.
Spec
.
Selector
,
newService
.
Spec
.
Selector
)
{
c
.
equivalencePodCache
.
InvalidatePredicates
(
serviceAffinitySet
)
}
}
c
.
podQueue
.
MoveAllToActiveQueue
()
}
func
(
c
*
configFactory
)
onServiceDelete
(
obj
interface
{})
{
if
c
.
enableEquivalenceClassCache
{
c
.
equivalencePodCache
.
InvalidatePredicates
(
serviceAffinitySet
)
}
c
.
podQueue
.
MoveAllToActiveQueue
()
}
...
...
@@ -878,24 +747,6 @@ func (c *configFactory) deletePodFromSchedulingQueue(obj interface{}) {
}
func
(
c
*
configFactory
)
invalidateCachedPredicatesOnUpdatePod
(
newPod
*
v1
.
Pod
,
oldPod
*
v1
.
Pod
)
{
if
c
.
enableEquivalenceClassCache
{
// if the pod does not have bound node, updating equivalence cache is meaningless;
// if pod's bound node has been changed, that case should be handled by pod add & delete.
if
len
(
newPod
.
Spec
.
NodeName
)
!=
0
&&
newPod
.
Spec
.
NodeName
==
oldPod
.
Spec
.
NodeName
{
if
!
reflect
.
DeepEqual
(
oldPod
.
GetLabels
(),
newPod
.
GetLabels
())
{
// MatchInterPodAffinity need to be reconsidered for this node,
// as well as all nodes in its same failure domain.
c
.
equivalencePodCache
.
InvalidatePredicates
(
matchInterPodAffinitySet
)
}
// if requested container resource changed, invalidate GeneralPredicates of this node
if
!
reflect
.
DeepEqual
(
predicates
.
GetResourceRequest
(
newPod
),
predicates
.
GetResourceRequest
(
oldPod
))
{
c
.
equivalencePodCache
.
InvalidatePredicatesOnNode
(
newPod
.
Spec
.
NodeName
,
generalPredicatesSets
)
}
}
}
}
func
(
c
*
configFactory
)
deletePodFromCache
(
obj
interface
{})
{
...
...
@@ -928,24 +779,6 @@ func (c *configFactory) deletePodFromCache(obj interface{}) {
}
func
(
c
*
configFactory
)
invalidateCachedPredicatesOnDeletePod
(
pod
*
v1
.
Pod
)
{
if
c
.
enableEquivalenceClassCache
{
// part of this case is the same as pod add.
c
.
equivalencePodCache
.
InvalidateCachedPredicateItemForPodAdd
(
pod
,
pod
.
Spec
.
NodeName
)
// MatchInterPodAffinity need to be reconsidered for this node,
// as well as all nodes in its same failure domain.
// TODO(resouer) can we just do this for nodes in the same failure domain
c
.
equivalencePodCache
.
InvalidatePredicates
(
matchInterPodAffinitySet
)
// if this pod have these PV, cached result of disk conflict will become invalid.
for
_
,
volume
:=
range
pod
.
Spec
.
Volumes
{
if
volume
.
GCEPersistentDisk
!=
nil
||
volume
.
AWSElasticBlockStore
!=
nil
||
volume
.
RBD
!=
nil
||
volume
.
ISCSI
!=
nil
{
c
.
equivalencePodCache
.
InvalidatePredicatesOnNode
(
pod
.
Spec
.
NodeName
,
noDiskConflictSet
)
}
}
}
}
func
(
c
*
configFactory
)
addNodeToCache
(
obj
interface
{})
{
...
...
@@ -955,12 +788,6 @@ func (c *configFactory) addNodeToCache(obj interface{}) {
return
}
// NOTE: Because the scheduler uses equivalence cache for nodes, we need
// to create it before adding node into scheduler cache.
if
c
.
enableEquivalenceClassCache
{
// GetNodeCache() will lazily create NodeCache for given node if it does not exist.
c
.
equivalencePodCache
.
GetNodeCache
(
node
.
GetName
())
}
if
err
:=
c
.
schedulerCache
.
AddNode
(
node
);
err
!=
nil
{
klog
.
Errorf
(
"scheduler cache AddNode failed: %v"
,
err
)
...
...
@@ -1003,72 +830,6 @@ func (c *configFactory) updateNodeInCache(oldObj, newObj interface{}) {
}
func
(
c
*
configFactory
)
invalidateCachedPredicatesOnNodeUpdate
(
newNode
*
v1
.
Node
,
oldNode
*
v1
.
Node
)
{
if
c
.
enableEquivalenceClassCache
{
// Begin to update equivalence cache based on node update
// TODO(resouer): think about lazily initialize this set
invalidPredicates
:=
sets
.
NewString
()
if
!
reflect
.
DeepEqual
(
oldNode
.
Status
.
Allocatable
,
newNode
.
Status
.
Allocatable
)
{
invalidPredicates
.
Insert
(
predicates
.
GeneralPred
)
// "PodFitsResources"
}
if
!
reflect
.
DeepEqual
(
oldNode
.
GetLabels
(),
newNode
.
GetLabels
())
{
invalidPredicates
.
Insert
(
predicates
.
GeneralPred
,
predicates
.
CheckServiceAffinityPred
)
// "PodSelectorMatches"
for
k
,
v
:=
range
oldNode
.
GetLabels
()
{
// any label can be topology key of pod, we have to invalidate in all cases
if
v
!=
newNode
.
GetLabels
()[
k
]
{
invalidPredicates
.
Insert
(
predicates
.
MatchInterPodAffinityPred
)
}
// NoVolumeZoneConflict will only be affected by zone related label change
if
isZoneRegionLabel
(
k
)
{
if
v
!=
newNode
.
GetLabels
()[
k
]
{
invalidPredicates
.
Insert
(
predicates
.
NoVolumeZoneConflictPred
)
}
}
}
}
oldTaints
,
oldErr
:=
helper
.
GetTaintsFromNodeAnnotations
(
oldNode
.
GetAnnotations
())
if
oldErr
!=
nil
{
klog
.
Errorf
(
"Failed to get taints from old node annotation for equivalence cache"
)
}
newTaints
,
newErr
:=
helper
.
GetTaintsFromNodeAnnotations
(
newNode
.
GetAnnotations
())
if
newErr
!=
nil
{
klog
.
Errorf
(
"Failed to get taints from new node annotation for equivalence cache"
)
}
if
!
reflect
.
DeepEqual
(
oldTaints
,
newTaints
)
||
!
reflect
.
DeepEqual
(
oldNode
.
Spec
.
Taints
,
newNode
.
Spec
.
Taints
)
{
invalidPredicates
.
Insert
(
predicates
.
PodToleratesNodeTaintsPred
)
}
if
!
reflect
.
DeepEqual
(
oldNode
.
Status
.
Conditions
,
newNode
.
Status
.
Conditions
)
{
oldConditions
:=
make
(
map
[
v1
.
NodeConditionType
]
v1
.
ConditionStatus
)
newConditions
:=
make
(
map
[
v1
.
NodeConditionType
]
v1
.
ConditionStatus
)
for
_
,
cond
:=
range
oldNode
.
Status
.
Conditions
{
oldConditions
[
cond
.
Type
]
=
cond
.
Status
}
for
_
,
cond
:=
range
newNode
.
Status
.
Conditions
{
newConditions
[
cond
.
Type
]
=
cond
.
Status
}
if
oldConditions
[
v1
.
NodeMemoryPressure
]
!=
newConditions
[
v1
.
NodeMemoryPressure
]
{
invalidPredicates
.
Insert
(
predicates
.
CheckNodeMemoryPressurePred
)
}
if
oldConditions
[
v1
.
NodeDiskPressure
]
!=
newConditions
[
v1
.
NodeDiskPressure
]
{
invalidPredicates
.
Insert
(
predicates
.
CheckNodeDiskPressurePred
)
}
if
oldConditions
[
v1
.
NodePIDPressure
]
!=
newConditions
[
v1
.
NodePIDPressure
]
{
invalidPredicates
.
Insert
(
predicates
.
CheckNodePIDPressurePred
)
}
if
oldConditions
[
v1
.
NodeReady
]
!=
newConditions
[
v1
.
NodeReady
]
||
oldConditions
[
v1
.
NodeOutOfDisk
]
!=
newConditions
[
v1
.
NodeOutOfDisk
]
||
oldConditions
[
v1
.
NodeNetworkUnavailable
]
!=
newConditions
[
v1
.
NodeNetworkUnavailable
]
{
invalidPredicates
.
Insert
(
predicates
.
CheckNodeConditionPred
)
}
}
if
newNode
.
Spec
.
Unschedulable
!=
oldNode
.
Spec
.
Unschedulable
{
invalidPredicates
.
Insert
(
predicates
.
CheckNodeConditionPred
)
}
c
.
equivalencePodCache
.
InvalidatePredicatesOnNode
(
newNode
.
GetName
(),
invalidPredicates
)
}
}
func
nodeSchedulingPropertiesChanged
(
newNode
*
v1
.
Node
,
oldNode
*
v1
.
Node
)
bool
{
...
...
@@ -1142,9 +903,6 @@ func (c *configFactory) deleteNodeFromCache(obj interface{}) {
if
err
:=
c
.
schedulerCache
.
RemoveNode
(
node
);
err
!=
nil
{
klog
.
Errorf
(
"scheduler cache RemoveNode failed: %v"
,
err
)
}
if
c
.
enableEquivalenceClassCache
{
c
.
equivalencePodCache
.
InvalidateAllPredicatesOnNode
(
node
.
GetName
())
}
}
// Create creates a scheduler with the default algorithm provider.
...
...
@@ -1279,15 +1037,8 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
return
nil
,
err
}
// Init equivalence class cache
if
c
.
enableEquivalenceClassCache
{
c
.
equivalencePodCache
=
equivalence
.
NewCache
(
predicates
.
Ordering
())
klog
.
Info
(
"Created equivalence class cache"
)
}
algo
:=
core
.
NewGenericScheduler
(
c
.
schedulerCache
,
c
.
equivalencePodCache
,
c
.
podQueue
,
predicateFuncs
,
predicateMetaProducer
,
...
...
@@ -1305,7 +1056,6 @@ func (c *configFactory) CreateFromKeys(predicateKeys, priorityKeys sets.String,
podBackoff
:=
util
.
CreateDefaultPodBackoff
()
return
&
Config
{
SchedulerCache
:
c
.
schedulerCache
,
Ecache
:
c
.
equivalencePodCache
,
// The scheduler only needs to consider schedulable nodes.
NodeLister
:
&
nodeLister
{
c
.
nodeLister
},
Algorithm
:
algo
,
...
...
@@ -1509,10 +1259,6 @@ func (c *configFactory) MakeDefaultErrorFunc(backoff *util.PodBackoff, podQueue
// snapshotted before updates are written, we would update equivalence
// cache with stale information which is based on snapshot of old cache.
c
.
schedulerCache
.
RemoveNode
(
&
node
)
// invalidate cached predicate for the node
if
c
.
enableEquivalenceClassCache
{
c
.
equivalencePodCache
.
InvalidateAllPredicatesOnNode
(
nodeName
)
}
}
}
}
else
{
...
...
pkg/scheduler/scheduler.go
View file @
89d6ec4b
...
...
@@ -25,7 +25,6 @@ import (
"k8s.io/api/core/v1"
metav1
"k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
appsinformers
"k8s.io/client-go/informers/apps/v1"
...
...
@@ -35,7 +34,6 @@ import (
clientset
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/record"
"k8s.io/kubernetes/pkg/features"
"k8s.io/kubernetes/pkg/scheduler/algorithm/predicates"
schedulerapi
"k8s.io/kubernetes/pkg/scheduler/api"
latestschedulerapi
"k8s.io/kubernetes/pkg/scheduler/api/latest"
kubeschedulerconfig
"k8s.io/kubernetes/pkg/scheduler/apis/config"
...
...
@@ -67,7 +65,6 @@ func (sched *Scheduler) Cache() schedulerinternalcache.Cache {
type
schedulerOptions
struct
{
schedulerName
string
hardPodAffinitySymmetricWeight
int32
enableEquivalenceClassCache
bool
disablePreemption
bool
percentageOfNodesToScore
int32
bindTimeoutSeconds
int64
...
...
@@ -90,13 +87,6 @@ func WithHardPodAffinitySymmetricWeight(hardPodAffinitySymmetricWeight int32) Op
}
}
// WithEquivalenceClassCacheEnabled sets enableEquivalenceClassCache for Scheduler, the default value is false
func
WithEquivalenceClassCacheEnabled
(
enableEquivalenceClassCache
bool
)
Option
{
return
func
(
o
*
schedulerOptions
)
{
o
.
enableEquivalenceClassCache
=
enableEquivalenceClassCache
}
}
// WithPreemptionDisabled sets disablePreemption for Scheduler, the default value is false
func
WithPreemptionDisabled
(
disablePreemption
bool
)
Option
{
return
func
(
o
*
schedulerOptions
)
{
...
...
@@ -121,7 +111,6 @@ func WithBindTimeoutSeconds(bindTimeoutSeconds int64) Option {
var
defaultSchedulerOptions
=
schedulerOptions
{
schedulerName
:
v1
.
DefaultSchedulerName
,
hardPodAffinitySymmetricWeight
:
v1
.
DefaultHardPodAffinitySymmetricWeight
,
enableEquivalenceClassCache
:
false
,
disablePreemption
:
false
,
percentageOfNodesToScore
:
schedulerapi
.
DefaultPercentageOfNodesToScore
,
bindTimeoutSeconds
:
BindTimeoutSeconds
,
...
...
@@ -164,7 +153,6 @@ func New(client clientset.Interface,
PdbInformer
:
pdbInformer
,
StorageClassInformer
:
storageClassInformer
,
HardPodAffinitySymmetricWeight
:
options
.
hardPodAffinitySymmetricWeight
,
EnableEquivalenceClassCache
:
options
.
enableEquivalenceClassCache
,
DisablePreemption
:
options
.
disablePreemption
,
PercentageOfNodesToScore
:
options
.
percentageOfNodesToScore
,
BindTimeoutSeconds
:
options
.
bindTimeoutSeconds
,
...
...
@@ -379,12 +367,6 @@ func (sched *Scheduler) assumeVolumes(assumed *v1.Pod, host string) (allBound bo
Message
:
err
.
Error
(),
})
}
// Invalidate ecache because assumed volumes could have affected the cached
// pvs for other pods
if
sched
.
config
.
Ecache
!=
nil
{
invalidPredicates
:=
sets
.
NewString
(
predicates
.
CheckVolumeBindingPred
)
sched
.
config
.
Ecache
.
InvalidatePredicates
(
invalidPredicates
)
}
}
return
}
...
...
@@ -464,12 +446,6 @@ func (sched *Scheduler) assume(assumed *v1.Pod, host string) error {
sched
.
config
.
SchedulingQueue
.
DeleteNominatedPodIfExists
(
assumed
)
}
// Optimistically assume that the binding will succeed, so we need to invalidate affected
// predicates in equivalence cache.
// If the binding fails, these invalidated item will not break anything.
if
sched
.
config
.
Ecache
!=
nil
{
sched
.
config
.
Ecache
.
InvalidateCachedPredicateItemForPodAdd
(
assumed
,
host
)
}
return
nil
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment