fix(e2e): create a generic cmd wrapper

- Add a wait function
pull/606/head
Adam Reese 10 years ago
parent fbf1fb64f6
commit a8582dbc97

@ -0,0 +1,79 @@
// build +e2e
package e2e
import (
"bytes"
"fmt"
"os/exec"
"regexp"
"strings"
"testing"
)
type Cmd struct {
t *testing.T
path string
args []string
ran bool
status error
stdout, stderr bytes.Buffer
}
func (h *Cmd) String() string {
return fmt.Sprintf("%s %s", h.path, strings.Join(h.args, " "))
}
func (h *Cmd) exec() error {
cmd := exec.Command(h.path, h.args...)
h.stdout.Reset()
h.stderr.Reset()
cmd.Stdout = &h.stdout
cmd.Stderr = &h.stderr
h.t.Logf("Executing command: %s", h)
h.status = cmd.Run()
if h.stdout.Len() > 0 {
h.t.Logf("standard output:\n%s", h.stdout.String())
}
if h.stderr.Len() > 0 {
h.t.Log("standard error: %s\n", h.stderr.String())
}
h.ran = true
return h.status
}
// Stdout returns standard output of the Cmd run as a string.
func (h *Cmd) Stdout() string {
if !h.ran {
h.t.Fatal("internal testsuite error: stdout called before run")
}
return h.stdout.String()
}
// Stderr returns standard error of the Cmd run as a string.
func (h *Cmd) Stderr() string {
if !h.ran {
h.t.Fatal("internal testsuite error: stdout called before run")
}
return h.stderr.String()
}
func (c *Cmd) Match(exp string) bool {
re := regexp.MustCompile(exp)
return re.MatchString(c.Stdout())
}
func (h *Cmd) StdoutContains(substring string) bool {
return strings.Contains(h.Stdout(), substring)
}
func (h *Cmd) StderrContains(substring string) bool {
return strings.Contains(h.Stderr(), substring)
}
func (h *Cmd) Contains(substring string) bool {
return h.StdoutContains(substring) || h.StderrContains(substring)
}

