Compare commits

...

6 Commits

Author SHA1 Message Date
renovate[bot]
8d9dd9816a
chore(deps): update dependency dagger/dagger to v0.21.4
| datasource  | package       | from    | to      |
| ----------- | ------------- | ------- | ------- |
| github-tags | dagger/dagger | v0.21.0 | v0.21.4 |


Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 10:30:06 +00:00
renovate[bot]
40aa4b21b8
fix(deps): update all non-major go dependencies to 5d46599 (#940)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[github.com/cloudnative-pg/barman-cloud](https://redirect.github.com/cloudnative-pg/barman-cloud)
| require | digest | `8e1642e` → `5d46599` |

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuNCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 12:21:08 +02:00
renovate[bot]
f7088258fd
fix(deps): update all non-major go dependencies to 8e1642e (#939)
Some checks failed
Deploy Docusaurus to GitHub Pages / build (push) Failing after 4s
Deploy Docusaurus to GitHub Pages / deploy (push) Has been skipped
release-please / release-please (push) Failing after 3s
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
|
[github.com/cloudnative-pg/barman-cloud](https://redirect.github.com/cloudnative-pg/barman-cloud)
| require | digest | `213211e` → `8e1642e` |

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDkuMCIsInVwZGF0ZWRJblZlciI6IjQzLjIwOS40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-04 10:32:50 +02:00
renovate[bot]
de2181479f
chore(deps): lock file maintenance (#931)
This PR contains the following updates:

| Update | Change |
|---|---|
| lockFileMaintenance | All locks refreshed |

---

> [!WARNING]
> Some dependencies could not be looked up. Check the [Dependency
Dashboard](../issues/7) for more information.

🔧 This Pull Request updates lock files to use the latest dependency
versions.

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - "before 4am on monday"
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Never, or you tick the rebase/retry checkbox.

👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config
help](https://redirect.github.com/renovatebot/renovate/discussions) if
that's undesired.

---

- [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check
this box

---

This PR was generated by [Mend Renovate](https://mend.io/renovate/).
View the [repository job
log](https://developer.mend.io/github/cloudnative-pg/plugin-barman-cloud).

<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My4yMDIuMSIsInVwZGF0ZWRJblZlciI6IjQzLjIwMi4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJhdXRvbWF0ZWQiLCJuby1pc3N1ZSJdfQ==-->

Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
2026-06-04 09:18:37 +02:00
Armando Ruocco
c5f149b86a
feat: pass additionalCommandArgs to barman-cloud-restore (#914)
Some checks failed
Deploy Docusaurus to GitHub Pages / build (push) Failing after 5s
Deploy Docusaurus to GitHub Pages / deploy (push) Has been skipped
release-please / release-please (push) Failing after 3s
An `ObjectStore` already lets users tack extra command-line flags onto
three of the four `barman-cloud-*` invocations: `barman-cloud-backup`
via
`data.additionalCommandArgs`, `barman-cloud-wal-archive` via
`wal.archiveAdditionalCommandArgs`, and `barman-cloud-wal-restore` via
`wal.restoreAdditionalCommandArgs`. The fourth — `barman-cloud-restore`,
the actual data-restore step in PITR and recovery-from-object-store —
has had no
equivalent, which is exactly the gap reported in #821 (the asker wanted
a longer `--read-timeout` for slow restores from S3).

This PR fills that gap by adding
`spec.configuration.data.restoreAdditionalCommandArgs` and wiring it
into the recovery job hook. In `restoreDataDir`,
the user-supplied flags are appended after the cloud-provider options
and `--endpoint-url` but before the positional `(destinationPath,
serverName,
backupID, pgdata)` arguments — same shape as the other three knobs. The
library's existing append/dedup logic still wins on conflicts, so users
can't
accidentally override flags the plugin already sets (`--cloud-provider`,
`--endpoint-url`, etc.).

Closes #821

Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@gmail.com>
Co-authored-by: Leonardo Cecchi <leonardo.cecchi@gmail.com>
2026-06-03 14:17:12 +02:00
Leonardo Cecchi
aaf6120eaa
fix: classify WAL restore errors with precise gRPC statuses (#927)
Map each restorer sentinel to the gRPC status that best reflects whether
the operator should retry:

  - ErrWALNotFound      -> NotFound        (terminal)
  - ErrInvalidWalName   -> InvalidArgument (terminal)
  - ErrConnectivity     -> Unavailable     (retry)
  - ErrGeneric          -> Unavailable     (retry)
  - anything else       -> Internal        (terminal)

Previously every non-NotFound failure was returned verbatim, leaving the
operator unable to tell transient blips apart from terminal conditions
like a malformed WAL name.

Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
Signed-off-by: Tao Li <tao.li@enterprisedb.com>
Co-authored-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
Co-authored-by: Tao Li <tao.li@enterprisedb.com>
2026-06-03 11:25:07 +02:00
15 changed files with 1123 additions and 873 deletions

View File

@ -44,7 +44,7 @@ jobs:
- name: Install Dagger
env:
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
DAGGER_VERSION: 0.21.0
DAGGER_VERSION: 0.21.4
run: |
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
- name: Run CI task

View File

@ -31,7 +31,7 @@ jobs:
- name: Install Dagger
env:
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
DAGGER_VERSION: 0.21.0
DAGGER_VERSION: 0.21.4
run: |
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
- name: Create image and manifest

View File

@ -21,7 +21,7 @@ jobs:
- name: Install Dagger
env:
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
DAGGER_VERSION: 0.21.0
DAGGER_VERSION: 0.21.4
run: |
curl -L https://dl.dagger.io/dagger/install.sh | BIN_DIR=$HOME/.local/bin sh
- name: Create image and manifest

View File

@ -206,7 +206,7 @@ tasks:
- start-build-network
vars:
# renovate: datasource=github-tags depName=dagger/dagger versioning=semver
DAGGER_VERSION: 0.21.0
DAGGER_VERSION: 0.21.4
DAGGER_ENGINE_IMAGE: registry.dagger.io/engine:v{{ .DAGGER_VERSION }}
cmds:
- >

View File

@ -176,6 +176,25 @@ spec:
format: int32
minimum: 1
type: integer
restoreAdditionalCommandArgs:
description: |-
Additional arguments that can be appended to the 'barman-cloud-restore'
command-line invocation. These arguments provide flexibility to customize
the data restore process further, according to specific requirements or
configurations.
Example:
In a scenario where specialized restore options are required, such as setting
a specific read timeout or defining custom behavior, users can use this field
to specify additional command arguments.
Note:
It's essential to ensure that the provided arguments are valid and supported
by the 'barman-cloud-restore' command, to avoid potential errors or unintended
behavior during execution.
items:
type: string
type: array
type: object
destinationPath:
description: |-

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.26.3
require (
github.com/cert-manager/cert-manager v1.20.2
github.com/cloudnative-pg/api v1.29.1
github.com/cloudnative-pg/barman-cloud v0.5.1
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260604074245-5d4659922a82
github.com/cloudnative-pg/cloudnative-pg v1.29.1
github.com/cloudnative-pg/cnpg-i v0.5.0
github.com/cloudnative-pg/cnpg-i-machinery v0.4.2

4
go.sum
View File

@ -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/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/barman-cloud v0.5.1 h1:vjkXrrxo2DQXHT9u9usqhtaHiPZ/lTfDVs/pIWYTepQ=
github.com/cloudnative-pg/barman-cloud v0.5.1/go.mod h1:XPc5IUFP1y4cZX1sg+Pd8j9V4tmUEVnv3BGCpfQOOg8=
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260604074245-5d4659922a82 h1:XKuLJWTREfjUHAw97LHA1qTcuv93oMu7ILASUYNFjpc=
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260604074245-5d4659922a82/go.mod h1:jCaMsQwJJ0f/49TIdSad1Ayjv52IoJXMZBhozqilY/g=
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/cnpg-i v0.5.0 h1:/TOzpNT6cwNgrpftTtrnLKdoHgMwd+88vZgXjlVgXeE=

View File

@ -20,10 +20,38 @@ SPDX-License-Identifier: Apache-2.0
package common
import (
"errors"
barmanRestorer "github.com/cloudnative-pg/barman-cloud/pkg/restorer"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// classifyWALRestoreError maps an error returned by the WAL
// restorer to a gRPC-coded error so the caller can tell terminal
// failures apart from transient ones via the status code.
func classifyWALRestoreError(walName string, walErr error) error {
switch {
case errors.Is(walErr, barmanRestorer.ErrWALNotFound):
return newWALNotFoundError(walName)
case errors.Is(walErr, barmanRestorer.ErrInvalidWALName):
// A malformed WAL name will never become valid on retry.
return newInvalidWALNameError(walName, walErr)
case errors.Is(walErr, barmanRestorer.ErrConnectivity),
errors.Is(walErr, barmanRestorer.ErrGeneric):
// barman-cloud exit codes 2 (connectivity) and 4
// (generic) both surface conditions that are retryable
// in practice — barman uses the "generic" bucket for
// some connection-class failures too, not just exit 2.
return newUnavailableError(walName, walErr)
default:
// Unrecognized exit codes and unexpected failures (e.g.
// the barman-cloud command could not be executed). No
// positive signal that retry would help.
return newInternalWALRestoreError(walName, walErr)
}
}
// ErrEndOfWALStreamReached is returned when end of WAL is detected in the cloud archive.
var ErrEndOfWALStreamReached = status.Errorf(codes.OutOfRange, "end of WAL reached")
@ -35,10 +63,48 @@ var ErrEndOfWALStreamReached = status.Errorf(codes.OutOfRange, "end of WAL reach
var ErrMissingPermissions = status.Errorf(codes.FailedPrecondition,
"no permission to download the backup credentials, retrying")
// newWALNotFoundError returns a error that states that a
// certain WAL file has not been found. This error is
// compatible with GRPC status codes, resulting in a 404
// being used as a response code.
// newWALNotFoundError reports that the requested WAL file is not
// present in the object store. Emits codes.NotFound: this is a
// terminal condition (the file won't appear on retry).
func newWALNotFoundError(walName string) error {
return status.Errorf(codes.NotFound, "wal %q not found", walName)
}
// newUnavailableError reports that downloading the WAL file failed
// for a reason expected to be transient (a barman-cloud connectivity
// blip, or a generic exit code that in practice covers retryable
// conditions too). Emits codes.Unavailable: per gRPC conventions,
// the canonical signal for "retry may succeed".
func newUnavailableError(walName string, err error) error {
return status.Errorf(
codes.Unavailable,
"transient error while downloading %q: %s",
walName,
err.Error(),
)
}
// newInvalidWALNameError reports that the requested WAL name is
// not a valid name. Emits codes.InvalidArgument: this is a
// terminal condition (the same name won't become valid on retry).
func newInvalidWALNameError(walName string, err error) error {
return status.Errorf(
codes.InvalidArgument,
"invalid WAL name %q: %s",
walName,
err.Error(),
)
}
// newInternalWALRestoreError reports that downloading the WAL
// file failed for an unclassified reason. Emits codes.Internal:
// we have no positive signal that retry would help, so by gRPC
// convention this is treated as terminal.
func newInternalWALRestoreError(walName string, err error) error {
return status.Errorf(
codes.Internal,
"internal error while downloading %q: %s",
walName,
err.Error(),
)
}

View File

@ -0,0 +1,86 @@
/*
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 (
"errors"
"fmt"
barmanRestorer "github.com/cloudnative-pg/barman-cloud/pkg/restorer"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
var _ = Describe("classifyWALRestoreError", func() {
const walName = "000000010000000000000001"
DescribeTable(
"maps barman restorer sentinels to gRPC status codes",
func(walErr error, expectedCode codes.Code) {
got := classifyWALRestoreError(walName, walErr)
Expect(got).To(HaveOccurred())
st, ok := status.FromError(got)
Expect(ok).To(BeTrue(), "returned error must carry a gRPC status")
Expect(st.Code()).To(Equal(expectedCode))
Expect(st.Message()).To(ContainSubstring(walName))
},
Entry("ErrWALNotFound -> NotFound",
fmt.Errorf("object storage or file not found: %w", barmanRestorer.ErrWALNotFound),
codes.NotFound),
Entry("ErrInvalidWALName -> InvalidArgument",
fmt.Errorf("invalid name for a WAL file: %w", barmanRestorer.ErrInvalidWALName),
codes.InvalidArgument),
Entry("ErrConnectivity -> Unavailable",
fmt.Errorf("connectivity failure, retrying: %w", barmanRestorer.ErrConnectivity),
codes.Unavailable),
Entry("ErrGeneric -> Unavailable (barman uses exit 4 for some retryable cases too)",
fmt.Errorf("generic error: %w", barmanRestorer.ErrGeneric),
codes.Unavailable),
Entry("unknown error -> Internal",
errors.New("something we did not classify"),
codes.Internal),
)
It("matches the sentinel even through several wrapping layers", func() {
// The plugin wraps barman errors via fmt.Errorf("...: %w", ...);
// classification must keep working if more wraps appear above.
inner := fmt.Errorf("connectivity failure, retrying: %w", barmanRestorer.ErrConnectivity)
wrapped := fmt.Errorf("while restoring WAL %s: %w", walName, inner)
got := classifyWALRestoreError(walName, wrapped)
st, ok := status.FromError(got)
Expect(ok).To(BeTrue())
Expect(st.Code()).To(Equal(codes.Unavailable))
})
It("treats ErrWALNotFound as terminal even when the error chain mentions other sentinels in its message", func() {
// Defensive: if the underlying error stringifies to something
// resembling another sentinel's message, the switch must still
// match by identity (errors.Is), not by substring.
walErr := fmt.Errorf("not found, looks like a connectivity failure: %w", barmanRestorer.ErrWALNotFound)
got := classifyWALRestoreError(walName, walErr)
st, _ := status.FromError(got)
Expect(st.Code()).To(Equal(codes.NotFound))
})
})

View 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 common
import (
"testing"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
func TestCommon(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Common Suite")
}

View File

@ -365,11 +365,7 @@ func (w WALServiceImplementation) restoreFromBarmanObjectStore(
// is the one that PostgreSQL has requested to restore.
// The failure has already been logged in walRestorer.RestoreList method
if walStatus[0].Err != nil {
if errors.Is(walStatus[0].Err, barmanRestorer.ErrWALNotFound) {
return newWALNotFoundError(walStatus[0].WalName)
}
return walStatus[0].Err
return classifyWALRestoreError(walStatus[0].WalName, walStatus[0].Err)
}
// We skip this step if streaming connection is not available

View File

@ -181,6 +181,9 @@ func (impl JobHookImpl) restoreDataDir(
if backup.Status.EndpointURL != "" {
options = append(options, "--endpoint-url", backup.Status.EndpointURL)
}
options = barmanConfiguration.Data.AppendRestoreAdditionalCommandArgs(options)
options = append(options, backup.Status.DestinationPath)
options = append(options, backup.Status.ServerName)
options = append(options, backup.Status.BackupID)

View File

@ -175,6 +175,25 @@ spec:
format: int32
minimum: 1
type: integer
restoreAdditionalCommandArgs:
description: |-
Additional arguments that can be appended to the 'barman-cloud-restore'
command-line invocation. These arguments provide flexibility to customize
the data restore process further, according to specific requirements or
configurations.
Example:
In a scenario where specialized restore options are required, such as setting
a specific read timeout or defining custom behavior, users can use this field
to specify additional command arguments.
Note:
It's essential to ensure that the provided arguments are valid and supported
by the 'barman-cloud-restore' command, to avoid potential errors or unintended
behavior during execution.
items:
type: string
type: array
type: object
destinationPath:
description: |-

View File

@ -32,17 +32,20 @@ spec:
[...]
```
## Extra Options for Backup and WAL Archiving
## Extra Options for Backup, WAL Archiving, and Restore
You can pass additional command-line arguments to `barman-cloud-backup` and
`barman-cloud-wal-archive` using the `additionalCommandArgs` field in the
`ObjectStore` configuration.
You can pass additional command-line arguments to the underlying
`barman-cloud-*` commands using the corresponding fields in the `ObjectStore`
configuration.
- `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup`
- `.spec.configuration.data.restoreAdditionalCommandArgs`: for `barman-cloud-restore`
- `.spec.configuration.wal.archiveAdditionalCommandArgs`: for `barman-cloud-wal-archive`
- `.spec.configuration.wal.restoreAdditionalCommandArgs`: for `barman-cloud-wal-restore`
Each field accepts a list of string arguments. If an argument is already
configured elsewhere in the plugin, the duplicate will be ignored.
Each field accepts a list of string arguments. If an argument conflicts with
one already set by the plugin, the user-provided value will be ignored. These
fields are intended to pass options the plugin does not configure automatically.
### Example: Extra Backup Options
@ -58,6 +61,19 @@ spec:
- "--read-timeout=60"
```
### Example: Extra Restore Options
```yaml
kind: ObjectStore
metadata:
name: my-store
spec:
configuration:
data:
restoreAdditionalCommandArgs:
- "--read-timeout=900"
```
### Example: Extra WAL Archive Options
```yaml
@ -72,6 +88,19 @@ spec:
- "--read-timeout=60"
```
### Example: Extra WAL Restore Options
```yaml
kind: ObjectStore
metadata:
name: my-store
spec:
configuration:
wal:
restoreAdditionalCommandArgs:
- "--read-timeout=60"
```
For a complete list of supported options, refer to the
[official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/).

File diff suppressed because it is too large Load Diff