Skip to content

Commit ec352ac

Browse files
armruleonardocegbartolini
authored
feat(spec): add support for additional sidecar container arguments (#520)
Introduce the `additionalContainerArgs` field in the `ObjectStore` resource. It allows specifying an optional list of command-line arguments appended to the Barman Cloud sidecar container at startup. Closes #501 Signed-off-by: Armando Ruocco <[email protected]> Signed-off-by: Leonardo Cecchi <[email protected]> Signed-off-by: Gabriele Bartolini <[email protected]> Co-authored-by: Leonardo Cecchi <[email protected]> Co-authored-by: Gabriele Bartolini <[email protected]>
1 parent 62e5441 commit ec352ac

File tree

9 files changed

+185
-12
lines changed

9 files changed

+185
-12
lines changed

.wordlist.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
AdditionalContainerArgs
12
Akamai
23
Azurite
34
BarmanObjectStore

api/v1/objectstore_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ type InstanceSidecarConfiguration struct {
3737
// Resources define cpu/memory requests and limits for the sidecar that runs in the instance pods.
3838
// +optional
3939
Resources corev1.ResourceRequirements `json:"resources,omitempty"`
40+
41+
// AdditionalContainerArgs is an optional list of command-line arguments
42+
// to be passed to the sidecar container when it starts.
43+
// The provided arguments are appended to the container’s default arguments.
44+
// +optional
45+
AdditionalContainerArgs []string `json:"additionalContainerArgs,omitempty"`
4046
}
4147

4248
// ObjectStoreSpec defines the desired state of ObjectStore.

api/v1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/barmancloud.cnpg.io_objectstores.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,6 +391,14 @@ spec:
391391
description: The configuration for the sidecar that runs in the instance
392392
pods
393393
properties:
394+
additionalContainerArgs:
395+
description: |-
396+
AdditionalContainerArgs is an optional list of command-line arguments
397+
to be passed to the sidecar container when it starts.
398+
The provided arguments are appended to the container’s default arguments.
399+
items:
400+
type: string
401+
type: array
394402
env:
395403
description: The environment to be explicitly passed to the sidecar
396404
items:

hack/examples/minio-store.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ spec:
1313
limits:
1414
memory: "512Mi"
1515
cpu: "500m"
16+
additionalContainerArgs:
17+
- --log-level=debug
1618
configuration:
1719
endpointCA:
1820
name: minio-server-tls

internal/cnpgi/operator/lifecycle.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"k8s.io/utils/ptr"
1818
"sigs.k8s.io/controller-runtime/pkg/client"
1919

20+
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
2021
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
2122
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
2223
)
@@ -133,9 +134,10 @@ func (impl LifecycleImplementation) reconcileJob(
133134
}
134135

135136
type sidecarConfiguration struct {
136-
env []corev1.EnvVar
137-
certificates []corev1.VolumeProjection
138-
resources corev1.ResourceRequirements
137+
env []corev1.EnvVar
138+
certificates []corev1.VolumeProjection
139+
resources corev1.ResourceRequirements
140+
additionalArgs []string
139141
}
140142

141143
func reconcileJob(
@@ -217,14 +219,47 @@ func (impl LifecycleImplementation) reconcilePod(
217219
return nil, err
218220
}
219221

220-
return reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{
221-
env: env,
222-
certificates: certificates,
223-
resources: resources,
222+
additionalArgs, err := impl.collectAdditionalInstanceArgs(ctx, pluginConfiguration)
223+
if err != nil {
224+
return nil, err
225+
}
226+
227+
return reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{
228+
env: env,
229+
certificates: certificates,
230+
resources: resources,
231+
additionalArgs: additionalArgs,
224232
})
225233
}
226234

