mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-07-06 17:52:21 +02:00
Compare commits
11 Commits
5131587316
...
788a83fb64
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
788a83fb64 | ||
|
|
0c687cbbf1 | ||
|
|
5a17340088 | ||
|
|
d55942a67f | ||
|
|
a45459582f | ||
|
|
eb5e860775 | ||
|
|
c34b2329ea | ||
|
|
f7f0136520 | ||
|
|
313b5cc202 | ||
|
|
81efbc50ff | ||
|
|
9383cf828b |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@ -32,7 +32,7 @@ jobs:
|
|||||||
df -h
|
df -h
|
||||||
echo "-----------------------------------------------------"
|
echo "-----------------------------------------------------"
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7.0.0
|
||||||
# We need the full history for the commitlint task
|
# We need the full history for the commitlint task
|
||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|||||||
2
.github/workflows/publish-docs.yml
vendored
2
.github/workflows/publish-docs.yml
vendored
@ -25,7 +25,7 @@ jobs:
|
|||||||
contents: read
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7.0.0
|
||||||
|
|
||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
|
|||||||
2
.github/workflows/release-please.yml
vendored
2
.github/workflows/release-please.yml
vendored
@ -19,7 +19,7 @@ jobs:
|
|||||||
# We'll use the cli until there's a fix for
|
# We'll use the cli until there's a fix for
|
||||||
# https://github.com/googleapis/release-please/issues/2280.
|
# https://github.com/googleapis/release-please/issues/2280.
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7.0.0
|
||||||
- name: Install node
|
- name: Install node
|
||||||
uses: actions/setup-node@v6
|
uses: actions/setup-node@v6
|
||||||
with:
|
with:
|
||||||
|
|||||||
2
.github/workflows/release-publish.yml
vendored
2
.github/workflows/release-publish.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
contents: write
|
contents: write
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v6
|
uses: actions/checkout@v7.0.0
|
||||||
- name: Install QEMU static binaries
|
- name: Install QEMU static binaries
|
||||||
uses: docker/setup-qemu-action@v4
|
uses: docker/setup-qemu-action@v4
|
||||||
- name: Install Task
|
- name: Install Task
|
||||||
|
|||||||
10
Taskfile.yml
10
Taskfile.yml
@ -46,7 +46,7 @@ tasks:
|
|||||||
- wordlist-ordered
|
- wordlist-ordered
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=git-refs depName=spellcheck lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
# renovate: datasource=git-refs depName=spellcheck lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
||||||
DAGGER_SPELLCHECK_SHA: 0a3b65e4cad754d9abdecbe5cf8d443dba63477c
|
DAGGER_SPELLCHECK_SHA: 679d5056358d3cd838ec226333decff673602937
|
||||||
cmds:
|
cmds:
|
||||||
- >
|
- >
|
||||||
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/spellcheck@${DAGGER_SPELLCHECK_SHA}
|
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/spellcheck@${DAGGER_SPELLCHECK_SHA}
|
||||||
@ -60,7 +60,7 @@ tasks:
|
|||||||
desc: Check for conventional commits
|
desc: Check for conventional commits
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=git-refs depName=commitlint lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
# renovate: datasource=git-refs depName=commitlint lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
||||||
DAGGER_COMMITLINT_SHA: 0a3b65e4cad754d9abdecbe5cf8d443dba63477c
|
DAGGER_COMMITLINT_SHA: 679d5056358d3cd838ec226333decff673602937
|
||||||
cmds:
|
cmds:
|
||||||
- >
|
- >
|
||||||
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/commitlint@${DAGGER_COMMITLINT_SHA}
|
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/commitlint@${DAGGER_COMMITLINT_SHA}
|
||||||
@ -74,7 +74,7 @@ tasks:
|
|||||||
- wordlist-ordered
|
- wordlist-ordered
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=git-refs depName=uncommitted lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
# renovate: datasource=git-refs depName=uncommitted lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
||||||
DAGGER_UNCOMMITTED_SHA: 0a3b65e4cad754d9abdecbe5cf8d443dba63477c
|
DAGGER_UNCOMMITTED_SHA: 679d5056358d3cd838ec226333decff673602937
|
||||||
cmds:
|
cmds:
|
||||||
- GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/uncommitted@${DAGGER_UNCOMMITTED_SHA} check-uncommitted --source . stdout
|
- GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/uncommitted@${DAGGER_UNCOMMITTED_SHA} check-uncommitted --source . stdout
|
||||||
sources:
|
sources:
|
||||||
@ -86,7 +86,7 @@ tasks:
|
|||||||
- controller-gen
|
- controller-gen
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=git-refs depName=crd-gen-refs lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
# renovate: datasource=git-refs depName=crd-gen-refs lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
||||||
DAGGER_CRDGENREF_SHA: 0a3b65e4cad754d9abdecbe5cf8d443dba63477c
|
DAGGER_CRDGENREF_SHA: 679d5056358d3cd838ec226333decff673602937
|
||||||
# renovate: datasource=go depName=github.com/elastic/crd-ref-docs
|
# renovate: datasource=go depName=github.com/elastic/crd-ref-docs
|
||||||
CRDREFDOCS_VERSION: v0.3.0
|
CRDREFDOCS_VERSION: v0.3.0
|
||||||
cmds:
|
cmds:
|
||||||
@ -381,7 +381,7 @@ tasks:
|
|||||||
run: once
|
run: once
|
||||||
env:
|
env:
|
||||||
# renovate: datasource=git-refs depName=controller-gen lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
# renovate: datasource=git-refs depName=controller-gen lookupName=https://github.com/cloudnative-pg/daggerverse currentValue=main
|
||||||
DAGGER_CONTROLLER_GEN_SHA: 0a3b65e4cad754d9abdecbe5cf8d443dba63477c
|
DAGGER_CONTROLLER_GEN_SHA: 679d5056358d3cd838ec226333decff673602937
|
||||||
cmds:
|
cmds:
|
||||||
- >
|
- >
|
||||||
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/controller-gen@${DAGGER_CONTROLLER_GEN_SHA}
|
GITHUB_REF= dagger -s call -m github.com/cloudnative-pg/daggerverse/controller-gen@${DAGGER_CONTROLLER_GEN_SHA}
|
||||||
|
|||||||
4
go.mod
4
go.mod
@ -5,7 +5,7 @@ go 1.26.3
|
|||||||
require (
|
require (
|
||||||
github.com/cert-manager/cert-manager v1.20.2
|
github.com/cert-manager/cert-manager v1.20.2
|
||||||
github.com/cloudnative-pg/api v1.29.1
|
github.com/cloudnative-pg/api v1.29.1
|
||||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260618100956-53ffbf09b567
|
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260619084538-8d9302e232e7
|
||||||
github.com/cloudnative-pg/cloudnative-pg v1.29.1
|
github.com/cloudnative-pg/cloudnative-pg v1.29.1
|
||||||
github.com/cloudnative-pg/cnpg-i v0.5.0
|
github.com/cloudnative-pg/cnpg-i v0.5.0
|
||||||
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2
|
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2
|
||||||
@ -20,7 +20,7 @@ require (
|
|||||||
k8s.io/apiextensions-apiserver v0.36.2
|
k8s.io/apiextensions-apiserver v0.36.2
|
||||||
k8s.io/apimachinery v0.36.2
|
k8s.io/apimachinery v0.36.2
|
||||||
k8s.io/client-go v0.36.2
|
k8s.io/client-go v0.36.2
|
||||||
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2
|
k8s.io/utils v0.0.0-20260617174310-a95e086a2553
|
||||||
sigs.k8s.io/controller-runtime v0.24.1
|
sigs.k8s.io/controller-runtime v0.24.1
|
||||||
sigs.k8s.io/kustomize/api v0.21.1
|
sigs.k8s.io/kustomize/api v0.21.1
|
||||||
sigs.k8s.io/kustomize/kyaml v0.21.1
|
sigs.k8s.io/kustomize/kyaml v0.21.1
|
||||||
|
|||||||
8
go.sum
8
go.sum
@ -20,8 +20,8 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
|||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/cloudnative-pg/api v1.29.1 h1:FWr8S7EQeOfdhYXyr2cof8wUXLARZiiQt5Qa6ltED7w=
|
github.com/cloudnative-pg/api v1.29.1 h1:FWr8S7EQeOfdhYXyr2cof8wUXLARZiiQt5Qa6ltED7w=
|
||||||
github.com/cloudnative-pg/api v1.29.1/go.mod h1:QtWF3yzSvIfORMHaSkPAk/o3bhCJwEHJgN3riyRiz3o=
|
github.com/cloudnative-pg/api v1.29.1/go.mod h1:QtWF3yzSvIfORMHaSkPAk/o3bhCJwEHJgN3riyRiz3o=
|
||||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260618100956-53ffbf09b567 h1:vqyRgqs1qDhOO2ZUEx3X4PKJGRnfwXN5xU5KSdjrnps=
|
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260619084538-8d9302e232e7 h1:lNWqibnwlJesDrwAukg8gQF4Hpys3jx31LaXZFoP9vA=
|
||||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260618100956-53ffbf09b567/go.mod h1:vmW7blcg0BECd/9YKqn7NdiF7jz9BlXCWJcGJpzzBCg=
|
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260619084538-8d9302e232e7/go.mod h1:vmW7blcg0BECd/9YKqn7NdiF7jz9BlXCWJcGJpzzBCg=
|
||||||
github.com/cloudnative-pg/cloudnative-pg v1.29.1 h1:ZNEt1TMlnQKXI1kho2UqQuqdfvIvjGln4kN7C1lsmGA=
|
github.com/cloudnative-pg/cloudnative-pg v1.29.1 h1:ZNEt1TMlnQKXI1kho2UqQuqdfvIvjGln4kN7C1lsmGA=
|
||||||
github.com/cloudnative-pg/cloudnative-pg v1.29.1/go.mod h1:Sbgx9jVmkle4/gR2U5JHrzDd74sRPOBHDtPkvncg5v8=
|
github.com/cloudnative-pg/cloudnative-pg v1.29.1/go.mod h1:Sbgx9jVmkle4/gR2U5JHrzDd74sRPOBHDtPkvncg5v8=
|
||||||
github.com/cloudnative-pg/cnpg-i v0.5.0 h1:/TOzpNT6cwNgrpftTtrnLKdoHgMwd+88vZgXjlVgXeE=
|
github.com/cloudnative-pg/cnpg-i v0.5.0 h1:/TOzpNT6cwNgrpftTtrnLKdoHgMwd+88vZgXjlVgXeE=
|
||||||
@ -326,8 +326,8 @@ k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787 h1:kHv8PETbPIVHfqKBYwTNNS
|
|||||||
k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787/go.mod h1:Cyq7UE0QtGe+Zo+/6XFrxiS4Mq0tLyQEONkFzSkfp9o=
|
k8s.io/kube-openapi v0.0.0-20260502001324-b7f5293f4787/go.mod h1:Cyq7UE0QtGe+Zo+/6XFrxiS4Mq0tLyQEONkFzSkfp9o=
|
||||||
k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98=
|
k8s.io/streaming v0.36.2 h1:NSKthPPg9UFSKsRauVJUVGH2Dvn8fhKmY4qrMkw/p98=
|
||||||
k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
|
k8s.io/streaming v0.36.2/go.mod h1:z6fV3D+NVkoeqRMtWwlUZK6U17SY/LqNzOxWL6GyR/s=
|
||||||
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2 h1:wU4tMEhLGgIbLvXQb1cfN+EcM0wf7zC6CPF+C79jroc=
|
k8s.io/utils v0.0.0-20260617174310-a95e086a2553 h1:hmGqDecjc8d7HVzWzRFl0QD9bYuYKbBEG7t8xwnVxfI=
|
||||||
k8s.io/utils v0.0.0-20260507154919-ff6756f316d2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
k8s.io/utils v0.0.0-20260617174310-a95e086a2553/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=
|
||||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec=
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec=
|
||||||
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw=
|
||||||
sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4=
|
sigs.k8s.io/controller-runtime v0.24.1 h1:miPEwrmirImAvgME1L9qebGHrOnGJoVmVdtOU9fRfo4=
|
||||||
|
|||||||
36
hack/examples/cluster-replica-streaming.yaml
Normal file
36
hack/examples/cluster-replica-streaming.yaml
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
apiVersion: postgresql.cnpg.io/v1
|
||||||
|
kind: Cluster
|
||||||
|
metadata:
|
||||||
|
name: cluster-replica
|
||||||
|
spec:
|
||||||
|
instances: 3
|
||||||
|
bootstrap:
|
||||||
|
pg_basebackup:
|
||||||
|
source: source
|
||||||
|
replica:
|
||||||
|
enabled: true
|
||||||
|
source: source
|
||||||
|
externalClusters:
|
||||||
|
- name: source
|
||||||
|
connectionParameters:
|
||||||
|
host: cluster-example-rw.default.svc
|
||||||
|
user: streaming_replica
|
||||||
|
sslmode: verify-full
|
||||||
|
dbname: postgres
|
||||||
|
sslKey:
|
||||||
|
name: cluster-example-replication
|
||||||
|
key: tls.key
|
||||||
|
sslCert:
|
||||||
|
name: cluster-example-replication
|
||||||
|
key: tls.crt
|
||||||
|
sslRootCert:
|
||||||
|
name: cluster-example-ca
|
||||||
|
key: ca.crt
|
||||||
|
plugin:
|
||||||
|
name: barman-cloud.cloudnative-pg.io
|
||||||
|
parameters:
|
||||||
|
barmanObjectName: minio-store
|
||||||
|
serverName: cluster-example
|
||||||
|
storage:
|
||||||
|
size: 1Gi
|
||||||
|
|
||||||
@ -224,7 +224,6 @@ func (w WALServiceImplementation) Archive(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore implements the WALService interface
|
// Restore implements the WALService interface
|
||||||
// nolint: gocognit
|
|
||||||
func (w WALServiceImplementation) Restore(
|
func (w WALServiceImplementation) Restore(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
request *wal.WALRestoreRequest,
|
request *wal.WALRestoreRequest,
|
||||||
@ -239,36 +238,7 @@ func (w WALServiceImplementation) Restore(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverName string
|
serverName, objectStoreKey := resolveRestoreObjectStore(configuration, w.InstanceName)
|
||||||
var objectStoreKey types.NamespacedName
|
|
||||||
|
|
||||||
var promotionToken string
|
|
||||||
if configuration.Cluster.Spec.ReplicaCluster != nil {
|
|
||||||
promotionToken = configuration.Cluster.Spec.ReplicaCluster.PromotionToken
|
|
||||||
}
|
|
||||||
|
|
||||||
switch {
|
|
||||||
case promotionToken != "" && configuration.Cluster.Status.LastPromotionToken != promotionToken:
|
|
||||||
// This is a replica cluster that is being promoted to a primary cluster
|
|
||||||
// Recover from the replica source object store
|
|
||||||
serverName = configuration.ReplicaSourceServerName
|
|
||||||
objectStoreKey = configuration.GetReplicaSourceBarmanObjectKey()
|
|
||||||
|
|
||||||
case configuration.Cluster.IsReplica() && configuration.Cluster.Status.CurrentPrimary == w.InstanceName:
|
|
||||||
// Designated primary on replica cluster, using replica source object store
|
|
||||||
serverName = configuration.ReplicaSourceServerName
|
|
||||||
objectStoreKey = configuration.GetReplicaSourceBarmanObjectKey()
|
|
||||||
|
|
||||||
case configuration.Cluster.Status.CurrentPrimary == "":
|
|
||||||
// Recovery from object store, using recovery object store
|
|
||||||
serverName = configuration.RecoveryServerName
|
|
||||||
objectStoreKey = configuration.GetRecoveryBarmanObjectKey()
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Using cluster object store
|
|
||||||
serverName = configuration.ServerName
|
|
||||||
objectStoreKey = configuration.GetBarmanObjectKey()
|
|
||||||
}
|
|
||||||
|
|
||||||
var objectStore barmancloudv1.ObjectStore
|
var objectStore barmancloudv1.ObjectStore
|
||||||
if err := w.Client.Get(ctx, objectStoreKey, &objectStore); err != nil {
|
if err := w.Client.Get(ctx, objectStoreKey, &objectStore); err != nil {
|
||||||
@ -284,6 +254,33 @@ func (w WALServiceImplementation) Restore(
|
|||||||
ctx, configuration.Cluster, &objectStore, serverName, walName, destinationPath)
|
ctx, configuration.Cluster, &objectStore, serverName, walName, destinationPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// resolveRestoreObjectStore selects the object store and server name to use when
|
||||||
|
// restoring a WAL file, based on the role this instance plays in the cluster.
|
||||||
|
func resolveRestoreObjectStore(
|
||||||
|
configuration *config.PluginConfiguration,
|
||||||
|
instanceName string,
|
||||||
|
) (serverName string, objectStoreKey types.NamespacedName) {
|
||||||
|
switch {
|
||||||
|
case configuration.Cluster.Status.CurrentPrimary == instanceName &&
|
||||||
|
len(configuration.ReplicaSourceBarmanObjectName) > 0:
|
||||||
|
// PostgreSQL never runs restore_command on a live primary; it runs only while
|
||||||
|
// the instance is in recovery (a genuine standby, restoring from a backup, or
|
||||||
|
// being rewound by pg_rewind). So a current primary that still has a replica
|
||||||
|
// source configured can only be a designated primary that has not finished
|
||||||
|
// promoting, and it must keep fetching WAL from the replica source.
|
||||||
|
// Token-agnostic: covers both switchover and failover.
|
||||||
|
return configuration.ReplicaSourceServerName, configuration.GetReplicaSourceBarmanObjectKey()
|
||||||
|
|
||||||
|
case configuration.Cluster.Status.CurrentPrimary == "":
|
||||||
|
// Recovery from object store, using recovery object store
|
||||||
|
return configuration.RecoveryServerName, configuration.GetRecoveryBarmanObjectKey()
|
||||||
|
|
||||||
|
default:
|
||||||
|
// Using cluster object store
|
||||||
|
return configuration.ServerName, configuration.GetBarmanObjectKey()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (w WALServiceImplementation) restoreFromBarmanObjectStore(
|
func (w WALServiceImplementation) restoreFromBarmanObjectStore(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cluster *cnpgv1.Cluster,
|
cluster *cnpgv1.Cluster,
|
||||||
|
|||||||
98
internal/cnpgi/common/wal_test.go
Normal file
98
internal/cnpgi/common/wal_test.go
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
Copyright © contributors to CloudNativePG, established as
|
||||||
|
CloudNativePG a Series of LF Projects, LLC.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("resolveRestoreObjectStore", func() {
|
||||||
|
const (
|
||||||
|
namespace = "test-ns"
|
||||||
|
instance = "cluster-1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// newConfig builds a PluginConfiguration with distinct, recognizable names
|
||||||
|
// for every candidate object store, so each test can assert exactly which
|
||||||
|
// one the routing selected.
|
||||||
|
newConfig := func(currentPrimary, replicaSourceObject string) *config.PluginConfiguration {
|
||||||
|
return &config.PluginConfiguration{
|
||||||
|
Cluster: &cnpgv1.Cluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Namespace: namespace},
|
||||||
|
Status: cnpgv1.ClusterStatus{CurrentPrimary: currentPrimary},
|
||||||
|
},
|
||||||
|
BarmanObjectName: "cluster-store",
|
||||||
|
ServerName: "cluster-server",
|
||||||
|
RecoveryBarmanObjectName: "recovery-store",
|
||||||
|
RecoveryServerName: "recovery-server",
|
||||||
|
ReplicaSourceBarmanObjectName: replicaSourceObject,
|
||||||
|
ReplicaSourceServerName: "replica-server",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DescribeTable(
|
||||||
|
"selects the correct object store for restoring WAL files",
|
||||||
|
func(cfg *config.PluginConfiguration, wantServer, wantObject string) {
|
||||||
|
gotServer, gotKey := resolveRestoreObjectStore(cfg, instance)
|
||||||
|
|
||||||
|
Expect(gotServer).To(Equal(wantServer))
|
||||||
|
Expect(gotKey.Name).To(Equal(wantObject))
|
||||||
|
Expect(gotKey.Namespace).To(Equal(namespace))
|
||||||
|
},
|
||||||
|
|
||||||
|
// The regression this guards: during a designated-primary promotion the
|
||||||
|
// instance is already the current primary while still in recovery, and it
|
||||||
|
// must pull remaining WALs from the replica source. The routing decision does
|
||||||
|
// not depend on the promotion token, so this single case covers both
|
||||||
|
// switchover and failover.
|
||||||
|
Entry("designated primary in promotion -> replica source",
|
||||||
|
newConfig(instance, "replica-store"),
|
||||||
|
"replica-server", "replica-store"),
|
||||||
|
|
||||||
|
// Guards the len(ReplicaSourceBarmanObjectName) > 0 gate: a current primary
|
||||||
|
// without a barman-backed replica source (plain HA primary, or a replica
|
||||||
|
// cluster whose source is streaming-only) must use the cluster store, not
|
||||||
|
// an empty-named replica source key.
|
||||||
|
Entry("current primary without a replica source -> cluster store",
|
||||||
|
newConfig(instance, ""),
|
||||||
|
"cluster-server", "cluster-store"),
|
||||||
|
|
||||||
|
// Bootstrap / PITR: no current primary yet. Recovery wins even if a replica
|
||||||
|
// source happens to be configured.
|
||||||
|
Entry("no current primary -> recovery store",
|
||||||
|
newConfig("", "replica-store"),
|
||||||
|
"recovery-server", "recovery-store"),
|
||||||
|
|
||||||
|
Entry("ordinary standby -> cluster store",
|
||||||
|
newConfig("cluster-2", ""),
|
||||||
|
"cluster-server", "cluster-store"),
|
||||||
|
|
||||||
|
// A non-primary instance must never route to the replica source, even when
|
||||||
|
// one is configured: only the designated primary catches up from the source.
|
||||||
|
Entry("standby in a replica cluster -> cluster store",
|
||||||
|
newConfig("cluster-2", "replica-store"),
|
||||||
|
"cluster-server", "cluster-store"),
|
||||||
|
)
|
||||||
|
})
|
||||||
@ -263,7 +263,9 @@ func getReplicaSourcePlugin(cluster *cnpgv1.Cluster) *cnpgv1.PluginConfiguration
|
|||||||
func (config *PluginConfiguration) Validate() error {
|
func (config *PluginConfiguration) Validate() error {
|
||||||
err := NewConfigurationError()
|
err := NewConfigurationError()
|
||||||
|
|
||||||
if len(config.BarmanObjectName) == 0 && len(config.RecoveryBarmanObjectName) == 0 {
|
if len(config.BarmanObjectName) == 0 &&
|
||||||
|
len(config.RecoveryBarmanObjectName) == 0 &&
|
||||||
|
len(config.ReplicaSourceBarmanObjectName) == 0 {
|
||||||
return err.WithMessage("no reference to barmanObjectName have been included")
|
return err.WithMessage("no reference to barmanObjectName have been included")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
126
internal/cnpgi/operator/config/config_test.go
Normal file
126
internal/cnpgi/operator/config/config_test.go
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
Copyright © contributors to CloudNativePG, established as
|
||||||
|
CloudNativePG a Series of LF Projects, LLC.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
|
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ = Describe("PluginConfiguration.Validate", func() {
|
||||||
|
It("fails when no barman object name is set", func() {
|
||||||
|
cfg := &PluginConfiguration{}
|
||||||
|
Expect(cfg.Validate()).To(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("passes when only BarmanObjectName is set (backup/archive)", func() {
|
||||||
|
cfg := &PluginConfiguration{BarmanObjectName: "my-store"}
|
||||||
|
Expect(cfg.Validate()).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("passes when only RecoveryBarmanObjectName is set (recovery bootstrap)", func() {
|
||||||
|
cfg := &PluginConfiguration{RecoveryBarmanObjectName: "my-store"}
|
||||||
|
Expect(cfg.Validate()).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("passes when only ReplicaSourceBarmanObjectName is set (pg_basebackup replica cluster)", func() {
|
||||||
|
cfg := &PluginConfiguration{ReplicaSourceBarmanObjectName: "my-store"}
|
||||||
|
Expect(cfg.Validate()).To(Succeed())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
var _ = Describe("NewFromCluster", func() {
|
||||||
|
enabled := true
|
||||||
|
|
||||||
|
It("derives the replica source object store for a pg_basebackup replica cluster", func() {
|
||||||
|
cluster := &cnpgv1.Cluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "cluster-replica", Namespace: "test-ns"},
|
||||||
|
Spec: cnpgv1.ClusterSpec{
|
||||||
|
Bootstrap: &cnpgv1.BootstrapConfiguration{
|
||||||
|
PgBaseBackup: &cnpgv1.BootstrapPgBaseBackup{Source: "source"},
|
||||||
|
},
|
||||||
|
ReplicaCluster: &cnpgv1.ReplicaClusterConfiguration{
|
||||||
|
Enabled: &enabled,
|
||||||
|
Source: "source",
|
||||||
|
},
|
||||||
|
ExternalClusters: []cnpgv1.ExternalCluster{
|
||||||
|
{
|
||||||
|
Name: "source",
|
||||||
|
PluginConfiguration: &cnpgv1.PluginConfiguration{
|
||||||
|
Name: metadata.PluginName,
|
||||||
|
Parameters: map[string]string{
|
||||||
|
"barmanObjectName": "minio-store",
|
||||||
|
"serverName": "cluster-example",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := NewFromCluster(cluster)
|
||||||
|
|
||||||
|
// The replica source object store is derived from the external cluster plugin,
|
||||||
|
// while the backup/archive and recovery object stores remain empty: this is the
|
||||||
|
// distinguishing trait of a pg_basebackup replica cluster (a recovery-bootstrapped
|
||||||
|
// replica would also populate RecoveryBarmanObjectName).
|
||||||
|
Expect(cfg.ReplicaSourceBarmanObjectName).To(Equal("minio-store"))
|
||||||
|
Expect(cfg.ReplicaSourceServerName).To(Equal("cluster-example"))
|
||||||
|
Expect(cfg.BarmanObjectName).To(BeEmpty())
|
||||||
|
Expect(cfg.RecoveryBarmanObjectName).To(BeEmpty())
|
||||||
|
|
||||||
|
// Validate must accept it, otherwise the lifecycle hook skips sidecar injection.
|
||||||
|
Expect(cfg.Validate()).To(Succeed())
|
||||||
|
})
|
||||||
|
|
||||||
|
It("ignores a replica source backed by a different plugin", func() {
|
||||||
|
cluster := &cnpgv1.Cluster{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "cluster-replica", Namespace: "test-ns"},
|
||||||
|
Spec: cnpgv1.ClusterSpec{
|
||||||
|
Bootstrap: &cnpgv1.BootstrapConfiguration{
|
||||||
|
PgBaseBackup: &cnpgv1.BootstrapPgBaseBackup{Source: "source"},
|
||||||
|
},
|
||||||
|
ReplicaCluster: &cnpgv1.ReplicaClusterConfiguration{
|
||||||
|
Enabled: &enabled,
|
||||||
|
Source: "source",
|
||||||
|
},
|
||||||
|
ExternalClusters: []cnpgv1.ExternalCluster{
|
||||||
|
{
|
||||||
|
Name: "source",
|
||||||
|
PluginConfiguration: &cnpgv1.PluginConfiguration{
|
||||||
|
Name: "some-other-plugin.cloudnative-pg.io",
|
||||||
|
Parameters: map[string]string{"barmanObjectName": "minio-store"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg := NewFromCluster(cluster)
|
||||||
|
|
||||||
|
Expect(cfg.ReplicaSourceBarmanObjectName).To(BeEmpty())
|
||||||
|
Expect(cfg.Validate()).NotTo(Succeed())
|
||||||
|
})
|
||||||
|
})
|
||||||
32
internal/cnpgi/operator/config/suite_test.go
Normal file
32
internal/cnpgi/operator/config/suite_test.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
Copyright © contributors to CloudNativePG, established as
|
||||||
|
CloudNativePG a Series of LF Projects, LLC.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo/v2"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConfig(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "Config Suite")
|
||||||
|
}
|
||||||
@ -273,8 +273,10 @@ func (impl LifecycleImplementation) collectAdditionalInstanceArgs(
|
|||||||
return args
|
return args
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prefer the cluster object store (backup/archive). If not set, fallback to the recovery object store.
|
// Only one object store provides the sidecar arguments, with precedence:
|
||||||
// If neither is configured, no additional args are provided.
|
// BarmanObjectName (backup/archive) > RecoveryBarmanObjectName (recovery
|
||||||
|
// bootstrap) > ReplicaSourceBarmanObjectName (pg_basebackup replica).
|
||||||
|
// If none is configured, no additional args are provided.
|
||||||
if len(pluginConfiguration.BarmanObjectName) > 0 {
|
if len(pluginConfiguration.BarmanObjectName) > 0 {
|
||||||
var barmanObjectStore barmancloudv1.ObjectStore
|
var barmanObjectStore barmancloudv1.ObjectStore
|
||||||
if err := impl.Client.Get(ctx, pluginConfiguration.GetBarmanObjectKey(), &barmanObjectStore); err != nil {
|
if err := impl.Client.Get(ctx, pluginConfiguration.GetBarmanObjectKey(), &barmanObjectStore); err != nil {
|
||||||
@ -303,6 +305,20 @@ func (impl LifecycleImplementation) collectAdditionalInstanceArgs(
|
|||||||
return args, nil
|
return args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(pluginConfiguration.ReplicaSourceBarmanObjectName) > 0 {
|
||||||
|
key := pluginConfiguration.GetReplicaSourceBarmanObjectKey()
|
||||||
|
var barmanObjectStore barmancloudv1.ObjectStore
|
||||||
|
if err := impl.Client.Get(ctx, key, &barmanObjectStore); err != nil {
|
||||||
|
return nil, fmt.Errorf("while getting replica source barman object store %s: %w", key.String(), err)
|
||||||
|
}
|
||||||
|
args := barmanObjectStore.Spec.InstanceSidecarConfiguration.AdditionalContainerArgs
|
||||||
|
args = append(
|
||||||
|
args,
|
||||||
|
collectTypedAdditionalArgs(&barmanObjectStore)...,
|
||||||
|
)
|
||||||
|
return args, nil
|
||||||
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -48,6 +48,9 @@ func (impl LifecycleImplementation) collectSidecarResourcesForPod(
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
configuration *config.PluginConfiguration,
|
configuration *config.PluginConfiguration,
|
||||||
) (corev1.ResourceRequirements, error) {
|
) (corev1.ResourceRequirements, error) {
|
||||||
|
// Only one object store provides the sidecar resources, with precedence:
|
||||||
|
// BarmanObjectName (backup/archive) > RecoveryBarmanObjectName (recovery
|
||||||
|
// bootstrap) > ReplicaSourceBarmanObjectName (pg_basebackup replica).
|
||||||
if len(configuration.BarmanObjectName) > 0 {
|
if len(configuration.BarmanObjectName) > 0 {
|
||||||
// On a replica cluster that also archives, the designated primary
|
// On a replica cluster that also archives, the designated primary
|
||||||
// will use both the replica source object store and the object store
|
// will use both the replica source object store and the object store
|
||||||
@ -64,10 +67,10 @@ func (impl LifecycleImplementation) collectSidecarResourcesForPod(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(configuration.RecoveryBarmanObjectName) > 0 {
|
if len(configuration.RecoveryBarmanObjectName) > 0 {
|
||||||
// On a replica cluster that doesn't archive, the designated primary
|
// On a cluster recovering from an object store (including log-shipping
|
||||||
// uses only the replica source object store.
|
// replica clusters, where the recovery and replica source object stores
|
||||||
// In this case, we use the replica source object store for configuring
|
// coincide), we use the recovery object store for configuring the
|
||||||
// the resources of the sidecar container.
|
// resources of the sidecar container.
|
||||||
var barmanObjectStore barmancloudv1.ObjectStore
|
var barmanObjectStore barmancloudv1.ObjectStore
|
||||||
if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
|
if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
|
||||||
return corev1.ResourceRequirements{}, err
|
return corev1.ResourceRequirements{}, err
|
||||||
@ -76,5 +79,18 @@ func (impl LifecycleImplementation) collectSidecarResourcesForPod(
|
|||||||
return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
|
return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(configuration.ReplicaSourceBarmanObjectName) > 0 {
|
||||||
|
// On a replica cluster bootstrapped via pg_basebackup, the designated
|
||||||
|
// primary uses only the replica source object store.
|
||||||
|
// In this case, we use the replica source object store for configuring
|
||||||
|
// the resources of the sidecar container.
|
||||||
|
var barmanObjectStore barmancloudv1.ObjectStore
|
||||||
|
if err := impl.Client.Get(ctx, configuration.GetReplicaSourceBarmanObjectKey(), &barmanObjectStore); err != nil {
|
||||||
|
return corev1.ResourceRequirements{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
|
||||||
|
}
|
||||||
|
|
||||||
return corev1.ResourceRequirements{}, nil
|
return corev1.ResourceRequirements{}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,6 +28,7 @@ import (
|
|||||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||||
batchv1 "k8s.io/api/batch/v1"
|
batchv1 "k8s.io/api/batch/v1"
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/resource"
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
"k8s.io/apimachinery/pkg/runtime"
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
"sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||||
@ -293,6 +294,24 @@ var _ = Describe("LifecycleImplementation", func() {
|
|||||||
Expect(args).To(Equal(recoveryArgs))
|
Expect(args).To(Equal(recoveryArgs))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("falls back to replica source object store when primary and recovery not set", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{
|
||||||
|
Cluster: cluster,
|
||||||
|
ReplicaSourceBarmanObjectName: "replica-source-store",
|
||||||
|
}
|
||||||
|
replicaArgs := []string{"--replica-a", "--replica-b"}
|
||||||
|
cli := buildClientFunc(
|
||||||
|
makeStoreFunc(ns, pc.ReplicaSourceBarmanObjectName, replicaArgs),
|
||||||
|
).Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(args).To(Equal(replicaArgs))
|
||||||
|
})
|
||||||
|
|
||||||
It("returns nil when neither object name is configured", func(ctx SpecContext) {
|
It("returns nil when neither object name is configured", func(ctx SpecContext) {
|
||||||
ns := "test-ns"
|
ns := "test-ns"
|
||||||
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
@ -386,6 +405,117 @@ var _ = Describe("LifecycleImplementation", func() {
|
|||||||
Expect(err).NotTo(HaveOccurred())
|
Expect(err).NotTo(HaveOccurred())
|
||||||
Expect(args).To(Equal([]string{"--log-level=info"}))
|
Expect(args).To(Equal([]string{"--log-level=info"}))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("includes --log-level from replica source object store when only replica source set", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{
|
||||||
|
Cluster: cluster,
|
||||||
|
ReplicaSourceBarmanObjectName: "replica-source-store",
|
||||||
|
}
|
||||||
|
store := &barmancloudv1.ObjectStore{
|
||||||
|
TypeMeta: metav1.TypeMeta{Kind: "ObjectStore", APIVersion: barmancloudv1.GroupVersion.String()},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: pc.ReplicaSourceBarmanObjectName, Namespace: ns},
|
||||||
|
Spec: barmancloudv1.ObjectStoreSpec{
|
||||||
|
InstanceSidecarConfiguration: barmancloudv1.InstanceSidecarConfiguration{
|
||||||
|
LogLevel: "warning",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
s := runtime.NewScheme()
|
||||||
|
barmancloudv1.AddKnownTypes(s)
|
||||||
|
cli := fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(store).Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(args).To(Equal([]string{"--log-level=warning"}))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("collectSidecarResourcesForPod", func() {
|
||||||
|
makeStoreWithResourcesFunc := func(ns, name string, res corev1.ResourceRequirements) *barmancloudv1.ObjectStore {
|
||||||
|
return &barmancloudv1.ObjectStore{
|
||||||
|
TypeMeta: metav1.TypeMeta{Kind: "ObjectStore", APIVersion: barmancloudv1.GroupVersion.String()},
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns},
|
||||||
|
Spec: barmancloudv1.ObjectStoreSpec{
|
||||||
|
InstanceSidecarConfiguration: barmancloudv1.InstanceSidecarConfiguration{
|
||||||
|
Resources: res,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
It("uses the cluster object store resources when set", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{Cluster: cluster, BarmanObjectName: "primary-store"}
|
||||||
|
res := corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("64Mi")},
|
||||||
|
}
|
||||||
|
cli := buildClientFunc(makeStoreWithResourcesFunc(ns, pc.BarmanObjectName, res)).Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
got, err := impl.collectSidecarResourcesForPod(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
gotMem := got.Requests[corev1.ResourceMemory]
|
||||||
|
Expect(gotMem.String()).To(Equal("64Mi"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("uses the recovery object store resources when only recovery is set", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{Cluster: cluster, RecoveryBarmanObjectName: "recovery-store"}
|
||||||
|
res := corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("128Mi")},
|
||||||
|
}
|
||||||
|
cli := buildClientFunc(makeStoreWithResourcesFunc(ns, pc.RecoveryBarmanObjectName, res)).Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
got, err := impl.collectSidecarResourcesForPod(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
gotMem := got.Requests[corev1.ResourceMemory]
|
||||||
|
Expect(gotMem.String()).To(Equal("128Mi"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("uses the replica source object store resources when only replica source is set", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{Cluster: cluster, ReplicaSourceBarmanObjectName: "replica-source-store"}
|
||||||
|
res := corev1.ResourceRequirements{
|
||||||
|
Requests: corev1.ResourceList{corev1.ResourceMemory: resource.MustParse("256Mi")},
|
||||||
|
}
|
||||||
|
cli := buildClientFunc(makeStoreWithResourcesFunc(ns, pc.ReplicaSourceBarmanObjectName, res)).Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
got, err := impl.collectSidecarResourcesForPod(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
gotMem := got.Requests[corev1.ResourceMemory]
|
||||||
|
Expect(gotMem.String()).To(Equal("256Mi"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns empty resources when no object store is configured", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{Cluster: cluster}
|
||||||
|
cli := buildClientFunc().Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
got, err := impl.collectSidecarResourcesForPod(ctx, pc)
|
||||||
|
Expect(err).NotTo(HaveOccurred())
|
||||||
|
Expect(got).To(Equal(corev1.ResourceRequirements{}))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("returns an error if the replica source object store cannot be retrieved", func(ctx SpecContext) {
|
||||||
|
ns := "test-ns"
|
||||||
|
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
|
||||||
|
pc := &config.PluginConfiguration{Cluster: cluster, ReplicaSourceBarmanObjectName: "missing-store"}
|
||||||
|
cli := buildClientFunc().Build()
|
||||||
|
|
||||||
|
impl := LifecycleImplementation{Client: cli}
|
||||||
|
_, err := impl.collectSidecarResourcesForPod(ctx, pc)
|
||||||
|
Expect(err).To(HaveOccurred())
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -129,6 +129,48 @@ These strategies help you safeguard backups without requiring broad delete
|
|||||||
permissions, ensuring both security and compliance with minimal operational
|
permissions, ensuring both security and compliance with minimal operational
|
||||||
overhead.
|
overhead.
|
||||||
|
|
||||||
|
### S3 Lifecycle Policy
|
||||||
|
|
||||||
|
Barman Cloud requires the following permissions in the S3 bucket:
|
||||||
|
|
||||||
|
- [`s3:AbortMultipartUpload`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_AbortMultipartUpload.html)
|
||||||
|
- [`s3:DeleteObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_DeleteObject.html)
|
||||||
|
- [`s3:GetObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_GetObject.html)
|
||||||
|
- [`s3:ListBucketMultipartUploads`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListMultipartUploads.html)
|
||||||
|
- [`s3:ListBucket`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListBuckets.html)
|
||||||
|
- [`s3:ListMultipartUploadParts`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_ListParts.html)
|
||||||
|
- [`s3:PutObject`](https://docs.aws.amazon.com/AmazonS3/latest/API/API_PutObject.html)
|
||||||
|
|
||||||
|
Here's an example of what such a bucket policy may look like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"Statement": [
|
||||||
|
{
|
||||||
|
"Action": [
|
||||||
|
"s3:AbortMultipartUpload",
|
||||||
|
"s3:DeleteObject",
|
||||||
|
"s3:GetObject",
|
||||||
|
"s3:ListBucket",
|
||||||
|
"s3:ListBucketMultipartUploads",
|
||||||
|
"s3:ListMultipartUploadParts",
|
||||||
|
"s3:PutObject"
|
||||||
|
],
|
||||||
|
"Effect": "Allow",
|
||||||
|
"Principal": {
|
||||||
|
"AWS": "arn:aws:iam::123456789012:user/MaryMajor"
|
||||||
|
},
|
||||||
|
"Resource": [
|
||||||
|
"arn:aws:s3:::amzn-s3-demo-bucket1",
|
||||||
|
"arn:aws:s3:::amzn-s3-demo-bucket1/*"
|
||||||
|
],
|
||||||
|
"Sid": "statement1",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"Version":"2012-10-17"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### S3-Compatible Storage Providers
|
### S3-Compatible Storage Providers
|
||||||
|
|
||||||
You can use S3-compatible services like **MinIO**, **Linode (Akamai) Object Storage**,
|
You can use S3-compatible services like **MinIO**, **Linode (Akamai) Object Storage**,
|
||||||
|
|||||||
@ -8396,9 +8396,9 @@ send@~0.19.0, send@~0.19.1:
|
|||||||
statuses "~2.0.2"
|
statuses "~2.0.2"
|
||||||
|
|
||||||
serialize-javascript@>=7.0.5, serialize-javascript@^6.0.0, serialize-javascript@^6.0.1:
|
serialize-javascript@>=7.0.5, serialize-javascript@^6.0.0, serialize-javascript@^6.0.1:
|
||||||
version "7.0.5"
|
version "7.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-7.0.5.tgz#c798cc0552ffbb08981914a42a8756e339d0d5b1"
|
resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-7.0.6.tgz#f2f20c8af0757e4d8fa329d0210636da0682ddef"
|
||||||
integrity sha512-F4LcB0UqUl1zErq+1nYEEzSHJnIwb3AF2XWB94b+afhrekOUijwooAYqFyRbjYkm2PAKBabx6oYv/xDxNi8IBw==
|
integrity sha512-ATTK5Q4gFVg0YDp1my2vqygyvhcklD/UV5GIlYHooGTn/NogJqIzpetkD6E5kmuVULqz/S9inUL25XcAgDRJQg==
|
||||||
|
|
||||||
serve-handler@^6.1.7:
|
serve-handler@^6.1.7:
|
||||||
version "6.1.7"
|
version "6.1.7"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user