feat: support custom CA certificates (#198)

This patch enables the use of custom CA certificates when connecting
to the object store in the barman-cloud plugin. The certificates are
injected into the sidecar via a projected volume and used by the
barman-cloud tool suite.

If the barman object name or the key name changes, users must trigger
a Pod rollout to apply the new values.

Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
Signed-off-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
Signed-off-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
Co-authored-by: Armando Ruocco <armando.ruocco@enterprisedb.com>
Co-authored-by: Marco Nenciarini <marco.nenciarini@enterprisedb.com>
This commit is contained in:
Leonardo Cecchi 2025-03-13 12:05:13 +01:00 committed by GitHub
parent 0872cf2013
commit fcbc472092
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 265 additions and 89 deletions

View File

@ -4,8 +4,11 @@ metadata:
name: minio-store name: minio-store
spec: spec:
configuration: configuration:
endpointCA:
name: minio-server-tls
key: tls.crt
destinationPath: s3://backups/ destinationPath: s3://backups/
endpointURL: http://minio:9000 endpointURL: https://minio:9000
s3Credentials: s3Credentials:
accessKeyId: accessKeyId:
name: minio name: minio

6
go.mod
View File

@ -7,12 +7,12 @@ toolchain go1.24.1
require ( require (
github.com/cert-manager/cert-manager v1.17.1 github.com/cert-manager/cert-manager v1.17.1
github.com/cloudnative-pg/api v1.25.1 github.com/cloudnative-pg/api v1.25.1
github.com/cloudnative-pg/barman-cloud v0.1.0 github.com/cloudnative-pg/barman-cloud v0.2.0
github.com/cloudnative-pg/cloudnative-pg v1.25.1 github.com/cloudnative-pg/cloudnative-pg v1.25.1
github.com/cloudnative-pg/cnpg-i v0.1.0 github.com/cloudnative-pg/cnpg-i v0.1.0
github.com/cloudnative-pg/cnpg-i-machinery v0.1.2 github.com/cloudnative-pg/cnpg-i-machinery v0.1.2
github.com/cloudnative-pg/machinery v0.1.0 github.com/cloudnative-pg/machinery v0.1.0
github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/ginkgo/v2 v2.23.0
github.com/onsi/gomega v1.36.2 github.com/onsi/gomega v1.36.2
github.com/spf13/cobra v1.9.1 github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0 github.com/spf13/viper v1.19.0
@ -118,7 +118,7 @@ require (
golang.org/x/term v0.29.0 // indirect golang.org/x/term v0.29.0 // indirect
golang.org/x/text v0.22.0 // indirect golang.org/x/text v0.22.0 // indirect
golang.org/x/time v0.9.0 // indirect golang.org/x/time v0.9.0 // indirect
golang.org/x/tools v0.28.0 // indirect golang.org/x/tools v0.30.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect

12
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/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudnative-pg/api v1.25.1 h1:uNjKiB0MIspUeH9l651SnFDcuflr1crB3t6LjxUCafQ= github.com/cloudnative-pg/api v1.25.1 h1:uNjKiB0MIspUeH9l651SnFDcuflr1crB3t6LjxUCafQ=
github.com/cloudnative-pg/api v1.25.1/go.mod h1:fwF5g4XkuNZqYXIeRR3AJvUfWlqWig+r2DXc5bEmw6U= github.com/cloudnative-pg/api v1.25.1/go.mod h1:fwF5g4XkuNZqYXIeRR3AJvUfWlqWig+r2DXc5bEmw6U=
github.com/cloudnative-pg/barman-cloud v0.1.0 h1:e/z52CehMBIh1LjZqNBJnncWJbS+1JYvRMBR8Js6Uiw= github.com/cloudnative-pg/barman-cloud v0.2.0 h1:KMwJPKjytDqljNNOounBGojsGTXiowztH0WQrVB8/DQ=
github.com/cloudnative-pg/barman-cloud v0.1.0/go.mod h1:rJUJO/f1yNckLZiVxHAyRmKY+4EPJkYRJsGbTZRJQSY= github.com/cloudnative-pg/barman-cloud v0.2.0/go.mod h1:kNIUU+fpnYjkr25YwHnteROLhbs6rqpjDiB8XW1+sug=
github.com/cloudnative-pg/cloudnative-pg v1.25.1 h1:Yc6T7ikQ1AiWXBQht+6C3DoihrIpUN2OkM1dIwqadTo= github.com/cloudnative-pg/cloudnative-pg v1.25.1 h1:Yc6T7ikQ1AiWXBQht+6C3DoihrIpUN2OkM1dIwqadTo=
github.com/cloudnative-pg/cloudnative-pg v1.25.1/go.mod h1:96b9bRFLSr3uFWHjhytPdcvKIKwy9H6AG7cH0O6jefs= github.com/cloudnative-pg/cloudnative-pg v1.25.1/go.mod h1:96b9bRFLSr3uFWHjhytPdcvKIKwy9H6AG7cH0O6jefs=
github.com/cloudnative-pg/cnpg-i v0.1.0 h1:QH2xTsrODMhEEc6B25GbOYe7ZIttDmSkYvXotfU5dfs= github.com/cloudnative-pg/cnpg-i v0.1.0 h1:QH2xTsrODMhEEc6B25GbOYe7ZIttDmSkYvXotfU5dfs=
@ -137,8 +137,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ=
github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
@ -271,8 +271,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.28.0 h1:WuB6qZ4RPCQo5aP3WdKZS7i595EdWqWR8vqJTlwTVK8= golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
golang.org/x/tools v0.28.0/go.mod h1:dcIOrVd3mfQKTgrDVQHqCPMWy6lnhfhtX3hLXYVLfRw= golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=

View File

@ -2,9 +2,12 @@ package common
import ( import (
"fmt" "fmt"
"path"
"strings" "strings"
barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api" barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
) )
// TODO: refactor. // TODO: refactor.
@ -70,3 +73,8 @@ func MergeEnv(env []string, incomingEnv []string) []string {
return result return result
} }
// BuildCertificateFilePath builds the path to the barman objectStore certificate
func BuildCertificateFilePath(objectStoreName string) string {
return path.Join(metadata.BarmanCertificatesPath, objectStoreName, metadata.BarmanCertificatesFileName)
}

View File

@ -77,12 +77,14 @@ func (w WALServiceImplementation) Archive(
return nil, err return nil, err
} }
envArchive, err := barmanCredentials.EnvSetBackupCloudCredentials( envArchive, err := barmanCredentials.EnvSetCloudCredentialsAndCertificates(
ctx, ctx,
w.Client, w.Client,
objectStore.Namespace, objectStore.Namespace,
&objectStore.Spec.Configuration, &objectStore.Spec.Configuration,
os.Environ()) os.Environ(),
BuildCertificateFilePath(objectStore.Name),
)
if err != nil { if err != nil {
if apierrors.IsForbidden(err) { if apierrors.IsForbidden(err) {
return nil, errors.New("backup credentials don't yet have access permissions. Will retry reconciliation loop") return nil, errors.New("backup credentials don't yet have access permissions. Will retry reconciliation loop")
@ -191,12 +193,13 @@ func (w WALServiceImplementation) restoreFromBarmanObjectStore(
barmanConfiguration := &objectStore.Spec.Configuration barmanConfiguration := &objectStore.Spec.Configuration
env := GetRestoreCABundleEnv(barmanConfiguration) env := GetRestoreCABundleEnv(barmanConfiguration)
credentialsEnv, err := barmanCredentials.EnvSetBackupCloudCredentials( credentialsEnv, err := barmanCredentials.EnvSetCloudCredentialsAndCertificates(
ctx, ctx,
w.Client, w.Client,
objectStore.Namespace, objectStore.Namespace,
&objectStore.Spec.Configuration, &objectStore.Spec.Configuration,
os.Environ(), os.Environ(),
BuildCertificateFilePath(objectStore.Name),
) )
if err != nil { if err != nil {
return fmt.Errorf("while getting recover credentials: %w", err) return fmt.Errorf("while getting recover credentials: %w", err)

View File

@ -98,12 +98,14 @@ func (b BackupServiceImplementation) Backup(
// PGHOST (and the like) to be available // PGHOST (and the like) to be available
osEnvironment := os.Environ() osEnvironment := os.Environ()
caBundleEnvironment := common.GetRestoreCABundleEnv(&objectStore.Spec.Configuration) caBundleEnvironment := common.GetRestoreCABundleEnv(&objectStore.Spec.Configuration)
env, err := barmanCredentials.EnvSetBackupCloudCredentials( env, err := barmanCredentials.EnvSetCloudCredentialsAndCertificates(
ctx, ctx,
b.Client, b.Client,
objectStore.Namespace, objectStore.Namespace,
&objectStore.Spec.Configuration, &objectStore.Spec.Configuration,
common.MergeEnv(osEnvironment, caBundleEnvironment)) common.MergeEnv(osEnvironment, caBundleEnvironment),
common.BuildCertificateFilePath(objectStore.Name),
)
if err != nil { if err != nil {
contextLogger.Error(err, "while setting backup cloud credentials") contextLogger.Error(err, "while setting backup cloud credentials")
return nil, err return nil, err

View File

@ -11,6 +11,14 @@ const (
// if present, requires the WAL archiver to check that the backup object // if present, requires the WAL archiver to check that the backup object
// store is empty. // store is empty.
CheckEmptyWalArchiveFile = ".check-empty-wal-archive" CheckEmptyWalArchiveFile = ".check-empty-wal-archive"
// BarmanCertificatesPath is the path where the Barman
// certificates will be installed
BarmanCertificatesPath = "/barman-certificates"
// BarmanCertificatesFileName is the path where the Barman
// certificates will be used
BarmanCertificatesFileName = "barman-ca.crt"
) )
// Data is the metadata of this plugin. // Data is the metadata of this plugin.

View File

@ -14,11 +14,9 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
batchv1 "k8s.io/api/batch/v1" batchv1 "k8s.io/api/batch/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/utils/ptr" "k8s.io/utils/ptr"
"sigs.k8s.io/controller-runtime/pkg/client" "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/metadata"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
) )
@ -107,56 +105,6 @@ func (impl LifecycleImplementation) LifecycleHook(
} }
} }
func (impl LifecycleImplementation) collectAdditionalEnvs(
ctx context.Context,
namespace string,
pluginConfiguration *config.PluginConfiguration,
) ([]corev1.EnvVar, error) {
var result []corev1.EnvVar
if len(pluginConfiguration.BarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreEnvs(
ctx,
types.NamespacedName{
Name: pluginConfiguration.BarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreEnvs(
ctx,
types.NamespacedName{
Name: pluginConfiguration.RecoveryBarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
return result, nil
}
func (impl LifecycleImplementation) collectObjectStoreEnvs(
ctx context.Context,
barmanObjectKey types.NamespacedName,
) ([]corev1.EnvVar, error) {
var objectStore barmancloudv1.ObjectStore
if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil {
return nil, err
}
return objectStore.Spec.InstanceSidecarConfiguration.Env, nil
}
func (impl LifecycleImplementation) reconcileJob( func (impl LifecycleImplementation) reconcileJob(
ctx context.Context, ctx context.Context,
cluster *cnpgv1.Cluster, cluster *cnpgv1.Cluster,
@ -165,10 +113,15 @@ func (impl LifecycleImplementation) reconcileJob(
) (*lifecycle.OperatorLifecycleResponse, error) { ) (*lifecycle.OperatorLifecycleResponse, error) {
env, err := impl.collectAdditionalEnvs(ctx, cluster.Namespace, pluginConfiguration) env, err := impl.collectAdditionalEnvs(ctx, cluster.Namespace, pluginConfiguration)
if err != nil { if err != nil {
return nil, nil return nil, err
} }
return reconcileJob(ctx, cluster, request, env) certificates, err := impl.collectAdditionalCertificates(ctx, cluster.Namespace, pluginConfiguration)
if err != nil {
return nil, err
}
return reconcileJob(ctx, cluster, request, env, certificates)
} }
func reconcileJob( func reconcileJob(
@ -176,6 +129,7 @@ func reconcileJob(
cluster *cnpgv1.Cluster, cluster *cnpgv1.Cluster,
request *lifecycle.OperatorLifecycleRequest, request *lifecycle.OperatorLifecycleRequest,
env []corev1.EnvVar, env []corev1.EnvVar,
certificates []corev1.VolumeProjection,
) (*lifecycle.OperatorLifecycleResponse, error) { ) (*lifecycle.OperatorLifecycleResponse, error) {
contextLogger := log.FromContext(ctx).WithName("lifecycle") contextLogger := log.FromContext(ctx).WithName("lifecycle")
if pluginConfig := cluster.GetRecoverySourcePlugin(); pluginConfig == nil || pluginConfig.Name != metadata.PluginName { if pluginConfig := cluster.GetRecoverySourcePlugin(); pluginConfig == nil || pluginConfig.Name != metadata.PluginName {
@ -212,6 +166,7 @@ func reconcileJob(
Args: []string{"restore"}, Args: []string{"restore"},
}, },
env, env,
certificates,
); err != nil { ); err != nil {
return nil, fmt.Errorf("while reconciling pod spec for job: %w", err) return nil, fmt.Errorf("while reconciling pod spec for job: %w", err)
} }
@ -235,10 +190,15 @@ func (impl LifecycleImplementation) reconcilePod(
) (*lifecycle.OperatorLifecycleResponse, error) { ) (*lifecycle.OperatorLifecycleResponse, error) {
env, err := impl.collectAdditionalEnvs(ctx, cluster.Namespace, pluginConfiguration) env, err := impl.collectAdditionalEnvs(ctx, cluster.Namespace, pluginConfiguration)
if err != nil { if err != nil {
return nil, nil return nil, err
} }
return reconcilePod(ctx, cluster, request, pluginConfiguration, env) certificates, err := impl.collectAdditionalCertificates(ctx, cluster.Namespace, pluginConfiguration)
if err != nil {
return nil, err
}
return reconcilePod(ctx, cluster, request, pluginConfiguration, env, certificates)
} }
func reconcilePod( func reconcilePod(
@ -247,6 +207,7 @@ func reconcilePod(
request *lifecycle.OperatorLifecycleRequest, request *lifecycle.OperatorLifecycleRequest,
pluginConfiguration *config.PluginConfiguration, pluginConfiguration *config.PluginConfiguration,
env []corev1.EnvVar, env []corev1.EnvVar,
certificates []corev1.VolumeProjection,
) (*lifecycle.OperatorLifecycleResponse, error) { ) (*lifecycle.OperatorLifecycleResponse, error) {
pod, err := decoder.DecodePodJSON(request.GetObjectDefinition()) pod, err := decoder.DecodePodJSON(request.GetObjectDefinition())
if err != nil { if err != nil {
@ -267,6 +228,7 @@ func reconcilePod(
Args: []string{"instance"}, Args: []string{"instance"},
}, },
env, env,
certificates,
); err != nil { ); err != nil {
return nil, fmt.Errorf("while reconciling pod spec for pod: %w", err) return nil, fmt.Errorf("while reconciling pod spec for pod: %w", err)
} }
@ -291,6 +253,7 @@ func reconcilePodSpec(
mainContainerName string, mainContainerName string,
sidecarConfig corev1.Container, sidecarConfig corev1.Container,
additionalEnvs []corev1.EnvVar, additionalEnvs []corev1.EnvVar,
certificates []corev1.VolumeProjection,
) error { ) error {
envs := []corev1.EnvVar{ envs := []corev1.EnvVar{
{ {
@ -360,10 +323,22 @@ func reconcilePodSpec(
} }
} }
if err := InjectPluginSidecarPodSpec(spec, &sidecarConfig, mainContainerName, true); err != nil { if err := injectPluginSidecarPodSpec(spec, &sidecarConfig, mainContainerName); err != nil {
return err return err
} }
// inject the volume containing the certificates if needed
if !volumeListHasVolume(spec.Volumes, barmanCertificatesVolumeName) {
spec.Volumes = append(spec.Volumes, corev1.Volume{
Name: barmanCertificatesVolumeName,
VolumeSource: corev1.VolumeSource{
Projected: &corev1.ProjectedVolumeSource{
Sources: certificates,
},
},
})
}
return nil return nil
} }
@ -407,16 +382,15 @@ func InjectPluginVolumePodSpec(spec *corev1.PodSpec, mainContainerName string) {
} }
} }
// InjectPluginSidecarPodSpec injects a plugin sidecar into a CNPG Pod spec. // injectPluginSidecarPodSpec injects a plugin sidecar into a CNPG Pod spec.
// //
// If the "injectMainContainerVolumes" flag is true, this will append all the volume // If the "injectMainContainerVolumes" flag is true, this will append all the volume
// mounts that are used in the instance manager Pod to the passed sidecar // mounts that are used in the instance manager Pod to the passed sidecar
// container, granting it superuser access to the PostgreSQL instance. // container, granting it superuser access to the PostgreSQL instance.
func InjectPluginSidecarPodSpec( func injectPluginSidecarPodSpec(
spec *corev1.PodSpec, spec *corev1.PodSpec,
sidecar *corev1.Container, sidecar *corev1.Container,
mainContainerName string, mainContainerName string,
injectMainContainerVolumes bool,
) error { ) error {
sidecar = sidecar.DeepCopy() sidecar = sidecar.DeepCopy()
InjectPluginVolumePodSpec(spec, mainContainerName) InjectPluginVolumePodSpec(spec, mainContainerName)
@ -447,11 +421,27 @@ func InjectPluginSidecarPodSpec(
} }
// Do not modify the passed sidecar definition // Do not modify the passed sidecar definition
if injectMainContainerVolumes { sidecar.VolumeMounts = append(
sidecar.VolumeMounts,
corev1.VolumeMount{
Name: barmanCertificatesVolumeName,
MountPath: metadata.BarmanCertificatesPath,
})
sidecar.VolumeMounts = append(sidecar.VolumeMounts, volumeMounts...) sidecar.VolumeMounts = append(sidecar.VolumeMounts, volumeMounts...)
}
sidecar.RestartPolicy = ptr.To(corev1.ContainerRestartPolicyAlways) sidecar.RestartPolicy = ptr.To(corev1.ContainerRestartPolicyAlways)
spec.InitContainers = append(spec.InitContainers, *sidecar) spec.InitContainers = append(spec.InitContainers, *sidecar)
return nil return nil
} }
// volumeListHasVolume check if a volume with a known name exists
// in the volume list
func volumeListHasVolume(volumes []corev1.Volume, name string) bool {
for i := range volumes {
if volumes[i].Name == name {
return true
}
}
return false
}

View File

@ -0,0 +1,89 @@
package operator
import (
"context"
"path"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
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"
)
// barmanCertificatesVolumeName is the name of the volume that hosts
// the barman certificates to be used
const barmanCertificatesVolumeName = "barman-certificates"
func (impl LifecycleImplementation) collectAdditionalCertificates(
ctx context.Context,
namespace string,
pluginConfiguration *config.PluginConfiguration,
) ([]corev1.VolumeProjection, error) {
var result []corev1.VolumeProjection
if len(pluginConfiguration.BarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreCertificates(
ctx,
types.NamespacedName{
Name: pluginConfiguration.BarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreCertificates(
ctx,
types.NamespacedName{
Name: pluginConfiguration.RecoveryBarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
return result, nil
}
func (impl LifecycleImplementation) collectObjectStoreCertificates(
ctx context.Context,
barmanObjectKey types.NamespacedName,
) ([]corev1.VolumeProjection, error) {
var objectStore barmancloudv1.ObjectStore
if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil {
return nil, err
}
endpointCA := objectStore.Spec.Configuration.EndpointCA
if endpointCA == nil {
return nil, nil
}
return []corev1.VolumeProjection{
{
Secret: &corev1.SecretProjection{
LocalObjectReference: corev1.LocalObjectReference{
Name: endpointCA.Name,
},
Items: []corev1.KeyToPath{
{
Key: endpointCA.Key,
Path: path.Join(
barmanObjectKey.Name,
metadata.BarmanCertificatesFileName,
),
},
},
},
},
}, nil
}

View File

@ -0,0 +1,61 @@
package operator
import (
"context"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/types"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
)
func (impl LifecycleImplementation) collectAdditionalEnvs(
ctx context.Context,
namespace string,
pluginConfiguration *config.PluginConfiguration,
) ([]corev1.EnvVar, error) {
var result []corev1.EnvVar
if len(pluginConfiguration.BarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreEnvs(
ctx,
types.NamespacedName{
Name: pluginConfiguration.BarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
envs, err := impl.collectObjectStoreEnvs(
ctx,
types.NamespacedName{
Name: pluginConfiguration.RecoveryBarmanObjectName,
Namespace: namespace,
},
)
if err != nil {
return nil, err
}
result = append(result, envs...)
}
return result, nil
}
func (impl LifecycleImplementation) collectObjectStoreEnvs(
ctx context.Context,
barmanObjectKey types.NamespacedName,
) ([]corev1.EnvVar, error) {
var objectStore barmancloudv1.ObjectStore
if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil {
return nil, err
}
return objectStore.Spec.InstanceSidecarConfiguration.Env, nil
}

View File

@ -107,7 +107,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: jobJSON, ObjectDefinition: jobJSON,
} }
response, err := reconcileJob(ctx, cluster, request, nil) response, err := reconcileJob(ctx, cluster, request, nil, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(response).NotTo(BeNil()) Expect(response).NotTo(BeNil())
Expect(response.JsonPatch).NotTo(BeEmpty()) Expect(response.JsonPatch).NotTo(BeEmpty())
@ -128,7 +128,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: jobJSON, ObjectDefinition: jobJSON,
} }
response, err := reconcileJob(ctx, cluster, request, nil) response, err := reconcileJob(ctx, cluster, request, nil, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(response).To(BeNil()) Expect(response).To(BeNil())
}) })
@ -138,7 +138,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: []byte("invalid-json"), ObjectDefinition: []byte("invalid-json"),
} }
response, err := reconcileJob(ctx, cluster, request, nil) response, err := reconcileJob(ctx, cluster, request, nil, nil)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(response).To(BeNil()) Expect(response).To(BeNil())
}) })
@ -165,7 +165,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: jobJSON, ObjectDefinition: jobJSON,
} }
response, err := reconcileJob(ctx, cluster, request, nil) response, err := reconcileJob(ctx, cluster, request, nil, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(response).To(BeNil()) Expect(response).To(BeNil())
}) })
@ -185,7 +185,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: podJSON, ObjectDefinition: podJSON,
} }
response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil) response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil)
Expect(err).NotTo(HaveOccurred()) Expect(err).NotTo(HaveOccurred())
Expect(response).NotTo(BeNil()) Expect(response).NotTo(BeNil())
Expect(response.JsonPatch).NotTo(BeEmpty()) Expect(response.JsonPatch).NotTo(BeEmpty())
@ -203,7 +203,7 @@ var _ = Describe("LifecycleImplementation", func() {
ObjectDefinition: []byte("invalid-json"), ObjectDefinition: []byte("invalid-json"),
} }
response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil) response, err := reconcilePod(ctx, cluster, request, pluginConfiguration, nil, nil)
Expect(err).To(HaveOccurred()) Expect(err).To(HaveOccurred())
Expect(response).To(BeNil()) Expect(response).To(BeNil())
}) })

