From 7d41164931b2bca0673d1ba9f39e5a0eb1b2bbb4 Mon Sep 17 00:00:00 2001 From: liangchenye Date: Mon, 1 Feb 2016 21:45:24 +0800 Subject: [PATCH 1/2] blackbox test: list, create, remove Signed-off-by: liangchenye --- test/e2e_node/conformance_test.go | 102 ++++++++++++++++++++++++ test/e2e_node/container.go | 128 ++++++++++++++++++++++++++++++ 2 files changed, 230 insertions(+) create mode 100644 test/e2e_node/conformance_test.go create mode 100644 test/e2e_node/container.go diff --git a/test/e2e_node/conformance_test.go b/test/e2e_node/conformance_test.go new file mode 100644 index 00000000000..d62a708f870 --- /dev/null +++ b/test/e2e_node/conformance_test.go @@ -0,0 +1,102 @@ +/* +Copyright 2015 The Kubernetes Authors 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 e2e_node + +import ( + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "k8s.io/kubernetes/pkg/api" + client "k8s.io/kubernetes/pkg/client/unversioned" +) + +var _ = Describe("Container Conformance Test", func() { + var cl *client.Client + + BeforeEach(func() { + // Setup the apiserver client + cl = client.NewOrDie(&client.Config{Host: *apiServerAddress}) + }) + + Describe("container conformance blackbox test", func() { + Context("when running a container that terminates", func() { + var terminateCase ConformanceContainer + BeforeEach(func() { + terminateCase = ConformanceContainer{ + Container: api.Container{ + Image: "gcc.io/google_testcontainers/busybox", + Name: "busybox", + Command: []string{"echo", "'Hello World'"}, + ImagePullPolicy: api.PullIfNotPresent, + }, + Status: "terminated", + Client: cl, + } + }) + It("it should start successfully [Conformance]", func() { + err := terminateCase.Create() + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() bool { + return terminateCase.IsStarted() + }, time.Minute*1, time.Second*30).Should(BeTrue()) + }) + It("it should report its status as 'terminated' [Conformance]", func() { + ccontainer, err := terminateCase.Get() + Expect(err).NotTo(HaveOccurred()) + Expect(ccontainer).Should(CContainerEqual(terminateCase)) + }) + It("it should be possible to delete [Conformance]", func() { + err := terminateCase.Delete() + Expect(err).NotTo(HaveOccurred()) + }) + }) + Context("when running a container with invalid image", func() { + var invalidImageCase ConformanceContainer + BeforeEach(func() { + invalidImageCase = ConformanceContainer{ + Container: api.Container{ + Image: "foo.com/foo/foo", + Name: "foo", + Command: []string{"foo", "'Should not work'"}, + ImagePullPolicy: api.PullIfNotPresent, + }, + Status: "waiting", + Client: cl, + } + }) + It("it should not start successfully [Conformance]", func() { + err := invalidImageCase.Create() + Expect(err).NotTo(HaveOccurred()) + + Eventually(func() bool { + return invalidImageCase.IsStarted() + }, time.Minute*1, time.Second*30).ShouldNot(BeTrue()) + }) + It("it should report its status as 'waiting' [Conformance]", func() { + ccontainer, err := invalidImageCase.Get() + Expect(err).NotTo(HaveOccurred()) + Expect(ccontainer).Should(CContainerEqual(invalidImageCase)) + }) + It("it should be possible to delete [Conformance]", func() { + err := invalidImageCase.Delete() + Expect(err).NotTo(HaveOccurred()) + }) + }) + }) +}) diff --git a/test/e2e_node/container.go b/test/e2e_node/container.go new file mode 100644 index 00000000000..ac13ad93bd3 --- /dev/null +++ b/test/e2e_node/container.go @@ -0,0 +1,128 @@ +/* +Copyright 2015 The Kubernetes Authors 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 e2e_node + +import ( + "errors" + "fmt" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" + + "k8s.io/kubernetes/pkg/api" + client "k8s.io/kubernetes/pkg/client/unversioned" +) + +//One pod one container +type ConformanceContainer struct { + Container api.Container + Client *client.Client + Status string +} + +type ConformanceContainerEqualMatcher struct { + Expected interface{} +} + +func CContainerEqual(expected interface{}) types.GomegaMatcher { + return &ConformanceContainerEqualMatcher{ + Expected: expected, + } +} + +func (matcher *ConformanceContainerEqualMatcher) Match(actual interface{}) (bool, error) { + if actual == nil && matcher.Expected == nil { + return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") + } + val := api.Semantic.DeepDerivative(matcher.Expected, actual) + return val, nil +} + +func (matcher *ConformanceContainerEqualMatcher) FailureMessage(actual interface{}) (message string) { + return format.Message(actual, "to equal", matcher.Expected) +} + +func (matcher *ConformanceContainerEqualMatcher) NegatedFailureMessage(actual interface{}) (message string) { + return format.Message(actual, "not to equal", matcher.Expected) +} + +func (cc *ConformanceContainer) Create() error { + pod := &api.Pod{ + ObjectMeta: api.ObjectMeta{ + //Same with the container name + Name: cc.Container.Name, + Namespace: api.NamespaceDefault, + }, + Spec: api.PodSpec{ + NodeName: *nodeName, + RestartPolicy: api.RestartPolicyNever, + Containers: []api.Container{ + cc.Container, + }, + }, + } + + _, err := cc.Client.Pods(api.NamespaceDefault).Create(pod) + return err +} + +func (cc *ConformanceContainer) IsStarted() bool { + if ccontainer, err := cc.Get(); err != nil { + return false + } else if ccontainer.Status == "running" || ccontainer.Status == "terminated" { + return true + } else { + return false + } +} + +//Same with 'delete' +func (cc *ConformanceContainer) Stop() error { + return cc.Client.Pods(api.NamespaceDefault).Delete(cc.Container.Name, &api.DeleteOptions{}) +} + +func (cc *ConformanceContainer) Delete() error { + return cc.Client.Pods(api.NamespaceDefault).Delete(cc.Container.Name, &api.DeleteOptions{}) +} + +func (cc *ConformanceContainer) Get() (ConformanceContainer, error) { + pod, err := cc.Client.Pods(api.NamespaceDefault).Get(cc.Container.Name) + if err != nil { + return ConformanceContainer{}, err + } + + containers := pod.Spec.Containers + if containers == nil || len(containers) != 1 { + return ConformanceContainer{}, errors.New("Failed to get container") + } + + cstatuss := pod.Status.ContainerStatuses + if cstatuss == nil || len(cstatuss) != 1 { + return ConformanceContainer{}, errors.New("Failed to get container status") + } + + var status string + if cstatuss[0].State.Running != nil { + status = "running" + } else if cstatuss[0].State.Terminated != nil { + status = "terminated" + } else { + status = "waiting" + } + + return ConformanceContainer{containers[0], cc.Client, status}, nil +} From e3861cd6fcc352d9e8eaf0ec103684aefcd79021 Mon Sep 17 00:00:00 2001 From: liangchenye Date: Tue, 16 Feb 2016 17:49:23 +0800 Subject: [PATCH 2/2] use phase to test container status Signed-off-by: liangchenye --- test/e2e_node/conformance_test.go | 53 +++++++++++++++++++++---------- test/e2e_node/container.go | 40 +++++------------------ 2 files changed, 44 insertions(+), 49 deletions(-) diff --git a/test/e2e_node/conformance_test.go b/test/e2e_node/conformance_test.go index d62a708f870..eadaddce710 100644 --- a/test/e2e_node/conformance_test.go +++ b/test/e2e_node/conformance_test.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors 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. @@ -19,10 +19,15 @@ package e2e_node import ( "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +const ( + serviceCreateTimeout = 2 * time.Minute ) var _ = Describe("Container Conformance Test", func() { @@ -39,24 +44,31 @@ var _ = Describe("Container Conformance Test", func() { BeforeEach(func() { terminateCase = ConformanceContainer{ Container: api.Container{ - Image: "gcc.io/google_testcontainers/busybox", + Image: "gcr.io/google_containers/busybox:1.24", Name: "busybox", - Command: []string{"echo", "'Hello World'"}, + Command: []string{"sh", "-c", "env"}, ImagePullPolicy: api.PullIfNotPresent, }, - Status: "terminated", - Client: cl, + Client: cl, + Phase: api.PodSucceeded, + NodeName: *nodeName, } }) It("it should start successfully [Conformance]", func() { err := terminateCase.Create() Expect(err).NotTo(HaveOccurred()) - Eventually(func() bool { - return terminateCase.IsStarted() - }, time.Minute*1, time.Second*30).Should(BeTrue()) + phase := api.PodPending + for start := time.Now(); time.Since(start) < serviceCreateTimeout; time.Sleep(time.Second * 30) { + ccontainer, err := terminateCase.Get() + if err != nil || ccontainer.Phase != api.PodPending { + phase = ccontainer.Phase + break + } + } + Expect(phase).Should(Equal(terminateCase.Phase)) }) - It("it should report its status as 'terminated' [Conformance]", func() { + It("it should report its phase as 'succeeded' [Conformance]", func() { ccontainer, err := terminateCase.Get() Expect(err).NotTo(HaveOccurred()) Expect(ccontainer).Should(CContainerEqual(terminateCase)) @@ -76,19 +88,26 @@ var _ = Describe("Container Conformance Test", func() { Command: []string{"foo", "'Should not work'"}, ImagePullPolicy: api.PullIfNotPresent, }, - Status: "waiting", - Client: cl, + Client: cl, + Phase: api.PodPending, + NodeName: *nodeName, } }) It("it should not start successfully [Conformance]", func() { err := invalidImageCase.Create() Expect(err).NotTo(HaveOccurred()) - Eventually(func() bool { - return invalidImageCase.IsStarted() - }, time.Minute*1, time.Second*30).ShouldNot(BeTrue()) + phase := api.PodPending + for start := time.Now(); time.Since(start) < serviceCreateTimeout; time.Sleep(time.Second * 30) { + ccontainer, err := invalidImageCase.Get() + if err != nil || ccontainer.Phase != api.PodPending { + phase = ccontainer.Phase + break + } + } + Expect(phase).Should(Equal(invalidImageCase.Phase)) }) - It("it should report its status as 'waiting' [Conformance]", func() { + It("it should report its phase as 'pending' [Conformance]", func() { ccontainer, err := invalidImageCase.Get() Expect(err).NotTo(HaveOccurred()) Expect(ccontainer).Should(CContainerEqual(invalidImageCase)) diff --git a/test/e2e_node/container.go b/test/e2e_node/container.go index ac13ad93bd3..e5bf8febb6c 100644 --- a/test/e2e_node/container.go +++ b/test/e2e_node/container.go @@ -1,5 +1,5 @@ /* -Copyright 2015 The Kubernetes Authors All rights reserved. +Copyright 2016 The Kubernetes Authors 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. @@ -20,18 +20,19 @@ import ( "errors" "fmt" - "github.com/onsi/gomega/format" - "github.com/onsi/gomega/types" - "k8s.io/kubernetes/pkg/api" client "k8s.io/kubernetes/pkg/client/unversioned" + + "github.com/onsi/gomega/format" + "github.com/onsi/gomega/types" ) //One pod one container type ConformanceContainer struct { Container api.Container Client *client.Client - Status string + Phase api.PodPhase + NodeName string } type ConformanceContainerEqualMatcher struct { @@ -68,7 +69,7 @@ func (cc *ConformanceContainer) Create() error { Namespace: api.NamespaceDefault, }, Spec: api.PodSpec{ - NodeName: *nodeName, + NodeName: cc.NodeName, RestartPolicy: api.RestartPolicyNever, Containers: []api.Container{ cc.Container, @@ -80,16 +81,6 @@ func (cc *ConformanceContainer) Create() error { return err } -func (cc *ConformanceContainer) IsStarted() bool { - if ccontainer, err := cc.Get(); err != nil { - return false - } else if ccontainer.Status == "running" || ccontainer.Status == "terminated" { - return true - } else { - return false - } -} - //Same with 'delete' func (cc *ConformanceContainer) Stop() error { return cc.Client.Pods(api.NamespaceDefault).Delete(cc.Container.Name, &api.DeleteOptions{}) @@ -109,20 +100,5 @@ func (cc *ConformanceContainer) Get() (ConformanceContainer, error) { if containers == nil || len(containers) != 1 { return ConformanceContainer{}, errors.New("Failed to get container") } - - cstatuss := pod.Status.ContainerStatuses - if cstatuss == nil || len(cstatuss) != 1 { - return ConformanceContainer{}, errors.New("Failed to get container status") - } - - var status string - if cstatuss[0].State.Running != nil { - status = "running" - } else if cstatuss[0].State.Terminated != nil { - status = "terminated" - } else { - status = "waiting" - } - - return ConformanceContainer{containers[0], cc.Client, status}, nil + return ConformanceContainer{containers[0], cc.Client, pod.Status.Phase, cc.NodeName}, nil }