Start e2e framework; print events

This commit is contained in:
Daniel Smith 2015-05-21 18:14:26 -07:00
parent 2a6cf783ec
commit 28b3224b72
4 changed files with 147 additions and 68 deletions

96
test/e2e/framework.go Normal file
View File

@ -0,0 +1,96 @@
/*
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
import (
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
// Framework supports common operations used by e2e tests; it will keep a client & a namespace for you.
// Eventual goal is to merge this with integration test framework.
type Framework struct {
BaseName string
Namespace *api.Namespace
Client *client.Client
}
// NewFramework makes a new framework and sets up a BeforeEach/AfterEach for
// you (you can write additional before/after each functions).
func NewFramework(baseName string) *Framework {
f := &Framework{
BaseName: baseName,
}
BeforeEach(f.beforeEach)
AfterEach(f.afterEach)
return f
}
// beforeEach gets a client and makes a namespace.
func (f *Framework) beforeEach() {
By("Creating a kubernetes client")
c, err := loadClient()
Expect(err).NotTo(HaveOccurred())
f.Client = c
By("Building a namespace api object")
namespace, err := createTestingNS(f.BaseName, f.Client)
Expect(err).NotTo(HaveOccurred())
f.Namespace = namespace
}
// afterEach deletes the namespace, after reading its events.
func (f *Framework) afterEach() {
// Print events if the test failed.
if CurrentGinkgoTestDescription().Failed {
By(fmt.Sprintf("Collecting events from namespace %q.", f.Namespace.Name))
events, err := f.Client.Events(f.Namespace.Name).List(labels.Everything(), fields.Everything())
Expect(err).NotTo(HaveOccurred())
for _, e := range events.Items {
Logf("event for %v: %v %v: %v", e.InvolvedObject.Name, e.Source, e.Reason, e.Message)
}
// Note that we don't wait for any cleanup to propagate, which means
// that if you delete a bunch of pods right before ending your test,
// you may or may not see the killing/deletion/cleanup events.
}
By(fmt.Sprintf("Destroying namespace %q for this suite.", f.Namespace.Name))
if err := f.Client.Namespaces().Delete(f.Namespace.Name); err != nil {
Failf("Couldn't delete ns %q: %s", f.Namespace.Name, err)
}
// Paranoia-- prevent reuse!
f.Namespace = nil
f.Client = nil
}
// WaitForPodRunning waits for the pod to run in the namespace.
func (f *Framework) WaitForPodRunning(podName string) error {
return waitForPodRunningInNamespace(f.Client, podName, f.Namespace.Name)
}

View File

@ -19,10 +19,10 @@ package e2e
import ( import (
"fmt" "fmt"
"net/http" "net/http"
"strings"
"time" "time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api" "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields" "github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels" "github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util" "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
@ -31,20 +31,17 @@ import (
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
) )
func LaunchNetTestPodPerNode(nodes *api.NodeList, name string, c *client.Client, ns string) []string { func LaunchNetTestPodPerNode(f *Framework, nodes *api.NodeList, name string) []string {
podNames := []string{} podNames := []string{}
totalPods := len(nodes.Items) totalPods := len(nodes.Items)
Expect(totalPods).NotTo(Equal(0)) Expect(totalPods).NotTo(Equal(0))
for i, node := range nodes.Items { for _, node := range nodes.Items {
podName := fmt.Sprintf("%s-%d", name, i) pod, err := f.Client.Pods(f.Namespace.Name).Create(&api.Pod{
podNames = append(podNames, podName)
Logf("Creating pod %s on node %s", podName, node.Name)
_, err := c.Pods(ns).Create(&api.Pod{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: podName, GenerateName: name + "-",
Labels: map[string]string{ Labels: map[string]string{
"name": name, "name": name,
}, },
@ -59,7 +56,7 @@ func LaunchNetTestPodPerNode(nodes *api.NodeList, name string, c *client.Client,
//peers >= totalPods should be asserted by the container. //peers >= totalPods should be asserted by the container.
//the nettest container finds peers by looking up list of svc endpoints. //the nettest container finds peers by looking up list of svc endpoints.
fmt.Sprintf("-peers=%d", totalPods), fmt.Sprintf("-peers=%d", totalPods),
"-namespace=" + ns}, "-namespace=" + f.Namespace.Name},
Ports: []api.ContainerPort{{ContainerPort: 8080}}, Ports: []api.ContainerPort{{ContainerPort: 8080}},
}, },
}, },
@ -68,16 +65,16 @@ func LaunchNetTestPodPerNode(nodes *api.NodeList, name string, c *client.Client,
}, },
}) })
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Logf("Created pod %s on node %s", pod.ObjectMeta.Name, node.Name)
podNames = append(podNames, pod.ObjectMeta.Name)
} }
return podNames return podNames
} }
var _ = Describe("Networking", func() { var _ = Describe("Networking", func() {
f := NewFramework("nettest")
//This namespace is modified throughout the course of the test.
var namespace *api.Namespace
var svcname = "nettest" var svcname = "nettest"
var c *client.Client = nil
BeforeEach(func() { BeforeEach(func() {
//Assert basic external connectivity. //Assert basic external connectivity.
@ -91,22 +88,6 @@ var _ = Describe("Networking", func() {
if resp.StatusCode != http.StatusOK { if resp.StatusCode != http.StatusOK {
Failf("Unexpected error code, expected 200, got, %v (%v)", resp.StatusCode, resp) Failf("Unexpected error code, expected 200, got, %v (%v)", resp.StatusCode, resp)
} }
By("Creating a kubernetes client")
c, err = loadClient()
Expect(err).NotTo(HaveOccurred())
By("Building a namespace api object")
namespace, err = createTestingNS("nettest", c)
Expect(err).NotTo(HaveOccurred())
})
AfterEach(func() {
By(fmt.Sprintf("Destroying namespace for this suite %v", namespace.Name))
if err := c.Namespaces().Delete(namespace.Name); err != nil {
Failf("Couldn't delete ns %s", err)
}
}) })
// First test because it has no dependencies on variables created later on. // First test because it has no dependencies on variables created later on.
@ -120,8 +101,8 @@ var _ = Describe("Networking", func() {
} }
for _, test := range tests { for _, test := range tests {
By(fmt.Sprintf("testing: %s", test.path)) By(fmt.Sprintf("testing: %s", test.path))
data, err := c.RESTClient.Get(). data, err := f.Client.RESTClient.Get().
Namespace(namespace.Name). Namespace(f.Namespace.Name).
AbsPath(test.path). AbsPath(test.path).
DoRaw() DoRaw()
if err != nil { if err != nil {
@ -139,8 +120,8 @@ var _ = Describe("Networking", func() {
return return
} }
By(fmt.Sprintf("Creating a service named [%s] in namespace %s", svcname, namespace.Name)) By(fmt.Sprintf("Creating a service named %q in namespace %q", svcname, f.Namespace.Name))
svc, err := c.Services(namespace.Name).Create(&api.Service{ svc, err := f.Client.Services(f.Namespace.Name).Create(&api.Service{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: svcname, Name: svcname,
Labels: map[string]string{ Labels: map[string]string{
@ -166,26 +147,26 @@ var _ = Describe("Networking", func() {
defer func() { defer func() {
defer GinkgoRecover() defer GinkgoRecover()
By("Cleaning up the service") By("Cleaning up the service")
if err = c.Services(namespace.Name).Delete(svc.Name); err != nil { if err = f.Client.Services(f.Namespace.Name).Delete(svc.Name); err != nil {
Failf("unable to delete svc %v: %v", svc.Name, err) Failf("unable to delete svc %v: %v", svc.Name, err)
} }
}() }()
By("Creating a webserver (pending) pod on each node") By("Creating a webserver (pending) pod on each node")
nodes, err := c.Nodes().List(labels.Everything(), fields.Everything()) nodes, err := f.Client.Nodes().List(labels.Everything(), fields.Everything())
if err != nil { if err != nil {
Failf("Failed to list nodes: %v", err) Failf("Failed to list nodes: %v", err)
} }
podNames := LaunchNetTestPodPerNode(nodes, svcname, c, namespace.Name) podNames := LaunchNetTestPodPerNode(f, nodes, svcname)
// Clean up the pods // Clean up the pods
defer func() { defer func() {
defer GinkgoRecover() defer GinkgoRecover()
By("Cleaning up the webserver pods") By("Cleaning up the webserver pods")
for _, podName := range podNames { for _, podName := range podNames {
if err = c.Pods(namespace.Name).Delete(podName, nil); err != nil { if err = f.Client.Pods(f.Namespace.Name).Delete(podName, nil); err != nil {
Logf("Failed to delete pod %s: %v", podName, err) Logf("Failed to delete pod %s: %v", podName, err)
} }
} }
@ -193,63 +174,67 @@ var _ = Describe("Networking", func() {
By("Waiting for the webserver pods to transition to Running state") By("Waiting for the webserver pods to transition to Running state")
for _, podName := range podNames { for _, podName := range podNames {
err = waitForPodRunningInNamespace(c, podName, namespace.Name) err = f.WaitForPodRunning(podName)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
} }
By("Waiting for connectivity to be verified") By("Waiting for connectivity to be verified")
const maxAttempts = 60
passed := false passed := false
//once response OK, evaluate response body for pass/fail. //once response OK, evaluate response body for pass/fail.
var body []byte var body []byte
getDetails := func() ([]byte, error) {
return f.Client.Get().
Namespace(f.Namespace.Name).
Prefix("proxy").
Resource("services").
Name(svc.Name).
Suffix("read").
DoRaw()
}
for i := 0; i < maxAttempts && !passed; i++ { getStatus := func() ([]byte, error) {
time.Sleep(2 * time.Second) return f.Client.Get().
Logf("About to make a proxy status call") Namespace(f.Namespace.Name).
start := time.Now()
body, err = c.Get().
Namespace(namespace.Name).
Prefix("proxy"). Prefix("proxy").
Resource("services"). Resource("services").
Name(svc.Name). Name(svc.Name).
Suffix("status"). Suffix("status").
DoRaw() DoRaw()
}
for i := 0; !passed; i++ { // Timeout will keep us from going forever.
time.Sleep(2 * time.Second)
Logf("About to make a proxy status call")
start := time.Now()
body, err = getStatus()
Logf("Proxy status call returned in %v", time.Since(start)) Logf("Proxy status call returned in %v", time.Since(start))
if err != nil { if err != nil {
Logf("Attempt %v/%v: service/pod still starting. (error: '%v')", i, maxAttempts, err) Logf("Attempt %v: service/pod still starting. (error: '%v')", i, err)
continue continue
} }
//Finally, we pass/fail the test based on if the container's response body, as to wether or not it was able to find peers. // Finally, we pass/fail the test based on if the container's response body, as to wether or not it was able to find peers.
switch string(body) { switch {
case "pass": case string(body) == "pass":
Logf("Passed on attempt %v. Cleaning up.", i) Logf("Passed on attempt %v. Cleaning up.", i)
passed = true passed = true
case "running": case string(body) == "running":
Logf("Attempt %v/%v: test still running", i, maxAttempts) Logf("Attempt %v: test still running", i)
case "fail": case string(body) == "fail":
if body, err = c.Get(). if body, err = getDetails(); err != nil {
Namespace(namespace.Name).Prefix("proxy").
Resource("services").
Name(svc.Name).Suffix("read").
DoRaw(); err != nil {
Failf("Failed on attempt %v. Cleaning up. Error reading details: %v", i, err) Failf("Failed on attempt %v. Cleaning up. Error reading details: %v", i, err)
} else { } else {
Failf("Failed on attempt %v. Cleaning up. Details:\n%s", i, string(body)) Failf("Failed on attempt %v. Cleaning up. Details:\n%s", i, string(body))
} }
case strings.Contains(string(body), "no endpoints available"):
Logf("Attempt %v: waiting on service/endpoints", i)
default: default:
Logf("Unexpected response: %q", body) Logf("Unexpected response:\n%s", body)
} }
} }
if !passed { if !passed {
if body, err = c.Get(). if body, err = getDetails(); err != nil {
Namespace(namespace.Name).
Prefix("proxy").
Resource("services").
Name(svc.Name).
Suffix("read").
DoRaw(); err != nil {
Failf("Timed out. Cleaning up. Error reading details: %v", err) Failf("Timed out. Cleaning up. Error reading details: %v", err)
} else { } else {
Failf("Timed out. Cleaning up. Details:\n%s", string(body)) Failf("Timed out. Cleaning up. Details:\n%s", string(body))

View File

@ -55,8 +55,7 @@ const (
) )
var _ = Describe("Reboot", func() { var _ = Describe("Reboot", func() {
var c *client.Client
var c *client.Client = nil
BeforeEach(func() { BeforeEach(func() {
var err error var err error

View File

@ -27,8 +27,7 @@ import (
) )
var _ = Describe("SSH", func() { var _ = Describe("SSH", func() {
var c *client.Client
var c *client.Client = nil
BeforeEach(func() { BeforeEach(func() {
var err error var err error