Skip to content

Commit fe32969

Browse files
committed
Add helper utilities for testing
- Add common kind utility for provider-based testing - Add flag helper - Add scheme helper - Add component helper
1 parent cf29f37 commit fe32969

File tree

10 files changed

+467
-1
lines changed

10 files changed

+467
-1
lines changed

Makefile

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,9 @@ TAG ?= dev
5454
ARCH ?= amd64
5555
ALL_ARCH = amd64 arm arm64 ppc64le s390x
5656

57+
# Allow overriding the imagePullPolicy
58+
PULL_POLICY ?= Always
59+
5760
all: test manager clusterctl
5861

5962
help: ## Display this help
@@ -71,6 +74,11 @@ test: generate lint ## Run tests
7174
test-integration: ## Run integration tests
7275
go test -v -tags=integration ./test/integration/...
7376

77+
.PHONY: test-e2e
78+
test-e2e: ## Run e2e tests
79+
PULL_POLICY=IfNotPresent $(MAKE) docker-build
80+
go test -v -tags=e2e -timeout=1h ./test/e2e/... -args --managerImage $(CONTROLLER_IMG)-$(ARCH):$(TAG)
81+
7482
## --------------------------------------
7583
## Binaries
7684
## --------------------------------------
@@ -147,6 +155,7 @@ modules: ## Runs go mod to ensure modules are up to date.
147155
docker-build: ## Build the docker image for controller-manager
148156
docker build --pull --build-arg ARCH=$(ARCH) . -t $(CONTROLLER_IMG)-$(ARCH):$(TAG)
149157
MANIFEST_IMG=$(CONTROLLER_IMG)-$(ARCH) MANIFEST_TAG=$(TAG) $(MAKE) set-manifest-image
158+
$(MAKE) set-manifest-pull-policy
150159

151160
.PHONY: docker-push
152161
docker-push: ## Push the docker image
@@ -176,12 +185,18 @@ docker-push-manifest: ## Push the fat manifest docker image.
176185
@for arch in $(ALL_ARCH); do docker manifest annotate --arch $${arch} ${CONTROLLER_IMG}:${TAG} ${CONTROLLER_IMG}-$${arch}:${TAG}; done
177186
docker manifest push --purge $(CONTROLLER_IMG):$(TAG)
178187
MANIFEST_IMG=$(CONTROLLER_IMG) MANIFEST_TAG=$(TAG) $(MAKE) set-manifest-image
188+
$(MAKE) set-manifest-pull-policy
179189

180190
.PHONY: set-manifest-image
181191
set-manifest-image:
182192
$(info Updating kustomize image patch file for manager resource)
183193
sed -i'' -e 's@image: .*@image: '"${MANIFEST_IMG}:$(MANIFEST_TAG)"'@' ./config/default/manager_image_patch.yaml
184194

195+
.PHONY: set-manifest-pull-policy
196+
set-manifest-pull-policy:
197+
$(info Updating kustomize pull policy file for manager resource)
198+
sed -i'' -e 's@imagePullPolicy: .*@imagePullPolicy: '"$(PULL_POLICY)"'@' ./config/default/manager_pull_policy.yaml
199+
185200
## --------------------------------------
186201
## Release
187202
## --------------------------------------
@@ -203,6 +218,7 @@ release: clean-release ## Builds and push container images using the latest git
203218
# Set the manifest image to the production bucket.
204219
MANIFEST_IMG=$(PROD_REGISTRY)/$(IMAGE_NAME) MANIFEST_TAG=$(RELEASE_TAG) \
205220
$(MAKE) set-manifest-image
221+
PULL_POLICY=IfNotPresent $(MAKE) set-manifest-pull-policy
206222
$(MAKE) release-manifests
207223
$(MAKE) release-binaries
208224

config/default/kustomization.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ namePrefix: capi-
1818
patchesStrategicMerge:
1919
- manager_image_patch.yaml
2020
- manager_label_patch.yaml
21+
- manager_pull_policy.yaml
2122

2223
# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in crd/kustomization.yaml
2324
#- manager_webhook_patch.yaml
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: controller-manager
5+
namespace: system
6+
spec:
7+
template:
8+
spec:
9+
containers:
10+
- name: manager
11+
imagePullPolicy: Always

