Skip to content

Commit 535517c

Browse files
runyontrFxKu
authored andcommitted
Custom annotations 329 (zalando#657)
* Add ability for custom annotations to database pods
1 parent 33e1d60 commit 535517c

15 files changed

+160
-13
lines changed

charts/postgres-operator/values-crd.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ configKubernetes:
5353
cluster_domain: cluster.local
5454
# additional labels assigned to the cluster objects
5555
cluster_labels:
56-
application: spilo
56+
application: spilo
5757
# label assigned to Kubernetes objects created by the operator
5858
cluster_name_label: cluster-name
59+
# additional annotations to add to every database pod
60+
custom_pod_annotations:
5961
# toggles pod anti affinity on the Postgres pods
6062
enable_pod_antiaffinity: false
6163
# toggles PDB to set to MinAvailabe 0 or 1

charts/postgres-operator/values.yaml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ configKubernetes:
5454
cluster_labels: application:spilo
5555
# label assigned to Kubernetes objects created by the operator
5656
cluster_name_label: version
57+
# annotations attached to each database pod
58+
# custom_pod_annotations: keya:valuea
5759
# toggles pod anti affinity on the Postgres pods
5860
enable_pod_antiaffinity: "false"
5961
# toggles PDB to set to MinAvailabe 0 or 1
@@ -127,8 +129,7 @@ configLoadBalancer:
127129
# DNS zone for cluster DNS name when load balancer is configured for cluster
128130
db_hosted_zone: db.example.com
129131
# annotations to apply to service when load balancing is enabled
130-
# custom_service_annotations:
131-
# "keyx:valuez,keya:valuea"
132+
# custom_service_annotations: "keyx:valuez,keya:valuea"
132133

133134
# toggles service type load balancer pointing to the master pod of the cluster
134135
enable_master_load_balancer: "true"

docs/reference/cluster_manifest.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ These parameters are grouped directly under the `spec` key in the manifest.
118118
then the default priority class is taken. The priority class itself must be
119119
defined in advance. Optional.
120120

121+
* **podAnnotations**
122+
A map of key value pairs that gets attached as [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)
123+
to each pod created for the database.
124+
125+
121126
* **enableShmVolume**
122127
Start a database pod without limitations on shm memory. By default docker
123128
limit `/dev/shm` to `64M` (see e.g. the [docker

docs/reference/operator_parameters.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ configuration they are grouped under the `kubernetes` key.
168168
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)
169169
after this timeout. The default is `5m`.
170170

171+
* **custom_pod_annotations**
172+
This key/value map provides a list of annotations that get attached to each pod
173+
of a database created by the operator. If the annotation key is also provided
174+
by the database definition, the database definition value is used.
175+
171176
* **watched_namespace**
172177
The operator watches for Postgres objects in the given namespace. If not
173178
specified, the value is taken from the operator namespace. A special `*`

manifests/complete-postgres-manifest.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ spec:
2525
- 127.0.0.1/32
2626
databases:
2727
foo: zalando
28-
28+
# podAnnotations:
29+
# annotation.key: value
2930
# Expert section
3031

3132
enableShmVolume: true

manifests/configmap.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ data:
1111
cluster_history_entries: "1000"
1212
cluster_labels: application:spilo
1313
cluster_name_label: version
14-
# custom_service_annotations:
15-
# "keyx:valuez,keya:valuea"
14+
# custom_service_annotations: "keyx:valuez,keya:valuea"
15+
# custom_pod_annotations: "keya:valuea"
1616
db_hosted_zone: db.example.com
1717
debug_logging: "true"
1818
# default_cpu_limit: "3"
@@ -37,7 +37,7 @@ data:
3737
# logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup"
3838
# logical_backup_s3_bucket: "my-bucket-url"
3939
# logical_backup_schedule: "30 00 * * *"
40-
master_dns_name_format: '{cluster}.{team}.staging.{hostedzone}'
40+
master_dns_name_format: "{cluster}.{team}.staging.{hostedzone}"
4141
# master_pod_move_timeout: 10m
4242
# max_instances: "-1"
4343
# min_instances: "-1"
@@ -60,13 +60,13 @@ data:
6060
ready_wait_interval: 3s
6161
ready_wait_timeout: 30s
6262
repair_period: 5m
63-
replica_dns_name_format: '{cluster}-repl.{team}.staging.{hostedzone}'
63+
replica_dns_name_format: "{cluster}-repl.{team}.staging.{hostedzone}"
6464
replication_username: standby
6565
resource_check_interval: 3s
6666
resource_check_timeout: 10m
6767
resync_period: 5m
6868
ring_log_lines: "100"
69-
secret_name_template: '{username}.{cluster}.credentials'
69+
secret_name_template: "{username}.{cluster}.credentials"
7070
# sidecar_docker_images: ""
7171
# set_memory_request_to_limit: "false"
7272
spilo_privileged: "false"

manifests/postgresql-operator-default-configuration.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ configuration:
2222
cluster_labels:
2323
application: spilo
2424
cluster_name_label: cluster-name
25+
# custom_pod_annotations:
26+
# keya: valuea
27+
# keyb: valueb
2528
enable_pod_antiaffinity: false
2629
enable_pod_disruption_budget: true
2730
# infrastructure_roles_secret_name: ""

pkg/apis/acid.zalan.do/v1/operator_configuration_type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type KubernetesMetaConfiguration struct {
5959
InheritedLabels []string `json:"inherited_labels,omitempty"`
6060
ClusterNameLabel string `json:"cluster_name_label,omitempty"`
6161
NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"`
62+
CustomPodAnnotations map[string]string `json:"custom_pod_annotations,omitempty"`
6263
// TODO: use a proper toleration structure?
6364
PodToleration map[string]string `json:"toleration,omitempty"`
6465
// TODO: use namespacedname

pkg/apis/acid.zalan.do/v1/postgresql_type.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ type PostgresSpec struct {
5959
EnableLogicalBackup bool `json:"enableLogicalBackup,omitempty"`
6060
LogicalBackupSchedule string `json:"logicalBackupSchedule,omitempty"`
6161
StandbyCluster *StandbyDescription `json:"standby"`
62+
PodAnnotations map[string]string `json:"podAnnotations"`
6263

6364
// deprecated json tags
6465
InitContainersOld []v1.Container `json:"init_containers,omitempty"`

pkg/apis/acid.zalan.do/v1/util_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,16 @@ var postgresqlList = []struct {
437437
PostgresqlList{},
438438
errors.New("unexpected end of JSON input")}}
439439

440+
var annotations = []struct {
441+
in []byte
442+
annotations map[string]string
443+
err error
444+
}{{
445+
in: []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"podAnnotations": {"foo": "bar"},"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
446+
annotations: map[string]string{"foo": "bar"},
447+
err: nil},
448+
}
449+
440450
func mustParseTime(s string) metav1.Time {
441451
v, err := time.Parse("15:04", s)
442452
if err != nil {
@@ -482,6 +492,25 @@ func TestWeekdayTime(t *testing.T) {
482492
}
483493
}
484494

495+
func TestClusterAnnotations(t *testing.T) {
496+
for _, tt := range annotations {
497+
var cluster Postgresql
498+
err := cluster.UnmarshalJSON(tt.in)
499+
if err != nil {
500+
if tt.err == nil || err.Error() != tt.err.Error() {
501+
t.Errorf("Unable to marshal cluster with annotations: expected %v got %v", tt.err, err)
502+
}
503+
continue
504+
}
505+
for k, v := range cluster.Spec.PodAnnotations {
506+
found, expected := v, tt.annotations[k]
507+
if found != expected {
508+
t.Errorf("Didn't find correct value for key %v in for podAnnotations: Expected %v found %v", k, expected, found)
509+
}
510+
}
511+
}
512+
}
513+
485514
func TestClusterName(t *testing.T) {
486515
for _, tt := range clusterNames {
487516
name, err := extractClusterName(tt.in, tt.inTeam)

pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/cluster/cluster_test.go

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import (
1111
"github.com/zalando/postgres-operator/pkg/util/config"
1212
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
1313
"github.com/zalando/postgres-operator/pkg/util/teams"
14-
"k8s.io/api/core/v1"
14+
v1 "k8s.io/api/core/v1"
1515
)
1616

1717
const (
@@ -328,3 +328,57 @@ func TestShouldDeleteSecret(t *testing.T) {
328328
}
329329
}
330330
}
331+
332+
func TestPodAnnotations(t *testing.T) {
333+
testName := "TestPodAnnotations"
334+
tests := []struct {
335+
subTest string
336+
operator map[string]string
337+
database map[string]string
338+
merged map[string]string
339+
}{
340+
{
341+
subTest: "No Annotations",
342+
operator: make(map[string]string),
343+
database: make(map[string]string),
344+
merged: make(map[string]string),
345+
},
346+
{
347+
subTest: "Operator Config Annotations",
348+
operator: map[string]string{"foo": "bar"},
349+
database: make(map[string]string),
350+
merged: map[string]string{"foo": "bar"},
351+
},
352+
{
353+
subTest: "Database Config Annotations",
354+
operator: make(map[string]string),
355+
database: map[string]string{"foo": "bar"},
356+
merged: map[string]string{"foo": "bar"},
357+
},
358+
{
359+
subTest: "Database Config overrides Operator Config Annotations",
360+
operator: map[string]string{"foo": "bar", "global": "foo"},
361+
database: map[string]string{"foo": "baz", "local": "foo"},
362+
merged: map[string]string{"foo": "baz", "global": "foo", "local": "foo"},
363+
},
364+
}
365+
366+
for _, tt := range tests {
367+
cl.OpConfig.CustomPodAnnotations = tt.operator
368+
cl.Postgresql.Spec.PodAnnotations = tt.database
369+
370+
annotations := cl.generatePodAnnotations(&cl.Postgresql.Spec)
371+
for k, v := range annotations {
372+
if observed, expected := v, tt.merged[k]; observed != expected {
373+
t.Errorf("%v expects annotation value %v for key %v, but found %v",
374+
testName+"/"+tt.subTest, expected, observed, k)
375+
}
376+
}
377+
for k, v := range tt.merged {
378+
if observed, expected := annotations[k], v; observed != expected {
379+
t.Errorf("%v expects annotation value %v for key %v, but found %v",
380+
testName+"/"+tt.subTest, expected, observed, k)
381+
}
382+
}
383+
}
384+
}

pkg/cluster/k8sres.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -430,6 +430,7 @@ func mountShmVolumeNeeded(opConfig config.Config, pgSpec *acidv1.PostgresSpec) *
430430
func generatePodTemplate(
431431
namespace string,
432432
labels labels.Set,
433+
annotations map[string]string,
433434
spiloContainer *v1.Container,
434435
initContainers []v1.Container,
435436
sidecarContainers []v1.Container,
@@ -485,13 +486,17 @@ func generatePodTemplate(
485486

486487
template := v1.PodTemplateSpec{
487488
ObjectMeta: metav1.ObjectMeta{
488-
Labels: labels,
489-
Namespace: namespace,
489+
Labels: labels,
490+
Namespace: namespace,
491+
Annotations: annotations,
490492
},
491493
Spec: podSpec,
492494
}
493495
if kubeIAMRole != "" {
494-
template.Annotations = map[string]string{constants.KubeIAmAnnotation: kubeIAMRole}
496+
if template.Annotations == nil{
497+
template.Annotations = make(map[string]string)
498+
}
499+
template.Annotations[constants.KubeIAmAnnotation] = kubeIAMRole
495500
}
496501

497502
return &template, nil
@@ -881,10 +886,13 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
881886
effectiveFSGroup = spec.SpiloFSGroup
882887
}
883888

889+
annotations := c.generatePodAnnotations(spec)
890+
884891
// generate pod template for the statefulset, based on the spilo container and sidecars
885892
if podTemplate, err = generatePodTemplate(
886893
c.Namespace,
887894
c.labelsSet(true),
895+
annotations,
888896
spiloContainer,
889897
spec.InitContainers,
890898
sidecarContainers,
@@ -949,6 +957,24 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
949957
return statefulSet, nil
950958
}
951959

960+
func (c *Cluster) generatePodAnnotations(spec *acidv1.PostgresSpec) map[string]string {
961+
annotations := make(map[string]string)
962+
for k, v := range c.OpConfig.CustomPodAnnotations {
963+
annotations[k] = v
964+
}
965+
if spec != nil || spec.PodAnnotations != nil {
966+
for k, v := range spec.PodAnnotations {
967+
annotations[k] = v
968+
}
969+
}
970+
971+
if len(annotations) == 0 {
972+
return nil
973+
}
974+
975+
return annotations
976+
}
977+
952978
func generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dockerImage string,
953979
containerResources *acidv1.Resources, logger *logrus.Entry) *acidv1.Sidecar {
954980
if APIKey == "" || dockerImage == "" {
@@ -1462,10 +1488,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
14621488
},
14631489
}}
14641490

1491+
annotations := c.generatePodAnnotations(&c.Spec)
1492+
14651493
// re-use the method that generates DB pod templates
14661494
if podTemplate, err = generatePodTemplate(
14671495
c.Namespace,
14681496
labels,
1497+
annotations,
14691498
logicalBackupContainer,
14701499
[]v1.Container{},
14711500
[]v1.Container{},

pkg/controller/operator_config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
4141
result.ReplicationUsername = fromCRD.PostgresUsersConfiguration.ReplicationUsername
4242

4343
// kubernetes config
44+
result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
4445
result.PodServiceAccountName = fromCRD.Kubernetes.PodServiceAccountName
4546
result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
4647
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition

pkg/util/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ type Config struct {
109109
EnableMasterLoadBalancer bool `name:"enable_master_load_balancer" default:"true"`
110110
EnableReplicaLoadBalancer bool `name:"enable_replica_load_balancer" default:"false"`
111111
CustomServiceAnnotations map[string]string `name:"custom_service_annotations"`
112+
CustomPodAnnotations map[string]string `name:"custom_pod_annotations"`
112113
EnablePodAntiAffinity bool `name:"enable_pod_antiaffinity" default:"false"`
113114
PodAntiAffinityTopologyKey string `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
114115
// deprecated and kept for backward compatibility

0 commit comments

Comments
 (0)