test(e2e): run on ephemeral clusters (#128)

Rework the e2e test to expect a working connection to a cluster when
they start. Developers can create their own clusters and run the tests.

Removed the code used to start kind clusters within the e2e tests.

Reworked the Taskfile to define two environments where the tests can run:

1. An ephemeral one running within Dagger, using the k3s module, to be
used by the CI.
2. A persistent one created with Kind, requiring the kind binary, to be
used for development and debugging when the ephemeral cluster is not
enough.

Signed-off-by: Francesco Canovai <francesco.canovai@enterprisedb.com>
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
This commit is contained in:
Francesco Canovai 2025-01-07 14:24:12 +01:00 committed by GitHub
parent 517c5327d1
commit 294942bb79
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 366 additions and 317 deletions

View File

@ -32,11 +32,6 @@ jobs:
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.sha }}
# TODO: remove this when we daggerize the e2e
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.23.x'
- name: Install Task
uses: arduino/setup-task@v2
- name: Install Dagger

View File

@ -1,7 +1,14 @@
version: 3
# Environment variables that are shared across tasks.
env:
# We have multiple parallel tasks that run for a long time. Prefix their output with the task name so we can understand
# what task is writing.
output: prefixed
# Variables that are shared across tasks.
vars:
# renovate: datasource=docker depName=kindest/node versioning=semver
E2E_KUBERNETES_VERSION: v1.32.0
E2E_CLUSTER_NAME: barman-cloud-plugin-e2e-{{.E2E_KUBERNETES_VERSION}}
REGISTRY_NETWORK: barman-cloud-plugin
REGISTRY_NAME: registry.barman-cloud-plugin
REGISTRY_PORT: 5000
@ -89,8 +96,8 @@ tasks:
openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem \
-subj "/O=CloudNativePG/OU=Barman Cloud Plugin Testing" &&
openssl genrsa -out server-key.pem 4096 &&
openssl req -subj "/CN=${REGISTRY_NAME}" -sha256 -new -key server-key.pem -out server.csr &&
echo subjectAltName = DNS:${REGISTRY_NAME},IP:127.0.0.1 >> extfile.cnf &&
openssl req -subj "/CN={{ .REGISTRY_NAME }}" -sha256 -new -key server-key.pem -out server.csr &&
echo subjectAltName = DNS:{{ .REGISTRY_NAME }},IP:127.0.0.1 >> extfile.cnf &&
echo extendedKeyUsage = serverAuth >> extfile.cnf &&
openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out server-cert.pem -extfile extfile.cnf &&
@ -106,9 +113,9 @@ tasks:
desc: Create a docker network for image building used by the dagger engine and the registry
run: once
cmds:
- docker network create ${REGISTRY_NETWORK}
- docker network create {{ .REGISTRY_NETWORK}}
status:
- docker network inspect ${REGISTRY_NETWORK}
- docker network inspect {{ .REGISTRY_NETWORK }}
start-registry:
desc: Start a container registry
@ -121,14 +128,14 @@ tasks:
REGISTRY_VERSION: 2
cmds:
- >
docker run -d --name ${REGISTRY_NAME}
-p ${REGISTRY_PORT}:5000
--network ${REGISTRY_NETWORK}
docker run -d --name {{ .REGISTRY_NAME }}
-p {{ .REGISTRY_PORT }}:5000
--network {{ .REGISTRY_NETWORK }}
-v $(pwd)/certs:/certs
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/server-cert.pem -e REGISTRY_HTTP_TLS_KEY=/certs/server-key.pem
registry:${REGISTRY_VERSION}
status:
- \[ "$(docker inspect -f {{`'{{.State.Running}}'`}} "${REGISTRY_NAME}" 2> /dev/null )" == 'true' \]
- \[ "$(docker inspect -f {{`'{{.State.Running}}'`}} "{{ .REGISTRY_NAME }}" 2> /dev/null )" == 'true' \]
# Start a dagger engine that mounts the CA certificate for the local registry.
@ -144,12 +151,12 @@ tasks:
DAGGER_ENGINE_IMAGE: registry.dagger.io/engine:v{{ .DAGGER_VERSION }}
cmds:
- >
docker run -d -v /var/lib/dagger --name "${DAGGER_ENGINE_CONTAINER_NAME}"
--network=${REGISTRY_NETWORK}
docker run -d -v /var/lib/dagger --name "{{ .DAGGER_ENGINE_CONTAINER_NAME }}"
--network={{ .REGISTRY_NETWORK }}
-v $(pwd)/certs/ca.pem:/usr/local/share/ca-certificates/ca.crt
--privileged {{ .DAGGER_ENGINE_IMAGE }}
status:
- \[ "$(docker inspect -f {{`'{{.State.Running}}'`}} "${DAGGER_ENGINE_CONTAINER_NAME}" 2> /dev/null )" == 'true' \]
- \[ "$(docker inspect -f {{`'{{.State.Running}}'`}} "{{ .DAGGER_ENGINE_CONTAINER_NAME }}" 2> /dev/null )" == 'true' \]
# We build an image and push it to a local registry.
# The name is always `plugin-barman-cloud:testing`.
@ -161,12 +168,12 @@ tasks:
env:
# renovate: datasource=git-refs depName=docker lookupName=https://github.com/purpleclay/daggerverse currentValue=main
DAGGER_DOCKER_SHA: 1379b455587e74072cee73db1b78e11af4215d53
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{.DAGGER_ENGINE_CONTAINER_NAME}}
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{ .DAGGER_ENGINE_CONTAINER_NAME }}
cmds:
- >
GITHUB_REF= dagger call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
GITHUB_REF= dagger -s call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
build --dir . --file containers/Dockerfile.plugin --platform linux/amd64
publish --ref ${REGISTRY_NAME}:${REGISTRY_PORT}/plugin-barman-cloud --tags testing
publish --ref {{ .REGISTRY_NAME }}:{{ .REGISTRY_PORT }}/plugin-barman-cloud --tags testing
# We build an image and push it to a local registry.
# The name is always `sidecar-barman-cloud:testing`.
@ -178,12 +185,12 @@ tasks:
env:
# renovate: datasource=git-refs depName=docker lookupName=https://github.com/purpleclay/daggerverse currentValue=main
DAGGER_DOCKER_SHA: 1379b455587e74072cee73db1b78e11af4215d53
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{.DAGGER_ENGINE_CONTAINER_NAME}}
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{ .DAGGER_ENGINE_CONTAINER_NAME }}
cmds:
- >
GITHUB_REF= dagger call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
GITHUB_REF= dagger -s call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
build --dir . --file containers/Dockerfile.sidecar --platform linux/amd64
publish --ref ${REGISTRY_NAME}:${REGISTRY_PORT}/sidecar-barman-cloud --tags testing
publish --ref {{ .REGISTRY_NAME }}:{{ .REGISTRY_PORT }}/sidecar-barman-cloud --tags testing
build-images:
desc: Build the container images for the plugin
@ -191,6 +198,39 @@ tasks:
- build-plugin-image
- build-sidecar-image
# Install kind if not at the expected version.
install-kind:
desc: Install kind
run: once
vars:
# renovate: datasource=git-refs depName=kind lookupName=https://github.com/kubernetes-sigs/kind versioning=semver
KIND_VERSION: v0.26.0
cmds:
- go install sigs.k8s.io/kind@{{.KIND_VERSION}}
- kind version | grep -q {{.KIND_VERSION}}
status:
- kind version | grep -q {{.KIND_VERSION}}
start-kind-cluster:
desc: Start a kind cluster
deps:
- install-kind
- start-build-network
run: once
cmds:
- >
kind create cluster --name {{ .E2E_CLUSTER_NAME }}
--image kindest/node:{{ .E2E_KUBERNETES_VERSION }}
--config hack/kind-config.yaml
--wait 5m
- >
for node in $(kind get nodes --name {{ .E2E_CLUSTER_NAME }} ); do
docker network connect {{ .REGISTRY_NETWORK }} $node;
docker exec $node sh -c "update-ca-certificates";
done
status:
- kind get clusters | grep -q {{ .E2E_CLUSTER_NAME }}
# TODO: see if it is possible to daggerize this. It will have to manage docker to make kind work.
# TODO: add a task to clean up the kind cluster for new test runs.
# Run the e2e tests. This task will start a kind cluster, deploy the plugin, and run the tests.
@ -199,22 +239,42 @@ tasks:
# * The registry to be in the same network of the dagger-engine.
# * The dagger-engine to mount the CA.
# * The kind cluster to mount the CA.
e2e:
desc: Run e2e tests
e2e-external-kind:
desc: Run e2e tests in a local kind cluster
deps:
- build-images
- start-kind-cluster
vars:
# renovate: datasource=docker depName=golang versioning=semver
GOLANG_IMAGE_VERSION: 1.23.4
KUBECONFIG_PATH:
sh: mktemp -t kubeconfig-XXXXX
env:
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{ .DAGGER_ENGINE_CONTAINER_NAME }}
cmds:
- kind get kubeconfig --internal --name {{ .E2E_CLUSTER_NAME }} > {{ .KUBECONFIG_PATH }}
- >
GITHUB_REF= dagger call -m dagger/e2e/ run
--source .
--kubeconfig {{.KUBECONFIG_PATH}}
--go-version {{ .GOLANG_IMAGE_VERSION }}
e2e-ephemeral:
desc: Run e2e tests in an ephemeral k3s cluster
deps:
- build-images
vars:
# renovate: datasource=docker depName=golang versioning=semver
GOLANG_IMAGE_VERSION: 1.23.4
env:
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{ .DAGGER_ENGINE_CONTAINER_NAME }}
cmds:
- >
go run github.com/onsi/ginkgo/v2/ginkgo
--procs=8
--randomize-all
--randomize-suites
--fail-on-pending
--fail-on-empty
--keep-going
--timeout=30m
--github-output
./test/e2e
GITHUB_REF= dagger call -m dagger/e2e/ run-ephemeral
--source .
--ca certs/ca.pem
--registry {{.REGISTRY_NAME}}:{{.REGISTRY_PORT}}
--go-version {{ .GOLANG_IMAGE_VERSION }}
ci:
desc: Run the CI pipeline
@ -224,7 +284,7 @@ tasks:
- uncommitted
- lint
- go-test
- e2e
- e2e-ephemeral
publish:
desc: Build and publish a container image for the plugin
@ -284,7 +344,7 @@ tasks:
- controller-gen
desc: Generate the manifest for the main branch
vars:
GITHUB_REPOSITORY: '{{ default "cloudnative-pg/plugin-barman-cloud" .GITHUB_REPOSITORY }}'
GITHUB_REPOSITORY: cloudnative-pg/plugin-barman-cloud
GITHUB_REF: main
GITHUB_REF_NAME: main
cmds:

4
dagger/e2e/.gitattributes vendored Normal file
View File

@ -0,0 +1,4 @@
/dagger.gen.go linguist-generated
/internal/dagger/** linguist-generated
/internal/querybuilder/** linguist-generated
/internal/telemetry/** linguist-generated

4
dagger/e2e/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/dagger.gen.go
/internal/dagger
/internal/querybuilder
/internal/telemetry

18
dagger/e2e/dagger.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "e2e",
"engineVersion": "v0.15.1",
"sdk": "go",
"dependencies": [
{
"name": "go",
"source": "github.com/sagikazarmark/daggerverse/go@go/v0.9.0",
"pin": "d9ba06776c4c1ccf6f329bd862b9b439c4582ab6"
},
{
"name": "k3s",
"source": "github.com/marcosnils/daggerverse/k3s@k3s/v0.1.7",
"pin": "833ec36632b2457862f6e3bf1f7107ad65e3e515"
}
],
"source": "."
}

51
dagger/e2e/go.mod Normal file
View File

@ -0,0 +1,51 @@
module dagger/e-2-e
go 1.23.2
require (
github.com/99designs/gqlgen v0.17.57
github.com/Khan/genqlient v0.7.0
github.com/vektah/gqlparser/v2 v2.5.19
go.opentelemetry.io/otel v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0
go.opentelemetry.io/otel/log v0.3.0
go.opentelemetry.io/otel/metric v1.27.0
go.opentelemetry.io/otel/sdk v1.27.0
go.opentelemetry.io/otel/sdk/log v0.3.0
go.opentelemetry.io/otel/sdk/metric v1.27.0
go.opentelemetry.io/otel/trace v1.27.0
go.opentelemetry.io/proto/otlp v1.3.1
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/sync v0.10.0
google.golang.org/grpc v1.68.0
)
require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
github.com/stretchr/testify v1.10.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.21.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/protobuf v1.35.2 // indirect
)
replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88
replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0
replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.3.0
replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.3.0

85
dagger/e2e/go.sum Normal file
View File

@ -0,0 +1,85 @@
github.com/99designs/gqlgen v0.17.57 h1:Ak4p60BRq6QibxY0lEc0JnQhDurfhxA67sp02lMjmPc=
github.com/99designs/gqlgen v0.17.57/go.mod h1:Jx61hzOSTcR4VJy/HFIgXiQ5rJ0Ypw8DxWLjbYDAUw0=
github.com/Khan/genqlient v0.7.0 h1:GZ1meyRnzcDTK48EjqB8t3bcfYvHArCUUvgOwpz1D4w=
github.com/Khan/genqlient v0.7.0/go.mod h1:HNyy3wZvuYwmW3Y7mkoQLZsa/R5n5yIRajS1kPBvSFM=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sosodev/duration v1.3.1 h1:qtHBDMQ6lvMQsL15g4aopM4HEfOaYuhWBw3NPTtlqq4=
github.com/sosodev/duration v1.3.1/go.mod h1:RQIBBX0+fMLc/D9+Jb/fwvVmo0eZvDDEERAikUR6SDg=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/vektah/gqlparser/v2 v2.5.19 h1:bhCPCX1D4WWzCDvkPl4+TP1N8/kLrWnp43egplt7iSg=
github.com/vektah/gqlparser/v2 v2.5.19/go.mod h1:y7kvl5bBlDeuWIvLtA9849ncyvx6/lj06RsMrEjVy3U=
go.opentelemetry.io/otel v1.27.0 h1:9BZoF3yMK/O1AafMiQTVu0YDj5Ea4hPhxCs7sGva+cg=
go.opentelemetry.io/otel v1.27.0/go.mod h1:DMpAK8fzYRzs+bi3rS5REupisuqTheUlSZJ1WnZaPAQ=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88 h1:oM0GTNKGlc5qHctWeIGTVyda4iFFalOzMZ3Ehj5rwB4=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.0.0-20240518090000-14441aefdf88/go.mod h1:JGG8ebaMO5nXOPnvKEl+DiA4MGwFjCbjsxT1WHIEBPY=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0 h1:ccBrA8nCY5mM0y5uO7FT0ze4S0TuFcWdDB2FxGMTjkI=
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.3.0/go.mod h1:/9pb6634zi2Lk8LYg9Q0X8Ar6jka4dkFOylBLbVQPCE=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0 h1:bFgvUr3/O4PHj3VQcFEuYKvRZJX1SJDQ+11JXuSB3/w=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.27.0/go.mod h1:xJntEd2KL6Qdg5lwp97HMLQDVeAhrYxmzFseAMDPQ8I=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0 h1:CIHWikMsN3wO+wq1Tp5VGdVRTcON+DmOJSfDjXypKOc=
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.27.0/go.mod h1:TNupZ6cxqyFEpLXAZW7On+mLFL0/g0TE3unIYL91xWc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0 h1:R9DE4kQ4k+YtfLI2ULwX82VtNQ2J8yZmA7ZIF/D+7Mc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.27.0/go.mod h1:OQFyQVrDlbe+R7xrEyDr/2Wr67Ol0hRUgsfA+V5A95s=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0 h1:QY7/0NeRPKlzusf40ZE4t1VlMKbqSNT7cJRYzWuja0s=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.27.0/go.mod h1:HVkSiDhTM9BoUJU8qE6j2eSWLLXvi1USXjyd2BXT8PY=
go.opentelemetry.io/otel/log v0.3.0 h1:kJRFkpUFYtny37NQzL386WbznUByZx186DpEMKhEGZs=
go.opentelemetry.io/otel/log v0.3.0/go.mod h1:ziCwqZr9soYDwGNbIL+6kAvQC+ANvjgG367HVcyR/ys=
go.opentelemetry.io/otel/metric v1.27.0 h1:hvj3vdEKyeCi4YaYfNjv2NUje8FqKqUY8IlF0FxV/ik=
go.opentelemetry.io/otel/metric v1.27.0/go.mod h1:mVFgmRlhljgBiuk/MP/oKylr4hs85GZAylncepAX/ak=
go.opentelemetry.io/otel/sdk v1.27.0 h1:mlk+/Y1gLPLn84U4tI8d3GNJmGT/eXe3ZuOXN9kTWmI=
go.opentelemetry.io/otel/sdk v1.27.0/go.mod h1:Ha9vbLwJE6W86YstIywK2xFfPjbWlCuwPtMkKdz/Y4A=
go.opentelemetry.io/otel/sdk/log v0.3.0 h1:GEjJ8iftz2l+XO1GF2856r7yYVh74URiF9JMcAacr5U=
go.opentelemetry.io/otel/sdk/log v0.3.0/go.mod h1:BwCxtmux6ACLuys1wlbc0+vGBd+xytjmjajwqqIul2g=
go.opentelemetry.io/otel/sdk/metric v1.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI=
go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw=
go.opentelemetry.io/otel/trace v1.27.0 h1:IqYb813p7cmbHk0a5y6pD5JPakbVfftRXABGt5/Rscw=
go.opentelemetry.io/otel/trace v1.27.0/go.mod h1:6RiD1hkAprV4/q+yd2ln1HG9GoPx39SuvvstaLBl+l4=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc=
google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0=
google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA=
google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io=
google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

98
dagger/e2e/main.go Normal file
View File

@ -0,0 +1,98 @@
package main
import (
"context"
"fmt"
"dagger/e-2-e/internal/dagger"
)
type E2E struct{}
// Run runs the E2E tests on a Kubernetes cluster. It returns the output of the tests.
// We expect a kubeconfig file that allows access to the cluster, and optionally
// a service to bind to, if the cluster is not directly exposed to the dagger container running the tests.
func (m *E2E) Run(
ctx context.Context,
// source is the directory containing the source code for the project
source *dagger.Directory,
// kubeconfig is the kubeconfig file to use for the tests
kubeconfig *dagger.File,
// svc is the Kubernetes service to bind to. It will be known as "kubernetes" in the container.
// +optional
svc *dagger.Service,
// version of the golang image to use
// +optional
// +default="latest"
goVersion string,
) (string, error) {
goDag := dag.Go(dagger.GoOpts{Version: goVersion}).WithCgoDisabled().WithSource(source)
if svc != nil {
goDag = goDag.WithServiceBinding("kubernetes", svc)
}
return goDag.Container().
WithMountedFile("/kubeconfig", kubeconfig).
WithEnvVariable("KUBECONFIG", "/kubeconfig").
WithExec([]string{"go", "run", "github.com/onsi/ginkgo/v2/ginkgo",
"--procs=8",
"--randomize-all",
"--randomize-suites",
"--fail-on-pending",
"--fail-on-empty",
"--keep-going",
"--timeout=30m",
"--github-output",
"./test/e2e"}).Stdout(ctx)
}
// RunEphemeral creates a k3s cluster in dagger and then runs the E2E tests on it.
// If a private registry is used, its url and the ca certificate for the registry should be provided.
func (m *E2E) RunEphemeral(
ctx context.Context,
// source is the directory containing the source code for the project
source *dagger.Directory,
// registry is a private registry
// +optional
// +default="registry.barman-cloud-plugin:5000"
registry string,
// ca is the certificate authority for the registry
// +optional
ca *dagger.File,
// name is the name of the ephemeral container
// +optional
// +default="e2e"
name string,
// version of the golang image to use
// +optional
// +default="latest"
goVersion string,
) (string, error) {
k3s := dag.K3S(name)
ctr := k3s.Container()
if ca != nil {
ctr = ctr.WithMountedFile("/usr/local/share/ca-certificates/ca.crt", ca)
}
if registry != "" {
ctr = ctr.WithNewFile("/registries.yaml", fmt.Sprintf(`
configs:
"%s":
tls:
ca_file: "/usr/local/share/ca-certificates/ca.crt"
`, registry)).
WithExec([]string{"sh", "-c", "cat /registries.yaml > /etc/rancher/k3s/registries.yaml"})
}
ctr, err := ctr.Sync(ctx)
if err != nil {
return "", err
}
kServer := k3s.WithContainer(ctr).Server()
kServer, err = kServer.Start(ctx)
if err != nil {
return "", err
}
defer kServer.Stop(ctx)
return m.Run(ctx, source, k3s.Config(), kServer, goVersion)
}

View File

@ -6,6 +6,6 @@ apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
extraMounts:
- hostPath: ../../certs/ca.pem
- hostPath: certs/ca.pem
containerPath: /usr/local/share/ca-certificates/ca.crt
readOnly: true

View File

@ -24,10 +24,10 @@ import (
apimachineryTypes "k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/wait"
"sigs.k8s.io/controller-runtime/pkg/client"
kustomizeTypes "sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kyaml/resid"
internalClient "github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/client"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/deployment"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/e2etestenv"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/kustomize"
@ -41,10 +41,12 @@ import (
// We don't want multiple ginkgo nodes to run the setup concurrently, we use a single cluster for all tests.
var _ = SynchronizedBeforeSuite(func(ctx SpecContext) []byte {
var cl client.Client
var err error
if cl, err = e2etestenv.Setup(ctx,
e2etestenv.WithKindAdditionalNetworks([]string{"barman-cloud-plugin"})); err != nil {
cl, _, err := internalClient.NewClient()
if err != nil {
Fail(fmt.Sprintf("failed to create Kubernetes client: %v", err))
}
if err = e2etestenv.Setup(ctx, cl); err != nil {
Fail(fmt.Sprintf("failed to setup environment: %v", err))
}

View File

@ -19,26 +19,16 @@ package e2etestenv
import (
"context"
"fmt"
"strings"
"time"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/kind/pkg/cluster"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/certmanager"
internalClient "github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/client"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/cloudnativepg"
"github.com/cloudnative-pg/plugin-barman-cloud/test/e2e/internal/kind"
)
// SetupOptions contains the options for setting up the test environment.
type SetupOptions struct {
K8sVersion string
KindVersion string
KindClusterNamePrefix string
KindAdditionalNetworks []string
CNPGKustomizationURL string
CNPGKustomizationRef string
CNPGKustomizationTimeout string
@ -53,27 +43,6 @@ type SetupOptions struct {
// SetupOption is a function that sets up an option for the test environment setup.
type SetupOption func(*SetupOptions)
// WithK8sVersion sets the Kubernetes version for the test environment.
func WithK8sVersion(version string) SetupOption {
return func(opts *SetupOptions) {
opts.K8sVersion = version
}
}
// WithKindVersion sets the Kind version for the test environment.
func WithKindVersion(version string) SetupOption {
return func(opts *SetupOptions) {
opts.KindVersion = version
}
}
// WithKindAdditionalNetworks sets the additional networks for the Kind cluster for the test environment.
func WithKindAdditionalNetworks(networks []string) SetupOption {
return func(opts *SetupOptions) {
opts.KindAdditionalNetworks = networks
}
}
// WithCNPGKustomizationURL sets the CloudNativePG kustomization URL for the test environment.
func WithCNPGKustomizationURL(url string) SetupOption {
return func(opts *SetupOptions) {
@ -124,56 +93,31 @@ func WithIgnoreExistingResources(ignore bool) SetupOption {
}
}
// WithKindClusterNamePrefix sets the prefix for the Kind cluster name for the test environment.
func withKindClusterNamePrefix(name string) SetupOption {
return func(opts *SetupOptions) {
opts.KindClusterNamePrefix = name
}
}
const (
kindConfigFile = "config/kind-config.yaml"
)
func defaultSetupOptions() SetupOptions {
// TODO: renovate
return SetupOptions{
K8sVersion: "v1.31.1",
KindVersion: "v0.24.0",
CertManagerVersion: "v1.15.1",
KindClusterNamePrefix: "e2e",
KindAdditionalNetworks: []string{},
CertManagerVersion: "v1.15.1",
}
}
// Setup sets up the test environment for the e2e tests, starting kind and installing the necessary components.
//
//nolint:ireturn
func Setup(ctx context.Context, opts ...SetupOption) (client.Client, error) {
func Setup(ctx context.Context, cl client.Client, opts ...SetupOption) error {
options := defaultSetupOptions()
for _, opt := range opts {
opt(&options)
}
if err := setupKind(ctx, options); err != nil {
return nil, err
}
cl, _, err := internalClient.NewClient()
if err != nil {
return nil, fmt.Errorf("failed to create Kubernetes client: %w", err)
}
if err := installCertManager(ctx, cl, options); err != nil {
return nil, err
return err
}
if err := installCNPG(ctx, cl, options); err != nil {
return nil, err
return err
}
// Return the Kubernetes client used for the tests
return cl, nil
return nil
}
func installCNPG(ctx context.Context, cl client.Client, options SetupOptions) error {
@ -227,32 +171,3 @@ func installCertManager(ctx context.Context, cl client.Client, options SetupOpti
return nil
}
func setupKind(ctx context.Context, options SetupOptions) error {
// This function sets up the environment for the e2e tests
// by creating the cluster and installing the necessary
// components.
expectedClusterName := kindClusterName(options.KindClusterNamePrefix, options.K8sVersion)
provider := cluster.NewProvider()
clusterIsRunning, err := kind.IsClusterRunning(provider, expectedClusterName)
if err != nil {
return fmt.Errorf("failed to check if Kind cluster is running: %w", err)
}
if !clusterIsRunning {
kindOpts := []kind.CreateClusterOption{
kind.WithK8sVersion(options.K8sVersion),
kind.WithConfigFile(kindConfigFile),
kind.WithNetworks(options.KindAdditionalNetworks),
}
if err := kind.CreateCluster(ctx, provider, expectedClusterName, kindOpts...); err != nil {
return fmt.Errorf("failed to create Kind cluster: %w", err)
}
}
return nil
}
func kindClusterName(prefix, k8sVersion string) string {
k8sVersion = strings.ReplaceAll(k8sVersion, ".", "-")
return fmt.Sprintf("%s-%s", prefix, k8sVersion)
}

View File

@ -1,164 +0,0 @@
/*
Copyright 2024.
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 kind
import (
"context"
"fmt"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/strslice"
"github.com/docker/docker/client"
"sigs.k8s.io/kind/pkg/cluster"
"sigs.k8s.io/kind/pkg/cluster/nodes"
)
// IsClusterRunning checks if a Kind cluster with the given name is running.
func IsClusterRunning(provider *cluster.Provider, clusterName string) (bool, error) {
clusters, err := provider.List()
if err != nil {
return false, fmt.Errorf("failed to list Kind clusters: %w", err)
}
for _, c := range clusters {
if c == clusterName {
return true, nil
}
}
return false, nil
}
// CreateClusterOptions are the options for creating a Kind cluster.
type CreateClusterOptions struct {
ConfigFile string
K8sVersion string
Networks []string
}
// CreateClusterOption is the option for creating a Kind cluster.
type CreateClusterOption func(*CreateClusterOptions)
// WithConfigFile sets the config file for creating a Kind cluster.
func WithConfigFile(configFile string) CreateClusterOption {
return func(opts *CreateClusterOptions) {
opts.ConfigFile = configFile
}
}
// WithK8sVersion sets the Kubernetes version for creating a Kind cluster.
func WithK8sVersion(k8sVersion string) CreateClusterOption {
return func(opts *CreateClusterOptions) {
opts.K8sVersion = k8sVersion
}
}
// WithNetworks sets the network for creating a Kind cluster.
func WithNetworks(networks []string) CreateClusterOption {
return func(opts *CreateClusterOptions) {
opts.Networks = networks
}
}
// CreateCluster creates a Kind cluster with the given name.
func CreateCluster(ctx context.Context, provider *cluster.Provider, name string, opts ...CreateClusterOption) error {
options := &CreateClusterOptions{}
for _, opt := range opts {
opt(options)
}
createOpts := []cluster.CreateOption{
cluster.CreateWithRetain(true),
cluster.CreateWithDisplayUsage(true),
cluster.CreateWithDisplaySalutation(true),
}
if options.ConfigFile != "" {
createOpts = append(createOpts, cluster.CreateWithConfigFile(options.ConfigFile))
}
if options.K8sVersion != "" {
createOpts = append(createOpts, cluster.CreateWithNodeImage(fmt.Sprintf("kindest/node:%s", options.K8sVersion)))
}
err := provider.Create(name, createOpts...)
if err != nil {
return fmt.Errorf("kind cluster creation failed: %w", err)
}
// Initialize Docker client
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
return fmt.Errorf("failed to create Docker client: %w", err)
}
// Since a cluster can mount additional certificates, we need to make sure they are
// usable by the nodes in the cluster.
nodeList, err := getNodes(provider, name)
if err != nil {
return err
}
if err := updateCACertificates(ctx, cli, nodeList); err != nil {
return fmt.Errorf("failed to update CA certificates: %w", err)
}
if err := connectNetworks(ctx, cli, nodeList, options.Networks); err != nil {
return fmt.Errorf("failed to connect networks: %w", err)
}
return nil
}
func updateCACertificates(ctx context.Context, cli *client.Client, nodes []nodes.Node) error {
for _, node := range nodes {
execConfig := container.ExecOptions{
Cmd: strslice.StrSlice([]string{"update-ca-certificates"}),
AttachStdout: true,
AttachStderr: true,
}
execID, err := cli.ContainerExecCreate(ctx, node.String(), execConfig)
if err != nil {
return fmt.Errorf("failed to create exec instance in node %s: %w", node.String(), err)
}
err = cli.ContainerExecStart(ctx, execID.ID, container.ExecStartOptions{})
if err != nil {
return fmt.Errorf("failed to start exec instance in node %s: %w", node.String(), err)
}
}
return nil
}
func connectNetworks(ctx context.Context, cli *client.Client, nodes []nodes.Node, networks []string) error {
for _, netw := range networks {
for _, node := range nodes {
err := cli.NetworkConnect(ctx, netw, node.String(), nil)
if err != nil {
return fmt.Errorf("failed to connect node %s to network %s: %w", node.String(), netw, err)
}
}
}
return nil
}
func getNodes(provider *cluster.Provider, clusterName string) ([]nodes.Node, error) {
nodeList, err := provider.ListNodes(clusterName)
if err != nil {
return nil, fmt.Errorf("failed to get Kind nodes: %w", err)
}
return nodeList, nil
}

View File

@ -1,19 +0,0 @@
/*
Copyright 2024.
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 kind provides utilities for ensuring the presence and correct version of the Kind binary,
// as well as functions for installing and managing the Kind binary in a local project.
package kind