scripts/ci-integration.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ build_containers() {
5757
export CONTROLLER_IMG="${CONTROLLER_REPO}"
5858
export EXAMPLE_PROVIDER_IMG="${EXAMPLE_PROVIDER_REPO}"
5959

60-
"${MAKE}" docker-build TAG="${VERSION}" ARCH="${GOARCH}"
60+
"${MAKE}" docker-build TAG="${VERSION}" ARCH="${GOARCH}" PULL_POLICY=IfNotPresent
6161
"${MAKE}" docker-build-example-provider TAG="${VERSION}" ARCH="${GOARCH}"
6262
}
6363

test/e2e/capi_test.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// +build e2e
2+
3+
/*
4+
Copyright 2018 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package e2e_test
20+
21+
import (
22+
"github.com/onsi/ginkgo"
23+
)
24+
25+
var _ = ginkgo.Describe("functional tests", func() {
26+
})

test/e2e/e2e_suite_test.go

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
// +build e2e
2+
3+
/*
4+
Copyright 2018 The Kubernetes Authors.
5+
6+
Licensed under the Apache License, Version 2.0 (the "License");
7+
you may not use this file except in compliance with the License.
8+
You may obtain a copy of the License at
9+
10+
http://www.apache.org/licenses/LICENSE-2.0
11+
12+
Unless required by applicable law or agreed to in writing, software
13+
distributed under the License is distributed on an "AS IS" BASIS,
14+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
See the License for the specific language governing permissions and
16+
limitations under the License.
17+
*/
18+
19+
package e2e_test
20+
21+
import (
22+
"context"
23+
"flag"
24+
"fmt"
25+
"io/ioutil"
26+
"os"
27+
"os/exec"
28+
"path"
29+
"testing"
30+
"time"
31+
32+
"github.com/onsi/ginkgo"
33+
"github.com/onsi/gomega"
34+
appsv1 "k8s.io/api/apps/v1"
35+
apimachinerytypes "k8s.io/apimachinery/pkg/types"
36+
"sigs.k8s.io/cluster-api/test/helpers/kind"
37+
"sigs.k8s.io/cluster-api/test/helpers/scheme"
38+
"sigs.k8s.io/cluster-api/util"
39+
crclient "sigs.k8s.io/controller-runtime/pkg/client"
40+
)
41+
42+
func TestE2E(t *testing.T) {
43+
gomega.RegisterFailHandler(ginkgo.Fail)
44+
ginkgo.RunSpecs(t, "e2e Suite")
45+
}
46+
47+
const (
48+
capiNamespace = "capi-system"
49+
capiDeploymentName = "capi-controller-manager"
50+
setupTimeout = 10 * 60
51+
)
52+
53+
var (
54+
managerImage = flag.String("managerImage", "", "Docker image to load into the kind cluster for testing")
55+
capiComponents = flag.String("capiComponents", "", "URL to CAPI components to load")
56+
kustomizeBinary = flag.String("kustomizeBinary", "kustomize", "path to the kustomize binary")
57+
58+
kindCluster kind.Cluster
59+
kindClient crclient.Client
60+
suiteTmpDir string
61+
)
62+
63+
var _ = ginkgo.BeforeSuite(func() {
64+
fmt.Fprintf(ginkgo.GinkgoWriter, "Setting up kind cluster\n")
65+
66+
var err error
67+
suiteTmpDir, err = ioutil.TempDir("", "capi-e2e-suite")
68+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
69+
70+
kindCluster = kind.Cluster{
71+
Name: "capi-test-" + util.RandomString(6),
72+
}
73+
kindCluster.Setup()
74+
loadManagerImage(kindCluster)
75+
76+
// Deploy the CAPA components
77+
deployCAPIComponents(kindCluster)
78+
79+
kindClient, err = crclient.New(kindCluster.RestConfig(), crclient.Options{Scheme: scheme.SetupScheme()})
80+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
81+
82+
// Verify capi components are deployed
83+
waitDeployment(capiNamespace, capiDeploymentName)
84+
}, setupTimeout)
85+
86+
var _ = ginkgo.AfterSuite(func() {
87+
fmt.Fprintf(ginkgo.GinkgoWriter, "Tearing down kind cluster\n")
88+
kindCluster.Teardown()
89+
os.RemoveAll(suiteTmpDir)
90+
})
91+
92+
func waitDeployment(namespace, name string) {
93+
fmt.Fprintf(ginkgo.GinkgoWriter, "Ensuring %s/%s is deployed\n", namespace, name)
94+
gomega.Eventually(
95+
func() (int32, error) {
96+
deployment := &appsv1.Deployment{}
97+
if err := kindClient.Get(context.TODO(), apimachinerytypes.NamespacedName{Namespace: namespace, Name: name}, deployment); err != nil {
98+
return 0, err
99+
}
100+
return deployment.Status.ReadyReplicas, nil
101+
}, 5*time.Minute, 15*time.Second,
102+
).ShouldNot(gomega.BeZero())
103+
}
104+
105+
func loadManagerImage(kindCluster kind.Cluster) {
106+
if managerImage != nil && *managerImage != "" {
107+
kindCluster.LoadImage(*managerImage)
108+
}
109+
}
110+
111+
func applyManifests(kindCluster kind.Cluster, manifests *string) {
112+
gomega.Expect(manifests).ToNot(gomega.BeNil())
113+
fmt.Fprintf(ginkgo.GinkgoWriter, "Applying manifests for %s\n", *manifests)
114+
gomega.Expect(*manifests).ToNot(gomega.BeEmpty())
115+
kindCluster.ApplyYAML(*manifests)
116+
}
117+
118+
func deployCAPIComponents(kindCluster kind.Cluster) {
119+
if capiComponents != nil && *capiComponents != "" {
120+
applyManifests(kindCluster, capiComponents)
121+
return
122+
}
123+
124+
fmt.Fprintf(ginkgo.GinkgoWriter, "Generating CAPI manifests\n")
125+
126+
// Build the manifests using kustomize
127+
capiManifests, err := exec.Command(*kustomizeBinary, "build", "../../config/default").Output()
128+
if err != nil {
129+
if exitError, ok := err.(*exec.ExitError); ok {
130+
fmt.Fprintf(ginkgo.GinkgoWriter, "Error: %s\n", string(exitError.Stderr))
131+
}
132+
}
133+
gomega.Expect(err).NotTo(gomega.HaveOccurred())
134+
135+
// write out the manifests
136+
manifestFile := path.Join(suiteTmpDir, "cluster-api-components.yaml")
137+
gomega.Expect(ioutil.WriteFile(manifestFile, capiManifests, 0644)).NotTo(gomega.HaveOccurred())
138+
139+
// apply generated manifests
140+
applyManifests(kindCluster, &manifestFile)
141+
}

