Skip to content
This repository was archived by the owner on Aug 12, 2025. It is now read-only.

Commit 022e5e7

Browse files
authored
Merge pull request #205 from cpanato/tests-conformance
✨ Add conformance tests
2 parents bba2102 + 32f7748 commit 022e5e7

File tree

9 files changed

+231
-6
lines changed

9 files changed

+231
-6
lines changed

Makefile

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,16 @@ e2e-image:
234234
.PHONY: e2e
235235
e2e: e2e-image
236236
# This is the name used inside the component.yaml for the container that runs the manager
237-
# The image gets loaded inside kind from ./test/e2e/config/packet-dev.yaml
237+
# The image gets loaded inside kind from ./test/e2e/config/packet-ci.yaml
238238
$(E2E_FLAGS) $(MAKE) -C $(TEST_E2E_DIR) run
239239

240+
# Run conformance tests
241+
.PHONY: conformance
242+
conformance: e2e-image
243+
# This is the name used inside the component.yaml for the container that runs the manager
244+
# The image gets loaded inside kind from ./test/e2e/config/packet-ci.yaml
245+
$(E2E_FLAGS) $(MAKE) -C $(TEST_E2E_DIR) run-conformance
246+
240247
# Build manager binary
241248
manager: $(MANAGER)
242249
$(MANAGER): generate fmt vet
@@ -407,4 +414,4 @@ cluster-init-manual: core managerless release
407414
.PHONY: modules
408415
modules: ## Runs go mod to ensure modules are up to date.
409416
go mod tidy
410-
cd $(TOOLS_DIR); go mod tidy
417+
cd $(TOOLS_DIR); go mod tidy

scripts/ci-conformance.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/bin/bash
2+
3+
# Copyright 2020 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
###############################################################################
18+
19+
# This script is executed by presubmit `pull-cluster-api-provider-azure-e2e`
20+
# To run locally, set PACKET_API_KEY, PROJECT_ID, ``
21+
22+
set -o errexit
23+
set -o nounset
24+
set -o pipefail
25+
26+
REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")/..
27+
cd "${REPO_ROOT}" || exit 1
28+
29+
# shellcheck source=../hack/ensure-go.sh
30+
source "${REPO_ROOT}/hack/ensure-go.sh"
31+
# shellcheck source=../hack/ensure-kind.sh
32+
source "${REPO_ROOT}/hack/ensure-kind.sh"
33+
# shellcheck source=../hack/ensure-kubectl.sh
34+
source "${REPO_ROOT}/hack/ensure-kubectl.sh"
35+
# shellcheck source=../hack/ensure-kustomize.sh
36+
source "${REPO_ROOT}/hack/ensure-kustomize.sh"
37+
# shellcheck source=../hack/ensure-packet-cli.sh
38+
source "${REPO_ROOT}/hack/ensure-packet-cli.sh"
39+
40+
# Verify the required Environment Variables are present.
41+
: "${PACKET_API_KEY:?Environment variable empty or not defined.}"
42+
: "${PROJECT_ID:?Environment variable empty or not defined.}"
43+
44+
get_random_facility() {
45+
# local FACILITIES=("sjc1" "lax1" "ams1" "dfw2" "ewr1" "ny5")
46+
local FACILITIES=("ewr1")
47+
echo "${FACILITIES[${RANDOM} % ${#FACILITIES[@]}]}"
48+
}
49+
50+
export GINKGO_NODES=3
51+
export FACILITY="${FACILITY:-$(get_random_facility)}"
52+
export PACKET_TOKEN=${PACKET_API_KEY}
53+
54+
export SSH_KEY_NAME=capp-e2e-$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 12 ; echo '')
55+
export SSH_KEY_PATH=/tmp/${SSH_KEY_NAME}
56+
export SSH_KEY_UUID=""
57+
create_ssh_key() {
58+
echo "generating new ssh key"
59+
ssh-keygen -t rsa -f ${SSH_KEY_PATH} -N '' 2>/dev/null <<< y >/dev/null
60+
echo "importing ssh key "
61+
SSH_KEY_STRING=$(cat ${SSH_KEY_PATH}.pub)
62+
SSH_KEY_UUID=$(packet ssh-key create --key "${SSH_KEY_STRING}" --label "${SSH_KEY_NAME}" --json | jq -r '.id')
63+
}
64+
65+
cleanup() {
66+
echo "removing ssh key"
67+
packet ssh-key delete --id ${SSH_KEY_UUID} --force || true
68+
rm -f ${SSH_KEY_PATH} || true
69+
70+
${REPO_ROOT}/hack/log/redact.sh || true
71+
}
72+
73+
create_ssh_key
74+
trap cleanup EXIT
75+
76+
export SSH_KEY=${SSH_KEY_NAME}
77+
make conformance
78+
test_status="${?}"

test/e2e/Makefile

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,22 @@ ARTIFACTS ?= ${REPO_ROOT}/_artifacts
5757
SKIP_RESOURCE_CLEANUP ?= false
5858
USE_EXISTING_CLUSTER ?= false
5959
GINKGO_NOCOLOR ?= false
60+
E2E_DATA_DIR ?= $(TEST_E2E_DIR)/data
61+
KUBETEST_CONF_PATH ?= $(abspath $(E2E_DATA_DIR)/kubetest/conformance.yaml)
6062

