Merge pull request #37109 from Random-Liu/fix-lifecycle-hook-test

Automatic merge from submit-queue

Use netexec container in http lifecycle hook test.

Fixes https://github.com/kubernetes/kubernetes/issues/33636.

The original test is using `"echo -e \"HTTP/1.1 200 OK\n\" | nc -l -p 1234` as a simple http server.

However, it seems that this is not very reliable, which may response before golang thinks it should.
So we get the error:
```
I1106 06:14:13.325397    2096 logs.go:41] Unsolicited response received on idle HTTP channel starting with "HTTP/1.1 200 OK\n\n"; err=<nil>
```

This PR changes the test to use the `netexec` container which is a simple http server written by golang and used in many of our networking e2e test. It should be more reliable.
Mark 1.5 since this is fixing a 1.5 release blocking issue. Mark P0 to match the original issue.

@dchen1107
This commit is contained in:
Kubernetes Submit Queue 2016-11-22 12:41:37 -08:00 committed by GitHub
commit e4724e8ab0
2 changed files with 147 additions and 74 deletions

View File

@ -18,6 +18,7 @@ package framework
import ( import (
"fmt" "fmt"
"regexp"
"sync" "sync"
"time" "time"
@ -173,3 +174,20 @@ func (c *PodClient) WaitForSuccess(name string, timeout time.Duration) {
}, },
)).To(Succeed(), "wait for pod %q to success", name) )).To(Succeed(), "wait for pod %q to success", name)
} }
// MatchContainerOutput gest output of a container and match expected regexp in the output.
func (c *PodClient) MatchContainerOutput(name string, containerName string, expectedRegexp string) error {
f := c.f
output, err := GetPodLogs(f.ClientSet, f.Namespace.Name, name, containerName)
if err != nil {
return fmt.Errorf("failed to get output for container %q of pod %q", containerName, name)
}
regex, err := regexp.Compile(expectedRegexp)
if err != nil {
return fmt.Errorf("failed to compile regexp %q: %v", expectedRegexp, err)
}
if !regex.MatchString(output) {
return fmt.Errorf("failed to match regexp %q in output %q", expectedRegexp, output)
}
return nil
}

View File

