Skip to content

Commit 66cfacf

Browse files
authored
Merge pull request #1 from aws/feature/add-e2e-tests
Feature/add e2e tests
2 parents 556e75e + 1aa0e8c commit 66cfacf

File tree

68 files changed

+4791
-32
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+4791
-32
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ pkg/mocks/*
2020
out
2121
.build
2222
.DS_Store
23+
_artifacts
2324

2425
# CAPI used with Tilt.
2526
cluster-api

Dockerfile

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,8 @@
1-
# Build the manager binary
2-
FROM golang:1.17 as builder
3-
4-
WORKDIR /workspace
5-
# Copy the Go Modules manifests
6-
COPY go.mod go.mod
7-
COPY go.sum go.sum
8-
# cache deps before building and copying source so that we don't need to re-download as much
9-
# and so that source changes don't invalidate our downloaded layer
10-
# TODO make this env var an optional input. Maybe use args, but need to consider Tilt usage.
11-
ENV GOPROXY=direct
12-
RUN go mod download
13-
14-
# Copy the go source
15-
COPY main.go main.go
16-
COPY api/ api/
17-
COPY controllers/ controllers/
18-
COPY pkg/ pkg/
19-
20-
# Build
21-
ARG ldflags
22-
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
23-
go build -a -ldflags "${ldflags} -extldflags '-static'" \
24-
-o manager main.go
25-
261
# Use distroless as minimal base image to package the manager binary
272
# Refer to https://github.com/GoogleContainerTools/distroless for more details
283
FROM gcr.io/distroless/static:nonroot
294
WORKDIR /
30-
COPY --from=builder /workspace/manager .
5+
COPY bin/manager-linux-amd64 ./manager
316
USER 65532:65532
327

338
ENTRYPOINT ["/manager"]

Makefile

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# Image URL to use all building/pushing image targets
22
IMG ?= public.ecr.aws/a4z9h2b1/cluster-api-provider-capc:latest
3+
IMG_LOCAL ?= localhost:5000/cluster-api-provider-cloudstack:latest
34

45
# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
56
ifeq (,$(shell go env GOBIN))
@@ -43,7 +44,8 @@ all: build
4344
help: ## Display this help.
4445
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
4546

46-
MANIFEST_GEN_INPUTS=$(shell find ./api ./controllers -type f -name "*test*" -prune -o -name "*zz_generated*" -prune -o -print)
47+
MANIFEST_GEN_INPUTS=$(shell find ./api ./controllers -type f -name "*test*" -prune -o -name "*zz_generated*" -prune -o -print)
48+
4749
# Using a flag file here as config output is too complicated to be a target.
4850
# The following triggers manifest building if $(IMG) differs from that found in config/default/manager_image_patch.yaml.
4951
$(shell grep -qs "$(IMG)" config/default/manager_image_patch_edited.yaml || rm -f config/.flag.mk)
@@ -78,14 +80,21 @@ build: binaries generate-deepcopy lint manifests release-manifests ## Build mana
7880
bin/manager: $(MANAGER_BIN_INPUTS)
7981
go build -o bin/manager main.go
8082

83+
.PHONY: build-for-docker
84+
build-for-docker: bin/manager-linux-amd64 ## Build manager binary for docker image building.
85+
bin/manager-linux-amd64: $(MANAGER_BIN_INPUTS)
86+
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
87+
go build -a -ldflags "${ldflags} -extldflags '-static'" \
88+
-o bin/manager-linux-amd64 main.go
89+
8190
.PHONY: run
8291
run: generate-deepcopy ## Run a controller from your host.
8392
go run ./main.go
8493

8594
# Using a flag file here as docker build doesn't produce a target file.
8695
DOCKER_BUILD_INPUTS=$(MANAGER_BIN_INPUTS) Dockerfile
8796
.PHONY: docker-build
88-
docker-build: .dockerflag.mk ## Build docker image containing the controller manager.
97+
docker-build: generate-deepcopy build-for-docker .dockerflag.mk ## Build docker image containing the controller manager.
8998
.dockerflag.mk: $(DOCKER_BUILD_INPUTS)
9099
docker build -t ${IMG} .
91100
@touch .dockerflag.mk
@@ -173,16 +182,36 @@ pkg/mocks/mock%.go: $(shell find ./pkg/cloud -type f -name "*test*" -prune -o -p
173182
##@ Tilt
174183

175184
.PHONY: tilt-up
176-
tilt-up: cluster-api kind-cluster cluster-api/tilt-settings.json manifests cloud-config # Setup and run tilt for development.
185+
tilt-up: cluster-api kind-cluster cluster-api/tilt-settings.json manifests cloud-config ## Setup and run tilt for development.
177186
export CLOUDSTACK_B64ENCODED_SECRET=$$(base64 -w0 -i cloud-config 2>/dev/null || base64 -b 0 -i cloud-config) && cd cluster-api && tilt up
178187

179188
.PHONY: kind-cluster
180-
kind-cluster: cluster-api # Create a kind cluster with a local Docker repository.
189+
kind-cluster: cluster-api ## Create a kind cluster with a local Docker repository.
181190
-./cluster-api/hack/kind-install-for-capd.sh
182191

183-
cluster-api: # Clone cluster-api repository for tilt use.
192+
cluster-api: ## Clone cluster-api repository for tilt use.
184193
git clone --branch v1.0.0 https://github.com/kubernetes-sigs/cluster-api.git
185194

186195
cluster-api/tilt-settings.json: hack/tilt-settings.json cluster-api
187196
cp ./hack/tilt-settings.json cluster-api
188197

198+
##@ End-to-End Testing
199+
200+
CLUSTER_TEMPLATES_INPUT_FILES=$(shell find test/e2e/data/infrastructure-cloudstack/*/cluster-template*/* test/e2e/data/infrastructure-cloudstack/*/bases/* -type f)
201+
CLUSTER_TEMPLATES_OUTPUT_FILES=$(shell find test/e2e/data/infrastructure-cloudstack -type d -name "cluster-template*" -exec echo {}.yaml \;)
202+
.PHONY: e2e-cluster-templates
203+
e2e-cluster-templates: $(CLUSTER_TEMPLATES_OUTPUT_FILES) ## Generate cluster template files for e2e testing.
204+
cluster-template%yaml: bin/kustomize $(CLUSTER_TEMPLATES_INPUT_FILES)
205+
kustomize build --load-restrictor LoadRestrictionsNone $(basename $@) > $@
206+
207+
e2e-essentials: bin/ginkgo e2e-cluster-templates kind-cluster ## Fulfill essential tasks for e2e testing.
208+
IMG=$(IMG_LOCAL) make manifests docker-build docker-push
209+
210+
JOB ?= .*
211+
run-e2e: e2e-essentials ## Run e2e testing. JOB is an optional REGEXP to select certainn test cases to run. e.g. JOB=PR-Blocking, JOB=Conformance
212+
cd test/e2e && \
213+
ginkgo -v -trace -tags=e2e -focus=$(JOB) -skip=Conformance -nodes=1 --noColor=false ./... -- \
214+
-e2e.artifacts-folder=${PROJECT_DIR}/_artifacts \
215+
-e2e.config=${PROJECT_DIR}/test/e2e/config/cloudstack.yaml \
216+
-e2e.skip-resource-cleanup=false -e2e.use-existing-cluster=true
217+
kind delete clusters capi-test

config/default/credentials.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ metadata:
55
namespace: system
66
type: Opaque
77
data:
8-
cloud-config: ${CLOUDSTACK_B64ENCODED_SECRET}
8+
cloud-config: "${CLOUDSTACK_B64ENCODED_SECRET}"

test/e2e/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
kind-install-for-capd.sh
2+
data/infrastructure-cloudstack/v1beta1/*.yaml

test/e2e/README.md

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# Testing
2+
3+
This document is to help developers understand how to test CAPC.
4+
5+
## Code Origin
6+
7+
The most of the code under test/e2e is from CAPD (Cluster API for Docker) e2e testing (https://github.com/kubernetes-sigs/cluster-api/tree/main/test/e2e)
8+
The ACS specific things are under test/e2e/config and test/e2e/data/infrastructure-cloudstack.
9+
10+
## e2e
11+
12+
This section describes how to run end-to-end (e2e) testing with CAPC.
13+
14+
### Requirements
15+
16+
* Admin access to a Apache CloudStack (ACS) server
17+
* The testing must occur on a host that can access the ACS server
18+
* Docker ([download](https://www.docker.com/get-started))
19+
* Kind ([download](https://kind.sigs.k8s.io/docs/user/quick-start/#installing-with-a-package-manager))
20+
21+
### Environment variables
22+
23+
The first step to running the e2e tests is setting up the required environment variables:
24+
25+
| Environment variable | Description | Default Value |
26+
|---------------------------------------------|----------------------------------------------------------------------------------|-----------------------------|
27+
| `CLOUDSTACK_ZONE_NAME` | The zone name | `zone1` |
28+
| `CLOUDSTACK_NETWORK_NAME` | The network name. If not exisiting an isolated network with the name is created. | `Shared1` |
29+
| `CLUSTER_ENDPOINT_IP` | The cluster endpoint IP | `172.16.2.199` |
30+
| `CLUSTER_ENDPOINT_PORT` | The cluster endpoint port | `6443` |
31+
| `CLUSTER_ENDPOINT_IP_2` | The cluster endpoint IP for a second cluster | `172.16.2.199` |
32+
| `CLUSTER_ENDPOINT_PORT_2` | The cluster endpoint port for a second cluster | `6444` |
33+
| `CLOUDSTACK_CONTROL_PLANE_MACHINE_OFFERING` | The machine offering for the control plane VM instances | `Large Instance` |
34+
| `CLOUDSTACK_WORKER_MACHINE_OFFERING` | The machine offering for the worker node VM instances | `Medium Instance` |
35+
| `CLOUDSTACK_TEMPLATE_NAME` | The machine template for both control plane and worke node VM instances | `kube-v1.20.10/ubuntu-2004` |
36+
| `CLOUDSTACK_SSH_KEY_NAME` | The name of SSH key added to the VM instances | `CAPCKeyPair6` |
37+
38+
You also have to export `CLOUDSTACK_B64ENCODED_SECRET` environment variable using this command `export CLOUDSTACK_B64ENCODED_SECRET=$(base64 -i cloud-config)` after creating `cloud-config` file with the following format.
39+
40+
```
41+
[Global]
42+
api-key = XXXXX
43+
secret-key = XXXXX
44+
api-url = http://192.168.1.96:8080/client/api
45+
verify-ssl = true or false
46+
```
47+
48+
The api-key and secret-key can be found or generated at Home > Accounts > admin > Users > admin of the ACS management UI. `verify-ssl` is an optional flag and its default value is true. CAPC skips verifying the host SSL certificates when the flag is set to false.
49+
50+
### Running the e2e tests
51+
52+
Run the following command to execute the CAPC e2e tests:
53+
54+
```shell
55+
make run-e2e
56+
```
57+
This command runs all e2e test cases.
58+
59+
You can specify JOB environment variable which value is a regular expression to select test cases to execute.
60+
For example,
61+
62+
```shell
63+
JOB=PR-Blocking make run
64+
```
65+
This command runs the e2e tests that contains `PR-Blocking` in their spec names.

test/e2e/affinity_group.go

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/*
2+
Copyright 2020 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package e2e
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"os"
23+
"path/filepath"
24+
25+
. "github.com/onsi/ginkgo"
26+
. "github.com/onsi/gomega"
27+
corev1 "k8s.io/api/core/v1"
28+
"k8s.io/utils/pointer"
29+
30+
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
31+
"sigs.k8s.io/cluster-api/util"
32+
)
33+
34+
// AffinityGroupSpec implements a test that verifies that an app deployed to the workload cluster works.
35+
func AffinityGroupSpec(ctx context.Context, inputGetter func() CommonSpecInput) {
36+
var (
37+
specName = "affinity-group"
38+
input CommonSpecInput
39+
namespace *corev1.Namespace
40+
cancelWatches context.CancelFunc
41+
clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult
42+
affinityIds []string
43+
)
44+
45+
BeforeEach(func() {
46+
Expect(ctx).NotTo(BeNil(), "ctx is required for %s spec", specName)
47+
input = inputGetter()
48+
Expect(input.E2EConfig).ToNot(BeNil(), "Invalid argument. input.E2EConfig can't be nil when calling %s spec", specName)
49+
Expect(input.ClusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. input.ClusterctlConfigPath must be an existing file when calling %s spec", specName)
50+
Expect(input.BootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. input.BootstrapClusterProxy can't be nil when calling %s spec", specName)
51+
Expect(os.MkdirAll(input.ArtifactFolder, 0750)).To(Succeed(), "Invalid argument. input.ArtifactFolder can't be created for %s spec", specName)
52+
53+
Expect(input.E2EConfig.Variables).To(HaveKey(KubernetesVersion))
54+
55+
// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
56+
namespace, cancelWatches = setupSpecNamespace(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder)
57+
clusterResources = new(clusterctl.ApplyClusterTemplateAndWaitResult)
58+
})
59+
60+
It("Should have host affinity group when affinity is pro", func() {
61+
executeTest(ctx, input, namespace, specName, clusterResources, "pro")
62+
})
63+
64+
It("Should have host affinity group when affinity is anti", func() {
65+
Skip("The ACS used by Prow doesn't have multiple hosts in the target zone")
66+
executeTest(ctx, input, namespace, specName, clusterResources, "anti")
67+
})
68+
69+
AfterEach(func() {
70+
// Dumps all the resources in the spec namespace, then cleanups the cluster object and the spec namespace itself.
71+
dumpSpecResourcesAndCleanup(ctx, specName, input.BootstrapClusterProxy, input.ArtifactFolder, namespace, cancelWatches, clusterResources.Cluster, input.E2EConfig.GetIntervals, input.SkipCleanup)
72+
73+
err := CheckAffinityGroupsDeleted(affinityIds)
74+
if err != nil {
75+
Fail(err.Error())
76+
}
77+
})
78+
}
79+
80+
func executeTest(ctx context.Context, input CommonSpecInput, namespace *corev1.Namespace, specName string, clusterResources *clusterctl.ApplyClusterTemplateAndWaitResult, affinityType string) []string {
81+
clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
82+
ClusterProxy: input.BootstrapClusterProxy,
83+
CNIManifestPath: input.E2EConfig.GetVariable(CNIPath),
84+
ConfigCluster: clusterctl.ConfigClusterInput{
85+
LogFolder: filepath.Join(input.ArtifactFolder, "clusters", input.BootstrapClusterProxy.GetName()),
86+
ClusterctlConfigPath: input.ClusterctlConfigPath,
87+
KubeconfigPath: input.BootstrapClusterProxy.GetKubeconfigPath(),
88+
InfrastructureProvider: clusterctl.DefaultInfrastructureProvider,
89+
Flavor: "affinity-group-" + affinityType,
90+
Namespace: namespace.Name,
91+
ClusterName: fmt.Sprintf("%s-%s", specName, util.RandomString(6)),
92+
KubernetesVersion: input.E2EConfig.GetVariable(KubernetesVersion),
93+
ControlPlaneMachineCount: pointer.Int64Ptr(3),
94+
WorkerMachineCount: pointer.Int64Ptr(3),
95+
},
96+
WaitForClusterIntervals: input.E2EConfig.GetIntervals(specName, "wait-cluster"),
97+
WaitForControlPlaneIntervals: input.E2EConfig.GetIntervals(specName, "wait-control-plane"),
98+
WaitForMachineDeployments: input.E2EConfig.GetIntervals(specName, "wait-worker-nodes"),
99+
}, clusterResources)
100+
101+
affinityIds := CheckAffinityGroup(clusterResources.Cluster.Name, affinityType)
102+
103+
By("PASSED!")
104+
105+
return affinityIds
106+
}

test/e2e/affinity_group_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
//go:build e2e
2+
// +build e2e
3+
4+
/*
5+
Copyright 2020 The Kubernetes Authors.
6+
7+
Licensed under the Apache License, Version 2.0 (the "License");
8+
you may not use this file except in compliance with the License.
9+
You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
*/
19+
20+
package e2e
21+
22+
import (
23+
"context"
24+
. "github.com/onsi/ginkgo"
25+
)
26+
27+
var _ = Describe("When testing affinity group", func() {
28+
AffinityGroupSpec(context.TODO(), func() CommonSpecInput {
29+
return CommonSpecInput{
30+
E2EConfig: e2eConfig,
31+
ClusterctlConfigPath: clusterctlConfigPath,
32+
BootstrapClusterProxy: bootstrapClusterProxy,
33+
ArtifactFolder: artifactFolder,
34+
SkipCleanup: skipCleanup,
35+
}
36+
})
37+
38+
})

0 commit comments

Comments
 (0)