Add inherited_labels (#459)

* add support for inherited_labels

Signed-off-by: Stephane Tang <hi@stang.sh>

* update docs with inherited_labels

Signed-off-by: Stephane Tang <hi@stang.sh>
This commit is contained in:
Stephane T 2019-02-14 11:29:06 +00:00 committed by Sergey Dudoladov
parent f2dddb0f2b
commit d11b23bd71
10 changed files with 108 additions and 22 deletions

View File

@ -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 ## Custom Pod Environment Variables
It is possible to configure a ConfigMap which is used by the Postgres pods as It is possible to configure a ConfigMap which is used by the Postgres pods as

View File

@ -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 services, secrets) for the cluster. Changing it after the cluster creation
results in deploying or updating a completely separate cluster in the target results in deploying or updating a completely separate cluster in the target
namespace. Optional (if present, should match the namespace where the 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 ## 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/) examples](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
for details on tolerations and possible values of those keys. When set, this for details on tolerations and possible values of those keys. When set, this
value overrides the `pod_toleration` setting from the operator. Optional. value overrides the `pod_toleration` setting from the operator. Optional.
* **podPriorityClassName** * **podPriorityClassName**
a name of the [priority a name of the [priority
class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) 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`, a map of key-value pairs describing initdb parameters. For `data-checksum`,
`debug`, `no-locale`, `noclean`, `nosync` and `sync-only` parameters use `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 `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** * **pg_hba**
list of custom `pg_hba` lines to replace default ones. Note that the default 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 different namespaces) , the operator uses UID in the S3 bucket name in order
to guarantee uniqueness. Has no effect when cloning from the running to guarantee uniqueness. Has no effect when cloning from the running
clusters. Optional. clusters. Optional.
* **timestamp** * **timestamp**
the timestamp up to which the recovery should proceed. The operator always the timestamp up to which the recovery should proceed. The operator always
configures non-inclusive recovery target, stopping right before the given 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. the name of the Kubernetes storage class to draw the persistent volume from.
See [Kubernetes See [Kubernetes
documentation](https://kubernetes.io/docs/concepts/storage/storage-classes/) 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 ### Sidecar definitions

View File

@ -12,12 +12,12 @@ configuration.
* CRD-based configuration. The configuration is stored in a custom YAML * CRD-based configuration. The configuration is stored in a custom YAML
manifest. The manifest is an instance of the custom resource definition (CRD) called 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 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. `postgresql-operator-configuration` object in the operator's namespace.
The CRD-based configuration is a regular YAML 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 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 not supplied in the configuration receives an empty value. In order to
create your own configuration just copy the [default 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 list of `name:value` pairs for additional labels assigned to the cluster
objects. The default is `application:spilo`. 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** * **cluster_name_label**
name of the label assigned to Kubernetes objects created by the operator that name of the label assigned to Kubernetes objects created by the operator that
indicates which cluster a given object belongs to. The default is 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 All variables from that ConfigMap are injected to the pod's environment, on
conflicts they are overridden by the environment variables generated by the conflicts they are overridden by the environment variables generated by the
operator. The default is empty. operator. The default is empty.
* **pod_priority_class_name** * **pod_priority_class_name**
a name of the [priority a name of the [priority
class](https://kubernetes.io/docs/concepts/configuration/pod-priority-preemption/#priorityclass) 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. 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). Default is empty (use the default priority class).
## Kubernetes resource requests ## 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 boolean parameter that toggles the functionality of the operator that require
access to the postgres database, i.e. creating databases and users. The default access to the postgres database, i.e. creating databases and users. The default
is `true`. is `true`.
## Automatic creation of human users in the database ## Automatic creation of human users in the database
Options to automate creation of human users with the aid of the teams API 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`. 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) 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)

View File

@ -25,8 +25,11 @@ configuration:
pod_role_label: spilo-role pod_role_label: spilo-role
cluster_labels: cluster_labels:
application: spilo application: spilo
# inherited_labels:
# - application
# - app
cluster_name_label: cluster-name cluster_name_label: cluster-name
# watched_namespace:"" # watched_namespace:""
# node_readiness_label: "" # node_readiness_label: ""
# toleration: {} # toleration: {}
# infrastructure_roles_secret_name: "" # infrastructure_roles_secret_name: ""
@ -53,7 +56,7 @@ configuration:
replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}" replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}"
aws_or_gcp: aws_or_gcp:
# db_hosted_zone: "" # db_hosted_zone: ""
# wal_s3_bucket: "" # wal_s3_bucket: ""
# log_s3_bucket: "" # log_s3_bucket: ""
# kube_iam_role: "" # kube_iam_role: ""
aws_region: eu-central-1 aws_region: eu-central-1
@ -62,13 +65,13 @@ configuration:
enable_database_access: true enable_database_access: true
teams_api: teams_api:
enable_teams_api: false enable_teams_api: false
team_api_role_configuration: team_api_role_configuration:
log_statement: all log_statement: all
enable_team_superuser: false enable_team_superuser: false
team_admin_role: admin team_admin_role: admin
pam_role_name: zalandos pam_role_name: zalandos
# pam_configuration: "" # pam_configuration: ""
protected_role_names: protected_role_names:
- admin - admin
# teams_api_url: "" # teams_api_url: ""
# postgres_superuser_teams: "postgres_superusers" # postgres_superuser_teams: "postgres_superusers"

View File

