804 lines
23 KiB
Go
804 lines
23 KiB
Go
/*
|
|
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)
|
|
}
|
|
}
|