Skip to content

Commit b4ee2af

Browse files
authored
[repo-ci-improvement] : update GHA to run e2e tests (#248)
* update GHA to run e2e tests * simplify go versions used for ci tests * update helm version, update Makefile * update k8s version * fix linting * address review comments
1 parent 3510bc4 commit b4ee2af

File tree

7 files changed

+225
-28
lines changed

7 files changed

+225
-28
lines changed

.github/filters.yml

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Any file that is not a doc *.md file
2+
src:
3+
- "!**/**.md"

.github/workflows/ci.yml

+106-9
Original file line numberDiff line numberDiff line change
@@ -6,37 +6,92 @@ on:
66
- main
77
pull_request: null
88

9+
permissions:
10+
contents: read
11+
pull-requests: read
12+
actions: read
13+
14+
concurrency:
15+
group: ci-${{ github.ref }}
16+
cancel-in-progress: true
17+
918
jobs:
10-
ci:
19+
changes:
20+
runs-on: ubuntu-latest
21+
outputs:
22+
paths: ${{ steps.filter.outputs.changes }}
23+
steps:
24+
- uses: actions/checkout@v4
25+
- name: Harden Runner
26+
uses: step-security/harden-runner@v2
27+
with:
28+
disable-sudo: true
29+
egress-policy: block
30+
allowed-endpoints: >
31+
api.github.com:443
32+
github.com:443
33+
- uses: dorny/paths-filter@v3
34+
id: filter
35+
with:
36+
base: ${{ github.ref }}
37+
filters: .github/filters.yml
38+
39+
build-test:
1140
runs-on: ubuntu-latest
12-
strategy:
13-
matrix:
14-
go-version: [ 'stable', '1.22' ]
41+
needs: changes
42+
if: ${{ contains(fromJSON(needs.changes.outputs.paths), 'src') }}
1543
steps:
44+
- name: Harden Runner
45+
uses: step-security/harden-runner@v2
46+
with:
47+
disable-sudo: true
48+
egress-policy: block
49+
allowed-endpoints: >
50+
api.github.com:443
51+
github.com:443
52+
golang.org:443
53+
proxy.golang.org:443
54+
sum.golang.org:443
55+
objects.githubusercontent.com:443
56+
storage.googleapis.com:443
57+
cli.codecov.io:443
58+
api.codecov.io:443
59+
raw.githubusercontent.com:443
60+
get.helm.sh:443
61+
1662
- uses: actions/checkout@v4
1763
with:
1864
fetch-depth: 0
19-
- uses: actions/setup-go@v4
65+
- uses: actions/setup-go@v5
2066
with:
21-
go-version: ${{ matrix.go-version }}
67+
go-version-file: go.mod
68+
check-latest: true
69+
2270
- name: Vet
2371
run: make vet
24-
- name: Lint
25-
run: make lint
72+
73+
- name: lint
74+
uses: golangci/golangci-lint-action@v6
75+
with:
76+
version: latest
77+
2678
- name: Helm Lint
2779
run: make helm-lint
80+
2881
- name: Test
2982
run: make test
83+
3084
- name: Upload coverage reports to Codecov
3185
uses: codecov/codecov-action@v4
3286
with:
3387
files: ./coverage.out
34-
fail_ci_if_error: true
3588
verbose: true
3689
token: ${{ secrets.CODECOV_TOKEN }}
3790
slug: linode/linode-cloud-controller-manager
91+
3892
- name: Build
3993
run: make build
94+
4095
docker-build:
4196
runs-on: ubuntu-latest
4297
steps:
@@ -61,3 +116,45 @@ jobs:
61116
labels: ${{ steps.meta.outputs.labels }}
62117
build-args: |
63118
REV=${{ github.ref_name }}
119+
120+
e2e-tests:
121+
runs-on: ubuntu-latest
122+
needs: changes
123+
if: ${{ contains(fromJSON(needs.changes.outputs.paths), 'src') }}
124+
env:
125+
GITHUB_TOKEN: ${{ secrets.github_token }}
126+
LINODE_TOKEN: ${{ secrets.LINODE_TOKEN }}
127+
IMG: linode/linode-cloud-controller-manager:${{ github.ref == 'refs/heads/main' && 'latest' || format('pr-{0}', github.event.number) || github.ref_name }}
128+
LINODE_REGION: us-lax
129+
LINODE_CONTROL_PLANE_MACHINE_TYPE: g6-standard-2
130+
LINODE_MACHINE_TYPE: g6-standard-2
131+
WORKER_NODES: '2'
132+
steps:
133+
- uses: actions/checkout@v4
134+
with:
135+
fetch-depth: 0
136+
137+
- name: Set up Go
138+
uses: actions/setup-go@v5
139+
with:
140+
go-version-file: 'go.mod'
141+
check-latest: true
142+
143+
- name: Login to Docker Hub
144+
uses: docker/login-action@v3
145+
with:
146+
username: ${{ secrets.DOCKER_USERNAME }}
147+
password: ${{ secrets.DOCKER_PASSWORD }}
148+
149+
- name: Install devbox
150+
uses: jetify-com/[email protected]
151+
152+
- name: Setup CAPL Management Kind Cluster and CAPL Child Cluster For Testing
153+
run: devbox run mgmt-and-capl-cluster
154+
155+
- name: Run E2E Tests
156+
run: devbox run e2e-test
157+
158+
- name: Cleanup Resources
159+
if: always()
160+
run: devbox run cleanup-cluster

Makefile

+96-15
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,29 @@
1-
IMG ?= linode/linode-cloud-controller-manager:canary
2-
RELEASE_DIR ?= release
3-
PLATFORM ?= linux/amd64
1+
IMG ?= linode/linode-cloud-controller-manager:canary
2+
RELEASE_DIR ?= release
3+
PLATFORM ?= linux/amd64
44

55
# Use CACHE_BIN for tools that cannot use devbox and LOCALBIN for tools that can use either method
6-
CACHE_BIN ?= $(CURDIR)/bin
7-
LOCALBIN ?= $(CACHE_BIN)
8-
9-
DEVBOX_BIN ?= $(DEVBOX_PACKAGES_DIR)/bin
6+
CACHE_BIN ?= $(CURDIR)/bin
7+
LOCALBIN ?= $(CACHE_BIN)
8+
9+
DEVBOX_BIN ?= $(DEVBOX_PACKAGES_DIR)/bin
10+
HELM ?= $(LOCALBIN)/helm
11+
HELM_VERSION ?= v3.16.3
12+
13+
#####################################################################
14+
# Dev Setup
15+
#####################################################################
16+
CLUSTER_NAME ?= ccm-$(shell git rev-parse --short HEAD)
17+
K8S_VERSION ?= "v1.31.2"
18+
CAPI_VERSION ?= "v1.6.3"
19+
CAAPH_VERSION ?= "v0.2.1"
20+
CAPL_VERSION ?= "v0.7.1"
21+
CONTROLPLANE_NODES ?= 1
22+
WORKER_NODES ?= 1
23+
LINODE_FIREWALL_ENABLED ?= true
24+
LINODE_REGION ?= us-lax
25+
LINODE_OS ?= linode/ubuntu22.04
26+
KUBECONFIG_PATH ?= $(CURDIR)/test-cluster-kubeconfig.yaml
1027

1128
# if the $DEVBOX_PACKAGES_DIR env variable exists that means we are within a devbox shell and can safely
1229
# use devbox's bin for our tools
@@ -41,10 +58,15 @@ vet: fmt
4158

4259
.PHONY: lint
4360
lint:
44-
docker run --rm -v "$(shell pwd):/var/work:ro" -w /var/work \
45-
golangci/golangci-lint:v1.57.2 golangci-lint run -v --timeout=5m
46-
docker run --rm -v "$(shell pwd):/var/work:ro" -w /var/work/e2e \
47-
golangci/golangci-lint:v1.57.2 golangci-lint run -v --timeout=5m
61+
docker run --rm -v "$(PWD):/var/work:ro" -w /var/work \
62+
golangci/golangci-lint:latest golangci-lint run -v --timeout=5m
63+
docker run --rm -v "$(PWD):/var/work:ro" -w /var/work/e2e \
64+
golangci/golangci-lint:latest golangci-lint run -v --timeout=5m
65+
66+
.PHONY: gosec
67+
gosec: ## Run gosec against code.
68+
docker run --rm -v "$(PWD):/var/work:ro" -w /var/work securego/gosec:2.19.0 \
69+
-exclude-dir=bin -exclude-generated ./...
4870

4971
.PHONY: fmt
5072
fmt:
@@ -88,9 +110,11 @@ docker-build: build-linux
88110
.PHONY: docker-push
89111
# must run the docker build before pushing the image
90112
docker-push:
91-
echo "[reminder] Did you run `make docker-build`?"
92113
docker push ${IMG}
93114

115+
.PHONY: docker-setup
116+
docker-setup: docker-build docker-push
117+
94118
.PHONY: run
95119
# run the ccm locally, really only makes sense on linux anyway
96120
run: build
@@ -108,6 +132,66 @@ run-debug: build
108132
--kubeconfig=${KUBECONFIG} \
109133
--linodego-debug
110134

135+
#####################################################################
136+
# E2E Test Setup
137+
#####################################################################
138+
139+
.PHONY: mgmt-and-capl-cluster
140+
mgmt-and-capl-cluster: docker-setup mgmt-cluster capl-cluster
141+
142+
.PHONY: capl-cluster
143+
capl-cluster: generate-capl-cluster-manifests create-capl-cluster patch-linode-ccm
144+
145+
.PHONY: generate-capl-cluster-manifests
146+
generate-capl-cluster-manifests:
147+
# Create the CAPL cluster manifests without any CSI driver stuff
148+
LINODE_FIREWALL_ENABLED=$(LINODE_FIREWALL_ENABLED) LINODE_OS=$(LINODE_OS) clusterctl generate cluster $(CLUSTER_NAME) \
149+
--kubernetes-version $(K8S_VERSION) --infrastructure linode-linode:$(CAPL_VERSION) \
150+
--control-plane-machine-count $(CONTROLPLANE_NODES) --worker-machine-count $(WORKER_NODES) > capl-cluster-manifests.yaml
151+
152+
.PHONY: create-capl-cluster
153+
create-capl-cluster:
154+
# Create a CAPL cluster with updated CCM and wait for it to be ready
155+
kubectl apply -f capl-cluster-manifests.yaml
156+
kubectl wait --for=condition=ControlPlaneReady cluster/$(CLUSTER_NAME) --timeout=600s || (kubectl get cluster -o yaml; kubectl get linodecluster -o yaml; kubectl get linodemachines -o yaml)
157+
kubectl wait --for=condition=NodeHealthy=true machines -l cluster.x-k8s.io/cluster-name=$(CLUSTER_NAME) --timeout=900s
158+
clusterctl get kubeconfig $(CLUSTER_NAME) > $(KUBECONFIG_PATH)
159+
KUBECONFIG=$(KUBECONFIG_PATH) kubectl wait --for=condition=Ready nodes --all --timeout=600s
160+
# Remove all taints from control plane node so that pods scheduled on it by tests can run (without this, some tests fail)
161+
KUBECONFIG=$(KUBECONFIG_PATH) kubectl taint nodes -l node-role.kubernetes.io/control-plane node-role.kubernetes.io/control-plane-
162+
163+
.PHONY: patch-linode-ccm
164+
patch-linode-ccm:
165+
KUBECONFIG=$(KUBECONFIG_PATH) kubectl patch -n kube-system daemonset ccm-linode --type='json' -p="[{'op': 'replace', 'path': '/spec/template/spec/containers/0/image', 'value': '${IMG}'}]"
166+
KUBECONFIG=$(KUBECONFIG_PATH) kubectl rollout status -n kube-system daemonset/ccm-linode --timeout=600s
167+
KUBECONFIG=$(KUBECONFIG_PATH) kubectl -n kube-system get daemonset/ccm-linode -o yaml
168+
169+
.PHONY: mgmt-cluster
170+
mgmt-cluster:
171+
# Create a mgmt cluster
172+
ctlptl apply -f e2e/setup/ctlptl-config.yaml
173+
clusterctl init \
174+
--wait-providers \
175+
--wait-provider-timeout 600 \
176+
--core cluster-api:$(CAPI_VERSION) \
177+
--addon helm:$(CAAPH_VERSION) \
178+
--infrastructure linode-linode:$(CAPL_VERSION)
179+
180+
.PHONY: cleanup-cluster
181+
cleanup-cluster:
182+
kubectl delete cluster -A --all --timeout=180s
183+
kubectl delete linodefirewalls -A --all --timeout=60s
184+
kubectl delete lvpc -A --all --timeout=60s
185+
kind delete cluster -n caplccm
186+
187+
.PHONY: e2e-test
188+
e2e-test:
189+
$(MAKE) -C e2e test LINODE_API_TOKEN=$(LINODE_TOKEN) SUITE_ARGS="--region=$(LINODE_REGION) --use-existing --timeout=5m --kubeconfig=$(KUBECONFIG_PATH) --image=$(IMG) --linode-url https://api.linode.com/"
190+
191+
#####################################################################
192+
# OS / ARCH
193+
#####################################################################
194+
111195
# Set the host's OS. Only linux and darwin supported for now
112196
HOSTOS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
113197
ifeq ($(filter darwin linux,$(HOSTOS)),)
@@ -121,9 +205,6 @@ else ifeq ($(ARCH_SHORT),aarch64)
121205
ARCH_SHORT := arm64
122206
endif
123207

124-
HELM ?= $(LOCALBIN)/helm
125-
HELM_VERSION ?= v3.9.1
126-
127208
.PHONY: helm
128209
helm: $(HELM) ## Download helm locally if necessary
129210
$(HELM): $(LOCALBIN)

cloud/linode/cloud.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ func newCloud() (cloudprovider.Interface, error) {
117117
if len(Options.IpHolderSuffix) > 23 {
118118
msg := fmt.Sprintf("ip-holder-suffix must be 23 characters or less: %s is %d characters\n", Options.IpHolderSuffix, len(Options.IpHolderSuffix))
119119
klog.Error(msg)
120-
return nil, fmt.Errorf(msg)
120+
return nil, fmt.Errorf("%s", msg)
121121
}
122122

123123
// create struct that satisfies cloudprovider.Interface

devbox.json

+8-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@
1818
"init_hook": [
1919
"export \"GOROOT=$(go env GOROOT)\""
2020
],
21-
"scripts": {}
21+
"scripts": {
22+
"mgmt-and-capl-cluster": "make mgmt-and-capl-cluster",
23+
"e2e-test": "make e2e-test",
24+
"cleanup-cluster": "make cleanup-cluster"
25+
}
26+
},
27+
"env": {
28+
"EXP_CLUSTER_RESOURCE_SET": "true"
2229
}
2330
}

e2e/setup/ctlptl-config.yaml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
apiVersion: ctlptl.dev/v1alpha1
3+
kind: Cluster
4+
product: kind
5+
kindV1Alpha4Cluster:
6+
name: caplccm
7+
nodes:
8+
- role: control-plane
9+
image: kindest/node:v1.31.2

main.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ func main() {
113113
linode.Options.KubeconfigFlag = command.Flags().Lookup("kubeconfig")
114114
if linode.Options.KubeconfigFlag == nil {
115115
msg := "kubeconfig missing from CCM flag set"
116-
sentry.CaptureError(ctx, fmt.Errorf(msg))
116+
sentry.CaptureError(ctx, fmt.Errorf("%s", msg))
117117
fmt.Fprintf(os.Stderr, "kubeconfig missing from CCM flag set"+"\n")
118118
os.Exit(1)
119119
}
@@ -122,7 +122,7 @@ func main() {
122122
_, network, err := net.ParseCIDR(externalSubnet)
123123
if err != nil {
124124
msg := fmt.Sprintf("Unable to parse %s as network subnet: %v", externalSubnet, err)
125-
sentry.CaptureError(ctx, fmt.Errorf(msg))
125+
sentry.CaptureError(ctx, fmt.Errorf("%s", msg))
126126
fmt.Fprintf(os.Stderr, "%v\n", msg)
127127
os.Exit(1)
128128
}

0 commit comments

Comments
 (0)