├── .dockerignore ├── .github └── workflows │ ├── ci.yml │ ├── publish-docs.yml │ ├── release-please.yml │ └── release-publish.yml ├── .gitignore ├── .golangci.yml ├── .release-please-manifest.json ├── .spellcheck.yaml ├── .wordlist.txt ├── CHANGELOG.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── GOVERNANCE.md ├── LICENSE ├── Makefile ├── PROJECT ├── README.md ├── RELEASE-PROCEDURE.md ├── Taskfile.yml ├── api └── v1 │ ├── doc.go │ ├── groupversion_info.go │ ├── objectstore_types.go │ └── zz_generated.deepcopy.go ├── cmd └── manager │ ├── doc.go │ └── main.go ├── commitlint.config.js ├── config ├── crd │ ├── bases │ │ └── barmancloud.cnpg.io_objectstores.yaml │ ├── kustomization.yaml │ └── kustomizeconfig.yaml ├── default │ ├── kustomization.yaml │ ├── manager_metrics_patch.yaml │ └── metrics_service.yaml ├── manager │ ├── kustomization.yaml │ └── manager.yaml ├── network-policy │ ├── allow-metrics-traffic.yaml │ └── kustomization.yaml ├── prometheus │ ├── kustomization.yaml │ └── monitor.yaml ├── rbac │ ├── kustomization.yaml │ ├── leader_election_role.yaml │ ├── leader_election_role_binding.yaml │ ├── metrics_auth_role.yaml │ ├── metrics_auth_role_binding.yaml │ ├── metrics_reader_role.yaml │ ├── objectstore_editor_role.yaml │ ├── objectstore_viewer_role.yaml │ ├── role.yaml │ ├── role_binding.yaml │ └── service_account.yaml └── samples │ └── kustomization.yaml ├── containers ├── Dockerfile.plugin ├── Dockerfile.sidecar ├── sidecar-requirements.in └── sidecar-requirements.txt ├── dagger ├── check-doc-version │ ├── .gitattributes │ ├── .gitignore │ ├── dagger.json │ ├── go.mod │ ├── go.sum │ └── main.go ├── e2e │ ├── .gitattributes │ ├── .gitignore │ ├── dagger.json │ ├── go.mod │ ├── go.sum │ └── main.go └── gotest │ ├── .gitattributes │ ├── .gitignore │ ├── dagger.json │ ├── go.mod │ ├── go.sum │ └── main.go ├── go.mod ├── go.sum ├── hack ├── boilerplate.go.txt ├── build-dev-image.sh ├── crd-gen-refs │ ├── config.yaml │ └── markdown │ │ ├── gv_details.tpl │ │ ├── gv_list.tpl │ │ ├── type.tpl │ │ └── type_members.tpl ├── examples │ ├── backup-example.yaml │ ├── cluster-example-legacy.yaml │ ├── cluster-example.yaml │ ├── cluster-replica-log-shipping.yaml │ ├── cluster-restore-archive.yaml │ ├── cluster-restore.yaml │ └── minio-store.yaml ├── kind-config.yaml └── minio │ ├── minio-client.yaml │ ├── minio-deployment.yaml │ ├── minio-pvc.yaml │ ├── minio-secret.yaml │ └── minio-service.yaml ├── internal ├── cmd │ ├── healthcheck │ │ ├── doc.go │ │ └── main.go │ ├── instance │ │ └── main.go │ ├── operator │ │ └── main.go │ └── restore │ │ └── main.go ├── cnpgi │ ├── common │ │ ├── common.go │ │ ├── doc.go │ │ ├── errors.go │ │ ├── health.go │ │ ├── wal.go │ │ └── wal_import.go │ ├── instance │ │ ├── backup.go │ │ ├── doc.go │ │ ├── identity.go │ │ ├── internal │ │ │ └── client │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── doc.go │ │ │ │ └── suite_test.go │ │ ├── manager.go │ │ ├── retention.go │ │ ├── start.go │ │ └── types.go │ ├── metadata │ │ ├── constants.go │ │ └── doc.go │ ├── operator │ │ ├── config │ │ │ ├── config.go │ │ │ └── doc.go │ │ ├── doc.go │ │ ├── identity.go │ │ ├── lifecycle.go │ │ ├── lifecycle_certificates.go │ │ ├── lifecycle_envs.go │ │ ├── lifecycle_resources.go │ │ ├── lifecycle_test.go │ │ ├── manager.go │ │ ├── ownership.go │ │ ├── reconciler.go │ │ ├── specs │ │ │ ├── role.go │ │ │ ├── secrets.go │ │ │ └── specs.go │ │ ├── start.go │ │ └── suite_test.go │ └── restore │ │ ├── doc.go │ │ ├── identity.go │ │ ├── manager.go │ │ ├── restore.go │ │ └── start.go └── controller │ ├── doc.go │ ├── objectstore_controller.go │ ├── objectstore_controller_test.go │ └── suite_test.go ├── kubernetes ├── certificate-issuer.yaml ├── client-certificate.yaml ├── deployment.yaml ├── kustomization.yaml ├── server-certificate.yaml └── service.yaml ├── logo └── cloudnativepg.png ├── manifest.yaml ├── release-please-config.json ├── renovate.json5 ├── scripts ├── cleanup.sh ├── minio-delete.sh └── run.sh ├── test └── e2e │ ├── e2e_suite_test.go │ ├── internal │ ├── certmanager │ │ ├── certmanager.go │ │ └── doc.go │ ├── client │ │ ├── client.go │ │ └── doc.go │ ├── cloudnativepg │ │ ├── cloudnativepg.go │ │ └── doc.go │ ├── cluster │ │ ├── cluster.go │ │ └── doc.go │ ├── command │ │ ├── command.go │ │ └── doc.go │ ├── deployment │ │ ├── deployment.go │ │ └── doc.go │ ├── e2etestenv │ │ ├── doc.go │ │ └── main.go │ ├── kustomize │ │ ├── doc.go │ │ └── kustomize.go │ ├── namespace │ │ ├── doc.go │ │ └── namespace.go │ ├── objectstore │ │ ├── azurite.go │ │ ├── doc.go │ │ ├── fakegcsserver.go │ │ ├── minio.go │ │ └── objectstore.go │ └── tests │ │ ├── backup │ │ ├── backup_restore.go │ │ ├── doc.go │ │ └── fixtures.go │ │ └── replicacluster │ │ ├── doc.go │ │ ├── fixtures.go │ │ └── replica_cluster.go │ └── kustomize │ ├── config │ └── kubernetes └── web ├── .gitignore ├── README.md ├── docs ├── compression.md ├── concepts.md ├── images.md ├── installation.mdx ├── intro.md ├── migration.md ├── misc.md ├── object_stores.md ├── parameters.md ├── plugin-barman-cloud.v1.md ├── retention.md └── usage.md ├── docusaurus.config.ts ├── package.json ├── sidebars.ts ├── src ├── components │ ├── HomepageFeatures │ │ ├── feature.tsx │ │ ├── index.tsx │ │ └── styles.module.css │ └── Installation │ │ └── index.tsx ├── css │ └── custom.css ├── hooks │ └── versions.ts └── pages │ ├── index.module.css │ └── index.tsx ├── static ├── .nojekyll └── img │ ├── cloudnativepg-landscape-white.png │ ├── cloudnativepg-social-card.png │ ├── cloudnativepg.png │ ├── favicon.ico │ ├── github-mark-white.svg │ ├── github-mark.svg │ ├── logo.svg │ ├── undraw_going-up_g8av.svg │ ├── undraw_maintenance_rjtm.svg │ └── undraw_season-change_ohe6.svg ├── tsconfig.json ├── versioned_docs ├── version-0.3.0 │ └── welcome.md ├── version-0.4.0 │ ├── compression.md │ ├── concepts.md │ ├── images.md │ ├── installation.md │ ├── intro.md │ ├── migration.md │ ├── misc.md │ ├── object_stores.md │ ├── parameters.md │ ├── plugin-barman-cloud.v1.md │ ├── retention.md │ └── usage.md ├── version-0.4.1 │ ├── compression.md │ ├── concepts.md │ ├── images.md │ ├── installation.mdx │ ├── intro.md │ ├── migration.md │ ├── misc.md │ ├── object_stores.md │ ├── parameters.md │ ├── plugin-barman-cloud.v1.md │ ├── retention.md │ └── usage.md └── version-0.5.0 │ ├── compression.md │ ├── concepts.md │ ├── images.md │ ├── installation.mdx │ ├── intro.md │ ├── migration.md │ ├── misc.md │ ├── object_stores.md │ ├── parameters.md │ ├── plugin-barman-cloud.v1.md │ ├── retention.md │ └── usage.md ├── versioned_sidebars ├── version-0.3.0-sidebars.json ├── version-0.4.0-sidebars.json ├── version-0.4.1-sidebars.json └── version-0.5.0-sidebars.json ├── versions.json └── yarn.lock /.dockerignore: -------------------------------------------------------------------------------- 1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file 2 | # Ignore build and test binaries. 3 | bin/ 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | pull_request: 5 | workflow_dispatch: 6 | 7 | permissions: read-all 8 | 9 | jobs: 10 | ci: 11 | runs-on: ubuntu-latest 12 | permissions: 13 | packages: write 14 | contents: write 15 | steps: 16 | - name: Cleanup Disk 17 | uses: jlumbroso/free-disk-space@v1.3.1 18 | with: 19 | android: true 20 | dotnet: true 21 | haskell: true 22 | tool-cache: true 23 | large-packages: false 24 | swap-storage: false 25 | - name: Cleanup docker cache 26 | run: | 27 | echo "-------------Disk info before cleanup----------------" 28 | df -h 29 | echo "-----------------------------------------------------" 30 | docker system prune -a -f 31 | echo "-------------Disk info after cleanup----------------" 32 | df -h 33 | echo "-----------------------------------------------------" 34 | - name: Checkout 35 | uses: actions/checkout@v4 36 | # We need the full history for the commitlint task 37 | with: 38 | fetch-depth: 0 39 | ref: ${{ github.event.pull_request.head.sha }} 40 | - name: Install QEMU static binaries 41 | uses: docker/setup-qemu-action@v3 42 | - name: Install Task 43 | uses: arduino/setup-task@v2 44 | - name: Install Dagger 45 | env: 46 | # renovate: datasource=github-tags depName=dagger/dagger versioning=semver 47 | DAGGER_VERSION: 0.18.5 48 | run: | 49 | curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh 50 | - name: Run CI task 51 | run: | 52 | task ci 53 | - name: Write manifest 54 | run: | 55 | task manifest 56 | - name: Publish images 57 | if: | 58 | github.event_name == 'workflow_dispatch' || 59 | github.event.pull_request.head.repo.full_name == github.repository 60 | env: 61 | REGISTRY_USER: ${{ github.actor }} 62 | REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 63 | run: | 64 | task publish 65 | - name: Attach manifest to workflow run 66 | uses: actions/upload-artifact@v4 67 | with: 68 | name: manifest.yaml 69 | path: ./manifest.yaml 70 | -------------------------------------------------------------------------------- /.github/workflows/publish-docs.yml: -------------------------------------------------------------------------------- 1 | # This workflow builds and deploys the Docusaurus documentation to GitHub Pages 2 | # when changes are pushed to the main branch. 3 | name: Deploy Docusaurus to GitHub Pages 4 | 5 | on: 6 | workflow_dispatch: 7 | push: 8 | branches: 9 | - main 10 | paths: 11 | - 'web/**' 12 | 13 | # Prevent parallel deployments when multiple commits are pushed to main 14 | # in a short time. 15 | concurrency: 16 | group: "pages" 17 | cancel-in-progress: false 18 | 19 | permissions: read-all 20 | 21 | jobs: 22 | build: 23 | runs-on: ubuntu-latest 24 | permissions: 25 | contents: read 26 | steps: 27 | - name: Checkout 28 | uses: actions/checkout@v4 29 | 30 | - name: Setup Node 31 | uses: actions/setup-node@v4 32 | with: 33 | # Use the latest LTS version of Node.js already installed on the runner. 34 | node-version: latest 35 | check-latest: 'false' 36 | cache: yarn 37 | cache-dependency-path: web/yarn.lock 38 | 39 | - name: Setup Pages 40 | uses: actions/configure-pages@v5 41 | 42 | - name: Install dependencies 43 | working-directory: web 44 | run: yarn install --frozen-lockfile --non-interactive 45 | 46 | - name: Build website 47 | working-directory: web 48 | run: yarn build 49 | 50 | - name: Upload artifact 51 | uses: actions/upload-pages-artifact@v3 52 | with: 53 | path: web/build 54 | 55 | deploy: 56 | permissions: 57 | pages: write 58 | id-token: write 59 | environment: 60 | name: github-pages 61 | url: ${{ steps.deployment.outputs.page_url }} 62 | needs: build 63 | runs-on: ubuntu-latest 64 | steps: 65 | - name: Deploy to GitHub Pages 66 | id: deployment 67 | uses: actions/deploy-pages@v4 68 | -------------------------------------------------------------------------------- /.github/workflows/release-please.yml: -------------------------------------------------------------------------------- 1 | name: release-please 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: read-all 9 | 10 | jobs: 11 | release-please: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | pull-requests: write 16 | packages: write 17 | steps: 18 | # TODO: googleapis/release-please-action cannot sign commits yet. 19 | # We'll use the cli until there's a fix for 20 | # https://github.com/googleapis/release-please/issues/2280. 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | - name: Install node 24 | uses: actions/setup-node@v4 25 | with: 26 | node-version: 20 27 | - name: Install QEMU static binaries 28 | uses: docker/setup-qemu-action@v3 29 | - name: Install Task 30 | uses: arduino/setup-task@v2 31 | - name: Install Dagger 32 | env: 33 | # renovate: datasource=github-tags depName=dagger/dagger versioning=semver 34 | DAGGER_VERSION: 0.18.5 35 | run: | 36 | curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh 37 | - name: Create image and manifest 38 | env: 39 | REGISTRY_USER: ${{ github.actor }} 40 | REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 41 | run: | 42 | task publish 43 | task manifest 44 | - name: Attach manifest to workflow run 45 | uses: actions/upload-artifact@v4 46 | with: 47 | name: manifest.yaml 48 | path: ./manifest.yaml 49 | # TODO: remove bump-minor-pre-major when in production. It prevents 50 | # release-please from bumping the major version on breaking changes. 51 | # We use a GitHub token with write permissions to create the release, 52 | # otherwise we won't be able to trigger a new run when pushing on main. 53 | - name: Run release-please 54 | run: | 55 | npx release-please release-pr \ 56 | --token="${{ secrets.REPO_PAT }}" \ 57 | --repo-url="${{ github.repository }}" 58 | npx release-please github-release \ 59 | --token="${{ secrets.REPO_PAT }}" \ 60 | --repo-url="${{ github.repository }}" 61 | -------------------------------------------------------------------------------- /.github/workflows/release-publish.yml: -------------------------------------------------------------------------------- 1 | name: Release Publish Artifacts 2 | on: 3 | release: 4 | types: [published] 5 | 6 | permissions: read-all 7 | 8 | jobs: 9 | release-publish-artifacts: 10 | runs-on: ubuntu-latest 11 | permissions: 12 | packages: write 13 | contents: write 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | - name: Install QEMU static binaries 18 | uses: docker/setup-qemu-action@v3 19 | - name: Install Task 20 | uses: arduino/setup-task@v2 21 | - name: Install Dagger 22 | env: 23 | # renovate: datasource=github-tags depName=dagger/dagger versioning=semver 24 | DAGGER_VERSION: 0.18.5 25 | run: | 26 | curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh 27 | - name: Create image and manifest 28 | env: 29 | REGISTRY_USER: ${{ github.actor }} 30 | REGISTRY_PASSWORD: ${{ secrets.GITHUB_TOKEN }} 31 | run: | 32 | task publish 33 | task manifest 34 | - name: Attach manifest to release 35 | env: 36 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 37 | run: | 38 | task upload-manifest-to-release 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | bin/* 8 | Dockerfile.cross 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # Go workspace file 17 | go.work 18 | 19 | # Kubernetes Generated files - skip generated files, except for vendored files 20 | !vendor/**/zz_generated.* 21 | 22 | # editor and IDE paraphernalia 23 | .idea 24 | .vscode 25 | *.swp 26 | *.swo 27 | *~ 28 | 29 | # Dependency directories (remove the comment below to include it) 30 | vendor/ 31 | 32 | # Go workspace file 33 | go.work 34 | 35 | # Taskfile cache 36 | .task 37 | 38 | # E2e test artifacts 39 | test/e2e/bin 40 | test/e2e/kustomization.yaml 41 | 42 | # Test registry certificates 43 | certs/ 44 | # Utility dir for manifest build 45 | manifest-build/ 46 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | linters: 3 | default: none 4 | enable: 5 | - asciicheck 6 | - bodyclose 7 | - copyloopvar 8 | - dogsled 9 | - dupl 10 | - durationcheck 11 | - errcheck 12 | - ginkgolinter 13 | - gocognit 14 | - goconst 15 | - gocritic 16 | - gocyclo 17 | - goheader 18 | - gomoddirectives 19 | - gomodguard 20 | - goprintffuncname 21 | - gosec 22 | - govet 23 | - importas 24 | - ineffassign 25 | - lll 26 | - makezero 27 | - misspell 28 | - nakedret 29 | - nestif 30 | - prealloc 31 | - predeclared 32 | - revive 33 | - rowserrcheck 34 | - sqlclosecheck 35 | - staticcheck 36 | - thelper 37 | - tparallel 38 | - unconvert 39 | - unparam 40 | - unused 41 | - wastedassign 42 | - whitespace 43 | settings: 44 | lll: 45 | line-length: 120 46 | exclusions: 47 | generated: lax 48 | rules: 49 | - linters: 50 | - revive 51 | text: should not use dot imports 52 | source: ginkgo|gomega 53 | - linters: 54 | - goconst 55 | path: _test\.go 56 | - linters: 57 | - lll 58 | source: //\s*\+ 59 | - linters: 60 | - staticcheck 61 | path: api/ 62 | text: 'ST1016:' 63 | paths: 64 | - zz_generated.* 65 | - internal/operator/controller/suite_test.go 66 | - test 67 | - third_party$ 68 | - builtin$ 69 | - examples$ 70 | formatters: 71 | enable: 72 | - gci 73 | - gofmt 74 | - gofumpt 75 | - goimports 76 | settings: 77 | gci: 78 | sections: 79 | - standard 80 | - default 81 | - prefix(github.com/cloudnative-pg/plugin-barman-cloud) 82 | - blank 83 | - dot 84 | exclusions: 85 | generated: lax 86 | paths: 87 | - zz_generated.* 88 | - internal/operator/controller/suite_test.go 89 | - test 90 | - third_party$ 91 | - builtin$ 92 | - examples$ 93 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "0.5.0" 3 | } 4 | -------------------------------------------------------------------------------- /.spellcheck.yaml: -------------------------------------------------------------------------------- 1 | matrix: 2 | - name: Markdown 3 | sources: 4 | # Ignore the CHANGELOG.md file, it is autogenerated 5 | - ./*.md|!./CHANGELOG.md 6 | - ./web/docs/**/*.md 7 | dictionary: 8 | wordlists: 9 | - .wordlist.txt 10 | aspell: 11 | lang: en 12 | d: en_US 13 | pipeline: 14 | - pyspelling.filters.markdown: 15 | - pyspelling.filters.html: 16 | ignores: 17 | - ':matches(code, pre)' 18 | - 'code' 19 | - 'pre' 20 | -------------------------------------------------------------------------------- /.wordlist.txt: -------------------------------------------------------------------------------- 1 | Akamai 2 | Azurite 3 | BarmanObjectStore 4 | BarmanObjectStoreConfiguration 5 | BarmanObjectStores 6 | CNCF 7 | CRD 8 | CloudNativePG 9 | DataBackupConfiguration 10 | DigitalOcean 11 | Docusaurus 12 | EDB 13 | EKS 14 | EnvVar 15 | GCP 16 | GKE 17 | Gi 18 | IAM 19 | IRSA 20 | IfNotPresent 21 | InstanceSidecarConfiguration 22 | JSON 23 | Lifecycle 24 | Linode 25 | MinIO 26 | ObjectMeta 27 | ObjectStore 28 | ObjectStoreSpec 29 | ObjectStoreStatus 30 | PITR 31 | PoR 32 | PostgreSQL 33 | Postgres 34 | README 35 | RPO 36 | RTO 37 | RecoveryWindow 38 | ResourceRequirements 39 | RetentionPolicy 40 | SAS 41 | SFO 42 | SPDX 43 | SPDX 44 | ServerRecoveryWindow 45 | Slonik 46 | TLS 47 | TODO 48 | Uncomment 49 | WAL 50 | WALBackupConfiguration 51 | WALs 52 | api 53 | apiVersion 54 | apiextensions 55 | args 56 | auth 57 | aws 58 | backend 59 | backends 60 | barmanObjectName 61 | barmancloud 62 | bzip 63 | cd 64 | cloudnative 65 | clusterrole 66 | clusterrolebinding 67 | cmctl 68 | cnpg 69 | codebase 70 | containerPort 71 | cpu 72 | creds 73 | csi 74 | customresourcedefinition 75 | deps 76 | devel 77 | env 78 | externalClusters 79 | gcs 80 | gf 81 | github 82 | gzip 83 | hostpath 84 | http 85 | https 86 | imagePullPolicy 87 | io 88 | isWALArchiver 89 | kb 90 | kubectl 91 | kubernetes 92 | lifecycle 93 | localhost 94 | md 95 | minio 96 | namespace 97 | namespaces 98 | objectstore 99 | objectstores 100 | pluginConfiguration 101 | postgresql 102 | primaryUpdateStrategy 103 | rbac 104 | rc 105 | recoverability 106 | repos 107 | retentionCheckInterval 108 | retentionPolicy 109 | rolebinding 110 | rollout 111 | sc 112 | secretKeyRef 113 | selfsigned 114 | serverName 115 | serviceaccount 116 | sig 117 | storageClass 118 | subcommand 119 | tfddg 120 | tgz 121 | valueFrom 122 | xvzf 123 | yaml 124 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # The CODEOWNERS file is used to define individuals or teams that are 2 | # responsible for code in a repository. For details, please refer to 3 | # https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/about-code-owners 4 | 5 | * @cloudnative-pg/maintainers 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | The Code of Conduct for the CloudNativePG Community can be found in the 4 | [governance repository](https://github.com/cloudnative-pg/governance/blob/main/CODE_OF_CONDUCT.md). 5 | -------------------------------------------------------------------------------- /GOVERNANCE.md: -------------------------------------------------------------------------------- 1 | # CloudNativePG Governance 2 | 3 | Explore the governance policies for the CloudNativePG Community in the 4 | [dedicated repository](https://github.com/cloudnative-pg/governance/blob/main/GOVERNANCE.md). 5 | 6 | -------------------------------------------------------------------------------- /PROJECT: -------------------------------------------------------------------------------- 1 | # Code generated by tool. DO NOT EDIT. 2 | # This file is used to track the info used to scaffold your project 3 | # and allow the plugins properly work. 4 | # More info: https://book.kubebuilder.io/reference/project-config.html 5 | domain: cnpg.io 6 | layout: 7 | - go.kubebuilder.io/v4 8 | projectName: plugin-barman-cloud 9 | repo: github.com/cloudnative-pg/plugin-barman-cloud 10 | resources: 11 | - api: 12 | crdVersion: v1 13 | namespaced: true 14 | controller: true 15 | domain: cnpg.io 16 | group: barmancloud 17 | kind: ObjectStore 18 | path: github.com/cloudnative-pg/plugin-barman-cloud/api/v1 19 | version: v1 20 | version: "3" 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![CloudNativePG](./logo/cloudnativepg.png)](https://cloudnative-pg.io/) 2 | 3 | # Barman Cloud CNPG-I plugin for CloudNativePG 4 | 5 | The documentation for the Barman Cloud Plugin for CloudNativePG is available at 6 | [https://cloudnative-pg.io/plugin-barman-cloud](https://cloudnative-pg.github.io/plugin-barman-cloud). 7 | 8 | --- 9 | 10 | The Barman Cloud CNPG-I plugin is a component of the 11 | [CloudNativePG project](https://github.com/cloudnative-pg) and adheres to the 12 | same community-driven [governance](GOVERNANCE.md) model under the 13 | [CNCF](https://cncf.io). 14 | 15 |

16 | 17 | 18 | 19 | CNCF logo 20 | 21 |

22 | 23 | --- 24 | 25 |

26 | CloudNativePG was originally built and sponsored by EDB. 27 |

28 | 29 |

30 | 31 | 32 | 33 | EDB logo 34 | 35 |

36 | 37 | --- 38 | 39 |

40 | Postgres, PostgreSQL, and the Slonik Logo 41 | are trademarks or registered trademarks of the PostgreSQL Community Association 42 | of Canada, and used with their permission. 43 |