227-
func reconcilePod(
235+
func (impl LifecycleImplementation) collectAdditionalInstanceArgs(
236+
ctx context.Context,
237+
pluginConfiguration *config.PluginConfiguration,
238+
) ([]string, error) {
239+
// Prefer the cluster object store (backup/archive). If not set, fallback to the recovery object store.
240+
// If neither is configured, no additional args are provided.
241+
if len(pluginConfiguration.BarmanObjectName) > 0 {
242+
var barmanObjectStore barmancloudv1.ObjectStore
243+
if err := impl.Client.Get(ctx, pluginConfiguration.GetBarmanObjectKey(), &barmanObjectStore); err != nil {
244+
return nil, fmt.Errorf("while getting barman object store %s: %w",
245+
pluginConfiguration.GetBarmanObjectKey().String(), err)
246+
}
247+
return barmanObjectStore.Spec.InstanceSidecarConfiguration.AdditionalContainerArgs, nil
248+
}
249+
250+
if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
251+
var barmanObjectStore barmancloudv1.ObjectStore
252+
if err := impl.Client.Get(ctx, pluginConfiguration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
253+
return nil, fmt.Errorf("while getting recovery barman object store %s: %w",
254+
pluginConfiguration.GetRecoveryBarmanObjectKey().String(), err)
255+
}
256+
return barmanObjectStore.Spec.InstanceSidecarConfiguration.AdditionalContainerArgs, nil
257+
}
258+
259+
return nil, nil
260+
}
261+
262+
func reconcileInstancePod(
228263
ctx context.Context,
229264
cluster *cnpgv1.Cluster,
230265
request *lifecycle.OperatorLifecycleRequest,
@@ -332,6 +367,7 @@ func reconcilePodSpec(
332367
}
333368
sidecarTemplate.RestartPolicy = ptr.To(corev1.ContainerRestartPolicyAlways)
334369
sidecarTemplate.Resources = config.resources
370+
sidecarTemplate.Args = append(sidecarTemplate.Args, config.additionalArgs...)
335371

336372
// merge the main container envs if they aren't already set
337373
for _, container := range spec.Containers {

internal/cnpgi/operator/lifecycle_test.go

Lines changed: 110 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ import (
66
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
77
"github.com/cloudnative-pg/cloudnative-pg/pkg/utils"
88
"github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
9+
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
910
batchv1 "k8s.io/api/batch/v1"
1011
corev1 "k8s.io/api/core/v1"
1112
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
"k8s.io/apimachinery/pkg/runtime"
14+
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1215

1316
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
1417

@@ -18,7 +21,6 @@ import (
1821

1922
var _ = Describe("LifecycleImplementation", func() {
2023
var (
21-
lifecycleImpl LifecycleImplementation
2224
pluginConfiguration *config.PluginConfiguration
2325
cluster *cnpgv1.Cluster
2426
jobTypeMeta = metav1.TypeMeta{
@@ -31,6 +33,26 @@ var _ = Describe("LifecycleImplementation", func() {
3133
}
3234
)
3335

36+
// helper to build a fake client with our scheme and optional objects
37+
buildClientFunc := func(objs ...runtime.Object) *fake.ClientBuilder {
38+
s := runtime.NewScheme()
39+
_ = barmancloudv1.AddToScheme(s)
40+
return fake.NewClientBuilder().WithScheme(s).WithRuntimeObjects(objs...)
41+
}
42+
43+
// helper to create an ObjectStore with given args
44+
makeStoreFunc := func(ns, name string, args []string) *barmancloudv1.ObjectStore {
45+
return &barmancloudv1.ObjectStore{
46+
TypeMeta: metav1.TypeMeta{Kind: "ObjectStore", APIVersion: barmancloudv1.GroupVersion.String()},
47+
ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: ns},
48+
Spec: barmancloudv1.ObjectStoreSpec{
49+
InstanceSidecarConfiguration: barmancloudv1.InstanceSidecarConfiguration{
50+
AdditionalContainerArgs: args,
51+
},
52+
},
53+
}
54+
}
55+
3456
BeforeEach(func() {
3557
pluginConfiguration = &config.PluginConfiguration{
3658
BarmanObjectName: "minio-store-dest",
@@ -67,6 +89,7 @@ var _ = Describe("LifecycleImplementation", func() {
6789

6890
Describe("GetCapabilities", func() {
6991
It("returns the correct capabilities", func(ctx SpecContext) {
92+
var lifecycleImpl LifecycleImplementation
7093
response, err := lifecycleImpl.GetCapabilities(ctx, &lifecycle.OperatorLifecycleCapabilitiesRequest{})
7194
Expect(err).NotTo(HaveOccurred())
7295
Expect(response).NotTo(BeNil())
@@ -76,6 +99,7 @@ var _ = Describe("LifecycleImplementation", func() {
7699

77100
Describe("LifecycleHook", func() {
78101
It("returns an error if object definition is invalid", func(ctx SpecContext) {
102+
var lifecycleImpl LifecycleImplementation
79103
request := &lifecycle.OperatorLifecycleRequest{
80104
ObjectDefinition: []byte("invalid-json"),
81105
}
@@ -171,7 +195,7 @@ var _ = Describe("LifecycleImplementation", func() {
171195
})
172196
})
173197

174-
Describe("reconcilePod", func() {
198+
Describe("reconcileInstancePod", func() {
175199
It("returns a patch for a valid pod", func(ctx SpecContext) {
176200
pod := &corev1.Pod{
177201
TypeMeta: podTypeMeta,
@@ -185,7 +209,7 @@ var _ = Describe("LifecycleImplementation", func() {
185209
ObjectDefinition: podJSON,
186210
}
187211

188-
response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
212+
response, err := reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
189213
Expect(err).NotTo(HaveOccurred())
190214
Expect(response).NotTo(BeNil())
191215
Expect(response.JsonPatch).NotTo(BeEmpty())
@@ -203,11 +227,93 @@ var _ = Describe("LifecycleImplementation", func() {
203227
ObjectDefinition: []byte("invalid-json"),
204228
}
205229

206-
response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
230+
response, err := reconcileInstancePod(ctx, cluster, request, pluginConfiguration, sidecarConfiguration{})
207231
Expect(err).To(HaveOccurred())
208232
Expect(response).To(BeNil())
209233
})
210234
})
235+
236+
Describe("collectAdditionalInstanceArgs", func() {
237+
It("prefers cluster object store when both are configured", func(ctx SpecContext) {
238+
ns := "test-ns"
239+
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
240+
pc := &config.PluginConfiguration{
241+
Cluster: cluster,
242+
BarmanObjectName: "primary-store",
243+
RecoveryBarmanObjectName: "recovery-store",
244+
}
245+
primaryArgs := []string{"--primary-a", "--primary-b"}
246+
recoveryArgs := []string{"--reco-a"}
247+
cli := buildClientFunc(
248+
makeStoreFunc(ns, pc.BarmanObjectName, primaryArgs),
249+
makeStoreFunc(ns, pc.RecoveryBarmanObjectName, recoveryArgs),
250+
).Build()
251+
252+
impl := LifecycleImplementation{Client: cli}
253+
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
254+
Expect(err).NotTo(HaveOccurred())
255+
Expect(args).To(Equal(primaryArgs))
256+
})
257+
258+
It("falls back to recovery object store when primary not set", func(ctx SpecContext) {
259+
ns := "test-ns"
260+
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
261+
pc := &config.PluginConfiguration{
262+
Cluster: cluster,
263+
BarmanObjectName: "",
264+
RecoveryBarmanObjectName: "recovery-store",
265+
}
266+
recoveryArgs := []string{"--reco-x", "--reco-y"}
267+
cli := buildClientFunc(
268+
makeStoreFunc(ns, pc.RecoveryBarmanObjectName, recoveryArgs),
269+
).Build()
270+
271+
impl := LifecycleImplementation{Client: cli}
272+
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
273+
Expect(err).NotTo(HaveOccurred())
274+
Expect(args).To(Equal(recoveryArgs))
275+
})
276+
277+
It("returns nil when neither object name is configured", func(ctx SpecContext) {
278+
ns := "test-ns"
279+
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
280+
pc := &config.PluginConfiguration{Cluster: cluster}
281+
cli := buildClientFunc().Build()
282+
283+
impl := LifecycleImplementation{Client: cli}
284+
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
285+
Expect(err).NotTo(HaveOccurred())
286+
Expect(args).To(BeNil())
287+
})
288+
289+
It("returns error if primary object store cannot be retrieved", func(ctx SpecContext) {
290+
ns := "test-ns"
291+
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
292+
pc := &config.PluginConfiguration{Cluster: cluster, BarmanObjectName: "missing-store"}
293+
cli := buildClientFunc().Build()
294+
295+
impl := LifecycleImplementation{Client: cli}
296+
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
297+
Expect(err).To(HaveOccurred())
298+
Expect(err.Error()).To(ContainSubstring("while getting barman object store"))
299+
Expect(err.Error()).To(ContainSubstring(ns + "/" + pc.BarmanObjectName))
300+
Expect(args).To(BeNil())
301+
})
302+
303+
It("returns error if recovery object store cannot be retrieved", func(ctx SpecContext) {
304+
ns := "test-ns"
305+
cluster := &cnpgv1.Cluster{ObjectMeta: metav1.ObjectMeta{Name: "c", Namespace: ns}}
306+
pc := &config.PluginConfiguration{Cluster: cluster, RecoveryBarmanObjectName: "missing-reco"}
307+
cli := buildClientFunc().Build()
308+
309+
impl := LifecycleImplementation{Client: cli}
310+
args, err := impl.collectAdditionalInstanceArgs(ctx, pc)
311+
Expect(err).To(HaveOccurred())
312+
Expect(err.Error()).To(ContainSubstring("while getting recovery barman object store"))
313+
Expect(err.Error()).To(ContainSubstring(ns + "/" + pc.RecoveryBarmanObjectName))
314+
Expect(args).To(BeNil())
315+
})
316+
})
211317
})
212318

213319
var _ = Describe("Volume utilities", func() {

manifest.yaml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,14 @@ spec:
390390
description: The configuration for the sidecar that runs in the instance
391391
pods
392392
properties:
393+
additionalContainerArgs:
394+
description: |-
395+
AdditionalContainerArgs is an optional list of command-line arguments
396+
to be passed to the sidecar container when it starts.
397+
The provided arguments are appended to the container’s default arguments.
398+
items:
399+
type: string
400+
type: array
393401
env:
394402
description: The environment to be explicitly passed to the sidecar
395403
items:

web/docs/plugin-barman-cloud.v1.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ _Appears in:_
2929
| `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 | | | |
3030
| `retentionPolicyIntervalSeconds` _integer_ | The retentionCheckInterval defines the frequency at which the<br />system checks and enforces retention policies. | | 1800 | |
3131
| `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. | | | |
32+
| `additionalContainerArgs` _string array_ | AdditionalContainerArgs is an optional list of command-line arguments<br />to be passed to the sidecar container when it starts.<br />The provided arguments are appended to the container’s default arguments. | | | |
3233

3334

3435
#### ObjectStore

0 commit comments

Comments
 (0)