@ -26,16 +26,27 @@ import (
"k8s.io/kubernetes/test/e2e/framework" "k8s.io/kubernetes/test/e2e/framework"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
) )
var _ = framework.KubeDescribe("Container Lifecycle Hook", func() { var _ = framework.KubeDescribe("Container Lifecycle Hook", func() {
f := framework.NewDefaultFramework("container-lifecycle-hook") f := framework.NewDefaultFramework("container-lifecycle-hook")
var podClient *framework.PodClient var podClient *framework.PodClient
var file string const (
const podWaitTimeout = 2 * time.Minute podCheckInterval = 1 * time.Second
podWaitTimeout = 2 * time.Minute
postStartWaitTimeout = 2 * time.Minute
preStopWaitTimeout = 30 * time.Second
)
Context("when create a pod with lifecycle hook", func() {
BeforeEach(func() {
podClient = f.PodClient()
})
testPodWithHook := func(podWithHook *api.Pod) { Context("when it is exec hook", func() {
podCheckHook := getLifecycleHookTestPod("pod-check-hook", var file string
testPodWithExecHook := func(podWithHook *api.Pod) {
podCheckHook := getExecHookTestPod("pod-check-hook",
// Wait until the file is created. // Wait until the file is created.
[]string{"sh", "-c", fmt.Sprintf("while [ ! -e %s ]; do sleep 1; done", file)}, []string{"sh", "-c", fmt.Sprintf("while [ ! -e %s ]; do sleep 1; done", file)},
) )
@ -45,7 +56,7 @@ var _ = framework.KubeDescribe("Container Lifecycle Hook", func() {
By("create the hook check pod") By("create the hook check pod")
podClient.Create(podCheckHook) podClient.Create(podCheckHook)
By("wait for the hook check pod to success") By("wait for the hook check pod to success")
podClient.WaitForSuccess(podCheckHook.Name, podWaitTimeout) podClient.WaitForSuccess(podCheckHook.Name, postStartWaitTimeout)
} }
By("delete the pod with lifecycle hook") By("delete the pod with lifecycle hook")
podClient.DeleteSync(podWithHook.Name, api.NewDeleteOptions(15), podWaitTimeout) podClient.DeleteSync(podWithHook.Name, api.NewDeleteOptions(15), podWaitTimeout)
@ -53,26 +64,23 @@ var _ = framework.KubeDescribe("Container Lifecycle Hook", func() {
By("create the hook check pod") By("create the hook check pod")
podClient.Create(podCheckHook) podClient.Create(podCheckHook)
By("wait for the prestop check pod to success") By("wait for the prestop check pod to success")
podClient.WaitForSuccess(podCheckHook.Name, podWaitTimeout) podClient.WaitForSuccess(podCheckHook.Name, preStopWaitTimeout)
} }
} }
Context("when create a pod with lifecycle hook", func() {
BeforeEach(func() { BeforeEach(func() {
podClient = f.PodClient()
file = "/tmp/test-" + string(uuid.NewUUID()) file = "/tmp/test-" + string(uuid.NewUUID())
}) })
AfterEach(func() { AfterEach(func() {
By("cleanup the temporary file created in the test.") By("cleanup the temporary file created in the test.")
cleanupPod := getLifecycleHookTestPod("pod-clean-up", []string{"rm", file}) cleanupPod := getExecHookTestPod("pod-clean-up", []string{"rm", file})
podClient.Create(cleanupPod) podClient.Create(cleanupPod)
podClient.WaitForSuccess(cleanupPod.Name, podWaitTimeout) podClient.WaitForSuccess(cleanupPod.Name, podWaitTimeout)
}) })
Context("when it is exec hook", func() {
It("should execute poststart exec hook properly [Conformance]", func() { It("should execute poststart exec hook properly [Conformance]", func() {
podWithHook := getLifecycleHookTestPod("pod-with-poststart-exec-hook", podWithHook := getExecHookTestPod("pod-with-poststart-exec-hook",
// Block forever // Block forever
[]string{"tail", "-f", "/dev/null"}, []string{"tail", "-f", "/dev/null"},
) )
@ -81,11 +89,11 @@ var _ = framework.KubeDescribe("Container Lifecycle Hook", func() {
Exec: &api.ExecAction{Command: []string{"touch", file}}, Exec: &api.ExecAction{Command: []string{"touch", file}},
}, },
} }
testPodWithHook(podWithHook) testPodWithExecHook(podWithHook)
}) })
It("should execute prestop exec hook properly [Conformance]", func() { It("should execute prestop exec hook properly [Conformance]", func() {
podWithHook := getLifecycleHookTestPod("pod-with-prestop-exec-hook", podWithHook := getExecHookTestPod("pod-with-prestop-exec-hook",
// Block forever // Block forever
[]string{"tail", "-f", "/dev/null"}, []string{"tail", "-f", "/dev/null"},
) )
@ -94,64 +102,111 @@ var _ = framework.KubeDescribe("Container Lifecycle Hook", func() {
Exec: &api.ExecAction{Command: []string{"touch", file}}, Exec: &api.ExecAction{Command: []string{"touch", file}},
}, },
} }
testPodWithHook(podWithHook) testPodWithExecHook(podWithHook)
}) })
}) })
Context("when it is http hook", func() { Context("when it is http hook", func() {
var targetIP string var targetIP string
BeforeEach(func() { podHandleHookRequest := &api.Pod{
By("cleanup the container to handle the HTTPGet hook request.") ObjectMeta: api.ObjectMeta{
podHandleHookRequest := getLifecycleHookTestPod("pod-handle-http-request", Name: "pod-handle-http-request",
[]string{"sh", "-c",
// Create test file when receive request on 1234.
fmt.Sprintf("echo -e \"HTTP/1.1 200 OK\n\" | nc -l -p 1234; touch %s", file),
}, },
) Spec: api.PodSpec{
podHandleHookRequest.Spec.Containers[0].Ports = []api.ContainerPort{ Containers: []api.Container{
{ {
ContainerPort: 1234, Name: "pod-handle-http-request",
Image: "gcr.io/google_containers/netexec:1.7",
Ports: []api.ContainerPort{
{
ContainerPort: 8080,
Protocol: api.ProtocolTCP, Protocol: api.ProtocolTCP,
}, },
},
},
},
},
} }
podHandleHookRequest = podClient.CreateSync(podHandleHookRequest) BeforeEach(func() {
targetIP = podHandleHookRequest.Status.PodIP By("create the container to handle the HTTPGet hook request.")
newPod := podClient.CreateSync(podHandleHookRequest)
targetIP = newPod.Status.PodIP
}) })
testPodWithHttpHook := func(podWithHook *api.Pod) {
By("create the pod with lifecycle hook")
podClient.CreateSync(podWithHook)
if podWithHook.Spec.Containers[0].Lifecycle.PostStart != nil {
By("check poststart hook")
Eventually(func() error {
return podClient.MatchContainerOutput(podHandleHookRequest.Name, podHandleHookRequest.Spec.Containers[0].Name,
`GET /echo\?msg=poststart`)
}, postStartWaitTimeout, podCheckInterval).Should(BeNil())
}
By("delete the pod with lifecycle hook")
podClient.DeleteSync(podWithHook.Name, api.NewDeleteOptions(15), podWaitTimeout)
if podWithHook.Spec.Containers[0].Lifecycle.PreStop != nil {
By("check prestop hook")
Eventually(func() error {
return podClient.MatchContainerOutput(podHandleHookRequest.Name, podHandleHookRequest.Spec.Containers[0].Name,
`GET /echo\?msg=prestop`)
}, preStopWaitTimeout, podCheckInterval).Should(BeNil())
}
}
It("should execute poststart http hook properly [Conformance]", func() { It("should execute poststart http hook properly [Conformance]", func() {
podWithHook := getLifecycleHookTestPod("pod-with-poststart-http-hook", podWithHook := &api.Pod{
// Block forever ObjectMeta: api.ObjectMeta{
[]string{"tail", "-f", "/dev/null"}, Name: "pod-with-poststart-http-hook",
) },
podWithHook.Spec.Containers[0].Lifecycle = &api.Lifecycle{ Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "pod-with-poststart-http-hook",
Image: framework.GetPauseImageNameForHostArch(),
Lifecycle: &api.Lifecycle{
PostStart: &api.Handler{ PostStart: &api.Handler{
HTTPGet: &api.HTTPGetAction{ HTTPGet: &api.HTTPGetAction{
Path: "/echo?msg=poststart",
Host: targetIP, Host: targetIP,
Port: intstr.FromInt(1234), Port: intstr.FromInt(8080),
},
},
},
},
}, },
}, },
} }
testPodWithHook(podWithHook) testPodWithHttpHook(podWithHook)
}) })
It("should execute prestop http hook properly [Conformance]", func() { It("should execute prestop http hook properly [Conformance]", func() {
podWithHook := getLifecycleHookTestPod("pod-with-prestop-http-hook", podWithHook := &api.Pod{
// Block forever ObjectMeta: api.ObjectMeta{
[]string{"tail", "-f", "/dev/null"}, Name: "pod-with-prestop-http-hook",
) },
podWithHook.Spec.Containers[0].Lifecycle = &api.Lifecycle{ Spec: api.PodSpec{
Containers: []api.Container{
{
Name: "pod-with-prestop-http-hook",
Image: framework.GetPauseImageNameForHostArch(),
Lifecycle: &api.Lifecycle{
PreStop: &api.Handler{ PreStop: &api.Handler{
HTTPGet: &api.HTTPGetAction{ HTTPGet: &api.HTTPGetAction{
Path: "/echo?msg=prestop",
Host: targetIP, Host: targetIP,
Port: intstr.FromInt(1234), Port: intstr.FromInt(8080),
},
},
},
},
}, },
}, },
} }
testPodWithHook(podWithHook) testPodWithHttpHook(podWithHook)
}) })
}) })
}) })
}) })
func getLifecycleHookTestPod(name string, cmd []string) *api.Pod { func getExecHookTestPod(name string, cmd []string) *api.Pod {
return &api.Pod{ return &api.Pod{
ObjectMeta: api.ObjectMeta{ ObjectMeta: api.ObjectMeta{
Name: name, Name: name,