mirror of
https://github.com/zalando/postgres-operator.git
synced 2026-10-21 01:22:41 +02:00
Allow pod environment variables to also be sourced from a secret (#946)
* Extend operator configuration to allow for a pod_environment_secret just like pod_environment_configmap * Add all keys from PodEnvironmentSecrets as ENV vars (using SecretKeyRef to protect the value) * Apply envVars from pod_environment_configmap and pod_environment_secrets before doing the global settings from the operator config. This allows them to be overriden by the user (via configmap / secret) * Add ability use a Secret for custom pod envVars (via pod_environment_secret) to admin documentation * Add pod_environment_secret to Helm chart values.yaml * Add unit tests for PodEnvironmentConfigMap and PodEnvironmentSecret - highly inspired by @kupson and his very similar PR #481 * Added new parameter pod_environment_secret to operatorconfig CRD and configmap examples * Add pod_environment_secret to the operationconfiguration CRD Co-authored-by: Christian Rohmann <christian.rohmann@inovex.de>
This commit is contained in:
co-authored by
Christian Rohmann
parent
102a353649
commit
ece341d516
@@ -1,6 +1,7 @@
|
||||
package cluster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
|
||||
"github.com/zalando/postgres-operator/pkg/spec"
|
||||
"github.com/zalando/postgres-operator/pkg/util"
|
||||
"github.com/zalando/postgres-operator/pkg/util/config"
|
||||
"github.com/zalando/postgres-operator/pkg/util/constants"
|
||||
@@ -22,6 +24,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/apimachinery/pkg/util/intstr"
|
||||
v1core "k8s.io/client-go/kubernetes/typed/core/v1"
|
||||
)
|
||||
|
||||
// For testing purposes
|
||||
@@ -713,6 +716,211 @@ func TestSecretVolume(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
testPodEnvironmentConfigMapName = "pod_env_cm"
|
||||
testPodEnvironmentSecretName = "pod_env_sc"
|
||||
)
|
||||
|
||||
type mockSecret struct {
|
||||
v1core.SecretInterface
|
||||
}
|
||||
|
||||
type mockConfigMap struct {
|
||||
v1core.ConfigMapInterface
|
||||
}
|
||||
|
||||
func (c *mockSecret) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1.Secret, error) {
|
||||
if name != testPodEnvironmentSecretName {
|
||||
return nil, fmt.Errorf("Secret PodEnvironmentSecret not found")
|
||||
}
|
||||
secret := &v1.Secret{}
|
||||
secret.Name = testPodEnvironmentSecretName
|
||||
secret.Data = map[string][]byte{
|
||||
"minio_access_key": []byte("alpha"),
|
||||
"minio_secret_key": []byte("beta"),
|
||||
}
|
||||
return secret, nil
|
||||
}
|
||||
|
||||
func (c *mockConfigMap) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1.ConfigMap, error) {
|
||||
if name != testPodEnvironmentConfigMapName {
|
||||
return nil, fmt.Errorf("NotFound")
|
||||
}
|
||||
configmap := &v1.ConfigMap{}
|
||||
configmap.Name = testPodEnvironmentConfigMapName
|
||||
configmap.Data = map[string]string{
|
||||
"foo": "bar",
|
||||
}
|
||||
return configmap, nil
|
||||
}
|
||||
|
||||
type MockSecretGetter struct {
|
||||
}
|
||||
|
||||
type MockConfigMapsGetter struct {
|
||||
}
|
||||
|
||||
func (c *MockSecretGetter) Secrets(namespace string) v1core.SecretInterface {
|
||||
return &mockSecret{}
|
||||
}
|
||||
|
||||
func (c *MockConfigMapsGetter) ConfigMaps(namespace string) v1core.ConfigMapInterface {
|
||||
return &mockConfigMap{}
|
||||
}
|
||||
|
||||
func newMockKubernetesClient() k8sutil.KubernetesClient {
|
||||
return k8sutil.KubernetesClient{
|
||||
SecretsGetter: &MockSecretGetter{},
|
||||
ConfigMapsGetter: &MockConfigMapsGetter{},
|
||||
}
|
||||
}
|
||||
func newMockCluster(opConfig config.Config) *Cluster {
|
||||
cluster := &Cluster{
|
||||
Config: Config{OpConfig: opConfig},
|
||||
KubeClient: newMockKubernetesClient(),
|
||||
}
|
||||
return cluster
|
||||
}
|
||||
|
||||
func TestPodEnvironmentConfigMapVariables(t *testing.T) {
|
||||
testName := "TestPodEnvironmentConfigMapVariables"
|
||||
tests := []struct {
|
||||
subTest string
|
||||
opConfig config.Config
|
||||
envVars []v1.EnvVar
|
||||
err error
|
||||
}{
|
||||
{
|
||||
subTest: "no PodEnvironmentConfigMap",
|
||||
envVars: []v1.EnvVar{},
|
||||
},
|
||||
{
|
||||
subTest: "missing PodEnvironmentConfigMap",
|
||||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentConfigMap: spec.NamespacedName{
|
||||
Name: "idonotexist",
|
||||
},
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("could not read PodEnvironmentConfigMap: NotFound"),
|
||||
},
|
||||
{
|
||||
subTest: "simple PodEnvironmentConfigMap",
|
||||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentConfigMap: spec.NamespacedName{
|
||||
Name: testPodEnvironmentConfigMapName,
|
||||
},
|
||||
},
|
||||
},
|
||||
envVars: []v1.EnvVar{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
c := newMockCluster(tt.opConfig)
|
||||
vars, err := c.getPodEnvironmentConfigMapVariables()
|
||||
if !reflect.DeepEqual(vars, tt.envVars) {
|
||||
t.Errorf("%s %s: expected `%v` but got `%v`",
|
||||
testName, tt.subTest, tt.envVars, vars)
|
||||
}
|
||||
if tt.err != nil {
|
||||
if err.Error() != tt.err.Error() {
|
||||
t.Errorf("%s %s: expected error `%v` but got `%v`",
|
||||
testName, tt.subTest, tt.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("%s %s: expected no error but got error: `%v`",
|
||||
testName, tt.subTest, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test if the keys of an existing secret are properly referenced
|
||||
func TestPodEnvironmentSecretVariables(t *testing.T) {
|
||||
testName := "TestPodEnvironmentSecretVariables"
|
||||
tests := []struct {
|
||||
subTest string
|
||||
opConfig config.Config
|
||||
envVars []v1.EnvVar
|
||||
err error
|
||||
}{
|
||||
{
|
||||
subTest: "No PodEnvironmentSecret configured",
|
||||
envVars: []v1.EnvVar{},
|
||||
},
|
||||
{
|
||||
subTest: "Secret referenced by PodEnvironmentSecret does not exist",
|
||||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: "idonotexist",
|
||||
},
|
||||
},
|
||||
err: fmt.Errorf("could not read Secret PodEnvironmentSecretName: Secret PodEnvironmentSecret not found"),
|
||||
},
|
||||
{
|
||||
subTest: "Pod environment vars reference all keys from secret configured by PodEnvironmentSecret",
|
||||
opConfig: config.Config{
|
||||
Resources: config.Resources{
|
||||
PodEnvironmentSecret: testPodEnvironmentSecretName,
|
||||
},
|
||||
},
|
||||
envVars: []v1.EnvVar{
|
||||
{
|
||||
Name: "minio_access_key",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
SecretKeyRef: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: testPodEnvironmentSecretName,
|
||||
},
|
||||
Key: "minio_access_key",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "minio_secret_key",
|
||||
ValueFrom: &v1.EnvVarSource{
|
||||
SecretKeyRef: &v1.SecretKeySelector{
|
||||
LocalObjectReference: v1.LocalObjectReference{
|
||||
Name: testPodEnvironmentSecretName,
|
||||
},
|
||||
Key: "minio_secret_key",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
c := newMockCluster(tt.opConfig)
|
||||
vars, err := c.getPodEnvironmentSecretVariables()
|
||||
if !reflect.DeepEqual(vars, tt.envVars) {
|
||||
t.Errorf("%s %s: expected `%v` but got `%v`",
|
||||
testName, tt.subTest, tt.envVars, vars)
|
||||
}
|
||||
if tt.err != nil {
|
||||
if err.Error() != tt.err.Error() {
|
||||
t.Errorf("%s %s: expected error `%v` but got `%v`",
|
||||
testName, tt.subTest, tt.err, err)
|
||||
}
|
||||
} else {
|
||||
if err != nil {
|
||||
t.Errorf("%s %s: expected no error but got error: `%v`",
|
||||
testName, tt.subTest, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func testResources(cluster *Cluster, podSpec *v1.PodTemplateSpec) error {
|
||||
cpuReq := podSpec.Spec.Containers[0].Resources.Requests["cpu"]
|
||||
if cpuReq.String() != cluster.OpConfig.ConnectionPooler.ConnectionPoolerDefaultCPURequest {
|
||||
|
||||
Reference in New Issue
Block a user