44 | 45 | --- 46 | -------------------------------------------------------------------------------- /RELEASE-PROCEDURE.md: -------------------------------------------------------------------------------- 1 | # Release Procedure 2 | 3 | The Barman Cloud Plugin follows [Semantic Versioning](https://semver.org/) and 4 | uses [Conventional Commits](https://www.conventionalcommits.org/) to structure 5 | commit messages. 6 | It leverages the [Release Please](https://github.com/googleapis/release-please) 7 | tool to automate the release process. 8 | 9 | The only requirement is that each release must include versioned documentation. 10 | For more details, please refer to [web/README.md](web/README.md). 11 | -------------------------------------------------------------------------------- /api/v1/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright The CloudNativePG Contributors 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package v1 contains API Schema definitions for the barmancloud v1 API group 18 | // +kubebuilder:object:generate=true 19 | // +groupName=barmancloud.cnpg.io 20 | package v1 21 | -------------------------------------------------------------------------------- /api/v1/groupversion_info.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package v1 18 | 19 | import ( 20 | "k8s.io/apimachinery/pkg/runtime/schema" 21 | "sigs.k8s.io/controller-runtime/pkg/scheme" 22 | ) 23 | 24 | var ( 25 | // GroupVersion is group version used to register these objects. 26 | GroupVersion = schema.GroupVersion{Group: "barmancloud.cnpg.io", Version: "v1"} 27 | 28 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme. 29 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} 30 | 31 | // AddToScheme adds the types in this group-version to the given scheme. 32 | AddToScheme = SchemeBuilder.AddToScheme 33 | ) 34 | -------------------------------------------------------------------------------- /cmd/manager/doc.go: -------------------------------------------------------------------------------- 1 | // Package main is the entrypoint for the plugin 2 | package main 3 | -------------------------------------------------------------------------------- /cmd/manager/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "os" 8 | 9 | "github.com/cloudnative-pg/machinery/pkg/log" 10 | "github.com/spf13/cobra" 11 | ctrl "sigs.k8s.io/controller-runtime" 12 | 13 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/healthcheck" 14 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/instance" 15 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/operator" 16 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/restore" 17 | ) 18 | 19 | func main() { 20 | cobra.EnableTraverseRunHooks = true 21 | 22 | logFlags := &log.Flags{} 23 | rootCmd := &cobra.Command{ 24 | Use: "manager [cmd]", 25 | PersistentPreRun: func(cmd *cobra.Command, _ []string) { 26 | logFlags.ConfigureLogging() 27 | cmd.SetContext(log.IntoContext(cmd.Context(), log.GetLogger())) 28 | }, 29 | } 30 | 31 | logFlags.AddFlags(rootCmd.PersistentFlags()) 32 | 33 | rootCmd.AddCommand(instance.NewCmd()) 34 | rootCmd.AddCommand(operator.NewCmd()) 35 | rootCmd.AddCommand(restore.NewCmd()) 36 | rootCmd.AddCommand(healthcheck.NewCmd()) 37 | 38 | if err := rootCmd.ExecuteContext(ctrl.SetupSignalHandler()); err != nil { 39 | if !errors.Is(err, context.Canceled) { 40 | fmt.Println(err) 41 | os.Exit(1) 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | const Configuration= { 2 | extends: ['@commitlint/config-conventional'], 3 | formatter: '@commitlint/format', 4 | rules: { 5 | 'body-empty': [1, 'never'], 6 | 'body-case': [2, 'always', 'sentence-case'], 7 | 'body-max-line-length': [1, 'always', 100], 8 | 'references-empty': [1, 'never'], 9 | 'signed-off-by': [2, 'always', 'Signed-off-by:'], 10 | }, 11 | }; 12 | 13 | module.exports = Configuration; 14 | -------------------------------------------------------------------------------- /config/crd/kustomization.yaml: -------------------------------------------------------------------------------- 1 | # This kustomization.yaml is not intended to be run by itself, 2 | # since it depends on service name and namespace that are out of this kustomize package. 3 | # It should be run by config/default 4 | resources: 5 | - bases/barmancloud.cnpg.io_objectstores.yaml 6 | # +kubebuilder:scaffold:crdkustomizeresource 7 | 8 | patches: 9 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix. 10 | # patches here are for enabling the conversion webhook for each CRD 11 | # +kubebuilder:scaffold:crdkustomizewebhookpatch 12 | 13 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix. 14 | # patches here are for enabling the CA injection for each CRD 15 | #- path: patches/cainjection_in_objectstores.yaml 16 | # +kubebuilder:scaffold:crdkustomizecainjectionpatch 17 | 18 | # [WEBHOOK] To enable webhook, uncomment the following section 19 | # the following config is for teaching kustomize how to do kustomization for CRDs. 20 | 21 | #configurations: 22 | #- kustomizeconfig.yaml 23 | -------------------------------------------------------------------------------- /config/crd/kustomizeconfig.yaml: -------------------------------------------------------------------------------- 1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD 2 | nameReference: 3 | - kind: Service 4 | version: v1 5 | fieldSpecs: 6 | - kind: CustomResourceDefinition 7 | version: v1 8 | group: apiextensions.k8s.io 9 | path: spec/conversion/webhook/clientConfig/service/name 10 | 11 | namespace: 12 | - kind: CustomResourceDefinition 13 | version: v1 14 | group: apiextensions.k8s.io 15 | path: spec/conversion/webhook/clientConfig/service/namespace 16 | create: false 17 | 18 | varReference: 19 | - path: metadata/annotations 20 | -------------------------------------------------------------------------------- /config/default/manager_metrics_patch.yaml: -------------------------------------------------------------------------------- 1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS 2 | - op: add 3 | path: /spec/template/spec/containers/0/args/0 4 | value: --metrics-bind-address=:8443 5 | -------------------------------------------------------------------------------- /config/default/metrics_service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | control-plane: controller-manager 6 | app.kubernetes.io/name: plugin-barman-cloud 7 | app.kubernetes.io/managed-by: kustomize 8 | name: controller-manager-metrics-service 9 | namespace: system 10 | spec: 11 | ports: 12 | - name: https 13 | port: 8443 14 | protocol: TCP 15 | targetPort: 8443 16 | selector: 17 | control-plane: controller-manager 18 | -------------------------------------------------------------------------------- /config/manager/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - manager.yaml 3 | -------------------------------------------------------------------------------- /config/network-policy/allow-metrics-traffic.yaml: -------------------------------------------------------------------------------- 1 | # This NetworkPolicy allows ingress traffic 2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those 3 | # namespaces are able to gathering data from the metrics endpoint. 4 | apiVersion: networking.k8s.io/v1 5 | kind: NetworkPolicy 6 | metadata: 7 | labels: 8 | app.kubernetes.io/name: plugin-barman-cloud 9 | app.kubernetes.io/managed-by: kustomize 10 | name: allow-metrics-traffic 11 | namespace: system 12 | spec: 13 | podSelector: 14 | matchLabels: 15 | control-plane: controller-manager 16 | policyTypes: 17 | - Ingress 18 | ingress: 19 | # This allows ingress traffic from any namespace with the label metrics: enabled 20 | - from: 21 | - namespaceSelector: 22 | matchLabels: 23 | metrics: enabled # Only from namespaces with this label 24 | ports: 25 | - port: 8443 26 | protocol: TCP 27 | -------------------------------------------------------------------------------- /config/network-policy/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - allow-metrics-traffic.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | - monitor.yaml 3 | -------------------------------------------------------------------------------- /config/prometheus/monitor.yaml: -------------------------------------------------------------------------------- 1 | # Prometheus Monitor Service (Metrics) 2 | apiVersion: monitoring.coreos.com/v1 3 | kind: ServiceMonitor 4 | metadata: 5 | labels: 6 | control-plane: controller-manager 7 | app.kubernetes.io/name: plugin-barman-cloud 8 | app.kubernetes.io/managed-by: kustomize 9 | name: controller-manager-metrics-monitor 10 | namespace: system 11 | spec: 12 | endpoints: 13 | - path: /metrics 14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics 15 | scheme: https 16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token 17 | tlsConfig: 18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables 19 | # certificate verification. This poses a significant security risk by making the system vulnerable to 20 | # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between 21 | # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data, 22 | # compromising the integrity and confidentiality of the information. 23 | # Please use the following options for secure configurations: 24 | # caFile: /etc/metrics-certs/ca.crt 25 | # certFile: /etc/metrics-certs/tls.crt 26 | # keyFile: /etc/metrics-certs/tls.key 27 | insecureSkipVerify: true 28 | selector: 29 | matchLabels: 30 | control-plane: controller-manager 31 | -------------------------------------------------------------------------------- /config/rbac/kustomization.yaml: -------------------------------------------------------------------------------- 1 | resources: 2 | # All RBAC will be applied under this service account in 3 | # the deployment namespace. You may comment out this resource 4 | # if your manager will use a service account that exists at 5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding 6 | # subjects if changing service account names. 7 | - service_account.yaml 8 | - role.yaml 9 | - role_binding.yaml 10 | - leader_election_role.yaml 11 | - leader_election_role_binding.yaml 12 | # The following RBAC configurations are used to protect 13 | # the metrics endpoint with authn/authz. These configurations 14 | # ensure that only authorized users and service accounts 15 | # can access the metrics endpoint. Comment the following 16 | # permissions if you want to disable this protection. 17 | # More info: https://book.kubebuilder.io/reference/metrics.html 18 | - metrics_auth_role.yaml 19 | - metrics_auth_role_binding.yaml 20 | - metrics_reader_role.yaml 21 | # For each CRD, "Editor" and "Viewer" roles are scaffolded by 22 | # default, aiding admins in cluster management. Those roles are 23 | # not used by the Project itself. You can comment the following lines 24 | # if you do not want those helpers be installed with your Project. 25 | - objectstore_editor_role.yaml 26 | - objectstore_viewer_role.yaml 27 | 28 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions to do leader election. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: Role 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: plugin-barman-cloud 7 | app.kubernetes.io/managed-by: kustomize 8 | name: leader-election-role 9 | rules: 10 | - apiGroups: 11 | - "" 12 | resources: 13 | - configmaps 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - create 19 | - update 20 | - patch 21 | - delete 22 | - apiGroups: 23 | - coordination.k8s.io 24 | resources: 25 | - leases 26 | verbs: 27 | - get 28 | - list 29 | - watch 30 | - create 31 | - update 32 | - patch 33 | - delete 34 | - apiGroups: 35 | - "" 36 | resources: 37 | - events 38 | verbs: 39 | - create 40 | - patch 41 | -------------------------------------------------------------------------------- /config/rbac/leader_election_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: RoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: plugin-barman-cloud 6 | app.kubernetes.io/managed-by: kustomize 7 | name: leader-election-rolebinding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: Role 11 | name: leader-election-role 12 | subjects: 13 | - kind: ServiceAccount 14 | name: plugin-barman-cloud 15 | namespace: cnpg-system 16 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-auth-role 5 | rules: 6 | - apiGroups: 7 | - authentication.k8s.io 8 | resources: 9 | - tokenreviews 10 | verbs: 11 | - create 12 | - apiGroups: 13 | - authorization.k8s.io 14 | resources: 15 | - subjectaccessreviews 16 | verbs: 17 | - create 18 | -------------------------------------------------------------------------------- /config/rbac/metrics_auth_role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | name: metrics-auth-rolebinding 5 | roleRef: 6 | apiGroup: rbac.authorization.k8s.io 7 | kind: ClusterRole 8 | name: metrics-auth-role 9 | subjects: 10 | - kind: ServiceAccount 11 | name: plugin-barman-cloud 12 | namespace: cnpg-system 13 | -------------------------------------------------------------------------------- /config/rbac/metrics_reader_role.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRole 3 | metadata: 4 | name: metrics-reader 5 | rules: 6 | - nonResourceURLs: 7 | - "/metrics" 8 | verbs: 9 | - get 10 | -------------------------------------------------------------------------------- /config/rbac/objectstore_editor_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to edit objectstores. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: plugin-barman-cloud 7 | app.kubernetes.io/managed-by: kustomize 8 | name: objectstore-editor-role 9 | rules: 10 | - apiGroups: 11 | - barmancloud.cnpg.io 12 | resources: 13 | - objectstores 14 | verbs: 15 | - create 16 | - delete 17 | - get 18 | - list 19 | - patch 20 | - update 21 | - watch 22 | - apiGroups: 23 | - barmancloud.cnpg.io 24 | resources: 25 | - objectstores/status 26 | verbs: 27 | - get 28 | -------------------------------------------------------------------------------- /config/rbac/objectstore_viewer_role.yaml: -------------------------------------------------------------------------------- 1 | # permissions for end users to view objectstores. 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | labels: 6 | app.kubernetes.io/name: plugin-barman-cloud 7 | app.kubernetes.io/managed-by: kustomize 8 | name: objectstore-viewer-role 9 | rules: 10 | - apiGroups: 11 | - barmancloud.cnpg.io 12 | resources: 13 | - objectstores 14 | verbs: 15 | - get 16 | - list 17 | - watch 18 | - apiGroups: 19 | - barmancloud.cnpg.io 20 | resources: 21 | - objectstores/status 22 | verbs: 23 | - get 24 | -------------------------------------------------------------------------------- /config/rbac/role.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: rbac.authorization.k8s.io/v1 3 | kind: ClusterRole 4 | metadata: 5 | name: plugin-barman-cloud 6 | rules: 7 | - apiGroups: 8 | - "" 9 | resources: 10 | - secrets 11 | verbs: 12 | - create 13 | - delete 14 | - get 15 | - list 16 | - watch 17 | - apiGroups: 18 | - barmancloud.cnpg.io 19 | resources: 20 | - objectstores 21 | verbs: 22 | - create 23 | - delete 24 | - get 25 | - list 26 | - patch 27 | - update 28 | - watch 29 | - apiGroups: 30 | - barmancloud.cnpg.io 31 | resources: 32 | - objectstores/finalizers 33 | verbs: 34 | - update 35 | - apiGroups: 36 | - barmancloud.cnpg.io 37 | resources: 38 | - objectstores/status 39 | verbs: 40 | - get 41 | - patch 42 | - update 43 | - apiGroups: 44 | - postgresql.cnpg.io 45 | resources: 46 | - backups 47 | verbs: 48 | - get 49 | - list 50 | - watch 51 | - apiGroups: 52 | - rbac.authorization.k8s.io 53 | resources: 54 | - rolebindings 55 | - roles 56 | verbs: 57 | - create 58 | - get 59 | - list 60 | - patch 61 | - update 62 | - watch 63 | -------------------------------------------------------------------------------- /config/rbac/role_binding.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: rbac.authorization.k8s.io/v1 2 | kind: ClusterRoleBinding 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: plugin-barman-cloud 6 | app.kubernetes.io/managed-by: kustomize 7 | name: plugin-barman-cloud-binding 8 | roleRef: 9 | apiGroup: rbac.authorization.k8s.io 10 | kind: ClusterRole 11 | name: plugin-barman-cloud 12 | subjects: 13 | - kind: ServiceAccount 14 | name: plugin-barman-cloud 15 | namespace: cnpg-system 16 | -------------------------------------------------------------------------------- /config/rbac/service_account.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: ServiceAccount 3 | metadata: 4 | labels: 5 | app.kubernetes.io/name: plugin-barman-cloud 6 | app.kubernetes.io/managed-by: kustomize 7 | name: plugin-barman-cloud 8 | -------------------------------------------------------------------------------- /config/samples/kustomization.yaml: -------------------------------------------------------------------------------- 1 | ## Append samples of your project ## 2 | resources: 3 | - barmancloud_v1_objectstore.yaml 4 | # +kubebuilder:scaffold:manifestskustomizesamples 5 | -------------------------------------------------------------------------------- /containers/Dockerfile.plugin: -------------------------------------------------------------------------------- 1 | # Build the manager binary 2 | FROM --platform=$BUILDPLATFORM golang:1.24.3 AS gobuilder 3 | ARG TARGETOS 4 | ARG TARGETARCH 5 | 6 | WORKDIR /workspace 7 | # Copy the Go Modules manifests 8 | COPY ../go.mod go.mod 9 | COPY ../go.sum go.sum 10 | # cache deps before building and copying source so that we don't need to re-download as much 11 | # and so that source changes don't invalidate our downloaded layer 12 | RUN go mod download 13 | 14 | # Copy the go source 15 | COPY ../cmd/manager/main.go cmd/manager/main.go 16 | COPY ../api/ api/ 17 | COPY ../internal/ internal/ 18 | 19 | ENV GOCACHE=/root/.cache/go-build 20 | ENV GOMODCACHE=/go/pkg/mod 21 | 22 | # Build 23 | # the GOARCH has not a default value to allow the binary be built according to the host where the command 24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO 25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, 26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 27 | RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \ 28 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go 29 | 30 | # Use a minimal base image to package the manager binary 31 | # Refer to https://www.redhat.com/en/blog/introduction-ubi-micro for more details 32 | FROM registry.access.redhat.com/ubi9/ubi-micro 33 | 34 | ENV SUMMARY="CloudNativePG Barman plugin" \ 35 | DESCRIPTION="Container image that provides the barman-cloud plugin" 36 | 37 | LABEL summary="$SUMMARY" \ 38 | description="$DESCRIPTION" \ 39 | io.k8s.display-name="$SUMMARY" \ 40 | io.k8s.description="$DESCRIPTION" \ 41 | name="$SUMMARY" \ 42 | vendor="CloudNativePG Contributors" \ 43 | url="https://cloudnative-pg.io/" \ 44 | version="" \ 45 | release="1" 46 | 47 | WORKDIR / 48 | COPY --from=gobuilder /workspace/manager . 49 | USER 65532:65532 50 | 51 | ENTRYPOINT ["/manager"] 52 | -------------------------------------------------------------------------------- /containers/Dockerfile.sidecar: -------------------------------------------------------------------------------- 1 | # Sidecar 2 | # The container needs to provide and build two components: 3 | # * barman-cloud 4 | # * instance plugin 5 | # Both components are built before going into a distroless container 6 | 7 | # Build the manager binary 8 | FROM --platform=$BUILDPLATFORM golang:1.24.3 AS gobuilder 9 | ARG TARGETOS 10 | ARG TARGETARCH 11 | 12 | WORKDIR /workspace 13 | # Copy the Go Modules manifests 14 | COPY ../go.mod go.mod 15 | COPY ../go.sum go.sum 16 | # cache deps before building and copying source so that we don't need to re-download as much 17 | # and so that source changes don't invalidate our downloaded layer 18 | RUN go mod download 19 | 20 | ENV GOCACHE=/root/.cache/go-build 21 | ENV GOMODCACHE=/go/pkg/mod 22 | 23 | # Copy the go source 24 | COPY ../cmd/manager/main.go cmd/manager/main.go 25 | COPY ../api/ api/ 26 | COPY ../internal/ internal/ 27 | 28 | # Build 29 | # the GOARCH has not a default value to allow the binary be built according to the host where the command 30 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO 31 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore, 32 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform. 33 | RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \ 34 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go 35 | 36 | # Build barman-cloud 37 | # pip will build everything inside /usr/ since this is the case 38 | # we should build and then copy every file into a destination that will 39 | # then copy into the distroless container 40 | FROM python:3.13-slim AS pythonbuilder 41 | COPY containers/sidecar-requirements.txt . 42 | RUN apt-get update && \ 43 | apt-get install -y postgresql-common build-essential && \ 44 | /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \ 45 | apt-get install -y libpq-dev && \ 46 | pip install -r sidecar-requirements.txt 47 | # Prepare a new /usr/ directory with the files we'll need in the final image 48 | RUN mkdir /new-usr/ && \ 49 | cp -r --parents /usr/local/lib/ /usr/lib/*-linux-gnu/ /usr/local/bin/ \ 50 | /new-usr/ 51 | 52 | # Joint process 53 | # Now we put everything that was build from the origin into our 54 | # distroless container 55 | FROM gcr.io/distroless/python3-debian12:nonroot 56 | 57 | ENV SUMMARY="CloudNativePG Barman plugin" \ 58 | DESCRIPTION="Container image that provides the barman-cloud sidecar" 59 | 60 | LABEL summary="$SUMMARY" \ 61 | description="$DESCRIPTION" \ 62 | io.k8s.display-name="$SUMMARY" \ 63 | io.k8s.description="$DESCRIPTION" \ 64 | name="CloudNativePG Barman plugin sidecar" \ 65 | vendor="CloudNativePG Contributors" \ 66 | url="https://cloudnative-pg.io/" \ 67 | version="" \ 68 | release="1" 69 | 70 | COPY --from=pythonbuilder /new-usr/* /usr/ 71 | COPY --from=gobuilder /workspace/manager /manager 72 | USER 26:26 73 | ENTRYPOINT ["/manager"] 74 | -------------------------------------------------------------------------------- /containers/sidecar-requirements.in: -------------------------------------------------------------------------------- 1 | barman[azure,cloud,google,snappy,zstandard,lz4]==3.14.0 2 | setuptools==80.9.0 3 | -------------------------------------------------------------------------------- /dagger/check-doc-version/.gitattributes: -------------------------------------------------------------------------------- 1 | /dagger.gen.go linguist-generated 2 | /internal/dagger/** linguist-generated 3 | /internal/querybuilder/** linguist-generated 4 | /internal/telemetry/** linguist-generated 5 | -------------------------------------------------------------------------------- /dagger/check-doc-version/.gitignore: -------------------------------------------------------------------------------- 1 | /dagger.gen.go 2 | /internal/dagger 3 | /internal/querybuilder 4 | /internal/telemetry 5 | -------------------------------------------------------------------------------- /dagger/check-doc-version/dagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "check-doc-version", 3 | "engineVersion": "v0.18.5", 4 | "sdk": { 5 | "source": "go" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /dagger/check-doc-version/go.mod: -------------------------------------------------------------------------------- 1 | module dagger/check-doc-version 2 | 3 | go 1.23.6 4 | 5 | require ( 6 | github.com/99designs/gqlgen v0.17.70 7 | github.com/Khan/genqlient v0.8.0 8 | github.com/vektah/gqlparser/v2 v2.5.23 9 | go.opentelemetry.io/otel v1.34.0 10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 12 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 13 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 14 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 15 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 16 | go.opentelemetry.io/otel/log v0.8.0 17 | go.opentelemetry.io/otel/metric v1.34.0 18 | go.opentelemetry.io/otel/sdk v1.34.0 19 | go.opentelemetry.io/otel/sdk/log v0.8.0 20 | go.opentelemetry.io/otel/sdk/metric v1.34.0 21 | go.opentelemetry.io/otel/trace v1.34.0 22 | go.opentelemetry.io/proto/otlp v1.3.1 23 | golang.org/x/sync v0.12.0 24 | google.golang.org/grpc v1.71.0 25 | ) 26 | 27 | require ( 28 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 29 | github.com/go-logr/logr v1.4.2 // indirect 30 | github.com/go-logr/stdr v1.2.2 // indirect 31 | github.com/google/uuid v1.6.0 // indirect 32 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect 33 | github.com/sosodev/duration v1.3.1 // indirect 34 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect 36 | golang.org/x/net v0.38.0 // indirect 37 | golang.org/x/sys v0.31.0 // indirect 38 | golang.org/x/text v0.23.0 // indirect 39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect 40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect 41 | google.golang.org/protobuf v1.36.6 // indirect 42 | ) 43 | 44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 45 | 46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 47 | 48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0 49 | 50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0 51 | -------------------------------------------------------------------------------- /dagger/check-doc-version/main.go: -------------------------------------------------------------------------------- 1 | // The CheckDocVersion module is designed to check if the version of the 2 | // documentation exists for the version specified in the release-please manifest. 3 | // This is used to ensure that we do not release a new version of the plugin 4 | // without having the corresponding documentation ready. 5 | 6 | package main 7 | 8 | import ( 9 | "context" 10 | "fmt" 11 | "strings" 12 | 13 | "dagger/check-doc-version/internal/dagger" 14 | ) 15 | 16 | type CheckDocVersion struct{} 17 | 18 | // HasVersionDocumentation checks if a version of the documentation exists for the 19 | // version in the release-please manifest. 20 | func (m *CheckDocVersion) HasVersionDocumentation(ctx context.Context, src *dagger.Directory) (bool, error) { 21 | releasePleaseManifest := ".release-please-manifest.json" 22 | docusaurusVersions := "web/versions.json" 23 | ctr := dag.Container().From("alpine:latest"). 24 | WithDirectory("/src", src). 25 | WithWorkdir("/src"). 26 | WithExec([]string{"apk", "add", "jq"}) 27 | nextVersion, err := ctr. 28 | WithExec([]string{"jq", "-r", ".[\".\"]", releasePleaseManifest}). 29 | Stdout(ctx) 30 | nextVersion = strings.TrimSpace(nextVersion) 31 | if err != nil { 32 | return false, fmt.Errorf("cannot find proposed release-please version in %v: %w", releasePleaseManifest, 33 | err) 34 | } 35 | currVersion, err := ctr.WithExec([]string{"jq", "-r", fmt.Sprintf(". | index(\"%v\")", nextVersion), 36 | docusaurusVersions}).Stdout(ctx) 37 | currVersion = strings.TrimSpace(currVersion) 38 | if err != nil { 39 | return false, fmt.Errorf("error querying versions in %v: %w", docusaurusVersions, err) 40 | } 41 | if currVersion == "null" { 42 | return false, nil 43 | } 44 | return true, nil 45 | } 46 | -------------------------------------------------------------------------------- /dagger/e2e/.gitattributes: -------------------------------------------------------------------------------- 1 | /dagger.gen.go linguist-generated 2 | /internal/dagger/** linguist-generated 3 | /internal/querybuilder/** linguist-generated 4 | /internal/telemetry/** linguist-generated 5 | -------------------------------------------------------------------------------- /dagger/e2e/.gitignore: -------------------------------------------------------------------------------- 1 | /dagger.gen.go 2 | /internal/dagger 3 | /internal/querybuilder 4 | /internal/telemetry 5 | -------------------------------------------------------------------------------- /dagger/e2e/dagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "e2e", 3 | "engineVersion": "v0.18.5", 4 | "sdk": { 5 | "source": "go" 6 | }, 7 | "dependencies": [ 8 | { 9 | "name": "go", 10 | "source": "github.com/sagikazarmark/daggerverse/go@go/v0.9.0", 11 | "pin": "d9ba06776c4c1ccf6f329bd862b9b439c4582ab6" 12 | }, 13 | { 14 | "name": "k3s", 15 | "source": "github.com/marcosnils/daggerverse/k3s@k3s/v0.1.10", 16 | "pin": "28eea1fcf3b6ecb38a628186107760acd717442f" 17 | } 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /dagger/e2e/go.mod: -------------------------------------------------------------------------------- 1 | module dagger/e-2-e 2 | 3 | go 1.23.2 4 | 5 | require ( 6 | github.com/99designs/gqlgen v0.17.70 7 | github.com/Khan/genqlient v0.8.0 8 | github.com/vektah/gqlparser/v2 v2.5.23 9 | go.opentelemetry.io/otel v1.34.0 10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 12 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 13 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 14 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 15 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 16 | go.opentelemetry.io/otel/log v0.8.0 17 | go.opentelemetry.io/otel/metric v1.34.0 18 | go.opentelemetry.io/otel/sdk v1.34.0 19 | go.opentelemetry.io/otel/sdk/log v0.8.0 20 | go.opentelemetry.io/otel/sdk/metric v1.34.0 21 | go.opentelemetry.io/otel/trace v1.34.0 22 | go.opentelemetry.io/proto/otlp v1.3.1 23 | golang.org/x/sync v0.12.0 24 | google.golang.org/grpc v1.71.0 25 | ) 26 | 27 | require ( 28 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 29 | github.com/go-logr/logr v1.4.2 // indirect 30 | github.com/go-logr/stdr v1.2.2 // indirect 31 | github.com/google/uuid v1.6.0 // indirect 32 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect 33 | github.com/sosodev/duration v1.3.1 // indirect 34 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect 36 | golang.org/x/net v0.38.0 // indirect 37 | golang.org/x/sys v0.31.0 // indirect 38 | golang.org/x/text v0.23.0 // indirect 39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect 40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect 41 | google.golang.org/protobuf v1.36.6 // indirect 42 | ) 43 | 44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 45 | 46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 47 | 48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0 49 | 50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0 51 | -------------------------------------------------------------------------------- /dagger/e2e/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | 7 | "dagger/e-2-e/internal/dagger" 8 | ) 9 | 10 | type E2E struct{} 11 | 12 | // Run runs the E2E tests on a Kubernetes cluster. It returns the output of the tests. 13 | // We expect a kubeconfig file that allows access to the cluster, and optionally 14 | // a service to bind to, if the cluster is not directly exposed to the dagger container running the tests. 15 | func (m *E2E) Run( 16 | ctx context.Context, 17 | // source is the directory containing the source code for the project 18 | source *dagger.Directory, 19 | // kubeconfig is the kubeconfig file to use for the tests 20 | kubeconfig *dagger.File, 21 | // svc is the Kubernetes service to bind to. It will be known as "kubernetes" in the container. 22 | // +optional 23 | svc *dagger.Service, 24 | // version of the golang image to use 25 | // +optional 26 | // +default="latest" 27 | goVersion string, 28 | ) (string, error) { 29 | goDag := dag.Go(dagger.GoOpts{Version: goVersion}).WithCgoDisabled().WithSource(source) 30 | if svc != nil { 31 | goDag = goDag.WithServiceBinding("kubernetes", svc) 32 | } 33 | return goDag.Container(). 34 | WithMountedFile("/kubeconfig", kubeconfig). 35 | WithEnvVariable("KUBECONFIG", "/kubeconfig"). 36 | WithExec([]string{"go", "run", "github.com/onsi/ginkgo/v2/ginkgo", 37 | "--procs=4", 38 | "--randomize-all", 39 | "--randomize-suites", 40 | "--fail-on-pending", 41 | "--fail-on-empty", 42 | "--keep-going", 43 | "--timeout=45m", 44 | "--github-output", 45 | "./test/e2e"}).Stdout(ctx) 46 | } 47 | 48 | // RunEphemeral creates a k3s cluster in dagger and then runs the E2E tests on it. 49 | // If a private registry is used, its url and the ca certificate for the registry should be provided. 50 | func (m *E2E) RunEphemeral( 51 | ctx context.Context, 52 | // source is the directory containing the source code for the project 53 | source *dagger.Directory, 54 | // registry is a private registry 55 | // +optional 56 | // +default="registry.barman-cloud-plugin:5000" 57 | registry string, 58 | // ca is the certificate authority for the registry 59 | // +optional 60 | ca *dagger.File, 61 | // name is the name of the ephemeral container 62 | // +optional 63 | // +default="e2e" 64 | name string, 65 | // version of the golang image to use 66 | // +optional 67 | // +default="latest" 68 | goVersion string, 69 | ) (string, error) { 70 | k3s := dag.K3S(name) 71 | ctr := k3s.Container() 72 | if ca != nil { 73 | ctr = ctr.WithMountedFile("/usr/local/share/ca-certificates/ca.crt", ca) 74 | } 75 | if registry != "" { 76 | ctr = ctr.WithNewFile("/registries.yaml", fmt.Sprintf(` 77 | configs: 78 | "%s": 79 | tls: 80 | ca_file: "/usr/local/share/ca-certificates/ca.crt" 81 | `, registry)). 82 | WithExec([]string{"sh", "-c", "cat /registries.yaml > /etc/rancher/k3s/registries.yaml"}) 83 | } 84 | 85 | ctr, err := ctr.Sync(ctx) 86 | if err != nil { 87 | return "", err 88 | } 89 | kServer := k3s.WithContainer(ctr).Server() 90 | 91 | kServer, err = kServer.Start(ctx) 92 | if err != nil { 93 | return "", err 94 | } 95 | defer kServer.Stop(ctx) 96 | 97 | return m.Run(ctx, source, k3s.Config(), kServer, goVersion) 98 | } 99 | -------------------------------------------------------------------------------- /dagger/gotest/.gitattributes: -------------------------------------------------------------------------------- 1 | /dagger.gen.go linguist-generated 2 | /internal/dagger/** linguist-generated 3 | /internal/querybuilder/** linguist-generated 4 | /internal/telemetry/** linguist-generated 5 | -------------------------------------------------------------------------------- /dagger/gotest/.gitignore: -------------------------------------------------------------------------------- 1 | /dagger.gen.go 2 | /internal/dagger 3 | /internal/querybuilder 4 | /internal/telemetry 5 | -------------------------------------------------------------------------------- /dagger/gotest/dagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gotest", 3 | "engineVersion": "v0.18.5", 4 | "sdk": { 5 | "source": "go" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /dagger/gotest/go.mod: -------------------------------------------------------------------------------- 1 | module dagger/gotest 2 | 3 | go 1.23.1 4 | 5 | require ( 6 | github.com/99designs/gqlgen v0.17.70 7 | github.com/Khan/genqlient v0.8.0 8 | github.com/vektah/gqlparser/v2 v2.5.23 9 | go.opentelemetry.io/otel v1.34.0 10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 12 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 13 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 14 | go.opentelemetry.io/otel/log v0.8.0 15 | go.opentelemetry.io/otel/sdk v1.34.0 16 | go.opentelemetry.io/otel/sdk/log v0.8.0 17 | go.opentelemetry.io/otel/trace v1.34.0 18 | go.opentelemetry.io/proto/otlp v1.3.1 19 | golang.org/x/sync v0.12.0 20 | google.golang.org/grpc v1.71.0 21 | ) 22 | 23 | require ( 24 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 25 | github.com/go-logr/logr v1.4.2 // indirect 26 | github.com/go-logr/stdr v1.2.2 // indirect 27 | github.com/google/uuid v1.6.0 // indirect 28 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect 29 | github.com/sosodev/duration v1.3.1 // indirect 30 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 31 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 32 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 33 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect 34 | go.opentelemetry.io/otel/metric v1.34.0 35 | go.opentelemetry.io/otel/sdk/metric v1.34.0 36 | golang.org/x/net v0.38.0 // indirect 37 | golang.org/x/sys v0.31.0 // indirect 38 | golang.org/x/text v0.23.0 // indirect 39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect 40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect 41 | google.golang.org/protobuf v1.36.6 // indirect 42 | ) 43 | 44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 45 | 46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 47 | 48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0 49 | 50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0 51 | -------------------------------------------------------------------------------- /dagger/gotest/main.go: -------------------------------------------------------------------------------- 1 | // A generated module for Gotest functions 2 | // 3 | // This module has been generated via dagger init and serves as a reference to 4 | // basic module structure as you get started with Dagger. 5 | // 6 | // Two functions have been pre-created. You can modify, delete, or add to them, 7 | // as needed. They demonstrate usage of arguments and return types using simple 8 | // echo and grep commands. The functions can be called from the dagger CLI or 9 | // from one of the SDKs. 10 | // 11 | // The first line in this comment block is a short description line and the 12 | // rest is a long description with more detail on the module's purpose or usage, 13 | // if appropriate. All modules should have a short description. 14 | 15 | package main 16 | 17 | import ( 18 | "context" 19 | "fmt" 20 | 21 | "dagger/gotest/internal/dagger" 22 | ) 23 | 24 | type Gotest struct { 25 | // +private 26 | Ctr *dagger.Container 27 | // +private 28 | KubeVersion string 29 | } 30 | 31 | func New( 32 | // Go version 33 | // 34 | // +optional 35 | // +default="latest" 36 | goVersion string, 37 | // setup-envtest version 38 | // +optional 39 | // +default="0.19.0" 40 | setupEnvtestVersion string, 41 | // Kubernetes version 42 | // +optional 43 | // +default="1.31.0" 44 | kubeVersion string, 45 | // Container to run the tests 46 | // +optional 47 | ctr *dagger.Container, 48 | ) *Gotest { 49 | if ctr != nil { 50 | return &Gotest{Ctr: ctr} 51 | } 52 | 53 | user := "noroot" 54 | modCachePath := fmt.Sprintf("/home/%s/go/pkg/mod", user) 55 | goCachePath := fmt.Sprintf("/home/%s/.cache/go-build", user) 56 | ctr = dag.Container().From("golang:"+goVersion). 57 | WithExec([]string{"curl", "-L", 58 | fmt.Sprintf("https://dl.k8s.io/release/v%v/bin/linux/amd64/kubectl", kubeVersion), 59 | "-o", "/usr/local/bin/kubectl"}). 60 | WithExec([]string{"chmod", "+x", "/usr/local/bin/kubectl"}). 61 | WithExec([]string{"curl", "-L", 62 | fmt.Sprintf( 63 | "https://github.com/kubernetes-sigs/controller-runtime/releases/download/v%v/setup-envtest-linux-amd64", 64 | setupEnvtestVersion), 65 | "-o", "/usr/local/bin/setup-envtest"}). 66 | WithExec([]string{"chmod", "+x", "/usr/local/bin/setup-envtest"}). 67 | WithExec([]string{"useradd", "-m", user}). 68 | WithUser(user). 69 | WithEnvVariable("CGO_ENABLED", "0"). 70 | WithEnvVariable("GOMODCACHE", modCachePath). 71 | WithEnvVariable("GOCACHE", goCachePath). 72 | WithMountedCache(modCachePath, dag.CacheVolume("go-mod"), 73 | dagger.ContainerWithMountedCacheOpts{Owner: user}). 74 | WithMountedCache(goCachePath, dag.CacheVolume("go-build"), 75 | dagger.ContainerWithMountedCacheOpts{Owner: user}) 76 | 77 | return &Gotest{Ctr: ctr, KubeVersion: kubeVersion} 78 | } 79 | 80 | func (m *Gotest) UnitTest( 81 | ctx context.Context, 82 | // Source directory 83 | // +required 84 | src *dagger.Directory, 85 | ) (string, error) { 86 | envtestCmd := []string{"setup-envtest", "use", "-p", "path", m.KubeVersion} 87 | return m.Ctr.WithDirectory("/src", src). 88 | // Setup envtest. There is no proper way to install it from a git release, so we use the go install command 89 | WithExec(envtestCmd). 90 | WithEnvVariable("KUBEBUILDER_ASSETS", 91 | fmt.Sprintf("/home/noroot/.local/share/kubebuilder-envtest/k8s/%v-linux-amd64", m.KubeVersion), 92 | ). 93 | WithWorkdir("/src"). 94 | // Exclude the e2e tests, we don't want to run them here 95 | WithoutDirectory("/src/test/e2e"). 96 | WithExec([]string{"go", "test", "./..."}).Stdout(ctx) 97 | } 98 | -------------------------------------------------------------------------------- /hack/boilerplate.go.txt: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ -------------------------------------------------------------------------------- /hack/build-dev-image.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # This script builds the images of the barman cloud plugin, to be used 4 | # to quickly test images in a development environment. 5 | # 6 | # After each run, the built images will have these names: 7 | # 8 | # - `plugin-barman-cloud:dev` 9 | # - `plugin-barman-cloud-sidecar:dev` 10 | 11 | set -eu 12 | 13 | docker build -t plugin-barman-cloud:dev --file containers/Dockerfile.plugin . 14 | docker build -t plugin-barman-cloud-sidecar:dev --file containers/Dockerfile.sidecar . 15 | -------------------------------------------------------------------------------- /hack/crd-gen-refs/config.yaml: -------------------------------------------------------------------------------- 1 | processor: 2 | ignoreGroupVersions: 3 | - "GVK" 4 | customMarkers: 5 | - name: "optional" 6 | target: field 7 | ignoreFields: 8 | # - "status$" 9 | - "TypeMeta$" 10 | ignoreTypes: 11 | - "ObjectStoreList$" 12 | 13 | render: 14 | # Version of Kubernetes to use when generating links to Kubernetes API documentation. 15 | # renovate: datasource=git-refs depName=kubernetes/kubernetes lookupName=https://github.com/kubernetes/kubernetes 16 | kubernetesVersion: 1.32 17 | knownTypes: 18 | - name: BarmanObjectStoreConfiguration 19 | package: github.com/cloudnative-pg/barman-cloud/pkg/api 20 | link: https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#BarmanObjectStoreConfiguration 21 | -------------------------------------------------------------------------------- /hack/crd-gen-refs/markdown/gv_details.tpl: -------------------------------------------------------------------------------- 1 | {{- define "gvDetails" -}} 2 | {{- $gv := . -}} 3 | 4 | ## {{ $gv.GroupVersionString }} 5 | 6 | {{ $gv.Doc }} 7 | 8 | {{- if $gv.Kinds }} 9 | ### Resource Types 10 | {{- range $gv.SortedKinds }} 11 | - {{ $gv.TypeForKind . | markdownRenderTypeLink }} 12 | {{- end }} 13 | {{ end }} 14 | 15 | {{ range $gv.SortedTypes }} 16 | {{ template "type" . }} 17 | {{ end }} 18 | 19 | {{- end -}} 20 | -------------------------------------------------------------------------------- /hack/crd-gen-refs/markdown/gv_list.tpl: -------------------------------------------------------------------------------- 1 | {{- define "gvList" -}} 2 | {{- $groupVersions := . -}} 3 | 4 | # API Reference 5 | 6 | ## Packages 7 | {{- range $groupVersions }} 8 | - {{ markdownRenderGVLink . }} 9 | {{- end }} 10 | 11 | {{ range $groupVersions }} 12 | {{ template "gvDetails" . }} 13 | {{ end }} 14 | 15 | {{- end -}} 16 | -------------------------------------------------------------------------------- /hack/crd-gen-refs/markdown/type.tpl: -------------------------------------------------------------------------------- 1 | {{- define "type" -}} 2 | {{- $type := . -}} 3 | {{- if markdownShouldRenderType $type -}} 4 | 5 | #### {{ $type.Name }} 6 | 7 | {{ if $type.IsAlias }}_Underlying type:_ _{{ markdownRenderTypeLink $type.UnderlyingType }}_{{ end }} 8 | 9 | {{ $type.Doc }} 10 | 11 | {{ if $type.Validation -}} 12 | _Validation:_ 13 | {{- range $type.Validation }} 14 | - {{ . }} 15 | {{- end }} 16 | {{- end }} 17 | 18 | {{ if $type.References -}} 19 | _Appears in:_ 20 | {{- range $type.SortedReferences }} 21 | - {{ markdownRenderTypeLink . }} 22 | {{- end }} 23 | {{- end }} 24 | 25 | {{ if $type.Members -}} 26 | | Field | Description | Required | Default | Validation | 27 | | --- | --- | --- | --- | --- | 28 | {{ if $type.GVK -}} 29 | | `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | True | | | 30 | | `kind` _string_ | `{{ $type.GVK.Kind }}` | True | | | 31 | {{ end -}} 32 | 33 | {{ range $type.Members -}} 34 | | `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ if not .Markers.optional -}}True{{- end }} | {{ markdownRenderDefault .Default }} | {{ range .Validation -}} {{ markdownRenderFieldDoc . }}
{{ end }} | 35 | {{ end -}} 36 | 37 | {{ end -}} 38 | 39 | {{ if $type.EnumValues -}} 40 | | Field | Description | 41 | | --- | --- | 42 | {{ range $type.EnumValues -}} 43 | | `{{ .Name }}` | {{ markdownRenderFieldDoc .Doc }} | 44 | {{ end -}} 45 | {{ end -}} 46 | 47 | 48 | {{- end -}} 49 | {{- end -}} 50 | -------------------------------------------------------------------------------- /hack/crd-gen-refs/markdown/type_members.tpl: -------------------------------------------------------------------------------- 1 | {{- define "type_members" -}} 2 | {{- $field := . -}} 3 | {{- if eq $field.Name "metadata" -}} 4 | Refer to Kubernetes API documentation for fields of `metadata`. 5 | {{- else -}} 6 | {{ markdownRenderFieldDoc $field.Doc }} 7 | {{- end -}} 8 | {{- end -}} 9 | -------------------------------------------------------------------------------- /hack/examples/backup-example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Backup 3 | metadata: 4 | name: backup-example 5 | spec: 6 | method: plugin 7 | 8 | cluster: 9 | name: cluster-example 10 | 11 | pluginConfiguration: 12 | name: barman-cloud.cloudnative-pg.io -------------------------------------------------------------------------------- /hack/examples/cluster-example-legacy.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-example 5 | spec: 6 | instances: 3 7 | 8 | backup: 9 | barmanObjectStore: 10 | endpointCA: 11 | name: minio-server-tls 12 | key: tls.crt 13 | destinationPath: s3://backups/ 14 | endpointURL: https://minio:9000 15 | s3Credentials: 16 | accessKeyId: 17 | name: minio 18 | key: ACCESS_KEY_ID 19 | secretAccessKey: 20 | name: minio 21 | key: ACCESS_SECRET_KEY 22 | wal: 23 | compression: gzip 24 | data: 25 | additionalCommandArgs: 26 | - "--min-chunk-size=5MB" 27 | - "--read-timeout=60" 28 | - "-vv" 29 | 30 | storage: 31 | size: 1Gi 32 | -------------------------------------------------------------------------------- /hack/examples/cluster-example.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-example 5 | spec: 6 | instances: 3 7 | plugins: 8 | - name: barman-cloud.cloudnative-pg.io 9 | isWALArchiver: true 10 | parameters: 11 | barmanObjectName: minio-store 12 | 13 | storage: 14 | size: 1Gi 15 | -------------------------------------------------------------------------------- /hack/examples/cluster-replica-log-shipping.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-replica 5 | spec: 6 | instances: 3 7 | bootstrap: 8 | recovery: 9 | source: source 10 | replica: 11 | enabled: true 12 | source: source 13 | externalClusters: 14 | - name: source 15 | plugin: 16 | name: barman-cloud.cloudnative-pg.io 17 | parameters: 18 | barmanObjectName: minio-store 19 | serverName: cluster-example 20 | storage: 21 | size: 1Gi 22 | 23 | -------------------------------------------------------------------------------- /hack/examples/cluster-restore-archive.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-restore 5 | spec: 6 | instances: 3 7 | imagePullPolicy: IfNotPresent 8 | 9 | bootstrap: 10 | recovery: 11 | source: source 12 | 13 | plugins: 14 | - name: barman-cloud.cloudnative-pg.io 15 | isWALArchiver: true 16 | parameters: 17 | barmanObjectName: minio-store-bis 18 | 19 | externalClusters: 20 | - name: source 21 | plugin: 22 | name: barman-cloud.cloudnative-pg.io 23 | parameters: 24 | barmanObjectName: minio-store 25 | serverName: cluster-example 26 | 27 | storage: 28 | size: 1Gi 29 | -------------------------------------------------------------------------------- /hack/examples/cluster-restore.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: postgresql.cnpg.io/v1 2 | kind: Cluster 3 | metadata: 4 | name: cluster-restore 5 | spec: 6 | instances: 3 7 | imagePullPolicy: IfNotPresent 8 | 9 | bootstrap: 10 | recovery: 11 | source: source 12 | 13 | externalClusters: 14 | - name: source 15 | plugin: 16 | name: barman-cloud.cloudnative-pg.io 17 | parameters: 18 | barmanObjectName: minio-store 19 | serverName: cluster-example 20 | 21 | storage: 22 | size: 1Gi 23 | -------------------------------------------------------------------------------- /hack/examples/minio-store.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: barmancloud.cnpg.io/v1 2 | kind: ObjectStore 3 | metadata: 4 | name: minio-store 5 | spec: 6 | retentionPolicy: "1m" 7 | instanceSidecarConfiguration: 8 | retentionPolicyIntervalSeconds: 1800 9 | resources: 10 | requests: 11 | memory: "64Mi" 12 | cpu: "250m" 13 | limits: 14 | memory: "512Mi" 15 | cpu: "500m" 16 | configuration: 17 | endpointCA: 18 | name: minio-server-tls 19 | key: tls.crt 20 | destinationPath: s3://backups/ 21 | endpointURL: https://minio:9000 22 | s3Credentials: 23 | accessKeyId: 24 | name: minio 25 | key: ACCESS_KEY_ID 26 | secretAccessKey: 27 | name: minio 28 | key: ACCESS_SECRET_KEY 29 | wal: 30 | compression: gzip 31 | maxParallel: 8 32 | data: 33 | additionalCommandArgs: 34 | - "--min-chunk-size=5MB" 35 | - "--read-timeout=60" 36 | - "-vv" 37 | -------------------------------------------------------------------------------- /hack/kind-config.yaml: -------------------------------------------------------------------------------- 1 | # Kind configuration file for running e2e tests 2 | # Certificates must be mounted on each node because the registry is using TLS 3 | 4 | kind: Cluster 5 | apiVersion: kind.x-k8s.io/v1alpha4 6 | nodes: 7 | - role: control-plane 8 | extraMounts: 9 | - hostPath: certs/ca.pem 10 | containerPath: /usr/local/share/ca-certificates/ca.crt 11 | readOnly: true 12 | -------------------------------------------------------------------------------- /hack/minio/minio-client.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Pod 3 | metadata: 4 | labels: 5 | run: mc 6 | name: mc 7 | spec: 8 | containers: 9 | - env: 10 | - name: MC_HOST_minio 11 | value: http://chooJeiroroo2noquomei2uuceisheth:ongeiqueitohL0queeLohkiur2quaing@minio:9000 12 | image: minio/mc 13 | name: mc 14 | resources: {} 15 | # Keep the pod up to exec stuff on it 16 | command: 17 | - sleep 18 | - "3600" 19 | dnsPolicy: ClusterFirst 20 | restartPolicy: Always 21 | -------------------------------------------------------------------------------- /hack/minio/minio-deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: minio 5 | labels: 6 | app: minio 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: minio 12 | template: 13 | metadata: 14 | labels: 15 | app: minio 16 | spec: 17 | containers: 18 | - name: minio 19 | image: minio/minio 20 | ports: 21 | - containerPort: 9000 22 | volumeMounts: 23 | - mountPath: /data 24 | name: data 25 | args: 26 | - server 27 | - /data 28 | env: 29 | - name: MINIO_ACCESS_KEY 30 | valueFrom: 31 | secretKeyRef: 32 | name: minio 33 | key: ACCESS_KEY_ID 34 | - name: MINIO_SECRET_KEY 35 | valueFrom: 36 | secretKeyRef: 37 | name: minio 38 | key: ACCESS_SECRET_KEY 39 | volumes: 40 | - name: data 41 | persistentVolumeClaim: 42 | claimName: minio 43 | -------------------------------------------------------------------------------- /hack/minio/minio-pvc.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: PersistentVolumeClaim 3 | metadata: 4 | name: minio 5 | spec: 6 | accessModes: 7 | - ReadWriteOnce 8 | volumeMode: Filesystem 9 | resources: 10 | requests: 11 | storage: 1Gi 12 | -------------------------------------------------------------------------------- /hack/minio/minio-secret.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | data: 3 | ACCESS_KEY_ID: Y2hvb0plaXJvcm9vMm5vcXVvbWVpMnV1Y2Vpc2hldGg= 4 | ACCESS_SECRET_KEY: b25nZWlxdWVpdG9oTDBxdWVlTG9oa2l1cjJxdWFpbmc= 5 | kind: Secret 6 | metadata: 7 | name: minio 8 | -------------------------------------------------------------------------------- /hack/minio/minio-service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: minio 5 | spec: 6 | selector: 7 | app: minio 8 | ports: 9 | - protocol: TCP 10 | port: 9000 11 | targetPort: 9000 12 | -------------------------------------------------------------------------------- /internal/cmd/healthcheck/doc.go: -------------------------------------------------------------------------------- 1 | // Package healthcheck contains the logic to execute an healthcheck on the plugin through a command 2 | package healthcheck 3 | -------------------------------------------------------------------------------- /internal/cmd/healthcheck/main.go: -------------------------------------------------------------------------------- 1 | package healthcheck 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path" 7 | 8 | "github.com/cloudnative-pg/machinery/pkg/log" 9 | "github.com/spf13/cobra" 10 | "google.golang.org/grpc" 11 | "google.golang.org/grpc/credentials/insecure" 12 | "google.golang.org/grpc/health/grpc_health_v1" 13 | 14 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 15 | ) 16 | 17 | // NewCmd returns the healthcheck command 18 | func NewCmd() *cobra.Command { 19 | cmd := &cobra.Command{ 20 | Use: "healthcheck", 21 | Short: "healthcheck commands", 22 | } 23 | 24 | cmd.AddCommand(unixHealthCheck()) 25 | 26 | return cmd 27 | } 28 | 29 | func unixHealthCheck() *cobra.Command { 30 | cmd := &cobra.Command{ 31 | Use: "unix", 32 | Short: "executes the health check command on unix:///plugins/barman-cloud.cloudnative-pg.io", 33 | RunE: func(cmd *cobra.Command, _ []string) error { 34 | dialPath := fmt.Sprintf("unix://%s", path.Join("/plugins", metadata.PluginName)) 35 | cli, cliErr := grpc.NewClient(dialPath, grpc.WithTransportCredentials(insecure.NewCredentials())) 36 | if cliErr != nil { 37 | log.Error(cliErr, "while building the client") 38 | return cliErr 39 | } 40 | 41 | healthCli := grpc_health_v1.NewHealthClient(cli) 42 | res, healthErr := healthCli.Check( 43 | cmd.Context(), 44 | &grpc_health_v1.HealthCheckRequest{}, 45 | ) 46 | if healthErr != nil { 47 | log.Error(healthErr, "while executing the healthcheck call") 48 | return healthErr 49 | } 50 | 51 | if res.Status == grpc_health_v1.HealthCheckResponse_SERVING { 52 | log.Trace("healthcheck response OK") 53 | os.Exit(0) 54 | return nil 55 | } 56 | 57 | log.Error(fmt.Errorf("unexpected healthcheck status: %v", res.Status), 58 | "while processing healthcheck response") 59 | 60 | // exit code 1 is returned when we exit from the function with an error 61 | switch res.Status { 62 | case grpc_health_v1.HealthCheckResponse_UNKNOWN: 63 | os.Exit(2) 64 | case grpc_health_v1.HealthCheckResponse_NOT_SERVING: 65 | os.Exit(3) 66 | default: 67 | os.Exit(125) 68 | } 69 | 70 | return nil 71 | }, 72 | } 73 | 74 | return cmd 75 | } 76 | -------------------------------------------------------------------------------- /internal/cmd/instance/main.go: -------------------------------------------------------------------------------- 1 | // Package instance is the entrypoint of instance plugin 2 | package instance 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | 10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/instance" 11 | ) 12 | 13 | // NewCmd creates a new instance command 14 | func NewCmd() *cobra.Command { 15 | cmd := &cobra.Command{ 16 | Use: "instance", 17 | Short: "Starts the Barman Cloud CNPG-I sidecar plugin", 18 | RunE: func(cmd *cobra.Command, _ []string) error { 19 | requiredSettings := []string{ 20 | "namespace", 21 | "cluster-name", 22 | "pod-name", 23 | "spool-directory", 24 | } 25 | 26 | for _, k := range requiredSettings { 27 | if len(viper.GetString(k)) == 0 { 28 | return fmt.Errorf("missing required %s setting", k) 29 | } 30 | } 31 | 32 | return instance.Start(cmd.Context()) 33 | }, 34 | } 35 | 36 | _ = viper.BindEnv("namespace", "NAMESPACE") 37 | _ = viper.BindEnv("cluster-name", "CLUSTER_NAME") 38 | _ = viper.BindEnv("pod-name", "POD_NAME") 39 | _ = viper.BindEnv("pgdata", "PGDATA") 40 | _ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY") 41 | _ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP") 42 | _ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSIONXS") 43 | 44 | return cmd 45 | } 46 | -------------------------------------------------------------------------------- /internal/cmd/operator/main.go: -------------------------------------------------------------------------------- 1 | // Package operator is the entrypoint of operator plugin 2 | package operator 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | 10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator" 11 | ) 12 | 13 | // NewCmd creates a new operator command 14 | func NewCmd() *cobra.Command { 15 | cmd := &cobra.Command{ 16 | Use: "operator", 17 | Short: "Starts the BarmanObjectStore reconciler and the Barman Cloud CNPG-i plugin", 18 | RunE: func(cmd *cobra.Command, _ []string) error { 19 | if len(viper.GetString("sidecar-image")) == 0 { 20 | return fmt.Errorf("missing required SIDECAR_IMAGE environment variable") 21 | } 22 | 23 | return operator.Start(cmd.Context()) 24 | }, 25 | PersistentPreRunE: func(_ *cobra.Command, _ []string) error { 26 | return nil 27 | }, 28 | } 29 | 30 | cmd.Flags().String("metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ 31 | "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") 32 | _ = viper.BindPFlag("metrics-bind-address", cmd.Flags().Lookup("metrics-bind-address")) 33 | 34 | cmd.Flags().String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") 35 | _ = viper.BindPFlag("health-probe-bind-address", cmd.Flags().Lookup("health-probe-bind-address")) 36 | 37 | cmd.Flags().Bool("leader-elect", false, 38 | "Enable leader election for controller manager. "+ 39 | "Enabling this will ensure there is only one active controller manager.") 40 | _ = viper.BindPFlag("leader-elect", cmd.Flags().Lookup("leader-elect")) 41 | 42 | cmd.Flags().Bool("metrics-secure", true, 43 | "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") 44 | _ = viper.BindPFlag("metrics-secure", cmd.Flags().Lookup("metrics-secure")) 45 | 46 | cmd.Flags().Bool("enable-http2", false, 47 | "If set, HTTP/2 will be enabled for the metrics and webhook servers") 48 | _ = viper.BindPFlag("enable-http2", cmd.Flags().Lookup("enable-http2")) 49 | 50 | cmd.Flags().String( 51 | "plugin-path", 52 | "", 53 | "The plugins socket path", 54 | ) 55 | _ = viper.BindPFlag("plugin-path", cmd.Flags().Lookup("plugin-path")) 56 | 57 | cmd.Flags().String( 58 | "server-cert", 59 | "", 60 | "The public key to be used for the server process", 61 | ) 62 | _ = viper.BindPFlag("server-cert", cmd.Flags().Lookup("server-cert")) 63 | 64 | cmd.Flags().String( 65 | "server-key", 66 | "", 67 | "The key to be used for the server process", 68 | ) 69 | _ = viper.BindPFlag("server-key", cmd.Flags().Lookup("server-key")) 70 | 71 | cmd.Flags().String( 72 | "client-cert", 73 | "", 74 | "The client public key to verify the connection", 75 | ) 76 | _ = viper.BindPFlag("client-cert", cmd.Flags().Lookup("client-cert")) 77 | 78 | cmd.Flags().String( 79 | "server-address", 80 | "", 81 | "The address where to listen (i.e. 0:9090)", 82 | ) 83 | _ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address")) 84 | 85 | _ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE") 86 | 87 | return cmd 88 | } 89 | -------------------------------------------------------------------------------- /internal/cmd/restore/main.go: -------------------------------------------------------------------------------- 1 | // Package restore is the entrypoint of restore capabilities 2 | package restore 3 | 4 | import ( 5 | "fmt" 6 | 7 | "github.com/spf13/cobra" 8 | "github.com/spf13/viper" 9 | 10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/restore" 11 | ) 12 | 13 | // NewCmd creates the "restore" subcommand 14 | func NewCmd() *cobra.Command { 15 | cobra.EnableTraverseRunHooks = true 16 | 17 | cmd := &cobra.Command{ 18 | Use: "restore", 19 | Short: "Starts the Barman Cloud CNPG-I sidecar plugin", 20 | RunE: func(cmd *cobra.Command, _ []string) error { 21 | requiredSettings := []string{ 22 | "namespace", 23 | "pod-name", 24 | "spool-directory", 25 | } 26 | 27 | for _, k := range requiredSettings { 28 | if len(viper.GetString(k)) == 0 { 29 | return fmt.Errorf("missing required %s setting", k) 30 | } 31 | } 32 | 33 | return restore.Start(cmd.Context()) 34 | }, 35 | } 36 | 37 | _ = viper.BindEnv("namespace", "NAMESPACE") 38 | _ = viper.BindEnv("pod-name", "POD_NAME") 39 | _ = viper.BindEnv("pgdata", "PGDATA") 40 | _ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY") 41 | 42 | return cmd 43 | } 44 | -------------------------------------------------------------------------------- /internal/cnpgi/common/common.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "fmt" 5 | "path" 6 | "strings" 7 | 8 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api" 9 | 10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 11 | ) 12 | 13 | // TODO: refactor. 14 | const ( 15 | // ScratchDataDirectory is the directory to be used for scratch data. 16 | ScratchDataDirectory = "/controller" 17 | 18 | // CertificatesDir location to store the certificates. 19 | CertificatesDir = ScratchDataDirectory + "/certificates/" 20 | 21 | // BarmanBackupEndpointCACertificateLocation is the location where the barman endpoint 22 | // CA certificate is stored. 23 | BarmanBackupEndpointCACertificateLocation = CertificatesDir + BarmanBackupEndpointCACertificateFileName 24 | 25 | // BarmanBackupEndpointCACertificateFileName is the name of the file in which the barman endpoint 26 | // CA certificate for backups is stored. 27 | BarmanBackupEndpointCACertificateFileName = "backup-" + BarmanEndpointCACertificateFileName 28 | 29 | // BarmanRestoreEndpointCACertificateFileName is the name of the file in which the barman endpoint 30 | // CA certificate for restores is stored. 31 | BarmanRestoreEndpointCACertificateFileName = "restore-" + BarmanEndpointCACertificateFileName 32 | 33 | // BarmanEndpointCACertificateFileName is the name of the file in which the barman endpoint 34 | // CA certificate is stored. 35 | BarmanEndpointCACertificateFileName = "barman-ca.crt" 36 | ) 37 | 38 | // GetRestoreCABundleEnv gets the enveronment variables to be used when custom 39 | // Object Store CA is present 40 | func GetRestoreCABundleEnv(configuration *barmanapi.BarmanObjectStoreConfiguration) []string { 41 | var env []string 42 | 43 | if configuration.EndpointCA != nil && configuration.AWS != nil { 44 | env = append(env, fmt.Sprintf("AWS_CA_BUNDLE=%s", BarmanBackupEndpointCACertificateLocation)) 45 | } else if configuration.EndpointCA != nil && configuration.Azure != nil { 46 | env = append(env, fmt.Sprintf("REQUESTS_CA_BUNDLE=%s", BarmanBackupEndpointCACertificateLocation)) 47 | } 48 | return env 49 | } 50 | 51 | // MergeEnv merges all the values inside incomingEnv into env. 52 | func MergeEnv(env []string, incomingEnv []string) []string { 53 | result := make([]string, len(env), len(env)+len(incomingEnv)) 54 | copy(result, env) 55 | 56 | for _, incomingItem := range incomingEnv { 57 | incomingKV := strings.SplitAfterN(incomingItem, "=", 2) 58 | if len(incomingKV) != 2 { 59 | continue 60 | } 61 | 62 | found := false 63 | for idx, item := range result { 64 | if strings.HasPrefix(item, incomingKV[0]) { 65 | result[idx] = incomingItem 66 | found = true 67 | } 68 | } 69 | if !found { 70 | result = append(result, incomingItem) 71 | } 72 | } 73 | 74 | return result 75 | } 76 | 77 | // BuildCertificateFilePath builds the path to the barman objectStore certificate 78 | func BuildCertificateFilePath(objectStoreName string) string { 79 | return path.Join(metadata.BarmanCertificatesPath, objectStoreName, metadata.BarmanCertificatesFileName) 80 | } 81 | -------------------------------------------------------------------------------- /internal/cnpgi/common/doc.go: -------------------------------------------------------------------------------- 1 | // Package common contains reusable structs and methods for CNPGI plugins. 2 | package common 3 | -------------------------------------------------------------------------------- /internal/cnpgi/common/errors.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | // walNotFoundError is raised when a WAL file has not been found in the object store 4 | type walNotFoundError struct{} 5 | 6 | func newWALNotFoundError() *walNotFoundError { return &walNotFoundError{} } 7 | 8 | // ShouldPrintStackTrace tells whether the sidecar log stream should contain the stack trace 9 | func (e walNotFoundError) ShouldPrintStackTrace() bool { 10 | return false 11 | } 12 | 13 | // Error implements the error interface 14 | func (e walNotFoundError) Error() string { 15 | return "WAL file not found" 16 | } 17 | -------------------------------------------------------------------------------- /internal/cnpgi/common/health.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/machinery/pkg/log" 7 | "google.golang.org/grpc" 8 | "google.golang.org/grpc/health/grpc_health_v1" 9 | ) 10 | 11 | // AddHealthCheck adds a health check service to the gRPC server with the tag 'plugin-barman-cloud' 12 | func AddHealthCheck(server *grpc.Server) { 13 | grpc_health_v1.RegisterHealthServer(server, &healthServer{}) // replaces default registration 14 | } 15 | 16 | type healthServer struct { 17 | grpc_health_v1.UnimplementedHealthServer 18 | } 19 | 20 | // Check is the response handle for the healthcheck request 21 | func (h healthServer) Check( 22 | ctx context.Context, 23 | _ *grpc_health_v1.HealthCheckRequest, 24 | ) (*grpc_health_v1.HealthCheckResponse, error) { 25 | contextLogger := log.FromContext(ctx) 26 | contextLogger.Trace("serving health check response") 27 | return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil 28 | } 29 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/doc.go: -------------------------------------------------------------------------------- 1 | // Package instance implements the capabilities used by the operator sidecar 2 | package instance 3 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/identity.go: -------------------------------------------------------------------------------- 1 | package instance 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | 9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 10 | ) 11 | 12 | // IdentityImplementation implements IdentityServer 13 | type IdentityImplementation struct { 14 | identity.UnimplementedIdentityServer 15 | Client client.Client 16 | } 17 | 18 | // GetPluginMetadata implements IdentityServer 19 | func (i IdentityImplementation) GetPluginMetadata( 20 | _ context.Context, 21 | _ *identity.GetPluginMetadataRequest, 22 | ) (*identity.GetPluginMetadataResponse, error) { 23 | return &metadata.Data, nil 24 | } 25 | 26 | // GetPluginCapabilities implements IdentityServer 27 | func (i IdentityImplementation) GetPluginCapabilities( 28 | _ context.Context, 29 | _ *identity.GetPluginCapabilitiesRequest, 30 | ) (*identity.GetPluginCapabilitiesResponse, error) { 31 | return &identity.GetPluginCapabilitiesResponse{ 32 | Capabilities: []*identity.PluginCapability{ 33 | { 34 | Type: &identity.PluginCapability_Service_{ 35 | Service: &identity.PluginCapability_Service{ 36 | Type: identity.PluginCapability_Service_TYPE_WAL_SERVICE, 37 | }, 38 | }, 39 | }, 40 | { 41 | Type: &identity.PluginCapability_Service_{ 42 | Service: &identity.PluginCapability_Service{ 43 | Type: identity.PluginCapability_Service_TYPE_BACKUP_SERVICE, 44 | }, 45 | }, 46 | }, 47 | }, 48 | }, nil 49 | } 50 | 51 | // Probe implements IdentityServer 52 | func (i IdentityImplementation) Probe( 53 | _ context.Context, 54 | _ *identity.ProbeRequest, 55 | ) (*identity.ProbeResponse, error) { 56 | return &identity.ProbeResponse{ 57 | Ready: true, 58 | }, nil 59 | } 60 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/internal/client/client_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "time" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | "k8s.io/apimachinery/pkg/runtime" 9 | "sigs.k8s.io/controller-runtime/pkg/client" 10 | "sigs.k8s.io/controller-runtime/pkg/client/fake" 11 | 12 | v1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 13 | 14 | . "github.com/onsi/ginkgo/v2" 15 | . "github.com/onsi/gomega" 16 | ) 17 | 18 | var scheme = buildScheme() 19 | 20 | func buildScheme() *runtime.Scheme { 21 | scheme := runtime.NewScheme() 22 | _ = corev1.AddToScheme(scheme) 23 | _ = v1.AddToScheme(scheme) 24 | 25 | return scheme 26 | } 27 | 28 | var _ = Describe("ExtendedClient Get", func() { 29 | var ( 30 | extendedClient *ExtendedClient 31 | secretInClient *corev1.Secret 32 | objectStore *v1.ObjectStore 33 | ) 34 | 35 | BeforeEach(func() { 36 | secretInClient = &corev1.Secret{ 37 | ObjectMeta: metav1.ObjectMeta{ 38 | Namespace: "default", 39 | Name: "test-secret", 40 | }, 41 | } 42 | objectStore = &v1.ObjectStore{ 43 | ObjectMeta: metav1.ObjectMeta{ 44 | Namespace: "default", 45 | Name: "test-object-store", 46 | }, 47 | Spec: v1.ObjectStoreSpec{}, 48 | } 49 | 50 | baseClient := fake.NewClientBuilder(). 51 | WithScheme(scheme). 52 | WithObjects(secretInClient, objectStore).Build() 53 | extendedClient = NewExtendedClient(baseClient).(*ExtendedClient) 54 | }) 55 | 56 | It("returns secret from cache if not expired", func(ctx SpecContext) { 57 | secretNotInClient := &corev1.Secret{ 58 | ObjectMeta: metav1.ObjectMeta{ 59 | Namespace: "default", 60 | Name: "test-secret-not-in-client", 61 | }, 62 | } 63 | 64 | // manually add the secret to the cache, this is not present in the fake client so we are sure it is from the 65 | // cache 66 | extendedClient.cachedObjects = []cachedEntry{ 67 | { 68 | entry: secretNotInClient, 69 | fetchUnixTime: time.Now().Unix(), 70 | }, 71 | } 72 | 73 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretNotInClient), secretInClient) 74 | Expect(err).NotTo(HaveOccurred()) 75 | Expect(secretNotInClient).To(Equal(extendedClient.cachedObjects[0].entry)) 76 | }) 77 | 78 | It("fetches secret from base client if cache is expired", func(ctx SpecContext) { 79 | extendedClient.cachedObjects = []cachedEntry{ 80 | { 81 | entry: secretInClient.DeepCopy(), 82 | fetchUnixTime: time.Now().Add(-2 * time.Minute).Unix(), 83 | }, 84 | } 85 | 86 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient) 87 | Expect(err).NotTo(HaveOccurred()) 88 | }) 89 | 90 | It("fetches secret from base client if not in cache", func(ctx SpecContext) { 91 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient) 92 | Expect(err).NotTo(HaveOccurred()) 93 | }) 94 | 95 | It("does not cache non-secret objects", func(ctx SpecContext) { 96 | configMap := &corev1.ConfigMap{ 97 | ObjectMeta: metav1.ObjectMeta{ 98 | Namespace: "default", 99 | Name: "test-configmap", 100 | }, 101 | } 102 | err := extendedClient.Create(ctx, configMap) 103 | Expect(err).ToNot(HaveOccurred()) 104 | 105 | err = extendedClient.Get(ctx, client.ObjectKeyFromObject(configMap), configMap) 106 | Expect(err).NotTo(HaveOccurred()) 107 | Expect(extendedClient.cachedObjects).To(BeEmpty()) 108 | }) 109 | }) 110 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/internal/client/doc.go: -------------------------------------------------------------------------------- 1 | // Package client provides an extended client that is capable of caching multiple secrets without relying on 2 | // informers 3 | package client 4 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/internal/client/suite_test.go: -------------------------------------------------------------------------------- 1 | package client_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestClient(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Client Suite") 13 | } 14 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/start.go: -------------------------------------------------------------------------------- 1 | package instance 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http" 7 | "github.com/cloudnative-pg/cnpg-i/pkg/backup" 8 | "github.com/cloudnative-pg/cnpg-i/pkg/wal" 9 | "google.golang.org/grpc" 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | 12 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common" 13 | ) 14 | 15 | // CNPGI is the implementation of the PostgreSQL sidecar 16 | type CNPGI struct { 17 | Client client.Client 18 | PGDataPath string 19 | PGWALPath string 20 | SpoolDirectory string 21 | // mutually exclusive with serverAddress 22 | PluginPath string 23 | InstanceName string 24 | } 25 | 26 | // Start starts the GRPC service 27 | func (c *CNPGI) Start(ctx context.Context) error { 28 | enrich := func(server *grpc.Server) error { 29 | wal.RegisterWALServer(server, common.WALServiceImplementation{ 30 | InstanceName: c.InstanceName, 31 | Client: c.Client, 32 | SpoolDirectory: c.SpoolDirectory, 33 | PGDataPath: c.PGDataPath, 34 | PGWALPath: c.PGWALPath, 35 | }) 36 | backup.RegisterBackupServer(server, BackupServiceImplementation{ 37 | Client: c.Client, 38 | InstanceName: c.InstanceName, 39 | }) 40 | common.AddHealthCheck(server) 41 | return nil 42 | } 43 | 44 | srv := http.Server{ 45 | IdentityImpl: IdentityImplementation{ 46 | Client: c.Client, 47 | }, 48 | Enrichers: []http.ServerEnricher{enrich}, 49 | PluginPath: c.PluginPath, 50 | } 51 | 52 | return srv.Start(ctx) 53 | } 54 | -------------------------------------------------------------------------------- /internal/cnpgi/instance/types.go: -------------------------------------------------------------------------------- 1 | package instance 2 | 3 | import ( 4 | "strconv" 5 | 6 | "k8s.io/apimachinery/pkg/types" 7 | 8 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 9 | ) 10 | 11 | type backupResultMetadata struct { 12 | timeline string 13 | version string 14 | name string 15 | displayName string 16 | clusterUID string 17 | pluginName string 18 | } 19 | 20 | func (b backupResultMetadata) toMap() map[string]string { 21 | return map[string]string{ 22 | "timeline": b.timeline, 23 | "version": b.version, 24 | "name": b.name, 25 | "displayName": b.displayName, 26 | "clusterUID": b.clusterUID, 27 | "pluginName": b.pluginName, 28 | } 29 | } 30 | 31 | func newBackupResultMetadata(clusterUID types.UID, timeline int) backupResultMetadata { 32 | return backupResultMetadata{ 33 | timeline: strconv.Itoa(timeline), 34 | clusterUID: string(clusterUID), 35 | // static values 36 | version: metadata.Data.Version, 37 | name: metadata.Data.Name, 38 | displayName: metadata.Data.DisplayName, 39 | pluginName: metadata.PluginName, 40 | } 41 | } 42 | 43 | func newBackupResultMetadataFromMap(m map[string]string) backupResultMetadata { 44 | if m == nil { 45 | return backupResultMetadata{} 46 | } 47 | 48 | return backupResultMetadata{ 49 | timeline: m["timeline"], 50 | version: m["version"], 51 | name: m["name"], 52 | displayName: m["displayName"], 53 | clusterUID: m["clusterUID"], 54 | pluginName: m["pluginName"], 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /internal/cnpgi/metadata/constants.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import "github.com/cloudnative-pg/cnpg-i/pkg/identity" 4 | 5 | // PluginName is the name of the plugin from the instance manager 6 | // Point-of-view 7 | const PluginName = "barman-cloud.cloudnative-pg.io" 8 | 9 | const ( 10 | // CheckEmptyWalArchiveFile is the name of the file in the PGDATA that, 11 | // if present, requires the WAL archiver to check that the backup object 12 | // store is empty. 13 | CheckEmptyWalArchiveFile = ".check-empty-wal-archive" 14 | 15 | // BarmanCertificatesPath is the path where the Barman 16 | // certificates will be installed 17 | BarmanCertificatesPath = "/barman-certificates" 18 | 19 | // BarmanCertificatesFileName is the path where the Barman 20 | // certificates will be used 21 | BarmanCertificatesFileName = "barman-ca.crt" 22 | ) 23 | 24 | // Data is the metadata of this plugin. 25 | var Data = identity.GetPluginMetadataResponse{ 26 | Name: PluginName, 27 | Version: "0.5.0", // x-release-please-version 28 | DisplayName: "BarmanCloudInstance", 29 | ProjectUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud", 30 | RepositoryUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud", 31 | License: "APACHE 2.0", 32 | LicenseUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud/LICENSE", 33 | Maturity: "alpha", 34 | } 35 | -------------------------------------------------------------------------------- /internal/cnpgi/metadata/doc.go: -------------------------------------------------------------------------------- 1 | // Package metadata contains the common metadata on the operator 2 | // and on the instance manager 3 | package metadata 4 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/config/doc.go: -------------------------------------------------------------------------------- 1 | // Package config contains the functions to parse the plugin configuration 2 | package config 3 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/doc.go: -------------------------------------------------------------------------------- 1 | // Package operator implements the capabilities used by CNPG 2 | package operator 3 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/identity.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity" 7 | 8 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 9 | ) 10 | 11 | // IdentityImplementation is the implementation of the CNPG-i 12 | // Identity entrypoint 13 | type IdentityImplementation struct { 14 | identity.UnimplementedIdentityServer 15 | } 16 | 17 | // GetPluginMetadata implements Identity 18 | func (i IdentityImplementation) GetPluginMetadata( 19 | _ context.Context, 20 | _ *identity.GetPluginMetadataRequest, 21 | ) (*identity.GetPluginMetadataResponse, error) { 22 | return &metadata.Data, nil 23 | } 24 | 25 | // GetPluginCapabilities implements identity 26 | func (i IdentityImplementation) GetPluginCapabilities( 27 | _ context.Context, 28 | _ *identity.GetPluginCapabilitiesRequest, 29 | ) (*identity.GetPluginCapabilitiesResponse, error) { 30 | return &identity.GetPluginCapabilitiesResponse{ 31 | Capabilities: []*identity.PluginCapability{ 32 | { 33 | Type: &identity.PluginCapability_Service_{ 34 | Service: &identity.PluginCapability_Service{ 35 | Type: identity.PluginCapability_Service_TYPE_RECONCILER_HOOKS, 36 | }, 37 | }, 38 | }, 39 | { 40 | Type: &identity.PluginCapability_Service_{ 41 | Service: &identity.PluginCapability_Service{ 42 | Type: identity.PluginCapability_Service_TYPE_LIFECYCLE_SERVICE, 43 | }, 44 | }, 45 | }, 46 | }, 47 | }, nil 48 | } 49 | 50 | // Probe implements Identity 51 | func (i IdentityImplementation) Probe( 52 | _ context.Context, 53 | _ *identity.ProbeRequest, 54 | ) (*identity.ProbeResponse, error) { 55 | return &identity.ProbeResponse{ 56 | Ready: true, 57 | }, nil 58 | } 59 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/lifecycle_certificates.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | "path" 6 | 7 | corev1 "k8s.io/api/core/v1" 8 | "k8s.io/apimachinery/pkg/types" 9 | 10 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 11 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 12 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" 13 | ) 14 | 15 | // barmanCertificatesVolumeName is the name of the volume that hosts 16 | // the barman certificates to be used 17 | const barmanCertificatesVolumeName = "barman-certificates" 18 | 19 | func (impl LifecycleImplementation) collectAdditionalCertificates( 20 | ctx context.Context, 21 | pluginConfiguration *config.PluginConfiguration, 22 | ) ([]corev1.VolumeProjection, error) { 23 | var result []corev1.VolumeProjection 24 | 25 | for _, barmanObjectKey := range pluginConfiguration.GetReferredBarmanObjectsKey() { 26 | certs, err := impl.collectObjectStoreCertificates(ctx, barmanObjectKey) 27 | if err != nil { 28 | return nil, err 29 | } 30 | result = append(result, certs...) 31 | } 32 | 33 | return result, nil 34 | } 35 | 36 | func (impl LifecycleImplementation) collectObjectStoreCertificates( 37 | ctx context.Context, 38 | barmanObjectKey types.NamespacedName, 39 | ) ([]corev1.VolumeProjection, error) { 40 | var objectStore barmancloudv1.ObjectStore 41 | if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil { 42 | return nil, err 43 | } 44 | 45 | endpointCA := objectStore.Spec.Configuration.EndpointCA 46 | if endpointCA == nil { 47 | return nil, nil 48 | } 49 | 50 | return []corev1.VolumeProjection{ 51 | { 52 | Secret: &corev1.SecretProjection{ 53 | LocalObjectReference: corev1.LocalObjectReference{ 54 | Name: endpointCA.Name, 55 | }, 56 | Items: []corev1.KeyToPath{ 57 | { 58 | Key: endpointCA.Key, 59 | Path: path.Join( 60 | barmanObjectKey.Name, 61 | metadata.BarmanCertificatesFileName, 62 | ), 63 | }, 64 | }, 65 | }, 66 | }, 67 | }, nil 68 | } 69 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/lifecycle_envs.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | "k8s.io/apimachinery/pkg/types" 8 | 9 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" 11 | ) 12 | 13 | func (impl LifecycleImplementation) collectAdditionalEnvs( 14 | ctx context.Context, 15 | namespace string, 16 | pluginConfiguration *config.PluginConfiguration, 17 | ) ([]corev1.EnvVar, error) { 18 | var result []corev1.EnvVar 19 | 20 | // TODO: check if the environment variables are clashing and in 21 | // that case raise an error 22 | 23 | if len(pluginConfiguration.BarmanObjectName) > 0 { 24 | envs, err := impl.collectObjectStoreEnvs( 25 | ctx, 26 | types.NamespacedName{ 27 | Name: pluginConfiguration.BarmanObjectName, 28 | Namespace: namespace, 29 | }, 30 | ) 31 | if err != nil { 32 | return nil, err 33 | } 34 | result = append(result, envs...) 35 | } 36 | 37 | if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 { 38 | envs, err := impl.collectObjectStoreEnvs( 39 | ctx, 40 | types.NamespacedName{ 41 | Name: pluginConfiguration.RecoveryBarmanObjectName, 42 | Namespace: namespace, 43 | }, 44 | ) 45 | if err != nil { 46 | return nil, err 47 | } 48 | result = append(result, envs...) 49 | } 50 | 51 | if len(pluginConfiguration.ReplicaSourceBarmanObjectName) > 0 { 52 | envs, err := impl.collectObjectStoreEnvs( 53 | ctx, 54 | types.NamespacedName{ 55 | Name: pluginConfiguration.ReplicaSourceBarmanObjectName, 56 | Namespace: namespace, 57 | }, 58 | ) 59 | if err != nil { 60 | return nil, err 61 | } 62 | result = append(result, envs...) 63 | } 64 | 65 | return result, nil 66 | } 67 | 68 | func (impl LifecycleImplementation) collectObjectStoreEnvs( 69 | ctx context.Context, 70 | barmanObjectKey types.NamespacedName, 71 | ) ([]corev1.EnvVar, error) { 72 | var objectStore barmancloudv1.ObjectStore 73 | if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil { 74 | return nil, err 75 | } 76 | 77 | return objectStore.Spec.InstanceSidecarConfiguration.Env, nil 78 | } 79 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/lifecycle_resources.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | corev1 "k8s.io/api/core/v1" 7 | 8 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" 10 | ) 11 | 12 | func (impl LifecycleImplementation) collectSidecarResourcesForRecoveryJob( 13 | ctx context.Context, 14 | configuration *config.PluginConfiguration, 15 | ) (corev1.ResourceRequirements, error) { 16 | if len(configuration.RecoveryBarmanObjectName) > 0 { 17 | var barmanObjectStore barmancloudv1.ObjectStore 18 | if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil { 19 | return corev1.ResourceRequirements{}, err 20 | } 21 | 22 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil 23 | } 24 | 25 | return corev1.ResourceRequirements{}, nil 26 | } 27 | 28 | func (impl LifecycleImplementation) collectSidecarResourcesForPod( 29 | ctx context.Context, 30 | configuration *config.PluginConfiguration, 31 | ) (corev1.ResourceRequirements, error) { 32 | if len(configuration.BarmanObjectName) > 0 { 33 | // On a replica cluster that also archives, the designated primary 34 | // will use both the replica source object store and the object store 35 | // of the cluster. 36 | // In this case, we use the cluster object store for configuring 37 | // the resources of the sidecar container. 38 | 39 | var barmanObjectStore barmancloudv1.ObjectStore 40 | if err := impl.Client.Get(ctx, configuration.GetBarmanObjectKey(), &barmanObjectStore); err != nil { 41 | return corev1.ResourceRequirements{}, err 42 | } 43 | 44 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil 45 | } 46 | 47 | if len(configuration.RecoveryBarmanObjectName) > 0 { 48 | // On a replica cluster that doesn't archive, the designated primary 49 | // uses only the replica source object store. 50 | // In this case, we use the replica source object store for configuring 51 | // the resources of the sidecar container. 52 | var barmanObjectStore barmancloudv1.ObjectStore 53 | if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil { 54 | return corev1.ResourceRequirements{}, err 55 | } 56 | 57 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil 58 | } 59 | 60 | return corev1.ResourceRequirements{}, nil 61 | } 62 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/ownership.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "fmt" 5 | 6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | "k8s.io/apimachinery/pkg/runtime" 8 | "k8s.io/utils/ptr" 9 | ) 10 | 11 | // setOwnerReference explicitly set the owner reference between an 12 | // owner object and a controller one. 13 | // 14 | // Important: this function won't use any registered scheme and will 15 | // fail unless the metadata has been correctly set into the owner 16 | // object. 17 | func setOwnerReference(owner, controlled metav1.Object) error { 18 | ro, ok := owner.(runtime.Object) 19 | if !ok { 20 | return fmt.Errorf("%T is not a runtime.Object, cannot call setOwnerReference", owner) 21 | } 22 | 23 | if len(ro.DeepCopyObject().GetObjectKind().GroupVersionKind().Group) == 0 { 24 | return fmt.Errorf("%T metadata have not been set, cannot call setOwnerReference", owner) 25 | } 26 | 27 | controlled.SetOwnerReferences([]metav1.OwnerReference{ 28 | { 29 | APIVersion: ro.GetObjectKind().GroupVersionKind().GroupVersion().String(), 30 | Kind: ro.GetObjectKind().GroupVersionKind().Kind, 31 | Name: owner.GetName(), 32 | UID: owner.GetUID(), 33 | BlockOwnerDeletion: ptr.To(true), 34 | Controller: ptr.To(true), 35 | }, 36 | }) 37 | 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/specs/role.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | "fmt" 5 | 6 | cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" 7 | "github.com/cloudnative-pg/machinery/pkg/stringset" 8 | rbacv1 "k8s.io/api/rbac/v1" 9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | 11 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 12 | ) 13 | 14 | // BuildRole builds the Role object for this cluster 15 | func BuildRole( 16 | cluster *cnpgv1.Cluster, 17 | barmanObjects []barmancloudv1.ObjectStore, 18 | ) *rbacv1.Role { 19 | role := &rbacv1.Role{ 20 | ObjectMeta: metav1.ObjectMeta{ 21 | Namespace: cluster.Namespace, 22 | Name: GetRBACName(cluster.Name), 23 | }, 24 | 25 | Rules: []rbacv1.PolicyRule{}, 26 | } 27 | 28 | secretsSet := stringset.New() 29 | barmanObjectsSet := stringset.New() 30 | 31 | for _, barmanObject := range barmanObjects { 32 | barmanObjectsSet.Put(barmanObject.Name) 33 | for _, secret := range CollectSecretNamesFromCredentials(&barmanObject.Spec.Configuration.BarmanCredentials) { 34 | secretsSet.Put(secret) 35 | } 36 | } 37 | 38 | role.Rules = append( 39 | role.Rules, 40 | rbacv1.PolicyRule{ 41 | APIGroups: []string{ 42 | "barmancloud.cnpg.io", 43 | }, 44 | Verbs: []string{ 45 | "get", 46 | "watch", 47 | "list", 48 | }, 49 | Resources: []string{ 50 | "objectstores", 51 | }, 52 | ResourceNames: barmanObjectsSet.ToSortedList(), 53 | }, 54 | rbacv1.PolicyRule{ 55 | APIGroups: []string{ 56 | "barmancloud.cnpg.io", 57 | }, 58 | Verbs: []string{ 59 | "update", 60 | }, 61 | Resources: []string{ 62 | "objectstores/status", 63 | }, 64 | ResourceNames: barmanObjectsSet.ToSortedList(), 65 | }, 66 | rbacv1.PolicyRule{ 67 | APIGroups: []string{ 68 | "", 69 | }, 70 | Resources: []string{ 71 | "secrets", 72 | }, 73 | Verbs: []string{ 74 | "get", 75 | "watch", 76 | "list", 77 | }, 78 | ResourceNames: secretsSet.ToSortedList(), 79 | }, 80 | ) 81 | 82 | return role 83 | } 84 | 85 | // BuildRoleBinding builds the role binding object for this cluster 86 | func BuildRoleBinding( 87 | cluster *cnpgv1.Cluster, 88 | ) *rbacv1.RoleBinding { 89 | return &rbacv1.RoleBinding{ 90 | ObjectMeta: metav1.ObjectMeta{ 91 | Namespace: cluster.Namespace, 92 | Name: GetRBACName(cluster.Name), 93 | }, 94 | Subjects: []rbacv1.Subject{ 95 | { 96 | Kind: "ServiceAccount", 97 | APIGroup: "", 98 | Name: cluster.Name, 99 | Namespace: cluster.Namespace, 100 | }, 101 | }, 102 | RoleRef: rbacv1.RoleRef{ 103 | APIGroup: "rbac.authorization.k8s.io", 104 | Kind: "Role", 105 | Name: GetRBACName(cluster.Name), 106 | }, 107 | } 108 | } 109 | 110 | // GetRBACName returns the name of the RBAC entities for the 111 | // barman cloud plugin 112 | func GetRBACName(clusterName string) string { 113 | return fmt.Sprintf("%s-barman-cloud", clusterName) 114 | } 115 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/specs/secrets.go: -------------------------------------------------------------------------------- 1 | package specs 2 | 3 | import ( 4 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api" 5 | machineryapi "github.com/cloudnative-pg/machinery/pkg/api" 6 | ) 7 | 8 | // CollectSecretNamesFromCredentials collects the names of the secrets 9 | func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCredentials) []string { 10 | var references []*machineryapi.SecretKeySelector 11 | if barmanCredentials.AWS != nil { 12 | references = append( 13 | references, 14 | barmanCredentials.AWS.AccessKeyIDReference, 15 | barmanCredentials.AWS.SecretAccessKeyReference, 16 | barmanCredentials.AWS.RegionReference, 17 | barmanCredentials.AWS.SessionToken, 18 | ) 19 | } 20 | if barmanCredentials.Azure != nil { 21 | references = append( 22 | references, 23 | barmanCredentials.Azure.ConnectionString, 24 | barmanCredentials.Azure.StorageAccount, 25 | barmanCredentials.Azure.StorageKey, 26 | barmanCredentials.Azure.StorageSasToken, 27 | ) 28 | } 29 | if barmanCredentials.Google != nil { 30 | references = append( 31 | references, 32 | barmanCredentials.Google.ApplicationCredentials, 33 | ) 34 | } 35 | 36 | result := make([]string, 0, len(references)) 37 | for _, reference := range references { 38 | if reference == nil { 39 | continue 40 | } 41 | result = append(result, reference.Name) 42 | } 43 | 44 | // TODO: stringset belongs to machinery :( 45 | 46 | return result 47 | } 48 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/specs/specs.go: -------------------------------------------------------------------------------- 1 | // Package specs contains the specification of the kubernetes objects 2 | // that are created by the plugin 3 | package specs 4 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/start.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http" 7 | "github.com/cloudnative-pg/cnpg-i/pkg/lifecycle" 8 | "github.com/cloudnative-pg/cnpg-i/pkg/reconciler" 9 | "google.golang.org/grpc" 10 | "sigs.k8s.io/controller-runtime/pkg/client" 11 | ) 12 | 13 | // CNPGI is the implementation of the CNPG-i server 14 | type CNPGI struct { 15 | Client client.Client 16 | PluginPath string 17 | ServerCertPath string 18 | ServerKeyPath string 19 | ClientCertPath string 20 | ServerAddress string 21 | } 22 | 23 | // Start starts the GRPC server 24 | // of the operator plugin 25 | func (c *CNPGI) Start(ctx context.Context) error { 26 | enrich := func(server *grpc.Server) error { 27 | reconciler.RegisterReconcilerHooksServer(server, ReconcilerImplementation{ 28 | Client: c.Client, 29 | }) 30 | lifecycle.RegisterOperatorLifecycleServer(server, LifecycleImplementation{ 31 | Client: c.Client, 32 | }) 33 | return nil 34 | } 35 | 36 | srv := http.Server{ 37 | IdentityImpl: IdentityImplementation{}, 38 | Enrichers: []http.ServerEnricher{enrich}, 39 | PluginPath: c.PluginPath, 40 | ServerCertPath: c.ServerCertPath, 41 | ServerKeyPath: c.ServerKeyPath, 42 | ClientCertPath: c.ClientCertPath, 43 | ServerAddress: c.ServerAddress, 44 | } 45 | 46 | return srv.Start(ctx) 47 | } 48 | -------------------------------------------------------------------------------- /internal/cnpgi/operator/suite_test.go: -------------------------------------------------------------------------------- 1 | package operator 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo/v2" 7 | . "github.com/onsi/gomega" 8 | ) 9 | 10 | func TestOperator(t *testing.T) { 11 | RegisterFailHandler(Fail) 12 | RunSpecs(t, "Operator Suite") 13 | } 14 | -------------------------------------------------------------------------------- /internal/cnpgi/restore/doc.go: -------------------------------------------------------------------------------- 1 | // Package restore provides the restore functionality for CNPGI. 2 | package restore 3 | -------------------------------------------------------------------------------- /internal/cnpgi/restore/identity.go: -------------------------------------------------------------------------------- 1 | package restore 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity" 7 | "sigs.k8s.io/controller-runtime/pkg/client" 8 | 9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" 10 | ) 11 | 12 | // IdentityImplementation implements IdentityServer 13 | type IdentityImplementation struct { 14 | identity.UnimplementedIdentityServer 15 | BarmanObjectKey client.ObjectKey 16 | Client client.Client 17 | } 18 | 19 | // GetPluginMetadata implements IdentityServer 20 | func (i IdentityImplementation) GetPluginMetadata( 21 | _ context.Context, 22 | _ *identity.GetPluginMetadataRequest, 23 | ) (*identity.GetPluginMetadataResponse, error) { 24 | return &metadata.Data, nil 25 | } 26 | 27 | // GetPluginCapabilities implements IdentityServer 28 | func (i IdentityImplementation) GetPluginCapabilities( 29 | _ context.Context, 30 | _ *identity.GetPluginCapabilitiesRequest, 31 | ) (*identity.GetPluginCapabilitiesResponse, error) { 32 | return &identity.GetPluginCapabilitiesResponse{ 33 | Capabilities: []*identity.PluginCapability{ 34 | { 35 | Type: &identity.PluginCapability_Service_{ 36 | Service: &identity.PluginCapability_Service{ 37 | Type: identity.PluginCapability_Service_TYPE_RESTORE_JOB, 38 | }, 39 | }, 40 | }, 41 | { 42 | Type: &identity.PluginCapability_Service_{ 43 | Service: &identity.PluginCapability_Service{ 44 | Type: identity.PluginCapability_Service_TYPE_WAL_SERVICE, 45 | }, 46 | }, 47 | }, 48 | }, 49 | }, nil 50 | } 51 | 52 | // Probe implements IdentityServer 53 | func (i IdentityImplementation) Probe( 54 | _ context.Context, 55 | _ *identity.ProbeRequest, 56 | ) (*identity.ProbeResponse, error) { 57 | return &identity.ProbeResponse{ 58 | Ready: true, 59 | }, nil 60 | } 61 | -------------------------------------------------------------------------------- /internal/cnpgi/restore/manager.go: -------------------------------------------------------------------------------- 1 | package restore 2 | 3 | import ( 4 | "context" 5 | 6 | cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1" 7 | "github.com/cloudnative-pg/machinery/pkg/log" 8 | "github.com/spf13/viper" 9 | corev1 "k8s.io/api/core/v1" 10 | "k8s.io/apimachinery/pkg/runtime" 11 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 12 | clientgoscheme "k8s.io/client-go/kubernetes/scheme" 13 | ctrl "sigs.k8s.io/controller-runtime" 14 | "sigs.k8s.io/controller-runtime/pkg/client" 15 | 16 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 17 | ) 18 | 19 | var scheme = runtime.NewScheme() 20 | 21 | func init() { 22 | utilruntime.Must(barmancloudv1.AddToScheme(scheme)) 23 | utilruntime.Must(cnpgv1.AddToScheme(scheme)) 24 | utilruntime.Must(clientgoscheme.AddToScheme(scheme)) 25 | } 26 | 27 | // Start starts the sidecar informers and CNPG-i server 28 | func Start(ctx context.Context) error { 29 | setupLog := log.FromContext(ctx) 30 | setupLog.Info("Starting barman cloud instance plugin") 31 | 32 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ 33 | Scheme: scheme, 34 | Client: client.Options{ 35 | Cache: &client.CacheOptions{ 36 | DisableFor: []client.Object{ 37 | &corev1.Secret{}, 38 | &barmancloudv1.ObjectStore{}, 39 | }, 40 | }, 41 | }, 42 | }) 43 | if err != nil { 44 | setupLog.Error(err, "unable to start manager") 45 | return err 46 | } 47 | 48 | if err := mgr.Add(&CNPGI{ 49 | PluginPath: viper.GetString("plugin-path"), 50 | SpoolDirectory: viper.GetString("spool-directory"), 51 | Client: mgr.GetClient(), 52 | PGDataPath: viper.GetString("pgdata"), 53 | InstanceName: viper.GetString("pod-name"), 54 | }); err != nil { 55 | setupLog.Error(err, "unable to create CNPGI runnable") 56 | return err 57 | } 58 | 59 | if err := mgr.Start(ctx); err != nil { 60 | return err 61 | } 62 | 63 | return nil 64 | } 65 | -------------------------------------------------------------------------------- /internal/cnpgi/restore/start.go: -------------------------------------------------------------------------------- 1 | package restore 2 | 3 | import ( 4 | "context" 5 | "path" 6 | 7 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http" 8 | restore "github.com/cloudnative-pg/cnpg-i/pkg/restore/job" 9 | "github.com/cloudnative-pg/cnpg-i/pkg/wal" 10 | "google.golang.org/grpc" 11 | "sigs.k8s.io/controller-runtime/pkg/client" 12 | 13 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common" 14 | ) 15 | 16 | // CNPGI is the implementation of the PostgreSQL sidecar 17 | type CNPGI struct { 18 | PluginPath string 19 | SpoolDirectory string 20 | 21 | Client client.Client 22 | PGDataPath string 23 | InstanceName string 24 | } 25 | 26 | // Start starts the GRPC service 27 | func (c *CNPGI) Start(ctx context.Context) error { 28 | // PgWalVolumePgWalPath is the path of pg_wal directory inside the WAL volume when present 29 | const PgWalVolumePgWalPath = "/var/lib/postgresql/wal/pg_wal" 30 | 31 | enrich := func(server *grpc.Server) error { 32 | wal.RegisterWALServer(server, common.WALServiceImplementation{ 33 | InstanceName: c.InstanceName, 34 | Client: c.Client, 35 | SpoolDirectory: c.SpoolDirectory, 36 | PGDataPath: c.PGDataPath, 37 | PGWALPath: path.Join(c.PGDataPath, "pg_wal"), 38 | }) 39 | 40 | restore.RegisterRestoreJobHooksServer(server, &JobHookImpl{ 41 | Client: c.Client, 42 | SpoolDirectory: c.SpoolDirectory, 43 | PgDataPath: c.PGDataPath, 44 | PgWalFolderToSymlink: PgWalVolumePgWalPath, 45 | }) 46 | 47 | common.AddHealthCheck(server) 48 | 49 | return nil 50 | } 51 | 52 | srv := http.Server{ 53 | IdentityImpl: IdentityImplementation{}, 54 | Enrichers: []http.ServerEnricher{enrich}, 55 | PluginPath: c.PluginPath, 56 | } 57 | 58 | return srv.Start(ctx) 59 | } 60 | -------------------------------------------------------------------------------- /internal/controller/doc.go: -------------------------------------------------------------------------------- 1 | // Package controller implements a controller for the CRDs as defined 2 | // by this operator 3 | package controller 4 | -------------------------------------------------------------------------------- /internal/controller/objectstore_controller.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/cloudnative-pg/machinery/pkg/log" 24 | "k8s.io/apimachinery/pkg/runtime" 25 | ctrl "sigs.k8s.io/controller-runtime" 26 | "sigs.k8s.io/controller-runtime/pkg/client" 27 | 28 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 29 | ) 30 | 31 | // ObjectStoreReconciler reconciles a ObjectStore object. 32 | type ObjectStoreReconciler struct { 33 | client.Client 34 | Scheme *runtime.Scheme 35 | } 36 | 37 | // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=create;patch;update;get;list;watch 38 | // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=create;patch;update;get;list;watch 39 | // +kubebuilder:rbac:groups="",resources=secrets,verbs=create;list;get;watch;delete 40 | // +kubebuilder:rbac:groups=postgresql.cnpg.io,resources=backups,verbs=get;list;watch 41 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores,verbs=get;list;watch;create;update;patch;delete 42 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/status,verbs=get;update;patch 43 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/finalizers,verbs=update 44 | 45 | // Reconcile is part of the main kubernetes reconciliation loop which aims to 46 | // move the current state of the cluster closer to the desired state. 47 | // TODO(user): Modify the Reconcile function to compare the state specified by 48 | // the ObjectStore object against the actual cluster state, and then 49 | // perform operations to make the cluster state reflect the state specified by 50 | // the user. 51 | // 52 | // For more details, check Reconcile and its Result here: 53 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile 54 | func (r *ObjectStoreReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) { 55 | _ = log.FromContext(ctx) 56 | 57 | // TODO(user): your logic here 58 | 59 | return ctrl.Result{}, nil 60 | } 61 | 62 | // SetupWithManager sets up the controller with the Manager. 63 | func (r *ObjectStoreReconciler) SetupWithManager(mgr ctrl.Manager) error { 64 | err := ctrl.NewControllerManagedBy(mgr). 65 | For(&barmancloudv1.ObjectStore{}). 66 | Complete(r) 67 | if err != nil { 68 | return fmt.Errorf("unable to create controller: %w", err) 69 | } 70 | 71 | return nil 72 | } 73 | -------------------------------------------------------------------------------- /internal/controller/objectstore_controller_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | 22 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api" 23 | "k8s.io/apimachinery/pkg/api/errors" 24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | "sigs.k8s.io/controller-runtime/pkg/reconcile" 27 | 28 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 29 | 30 | . "github.com/onsi/ginkgo/v2" 31 | . "github.com/onsi/gomega" 32 | ) 33 | 34 | var _ = Describe("ObjectStore Controller", func() { 35 | Context("When reconciling a resource", func() { 36 | const resourceName = "test-resource" 37 | 38 | ctx := context.Background() 39 | 40 | typeNamespacedName := types.NamespacedName{ 41 | Name: resourceName, 42 | Namespace: "default", // TODO(user):Modify as needed 43 | } 44 | objectstore := &barmancloudv1.ObjectStore{} 45 | 46 | BeforeEach(func() { 47 | By("creating the custom resource for the Kind ObjectStore") 48 | err := k8sClient.Get(ctx, typeNamespacedName, objectstore) 49 | if err != nil && errors.IsNotFound(err) { 50 | resource := &barmancloudv1.ObjectStore{ 51 | ObjectMeta: metav1.ObjectMeta{ 52 | Name: resourceName, 53 | Namespace: "default", 54 | }, 55 | Spec: barmancloudv1.ObjectStoreSpec{ 56 | Configuration: barmanapi.BarmanObjectStoreConfiguration{DestinationPath: "/tmp"}, 57 | }, 58 | // TODO(user): Specify other spec details if needed. 59 | } 60 | Expect(k8sClient.Create(ctx, resource)).To(Succeed()) 61 | } 62 | }) 63 | 64 | AfterEach(func() { 65 | // TODO(user): Cleanup logic after each test, like removing the resource instance. 66 | resource := &barmancloudv1.ObjectStore{} 67 | err := k8sClient.Get(ctx, typeNamespacedName, resource) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | By("Cleanup the specific resource instance ObjectStore") 71 | Expect(k8sClient.Delete(ctx, resource)).To(Succeed()) 72 | }) 73 | It("should successfully reconcile the resource", func() { 74 | By("Reconciling the created resource") 75 | controllerReconciler := &ObjectStoreReconciler{ 76 | Client: k8sClient, 77 | Scheme: k8sClient.Scheme(), 78 | } 79 | 80 | _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{ 81 | NamespacedName: typeNamespacedName, 82 | }) 83 | Expect(err).NotTo(HaveOccurred()) 84 | // TODO(user): Add more specific assertions depending on your controller's reconciliation logic. 85 | // Example: If you expect a certain status condition after reconciliation, verify it here. 86 | }) 87 | }) 88 | }) 89 | -------------------------------------------------------------------------------- /internal/controller/suite_test.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package controller 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "path/filepath" 23 | "runtime" 24 | "testing" 25 | 26 | // +kubebuilder:scaffold:imports 27 | "k8s.io/client-go/kubernetes/scheme" 28 | "k8s.io/client-go/rest" 29 | "sigs.k8s.io/controller-runtime/pkg/client" 30 | "sigs.k8s.io/controller-runtime/pkg/envtest" 31 | logf "sigs.k8s.io/controller-runtime/pkg/log" 32 | "sigs.k8s.io/controller-runtime/pkg/log/zap" 33 | 34 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" 35 | 36 | . "github.com/onsi/ginkgo/v2" 37 | . "github.com/onsi/gomega" 38 | ) 39 | 40 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to 41 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. 42 | 43 | var ( 44 | cfg *rest.Config 45 | k8sClient client.Client 46 | testEnv *envtest.Environment 47 | ctx context.Context 48 | cancel context.CancelFunc 49 | ) 50 | 51 | func TestControllers(t *testing.T) { 52 | RegisterFailHandler(Fail) 53 | 54 | RunSpecs(t, "Controller Suite") 55 | } 56 | 57 | var _ = BeforeSuite(func() { 58 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) 59 | 60 | ctx, cancel = context.WithCancel(context.TODO()) 61 | 62 | By("bootstrapping test environment") 63 | testEnv = &envtest.Environment{ 64 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, 65 | ErrorIfCRDPathMissing: true, 66 | 67 | // The BinaryAssetsDirectory is only required if you want to run the tests directly 68 | // without call the makefile target test. If not informed it will look for the 69 | // default path defined in controller-runtime which is /usr/local/kubebuilder/. 70 | // Note that you must have the required binaries setup under the bin directory to perform 71 | // the tests directly. When we run make test it will be setup and used automatically. 72 | BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s", 73 | fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)), 74 | } 75 | 76 | var err error 77 | // cfg is defined in this file globally. 78 | cfg, err = testEnv.Start() 79 | Expect(err).NotTo(HaveOccurred()) 80 | Expect(cfg).NotTo(BeNil()) 81 | 82 | err = barmancloudv1.AddToScheme(scheme.Scheme) 83 | Expect(err).NotTo(HaveOccurred()) 84 | 85 | // +kubebuilder:scaffold:scheme 86 | 87 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) 88 | Expect(err).NotTo(HaveOccurred()) 89 | Expect(k8sClient).NotTo(BeNil()) 90 | }) 91 | 92 | var _ = AfterSuite(func() { 93 | By("tearing down the test environment") 94 | cancel() 95 | err := testEnv.Stop() 96 | Expect(err).NotTo(HaveOccurred()) 97 | }) 98 | -------------------------------------------------------------------------------- /kubernetes/certificate-issuer.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Issuer 3 | metadata: 4 | name: selfsigned-issuer 5 | spec: 6 | selfSigned: {} 7 | -------------------------------------------------------------------------------- /kubernetes/client-certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Certificate 3 | metadata: 4 | name: barman-cloud-client 5 | spec: 6 | secretName: barman-cloud-client-tls 7 | 8 | commonName: "barman-cloud-client" 9 | duration: 2160h # 90d 10 | renewBefore: 360h # 15d 11 | 12 | isCA: false 13 | usages: 14 | - client auth 15 | 16 | issuerRef: 17 | name: selfsigned-issuer 18 | kind: Issuer 19 | group: cert-manager.io 20 | -------------------------------------------------------------------------------- /kubernetes/deployment.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | labels: 5 | app: barman-cloud 6 | name: barman-cloud 7 | spec: 8 | replicas: 1 9 | selector: 10 | matchLabels: 11 | app: barman-cloud 12 | strategy: 13 | type: Recreate 14 | template: 15 | metadata: 16 | labels: 17 | app: barman-cloud 18 | spec: 19 | securityContext: 20 | runAsNonRoot: true 21 | seccompProfile: 22 | type: RuntimeDefault 23 | serviceAccountName: plugin-barman-cloud 24 | containers: 25 | - image: plugin-barman-cloud:latest 26 | name: barman-cloud 27 | ports: 28 | - containerPort: 9090 29 | protocol: TCP 30 | env: 31 | - name: SIDECAR_IMAGE 32 | valueFrom: 33 | secretKeyRef: 34 | key: SIDECAR_IMAGE 35 | name: plugin-barman-cloud 36 | args: 37 | - operator 38 | - --server-cert=/server/tls.crt 39 | - --server-key=/server/tls.key 40 | - --client-cert=/client/tls.crt 41 | - --server-address=:9090 42 | - --leader-elect 43 | - --log-level=debug 44 | readinessProbe: 45 | tcpSocket: 46 | port: 9090 47 | initialDelaySeconds: 10 48 | periodSeconds: 10 49 | volumeMounts: 50 | - mountPath: /server 51 | name: server 52 | - mountPath: /client 53 | name: client 54 | resources: {} 55 | securityContext: 56 | allowPrivilegeEscalation: false 57 | capabilities: 58 | drop: 59 | - ALL 60 | readOnlyRootFilesystem: true 61 | runAsGroup: 10001 62 | runAsUser: 10001 63 | seccompProfile: 64 | type: RuntimeDefault 65 | volumes: 66 | - name: server 67 | secret: 68 | secretName: barman-cloud-server-tls 69 | - name: client 70 | secret: 71 | secretName: barman-cloud-client-tls 72 | -------------------------------------------------------------------------------- /kubernetes/kustomization.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: kustomize.config.k8s.io/v1beta1 2 | kind: Kustomization 3 | namespace: cnpg-system 4 | resources: 5 | - certificate-issuer.yaml 6 | - client-certificate.yaml 7 | - deployment.yaml 8 | - server-certificate.yaml 9 | - service.yaml 10 | - ../config/crd 11 | - ../config/rbac 12 | images: 13 | - name: plugin-barman-cloud 14 | newName: ghcr.io/cloudnative-pg/plugin-barman-cloud-testing 15 | newTag: main 16 | secretGenerator: 17 | - literals: 18 | - SIDECAR_IMAGE=ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar-testing:main 19 | name: plugin-barman-cloud 20 | -------------------------------------------------------------------------------- /kubernetes/server-certificate.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: cert-manager.io/v1 2 | kind: Certificate 3 | metadata: 4 | name: barman-cloud-server 5 | spec: 6 | secretName: barman-cloud-server-tls 7 | commonName: barman-cloud 8 | dnsNames: 9 | - barman-cloud 10 | 11 | duration: 2160h # 90d 12 | renewBefore: 360h # 15d 13 | 14 | isCA: false 15 | usages: 16 | - server auth 17 | 18 | issuerRef: 19 | name: selfsigned-issuer 20 | kind: Issuer 21 | group: cert-manager.io 22 | -------------------------------------------------------------------------------- /kubernetes/service.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | labels: 5 | app: barman-cloud 6 | cnpg.io/pluginName: barman-cloud.cloudnative-pg.io 7 | annotations: 8 | cnpg.io/pluginClientSecret: barman-cloud-client-tls 9 | cnpg.io/pluginServerSecret: barman-cloud-server-tls 10 | cnpg.io/pluginPort: "9090" 11 | name: barman-cloud 12 | spec: 13 | ports: 14 | - port: 9090 15 | protocol: TCP 16 | targetPort: 9090 17 | selector: 18 | app: barman-cloud 19 | -------------------------------------------------------------------------------- /logo/cloudnativepg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/logo/cloudnativepg.png -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "changelog-path": "CHANGELOG.md", 3 | "release-type": "go", 4 | "bump-minor-pre-major": true, 5 | "bump-patch-for-minor-pre-major": false, 6 | "draft": false, 7 | "extra-files": [ 8 | "README.md", 9 | "internal/cnpgi/metadata/constants.go" 10 | ], 11 | "prerelease": false, 12 | "packages": { 13 | ".": {} 14 | }, 15 | "plugins": [ "sentence-case" ], 16 | "signoff": "Peggie ", 17 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" 18 | } 19 | -------------------------------------------------------------------------------- /scripts/cleanup.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | cd "$(dirname "$0")/.." || exit 6 | 7 | kubectl delete clusters --all 8 | kubectl delete backups --all 9 | kubectl exec -ti mc -- mc rm -r --force minio/backups -------------------------------------------------------------------------------- /scripts/minio-delete.sh: -------------------------------------------------------------------------------- 1 | kubectl exec -ti mc -- mc rm -r --force minio/backups 2 | -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | cd "$(dirname "$0")/.." || exit 6 | 7 | if [ -f .env ]; then 8 | source .env 9 | fi 10 | 11 | 12 | MYTMPDIR="$(mktemp -d)" 13 | trap 'rm -rf -- "$MYTMPDIR"' EXIT 14 | 15 | current_context=$(kubectl config view --raw -o json | jq -r '."current-context"' | sed "s/kind-//") 16 | operator_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local ko build -BP ./cmd/manager) 17 | instance_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local KO_DEFAULTBASEIMAGE="ghcr.io/cloudnative-pg/postgresql:17.0" ko build -BP ./cmd/manager) 18 | 19 | # Now we deploy the plugin inside the `cnpg-system` workspace 20 | ( 21 | cp -r kubernetes config "$MYTMPDIR" 22 | cd "$MYTMPDIR/kubernetes" 23 | kustomize edit set image "plugin-barman-cloud=$operator_image" 24 | kustomize edit set secret plugin-barman-cloud "--from-literal=SIDECAR_IMAGE=$instance_image" 25 | kubectl apply -k . 26 | ) 27 | -------------------------------------------------------------------------------- /test/e2e/internal/certmanager/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package certmanager provides utilities for setting up and managing 18 | // cert-manager for end-to-end testing. 19 | package certmanager 20 | -------------------------------------------------------------------------------- /test/e2e/internal/client/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package client provides function to create Kubernetes clients. 18 | package client 19 | -------------------------------------------------------------------------------- /test/e2e/internal/cloudnativepg/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package cloudnativepg provides utilities for setting up and managing 18 | // CloudNativePG environments for end-to-end testing. 19 | package cloudnativepg 20 | -------------------------------------------------------------------------------- /test/e2e/internal/cluster/cluster.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package cluster 18 | 19 | import ( 20 | v1 "github.com/cloudnative-pg/api/pkg/api/v1" 21 | ) 22 | 23 | // TODO: improve this with what we already have in CloudNativePG e2e. 24 | func IsReady(cluster v1.Cluster) bool { 25 | if cluster.Status.ReadyInstances != cluster.Spec.Instances { 26 | return false 27 | } 28 | for _, condition := range cluster.Status.Conditions { 29 | if condition.Type == string(v1.ConditionClusterReady) { 30 | return string(condition.Status) == string(v1.ConditionTrue) 31 | } 32 | } 33 | 34 | return false 35 | } 36 | -------------------------------------------------------------------------------- /test/e2e/internal/cluster/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package cluster contains functions to interact with the CloudNativePG clusters 18 | package cluster 19 | -------------------------------------------------------------------------------- /test/e2e/internal/command/command.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package command 18 | 19 | import ( 20 | "bytes" 21 | "context" 22 | "fmt" 23 | "time" 24 | 25 | "k8s.io/client-go/kubernetes" 26 | "k8s.io/client-go/rest" 27 | "k8s.io/client-go/tools/remotecommand" 28 | ) 29 | 30 | // TODO: extract this and the one in CloudNativePG to a common library 31 | 32 | // ContainerLocator is a struct that contains the information needed to locate a container in a pod. 33 | type ContainerLocator struct { 34 | NamespaceName string 35 | PodName string 36 | ContainerName string 37 | } 38 | 39 | // ExecuteInContainer executes a command in a container. If timeout is not nil, the command will be 40 | // executed with the specified timeout. The function returns the stdout and stderr of the command. 41 | func ExecuteInContainer( 42 | ctx context.Context, 43 | clientSet kubernetes.Clientset, 44 | cfg *rest.Config, 45 | container ContainerLocator, 46 | timeout *time.Duration, 47 | command []string, 48 | ) (string, string, error) { 49 | req := clientSet.CoreV1().RESTClient().Post(). 50 | Resource("pods"). 51 | Name(container.PodName). 52 | Namespace(container.NamespaceName). 53 | SubResource("exec"). 54 | Param("container", container.ContainerName). 55 | Param("stdout", "true"). 56 | Param("stderr", "true") 57 | for _, cmd := range command { 58 | req.Param("command", cmd) 59 | } 60 | 61 | newConfig := *cfg // local copy avoids modifying the passed config arg 62 | if timeout != nil { 63 | req.Timeout(*timeout) 64 | newConfig.Timeout = *timeout 65 | timedCtx, cancelFunc := context.WithTimeout(ctx, *timeout) 66 | defer cancelFunc() 67 | ctx = timedCtx 68 | } 69 | 70 | exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL()) 71 | if err != nil { 72 | return "", "", fmt.Errorf("error creating executor: %w", err) 73 | } 74 | 75 | var stdout, stderr bytes.Buffer 76 | err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{ 77 | Stdout: &stdout, 78 | Stderr: &stderr, 79 | }) 80 | if err != nil { 81 | return "", "", fmt.Errorf("error executing command in pod '%s/%s': %w", 82 | container.NamespaceName, container.PodName, err) 83 | } 84 | 85 | return stdout.String(), stderr.String(), nil 86 | } 87 | -------------------------------------------------------------------------------- /test/e2e/internal/command/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package command provides function to execute commands in k8s pods. 18 | package command 19 | -------------------------------------------------------------------------------- /test/e2e/internal/deployment/deployment.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package deployment 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "time" 23 | 24 | appsv1 "k8s.io/api/apps/v1" 25 | "k8s.io/apimachinery/pkg/types" 26 | "k8s.io/apimachinery/pkg/util/wait" 27 | "sigs.k8s.io/controller-runtime/pkg/client" 28 | ) 29 | 30 | // IsReady checks if the deployment is ready. 31 | func IsReady(ctx context.Context, cl client.Client, name types.NamespacedName) (bool, error) { 32 | deployment := &appsv1.Deployment{} 33 | err := cl.Get(ctx, name, deployment) 34 | if err != nil { 35 | return false, fmt.Errorf("failed to get %s deployment: %w", name, err) 36 | } 37 | 38 | // Check if the deployment is ready 39 | ready := false 40 | for _, condition := range deployment.Status.Conditions { 41 | if condition.Type == appsv1.DeploymentAvailable && condition.Status == "True" { 42 | ready = true 43 | break 44 | } 45 | } 46 | if !ready { 47 | return false, nil 48 | } 49 | 50 | return true, nil 51 | } 52 | 53 | // WaitForDeploymentReady waits for the deployment to be ready. ctx should have a timeout set. 54 | func WaitForDeploymentReady( 55 | ctx context.Context, cl client.Client, namespacedName types.NamespacedName, interval time.Duration, 56 | ) error { 57 | err := wait.PollUntilContextCancel(ctx, interval, false, 58 | func(ctx context.Context) (bool, error) { 59 | ready, err := IsReady(ctx, cl, namespacedName) 60 | if err != nil { 61 | return false, fmt.Errorf("failed to check if %s is ready: %w", namespacedName, err) 62 | } 63 | if ready { 64 | return true, nil 65 | } 66 | 67 | return false, nil 68 | }) 69 | if err != nil { 70 | return fmt.Errorf("failed to wait for %s to be ready: %w", namespacedName, err) 71 | } 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /test/e2e/internal/deployment/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package deployment provides utilities for managing Kubernetes deployments 18 | package deployment 19 | -------------------------------------------------------------------------------- /test/e2e/internal/e2etestenv/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package e2etestenv provides a test environment for end-to-end tests. 18 | package e2etestenv 19 | -------------------------------------------------------------------------------- /test/e2e/internal/kustomize/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package kustomize provides utilities for applying and managing Kubernetes 18 | // customizations using Kustomize. 19 | package kustomize 20 | -------------------------------------------------------------------------------- /test/e2e/internal/namespace/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package namespace provides utilities to manage namespaces. 18 | package namespace 19 | -------------------------------------------------------------------------------- /test/e2e/internal/namespace/namespace.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package namespace 18 | 19 | import ( 20 | "context" 21 | "crypto/rand" 22 | "fmt" 23 | "math/big" 24 | 25 | corev1 "k8s.io/api/core/v1" 26 | apierrors "k8s.io/apimachinery/pkg/api/errors" 27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 | "sigs.k8s.io/controller-runtime/pkg/client" 29 | ) 30 | 31 | // CreateUniqueNamespace creates a namespace with an unique suffix. 32 | func CreateUniqueNamespace(ctx context.Context, cl client.Client, prefix string) (*corev1.Namespace, error) { 33 | for { 34 | randInt, err := rand.Int(rand.Reader, big.NewInt(100000)) 35 | if err != nil { 36 | return nil, fmt.Errorf("failed to generate random number: %w", err) 37 | } 38 | namespaceName := fmt.Sprintf("%s-%d", prefix, randInt) 39 | namespace := &corev1.Namespace{ 40 | ObjectMeta: metav1.ObjectMeta{ 41 | Name: namespaceName, 42 | }, 43 | } 44 | 45 | err = cl.Create(ctx, namespace) 46 | if err == nil { 47 | return namespace, nil 48 | } 49 | if !apierrors.IsAlreadyExists(err) { 50 | return nil, fmt.Errorf("failed to create namespace: %w", err) 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /test/e2e/internal/objectstore/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package objectstore provides shared examples for object store resources. 18 | package objectstore 19 | -------------------------------------------------------------------------------- /test/e2e/internal/objectstore/objectstore.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | package objectstore 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | appsv1 "k8s.io/api/apps/v1" 24 | corev1 "k8s.io/api/core/v1" 25 | "sigs.k8s.io/controller-runtime/pkg/client" 26 | ) 27 | 28 | const ( 29 | // DefaultSize is the default size of the PVCs for the object stores. 30 | DefaultSize = "1Gi" 31 | ) 32 | 33 | // Resources represents the resources required to create an object store. 34 | type Resources struct { 35 | Deployment *appsv1.Deployment 36 | Service *corev1.Service 37 | Secret *corev1.Secret 38 | PVC *corev1.PersistentVolumeClaim 39 | } 40 | 41 | // Create creates the object store resources. 42 | func (osr Resources) Create(ctx context.Context, cl client.Client) error { 43 | if osr.PVC != nil { 44 | if err := cl.Create(ctx, osr.PVC); err != nil { 45 | return fmt.Errorf("failed to create PVC: %w", err) 46 | } 47 | } 48 | if osr.Secret != nil { 49 | if err := cl.Create(ctx, osr.Secret); err != nil { 50 | return fmt.Errorf("failed to create secret: %w", err) 51 | } 52 | } 53 | if osr.Deployment != nil { 54 | if err := cl.Create(ctx, osr.Deployment); err != nil { 55 | return fmt.Errorf("failed to create deployment: %w", err) 56 | } 57 | } 58 | if osr.Service != nil { 59 | if err := cl.Create(ctx, osr.Service); err != nil { 60 | return fmt.Errorf("failed to create service: %w", err) 61 | } 62 | } 63 | 64 | return nil 65 | } 66 | -------------------------------------------------------------------------------- /test/e2e/internal/tests/backup/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package backup contains tests for the backup and restore functionality 18 | // of the Barman Cloud Plugin. 19 | package backup 20 | -------------------------------------------------------------------------------- /test/e2e/internal/tests/replicacluster/doc.go: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright 2024. 3 | 4 | Licensed under the Apache License, Version 2.0 (the "License"); 5 | you may not use this file except in compliance with the License. 6 | You may obtain a copy of the License at 7 | 8 | http://www.apache.org/licenses/LICENSE-2.0 9 | 10 | Unless required by applicable law or agreed to in writing, software 11 | distributed under the License is distributed on an "AS IS" BASIS, 12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | See the License for the specific language governing permissions and 14 | limitations under the License. 15 | */ 16 | 17 | // Package replicacluster contains tests validating replica clusters 18 | // using the Barman Cloud Plugin. 19 | package replicacluster 20 | -------------------------------------------------------------------------------- /test/e2e/kustomize/config: -------------------------------------------------------------------------------- 1 | ../../../config/ -------------------------------------------------------------------------------- /test/e2e/kustomize/kubernetes: -------------------------------------------------------------------------------- 1 | ../../../kubernetes/ -------------------------------------------------------------------------------- /web/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | package-lock.json 22 | -------------------------------------------------------------------------------- /web/README.md: -------------------------------------------------------------------------------- 1 | # Website 2 | 3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern 4 | static website generator. 5 | 6 | ### Requirements 7 | 8 | - Docker 9 | - [Yarn](https://yarnpkg.com/) 10 | - [Dagger](https://dagger.io/) 11 | - [Task](https://taskfile.dev/) 12 | 13 | ### Installation 14 | 15 | ```shell 16 | $ yarn 17 | ``` 18 | 19 | ### Local Development 20 | 21 | ```shell 22 | $ yarn start 23 | ``` 24 | 25 | This command starts a local development server and opens up a browser window. 26 | Most changes are reflected live without having to restart the server. 27 | 28 | ### Build 29 | 30 | ```shell 31 | $ yarn build 32 | ``` 33 | 34 | This command generates static content into the `build` directory and can be 35 | served using any static contents hosting service. 36 | 37 | ### Test the build 38 | 39 | ```shell 40 | $ yarn serve 41 | ``` 42 | 43 | By default, this will load your site at http://localhost:3000/. 44 | 45 | ### Spellchecking 46 | 47 | From the top directory: 48 | 49 | ```shell 50 | task spellcheck 51 | ``` 52 | 53 | ### Versioning 54 | 55 | Docusaurus allows versioning of the documentation to maintain separate sets of 56 | documentation for different software versions. 57 | 58 | To create a new documentation version: 59 | 60 | ```shell 61 | $ yarn docusaurus docs:version X.Y.Z 62 | ``` 63 | -------------------------------------------------------------------------------- /web/docs/compression.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 80 3 | --- 4 | 5 | # Compression 6 | 7 | 8 | 9 | By default, backups and WAL files are archived **uncompressed**. However, the 10 | Barman Cloud Plugin supports multiple compression algorithms via 11 | `barman-cloud-backup` and `barman-cloud-wal-archive`, allowing you to optimize 12 | for space, speed, or a balance of both. 13 | 14 | ### Supported Compression Algorithms 15 | 16 | - `bzip2` 17 | - `gzip` 18 | - `lz4` (WAL only) 19 | - `snappy` 20 | - `xz` (WAL only) 21 | - `zstd` (WAL only) 22 | 23 | Compression settings for base backups and WAL archives are configured 24 | independently. For implementation details, refer to the corresponding API 25 | definitions: 26 | 27 | - [`DataBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#DataBackupConfiguration) 28 | - [`WALBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#WalBackupConfiguration) 29 | 30 | :::important 31 | Compression impacts both performance and storage efficiency. Choose the right 32 | algorithm based on your recovery time objectives (RTO), storage capacity, and 33 | network throughput. 34 | ::: 35 | 36 | ## Compression Benchmark (on MinIO) 37 | 38 | | Compression | Backup Time (ms) | Restore Time (ms) | Uncompressed Size (MB) | Compressed Size (MB) | Ratio | 39 | | ----------- | ---------------- | ----------------- | ---------------------- | -------------------- | ----- | 40 | | None | 10,927 | 7,553 | 395 | 395 | 1.0:1 | 41 | | bzip2 | 25,404 | 13,886 | 395 | 67 | 5.9:1 | 42 | | gzip | 116,281 | 3,077 | 395 | 91 | 4.3:1 | 43 | | snappy | 8,134 | 8,341 | 395 | 166 | 2.4:1 | 44 | -------------------------------------------------------------------------------- /web/docs/images.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 99 3 | --- 4 | 5 | # Container Images 6 | 7 | 8 | 9 | The Barman Cloud Plugin is distributed using two container images: 10 | 11 | - One for deploying the plugin components 12 | - One for the sidecar that runs alongside each PostgreSQL instance in a 13 | CloudNativePG `Cluster` using the plugin 14 | 15 | ## Plugin Container Image 16 | 17 | The plugin image contains the logic required to operate the Barman Cloud Plugin 18 | within your Kubernetes environment with CloudNativePG. It is published on the 19 | GitHub Container Registry at `ghcr.io/cloudnative-pg/plugin-barman-cloud`. 20 | 21 | This image is built from the 22 | [`Dockerfile.plugin`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.plugin) 23 | in the plugin repository. 24 | 25 | ## Sidecar Container Image 26 | 27 | The sidecar image is used within each PostgreSQL pod in the cluster. It 28 | includes the latest supported version of Barman Cloud and is responsible for 29 | performing WAL archiving and backups on behalf of CloudNativePG. 30 | 31 | It is available at `ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar` and is 32 | built from the 33 | [`Dockerfile.sidecar`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.sidecar). 34 | 35 | These sidecar images are designed to work seamlessly with the 36 | [`minimal` PostgreSQL container images](https://github.com/cloudnative-pg/postgres-containers?tab=readme-ov-file#minimal-images) 37 | maintained by the CloudNativePG Community. 38 | -------------------------------------------------------------------------------- /web/docs/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | sidebar_label: "Introduction" 4 | --- 5 | 6 | # Barman Cloud Plugin 7 | 8 | 9 | 10 | The **Barman Cloud Plugin** for [CloudNativePG](https://cloudnative-pg.io/) 11 | enables online continuous physical backups of PostgreSQL clusters to object storage 12 | using the `barman-cloud` suite from the [Barman](https://docs.pgbarman.org/release/latest/) 13 | project. 14 | 15 | :::important 16 | If you plan to migrate your existing CloudNativePG cluster to the new 17 | plugin-based approach using the Barman Cloud Plugin, see 18 | ["Migrating from Built-in CloudNativePG Backup"](migration.md) 19 | for detailed instructions. 20 | ::: 21 | 22 | ## Requirements 23 | 24 | To use the Barman Cloud Plugin, you need: 25 | 26 | - [CloudNativePG](https://cloudnative-pg.io) version **1.26** 27 | - [cert-manager](https://cert-manager.io/) to enable TLS communication between 28 | the plugin and the operator 29 | 30 | ## Key Features 31 | 32 | This plugin provides the following capabilities: 33 | 34 | - Physical online backup of the data directory 35 | - Physical restore of the data directory 36 | - Write-Ahead Log (WAL) archiving 37 | - WAL restore 38 | - Full cluster recovery 39 | - Point-in-Time Recovery (PITR) 40 | - Seamless integration with replica clusters for bootstrap and WAL restore from archive 41 | 42 | :::important 43 | The Barman Cloud Plugin is designed to **replace the in-tree object storage support** 44 | previously provided via the `.spec.backup.barmanObjectStore` section in the 45 | `Cluster` resource. 46 | Backups created using the in-tree approach are fully supported and compatible 47 | with this plugin. 48 | ::: 49 | 50 | ## Supported Object Storage Providers 51 | 52 | The plugin works with all storage backends supported by `barman-cloud`, including: 53 | 54 | - **Amazon S3** 55 | - **Google Cloud Storage** 56 | - **Microsoft Azure Blob Storage** 57 | 58 | In addition, the following S3-compatible and simulator solutions have been 59 | tested and verified: 60 | 61 | - [MinIO](https://min.io/) – An S3-compatible storage solution 62 | - [Azurite](https://github.com/Azure/Azurite) – A simulator for Azure Blob Storage 63 | - [fake-gcs-server](https://github.com/fsouza/fake-gcs-server) – A simulator for Google Cloud Storage 64 | 65 | :::tip 66 | For more details, refer to [Object Store Providers](object_stores.md). 67 | ::: 68 | -------------------------------------------------------------------------------- /web/docs/misc.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 90 3 | --- 4 | 5 | # Miscellaneous 6 | 7 | 8 | 9 | ## Backup Object Tagging 10 | 11 | You can attach key-value metadata tags to backup artifacts—such as base 12 | backups, WAL files, and history files—via the `.spec.configuration` section of 13 | the `ObjectStore` resource. 14 | 15 | - `tags`: applied to base backups and WAL files 16 | - `historyTags`: applied to history files only 17 | 18 | ### Example 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | configuration: 27 | [...] 28 | tags: 29 | backupRetentionPolicy: "expire" 30 | historyTags: 31 | backupRetentionPolicy: "keep" 32 | [...] 33 | ``` 34 | 35 | ## Extra Options for Backup and WAL Archiving 36 | 37 | You can pass additional command-line arguments to `barman-cloud-backup` and 38 | `barman-cloud-wal-archive` using the `additionalCommandArgs` field in the 39 | `ObjectStore` configuration. 40 | 41 | - `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup` 42 | - `.spec.configuration.wal.additionalCommandArgs`: for `barman-cloud-wal-archive` 43 | 44 | Each field accepts a list of string arguments. If an argument is already 45 | configured elsewhere in the plugin, the duplicate will be ignored. 46 | 47 | ### Example: Extra Backup Options 48 | 49 | ```yaml 50 | kind: ObjectStore 51 | metadata: 52 | name: my-store 53 | spec: 54 | configuration: 55 | data: 56 | additionalCommandArgs: 57 | - "--min-chunk-size=5MB" 58 | - "--read-timeout=60" 59 | ``` 60 | 61 | ### Example: Extra WAL Archive Options 62 | 63 | ```yaml 64 | kind: ObjectStore 65 | metadata: 66 | name: my-store 67 | spec: 68 | configuration: 69 | wal: 70 | additionalCommandArgs: 71 | - "--max-concurrency=1" 72 | - "--read-timeout=60" 73 | ``` 74 | 75 | For a complete list of supported options, refer to the 76 | [official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/). 77 | -------------------------------------------------------------------------------- /web/docs/parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 100 3 | --- 4 | 5 | # Parameters 6 | 7 | 8 | 9 | The following parameters are available for the Barman Cloud Plugin: 10 | 11 | - `barmanObjectName`: references the `ObjectStore` resource to be used by the 12 | plugin. 13 | - `serverName`: Specifies the server name in the object store. 14 | 15 | :::important 16 | The `serverName` parameter in the `ObjectStore` resource is retained solely for 17 | API compatibility with the in-tree `barmanObjectStore` and must always be left empty. 18 | When needed, use the `serverName` plugin parameter in the Cluster configuration instead. 19 | ::: 20 | -------------------------------------------------------------------------------- /web/docs/retention.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 60 3 | --- 4 | 5 | # Retention Policies 6 | 7 | 8 | 9 | The Barman Cloud Plugin supports **automated cleanup of obsolete backups** via 10 | retention policies, configured in the `.spec.retentionPolicy` field of the 11 | `ObjectStore` resource. 12 | 13 | :::note 14 | This feature uses the `barman-cloud-backup-delete` command with the 15 | `--retention-policy "RECOVERY WINDOW OF {{ value }} {{ unit }}"` syntax. 16 | ::: 17 | 18 | #### Example: 30-Day Retention Policy 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | [...] 27 | retentionPolicy: "30d" 28 | ```` 29 | 30 | :::note 31 | A **recovery window retention policy** ensures the cluster can be restored to 32 | any point in time between the calculated *Point of Recoverability* (PoR) and 33 | the latest WAL archive. The PoR is defined as `current time - recovery window`. 34 | The **first valid backup** is the most recent backup completed before the PoR. 35 | Backups older than that are marked as *obsolete* and deleted after the next 36 | backup completes. 37 | ::: 38 | 39 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids", 15 | "typecheck": "tsc" 16 | }, 17 | "dependencies": { 18 | "@docusaurus/core": "3.8.0", 19 | "@docusaurus/preset-classic": "3.8.0", 20 | "@easyops-cn/docusaurus-search-local": "^0.50.0", 21 | "@mdx-js/react": "^3.0.0", 22 | "clsx": "^2.0.0", 23 | "prism-react-renderer": "^2.3.0", 24 | "react": "^19.0.0", 25 | "react-dom": "^19.0.0" 26 | }, 27 | "devDependencies": { 28 | "@docusaurus/module-type-aliases": "3.8.0", 29 | "@docusaurus/tsconfig": "3.8.0", 30 | "@docusaurus/types": "3.8.0", 31 | "typescript": "~5.8.0" 32 | }, 33 | "browserslist": { 34 | "production": [ 35 | ">0.5%", 36 | "not dead", 37 | "not op_mini all" 38 | ], 39 | "development": [ 40 | "last 3 chrome version", 41 | "last 3 firefox version", 42 | "last 5 safari version" 43 | ] 44 | }, 45 | "engines": { 46 | "node": ">=18.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /web/sidebars.ts: -------------------------------------------------------------------------------- 1 | import type {SidebarsConfig} from '@docusaurus/plugin-content-docs'; 2 | 3 | // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) 4 | 5 | /** 6 | * Creating a sidebar enables you to: 7 | - create an ordered group of docs 8 | - render a sidebar for each doc of that group 9 | - provide next/previous navigation 10 | 11 | The sidebars can be generated from the filesystem, or explicitly defined here. 12 | 13 | Create as many sidebars as you want. 14 | */ 15 | 16 | // Export the combined sidebars 17 | export default { 18 | // The key 'documentation' is the sidebarId referenced in navbar 19 | docs: [{type: 'autogenerated', dirName: '.'}], 20 | }; 21 | -------------------------------------------------------------------------------- /web/src/components/HomepageFeatures/feature.tsx: -------------------------------------------------------------------------------- 1 | import type {ComponentProps, ComponentType, ReactElement} from "react"; 2 | import clsx from "clsx"; 3 | import styles from "@site/src/components/HomepageFeatures/styles.module.css"; 4 | import Heading from "@theme/Heading"; 5 | 6 | type FeatureItem = { 7 | title: string; 8 | Svg: ComponentType>; 9 | description: string; 10 | }; 11 | 12 | function Feature({title, Svg, description}: FeatureItem): ReactElement { 13 | return ( 14 |
15 |
16 | 17 |
18 |
19 | {title} 20 |

