diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index f216797c0..bc0b108cc 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -15,6 +15,7 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/spec" "github.com/zalando-incubator/postgres-operator/pkg/util/constants" + "github.com/zalando-incubator/postgres-operator/pkg/util/k8sutil" ) const ( @@ -319,6 +320,7 @@ func (c *Cluster) generatePodTemplate( cloneDescription *spec.CloneDescription, dockerImage *string, customPodEnvVars map[string]string, + customPodSecrets map[string][]byte, ) *v1.PodTemplateSpec { spiloConfiguration := c.generateSpiloJSONConfiguration(pgParameters, patroniParameters) @@ -426,11 +428,43 @@ func (c *Cluster) generatePodTemplate( name, c.OpConfig.PodEnvironmentConfigMap) } } + sort.Strings(names) for _, name := range names { envVars = append(envVars, v1.EnvVar{Name: name, Value: customPodEnvVars[name]}) } + //TODO: move the variable comparing and sorting code to a sepatate function + var secretVarNames []string + // include references to the secrets provided in the operator configuration. + // TODO: what happens with the secrets located in a different namespace? + if c.OpConfig.PodEnvironmentSecret != "" { + for secretVarName := range customPodSecrets { + // environment variable names have scricter rules than secrets + if !k8sutil.EnvironmentVariableNameIsValid(secretVarName) { + c.logger.Warningf("Secret key %s cannot be a name of an environment variable") + continue + } + if _, ok := envVarsMap[secretVarName]; !ok { + secretVarNames = append(secretVarNames, secretVarName) + } else { + c.logger.Warningf("variable %q value from %q is ignored: conflict with the definition from the operator", + secretVarNames, c.OpConfig.PodEnvironmentConfigMap) + } + } + sort.Strings(secretVarNames) + for _, name := range secretVarNames { + envVars = append(envVars, v1.EnvVar{Name: name, ValueFrom: &v1.EnvVarSource{ + SecretKeyRef: &v1.SecretKeySelector{ + LocalObjectReference: v1.LocalObjectReference{ + Name: c.OpConfig.PodEnvironmentSecret, + }, + Key: name, + }, + }}) + } + } + privilegedMode := true containerImage := c.OpConfig.DockerImage if dockerImage != nil && *dockerImage != "" { @@ -575,7 +609,11 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu if err != nil { return nil, fmt.Errorf("could not generate Scalyr sidecar resource requirements: %v", err) } - var customPodEnvVars map[string]string + var ( + customPodEnvVars map[string]string + customPodSecrets map[string][]byte + ) + // TODO: pod environment configmaps are read from the cluster namespace if c.OpConfig.PodEnvironmentConfigMap != "" { if cm, err := c.KubeClient.ConfigMaps(c.Namespace).Get(c.OpConfig.PodEnvironmentConfigMap, metav1.GetOptions{}); err != nil { return nil, fmt.Errorf("could not read PodEnvironmentConfigMap: %v", err) @@ -583,7 +621,24 @@ func (c *Cluster) generateStatefulSet(spec *spec.PostgresSpec) (*v1beta1.Statefu customPodEnvVars = cm.Data } } - podTemplate := c.generatePodTemplate(c.Postgresql.GetUID(), resourceRequirements, resourceRequirementsScalyrSidecar, &spec.Tolerations, &spec.PostgresqlParam, &spec.Patroni, &spec.Clone, &spec.DockerImage, customPodEnvVars) + // TODO: pod environment secrets are read from the cluster namespace + if c.OpConfig.PodEnvironmentSecret != "" { + if secret, err := c.KubeClient.Secrets(c.Namespace).Get(c.OpConfig.PodEnvironmentSecret, metav1.GetOptions{}); err != nil { + return nil, fmt.Errorf("could not read PodEnvironmentSecret secrets", err) + } else { + customPodSecrets = secret.Data + } + } + podTemplate := c.generatePodTemplate(c.Postgresql.GetUID(), + resourceRequirements, + resourceRequirementsScalyrSidecar, + &spec.Tolerations, + &spec.PostgresqlParam, + &spec.Patroni, + &spec.Clone, + &spec.DockerImage, + customPodEnvVars, + customPodSecrets) volumeClaimTemplate, err := generatePersistentVolumeClaimTemplate(spec.Volume.Size, spec.Volume.StorageClass) if err != nil { return nil, fmt.Errorf("could not generate volume claim template: %v", err) diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 2bce980b1..dbc35f7ae 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -33,6 +33,7 @@ type Resources struct { DefaultCPULimit string `name:"default_cpu_limit" default:"3"` DefaultMemoryLimit string `name:"default_memory_limit" default:"1Gi"` PodEnvironmentConfigMap string `name:"pod_environment_configmap" default:""` + PodEnvironmentSecret string `name:"pod_environment_secret" default:""` NodeReadinessLabel map[string]string `name:"node_readiness_label" default:""` MaxInstances int32 `name:"max_instances" default:"-1"` MinInstances int32 `name:"min_instances" default:"-1"` diff --git a/pkg/util/k8sutil/k8sutil.go b/pkg/util/k8sutil/k8sutil.go index 142d4f822..b19489708 100644 --- a/pkg/util/k8sutil/k8sutil.go +++ b/pkg/util/k8sutil/k8sutil.go @@ -3,6 +3,7 @@ package k8sutil import ( "fmt" "reflect" + "regexp" apiextclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1" @@ -22,6 +23,14 @@ import ( "github.com/zalando-incubator/postgres-operator/pkg/util/constants" ) +const ( + EnvironmentNameRegexpString = "[-._a-zA-Z][-._a-zA-Z0-9]*" +) + +var ( + environmentNameRegexp = regexp.MustCompile(EnvironmentNameRegexpString) +) + // KubernetesClient describes getters for Kubernetes objects type KubernetesClient struct { v1core.SecretsGetter @@ -153,3 +162,7 @@ func SamePDB(cur, new *policybeta1.PodDisruptionBudget) (match bool, reason stri return } + +func EnvironmentVariableNameIsValid(name string) bool { + return environmentNameRegexp.MatchString(name) +}