2014-12-19 09:27:01 +00:00
|
|
|
/*
|
2017-10-11 23:36:39 +00:00
|
|
|
Copyright 2017 The Kubernetes Authors.
|
2014-12-19 09:27:01 +00:00
|
|
|
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
package nodelifecycle
|
2014-12-19 09:27:01 +00:00
|
|
|
|
|
|
|
import (
|
2016-09-12 14:47:17 +00:00
|
|
|
"strings"
|
2014-12-19 09:27:01 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2018-08-16 02:06:39 +00:00
|
|
|
apps "k8s.io/api/apps/v1"
|
2018-10-01 18:32:56 +00:00
|
|
|
coordv1beta1 "k8s.io/api/coordination/v1beta1"
|
2017-06-22 18:24:23 +00:00
|
|
|
"k8s.io/api/core/v1"
|
2017-01-25 13:39:54 +00:00
|
|
|
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
2017-01-25 13:13:07 +00:00
|
|
|
"k8s.io/apimachinery/pkg/api/resource"
|
2017-01-11 14:09:48 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/util/diff"
|
2018-10-01 18:32:56 +00:00
|
|
|
utilfeature "k8s.io/apiserver/pkg/util/feature"
|
|
|
|
utilfeaturetesting "k8s.io/apiserver/pkg/util/feature/testing"
|
2017-06-23 20:56:37 +00:00
|
|
|
"k8s.io/client-go/informers"
|
2018-08-16 02:06:39 +00:00
|
|
|
appsinformers "k8s.io/client-go/informers/apps/v1"
|
2018-10-01 18:32:56 +00:00
|
|
|
coordinformers "k8s.io/client-go/informers/coordination/v1beta1"
|
2017-06-23 20:56:37 +00:00
|
|
|
coreinformers "k8s.io/client-go/informers/core/v1"
|
|
|
|
clientset "k8s.io/client-go/kubernetes"
|
|
|
|
"k8s.io/client-go/kubernetes/fake"
|
2017-01-25 20:07:10 +00:00
|
|
|
testcore "k8s.io/client-go/testing"
|
2016-09-23 16:01:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller"
|
2017-10-11 23:36:39 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller/nodelifecycle/scheduler"
|
2017-08-04 23:34:28 +00:00
|
|
|
"k8s.io/kubernetes/pkg/controller/testutil"
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeutil "k8s.io/kubernetes/pkg/controller/util/node"
|
2018-10-01 18:32:56 +00:00
|
|
|
"k8s.io/kubernetes/pkg/features"
|
2019-02-23 00:09:07 +00:00
|
|
|
kubeletapis "k8s.io/kubernetes/pkg/kubelet/apis"
|
2018-09-28 02:37:38 +00:00
|
|
|
schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
|
2016-11-02 19:56:19 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/node"
|
2017-07-06 13:13:13 +00:00
|
|
|
taintutils "k8s.io/kubernetes/pkg/util/taints"
|
2018-10-01 18:32:56 +00:00
|
|
|
"k8s.io/utils/pointer"
|
2014-12-19 09:27:01 +00:00
|
|
|
)
|
|
|
|
|
2015-03-31 11:17:12 +00:00
|
|
|
const (
|
2015-04-07 19:36:09 +00:00
|
|
|
testNodeMonitorGracePeriod = 40 * time.Second
|
|
|
|
testNodeStartupGracePeriod = 60 * time.Second
|
2015-03-31 11:17:12 +00:00
|
|
|
testNodeMonitorPeriod = 5 * time.Second
|
2016-07-12 12:29:46 +00:00
|
|
|
testRateLimiterQPS = float32(10000)
|
2016-08-05 12:50:19 +00:00
|
|
|
testLargeClusterThreshold = 20
|
2017-06-05 08:36:09 +00:00
|
|
|
testUnhealthyThreshold = float32(0.55)
|
2015-03-31 11:17:12 +00:00
|
|
|
)
|
|
|
|
|
2017-02-06 18:35:50 +00:00
|
|
|
func alwaysReady() bool { return true }
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
type nodeLifecycleController struct {
|
2017-08-08 23:25:20 +00:00
|
|
|
*Controller
|
2018-10-01 18:32:56 +00:00
|
|
|
leaseInformer coordinformers.LeaseInformer
|
2017-02-06 18:35:50 +00:00
|
|
|
nodeInformer coreinformers.NodeInformer
|
2018-08-16 02:06:39 +00:00
|
|
|
daemonSetInformer appsinformers.DaemonSetInformer
|
2017-02-06 18:35:50 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
// doEviction does the fake eviction and returns the status of eviction operation.
|
|
|
|
func (nc *nodeLifecycleController) doEviction(fakeNodeHandler *testutil.FakeNodeHandler) bool {
|
|
|
|
var podEvicted bool
|
|
|
|
zones := testutil.GetZones(fakeNodeHandler)
|
|
|
|
for _, zone := range zones {
|
|
|
|
nc.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
|
|
|
|
uid, _ := value.UID.(string)
|
|
|
|
nodeutil.DeletePods(fakeNodeHandler, nc.recorder, value.Value, uid, nc.daemonSetStore)
|
|
|
|
return true, 0
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, action := range fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
|
|
podEvicted = true
|
|
|
|
return podEvicted
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return podEvicted
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
func createNodeLease(nodeName string, renewTime metav1.MicroTime) *coordv1beta1.Lease {
|
|
|
|
return &coordv1beta1.Lease{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: nodeName,
|
|
|
|
Namespace: v1.NamespaceNodeLease,
|
|
|
|
},
|
|
|
|
Spec: coordv1beta1.LeaseSpec{
|
|
|
|
HolderIdentity: pointer.StringPtr(nodeName),
|
|
|
|
RenewTime: &renewTime,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (nc *nodeLifecycleController) syncLeaseStore(lease *coordv1beta1.Lease) error {
|
|
|
|
if lease == nil {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
newElems := make([]interface{}, 0, 1)
|
|
|
|
newElems = append(newElems, lease)
|
|
|
|
return nc.leaseInformer.Informer().GetStore().Replace(newElems, "newRV")
|
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
func (nc *nodeLifecycleController) syncNodeStore(fakeNodeHandler *testutil.FakeNodeHandler) error {
|
|
|
|
nodes, err := fakeNodeHandler.List(metav1.ListOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
newElems := make([]interface{}, 0, len(nodes.Items))
|
|
|
|
for i := range nodes.Items {
|
|
|
|
newElems = append(newElems, &nodes.Items[i])
|
|
|
|
}
|
|
|
|
return nc.nodeInformer.Informer().GetStore().Replace(newElems, "newRV")
|
|
|
|
}
|
|
|
|
|
|
|
|
func newNodeLifecycleControllerFromClient(
|
2016-09-23 16:01:58 +00:00
|
|
|
kubeClient clientset.Interface,
|
|
|
|
podEvictionTimeout time.Duration,
|
|
|
|
evictionLimiterQPS float32,
|
|
|
|
secondaryEvictionLimiterQPS float32,
|
|
|
|
largeClusterThreshold int32,
|
|
|
|
unhealthyZoneThreshold float32,
|
|
|
|
nodeMonitorGracePeriod time.Duration,
|
|
|
|
nodeStartupGracePeriod time.Duration,
|
|
|
|
nodeMonitorPeriod time.Duration,
|
2017-04-04 13:35:44 +00:00
|
|
|
useTaints bool,
|
2017-10-11 23:36:39 +00:00
|
|
|
) (*nodeLifecycleController, error) {
|
2016-09-23 16:01:58 +00:00
|
|
|
|
2017-02-08 21:18:21 +00:00
|
|
|
factory := informers.NewSharedInformerFactory(kubeClient, controller.NoResyncPeriodFunc())
|
2016-09-23 16:01:58 +00:00
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
leaseInformer := factory.Coordination().V1beta1().Leases()
|
2017-02-06 18:35:50 +00:00
|
|
|
nodeInformer := factory.Core().V1().Nodes()
|
2018-08-16 02:06:39 +00:00
|
|
|
daemonSetInformer := factory.Apps().V1().DaemonSets()
|
2017-02-06 18:35:50 +00:00
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
nc, err := NewNodeLifecycleController(
|
2018-10-01 18:32:56 +00:00
|
|
|
leaseInformer,
|
2017-02-06 18:35:50 +00:00
|
|
|
factory.Core().V1().Pods(),
|
|
|
|
nodeInformer,
|
|
|
|
daemonSetInformer,
|
|
|
|
kubeClient,
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeMonitorPeriod,
|
|
|
|
nodeStartupGracePeriod,
|
|
|
|
nodeMonitorGracePeriod,
|
2017-02-06 18:35:50 +00:00
|
|
|
podEvictionTimeout,
|
|
|
|
evictionLimiterQPS,
|
|
|
|
secondaryEvictionLimiterQPS,
|
|
|
|
largeClusterThreshold,
|
|
|
|
unhealthyZoneThreshold,
|
2017-04-04 13:35:44 +00:00
|
|
|
useTaints,
|
|
|
|
useTaints,
|
2017-07-19 15:51:19 +00:00
|
|
|
useTaints,
|
2017-02-06 18:35:50 +00:00
|
|
|
)
|
2016-09-23 16:01:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
nc.leaseInformerSynced = alwaysReady
|
2017-02-06 18:35:50 +00:00
|
|
|
nc.podInformerSynced = alwaysReady
|
|
|
|
nc.nodeInformerSynced = alwaysReady
|
|
|
|
nc.daemonSetInformerSynced = alwaysReady
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
return &nodeLifecycleController{nc, leaseInformer, nodeInformer, daemonSetInformer}, nil
|
2016-12-19 10:15:39 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
func TestMonitorNodeHealthEvictPods(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-03-31 11:17:12 +00:00
|
|
|
evictionTimeout := 10 * time.Minute
|
2017-06-23 07:38:05 +00:00
|
|
|
labels := map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-06-23 07:38:05 +00:00
|
|
|
}
|
2015-03-31 15:15:39 +00:00
|
|
|
|
2016-05-16 09:20:23 +00:00
|
|
|
// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
|
|
|
|
// we need second healthy node in tests. Because of how the tests are written we need to update
|
|
|
|
// the status of this Node.
|
2016-11-18 20:50:17 +00:00
|
|
|
healthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-05-16 09:20:23 +00:00
|
|
|
// Node status has just been updated, and is NotReady for 10min.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2015-03-20 17:35:41 +00:00
|
|
|
table := []struct {
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler *testutil.FakeNodeHandler
|
2018-08-16 02:06:39 +00:00
|
|
|
daemonSets []apps.DaemonSet
|
2016-05-16 09:20:23 +00:00
|
|
|
timeToPass time.Duration
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus v1.NodeStatus
|
|
|
|
secondNodeNewStatus v1.NodeStatus
|
2016-05-16 09:20:23 +00:00
|
|
|
expectedEvictPods bool
|
|
|
|
description string
|
2015-03-20 17:35:41 +00:00
|
|
|
}{
|
|
|
|
// Node created recently, with no status (happens only at cluster startup).
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-20 17:35:41 +00:00
|
|
|
Name: "node0",
|
2015-03-23 01:10:35 +00:00
|
|
|
CreationTimestamp: fakeNow,
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
daemonSets: nil,
|
|
|
|
timeToPass: 0,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{},
|
2016-05-16 09:20:23 +00:00
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Node created recently, with no status.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2017-06-23 07:38:05 +00:00
|
|
|
// Node created recently without FailureDomain labels which is added back later, with no status (happens only at cluster startup).
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node1",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
daemonSets: nil,
|
|
|
|
timeToPass: 0,
|
|
|
|
newNodeStatus: v1.NodeStatus{},
|
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Node created recently without FailureDomain labels which is added back later, with no status (happens only at cluster startup).",
|
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, and kubelet posted NotReady for a short period of time.
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-20 17:35:41 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
daemonSets: nil,
|
2015-03-31 11:17:12 +00:00
|
|
|
timeToPass: evictionTimeout,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-31 15:15:39 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2015-03-31 15:15:39 +00:00
|
|
|
// Node status has just been updated, and is NotReady for 10min.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Node created long time ago, and kubelet posted NotReady for a short period of time.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
// Pod is ds-managed, and kubelet posted NotReady for a long period of time.
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2016-01-08 21:38:02 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-01-08 21:38:02 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-01-08 21:38:02 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-01-08 21:38:02 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(
|
2016-11-18 20:50:17 +00:00
|
|
|
&v1.PodList{
|
|
|
|
Items: []v1.Pod{
|
2016-01-08 21:38:02 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-01-08 21:38:02 +00:00
|
|
|
Name: "pod0",
|
|
|
|
Namespace: "default",
|
|
|
|
Labels: map[string]string{"daemon": "yes"},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Spec: v1.PodSpec{
|
2016-01-08 21:38:02 +00:00
|
|
|
NodeName: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
},
|
2018-08-16 02:06:39 +00:00
|
|
|
daemonSets: []apps.DaemonSet{
|
2016-01-08 21:38:02 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-01-08 21:38:02 +00:00
|
|
|
Name: "ds0",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
2018-08-16 02:06:39 +00:00
|
|
|
Spec: apps.DaemonSetSpec{
|
2016-12-03 18:57:26 +00:00
|
|
|
Selector: &metav1.LabelSelector{
|
2016-01-08 21:38:02 +00:00
|
|
|
MatchLabels: map[string]string{"daemon": "yes"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
timeToPass: time.Hour,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-01-08 21:38:02 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2016-01-08 21:38:02 +00:00
|
|
|
// Node status has just been updated, and is NotReady for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-01-08 21:38:02 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Pod is ds-managed, and kubelet posted NotReady for a long period of time.",
|
2016-01-08 21:38:02 +00:00
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, and kubelet posted NotReady for a long period of time.
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-20 17:35:41 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
daemonSets: nil,
|
2015-03-31 15:15:39 +00:00
|
|
|
timeToPass: time.Hour,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-31 15:15:39 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
2015-03-31 15:15:39 +00:00
|
|
|
// Node status has just been updated, and is NotReady for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: true,
|
|
|
|
description: "Node created long time ago, and kubelet posted NotReady for a long period of time.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, node controller posted Unknown for a short period of time.
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-20 17:35:41 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-01-08 21:38:02 +00:00
|
|
|
daemonSets: nil,
|
2015-03-31 11:17:12 +00:00
|
|
|
timeToPass: evictionTimeout - testNodeMonitorGracePeriod,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-31 15:15:39 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2015-03-31 15:15:39 +00:00
|
|
|
// Node status was updated by nodecontroller 10min ago
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Node created long time ago, node controller posted Unknown for a short period of time.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, node controller posted Unknown for a long period of time.
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-20 17:35:41 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-20 17:35:41 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-05-16 09:20:23 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
daemonSets: nil,
|
|
|
|
timeToPass: 60 * time.Minute,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-05-16 09:20:23 +00:00
|
|
|
// Node status was updated by nodecontroller 1hr ago
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedEvictPods: true,
|
|
|
|
description: "Node created long time ago, node controller posted Unknown for a long period of time.",
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range table {
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
2017-02-27 08:33:55 +00:00
|
|
|
item.fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
2017-06-05 08:36:09 +00:00
|
|
|
testUnhealthyThreshold,
|
2017-02-27 08:33:55 +00:00
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2017-02-13 10:48:34 +00:00
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2016-07-13 14:57:22 +00:00
|
|
|
for _, ds := range item.daemonSets {
|
2017-02-06 18:35:50 +00:00
|
|
|
nodeController.daemonSetInformer.Informer().GetStore().Add(&ds)
|
2016-07-13 14:57:22 +00:00
|
|
|
}
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-07-13 14:57:22 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if item.timeToPass > 0 {
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2016-07-13 14:57:22 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
|
|
|
item.fakeNodeHandler.Existing[1].Status = item.secondNodeNewStatus
|
|
|
|
}
|
2017-06-23 07:38:05 +00:00
|
|
|
if len(item.fakeNodeHandler.Existing[0].Labels) == 0 && len(item.fakeNodeHandler.Existing[1].Labels) == 0 {
|
|
|
|
item.fakeNodeHandler.Existing[0].Labels = labels
|
|
|
|
item.fakeNodeHandler.Existing[1].Labels = labels
|
|
|
|
}
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-07-13 14:57:22 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2016-11-23 10:30:36 +00:00
|
|
|
zones := testutil.GetZones(item.fakeNodeHandler)
|
2016-07-13 14:57:22 +00:00
|
|
|
for _, zone := range zones {
|
2017-02-06 12:58:48 +00:00
|
|
|
if _, ok := nodeController.zonePodEvictor[zone]; ok {
|
2017-08-08 19:55:57 +00:00
|
|
|
nodeController.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
|
2017-08-08 23:25:20 +00:00
|
|
|
nodeUID, _ := value.UID.(string)
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeutil.DeletePods(item.fakeNodeHandler, nodeController.recorder, value.Value, nodeUID, nodeController.daemonSetInformer.Lister())
|
2017-02-06 12:58:48 +00:00
|
|
|
return true, 0
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
t.Fatalf("Zone %v was unitialized!", zone)
|
|
|
|
}
|
2016-07-13 14:57:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
podEvicted := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "delete" && action.GetResource().Resource == "pods" {
|
|
|
|
podEvicted = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if item.expectedEvictPods != podEvicted {
|
|
|
|
t.Errorf("expected pod eviction: %+v, got %+v for %+v", item.expectedEvictPods,
|
|
|
|
podEvicted, item.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-02 19:56:19 +00:00
|
|
|
func TestPodStatusChange(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2016-11-02 19:56:19 +00:00
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
|
|
|
|
// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
|
|
|
|
// we need second healthy node in tests. Because of how the tests are written we need to update
|
|
|
|
// the status of this Node.
|
2016-11-18 20:50:17 +00:00
|
|
|
healthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-11-02 19:56:19 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-11-02 19:56:19 +00:00
|
|
|
// Node status has just been updated, and is NotReady for 10min.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
// Node created long time ago, node controller posted Unknown for a long period of time.
|
|
|
|
table := []struct {
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler *testutil.FakeNodeHandler
|
2016-11-02 19:56:19 +00:00
|
|
|
timeToPass time.Duration
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus v1.NodeStatus
|
|
|
|
secondNodeNewStatus v1.NodeStatus
|
2016-11-02 19:56:19 +00:00
|
|
|
expectedPodUpdate bool
|
|
|
|
expectedReason string
|
|
|
|
description string
|
|
|
|
}{
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2016-11-02 19:56:19 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-11-02 19:56:19 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-11-02 19:56:19 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-11-02 19:56:19 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-11-02 19:56:19 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
timeToPass: 60 * time.Minute,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-11-02 19:56:19 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-11-02 19:56:19 +00:00
|
|
|
// Node status was updated by nodecontroller 1hr ago
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-11-02 19:56:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
secondNodeNewStatus: healthyNodeNewStatus,
|
|
|
|
expectedPodUpdate: true,
|
|
|
|
expectedReason: node.NodeUnreachablePodReason,
|
|
|
|
description: "Node created long time ago, node controller posted Unknown for a " +
|
|
|
|
"long period of time, the pod status must include reason for termination.",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range table {
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
item.fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2017-02-13 10:48:34 +00:00
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-11-02 19:56:19 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if item.timeToPass > 0 {
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2016-11-02 19:56:19 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
|
|
|
item.fakeNodeHandler.Existing[1].Status = item.secondNodeNewStatus
|
|
|
|
}
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-11-02 19:56:19 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2016-11-23 10:30:36 +00:00
|
|
|
zones := testutil.GetZones(item.fakeNodeHandler)
|
2016-11-02 19:56:19 +00:00
|
|
|
for _, zone := range zones {
|
2017-08-08 19:55:57 +00:00
|
|
|
nodeController.zonePodEvictor[zone].Try(func(value scheduler.TimedValue) (bool, time.Duration) {
|
2017-08-08 23:25:20 +00:00
|
|
|
nodeUID, _ := value.UID.(string)
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeutil.DeletePods(item.fakeNodeHandler, nodeController.recorder, value.Value, nodeUID, nodeController.daemonSetStore)
|
2016-11-02 19:56:19 +00:00
|
|
|
return true, 0
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
podReasonUpdate := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "update" && action.GetResource().Resource == "pods" {
|
2016-11-18 20:50:17 +00:00
|
|
|
updateReason := action.(testcore.UpdateActionImpl).GetObject().(*v1.Pod).Status.Reason
|
2016-11-02 19:56:19 +00:00
|
|
|
podReasonUpdate = true
|
|
|
|
if updateReason != item.expectedReason {
|
|
|
|
t.Errorf("expected pod status reason: %+v, got %+v for %+v", item.expectedReason, updateReason, item.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if podReasonUpdate != item.expectedPodUpdate {
|
|
|
|
t.Errorf("expected pod update: %+v, got %+v for %+v", podReasonUpdate, item.expectedPodUpdate, item.description)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
func TestMonitorNodeHealthEvictPodsWithDisruption(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2016-07-13 14:57:22 +00:00
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
timeToPass := 60 * time.Minute
|
|
|
|
|
|
|
|
// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
|
|
|
|
// we need second healthy node in tests. Because of how the tests are written we need to update
|
|
|
|
// the status of this Node.
|
2016-11-18 20:50:17 +00:00
|
|
|
healthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 13, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2016-11-18 20:50:17 +00:00
|
|
|
unhealthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-07-13 14:57:22 +00:00
|
|
|
// Node status was updated by nodecontroller 1hr ago
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
table := []struct {
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList []*v1.Node
|
|
|
|
podList []v1.Pod
|
|
|
|
updatedNodeStatuses []v1.NodeStatus
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates map[string]ZoneState
|
|
|
|
expectedFollowingStates map[string]ZoneState
|
2016-07-13 14:57:22 +00:00
|
|
|
expectedEvictPods bool
|
|
|
|
description string
|
|
|
|
}{
|
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
|
|
|
|
// Only zone is down - eviction shouldn't take place
|
2016-05-16 09:20:23 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{testutil.CreateZoneID("region1", "zone1"): stateFullDisruption},
|
|
|
|
expectedFollowingStates: map[string]ZoneState{testutil.CreateZoneID("region1", "zone1"): stateFullDisruption},
|
2016-07-13 14:57:22 +00:00
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Network Disruption: Only zone is down - eviction shouldn't take place.",
|
|
|
|
},
|
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
|
|
|
|
// Both zones down - eviction shouldn't take place
|
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region2",
|
|
|
|
v1.LabelZoneFailureDomain: "zone2",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region2", "zone2"): stateFullDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedFollowingStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region2", "zone2"): stateFullDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Network Disruption: Both zones down - eviction shouldn't take place.",
|
|
|
|
},
|
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
|
|
|
|
// One zone is down - eviction should take place
|
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone2",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
healthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region1", "zone2"): stateNormal,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedFollowingStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region1", "zone2"): stateNormal,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
expectedEvictPods: true,
|
|
|
|
description: "Network Disruption: One zone is down - eviction should take place.",
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period
|
2016-05-16 09:20:23 +00:00
|
|
|
// of on first Node, eviction should stop even though -master Node is healthy.
|
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node-master",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
healthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedFollowingStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "NetworkDisruption: eviction should stop, only -master Node is healthy",
|
|
|
|
},
|
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
|
|
|
|
// Initially both zones down, one comes back - eviction should take place
|
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-12 12:29:46 +00:00
|
|
|
},
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone2",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-05-16 09:20:23 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
healthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region1", "zone2"): stateFullDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedFollowingStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): stateFullDisruption,
|
|
|
|
testutil.CreateZoneID("region1", "zone2"): stateNormal,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
expectedEvictPods: true,
|
|
|
|
description: "Initially both zones down, one comes back - eviction should take place",
|
|
|
|
},
|
|
|
|
// NetworkDisruption: Node created long time ago, node controller posted Unknown for a long period of time on both Nodes.
|
|
|
|
// Zone is partially disrupted - eviction should take place
|
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
nodeList: []*v1.Node{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node1",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node2",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node3",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-07-13 14:57:22 +00:00
|
|
|
Name: "node4",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-07-13 14:57:22 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
},
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-07-13 14:57:22 +00:00
|
|
|
|
2016-11-23 10:30:36 +00:00
|
|
|
podList: []v1.Pod{*testutil.NewPod("pod0", "node0")},
|
2016-11-18 20:50:17 +00:00
|
|
|
updatedNodeStatuses: []v1.NodeStatus{
|
2016-07-13 14:57:22 +00:00
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
unhealthyNodeNewStatus,
|
|
|
|
healthyNodeNewStatus,
|
|
|
|
healthyNodeNewStatus,
|
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedInitialStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): statePartialDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
2017-08-08 23:25:20 +00:00
|
|
|
expectedFollowingStates: map[string]ZoneState{
|
2016-11-23 10:30:36 +00:00
|
|
|
testutil.CreateZoneID("region1", "zone1"): statePartialDisruption,
|
2016-07-13 14:57:22 +00:00
|
|
|
},
|
|
|
|
expectedEvictPods: true,
|
|
|
|
description: "Zone is partially disrupted - eviction should take place.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range table {
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
2016-07-13 14:57:22 +00:00
|
|
|
Existing: item.nodeList,
|
2016-11-18 20:50:17 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: item.podList}),
|
2016-07-13 14:57:22 +00:00
|
|
|
}
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2016-08-05 12:50:19 +00:00
|
|
|
nodeController.enterPartialDisruptionFunc = func(nodeNum int) float32 {
|
2016-07-13 14:57:22 +00:00
|
|
|
return testRateLimiterQPS
|
|
|
|
}
|
2017-02-13 10:48:34 +00:00
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2016-08-05 12:50:19 +00:00
|
|
|
nodeController.enterFullDisruptionFunc = func(nodeNum int) float32 {
|
2016-07-13 14:57:22 +00:00
|
|
|
return testRateLimiterQPS
|
2016-01-08 21:38:02 +00:00
|
|
|
}
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-07-13 14:57:22 +00:00
|
|
|
t.Errorf("%v: unexpected error: %v", item.description, err)
|
2015-03-20 17:35:41 +00:00
|
|
|
}
|
2016-07-13 14:57:22 +00:00
|
|
|
|
|
|
|
for zone, state := range item.expectedInitialStates {
|
|
|
|
if state != nodeController.zoneStates[zone] {
|
|
|
|
t.Errorf("%v: Unexpected zone state: %v: %v instead %v", item.description, zone, nodeController.zoneStates[zone], state)
|
|
|
|
}
|
2015-03-31 15:15:39 +00:00
|
|
|
}
|
2016-07-13 14:57:22 +00:00
|
|
|
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(timeToPass)} }
|
2016-07-13 14:57:22 +00:00
|
|
|
for i := range item.updatedNodeStatuses {
|
|
|
|
fakeNodeHandler.Existing[i].Status = item.updatedNodeStatuses[i]
|
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-07-13 14:57:22 +00:00
|
|
|
t.Errorf("%v: unexpected error: %v", item.description, err)
|
2015-03-31 15:15:39 +00:00
|
|
|
}
|
2016-07-13 14:57:22 +00:00
|
|
|
for zone, state := range item.expectedFollowingStates {
|
|
|
|
if state != nodeController.zoneStates[zone] {
|
|
|
|
t.Errorf("%v: Unexpected zone state: %v: %v instead %v", item.description, zone, nodeController.zoneStates[zone], state)
|
|
|
|
}
|
|
|
|
}
|
2017-05-26 21:44:32 +00:00
|
|
|
var podEvicted bool
|
|
|
|
start := time.Now()
|
|
|
|
// Infinite loop, used for retrying in case ratelimiter fails to reload for Try function.
|
|
|
|
// this breaks when we have the status that we need for test case or when we don't see the
|
|
|
|
// intended result after 1 minute.
|
|
|
|
for {
|
|
|
|
podEvicted = nodeController.doEviction(fakeNodeHandler)
|
|
|
|
if podEvicted == item.expectedEvictPods || time.Since(start) > 1*time.Minute {
|
2016-07-13 14:57:22 +00:00
|
|
|
break
|
2015-03-20 17:35:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if item.expectedEvictPods != podEvicted {
|
2016-07-13 14:57:22 +00:00
|
|
|
t.Errorf("%v: expected pod eviction: %+v, got %+v", item.description, item.expectedEvictPods, podEvicted)
|
2015-03-20 17:35:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
func TestMonitorNodeHealthUpdateStatus(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-01-16 22:28:20 +00:00
|
|
|
table := []struct {
|
2018-10-01 18:32:56 +00:00
|
|
|
fakeNodeHandler *testutil.FakeNodeHandler
|
|
|
|
timeToPass time.Duration
|
|
|
|
newNodeStatus v1.NodeStatus
|
|
|
|
expectedRequestCount int
|
|
|
|
expectedNodes []*v1.Node
|
|
|
|
expectedPodStatusUpdate bool
|
2015-01-16 22:28:20 +00:00
|
|
|
}{
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, without status:
|
|
|
|
// Expect Unknown status posted from node controller.
|
2015-01-16 22:28:20 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
expectedRequestCount: 2, // List+Update
|
2016-11-18 20:50:17 +00:00
|
|
|
expectedNodes: []*v1.Node{
|
2015-01-16 22:28:20 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-02-04 21:56:59 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2015-09-11 10:08:09 +00:00
|
|
|
Reason: "NodeStatusNeverUpdated",
|
2015-10-22 19:47:43 +00:00
|
|
|
Message: "Kubelet never posted node status.",
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-10-22 19:47:43 +00:00
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
2016-11-10 18:09:27 +00:00
|
|
|
{
|
|
|
|
Type: v1.NodeMemoryPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeDiskPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
2018-09-14 00:50:05 +00:00
|
|
|
{
|
|
|
|
Type: v1.NodePIDPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
2015-02-04 21:56:59 +00:00
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-10-01 18:32:56 +00:00
|
|
|
expectedPodStatusUpdate: false, // Pod was never scheduled
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created recently, without status.
|
|
|
|
// Expect no action from node controller (within startup grace period).
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2015-03-23 01:10:35 +00:00
|
|
|
CreationTimestamp: fakeNow,
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2018-10-01 18:32:56 +00:00
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
|
|
|
expectedPodStatusUpdate: false,
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node created long time ago, with status updated by kubelet exceeds grace period.
|
|
|
|
// Expect Unknown status posted from node controller.
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node status hasn't been updated for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-03-24 17:24:07 +00:00
|
|
|
},
|
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-03-31 15:15:39 +00:00
|
|
|
expectedRequestCount: 3, // (List+)List+Update
|
|
|
|
timeToPass: time.Hour,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-31 15:15:39 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-03-31 15:15:39 +00:00
|
|
|
// Node status hasn't been updated for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
expectedNodes: []*v1.Node{
|
2015-01-16 22:28:20 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-02-04 21:56:59 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2015-10-22 19:47:43 +00:00
|
|
|
Reason: "NodeStatusUnknown",
|
|
|
|
Message: "Kubelet stopped posting node status.",
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2018-02-02 19:15:27 +00:00
|
|
|
LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
|
|
|
},
|
2016-11-10 18:09:27 +00:00
|
|
|
{
|
|
|
|
Type: v1.NodeMemoryPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeDiskPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
|
|
|
},
|
2018-09-14 00:50:05 +00:00
|
|
|
{
|
|
|
|
Type: v1.NodePIDPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC), // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
|
|
|
},
|
2015-02-04 21:56:59 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-03-24 17:24:07 +00:00
|
|
|
},
|
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
2018-10-01 18:32:56 +00:00
|
|
|
expectedPodStatusUpdate: true,
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated recently.
|
2015-03-23 01:10:35 +00:00
|
|
|
// Expect no action from node controller (within monitor grace period).
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-03-12 03:00:52 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-03-12 03:00:52 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node status has just been updated.
|
2015-03-27 14:09:51 +00:00
|
|
|
LastHeartbeatTime: fakeNow,
|
2015-03-23 01:10:35 +00:00
|
|
|
LastTransitionTime: fakeNow,
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-03-24 17:24:07 +00:00
|
|
|
},
|
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2018-10-01 18:32:56 +00:00
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
|
|
|
expectedPodStatusUpdate: false,
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
2015-10-22 19:47:43 +00:00
|
|
|
for i, item := range table {
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
item.fakeNodeHandler,
|
|
|
|
5*time.Minute,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2017-02-13 10:48:34 +00:00
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2015-01-16 22:28:20 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2015-03-31 15:15:39 +00:00
|
|
|
if item.timeToPass > 0 {
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2015-03-31 15:15:39 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2015-03-31 15:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
2015-03-12 03:00:52 +00:00
|
|
|
if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
|
2015-01-16 22:28:20 +00:00
|
|
|
t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
|
|
|
|
}
|
2017-01-25 13:39:54 +00:00
|
|
|
if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
|
2016-03-11 02:43:55 +00:00
|
|
|
t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
|
2015-10-22 19:47:43 +00:00
|
|
|
}
|
2017-01-25 13:39:54 +00:00
|
|
|
if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
|
2016-03-11 02:43:55 +00:00
|
|
|
t.Errorf("Case[%d] unexpected nodes: %s", i, diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
|
2015-01-16 22:28:20 +00:00
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
|
|
|
|
podStatusUpdated := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
|
|
|
|
podStatusUpdated = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if podStatusUpdated != item.expectedPodStatusUpdate {
|
|
|
|
t.Errorf("Case[%d] expect pod status updated to be %v, but got %v", i, item.expectedPodStatusUpdate, podStatusUpdated)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMonitorNodeHealthUpdateNodeAndPodStatusWithLease(t *testing.T) {
|
|
|
|
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeLease, true)()
|
|
|
|
|
|
|
|
nodeCreationTime := metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC)
|
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
testcases := []struct {
|
|
|
|
description string
|
|
|
|
fakeNodeHandler *testutil.FakeNodeHandler
|
|
|
|
lease *coordv1beta1.Lease
|
|
|
|
timeToPass time.Duration
|
|
|
|
newNodeStatus v1.NodeStatus
|
|
|
|
newLease *coordv1beta1.Lease
|
|
|
|
expectedRequestCount int
|
|
|
|
expectedNodes []*v1.Node
|
|
|
|
expectedPodStatusUpdate bool
|
|
|
|
}{
|
|
|
|
// Node created recently, without status. Node lease is missing.
|
|
|
|
// Expect no action from node controller (within startup grace period).
|
|
|
|
{
|
|
|
|
description: "Node created recently, without status. Node lease is missing.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created recently, without status. Node lease is renewed recently.
|
|
|
|
// Expect no action from node controller (within startup grace period).
|
|
|
|
{
|
|
|
|
description: "Node created recently, without status. Node lease is renewed recently.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, without status. Node lease is missing.
|
|
|
|
// Expect Unknown status posted from node controller.
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, without status. Node lease is missing.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
expectedRequestCount: 2, // List+Update
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeMemoryPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeDiskPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodePIDPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false, // Pod was never scheduled because the node was never ready.
|
|
|
|
},
|
|
|
|
// Node created long time ago, without status. Node lease is renewed recently.
|
|
|
|
// Expect no action from node controller (within monitor grace period).
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, without status. Node lease is renewed recently.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time.Add(time.Hour))), // Lease is renewed after 1 hour.
|
|
|
|
expectedRequestCount: 2, // List+List
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, without status. Node lease is expired.
|
|
|
|
// Expect Unknown status posted from node controller.
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, without status. Node lease is expired.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
|
|
|
|
expectedRequestCount: 3, // List+List+Update
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeMemoryPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeDiskPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodePIDPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime,
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is renewed.
|
|
|
|
// Expect no action from node controller (within monitor grace period).
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is renewed.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
expectedRequestCount: 2, // List+List
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
// Node status hasn't been updated for 1 hour.
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time.Add(time.Hour))), // Lease is renewed after 1 hour.
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated by kubelet recently. Node lease is expired.
|
|
|
|
// Expect no action from node controller (within monitor grace period).
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, with status updated by kubelet recently. Node lease is expired.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
expectedRequestCount: 2, // List+List
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
// Node status is updated after 1 hour.
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Type: v1.NodeDiskPressure,
|
2018-10-01 18:32:56 +00:00
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is also expired.
|
|
|
|
// Expect Unknown status posted from node controller.
|
|
|
|
{
|
|
|
|
description: "Node created long time ago, with status updated by kubelet exceeds grace period. Node lease is also expired.",
|
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
},
|
|
|
|
lease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)),
|
|
|
|
expectedRequestCount: 3, // List+List+Update
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
// Node status hasn't been updated for 1 hour.
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
newLease: createNodeLease("node0", metav1.NewMicroTime(fakeNow.Time)), // Lease is not renewed after 1 hour.
|
|
|
|
expectedNodes: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: nodeCreationTime,
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusUnknown",
|
|
|
|
Message: "Kubelet stopped posting node status.",
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeMemoryPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeDiskPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodePIDPressure,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
|
|
|
LastHeartbeatTime: nodeCreationTime, // should default to node creation time if condition was never updated
|
|
|
|
LastTransitionTime: metav1.Time{Time: fakeNow.Add(time.Hour)},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range testcases {
|
|
|
|
t.Run(item.description, func(t *testing.T) {
|
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
item.fakeNodeHandler,
|
|
|
|
5*time.Minute,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.syncLeaseStore(item.lease); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if item.timeToPass > 0 {
|
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
|
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.syncLeaseStore(item.newLease); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if item.expectedRequestCount != item.fakeNodeHandler.RequestCount {
|
|
|
|
t.Errorf("expected %v call, but got %v.", item.expectedRequestCount, item.fakeNodeHandler.RequestCount)
|
|
|
|
}
|
|
|
|
if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
|
|
|
|
t.Errorf("unexpected nodes: %s", diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
|
|
|
|
}
|
|
|
|
if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !apiequality.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
|
|
|
|
t.Errorf("unexpected nodes: %s", diff.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
|
|
|
|
}
|
|
|
|
|
|
|
|
podStatusUpdated := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
|
|
|
|
podStatusUpdated = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if podStatusUpdated != item.expectedPodStatusUpdate {
|
|
|
|
t.Errorf("expect pod status updated to be %v, but got %v", item.expectedPodStatusUpdate, podStatusUpdated)
|
|
|
|
}
|
|
|
|
})
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
2015-01-16 22:28:20 +00:00
|
|
|
}
|
|
|
|
|
2018-10-01 18:32:56 +00:00
|
|
|
func TestMonitorNodeHealthMarkPodsNotReady(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-11-24 22:46:17 +00:00
|
|
|
table := []struct {
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler *testutil.FakeNodeHandler
|
2015-11-24 22:46:17 +00:00
|
|
|
timeToPass time.Duration
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus v1.NodeStatus
|
2015-11-24 22:46:17 +00:00
|
|
|
expectedPodStatusUpdate bool
|
|
|
|
}{
|
|
|
|
// Node created recently, without status.
|
|
|
|
// Expect no action from node controller (within startup grace period).
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-11-24 22:46:17 +00:00
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated recently.
|
|
|
|
// Expect no action from node controller (within monitor grace period).
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-11-24 22:46:17 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-11-24 22:46:17 +00:00
|
|
|
// Node status has just been updated.
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: false,
|
|
|
|
},
|
|
|
|
// Node created long time ago, with status updated by kubelet exceeds grace period.
|
|
|
|
// Expect pods status updated and Unknown node status posted from node controller
|
|
|
|
{
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler: &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2015-11-24 22:46:17 +00:00
|
|
|
Name: "node0",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-11-24 22:46:17 +00:00
|
|
|
// Node status hasn't been updated for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
timeToPass: 1 * time.Minute,
|
2016-11-18 20:50:17 +00:00
|
|
|
newNodeStatus: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2015-11-24 22:46:17 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
2015-11-24 22:46:17 +00:00
|
|
|
// Node status hasn't been updated for 1hr.
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Capacity: v1.ResourceList{
|
|
|
|
v1.ResourceName(v1.ResourceCPU): resource.MustParse("10"),
|
|
|
|
v1.ResourceName(v1.ResourceMemory): resource.MustParse("10G"),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, item := range table {
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
item.fakeNodeHandler,
|
|
|
|
5*time.Minute,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2017-02-13 10:48:34 +00:00
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2015-11-24 22:46:17 +00:00
|
|
|
t.Errorf("Case[%d] unexpected error: %v", i, err)
|
|
|
|
}
|
|
|
|
if item.timeToPass > 0 {
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2015-11-24 22:46:17 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(item.fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2015-11-24 22:46:17 +00:00
|
|
|
t.Errorf("Case[%d] unexpected error: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
podStatusUpdated := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
2016-04-13 22:33:15 +00:00
|
|
|
if action.GetVerb() == "update" && action.GetResource().Resource == "pods" && action.GetSubresource() == "status" {
|
2015-11-24 22:46:17 +00:00
|
|
|
podStatusUpdated = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if podStatusUpdated != item.expectedPodStatusUpdate {
|
|
|
|
t.Errorf("Case[%d] expect pod status updated to be %v, but got %v", i, item.expectedPodStatusUpdate, podStatusUpdated)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-15 00:09:42 +00:00
|
|
|
// TestApplyNoExecuteTaints, ensures we just have a NoExecute taint applied to node.
|
|
|
|
// NodeController is just responsible for enqueuing the node to tainting queue from which taint manager picks up
|
|
|
|
// and evicts the pods on the node.
|
|
|
|
func TestApplyNoExecuteTaints(t *testing.T) {
|
|
|
|
fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
|
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
// Unreachable Taint with effect 'NoExecute' should be applied to this node.
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2018-10-15 00:09:42 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
|
|
|
|
// we need second healthy node in tests.
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node1",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2018-10-15 00:09:42 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// NotReady Taint with NoExecute effect should be applied to this node.
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node2",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2018-10-15 00:09:42 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
}
|
|
|
|
healthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 10, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
originalTaint := UnreachableTaintTemplate
|
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
true)
|
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
nodeController.doNoExecuteTaintingPass()
|
|
|
|
node0, err := fakeNodeHandler.Get("node0", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node0...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !taintutils.TaintExists(node0.Spec.Taints, UnreachableTaintTemplate) {
|
|
|
|
t.Errorf("Can't find taint %v in %v", originalTaint, node0.Spec.Taints)
|
|
|
|
}
|
|
|
|
node2, err := fakeNodeHandler.Get("node2", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node2...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if !taintutils.TaintExists(node2.Spec.Taints, NotReadyTaintTemplate) {
|
|
|
|
t.Errorf("Can't find taint %v in %v", NotReadyTaintTemplate, node2.Spec.Taints)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make node3 healthy again.
|
|
|
|
node2.Status = healthyNodeNewStatus
|
|
|
|
_, err = fakeNodeHandler.UpdateStatus(node2)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
nodeController.doNoExecuteTaintingPass()
|
|
|
|
|
|
|
|
node2, err = fakeNodeHandler.Get("node2", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node2...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
// We should not see any taint on the node(especially the Not-Ready taint with NoExecute effect).
|
|
|
|
if taintutils.TaintExists(node2.Spec.Taints, NotReadyTaintTemplate) || len(node2.Spec.Taints) > 0 {
|
|
|
|
t.Errorf("Found taint %v in %v, which should not be present", NotReadyTaintTemplate, node2.Spec.Taints)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-04 13:35:44 +00:00
|
|
|
func TestSwapUnreachableNotReadyTaints(t *testing.T) {
|
|
|
|
fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
|
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-04-04 13:35:44 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Because of the logic that prevents NC from evicting anything when all Nodes are NotReady
|
|
|
|
// we need second healthy node in tests. Because of how the tests are written we need to update
|
|
|
|
// the status of this Node.
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node1",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-04-04 13:35:44 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
}
|
|
|
|
timeToPass := evictionTimeout
|
|
|
|
newNodeStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
// Node status has just been updated, and is NotReady for 10min.
|
|
|
|
LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 9, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
healthyNodeNewStatus := v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2017, 1, 1, 12, 10, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
originalTaint := UnreachableTaintTemplate
|
|
|
|
updatedTaint := NotReadyTaintTemplate
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
true)
|
2017-04-04 13:35:44 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2017-07-31 11:47:57 +00:00
|
|
|
nodeController.doNoExecuteTaintingPass()
|
2017-04-04 13:35:44 +00:00
|
|
|
|
|
|
|
node0, err := fakeNodeHandler.Get("node0", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node0...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
node1, err := fakeNodeHandler.Get("node1", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node1...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-07-06 13:13:13 +00:00
|
|
|
if originalTaint != nil && !taintutils.TaintExists(node0.Spec.Taints, originalTaint) {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("Can't find taint %v in %v", originalTaint, node0.Spec.Taints)
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeController.now = func() metav1.Time { return metav1.Time{Time: fakeNow.Add(timeToPass)} }
|
|
|
|
|
|
|
|
node0.Status = newNodeStatus
|
|
|
|
node1.Status = healthyNodeNewStatus
|
|
|
|
_, err = fakeNodeHandler.UpdateStatus(node0)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err = fakeNodeHandler.UpdateStatus(node1)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf(err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2017-07-31 11:47:57 +00:00
|
|
|
nodeController.doNoExecuteTaintingPass()
|
2017-04-04 13:35:44 +00:00
|
|
|
|
|
|
|
node0, err = fakeNodeHandler.Get("node0", metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node0...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if updatedTaint != nil {
|
2017-07-06 13:13:13 +00:00
|
|
|
if !taintutils.TaintExists(node0.Spec.Taints, updatedTaint) {
|
2017-04-04 13:35:44 +00:00
|
|
|
t.Errorf("Can't find taint %v in %v", updatedTaint, node0.Spec.Taints)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-19 15:51:19 +00:00
|
|
|
func TestTaintsNodeByCondition(t *testing.T) {
|
|
|
|
fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
|
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-07-19 15:51:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
true)
|
2017-07-19 15:51:19 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
|
|
|
|
|
|
|
networkUnavailableTaint := &v1.Taint{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.TaintNodeNetworkUnavailable,
|
2017-07-19 15:51:19 +00:00
|
|
|
Effect: v1.TaintEffectNoSchedule,
|
|
|
|
}
|
2018-05-14 06:55:42 +00:00
|
|
|
notReadyTaint := &v1.Taint{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.TaintNodeNotReady,
|
2018-05-14 06:55:42 +00:00
|
|
|
Effect: v1.TaintEffectNoSchedule,
|
|
|
|
}
|
2018-08-22 22:26:46 +00:00
|
|
|
unreachableTaint := &v1.Taint{
|
2018-09-28 02:37:38 +00:00
|
|
|
Key: schedulerapi.TaintNodeUnreachable,
|
2018-08-22 22:26:46 +00:00
|
|
|
Effect: v1.TaintEffectNoSchedule,
|
|
|
|
}
|
2017-07-19 15:51:19 +00:00
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
Name string
|
|
|
|
Node *v1.Node
|
|
|
|
ExpectedTaints []*v1.Taint
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Name: "NetworkUnavailable is true",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-07-19 15:51:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeNetworkUnavailable,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedTaints: []*v1.Taint{networkUnavailableTaint},
|
|
|
|
},
|
|
|
|
{
|
2018-12-13 08:31:46 +00:00
|
|
|
Name: "NetworkUnavailable is true",
|
2017-07-19 15:51:19 +00:00
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2017-07-19 15:51:19 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: v1.NodeNetworkUnavailable,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedTaints: []*v1.Taint{networkUnavailableTaint},
|
|
|
|
},
|
2018-05-14 06:55:42 +00:00
|
|
|
{
|
|
|
|
Name: "Ready is false",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2018-05-14 06:55:42 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionFalse,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedTaints: []*v1.Taint{notReadyTaint},
|
|
|
|
},
|
2018-08-22 22:26:46 +00:00
|
|
|
{
|
|
|
|
Name: "Ready is unknown",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
2019-02-01 02:43:49 +00:00
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
2018-08-22 22:26:46 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedTaints: []*v1.Taint{unreachableTaint},
|
|
|
|
},
|
2017-07-19 15:51:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
fakeNodeHandler.Update(test.Node)
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2017-07-19 15:51:19 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-09-11 00:24:16 +00:00
|
|
|
nodeController.doNoScheduleTaintingPass(test.Node.Name)
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2017-07-19 15:51:19 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
node0, err := nodeController.nodeLister.Get("node0")
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Can't get current node0...")
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if len(node0.Spec.Taints) != len(test.ExpectedTaints) {
|
|
|
|
t.Errorf("%s: Unexpected number of taints: expected %d, got %d",
|
|
|
|
test.Name, len(test.ExpectedTaints), len(node0.Spec.Taints))
|
|
|
|
}
|
|
|
|
for _, taint := range test.ExpectedTaints {
|
|
|
|
if !taintutils.TaintExists(node0.Spec.Taints, taint) {
|
|
|
|
t.Errorf("%s: Can't find taint %v in %v", test.Name, taint, node0.Spec.Taints)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-14 01:41:20 +00:00
|
|
|
func TestNodeEventGeneration(t *testing.T) {
|
2016-12-03 18:57:26 +00:00
|
|
|
fakeNow := metav1.Date(2016, 9, 10, 12, 0, 0, 0, time.UTC)
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
2016-11-18 20:50:17 +00:00
|
|
|
Existing: []*v1.Node{
|
2016-08-14 01:41:20 +00:00
|
|
|
{
|
2017-01-17 03:38:19 +00:00
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
2016-08-14 01:41:20 +00:00
|
|
|
Name: "node0",
|
|
|
|
UID: "1234567890",
|
2016-12-03 18:57:26 +00:00
|
|
|
CreationTimestamp: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
|
2016-08-14 01:41:20 +00:00
|
|
|
},
|
2016-11-18 20:50:17 +00:00
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
2016-08-14 01:41:20 +00:00
|
|
|
{
|
2016-11-18 20:50:17 +00:00
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionUnknown,
|
2016-12-03 18:57:26 +00:00
|
|
|
LastHeartbeatTime: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 8, 10, 0, 0, 0, 0, time.UTC),
|
2016-08-14 01:41:20 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-11-23 10:30:36 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
2016-08-14 01:41:20 +00:00
|
|
|
}
|
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
5*time.Minute,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
false)
|
2016-12-03 18:57:26 +00:00
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
2016-11-23 10:30:36 +00:00
|
|
|
fakeRecorder := testutil.NewFakeRecorder()
|
2016-08-14 01:41:20 +00:00
|
|
|
nodeController.recorder = fakeRecorder
|
2018-10-29 01:57:23 +00:00
|
|
|
|
2017-10-11 23:36:39 +00:00
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
2016-12-19 10:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-01 18:32:56 +00:00
|
|
|
if err := nodeController.monitorNodeHealth(); err != nil {
|
2016-08-14 01:41:20 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2018-10-29 01:57:23 +00:00
|
|
|
if len(fakeRecorder.Events) != 1 {
|
|
|
|
t.Fatalf("unexpected events, got %v, expected %v: %+v", len(fakeRecorder.Events), 1, fakeRecorder.Events)
|
2016-08-14 01:41:20 +00:00
|
|
|
}
|
2018-10-29 01:57:23 +00:00
|
|
|
if fakeRecorder.Events[0].Reason != "RegisteredNode" {
|
2016-09-12 14:47:17 +00:00
|
|
|
var reasons []string
|
2016-11-23 10:30:36 +00:00
|
|
|
for _, event := range fakeRecorder.Events {
|
2016-09-12 14:47:17 +00:00
|
|
|
reasons = append(reasons, event.Reason)
|
|
|
|
}
|
|
|
|
t.Fatalf("unexpected events generation: %v", strings.Join(reasons, ","))
|
2016-08-14 01:41:20 +00:00
|
|
|
}
|
2016-11-23 10:30:36 +00:00
|
|
|
for _, event := range fakeRecorder.Events {
|
2016-08-14 01:41:20 +00:00
|
|
|
involvedObject := event.InvolvedObject
|
|
|
|
actualUID := string(involvedObject.UID)
|
|
|
|
if actualUID != "1234567890" {
|
|
|
|
t.Fatalf("unexpected event uid: %v", actualUID)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2019-02-23 00:09:07 +00:00
|
|
|
|
|
|
|
func TestReconcileNodeLabels(t *testing.T) {
|
|
|
|
fakeNow := metav1.Date(2017, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
evictionTimeout := 10 * time.Minute
|
|
|
|
|
|
|
|
fakeNodeHandler := &testutil.FakeNodeHandler{
|
|
|
|
Existing: []*v1.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
v1.LabelZoneFailureDomain: "zone1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Status: v1.NodeStatus{
|
|
|
|
Conditions: []v1.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: v1.NodeReady,
|
|
|
|
Status: v1.ConditionTrue,
|
|
|
|
LastHeartbeatTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: metav1.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Clientset: fake.NewSimpleClientset(&v1.PodList{Items: []v1.Pod{*testutil.NewPod("pod0", "node0")}}),
|
|
|
|
}
|
|
|
|
|
|
|
|
nodeController, _ := newNodeLifecycleControllerFromClient(
|
|
|
|
fakeNodeHandler,
|
|
|
|
evictionTimeout,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testRateLimiterQPS,
|
|
|
|
testLargeClusterThreshold,
|
|
|
|
testUnhealthyThreshold,
|
|
|
|
testNodeMonitorGracePeriod,
|
|
|
|
testNodeStartupGracePeriod,
|
|
|
|
testNodeMonitorPeriod,
|
|
|
|
true)
|
|
|
|
nodeController.now = func() metav1.Time { return fakeNow }
|
|
|
|
nodeController.recorder = testutil.NewFakeRecorder()
|
|
|
|
|
|
|
|
tests := []struct {
|
|
|
|
Name string
|
|
|
|
Node *v1.Node
|
|
|
|
ExpectedLabels map[string]string
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
Name: "No-op if node has no labels",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedLabels: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "No-op if no target labels present",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedLabels: map[string]string{
|
|
|
|
v1.LabelZoneRegion: "region1",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Create OS/arch stable labels when they don't exist",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
|
|
|
kubeletapis.LabelOS: "linux",
|
|
|
|
kubeletapis.LabelArch: "amd64",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedLabels: map[string]string{
|
|
|
|
kubeletapis.LabelOS: "linux",
|
|
|
|
kubeletapis.LabelArch: "amd64",
|
|
|
|
v1.LabelOSStable: "linux",
|
|
|
|
v1.LabelArchStable: "amd64",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "Reconcile OS/arch stable labels to match beta labels",
|
|
|
|
Node: &v1.Node{
|
|
|
|
ObjectMeta: metav1.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: metav1.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
Labels: map[string]string{
|
|
|
|
kubeletapis.LabelOS: "linux",
|
|
|
|
kubeletapis.LabelArch: "amd64",
|
|
|
|
v1.LabelOSStable: "windows",
|
|
|
|
v1.LabelArchStable: "arm",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
ExpectedLabels: map[string]string{
|
|
|
|
kubeletapis.LabelOS: "linux",
|
|
|
|
kubeletapis.LabelArch: "amd64",
|
|
|
|
v1.LabelOSStable: "linux",
|
|
|
|
v1.LabelArchStable: "amd64",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
fakeNodeHandler.Update(test.Node)
|
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
nodeController.reconcileNodeLabels(test.Node.Name)
|
|
|
|
if err := nodeController.syncNodeStore(fakeNodeHandler); err != nil {
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
}
|
|
|
|
node0, err := nodeController.nodeLister.Get("node0")
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Can't get current node0...")
|
|
|
|
}
|
|
|
|
if len(node0.Labels) != len(test.ExpectedLabels) {
|
|
|
|
t.Errorf("%s: Unexpected number of taints: expected %d, got %d",
|
|
|
|
test.Name, len(test.ExpectedLabels), len(node0.Labels))
|
|
|
|
}
|
|
|
|
for key, expectedValue := range test.ExpectedLabels {
|
|
|
|
actualValue, ok := node0.Labels[key]
|
|
|
|
if !ok {
|
|
|
|
t.Errorf("%s: Can't find label %v in %v", test.Name, key, node0.Labels)
|
|
|
|
}
|
|
|
|
if actualValue != expectedValue {
|
|
|
|
t.Errorf("%s: label %q: expected value %q, got value %q", test.Name, key, expectedValue, actualValue)
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|