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

@ -35,6 +35,14 @@ Those parameters are grouped under the `metadata` top-level key.
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
Those are parameters grouped directly under the `spec` key in the manifest. Those are parameters grouped directly under the `spec` key in the manifest.

View File

@ -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

View File

@ -25,6 +25,9 @@ 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: ""

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:""`