From 60bce486315b4b5763d2bd1e3282275f4c171b8e Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 31 Mar 2016 16:58:20 -0700 Subject: [PATCH 01/26] feat(e2e): starting framework for e2e tests --- test/e2e/helm.go | 75 +++++++++++++++++++++++++++++++++++++++++++ test/e2e/helm_test.go | 34 ++++++++++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 test/e2e/helm.go create mode 100644 test/e2e/helm_test.go diff --git a/test/e2e/helm.go b/test/e2e/helm.go new file mode 100644 index 000000000..7b0e39fa9 --- /dev/null +++ b/test/e2e/helm.go @@ -0,0 +1,75 @@ +package e2e + +import ( + "bytes" + "os" + "os/exec" + "path" + "path/filepath" + "testing" +) + +type HelmContext struct { + t *testing.T + Path string + Host string +} + +func NewHelmContext(t *testing.T) *HelmContext { + return &HelmContext{ + t: t, + Path: RepoRoot() + "/bin/helm", + } +} + +func (h *HelmContext) Run(args ...string) *HelmCmd { + cmd := h.newCmd() + if status := cmd.exec(args...); status != nil { + h.t.Fatalf("helm %v failed unexpectedly: %v", args, status) + } + return cmd +} + +func (h *HelmContext) newCmd() *HelmCmd { + return &HelmCmd{ + ctx: h, + } +} + +type HelmCmd struct { + ctx *HelmContext + path string + ran bool + stdout, stderr bytes.Buffer +} + +func (h *HelmCmd) exec(args ...string) error { + cmd := exec.Command(h.ctx.Path, args...) + h.stdout.Reset() + h.stderr.Reset() + cmd.Stdout = &h.stdout + cmd.Stderr = &h.stderr + status := cmd.Run() + h.ran = true + return 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 RepoRoot() string { + return filepath.Clean(filepath.Join(path.Base(os.Args[0]), "../../..")) +} diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go new file mode 100644 index 000000000..5c474796d --- /dev/null +++ b/test/e2e/helm_test.go @@ -0,0 +1,34 @@ +package e2e + +import ( + "os/exec" + "strings" + "testing" +) + +func TestHelm(t *testing.T) { + if !kubeRunning() { + t.Fatal("Not connected to kubernetes") + } + + helm := NewHelmContext(t) + if !helmRunning(helm) { + t.Fatal("Helm is not installed") + } + + // Setup helm host + + // Run deploy + + // Test deployment +} + +func helmRunning(h *HelmContext) bool { + out := h.Run("server", "status").Stdout() + return strings.Count(out, "Running") == 5 +} + +func kubeRunning() bool { + _, err := exec.Command("kubectl", "cluster-info").CombinedOutput() + return err == nil +} From 4c30e6aecffd9ee418b43b68245fd2e2e36100a8 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 31 Mar 2016 22:41:20 -0700 Subject: [PATCH 02/26] feat(e2e): add test scenario --- test/e2e/helm.go | 14 ++++++++ test/e2e/helm_test.go | 46 +++++++++++++++++++------- test/e2e/kubernetes.go | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 11 deletions(-) create mode 100644 test/e2e/kubernetes.go diff --git a/test/e2e/helm.go b/test/e2e/helm.go index 7b0e39fa9..aa527de77 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -9,6 +9,11 @@ import ( "testing" ) +const ( + namespace = "dm" + apiProxy = "/api/v1/proxy/namespaces/" + namespace + "/services/manager-service:manager/" +) + type HelmContext struct { t *testing.T Path string @@ -44,12 +49,21 @@ type HelmCmd struct { } func (h *HelmCmd) exec(args ...string) error { + args = append([]string{"--host", h.ctx.Host}, args...) cmd := exec.Command(h.ctx.Path, args...) h.stdout.Reset() h.stderr.Reset() cmd.Stdout = &h.stdout cmd.Stderr = &h.stderr 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 status } diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 5c474796d..ce4f40aee 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -1,34 +1,58 @@ package e2e import ( - "os/exec" + "fmt" + "math/rand" "strings" "testing" + "time" ) +func init() { + rand.Seed(time.Now().Unix()) +} + +var chart = "gs://areese-charts/replicatedservice-3.tgz" + func TestHelm(t *testing.T) { - if !kubeRunning() { + kube := NewKubeContext() + helm := NewHelmContext(t) + + t.Log(kube.CurrentContext()) + t.Log(kube.Cluster()) + t.Log(kube.Server()) + + if !kube.Running() { t.Fatal("Not connected to kubernetes") } - helm := NewHelmContext(t) if !helmRunning(helm) { t.Fatal("Helm is not installed") } - // Setup helm host + helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy) + t.Logf("Using host: %v", helm.Host) - // Run deploy + t.Log("Executing deployment list") + helm.Run("deployment", "list") - // Test deployment + deploymentName := genName() + + t.Log("Executing deploy") + helm.Run("deploy", "--name", deploymentName, chart) + + t.Log("Executing deployment list") + helm.Run("deployment", "list") + + t.Log("Executing deployment delete") + helm.Run("deployment", "delete", deploymentName) +} + +func genName() string { + return fmt.Sprintf("%d", rand.Uint32()) } func helmRunning(h *HelmContext) bool { out := h.Run("server", "status").Stdout() return strings.Count(out, "Running") == 5 } - -func kubeRunning() bool { - _, err := exec.Command("kubectl", "cluster-info").CombinedOutput() - return err == nil -} diff --git a/test/e2e/kubernetes.go b/test/e2e/kubernetes.go new file mode 100644 index 000000000..bb15e839b --- /dev/null +++ b/test/e2e/kubernetes.go @@ -0,0 +1,74 @@ +package e2e + +import ( + "encoding/json" + "fmt" + "os/exec" + "strings" +) + +const defaultKubectlPath = "kubectl" + +type KubeContext struct { + Path string + Config *Config +} + +func NewKubeContext() *KubeContext { + return &KubeContext{ + Path: defaultKubectlPath, + } +} + +type Config struct { + Clusters []struct { + // Name is the nickname for this Cluster + Name string `json:"name"` + // Cluster holds the cluster information + Cluster struct { + // Server is the address of the kubernetes cluster (https://hostname:port). + Server string `json:"server"` + // APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). + APIVersion string `json:"api-version,omitempty"` + // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. + InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` + // CertificateAuthority is the path to a cert file for the certificate authority. + CertificateAuthority string `json:"certificate-authority,omitempty"` + // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority + CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"` + // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields + Extensions []struct { + // Name is the nickname for this Extension + Name string `json:"name"` + } `json:"extensions,omitempty"` + } `json:"cluster"` + } +} + +func (k *KubeContext) ParseConfig() { + out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o=json").Output() + err := json.Unmarshal(out, &k.Config) + if err != nil { + fmt.Println(err.Error()) + } +} + +func (k *KubeContext) Cluster() string { + out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].name}'").Output() + return string(out) +} + +func (k *KubeContext) Server() string { + out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].cluster.server}'").Output() + return strings.Replace(string(out), "'", "", -1) +} + +func (k *KubeContext) CurrentContext() string { + out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.current-context}'").Output() + return string(out) +} + +func (k *KubeContext) Running() bool { + _, err := exec.Command(k.Path, "cluster-info").CombinedOutput() + return err == nil +} From b60c3e7ab1b0c38621c4e50115133ff82cc8ed99 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 08:58:40 -0700 Subject: [PATCH 03/26] feat(e2e): add build flags --- test/e2e/helm.go | 2 ++ test/e2e/helm_test.go | 2 ++ test/e2e/kubernetes.go | 2 ++ 3 files changed, 6 insertions(+) diff --git a/test/e2e/helm.go b/test/e2e/helm.go index aa527de77..475e588a3 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -1,3 +1,5 @@ +// build +e2e + package e2e import ( diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index ce4f40aee..9bedab317 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -1,3 +1,5 @@ +// build +e2e + package e2e import ( diff --git a/test/e2e/kubernetes.go b/test/e2e/kubernetes.go index bb15e839b..a4f2ff346 100644 --- a/test/e2e/kubernetes.go +++ b/test/e2e/kubernetes.go @@ -1,3 +1,5 @@ +// build +e2e + package e2e import ( From 11e5d3e697b58d47536f5f71980f875fb66f039e Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 08:59:24 -0700 Subject: [PATCH 04/26] feat(e2e): add options as flags --- test/e2e/helm_test.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 9bedab317..e1cd8615e 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -3,6 +3,7 @@ package e2e import ( + "flag" "fmt" "math/rand" "strings" @@ -14,7 +15,11 @@ func init() { rand.Seed(time.Now().Unix()) } -var chart = "gs://areese-charts/replicatedservice-3.tgz" +var ( + repoURL = flag.String("repo-url", "gs://areese-charts", "Repository URL") + repoName = flag.String("repo-name", "areese-charts", "Repository name") + chart = flag.String("chart", "gs://areese-charts/replicatedservice-3.tgz", "Chart to deploy") +) func TestHelm(t *testing.T) { kube := NewKubeContext() @@ -38,10 +43,13 @@ func TestHelm(t *testing.T) { t.Log("Executing deployment list") helm.Run("deployment", "list") + t.Logf("Adding repo %s %s", *repoName, *repoURL) + helm.Run("repo", "add", *repoName, *repoURL) + deploymentName := genName() t.Log("Executing deploy") - helm.Run("deploy", "--name", deploymentName, chart) + helm.Run("deploy", "--name", deploymentName, *chart) t.Log("Executing deployment list") helm.Run("deployment", "list") From 00cf94bd6afcb9bc0a1159c1ac83f3d42d9b758f Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 08:59:53 -0700 Subject: [PATCH 05/26] feat(e2e): log kubernetes version --- test/e2e/helm_test.go | 3 +++ test/e2e/kubernetes.go | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index e1cd8615e..9ea9ca8ba 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -33,6 +33,9 @@ func TestHelm(t *testing.T) { t.Fatal("Not connected to kubernetes") } + t.Log("Kuberneter Version") + t.Log(kube.Version()) + if !helmRunning(helm) { t.Fatal("Helm is not installed") } diff --git a/test/e2e/kubernetes.go b/test/e2e/kubernetes.go index a4f2ff346..9505b29a3 100644 --- a/test/e2e/kubernetes.go +++ b/test/e2e/kubernetes.go @@ -74,3 +74,8 @@ func (k *KubeContext) Running() bool { _, err := exec.Command(k.Path, "cluster-info").CombinedOutput() return err == nil } + +func (k *KubeContext) Version() string { + out, _ := exec.Command(k.Path, "version").Output() + return string(out) +} From 46e5075dbe32cf26d4e4183ffd77b396febfbbfb Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 10:49:17 -0700 Subject: [PATCH 06/26] fix(e2e): working test scenario --- test/e2e/helm.go | 13 +++++++++++++ test/e2e/helm_test.go | 41 +++++++++++++++++++++++++++++++---------- 2 files changed, 44 insertions(+), 10 deletions(-) diff --git a/test/e2e/helm.go b/test/e2e/helm.go index 475e588a3..6b5bde4f9 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -8,6 +8,7 @@ import ( "os/exec" "path" "path/filepath" + "strings" "testing" ) @@ -86,6 +87,18 @@ func (h *HelmCmd) Stderr() string { 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 { + return h.StdoutContains(substring) || h.StderrContains(substring) +} + func RepoRoot() string { return filepath.Clean(filepath.Join(path.Base(os.Args[0]), "../../..")) } diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 9ea9ca8ba..2095346a3 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -6,6 +6,7 @@ import ( "flag" "fmt" "math/rand" + "os" "strings" "testing" "time" @@ -19,6 +20,7 @@ var ( repoURL = flag.String("repo-url", "gs://areese-charts", "Repository URL") repoName = flag.String("repo-name", "areese-charts", "Repository name") chart = flag.String("chart", "gs://areese-charts/replicatedservice-3.tgz", "Chart to deploy") + host = flag.String("host", "", "The URL to the helm server") ) func TestHelm(t *testing.T) { @@ -40,29 +42,48 @@ func TestHelm(t *testing.T) { t.Fatal("Helm is not installed") } - helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy) - t.Logf("Using host: %v", helm.Host) + helm.Host = helmHost() - t.Log("Executing deployment list") - helm.Run("deployment", "list") + if helm.Host == "" { + helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy) + } + t.Logf("Using host: %v", helm.Host) - t.Logf("Adding repo %s %s", *repoName, *repoURL) - helm.Run("repo", "add", *repoName, *repoURL) + if !helm.Run("repo", "list").Contains(*repoURL) { + t.Logf("Adding repo %s %s", *repoName, *repoURL) + helm.Run("repo", "add", *repoName, *repoURL) + } deploymentName := genName() t.Log("Executing deploy") - helm.Run("deploy", "--name", deploymentName, *chart) + helm.Run("deploy", "--properties", "container_port=6379,image=kubernetes/redis:v1,replicas=2", "--name", deploymentName, *chart) t.Log("Executing deployment list") - helm.Run("deployment", "list") + if !helm.Run("deployment", "list").Contains(deploymentName) { + t.Fatal("Could not list deployment") + } + + t.Log("Executing deployment info") + if !helm.Run("deployment", "info", deploymentName).Contains("Deployed") { + t.Fatal("Could not deploy") + } t.Log("Executing deployment delete") - helm.Run("deployment", "delete", deploymentName) + if !helm.Run("deployment", "rm", deploymentName).Contains("Deleted") { + t.Fatal("Could not delete deployment") + } } func genName() string { - return fmt.Sprintf("%d", rand.Uint32()) + return fmt.Sprintf("e2e-%d", rand.Uint32()) +} + +func helmHost() string { + if *host != "" { + return *host + } + return os.Getenv("HELM_HOST") } func helmRunning(h *HelmContext) bool { From d14855cec74f116cea538d456bf0e293b319a274 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 10:58:32 -0700 Subject: [PATCH 07/26] fix(e2e): cleanup config --- test/e2e/kubernetes.go | 38 +------------------------------------- 1 file changed, 1 insertion(+), 37 deletions(-) diff --git a/test/e2e/kubernetes.go b/test/e2e/kubernetes.go index 9505b29a3..8a697f488 100644 --- a/test/e2e/kubernetes.go +++ b/test/e2e/kubernetes.go @@ -3,8 +3,6 @@ package e2e import ( - "encoding/json" - "fmt" "os/exec" "strings" ) @@ -12,8 +10,7 @@ import ( const defaultKubectlPath = "kubectl" type KubeContext struct { - Path string - Config *Config + Path string } func NewKubeContext() *KubeContext { @@ -22,39 +19,6 @@ func NewKubeContext() *KubeContext { } } -type Config struct { - Clusters []struct { - // Name is the nickname for this Cluster - Name string `json:"name"` - // Cluster holds the cluster information - Cluster struct { - // Server is the address of the kubernetes cluster (https://hostname:port). - Server string `json:"server"` - // APIVersion is the preferred api version for communicating with the kubernetes cluster (v1, v2, etc). - APIVersion string `json:"api-version,omitempty"` - // InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure. - InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"` - // CertificateAuthority is the path to a cert file for the certificate authority. - CertificateAuthority string `json:"certificate-authority,omitempty"` - // CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority - CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"` - // Extensions holds additional information. This is useful for extenders so that reads and writes don't clobber unknown fields - Extensions []struct { - // Name is the nickname for this Extension - Name string `json:"name"` - } `json:"extensions,omitempty"` - } `json:"cluster"` - } -} - -func (k *KubeContext) ParseConfig() { - out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o=json").Output() - err := json.Unmarshal(out, &k.Config) - if err != nil { - fmt.Println(err.Error()) - } -} - func (k *KubeContext) Cluster() string { out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].name}'").Output() return string(out) From 97b1a7b815d02bf9d0ba2314db7f0fd4b516e21d Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 12:13:27 -0700 Subject: [PATCH 08/26] feat(e2e): add must run and image flags for install --- test/e2e/helm.go | 21 ++++++++++++++++++--- test/e2e/helm_test.go | 32 ++++++++++++++++++++------------ 2 files changed, 38 insertions(+), 15 deletions(-) diff --git a/test/e2e/helm.go b/test/e2e/helm.go index 6b5bde4f9..ea7ccc257 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -30,7 +30,7 @@ func NewHelmContext(t *testing.T) *HelmContext { } } -func (h *HelmContext) Run(args ...string) *HelmCmd { +func (h *HelmContext) MustRun(args ...string) *HelmCmd { cmd := h.newCmd() if status := cmd.exec(args...); status != nil { h.t.Fatalf("helm %v failed unexpectedly: %v", args, status) @@ -38,6 +38,20 @@ func (h *HelmContext) Run(args ...string) *HelmCmd { return cmd } +func (h *HelmContext) Run(args ...string) *HelmCmd { + cmd := h.newCmd() + cmd.exec(args...) + return cmd +} + +func (h *HelmContext) RunFail(args ...string) *HelmCmd { + cmd := h.newCmd() + if status := cmd.exec(args...); status == nil { + h.t.Fatalf("helm unexpected to fail: %v", args, status) + } + return cmd +} + func (h *HelmContext) newCmd() *HelmCmd { return &HelmCmd{ ctx: h, @@ -48,6 +62,7 @@ type HelmCmd struct { ctx *HelmContext path string ran bool + status error stdout, stderr bytes.Buffer } @@ -58,7 +73,7 @@ func (h *HelmCmd) exec(args ...string) error { h.stderr.Reset() cmd.Stdout = &h.stdout cmd.Stderr = &h.stderr - status := cmd.Run() + h.status = cmd.Run() if h.stdout.Len() > 0 { h.ctx.t.Log("standard output:") h.ctx.t.Log(h.stdout.String()) @@ -68,7 +83,7 @@ func (h *HelmCmd) exec(args ...string) error { h.ctx.t.Log(h.stderr.String()) } h.ran = true - return status + return h.status } // Stdout returns standard output of the helmCmd run as a string. diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 2095346a3..0175c6e22 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -21,25 +21,31 @@ var ( repoName = flag.String("repo-name", "areese-charts", "Repository name") chart = flag.String("chart", "gs://areese-charts/replicatedservice-3.tgz", "Chart to deploy") host = flag.String("host", "", "The URL to the helm server") + + resourcifierImage = "quay.io/adamreese/resourcifier:latest" + expandybirdImage = "quay.io/adamreese/expandybird:latest" + managerImage = "quay.io/adamreese/manager:latest" ) func TestHelm(t *testing.T) { kube := NewKubeContext() helm := NewHelmContext(t) - t.Log(kube.CurrentContext()) - t.Log(kube.Cluster()) - t.Log(kube.Server()) + t.Logf("Kubenetes context: %s", kube.CurrentContext()) + t.Logf("Cluster: %s", kube.Cluster()) + t.Logf("Server: %s", kube.Server()) if !kube.Running() { t.Fatal("Not connected to kubernetes") } - t.Log("Kuberneter Version") t.Log(kube.Version()) + //TODO: skip check if running local binaries if !helmRunning(helm) { - t.Fatal("Helm is not installed") + 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() @@ -49,28 +55,30 @@ func TestHelm(t *testing.T) { } t.Logf("Using host: %v", helm.Host) - if !helm.Run("repo", "list").Contains(*repoURL) { + // Add repo if it does not exsit + if !helm.MustRun("repo", "list").Contains(*repoURL) { t.Logf("Adding repo %s %s", *repoName, *repoURL) - helm.Run("repo", "add", *repoName, *repoURL) + helm.MustRun("repo", "add", *repoName, *repoURL) } + // Generate a name deploymentName := genName() t.Log("Executing deploy") - helm.Run("deploy", "--properties", "container_port=6379,image=kubernetes/redis:v1,replicas=2", "--name", deploymentName, *chart) + helm.MustRun("deploy", "--properties", "container_port=6379,image=kubernetes/redis:v1,replicas=2", "--name", deploymentName, *chart) t.Log("Executing deployment list") - if !helm.Run("deployment", "list").Contains(deploymentName) { + if !helm.MustRun("deployment", "list").Contains(deploymentName) { t.Fatal("Could not list deployment") } t.Log("Executing deployment info") - if !helm.Run("deployment", "info", deploymentName).Contains("Deployed") { + if !helm.MustRun("deployment", "info", deploymentName).Contains("Deployed") { t.Fatal("Could not deploy") } t.Log("Executing deployment delete") - if !helm.Run("deployment", "rm", deploymentName).Contains("Deleted") { + if !helm.MustRun("deployment", "rm", deploymentName).Contains("Deleted") { t.Fatal("Could not delete deployment") } } @@ -87,6 +95,6 @@ func helmHost() string { } func helmRunning(h *HelmContext) bool { - out := h.Run("server", "status").Stdout() + out := h.MustRun("server", "status").Stdout() return strings.Count(out, "Running") == 5 } From 3921637878bc10e07433950f86ea5ffadaaae9e8 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 1 Apr 2016 14:21:02 -0700 Subject: [PATCH 09/26] fix(e2e): set longer timeout for docker --- test/e2e/helm.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/e2e/helm.go b/test/e2e/helm.go index ea7ccc257..d53974193 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -10,6 +10,7 @@ import ( "path/filepath" "strings" "testing" + "time" ) const ( @@ -18,15 +19,17 @@ const ( ) type HelmContext struct { - t *testing.T - Path string - Host string + t *testing.T + Path string + Host string + Timeout time.Duration } func NewHelmContext(t *testing.T) *HelmContext { return &HelmContext{ - t: t, - Path: RepoRoot() + "/bin/helm", + t: t, + Path: RepoRoot() + "/bin/helm", + Timeout: time.Second * 20, } } From 0ac3a7f710e8364b5a9ae26c975584f9b82abbd5 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 01:30:48 -0700 Subject: [PATCH 10/26] fix(e2e): use helm namespace --- test/e2e/helm.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/helm.go b/test/e2e/helm.go index d53974193..43b6b274a 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -14,7 +14,7 @@ import ( ) const ( - namespace = "dm" + namespace = "helm" apiProxy = "/api/v1/proxy/namespaces/" + namespace + "/services/manager-service:manager/" ) From fbf1fb64f64c3ad2c07e7a5d80c563873da77906 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 01:31:48 -0700 Subject: [PATCH 11/26] fix(e2e): default to kubernetes-charts-testing repo Until official repo is ready --- test/e2e/helm_test.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 0175c6e22..4ac389e98 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -16,10 +16,15 @@ func init() { rand.Seed(time.Now().Unix()) } +const ( + timeout = 10 * time.Second + poll = 2 * time.Second +) + var ( - repoURL = flag.String("repo-url", "gs://areese-charts", "Repository URL") - repoName = flag.String("repo-name", "areese-charts", "Repository name") - chart = flag.String("chart", "gs://areese-charts/replicatedservice-3.tgz", "Chart to deploy") + repoURL = flag.String("repo-url", "gs://kubernetes-charts-testing", "Repository URL") + repoName = flag.String("repo-name", "kubernetes-charts-testing", "Repository name") + chart = flag.String("chart", "gs://kubernetes-charts-testing/redis-2.tgz", "Chart to deploy") host = flag.String("host", "", "The URL to the helm server") resourcifierImage = "quay.io/adamreese/resourcifier:latest" From a8582dbc97e8b7d706b29e1103c3c3f9ce654fe0 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 01:35:14 -0700 Subject: [PATCH 12/26] fix(e2e): create a generic cmd wrapper - Add a wait function --- test/e2e/command.go | 79 +++++++++++++++++++++++++++++++++++ test/e2e/helm.go | 94 ++++++++++++------------------------------ test/e2e/helm_test.go | 70 ++++++++++++++++++++++--------- test/e2e/kubernetes.go | 38 ++++++++++++----- 4 files changed, 183 insertions(+), 98 deletions(-) create mode 100644 test/e2e/command.go diff --git a/test/e2e/command.go b/test/e2e/command.go new file mode 100644 index 000000000..fba57c812 --- /dev/null +++ b/test/e2e/command.go @@ -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) +} diff --git a/test/e2e/helm.go b/test/e2e/helm.go index 43b6b274a..c1bd743a8 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -3,12 +3,10 @@ package e2e import ( - "bytes" + "net/http" "os" - "os/exec" "path" "path/filepath" - "strings" "testing" "time" ) @@ -33,88 +31,50 @@ func NewHelmContext(t *testing.T) *HelmContext { } } -func (h *HelmContext) MustRun(args ...string) *HelmCmd { - cmd := h.newCmd() - if status := cmd.exec(args...); status != nil { - h.t.Fatalf("helm %v failed unexpectedly: %v", args, status) +func (h *HelmContext) MustRun(args ...string) *Cmd { + cmd := h.newCmd(args...) + if status := cmd.exec(); status != nil { + h.t.Errorf("helm %v failed unexpectedly: %v", args, status) + h.t.Errorf("%s", cmd.Stderr()) + h.t.FailNow() } return cmd } -func (h *HelmContext) Run(args ...string) *HelmCmd { - cmd := h.newCmd() - cmd.exec(args...) +func (h *HelmContext) Run(args ...string) *Cmd { + cmd := h.newCmd(args...) + cmd.exec() return cmd } -func (h *HelmContext) RunFail(args ...string) *HelmCmd { - cmd := h.newCmd() - if status := cmd.exec(args...); status == nil { +func (h *HelmContext) RunFail(args ...string) *Cmd { + cmd := h.newCmd(args...) + if status := cmd.exec(); status == nil { h.t.Fatalf("helm unexpected to fail: %v", args, status) } return cmd } -func (h *HelmContext) newCmd() *HelmCmd { - return &HelmCmd{ - ctx: h, +func (h *HelmContext) newCmd(args ...string) *Cmd { + args = append([]string{"--host", h.Host}, args...) + return &Cmd{ + t: h.t, + path: h.Path, + args: args, } } -type HelmCmd struct { - ctx *HelmContext - path string - ran bool - status error - stdout, stderr bytes.Buffer -} +func (h *HelmContext) Running() bool { + endpoint := h.Host + "healthz" -func (h *HelmCmd) exec(args ...string) error { - args = append([]string{"--host", h.ctx.Host}, args...) - cmd := exec.Command(h.ctx.Path, args...) - 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()) + resp, err := http.Get(endpoint) + if err != nil { + h.t.Errorf("Could not GET %s: %s", endpoint, err) } - h.ran = true - 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) -} + return resp.StatusCode == 200 -func (h *HelmCmd) Contains(substring string) bool { - return h.StdoutContains(substring) || h.StderrContains(substring) + //out := h.MustRun("server", "status").Stdout() + //return strings.Count(out, "Running") == 5 } func RepoRoot() string { diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 4ac389e98..b0b090dd0 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -7,7 +7,6 @@ import ( "fmt" "math/rand" "os" - "strings" "testing" "time" ) @@ -32,34 +31,43 @@ var ( 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) { - kube := NewKubeContext() + kube := NewKubeContext(t) helm := NewHelmContext(t) - t.Logf("Kubenetes context: %s", kube.CurrentContext()) - t.Logf("Cluster: %s", kube.Cluster()) - t.Logf("Server: %s", kube.Server()) + logKubeEnv(kube) if !kube.Running() { t.Fatal("Not connected to kubernetes") } - 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() - if helm.Host == "" { helm.Host = fmt.Sprintf("%s%s", kube.Server(), apiProxy) } 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 if !helm.MustRun("repo", "list").Contains(*repoURL) { t.Logf("Adding repo %s %s", *repoName, *repoURL) @@ -70,7 +78,19 @@ func TestHelm(t *testing.T) { deploymentName := genName() 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") if !helm.MustRun("deployment", "list").Contains(deploymentName) { @@ -82,12 +102,27 @@ func TestHelm(t *testing.T) { t.Fatal("Could not deploy") } + t.Log("Executing deployment describe") + helm.MustRun("deployment", "describe", deploymentName) + t.Log("Executing deployment delete") if !helm.MustRun("deployment", "rm", deploymentName).Contains("Deleted") { 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 { return fmt.Sprintf("e2e-%d", rand.Uint32()) } @@ -98,8 +133,3 @@ func helmHost() string { } return os.Getenv("HELM_HOST") } - -func helmRunning(h *HelmContext) bool { - out := h.MustRun("server", "status").Stdout() - return strings.Count(out, "Running") == 5 -} diff --git a/test/e2e/kubernetes.go b/test/e2e/kubernetes.go index 8a697f488..a450e1f2c 100644 --- a/test/e2e/kubernetes.go +++ b/test/e2e/kubernetes.go @@ -3,43 +3,59 @@ package e2e import ( - "os/exec" "strings" + "testing" ) const defaultKubectlPath = "kubectl" type KubeContext struct { + t *testing.T Path string } -func NewKubeContext() *KubeContext { +func NewKubeContext(t *testing.T) *KubeContext { return &KubeContext{ + t: t, 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 { - out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].name}'").Output() - return string(out) + return k.getConfigValue("'{.clusters[0].name}'") } func (k *KubeContext) Server() string { - out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.clusters[0].cluster.server}'").Output() - return strings.Replace(string(out), "'", "", -1) + return k.getConfigValue("'{.clusters[0].cluster.server}'") } func (k *KubeContext) CurrentContext() string { - out, _ := exec.Command(k.Path, "config", "view", "--flatten=true", "--minify=true", "-o", "jsonpath='{.current-context}'").Output() - return string(out) + return k.getConfigValue("'{.current-context}'") } func (k *KubeContext) Running() bool { - _, err := exec.Command(k.Path, "cluster-info").CombinedOutput() + err := k.Run("cluster-info").exec() return err == nil } func (k *KubeContext) Version() string { - out, _ := exec.Command(k.Path, "version").Output() - return string(out) + return k.Run("version").Stdout() } From a47a4ec036f7c8ff040e404b4efaacd3f0823c81 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 01:42:26 -0700 Subject: [PATCH 13/26] feat(e2e): add a script to run tests --- scripts/e2e.sh | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100755 scripts/e2e.sh diff --git a/scripts/e2e.sh b/scripts/e2e.sh new file mode 100755 index 000000000..afffc3a14 --- /dev/null +++ b/scripts/e2e.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +# 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. +# 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. + +set -o errexit +set -o pipefail +[[ "$TRACE" ]] && set -x + +HELM_ROOT="${BASH_SOURCE[0]%/*}/.." +cd "$HELM_ROOT" + +go test -v ./test/e2e -tags e2e + +exit 0 From 7fd056e7e04d7826ebfcadd3743a3c4403901114 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 02:59:10 -0700 Subject: [PATCH 14/26] fix(e2e): logging typo --- test/e2e/command.go | 2 +- test/e2e/helm_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/e2e/command.go b/test/e2e/command.go index fba57c812..5980d72d4 100644 --- a/test/e2e/command.go +++ b/test/e2e/command.go @@ -38,7 +38,7 @@ func (h *Cmd) exec() error { 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.t.Logf("standard error: %s\n", h.stderr.String()) } h.ran = true diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index b0b090dd0..b093d02b5 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -32,7 +32,7 @@ var ( ) func logKubeEnv(k *KubeContext) { - config := k.Run("config", "view", "--flattend", "--minified").Stdout() + config := k.Run("config", "view", "--flatten", "--minified").Stdout() k.t.Logf("Kubernetes Environment\n%s", config) } From ee813c8cab31e9d1b7e45c08f5741aa473b6e445 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 02:59:59 -0700 Subject: [PATCH 15/26] fix(e2e): print helm version under test --- test/e2e/helm_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index b093d02b5..98de7c1c1 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -46,6 +46,7 @@ func TestHelm(t *testing.T) { t.Fatal("Not connected to kubernetes") } t.Log(kube.Version()) + t.Log(helm.MustRun("version").Stdout()) helm.Host = helmHost() if helm.Host == "" { From a0a59190d55c501385fc106ec56d6dd9ec0d3913 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 07:30:18 -0700 Subject: [PATCH 16/26] fix(e2e): fix return on wait func --- test/e2e/helm_test.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 98de7c1c1..81b7bee4a 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -64,9 +64,9 @@ func TestHelm(t *testing.T) { "--expandybird-image", expandybirdImage, "--manager-image", managerImage, ) - wait(func() bool { - return helm.Running() - }) + if err := wait(helm.Running); err != nil { + t.Fatal(err) + } } // Add repo if it does not exsit @@ -85,10 +85,9 @@ func TestHelm(t *testing.T) { *chart, ) - err := wait(func() bool { + if err := wait(func() bool { return kube.Run("get", "pods").Match("redis.*Running") - }) - if err != nil { + }); err != nil { t.Fatal(err) } t.Log(kube.Run("get", "pods").Stdout()) @@ -112,16 +111,15 @@ func TestHelm(t *testing.T) { } } -func wait(fn func() bool) error { +type conditionFunc func() bool + +func wait(fn conditionFunc) error { for start := time.Now(); time.Since(start) < timeout; time.Sleep(poll) { if fn() { - continue + return nil } } - if !fn() { - return fmt.Errorf("Polling timeout") - } - return nil + return fmt.Errorf("Polling timeout") } func genName() string { From 4ed55097096e3ee0bb738e74ca43585eb8f65551 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Wed, 6 Apr 2016 22:21:58 -0700 Subject: [PATCH 17/26] fix(e2e): typo in kubectl config command --- test/e2e/helm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 81b7bee4a..a394161f9 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -32,7 +32,7 @@ var ( ) func logKubeEnv(k *KubeContext) { - config := k.Run("config", "view", "--flatten", "--minified").Stdout() + config := k.Run("config", "view", "--flatten", "--minify").Stdout() k.t.Logf("Kubernetes Environment\n%s", config) } From 1d6194d8a5f863fe4bc8667286573f32568f1726 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 7 Apr 2016 09:08:37 -0700 Subject: [PATCH 18/26] feat(e2e): add some instrumentation for commands --- test/e2e/command.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/e2e/command.go b/test/e2e/command.go index 5980d72d4..fbe22ff9a 100644 --- a/test/e2e/command.go +++ b/test/e2e/command.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" "testing" + "time" ) type Cmd struct { @@ -32,7 +33,9 @@ func (h *Cmd) exec() error { cmd.Stderr = &h.stderr h.t.Logf("Executing command: %s", h) + start := time.Now() h.status = cmd.Run() + h.t.Logf("Finished in %v", time.Since(start)) if h.stdout.Len() > 0 { h.t.Logf("standard output:\n%s", h.stdout.String()) From b380e64d94224d68e3b99499beee6d2e10e3d566 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 7 Apr 2016 09:09:31 -0700 Subject: [PATCH 19/26] fix(e2e): remove hardcoded image names --- test/e2e/helm_test.go | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index a394161f9..ee148f4b1 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -16,19 +16,18 @@ func init() { } const ( - timeout = 10 * time.Second + timeout = 20 * time.Second poll = 2 * time.Second ) var ( - repoURL = flag.String("repo-url", "gs://kubernetes-charts-testing", "Repository URL") - repoName = flag.String("repo-name", "kubernetes-charts-testing", "Repository name") - chart = flag.String("chart", "gs://kubernetes-charts-testing/redis-2.tgz", "Chart to deploy") - host = flag.String("host", "", "The URL to the helm server") - - resourcifierImage = "quay.io/adamreese/resourcifier:latest" - expandybirdImage = "quay.io/adamreese/expandybird:latest" - managerImage = "quay.io/adamreese/manager:latest" + repoURL = flag.String("repo-url", "gs://kubernetes-charts-testing", "Repository URL") + repoName = flag.String("repo-name", "kubernetes-charts-testing", "Repository name") + chart = flag.String("chart", "gs://kubernetes-charts-testing/redis-2.tgz", "Chart to deploy") + host = flag.String("host", "", "The URL to the helm server") + resourcifierImage = flag.String("resourcifier-image", "", "The full image name of the Docker image for resourcifier.") + expandybirdImage = flag.String("expandybird-image", "", "The full image name of the Docker image for expandybird.") + managerImage = flag.String("manager-image", "", "The full image name of the Docker image for manager.") ) func logKubeEnv(k *KubeContext) { @@ -54,16 +53,12 @@ func TestHelm(t *testing.T) { } t.Logf("Using host: %v", helm.Host) - //TODO: skip check if running local binaries + //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, - ) + + //TODO wire in flag overides + helm.MustRun("server", "install") if err := wait(helm.Running); err != nil { t.Fatal(err) } From d1e19cf58248e546826180f4e29b360797f3a20d Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Thu, 7 Apr 2016 11:04:07 -0700 Subject: [PATCH 20/26] fix(e2e): correct helm version command --- test/e2e/helm_test.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index ee148f4b1..e413dab8c 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -45,7 +45,7 @@ func TestHelm(t *testing.T) { t.Fatal("Not connected to kubernetes") } t.Log(kube.Version()) - t.Log(helm.MustRun("version").Stdout()) + t.Log(helm.MustRun("--version").Stdout()) helm.Host = helmHost() if helm.Host == "" { @@ -80,6 +80,7 @@ func TestHelm(t *testing.T) { *chart, ) + //TODO get pods to lookup dynamically if err := wait(func() bool { return kube.Run("get", "pods").Match("redis.*Running") }); err != nil { From 66d681e43582f56c2dff8320a051406a10d5f6fb Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Fri, 8 Apr 2016 13:03:33 -0700 Subject: [PATCH 21/26] fix(e2e): bump timeout to 180 --- test/e2e/helm_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index e413dab8c..691164837 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -16,7 +16,7 @@ func init() { } const ( - timeout = 20 * time.Second + timeout = 180 * time.Second poll = 2 * time.Second ) From 389b0a43da54dacb791c11ca2ce0c62f0cd27ec7 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 11 Apr 2016 12:59:43 -0700 Subject: [PATCH 22/26] feat(e2e): download correct version of kubectl --- scripts/kube-up.sh | 82 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/scripts/kube-up.sh b/scripts/kube-up.sh index 23bd0d4c3..5efe497fd 100755 --- a/scripts/kube-up.sh +++ b/scripts/kube-up.sh @@ -21,10 +21,12 @@ set -eo pipefail [[ "$TRACE" ]] && set -x HELM_ROOT="${BASH_SOURCE[0]%/*}/.." -source "${HELM_ROOT}/scripts/common.sh" -source "${HELM_ROOT}/scripts/docker.sh" +cd "$HELM_ROOT" -K8S_VERSION=${K8S_VERSION:-1.2.1} +source ./scripts/common.sh +source ./scripts/docker.sh + +KUBE_VERSION=${KUBE_VERSION:-1.2.1} KUBE_PORT=${KUBE_PORT:-8080} KUBE_MASTER_IP=${KUBE_MASTER_IP:-$DOCKER_HOST_IP} KUBE_MASTER_IP=${KUBE_MASTER_IP:-localhost} @@ -100,7 +102,7 @@ start_kubernetes() { --pid=host \ --privileged=true \ -d \ - gcr.io/google_containers/hyperkube-amd64:v${K8S_VERSION} \ + gcr.io/google_containers/hyperkube-amd64:v${KUBE_VERSION} \ /hyperkube kubelet \ --hostname-override="127.0.0.1" \ --address="0.0.0.0" \ @@ -133,13 +135,13 @@ wait_for_kubernetes_master() { create_kube_system_namespace() { echo "Creating kube-system namespace..." - $KUBECTL create -f "${HELM_ROOT}/scripts/cluster/kube-system.yaml" >/dev/null + $KUBECTL create -f ./scripts/cluster/kube-system.yaml >/dev/null } create_kube_dns() { echo "Setting up internal dns..." - $KUBECTL create -f "${HELM_ROOT}/scripts/cluster/skydns.yaml" >/dev/null + $KUBECTL create -f ./scripts/cluster/skydns.yaml >/dev/null } # Generate kubeconfig data for the created cluster. @@ -175,21 +177,67 @@ cleanup_volumes() { fi } -verify_prereqs -cleanup_volumes +uname=$(uname) +if [[ "${uname}" == "Darwin" ]]; then + platform="darwin" +elif [[ "${uname}" == "Linux" ]]; then + platform="linux" +else + error_exit "unsupported platform: (${uname})." +fi -if is_docker_machine; then - setup_iptables +machine=$(uname -m) +if [[ "${machine}" == "x86_64" ]]; then + arch="amd64" +elif [[ "${machine}" == "i686" ]]; then + arch="386" +elif [[ "${machine}" == "arm*" ]]; then + arch="arm" +elif [[ "${machine}" == "s390x*" ]]; then + arch="s390x" +else + error_exit "unsupported architecture (${machine})." fi -start_kubernetes -wait_for_kubernetes_master -create_kube_system_namespace -create_kube_dns -wait_for_kubernetes_cluster +download_kubectl() { + echo "Downloading kubectl binary..." + + kubectl_url="https://storage.googleapis.com/kubernetes-release/release/v${KUBE_VERSION}/bin/${platform}/${arch}/kubectl" + ( + cd ./bin + # cleanup anything old + rm ./kubectl + if [[ $(which wget) ]]; then + wget "${kubectl_url}" + elif [[ $(which curl) ]]; then + curl -OL "${kubectl_url}" + else + error_exit "Couldn't find curl or wget. Bailing out." + fi + chmod a+x kubectl + ) +} + +main() { + verify_prereqs + cleanup_volumes + + if is_docker_machine; then + setup_iptables + fi + + download_kubectl + start_kubernetes + wait_for_kubernetes_master -create_kubeconfig + create_kube_system_namespace + create_kube_dns + wait_for_kubernetes_cluster + create_kubeconfig + + $KUBECTL cluster-info +} -$KUBECTL cluster-info +main "$@" From 653bb68f95384a16201a1cc8c65d1f9ca4f72262 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 11 Apr 2016 13:02:18 -0700 Subject: [PATCH 23/26] feat(e2e): bypass using a registry --- Makefile | 14 ++++++++++++++ circle.yml | 2 +- scripts/docker.sh | 8 -------- test/e2e/command.go | 1 + test/e2e/helm.go | 11 ++--------- test/e2e/helm_test.go | 28 +++++++++++++++++++++------- 6 files changed, 39 insertions(+), 25 deletions(-) diff --git a/Makefile b/Makefile index aba820c6b..1c1b14f22 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,8 @@ GO_DIRS ?= $(shell glide nv -x ) GO_PKGS ?= $(shell glide nv) +BIN_DIR := bin +PATH_WITH_BIN = PATH="$(shell pwd)/$(BIN_DIR):$(PATH)" ROOTFS := rootfs CLIENT := cmd/helm @@ -86,6 +88,18 @@ test-flake8: test-style: @scripts/validate-go.sh +.PHONY: test-e2e +test-e2e: container + $(PATH_WITH_BIN) go test -tags=e2e ./test/e2e -v --manager-image=${DOCKER_REGISTRY}/manager:${TAG} --resourcifier-image=${DOCKER_REGISTRY}/resourcifier:${TAG} --expandybird-image=${DOCKER_REGISTRY}/expandybird:${TAG} + +.PHONY: local-cluster-up +local-cluster-up: + @scripts/kube-up.sh + +.PHONY: local-cluster-down +local-cluster-down: + @scripts/kube-down.sh + HAS_GLIDE := $(shell command -v glide;) HAS_GOLINT := $(shell command -v golint;) HAS_GOVET := $(shell command -v go tool vet;) diff --git a/circle.yml b/circle.yml index c66114bea..a60780894 100644 --- a/circle.yml +++ b/circle.yml @@ -30,4 +30,4 @@ dependencies: test: override: - - cd $GOPATH/src/$IMPORT_PATH && make bootstrap test + - cd $GOPATH/src/$IMPORT_PATH && make info bootstrap test local-cluster-up test-e2e DOCKER_REGISTRY=e2e diff --git a/scripts/docker.sh b/scripts/docker.sh index c6174598a..9cd21f6e6 100644 --- a/scripts/docker.sh +++ b/scripts/docker.sh @@ -44,11 +44,3 @@ delete_container() { docker wait "${container[@]}" &>/dev/null || : docker rm --force --volumes "${container[@]}" &>/dev/null || : } - -dev_registry() { - if docker inspect registry >/dev/null 2>&1; then - docker start registry - else - docker run --restart="always" -d -p 5000:5000 --name registry registry:2 - fi -} diff --git a/test/e2e/command.go b/test/e2e/command.go index fbe22ff9a..c01f18293 100644 --- a/test/e2e/command.go +++ b/test/e2e/command.go @@ -12,6 +12,7 @@ import ( "time" ) +// Cmd provides helpers for command output type Cmd struct { t *testing.T path string diff --git a/test/e2e/helm.go b/test/e2e/helm.go index c1bd743a8..e7af6a21b 100644 --- a/test/e2e/helm.go +++ b/test/e2e/helm.go @@ -4,9 +4,6 @@ package e2e import ( "net/http" - "os" - "path" - "path/filepath" "testing" "time" ) @@ -26,7 +23,7 @@ type HelmContext struct { func NewHelmContext(t *testing.T) *HelmContext { return &HelmContext{ t: t, - Path: RepoRoot() + "/bin/helm", + Path: "helm", Timeout: time.Second * 20, } } @@ -50,7 +47,7 @@ func (h *HelmContext) Run(args ...string) *Cmd { func (h *HelmContext) RunFail(args ...string) *Cmd { cmd := h.newCmd(args...) if status := cmd.exec(); status == nil { - h.t.Fatalf("helm unexpected to fail: %v", args, status) + h.t.Fatalf("helm unexpected to fail: %v %v", args, status) } return cmd } @@ -76,7 +73,3 @@ func (h *HelmContext) Running() bool { //out := h.MustRun("server", "status").Stdout() //return strings.Count(out, "Running") == 5 } - -func RepoRoot() string { - return filepath.Clean(filepath.Join(path.Base(os.Args[0]), "../../..")) -} diff --git a/test/e2e/helm_test.go b/test/e2e/helm_test.go index 691164837..281b0e6a4 100644 --- a/test/e2e/helm_test.go +++ b/test/e2e/helm_test.go @@ -53,15 +53,11 @@ func TestHelm(t *testing.T) { } t.Logf("Using host: %v", helm.Host) - //TODO skip check if running local binaries if !helm.Running() { - t.Error("Helm is not installed") + t.Log("Helm is not installed") + + install(helm) - //TODO wire in flag overides - helm.MustRun("server", "install") - if err := wait(helm.Running); err != nil { - t.Fatal(err) - } } // Add repo if it does not exsit @@ -128,3 +124,21 @@ func helmHost() string { } return os.Getenv("HELM_HOST") } + +func install(h *HelmContext) { + args := []string{"server", "install"} + if *expandybirdImage != "" { + args = append(args, *expandybirdImage) + } + if *managerImage != "" { + args = append(args, *managerImage) + } + if *resourcifierImage != "" { + args = append(args, *resourcifierImage) + } + + h.MustRun(args...) + if err := wait(h.Running); err != nil { + h.t.Fatal(err) + } +} From 25a5a11b1079ca160efc4be9754046c8ddcf2fec Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 11 Apr 2016 13:10:24 -0700 Subject: [PATCH 24/26] fix(e2e): skip e2e during unit testing --- test/e2e/main_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 test/e2e/main_test.go diff --git a/test/e2e/main_test.go b/test/e2e/main_test.go new file mode 100644 index 000000000..76793e638 --- /dev/null +++ b/test/e2e/main_test.go @@ -0,0 +1,12 @@ +// +build !e2e + +package e2e + +import ( + "os" + "testing" +) + +func TestMain(m *testing.M) { + os.Exit(0) +} From 2e04d3c0a61d41c27a05dcc2d1fe8b9bf55043eb Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 11 Apr 2016 13:21:59 -0700 Subject: [PATCH 25/26] fix(e2e): get circleci working --- Makefile | 2 +- circle.yml | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1c1b14f22..8164770f0 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ test-style: @scripts/validate-go.sh .PHONY: test-e2e -test-e2e: container +test-e2e: container local-cluster-up $(PATH_WITH_BIN) go test -tags=e2e ./test/e2e -v --manager-image=${DOCKER_REGISTRY}/manager:${TAG} --resourcifier-image=${DOCKER_REGISTRY}/resourcifier:${TAG} --expandybird-image=${DOCKER_REGISTRY}/expandybird:${TAG} .PHONY: local-cluster-up diff --git a/circle.yml b/circle.yml index a60780894..87cfb38a7 100644 --- a/circle.yml +++ b/circle.yml @@ -1,4 +1,10 @@ machine: + pre: + - curl -sSL https://s3.amazonaws.com/circle-downloads/install-circleci-docker.sh | bash -s -- 1.10.3 + + services: + - docker + environment: GLIDE_VERSION: "0.10.1" GO15VENDOREXPERIMENT: 1 @@ -15,6 +21,7 @@ dependencies: - tar -C $HOME -xzf go1.6.linux-amd64.tar.gz - go version - go env + - docker info - sudo chown -R $(whoami):staff /usr/local - cd $GOPATH - mkdir -p $GOPATH/src/$IMPORT_PATH @@ -30,4 +37,4 @@ dependencies: test: override: - - cd $GOPATH/src/$IMPORT_PATH && make info bootstrap test local-cluster-up test-e2e DOCKER_REGISTRY=e2e + - cd $GOPATH/src/$IMPORT_PATH && make info bootstrap test test-e2e DOCKER_REGISTRY=e2e From 081fcd0ac831bdfb9926985bfb9dfbcfea9fc9f5 Mon Sep 17 00:00:00 2001 From: Adam Reese Date: Mon, 11 Apr 2016 14:25:40 -0700 Subject: [PATCH 26/26] fix(e2e): circleci does not like this docker check --- rootfs/include.mk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rootfs/include.mk b/rootfs/include.mk index f52dcfe2c..5116f3dfc 100644 --- a/rootfs/include.mk +++ b/rootfs/include.mk @@ -61,7 +61,7 @@ else endif .PHONY: container -container: .project .docker binary extras +container: .project binary extras docker build -t $(FULL_IMAGE):$(TAG) -f Dockerfile . docker tag -f $(FULL_IMAGE):$(TAG) $(FULL_IMAGE):latest