Merge pull request #6857 from wojtek-t/fix_scheduler_nodes

Fix listing nodes in scheduler
pull/6/head
Daniel Smith 2015-04-15 17:05:04 -07:00
commit 835a87aed2
7 changed files with 26 additions and 206 deletions

View File

@ -1579,6 +1579,8 @@ func init() {
switch label {
case "name":
return "metadata.name", value, nil
case "unschedulable":
return "spec.unschedulable", value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}

View File

@ -1504,6 +1504,8 @@ func init() {
switch label {
case "name":
return "metadata.name", value, nil
case "unschedulable":
return "spec.unschedulable", value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}

View File

@ -44,6 +44,8 @@ func init() {
switch label {
case "metadata.name":
return label, value, nil
case "spec.unschedulable":
return label, value, nil
default:
return "", "", fmt.Errorf("field label not supported: %s", label)
}

View File

@ -250,8 +250,9 @@ func (r *Request) RequestURI(uri string) *Request {
const (
// A constant that clients can use to refer in a field selector to the object name field.
// Will be automatically emitted as the correct name for the API version.
ObjectNameField = "metadata.name"
PodHost = "spec.host"
NodeUnschedulable = "spec.unschedulable"
ObjectNameField = "metadata.name"
PodHost = "spec.host"
)
type clientFieldNameToAPIVersionFieldName map[string]string
@ -294,10 +295,12 @@ func (v versionToResourceToFieldMapping) filterField(apiVersion, resourceType, f
var fieldMappings = versionToResourceToFieldMapping{
"v1beta1": resourceTypeToFieldMapping{
"nodes": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
ObjectNameField: "name",
NodeUnschedulable: "unschedulable",
},
"minions": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
ObjectNameField: "name",
NodeUnschedulable: "unschedulable",
},
"pods": clientFieldNameToAPIVersionFieldName{
PodHost: "DesiredState.Host",
@ -305,10 +308,12 @@ var fieldMappings = versionToResourceToFieldMapping{
},
"v1beta2": resourceTypeToFieldMapping{
"nodes": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
ObjectNameField: "name",
NodeUnschedulable: "unschedulable",
},
"minions": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "name",
ObjectNameField: "name",
NodeUnschedulable: "unschedulable",
},
"pods": clientFieldNameToAPIVersionFieldName{
PodHost: "DesiredState.Host",
@ -316,10 +321,12 @@ var fieldMappings = versionToResourceToFieldMapping{
},
"v1beta3": resourceTypeToFieldMapping{
"nodes": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "metadata.name",
ObjectNameField: "metadata.name",
NodeUnschedulable: "spec.unschedulable",
},
"minions": clientFieldNameToAPIVersionFieldName{
ObjectNameField: "metadata.name",
ObjectNameField: "metadata.name",
NodeUnschedulable: "spec.unschedulable",
},
"pods": clientFieldNameToAPIVersionFieldName{
PodHost: "spec.host",

View File

@ -107,7 +107,8 @@ type ResourceGetter interface {
// NodeToSelectableFields returns a label set that represents the object.
func NodeToSelectableFields(node *api.Node) fields.Set {
return fields.Set{
"metadata.name": node.Name,
"metadata.name": node.Name,
"spec.unschedulable": fmt.Sprint(node.Spec.Unschedulable),
}
}

View File

@ -28,7 +28,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
"github.com/GoogleCloudPlatform/kubernetes/pkg/controller/framework"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
algorithm "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler"
@ -230,40 +229,9 @@ func (factory *ConfigFactory) createAssignedPodLW() *cache.ListWatch {
// createMinionLW returns a cache.ListWatch that gets all changes to minions.
func (factory *ConfigFactory) createMinionLW() *cache.ListWatch {
return cache.NewListWatchFromClient(factory.Client, "nodes", api.NamespaceAll, parseSelectorOrDie(""))
}
// Lists all minions and filter out unhealthy ones, then returns
// an enumerator for cache.Poller.
func (factory *ConfigFactory) pollMinions() (cache.Enumerator, error) {
allNodes, err := factory.Client.Nodes().List(labels.Everything(), fields.Everything())
if err != nil {
return nil, err
}
nodes := &api.NodeList{
TypeMeta: allNodes.TypeMeta,
ListMeta: allNodes.ListMeta,
}
for _, node := range allNodes.Items {
conditionMap := make(map[api.NodeConditionType]*api.NodeCondition)
for i := range node.Status.Conditions {
cond := node.Status.Conditions[i]
conditionMap[cond.Type] = &cond
}
if node.Spec.Unschedulable {
continue
}
if condition, ok := conditionMap[api.NodeReady]; ok {
if condition.Status == api.ConditionTrue {
nodes.Items = append(nodes.Items, node)
}
} else {
// If no condition is set, we get unknown node condition. In such cases,
// do not add the node.
glog.V(2).Infof("Minion %s is not available. Skipping", node.Name)
}
}
return &nodeEnumerator{nodes}, nil
// TODO: Filter out nodes that doesn't have NodeReady condition.
fields := fields.Set{client.NodeUnschedulable: "false"}.AsSelector()
return cache.NewListWatchFromClient(factory.Client, "nodes", api.NamespaceAll, fields)
}
// Returns a cache.ListWatch that gets all changes to services.

View File

@ -131,168 +131,6 @@ func PriorityTwo(pod api.Pod, podLister algorithm.PodLister, minionLister algori
return []algorithm.HostPriority{}, nil
}
func TestPollMinions(t *testing.T) {
table := []struct {
minions []api.Node
expectedCount int
}{
{
minions: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
},
},
},
{
ObjectMeta: api.ObjectMeta{Name: "fiz"},
Spec: api.NodeSpec{
Unschedulable: false,
},
},
{
ObjectMeta: api.ObjectMeta{Name: "baz"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
{Type: api.NodeReady, Status: api.ConditionTrue},
},
},
},
{
ObjectMeta: api.ObjectMeta{Name: "fuz"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
},
},
Spec: api.NodeSpec{
Unschedulable: false,
},
},
{
ObjectMeta: api.ObjectMeta{Name: "buz"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
},
},
Spec: api.NodeSpec{
Unschedulable: true,
},
},
{
ObjectMeta: api.ObjectMeta{Name: "foobar"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionFalse},
},
},
Spec: api.NodeSpec{
Unschedulable: false,
},
},
},
expectedCount: 3,
},
{
minions: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
},
},
},
{
ObjectMeta: api.ObjectMeta{Name: "bar"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionFalse},
},
},
},
},
expectedCount: 1,
},
{
minions: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.NodeSpec{
Unschedulable: false,
},
},
{
ObjectMeta: api.ObjectMeta{Name: "bar"},
Spec: api.NodeSpec{
Unschedulable: true,
},
},
},
expectedCount: 0,
},
{
minions: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{
{Type: api.NodeReady, Status: api.ConditionTrue},
{Type: "invalidValue", Status: api.ConditionFalse}},
},
},
},
expectedCount: 1,
},
{
minions: []api.Node{
{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Status: api.NodeStatus{
Conditions: []api.NodeCondition{},
},
},
},
expectedCount: 0,
},
}
for _, item := range table {
ml := &api.NodeList{Items: item.minions}
handler := util.FakeHandler{
StatusCode: 200,
ResponseBody: runtime.EncodeOrDie(latest.Codec, ml),
T: t,
}
mux := http.NewServeMux()
// FakeHandler musn't be sent requests other than the one you want to test.
resource := "nodes"
if api.PreV1Beta3(testapi.Version()) {
resource = "minions"
}
mux.Handle(testapi.ResourcePath(resource, api.NamespaceAll, ""), &handler)
server := httptest.NewServer(mux)
defer server.Close()
client := client.NewOrDie(&client.Config{Host: server.URL, Version: testapi.Version()})
cf := NewConfigFactory(client)
ce, err := cf.pollMinions()
if err != nil {
t.Errorf("Unexpected error: %v", err)
continue
}
handler.ValidateRequest(t, testapi.ResourcePath(resource, api.NamespaceAll, ""), "GET", nil)
if a := ce.Len(); item.expectedCount != a {
t.Errorf("Expected %v, got %v", item.expectedCount, a)
}
}
}
func TestDefaultErrorFunc(t *testing.T) {
testPod := &api.Pod{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},