6163
.PHONY: run
6264
run: ginkgo ## Run the end-to-end tests
6365
cd $(TEST_E2E_DIR); $(GINKGO) -v -trace -tags=e2e -focus=$(GINKGO_FOCUS) -nodes=$(GINKGO_NODES) --noColor=$(GINKGO_NOCOLOR) . -- \
6466
-e2e.artifacts-folder="$(ARTIFACTS)" \
6567
-e2e.config="$(E2E_CONF_FILE)" \
66-
-e2e.skip-resource-cleanup=$(SKIP_RESOURCE_CLEANUP) -e2e.use-existing-cluster=$(USE_EXISTING_CLUSTER)
68+
-e2e.skip-resource-cleanup=$(SKIP_RESOURCE_CLEANUP) \
69+
-e2e.use-existing-cluster=$(USE_EXISTING_CLUSTER)
70+
71+
.PHONY: run-conformance
72+
run-conformance: ginkgo ## Run the conformance tests
73+
cd $(TEST_E2E_DIR); $(GINKGO) -v -trace -stream -progress -tags=e2e -focus='Conformance Tests' -nodes=$(GINKGO_NODES) --noColor=$(GINKGO_NOCOLOR) . -- \
74+
-e2e.artifacts-folder="$(ARTIFACTS)" \
75+
-e2e.config="$(E2E_CONF_FILE)" \
76+
-kubetest.config-file=$(KUBETEST_CONF_PATH) \
77+
-e2e.skip-resource-cleanup=$(SKIP_RESOURCE_CLEANUP) \
78+
-e2e.use-existing-cluster=$(USE_EXISTING_CLUSTER)

test/e2e/common.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,8 @@ func dumpSpecResourcesAndCleanup(ctx context.Context, specName string, clusterPr
8282
Deleter: bootstrapClusterProxy.GetClient(),
8383
Name: namespace.Name,
8484
})
85-
86-
// Will call the clean resources just to make sure we clean everything
87-
By(fmt.Sprintf("Making sure there is no leftover running for %s", cluster.Name))
8885
}
86+
8987
cancelWatches()
9088
redactLogs()
9189
}

test/e2e/config/packet-ci.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ variables:
7676
WORKER_NODE_TYPE: "t1.small"
7777
POD_CIDR: "192.168.0.0/16"
7878
SERVICE_CIDR: "172.26.0.0/16"
79+
CONFORMANCE_WORKER_MACHINE_COUNT: "3"
80+
CONFORMANCE_CONTROL_PLANE_MACHINE_COUNT: "1"
7981
CNI: "../../templates/addons/calico.yaml"
8082
REDACT_LOG_SCRIPT: "../../../hack/log/redact.sh"
8183

