Skip to content

Commit

Permalink
Add controlplane upgrade e2e test using CAPI framework (#845)
Browse files Browse the repository at this point in the history
* Add e2e using capi framework

Signed-off-by: Adrian Pedriza <[email protected]>

* Remove WIP from workflow

Signed-off-by: Adrian Pedriza <[email protected]>

* Remove unused ginkgo test suite for k0smotron HCP

Signed-off-by: Adrian Pedriza <[email protected]>

* Remove unused code

Signed-off-by: Adrian Pedriza <[email protected]>

* Fix typos

Signed-off-by: Adrian Pedriza <[email protected]>

* Rename cluster-template

Signed-off-by: Adrian Pedriza <[email protected]>

* Wait for cluster deploy before update

Signed-off-by: Adrian Pedriza <[email protected]>

---------

Signed-off-by: Adrian Pedriza <[email protected]>
Co-authored-by: Adrian Pedriza <[email protected]>
  • Loading branch information
apedriza and AdrianPedriza authored Feb 10, 2025
1 parent e895d19 commit a71c97c
Show file tree
Hide file tree
Showing 23 changed files with 1,636 additions and 115 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,31 @@ jobs:
uses: ./.github/workflows/capi-smoke-tests.yml
with:
smoke-suite: ${{ matrix.smoke-suite }}
e2e-migration:
name: E2E
needs: build

runs-on: ubuntu-latest

steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v5
with:
go-version-file: go.mod

- name: Build e2e images
run: make docker-build

- name: Run e2e tests
run: make e2e

- name: Archive artifacts
if: failure()
uses: actions/[email protected]
with:
name: e2e-artifacts
path: _artifacts
if-no-files-found: ignore
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ k0smotron-image-bundle.tar
*.swp
*.swo
*~

# test results
_artifacts
3 changes: 1 addition & 2 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,4 @@ linters:
- depguard # Checks if package imports are in a list of acceptable packages
- gofmt # Checks whether code was gofmt-ed
- goheader # Checks is file headers matche a given pattern
- revive # Stricter drop-in replacement for golint
- ginkgolinter # Enforces standards of using ginkgo and gomega
- revive # Stricter drop-in replacement for golint
14 changes: 14 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen
ENVTEST ?= $(LOCALBIN)/setup-envtest
CRDOC ?= $(LOCALBIN)/crdoc

## e2e configuration
E2E_CONF_FILE ?= $(shell pwd)/e2e/config/docker.yaml
SKIP_RESOURCE_CLEANUP ?= false
# Artifacts folder generated for e2e tests
ARTIFACTS ?= $(shell pwd)/_artifacts

# Image URL to use all building/pushing image targets
IMG ?= quay.io/k0sproject/k0smotron:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
Expand Down Expand Up @@ -100,6 +106,14 @@ vet: ## Run go vet against code.
test: manifests generate fmt vet envtest ## Run tests.
KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $(GO_TEST_DIRS) -coverprofile cover.out

.PHONY: e2e
e2e: ## Run the end-to-end tests
go test -v ./e2e -tags e2e \
-artifacts-folder="$(ARTIFACTS)" \
-config="$(E2E_CONF_FILE)" \
-skip-resource-cleanup=$(SKIP_RESOURCE_CLEANUP) \
-timeout=30m

##@ Build

.PHONY: build
Expand Down
25 changes: 24 additions & 1 deletion docs/contributing/contribute-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,27 @@ K0smotron uses [go test](https://pkg.go.dev/testing) as the foundation for all o
Following the best practices suggested in the [Cluster API documentation](https://cluster-api.sigs.k8s.io/developer/core/testing), integration tests are written using [**generic infrastructure providers**](https://cluster-api.sigs.k8s.io/developer/core/testing#generic-providers) rather than a specific provider. This ensures that tests remain agnostic and reusable across different infrastructures, fostering better maintainability and adaptability.

## E2E testing
TBD

K0smotron's end-to-end (E2E) testing leverages the [CAPI E2E framework](https://pkg.go.dev/sigs.k8s.io/cluster-api/test/framework) to provide configurability and utilities that support various phases of E2E testing, including the creation and configuration of the management cluster, waiting for specific resources, log dumping, and more.

To fully utilize CAPI's E2E framework, it is necessary to integrate [Ginkgo](https://onsi.github.io/ginkgo/) into the project. However, in K0smotron, we intentionally avoid using this testing framework for several reasons, primarily to maintain a unified approach to writing tests using standard Go testing conventions. As a result, certain methods from CAPI's E2E framework have been reimplemented within K0smotron to remove their direct dependency on Ginkgo.

### Run E2E

You can run the tests using the command:

``` cmd
make e2e
```

This will perform the following actions:

1. Deploy a local cluster using [Kind](https://github.com/kubernetes-sigs/kind) as the management cluster.
2. Install the desired providers. Basically the same achieved by executing the command `clusterctl init ...`, by including:
- Cluster API for core components.
- k0smotron as controlplane and bootstrap provider.
- Configurable infrastructure provider (currently only docker supported).

1. Execute the E2E test suite.

> NOTE: This command will run the tests using docker as infrastructure provider but it is intended to make use of the configurability offered by the CAPI E2E framework to add other infrastructure providers that can be used in e2e testing.
29 changes: 29 additions & 0 deletions e2e/common.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//go:build e2e

/*
Copyright 2025.
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.
*/

package e2e

// Test suite constants for e2e config variables.
const (
KubernetesVersion = "KUBERNETES_VERSION"
KubernetesVersionManagement = "KUBERNETES_VERSION_MANAGEMENT"
KubernetesVersionFirstUpgradeTo = "KUBERNETES_VERSION_FIRST_UPGRADE_TO"
KubernetesVersionSecondUpgradeTo = "KUBERNETES_VERSION_SECOND_UPGRADE_TO"
ControlPlaneMachineCount = "CONTROL_PLANE_MACHINE_COUNT"
IPFamily = "IP_FAMILY"
)
99 changes: 99 additions & 0 deletions e2e/config/docker.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
# E2E test scenario using local dev images and manifests built from the source tree for following providers:
# - cluster-api
# - bootstrap k0smotron
# - control-plane k0smotron
# - infrastructure docker
images:
- name: quay.io/k0sproject/k0smotron
loadBehavior: mustLoad

providers:
- name: cluster-api
type: CoreProvider
versions:
- name: v1.8.5
value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.8.5/core-components.yaml
type: url
contract: v1beta1
files:
- sourcePath: "../data/shared/v1beta1/metadata.yaml"
replacements:
- old: "imagePullPolicy: Always"
new: "imagePullPolicy: IfNotPresent"
- name: docker
type: InfrastructureProvider
versions:
- name: v1.8.1
value: https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.8.1/infrastructure-components-development.yaml
type: url
contract: v1beta1
files:
- sourcePath: "../data/shared/v1beta1/metadata.yaml"
replacements:
- old: "imagePullPolicy: Always"
new: "imagePullPolicy: IfNotPresent"
files:
- sourcePath: "../data/infrastructure-docker/cluster-template-out-of-cluster.yaml"
- name: k0sproject-k0smotron
type: ControlPlaneProvider
versions:
- name: v1.4.1
value: ../../config/default
contract: v1beta1
files:
- sourcePath: "../../metadata.yaml"
replacements:
- old: "imagePullPolicy: Always"
new: "imagePullPolicy: IfNotPresent"
- old: "image: k0s/k0smotron:latest"
new: "image: quay.io/k0sproject/k0smotron:latest"
- name: k0sproject-k0smotron
type: BootstrapProvider
versions:
- name: v1.4.1
value: ../../config/default
contract: v1beta1
files:
- sourcePath: "../../metadata.yaml"
replacements:
- old: "imagePullPolicy: Always"
new: "imagePullPolicy: IfNotPresent"
- old: "image: k0s/k0smotron:latest"
new: "image: quay.io/k0sproject/k0smotron:latest"

variables:
KUBERNETES_VERSION_MANAGEMENT: "v1.30.0"
KUBERNETES_VERSION: "v1.31.0"
KUBERNETES_VERSION_FIRST_UPGRADE_TO: "v1.30.2+k0s.0"
KUBERNETES_VERSION_SECOND_UPGRADE_TO: "v1.31.2+k0s.0"
IP_FAMILY: "IPv4"
KIND_IMAGE_VERSION: "v1.30.0"
# Used during clusterctl upgrade test
CAPI_CORE_VERSION: "1.8.5"
# Enabling the feature flags by setting the env variables.
CLUSTER_TOPOLOGY: "true"
EXP_MACHINE_POOL: "true"

intervals:
# The array is defined as [timeout, polling interval]
# copied from https://github.com/kubernetes-sigs/cluster-api/blob/main/test/e2e/config/docker.yaml
default/wait-controllers: ["3m", "10s"]
default/wait-cluster: ["5m", "10s"]
default/wait-control-plane: ["10m", "10s"]
default/wait-worker-nodes: ["10m", "10s"]
default/wait-machine-pool-nodes: ["10m", "10s"]
default/wait-delete-cluster: ["3m", "10s"]
default/wait-kube-proxy-upgrade: ["30m", "10s"]
default/wait-machine-pool-upgrade: ["30m", "10s"]
default/wait-nodes-ready: ["10m", "10s"]
default/wait-machine-remediation: ["5m", "10s"]
default/wait-autoscaler: ["5m", "10s"]
bootstrap/wait-deployment-available: ["3m", "10s"]
node-drain/wait-deployment-available: ["3m", "10s"]
node-drain/wait-control-plane: ["15m", "10s"]
node-drain/wait-machine-deleted: ["2m", "10s"]
# Giving a bit more time during upgrade tests
workload-upgrade/wait-cluster: ["10m", "10s"]
workload-upgrade/wait-control-plane: ["20m", "10s"]
workload-upgrade/wait-worker-nodes: ["20m", "10s"]
138 changes: 138 additions & 0 deletions e2e/data/infrastructure-docker/cluster-template-out-of-cluster.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# This cluster template is used when a cluster needs to be deployed using the Out-Of-Cluster mode,
# i.e. the control plane is running on CAPI managed Machines.
apiVersion: cluster.x-k8s.io/v1beta1
kind: Cluster
metadata:
name: ${CLUSTER_NAME}
namespace: ${NAMESPACE}
spec:
clusterNetwork:
pods:
cidrBlocks:
- 192.168.0.0/16
serviceDomain: cluster.local
services:
cidrBlocks:
- 10.128.0.0/12
controlPlaneRef:
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: K0sControlPlane
name: ${CLUSTER_NAME}-docker-test
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerCluster
name: docker-test
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
metadata:
name: docker-test-cp-template
namespace: ${NAMESPACE}
spec:
template:
spec:
customImage: kindest/node:v1.31.0
---
apiVersion: controlplane.cluster.x-k8s.io/v1beta1
kind: K0sControlPlane
metadata:
name: ${CLUSTER_NAME}-docker-test
namespace: ${NAMESPACE}
spec:
replicas: ${CONTROL_PLANE_MACHINE_COUNT}
version: v1.30.1+k0s.0
updateStrategy: Recreate
k0sConfigSpec:
args:
- --enable-worker
k0s:
apiVersion: k0s.k0sproject.io/v1beta1
kind: ClusterConfig
metadata:
name: k0s
spec:
api:
extraArgs:
anonymous-auth: "true"
telemetry:
enabled: false
network:
controlPlaneLoadBalancing:
enabled: false
files:
- path: /tmp/test-file-secret
contentFrom:
secretRef:
name: test-file-secret
key: value
machineTemplate:
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachineTemplate
name: docker-test-cp-template
namespace: ${NAMESPACE}
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerCluster
metadata:
name: docker-test
namespace: ${NAMESPACE}
spec:
---
apiVersion: cluster.x-k8s.io/v1beta1
kind: Machine
metadata:
name: ${CLUSTER_NAME}-docker-test-worker-0
namespace: ${NAMESPACE}
spec:
version: v1.30.1
clusterName: ${CLUSTER_NAME}
bootstrap:
configRef:
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: K0sWorkerConfig
name: docker-test-worker-0
infrastructureRef:
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachine
name: ${CLUSTER_NAME}-docker-test-worker-0
---
apiVersion: bootstrap.cluster.x-k8s.io/v1beta1
kind: K0sWorkerConfig
metadata:
name: docker-test-worker-0
namespace: ${NAMESPACE}
spec:
# version is deliberately different to be able to verify we actually pick it up :)
version: v1.30.1+k0s.0
args:
- --labels=k0sproject.io/foo=bar
preStartCommands:
- echo -n "pre-start" > /tmp/pre-start
postStartCommands:
- echo -n "post-start" > /tmp/post-start
files:
- path: /tmp/test-file
content: test-file
- path: /tmp/test-file-secret
contentFrom:
secretRef:
name: test-file-secret
key: value
---
apiVersion: infrastructure.cluster.x-k8s.io/v1beta1
kind: DockerMachine
metadata:
name: ${CLUSTER_NAME}-docker-test-worker-0
namespace: ${NAMESPACE}
spec:
customImage: kindest/node:v1.31.0
---
apiVersion: v1
kind: Secret
metadata:
name: test-file-secret
namespace: ${NAMESPACE}
type: Opaque
data:
value: dGVzdA==
9 changes: 9 additions & 0 deletions e2e/data/shared/v1beta1/metadata.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# maps release series of major.minor to cluster-api contract version,
# update this file only when you update the version for cluster-api
# CoreProvider and docker InfrastructureProvider in test/e2e/config/k3s-docker.yaml
apiVersion: clusterctl.cluster.x-k8s.io/v1alpha3
kind: Metadata
releaseSeries:
- major: 1
minor: 8
contract: v1beta1
Loading

0 comments on commit a71c97c

Please sign in to comment.