From 1bde95aba59daa4d9c69317df8824d5aec5ec52c Mon Sep 17 00:00:00 2001 From: Manuel de Brito Fontes Date: Fri, 2 Jun 2017 14:56:29 -0400 Subject: [PATCH] Refactoring of test scripts --- Makefile | 8 +-- hack/default-backend.yaml | 51 +++++++++++++ hack/e2e-internal/e2e-env.sh | 2 +- hack/e2e-internal/ginkgo-e2e.sh | 3 - hack/e2e-internal/run-e2e.sh | 3 + hack/e2e.go | 35 ++++++--- hack/e2e_types.go | 61 ++++++++++++++++ hack/ingress_test.go | 111 +++++++++++++++++++++++++++++ hack/nginx-ingress-controller.yaml | 47 ++++++++++++ hack/suite/0001.yaml | 1 + 10 files changed, 304 insertions(+), 18 deletions(-) create mode 100644 hack/default-backend.yaml delete mode 100755 hack/e2e-internal/ginkgo-e2e.sh create mode 100755 hack/e2e-internal/run-e2e.sh create mode 100644 hack/e2e_types.go create mode 100644 hack/ingress_test.go create mode 100644 hack/nginx-ingress-controller.yaml create mode 100644 hack/suite/0001.yaml diff --git a/Makefile b/Makefile index ce21c9178..69a7798fe 100644 --- a/Makefile +++ b/Makefile @@ -38,8 +38,8 @@ test: @go test -v -race -tags "$(BUILDTAGS) cgo" ${GO_LIST_FILES} .PHONY: test-e2e -test-e2e: ginkgo - @go run hack/e2e.go -v --up --test --down +test-e2e: + @go run hack/e2e.go --verbose --up --test --down --files="hack/nginx-ingress-controller.yaml,hack/default-backend.yaml" .PHONY: cover cover: @@ -66,7 +66,3 @@ docker-build: .PHONY: docker-push docker-push: make -C controllers/nginx push - -.PHONY: ginkgo -ginkgo: - go get github.com/onsi/ginkgo/ginkgo diff --git a/hack/default-backend.yaml b/hack/default-backend.yaml new file mode 100644 index 000000000..3c40989a3 --- /dev/null +++ b/hack/default-backend.yaml @@ -0,0 +1,51 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: default-http-backend + labels: + k8s-app: default-http-backend + namespace: kube-system +spec: + replicas: 1 + template: + metadata: + labels: + k8s-app: default-http-backend + spec: + terminationGracePeriodSeconds: 60 + containers: + - name: default-http-backend + # Any image is permissable as long as: + # 1. It serves a 404 page at / + # 2. It serves 200 on a /healthz endpoint + image: gcr.io/google_containers/defaultbackend:1.0 + livenessProbe: + httpGet: + path: /healthz + port: 8080 + scheme: HTTP + initialDelaySeconds: 30 + timeoutSeconds: 5 + ports: + - containerPort: 8080 + resources: + limits: + cpu: 10m + memory: 20Mi + requests: + cpu: 10m + memory: 20Mi +--- +apiVersion: v1 +kind: Service +metadata: + name: default-http-backend + namespace: kube-system + labels: + k8s-app: default-http-backend +spec: + ports: + - port: 80 + targetPort: 8080 + selector: + k8s-app: default-http-backend diff --git a/hack/e2e-internal/e2e-env.sh b/hack/e2e-internal/e2e-env.sh index 0445f0bd7..eccb70d31 100755 --- a/hack/e2e-internal/e2e-env.sh +++ b/hack/e2e-internal/e2e-env.sh @@ -18,7 +18,7 @@ if [ ! -e ${KUBECTL} ]; then fi if [ ! -e ${MINIKUBE} ]; then - echo "kubectl binary is missing. downloading..." + echo "minikube binary is missing. downloading..." curl -sSLo ${MINIKUBE} https://storage.googleapis.com/minikube/releases/v${MINIKUBE_VERSION}/minikube-linux-amd64 chmod +x ${MINIKUBE} fi diff --git a/hack/e2e-internal/ginkgo-e2e.sh b/hack/e2e-internal/ginkgo-e2e.sh deleted file mode 100755 index aa3c61ce6..000000000 --- a/hack/e2e-internal/ginkgo-e2e.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -echo "running ginkgo" \ No newline at end of file diff --git a/hack/e2e-internal/run-e2e.sh b/hack/e2e-internal/run-e2e.sh new file mode 100755 index 000000000..2e00589db --- /dev/null +++ b/hack/e2e-internal/run-e2e.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash + +go test -v k8s.io/ingress/hack/... -tags -run ^TestIngressSuite$ --args --alsologtostderr --v=10 diff --git a/hack/e2e.go b/hack/e2e.go index be9f2aa28..e87f1d4eb 100644 --- a/hack/e2e.go +++ b/hack/e2e.go @@ -34,11 +34,12 @@ var ( build = flag.Bool("build", true, "Build the backends images indicated by the env var BACKENDS required to run e2e tests.") up = flag.Bool("up", true, "Creates a kubernetes cluster using hyperkube (containerized kubelet).") down = flag.Bool("down", true, "destroys the created cluster.") - test = flag.Bool("test", true, "Run Ginkgo tests.") + test = flag.Bool("test", true, "Run tests.") dump = flag.String("dump", "", "If set, dump cluster logs to this location on test or cluster-up failure") - testArgs = flag.String("test-args", "", "Space-separated list of arguments to pass to Ginkgo test runner.") + testArgs = flag.String("test-args", "", "Space-separated list of arguments to pass to the test runner.") deployment = flag.String("deployment", "bash", "up/down mechanism") - verbose = flag.Bool("v", false, "If true, print all command output.") + verbose = flag.Bool("verbose", false, "If true, print all command output.") + files = flag.String("files", "", "Path to a file/S descriptor that will create an Ingress controller") ) func appendError(errs []error, err error) []error { @@ -139,6 +140,10 @@ func main() { } func run(deploy deployer) error { + if *files == "" { + return fmt.Errorf("missing required flag --files") + } + if *dump != "" { defer writeXML(time.Now()) } @@ -172,7 +177,7 @@ func run(deploy deployer) error { return fmt.Errorf("starting e2e cluster: %s", err) } if *dump != "" { - cmd := exec.Command("./cluster/kubectl.sh", "--match-server-version=false", "get", "nodes", "-oyaml") + cmd := exec.Command("./hack/e2e-internal/kubectl", "get", "nodes", "-oyaml") b, err := cmd.CombinedOutput() if *verbose { log.Printf("kubectl get nodes:\n%s", string(b)) @@ -187,6 +192,10 @@ func run(deploy deployer) error { } } + if err := deploy.SetupController(*files); err != nil { + errs = appendError(errs, err) + } + if *test { if err := xmlWrap("IsUp", deploy.IsUp); err != nil { errs = appendError(errs, err) @@ -226,6 +235,7 @@ type deployer interface { Up() error IsUp() error SetupKubecfg() error + SetupController(p string) error Down() error } @@ -252,6 +262,18 @@ func (b bash) SetupKubecfg() error { return nil } +func (b bash) SetupController(p string) error { + files := strings.Split(p, ",") + for _, f := range files { + err := finishRunning("setup controller", exec.Command("./hack/e2e-internal/kubectl", "create", "-f", f)) + if err != nil { + return err + } + } + + return nil +} + func (b bash) Down() error { return finishRunning("teardown", exec.Command("./hack/e2e-internal/e2e-down.sh")) } @@ -262,10 +284,7 @@ func DumpClusterLogs(location string) error { } func Test() error { - if *testArgs == "" { - *testArgs = "--ginkgo.focus=\\[Feature:Ingress\\]" - } - return finishRunning("Ginkgo tests", exec.Command("./hack/e2e-internal/ginkgo-e2e.sh", strings.Fields(*testArgs)...)) + return finishRunning("Ingress tests", exec.Command("./hack/e2e-internal/run-e2e.sh", strings.Fields(*testArgs)...)) } func finishRunning(stepName string, cmd *exec.Cmd) error { diff --git a/hack/e2e_types.go b/hack/e2e_types.go new file mode 100644 index 000000000..4e5e62972 --- /dev/null +++ b/hack/e2e_types.go @@ -0,0 +1,61 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 +*/ + +package main + +import ( + "time" + + "k8s.io/client-go/pkg/api" + extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1" +) + +// IngressTestCase defines a test case for Ingress +type IngressTestCase struct { + Name string `yaml:"name"` + Description string `yaml:"description"` + Pod *api.Pod `yaml:"pod"` + ReplicationController *api.ReplicationController `yaml:"replicationController"` + Deployment *extensions.Deployment `yaml:"deployment"` + Assert []*Assert `yaml:"tests"` +} + +// Assert defines a verification over the +type Assert struct { + Name string `yaml:"name"` + Request Request `yaml:"request"` + Expect []*Expect `yaml:"expect"` + Timeout time.Duration `yaml:"timeout"` +} + +// Request defines a HTTP/s request to be executed against an Ingress +type Request struct { + Method string `yaml:"method"` + URL string `yaml:"url"` + Query map[string]interface{} `yaml:"query"` + Form map[string]interface{} `yaml:"form"` + Body interface{} `yaml:"body"` + Headers map[string]string `yaml:"headers"` +} + +// Expect defines the required conditions that must be true from a request response +type Expect struct { + Body []byte `yaml:"body"` + ContentType string `yaml:"contentType"` + Header []string `yaml:"header"` + HeaderAndValue map[string]string `yaml:"headerAndValue"` + Statuscode int `yaml:"statucDode"` +} diff --git a/hack/ingress_test.go b/hack/ingress_test.go new file mode 100644 index 000000000..faf870920 --- /dev/null +++ b/hack/ingress_test.go @@ -0,0 +1,111 @@ +/* +Copyright 2017 The Kubernetes Authors. + +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 +*/ + +package main + +import ( + "fmt" + "os" + "path" + "path/filepath" + "strings" + "testing" +) + +func TestIngressSuite(t *testing.T) { + pwd, _ := os.Getwd() + filepath.Walk(path.Join(pwd, "/suite"), + func(path string, info os.FileInfo, err error) error { + if strings.HasSuffix(path, ".yaml") { + t.Log(path) + runTestCase(path, t) + } + return nil + }) +} + +func runTestCase(rawtc string, t *testing.T) { + tc, err := parseTestCase(rawtc) + if err != nil { + t.Fatalf("unexpected error reading Ingress test case file %v: %v", rawtc, err) + } + + t.Run(tc.Name, func(t *testing.T) { + t.Logf("starting deploy of requirements for test case '%v'", tc.Name) + err := tc.deploy() + if err != nil { + t.Fatalf("unexpected error in test case deploy process: %v", err) + } + + if tc.Assert == nil { + t.Fatalf("test case %v does not contains tests", tc.Name) + } + + for _, assert := range tc.Assert { + t.Logf("running assert %v", assert.Name) + } + + err = tc.undeploy() + if err != nil { + t.Fatalf("unexpected error in test case deploy process: %v", err) + } + }) +} + +// parseTestCase parses a test case from a yaml file +func parseTestCase(p string) (IngressTestCase, error) { + return IngressTestCase{ + Name: "basic test", + }, nil +} + +// deploy creates the kubernetes object specified in the test case +func (tc IngressTestCase) deploy() error { + if tc.Pod != nil { + return nil + } + + if tc.ReplicationController != nil { + return nil + } + + if tc.Deployment != nil { + return nil + } + + return fmt.Errorf("invalid deployment option. Please check the test case") +} + +// undeploy removes the kubernetes object created by the test case +func (tc IngressTestCase) undeploy() error { + if tc.Pod != nil { + return nil + } + + if tc.ReplicationController != nil { + return nil + } + + if tc.Deployment != nil { + return nil + } + + return nil +} + +func deployIngressController() error { + return nil +} diff --git a/hack/nginx-ingress-controller.yaml b/hack/nginx-ingress-controller.yaml new file mode 100644 index 000000000..560774de4 --- /dev/null +++ b/hack/nginx-ingress-controller.yaml @@ -0,0 +1,47 @@ +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: nginx-ingress-controller + labels: + k8s-app: nginx-ingress-controller + namespace: kube-system +spec: + replicas: 1 + template: + metadata: + labels: + k8s-app: nginx-ingress-controller + spec: + terminationGracePeriodSeconds: 60 + containers: + - image: gcr.io/google_containers/nginx-ingress-controller:0.9.0-beta.7 + name: nginx-ingress-controller + readinessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + livenessProbe: + httpGet: + path: /healthz + port: 10254 + scheme: HTTP + initialDelaySeconds: 10 + timeoutSeconds: 1 + ports: + - containerPort: 80 + hostPort: 80 + - containerPort: 443 + hostPort: 443 + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + args: + - /nginx-ingress-controller + - --default-backend-service=$(POD_NAMESPACE)/default-http-backend diff --git a/hack/suite/0001.yaml b/hack/suite/0001.yaml new file mode 100644 index 000000000..3cf20d57b --- /dev/null +++ b/hack/suite/0001.yaml @@ -0,0 +1 @@ +- \ No newline at end of file