test/e2e/conformance_test.go

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// +build e2e
2+
3+
/*
4+
Copyright 2020 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
20+
21+
import (
22+
"context"
23+
"fmt"
24+
"os"
25+
"path/filepath"
26+
"strconv"
27+
28+
. "github.com/onsi/ginkgo"
29+
. "github.com/onsi/gomega"
30+
corev1 "k8s.io/api/core/v1"
31+
"k8s.io/utils/pointer"
32+
clusterv1 "sigs.k8s.io/cluster-api/api/v1alpha3"
33+
capi_e2e "sigs.k8s.io/cluster-api/test/e2e"
34+
"sigs.k8s.io/cluster-api/test/framework/clusterctl"
35+
"sigs.k8s.io/cluster-api/test/framework/kubetest"
36+
"sigs.k8s.io/cluster-api/util"
37+
)
38+
39+
var _ = Describe("Conformance Tests", func() {
40+
var (
41+
ctx = context.TODO()
42+
specName = "conformance-tests"
43+
namespace *corev1.Namespace
44+
cancelWatches context.CancelFunc
45+
cluster *clusterv1.Cluster
46+
clusterName string
47+
clusterctlLogFolder string
48+
)
49+
50+
BeforeEach(func() {
51+
Expect(e2eConfig).ToNot(BeNil(), "Invalid argument. e2eConfig can't be nil when calling %s spec", specName)
52+
Expect(clusterctlConfigPath).To(BeAnExistingFile(), "Invalid argument. clusterctlConfigPath must be an existing file when calling %s spec", specName)
53+
Expect(bootstrapClusterProxy).ToNot(BeNil(), "Invalid argument. bootstrapClusterProxy can't be nil when calling %s spec", specName)
54+
Expect(os.MkdirAll(artifactFolder, 0755)).To(Succeed(), "Invalid argument. artifactFolder can't be created for %s spec", specName)
55+
Expect(kubetestConfigFilePath).ToNot(BeNil(), "Invalid argument. kubetestConfigFilePath can't be nil")
56+
57+
Expect(e2eConfig.Variables).To(HaveKey(capi_e2e.KubernetesVersion))
58+
Expect(e2eConfig.Variables).To(HaveKey(capi_e2e.CNIPath))
59+
60+
clusterName = fmt.Sprintf("capp-conf-%s", util.RandomString(6))
61+
62+
// Setup a Namespace where to host objects for this spec and create a watcher for the namespace events.
63+
namespace, cancelWatches = setupSpecNamespace(ctx, specName, bootstrapClusterProxy, artifactFolder)
64+
65+
// We need to override clusterctl apply log folder to avoid getting our credentials exposed.
66+
clusterctlLogFolder = filepath.Join(os.TempDir(), "clusters", bootstrapClusterProxy.GetName())
67+
})
68+
69+
Measure(specName, func(b Benchmarker) {
70+
var err error
71+
72+
workerMachineCount, err := strconv.ParseInt(e2eConfig.GetVariable("CONFORMANCE_WORKER_MACHINE_COUNT"), 10, 64)
73+
Expect(err).NotTo(HaveOccurred())
74+
controlPlaneMachineCount, err := strconv.ParseInt(e2eConfig.GetVariable("CONFORMANCE_CONTROL_PLANE_MACHINE_COUNT"), 10, 64)
75+
Expect(err).NotTo(HaveOccurred())
76+
77+
runtime := b.Time("cluster creation", func() {
78+
result := clusterctl.ApplyClusterTemplateAndWait(ctx, clusterctl.ApplyClusterTemplateAndWaitInput{
79+
ClusterProxy: bootstrapClusterProxy,
80+
ConfigCluster: clusterctl.ConfigClusterInput{
81+
LogFolder: clusterctlLogFolder,
82+
ClusterctlConfigPath: clusterctlConfigPath,
83+
KubeconfigPath: bootstrapClusterProxy.GetKubeconfigPath(),
84+
InfrastructureProvider: clusterctl.DefaultInfrastructureProvider,
85+
Flavor: clusterctl.DefaultFlavor,
86+
Namespace: namespace.Name,
87+
ClusterName: clusterName,
88+
KubernetesVersion: e2eConfig.GetVariable(capi_e2e.KubernetesVersion),
89+
ControlPlaneMachineCount: pointer.Int64Ptr(controlPlaneMachineCount),
90+
WorkerMachineCount: pointer.Int64Ptr(workerMachineCount),
91+
},
92+
WaitForClusterIntervals: e2eConfig.GetIntervals(specName, "wait-cluster"),
93+
WaitForControlPlaneIntervals: e2eConfig.GetIntervals(specName, "wait-control-plane"),
94+
WaitForMachineDeployments: e2eConfig.GetIntervals(specName, "wait-worker-nodes"),
95+
})
96+
97+
cluster = result.Cluster
98+
})
99+
100+
b.RecordValue("cluster creation", runtime.Seconds())
101+
workloadProxy := bootstrapClusterProxy.GetWorkloadCluster(ctx, namespace.Name, clusterName)
102+
runtime = b.Time("conformance suite", func() {
103+
kubetest.Run(
104+
kubetest.RunInput{
105+
ClusterProxy: workloadProxy,
106+
NumberOfNodes: int(workerMachineCount),
107+
ConfigFilePath: kubetestConfigFilePath,
108+
},
109+
)
110+
})
111+
b.RecordValue("conformance suite run time", runtime.Seconds())
112+
}, 1)
113+
114+
AfterEach(func() {
115+
dumpSpecResourcesAndCleanup(ctx, specName, bootstrapClusterProxy, artifactFolder, namespace, cancelWatches, cluster, e2eConfig.GetIntervals, clusterName, clusterctlLogFolder, skipCleanup)
116+
})
117+
})
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
ginkgo.focus: \[Conformance\]
2+
disable-log-dump: true
3+
ginkgo.progress: true
4+
ginkgo.slowSpecThreshold: 120.0
5+
ginkgo.flakeAttempts: 3
6+
ginkgo.trace: true
7+
ginkgo.v: true

test/e2e/suite_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,17 @@ var (
6969

7070
// bootstrapClusterProxy allows to interact with the bootstrap cluster to be used for the e2e tests.
7171
bootstrapClusterProxy framework.ClusterProxy
72+
73+
// kubetestConfigFilePath is the path to the kubetest configuration file
74+
kubetestConfigFilePath string
7275
)
7376

7477
func init() {
7578
flag.StringVar(&configPath, "e2e.config", "", "path to the e2e config file")
7679
flag.StringVar(&artifactFolder, "e2e.artifacts-folder", "", "folder where e2e test artifact should be stored")
7780
flag.BoolVar(&skipCleanup, "e2e.skip-resource-cleanup", false, "if true, the resource cleanup after tests will be skipped")
7881
flag.BoolVar(&useExistingCluster, "e2e.use-existing-cluster", false, "if true, the test uses the current cluster instead of creating a new one (default discovery rules apply)")
82+
flag.StringVar(&kubetestConfigFilePath, "kubetest.config-file", "", "path to the kubetest configuration file")
7983
}
8084

8185
func TestE2E(t *testing.T) {

0 commit comments

Comments
 (0)