Skip to content

Commit

Permalink
Adds e2e Testing (#133)
Browse files Browse the repository at this point in the history
Signed-off-by: Daneyon Hansen <[email protected]>
  • Loading branch information
danehans authored Dec 2, 2020
1 parent 6cbba1e commit f341fae
Show file tree
Hide file tree
Showing 6 changed files with 478 additions and 6 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/prbuild.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ jobs:
- name: test
run: |
go mod vendor
make local-cluster install deploy test
make local-cluster test test-e2e
codespell:
name: Codespell
runs-on: ubuntu-latest
Expand Down
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ VERSION ?= $(GIT_REF)
OLD_VERSION ?= main
NEW_VERSION ?= $(OLD_VERSION)

# Used as a go test argument for running e2e tests.
TEST ?= .*

# Image URL to use all building/pushing image targets
IMAGE ?= docker.io/projectcontour/contour-operator

Expand Down Expand Up @@ -190,3 +193,8 @@ release: ## Prepares a tagged release of the operator.
.PHONY: release
release:
./hack/release/make-release-tag.sh $(OLD_VERSION) $(NEW_VERSION)

test-e2e: ## Runs e2e tests.
.PHONY: test-e2e
test-e2e: deploy
go test -timeout 20m -count 1 -v -tags e2e -run "$(TEST)" ./test/e2e
22 changes: 17 additions & 5 deletions docs/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,9 @@ make manager

This produces a `contour-operator` binary in your `$GOPATH/bin` directory and runs go fmt and go vet against the code.

### Running the unit tests
### Running the tests

You can run all the unit tests for the project:
To run all the unit tests for the project:

```
make check
Expand All @@ -54,7 +54,20 @@ To run the tests for a single package, change to package directory and run:
go test .
```

__Note:__ Unit tests must pass for your PR to get merged.
The e2e tests require a Kubernetes cluster and uses your current cluster context.
To create a [kind](https://kind.sigs.k8s.io/) Kubernetes cluster:

```
make local-cluster
```

To run the e2e tests for the project:

```
make test-e2e
```

__Note:__ Unit and e2e tests must pass for your PR to get merged.

## Contribution workflow

Expand Down Expand Up @@ -169,8 +182,7 @@ Operator CRDs and run the operator in the foreground:
make run
```

Before submitting your changes, follow the image-based deployment instructions to ensure the operator works as
expected within a Kubernetes cluster.
Before submitting your changes, run the [required tests](#Running-the-tests).

## DCO Sign off

Expand Down
123 changes: 123 additions & 0 deletions test/e2e/operator_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright Project Contour 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 under the License.

// +build e2e

package e2e

import (
"context"
"os"
"testing"
"time"

operatorv1alpha1 "github.com/projectcontour/contour-operator/api/v1alpha1"

appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

var (
// kclient is the Kubernetes client used for e2e tests.
kclient client.Client
// ctx is an empty context used for client calls.
ctx = context.TODO()
// operatorName is the name of the operator.
operatorName = "contour-operator"
// operatorNs is the name of the operator's namespace.
operatorNs = "contour-operator"
// defaultContourNs is the default spec.namespace.name of a Contour.
defaultContourNs = "projectcontour"
// testUrl is the url used to test e2e functionality.
testUrl = "http://local.projectcontour.io/"
// expectedDeploymentConditions are the expected status conditions of a
// deployment.
expectedDeploymentConditions = []appsv1.DeploymentCondition{
{Type: appsv1.DeploymentAvailable, Status: corev1.ConditionTrue},
}
)

func TestMain(m *testing.M) {
cl, err := newClient()
if err != nil {
os.Exit(1)
}
kclient = cl

os.Exit(m.Run())
}

func TestOperatorDeploymentAvailable(t *testing.T) {
t.Helper()
if err := waitForDeploymentStatusConditions(ctx, kclient, 3*time.Minute, operatorName, operatorNs, expectedDeploymentConditions...); err != nil {
t.Fatalf("failed to observe expected conditions for deployment %s/%s: %v", operatorNs, operatorName, err)
}
t.Logf("observed expected status conditions for deployment %s/%s", operatorNs, operatorName)
}

func TestDefaultContour(t *testing.T) {
testName := "test-default-contour"
if err := newContour(ctx, kclient, testName, operatorNs); err != nil {
t.Fatalf("failed to create contour %s/%s: %v", operatorNs, testName, err)
}
t.Logf("created contour %s/%s", operatorNs, testName)

expectedContourConditions := []metav1.Condition{
{Type: operatorv1alpha1.ContourAvailableConditionType, Status: metav1.ConditionTrue},
// TODO [danehans]: Update when additional status conditions are added to Contour.
}
if err := waitForContourStatusConditions(ctx, kclient, 5*time.Minute, testName, operatorNs, expectedContourConditions...); err != nil {
t.Fatalf("failed to observe expected status conditions for contour %s/%s: %v", operatorNs, testName, err)
}
t.Logf("observed expected status conditions for contour %s/%s", testName, operatorNs)

// Create a sample workload for e2e testing.
appName := "kuard"
if err:= newDeployment(ctx, kclient, appName, defaultContourNs, "gcr.io/kuar-demo/kuard-amd64:1", 3); err != nil {
t.Fatalf("failed to create deployment %s/%s: %v", defaultContourNs, appName, err)
}
t.Logf("created deployment %s/%s", defaultContourNs, appName)

if err := waitForDeploymentStatusConditions(ctx, kclient, 3*time.Minute, appName, defaultContourNs, expectedDeploymentConditions...); err != nil {
t.Fatalf("failed to observe expected status conditions for deployment %s/%s: %v", defaultContourNs, appName, err)
}
t.Logf("observed expected status conditions for deployment %s/%s", defaultContourNs, appName)

if err := newClusterIPService(ctx, kclient, appName, defaultContourNs, 80, 8080); err != nil {
t.Fatalf("failed to create service %s/%s: %v", defaultContourNs, appName, err)
}
t.Logf("created service %s/%s", defaultContourNs, appName)

if err := newIngress(ctx, kclient, appName, defaultContourNs, appName, 80); err != nil {
t.Fatalf("failed to create ingress %s/%s: %v", defaultContourNs, appName, err)
}
t.Logf("created ingress %s/%s", defaultContourNs, appName)

if err := waitForHTTPResponse(testUrl, 1*time.Minute); err != nil {
t.Fatalf("failed to receive http response for %q: %v", testUrl, err)
}
t.Logf("received http response for %q", testUrl)

// Ensure the default contour can be deleted and clean-up.
if err := deleteContour(ctx, kclient, 3*time.Minute, testName, operatorNs); err != nil {
t.Fatalf("failed to delete contour %s/%s: %v", operatorNs, testName, err)
}

// Delete the operand namespace since contour.spec.namespace.removeOnDeletion
// defaults to false.
if err := deleteNamespace(ctx, kclient, 5*time.Minute, defaultContourNs); err != nil {
t.Fatalf("failed to delete namespace %s: %v", defaultContourNs, err)
}
}
Loading

0 comments on commit f341fae

Please sign in to comment.