@ -3,12 +3,10 @@
package e2e package e2e
import ( import (
"bytes" "net/http"
"os" "os"
"os/exec"
"path" "path"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"time" "time"
) )
@ -33,88 +31,50 @@ func NewHelmContext(t *testing.T) *HelmContext {
} }
} }
func (h *HelmContext) MustRun(args ...string) *HelmCmd { func (h *HelmContext) MustRun(args ...string) *Cmd {
cmd := h.newCmd() cmd := h.newCmd(args...)
if status := cmd.exec(args...); status != nil { if status := cmd.exec(); status != nil {
h.t.Fatalf("helm %v failed unexpectedly: %v", args, status) h.t.Errorf("helm %v failed unexpectedly: %v", args, status)
h.t.Errorf("%s", cmd.Stderr())
h.t.FailNow()
} }
return cmd return cmd
} }
func (h *HelmContext) Run(args ...string) *HelmCmd { func (h *HelmContext) Run(args ...string) *Cmd {
cmd := h.newCmd() cmd := h.newCmd(args...)
cmd.exec(args...) cmd.exec()
return cmd return cmd
} }
func (h *HelmContext) RunFail(args ...string) *HelmCmd { func (h *HelmContext) RunFail(args ...string) *Cmd {
cmd := h.newCmd() cmd := h.newCmd(args...)
if status := cmd.exec(args...); status == nil { if status := cmd.exec(); status == nil {
h.t.Fatalf("helm unexpected to fail: %v", args, status) h.t.Fatalf("helm unexpected to fail: %v", args, status)
} }
return cmd return cmd
} }
func (h *HelmContext) newCmd() *HelmCmd { func (h *HelmContext) newCmd(args ...string) *Cmd {
return &HelmCmd{ args = append([]string{"--host", h.Host}, args...)
ctx: h, return &Cmd{
t: h.t,
path: h.Path,
args: args,
} }
} }
type HelmCmd struct { func (h *HelmContext) Running() bool {
ctx *HelmContext endpoint := h.Host + "healthz"
path string
ran bool
status error
stdout, stderr bytes.Buffer
}
func (h *HelmCmd) exec(args ...string) error { resp, err := http.Get(endpoint)
args = append([]string{"--host", h.ctx.Host}, args...) if err != nil {
cmd := exec.Command(h.ctx.Path, args...) h.t.Errorf("Could not GET %s: %s", endpoint, err)
h.stdout.Reset()
h.stderr.Reset()
cmd.Stdout = &h.stdout
cmd.Stderr = &h.stderr
h.status = cmd.Run()
if h.stdout.Len() > 0 {
h.ctx.t.Log("standard output:")
h.ctx.t.Log(h.stdout.String())
}
if h.stderr.Len() > 0 {
h.ctx.t.Log("standard error:")
h.ctx.t.Log(h.stderr.String())
} }
h.ran = true return resp.StatusCode == 200
return h.status
}
// Stdout returns standard output of the helmCmd run as a string.
func (h *HelmCmd) Stdout() string {
if !h.ran {
h.ctx.t.Fatal("internal testsuite error: stdout called before run")
}
return h.stdout.String()
}
// Stderr returns standard error of the helmCmd run as a string.
func (h *HelmCmd) Stderr() string {
if !h.ran {
h.ctx.t.Fatal("internal testsuite error: stdout called before run")
}
return h.stderr.String()
}
func (h *HelmCmd) StdoutContains(substring string) bool {
return strings.Contains(h.Stdout(), substring)
}
func (h *HelmCmd) StderrContains(substring string) bool {
return strings.Contains(h.Stderr(), substring)
}
func (h *HelmCmd) Contains(substring string) bool { //out := h.MustRun("server", "status").Stdout()
return h.StdoutContains(substring) || h.StderrContains(substring) //return strings.Count(out, "Running") == 5
} }
func RepoRoot() string { func RepoRoot() string {

@ -7,7 +7,6 @@ import (
"fmt" "fmt"
"math/rand" "math/rand"
"os" "os"
"strings"
"testing" "testing"
"time" "time"
) )
@ -32,34 +31,43 @@ var (
managerImage = "quay.io/adamreese/manager:latest" managerImage = "quay.io/adamreese/manager:latest"
) )
func logKubeEnv(k *KubeContext) {
config := k.Run("config", "view", "--flattend", "--minified").Stdout()
k.t.Logf("Kubernetes Environment\n%s", config)
}
func TestHelm(t *testing.T) { func TestHelm(t *testing.T) {
kube := NewKubeContext() kube := NewKubeContext(t)
helm := NewHelmContext(t) helm := NewHelmContext(t)
t.Logf("Kubenetes context: %s", kube.CurrentContext()) logKubeEnv(kube)
t.Logf("Cluster: %s", kube.Cluster())
t.Logf("Server: %s", kube.Server())
if !kube.Running() { if !kube.Running() {
t.Fatal("Not connected to kubernetes") t.Fatal("Not connected to kubernetes")
} }
t.Log(kube.Version()) t.Log(kube.Version())
//TODO: skip check if running local binaries
if !helmRunning(helm) {
t.Error("Helm is not installed")
helm.MustRun("server", "install", "--resourcifier-image", resourcifierImage, "--expandybird-image", expandybirdImage, "--manager-image", managerImage)
//TODO: wait for pods to be ready
}
helm.Host = helmHost() helm.Host = helmHost()
if helm.Host == "" { if helm.Host == "" {
helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy) helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy)
} }
t.Logf("Using host: %v", helm.Host) t.Logf("Using host: %v", helm.Host)
//TODO: skip check if running local binaries
if !helm.Running() {
t.Error("Helm is not installed")
helm.MustRun(
"server",
"install",
"--resourcifier-image", resourcifierImage,
"--expandybird-image", expandybirdImage,
"--manager-image", managerImage,
)
wait(func() bool {
return helm.Running()
})
}
// Add repo if it does not exsit // Add repo if it does not exsit
if !helm.MustRun("repo", "list").Contains(*repoURL) { if !helm.MustRun("repo", "list").Contains(*repoURL) {
t.Logf("Adding repo %s %s", *repoName, *repoURL) t.Logf("Adding repo %s %s", *repoName, *repoURL)
@ -70,7 +78,19 @@ func TestHelm(t *testing.T) {
deploymentName := genName() deploymentName := genName()
t.Log("Executing deploy") t.Log("Executing deploy")
helm.MustRun("deploy", "--properties", "container_port=6379,image=kubernetes/redis:v1,replicas=2", "--name", deploymentName, *chart) helm.MustRun("deploy",
"--properties", "namespace=e2e",
"--name", deploymentName,
*chart,
)
err := wait(func() bool {
return kube.Run("get", "pods").Match("redis.*Running")
})
if err != nil {
t.Fatal(err)
}
t.Log(kube.Run("get", "pods").Stdout())
t.Log("Executing deployment list") t.Log("Executing deployment list")
if !helm.MustRun("deployment", "list").Contains(deploymentName) { if !helm.MustRun("deployment", "list").Contains(deploymentName) {
@ -82,12 +102,27 @@ func TestHelm(t *testing.T) {
t.Fatal("Could not deploy") t.Fatal("Could not deploy")
} }
t.Log("Executing deployment describe")
helm.MustRun("deployment", "describe", deploymentName)
t.Log("Executing deployment delete") t.Log("Executing deployment delete")
if !helm.MustRun("deployment", "rm", deploymentName).Contains("Deleted") { if !helm.MustRun("deployment", "rm", deploymentName).Contains("Deleted") {
t.Fatal("Could not delete deployment") t.Fatal("Could not delete deployment")
} }
} }
func wait(fn func() bool) error {
for start := time.Now(); time.Since(start) < timeout; time.Sleep(poll) {
if fn() {
continue
}
}
if !fn() {
return fmt.Errorf("Polling timeout")
}
return nil
}
func genName() string { func genName() string {
return fmt.Sprintf("e2e-%d", rand.Uint32()) return fmt.Sprintf("e2e-%d", rand.Uint32())
} }
@ -98,8 +133,3 @@ func helmHost() string {
} }
return os.Getenv("HELM_HOST") return os.Getenv("HELM_HOST")
} }
func helmRunning(h *HelmContext) bool {
out := h.MustRun("server", "status").Stdout()
return strings.Count(out, "Running") == 5
}