test/helpers/components/common.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
Copyright 2018 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 common
18+
19+
import (
20+
"context"
21+
"fmt"
22+
"time"
23+
24+
"github.com/onsi/ginkgo"
25+
"github.com/onsi/gomega"
26+
appsv1 "k8s.io/api/apps/v1"
27+
"sigs.k8s.io/cluster-api/test/helpers/flag"
28+
"sigs.k8s.io/cluster-api/test/helpers/kind"
29+
"sigs.k8s.io/controller-runtime/pkg/client"
30+
)
31+
32+
const (
33+
CAPIVersion = "v0.2.2"
34+
)
35+
36+
var (
37+
capiComponents = flag.DefineOrLookupStringFlag("capiComponents", "https://github.com/kubernetes-sigs/cluster-api/releases/download/"+CAPIVersion+"/cluster-api-components.yaml", "URL to CAPI components to load")
38+
)
39+
40+
func DeployCAPIComponents(kindCluster kind.Cluster) {
41+
gomega.Expect(capiComponents).ToNot(gomega.BeNil())
42+
fmt.Fprintf(ginkgo.GinkgoWriter, "Applying cluster-api components\n")
43+
gomega.Expect(*capiComponents).ToNot(gomega.BeEmpty())
44+
kindCluster.ApplyYAML(*capiComponents)
45+
}
46+
47+
func WaitDeployment(c client.Client, namespace, name string) {
48+
fmt.Fprintf(ginkgo.GinkgoWriter, "Ensuring %s/%s is deployed\n", namespace, name)
49+
gomega.Eventually(
50+
func() (int32, error) {
51+
deployment := &appsv1.Deployment{}
52+
if err := c.Get(context.TODO(), client.ObjectKey{Namespace: namespace, Name: name}, deployment); err != nil {
53+
return 0, err
54+
}
55+
return deployment.Status.ReadyReplicas, nil
56+
}, 5*time.Minute, 15*time.Second,
57+
).ShouldNot(gomega.BeZero())
58+
}

test/helpers/flag/flag.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
Copyright 2018 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 flag
18+
19+
import (
20+
"flag"
21+
)
22+
23+
func DefineOrLookupStringFlag(name string, value string, usage string) *string {
24+
f := flag.Lookup(name)
25+
if f != nil {
26+
v := f.Value.String()
27+
return &v
28+
}
29+
return flag.String(name, value, usage)
30+
}

0 commit comments

Comments
 (0)