diff --git a/.wordlist-en-custom.txt b/.wordlist-en-custom.txt index 42d7a6fa5b..ec5850464a 100644 --- a/.wordlist-en-custom.txt +++ b/.wordlist-en-custom.txt @@ -154,6 +154,8 @@ EnvVar EphemeralVolumeSource EphemeralVolumesSizeLimit EphemeralVolumesSizeLimitConfiguration +ExtensionConfiguration +ExtensionConfigurationList ExternalCluster ExternalClusterList FQDN diff --git a/api/v1/cluster_funcs.go b/api/v1/cluster_funcs.go index 3ffba1a3da..f388121827 100644 --- a/api/v1/cluster_funcs.go +++ b/api/v1/cluster_funcs.go @@ -1109,6 +1109,11 @@ func (cluster *Cluster) IsPodMonitorEnabled() bool { return false } +// UsesExtensions checks if Extensions are requested +func (cluster *Cluster) UsesExtensions() bool { + return len(cluster.Spec.Extensions) != 0 +} + // IsMetricsTLSEnabled checks if the metrics endpoint should use TLS func (cluster *Cluster) IsMetricsTLSEnabled() bool { if cluster.Spec.Monitoring != nil && cluster.Spec.Monitoring.TLSConfig != nil { diff --git a/api/v1/cluster_types.go b/api/v1/cluster_types.go index 2814f18cb6..e795ba432b 100644 --- a/api/v1/cluster_types.go +++ b/api/v1/cluster_types.go @@ -481,6 +481,29 @@ type ClusterSpec struct { // in the PostgreSQL Pods. // +optional Probes *ProbesConfiguration `json:"probes,omitempty"` + + // The configuration of the extensions to be added + // +optional + Extensions ExtensionConfigurationList `json:"extensions,omitempty"` +} + +// ExtensionConfigurationList is a list of ExtensionConfiguration +type ExtensionConfigurationList []ExtensionConfiguration + +// ExtensionConfiguration is the configuration used to add +// a PostgreSQL extensions to the Cluster +type ExtensionConfiguration struct { + // The name of the extension, required + Name string `json:"name"` + + // The image containing the extension, required + ImageVolumeSource corev1.ImageVolumeSource `json:"image"` + + // The path to the directory containing the extension files + ShareDirectoryPath string `json:"shareDirectoryPath,omitempty"` + + // The path to the directory containing the libraries + LibDirectoryPath string `json:"libDirectoryPath,omitempty"` } // ProbesConfiguration represent the configuration for the probes diff --git a/api/v1/zz_generated.deepcopy.go b/api/v1/zz_generated.deepcopy.go index fbaec944e2..837ca8e26b 100644 --- a/api/v1/zz_generated.deepcopy.go +++ b/api/v1/zz_generated.deepcopy.go @@ -848,6 +848,11 @@ func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { *out = new(ProbesConfiguration) (*in).DeepCopyInto(*out) } + if in.Extensions != nil { + in, out := &in.Extensions, &out.Extensions + *out = make(ExtensionConfigurationList, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSpec. @@ -1206,6 +1211,41 @@ func (in *EphemeralVolumesSizeLimitConfiguration) DeepCopy() *EphemeralVolumesSi return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ExtensionConfiguration) DeepCopyInto(out *ExtensionConfiguration) { + *out = *in + out.ImageVolumeSource = in.ImageVolumeSource +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionConfiguration. +func (in *ExtensionConfiguration) DeepCopy() *ExtensionConfiguration { + if in == nil { + return nil + } + out := new(ExtensionConfiguration) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in ExtensionConfigurationList) DeepCopyInto(out *ExtensionConfigurationList) { + { + in := &in + *out = make(ExtensionConfigurationList, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ExtensionConfigurationList. +func (in ExtensionConfigurationList) DeepCopy() ExtensionConfigurationList { + if in == nil { + return nil + } + out := new(ExtensionConfigurationList) + in.DeepCopyInto(out) + return *out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ExternalCluster) DeepCopyInto(out *ExternalCluster) { *out = *in diff --git a/config/crd/bases/postgresql.cnpg.io_clusters.yaml b/config/crd/bases/postgresql.cnpg.io_clusters.yaml index e185082fa7..6279b07367 100644 --- a/config/crd/bases/postgresql.cnpg.io_clusters.yaml +++ b/config/crd/bases/postgresql.cnpg.io_clusters.yaml @@ -2549,6 +2549,49 @@ spec: pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ x-kubernetes-int-or-string: true type: object + extensions: + description: The configuration of the extensions to be added + items: + description: |- + ExtensionConfiguration is the configuration used to add + a PostgreSQL extensions to the Cluster + properties: + image: + description: The image containing the extension, required + properties: + pullPolicy: + description: |- + Policy for pulling OCI objects. Possible values are: + Always: the kubelet always attempts to pull the reference. Container creation will fail If the pull fails. + Never: the kubelet never pulls the reference and only uses a local image or artifact. Container creation will fail if the reference isn't present. + IfNotPresent: the kubelet pulls if the reference isn't already present on disk. Container creation will fail if the reference isn't present and the pull fails. + Defaults to Always if :latest tag is specified, or IfNotPresent otherwise. + type: string + reference: + description: |- + Required: Image or artifact reference to be used. + Behaves in the same way as pod.spec.containers[*].image. + Pull secrets will be assembled in the same way as for the container image by looking up node credentials, SA image pull secrets, and pod spec image pull secrets. + More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config management to default or override + container images in workload controllers like Deployments and StatefulSets. + type: string + type: object + libDirectoryPath: + description: The path to the directory containing the libraries + type: string + name: + description: The name of the extension, required + type: string + shareDirectoryPath: + description: The path to the directory containing the extension + files + type: string + required: + - image + - name + type: object + type: array externalClusters: description: The list of external clusters which are used in the configuration items: diff --git a/docs/src/cloudnative-pg.v1.md b/docs/src/cloudnative-pg.v1.md index 48b53866ef..3838150c07 100644 --- a/docs/src/cloudnative-pg.v1.md +++ b/docs/src/cloudnative-pg.v1.md @@ -1940,6 +1940,13 @@ any plugin to be loaded with the corresponding configuration

