mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-07-06 17:52:21 +02:00
Merge branch 'main' into renovate/lock-file-maintenance-documentation-dependencies
This commit is contained in:
commit
a96a9b1aa1
@ -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
2
go.mod
@ -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.20260603073812-213211ec5e6f
|
||||
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
|
||||
|
||||
6
go.sum
6
go.sum
@ -20,8 +20,10 @@ 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.20260513095406-dac43abc6cf6 h1:FxIX5u8Kf1aJQnmW/5eRo+1hx/okApecnIJAmvliaJ8=
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260513095406-dac43abc6cf6/go.mod h1:VrubJGTgO94O2+m4EX68/wlbDmluc6ITL/VjTcMN3H4=
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260603073812-213211ec5e6f h1:37XPQQ7PY1tffCXS4QuRK7eSDIzn6oeF8h3cIteN2SE=
|
||||
github.com/cloudnative-pg/barman-cloud v0.5.2-0.20260603073812-213211ec5e6f/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=
|
||||
|
||||
@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
86
internal/cnpgi/common/errors_test.go
Normal file
86
internal/cnpgi/common/errors_test.go
Normal 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))
|
||||
})
|
||||
})
|
||||
32
internal/cnpgi/common/suite_test.go
Normal file
32
internal/cnpgi/common/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 common
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestCommon(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Common Suite")
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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: |-
|
||||
|
||||
@ -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/).
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user