Skip to content

Commit eda8064

Browse files
authored
test: use remote coverage (#945)
We have already implemented this in the csi-driver. With this solution we can test more paths (e.g., metrics) in the code and get a more accurate test coverage.
1 parent e6a1eb8 commit eda8064

7 files changed

Lines changed: 79 additions & 2 deletions

File tree

.github/workflows/test_e2e.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,12 @@ jobs:
7575
- name: Run tests
7676
run: |
7777
source dev/files/env.sh
78-
go test ./tests/e2e -tags e2e -v -race -timeout 60m -coverprofile=coverage.txt
78+
go test ./tests/e2e -tags e2e -v -race -timeout 60m
79+
80+
- name: Fetch coverage data
81+
run: |
82+
source dev/files/env.sh
83+
bash scripts/get-coverage-from-k8s.sh
7984
8085
- name: Upload coverage reports to Codecov
8186
uses: codecov/codecov-action@v5
@@ -84,6 +89,7 @@ jobs:
8489
!startsWith(github.head_ref, 'releaser-pleaser--')
8590
with:
8691
token: ${{ secrets.CODECOV_TOKEN }}
92+
files: ./coverage/coverage.txt
8793

8894
- name: Dump logs & events
8995
if: always()

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ deploy/gen/
77
hcloud-cloud-controller-manager
88
*.tgz
99
hack/.*
10+
coverage/

codecov.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
ignore:
2+
- "test"
3+
- "internal/mocks"

dev/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ RUN ls -al
1111
ARG CGO_ENABLED=0
1212
# `skaffold debug` sets SKAFFOLD_GO_GCFLAGS to disable compiler optimizations
1313
ARG SKAFFOLD_GO_GCFLAGS
14+
ARG GO_BUILDFLAGS
1415
ENV GOCACHE=/cache
15-
RUN --mount=type=cache,target="/cache" go build -gcflags="$SKAFFOLD_GO_GCFLAGS" -o hcloud-cloud-controller-manager.bin github.com/hetznercloud/hcloud-cloud-controller-manager
16+
RUN --mount=type=cache,target="/cache" go build -gcflags="$SKAFFOLD_GO_GCFLAGS" $GO_BUILDFLAGS -o hcloud-cloud-controller-manager.bin github.com/hetznercloud/hcloud-cloud-controller-manager
1617

1718
FROM alpine:3.22
1819

main.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ package main
2222

2323
import (
2424
"os"
25+
"os/signal"
26+
"runtime/coverage"
27+
"syscall"
2528

2629
"github.com/spf13/pflag"
2730
"k8s.io/apimachinery/pkg/util/wait"
@@ -43,6 +46,8 @@ func main() {
4346
klog.Fatalf("unable to initialize command options: %v", err)
4447
}
4548

49+
setupCoverageSignalHandler()
50+
4651
fss := cliflag.NamedFlagSets{}
4752
command := app.NewCloudControllerManagerCommand(ccmOptions, cloudInitializer, app.DefaultInitFuncConstructors, names.CCMControllerAliases(), fss, wait.NeverStop)
4853

@@ -74,3 +79,24 @@ func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface {
7479
}
7580
return cloud
7681
}
82+
83+
func setupCoverageSignalHandler() {
84+
coverDir, exists := os.LookupEnv("GOCOVERDIR")
85+
if !exists {
86+
return
87+
}
88+
89+
c := make(chan os.Signal, 1)
90+
signal.Notify(c, syscall.SIGUSR1)
91+
92+
go func() {
93+
for {
94+
<-c
95+
klog.Info("Writing coverage profile")
96+
97+
if err := coverage.WriteCountersDir(coverDir); err != nil {
98+
klog.Warning("failed to write coverage profile", "err", err)
99+
}
100+
}
101+
}()
102+
}

scripts/get-coverage-from-k8s.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
set -ueo pipefail
3+
4+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
5+
COVERDIR="$SCRIPT_DIR/../coverage"
6+
7+
# Create/clean coverage directory
8+
if [ -d "$COVERDIR" ]; then
9+
echo "$COVERDIR already exists; cleaning"
10+
rm -r "$COVERDIR"
11+
fi
12+
mkdir -p "$COVERDIR"
13+
14+
signal_coverage_write() {
15+
for i in "${@:2}"; do
16+
echo "Sending USR1 signal to $i"
17+
kubectl -n kube-system exec -t "$i" -- kill -USR1 1
18+
sleep 0.5
19+
echo "Pulling coverage from $i into $1"
20+
kubectl cp -n kube-system "$i:/coverage" "$1"
21+
done
22+
23+
go tool covdata textfmt -i "$1" -o "$1/coverage.txt"
24+
}
25+
26+
PODS=$(
27+
kubectl -n kube-system get pods \
28+
--no-headers -o custom-columns=":metadata.name" \
29+
-l app.kubernetes.io/instance=hccm
30+
)
31+
32+
# shellcheck disable=SC2086
33+
signal_coverage_write "$COVERDIR" $PODS

skaffold.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ build:
77
- image: docker.io/hetznercloud/hcloud-cloud-controller-manager
88
docker:
99
dockerfile: dev/Dockerfile
10+
buildArgs:
11+
GO_BUILDFLAGS: -covermode=atomic -coverpkg=github.com/hetznercloud/hcloud-cloud-controller-manager/...
1012
local:
1113
useBuildkit: true
1214
insecureRegistries:
@@ -24,6 +26,11 @@ manifests:
2426
# We circumvent this by co-locating the registry & HCCM, so it's always a local pull.
2527
affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].topologyKey: "kubernetes.io/hostname"
2628
affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[0].labelSelector.matchLabels.app: docker-registry
29+
env.GOCOVERDIR.value: "/coverage"
30+
extraVolumes[0].name: coverage
31+
extraVolumes[0].emptyDir: {}
32+
extraVolumeMounts[0].name: coverage
33+
extraVolumeMounts[0].mountPath: "/coverage"
2734

2835
profiles:
2936
# Clusters with Robot Servers do not support the native Routing functionality right now.

0 commit comments

Comments
 (0)