From 173396be90eb54181892848c31fbc9a11515de11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20=22Pisco=22=20Fernandes?= Date: Thu, 11 Sep 2025 11:33:24 +0100 Subject: [PATCH] TUN-9800: Migrate cloudflared-ci pipelines to Gitlab CI ## Summary This commit migrates the cloduflared ci pipelines, that built, tested and component tested the linux binaries to gitlab ci. The only thing that is remaining to move from teamcity to gitlab are now the release pipelines that run on master. Relates to TUN-9800 --- .ci/ci-image.gitlab-ci.yml | 12 ++-- .ci/commons.gitlab-ci.yml | 34 +++++++-- .ci/image/Dockerfile | 3 +- .ci/linux.gitlab-ci.yml | 90 ++++++++++++++++++++++++ .ci/mac.gitlab-ci.yml | 2 +- .ci/scripts/component-tests.sh | 25 +++++++ fmt-check.sh => .ci/scripts/fmt-check.sh | 3 +- .ci/scripts/windows/component-test.ps1 | 2 +- .ci/scripts/windows/go-wrapper.ps1 | 2 +- .ci/windows.gitlab-ci.yml | 22 +++--- .gitlab-ci.yml | 49 +++---------- Makefile | 36 +++++++--- component-tests/test_service.py | 5 +- component-tests/util.py | 7 +- 14 files changed, 209 insertions(+), 83 deletions(-) create mode 100644 .ci/linux.gitlab-ci.yml create mode 100755 .ci/scripts/component-tests.sh rename fmt-check.sh => .ci/scripts/fmt-check.sh (54%) diff --git a/.ci/ci-image.gitlab-ci.yml b/.ci/ci-image.gitlab-ci.yml index ea5a8623..e5202586 100644 --- a/.ci/ci-image.gitlab-ci.yml +++ b/.ci/ci-image.gitlab-ci.yml @@ -8,10 +8,9 @@ include: inputs: stage: pre-build jobPrefix: ci-image - # runOnChangesTo: [".ci/image/**"] - # runOnMR: true - # runOnBranches: '^master$' - runOnBranches: "^.+$" + runOnChangesTo: [".ci/image/**"] + runOnMR: true + runOnBranches: '^master$' commentImageRefs: false runner: vm-linux-x86-4cpu-8gb EXTRA_DIB_ARGS: "--manifest=.ci/image/.docker-images" @@ -23,9 +22,8 @@ include: inputs: stage: pre-build jobPrefix: ci-image - # runOnMR: true - # runOnBranches: '^master$' - runOnBranches: "^.+$" + runOnMR: true + runOnBranches: '^master$' IMAGE_PATH: "$REGISTRY_HOST/stash/tun/cloudflared/ci-image/master" VARIABLE_NAME: BUILD_IMAGE needs: diff --git a/.ci/commons.gitlab-ci.yml b/.ci/commons.gitlab-ci.yml index 7eea1e5e..990c5709 100644 --- a/.ci/commons.gitlab-ci.yml +++ b/.ci/commons.gitlab-ci.yml @@ -5,13 +5,19 @@ - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: always - when: never - # Rules to run the job only on branches that are not master. This is needed because for now - # we need to keep a similar behavior due to the integration with teamcity, which requires us - # to not trigger pipelines on tags and/or merge requests. - run-on-branch: + # Rules to run the job only on merge requests + run-on-mr: - if: $CI_COMMIT_TAG when: never - - if: $CI_PIPELINE_SOURCE != "merge_request_event" && $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + when: always + - when: never + # Rules to run the job on merge_requests and master branch + run-always: + - if: $CI_COMMIT_TAG + when: never + - if: $CI_PIPELINE_SOURCE == "merge_request_event" + - if: $CI_COMMIT_BRANCH != null && $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH when: always - when: never @@ -28,4 +34,20 @@ else echo "No tag present — skipping." exit 0 - fi \ No newline at end of file + fi + +.component-tests: + image: $BUILD_IMAGE + rules: + - !reference [.default-rules, run-always] + variables: + COMPONENT_TESTS_CONFIG: component-test-config.yaml + COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZC5leGUKY3JlZGVudGlhbHNfZmlsZTogY3JlZC5qc29uCm9yaWdpbmNlcnQ6IGNlcnQucGVtCnpvbmVfZG9tYWluOiBhcmdvdHVubmVsdGVzdC5jb20Kem9uZV90YWc6IDQ4Nzk2ZjFlNzBiYjc2NjljMjliYjUxYmEyODJiZjY1 + secrets: + DNS_API_TOKEN: + vault: gitlab/cloudflare/tun/cloudflared/_dev/_terraform_atlantis/component_tests_token/data@kv + file: false + COMPONENT_TESTS_ORIGINCERT: + vault: gitlab/cloudflare/tun/cloudflared/_dev/component_tests_cert_pem/data@kv + file: false + cache: {} diff --git a/.ci/image/Dockerfile b/.ci/image/Dockerfile index 28513023..4e92860e 100644 --- a/.ci/image/Dockerfile +++ b/.ci/image/Dockerfile @@ -7,8 +7,9 @@ RUN apt-get update && \ apt-get install --no-install-recommends --allow-downgrades -y \ build-essential \ git \ - go-boring=1.24.4-1 \ + go-boring=1.24.6-1 \ libffi-dev \ + procps \ python3-dev \ python3-pip \ python3-setuptools \ diff --git a/.ci/linux.gitlab-ci.yml b/.ci/linux.gitlab-ci.yml new file mode 100644 index 00000000..e1104145 --- /dev/null +++ b/.ci/linux.gitlab-ci.yml @@ -0,0 +1,90 @@ +.golang-inputs: &golang_inputs + runOnMR: true + runOnBranches: '^master$' + outputDir: artifacts + runner: linux-x86-8cpu-16gb + stage: build + golangVersion: "boring-1.24" + CGO_ENABLED: 1 + +include: + ################### + ### Linux Build ### + ################### + - component: $CI_SERVER_FQDN/cloudflare/ci/golang/boring-make@~latest + inputs: + <<: *golang_inputs + jobPrefix: linux-build + GOLANG_MAKE_TARGET: ci-build + + ######################## + ### Linux FIPS Build ### + ######################## + - component: $CI_SERVER_FQDN/cloudflare/ci/golang/boring-make@~latest + inputs: + <<: *golang_inputs + jobPrefix: linux-fips-build + GOLANG_MAKE_TARGET: ci-fips-build + + ################# + ### Unit Tests ## + ################# + - component: $CI_SERVER_FQDN/cloudflare/ci/golang/boring-make@~latest + inputs: + <<: *golang_inputs + stage: test + jobPrefix: test + GOLANG_MAKE_TARGET: ci-test + + ###################### + ### Unit Tests FIPS ## + ###################### + - component: $CI_SERVER_FQDN/cloudflare/ci/golang/boring-make@~latest + inputs: + <<: *golang_inputs + stage: test + jobPrefix: test-fips + GOLANG_MAKE_TARGET: ci-fips-test + + ################# + ### Vuln Check ## + ################# + - component: $CI_SERVER_FQDN/cloudflare/ci/golang/boring-make@~latest + inputs: + <<: *golang_inputs + runOnBranches: '^$' + stage: validate + jobPrefix: vulncheck + GOLANG_MAKE_TARGET: vulncheck + +################################# +### Run Linux Component Tests ### +################################# +component-tests-linux: &component-tests-linux + stage: test + extends: .component-tests + needs: + - ci-image-get-image-ref + - linux-build-boring-make + script: + - ./.ci/scripts/component-tests.sh + variables: &component-tests-variables + CI: 1 + COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZApjcmVkZW50aWFsc19maWxlOiBjcmVkLmpzb24Kb3JpZ2luY2VydDogY2VydC5wZW0Kem9uZV9kb21haW46IGFyZ290dW5uZWx0ZXN0LmNvbQp6b25lX3RhZzogNDg3OTZmMWU3MGJiNzY2OWMyOWJiNTFiYTI4MmJmNjU= + tags: + - linux-x86-8cpu-16gb + artifacts: + reports: + junit: report.xml + +###################################### +### Run Linux FIPS Component Tests ### +###################################### +component-tests-linux-fips: + <<: *component-tests-linux + needs: + - ci-image-get-image-ref + - linux-fips-build-boring-make + variables: + <<: *component-tests-variables + COMPONENT_TESTS_FIPS: 1 diff --git a/.ci/mac.gitlab-ci.yml b/.ci/mac.gitlab-ci.yml index 1fc433de..94e16fbd 100644 --- a/.ci/mac.gitlab-ci.yml +++ b/.ci/mac.gitlab-ci.yml @@ -6,7 +6,7 @@ include: ############################### .mac-build-defaults: &mac-build-defaults rules: - - !reference [.default-rules, run-on-branch] + - !reference [.default-rules, run-on-mr] tags: - "macstadium-${RUNNER_ARCH}" parallel: diff --git a/.ci/scripts/component-tests.sh b/.ci/scripts/component-tests.sh new file mode 100755 index 00000000..68abbf1d --- /dev/null +++ b/.ci/scripts/component-tests.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e -o pipefail + +# Fetch cloudflared from the artifacts folder +mv ./artifacts/cloudflared ./cloudflared + +python3 -m venv env +. env/bin/activate + +pip install --upgrade -r component-tests/requirements.txt + +# Creates and routes a Named Tunnel for this build. Also constructs +# config file from env vars. +python3 component-tests/setup.py --type create + +# Define the cleanup function +cleanup() { + # The Named Tunnel is deleted and its route unprovisioned here. + python3 component-tests/setup.py --type cleanup +} + +# The trap will call the cleanup function on script exit +trap cleanup EXIT + +pytest component-tests -o log_cli=true --log-cli-level=INFO --junit-xml=report.xml diff --git a/fmt-check.sh b/.ci/scripts/fmt-check.sh similarity index 54% rename from fmt-check.sh rename to .ci/scripts/fmt-check.sh index 31fc7abc..4c1cbad0 100755 --- a/fmt-check.sh +++ b/.ci/scripts/fmt-check.sh @@ -1,8 +1,7 @@ #!/bin/bash - set -e -o pipefail -OUTPUT=$(goimports -l -d -local github.com/cloudflare/cloudflared $(go list -mod=vendor -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc)) +OUTPUT=$(go run -mod=readonly golang.org/x/tools/cmd/goimports@v0.30.0 -l -d -local github.com/cloudflare/cloudflared $(go list -mod=vendor -f '{{.Dir}}' -a ./... | fgrep -v tunnelrpc)) if [ -n "$OUTPUT" ] ; then PAGER=$(which colordiff || echo cat) diff --git a/.ci/scripts/windows/component-test.ps1 b/.ci/scripts/windows/component-test.ps1 index abf024ff..dea9e115 100644 --- a/.ci/scripts/windows/component-test.ps1 +++ b/.ci/scripts/windows/component-test.ps1 @@ -31,7 +31,7 @@ Write-Host "Running component tests" try { python -m pip --disable-pip-version-check install --upgrade -r component-tests/requirements.txt --use-pep517 python component-tests/setup.py --type create - python -m pytest component-tests -o log_cli=true --log-cli-level=INFO + python -m pytest component-tests -o log_cli=true --log-cli-level=INFO --junit-xml=report.xml if ($LASTEXITCODE -ne 0) { throw "Failed component tests" } diff --git a/.ci/scripts/windows/go-wrapper.ps1 b/.ci/scripts/windows/go-wrapper.ps1 index 217553a3..8eca1ae8 100644 --- a/.ci/scripts/windows/go-wrapper.ps1 +++ b/.ci/scripts/windows/go-wrapper.ps1 @@ -3,7 +3,7 @@ Param( [string]$ScriptToExecute ) -# This script its a wrapper that downloads a specific version +# The script is a wrapper that downloads a specific version # of go, adds it to the PATH and executes a script with that go # version in the path. diff --git a/.ci/windows.gitlab-ci.yml b/.ci/windows.gitlab-ci.yml index 256c7ff4..fdc5ac0a 100644 --- a/.ci/windows.gitlab-ci.yml +++ b/.ci/windows.gitlab-ci.yml @@ -6,7 +6,7 @@ include: ################################### .windows-build-defaults: &windows-build-defaults rules: - - !reference [.default-rules, run-on-branch] + - !reference [.default-rules, run-always] tags: - windows-x86 cache: {} @@ -27,27 +27,20 @@ build-cloudflared-windows: ### Load Environment Variables for Component Tests ### ###################################################### load-windows-env-variables: - rules: - - !reference [.default-rules, run-on-branch] stage: pre-build + extends: .component-tests script: - - echo "COMPONENT_TESTS_CONFIG=component-test-config.yaml" >> windows.env - - echo "COMPONENT_TESTS_CONFIG_CONTENT=Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZC5leGUKY3JlZGVudGlhbHNfZmlsZTogY3JlZC5qc29uCm9yaWdpbmNlcnQ6IGNlcnQucGVtCnpvbmVfZG9tYWluOiBhcmdvdHVubmVsdGVzdC5jb20Kem9uZV90YWc6IDQ4Nzk2ZjFlNzBiYjc2NjljMjliYjUxYmEyODJiZjY1" >> windows.env + - echo "COMPONENT_TESTS_CONFIG=$COMPONENT_TESTS_CONFIG" >> windows.env + - echo "COMPONENT_TESTS_CONFIG_CONTENT=$COMPONENT_TESTS_CONFIG_CONTENT" >> windows.env - echo "DNS_API_TOKEN=$DNS_API_TOKEN" >> windows.env # We have to encode the `COMPONENT_TESTS_ORIGINCERT` secret, because it content is a file, otherwise we can't export it using gitlab - echo "COMPONENT_TESTS_ORIGINCERT=$(echo "$COMPONENT_TESTS_ORIGINCERT" | base64 -w0)" >> windows.env - secrets: - DNS_API_TOKEN: - vault: gitlab/cloudflare/tun/cloudflared/_dev/_terraform_atlantis/component_tests_token/data@kv - file: false - COMPONENT_TESTS_ORIGINCERT: - vault: gitlab/cloudflare/tun/cloudflared/_dev/component_tests_cert_pem/data@kv - file: false + variables: + COMPONENT_TESTS_CONFIG_CONTENT: Y2xvdWRmbGFyZWRfYmluYXJ5OiBjbG91ZGZsYXJlZC5leGUKY3JlZGVudGlhbHNfZmlsZTogY3JlZC5qc29uCm9yaWdpbmNlcnQ6IGNlcnQucGVtCnpvbmVfZG9tYWluOiBhcmdvdHVubmVsdGVzdC5jb20Kem9uZV90YWc6IDQ4Nzk2ZjFlNzBiYjc2NjljMjliYjUxYmEyODJiZjY1 artifacts: access: 'none' reports: dotenv: windows.env - cache: {} ################################### ### Run Windows Component Tests ### @@ -60,6 +53,9 @@ component-tests-cloudflared-windows: # We have to decode the secret we encoded on the `load-windows-env-variables` job - $env:COMPONENT_TESTS_ORIGINCERT = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:COMPONENT_TESTS_ORIGINCERT)) - powershell -ExecutionPolicy Bypass -File ".\.ci\scripts\windows\go-wrapper.ps1" "${GO_VERSION}" ".\.ci\scripts\windows\component-test.ps1" + artifacts: + reports: + junit: report.xml ################################ ### Package Windows Binaries ### diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a4ea2864..09826a2c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,27 +1,13 @@ variables: - # Define GOPATH within the project directory to allow GitLab CI to cache it. - # By default, Go places modules in GOMODCACHE, often outside the project. - # Explicitly setting GOMODCACHE ensures it's within the cached path. - GOPATH: "$CI_PROJECT_DIR/.go" - GOMODCACHE: "$GOPATH/pkg/mod" - GO_BIN_DIR: "$GOPATH/bin" - GO_VERSION: "go1.24.4" - -cache: - # Cache Go modules and the binaries. - # The 'key' ensures a unique cache per branch, or you can use a fixed key - # for a shared cache across all branches if that fits your workflow. - key: "$CI_COMMIT_REF_SLUG" - paths: - - ${GOPATH}/pkg/mod/ # For Go modules - - ${GO_BIN_DIR}/ + GO_VERSION: "go1.24.6" + GIT_DEPTH: "0" default: id_tokens: VAULT_ID_TOKEN: aud: https://vault.cfdata.org -stages: [pre-build, build, test, package, release] +stages: [pre-build, build, validate, test, package, release] include: ##################################################### @@ -34,6 +20,11 @@ include: ##################################################### - local: .ci/ci-image.gitlab-ci.yml + ##################################################### + ################## Linux Builds ################### + ##################################################### + - local: .ci/linux.gitlab-ci.yml + ##################################################### ################## Windows Builds ################### ##################################################### @@ -48,27 +39,3 @@ include: ################# Release Packages ################## ##################################################### - local: .ci/release.gitlab-ci.yml - -# Template for Go setup, including caching and installation -.go_setup: - image: docker-registry.cfdata.org/stash/devtools/ci-builders/golang-1.24/master:3219-cc8b513@sha256:4fe3ff47ba07f9b23429f49fbd063cc1a34156dd11b8113e325ad3762f94a1db - before_script: - - mkdir -p ${GOPATH} ${GOMODCACHE} ${GO_BIN_DIR} - - export PATH=$PATH:${GO_BIN_DIR} - - go env -w GOMODCACHE=${GOMODCACHE} # Ensure go uses the cached module path - - # Check if govulncheck is already installed and install it if not - - if [ ! -f ${GO_BIN_DIR}/govulncheck ]; then - echo "govulncheck not found in cache, installing..."; - go install golang.org/x/vuln/cmd/govulncheck@latest; - else - echo "govulncheck found in cache, skipping installation."; - fi - -vulncheck: - stage: build - extends: .go_setup - rules: - - !reference [.default-rules, run-on-branch] - script: - - make vulncheck diff --git a/Makefile b/Makefile index fa694c94..96271ae7 100644 --- a/Makefile +++ b/Makefile @@ -128,6 +128,8 @@ endif #for FIPS compliance, FPM defaults to MD5. RPM_DIGEST := --rpm-digest sha256 +GO_TEST_LOG_OUTPUT = /tmp/gotest.log + .PHONY: all all: cloudflared test @@ -137,7 +139,7 @@ clean: .PHONY: vulncheck vulncheck: - @govulncheck ./... + @go run -mod=readonly golang.org/x/vuln/cmd/govulncheck@latest ./... .PHONY: cloudflared cloudflared: @@ -160,11 +162,9 @@ generate-docker-version: .PHONY: test test: vet -ifndef CI - go test -v -mod=vendor -race $(LDFLAGS) ./... -else - @mkdir -p .cover - go test -v -mod=vendor -race $(LDFLAGS) -coverprofile=".cover/c.out" ./... + $Q go test -json -v -mod=vendor -race $(LDFLAGS) ./... 2>&1 | tee $(GO_TEST_LOG_OUTPUT) +ifneq ($(FIPS), true) + @go run -mod=readonly github.com/gotesttools/gotestfmt/v2/cmd/gotestfmt@latest -input $(GO_TEST_LOG_OUTPUT) endif .PHONY: cover @@ -254,7 +254,7 @@ capnp: .PHONY: vet vet: - go vet -mod=vendor github.com/cloudflare/cloudflared/... + $Q go vet -mod=vendor github.com/cloudflare/cloudflared/... .PHONY: fmt fmt: @@ -263,7 +263,7 @@ fmt: .PHONY: fmt-check fmt-check: - @./fmt-check.sh + @./.ci/scripts/fmt-check.sh .PHONY: lint lint: @@ -272,3 +272,23 @@ lint: .PHONY: mocks mocks: go generate mocks/mockgen.go + +.PHONY: ci-build +ci-build: + @GOOS=linux GOARCH=amd64 $(MAKE) cloudflared + @mkdir -p artifacts + @mv cloudflared artifacts/cloudflared + +.PHONY: ci-fips-build +ci-fips-build: + @FIPS=true GOOS=linux GOARCH=amd64 $(MAKE) cloudflared + @mkdir -p artifacts + @mv cloudflared artifacts/cloudflared + +.PHONY: ci-test +ci-test: fmt-check lint test + @go run -mod=readonly github.com/jstemmer/go-junit-report/v2@latest -in $(GO_TEST_LOG_OUTPUT) -parser gojson -out report.xml -set-exit-code + +.PHONY: ci-fips-test +ci-fips-test: + @FIPS=true $(MAKE) ci-test diff --git a/component-tests/test_service.py b/component-tests/test_service.py index e1c155e6..e992be33 100644 --- a/component-tests/test_service.py +++ b/component-tests/test_service.py @@ -9,7 +9,7 @@ import pytest import test_logging from conftest import CfdModes -from util import select_platform, start_cloudflared, wait_tunnel_ready, write_config +from util import select_platform, skip_on_ci, start_cloudflared, wait_tunnel_ready, write_config def default_config_dir(): @@ -82,6 +82,7 @@ class TestServiceMode: os.remove(default_config_file()) self.launchctl_cmd("list", success=False) + @skip_on_ci("we can't run sudo command on CI") @select_platform("Linux") @pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), reason=f"There is already a config file in default path") @@ -98,6 +99,7 @@ class TestServiceMode: self.sysv_service_scenario(config, tmp_path, assert_log_file) + @skip_on_ci("we can't run sudo command on CI") @select_platform("Linux") @pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), reason=f"There is already a config file in default path") @@ -116,6 +118,7 @@ class TestServiceMode: self.sysv_service_scenario(config, tmp_path, assert_rotating_log) + @skip_on_ci("we can't run sudo command on CI") @select_platform("Linux") @pytest.mark.skipif(os.path.exists("/etc/cloudflared/config.yml"), reason=f"There is already a config file in default path") diff --git a/component-tests/util.py b/component-tests/util.py index c7fd59c4..b4c9b51b 100644 --- a/component-tests/util.py +++ b/component-tests/util.py @@ -10,7 +10,6 @@ import pytest import requests import yaml -import json from retrying import retry from constants import METRICS_PORT, MAX_RETRIES, BACKOFF_SECS @@ -35,6 +34,12 @@ def fips_enabled(): nofips = pytest.mark.skipif( fips_enabled(), reason=f"Only runs without FIPS (COMPONENT_TESTS_FIPS=0)") +def skip_on_ci(reason): + env_ci = os.getenv("CI") + running_in_ci = env_ci is not None and env_ci != "0" + return pytest.mark.skipif( + running_in_ci, reason=f"This test can't run on CI due to: {reason}") + def write_config(directory, config): config_path = directory / "config.yml" with open(config_path, 'w') as outfile: