2015-04-17 10:00:03 +00:00
|
|
|
// +build integration,!no-etcd
|
|
|
|
|
|
|
|
/*
|
2015-05-01 16:19:44 +00:00
|
|
|
Copyright 2015 The Kubernetes Authors All rights reserved.
|
2015-04-17 10:00:03 +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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package integration
|
|
|
|
|
|
|
|
// This file tests the scheduler.
|
|
|
|
|
|
|
|
import (
|
2015-05-02 00:00:37 +00:00
|
|
|
"fmt"
|
2015-04-17 10:00:03 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api"
|
|
|
|
"k8s.io/kubernetes/pkg/api/errors"
|
|
|
|
"k8s.io/kubernetes/pkg/api/resource"
|
|
|
|
"k8s.io/kubernetes/pkg/api/testapi"
|
2015-09-17 22:21:55 +00:00
|
|
|
"k8s.io/kubernetes/pkg/api/unversioned"
|
2015-09-03 21:40:58 +00:00
|
|
|
"k8s.io/kubernetes/pkg/client/cache"
|
|
|
|
"k8s.io/kubernetes/pkg/client/record"
|
2015-09-03 21:43:19 +00:00
|
|
|
client "k8s.io/kubernetes/pkg/client/unversioned"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/master"
|
2015-09-23 18:22:54 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util"
|
2015-08-05 22:03:47 +00:00
|
|
|
"k8s.io/kubernetes/pkg/util/wait"
|
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler"
|
|
|
|
_ "k8s.io/kubernetes/plugin/pkg/scheduler/algorithmprovider"
|
|
|
|
"k8s.io/kubernetes/plugin/pkg/scheduler/factory"
|
|
|
|
"k8s.io/kubernetes/test/integration/framework"
|
2015-04-17 10:00:03 +00:00
|
|
|
)
|
|
|
|
|
2015-05-02 00:00:37 +00:00
|
|
|
type nodeMutationFunc func(t *testing.T, n *api.Node, nodeStore cache.Store, c *client.Client)
|
|
|
|
|
|
|
|
type nodeStateManager struct {
|
|
|
|
makeSchedulable nodeMutationFunc
|
|
|
|
makeUnSchedulable nodeMutationFunc
|
|
|
|
}
|
|
|
|
|
2015-04-17 10:00:03 +00:00
|
|
|
func TestUnschedulableNodes(t *testing.T) {
|
2015-04-09 21:50:27 +00:00
|
|
|
framework.DeleteAllEtcdKeys()
|
2015-04-17 10:00:03 +00:00
|
|
|
|
|
|
|
var m *master.Master
|
|
|
|
s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
|
|
|
m.Handler.ServeHTTP(w, req)
|
|
|
|
}))
|
|
|
|
defer s.Close()
|
|
|
|
|
2015-12-02 09:46:27 +00:00
|
|
|
masterConfig := framework.NewIntegrationTestMasterConfig()
|
|
|
|
m = master.New(masterConfig)
|
2015-04-17 10:00:03 +00:00
|
|
|
|
2015-11-13 21:20:54 +00:00
|
|
|
restClient := client.NewOrDie(&client.Config{Host: s.URL, GroupVersion: testapi.Default.GroupVersion()})
|
2015-04-17 10:00:03 +00:00
|
|
|
|
2015-07-27 15:22:38 +00:00
|
|
|
schedulerConfigFactory := factory.NewConfigFactory(restClient, nil)
|
2015-04-17 10:00:03 +00:00
|
|
|
schedulerConfig, err := schedulerConfigFactory.Create()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Couldn't create scheduler config: %v", err)
|
|
|
|
}
|
|
|
|
eventBroadcaster := record.NewBroadcaster()
|
|
|
|
schedulerConfig.Recorder = eventBroadcaster.NewRecorder(api.EventSource{Component: "scheduler"})
|
2015-05-02 00:00:37 +00:00
|
|
|
eventBroadcaster.StartRecordingToSink(restClient.Events(""))
|
2015-04-17 10:00:03 +00:00
|
|
|
scheduler.New(schedulerConfig).Run()
|
|
|
|
|
|
|
|
defer close(schedulerConfig.StopEverything)
|
|
|
|
|
2015-05-02 00:00:37 +00:00
|
|
|
DoTestUnschedulableNodes(t, restClient, schedulerConfigFactory.NodeLister.Store)
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func podScheduled(c *client.Client, podNamespace, podName string) wait.ConditionFunc {
|
|
|
|
return func() (bool, error) {
|
|
|
|
pod, err := c.Pods(podNamespace).Get(podName)
|
|
|
|
if errors.IsNotFound(err) {
|
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
// This could be a connection error so we want to retry.
|
|
|
|
return false, nil
|
|
|
|
}
|
2015-05-22 23:40:57 +00:00
|
|
|
if pod.Spec.NodeName == "" {
|
2015-04-17 10:00:03 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-02 00:00:37 +00:00
|
|
|
// Wait till the passFunc confirms that the object it expects to see is in the store.
|
|
|
|
// Used to observe reflected events.
|
|
|
|
func waitForReflection(s cache.Store, key string, passFunc func(n interface{}) bool) error {
|
|
|
|
return wait.Poll(time.Millisecond*10, time.Second*20, func() (bool, error) {
|
|
|
|
if n, _, err := s.GetByKey(key); err == nil && passFunc(n) {
|
|
|
|
return true, nil
|
|
|
|
}
|
|
|
|
return false, nil
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func DoTestUnschedulableNodes(t *testing.T, restClient *client.Client, nodeStore cache.Store) {
|
|
|
|
goodCondition := api.NodeCondition{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionTrue,
|
|
|
|
Reason: fmt.Sprintf("schedulable condition"),
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Time{time.Now()},
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
2015-05-02 00:00:37 +00:00
|
|
|
badCondition := api.NodeCondition{
|
|
|
|
Type: api.NodeReady,
|
|
|
|
Status: api.ConditionUnknown,
|
|
|
|
Reason: fmt.Sprintf("unschedulable condition"),
|
2015-09-17 22:21:55 +00:00
|
|
|
LastHeartbeatTime: unversioned.Time{time.Now()},
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
2015-05-02 00:00:37 +00:00
|
|
|
// Create a new schedulable node, since we're first going to apply
|
|
|
|
// the unschedulable condition and verify that pods aren't scheduled.
|
|
|
|
node := &api.Node{
|
|
|
|
ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-node"},
|
|
|
|
Spec: api.NodeSpec{Unschedulable: false},
|
|
|
|
Status: api.NodeStatus{
|
2015-03-17 14:43:49 +00:00
|
|
|
Capacity: api.ResourceList{
|
2015-05-18 22:32:32 +00:00
|
|
|
api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
|
2015-03-17 14:43:49 +00:00
|
|
|
},
|
2015-05-02 00:00:37 +00:00
|
|
|
Conditions: []api.NodeCondition{goodCondition},
|
2015-04-17 10:00:03 +00:00
|
|
|
},
|
|
|
|
}
|
2015-05-02 00:00:37 +00:00
|
|
|
nodeKey, err := cache.MetaNamespaceKeyFunc(node)
|
2015-04-17 10:00:03 +00:00
|
|
|
if err != nil {
|
2015-05-02 00:00:37 +00:00
|
|
|
t.Fatalf("Couldn't retrieve key for node %v", node.Name)
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
|
|
|
|
2015-05-02 00:00:37 +00:00
|
|
|
// The test does the following for each nodeStateManager in this list:
|
|
|
|
// 1. Create a new node
|
|
|
|
// 2. Apply the makeUnSchedulable function
|
|
|
|
// 3. Create a new pod
|
|
|
|
// 4. Check that the pod doesn't get assigned to the node
|
|
|
|
// 5. Apply the schedulable function
|
|
|
|
// 6. Check that the pod *does* get assigned to the node
|
|
|
|
// 7. Delete the pod and node.
|
|
|
|
|
|
|
|
nodeModifications := []nodeStateManager{
|
|
|
|
// Test node.Spec.Unschedulable=true/false
|
|
|
|
{
|
|
|
|
makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) {
|
|
|
|
n.Spec.Unschedulable = true
|
|
|
|
if _, err := c.Nodes().Update(n); err != nil {
|
|
|
|
t.Fatalf("Failed to update node with unschedulable=true: %v", err)
|
|
|
|
}
|
|
|
|
err = waitForReflection(s, nodeKey, func(node interface{}) bool {
|
|
|
|
// An unschedulable node should get deleted from the store
|
|
|
|
return node == nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to observe reflected update for setting unschedulable=true: %v", err)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) {
|
|
|
|
n.Spec.Unschedulable = false
|
|
|
|
if _, err := c.Nodes().Update(n); err != nil {
|
|
|
|
t.Fatalf("Failed to update node with unschedulable=false: %v", err)
|
|
|
|
}
|
|
|
|
err = waitForReflection(s, nodeKey, func(node interface{}) bool {
|
|
|
|
return node != nil && node.(*api.Node).Spec.Unschedulable == false
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to observe reflected update for setting unschedulable=false: %v", err)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
// Test node.Status.Conditions=ConditionTrue/Unknown
|
|
|
|
{
|
|
|
|
makeUnSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) {
|
|
|
|
n.Status = api.NodeStatus{
|
2015-03-17 14:43:49 +00:00
|
|
|
Capacity: api.ResourceList{
|
2015-05-18 22:32:32 +00:00
|
|
|
api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
|
2015-03-17 14:43:49 +00:00
|
|
|
},
|
2015-05-02 00:00:37 +00:00
|
|
|
Conditions: []api.NodeCondition{badCondition},
|
|
|
|
}
|
|
|
|
if _, err = c.Nodes().UpdateStatus(n); err != nil {
|
|
|
|
t.Fatalf("Failed to update node with bad status condition: %v", err)
|
|
|
|
}
|
|
|
|
err = waitForReflection(s, nodeKey, func(node interface{}) bool {
|
|
|
|
return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionUnknown
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to observe reflected update for status condition update: %v", err)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
makeSchedulable: func(t *testing.T, n *api.Node, s cache.Store, c *client.Client) {
|
|
|
|
n.Status = api.NodeStatus{
|
2015-03-17 14:43:49 +00:00
|
|
|
Capacity: api.ResourceList{
|
2015-05-18 22:32:32 +00:00
|
|
|
api.ResourcePods: *resource.NewQuantity(32, resource.DecimalSI),
|
2015-03-17 14:43:49 +00:00
|
|
|
},
|
2015-05-02 00:00:37 +00:00
|
|
|
Conditions: []api.NodeCondition{goodCondition},
|
|
|
|
}
|
|
|
|
if _, err = c.Nodes().UpdateStatus(n); err != nil {
|
|
|
|
t.Fatalf("Failed to update node with healthy status condition: %v", err)
|
|
|
|
}
|
|
|
|
waitForReflection(s, nodeKey, func(node interface{}) bool {
|
|
|
|
return node != nil && node.(*api.Node).Status.Conditions[0].Status == api.ConditionTrue
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to observe reflected update for status condition update: %v", err)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
|
|
|
|
2015-05-02 00:00:37 +00:00
|
|
|
for i, mod := range nodeModifications {
|
|
|
|
unSchedNode, err := restClient.Nodes().Create(node)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to create node: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the unschedulable modification to the node, and wait for the reflection
|
|
|
|
mod.makeUnSchedulable(t, unSchedNode, nodeStore, restClient)
|
|
|
|
|
|
|
|
// Create the new pod, note that this needs to happen post unschedulable
|
|
|
|
// modification or we have a race in the test.
|
|
|
|
pod := &api.Pod{
|
|
|
|
ObjectMeta: api.ObjectMeta{Name: "node-scheduling-test-pod"},
|
|
|
|
Spec: api.PodSpec{
|
|
|
|
Containers: []api.Container{{Name: "container", Image: "kubernetes/pause:go"}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
myPod, err := restClient.Pods(api.NamespaceDefault).Create(pod)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to create pod: %v", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// There are no schedulable nodes - the pod shouldn't be scheduled.
|
2015-09-23 18:22:54 +00:00
|
|
|
err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name))
|
2015-05-02 00:00:37 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Errorf("Pod scheduled successfully on unschedulable nodes")
|
|
|
|
}
|
|
|
|
if err != wait.ErrWaitTimeout {
|
2015-05-10 04:25:14 +00:00
|
|
|
t.Errorf("Test %d: failed while trying to confirm the pod does not get scheduled on the node: %v", i, err)
|
2015-05-02 00:00:37 +00:00
|
|
|
} else {
|
|
|
|
t.Logf("Test %d: Pod did not get scheduled on an unschedulable node", i)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Apply the schedulable modification to the node, and wait for the reflection
|
|
|
|
schedNode, err := restClient.Nodes().Get(unSchedNode.Name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Failed to get node: %v", err)
|
|
|
|
}
|
|
|
|
mod.makeSchedulable(t, schedNode, nodeStore, restClient)
|
|
|
|
|
|
|
|
// Wait until the pod is scheduled.
|
2015-09-23 18:22:54 +00:00
|
|
|
err = wait.Poll(time.Second, util.ForeverTestTimeout, podScheduled(restClient, myPod.Namespace, myPod.Name))
|
2015-05-02 00:00:37 +00:00
|
|
|
if err != nil {
|
2015-05-10 04:25:14 +00:00
|
|
|
t.Errorf("Test %d: failed to schedule a pod: %v", i, err)
|
2015-05-02 00:00:37 +00:00
|
|
|
} else {
|
|
|
|
t.Logf("Test %d: Pod got scheduled on a schedulable node", i)
|
|
|
|
}
|
|
|
|
|
2015-08-20 02:09:57 +00:00
|
|
|
err = restClient.Pods(api.NamespaceDefault).Delete(myPod.Name, api.NewDeleteOptions(0))
|
2015-05-02 00:00:37 +00:00
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to delete pod: %v", err)
|
|
|
|
}
|
|
|
|
err = restClient.Nodes().Delete(schedNode.Name)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Failed to delete node: %v", err)
|
|
|
|
}
|
2015-04-17 10:00:03 +00:00
|
|
|
}
|
|
|
|
}
|