From c37701113864c144380a46ebb69aa50894f3dbaf Mon Sep 17 00:00:00 2001 From: Matt Liggett Date: Tue, 23 Aug 2016 11:56:12 -0700 Subject: [PATCH] Add eviction e2e tests. Also refactor the test a bit. --- .../typed/core/v1/fake/fake_pod_expansion.go | 12 ++ .../typed/core/v1/pod_expansion.go | 6 + test/e2e/disruption.go | 160 +++++++++++++----- 3 files changed, 132 insertions(+), 46 deletions(-) diff --git a/pkg/client/clientset_generated/release_1_4/typed/core/v1/fake/fake_pod_expansion.go b/pkg/client/clientset_generated/release_1_4/typed/core/v1/fake/fake_pod_expansion.go index a166f756cea..373ebf6bd29 100644 --- a/pkg/client/clientset_generated/release_1_4/typed/core/v1/fake/fake_pod_expansion.go +++ b/pkg/client/clientset_generated/release_1_4/typed/core/v1/fake/fake_pod_expansion.go @@ -18,6 +18,7 @@ package fake import ( "k8s.io/kubernetes/pkg/api/v1" + policy "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" "k8s.io/kubernetes/pkg/client/restclient" "k8s.io/kubernetes/pkg/client/testing/core" ) @@ -44,3 +45,14 @@ func (c *FakePods) GetLogs(name string, opts *v1.PodLogOptions) *restclient.Requ _, _ = c.Fake.Invokes(action, &v1.Pod{}) return &restclient.Request{} } + +func (c *FakePods) Evict(eviction *policy.Eviction) error { + action := core.CreateActionImpl{} + action.Verb = "create" + action.Resource = podsResource + action.Subresource = "evictions" + action.Object = eviction + + _, err := c.Fake.Invokes(action, eviction) + return err +} diff --git a/pkg/client/clientset_generated/release_1_4/typed/core/v1/pod_expansion.go b/pkg/client/clientset_generated/release_1_4/typed/core/v1/pod_expansion.go index fba1fd069cc..beb61d4ff02 100644 --- a/pkg/client/clientset_generated/release_1_4/typed/core/v1/pod_expansion.go +++ b/pkg/client/clientset_generated/release_1_4/typed/core/v1/pod_expansion.go @@ -19,12 +19,14 @@ package v1 import ( "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/v1" + policy "k8s.io/kubernetes/pkg/apis/policy/v1alpha1" "k8s.io/kubernetes/pkg/client/restclient" ) // The PodExpansion interface allows manually adding extra methods to the PodInterface. type PodExpansion interface { Bind(binding *v1.Binding) error + Evict(eviction *policy.Eviction) error GetLogs(name string, opts *v1.PodLogOptions) *restclient.Request } @@ -33,6 +35,10 @@ func (c *pods) Bind(binding *v1.Binding) error { return c.client.Post().Namespace(c.ns).Resource("pods").Name(binding.Name).SubResource("binding").Body(binding).Do().Error() } +func (c *pods) Evict(eviction *policy.Eviction) error { + return c.client.Post().Namespace(c.ns).Resource("pods").Name(eviction.Name).SubResource("eviction").Body(eviction).Do().Error() +} + // Get constructs a request for getting the logs for a pod func (c *pods) GetLogs(name string, opts *v1.PodLogOptions) *restclient.Request { return c.client.Get().Namespace(c.ns).Name(name).Resource("pods").SubResource("log").VersionedParams(opts, api.ParameterCodec) diff --git a/test/e2e/disruption.go b/test/e2e/disruption.go index 2435f953b3a..21a2b5cbbb6 100644 --- a/test/e2e/disruption.go +++ b/test/e2e/disruption.go @@ -31,7 +31,7 @@ import ( "k8s.io/kubernetes/test/e2e/framework" ) -var _ = framework.KubeDescribe("DisruptionController [Feature:PodDisruptionbudget]", func() { +var _ = framework.KubeDescribe("DisruptionController", func() { f := framework.NewDefaultFramework("disruption") var ns string var cs *release_1_4.Clientset @@ -42,55 +42,17 @@ var _ = framework.KubeDescribe("DisruptionController [Feature:PodDisruptionbudge }) It("should create a PodDisruptionBudget", func() { - pdb := policy.PodDisruptionBudget{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - Spec: policy.PodDisruptionBudgetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - MinAvailable: intstr.FromString("1%"), - }, - } - _, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb) - Expect(err).NotTo(HaveOccurred()) + createPodDisruptionBudgetOrDie(cs, ns, intstr.FromString("1%")) }) It("should update PodDisruptionBudget status", func() { - pdb := policy.PodDisruptionBudget{ - ObjectMeta: api.ObjectMeta{ - Name: "foo", - Namespace: ns, - }, - Spec: policy.PodDisruptionBudgetSpec{ - Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, - MinAvailable: intstr.FromInt(2), - }, - } - _, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb) - Expect(err).NotTo(HaveOccurred()) - for i := 0; i < 2; i++ { - pod := &api.Pod{ - ObjectMeta: api.ObjectMeta{ - Name: fmt.Sprintf("pod-%d", i), - Namespace: ns, - Labels: map[string]string{"foo": "bar"}, - }, - Spec: api.PodSpec{ - Containers: []api.Container{ - { - Name: "busybox", - Image: "gcr.io/google_containers/echoserver:1.4", - }, - }, - RestartPolicy: api.RestartPolicyAlways, - }, - } + createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2)) - _, err := cs.Pods(ns).Create(pod) - framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns) - } - err = wait.PollImmediate(framework.Poll, 60*time.Second, func() (bool, error) { + createPodsOrDie(cs, ns, 2) + + // Since disruptionAllowed starts out false, if we see it ever become true, + // that means the controller is working. + err := wait.PollImmediate(framework.Poll, 60*time.Second, func() (bool, error) { pdb, err := cs.Policy().PodDisruptionBudgets(ns).Get("foo") if err != nil { return false, err @@ -101,4 +63,110 @@ var _ = framework.KubeDescribe("DisruptionController [Feature:PodDisruptionbudge }) + It("should allow an eviction when there is no PDB", func() { + createPodsOrDie(cs, ns, 1) + + pod, err := cs.Pods(ns).Get("pod-0") + Expect(err).NotTo(HaveOccurred()) + + e := &policy.Eviction{ + ObjectMeta: api.ObjectMeta{ + Name: pod.Name, + Namespace: ns, + }, + } + + err = cs.Pods(ns).Evict(e) + Expect(err).NotTo(HaveOccurred()) + }) + + It("should not allow an eviction when too few pods", func() { + createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2)) + + createPodsOrDie(cs, ns, 1) + + pod, err := cs.Pods(ns).Get("pod-0") + Expect(err).NotTo(HaveOccurred()) + + e := &policy.Eviction{ + ObjectMeta: api.ObjectMeta{ + Name: pod.Name, + Namespace: ns, + }, + } + + // Since disruptionAllowed starts out false, wait at least 60s hoping that + // this gives the controller enough time to have truly set the status. + time.Sleep(60 * time.Second) + + err = cs.Pods(ns).Evict(e) + Expect(err).Should(MatchError("Cannot evict pod as it would violate the pod's disruption budget.")) + }) + + It("should allow an eviction when enough pods", func() { + createPodDisruptionBudgetOrDie(cs, ns, intstr.FromInt(2)) + + createPodsOrDie(cs, ns, 2) + + pod, err := cs.Pods(ns).Get("pod-0") + Expect(err).NotTo(HaveOccurred()) + + e := &policy.Eviction{ + ObjectMeta: api.ObjectMeta{ + Name: pod.Name, + Namespace: ns, + }, + } + + // Since disruptionAllowed starts out false, if an eviction is ever allowed, + // that means the controller is working. + err = wait.PollImmediate(framework.Poll, 60*time.Second, func() (bool, error) { + err = cs.Pods(ns).Evict(e) + if err != nil { + return false, nil + } else { + return true, nil + } + }) + Expect(err).NotTo(HaveOccurred()) + }) }) + +func createPodDisruptionBudgetOrDie(cs *release_1_4.Clientset, ns string, minAvailable intstr.IntOrString) { + pdb := policy.PodDisruptionBudget{ + ObjectMeta: api.ObjectMeta{ + Name: "foo", + Namespace: ns, + }, + Spec: policy.PodDisruptionBudgetSpec{ + Selector: &unversioned.LabelSelector{MatchLabels: map[string]string{"foo": "bar"}}, + MinAvailable: minAvailable, + }, + } + _, err := cs.Policy().PodDisruptionBudgets(ns).Create(&pdb) + Expect(err).NotTo(HaveOccurred()) +} + +func createPodsOrDie(cs *release_1_4.Clientset, ns string, n int) { + for i := 0; i < n; i++ { + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + Name: fmt.Sprintf("pod-%d", i), + Namespace: ns, + Labels: map[string]string{"foo": "bar"}, + }, + Spec: api.PodSpec{ + Containers: []api.Container{ + { + Name: "busybox", + Image: "gcr.io/google_containers/echoserver:1.4", + }, + }, + RestartPolicy: api.RestartPolicyAlways, + }, + } + + _, err := cs.Pods(ns).Create(pod) + framework.ExpectNoError(err, "Creating pod %q in namespace %q", pod.Name, ns) + } +}