View File

@ -26,6 +26,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client"
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1" barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata" "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config" "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
) )
@ -87,7 +88,12 @@ func (impl JobHookImpl) Restore(
return nil, err return nil, err
} }
if err := impl.checkBackupDestination(ctx, configuration.Cluster, &targetObjectStore.Spec.Configuration); err != nil { if err := impl.checkBackupDestination(
ctx,
configuration.Cluster,
&targetObjectStore.Spec.Configuration,
targetObjectStore.Name,
); err != nil {
return nil, err return nil, err
} }
} }
@ -98,6 +104,7 @@ func (impl JobHookImpl) Restore(
impl.Client, impl.Client,
configuration.Cluster, configuration.Cluster,
&recoveryObjectStore.Spec.Configuration, &recoveryObjectStore.Spec.Configuration,
recoveryObjectStore.Name,
configuration.RecoveryServerName, configuration.RecoveryServerName,
) )
if err != nil { if err != nil {
@ -220,13 +227,16 @@ func (impl *JobHookImpl) checkBackupDestination(
ctx context.Context, ctx context.Context,
cluster *cnpgv1.Cluster, cluster *cnpgv1.Cluster,
barmanConfiguration *cnpgv1.BarmanObjectStoreConfiguration, barmanConfiguration *cnpgv1.BarmanObjectStoreConfiguration,
objectStoreName string,
) error { ) error {
// Get environment from cache // Get environment from cache
env, err := barmanCredentials.EnvSetRestoreCloudCredentials(ctx, env, err := barmanCredentials.EnvSetCloudCredentialsAndCertificates(ctx,
impl.Client, impl.Client,
cluster.Namespace, cluster.Namespace,
barmanConfiguration, barmanConfiguration,
os.Environ()) os.Environ(),
common.BuildCertificateFilePath(objectStoreName),
)
if err != nil { if err != nil {
return fmt.Errorf("can't get credentials for cluster %v: %w", cluster.Name, err) return fmt.Errorf("can't get credentials for cluster %v: %w", cluster.Name, err)
} }
@ -329,6 +339,7 @@ func loadBackupObjectFromExternalCluster(
typedClient client.Client, typedClient client.Client,
cluster *cnpgv1.Cluster, cluster *cnpgv1.Cluster,
recoveryObjectStore *api.BarmanObjectStoreConfiguration, recoveryObjectStore *api.BarmanObjectStoreConfiguration,
recoveryObjectStoreName string,
serverName string, serverName string,
) (*cnpgv1.Backup, []string, error) { ) (*cnpgv1.Backup, []string, error) {
contextLogger := log.FromContext(ctx) contextLogger := log.FromContext(ctx)
@ -337,12 +348,13 @@ func loadBackupObjectFromExternalCluster(
"serverName", serverName, "serverName", serverName,
"objectStore", recoveryObjectStore) "objectStore", recoveryObjectStore)
env, err := barmanCredentials.EnvSetRestoreCloudCredentials( env, err := barmanCredentials.EnvSetCloudCredentialsAndCertificates(
ctx, ctx,
typedClient, typedClient,
cluster.Namespace, cluster.Namespace,
recoveryObjectStore, recoveryObjectStore,
os.Environ()) os.Environ(),
common.BuildCertificateFilePath(recoveryObjectStoreName))
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }