From d11b23bd71ce97e135e7c97b4e8d5a565bb1b56a Mon Sep 17 00:00:00 2001 From: Stephane T Date: Thu, 14 Feb 2019 11:29:06 +0000 Subject: [PATCH] Add inherited_labels (#459) * add support for inherited_labels Signed-off-by: Stephane Tang * update docs with inherited_labels Signed-off-by: Stephane Tang --- docs/administrator.md | 51 +++++++++++++++++++ docs/reference/cluster_manifest.md | 18 +++++-- docs/reference/operator_parameters.md | 20 +++++--- ...gresql-operator-default-configuration.yaml | 11 ++-- .../v1/operator_configuration_type.go | 1 + pkg/cluster/k8sres.go | 8 +-- pkg/cluster/pod.go | 2 +- pkg/cluster/util.go | 17 ++++++- pkg/controller/operator_config.go | 1 + pkg/util/config/config.go | 1 + 10 files changed, 108 insertions(+), 22 deletions(-) diff --git a/docs/administrator.md b/docs/administrator.md index a69dc99cf..8abd31c55 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -146,6 +146,57 @@ data: ... ``` +### Add cluster-specific labels + +In some cases, you might want to add `labels` that are specific to a given +postgres cluster, in order to identify its child objects. +The typical use case is to add labels that identifies the `Pods` created by the +operator, in order to implement fine-controlled `NetworkPolicies`. + +**OperatorConfiguration** + +```yaml +apiVersion: "acid.zalan.do/v1" +kind: OperatorConfiguration +metadata: + name: postgresql-operator-configuration +configuration: + kubernetes: + inherited_labels: + - application + - environment +... +``` + +**cluster manifest** + +```yaml +apiVersion: "acid.zalan.do/v1" +kind: postgresql +metadata: + name: demo-cluster + labels: + application: my-app + environment: demo +spec: +... +``` + +**network policy** + +```yaml +kind: NetworkPolicy +apiVersion: networking.k8s.io/v1 +metadata: + name: netpol-example +spec: + podSelector: + matchLabels: + application: my-app + environment: demo +... +``` + ## Custom Pod Environment Variables It is possible to configure a ConfigMap which is used by the Postgres pods as diff --git a/docs/reference/cluster_manifest.md b/docs/reference/cluster_manifest.md index efc850aff..a7c0e9840 100644 --- a/docs/reference/cluster_manifest.md +++ b/docs/reference/cluster_manifest.md @@ -33,7 +33,15 @@ Those parameters are grouped under the `metadata` top-level key. services, secrets) for the cluster. Changing it after the cluster creation results in deploying or updating a completely separate cluster in the target namespace. Optional (if present, should match the namespace where the - manifest is applied). + manifest is applied). + +* **labels** + if labels are matching one of the `inherited_labels` [configured in the + operator parameters](operator_parameters.md#kubernetes-resources), + they will automatically be added to all the objects (StatefulSet, Service, + Endpoints, etc.) that are created by the operator. + Labels that are set here but not listed as `inherited_labels` in the operator + parameters are ignored. ## Top-level parameters @@ -89,7 +97,7 @@ Those are parameters grouped directly under the `spec` key in the manifest. examples](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/) for details on tolerations and possible values of those keys. When set, this value overrides the `pod_toleration` setting from the operator. Optional. - + * **podPriorityClassName** a name of the [priority class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) @@ -135,7 +143,7 @@ explanation of `ttl` and `loop_wait` parameters. a map of key-value pairs describing initdb parameters. For `data-checksum`, `debug`, `no-locale`, `noclean`, `nosync` and `sync-only` parameters use `true` as the value if you want to set them. Changes to this option do not - affect the already initialized clusters. Optional. + affect the already initialized clusters. Optional. * **pg_hba** list of custom `pg_hba` lines to replace default ones. Note that the default @@ -215,7 +223,7 @@ under the `clone` top-level key and do not affect the already running cluster. different namespaces) , the operator uses UID in the S3 bucket name in order to guarantee uniqueness. Has no effect when cloning from the running clusters. Optional. - + * **timestamp** the timestamp up to which the recovery should proceed. The operator always configures non-inclusive recovery target, stopping right before the given @@ -235,7 +243,7 @@ properties of the persistent storage that stores postgres data. the name of the Kubernetes storage class to draw the persistent volume from. See [Kubernetes documentation](https://kubernetes.io/docs/concepts/storage/storage-classes/) - for the details on storage classes. Optional. + for the details on storage classes. Optional. ### Sidecar definitions diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 39320fa76..06a779c1e 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -12,12 +12,12 @@ configuration. * CRD-based configuration. The configuration is stored in a custom YAML manifest. The manifest is an instance of the custom resource definition (CRD) called - `OperatorConfiguration`. The operator registers this CRD + `OperatorConfiguration`. The operator registers this CRD during the start and uses it for configuration if the [operator deployment manifest ](https://github.com/zalando-incubator/postgres-operator/blob/master/manifests/postgres-operator.yaml#L21) sets the `POSTGRES_OPERATOR_CONFIGURATION_OBJECT` env variable to a non-empty value. The variable should point to the `postgresql-operator-configuration` object in the operator's namespace. The CRD-based configuration is a regular YAML - document; non-scalar keys are simply represented in the usual YAML way. + document; non-scalar keys are simply represented in the usual YAML way. There are no default values built-in in the operator, each parameter that is not supplied in the configuration receives an empty value. In order to create your own configuration just copy the [default @@ -172,6 +172,14 @@ configuration they are grouped under the `kubernetes` key. list of `name:value` pairs for additional labels assigned to the cluster objects. The default is `application:spilo`. +* **inherited_labels** + list of labels that can be inherited from the cluster manifest, and added to + each child objects (`StatefulSet`, `Pod`, `Service` and `Endpoints`) created by + the opertor. + Typical use case is to dynamically pass labels that are specific to a given + postgres cluster, in order to implement `NetworkPolicy`. + The default is empty. + * **cluster_name_label** name of the label assigned to Kubernetes objects created by the operator that indicates which cluster a given object belongs to. The default is @@ -198,13 +206,13 @@ configuration they are grouped under the `kubernetes` key. All variables from that ConfigMap are injected to the pod's environment, on conflicts they are overridden by the environment variables generated by the operator. The default is empty. - + * **pod_priority_class_name** a name of the [priority class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) that should be assigned to the Postgres pods. The priority class itself must be defined in advance. Default is empty (use the default priority class). - + ## Kubernetes resource requests @@ -350,7 +358,7 @@ Options to aid debugging of the operator itself. Grouped under the `debug` key. boolean parameter that toggles the functionality of the operator that require access to the postgres database, i.e. creating databases and users. The default is `true`. - + ## Automatic creation of human users in the database Options to automate creation of human users with the aid of the teams API @@ -448,4 +456,4 @@ scalyr sidecar. In the CRD-based configuration they are grouped under the Memory limit value for the Scalyr sidecar. The default is `1Gi`. -For the configmap operator configuration, the [default parameter values](https://github.com/zalando-incubator/postgres-operator/blob/master/pkg/util/config/config.go#L14) mentioned here are likely to be overwritten in your local operator installation via your local version of the operator configmap. In the case you use the operator CRD, all the CRD defaults are provided in the [operator's default configuration manifest](https://github.com/zalando-incubator/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml) \ No newline at end of file +For the configmap operator configuration, the [default parameter values](https://github.com/zalando-incubator/postgres-operator/blob/master/pkg/util/config/config.go#L14) mentioned here are likely to be overwritten in your local operator installation via your local version of the operator configmap. In the case you use the operator CRD, all the CRD defaults are provided in the [operator's default configuration manifest](https://github.com/zalando-incubator/postgres-operator/blob/master/manifests/postgresql-operator-default-configuration.yaml) diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index cba8ea38e..5b9de1073 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -25,8 +25,11 @@ configuration: pod_role_label: spilo-role cluster_labels: application: spilo + # inherited_labels: + # - application + # - app cluster_name_label: cluster-name - # watched_namespace:"" + # watched_namespace:"" # node_readiness_label: "" # toleration: {} # infrastructure_roles_secret_name: "" @@ -53,7 +56,7 @@ configuration: replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" aws_or_gcp: # db_hosted_zone: "" - # wal_s3_bucket: "" + # wal_s3_bucket: "" # log_s3_bucket: "" # kube_iam_role: "" aws_region: eu-central-1 @@ -62,13 +65,13 @@ configuration: enable_database_access: true teams_api: enable_teams_api: false - team_api_role_configuration: + team_api_role_configuration: log_statement: all enable_team_superuser: false team_admin_role: admin pam_role_name: zalandos # pam_configuration: "" - protected_role_names: + protected_role_names: - admin # teams_api_url: "" # postgres_superuser_teams: "postgres_superusers" diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index f5aac03b6..1b6939dfa 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -52,6 +52,7 @@ type KubernetesMetaConfiguration struct { InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` PodRoleLabel string `json:"pod_role_label,omitempty"` ClusterLabels map[string]string `json:"cluster_labels,omitempty"` + InheritedLabels []string `json:"inherited_labels,omitempty"` ClusterNameLabel string `json:"cluster_name_label,omitempty"` NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` // TODO: use a proper toleration structure? diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index cbc5f8bd7..c0ae1648c 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -1073,7 +1073,7 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec) } if role == Replica { - serviceSpec.Selector = c.roleLabelsSet(role) + serviceSpec.Selector = c.roleLabelsSet(false, role) } var annotations map[string]string @@ -1113,7 +1113,7 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec) ObjectMeta: metav1.ObjectMeta{ Name: c.serviceName(role), Namespace: c.Namespace, - Labels: c.roleLabelsSet(role), + Labels: c.roleLabelsSet(true, role), Annotations: annotations, }, Spec: serviceSpec, @@ -1127,7 +1127,7 @@ func (c *Cluster) generateEndpoint(role PostgresRole, subsets []v1.EndpointSubse ObjectMeta: metav1.ObjectMeta{ Name: c.endpointName(role), Namespace: c.Namespace, - Labels: c.roleLabelsSet(role), + Labels: c.roleLabelsSet(true, role), }, } if len(subsets) > 0 { @@ -1191,7 +1191,7 @@ func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget Spec: policybeta1.PodDisruptionBudgetSpec{ MinAvailable: &minAvailable, Selector: &metav1.LabelSelector{ - MatchLabels: c.roleLabelsSet(Master), + MatchLabels: c.roleLabelsSet(false, Master), }, }, } diff --git a/pkg/cluster/pod.go b/pkg/cluster/pod.go index 7fc068bf2..52b8b4e09 100644 --- a/pkg/cluster/pod.go +++ b/pkg/cluster/pod.go @@ -27,7 +27,7 @@ func (c *Cluster) listPods() ([]v1.Pod, error) { func (c *Cluster) getRolePods(role PostgresRole) ([]v1.Pod, error) { listOptions := metav1.ListOptions{ - LabelSelector: c.roleLabelsSet(role).String(), + LabelSelector: c.roleLabelsSet(false, role).String(), } pods, err := c.KubeClient.Pods(c.Namespace).List(listOptions) diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index f6e2ff096..74a009d2c 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -389,6 +389,19 @@ func (c *Cluster) labelsSet(shouldAddExtraLabels bool) labels.Set { if shouldAddExtraLabels { // enables filtering resources owned by a team lbls["team"] = c.Postgresql.Spec.TeamID + + // allow to inherit certain labels from the 'postgres' object + if spec, err := c.GetSpec(); err == nil { + for k, v := range spec.ObjectMeta.Labels { + for _, match := range c.OpConfig.InheritedLabels { + if k == match { + lbls[k] = v + } + } + } + } else { + c.logger.Warningf("could not get the list of InheritedLabels for cluster %q: %v", c.Name, err) + } } return labels.Set(lbls) @@ -398,8 +411,8 @@ func (c *Cluster) labelsSelector() *metav1.LabelSelector { return &metav1.LabelSelector{MatchLabels: c.labelsSet(false), MatchExpressions: nil} } -func (c *Cluster) roleLabelsSet(role PostgresRole) labels.Set { - lbls := c.labelsSet(false) +func (c *Cluster) roleLabelsSet(shouldAddExtraLabels bool, role PostgresRole) labels.Set { + lbls := c.labelsSet(shouldAddExtraLabels) lbls[c.OpConfig.PodRoleLabel] = string(role) return lbls } diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 179a9dee7..74549cbb8 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -48,6 +48,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.InfrastructureRolesSecretName = fromCRD.Kubernetes.InfrastructureRolesSecretName result.PodRoleLabel = fromCRD.Kubernetes.PodRoleLabel result.ClusterLabels = fromCRD.Kubernetes.ClusterLabels + result.InheritedLabels = fromCRD.Kubernetes.InheritedLabels result.ClusterNameLabel = fromCRD.Kubernetes.ClusterNameLabel result.NodeReadinessLabel = fromCRD.Kubernetes.NodeReadinessLabel result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 31cda4b98..371b7cb65 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -27,6 +27,7 @@ type Resources struct { PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` PodPriorityClassName string `name:"pod_priority_class_name"` ClusterLabels map[string]string `name:"cluster_labels" default:"application:spilo"` + InheritedLabels []string `name:"inherited_labels" default:""` ClusterNameLabel string `name:"cluster_name_label" default:"cluster-name"` PodRoleLabel string `name:"pod_role_label" default:"spilo-role"` PodToleration map[string]string `name:"toleration" default:""`