Unverified Commit 13b4d5ec authored by André Carvalho's avatar André Carvalho Committed by GitHub

Run integration tests in travis. (#2)

This commit adds stages for running integration tests with two Kubernetes versions on travis using a local minikube installation.
parent af7b7eee
......@@ -5,11 +5,12 @@ addons:
packages:
- docker-ce
install:
- go get -u github.com/golang/dep/cmd/dep
- mkdir -p $GOPATH/src/github.com/operator-framework && cd $GOPATH/src/github.com/operator-framework
- git clone https://github.com/operator-framework/operator-sdk.git
- cd operator-sdk && make dep && make install
- cd $TRAVIS_BUILD_DIR
- go get -u github.com/golang/dep/cmd/dep
- mkdir -p $GOPATH/src/github.com/operator-framework && cd $GOPATH/src/github.com/operator-framework
- git clone https://github.com/operator-framework/operator-sdk.git
- cd operator-sdk && make dep && make install
- cd $TRAVIS_BUILD_DIR
jobs:
fast_finish: true
include:
......@@ -21,14 +22,30 @@ jobs:
script: make test
env: GOARCH=amd64
go: master
- stage: build
script: make build
env: GOARCH=amd64
go: 1.10.x
- stage: build
script: make build
env: GOARCH=amd64
go: master
- stage: integration
script: ./scripts/localkube-integration.sh
after_failure:
- kubectl get deployment --all-namespaces
- kubectl get pods --all-namespaces
- kubectl describe pods --all-namespaces
- kubectl describe nginx --all-namespaces
env:
- KUBERNETES_VERSION=v1.9.0
- stage: integration
script: ./scripts/localkube-integration.sh
after_failure:
- kubectl get deployment --all-namespaces
- kubectl get pods --all-namespaces
- kubectl describe pods --all-namespaces
- kubectl describe nginx --all-namespaces
env:
- KUBERNETES_VERSION=v1.10.0
- stage: push to docker
script:
- echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin
- if [ -n "$TRAVIS_TAG" ]; then make TAG=$TRAVIS_TAG push; else make push; fi
go: "1.10.x"
if: (NOT type IN (pull_request)) AND (branch = master) OR (tag IS present)
matrix:
allow_failures:
- go: master
......@@ -38,4 +55,9 @@ notifications:
secure: QffxEkIxqwEVgb1bkCWunkErtsPIm2rkwzFiH2cbbBRYQsfYfK1Qn4MwjgftH2zfJxOrAgQ7onMoTc6rxih+XE5V3I/Ct+S6qywqRX60JeoVc/RCP+ywtBnCDwNFtKLc7J8uKh5ZuhdDnx/r7SkCkIZJtoE3QF721PM5JpFNHrNHvs8q6DEgoqHxHHvtWPVuBOiPkLBv0/m7p9SYwXdWbVrjmrhynnvOsmhMfv1sXQUFOSKsus9QESWdct8xnJTOtmsCRzLR7jSViP9e7+QHnyzB/4DcMrhIKKyNptFGR+G30WGY45Wx4w7AvXkDqiNp9RWOFHrt8BGkPv+DbhVEbx1mwArEEVqJEu0Na6WuQGbZUm0uhcjBr+KWJ56AqhmwsX98PHrD6tTfilIQRPGddgltGcYZqNJAl1l/RSZcTGvYdZmGmEPy+xQO3Gg4NmMiRoLe1rC8jNVLodG1yjv3lnnC8sVHROEGHtT4XTYrTc2knGGbZ7s6JKgNztls71+SvISS1PBKOrMMBLC77D8hgeCOY8xePL1UbNmHCroK5i6pDeKohyIhIs+pm93S3kQoBIKuV+oF7y3b+gCQouwAf0B2cnMm0TEci3qlZ8/NOezbaVtxBdeWvlfYk5KxmftYiArwz3AMpgj9wpUu7H6WusSfrl6+WyJN+vSY/OHnOus=
env:
global:
- secure: k8gjK/m52Q5H3RgaPWyU9jCXKLTGA/uFzKGWK3ns1gJEcaMetTXAesPLYewILrNpLLtwbi8iATLNxzf4eLJGtQzpqZkXPdUxrWxcME3Xfh3HxmsLRVFdgwF3tFXtnxXsURjv/MELO4m3fVb/wHGmfeSo4kV3zyaUr2NM8osQ6Y113SB1uAI03modE+Ul9NOzLQzWBW3ET3WlY+7rnavrx5gRkU8i8RGVjzm+brUsn08xM1SSk61+46Y04OJeJV0AV3y4/ELb8/hp6GSrGAPjRsoSLwLEv8tFSSXyCiaM5m5vvAjge84PJcDawOVhxAzB/j0Kt2W5GjemzLFM8/057OkWZAJqNDc7jOiDtOzHItYCwVQA+qlzYfZbUY4YPq52xUKdtuN51OihxJDobbouwn+N4lv0AvJXhv3TG+2mfPTXb80VaKOXhKjXq7WV4lUip0wpRLdAyhroUo5TtW0iODx2vDAoSW2zeOoBYa8jdxpN99hGcnC9M+EBb/SNDEgrFIT7RLT20T9qlXYg4qthWT2Q6g3+kX4CdIete55Cd3VH1oRGO6RxIcSGQO8hSpWbNwfg237V/mLRND+qvayZOoFcx7aB4sm2CdkXRR1IYFGN4NrdAMfYf5neMMhseisMf03/kD8zGNwH5B6p/uw2BUob2f6Pp4UsD2pi1/qZ6x8=
- KUBERNETES_VERSION=v1.10.0
- MINIKUBE_WANTUPDATENOTIFICATION=false
- MINIKUBE_WANTREPORTERRORPROMPT=false
- MINIKUBE_HOME=$HOME
- CHANGE_MINIKUBE_NONE_USER=true
- secure: k8gjK/m52Q5H3RgaPWyU9jCXKLTGA/uFzKGWK3ns1gJEcaMetTXAesPLYewILrNpLLtwbi8iATLNxzf4eLJGtQzpqZkXPdUxrWxcME3Xfh3HxmsLRVFdgwF3tFXtnxXsURjv/MELO4m3fVb/wHGmfeSo4kV3zyaUr2NM8osQ6Y113SB1uAI03modE+Ul9NOzLQzWBW3ET3WlY+7rnavrx5gRkU8i8RGVjzm+brUsn08xM1SSk61+46Y04OJeJV0AV3y4/ELb8/hp6GSrGAPjRsoSLwLEv8tFSSXyCiaM5m5vvAjge84PJcDawOVhxAzB/j0Kt2W5GjemzLFM8/057OkWZAJqNDc7jOiDtOzHItYCwVQA+qlzYfZbUY4YPq52xUKdtuN51OihxJDobbouwn+N4lv0AvJXhv3TG+2mfPTXb80VaKOXhKjXq7WV4lUip0wpRLdAyhroUo5TtW0iODx2vDAoSW2zeOoBYa8jdxpN99hGcnC9M+EBb/SNDEgrFIT7RLT20T9qlXYg4qthWT2Q6g3+kX4CdIete55Cd3VH1oRGO6RxIcSGQO8hSpWbNwfg237V/mLRND+qvayZOoFcx7aB4sm2CdkXRR1IYFGN4NrdAMfYf5neMMhseisMf03/kD8zGNwH5B6p/uw2BUob2f6Pp4UsD2pi1/qZ6x8=
\ No newline at end of file
#!/bin/bash -xe
# Download kubectl, which is a requirement for using minikube.
curl -Lo kubectl https://storage.googleapis.com/kubernetes-release/release/v1.9.0/bin/linux/amd64/kubectl && chmod +x kubectl && sudo mv kubectl /usr/local/bin/
# Download minikube.
curl -Lo minikube https://storage.googleapis.com/minikube/releases/v0.25.2/minikube-linux-amd64 && chmod +x minikube && sudo mv minikube /usr/local/bin/
sudo minikube start --vm-driver=none --kubernetes-version=$KUBERNETES_VERSION
# Fix the kubectl context, as it's often stale.
minikube update-context
# Wait for Kubernetes to be up and ready.
JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}'; until kubectl get nodes -o jsonpath="$JSONPATH" 2>&1 | grep -q "Ready=True"; do sleep 1; done
make build
kubectl create namespace nginx-operator-integration
sed -ie 's/imagePullPolicy: Always/imagePullPolicy: Never/g' deploy/operator.yaml
kubectl apply -f deploy/ --namespace nginx-operator-integration
sleep 30s
kubectl get deployment --all-namespaces
kubectl get pods --all-namespaces
NGINX_OPERATOR_INTEGRATION=1 go test ./...
\ No newline at end of file
......@@ -10,14 +10,17 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
func createNamespace(ns string) error {
func createNamespace(ns string) (func() error, error) {
nilFunc := func() error { return nil }
if out, err := kubectl("create", "namespace", ns); err != nil {
if strings.Contains(string(out), "AlreadyExists") {
return nil
if strings.Contains(string(out)+err.Error(), "AlreadyExists") {
return nilFunc, nil
}
return fmt.Errorf("failed to create namespace %q: %v - out: %v", ns, err, string(out))
return nilFunc, fmt.Errorf("failed to create namespace %q: %v - out: %v", ns, err, string(out))
}
return nil
return func() error {
return deleteNamespace(ns)
}, nil
}
func deleteNamespace(ns string) error {
......@@ -41,8 +44,8 @@ func delete(file string, ns string) error {
return nil
}
func get(obj runtime.Object, name string) error {
out, err := kubectl("get", obj.GetObjectKind().GroupVersionKind().Kind, "-o", "json", name)
func get(obj runtime.Object, name, ns string) error {
out, err := kubectl("get", obj.GetObjectKind().GroupVersionKind().Kind, "-o", "json", name, "--namespace", ns)
if err != nil {
return err
}
......@@ -51,6 +54,9 @@ func get(obj runtime.Object, name string) error {
func kubectl(arg ...string) ([]byte, error) {
cmd := exec.CommandContext(context.TODO(), "kubectl", arg...)
fmt.Printf("Running %v\n", cmd)
return cmd.CombinedOutput()
out, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("Err running cmd %v: %v. Output: %s", cmd, err, string(out))
}
return out, nil
}
......@@ -24,14 +24,11 @@ func TestMain(m *testing.M) {
os.Exit(m.Run())
}
func Test_Operator(t *testing.T) {
if err := createNamespace(testingNamespace); err != nil {
cleanup, err := createNamespace(testingNamespace)
if err != nil {
t.Fatal(err)
}
defer func() {
if err := deleteNamespace(testingNamespace); err != nil {
t.Fatal(err)
}
}()
defer cleanup()
t.Run("simple.yaml", func(t *testing.T) {
if err := apply("testdata/simple.yaml", testingNamespace); err != nil {
......@@ -40,6 +37,7 @@ func Test_Operator(t *testing.T) {
nginx, err := getReadyNginx("simple", 2, 1)
assert.Nil(t, err)
assert.NotNil(t, nginx)
assert.Equal(t, 2, len(nginx.Status.Pods))
assert.Equal(t, 1, len(nginx.Status.Services))
})
......@@ -49,16 +47,17 @@ func getReadyNginx(name string, expectedPods int, expectedSvcs int) (*v1alpha1.N
nginx := &v1alpha1.Nginx{TypeMeta: metav1.TypeMeta{Kind: "Nginx"}}
timeout := time.After(10 * time.Second)
for {
err := get(nginx, name, testingNamespace)
if err != nil {
fmt.Printf("Err getting nginx %q: %v. Retrying...\n", name, err)
}
if len(nginx.Status.Pods) == expectedPods && len(nginx.Status.Services) == expectedSvcs {
return nginx, nil
}
if err := get(nginx, name); err != nil {
return nil, err
}
select {
case <-timeout:
return nil, fmt.Errorf("Timeout waiting for nginx status. Last status: %v", nginx.Status)
default:
return nil, fmt.Errorf("Timeout waiting for nginx status. Last status: %v. Last error: %v", nginx.Status, err)
case <-time.After(time.Millisecond * 100):
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment