diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 94db2d442..4d534c16e 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -454,6 +454,51 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: An optional identifier to prepend to each key in + the ConfigMap. Must be a C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array + x-kubernetes-list-type: atomic initContainers: items: description: A single application container that you want to run diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 94db2d442..0e3558638 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -454,6 +454,52 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set of ConfigMaps + or Secrets + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array initContainers: items: description: A single application container that you want to run diff --git a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml index 94db2d442..0e3558638 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml +++ b/pkg/apis/acid.zalan.do/v1/postgresql.crd.yaml @@ -454,6 +454,52 @@ spec: - name type: object type: array + envFrom: + items: + description: EnvFromSource represents the source of a set of ConfigMaps + or Secrets + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the ConfigMap must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + prefix: + description: |- + Optional text to prepend to the name of each environment variable. + May consist of any printable ASCII characters except '='. + type: string + secretRef: + description: The Secret to select from + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + optional: + description: Specify whether the Secret must be defined + type: boolean + type: object + x-kubernetes-map-type: atomic + type: object + type: array initContainers: items: description: A single application container that you want to run diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index 52d51cd41..a03bb13cc 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -124,6 +124,7 @@ type PostgresSpec struct { AdditionalVolumes []AdditionalVolume `json:"additionalVolumes,omitempty"` Streams []Stream `json:"streams,omitempty"` Env []v1.EnvVar `json:"env,omitempty"` + EnvFrom []v1.EnvFromSource `json:"envFrom,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 4601cf668..60003adfa 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -1016,6 +1016,13 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.EnvFrom != nil { + in, out := &in.EnvFrom, &out.EnvFrom + *out = make([]corev1.EnvFromSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } return } diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index b2c6bee68..2db1223b4 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -229,6 +229,63 @@ func TestStatefulSetUpdateWithEnv(t *testing.T) { } } +func TestStatefulSetUpdateWithEnvFrom(t *testing.T) { + oldSpec := &acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + } + oldSS, err := cl.generateStatefulSet(oldSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + newSpec := oldSpec.DeepCopy() + newSS, err := cl.generateStatefulSet(newSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + if !reflect.DeepEqual(oldSS, newSS) { + t.Errorf("in %s StatefulSet's must be equal", t.Name()) + } + + newSpec.EnvFrom = []v1.EnvFromSource{ + { + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "test-configmap", + }, + }, + }, + { + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{ + Name: "test-secret", + }, + }, + }, + } + newSS, err = cl.generateStatefulSet(newSpec) + if err != nil { + t.Errorf("in %s no StatefulSet created %v", t.Name(), err) + } + + if reflect.DeepEqual(oldSS, newSS) { + t.Errorf("in %s StatefulSet's must be not equal", t.Name()) + } + + postgresContainer := newSS.Spec.Template.Spec.Containers[0] + if !reflect.DeepEqual(postgresContainer.EnvFrom, newSpec.EnvFrom) { + t.Errorf("expected envFrom %v, got %v", newSpec.EnvFrom, postgresContainer.EnvFrom) + } +} + func TestInitRobotUsers(t *testing.T) { tests := []struct { testCase string diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 49aeab8a0..4814a5546 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -697,6 +697,7 @@ func generateContainer( dockerImage *string, resourceRequirements *v1.ResourceRequirements, envVars []v1.EnvVar, + envFrom []v1.EnvFromSource, volumeMounts []v1.VolumeMount, privilegedMode bool, privilegeEscalationMode *bool, @@ -723,6 +724,7 @@ func generateContainer( }, VolumeMounts: volumeMounts, Env: envVars, + EnvFrom: envFrom, SecurityContext: &v1.SecurityContext{ AllowPrivilegeEscalation: privilegeEscalationMode, Privileged: &privilegedMode, @@ -1385,6 +1387,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef &effectiveDockerImage, resourceRequirements, spiloEnvVars, + spec.EnvFrom, volumeMounts, c.OpConfig.Resources.SpiloPrivileged, c.OpConfig.Resources.SpiloAllowPrivilegeEscalation, @@ -2385,6 +2388,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1.CronJob, error) { &c.OpConfig.LogicalBackup.LogicalBackupDockerImage, resourceRequirements, envVars, + nil, []v1.VolumeMount{}, c.OpConfig.SpiloPrivileged, // use same value as for normal DB pods c.OpConfig.SpiloAllowPrivilegeEscalation, diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index cb2655777..c9ebc020f 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -4488,6 +4488,48 @@ func TestGenerateCapabilities(t *testing.T) { } } +func TestGenerateContainerWithEnvFrom(t *testing.T) { + dockerImage := "test-image" + resourceRequirements := &v1.ResourceRequirements{ + Requests: v1.ResourceList{ + v1.ResourceCPU: resource.MustParse("100m"), + v1.ResourceMemory: resource.MustParse("100Mi"), + }, + } + envVars := []v1.EnvVar{{Name: "TEST_VAR", Value: "test-value"}} + envFrom := []v1.EnvFromSource{ + { + ConfigMapRef: &v1.ConfigMapEnvSource{ + LocalObjectReference: v1.LocalObjectReference{Name: "test-configmap"}, + }, + }, + { + SecretRef: &v1.SecretEnvSource{ + LocalObjectReference: v1.LocalObjectReference{Name: "test-secret"}, + }, + }, + } + + container := generateContainer( + constants.PostgresContainerName, + &dockerImage, + resourceRequirements, + envVars, + envFrom, + []v1.VolumeMount{}, + false, + util.False(), + nil, + ) + + if !reflect.DeepEqual(container.Env, envVars) { + t.Errorf("expected env %v, got %v", envVars, container.Env) + } + if !reflect.DeepEqual(container.EnvFrom, envFrom) { + t.Errorf("expected envFrom %v, got %v", envFrom, container.EnvFrom) + } +} + func TestTopologySpreadConstraints(t *testing.T) { clusterName := "acid-test-cluster" namespace := "default"