@ -3,43 +3,59 @@
package e2e package e2e
import ( import (
"os/exec"
"strings" "strings"
"testing"
) )
const defaultKubectlPath = "kubectl" const defaultKubectlPath = "kubectl"
type KubeContext struct { type KubeContext struct {
t *testing.T
Path string Path string
} }
func NewKubeContext() *KubeContext { func NewKubeContext(t *testing.T) *KubeContext {
return &KubeContext{ return &KubeContext{
t: t,
Path: defaultKubectlPath, Path: defaultKubectlPath,
} }
} }
func (k *KubeContext) Run(args ...string) *Cmd {
cmd := k.newCmd(args...)
cmd.exec()
return cmd
}
func (k *KubeContext) newCmd(args ...string) *Cmd {
return &Cmd{
t: k.t,
path: k.Path,
args: args,
}
}
func (k *KubeContext) getConfigValue(jsonpath string) string {
return strings.Replace(k.Run("config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath="+jsonpath).Stdout(), "'", "", -1)
}
func (k *KubeContext) Cluster() string { func (k *KubeContext) Cluster() string {
out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].name}'").Output() return k.getConfigValue("'{.clusters[0].name}'")
return string(out)
} }
func (k *KubeContext) Server() string { func (k *KubeContext) Server() string {
out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].cluster.server}'").Output() return k.getConfigValue("'{.clusters[0].cluster.server}'")
return strings.Replace(string(out), "'", "", -1)
} }
func (k *KubeContext) CurrentContext() string { func (k *KubeContext) CurrentContext() string {
out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.current-context}'").Output() return k.getConfigValue("'{.current-context}'")
return string(out)
} }
func (k *KubeContext) Running() bool { func (k *KubeContext) Running() bool {
_, err := exec.Command(k.Path, "cluster-info").CombinedOutput() err := k.Run("cluster-info").exec()
return err == nil return err == nil
} }
func (k *KubeContext) Version() string { func (k *KubeContext) Version() string {
out, _ := exec.Command(k.Path, "version").Output() return k.Run("version").Stdout()
return string(out)
} }

Loading…
Cancel
Save