in the PostgreSQL Pods.

+extensions
+ExtensionConfigurationList + + +

The configuration of the extensions to be added

+ + @@ -2712,6 +2719,20 @@ storage

+## ExtensionConfigurationList {#postgresql-cnpg-io-v1-ExtensionConfigurationList} + +(Alias of `[]github.com/cloudnative-pg/cloudnative-pg/api/v1.ExtensionConfiguration`) + +**Appears in:** + +- [ClusterSpec](#postgresql-cnpg-io-v1-ClusterSpec) + + +

ExtensionConfigurationList is a list of ExtensionConfiguration

+ + + + ## ImageCatalogRef {#postgresql-cnpg-io-v1-ImageCatalogRef} diff --git a/pkg/specs/volumes.go b/pkg/specs/volumes.go index ffa755054f..1f2211a3c6 100644 --- a/pkg/specs/volumes.go +++ b/pkg/specs/volumes.go @@ -144,6 +144,20 @@ func createPostgresVolumes(cluster *apiv1.Cluster, podName string) []corev1.Volu if cluster.ShouldCreateProjectedVolume() { result = append(result, createProjectedVolume(cluster)) } + + if cluster.UsesExtensions() { + for _, extension := range cluster.Spec.Extensions { + result = append(result, + corev1.Volume{ + Name: extension.Name, + VolumeSource: corev1.VolumeSource{ + Image: &extension.ImageVolumeSource, + }, + }, + ) + } + } + return result } @@ -270,6 +284,18 @@ func createPostgresVolumeMounts(cluster apiv1.Cluster) []corev1.VolumeMount { ) } } + + if cluster.UsesExtensions() { + for _, extension := range cluster.Spec.Extensions { + volumeMounts = append(volumeMounts, + corev1.VolumeMount{ + Name: extension.Name, + MountPath: "/extensions", + }, + ) + } + } + return volumeMounts }