e2e test for kubectl exec, port-forward

pull/6/head
Jeff Lowdermilk 2015-06-30 09:43:37 -07:00
parent f0a36b0afd
commit 4b36b421aa
2 changed files with 103 additions and 0 deletions

View File

@ -20,7 +20,10 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"path/filepath"
"regexp"
"strings"
"time"
@ -42,6 +45,14 @@ const (
kubectlProxyPort = 8011
guestbookStartupTimeout = 10 * time.Minute
guestbookResponseTimeout = 3 * time.Minute
simplePodSelector = "name=nginx"
simplePodName = "nginx"
nginxDefaultOutput = "Welcome to nginx!"
simplePodPort = 80
)
var (
portForwardRegexp = regexp.MustCompile("Forwarding from 127.0.0.1:([0-9]+) -> 80")
)
var _ = Describe("Kubectl client", func() {
@ -127,8 +138,85 @@ var _ = Describe("Kubectl client", func() {
})
})
Describe("Simple pod", func() {
var podPath string
BeforeEach(func() {
podPath = filepath.Join(testContext.RepoRoot, "examples/pod.yaml")
By("creating the pod")
runKubectl("create", "-f", podPath, fmt.Sprintf("--namespace=%v", ns))
checkPodsRunningReady(c, ns, []string{simplePodName}, podStartTimeout)
})
AfterEach(func() {
cleanup(podPath, ns, simplePodSelector)
})
It("should support exec", func() {
By("executing a command in the container")
execOutput := runKubectl("exec", fmt.Sprintf("--namespace=%v", ns), simplePodName, "echo", "running", "in", "container")
expectedExecOutput := "running in container"
if execOutput != expectedExecOutput {
Failf("Unexpected kubectl exec output. Wanted '%s', got '%s'", execOutput, expectedExecOutput)
}
})
It("should support port-forward", func() {
By("forwarding the container port to a local port")
cmd := kubectlCmd("port-forward", fmt.Sprintf("--namespace=%v", ns), "-p", simplePodName, fmt.Sprintf(":%d", simplePodPort))
defer func() {
if err := cmd.Process.Kill(); err != nil {
Logf("ERROR failed to kill kubectl port-forward command! The process may leak")
}
}()
// This is somewhat ugly but is the only way to retrieve the port that was picked
// by the port-forward command. We don't want to hard code the port as we have no
// way of guaranteeing we can pick one that isn't in use, particularly on Jenkins.
Logf("starting port-forward command and streaming output")
stdout, stderr, err := startCmdAndStreamOutput(cmd)
if err != nil {
Failf("Failed to start port-forward command: %v", err)
}
defer stdout.Close()
defer stderr.Close()
buf := make([]byte, 128)
var n int
Logf("reading from `kubectl port-forward` command's stderr")
if n, err = stderr.Read(buf); err != nil {
Failf("Failed to read from kubectl port-forward stderr: %v", err)
}
portForwardOutput := string(buf[:n])
match := portForwardRegexp.FindStringSubmatch(portForwardOutput)
if len(match) != 2 {
Failf("Failed to parse kubectl port-forward output: %s", portForwardOutput)
}
By("curling local port output")
localAddr := fmt.Sprintf("http://localhost:%s", match[1])
body, err := curl(localAddr)
if err != nil {
Failf("Failed http.Get of forwarded port (%s): %v", localAddr, err)
}
if !strings.Contains(body, nginxDefaultOutput) {
Failf("Container port output missing expected value. Wanted:'%s', got: %s", nginxDefaultOutput, body)
}
})
})
})
func curl(addr string) (string, error) {
resp, err := http.Get(addr)
if err != nil {
return "", err
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(body[:]), nil
}
func validateGuestbookApp(c *client.Client, ns string) {
Logf("Waiting for frontend to serve content.")
if !waitForGuestbookResponse(c, "get", "", `{"data": ""}`, guestbookStartupTimeout, ns) {

View File

@ -19,6 +19,7 @@ package e2e
import (
"bytes"
"fmt"
"io"
"io/ioutil"
"math"
"math/rand"
@ -832,6 +833,20 @@ func runKubectl(args ...string) string {
return strings.TrimSpace(stdout.String())
}
func startCmdAndStreamOutput(cmd *exec.Cmd) (stdout, stderr io.ReadCloser, err error) {
stdout, err = cmd.StdoutPipe()
if err != nil {
return
}
stderr, err = cmd.StderrPipe()
if err != nil {
return
}
Logf("Asyncronously running '%s %s'", cmd.Path, strings.Join(cmd.Args, " "))
err = cmd.Start()
return
}
// testContainerOutput runs testContainerOutputInNamespace with the default namespace.
func testContainerOutput(scenarioName string, c *client.Client, pod *api.Pod, containerIndex int, expectedOutput []string) {
testContainerOutputInNamespace(scenarioName, c, pod, containerIndex, expectedOutput, api.NamespaceDefault)