Add envFrom support to Postgresql spec (#3118)

* add envFrom support
* generate files

---------

Co-authored-by: Felix Kunde <felix-kunde@gmx.de>
This commit is contained in:
annielzy 2026-07-22 08:21:31 -07:00 committed by GitHub
parent 1b460310a2
commit ca9513c831
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 248 additions and 0 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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,

View File

@ -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"