Add requirements based scheduling.

This commit is contained in:
Brendan Burns 2014-10-21 17:13:52 -07:00
parent dc7e3d6601
commit 5d4d60783d
10 changed files with 142 additions and 3 deletions

View File

@ -141,7 +141,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
return
} else {
if !reflect.DeepEqual(source, obj2) {
t.Errorf("1: %v: diff: %v", name, util.ObjectDiff(source, obj2))
t.Errorf("1: %v: diff: %v\nCodec: %v\nData: %s\nSource: %#v", name, util.ObjectDiff(source, obj2), codec, string(data), source)
return
}
}
@ -152,7 +152,7 @@ func runTest(t *testing.T, codec runtime.Codec, source runtime.Object) {
return
} else {
if !reflect.DeepEqual(source, obj3) {
t.Errorf("3: %v: diff: %v", name, util.ObjectDiff(source, obj3))
t.Errorf("3: %v: diff: %v\nCodec: %v", name, util.ObjectDiff(source, obj3), codec)
return
}
}

View File

@ -424,6 +424,8 @@ type Pod struct {
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"`
}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
@ -531,6 +533,8 @@ type Minion struct {
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
// Resources available on the node
NodeResources NodeResources `json:"resources,omitempty" yaml:"resources,omitempty"`
// Labels for the node
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
// MinionList is a list of minions.

View File

@ -177,6 +177,10 @@ func init() {
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err
}
if err := s.Convert(&in.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
return nil
},
func(in *Pod, out *newer.Pod, s conversion.Scope) error {
@ -196,6 +200,10 @@ func init() {
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err
}
if err := s.Convert(&in.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
return nil
},
@ -348,6 +356,9 @@ func init() {
if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil {
return err
}
out.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0)
@ -359,6 +370,9 @@ func init() {
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil {
return err
}
out.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0)

View File

@ -395,6 +395,8 @@ type Pod struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"`
}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
@ -491,6 +493,8 @@ type Minion struct {
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
// Resources available on the node
NodeResources NodeResources `json:"resources,omitempty" yaml:"resources,omitempty"`
// Labels for the node
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
// MinionList is a list of minions.

View File

@ -108,6 +108,10 @@ func init() {
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err
}
if err := s.Convert(&in.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
return nil
},
func(in *Pod, out *newer.Pod, s conversion.Scope) error {
@ -127,6 +131,10 @@ func init() {
if err := s.Convert(&in.CurrentState, &out.CurrentState, 0); err != nil {
return err
}
if err := s.Convert(&in.NodeSelector, &out.NodeSelector, 0); err != nil {
return err
}
return nil
},
@ -279,6 +287,9 @@ func init() {
if err := s.Convert(&in.ObjectMeta, &out.TypeMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil {
return err
}
out.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0)
@ -290,6 +301,9 @@ func init() {
if err := s.Convert(&in.TypeMeta, &out.ObjectMeta, 0); err != nil {
return err
}
if err := s.Convert(&in.Labels, &out.Labels, 0); err != nil {
return err
}
out.HostIP = in.HostIP
return s.Convert(&in.NodeResources, &out.NodeResources, 0)

View File

@ -360,6 +360,8 @@ type Pod struct {
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
DesiredState PodState `json:"desiredState,omitempty" yaml:"desiredState,omitempty"`
CurrentState PodState `json:"currentState,omitempty" yaml:"currentState,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"`
}
// ReplicationControllerState is the state of a replication controller, either input (create, update) or as output (list, get).
@ -456,6 +458,8 @@ type Minion struct {
HostIP string `json:"hostIP,omitempty" yaml:"hostIP,omitempty"`
// Resources available on the node
NodeResources NodeResources `json:"resources,omitempty" yaml:"resources,omitempty"`
// Labels for the node
Labels map[string]string `json:"labels,omitempty" yaml:"labels,omitempty"`
}
// MinionList is a list of minions.

View File

@ -432,6 +432,8 @@ type PodSpec struct {
Volumes []Volume `json:"volumes" yaml:"volumes"`
Containers []Container `json:"containers" yaml:"containers"`
RestartPolicy RestartPolicy `json:"restartPolicy,omitempty" yaml:"restartPolicy,omitempty"`
// NodeSelector is a selector which must be true for the pod to fit on a node
NodeSelector map[string]string `json:"nodeSelector,omitempty" yaml:"nodeSelector,omitempty"`
}
// PodStatus represents information about the status of a pod. Status may trail the actual

View File

@ -79,7 +79,7 @@ func testPrinter(t *testing.T, printer ResourcePrinter, unmarshalFunc func(data
t.Errorf("Unexpeted error: %#v", err)
}
if !reflect.DeepEqual(obj, &objOut) {
t.Errorf("Unexpected inequality: %#v vs %#v", obj, &objOut)
t.Errorf("Unexpected inequality:\n%#v \nvs\n%#v", obj, &objOut)
}
}

View File

@ -139,6 +139,29 @@ func NewResourceFitPredicate(info NodeInfo) FitPredicate {
return fit.PodFitsResources
}
func NewSelectorMatchPredicate(info NodeInfo) FitPredicate {
selector := &NodeSelector{
info: info,
}
return selector.PodSelectorMatches
}
type NodeSelector struct {
info NodeInfo
}
func (n *NodeSelector) PodSelectorMatches(pod api.Pod, existingPods []api.Pod, node string) (bool, error) {
if len(pod.NodeSelector) == 0 {
return true, nil
}
selector := labels.SelectorFromSet(pod.NodeSelector)
minion, err := n.info.GetNodeInfo(node)
if err != nil {
return false, err
}
return selector.Matches(labels.Set(minion.Labels)), nil
}
func PodFitsPorts(pod api.Pod, existingPods []api.Pod, node string) (bool, error) {
for _, scheduledPod := range existingPods {
for _, container := range pod.DesiredState.Manifest.Containers {

View File

@ -234,3 +234,77 @@ func TestDiskConflicts(t *testing.T) {
}
}
}
func TestPodFitsSelector(t *testing.T) {
tests := []struct {
pod api.Pod
labels map[string]string
fits bool
test string
}{
{
pod: api.Pod{},
fits: true,
test: "no selector",
},
{
pod: api.Pod{
NodeSelector: map[string]string{
"foo": "bar",
},
},
fits: false,
test: "missing labels",
},
{
pod: api.Pod{
NodeSelector: map[string]string{
"foo": "bar",
},
},
labels: map[string]string{
"foo": "bar",
},
fits: true,
test: "same labels",
},
{
pod: api.Pod{
NodeSelector: map[string]string{
"foo": "bar",
},
},
labels: map[string]string{
"foo": "bar",
"baz": "blah",
},
fits: true,
test: "node labels are superset",
},
{
pod: api.Pod{
NodeSelector: map[string]string{
"foo": "bar",
"baz": "blah",
},
},
labels: map[string]string{
"foo": "bar",
},
fits: false,
test: "node labels are subset",
},
}
for _, test := range tests {
node := api.Minion{Labels: test.labels}
fit := NodeSelector{FakeNodeInfo(node)}
fits, err := fit.PodSelectorMatches(test.pod, []api.Pod{}, "machine")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if fits != test.fits {
t.Errorf("%s: expected: %v got %v", test.test, test.fits, fits)
}
}
}