From 773f2e214b4e39797075d84910e29918bf7ed39c Mon Sep 17 00:00:00 2001 From: david Date: Mon, 10 Aug 2026 14:09:15 +0700 Subject: [PATCH] feat: support S3 addressing style across commands Signed-off-by: david --- pkg/api/config.go | 20 +++++++++++++ pkg/backup/backup_test.go | 19 ++++++++++++ pkg/command/commandbuilder.go | 46 +++++++++++++++++++++++++++++- pkg/command/commandbuilder_test.go | 45 +++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+), 1 deletion(-) diff --git a/pkg/api/config.go b/pkg/api/config.go index 0e731081..350cc140 100644 --- a/pkg/api/config.go +++ b/pkg/api/config.go @@ -165,6 +165,20 @@ type GoogleCredentials struct { GKEEnvironment bool `json:"gkeEnvironment,omitempty"` } +// S3AddressingStyle controls how Barman Cloud addresses S3 buckets. +type S3AddressingStyle string + +const ( + // S3AddressingStyleAuto lets Barman Cloud choose the addressing style. + S3AddressingStyleAuto S3AddressingStyle = "auto" + + // S3AddressingStyleVirtual addresses buckets as subdomains of the endpoint. + S3AddressingStyleVirtual S3AddressingStyle = "virtual" + + // S3AddressingStylePath addresses buckets as a path below the endpoint. + S3AddressingStylePath S3AddressingStyle = "path" +) + // BarmanObjectStoreConfiguration contains the backup configuration // using Barman against an S3-compatible object storage type BarmanObjectStoreConfiguration struct { @@ -176,6 +190,12 @@ type BarmanObjectStoreConfiguration struct { // +optional EndpointURL string `json:"endpointURL,omitempty"` + // S3AddressingStyle controls how Barman Cloud addresses S3 buckets. It is + // applied to every Barman Cloud command that uses this object store. + // +kubebuilder:validation:Enum=auto;virtual;path + // +optional + S3AddressingStyle S3AddressingStyle `json:"s3AddressingStyle,omitempty"` + // EndpointCA store the CA bundle of the barman endpoint. // Useful when using self-signed certificates to avoid // errors with certificate issuer and barman-cloud-wal-archive diff --git a/pkg/backup/backup_test.go b/pkg/backup/backup_test.go index 7d7586f6..d7cac6dc 100644 --- a/pkg/backup/backup_test.go +++ b/pkg/backup/backup_test.go @@ -130,4 +130,23 @@ var _ = Describe("GetBarmanCloudBackupOptions", func() { "s3://bucket-name/ test-cluster", )) }) + + It("should apply the configured S3 addressing style over additional arguments", func(ctx SpecContext) { + backupCommand.configuration.BarmanCredentials = barmanApi.BarmanCredentials{ + AWS: &barmanApi.S3Credentials{InheritFromIAMRole: true}, + } + backupCommand.configuration.S3AddressingStyle = barmanApi.S3AddressingStyleVirtual + backupCommand.configuration.Data.AdditionalCommandArgs = []string{ + "--addressing-style", "path", + } + + options, err := backupCommand.GetBarmanCloudBackupOptions(ctx, "test-backup", "test-cluster") + Expect(err).ToNot(HaveOccurred()) + Expect(strings.Join(options, " ")).To(Equal( + "--user postgres --name test-backup " + + "--gzip --encryption aes256 --immediate-checkpoint --jobs 4 " + + "--cloud-provider aws-s3 --addressing-style virtual " + + "s3://bucket-name/ test-cluster", + )) + }) }) diff --git a/pkg/command/commandbuilder.go b/pkg/command/commandbuilder.go index 70260b0b..5eddcc68 100644 --- a/pkg/command/commandbuilder.go +++ b/pkg/command/commandbuilder.go @@ -21,6 +21,8 @@ package command import ( "context" + "fmt" + "strings" barmanApi "github.com/cloudnative-pg/barman-cloud/pkg/api" ) @@ -62,7 +64,27 @@ func AppendCloudProviderOptionsFromConfiguration( options []string, barmanConfiguration *barmanApi.BarmanObjectStoreConfiguration, ) ([]string, error) { - return appendCloudProviderOptions(ctx, options, barmanConfiguration.BarmanCredentials) + if barmanConfiguration.S3AddressingStyle != "" { + if barmanConfiguration.AWS == nil { + return nil, fmt.Errorf("s3AddressingStyle requires s3Credentials") + } + + options = withoutOption(options, "--addressing-style") + } + + options, err := appendCloudProviderOptions(ctx, options, barmanConfiguration.BarmanCredentials) + if err != nil { + return nil, err + } + + if barmanConfiguration.S3AddressingStyle != "" { + options = append(options, + "--addressing-style", + string(barmanConfiguration.S3AddressingStyle), + ) + } + + return options, nil } // AppendCloudProviderOptionsFromBackup takes an options array and adds the cloud provider specified @@ -127,6 +149,28 @@ func appendCloudProviderOptions( return options, nil } +// withoutOption removes a command-line option and its value from options. +// The configured ObjectStore value must take precedence over a command-specific +// additional argument so every Barman command uses the same addressing style. +func withoutOption(options []string, option string) []string { + result := make([]string, 0, len(options)) + for index := 0; index < len(options); index++ { + current := options[index] + switch { + case current == option: + if index+1 < len(options) { + index++ + } + case strings.HasPrefix(current, option+"="): + continue + default: + result = append(result, current) + } + } + + return result +} + type contextKey string // contextKeyUseDefaultAzureCredentials contains a bool indicating if the default azure credentials should be used diff --git a/pkg/command/commandbuilder_test.go b/pkg/command/commandbuilder_test.go index bb2d1a0d..7b04aa60 100644 --- a/pkg/command/commandbuilder_test.go +++ b/pkg/command/commandbuilder_test.go @@ -62,6 +62,21 @@ var _ = Describe("barmanCloudWalRestoreOptions", func() { "s3://bucket-name/ test-cluster --read-timeout=60 -vv", )) }) + + It("should apply the configured S3 addressing style", func(ctx SpecContext) { + storageConf.BarmanCredentials = barmanApi.BarmanCredentials{ + AWS: &barmanApi.S3Credentials{InheritFromIAMRole: true}, + } + storageConf.S3AddressingStyle = barmanApi.S3AddressingStyleVirtual + + options, err := CloudWalRestoreOptions(ctx, storageConf, "test-cluster") + Expect(err).ToNot(HaveOccurred()) + Expect(options).To(Equal([]string{ + "--cloud-provider", "aws-s3", + "--addressing-style", "virtual", + "s3://bucket-name/", "test-cluster", + })) + }) }) var _ = Describe("useDefaultAzureCredentials", func() { @@ -172,3 +187,33 @@ var _ = Describe("AppendCloudProviderOptions with Azure credentials", func() { )) }) }) + +var _ = Describe("AppendCloudProviderOptionsFromConfiguration with S3 addressing", func() { + It("should replace an addressing style from command-specific arguments", func(ctx SpecContext) { + configuration := &barmanApi.BarmanObjectStoreConfiguration{ + BarmanCredentials: barmanApi.BarmanCredentials{ + AWS: &barmanApi.S3Credentials{InheritFromIAMRole: true}, + }, + S3AddressingStyle: barmanApi.S3AddressingStyleVirtual, + } + + options, err := AppendCloudProviderOptionsFromConfiguration(ctx, []string{ + "--addressing-style", "path", "--read-timeout=60", + }, configuration) + Expect(err).ToNot(HaveOccurred()) + Expect(options).To(Equal([]string{ + "--read-timeout=60", + "--cloud-provider", "aws-s3", + "--addressing-style", "virtual", + })) + }) + + It("should reject an addressing style without S3 credentials", func(ctx SpecContext) { + configuration := &barmanApi.BarmanObjectStoreConfiguration{ + S3AddressingStyle: barmanApi.S3AddressingStyleVirtual, + } + + _, err := AppendCloudProviderOptionsFromConfiguration(ctx, nil, configuration) + Expect(err).To(MatchError("s3AddressingStyle requires s3Credentials")) + }) +})