mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-07-09 19:22:21 +02:00
Compare commits
7 Commits
14f78b6fa6
...
62f2b012aa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
62f2b012aa | ||
|
|
3593c18f3f | ||
|
|
e8655233d6 | ||
|
|
12549771f9 | ||
|
|
a8b446faa8 | ||
|
|
27589c0eeb | ||
|
|
6ae101f016 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -44,7 +44,7 @@ jobs:
|
||||
- name: Install Dagger
|
||||
env:
|
||||
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
|
||||
DAGGER_VERSION: 0.20.1
|
||||
DAGGER_VERSION: 0.20.4
|
||||
run: |
|
||||
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
|
||||
- name: Run CI task
|
||||
|
||||
2
.github/workflows/release-please.yml
vendored
2
.github/workflows/release-please.yml
vendored
@ -31,7 +31,7 @@ jobs:
|
||||
- name: Install Dagger
|
||||
env:
|
||||
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
|
||||
DAGGER_VERSION: 0.20.1
|
||||
DAGGER_VERSION: 0.20.4
|
||||
run: |
|
||||
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
|
||||
- name: Create image and manifest
|
||||
|
||||
2
.github/workflows/release-publish.yml
vendored
2
.github/workflows/release-publish.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
||||
- name: Install Dagger
|
||||
env:
|
||||
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
|
||||
DAGGER_VERSION: 0.20.1
|
||||
DAGGER_VERSION: 0.20.4
|
||||
run: |
|
||||
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
|
||||
- name: Create image and manifest
|
||||
|
||||
27
Taskfile.yml
27
Taskfile.yml
@ -6,6 +6,8 @@ output: prefixed
|
||||
|
||||
# Variables that are shared across tasks.
|
||||
vars:
|
||||
GO_VERSION:
|
||||
sh: sed -n 's/^toolchain go//p' go.mod
|
||||
# renovate: datasource=docker depName=kindest/node versioning=semver
|
||||
E2E_KUBERNETES_VERSION: v1.35.1
|
||||
E2E_CLUSTER_NAME: barman-cloud-plugin-e2e-{{.E2E_KUBERNETES_VERSION}}
|
||||
@ -21,7 +23,7 @@ tasks:
|
||||
# renovate: datasource=git-refs depName=golangci-lint lookupName=https://github.com/sagikazarmark/daggerverse currentValue=main
|
||||
DAGGER_GOLANGCI_LINT_SHA: 5dcc7e4c4cd5ed230046955f42e27f2166545155
|
||||
# renovate: datasource=docker depName=golangci/golangci-lint versioning=semver
|
||||
GOLANGCI_LINT_VERSION: v2.11.2
|
||||
GOLANGCI_LINT_VERSION: v2.11.4
|
||||
cmds:
|
||||
- >
|
||||
GITHUB_REF= dagger -sc "github.com/sagikazarmark/daggerverse/golangci-lint@${DAGGER_GOLANGCI_LINT_SHA}
|
||||
@ -128,8 +130,6 @@ tasks:
|
||||
go-test:
|
||||
desc: Run go test
|
||||
env:
|
||||
# renovate: datasource=docker depName=golang versioning=semver
|
||||
GOLANG_IMAGE_VERSION: 1.26.1
|
||||
# renovate: datasource=git-refs depname=kubernetes packageName=https://github.com/kubernetes/kubernetes versioning=semver
|
||||
K8S_VERSION: 1.31.0
|
||||
# renovate: datasource=git-refs depName=controller-runtime packageName=https://github.com/kubernetes-sigs/controller-runtime versioning=semver
|
||||
@ -137,7 +137,7 @@ tasks:
|
||||
cmds:
|
||||
- >
|
||||
GITHUB_REF= dagger -s call -m ./dagger/gotest
|
||||
--go-version ${GOLANG_IMAGE_VERSION}
|
||||
--go-version {{.GO_VERSION}}
|
||||
--kube-version ${K8S_VERSION}
|
||||
--setup-envtest-version ${SETUP_ENVTEST_VERSION}
|
||||
unit-test --src .
|
||||
@ -206,7 +206,7 @@ tasks:
|
||||
- start-build-network
|
||||
vars:
|
||||
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
|
||||
DAGGER_VERSION: 0.20.1
|
||||
DAGGER_VERSION: 0.20.4
|
||||
DAGGER_ENGINE_IMAGE: registry.dagger.io/engine:v{{ .DAGGER_VERSION }}
|
||||
cmds:
|
||||
- >
|
||||
@ -231,7 +231,7 @@ tasks:
|
||||
cmds:
|
||||
- >
|
||||
GITHUB_REF= dagger -s call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
|
||||
build --dir . --file containers/Dockerfile.plugin --platform linux/amd64
|
||||
build --dir . --file containers/Dockerfile.plugin --args GO_VERSION={{.GO_VERSION}} --platform linux/amd64
|
||||
publish --ref {{ .REGISTRY_NAME }}:{{ .REGISTRY_PORT }}/plugin-barman-cloud --tags testing
|
||||
|
||||
# We build an image and push it to a local registry.
|
||||
@ -248,7 +248,7 @@ tasks:
|
||||
cmds:
|
||||
- >
|
||||
GITHUB_REF= dagger -s call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
|
||||
build --dir . --file containers/Dockerfile.sidecar --platform linux/amd64
|
||||
build --dir . --file containers/Dockerfile.sidecar --args GO_VERSION={{.GO_VERSION}} --platform linux/amd64
|
||||
publish --ref {{ .REGISTRY_NAME }}:{{ .REGISTRY_PORT }}/sidecar-barman-cloud --tags testing
|
||||
|
||||
build-images:
|
||||
@ -305,8 +305,6 @@ tasks:
|
||||
- build-images
|
||||
- start-kind-cluster
|
||||
vars:
|
||||
# renovate: datasource=docker depName=golang versioning=semver
|
||||
GOLANG_IMAGE_VERSION: 1.26.1
|
||||
KUBECONFIG_PATH:
|
||||
sh: mktemp -t kubeconfig-XXXXX
|
||||
env:
|
||||
@ -317,15 +315,12 @@ tasks:
|
||||
GITHUB_REF= dagger call -m dagger/e2e/ run
|
||||
--source .
|
||||
--kubeconfig {{.KUBECONFIG_PATH}}
|
||||
--go-version {{ .GOLANG_IMAGE_VERSION }}
|
||||
--go-version {{.GO_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.26.1
|
||||
env:
|
||||
_EXPERIMENTAL_DAGGER_RUNNER_HOST: docker-container://{{ .DAGGER_ENGINE_CONTAINER_NAME }}
|
||||
cmds:
|
||||
@ -334,7 +329,7 @@ tasks:
|
||||
--source .
|
||||
--ca certs/ca.pem
|
||||
--registry {{.REGISTRY_NAME}}:{{.REGISTRY_PORT}}
|
||||
--go-version {{ .GOLANG_IMAGE_VERSION }}
|
||||
--go-version {{.GO_VERSION}}
|
||||
|
||||
ci:
|
||||
desc: Run the CI pipeline
|
||||
@ -373,12 +368,12 @@ tasks:
|
||||
- >
|
||||
dagger call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
|
||||
--registry ghcr.io --username $REGISTRY_USER --password env:REGISTRY_PASSWORD
|
||||
build --dir . --file containers/Dockerfile.plugin --platform linux/amd64 --platform linux/arm64
|
||||
build --dir . --file containers/Dockerfile.plugin --args GO_VERSION={{.GO_VERSION}} --platform linux/amd64 --platform linux/arm64
|
||||
publish --ref {{.PLUGIN_IMAGE_NAME}} --tags {{.IMAGE_VERSION}}
|
||||
- >
|
||||
dagger call -m github.com/purpleclay/daggerverse/docker@${DAGGER_DOCKER_SHA}
|
||||
--registry ghcr.io --username $REGISTRY_USER --password env:REGISTRY_PASSWORD
|
||||
build --dir . --file containers/Dockerfile.sidecar --platform linux/amd64 --platform linux/arm64
|
||||
build --dir . --file containers/Dockerfile.sidecar --args GO_VERSION={{.GO_VERSION}} --platform linux/amd64 --platform linux/arm64
|
||||
publish --ref {{.SIDECAR_IMAGE_NAME}} --tags {{.IMAGE_VERSION}}
|
||||
|
||||
controller-gen:
|
||||
|
||||
@ -44,6 +44,7 @@ rules:
|
||||
- postgresql.cnpg.io
|
||||
resources:
|
||||
- backups
|
||||
- clusters
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
# Build the manager binary
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.1 AS gobuilder
|
||||
# GO_VERSION must be passed as a build arg (read from go.mod toolchain directive by Taskfile)
|
||||
ARG GO_VERSION
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS gobuilder
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
|
||||
@ -5,7 +5,9 @@
|
||||
# Both components are built before going into a distroless container
|
||||
|
||||
# Build the manager binary
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.1 AS gobuilder
|
||||
# GO_VERSION must be passed as a build arg (read from go.mod toolchain directive by Taskfile)
|
||||
ARG GO_VERSION
|
||||
FROM --platform=$BUILDPLATFORM golang:${GO_VERSION} AS gobuilder
|
||||
ARG TARGETOS
|
||||
ARG TARGETARCH
|
||||
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
barman[azure,cloud,google,snappy,zstandard,lz4]==3.17.0
|
||||
barman[azure,cloud,google,snappy,zstandard,lz4]==3.18.0
|
||||
setuptools==82.0.1
|
||||
zipp>=3.19.1 # not directly required, pinned by Snyk to avoid a vulnerability
|
||||
|
||||
@ -18,9 +18,9 @@ azure-storage-blob==12.28.0 \
|
||||
--hash=sha256:00fb1db28bf6a7b7ecaa48e3b1d5c83bfadacc5a678b77826081304bd87d6461 \
|
||||
--hash=sha256:e7d98ea108258d29aa0efbfd591b2e2075fa1722a2fae8699f0b3c9de11eff41
|
||||
# via barman
|
||||
barman==3.17.0 \
|
||||
--hash=sha256:07b033da14e72f103de44261c31bd0c3169bbb2e4de3481c6bb3510e9870d38e \
|
||||
--hash=sha256:d6618990a6dbb31af3286d746a278a038534b7e3cc617c2b379ef7ebdeb7ed5a
|
||||
barman==3.18.0 \
|
||||
--hash=sha256:8e752ac93d2f3a61e86b8374185209cae477a638ece7e6f540070f36d28d6997 \
|
||||
--hash=sha256:ff90c44dafa4107b7574142771cdc2611c4cf1af818d93d3e67440a0c81164b9
|
||||
# via -r sidecar-requirements.in
|
||||
boto3==1.42.63 \
|
||||
--hash=sha256:cd008cfd0d7ea30f1c5e22daf0998c55b7c6c68cb68eea05110e33fe641173d5 \
|
||||
|
||||
49
go.mod
49
go.mod
@ -5,23 +5,23 @@ go 1.25.0
|
||||
toolchain go1.26.1
|
||||
|
||||
require (
|
||||
github.com/cert-manager/cert-manager v1.19.4
|
||||
github.com/cert-manager/cert-manager v1.20.1
|
||||
github.com/cloudnative-pg/api v1.28.1
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.0
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.1
|
||||
github.com/cloudnative-pg/cnpg-i v0.3.1
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.29.0
|
||||
github.com/cloudnative-pg/cnpg-i v0.5.0
|
||||
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2
|
||||
github.com/cloudnative-pg/machinery v0.3.3
|
||||
github.com/onsi/ginkgo/v2 v2.28.1
|
||||
github.com/onsi/gomega v1.39.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/spf13/viper v1.21.0
|
||||
google.golang.org/grpc v1.79.3
|
||||
google.golang.org/grpc v1.80.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.35.2
|
||||
k8s.io/apiextensions-apiserver v0.35.2
|
||||
k8s.io/apimachinery v0.35.2
|
||||
k8s.io/client-go v0.35.2
|
||||
k8s.io/api v0.35.3
|
||||
k8s.io/apiextensions-apiserver v0.35.3
|
||||
k8s.io/apimachinery v0.35.3
|
||||
k8s.io/client-go v0.35.3
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2
|
||||
sigs.k8s.io/controller-runtime v0.23.3
|
||||
sigs.k8s.io/kustomize/api v0.21.1
|
||||
@ -32,6 +32,7 @@ require (
|
||||
cel.dev/expr v0.25.1 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1 // indirect
|
||||
github.com/avast/retry-go/v5 v5.0.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
@ -74,7 +75,7 @@ require (
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0 // indirect
|
||||
github.com/lib/pq v1.11.1 // indirect
|
||||
github.com/lib/pq v1.12.0 // indirect
|
||||
github.com/moby/spdystream v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
@ -113,29 +114,29 @@ require (
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||
golang.org/x/mod v0.32.0 // indirect
|
||||
golang.org/x/net v0.49.0 // indirect
|
||||
golang.org/x/oauth2 v0.34.0 // indirect
|
||||
golang.org/x/sync v0.19.0 // indirect
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/oauth2 v0.35.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/term v0.39.0 // indirect
|
||||
golang.org/x/text v0.33.0 // indirect
|
||||
golang.org/x/term v0.41.0 // indirect
|
||||
golang.org/x/text v0.35.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.41.0 // indirect
|
||||
golang.org/x/tools v0.42.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apiserver v0.35.2 // indirect
|
||||
k8s.io/component-base v0.35.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect
|
||||
k8s.io/apiserver v0.35.3 // indirect
|
||||
k8s.io/component-base v0.35.3 // indirect
|
||||
k8s.io/klog/v2 v2.140.0 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 // indirect
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 // indirect
|
||||
sigs.k8s.io/gateway-api v1.4.0 // indirect
|
||||
sigs.k8s.io/gateway-api v1.5.0 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
102
go.sum
102
go.sum
@ -6,24 +6,26 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/avast/retry-go/v5 v5.0.0 h1:kf1Qc2UsTZ4qq8elDymqfbISvkyMuhgRxuJqX2NHP7k=
|
||||
github.com/avast/retry-go/v5 v5.0.0/go.mod h1://d+usmKWio1agtZfS1H/ltTqwtIfBnRq9zEwjc3eH8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
|
||||
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/cert-manager/cert-manager v1.19.4 h1:7lOkSYj+nJNjgGFfAznQzPpOfWX+1Kgz6xUXwTa/K5k=
|
||||
github.com/cert-manager/cert-manager v1.19.4/go.mod h1:9uBnn3IK9NxjjuXmQDYhwOwFUU5BtGVB1g/voPvvcVw=
|
||||
github.com/cert-manager/cert-manager v1.20.1 h1:99ExHJu5TPp1V92AvvE4oY6BkOSyJiWLxxMkbqbdGaY=
|
||||
github.com/cert-manager/cert-manager v1.20.1/go.mod h1:ut67FnggYJJqAdDWLhSPnj10P06QwbNU88RYNh9MvMc=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudnative-pg/api v1.28.1 h1:hp/TDgR+RrTa4EMp/pAKZia+kZ52i8ZPLG+JG10eeVU=
|
||||
github.com/cloudnative-pg/api v1.28.1/go.mod h1:J0RTwrOYxQP17N1YMrDxcUmr/0wPYHQW179P7ibkVsM=
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.0 h1:DykSaX4o7ee2vyu5FQoG1RJsntHd+EIttIKZbJPlB1Q=
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.0/go.mod h1:SO2HzLa+GWlSIpGyxnISoJAFPIcaa/qDa33Bb3jefac=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.1 h1:HdOUWgFhta558uHfXeO/199qCApxaj5yi05x6nWNmgs=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.28.1/go.mod h1:yhRa4GqJAjNd0tT9AiRgk1KdqLhMjo/JmGGoASRl2CU=
|
||||
github.com/cloudnative-pg/cnpg-i v0.3.1 h1:fKj8NoToWI11HUL2UWYJBpkVzmaTvbs3kDMo7wQF8RU=
|
||||
github.com/cloudnative-pg/cnpg-i v0.3.1/go.mod h1:glRDiJLJY51FY8ScJIv/OkaGJxFnojJkkNAqSy5XC6s=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.29.0 h1:49Dm8+y4va7RODspJjeaK8uMWP3OGAD0gMsxhjm16Mo=
|
||||
github.com/cloudnative-pg/cloudnative-pg v1.29.0/go.mod h1:0Sgb/50VyaCnQm3IbWqgnhQG8Kb6mgqo8Jo1J+KtkSI=
|
||||
github.com/cloudnative-pg/cnpg-i v0.5.0 h1:/TOzpNT6cwNgrpftTtrnLKdoHgMwd+88vZgXjlVgXeE=
|
||||
github.com/cloudnative-pg/cnpg-i v0.5.0/go.mod h1:7Gh4+UzhBpGhr4DreB1GN9wGYfvxwXCXZUyVt3zE/3I=
|
||||
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2 h1:0reS9MtyLYINHXQ/MfxJ9jp39hhBf8e3Qdj+T5Nsq6I=
|
||||
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2/go.mod h1:gvrKabgxXq0zGthXGucemDdsxakLEQDMxn43M4HLW30=
|
||||
github.com/cloudnative-pg/machinery v0.3.3 h1:CaqXqLTJH9RrVv3R/YU0NmFaI/F18HLg2JfH3mQLcDk=
|
||||
@ -143,8 +145,8 @@ github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0 h1:bMqrb3UHgHbP+
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.4.0/go.mod h1:E3vdYxHj2C2q6qo8/Da4g7P+IcwqRZyy3gJBzYybV9Y=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/lib/pq v1.11.1 h1:wuChtj2hfsGmmx3nf1m7xC2XpK6OtelS2shMY+bGMtI=
|
||||
github.com/lib/pq v1.11.1/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/lib/pq v1.12.0 h1:mC1zeiNamwKBecjHarAr26c/+d8V5w/u4J0I/yASbJo=
|
||||
github.com/lib/pq v1.12.0/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/maruel/natural v1.1.1 h1:Hja7XhhmvEFhcByqDoHz9QZbkWey+COd9xWfCfn1ioo=
|
||||
github.com/maruel/natural v1.1.1/go.mod h1:v+Rfd79xlw1AgVBjbO0BEQmptqb5HvL/k9GRHB7ZKEg=
|
||||
github.com/mfridman/tparse v0.18.0 h1:wh6dzOKaIwkUGyKgOntDW4liXSo37qg5AXbIhkMV3vE=
|
||||
@ -269,34 +271,34 @@ go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 h1:R9PFI6EUdfVKgwKjZef7QIwGcBKu86OEFpJ9nUEP2l4=
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792/go.mod h1:A+z0yzpGtvnG90cToK5n2tu8UJVP2XUATh+r+sfOOOc=
|
||||
golang.org/x/mod v0.32.0 h1:9F4d3PHLljb6x//jOyokMv3eX+YDeepZSEo3mFJy93c=
|
||||
golang.org/x/mod v0.32.0/go.mod h1:SgipZ/3h2Ci89DlEtEXWUk/HteuRin+HHhN+WbNhguU=
|
||||
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
|
||||
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
|
||||
golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw=
|
||||
golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
|
||||
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
|
||||
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
|
||||
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/tools v0.41.0 h1:a9b8iMweWG+S0OBnlU36rzLp20z1Rp10w+IY2czHTQc=
|
||||
golang.org/x/tools v0.41.0/go.mod h1:XSY6eDqxVNiYgezAVqqCeihT4j1U2CCsqvH3WhQpnlg=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0=
|
||||
gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
@ -310,30 +312,30 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
k8s.io/api v0.35.2 h1:tW7mWc2RpxW7HS4CoRXhtYHSzme1PN1UjGHJ1bdrtdw=
|
||||
k8s.io/api v0.35.2/go.mod h1:7AJfqGoAZcwSFhOjcGM7WV05QxMMgUaChNfLTXDRE60=
|
||||
k8s.io/apiextensions-apiserver v0.35.2 h1:iyStXHoJZsUXPh/nFAsjC29rjJWdSgUmG1XpApE29c0=
|
||||
k8s.io/apiextensions-apiserver v0.35.2/go.mod h1:OdyGvcO1FtMDWQ+rRh/Ei3b6X3g2+ZDHd0MSRGeS8rU=
|
||||
k8s.io/apimachinery v0.35.2 h1:NqsM/mmZA7sHW02JZ9RTtk3wInRgbVxL8MPfzSANAK8=
|
||||
k8s.io/apimachinery v0.35.2/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/apiserver v0.35.2 h1:rb52v0CZGEL0FkhjS+I6jHflAp7fZ4MIaKcEHX7wmDk=
|
||||
k8s.io/apiserver v0.35.2/go.mod h1:CROJUAu0tfjZLyYgSeBsBan2T7LUJGh0ucWwTCSSk7g=
|
||||
k8s.io/client-go v0.35.2 h1:YUfPefdGJA4aljDdayAXkc98DnPkIetMl4PrKX97W9o=
|
||||
k8s.io/client-go v0.35.2/go.mod h1:4QqEwh4oQpeK8AaefZ0jwTFJw/9kIjdQi0jpKeYvz7g=
|
||||
k8s.io/component-base v0.35.2 h1:btgR+qNrpWuRSuvWSnQYsZy88yf5gVwemvz0yw79pGc=
|
||||
k8s.io/component-base v0.35.2/go.mod h1:B1iBJjooe6xIJYUucAxb26RwhAjzx0gHnqO9htWIX+0=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ=
|
||||
k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ=
|
||||
k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4=
|
||||
k8s.io/apiextensions-apiserver v0.35.3 h1:2fQUhEO7P17sijylbdwt0nBdXP0TvHrHj0KeqHD8FiU=
|
||||
k8s.io/apiextensions-apiserver v0.35.3/go.mod h1:tK4Kz58ykRpwAEkXUb634HD1ZAegEElktz/B3jgETd8=
|
||||
k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
|
||||
k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/apiserver v0.35.3 h1:D2eIcfJ05hEAEewoSDg+05e0aSRwx8Y4Agvd/wiomUI=
|
||||
k8s.io/apiserver v0.35.3/go.mod h1:JI0n9bHYzSgIxgIrfe21dbduJ9NHzKJ6RchcsmIKWKY=
|
||||
k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg=
|
||||
k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c=
|
||||
k8s.io/component-base v0.35.3 h1:mbKbzoIMy7JDWS/wqZobYW1JDVRn/RKRaoMQHP9c4P0=
|
||||
k8s.io/component-base v0.35.3/go.mod h1:IZ8LEG30kPN4Et5NeC7vjNv5aU73ku5MS15iZyvyMYk=
|
||||
k8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=
|
||||
k8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4 h1:HhDfevmPS+OalTjQRKbTHppRIz01AWi8s45TMXStgYY=
|
||||
k8s.io/kube-openapi v0.0.0-20260127142750-a19766b6e2d4/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=
|
||||
k8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0 h1:qPrZsv1cwQiFeieFlRqT627fVZ+tyfou/+S5S0H5ua0=
|
||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.33.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||
sigs.k8s.io/controller-runtime v0.23.3 h1:VjB/vhoPoA9l1kEKZHBMnQF33tdCLQKJtydy4iqwZ80=
|
||||
sigs.k8s.io/controller-runtime v0.23.3/go.mod h1:B6COOxKptp+YaUT5q4l6LqUJTRpizbgf9KSRNdQGns0=
|
||||
sigs.k8s.io/gateway-api v1.4.0 h1:ZwlNM6zOHq0h3WUX2gfByPs2yAEsy/EenYJB78jpQfQ=
|
||||
sigs.k8s.io/gateway-api v1.4.0/go.mod h1:AR5RSqciWP98OPckEjOjh2XJhAe2Na4LHyXD2FUY7Qk=
|
||||
sigs.k8s.io/gateway-api v1.5.0 h1:duoo14Ky/fJXpjpmyMISE2RTBGnfCg8zICfTYLTnBJA=
|
||||
sigs.k8s.io/gateway-api v1.5.0/go.mod h1:GvCETiaMAlLym5CovLxGjS0NysqFk3+Yuq3/rh6QL2o=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/kustomize/api v0.21.1 h1:lzqbzvz2CSvsjIUZUBNFKtIMsEw7hVLJp0JeSIVmuJs=
|
||||
@ -342,7 +344,7 @@ sigs.k8s.io/kustomize/kyaml v0.21.1 h1:IVlbmhC076nf6foyL6Taw4BkrLuEsXUXNpsE+ScX7
|
||||
sigs.k8s.io/kustomize/kyaml v0.21.1/go.mod h1:hmxADesM3yUN2vbA5z1/YTBnzLJ1dajdqpQonwBL1FQ=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482 h1:2WOzJpHUBVrrkDjU4KBT8n5LDcj824eX0I5UKcgeRUs=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2-0.20260122202528-d9cc6641c482/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2 h1:kwVWMx5yS1CrnFWA/2QHyRVJ8jM6dBA80uLmm0wJkk8=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.2/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
|
||||
@ -102,6 +102,8 @@ func NewCmd() *cobra.Command {
|
||||
_ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address"))
|
||||
|
||||
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
|
||||
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
|
||||
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -57,6 +57,8 @@ func NewCmd() *cobra.Command {
|
||||
_ = viper.BindEnv("pod-name", "POD_NAME")
|
||||
_ = viper.BindEnv("pgdata", "PGDATA")
|
||||
_ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
|
||||
_ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
|
||||
_ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSION")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -28,16 +28,15 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
extendedclient "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/instance/internal/client"
|
||||
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
|
||||
)
|
||||
|
||||
// Start starts the sidecar informers and CNPG-i server
|
||||
@ -127,26 +126,7 @@ func generateScheme(ctx context.Context) *runtime.Scheme {
|
||||
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(result))
|
||||
|
||||
cnpgGroup := viper.GetString("custom-cnpg-group")
|
||||
cnpgVersion := viper.GetString("custom-cnpg-version")
|
||||
if len(cnpgGroup) == 0 {
|
||||
cnpgGroup = cnpgv1.SchemeGroupVersion.Group
|
||||
}
|
||||
if len(cnpgVersion) == 0 {
|
||||
cnpgVersion = cnpgv1.SchemeGroupVersion.Version
|
||||
}
|
||||
|
||||
// Proceed with custom registration of the CNPG scheme
|
||||
schemeGroupVersion := schema.GroupVersion{Group: cnpgGroup, Version: cnpgVersion}
|
||||
schemeBuilder := &scheme.Builder{GroupVersion: schemeGroupVersion}
|
||||
schemeBuilder.Register(&cnpgv1.Cluster{}, &cnpgv1.ClusterList{})
|
||||
schemeBuilder.Register(&cnpgv1.Backup{}, &cnpgv1.BackupList{})
|
||||
schemeBuilder.Register(&cnpgv1.ScheduledBackup{}, &cnpgv1.ScheduledBackupList{})
|
||||
utilruntime.Must(schemeBuilder.AddToScheme(result))
|
||||
|
||||
schemeLog := log.FromContext(ctx)
|
||||
schemeLog.Info("CNPG types registration", "schemeGroupVersion", schemeGroupVersion)
|
||||
pluginscheme.AddCNPGToScheme(ctx, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
@ -24,7 +24,6 @@ import (
|
||||
"crypto/tls"
|
||||
|
||||
// +kubebuilder:scaffold:imports
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -38,25 +37,33 @@ import (
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/controller"
|
||||
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
// generateScheme creates a runtime.Scheme with all type definitions
|
||||
// needed by the operator. CNPG types are registered under a
|
||||
// configurable API group to support custom CNPG-based operators.
|
||||
func generateScheme(ctx context.Context) *runtime.Scheme {
|
||||
result := runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
||||
utilruntime.Must(cnpgv1.AddToScheme(scheme))
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(result))
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||
pluginscheme.AddCNPGToScheme(ctx, result)
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Start starts the manager
|
||||
func Start(ctx context.Context) error {
|
||||
setupLog := log.FromContext(ctx)
|
||||
|
||||
scheme := generateScheme(ctx)
|
||||
|
||||
var tlsOpts []func(*tls.Config)
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
|
||||
@ -1,58 +0,0 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package operator
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
// setOwnerReference explicitly set the owner reference between an
|
||||
// owner object and a controller one.
|
||||
//
|
||||
// Important: this function won't use any registered scheme and will
|
||||
// fail unless the metadata has been correctly set into the owner
|
||||
// object.
|
||||
func setOwnerReference(owner, controlled metav1.Object) error {
|
||||
ro, ok := owner.(runtime.Object)
|
||||
if !ok {
|
||||
return fmt.Errorf("%T is not a runtime.Object, cannot call setOwnerReference", owner)
|
||||
}
|
||||
|
||||
if len(ro.DeepCopyObject().GetObjectKind().GroupVersionKind().Group) == 0 {
|
||||
return fmt.Errorf("%T metadata have not been set, cannot call setOwnerReference", owner)
|
||||
}
|
||||
|
||||
controlled.SetOwnerReferences([]metav1.OwnerReference{
|
||||
{
|
||||
APIVersion: ro.GetObjectKind().GroupVersionKind().GroupVersion().String(),
|
||||
Kind: ro.GetObjectKind().GroupVersionKind().Kind,
|
||||
Name: owner.GetName(),
|
||||
UID: owner.GetUID(),
|
||||
BlockOwnerDeletion: ptr.To(true),
|
||||
Controller: ptr.To(true),
|
||||
},
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
158
internal/cnpgi/operator/rbac/ensure.go
Normal file
158
internal/cnpgi/operator/rbac/ensure.go
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Package rbac contains utilities to reconcile RBAC resources
|
||||
// for the barman-cloud plugin.
|
||||
package rbac
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
||||
)
|
||||
|
||||
// EnsureRole ensures the RBAC Role for the given Cluster matches
|
||||
// the desired state derived from the given ObjectStores. On creation,
|
||||
// the Cluster is set as the owner of the Role for garbage collection.
|
||||
//
|
||||
// This function is called from both the Pre hook (gRPC) and the
|
||||
// ObjectStore controller. To handle concurrent modifications
|
||||
// gracefully, AlreadyExists on Create and Conflict on Patch are
|
||||
// retried once rather than returned as errors.
|
||||
func EnsureRole(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
cluster *cnpgv1.Cluster,
|
||||
barmanObjects []barmancloudv1.ObjectStore,
|
||||
) error {
|
||||
newRole := specs.BuildRole(cluster, barmanObjects)
|
||||
|
||||
roleKey := client.ObjectKey{
|
||||
Namespace: newRole.Namespace,
|
||||
Name: newRole.Name,
|
||||
}
|
||||
|
||||
var role rbacv1.Role
|
||||
err := c.Get(ctx, roleKey, &role)
|
||||
|
||||
switch {
|
||||
case apierrs.IsNotFound(err):
|
||||
role, err := createRole(ctx, c, cluster, newRole)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if role == nil {
|
||||
// Created successfully, nothing else to do.
|
||||
return nil
|
||||
}
|
||||
// AlreadyExists: fall through to patch with the re-read role.
|
||||
return patchRoleRules(ctx, c, newRole.Rules, role)
|
||||
|
||||
case err != nil:
|
||||
return err
|
||||
|
||||
default:
|
||||
return patchRoleRules(ctx, c, newRole.Rules, &role)
|
||||
}
|
||||
}
|
||||
|
||||
// createRole attempts to create the Role. If another writer created
|
||||
// it concurrently (AlreadyExists), it re-reads and returns the
|
||||
// existing Role for the caller to patch. On success it returns nil.
|
||||
func createRole(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
cluster *cnpgv1.Cluster,
|
||||
newRole *rbacv1.Role,
|
||||
) (*rbacv1.Role, error) {
|
||||
contextLogger := log.FromContext(ctx)
|
||||
|
||||
if err := controllerutil.SetControllerReference(cluster, newRole, c.Scheme()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextLogger.Info("Creating role",
|
||||
"name", newRole.Name, "namespace", newRole.Namespace)
|
||||
|
||||
createErr := c.Create(ctx, newRole)
|
||||
if createErr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if !apierrs.IsAlreadyExists(createErr) {
|
||||
return nil, createErr
|
||||
}
|
||||
|
||||
contextLogger.Info("Role was created concurrently, checking rules")
|
||||
|
||||
var role rbacv1.Role
|
||||
if err := c.Get(ctx, client.ObjectKeyFromObject(newRole), &role); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &role, nil
|
||||
}
|
||||
|
||||
// patchRoleRules patches the Role's rules if they differ from the
|
||||
// desired state. On Conflict (concurrent modification), it retries
|
||||
// once with a fresh read.
|
||||
func patchRoleRules(
|
||||
ctx context.Context,
|
||||
c client.Client,
|
||||
desiredRules []rbacv1.PolicyRule,
|
||||
role *rbacv1.Role,
|
||||
) error {
|
||||
if equality.Semantic.DeepEqual(desiredRules, role.Rules) {
|
||||
return nil
|
||||
}
|
||||
|
||||
contextLogger := log.FromContext(ctx)
|
||||
contextLogger.Info("Patching role",
|
||||
"name", role.Name, "namespace", role.Namespace, "rules", desiredRules)
|
||||
|
||||
oldRole := role.DeepCopy()
|
||||
role.Rules = desiredRules
|
||||
|
||||
patchErr := c.Patch(ctx, role, client.MergeFrom(oldRole))
|
||||
if patchErr == nil || !apierrs.IsConflict(patchErr) {
|
||||
return patchErr
|
||||
}
|
||||
|
||||
// Conflict: re-read and retry once.
|
||||
contextLogger.Info("Role was modified concurrently, retrying patch")
|
||||
if err := c.Get(ctx, client.ObjectKeyFromObject(role), role); err != nil {
|
||||
return err
|
||||
}
|
||||
if equality.Semantic.DeepEqual(desiredRules, role.Rules) {
|
||||
return nil
|
||||
}
|
||||
|
||||
oldRole = role.DeepCopy()
|
||||
role.Rules = desiredRules
|
||||
|
||||
return c.Patch(ctx, role, client.MergeFrom(oldRole))
|
||||
}
|
||||
175
internal/cnpgi/operator/rbac/ensure_test.go
Normal file
175
internal/cnpgi/operator/rbac/ensure_test.go
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
|
||||
)
|
||||
|
||||
func newScheme() *runtime.Scheme {
|
||||
s := runtime.NewScheme()
|
||||
_ = rbacv1.AddToScheme(s)
|
||||
_ = cnpgv1.AddToScheme(s)
|
||||
_ = barmancloudv1.AddToScheme(s)
|
||||
return s
|
||||
}
|
||||
|
||||
func newCluster(name, namespace string) *cnpgv1.Cluster {
|
||||
return &cnpgv1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func newObjectStore(name, namespace, secretName string) barmancloudv1.ObjectStore {
|
||||
return barmancloudv1.ObjectStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: barmancloudv1.ObjectStoreSpec{
|
||||
Configuration: barmanapi.BarmanObjectStoreConfiguration{
|
||||
DestinationPath: "s3://bucket/path",
|
||||
BarmanCredentials: barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: secretName,
|
||||
},
|
||||
Key: "ACCESS_KEY_ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("EnsureRole", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
cluster *cnpgv1.Cluster
|
||||
objects []barmancloudv1.ObjectStore
|
||||
fakeClient client.Client
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background()
|
||||
cluster = newCluster("test-cluster", "default")
|
||||
objects = []barmancloudv1.ObjectStore{
|
||||
newObjectStore("my-store", "default", "aws-creds"),
|
||||
}
|
||||
})
|
||||
|
||||
Context("when the Role does not exist", func() {
|
||||
BeforeEach(func() {
|
||||
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
||||
})
|
||||
|
||||
It("should create the Role with owner reference", func() {
|
||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var role rbacv1.Role
|
||||
err = fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "test-cluster-barman-cloud",
|
||||
}, &role)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(role.Rules).To(HaveLen(3))
|
||||
|
||||
// Verify owner reference is set to the Cluster
|
||||
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
|
||||
Expect(role.OwnerReferences[0].Kind).To(Equal("Cluster"))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the Role exists with matching rules", func() {
|
||||
BeforeEach(func() {
|
||||
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
||||
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, objects)).To(Succeed())
|
||||
})
|
||||
|
||||
It("should not patch the Role", func() {
|
||||
var before rbacv1.Role
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "test-cluster-barman-cloud",
|
||||
}, &before)).To(Succeed())
|
||||
|
||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var after rbacv1.Role
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "test-cluster-barman-cloud",
|
||||
}, &after)).To(Succeed())
|
||||
|
||||
Expect(after.ResourceVersion).To(Equal(before.ResourceVersion))
|
||||
})
|
||||
})
|
||||
|
||||
Context("when the Role exists with different rules", func() {
|
||||
BeforeEach(func() {
|
||||
fakeClient = fake.NewClientBuilder().WithScheme(newScheme()).Build()
|
||||
oldObjects := []barmancloudv1.ObjectStore{
|
||||
newObjectStore("my-store", "default", "old-secret"),
|
||||
}
|
||||
Expect(rbac.EnsureRole(ctx, fakeClient, cluster, oldObjects)).To(Succeed())
|
||||
})
|
||||
|
||||
It("should patch the Role with new rules and preserve owner reference", func() {
|
||||
err := rbac.EnsureRole(ctx, fakeClient, cluster, objects)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var role rbacv1.Role
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "test-cluster-barman-cloud",
|
||||
}, &role)).To(Succeed())
|
||||
|
||||
secretsRule := role.Rules[2]
|
||||
Expect(secretsRule.ResourceNames).To(ContainElement("aws-creds"))
|
||||
Expect(secretsRule.ResourceNames).NotTo(ContainElement("old-secret"))
|
||||
|
||||
// Owner reference must survive the patch
|
||||
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||
Expect(role.OwnerReferences[0].Name).To(Equal("test-cluster"))
|
||||
})
|
||||
})
|
||||
})
|
||||
32
internal/cnpgi/operator/rbac/suite_test.go
Normal file
32
internal/cnpgi/operator/rbac/suite_test.go
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package rbac_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestRBAC(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "RBAC Suite")
|
||||
}
|
||||
@ -28,12 +28,13 @@ import (
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
"k8s.io/apimachinery/pkg/api/equality"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/specs"
|
||||
)
|
||||
|
||||
@ -113,7 +114,7 @@ func (r ReconcilerImplementation) Pre(
|
||||
barmanObjects = append(barmanObjects, barmanObject)
|
||||
}
|
||||
|
||||
if err := r.ensureRole(ctx, &cluster, barmanObjects); err != nil {
|
||||
if err := rbac.EnsureRole(ctx, r.Client, &cluster, barmanObjects); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -137,57 +138,6 @@ func (r ReconcilerImplementation) Post(
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) ensureRole(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
barmanObjects []barmancloudv1.ObjectStore,
|
||||
) error {
|
||||
contextLogger := log.FromContext(ctx)
|
||||
newRole := specs.BuildRole(cluster, barmanObjects)
|
||||
|
||||
var role rbacv1.Role
|
||||
if err := r.Client.Get(ctx, client.ObjectKey{
|
||||
Namespace: newRole.Namespace,
|
||||
Name: newRole.Name,
|
||||
}, &role); err != nil {
|
||||
if !apierrs.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
|
||||
contextLogger.Info(
|
||||
"Creating role",
|
||||
"name", newRole.Name,
|
||||
"namespace", newRole.Namespace,
|
||||
)
|
||||
|
||||
if err := setOwnerReference(cluster, newRole); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return r.Client.Create(ctx, newRole)
|
||||
}
|
||||
|
||||
if equality.Semantic.DeepEqual(newRole.Rules, role.Rules) {
|
||||
// There's no need to hit the API server again
|
||||
return nil
|
||||
}
|
||||
|
||||
contextLogger.Info(
|
||||
"Patching role",
|
||||
"name", newRole.Name,
|
||||
"namespace", newRole.Namespace,
|
||||
"rules", newRole.Rules,
|
||||
)
|
||||
|
||||
oldRole := role.DeepCopy()
|
||||
|
||||
// Apply to the role the new rules
|
||||
role.Rules = newRole.Rules
|
||||
|
||||
// Push it back to the API server
|
||||
return r.Client.Patch(ctx, &role, client.MergeFrom(oldRole))
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) ensureRoleBinding(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
@ -213,7 +163,7 @@ func (r ReconcilerImplementation) createRoleBinding(
|
||||
cluster *cnpgv1.Cluster,
|
||||
) error {
|
||||
roleBinding := specs.BuildRoleBinding(cluster)
|
||||
if err := setOwnerReference(cluster, roleBinding); err != nil {
|
||||
if err := controllerutil.SetControllerReference(cluster, roleBinding, r.Client.Scheme()); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Client.Create(ctx, roleBinding)
|
||||
|
||||
@ -22,7 +22,6 @@ package restore
|
||||
import (
|
||||
"context"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
@ -33,14 +32,20 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
pluginscheme "github.com/cloudnative-pg/plugin-barman-cloud/internal/scheme"
|
||||
)
|
||||
|
||||
var scheme = runtime.NewScheme()
|
||||
// generateScheme creates a runtime.Scheme with all type definitions
|
||||
// needed by the restore sidecar. CNPG types are registered under a
|
||||
// configurable API group to support custom CNPG-based operators.
|
||||
func generateScheme(ctx context.Context) *runtime.Scheme {
|
||||
result := runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
||||
utilruntime.Must(cnpgv1.AddToScheme(scheme))
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(result))
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(result))
|
||||
pluginscheme.AddCNPGToScheme(ctx, result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Start starts the sidecar informers and CNPG-i server
|
||||
@ -48,6 +53,8 @@ func Start(ctx context.Context) error {
|
||||
setupLog := log.FromContext(ctx)
|
||||
setupLog.Info("Starting barman cloud instance plugin")
|
||||
|
||||
scheme := generateScheme(ctx)
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Client: client.Options{
|
||||
|
||||
@ -23,12 +23,18 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/rbac"
|
||||
)
|
||||
|
||||
// ObjectStoreReconciler reconciles a ObjectStore object.
|
||||
@ -40,33 +46,96 @@ type ObjectStoreReconciler struct {
|
||||
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=create;patch;update;get;list;watch
|
||||
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=create;patch;update;get;list;watch
|
||||
// +kubebuilder:rbac:groups="",resources=secrets,verbs=create;list;get;watch;delete
|
||||
// +kubebuilder:rbac:groups=postgresql.cnpg.io,resources=clusters,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=postgresql.cnpg.io,resources=clusters/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups=postgresql.cnpg.io,resources=backups,verbs=get;list;watch
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the ObjectStore object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
|
||||
func (r *ObjectStoreReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
|
||||
_ = log.FromContext(ctx)
|
||||
// Reconcile ensures that the RBAC Role for each Cluster referencing
|
||||
// this ObjectStore is up to date with the current ObjectStore spec.
|
||||
func (r *ObjectStoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
contextLogger := log.FromContext(ctx).WithValues(
|
||||
"objectStoreName", req.Name,
|
||||
"namespace", req.Namespace,
|
||||
)
|
||||
ctx = log.IntoContext(ctx, contextLogger)
|
||||
|
||||
// TODO(user): your logic here
|
||||
contextLogger.Info("ObjectStore reconciliation start")
|
||||
|
||||
// List all Clusters in the same namespace
|
||||
var clusterList cnpgv1.ClusterList
|
||||
if err := r.List(ctx, &clusterList, client.InNamespace(req.Namespace)); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("while listing clusters: %w", err)
|
||||
}
|
||||
|
||||
// For each Cluster that references this ObjectStore, reconcile the Role
|
||||
for i := range clusterList.Items {
|
||||
cluster := &clusterList.Items[i]
|
||||
|
||||
pluginConfiguration := config.NewFromCluster(cluster)
|
||||
referredObjects := pluginConfiguration.GetReferredBarmanObjectsKey()
|
||||
|
||||
if !referencesObjectStore(referredObjects, req.NamespacedName) {
|
||||
continue
|
||||
}
|
||||
|
||||
contextLogger.Info("Reconciling RBAC for cluster",
|
||||
"clusterName", cluster.Name)
|
||||
|
||||
if err := r.reconcileRBACForCluster(ctx, cluster, referredObjects); err != nil {
|
||||
return ctrl.Result{}, fmt.Errorf("while reconciling RBAC for cluster %s: %w", cluster.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
contextLogger.Info("ObjectStore reconciliation completed")
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// reconcileRBACForCluster ensures the Role for the given Cluster is
|
||||
// up to date with the current ObjectStore specs.
|
||||
func (r *ObjectStoreReconciler) reconcileRBACForCluster(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
referredObjectKeys []client.ObjectKey,
|
||||
) error {
|
||||
contextLogger := log.FromContext(ctx)
|
||||
barmanObjects := make([]barmancloudv1.ObjectStore, 0, len(referredObjectKeys))
|
||||
for _, key := range referredObjectKeys {
|
||||
var barmanObject barmancloudv1.ObjectStore
|
||||
if err := r.Get(ctx, key, &barmanObject); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
contextLogger.Info("ObjectStore not found, skipping",
|
||||
"objectStoreName", key.Name)
|
||||
continue
|
||||
}
|
||||
return fmt.Errorf("while getting ObjectStore %s: %w", key, err)
|
||||
}
|
||||
barmanObjects = append(barmanObjects, barmanObject)
|
||||
}
|
||||
|
||||
return rbac.EnsureRole(ctx, r.Client, cluster, barmanObjects)
|
||||
}
|
||||
|
||||
// referencesObjectStore checks if the given ObjectStore is in the list
|
||||
// of referred barman objects.
|
||||
func referencesObjectStore(
|
||||
referredObjects []client.ObjectKey,
|
||||
objectStore client.ObjectKey,
|
||||
) bool {
|
||||
for _, ref := range referredObjects {
|
||||
if ref.Name == objectStore.Name && ref.Namespace == objectStore.Namespace {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *ObjectStoreReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
err := ctrl.NewControllerManagedBy(mgr).
|
||||
For(&barmancloudv1.ObjectStore{}).
|
||||
For(&barmancloudv1.ObjectStore{}, builder.WithPredicates(predicate.GenerationChangedPredicate{})).
|
||||
Complete(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create controller: %w", err)
|
||||
|
||||
@ -22,70 +22,301 @@ package controller
|
||||
import (
|
||||
"context"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
|
||||
"k8s.io/apimachinery/pkg/api/errors"
|
||||
machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
var _ = Describe("ObjectStore Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
const resourceName = "test-resource"
|
||||
func newFakeScheme() *runtime.Scheme {
|
||||
s := runtime.NewScheme()
|
||||
_ = rbacv1.AddToScheme(s)
|
||||
_ = cnpgv1.AddToScheme(s)
|
||||
_ = barmancloudv1.AddToScheme(s)
|
||||
return s
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
func newTestCluster(name, namespace, objectStoreName string) *cnpgv1.Cluster {
|
||||
return &cnpgv1.Cluster{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: cnpgv1.ClusterSpec{
|
||||
Plugins: []cnpgv1.PluginConfiguration{
|
||||
{
|
||||
Name: metadata.PluginName,
|
||||
Parameters: map[string]string{
|
||||
"barmanObjectName": objectStoreName,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
typeNamespacedName := types.NamespacedName{
|
||||
Name: resourceName,
|
||||
Namespace: "default", // TODO(user):Modify as needed
|
||||
func newTestObjectStore(name, namespace, secretName string) *barmancloudv1.ObjectStore {
|
||||
return &barmancloudv1.ObjectStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: name,
|
||||
Namespace: namespace,
|
||||
},
|
||||
Spec: barmancloudv1.ObjectStoreSpec{
|
||||
Configuration: barmanapi.BarmanObjectStoreConfiguration{
|
||||
DestinationPath: "s3://bucket/path",
|
||||
BarmanCredentials: barmanapi.BarmanCredentials{
|
||||
AWS: &barmanapi.S3Credentials{
|
||||
AccessKeyIDReference: &machineryapi.SecretKeySelector{
|
||||
LocalObjectReference: machineryapi.LocalObjectReference{
|
||||
Name: secretName,
|
||||
},
|
||||
Key: "ACCESS_KEY_ID",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("referencesObjectStore", func() {
|
||||
It("should return true when ObjectStore is in the list", func() {
|
||||
refs := []client.ObjectKey{
|
||||
{Name: "store-a", Namespace: "default"},
|
||||
{Name: "store-b", Namespace: "default"},
|
||||
}
|
||||
objectstore := &barmancloudv1.ObjectStore{}
|
||||
Expect(referencesObjectStore(refs, client.ObjectKey{
|
||||
Name: "store-b", Namespace: "default",
|
||||
})).To(BeTrue())
|
||||
})
|
||||
|
||||
BeforeEach(func() {
|
||||
By("creating the custom resource for the Kind ObjectStore")
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, objectstore)
|
||||
if err != nil && errors.IsNotFound(err) {
|
||||
resource := &barmancloudv1.ObjectStore{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: resourceName,
|
||||
Namespace: "default",
|
||||
},
|
||||
Spec: barmancloudv1.ObjectStoreSpec{
|
||||
Configuration: barmanapi.BarmanObjectStoreConfiguration{DestinationPath: "/tmp"},
|
||||
},
|
||||
// TODO(user): Specify other spec details if needed.
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, resource)).To(Succeed())
|
||||
}
|
||||
})
|
||||
It("should return false when ObjectStore is not in the list", func() {
|
||||
refs := []client.ObjectKey{
|
||||
{Name: "store-a", Namespace: "default"},
|
||||
}
|
||||
Expect(referencesObjectStore(refs, client.ObjectKey{
|
||||
Name: "store-b", Namespace: "default",
|
||||
})).To(BeFalse())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO(user): Cleanup logic after each test, like removing the resource instance.
|
||||
resource := &barmancloudv1.ObjectStore{}
|
||||
err := k8sClient.Get(ctx, typeNamespacedName, resource)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
It("should return false when namespace differs", func() {
|
||||
refs := []client.ObjectKey{
|
||||
{Name: "store-a", Namespace: "ns1"},
|
||||
}
|
||||
Expect(referencesObjectStore(refs, client.ObjectKey{
|
||||
Name: "store-a", Namespace: "ns2",
|
||||
})).To(BeFalse())
|
||||
})
|
||||
|
||||
By("Cleanup the specific resource instance ObjectStore")
|
||||
Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
|
||||
})
|
||||
It("should successfully reconcile the resource", func() {
|
||||
By("Reconciling the created resource")
|
||||
controllerReconciler := &ObjectStoreReconciler{
|
||||
Client: k8sClient,
|
||||
Scheme: k8sClient.Scheme(),
|
||||
It("should return false for empty list", func() {
|
||||
Expect(referencesObjectStore(nil, client.ObjectKey{
|
||||
Name: "store-a", Namespace: "default",
|
||||
})).To(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
var _ = Describe("ObjectStoreReconciler", func() {
|
||||
var (
|
||||
ctx context.Context
|
||||
scheme *runtime.Scheme
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
ctx = context.Background()
|
||||
scheme = newFakeScheme()
|
||||
})
|
||||
|
||||
Describe("Reconcile", func() {
|
||||
It("should create a Role for a Cluster that references the ObjectStore", func() {
|
||||
objectStore := newTestObjectStore("my-store", "default", "aws-creds")
|
||||
cluster := newTestCluster("my-cluster", "default", "my-store")
|
||||
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithObjects(objectStore, cluster).
|
||||
Build()
|
||||
|
||||
reconciler := &ObjectStoreReconciler{
|
||||
Client: fakeClient,
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
_, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: typeNamespacedName,
|
||||
result, err := reconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "my-store",
|
||||
Namespace: "default",
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
// TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
|
||||
// Example: If you expect a certain status condition after reconciliation, verify it here.
|
||||
Expect(result).To(Equal(reconcile.Result{}))
|
||||
|
||||
var role rbacv1.Role
|
||||
err = fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "my-cluster-barman-cloud",
|
||||
}, &role)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(role.Rules).To(HaveLen(3))
|
||||
|
||||
// Verify the secrets rule contains the expected secret
|
||||
secretsRule := role.Rules[2]
|
||||
Expect(secretsRule.ResourceNames).To(ContainElement("aws-creds"))
|
||||
|
||||
// Verify owner reference is set to the Cluster
|
||||
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||
Expect(role.OwnerReferences[0].Name).To(Equal("my-cluster"))
|
||||
Expect(role.OwnerReferences[0].Kind).To(Equal("Cluster"))
|
||||
})
|
||||
|
||||
It("should skip Clusters that don't reference the ObjectStore", func() {
|
||||
objectStore := newTestObjectStore("my-store", "default", "aws-creds")
|
||||
cluster := newTestCluster("my-cluster", "default", "other-store")
|
||||
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithObjects(objectStore, cluster).
|
||||
Build()
|
||||
|
||||
reconciler := &ObjectStoreReconciler{
|
||||
Client: fakeClient,
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
result, err := reconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "my-store",
|
||||
Namespace: "default",
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result).To(Equal(reconcile.Result{}))
|
||||
|
||||
// No Role should have been created
|
||||
var role rbacv1.Role
|
||||
err = fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "my-cluster-barman-cloud",
|
||||
}, &role)
|
||||
Expect(err).To(HaveOccurred())
|
||||
})
|
||||
|
||||
It("should succeed with no Clusters in the namespace", func() {
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
Build()
|
||||
|
||||
reconciler := &ObjectStoreReconciler{
|
||||
Client: fakeClient,
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
result, err := reconciler.Reconcile(ctx, reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: "my-store",
|
||||
Namespace: "default",
|
||||
},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(result).To(Equal(reconcile.Result{}))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("reconcileRBACForCluster", func() {
|
||||
It("should skip deleted ObjectStores and still reconcile the Role", func() {
|
||||
// Cluster references two ObjectStores, but one is deleted
|
||||
cluster := newTestCluster("my-cluster", "default", "store-a")
|
||||
existingStore := newTestObjectStore("store-a", "default", "aws-creds")
|
||||
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithObjects(existingStore).
|
||||
Build()
|
||||
|
||||
reconciler := &ObjectStoreReconciler{
|
||||
Client: fakeClient,
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
// Pass two keys, but "store-b" doesn't exist
|
||||
err := reconciler.reconcileRBACForCluster(ctx, cluster, []client.ObjectKey{
|
||||
{Name: "store-a", Namespace: "default"},
|
||||
{Name: "store-b", Namespace: "default"},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Role should be created with only store-a's secrets
|
||||
var role rbacv1.Role
|
||||
err = fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "my-cluster-barman-cloud",
|
||||
}, &role)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(role.Rules).To(HaveLen(3))
|
||||
|
||||
// ObjectStore rule should only reference store-a
|
||||
objectStoreRule := role.Rules[0]
|
||||
Expect(objectStoreRule.ResourceNames).To(ContainElement("store-a"))
|
||||
Expect(objectStoreRule.ResourceNames).NotTo(ContainElement("store-b"))
|
||||
|
||||
// Verify owner reference is set
|
||||
Expect(role.OwnerReferences).To(HaveLen(1))
|
||||
Expect(role.OwnerReferences[0].Name).To(Equal("my-cluster"))
|
||||
})
|
||||
|
||||
It("should update Role when ObjectStore credentials change", func() {
|
||||
cluster := newTestCluster("my-cluster", "default", "my-store")
|
||||
oldStore := newTestObjectStore("my-store", "default", "old-secret")
|
||||
|
||||
fakeClient := fake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithObjects(oldStore).
|
||||
Build()
|
||||
|
||||
reconciler := &ObjectStoreReconciler{
|
||||
Client: fakeClient,
|
||||
Scheme: scheme,
|
||||
}
|
||||
|
||||
// First reconcile - creates Role with old-secret
|
||||
err := reconciler.reconcileRBACForCluster(ctx, cluster, []client.ObjectKey{
|
||||
{Name: "my-store", Namespace: "default"},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// Update the ObjectStore with new credentials
|
||||
var currentStore barmancloudv1.ObjectStore
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKey{
|
||||
Name: "my-store", Namespace: "default",
|
||||
}, ¤tStore)).To(Succeed())
|
||||
currentStore.Spec.Configuration.BarmanCredentials.AWS.AccessKeyIDReference.LocalObjectReference.Name = "new-secret"
|
||||
Expect(fakeClient.Update(ctx, ¤tStore)).To(Succeed())
|
||||
|
||||
// Second reconcile - should patch Role with new-secret
|
||||
err = reconciler.reconcileRBACForCluster(ctx, cluster, []client.ObjectKey{
|
||||
{Name: "my-store", Namespace: "default"},
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
var role rbacv1.Role
|
||||
Expect(fakeClient.Get(ctx, client.ObjectKey{
|
||||
Namespace: "default",
|
||||
Name: "my-cluster-barman-cloud",
|
||||
}, &role)).To(Succeed())
|
||||
|
||||
secretsRule := role.Rules[2]
|
||||
Expect(secretsRule.ResourceNames).To(ContainElement("new-secret"))
|
||||
Expect(secretsRule.ResourceNames).NotTo(ContainElement("old-secret"))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -20,81 +20,14 @@ SPDX-License-Identifier: Apache-2.0
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
// +kubebuilder:scaffold:imports
|
||||
"k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/rest"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/envtest"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
// These tests use Ginkgo (BDD-style Go testing framework). Refer to
|
||||
// http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
|
||||
|
||||
var (
|
||||
cfg *rest.Config
|
||||
k8sClient client.Client
|
||||
testEnv *envtest.Environment
|
||||
ctx context.Context
|
||||
cancel context.CancelFunc
|
||||
)
|
||||
|
||||
func TestControllers(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
|
||||
RunSpecs(t, "Controller Suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
|
||||
|
||||
ctx, cancel = context.WithCancel(context.TODO())
|
||||
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||
ErrorIfCRDPathMissing: true,
|
||||
|
||||
// The BinaryAssetsDirectory is only required if you want to run the tests directly
|
||||
// without call the makefile target test. If not informed it will look for the
|
||||
// default path defined in controller-runtime which is /usr/local/kubebuilder/.
|
||||
// Note that you must have the required binaries setup under the bin directory to perform
|
||||
// the tests directly. When we run make test it will be setup and used automatically.
|
||||
BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
|
||||
fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
|
||||
}
|
||||
|
||||
var err error
|
||||
// cfg is defined in this file globally.
|
||||
cfg, err = testEnv.Start()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cfg).NotTo(BeNil())
|
||||
|
||||
err = barmancloudv1.AddToScheme(scheme.Scheme)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// +kubebuilder:scaffold:scheme
|
||||
|
||||
k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(k8sClient).NotTo(BeNil())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
By("tearing down the test environment")
|
||||
cancel()
|
||||
err := testEnv.Stop()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
58
internal/scheme/cnpg.go
Normal file
58
internal/scheme/cnpg.go
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
Copyright © contributors to CloudNativePG, established as
|
||||
CloudNativePG a Series of LF Projects, LLC.
|
||||
|
||||
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.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Package scheme provides utilities for building runtime schemes
|
||||
// with support for custom CNPG API groups.
|
||||
package scheme
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
crscheme "sigs.k8s.io/controller-runtime/pkg/scheme"
|
||||
)
|
||||
|
||||
// AddCNPGToScheme registers CNPG types into the given scheme using
|
||||
// the API group configured via CUSTOM_CNPG_GROUP/CUSTOM_CNPG_VERSION
|
||||
// environment variables, defaulting to postgresql.cnpg.io/v1.
|
||||
// This allows the plugin to work with any CNPG-based operator.
|
||||
func AddCNPGToScheme(ctx context.Context, s *runtime.Scheme) {
|
||||
cnpgGroup := viper.GetString("custom-cnpg-group")
|
||||
cnpgVersion := viper.GetString("custom-cnpg-version")
|
||||
if len(cnpgGroup) == 0 {
|
||||
cnpgGroup = cnpgv1.SchemeGroupVersion.Group
|
||||
}
|
||||
if len(cnpgVersion) == 0 {
|
||||
cnpgVersion = cnpgv1.SchemeGroupVersion.Version
|
||||
}
|
||||
|
||||
schemeGroupVersion := schema.GroupVersion{Group: cnpgGroup, Version: cnpgVersion}
|
||||
schemeBuilder := &crscheme.Builder{GroupVersion: schemeGroupVersion}
|
||||
schemeBuilder.Register(&cnpgv1.Cluster{}, &cnpgv1.ClusterList{})
|
||||
schemeBuilder.Register(&cnpgv1.Backup{}, &cnpgv1.BackupList{})
|
||||
schemeBuilder.Register(&cnpgv1.ScheduledBackup{}, &cnpgv1.ScheduledBackupList{})
|
||||
utilruntime.Must(schemeBuilder.AddToScheme(s))
|
||||
|
||||
log.FromContext(ctx).Info("CNPG types registration", "schemeGroupVersion", schemeGroupVersion)
|
||||
}
|
||||
@ -870,6 +870,7 @@ rules:
|
||||
- postgresql.cnpg.io
|
||||
resources:
|
||||
- backups
|
||||
- clusters
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
|
||||
Loading…
Reference in New Issue
Block a user