@ -52,6 +52,7 @@ type KubernetesMetaConfiguration struct {
InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"` InfrastructureRolesSecretName spec.NamespacedName `json:"infrastructure_roles_secret_name,omitempty"`
PodRoleLabel string `json:"pod_role_label,omitempty"` PodRoleLabel string `json:"pod_role_label,omitempty"`
ClusterLabels map[string]string `json:"cluster_labels,omitempty"` ClusterLabels map[string]string `json:"cluster_labels,omitempty"`
InheritedLabels []string `json:"inherited_labels,omitempty"`
ClusterNameLabel string `json:"cluster_name_label,omitempty"` ClusterNameLabel string `json:"cluster_name_label,omitempty"`
NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"` NodeReadinessLabel map[string]string `json:"node_readiness_label,omitempty"`
// TODO: use a proper toleration structure? // TODO: use a proper toleration structure?

View File

@ -1073,7 +1073,7 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec)
} }
if role == Replica { if role == Replica {
serviceSpec.Selector = c.roleLabelsSet(role) serviceSpec.Selector = c.roleLabelsSet(false, role)
} }
var annotations map[string]string var annotations map[string]string
@ -1113,7 +1113,7 @@ func (c *Cluster) generateService(role PostgresRole, spec *acidv1.PostgresSpec)
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: c.serviceName(role), Name: c.serviceName(role),
Namespace: c.Namespace, Namespace: c.Namespace,
Labels: c.roleLabelsSet(role), Labels: c.roleLabelsSet(true, role),
Annotations: annotations, Annotations: annotations,
}, },
Spec: serviceSpec, Spec: serviceSpec,
@ -1127,7 +1127,7 @@ func (c *Cluster) generateEndpoint(role PostgresRole, subsets []v1.EndpointSubse
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: c.endpointName(role), Name: c.endpointName(role),
Namespace: c.Namespace, Namespace: c.Namespace,
Labels: c.roleLabelsSet(role), Labels: c.roleLabelsSet(true, role),
}, },
} }
if len(subsets) > 0 { if len(subsets) > 0 {
@ -1191,7 +1191,7 @@ func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget
Spec: policybeta1.PodDisruptionBudgetSpec{ Spec: policybeta1.PodDisruptionBudgetSpec{
MinAvailable: &minAvailable, MinAvailable: &minAvailable,
Selector: &metav1.LabelSelector{ Selector: &metav1.LabelSelector{
MatchLabels: c.roleLabelsSet(Master), MatchLabels: c.roleLabelsSet(false, Master),
}, },
}, },
} }

View File

@ -27,7 +27,7 @@ func (c *Cluster) listPods() ([]v1.Pod, error) {
func (c *Cluster) getRolePods(role PostgresRole) ([]v1.Pod, error) { func (c *Cluster) getRolePods(role PostgresRole) ([]v1.Pod, error) {
listOptions := metav1.ListOptions{ listOptions := metav1.ListOptions{
LabelSelector: c.roleLabelsSet(role).String(), LabelSelector: c.roleLabelsSet(false, role).String(),
} }
pods, err := c.KubeClient.Pods(c.Namespace).List(listOptions) pods, err := c.KubeClient.Pods(c.Namespace).List(listOptions)

View File

@ -389,6 +389,19 @@ func (c *Cluster) labelsSet(shouldAddExtraLabels bool) labels.Set {
if shouldAddExtraLabels { if shouldAddExtraLabels {
// enables filtering resources owned by a team // enables filtering resources owned by a team
lbls["team"] = c.Postgresql.Spec.TeamID 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) return labels.Set(lbls)
@ -398,8 +411,8 @@ func (c *Cluster) labelsSelector() *metav1.LabelSelector {
return &metav1.LabelSelector{MatchLabels: c.labelsSet(false), MatchExpressions: nil} return &metav1.LabelSelector{MatchLabels: c.labelsSet(false), MatchExpressions: nil}
} }
func (c *Cluster) roleLabelsSet(role PostgresRole) labels.Set { func (c *Cluster) roleLabelsSet(shouldAddExtraLabels bool, role PostgresRole) labels.Set {
lbls := c.labelsSet(false) lbls := c.labelsSet(shouldAddExtraLabels)
lbls[c.OpConfig.PodRoleLabel] = string(role) lbls[c.OpConfig.PodRoleLabel] = string(role)
return lbls return lbls
} }

View File

@ -48,6 +48,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.InfrastructureRolesSecretName = fromCRD.Kubernetes.InfrastructureRolesSecretName result.InfrastructureRolesSecretName = fromCRD.Kubernetes.InfrastructureRolesSecretName
result.PodRoleLabel = fromCRD.Kubernetes.PodRoleLabel result.PodRoleLabel = fromCRD.Kubernetes.PodRoleLabel
result.ClusterLabels = fromCRD.Kubernetes.ClusterLabels result.ClusterLabels = fromCRD.Kubernetes.ClusterLabels
result.InheritedLabels = fromCRD.Kubernetes.InheritedLabels
result.ClusterNameLabel = fromCRD.Kubernetes.ClusterNameLabel result.ClusterNameLabel = fromCRD.Kubernetes.ClusterNameLabel
result.NodeReadinessLabel = fromCRD.Kubernetes.NodeReadinessLabel result.NodeReadinessLabel = fromCRD.Kubernetes.NodeReadinessLabel
result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName result.PodPriorityClassName = fromCRD.Kubernetes.PodPriorityClassName

View File

@ -27,6 +27,7 @@ type Resources struct {
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
PodPriorityClassName string `name:"pod_priority_class_name"` PodPriorityClassName string `name:"pod_priority_class_name"`
ClusterLabels map[string]string `name:"cluster_labels" default:"application:spilo"` 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"` ClusterNameLabel string `name:"cluster_name_label" default:"cluster-name"`
PodRoleLabel string `name:"pod_role_label" default:"spilo-role"` PodRoleLabel string `name:"pod_role_label" default:"spilo-role"`
PodToleration map[string]string `name:"toleration" default:""` PodToleration map[string]string `name:"toleration" default:""`