Skip to content

Commit 013896a

Browse files
authored
test,hack: add e2e test for new project layout (#3060)
1 parent f031621 commit 013896a

File tree

11 files changed

+715
-11
lines changed

11 files changed

+715
-11
lines changed

.travis.yml

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ jobs:
132132
name: Subcommands on Kubernetes
133133
script: make test-subcommand
134134

135-
# Build and test go
135+
# Build and test go for legacy project layouts
136136
- <<: *test
137-
name: Go on Kubernetes
137+
name: Go for legacy project layouts on Kubernetes
138138
before_script:
139139
- (cd / && go get github.com/mattn/goveralls)
140140
script:
@@ -143,6 +143,23 @@ jobs:
143143
- make test-e2e-go
144144
- make test-integration
145145

146+
# Build and test go for new project layouts
147+
- name: Go e2e tests for new project layouts
148+
before_install:
149+
# hack/ci/check-doc-only-update.sh needs to be sourced so
150+
# that it can properly exit the test early with success
151+
- source hack/ci/check-doc-only-update.sh
152+
script:
153+
- make test-e2e-go-new
154+
after_success:
155+
- echo "E2E tests passed"
156+
after_failure:
157+
- echo "E2E tests failed"
158+
- kubectl get all --all-namespaces
159+
- kubectl get events --all-namespaces --field-selector=type=Warning
160+
services:
161+
- docker
162+
146163
# Build and test helm
147164
- <<: *test
148165
name: Helm on Kubernetes

Makefile

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,14 +254,17 @@ test-subcommand-scorecard:
254254
test-subcommand-olm-install:
255255
./hack/tests/subcommand-olm-install.sh
256256

257-
# E2E and integration tests.
258-
.PHONY: test-e2e test-e2e-go test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm test-integration
257+
# E2E tests.
258+
.PHONY: test-e2e test-e2e-go test-e2e-go-new test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm
259259

260-
test-e2e: test-e2e-go test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm ## Run the e2e tests
260+
test-e2e: test-e2e-go test-e2e-go-new test-e2e-ansible test-e2e-ansible-molecule test-e2e-helm ## Run the e2e tests
261261

262262
test-e2e-go:
263263
./hack/tests/e2e-go.sh $(ARGS)
264264

265+
test-e2e-go-new:
266+
K8S_VERSION=$(K8S_VERSION) ./hack/tests/e2e-go-new.sh
267+
265268
test-e2e-ansible: image-build-ansible
266269
./hack/tests/e2e-ansible.sh
267270

@@ -271,5 +274,8 @@ test-e2e-ansible-molecule: image-build-ansible
271274
test-e2e-helm: image-build-helm
272275
./hack/tests/e2e-helm.sh
273276

274-
test-integration:
277+
# Integration tests.
278+
.PHONY: test-integration
279+
280+
test-integration: ## Run integration tests
275281
./hack/tests/integration.sh

hack/lib/common.sh

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
#!/usr/bin/env bash
22

3-
function log() { printf '%s\n' "$*"; }
4-
function error() { error_text "ERROR:" $* >&2; }
5-
function fatal() { error "$@"; exit 1; }
6-
3+
# Skip fetching and untaring the tools by setting the SKIP_FETCH_TOOLS variable
4+
# in your environment to any value:
5+
#
6+
# $ SKIP_FETCH_TOOLS=1 ./test.sh
7+
#
8+
# If you skip fetching tools, this script will use the tools already on your
9+
# machine, but rebuild the operator-sdk binary.
10+
SKIP_FETCH_TOOLS=${SKIP_FETCH_TOOLS:-""}
11+
# Current version of the 'kind' binary. Update this when a new breaking release
12+
# is made for a docker.io/kindest/node:${K8S_VERSION} image.
13+
KIND_VERSION="v0.8.1"
14+
# ENVTEST_TOOLS_VERSION is the version of k8s server tarballs used for envtest.
15+
# TODO: use K8S_VERSION once we start building our own server binary tarballs.
16+
ENVTEST_TOOLS_VERSION="1.16.4"
717
# Turn colors in this script off by setting the NO_COLOR variable in your
818
# environment to any value:
919
NO_COLOR=${NO_COLOR:-""}
@@ -17,6 +27,14 @@ else
1727
reset_color=''
1828
fi
1929

30+
# Roots used by tests.
31+
tmp_root=/tmp
32+
tmp_sdk_root=$tmp_root/operator-sdk
33+
34+
function log() { printf '%s\n' "$*"; }
35+
function error() { error_text "ERROR:" $* >&2; }
36+
function fatal() { error "$@"; exit 1; }
37+
2038
function header_text {
2139
echo "$header_color$*$reset_color"
2240
}
@@ -37,3 +55,79 @@ function is_installed {
3755
function install_service_monitor_crd {
3856
kubectl apply -f https://raw.githubusercontent.com/coreos/prometheus-operator/release-0.35/example/prometheus-operator-crd/monitoring.coreos.com_servicemonitors.yaml
3957
}
58+
59+
# prepare the e2e test staging dir, containing test tools (SKIP_FETCH_TOOLS aware).
60+
function prepare_staging_dir {
61+
62+
header_text "preparing staging dir $1"
63+
64+
if [[ -z "$SKIP_FETCH_TOOLS" ]]; then
65+
rm -rf "$1"
66+
else
67+
rm -f "$1/bin/operator-sdk"
68+
fi
69+
70+
mkdir -p "$1"
71+
}
72+
73+
# Fetch k8s API gen tools and make it available under $1/bin.
74+
function fetch_tools {
75+
if [[ -z "$SKIP_FETCH_TOOLS" ]]; then
76+
fetch_envtest_tools $@
77+
install_kind $@
78+
fi
79+
}
80+
81+
# Fetch tools required for envtest.
82+
function fetch_envtest_tools {
83+
84+
# TODO: make our own tarball containing envtest binaries: etcd, kubectl, kube-apiserver
85+
#
86+
# To get k8s server binaries:
87+
# server_tar="kubernetes-server-$(go env GOOS)-$(go env GOARCH).tar.gz"
88+
# url=https://dl.k8s.io/$K8S_VERSION/$server_tar
89+
# curl -fL --retry 3 --keepalive-time 2 "${url}" -o "${tmp_sdk_root}/${server_tar}"
90+
# tar -zxvf "${tmp_sdk_root}/${server_tar}"
91+
92+
local tools_archive_name="kubebuilder-tools-${ENVTEST_TOOLS_VERSION}-$(go env GOOS)-$(go env GOARCH).tar.gz"
93+
local tools_download_url="https://storage.googleapis.com/kubebuilder-tools/$tools_archive_name"
94+
95+
local tools_archive_path="$1/$tools_archive_name"
96+
if [[ ! -f $tools_archive_path ]]; then
97+
header_text "fetching envtest tools"
98+
curl -sSLo "$tools_archive_path" $tools_download_url
99+
else
100+
header_text "using existing envtest tools in $tools_archive_path"
101+
fi
102+
tar -zvxf "$tools_archive_path" -C "$1/" --strip-components=1
103+
}
104+
105+
# Set up test and envtest vars
106+
function setup_envs {
107+
header_text "setting up env vars"
108+
109+
export PATH="$1"/bin:$PATH
110+
export TEST_ASSET_KUBECTL="$1"/bin/kubectl
111+
export TEST_ASSET_KUBE_APISERVER="$1"/bin/kube-apiserver
112+
export TEST_ASSET_ETCD="$1"/bin/etcd
113+
}
114+
115+
# Build the operator-sdk binary.
116+
function build_sdk {
117+
header_text "building operator-sdk"
118+
119+
GO111MODULE=on make build/operator-sdk
120+
mv ./build/operator-sdk "$1"/bin/operator-sdk
121+
}
122+
123+
# Install the 'kind' binary at version $KIND_VERSION.
124+
function install_kind {
125+
126+
local kind_path="${1}/bin/kind"
127+
128+
header_text "installing kind $KIND_VERSION"
129+
local kind_binary="kind-$(go env GOOS)-$(go env GOARCH)"
130+
local kind_url="https://github.com/kubernetes-sigs/kind/releases/download/${KIND_VERSION}/$kind_binary"
131+
curl -sSLo "$kind_path" $kind_url
132+
chmod +x "$kind_path"
133+
}

hack/lib/image_lib.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ function is_latest_tag() {
120120
#
121121
function load_image_if_kind() {
122122
if [[ "$(kubectl config current-context)" == "kind-kind" ]]; then
123-
if which kind 2>/dev/null; then
123+
if is_installed kind; then
124124
kind load docker-image "$1"
125125
fi
126126
fi

hack/tests/e2e-go-new.sh

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/env bash
2+
3+
# remove running containers on exit
4+
function cleanup() {
5+
kind delete cluster
6+
}
7+
8+
set -o errexit
9+
set -o nounset
10+
set -o pipefail
11+
12+
source ./hack/lib/common.sh
13+
source ./hack/lib/test_lib.sh
14+
15+
test_dir=./test
16+
tests=$test_dir/e2e-new
17+
18+
export TRACE=1
19+
export GO111MODULE=on
20+
21+
: ${K8S_VERSION:?"must be set"}
22+
23+
prepare_staging_dir $tmp_sdk_root
24+
fetch_tools $tmp_sdk_root
25+
# These envtest environment variables are required for the default unit tests
26+
# scaffolded in the test operator project. No e2e tests currently use envtest.
27+
setup_envs $tmp_sdk_root
28+
build_sdk $tmp_sdk_root
29+
30+
# Create a cluster of version $K8S_VERSION.
31+
kind create cluster -v 4 --retain --wait=1m \
32+
--config $test_dir/kind-config.yaml \
33+
--image=kindest/node:$K8S_VERSION
34+
35+
kind export kubeconfig
36+
37+
kubectl cluster-info
38+
39+
docker pull gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
40+
kind load docker-image gcr.io/kubebuilder/kube-rbac-proxy:v0.5.0
41+
42+
trap_add cleanup EXIT
43+
go test -v $tests

test/e2e-new/e2e_suite.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
// Copyright 2020 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
// Modified from https://github.com/kubernetes-sigs/kubebuilder/tree/39224f0/test/e2e/v3
16+
17+
package e2e
18+
19+
import (
20+
"fmt"
21+
"path/filepath"
22+
"strings"
23+
"time"
24+
25+
. "github.com/onsi/ginkgo" //nolint:golint
26+
. "github.com/onsi/gomega" //nolint:golint
27+
28+
"github.com/operator-framework/operator-sdk/test/e2e-new/utils"
29+
)
30+
31+
var _ = Describe("operator-sdk", func() {
32+
Context("with the new project layout", func() {
33+
var tc *utils.TestContext
34+
BeforeEach(func() {
35+
36+
By("creating a new test context")
37+
var err error
38+
tc, err = utils.NewTestContext("operator-sdk", "GO111MODULE=on")
39+
Expect(err).NotTo(HaveOccurred())
40+
Expect(tc.Prepare()).To(Succeed())
41+
})
42+
43+
AfterEach(func() {
44+
By("cleaning up created API objects during test process")
45+
tc.CleanupManifests(filepath.Join("config", "default"))
46+
47+
By("removing container image and work dir")
48+
tc.Destroy()
49+
})
50+
51+
It("should generate a runnable project", func() {
52+
var controllerPodName string
53+
By("initializing a project")
54+
err := tc.Init(
55+
"--project-version", "3-alpha",
56+
"--domain", tc.Domain,
57+
"--fetch-deps=false")
58+
Expect(err).Should(Succeed())
59+
60+
By("creating an API definition")
61+
err = tc.CreateAPI(
62+
"--group", tc.Group,
63+
"--version", tc.Version,
64+
"--kind", tc.Kind,
65+
"--namespaced",
66+
"--resource",
67+
"--controller",
68+
"--make=false")
69+
Expect(err).Should(Succeed())
70+
71+
By("implementing the API")
72+
Expect(utils.InsertCode(
73+
filepath.Join(tc.Dir, "api", tc.Version, fmt.Sprintf("%s_types.go", strings.ToLower(tc.Kind))),
74+
fmt.Sprintf(`type %sSpec struct {
75+
`, tc.Kind),
76+
` // +optional
77+
Count int `+"`"+`json:"count,omitempty"`+"`"+`
78+
`)).Should(Succeed())
79+
80+
By("building the operator image")
81+
err = tc.Make("docker-build", "IMG="+tc.ImageName)
82+
Expect(err).Should(Succeed())
83+
84+
By("loading the operator image into the test cluster")
85+
err = tc.LoadImageToKindCluster()
86+
Expect(err).Should(Succeed())
87+
88+
By("deploying the controller manager")
89+
err = tc.Make("deploy", "IMG="+tc.ImageName)
90+
Expect(err).Should(Succeed())
91+
92+
By("ensuring the controller-manager pod is running as expected")
93+
verifyControllerUp := func() error {
94+
// Get pod name
95+
podOutput, err := tc.Kubectl.Get(
96+
true,
97+
"pods", "-l", "control-plane=controller-manager",
98+
"-o", "go-template={{ range .items }}{{ if not .metadata.deletionTimestamp }}{{ .metadata.name }}"+
99+
"{{ \"\\n\" }}{{ end }}{{ end }}")
100+
Expect(err).NotTo(HaveOccurred())
101+
podNames := utils.GetNonEmptyLines(podOutput)
102+
if len(podNames) != 1 {
103+
return fmt.Errorf("expect 1 controller pods running, but got %d", len(podNames))
104+
}
105+
controllerPodName = podNames[0]
106+
Expect(controllerPodName).Should(ContainSubstring("controller-manager"))
107+
108+
// Validate pod status
109+
status, err := tc.Kubectl.Get(
110+
true,
111+
"pods", controllerPodName, "-o", "jsonpath={.status.phase}")
112+
Expect(err).NotTo(HaveOccurred())
113+
if status != "Running" {
114+
return fmt.Errorf("controller pod in %s status", status)
115+
}
116+
return nil
117+
}
118+
Eventually(verifyControllerUp, time.Minute, time.Second).Should(Succeed())
119+
120+
By("creating an instance of CR")
121+
// currently controller-runtime doesn't provide a readiness probe, we retry a few times
122+
// we can change it to probe the readiness endpoint after CR supports it.
123+
sampleFile := filepath.Join("config", "samples",
124+
fmt.Sprintf("%s_%s_%s.yaml", tc.Group, tc.Version, strings.ToLower(tc.Kind)))
125+
Eventually(func() error {
126+
_, err = tc.Kubectl.Apply(true, "-f", sampleFile)
127+
return err
128+
}, time.Minute, time.Second).Should(Succeed())
129+
130+
By("ensuring the created resource object gets reconciled in controller")
131+
managerContainerLogs := func() string {
132+
logOutput, err := tc.Kubectl.Logs(controllerPodName, "-c", "manager")
133+
Expect(err).NotTo(HaveOccurred())
134+
return logOutput
135+
}
136+
Eventually(managerContainerLogs, time.Minute, time.Second).Should(ContainSubstring("Successfully Reconciled"))
137+
})
138+
})
139+
})

0 commit comments

Comments
 (0)