diff --git a/.wordlist.txt b/.wordlist.txt
index 2f436ef..a87ab7b 100644
--- a/.wordlist.txt
+++ b/.wordlist.txt
@@ -1,3 +1,4 @@
+AdditionalContainerArgs
Akamai
Azurite
BarmanObjectStore
diff --git a/api/v1/objectstore_types.go b/api/v1/objectstore_types.go
index 0db706a..d9ceb52 100644
--- a/api/v1/objectstore_types.go
+++ b/api/v1/objectstore_types.go
@@ -37,6 +37,12 @@ type InstanceSidecarConfiguration struct {
// Resources define cpu/memory requests and limits for the sidecar that runs in the instance pods.
// +optional
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
+
+ // AdditionalContainerArgs is an optional list of command-line arguments
+ // to be passed to the sidecar container when it starts.
+ // The provided arguments are appended to the container’s default arguments.
+ // +optional
+ AdditionalContainerArgs []string `json:"additionalContainerArgs,omitempty"`
}
// ObjectStoreSpec defines the desired state of ObjectStore.
diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go
index 1f92d88..a86696b 100644
--- a/api/v1/zz_generated.deepcopy.go
+++ b/api/v1/zz_generated.deepcopy.go
@@ -36,6 +36,11 @@ func (in *InstanceSidecarConfiguration) DeepCopyInto(out *InstanceSidecarConfigu
}
}
in.Resources.DeepCopyInto(&out.Resources)
+ if in.AdditionalContainerArgs != nil {
+ in, out := &in.AdditionalContainerArgs, &out.AdditionalContainerArgs
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InstanceSidecarConfiguration.
diff --git a/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml b/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml
index be1348d..0f733f0 100644
--- a/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml
+++ b/config/crd/bases/barmancloud.cnpg.io_objectstores.yaml
@@ -391,6 +391,14 @@ spec:
description: The configuration for the sidecar that runs in the instance
pods
properties:
+ additionalContainerArgs:
+ description: |-
+ AdditionalContainerArgs is an optional list of command-line arguments
+ to be passed to the sidecar container when it starts.
+ The provided arguments are appended to the container’s default arguments.
+ items:
+ type: string
+ type: array
env:
description: The environment to be explicitly passed to the sidecar
items:
diff --git a/hack/examples/minio-store.yaml b/hack/examples/minio-store.yaml
index de47ea7..26ed965 100644
--- a/hack/examples/minio-store.yaml
+++ b/hack/examples/minio-store.yaml
@@ -13,6 +13,8 @@ spec:
limits:
memory: "512Mi"
cpu: "500m"
+ additionalContainerArgs:
+ - --log-level=debug
configuration:
endpointCA:
name: minio-server-tls
diff --git a/internal/cnpgi/operator/lifecycle.go b/internal/cnpgi/operator/lifecycle.go
index d5c9918..d7eba1f 100644
--- a/internal/cnpgi/operator/lifecycle.go
+++ b/internal/cnpgi/operator/lifecycle.go
@@ -17,6 +17,7 @@ import (
"k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client"
+ barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
)
@@ -133,9 +134,10 @@ func (impl LifecycleImplementation) reconcileJob(
}
type sidecarConfiguration struct {
- env []corev1.EnvVar
- certificates []corev1.VolumeProjection
- resources corev1.ResourceRequirements
+ env []corev1.EnvVar
+ certificates []corev1.VolumeProjection
+ resources corev1.ResourceRequirements
+ additionalArgs []string
}
func reconcileJob(
@@ -217,14 +219,47 @@ func (impl LifecycleImplementation) reconcilePod(
return nil, err
}
- return reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{
- env: env,
- certificates: certificates,
- resources: resources,
+ additionalArgs, err := impl.collectAdditionalInstanceArgs(ctx, pluginConfiguration)
+ if err != nil {
+ return nil, err
+ }
+
+ return reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{
+ env: env,
+ certificates: certificates,
+ resources: resources,
+ additionalArgs: additionalArgs,
})
}
-func reconcilePod(
+func (impl LifecycleImplementation) collectAdditionalInstanceArgs(
+ ctx context.Context,
+ pluginConfiguration *config.PluginConfiguration,
+) ([]string, error) {
+ // Prefer the cluster object store (backup/archive). If not set, fallback to the recovery object store.
+ // If neither is configured, no additional args are provided.
+ if len(pluginConfiguration.BarmanObjectName) > 0 {
+ var barmanObjectStore barmancloudv1.ObjectStore
+ if err := impl.Client.Get(ctx, pluginConfiguration.GetBarmanObjectKey(), &barmanObjectStore); err != nil {
+ return nil, fmt.Errorf("while getting barman object store %s: %w",
+ pluginConfiguration.GetBarmanObjectKey().String(), err)
+ }
+ return barmanObjectStore.Spec.InstanceSidecarConfiguration.AdditionalContainerArgs, nil
+ }
+
+ if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
+ var barmanObjectStore barmancloudv1.ObjectStore
+ if err := impl.Client.Get(ctx, pluginConfiguration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
+ return nil, fmt.Errorf("while getting recovery barman object store %s: %w",
+ pluginConfiguration.GetRecoveryBarmanObjectKey().String(), err)
+ }
+ return barmanObjectStore.Spec.InstanceSidecarConfiguration.AdditionalContainerArgs, nil
+ }
+
+ return nil, nil
+}
+
+func reconcileInstancePod(
ctx context.Context,
cluster *cnpgv1.Cluster,
request *lifecycle.OperatorLifecycleRequest,
@@ -332,6 +367,7 @@ func reconcilePodSpec(
}
sidecarTemplate.RestartPolicy = ptr.To(corev1.ContainerRestartPolicyAlways)
sidecarTemplate.Resources = config.resources
+ sidecarTemplate.Args = append(sidecarTemplate.Args, config.additionalArgs...)
// merge the main container envs if they aren't already set
for _, container := range spec.Containers {
diff --git a/internal/cnpgi/operator/lifecycle_test.go b/internal/cnpgi/operator/lifecycle_test.go
index c3235de..675d657 100644
--- a/internal/cnpgi/operator/lifecycle_test.go
+++ b/internal/cnpgi/operator/lifecycle_test.go
@@ -6,9 +6,12 @@ import (
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
"github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
+ barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
@@ -18,7 +21,6 @@ import (
var _ = Describe("LifecycleImplementation", func() {
var (
- lifecycleImpl LifecycleImplementation
pluginConfiguration *config.PluginConfiguration
cluster *cnpgv1.Cluster
jobTypeMeta = metav1.TypeMeta{
@@ -31,6 +33,26 @@ var _ = Describe("LifecycleImplementation", func() {
}
)
+ // helper to build a fake client with our scheme and optional objects
+ buildClientFunc := func(objs ...runtime.Object) *fake.ClientBuilder {
+ s := runtime.NewScheme()
+ _ = barmancloudv1.AddToScheme(s)
+ return fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)
+ }
+
+ // helper to create an ObjectStore with given args
+ makeStoreFunc := func(ns, name string, args []string) *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{
+ AdditionalContainerArgs: args,
+ },
+ },
+ }
+ }
+
BeforeEach(func() {
pluginConfiguration = &config.PluginConfiguration{
BarmanObjectName: "minio-store-dest",
@@ -67,6 +89,7 @@ var _ = Describe("LifecycleImplementation", func() {
Describe("GetCapabilities", func() {
It("returns the correct capabilities", func(ctx SpecContext) {
+ var lifecycleImpl LifecycleImplementation
response, err := lifecycleImpl.GetCapabilities(ctx, &lifecycle.OperatorLifecycleCapabilitiesRequest{})
Expect(err).NotTo(HaveOccurred())
Expect(response).NotTo(BeNil())
@@ -76,6 +99,7 @@ var _ = Describe("LifecycleImplementation", func() {
Describe("LifecycleHook", func() {
It("returns an error if object definition is invalid", func(ctx SpecContext) {
+ var lifecycleImpl LifecycleImplementation
request := &lifecycle.OperatorLifecycleRequest{
ObjectDefinition: []byte("invalid-json"),
}
@@ -171,7 +195,7 @@ var _ = Describe("LifecycleImplementation", func() {
})
})
- Describe("reconcilePod", func() {
+ Describe("reconcileInstancePod", func() {
It("returns a patch for a valid pod", func(ctx SpecContext) {
pod := &corev1.Pod{
TypeMeta: podTypeMeta,
@@ -185,7 +209,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: podJSON,
}
- response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
+ response, err := reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
Expect(err).NotTo(HaveOccurred())
Expect(response).NotTo(BeNil())
Expect(response.JsonPatch).NotTo(BeEmpty())
@@ -203,11 +227,93 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: []byte("invalid-json"),
}
- response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
+ response, err := reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
Expect(err).To(HaveOccurred())
Expect(response).To(BeNil())
})
})
+
+ Describe("collectAdditionalInstanceArgs", func() {
+ It("prefers cluster object store when both are configured", func(ctx SpecContext) {
+ ns := "test-ns"
+ cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
+ pc := &config.PluginConfiguration{
+ Cluster: cluster,
+ BarmanObjectName: "primary-store",
+ RecoveryBarmanObjectName: "recovery-store",
+ }
+ primaryArgs := []string{"--primary-a", "--primary-b"}
+ recoveryArgs := []string{"--reco-a"}
+ cli := buildClientFunc(
+ makeStoreFunc(ns, pc.BarmanObjectName, primaryArgs),
+ makeStoreFunc(ns, pc.RecoveryBarmanObjectName, recoveryArgs),
+ ).Build()
+
+ impl := LifecycleImplementation{Client: cli}
+ args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(args).To(Equal(primaryArgs))
+ })
+
+ It("falls back to recovery object store when primary not set", func(ctx SpecContext) {
+ ns := "test-ns"
+ cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
+ pc := &config.PluginConfiguration{
+ Cluster: cluster,
+ BarmanObjectName: "",
+ RecoveryBarmanObjectName: "recovery-store",
+ }
+ recoveryArgs := []string{"--reco-x", "--reco-y"}
+ cli := buildClientFunc(
+ makeStoreFunc(ns, pc.RecoveryBarmanObjectName, recoveryArgs),
+ ).Build()
+
+ impl := LifecycleImplementation{Client: cli}
+ args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(args).To(Equal(recoveryArgs))
+ })
+
+ It("returns nil when neither object name 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}
+ args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
+ Expect(err).NotTo(HaveOccurred())
+ Expect(args).To(BeNil())
+ })
+
+ It("returns error if primary 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, BarmanObjectName: "missing-store"}
+ cli := buildClientFunc().Build()
+
+ impl := LifecycleImplementation{Client: cli}
+ args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("while getting barman object store"))
+ Expect(err.Error()).To(ContainSubstring(ns + "/" + pc.BarmanObjectName))
+ Expect(args).To(BeNil())
+ })
+
+ It("returns error if recovery 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, RecoveryBarmanObjectName: "missing-reco"}
+ cli := buildClientFunc().Build()
+
+ impl := LifecycleImplementation{Client: cli}
+ args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
+ Expect(err).To(HaveOccurred())
+ Expect(err.Error()).To(ContainSubstring("while getting recovery barman object store"))
+ Expect(err.Error()).To(ContainSubstring(ns + "/" + pc.RecoveryBarmanObjectName))
+ Expect(args).To(BeNil())
+ })
+ })
})
var _ = Describe("Volume utilities", func() {
diff --git a/manifest.yaml b/manifest.yaml
index 7248ce9..fb157ea 100644
--- a/manifest.yaml
+++ b/manifest.yaml
@@ -390,6 +390,14 @@ spec:
description: The configuration for the sidecar that runs in the instance
pods
properties:
+ additionalContainerArgs:
+ description: |-
+ AdditionalContainerArgs is an optional list of command-line arguments
+ to be passed to the sidecar container when it starts.
+ The provided arguments are appended to the container’s default arguments.
+ items:
+ type: string
+ type: array
env:
description: The environment to be explicitly passed to the sidecar
items:
diff --git a/web/docs/plugin-barman-cloud.v1.md b/web/docs/plugin-barman-cloud.v1.md
index 5723283..ea50bd9 100644
--- a/web/docs/plugin-barman-cloud.v1.md
+++ b/web/docs/plugin-barman-cloud.v1.md
@@ -29,6 +29,7 @@ _Appears in:_
| `env` _[EnvVar](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#envvar-v1-core) array_ | The environment to be explicitly passed to the sidecar | | | |
| `retentionPolicyIntervalSeconds` _integer_ | The retentionCheckInterval defines the frequency at which the
system checks and enforces retention policies. | | 1800 | |
| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.32/#resourcerequirements-v1-core)_ | Resources define cpu/memory requests and limits for the sidecar that runs in the instance pods. | | | |
+| `additionalContainerArgs` _string array_ | AdditionalContainerArgs is an optional list of command-line arguments
to be passed to the sidecar container when it starts.
The provided arguments are appended to the container’s default arguments. | | | |
#### ObjectStore