Custom annotations 329 (#657)

* Add ability for custom annotations to database pods
This commit is contained in:
Thomas Runyon
2019-11-11 10:45:35 +01:00
committed by Felix Kunde
parent 33e1d60703
commit 535517cd1b
15 changed files with 160 additions and 13 deletions
@@ -59,6 +59,7 @@ type KubernetesMetaConfiguration struct {
InheritedLabels []string `json:"inherited_labels,omitempty"`
ClusterNameLabel string `json:"cluster_name_label,omitempty"`
NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"`
CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"`
// TODO: use a proper toleration structure?
PodToleration map[string]string `json:"toleration,omitempty"`
// TODO: use namespacedname
@@ -59,6 +59,7 @@ type PostgresSpec struct {
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
StandbyCluster *StandbyDescription `json:"standby"`
PodAnnotations map[string]string `json:"podAnnotations"`
// deprecated json tags
InitContainersOld []v1.Container `json:"init_containers,omitempty"`
+29
View File
@@ -437,6 +437,16 @@ var postgresqlList = []struct {
PostgresqlList{},
errors.New("unexpected end of JSON input")}}
var annotations = []struct {
in []byte
annotations map[string]string
err error
}{{
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"podAnnotations": {"foo": "bar"},"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
annotations: map[string]string{"foo": "bar"},
err: nil},
}
func mustParseTime(s string) metav1.Time {
v, err := time.Parse("15:04", s)
if err != nil {
@@ -482,6 +492,25 @@ func TestWeekdayTime(t *testing.T) {
}
}
func TestClusterAnnotations(t *testing.T) {
for _, tt := range annotations {
var cluster Postgresql
err := cluster.UnmarshalJSON(tt.in)
if err != nil {
if tt.err == nil || err.Error() != tt.err.Error() {
t.Errorf("Unable to marshal cluster with annotations: expected %v got %v", tt.err, err)
}
continue
}
for k, v := range cluster.Spec.PodAnnotations {
found, expected := v, tt.annotations[k]
if found != expected {
t.Errorf("Didn't find correct value for key %v in for podAnnotations: Expected %v found %v", k, expected, found)
}
}
}
}
func TestClusterName(t *testing.T) {
for _, tt := range clusterNames {
name, err := extractClusterName(tt.in, tt.inTeam)
@@ -102,6 +102,13 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
(*out)[key] = val
}
}
if in.CustomPodAnnotations != nil {
in, out := &in.CustomPodAnnotations, &out.CustomPodAnnotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.PodToleration != nil {
in, out := &in.PodToleration, &out.PodToleration
*out = make(map[string]string, len(*in))
@@ -513,6 +520,13 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
*out = new(StandbyDescription)
**out = **in
}
if in.PodAnnotations != nil {
in, out := &in.PodAnnotations, &out.PodAnnotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.InitContainersOld != nil {
in, out := &in.InitContainersOld, &out.InitContainersOld
*out = make([]corev1.Container, len(*in))
+55 -1
View File
@@ -11,7 +11,7 @@ import (
"github.com/zalando/postgres-operator/pkg/util/config"
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
"github.com/zalando/postgres-operator/pkg/util/teams"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
)
const (
@@ -328,3 +328,57 @@ func TestShouldDeleteSecret(t *testing.T) {
}
}
}
func TestPodAnnotations(t *testing.T) {
testName := "TestPodAnnotations"
tests := []struct {
subTest string
operator map[string]string
database map[string]string
merged map[string]string
}{
{
subTest: "No Annotations",
operator: make(map[string]string),
database: make(map[string]string),
merged: make(map[string]string),
},
{
subTest: "Operator Config Annotations",
operator: map[string]string{"foo": "bar"},
database: make(map[string]string),
merged: map[string]string{"foo": "bar"},
},
{
subTest: "Database Config Annotations",
operator: make(map[string]string),
database: map[string]string{"foo": "bar"},
merged: map[string]string{"foo": "bar"},
},
{
subTest: "Database Config overrides Operator Config Annotations",
operator: map[string]string{"foo": "bar", "global": "foo"},
database: map[string]string{"foo": "baz", "local": "foo"},
merged: map[string]string{"foo": "baz", "global": "foo", "local": "foo"},
},
}
for _, tt := range tests {
cl.OpConfig.CustomPodAnnotations = tt.operator
cl.Postgresql.Spec.PodAnnotations = tt.database
annotations := cl.generatePodAnnotations(&cl.Postgresql.Spec)
for k, v := range annotations {
if observed, expected := v, tt.merged[k]; observed != expected {
t.Errorf("%v expects annotation value %v for key %v, but found %v",
testName+"/"+tt.subTest, expected, observed, k)
}
}
for k, v := range tt.merged {
if observed, expected := annotations[k], v; observed != expected {
t.Errorf("%v expects annotation value %v for key %v, but found %v",
testName+"/"+tt.subTest, expected, observed, k)
}
}
}
}
+32 -3
View File
@@ -430,6 +430,7 @@ func mountShmVolumeNeeded(opConfig config.Config, pgSpec *acidv1.PostgresSpec) *
func generatePodTemplate(
namespace string,
labels labels.Set,
annotations map[string]string,
spiloContainer *v1.Container,
initContainers []v1.Container,
sidecarContainers []v1.Container,
@@ -485,13 +486,17 @@ func generatePodTemplate(
template := v1.PodTemplateSpec{
ObjectMeta: metav1.ObjectMeta{
Labels: labels,
Namespace: namespace,
Labels: labels,
Namespace: namespace,
Annotations: annotations,
},
Spec: podSpec,
}
if kubeIAMRole != "" {
template.Annotations = map[string]string{constants.KubeIAmAnnotation: kubeIAMRole}
if template.Annotations == nil{
template.Annotations = make(map[string]string)
}
template.Annotations[constants.KubeIAmAnnotation] = kubeIAMRole
}
return &template, nil
@@ -881,10 +886,13 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
effectiveFSGroup = spec.SpiloFSGroup
}
annotations := c.generatePodAnnotations(spec)
// generate pod template for the statefulset, based on the spilo container and sidecars
if podTemplate, err = generatePodTemplate(
c.Namespace,
c.labelsSet(true),
annotations,
spiloContainer,
spec.InitContainers,
sidecarContainers,
@@ -949,6 +957,24 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
return statefulSet, nil
}
func (c *Cluster) generatePodAnnotations(spec *acidv1.PostgresSpec) map[string]string {
annotations := make(map[string]string)
for k, v := range c.OpConfig.CustomPodAnnotations {
annotations[k] = v
}
if spec != nil || spec.PodAnnotations != nil {
for k, v := range spec.PodAnnotations {
annotations[k] = v
}
}
if len(annotations) == 0 {
return nil
}
return annotations
}
func generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dockerImage string,
containerResources *acidv1.Resources, logger *logrus.Entry) *acidv1.Sidecar {
if APIKey == "" || dockerImage == "" {
@@ -1462,10 +1488,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
},
}}
annotations := c.generatePodAnnotations(&c.Spec)
// re-use the method that generates DB pod templates
if podTemplate, err = generatePodTemplate(
c.Namespace,
labels,
annotations,
logicalBackupContainer,
[]v1.Container{},
[]v1.Container{},
+1
View File
@@ -41,6 +41,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.ReplicationUsername = fromCRD.PostgresUsersConfiguration.ReplicationUsername
// kubernetes config
result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
result.PodServiceAccountName = fromCRD.Kubernetes.PodServiceAccountName
result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
+1
View File
@@ -109,6 +109,7 @@ type Config struct {
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
CustomPodAnnotations map[string]string `name:"custom_pod_annotations"`
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
// deprecated and kept for backward compatibility