diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f78af7..963f485 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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 diff --git a/Taskfile.yml b/Taskfile.yml index 8044022..e7fe23c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -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: 910e1ac9754f208569ac4d65f1ef52d9a2301833 - _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: 910e1ac9754f208569ac4d65f1ef52d9a2301833 - _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: diff --git a/dagger/e2e/.gitattributes b/dagger/e2e/.gitattributes new file mode 100644 index 0000000..3a45493 --- /dev/null +++ b/dagger/e2e/.gitattributes @@ -0,0 +1,4 @@ +/dagger.gen.go linguist-generated +/internal/dagger/** linguist-generated +/internal/querybuilder/** linguist-generated +/internal/telemetry/** linguist-generated diff --git a/dagger/e2e/.gitignore b/dagger/e2e/.gitignore new file mode 100644 index 0000000..7ebabcc --- /dev/null +++ b/dagger/e2e/.gitignore @@ -0,0 +1,4 @@ +/dagger.gen.go +/internal/dagger +/internal/querybuilder +/internal/telemetry diff --git a/dagger/e2e/dagger.json b/dagger/e2e/dagger.json new file mode 100644 index 0000000..ee944fa --- /dev/null +++ b/dagger/e2e/dagger.json @@ -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": "." +} diff --git a/dagger/e2e/go.mod b/dagger/e2e/go.mod new file mode 100644 index 0000000..61664d8 --- /dev/null +++ b/dagger/e2e/go.mod @@ -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 diff --git a/dagger/e2e/go.sum b/dagger/e2e/go.sum new file mode 100644 index 0000000..e566804 --- /dev/null +++ b/dagger/e2e/go.sum @@ -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= diff --git a/dagger/e2e/main.go b/dagger/e2e/main.go new file mode 100644 index 0000000..c997d35 --- /dev/null +++ b/dagger/e2e/main.go @@ -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) +} diff --git a/test/e2e/config/kind-config.yaml b/hack/kind-config.yaml similarity index 90% rename from test/e2e/config/kind-config.yaml rename to hack/kind-config.yaml index f755bdd..ec055f7 100644 --- a/test/e2e/config/kind-config.yaml +++ b/hack/kind-config.yaml @@ -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 diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 8c99052..1922afa 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -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)) } diff --git a/test/e2e/internal/e2etestenv/main.go b/test/e2e/internal/e2etestenv/main.go index 089da81..e7aad8d 100644 --- a/test/e2e/internal/e2etestenv/main.go +++ b/test/e2e/internal/e2etestenv/main.go @@ -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) -} diff --git a/test/e2e/internal/kind/cluster.go b/test/e2e/internal/kind/cluster.go deleted file mode 100644 index 05c6803..0000000 --- a/test/e2e/internal/kind/cluster.go +++ /dev/null @@ -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 -} diff --git a/test/e2e/internal/kind/doc.go b/test/e2e/internal/kind/doc.go deleted file mode 100644 index b770ac5..0000000 --- a/test/e2e/internal/kind/doc.go +++ /dev/null @@ -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