{description}

21 |
22 |
23 | ); 24 | } 25 | 26 | export function FeatureList(): ReactElement { 27 | return ( 28 |
29 | 36 | 42 | 47 |
48 | ) 49 | } 50 | -------------------------------------------------------------------------------- /web/src/components/HomepageFeatures/index.tsx: -------------------------------------------------------------------------------- 1 | import type {ReactElement} from 'react'; 2 | import styles from './styles.module.css'; 3 | import {FeatureList} from './feature'; 4 | 5 | export default function HomepageFeatures(): ReactElement { 6 | return ( 7 |
8 |
9 | 10 |
11 |
12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /web/src/components/HomepageFeatures/styles.module.css: -------------------------------------------------------------------------------- 1 | .features { 2 | display: flex; 3 | align-items: center; 4 | padding: 2rem 0; 5 | width: 100%; 6 | } 7 | 8 | .featureSvg { 9 | height: 200px; 10 | width: 200px; 11 | } 12 | -------------------------------------------------------------------------------- /web/src/components/Installation/index.tsx: -------------------------------------------------------------------------------- 1 | import {ReactElement} from 'react'; 2 | import CodeBlock from '@theme/CodeBlock'; 3 | import {useCurrentVersion} from '@site/src/hooks/versions'; 4 | 5 | // InstallationSnippet is the kubectl incantation to install the lastest 6 | // available version of the Barman Cloud Plugin. 7 | export function InstallationSnippet(): ReactElement { 8 | const latest = useCurrentVersion('latestReleased'); 9 | return ( 10 | 11 | {`kubectl apply -f \\ 12 | https://github.com/cloudnative-pg/plugin-barman-cloud/releases/download/v${latest}/manifest.yaml`} 13 | 14 | ); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /web/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #e63054; 10 | --ifm-color-primary-dark: #d8244a; 11 | --ifm-color-primary-darker: #cc2044; 12 | --ifm-color-primary-darkest: #a81a38; 13 | --ifm-color-primary-light: #e94a6a; 14 | --ifm-color-primary-lighter: #eb5875; 15 | --ifm-color-primary-lightest: #f07d94; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #ff3054; 23 | --ifm-color-primary-dark: #ff1642; 24 | --ifm-color-primary-darker: #ff0638; 25 | --ifm-color-primary-darkest: #d4002d; 26 | --ifm-color-primary-light: #ff4a66; 27 | --ifm-color-primary-lighter: #ff5a74; 28 | --ifm-color-primary-lightest: #ff899c; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | 32 | /* Configuration for the GitHub link in the header */ 33 | .header-github-link:hover { 34 | opacity: 0.6; 35 | } 36 | 37 | .header-github-link:before { 38 | content: ''; 39 | width: 24px; 40 | height: 24px; 41 | display: flex; 42 | background: url("/img/github-mark.svg") no-repeat ; 43 | background-size: 24px 24px; 44 | } 45 | 46 | html[data-theme='dark'] .header-github-link:before { 47 | background: url("/img/github-mark-white.svg") no-repeat; 48 | background-size: 24px 24px; 49 | } 50 | -------------------------------------------------------------------------------- /web/src/hooks/versions.ts: -------------------------------------------------------------------------------- 1 | import {useActiveVersion, useLatestVersion, useVersions} from '@docusaurus/plugin-content-docs/client'; 2 | 3 | 4 | export function useCurrentVersion(fallback: 'latest' | 'latestReleased' = 'latest'): string { 5 | switch (fallback) { 6 | case 'latestReleased': 7 | return useLatestReleasedVersion(); 8 | case 'latest': { 9 | const version = useActiveVersion('default'); 10 | return version?.name ?? useLatestVersion('default')?.name; 11 | } 12 | default: 13 | // The following line ensures that if `fallback` is not 'latest' or 'latestReleased', 14 | // an error is thrown. This can be useful for catching unexpected states. 15 | throw new Error(`Unhandled fallback type: ${fallback}`); 16 | } 17 | } 18 | 19 | export function useLatestReleasedVersion(): string { 20 | const allVersions = useVersions('default'); 21 | 22 | // Filter out "current" to only consider versioned docs 23 | const versionedDocs = allVersions.filter(version => version.name !== 'current'); 24 | 25 | // Handle the case where no versioned documents are found 26 | if (versionedDocs.length === 0) { 27 | return "unknown_version"; 28 | } 29 | 30 | const sortedVersions = versionedDocs.sort((a, b) => { 31 | return b.name.localeCompare(a.name, undefined, { numeric: true, sensitivity: 'base' }); 32 | }); 33 | 34 | // The latest version is the first in the sorted list since versionedDocs was not empty, 35 | return sortedVersions[0].name; 36 | } 37 | -------------------------------------------------------------------------------- /web/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | -------------------------------------------------------------------------------- /web/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | import type {ReactElement, ReactNode} from 'react'; 2 | import clsx from 'clsx'; 3 | import Link from '@docusaurus/Link'; 4 | import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; 5 | import Layout from '@theme/Layout'; 6 | import HomepageFeatures from '@site/src/components/HomepageFeatures'; 7 | import Heading from '@theme/Heading'; 8 | 9 | import styles from './index.module.css'; 10 | 11 | function HomepageHeader(): ReactElement { 12 | const { siteConfig } = useDocusaurusContext(); 13 | return ( 14 |
15 |
16 | 17 | {siteConfig.title} 18 | 19 |
20 |
21 | ); 22 | } 23 | 24 | export default function Home(): ReactElement { 25 | const {siteConfig} = useDocusaurusContext(); 26 | return ( 27 | 30 | 31 |
32 | 33 |
34 |
35 | ); 36 | } 37 | -------------------------------------------------------------------------------- /web/static/.nojekyll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/web/static/.nojekyll -------------------------------------------------------------------------------- /web/static/img/cloudnativepg-landscape-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/web/static/img/cloudnativepg-landscape-white.png -------------------------------------------------------------------------------- /web/static/img/cloudnativepg-social-card.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/web/static/img/cloudnativepg-social-card.png -------------------------------------------------------------------------------- /web/static/img/cloudnativepg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/web/static/img/cloudnativepg.png -------------------------------------------------------------------------------- /web/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/web/static/img/favicon.ico -------------------------------------------------------------------------------- /web/static/img/github-mark-white.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/static/img/github-mark.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // This file is not used in compilation. It is here just for a nice editor experience. 3 | "extends": "@docusaurus/tsconfig", 4 | "compilerOptions": { 5 | "baseUrl": ".", 6 | "strict": true 7 | }, 8 | "exclude": [ 9 | ".docusaurus", 10 | "build" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.3.0/welcome.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | sidebar_label: "Welcome" 4 | --- 5 | 6 | # Barman Cloud CNPG-I plugin 7 | 8 | **Status:** EXPERIMENTAL 9 | 10 | Welcome to the documentation of the [barman-cloud](https://pgbarman.org/) CNPG-I 11 | plugin for [CloudNativePG](https://cloudnative-pg.io/). 12 | 13 | Documentation for version **0.3.0** of the plugin is available in the 14 | [README](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/v0.3.0/README.md) 15 | on GitHub. 16 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/compression.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 80 3 | --- 4 | 5 | # Compression 6 | 7 | 8 | 9 | By default, backups and WAL files are archived **uncompressed**. However, the 10 | Barman Cloud Plugin supports multiple compression algorithms via 11 | `barman-cloud-backup` and `barman-cloud-wal-archive`, allowing you to optimize 12 | for space, speed, or a balance of both. 13 | 14 | ### Supported Compression Algorithms 15 | 16 | - `bzip2` 17 | - `gzip` 18 | - `lz4` (WAL only) 19 | - `snappy` 20 | - `xz` (WAL only) 21 | - `zstd` (WAL only) 22 | 23 | Compression settings for base backups and WAL archives are configured 24 | independently. For implementation details, refer to the corresponding API 25 | definitions: 26 | 27 | - [`DataBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#DataBackupConfiguration) 28 | - [`WALBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#WalBackupConfiguration) 29 | 30 | :::important 31 | Compression impacts both performance and storage efficiency. Choose the right 32 | algorithm based on your recovery time objectives (RTO), storage capacity, and 33 | network throughput. 34 | ::: 35 | 36 | ## Compression Benchmark (on MinIO) 37 | 38 | | Compression | Backup Time (ms) | Restore Time (ms) | Uncompressed Size (MB) | Compressed Size (MB) | Ratio | 39 | | ----------- | ---------------- | ----------------- | ---------------------- | -------------------- | ----- | 40 | | None | 10,927 | 7,553 | 395 | 395 | 1.0:1 | 41 | | bzip2 | 25,404 | 13,886 | 395 | 67 | 5.9:1 | 42 | | gzip | 116,281 | 3,077 | 395 | 91 | 4.3:1 | 43 | | snappy | 8,134 | 8,341 | 395 | 166 | 2.4:1 | 44 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/images.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 99 3 | --- 4 | 5 | # Container Images 6 | 7 | 8 | 9 | The Barman Cloud Plugin is distributed using two container images: 10 | 11 | - One for deploying the plugin components 12 | - One for the sidecar that runs alongside each PostgreSQL instance in a 13 | CloudNativePG `Cluster` using the plugin 14 | 15 | ## Plugin Container Image 16 | 17 | The plugin image contains the logic required to operate the Barman Cloud Plugin 18 | within your Kubernetes environment with CloudNativePG. It is published on the 19 | GitHub Container Registry at `ghcr.io/cloudnative-pg/plugin-barman-cloud`. 20 | 21 | This image is built from the 22 | [`Dockerfile.plugin`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.plugin) 23 | in the plugin repository. 24 | 25 | ## Sidecar Container Image 26 | 27 | The sidecar image is used within each PostgreSQL pod in the cluster. It 28 | includes the latest supported version of Barman Cloud and is responsible for 29 | performing WAL archiving and backups on behalf of CloudNativePG. 30 | 31 | It is available at `ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar` and is 32 | built from the 33 | [`Dockerfile.sidecar`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.sidecar). 34 | 35 | These sidecar images are designed to work seamlessly with the 36 | [`minimal` PostgreSQL container images](https://github.com/cloudnative-pg/postgres-containers?tab=readme-ov-file#minimal-images) 37 | maintained by the CloudNativePG Community. 38 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | sidebar_label: "Introduction" 4 | --- 5 | 6 | # Barman Cloud Plugin 7 | 8 | 9 | 10 | The **Barman Cloud Plugin** for [CloudNativePG](https://cloudnative-pg.io/) 11 | enables online continuous physical backups of PostgreSQL clusters to object storage 12 | using the `barman-cloud` suite from the [Barman](https://docs.pgbarman.org/release/latest/) 13 | project. 14 | 15 | :::important 16 | If you plan to migrate your existing CloudNativePG cluster to the new 17 | plugin-based approach using the Barman Cloud Plugin, see 18 | ["Migrating from Built-in CloudNativePG Backup"](migration.md) 19 | for detailed instructions. 20 | ::: 21 | 22 | ## Requirements 23 | 24 | To use the Barman Cloud Plugin, you need: 25 | 26 | - [CloudNativePG](https://cloudnative-pg.io) version **1.26** 27 | - [cert-manager](https://cert-manager.io/) to enable TLS communication between 28 | the plugin and the operator 29 | 30 | ## Key Features 31 | 32 | This plugin provides the following capabilities: 33 | 34 | - Physical online backup of the data directory 35 | - Physical restore of the data directory 36 | - Write-Ahead Log (WAL) archiving 37 | - WAL restore 38 | - Full cluster recovery 39 | - Point-in-Time Recovery (PITR) 40 | - Seamless integration with replica clusters for bootstrap and WAL restore from archive 41 | 42 | :::important 43 | The Barman Cloud Plugin is designed to **replace the in-tree object storage support** 44 | previously provided via the `.spec.backup.barmanObjectStore` section in the 45 | `Cluster` resource. 46 | Backups created using the in-tree approach are fully supported and compatible 47 | with this plugin. 48 | ::: 49 | 50 | ## Supported Object Storage Providers 51 | 52 | The plugin works with all storage backends supported by `barman-cloud`, including: 53 | 54 | - **Amazon S3** 55 | - **Google Cloud Storage** 56 | - **Microsoft Azure Blob Storage** 57 | 58 | In addition, the following S3-compatible and simulator solutions have been 59 | tested and verified: 60 | 61 | - [MinIO](https://min.io/) – An S3-compatible storage solution 62 | - [Azurite](https://github.com/Azure/Azurite) – A simulator for Azure Blob Storage 63 | - [fake-gcs-server](https://github.com/fsouza/fake-gcs-server) – A simulator for Google Cloud Storage 64 | 65 | :::tip 66 | For more details, refer to [Object Store Providers](object_stores.md). 67 | ::: 68 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/misc.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 90 3 | --- 4 | 5 | # Miscellaneous 6 | 7 | 8 | 9 | ## Backup Object Tagging 10 | 11 | You can attach key-value metadata tags to backup artifacts—such as base 12 | backups, WAL files, and history files—via the `.spec.configuration` section of 13 | the `ObjectStore` resource. 14 | 15 | - `tags`: applied to base backups and WAL files 16 | - `historyTags`: applied to history files only 17 | 18 | ### Example 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | configuration: 27 | [...] 28 | tags: 29 | backupRetentionPolicy: "expire" 30 | historyTags: 31 | backupRetentionPolicy: "keep" 32 | [...] 33 | ``` 34 | 35 | ## Extra Options for Backup and WAL Archiving 36 | 37 | You can pass additional command-line arguments to `barman-cloud-backup` and 38 | `barman-cloud-wal-archive` using the `additionalCommandArgs` field in the 39 | `ObjectStore` configuration. 40 | 41 | - `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup` 42 | - `.spec.configuration.wal.additionalCommandArgs`: for `barman-cloud-wal-archive` 43 | 44 | Each field accepts a list of string arguments. If an argument is already 45 | configured elsewhere in the plugin, the duplicate will be ignored. 46 | 47 | ### Example: Extra Backup Options 48 | 49 | ```yaml 50 | kind: ObjectStore 51 | metadata: 52 | name: my-store 53 | spec: 54 | configuration: 55 | data: 56 | additionalCommandArgs: 57 | - "--min-chunk-size=5MB" 58 | - "--read-timeout=60" 59 | ``` 60 | 61 | ### Example: Extra WAL Archive Options 62 | 63 | ```yaml 64 | kind: ObjectStore 65 | metadata: 66 | name: my-store 67 | spec: 68 | configuration: 69 | wal: 70 | additionalCommandArgs: 71 | - "--max-concurrency=1" 72 | - "--read-timeout=60" 73 | ``` 74 | 75 | For a complete list of supported options, refer to the 76 | [official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/). 77 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 100 3 | --- 4 | 5 | # Parameters 6 | 7 | 8 | 9 | The following parameters are available for the Barman Cloud Plugin: 10 | 11 | - `barmanObjectName`: references the `ObjectStore` resource to be used by the 12 | plugin. 13 | - `serverName`: Specifies the server name in the object store. 14 | 15 | :::important 16 | The `serverName` parameter in the `ObjectStore` resource is retained solely for 17 | API compatibility with the in-tree `barmanObjectStore` and must always be left empty. 18 | When needed, use the `serverName` plugin parameter in the Cluster configuration instead. 19 | ::: 20 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.0/retention.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 60 3 | --- 4 | 5 | # Retention Policies 6 | 7 | 8 | 9 | The Barman Cloud Plugin supports **automated cleanup of obsolete backups** via 10 | retention policies, configured in the `.spec.retentionPolicy` field of the 11 | `ObjectStore` resource. 12 | 13 | :::note 14 | This feature uses the `barman-cloud-backup-delete` command with the 15 | `--retention-policy "RECOVERY WINDOW OF {{ value }} {{ unit }}"` syntax. 16 | ::: 17 | 18 | #### Example: 30-Day Retention Policy 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | [...] 27 | retentionPolicy: "30d" 28 | ```` 29 | 30 | :::note 31 | A **recovery window retention policy** ensures the cluster can be restored to 32 | any point in time between the calculated *Point of Recoverability* (PoR) and 33 | the latest WAL archive. The PoR is defined as `current time - recovery window`. 34 | The **first valid backup** is the most recent backup completed before the PoR. 35 | Backups older than that are marked as *obsolete* and deleted after the next 36 | backup completes. 37 | ::: 38 | 39 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/compression.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 80 3 | --- 4 | 5 | # Compression 6 | 7 | 8 | 9 | By default, backups and WAL files are archived **uncompressed**. However, the 10 | Barman Cloud Plugin supports multiple compression algorithms via 11 | `barman-cloud-backup` and `barman-cloud-wal-archive`, allowing you to optimize 12 | for space, speed, or a balance of both. 13 | 14 | ### Supported Compression Algorithms 15 | 16 | - `bzip2` 17 | - `gzip` 18 | - `lz4` (WAL only) 19 | - `snappy` 20 | - `xz` (WAL only) 21 | - `zstd` (WAL only) 22 | 23 | Compression settings for base backups and WAL archives are configured 24 | independently. For implementation details, refer to the corresponding API 25 | definitions: 26 | 27 | - [`DataBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#DataBackupConfiguration) 28 | - [`WALBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#WalBackupConfiguration) 29 | 30 | :::important 31 | Compression impacts both performance and storage efficiency. Choose the right 32 | algorithm based on your recovery time objectives (RTO), storage capacity, and 33 | network throughput. 34 | ::: 35 | 36 | ## Compression Benchmark (on MinIO) 37 | 38 | | Compression | Backup Time (ms) | Restore Time (ms) | Uncompressed Size (MB) | Compressed Size (MB) | Ratio | 39 | | ----------- | ---------------- | ----------------- | ---------------------- | -------------------- | ----- | 40 | | None | 10,927 | 7,553 | 395 | 395 | 1.0:1 | 41 | | bzip2 | 25,404 | 13,886 | 395 | 67 | 5.9:1 | 42 | | gzip | 116,281 | 3,077 | 395 | 91 | 4.3:1 | 43 | | snappy | 8,134 | 8,341 | 395 | 166 | 2.4:1 | 44 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/images.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 99 3 | --- 4 | 5 | # Container Images 6 | 7 | 8 | 9 | The Barman Cloud Plugin is distributed using two container images: 10 | 11 | - One for deploying the plugin components 12 | - One for the sidecar that runs alongside each PostgreSQL instance in a 13 | CloudNativePG `Cluster` using the plugin 14 | 15 | ## Plugin Container Image 16 | 17 | The plugin image contains the logic required to operate the Barman Cloud Plugin 18 | within your Kubernetes environment with CloudNativePG. It is published on the 19 | GitHub Container Registry at `ghcr.io/cloudnative-pg/plugin-barman-cloud`. 20 | 21 | This image is built from the 22 | [`Dockerfile.plugin`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.plugin) 23 | in the plugin repository. 24 | 25 | ## Sidecar Container Image 26 | 27 | The sidecar image is used within each PostgreSQL pod in the cluster. It 28 | includes the latest supported version of Barman Cloud and is responsible for 29 | performing WAL archiving and backups on behalf of CloudNativePG. 30 | 31 | It is available at `ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar` and is 32 | built from the 33 | [`Dockerfile.sidecar`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.sidecar). 34 | 35 | These sidecar images are designed to work seamlessly with the 36 | [`minimal` PostgreSQL container images](https://github.com/cloudnative-pg/postgres-containers?tab=readme-ov-file#minimal-images) 37 | maintained by the CloudNativePG Community. 38 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | sidebar_label: "Introduction" 4 | --- 5 | 6 | # Barman Cloud Plugin 7 | 8 | 9 | 10 | The **Barman Cloud Plugin** for [CloudNativePG](https://cloudnative-pg.io/) 11 | enables online continuous physical backups of PostgreSQL clusters to object storage 12 | using the `barman-cloud` suite from the [Barman](https://docs.pgbarman.org/release/latest/) 13 | project. 14 | 15 | :::important 16 | If you plan to migrate your existing CloudNativePG cluster to the new 17 | plugin-based approach using the Barman Cloud Plugin, see 18 | ["Migrating from Built-in CloudNativePG Backup"](migration.md) 19 | for detailed instructions. 20 | ::: 21 | 22 | ## Requirements 23 | 24 | To use the Barman Cloud Plugin, you need: 25 | 26 | - [CloudNativePG](https://cloudnative-pg.io) version **1.26** 27 | - [cert-manager](https://cert-manager.io/) to enable TLS communication between 28 | the plugin and the operator 29 | 30 | ## Key Features 31 | 32 | This plugin provides the following capabilities: 33 | 34 | - Physical online backup of the data directory 35 | - Physical restore of the data directory 36 | - Write-Ahead Log (WAL) archiving 37 | - WAL restore 38 | - Full cluster recovery 39 | - Point-in-Time Recovery (PITR) 40 | - Seamless integration with replica clusters for bootstrap and WAL restore from archive 41 | 42 | :::important 43 | The Barman Cloud Plugin is designed to **replace the in-tree object storage support** 44 | previously provided via the `.spec.backup.barmanObjectStore` section in the 45 | `Cluster` resource. 46 | Backups created using the in-tree approach are fully supported and compatible 47 | with this plugin. 48 | ::: 49 | 50 | ## Supported Object Storage Providers 51 | 52 | The plugin works with all storage backends supported by `barman-cloud`, including: 53 | 54 | - **Amazon S3** 55 | - **Google Cloud Storage** 56 | - **Microsoft Azure Blob Storage** 57 | 58 | In addition, the following S3-compatible and simulator solutions have been 59 | tested and verified: 60 | 61 | - [MinIO](https://min.io/) – An S3-compatible storage solution 62 | - [Azurite](https://github.com/Azure/Azurite) – A simulator for Azure Blob Storage 63 | - [fake-gcs-server](https://github.com/fsouza/fake-gcs-server) – A simulator for Google Cloud Storage 64 | 65 | :::tip 66 | For more details, refer to [Object Store Providers](object_stores.md). 67 | ::: 68 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/misc.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 90 3 | --- 4 | 5 | # Miscellaneous 6 | 7 | 8 | 9 | ## Backup Object Tagging 10 | 11 | You can attach key-value metadata tags to backup artifacts—such as base 12 | backups, WAL files, and history files—via the `.spec.configuration` section of 13 | the `ObjectStore` resource. 14 | 15 | - `tags`: applied to base backups and WAL files 16 | - `historyTags`: applied to history files only 17 | 18 | ### Example 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | configuration: 27 | [...] 28 | tags: 29 | backupRetentionPolicy: "expire" 30 | historyTags: 31 | backupRetentionPolicy: "keep" 32 | [...] 33 | ``` 34 | 35 | ## Extra Options for Backup and WAL Archiving 36 | 37 | You can pass additional command-line arguments to `barman-cloud-backup` and 38 | `barman-cloud-wal-archive` using the `additionalCommandArgs` field in the 39 | `ObjectStore` configuration. 40 | 41 | - `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup` 42 | - `.spec.configuration.wal.additionalCommandArgs`: for `barman-cloud-wal-archive` 43 | 44 | Each field accepts a list of string arguments. If an argument is already 45 | configured elsewhere in the plugin, the duplicate will be ignored. 46 | 47 | ### Example: Extra Backup Options 48 | 49 | ```yaml 50 | kind: ObjectStore 51 | metadata: 52 | name: my-store 53 | spec: 54 | configuration: 55 | data: 56 | additionalCommandArgs: 57 | - "--min-chunk-size=5MB" 58 | - "--read-timeout=60" 59 | ``` 60 | 61 | ### Example: Extra WAL Archive Options 62 | 63 | ```yaml 64 | kind: ObjectStore 65 | metadata: 66 | name: my-store 67 | spec: 68 | configuration: 69 | wal: 70 | additionalCommandArgs: 71 | - "--max-concurrency=1" 72 | - "--read-timeout=60" 73 | ``` 74 | 75 | For a complete list of supported options, refer to the 76 | [official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/). 77 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 100 3 | --- 4 | 5 | # Parameters 6 | 7 | 8 | 9 | The following parameters are available for the Barman Cloud Plugin: 10 | 11 | - `barmanObjectName`: references the `ObjectStore` resource to be used by the 12 | plugin. 13 | - `serverName`: Specifies the server name in the object store. 14 | 15 | :::important 16 | The `serverName` parameter in the `ObjectStore` resource is retained solely for 17 | API compatibility with the in-tree `barmanObjectStore` and must always be left empty. 18 | When needed, use the `serverName` plugin parameter in the Cluster configuration instead. 19 | ::: 20 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.4.1/retention.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 60 3 | --- 4 | 5 | # Retention Policies 6 | 7 | 8 | 9 | The Barman Cloud Plugin supports **automated cleanup of obsolete backups** via 10 | retention policies, configured in the `.spec.retentionPolicy` field of the 11 | `ObjectStore` resource. 12 | 13 | :::note 14 | This feature uses the `barman-cloud-backup-delete` command with the 15 | `--retention-policy "RECOVERY WINDOW OF {{ value }} {{ unit }}"` syntax. 16 | ::: 17 | 18 | #### Example: 30-Day Retention Policy 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | [...] 27 | retentionPolicy: "30d" 28 | ```` 29 | 30 | :::note 31 | A **recovery window retention policy** ensures the cluster can be restored to 32 | any point in time between the calculated *Point of Recoverability* (PoR) and 33 | the latest WAL archive. The PoR is defined as `current time - recovery window`. 34 | The **first valid backup** is the most recent backup completed before the PoR. 35 | Backups older than that are marked as *obsolete* and deleted after the next 36 | backup completes. 37 | ::: 38 | 39 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/compression.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 80 3 | --- 4 | 5 | # Compression 6 | 7 | 8 | 9 | By default, backups and WAL files are archived **uncompressed**. However, the 10 | Barman Cloud Plugin supports multiple compression algorithms via 11 | `barman-cloud-backup` and `barman-cloud-wal-archive`, allowing you to optimize 12 | for space, speed, or a balance of both. 13 | 14 | ### Supported Compression Algorithms 15 | 16 | - `bzip2` 17 | - `gzip` 18 | - `lz4` (WAL only) 19 | - `snappy` 20 | - `xz` (WAL only) 21 | - `zstd` (WAL only) 22 | 23 | Compression settings for base backups and WAL archives are configured 24 | independently. For implementation details, refer to the corresponding API 25 | definitions: 26 | 27 | - [`DataBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#DataBackupConfiguration) 28 | - [`WALBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#WalBackupConfiguration) 29 | 30 | :::important 31 | Compression impacts both performance and storage efficiency. Choose the right 32 | algorithm based on your recovery time objectives (RTO), storage capacity, and 33 | network throughput. 34 | ::: 35 | 36 | ## Compression Benchmark (on MinIO) 37 | 38 | | Compression | Backup Time (ms) | Restore Time (ms) | Uncompressed Size (MB) | Compressed Size (MB) | Ratio | 39 | | ----------- | ---------------- | ----------------- | ---------------------- | -------------------- | ----- | 40 | | None | 10,927 | 7,553 | 395 | 395 | 1.0:1 | 41 | | bzip2 | 25,404 | 13,886 | 395 | 67 | 5.9:1 | 42 | | gzip | 116,281 | 3,077 | 395 | 91 | 4.3:1 | 43 | | snappy | 8,134 | 8,341 | 395 | 166 | 2.4:1 | 44 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/images.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 99 3 | --- 4 | 5 | # Container Images 6 | 7 | 8 | 9 | The Barman Cloud Plugin is distributed using two container images: 10 | 11 | - One for deploying the plugin components 12 | - One for the sidecar that runs alongside each PostgreSQL instance in a 13 | CloudNativePG `Cluster` using the plugin 14 | 15 | ## Plugin Container Image 16 | 17 | The plugin image contains the logic required to operate the Barman Cloud Plugin 18 | within your Kubernetes environment with CloudNativePG. It is published on the 19 | GitHub Container Registry at `ghcr.io/cloudnative-pg/plugin-barman-cloud`. 20 | 21 | This image is built from the 22 | [`Dockerfile.plugin`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.plugin) 23 | in the plugin repository. 24 | 25 | ## Sidecar Container Image 26 | 27 | The sidecar image is used within each PostgreSQL pod in the cluster. It 28 | includes the latest supported version of Barman Cloud and is responsible for 29 | performing WAL archiving and backups on behalf of CloudNativePG. 30 | 31 | It is available at `ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar` and is 32 | built from the 33 | [`Dockerfile.sidecar`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.sidecar). 34 | 35 | These sidecar images are designed to work seamlessly with the 36 | [`minimal` PostgreSQL container images](https://github.com/cloudnative-pg/postgres-containers?tab=readme-ov-file#minimal-images) 37 | maintained by the CloudNativePG Community. 38 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/intro.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 1 3 | sidebar_label: "Introduction" 4 | --- 5 | 6 | # Barman Cloud Plugin 7 | 8 | 9 | 10 | The **Barman Cloud Plugin** for [CloudNativePG](https://cloudnative-pg.io/) 11 | enables online continuous physical backups of PostgreSQL clusters to object storage 12 | using the `barman-cloud` suite from the [Barman](https://docs.pgbarman.org/release/latest/) 13 | project. 14 | 15 | :::important 16 | If you plan to migrate your existing CloudNativePG cluster to the new 17 | plugin-based approach using the Barman Cloud Plugin, see 18 | ["Migrating from Built-in CloudNativePG Backup"](migration.md) 19 | for detailed instructions. 20 | ::: 21 | 22 | ## Requirements 23 | 24 | To use the Barman Cloud Plugin, you need: 25 | 26 | - [CloudNativePG](https://cloudnative-pg.io) version **1.26** 27 | - [cert-manager](https://cert-manager.io/) to enable TLS communication between 28 | the plugin and the operator 29 | 30 | ## Key Features 31 | 32 | This plugin provides the following capabilities: 33 | 34 | - Physical online backup of the data directory 35 | - Physical restore of the data directory 36 | - Write-Ahead Log (WAL) archiving 37 | - WAL restore 38 | - Full cluster recovery 39 | - Point-in-Time Recovery (PITR) 40 | - Seamless integration with replica clusters for bootstrap and WAL restore from archive 41 | 42 | :::important 43 | The Barman Cloud Plugin is designed to **replace the in-tree object storage support** 44 | previously provided via the `.spec.backup.barmanObjectStore` section in the 45 | `Cluster` resource. 46 | Backups created using the in-tree approach are fully supported and compatible 47 | with this plugin. 48 | ::: 49 | 50 | ## Supported Object Storage Providers 51 | 52 | The plugin works with all storage backends supported by `barman-cloud`, including: 53 | 54 | - **Amazon S3** 55 | - **Google Cloud Storage** 56 | - **Microsoft Azure Blob Storage** 57 | 58 | In addition, the following S3-compatible and simulator solutions have been 59 | tested and verified: 60 | 61 | - [MinIO](https://min.io/) – An S3-compatible storage solution 62 | - [Azurite](https://github.com/Azure/Azurite) – A simulator for Azure Blob Storage 63 | - [fake-gcs-server](https://github.com/fsouza/fake-gcs-server) – A simulator for Google Cloud Storage 64 | 65 | :::tip 66 | For more details, refer to [Object Store Providers](object_stores.md). 67 | ::: 68 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/misc.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 90 3 | --- 4 | 5 | # Miscellaneous 6 | 7 | 8 | 9 | ## Backup Object Tagging 10 | 11 | You can attach key-value metadata tags to backup artifacts—such as base 12 | backups, WAL files, and history files—via the `.spec.configuration` section of 13 | the `ObjectStore` resource. 14 | 15 | - `tags`: applied to base backups and WAL files 16 | - `historyTags`: applied to history files only 17 | 18 | ### Example 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | configuration: 27 | [...] 28 | tags: 29 | backupRetentionPolicy: "expire" 30 | historyTags: 31 | backupRetentionPolicy: "keep" 32 | [...] 33 | ``` 34 | 35 | ## Extra Options for Backup and WAL Archiving 36 | 37 | You can pass additional command-line arguments to `barman-cloud-backup` and 38 | `barman-cloud-wal-archive` using the `additionalCommandArgs` field in the 39 | `ObjectStore` configuration. 40 | 41 | - `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup` 42 | - `.spec.configuration.wal.additionalCommandArgs`: for `barman-cloud-wal-archive` 43 | 44 | Each field accepts a list of string arguments. If an argument is already 45 | configured elsewhere in the plugin, the duplicate will be ignored. 46 | 47 | ### Example: Extra Backup Options 48 | 49 | ```yaml 50 | kind: ObjectStore 51 | metadata: 52 | name: my-store 53 | spec: 54 | configuration: 55 | data: 56 | additionalCommandArgs: 57 | - "--min-chunk-size=5MB" 58 | - "--read-timeout=60" 59 | ``` 60 | 61 | ### Example: Extra WAL Archive Options 62 | 63 | ```yaml 64 | kind: ObjectStore 65 | metadata: 66 | name: my-store 67 | spec: 68 | configuration: 69 | wal: 70 | additionalCommandArgs: 71 | - "--max-concurrency=1" 72 | - "--read-timeout=60" 73 | ``` 74 | 75 | For a complete list of supported options, refer to the 76 | [official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/). 77 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/parameters.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 100 3 | --- 4 | 5 | # Parameters 6 | 7 | 8 | 9 | The following parameters are available for the Barman Cloud Plugin: 10 | 11 | - `barmanObjectName`: references the `ObjectStore` resource to be used by the 12 | plugin. 13 | - `serverName`: Specifies the server name in the object store. 14 | 15 | :::important 16 | The `serverName` parameter in the `ObjectStore` resource is retained solely for 17 | API compatibility with the in-tree `barmanObjectStore` and must always be left empty. 18 | When needed, use the `serverName` plugin parameter in the Cluster configuration instead. 19 | ::: 20 | -------------------------------------------------------------------------------- /web/versioned_docs/version-0.5.0/retention.md: -------------------------------------------------------------------------------- 1 | --- 2 | sidebar_position: 60 3 | --- 4 | 5 | # Retention Policies 6 | 7 | 8 | 9 | The Barman Cloud Plugin supports **automated cleanup of obsolete backups** via 10 | retention policies, configured in the `.spec.retentionPolicy` field of the 11 | `ObjectStore` resource. 12 | 13 | :::note 14 | This feature uses the `barman-cloud-backup-delete` command with the 15 | `--retention-policy "RECOVERY WINDOW OF {{ value }} {{ unit }}"` syntax. 16 | ::: 17 | 18 | #### Example: 30-Day Retention Policy 19 | 20 | ```yaml 21 | apiVersion: barmancloud.cnpg.io/v1 22 | kind: ObjectStore 23 | metadata: 24 | name: my-store 25 | spec: 26 | [...] 27 | retentionPolicy: "30d" 28 | ```` 29 | 30 | :::note 31 | A **recovery window retention policy** ensures the cluster can be restored to 32 | any point in time between the calculated *Point of Recoverability* (PoR) and 33 | the latest WAL archive. The PoR is defined as `current time - recovery window`. 34 | The **first valid backup** is the most recent backup completed before the PoR. 35 | Backups older than that are marked as *obsolete* and deleted after the next 36 | backup completes. 37 | ::: 38 | 39 | -------------------------------------------------------------------------------- /web/versioned_sidebars/version-0.3.0-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/versioned_sidebars/version-0.4.0-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/versioned_sidebars/version-0.4.1-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/versioned_sidebars/version-0.5.0-sidebars.json: -------------------------------------------------------------------------------- 1 | { 2 | "docs": [ 3 | { 4 | "type": "autogenerated", 5 | "dirName": "." 6 | } 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /web/versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "0.5.0", 3 | "0.4.1", 4 | "0.4.0", 5 | "0.3.0" 6 | ] 7 | --------------------------------------------------------------------------------