diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index cf2cb1fa13..ad3c8d18ec 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -38,6 +38,7 @@ import ( func main() { runtime.GOMAXPROCS(4) + util.ReallyCrash = true util.InitLogs() defer util.FlushLogs() diff --git a/pkg/master/master.go b/pkg/master/master.go index c57d790b64..1c60eaba5a 100644 --- a/pkg/master/master.go +++ b/pkg/master/master.go @@ -25,6 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/registry" + "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler" "github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/coreos/go-etcd/etcd" "github.com/golang/glog" @@ -37,6 +38,7 @@ type Master struct { serviceRegistry registry.ServiceRegistry minionRegistry registry.MinionRegistry + // TODO: don't reuse non-threadsafe objects. random *rand.Rand storage map[string]apiserver.RESTStorage } @@ -86,8 +88,9 @@ func (m *Master) init(cloud cloudprovider.Interface) { m.random = rand.New(rand.NewSource(int64(time.Now().Nanosecond()))) podCache := NewPodCache(containerInfo, m.podRegistry, time.Second*30) go podCache.Loop() + s := scheduler.MakeFirstFitScheduler(m.podRegistry, m.random) m.storage = map[string]apiserver.RESTStorage{ - "pods": registry.MakePodRegistryStorage(m.podRegistry, containerInfo, registry.MakeFirstFitScheduler(m.minionRegistry, m.podRegistry, m.random), cloud, podCache), + "pods": registry.MakePodRegistryStorage(m.podRegistry, containerInfo, s, m.minionRegistry, cloud, podCache), "replicationControllers": registry.MakeControllerRegistryStorage(m.controllerRegistry, m.podRegistry), "services": registry.MakeServiceRegistryStorage(m.serviceRegistry, cloud, m.minionRegistry), "minions": registry.MakeMinionRegistryStorage(m.minionRegistry), diff --git a/pkg/registry/pod_registry.go b/pkg/registry/pod_registry.go index 708dfb24ac..c8a1856c13 100644 --- a/pkg/registry/pod_registry.go +++ b/pkg/registry/pod_registry.go @@ -25,6 +25,7 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/client" "github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/scheduler" "github.com/golang/glog" ) @@ -33,22 +34,30 @@ type PodRegistryStorage struct { registry PodRegistry containerInfo client.ContainerInfo podCache client.ContainerInfo - scheduler Scheduler + scheduler scheduler.Scheduler + minionLister scheduler.MinionLister cloud cloudprovider.Interface } // MakePodRegistryStorage makes a RESTStorage object for a pod registry. // Parameters: -// registry The pod registry -// containerInfo Source of fresh container info -// scheduler The scheduler for assigning pods to machines -// cloud Interface to a cloud provider (may be null) -// podCache Source of cached container info -func MakePodRegistryStorage(registry PodRegistry, containerInfo client.ContainerInfo, scheduler Scheduler, cloud cloudprovider.Interface, podCache client.ContainerInfo) apiserver.RESTStorage { +// registry: The pod registry +// containerInfo: Source of fresh container info +// scheduler: The scheduler for assigning pods to machines +// minionLister: Object which can list available minions for the scheduler +// cloud: Interface to a cloud provider (may be null) +// podCache: Source of cached container info +func MakePodRegistryStorage(registry PodRegistry, + containerInfo client.ContainerInfo, + scheduler scheduler.Scheduler, + minionLister scheduler.MinionLister, + cloud cloudprovider.Interface, + podCache client.ContainerInfo) apiserver.RESTStorage { return &PodRegistryStorage{ registry: registry, containerInfo: containerInfo, scheduler: scheduler, + minionLister: minionLister, cloud: cloud, podCache: podCache, } @@ -150,7 +159,7 @@ func (storage *PodRegistryStorage) Create(obj interface{}) (<-chan interface{}, return apiserver.MakeAsync(func() (interface{}, error) { // TODO(lavalamp): Separate scheduler more cleanly. - machine, err := storage.scheduler.Schedule(pod) + machine, err := storage.scheduler.Schedule(pod, storage.minionLister) if err != nil { return nil, err } diff --git a/pkg/registry/scheduler_test.go b/pkg/registry/scheduler_test.go deleted file mode 100644 index e32304a83b..0000000000 --- a/pkg/registry/scheduler_test.go +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2014 Google Inc. 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 registry - -import ( - "math/rand" - "testing" - - "github.com/GoogleCloudPlatform/kubernetes/pkg/api" -) - -func expectSchedule(scheduler Scheduler, pod api.Pod, expected string, t *testing.T) { - actual, err := scheduler.Schedule(pod) - expectNoError(t, err) - if actual != expected { - t.Errorf("Unexpected scheduling value: %v, expected %v", actual, expected) - } -} - -func TestRoundRobinScheduler(t *testing.T) { - scheduler := MakeRoundRobinScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3", "m4"})) - expectSchedule(scheduler, api.Pod{}, "m1", t) - expectSchedule(scheduler, api.Pod{}, "m2", t) - expectSchedule(scheduler, api.Pod{}, "m3", t) - expectSchedule(scheduler, api.Pod{}, "m4", t) -} - -func TestRandomScheduler(t *testing.T) { - random := rand.New(rand.NewSource(0)) - scheduler := MakeRandomScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3", "m4"}), *random) - _, err := scheduler.Schedule(api.Pod{}) - expectNoError(t, err) -} - -func TestFirstFitSchedulerNothingScheduled(t *testing.T) { - mockRegistry := MockPodRegistry{} - r := rand.New(rand.NewSource(0)) - scheduler := MakeFirstFitScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3"}), &mockRegistry, r) - expectSchedule(scheduler, api.Pod{}, "m3", t) -} - -func makePod(host string, hostPorts ...int) api.Pod { - networkPorts := []api.Port{} - for _, port := range hostPorts { - networkPorts = append(networkPorts, api.Port{HostPort: port}) - } - return api.Pod{ - CurrentState: api.PodState{ - Host: host, - }, - DesiredState: api.PodState{ - Manifest: api.ContainerManifest{ - Containers: []api.Container{ - { - Ports: networkPorts, - }, - }, - }, - }, - } -} - -func TestFirstFitSchedulerFirstScheduled(t *testing.T) { - mockRegistry := MockPodRegistry{ - pods: []api.Pod{ - makePod("m1", 8080), - }, - } - r := rand.New(rand.NewSource(0)) - scheduler := MakeFirstFitScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3"}), &mockRegistry, r) - expectSchedule(scheduler, makePod("", 8080), "m3", t) -} - -func TestFirstFitSchedulerFirstScheduledComplicated(t *testing.T) { - mockRegistry := MockPodRegistry{ - pods: []api.Pod{ - makePod("m1", 80, 8080), - makePod("m2", 8081, 8082, 8083), - makePod("m3", 80, 443, 8085), - }, - } - r := rand.New(rand.NewSource(0)) - scheduler := MakeFirstFitScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3"}), &mockRegistry, r) - expectSchedule(scheduler, makePod("", 8080, 8081), "m3", t) -} - -func TestFirstFitSchedulerFirstScheduledImpossible(t *testing.T) { - mockRegistry := MockPodRegistry{ - pods: []api.Pod{ - makePod("m1", 8080), - makePod("m2", 8081), - makePod("m3", 8080), - }, - } - r := rand.New(rand.NewSource(0)) - scheduler := MakeFirstFitScheduler(MakeMinionRegistry([]string{"m1", "m2", "m3"}), &mockRegistry, r) - _, err := scheduler.Schedule(makePod("", 8080, 8081)) - if err == nil { - t.Error("Unexpected non-error.") - } -} diff --git a/pkg/scheduler/doc.go b/pkg/scheduler/doc.go new file mode 100644 index 0000000000..1e194acd85 --- /dev/null +++ b/pkg/scheduler/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2014 Google Inc. 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 scheduler contains a generic Scheduler interface and several +// implementations. +package scheduler diff --git a/pkg/registry/scheduler.go b/pkg/scheduler/firstfit.go similarity index 54% rename from pkg/registry/scheduler.go rename to pkg/scheduler/firstfit.go index e0518259cb..e98c9a9693 100644 --- a/pkg/registry/scheduler.go +++ b/pkg/scheduler/firstfit.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package registry +package scheduler import ( "fmt" @@ -24,66 +24,16 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" ) -// Scheduler is an interface implemented by things that know how to schedule pods onto machines. -type Scheduler interface { - Schedule(api.Pod) (string, error) -} - -// RandomScheduler choses machines uniformly at random. -type RandomScheduler struct { - machines MinionRegistry - random rand.Rand -} - -func MakeRandomScheduler(machines MinionRegistry, random rand.Rand) Scheduler { - return &RandomScheduler{ - machines: machines, - random: random, - } -} - -func (s *RandomScheduler) Schedule(pod api.Pod) (string, error) { - machines, err := s.machines.List() - if err != nil { - return "", err - } - return machines[s.random.Int()%len(machines)], nil -} - -// RoundRobinScheduler chooses machines in order. -type RoundRobinScheduler struct { - machines MinionRegistry - currentIndex int -} - -func MakeRoundRobinScheduler(machines MinionRegistry) Scheduler { - return &RoundRobinScheduler{ - machines: machines, - currentIndex: -1, - } -} - -func (s *RoundRobinScheduler) Schedule(pod api.Pod) (string, error) { - machines, err := s.machines.List() - if err != nil { - return "", err - } - s.currentIndex = (s.currentIndex + 1) % len(machines) - result := machines[s.currentIndex] - return result, nil -} - type FirstFitScheduler struct { - machines MinionRegistry - registry PodRegistry - random *rand.Rand + podLister PodLister + // TODO: *rand.Rand is *not* threadsafe + random *rand.Rand } -func MakeFirstFitScheduler(machines MinionRegistry, registry PodRegistry, random *rand.Rand) Scheduler { +func MakeFirstFitScheduler(podLister PodLister, random *rand.Rand) Scheduler { return &FirstFitScheduler{ - machines: machines, - registry: registry, - random: random, + podLister: podLister, + random: random, } } @@ -98,13 +48,13 @@ func (s *FirstFitScheduler) containsPort(pod api.Pod, port api.Port) bool { return false } -func (s *FirstFitScheduler) Schedule(pod api.Pod) (string, error) { - machines, err := s.machines.List() +func (s *FirstFitScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) { + machines, err := minionLister.List() if err != nil { return "", err } machineToPods := map[string][]api.Pod{} - pods, err := s.registry.ListPods(labels.Everything()) + pods, err := s.podLister.ListPods(labels.Everything()) if err != nil { return "", err } diff --git a/pkg/scheduler/firstfit_test.go b/pkg/scheduler/firstfit_test.go new file mode 100644 index 0000000000..57e8444b0e --- /dev/null +++ b/pkg/scheduler/firstfit_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "math/rand" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestFirstFitSchedulerNothingScheduled(t *testing.T) { + fakeRegistry := FakePodLister{} + r := rand.New(rand.NewSource(0)) + st := schedulerTester{ + t: t, + scheduler: MakeFirstFitScheduler(&fakeRegistry, r), + minionLister: FakeMinionLister{"m1", "m2", "m3"}, + } + st.expectSchedule(api.Pod{}, "m3") +} + +func TestFirstFitSchedulerFirstScheduled(t *testing.T) { + fakeRegistry := FakePodLister{ + makePod("m1", 8080), + } + r := rand.New(rand.NewSource(0)) + st := schedulerTester{ + t: t, + scheduler: MakeFirstFitScheduler(fakeRegistry, r), + minionLister: FakeMinionLister{"m1", "m2", "m3"}, + } + st.expectSchedule(makePod("", 8080), "m3") +} + +func TestFirstFitSchedulerFirstScheduledComplicated(t *testing.T) { + fakeRegistry := FakePodLister{ + makePod("m1", 80, 8080), + makePod("m2", 8081, 8082, 8083), + makePod("m3", 80, 443, 8085), + } + r := rand.New(rand.NewSource(0)) + st := schedulerTester{ + t: t, + scheduler: MakeFirstFitScheduler(fakeRegistry, r), + minionLister: FakeMinionLister{"m1", "m2", "m3"}, + } + st.expectSchedule(makePod("", 8080, 8081), "m3") +} + +func TestFirstFitSchedulerFirstScheduledImpossible(t *testing.T) { + fakeRegistry := FakePodLister{ + makePod("m1", 8080), + makePod("m2", 8081), + makePod("m3", 8080), + } + r := rand.New(rand.NewSource(0)) + st := schedulerTester{ + t: t, + scheduler: MakeFirstFitScheduler(fakeRegistry, r), + minionLister: FakeMinionLister{"m1", "m2", "m3"}, + } + st.expectFailure(makePod("", 8080, 8081)) +} diff --git a/pkg/scheduler/listers.go b/pkg/scheduler/listers.go new file mode 100644 index 0000000000..ad7a41562f --- /dev/null +++ b/pkg/scheduler/listers.go @@ -0,0 +1,52 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" + "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" +) + +// Anything that can list minions for a scheduler. +type MinionLister interface { + List() (machines []string, err error) +} + +// Make a MinionLister from a []string +type FakeMinionLister []string + +// Returns minions as a []string +func (f FakeMinionLister) List() ([]string, error) { + return []string(f), nil +} + +// Anything that can list pods for a scheduler +type PodLister interface { + ListPods(labels.Selector) ([]api.Pod, error) +} + +// Make a MinionLister from an []api.Pods +type FakePodLister []api.Pod + +func (f FakePodLister) ListPods(s labels.Selector) (selected []api.Pod, err error) { + for _, pod := range f { + if s.Matches(labels.Set(pod.Labels)) { + selected = append(selected, pod) + } + } + return selected, nil +} diff --git a/pkg/scheduler/random.go b/pkg/scheduler/random.go new file mode 100644 index 0000000000..6d268a33f0 --- /dev/null +++ b/pkg/scheduler/random.go @@ -0,0 +1,43 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "math/rand" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// RandomScheduler choses machines uniformly at random. +type RandomScheduler struct { + // TODO: rand.Rand is *NOT* thread safe. + random *rand.Rand +} + +func MakeRandomScheduler(random *rand.Rand) Scheduler { + return &RandomScheduler{ + random: random, + } +} + +func (s *RandomScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) { + machines, err := minionLister.List() + if err != nil { + return "", err + } + return machines[s.random.Int()%len(machines)], nil +} diff --git a/pkg/scheduler/random_test.go b/pkg/scheduler/random_test.go new file mode 100644 index 0000000000..142b5f6436 --- /dev/null +++ b/pkg/scheduler/random_test.go @@ -0,0 +1,34 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "math/rand" + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestRandomScheduler(t *testing.T) { + random := rand.New(rand.NewSource(0)) + st := schedulerTester{ + t: t, + scheduler: MakeRandomScheduler(random), + minionLister: FakeMinionLister{"m1", "m2", "m3", "m4"}, + } + st.expectSuccess(api.Pod{}) +} diff --git a/pkg/scheduler/roundrobin.go b/pkg/scheduler/roundrobin.go new file mode 100644 index 0000000000..6d1df35fee --- /dev/null +++ b/pkg/scheduler/roundrobin.go @@ -0,0 +1,42 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// RoundRobinScheduler chooses machines in order. +type RoundRobinScheduler struct { + currentIndex int +} + +func MakeRoundRobinScheduler() Scheduler { + return &RoundRobinScheduler{ + currentIndex: -1, + } +} + +func (s *RoundRobinScheduler) Schedule(pod api.Pod, minionLister MinionLister) (string, error) { + machines, err := minionLister.List() + if err != nil { + return "", err + } + s.currentIndex = (s.currentIndex + 1) % len(machines) + result := machines[s.currentIndex] + return result, nil +} diff --git a/pkg/scheduler/roundrobin_test.go b/pkg/scheduler/roundrobin_test.go new file mode 100644 index 0000000000..3ce410a1df --- /dev/null +++ b/pkg/scheduler/roundrobin_test.go @@ -0,0 +1,35 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +func TestRoundRobinScheduler(t *testing.T) { + st := schedulerTester{ + t: t, + scheduler: MakeRoundRobinScheduler(), + minionLister: FakeMinionLister{"m1", "m2", "m3", "m4"}, + } + st.expectSchedule(api.Pod{}, "m1") + st.expectSchedule(api.Pod{}, "m2") + st.expectSchedule(api.Pod{}, "m3") + st.expectSchedule(api.Pod{}, "m4") +} diff --git a/pkg/scheduler/scheduler.go b/pkg/scheduler/scheduler.go new file mode 100644 index 0000000000..34e645fbe8 --- /dev/null +++ b/pkg/scheduler/scheduler.go @@ -0,0 +1,27 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// Scheduler is an interface implemented by things that know how to schedule pods +// onto machines. +type Scheduler interface { + Schedule(api.Pod, MinionLister) (selectedMachine string, err error) +} diff --git a/pkg/scheduler/scheduler_test.go b/pkg/scheduler/scheduler_test.go new file mode 100644 index 0000000000..bfec2dfc41 --- /dev/null +++ b/pkg/scheduler/scheduler_test.go @@ -0,0 +1,81 @@ +/* +Copyright 2014 Google Inc. 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 scheduler + +import ( + "testing" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/api" +) + +// Some functions used by multiple scheduler tests. + +type schedulerTester struct { + t *testing.T + scheduler Scheduler + minionLister MinionLister +} + +// Call if you know exactly where pod should get scheduled. +func (st *schedulerTester) expectSchedule(pod api.Pod, expected string) { + actual, err := st.scheduler.Schedule(pod, st.minionLister) + if err != nil { + st.t.Errorf("Unexpected error %v\nTried to scheduler: %#v", err, pod) + return + } + if actual != expected { + st.t.Errorf("Unexpected scheduling value: %v, expected %v", actual, expected) + } +} + +// Call if you can't predict where pod will be scheduled. +func (st *schedulerTester) expectSuccess(pod api.Pod) { + _, err := st.scheduler.Schedule(pod, st.minionLister) + if err != nil { + st.t.Errorf("Unexpected error %v\nTried to scheduler: %#v", err, pod) + return + } +} + +// Call if pod should *not* schedule. +func (st *schedulerTester) expectFailure(pod api.Pod) { + _, err := st.scheduler.Schedule(pod, st.minionLister) + if err == nil { + st.t.Error("Unexpected non-error") + } +} + +func makePod(host string, hostPorts ...int) api.Pod { + networkPorts := []api.Port{} + for _, port := range hostPorts { + networkPorts = append(networkPorts, api.Port{HostPort: port}) + } + return api.Pod{ + CurrentState: api.PodState{ + Host: host, + }, + DesiredState: api.PodState{ + Manifest: api.ContainerManifest{ + Containers: []api.Container{ + { + Ports: networkPorts, + }, + }, + }, + }, + } +} diff --git a/pkg/util/util.go b/pkg/util/util.go index 65a645af75..ba8eb2753e 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -25,8 +25,15 @@ import ( "github.com/golang/glog" ) +// For testing, bypass HandleCrash. +var ReallyCrash bool + // Simply catches a crash and logs an error. Meant to be called via defer. func HandleCrash() { + if ReallyCrash { + return + } + r := recover() if r != nil { callers := ""