Files
node-problem-detector/pkg/problemclient/problem_client_test.go
T

260 lines
7.4 KiB
Go

/*
Copyright 2016 The Kubernetes Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package problemclient
import (
"fmt"
"reflect"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/unversioned"
"k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset/fake"
"k8s.io/kubernetes/pkg/client/record"
"k8s.io/kubernetes/pkg/client/testing/core"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util"
)
const (
testSource = "test"
testNode = "test-node"
)
func newFakeProblemClient(fakeClient *fake.Clientset) *nodeProblemClient {
return &nodeProblemClient{
nodeName: testNode,
client: fakeClient,
clock: &util.FakeClock{},
recorders: make(map[string]record.EventRecorder),
nodeRef: getNodeRef(testNode),
}
}
func newFakeNode(conditions []api.NodeCondition) *api.Node {
node := &api.Node{}
node.Name = testNode
node.Status = api.NodeStatus{Conditions: conditions}
return node
}
type action struct {
verb string
resource string
subresource string
}
func TestSetConditions(t *testing.T) {
now := time.Now()
expectedActions := []action{
{
verb: "get",
resource: "nodes",
},
{
verb: "update",
resource: "nodes",
subresource: "status",
},
}
for _, test := range []struct {
init []api.NodeCondition
update []api.NodeCondition
expected []api.NodeCondition
}{
// Init condition with the same type should be override
{
init: []api.NodeCondition{
{
Type: "TestType",
Status: api.ConditionTrue,
},
},
update: []api.NodeCondition{
{
Type: "TestType",
Status: api.ConditionTrue,
LastTransitionTime: unversioned.NewTime(now),
Reason: "TestReason",
Message: "TestMessage",
},
},
expected: []api.NodeCondition{
{
// LastHeartbeatTime should be updated in SetConditions
Type: "TestType",
Status: api.ConditionTrue,
LastHeartbeatTime: unversioned.NewTime(now),
LastTransitionTime: unversioned.NewTime(now),
Reason: "TestReason",
Message: "TestMessage",
},
},
},
// Init condition with different type should be kept
{
init: []api.NodeCondition{
{
Type: "InitType",
Status: api.ConditionTrue,
LastTransitionTime: unversioned.NewTime(now),
Reason: "InitReason",
Message: "InitMessage",
},
},
update: []api.NodeCondition{
{
Type: "TestType",
Status: api.ConditionTrue,
LastTransitionTime: unversioned.NewTime(now),
Reason: "TestReason",
Message: "TestMessage",
},
},
expected: []api.NodeCondition{
{
Type: "InitType",
Status: api.ConditionTrue,
LastTransitionTime: unversioned.NewTime(now),
Reason: "InitReason",
Message: "InitMessage",
},
{
// LastHeartbeatTime should be updated in SetConditions
Type: "TestType",
Status: api.ConditionTrue,
LastHeartbeatTime: unversioned.NewTime(now),
LastTransitionTime: unversioned.NewTime(now),
Reason: "TestReason",
Message: "TestMessage",
},
},
},
// Condition with false status should be removed
{
init: []api.NodeCondition{
{
Type: "TestType",
Status: api.ConditionTrue,
LastHeartbeatTime: unversioned.NewTime(now),
LastTransitionTime: unversioned.NewTime(now),
Reason: "TestReason",
Message: "TestMessage",
},
},
update: []api.NodeCondition{
{
Type: "TestType",
Status: api.ConditionFalse,
},
},
expected: []api.NodeCondition{},
},
} {
fakeClient := fake.NewSimpleClientset(newFakeNode(test.init))
client := newFakeProblemClient(fakeClient)
clock := client.clock.(*util.FakeClock)
clock.SetTime(now)
client.SetConditions(test.update, 10*time.Second)
// The actions should match the expected actions
actions := fakeClient.Actions()
if len(expectedActions) != len(actions) {
t.Errorf("expected actions %+v, got %+v", expectedActions, fakeClient.Actions())
continue
}
for i, a := range actions {
if !a.Matches(expectedActions[i].verb, expectedActions[i].resource) || a.GetSubresource() != expectedActions[i].subresource {
t.Errorf("expected action %+v, got %+v", expectedActions[i], a)
}
}
// The last action should be an update
a, ok := actions[len(actions)-1].(core.UpdateAction)
if !ok {
t.Errorf("expected the last action to be update, got %+v", actions[len(actions)-1])
}
// The updated node conditions should match the expected conditions
node, ok := a.GetObject().(*api.Node)
if !ok {
t.Errorf("expected the update object to be node, got %+v", a.GetObject())
}
if !api.Semantic.DeepEqual(test.expected, node.Status.Conditions) {
t.Errorf("expected conditions %+v, got %+v", test.expected, node.Status.Conditions)
}
}
}
func TestSetConditionsError(t *testing.T) {
timeout := time.Duration(0)
node := newFakeNode([]api.NodeCondition{})
for c, test := range []struct {
errMap map[string]error
expectedErr error
}{
{
// Get error
errMap: map[string]error{"get": fmt.Errorf("get error")},
expectedErr: fmt.Errorf("get error"),
},
{
// Update error
errMap: map[string]error{"update": fmt.Errorf("update error")},
expectedErr: fmt.Errorf("update error"),
},
{
// Timeout error
errMap: map[string]error{
"update": &errors.StatusError{ErrStatus: unversioned.Status{Reason: unversioned.StatusReasonConflict}},
},
expectedErr: timeoutError{node: testNode, timeout: timeout},
},
{
// No error
errMap: map[string]error{},
expectedErr: nil,
},
} {
fakeClient := &fake.Clientset{}
client := newFakeProblemClient(fakeClient)
fakeClient.AddReactor("get", "nodes", func(action core.Action) (bool, runtime.Object, error) {
return true, node, test.errMap["get"]
})
fakeClient.AddReactor("update", "nodes", func(action core.Action) (bool, runtime.Object, error) {
return true, node, test.errMap["update"]
})
err := client.SetConditions([]api.NodeCondition{}, timeout)
if !reflect.DeepEqual(err, test.expectedErr) {
t.Errorf("case %d: expected error %v, got %v", c+1, test.expectedErr, err)
}
}
}
func TestEvent(t *testing.T) {
fakeRecorder := record.NewFakeRecorder(1)
client := newFakeProblemClient(&fake.Clientset{})
client.recorders[testSource] = fakeRecorder
client.Eventf(api.EventTypeWarning, testSource, "test reason", "test message")
expected := fmt.Sprintf("%s %s %s", api.EventTypeWarning, "test reason", "test message")
got := <-fakeRecorder.Events
if expected != got {
t.Errorf("expected event %q, got %q", expected, got)
}
}