2014-12-19 09:27:01 +00:00
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2014 The Kubernetes Authors All rights reserved.
|
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.
|
|
|
|
*/
|
|
|
|
|
2015-10-10 03:58:57 +00:00
|
|
|
package node
|
2014-12-19 09:27:01 +00:00
|
|
|
|
|
|
|
import (
|
2015-01-16 22:28:20 +00:00
|
|
|
"errors"
|
2015-04-28 15:02:45 +00:00
|
|
|
"sync"
|
2014-12-19 09:27:01 +00:00
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
apierrors "k8s.io/kubernetes/pkg/api/errors"
|
|
|
|
"k8s.io/kubernetes/pkg/api/resource"
|
2015-09-17 22:21:55 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2016-01-08 21:38:02 +00:00
|
|
|
"k8s.io/kubernetes/pkg/apis/extensions"
|
2015-10-20 02:25:31 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
2016-02-16 22:16:45 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
|
2016-02-16 17:54:53 +00:00
|
|
|
unversionedcore "k8s.io/kubernetes/pkg/client/typed/generated/core/unversioned"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util"
|
|
|
|
"k8s.io/kubernetes/pkg/watch"
|
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
|
|
|
|
)
|
|
|
|
|
2015-02-07 19:53:42 +00:00
|
|
|
// FakeNodeHandler is a fake implementation of NodesInterface and NodeInterface. It
|
2015-03-12 03:00:52 +00:00
|
|
|
// allows test cases to have fine-grained control over mock behaviors. We also need
|
2015-02-07 19:53:42 +00:00
|
|
|
// PodsInterface and PodInterface to test list & delet pods, which is implemented in
|
2015-08-08 21:29:57 +00:00
|
|
|
// the embedded client.Fake field.
|
2014-12-19 09:27:01 +00:00
|
|
|
type FakeNodeHandler struct {
|
2016-01-29 06:34:08 +00:00
|
|
|
*fake.Clientset
|
2014-12-19 09:27:01 +00:00
|
|
|
|
|
|
|
// Input: Hooks determine if request is valid or not
|
|
|
|
CreateHook func(*FakeNodeHandler, *api.Node) bool
|
|
|
|
Existing []*api.Node
|
|
|
|
|
|
|
|
// Output
|
2015-04-08 09:32:47 +00:00
|
|
|
CreatedNodes []*api.Node
|
|
|
|
DeletedNodes []*api.Node
|
|
|
|
UpdatedNodes []*api.Node
|
|
|
|
UpdatedNodeStatuses []*api.Node
|
|
|
|
RequestCount int
|
2015-04-28 15:02:45 +00:00
|
|
|
|
|
|
|
// Synchronization
|
|
|
|
createLock sync.Mutex
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
|
2016-01-29 06:34:08 +00:00
|
|
|
type FakeLegacyHandler struct {
|
2016-02-16 17:54:53 +00:00
|
|
|
unversionedcore.CoreInterface
|
2016-01-29 06:34:08 +00:00
|
|
|
n *FakeNodeHandler
|
|
|
|
}
|
|
|
|
|
2016-02-16 17:54:53 +00:00
|
|
|
func (c *FakeNodeHandler) Core() unversionedcore.CoreInterface {
|
2016-02-03 21:21:05 +00:00
|
|
|
return &FakeLegacyHandler{c.Clientset.Core(), c}
|
2016-01-29 06:34:08 +00:00
|
|
|
}
|
|
|
|
|
2016-02-16 17:54:53 +00:00
|
|
|
func (m *FakeLegacyHandler) Nodes() unversionedcore.NodeInterface {
|
2016-01-29 06:34:08 +00:00
|
|
|
return m.n
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *FakeNodeHandler) Create(node *api.Node) (*api.Node, error) {
|
2015-04-28 15:02:45 +00:00
|
|
|
m.createLock.Lock()
|
|
|
|
defer func() {
|
|
|
|
m.RequestCount++
|
|
|
|
m.createLock.Unlock()
|
|
|
|
}()
|
2015-02-05 16:02:56 +00:00
|
|
|
for _, n := range m.Existing {
|
|
|
|
if n.Name == node.Name {
|
2015-12-10 18:32:29 +00:00
|
|
|
return nil, apierrors.NewAlreadyExists(api.Resource("nodes"), node.Name)
|
2015-02-05 16:02:56 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-19 09:27:01 +00:00
|
|
|
if m.CreateHook == nil || m.CreateHook(m, node) {
|
2015-01-16 22:28:20 +00:00
|
|
|
nodeCopy := *node
|
|
|
|
m.CreatedNodes = append(m.CreatedNodes, &nodeCopy)
|
2014-12-19 09:27:01 +00:00
|
|
|
return node, nil
|
|
|
|
} else {
|
2015-01-30 20:50:47 +00:00
|
|
|
return nil, errors.New("Create error.")
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-07 19:53:42 +00:00
|
|
|
func (m *FakeNodeHandler) Get(name string) (*api.Node, error) {
|
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-12-10 09:39:03 +00:00
|
|
|
func (m *FakeNodeHandler) List(opts api.ListOptions) (*api.NodeList, error) {
|
2014-12-19 09:27:01 +00:00
|
|
|
defer func() { m.RequestCount++ }()
|
2015-01-16 22:28:20 +00:00
|
|
|
var nodes []*api.Node
|
|
|
|
for i := 0; i < len(m.UpdatedNodes); i++ {
|
|
|
|
if !contains(m.UpdatedNodes[i], m.DeletedNodes) {
|
|
|
|
nodes = append(nodes, m.UpdatedNodes[i])
|
|
|
|
}
|
|
|
|
}
|
2014-12-19 09:27:01 +00:00
|
|
|
for i := 0; i < len(m.Existing); i++ {
|
2015-01-16 22:28:20 +00:00
|
|
|
if !contains(m.Existing[i], m.DeletedNodes) && !contains(m.Existing[i], nodes) {
|
|
|
|
nodes = append(nodes, m.Existing[i])
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
for i := 0; i < len(m.CreatedNodes); i++ {
|
2015-01-16 22:28:20 +00:00
|
|
|
if !contains(m.Existing[i], m.DeletedNodes) && !contains(m.CreatedNodes[i], nodes) {
|
|
|
|
nodes = append(nodes, m.CreatedNodes[i])
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-16 22:28:20 +00:00
|
|
|
nodeList := &api.NodeList{}
|
|
|
|
for _, node := range nodes {
|
|
|
|
nodeList.Items = append(nodeList.Items, *node)
|
|
|
|
}
|
|
|
|
return nodeList, nil
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
|
|
|
|
2016-01-29 06:34:08 +00:00
|
|
|
func (m *FakeNodeHandler) Delete(id string, opt *api.DeleteOptions) error {
|
2014-12-19 09:27:01 +00:00
|
|
|
m.DeletedNodes = append(m.DeletedNodes, newNode(id))
|
|
|
|
m.RequestCount++
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2016-01-29 06:34:08 +00:00
|
|
|
func (m *FakeNodeHandler) DeleteCollection(opt *api.DeleteOptions, listOpts api.ListOptions) error {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2015-01-16 22:28:20 +00:00
|
|
|
func (m *FakeNodeHandler) Update(node *api.Node) (*api.Node, error) {
|
|
|
|
nodeCopy := *node
|
|
|
|
m.UpdatedNodes = append(m.UpdatedNodes, &nodeCopy)
|
|
|
|
m.RequestCount++
|
|
|
|
return node, nil
|
|
|
|
}
|
2014-12-19 09:27:01 +00:00
|
|
|
|
2015-04-08 09:32:47 +00:00
|
|
|
func (m *FakeNodeHandler) UpdateStatus(node *api.Node) (*api.Node, error) {
|
|
|
|
nodeCopy := *node
|
|
|
|
m.UpdatedNodeStatuses = append(m.UpdatedNodeStatuses, &nodeCopy)
|
|
|
|
m.RequestCount++
|
|
|
|
return node, nil
|
|
|
|
}
|
|
|
|
|
2015-12-10 09:39:03 +00:00
|
|
|
func (m *FakeNodeHandler) Watch(opts api.ListOptions) (watch.Interface, error) {
|
2015-03-20 16:52:32 +00:00
|
|
|
return nil, nil
|
|
|
|
}
|
|
|
|
|
2015-03-20 17:35:41 +00:00
|
|
|
func TestMonitorNodeStatusEvictPods(t *testing.T) {
|
2015-09-17 22:21:55 +00:00
|
|
|
fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-03-31 11:17:12 +00:00
|
|
|
evictionTimeout := 10 * time.Minute
|
2015-03-31 15:15:39 +00:00
|
|
|
|
2015-03-20 17:35:41 +00:00
|
|
|
table := []struct {
|
|
|
|
fakeNodeHandler *FakeNodeHandler
|
2016-01-08 21:38:02 +00:00
|
|
|
daemonSets []extensions.DaemonSet
|
2015-03-31 15:15:39 +00:00
|
|
|
timeToPass time.Duration
|
|
|
|
newNodeStatus api.NodeStatus
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods bool
|
2015-03-31 11:17:12 +00:00
|
|
|
description string
|
2015-03-20 17:35:41 +00:00
|
|
|
}{
|
|
|
|
// Node created recently, with no status (happens only at cluster startup).
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-03-23 01:10:35 +00:00
|
|
|
CreationTimestamp: fakeNow,
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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: 0,
|
|
|
|
newNodeStatus: api.NodeStatus{},
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods: false,
|
2015-03-31 11:17:12 +00:00
|
|
|
description: "Node created recently, with no status.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-31 15:15:39 +00:00
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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,
|
2015-03-31 15:15:39 +00:00
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status has just been updated, and is NotReady for 10min.
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 9, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods: false,
|
2015-03-31 11:17:12 +00:00
|
|
|
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.
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(
|
2016-01-08 21:38:02 +00:00
|
|
|
&api.PodList{
|
|
|
|
Items: []api.Pod{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "pod0",
|
|
|
|
Namespace: "default",
|
|
|
|
Labels: map[string]string{"daemon": "yes"},
|
|
|
|
},
|
|
|
|
Spec: api.PodSpec{
|
|
|
|
NodeName: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
),
|
|
|
|
},
|
|
|
|
daemonSets: []extensions.DaemonSet{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "ds0",
|
|
|
|
Namespace: "default",
|
|
|
|
},
|
|
|
|
Spec: extensions.DaemonSetSpec{
|
2016-02-02 05:34:42 +00:00
|
|
|
Selector: &unversioned.LabelSelector{
|
2016-01-08 21:38:02 +00:00
|
|
|
MatchLabels: map[string]string{"daemon": "yes"},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
timeToPass: time.Hour,
|
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status has just been updated, and is NotReady for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedEvictPods: false,
|
|
|
|
description: "Pod is ds-managed, and kubelet posted NotReady for a long period of time.",
|
|
|
|
},
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-31 15:15:39 +00:00
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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,
|
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status has just been updated, and is NotReady for 1hr.
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 59, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods: true,
|
2015-03-31 11:17:12 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-31 15:15:39 +00:00
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionUnknown,
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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,
|
2015-03-31 15:15:39 +00:00
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionUnknown,
|
|
|
|
// Node status was updated by nodecontroller 10min ago
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods: false,
|
2015-03-31 11:17:12 +00:00
|
|
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-31 15:15:39 +00:00
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionUnknown,
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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: 60 * time.Minute,
|
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionUnknown,
|
|
|
|
// Node status was updated by nodecontroller 1hr ago
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-03-20 17:35:41 +00:00
|
|
|
expectedEvictPods: true,
|
2015-03-31 11:17:12 +00:00
|
|
|
description: "Node created long time ago, node controller posted Unknown for a long period of time.",
|
2015-03-20 17:35:41 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, item := range table {
|
2015-08-04 12:44:14 +00:00
|
|
|
nodeController := NewNodeController(nil, item.fakeNodeHandler,
|
2016-02-05 15:52:07 +00:00
|
|
|
evictionTimeout, util.NewFakeAlwaysRateLimiter(), util.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod,
|
2015-05-14 08:17:04 +00:00
|
|
|
testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false)
|
2015-09-17 22:21:55 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return fakeNow }
|
2016-01-08 21:38:02 +00:00
|
|
|
for _, ds := range item.daemonSets {
|
|
|
|
nodeController.daemonSetStore.Add(&ds)
|
|
|
|
}
|
2015-04-10 22:30:11 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
2015-03-20 17:35:41 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2015-03-31 15:15:39 +00:00
|
|
|
if item.timeToPass > 0 {
|
2015-09-17 22:21:55 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2015-03-31 15:15:39 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
|
|
|
}
|
2015-04-10 22:30:11 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
2015-03-31 15:15:39 +00:00
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2015-05-19 11:23:59 +00:00
|
|
|
|
2015-08-21 01:11:40 +00:00
|
|
|
nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
|
|
|
|
remaining, _ := nodeController.deletePods(value.Value)
|
|
|
|
if remaining {
|
|
|
|
nodeController.terminationEvictor.Add(value.Value)
|
|
|
|
}
|
|
|
|
return true, 0
|
|
|
|
})
|
|
|
|
nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
|
2015-08-25 13:47:08 +00:00
|
|
|
nodeController.terminatePods(value.Value, value.AddedAt)
|
2015-08-21 01:11:40 +00:00
|
|
|
return true, 0
|
|
|
|
})
|
2015-03-20 17:35:41 +00:00
|
|
|
podEvicted := false
|
2015-07-06 21:37:46 +00:00
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
2015-08-03 13:21:11 +00:00
|
|
|
if action.GetVerb() == "delete" && action.GetResource() == "pods" {
|
2015-03-20 17:35:41 +00:00
|
|
|
podEvicted = true
|
|
|
|
}
|
|
|
|
}
|
2015-05-19 11:23:59 +00:00
|
|
|
|
2015-03-20 17:35:41 +00:00
|
|
|
if item.expectedEvictPods != podEvicted {
|
2015-03-31 11:17:12 +00:00
|
|
|
t.Errorf("expected pod eviction: %+v, got %+v for %+v", item.expectedEvictPods,
|
|
|
|
podEvicted, item.description)
|
2015-03-20 17:35:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMonitorNodeStatusUpdateStatus(t *testing.T) {
|
2015-09-17 22:21:55 +00:00
|
|
|
fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-01-16 22:28:20 +00:00
|
|
|
table := []struct {
|
|
|
|
fakeNodeHandler *FakeNodeHandler
|
2015-03-31 15:15:39 +00:00
|
|
|
timeToPass time.Duration
|
|
|
|
newNodeStatus api.NodeStatus
|
|
|
|
expectedEvictPods bool
|
2015-01-16 22:28:20 +00:00
|
|
|
expectedRequestCount int
|
2015-03-12 03:00:52 +00:00
|
|
|
expectedNodes []*api.Node
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
2015-03-12 03:00:52 +00:00
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
expectedRequestCount: 2, // List+Update
|
2015-01-16 22:28:20 +00:00
|
|
|
expectedNodes: []*api.Node{
|
|
|
|
{
|
2015-03-12 03:00:52 +00:00
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
Status: api.NodeStatus{
|
2015-02-04 21:56:59 +00:00
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-12 03:00:52 +00:00
|
|
|
Type: api.NodeReady,
|
2015-03-20 17:35:41 +00:00
|
|
|
Status: api.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.",
|
|
|
|
LastHeartbeatTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusNeverUpdated",
|
|
|
|
Message: "Kubelet never posted node status.",
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-23 01:10:35 +00:00
|
|
|
LastTransitionTime: fakeNow,
|
2015-02-17 20:03:14 +00:00
|
|
|
},
|
2015-02-04 21:56:59 +00:00
|
|
|
},
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-03-23 01:10:35 +00:00
|
|
|
CreationTimestamp: fakeNow,
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
|
|
|
},
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-23 01:10:35 +00:00
|
|
|
Type: api.NodeReady,
|
2015-03-23 18:33:55 +00:00
|
|
|
Status: api.ConditionTrue,
|
2015-03-23 01:10:35 +00:00
|
|
|
// Node status hasn't been updated for 1hr.
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-10-22 19:47:43 +00:00
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-03-24 17:24:07 +00:00
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
2015-03-25 13:44:40 +00:00
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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,
|
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
2015-10-22 19:47:43 +00:00
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
2015-03-31 15:15:39 +00:00
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
expectedNodes: []*api.Node{
|
2015-01-16 22:28:20 +00:00
|
|
|
{
|
2015-03-12 03:00:52 +00:00
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
Status: api.NodeStatus{
|
2015-02-04 21:56:59 +00:00
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-12 03:00:52 +00:00
|
|
|
Type: api.NodeReady,
|
2015-03-20 17:35:41 +00:00
|
|
|
Status: api.ConditionUnknown,
|
2015-10-22 19:47:43 +00:00
|
|
|
Reason: "NodeStatusUnknown",
|
|
|
|
Message: "Kubelet stopped posting node status.",
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Time{Time: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionUnknown,
|
|
|
|
Reason: "NodeStatusUnknown",
|
|
|
|
Message: "Kubelet stopped posting node status.",
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Time{Time: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC).Add(time.Hour)},
|
2015-02-17 20:03:14 +00:00
|
|
|
},
|
2015-02-04 21:56:59 +00:00
|
|
|
},
|
2015-03-24 17:24:07 +00:00
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
2015-03-25 13:44:40 +00:00
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
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
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-03-12 03:00:52 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
2015-03-23 01:10:35 +00:00
|
|
|
Type: api.NodeReady,
|
2015-03-23 18:33:55 +00:00
|
|
|
Status: api.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
|
|
|
},
|
|
|
|
},
|
2015-03-24 17:24:07 +00:00
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
2015-03-25 13:44:40 +00:00
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
2015-01-30 20:50:47 +00:00
|
|
|
},
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2015-03-12 03:00:52 +00:00
|
|
|
expectedRequestCount: 1, // List
|
|
|
|
expectedNodes: nil,
|
2015-01-16 22:28:20 +00:00
|
|
|
},
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
2015-01-16 22:28:20 +00:00
|
|
|
|
2015-10-22 19:47:43 +00:00
|
|
|
for i, item := range table {
|
2016-02-05 15:52:07 +00:00
|
|
|
nodeController := NewNodeController(nil, item.fakeNodeHandler, 5*time.Minute, util.NewFakeAlwaysRateLimiter(),
|
|
|
|
util.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false)
|
2015-09-17 22:21:55 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return fakeNow }
|
2015-04-10 22:30:11 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); 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 {
|
2015-09-17 22:21:55 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
|
2015-03-31 15:15:39 +00:00
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
2015-04-10 22:30:11 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); 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)
|
|
|
|
}
|
2015-04-08 09:32:47 +00:00
|
|
|
if len(item.fakeNodeHandler.UpdatedNodes) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodes) {
|
2015-10-22 19:47:43 +00:00
|
|
|
t.Errorf("Case[%d] unexpected nodes: %s", i, util.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodes[0]))
|
|
|
|
}
|
|
|
|
if len(item.fakeNodeHandler.UpdatedNodeStatuses) > 0 && !api.Semantic.DeepEqual(item.expectedNodes, item.fakeNodeHandler.UpdatedNodeStatuses) {
|
|
|
|
t.Errorf("Case[%d] unexpected nodes: %s", i, util.ObjectDiff(item.expectedNodes[0], item.fakeNodeHandler.UpdatedNodeStatuses[0]))
|
2015-01-16 22:28:20 +00:00
|
|
|
}
|
2014-12-19 09:27:01 +00:00
|
|
|
}
|
2015-01-16 22:28:20 +00:00
|
|
|
}
|
|
|
|
|
2015-11-24 22:46:17 +00:00
|
|
|
func TestMonitorNodeStatusMarkPodsNotReady(t *testing.T) {
|
|
|
|
fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
|
|
|
table := []struct {
|
|
|
|
fakeNodeHandler *FakeNodeHandler
|
|
|
|
timeToPass time.Duration
|
|
|
|
newNodeStatus api.NodeStatus
|
|
|
|
expectedPodStatusUpdate bool
|
|
|
|
}{
|
|
|
|
// Node created recently, without status.
|
|
|
|
// Expect no action from node controller (within startup grace period).
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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).
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status has just been updated.
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*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
|
|
|
|
{
|
|
|
|
fakeNodeHandler: &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0")}}),
|
2015-11-24 22:46:17 +00:00
|
|
|
},
|
|
|
|
timeToPass: 1 * time.Minute,
|
|
|
|
newNodeStatus: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Type: api.NodeOutOfDisk,
|
|
|
|
Status: api.ConditionFalse,
|
|
|
|
// Node status hasn't been updated for 1hr.
|
|
|
|
LastHeartbeatTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
LastTransitionTime: unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
expectedPodStatusUpdate: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, item := range table {
|
2016-02-05 15:52:07 +00:00
|
|
|
nodeController := NewNodeController(nil, item.fakeNodeHandler, 5*time.Minute, util.NewFakeAlwaysRateLimiter(),
|
|
|
|
util.NewFakeAlwaysRateLimiter(), testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false)
|
2015-11-24 22:46:17 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return fakeNow }
|
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
|
|
|
t.Errorf("Case[%d] unexpected error: %v", i, err)
|
|
|
|
}
|
|
|
|
if item.timeToPass > 0 {
|
|
|
|
nodeController.now = func() unversioned.Time { return unversioned.Time{Time: fakeNow.Add(item.timeToPass)} }
|
|
|
|
item.fakeNodeHandler.Existing[0].Status = item.newNodeStatus
|
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
|
|
|
t.Errorf("Case[%d] unexpected error: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
podStatusUpdated := false
|
|
|
|
for _, action := range item.fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "update" && action.GetResource() == "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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-05 13:22:13 +00:00
|
|
|
func TestNodeDeletion(t *testing.T) {
|
2015-09-17 22:21:55 +00:00
|
|
|
fakeNow := unversioned.Date(2015, 1, 1, 12, 0, 0, 0, time.UTC)
|
2015-08-05 13:22:13 +00:00
|
|
|
fakeNodeHandler := &FakeNodeHandler{
|
|
|
|
Existing: []*api.Node{
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node0",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-08-05 13:22:13 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status has just been updated.
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "node1",
|
2015-09-17 22:21:55 +00:00
|
|
|
CreationTimestamp: unversioned.Date(2012, 1, 1, 0, 0, 0, 0, time.UTC),
|
2015-08-05 13:22:13 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
Conditions: []api.NodeCondition{
|
|
|
|
{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
// Node status has just been updated.
|
|
|
|
LastHeartbeatTime: fakeNow,
|
|
|
|
LastTransitionTime: fakeNow,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: "node0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-01-29 06:34:08 +00:00
|
|
|
Clientset: fake.NewSimpleClientset(&api.PodList{Items: []api.Pod{*newPod("pod0", "node0"), *newPod("pod1", "node1")}}),
|
2015-08-05 13:22:13 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 15:52:07 +00:00
|
|
|
nodeController := NewNodeController(nil, fakeNodeHandler, 5*time.Minute, util.NewFakeAlwaysRateLimiter(), util.NewFakeAlwaysRateLimiter(),
|
2015-08-05 13:22:13 +00:00
|
|
|
testNodeMonitorGracePeriod, testNodeStartupGracePeriod, testNodeMonitorPeriod, nil, false)
|
2015-09-17 22:21:55 +00:00
|
|
|
nodeController.now = func() unversioned.Time { return fakeNow }
|
2015-08-05 13:22:13 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2016-01-29 06:34:08 +00:00
|
|
|
fakeNodeHandler.Delete("node1", nil)
|
2015-08-05 13:22:13 +00:00
|
|
|
if err := nodeController.monitorNodeStatus(); err != nil {
|
|
|
|
t.Errorf("unexpected error: %v", err)
|
|
|
|
}
|
2015-08-21 01:11:40 +00:00
|
|
|
nodeController.podEvictor.Try(func(value TimedValue) (bool, time.Duration) {
|
|
|
|
nodeController.deletePods(value.Value)
|
|
|
|
return true, 0
|
|
|
|
})
|
2015-08-05 13:22:13 +00:00
|
|
|
podEvicted := false
|
|
|
|
for _, action := range fakeNodeHandler.Actions() {
|
|
|
|
if action.GetVerb() == "delete" && action.GetResource() == "pods" {
|
|
|
|
podEvicted = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !podEvicted {
|
|
|
|
t.Error("expected pods to be evicted from the deleted node")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-20 02:25:31 +00:00
|
|
|
func TestCheckPod(t *testing.T) {
|
|
|
|
|
|
|
|
tcs := []struct {
|
|
|
|
pod api.Pod
|
|
|
|
prune bool
|
|
|
|
}{
|
|
|
|
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
|
|
|
|
Spec: api.PodSpec{NodeName: "new"},
|
|
|
|
},
|
|
|
|
prune: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
|
|
|
|
Spec: api.PodSpec{NodeName: "old"},
|
|
|
|
},
|
|
|
|
prune: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
|
|
|
|
Spec: api.PodSpec{NodeName: ""},
|
|
|
|
},
|
|
|
|
prune: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: nil},
|
|
|
|
Spec: api.PodSpec{NodeName: "nonexistant"},
|
|
|
|
},
|
|
|
|
prune: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: "new"},
|
|
|
|
},
|
|
|
|
prune: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: "old"},
|
|
|
|
},
|
|
|
|
prune: true,
|
|
|
|
},
|
2015-12-03 22:01:16 +00:00
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: "older"},
|
|
|
|
},
|
|
|
|
prune: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: "oldest"},
|
|
|
|
},
|
|
|
|
prune: true,
|
|
|
|
},
|
2015-10-20 02:25:31 +00:00
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: ""},
|
|
|
|
},
|
|
|
|
prune: true,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pod: api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{DeletionTimestamp: &unversioned.Time{}},
|
|
|
|
Spec: api.PodSpec{NodeName: "nonexistant"},
|
|
|
|
},
|
|
|
|
prune: true,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
nc := NewNodeController(nil, nil, 0, nil, nil, 0, 0, 0, nil, false)
|
|
|
|
nc.nodeStore.Store = cache.NewStore(cache.MetaNamespaceKeyFunc)
|
|
|
|
nc.nodeStore.Store.Add(&api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "new",
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
NodeInfo: api.NodeSystemInfo{
|
|
|
|
KubeletVersion: "v1.1.0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
nc.nodeStore.Store.Add(&api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "old",
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
NodeInfo: api.NodeSystemInfo{
|
|
|
|
KubeletVersion: "v1.0.0",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2015-12-03 22:01:16 +00:00
|
|
|
nc.nodeStore.Store.Add(&api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "older",
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
NodeInfo: api.NodeSystemInfo{
|
|
|
|
KubeletVersion: "v0.21.4",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
nc.nodeStore.Store.Add(&api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{
|
|
|
|
Name: "oldest",
|
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
|
|
|
NodeInfo: api.NodeSystemInfo{
|
|
|
|
KubeletVersion: "v0.19.3",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
2015-10-20 02:25:31 +00:00
|
|
|
|
|
|
|
for i, tc := range tcs {
|
|
|
|
var deleteCalls int
|
|
|
|
nc.forcefullyDeletePod = func(_ *api.Pod) {
|
|
|
|
deleteCalls++
|
|
|
|
}
|
|
|
|
|
|
|
|
nc.maybeDeleteTerminatingPod(&tc.pod)
|
|
|
|
|
|
|
|
if tc.prune && deleteCalls != 1 {
|
|
|
|
t.Errorf("[%v] expected number of delete calls to be 1 but got %v", i, deleteCalls)
|
|
|
|
}
|
|
|
|
if !tc.prune && deleteCalls != 0 {
|
|
|
|
t.Errorf("[%v] expected number of delete calls to be 0 but got %v", i, deleteCalls)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-16 22:28:20 +00:00
|
|
|
func newNode(name string) *api.Node {
|
2015-03-24 17:24:07 +00:00
|
|
|
return &api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{Name: name},
|
|
|
|
Spec: api.NodeSpec{
|
|
|
|
ExternalID: name,
|
2015-03-25 13:44:40 +00:00
|
|
|
},
|
|
|
|
Status: api.NodeStatus{
|
2015-03-24 17:24:07 +00:00
|
|
|
Capacity: api.ResourceList{
|
|
|
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
|
|
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2015-01-16 22:28:20 +00:00
|
|
|
}
|
|
|
|
|
2015-02-07 19:53:42 +00:00
|
|
|
func newPod(name, host string) *api.Pod {
|
2015-11-24 22:46:17 +00:00
|
|
|
return &api.Pod{ObjectMeta: api.ObjectMeta{Name: name}, Spec: api.PodSpec{NodeName: host},
|
|
|
|
Status: api.PodStatus{Conditions: []api.PodCondition{{Type: api.PodReady, Status: api.ConditionTrue}}}}
|
2015-02-07 19:53:42 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 09:27:01 +00:00
|
|
|
func contains(node *api.Node, nodes []*api.Node) bool {
|
|
|
|
for i := 0; i < len(nodes); i++ {
|
|
|
|
if node.Name == nodes[i].Name {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|