improve RBAC setup for postgres pods
changed the default name, updated helm chart and docs allow role to use privileged podsecurtitypolicies
This commit is contained in:
parent
702a194c41
commit
cb96454a56
|
|
@ -148,6 +148,8 @@ spec:
|
||||||
type: string
|
type: string
|
||||||
pod_service_account_name:
|
pod_service_account_name:
|
||||||
type: string
|
type: string
|
||||||
|
pod_service_account_role_definition:
|
||||||
|
type: string
|
||||||
pod_service_account_role_binding_definition:
|
pod_service_account_role_binding_definition:
|
||||||
type: string
|
type: string
|
||||||
pod_terminate_grace_period:
|
pod_terminate_grace_period:
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ metadata:
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
rules:
|
rules:
|
||||||
|
# all verbs allowed for custom operator resources
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- acid.zalan.do
|
- acid.zalan.do
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -16,7 +17,15 @@ rules:
|
||||||
- postgresqls/status
|
- postgresqls/status
|
||||||
- operatorconfigurations
|
- operatorconfigurations
|
||||||
verbs:
|
verbs:
|
||||||
- "*"
|
- create
|
||||||
|
- delete
|
||||||
|
- deletecollection
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
# to create or get/update CRDs when starting up
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apiextensions.k8s.io
|
- apiextensions.k8s.io
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -26,12 +35,14 @@ rules:
|
||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
- update
|
- update
|
||||||
|
# to read configuration from ConfigMaps
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- configmaps
|
- configmaps
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
|
# to manage endpoints which are also used by Patroni
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -43,7 +54,9 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
- watch # needed if zalando-postgres-operator account is used for pods as well
|
- update
|
||||||
|
- watch
|
||||||
|
# to CRUD secrets for database access
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -53,6 +66,7 @@ rules:
|
||||||
- update
|
- update
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
|
# to check nodes for node readiness label
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -61,6 +75,7 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
# to read or delete existing PVCs. Creation via StatefulSet
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -69,6 +84,7 @@ rules:
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
|
# to read existing PVs. Creation should be done via dynamic provisioning
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -77,6 +93,7 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- update # only for resizing AWS volumes
|
- update # only for resizing AWS volumes
|
||||||
|
# to watch Spilo pods and do rolling updates. Creation via StatefulSet
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -85,14 +102,17 @@ rules:
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
|
||||||
- patch
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
# to resize the filesystem in Spilo pods when increasing volume size
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- pods/exec
|
- pods/exec
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- create
|
||||||
|
# to CRUD services to point to Postgres cluster instances
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -102,6 +122,7 @@ rules:
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
- patch
|
- patch
|
||||||
|
# to CRUD the StatefulSet which controls the Postgres cluster instances
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apps
|
- apps
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -112,12 +133,26 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
|
# to CRUD cron jobs for logical backups
|
||||||
|
- apiGroups:
|
||||||
|
- batch
|
||||||
|
resources:
|
||||||
|
- cronjobs
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
# to get namespaces operator resources can run in
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
- namespaces
|
- namespaces
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
|
# to define PDBs. Update happens via delete/create
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- policy
|
- policy
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -126,6 +161,7 @@ rules:
|
||||||
- create
|
- create
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
|
# to create ServiceAccounts in each namespace the operator watches
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
resources:
|
resources:
|
||||||
|
|
@ -133,30 +169,22 @@ rules:
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- create
|
- create
|
||||||
|
# to create roles and role bindings to the pod service account
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- "rbac.authorization.k8s.io"
|
- rbac.authorization.k8s.io
|
||||||
resources:
|
resources:
|
||||||
- rolebindings
|
- rolebindings
|
||||||
|
- roles
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- create
|
- create
|
||||||
|
# needed to grant to namespaced roles when pods run in privileged mode
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- "rbac.authorization.k8s.io"
|
- extensions
|
||||||
resources:
|
resources:
|
||||||
- clusterroles
|
- podsecuritypolicies
|
||||||
verbs:
|
|
||||||
- bind
|
|
||||||
resourceNames:
|
resourceNames:
|
||||||
- {{ include "postgres-operator.serviceAccountName" . }}
|
- privileged
|
||||||
- apiGroups:
|
|
||||||
- batch
|
|
||||||
resources:
|
|
||||||
- cronjobs # enables logical backups
|
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- use
|
||||||
- delete
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- patch
|
|
||||||
- update
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -14,8 +14,6 @@ roleRef:
|
||||||
name: {{ include "postgres-operator.serviceAccountName" . }}
|
name: {{ include "postgres-operator.serviceAccountName" . }}
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
# note: the cluster role binding needs to be defined
|
|
||||||
# for every namespace the operator service account lives in.
|
|
||||||
name: {{ include "postgres-operator.serviceAccountName" . }}
|
name: {{ include "postgres-operator.serviceAccountName" . }}
|
||||||
namespace: {{ .Release.Namespace }}
|
namespace: {{ .Release.Namespace }}
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ metadata:
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||||
data:
|
data:
|
||||||
pod_service_account_name: {{ include "postgres-operator.serviceAccountName" . }}
|
|
||||||
{{ toYaml .Values.configGeneral | indent 2 }}
|
{{ toYaml .Values.configGeneral | indent 2 }}
|
||||||
{{ toYaml .Values.configUsers | indent 2 }}
|
{{ toYaml .Values.configUsers | indent 2 }}
|
||||||
{{ toYaml .Values.configKubernetes | indent 2 }}
|
{{ toYaml .Values.configKubernetes | indent 2 }}
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ configuration:
|
||||||
{{ toYaml .Values.configUsers | indent 4 }}
|
{{ toYaml .Values.configUsers | indent 4 }}
|
||||||
kubernetes:
|
kubernetes:
|
||||||
oauth_token_secret_name: {{ template "postgres-operator.fullname" . }}
|
oauth_token_secret_name: {{ template "postgres-operator.fullname" . }}
|
||||||
pod_service_account_name: {{ include "postgres-operator.serviceAccountName" . }}
|
|
||||||
{{ toYaml .Values.configKubernetes | indent 4 }}
|
{{ toYaml .Values.configKubernetes | indent 4 }}
|
||||||
postgres_pod_resources:
|
postgres_pod_resources:
|
||||||
{{ toYaml .Values.configPostgresPodResources | indent 4 }}
|
{{ toYaml .Values.configPostgresPodResources | indent 4 }}
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,17 @@ configKubernetes:
|
||||||
pod_management_policy: "ordered_ready"
|
pod_management_policy: "ordered_ready"
|
||||||
# label assigned to the Postgres pods (and services/endpoints)
|
# label assigned to the Postgres pods (and services/endpoints)
|
||||||
pod_role_label: spilo-role
|
pod_role_label: spilo-role
|
||||||
|
# service account definition as JSON/YAML string to be used by postgres cluster pods
|
||||||
|
# pod_service_account_definition: ""
|
||||||
|
|
||||||
|
# name of service account to be used by postgres cluster pods
|
||||||
|
pod_service_account_name: "postgres-pod"
|
||||||
|
# role definition as JSON/YAML string to be used by postgres cluster pods
|
||||||
|
# pod_service_account_role_definition: ""
|
||||||
|
|
||||||
|
# role binding definition as JSON/YAML string to be used by pod service account
|
||||||
|
# pod_service_account_role_binding_definition: ""
|
||||||
|
|
||||||
# Postgres pods are terminated forcefully after this timeout
|
# Postgres pods are terminated forcefully after this timeout
|
||||||
pod_terminate_grace_period: 5m
|
pod_terminate_grace_period: 5m
|
||||||
# template for database user secrets generated by the operator
|
# template for database user secrets generated by the operator
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,17 @@ configKubernetes:
|
||||||
pod_management_policy: "ordered_ready"
|
pod_management_policy: "ordered_ready"
|
||||||
# label assigned to the Postgres pods (and services/endpoints)
|
# label assigned to the Postgres pods (and services/endpoints)
|
||||||
pod_role_label: spilo-role
|
pod_role_label: spilo-role
|
||||||
|
# service account definition as JSON/YAML string to be used by postgres cluster pods
|
||||||
|
# pod_service_account_definition: ""
|
||||||
|
|
||||||
|
# name of service account to be used by postgres cluster pods
|
||||||
|
pod_service_account_name: "postgres-pod"
|
||||||
|
# role definition as JSON/YAML string to be used by postgres cluster pods
|
||||||
|
# pod_service_account_role_definition: ""
|
||||||
|
|
||||||
|
# role binding definition as JSON/YAML string to be used by pod service account
|
||||||
|
# pod_service_account_role_binding_definition: ""
|
||||||
|
|
||||||
# Postgres pods are terminated forcefully after this timeout
|
# Postgres pods are terminated forcefully after this timeout
|
||||||
pod_terminate_grace_period: 5m
|
pod_terminate_grace_period: 5m
|
||||||
# template for database user secrets generated by the operator
|
# template for database user secrets generated by the operator
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,12 @@ patching the CRD manifest:
|
||||||
zk8 patch crd postgresqls.acid.zalan.do -p '{"spec":{"validation": null}}'
|
zk8 patch crd postgresqls.acid.zalan.do -p '{"spec":{"validation": null}}'
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Non-default cluster domain
|
||||||
|
|
||||||
|
If your cluster uses a DNS domain other than the default `cluster.local`, this
|
||||||
|
needs to be set in the operator configuration (`cluster_domain` variable). This
|
||||||
|
is used by the operator to connect to the clusters after creation.
|
||||||
|
|
||||||
## Namespaces
|
## Namespaces
|
||||||
|
|
||||||
### Select the namespace to deploy to
|
### Select the namespace to deploy to
|
||||||
|
|
@ -89,30 +95,6 @@ lacks access rights to any of them (except K8s system namespaces like
|
||||||
'list pods' execute at the cluster scope and fail at the first violation of
|
'list pods' execute at the cluster scope and fail at the first violation of
|
||||||
access rights.
|
access rights.
|
||||||
|
|
||||||
The watched namespace also needs to have a (possibly different) service account
|
|
||||||
in the case database pods need to talk to the K8s API (e.g. when using
|
|
||||||
K8s-native configuration of Patroni). The operator checks that the
|
|
||||||
`pod_service_account_name` exists in the target namespace, and, if not, deploys
|
|
||||||
there the `pod_service_account_definition` from the operator
|
|
||||||
[`Config`](../pkg/util/config/config.go) with the default value of:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: operator
|
|
||||||
```
|
|
||||||
|
|
||||||
In this definition, the operator overwrites the account's name to match
|
|
||||||
`pod_service_account_name` and the `default` namespace to match the target
|
|
||||||
namespace. The operator performs **no** further syncing of this account.
|
|
||||||
|
|
||||||
## Non-default cluster domain
|
|
||||||
|
|
||||||
If your cluster uses a DNS domain other than the default `cluster.local`, this
|
|
||||||
needs to be set in the operator configuration (`cluster_domain` variable). This
|
|
||||||
is used by the operator to connect to the clusters after creation.
|
|
||||||
|
|
||||||
## Role-based access control for the operator
|
## Role-based access control for the operator
|
||||||
|
|
||||||
The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml)
|
The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml)
|
||||||
|
|
@ -127,14 +109,19 @@ kubectl create -f manifests/postgres-operator.yaml
|
||||||
kubectl create -f manifests/minimal-postgres-manifest.yaml
|
kubectl create -f manifests/minimal-postgres-manifest.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
### Service account and cluster roles
|
### Namespaced service account and roles
|
||||||
|
|
||||||
Note that the service account is named `zalando-postgres-operator`. You may have
|
For each namespace the operator watches it creates (or reads) a service account
|
||||||
to change the `service_account_name` in the operator ConfigMap and
|
to be used by the Postgres Pods when a new cluster is deployed. This service
|
||||||
`serviceAccountName` in the `postgres-operator` deployment appropriately. This
|
account is bound to a namespaced Role via RoleBinding, which are also created
|
||||||
is done intentionally to avoid breaking those setups that already work with the
|
(or read) by the operator. The name and definitions of these resources can be
|
||||||
default `operator` account. In the future the operator should ideally be run
|
[configured](reference/operator_parameters.md#kubernetes-resources).
|
||||||
under the `zalando-postgres-operator` service account.
|
Note, that the operator performs **no** further syncing of them.
|
||||||
|
|
||||||
|
Until v1.3.1, RoleBindings pointed to the operator ClusterRole by default. This
|
||||||
|
can still be configured but is not recommended as the Postgres Pods should only
|
||||||
|
run with the least privileges required for Patroni to work. By default, the
|
||||||
|
namespaced RBAC resources are named `postgres-pod`.
|
||||||
|
|
||||||
### Give K8s users access to create/list `postgresqls`
|
### Give K8s users access to create/list `postgresqls`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -152,21 +152,29 @@ configuration they are grouped under the `kubernetes` key.
|
||||||
service account used by Patroni running on individual Pods to communicate
|
service account used by Patroni running on individual Pods to communicate
|
||||||
with the operator. Required even if native Kubernetes support in Patroni is
|
with the operator. Required even if native Kubernetes support in Patroni is
|
||||||
not used, because Patroni keeps pod labels in sync with the instance role.
|
not used, because Patroni keeps pod labels in sync with the instance role.
|
||||||
The default is `operator`.
|
The default is `postgres-pod`.
|
||||||
|
|
||||||
* **pod_service_account_definition**
|
* **pod_service_account_definition**
|
||||||
The operator tries to create the pod Service Account in the namespace that
|
on Postgres cluster creation the operator tries to create the service account
|
||||||
doesn't define such an account using the YAML definition provided by this
|
for the Postgres pods if it does not exist in the namespace. The internal
|
||||||
option. If not defined, a simple definition that contains only the name will
|
default service account definition (defines only the name) can be overwritten
|
||||||
be used. The default is empty.
|
with this parameter. Make sure to provide a valid YAML or JSON string. The
|
||||||
|
default is empty.
|
||||||
|
|
||||||
|
* **pod_service_account_role_definition**
|
||||||
|
operator will try to create a role in the namespace to be used by pod service
|
||||||
|
account. The internal default definition contains permissions to manage pods
|
||||||
|
and endpoints necessary for Patroni to work. Therefore, when overwriting the
|
||||||
|
definition with this parameter make sure to provide sufficient access rights
|
||||||
|
in a valid YAML/JSON string. The default is empty.
|
||||||
|
|
||||||
* **pod_service_account_role_binding_definition**
|
* **pod_service_account_role_binding_definition**
|
||||||
This definition must bind pod service account to a role with permission
|
the created service account and role are referenced with a role binding. When
|
||||||
sufficient for the pods to start and for Patroni to access K8s endpoints;
|
overwriting its definition with this parameters using a valid YAML/JSON string
|
||||||
service account on its own lacks any such rights starting with K8s v1.8. If
|
check that the specified service account and role either exist in the K8s
|
||||||
not explicitly defined by the user, a simple definition that binds the
|
cluster or will be created by the operator. While it's possible to also
|
||||||
account to the operator's own 'zalando-postgres-operator' cluster role will
|
reference cluster roles, the binding itself can only be of kind `RoleBinding`,
|
||||||
be used. The default is empty.
|
not `ClusterRoleBinding`. The default is empty.
|
||||||
|
|
||||||
* **pod_terminate_grace_period**
|
* **pod_terminate_grace_period**
|
||||||
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)
|
Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)
|
||||||
|
|
|
||||||
|
|
@ -63,7 +63,10 @@ data:
|
||||||
pod_label_wait_timeout: 10m
|
pod_label_wait_timeout: 10m
|
||||||
pod_management_policy: "ordered_ready"
|
pod_management_policy: "ordered_ready"
|
||||||
pod_role_label: spilo-role
|
pod_role_label: spilo-role
|
||||||
pod_service_account_name: "zalando-postgres-operator"
|
# pod_service_account_definition: ""
|
||||||
|
pod_service_account_name: "postgres-pod"
|
||||||
|
# pod_service_account_role_definition: ""
|
||||||
|
# pod_service_account_role_binding_definition: '{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"RoleBinding","metadata":{"name":"postgres-pod"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind": "ClusterRole","name": "postgres-pod"},"subjects":[{"kind": "ServiceAccount","name": "postgres-pod"}]}'
|
||||||
pod_terminate_grace_period: 5m
|
pod_terminate_grace_period: 5m
|
||||||
# postgres_superuser_teams: "postgres_superusers"
|
# postgres_superuser_teams: "postgres_superusers"
|
||||||
# protected_role_names: "admin"
|
# protected_role_names: "admin"
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
apiVersion: v1
|
apiVersion: v1
|
||||||
kind: ServiceAccount
|
kind: ServiceAccount
|
||||||
metadata:
|
metadata:
|
||||||
name: zalando-postgres-operator
|
name: postgres-operator
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
metadata:
|
metadata:
|
||||||
name: zalando-postgres-operator
|
name: postgres-operator
|
||||||
rules:
|
rules:
|
||||||
# all verbs allowed for custom operator resources
|
# all verbs allowed for custom operator resources
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
|
|
@ -18,7 +18,14 @@ rules:
|
||||||
- postgresqls/status
|
- postgresqls/status
|
||||||
- operatorconfigurations
|
- operatorconfigurations
|
||||||
verbs:
|
verbs:
|
||||||
- "*"
|
- create
|
||||||
|
- delete
|
||||||
|
- deletecollection
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
# to create or get/update CRDs when starting up
|
# to create or get/update CRDs when starting up
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- apiextensions.k8s.io
|
- apiextensions.k8s.io
|
||||||
|
|
@ -48,7 +55,8 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
- watch # needed if zalando-postgres-operator account is used for pods as well
|
- update
|
||||||
|
- watch
|
||||||
# to CRUD secrets for database access
|
# to CRUD secrets for database access
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
|
|
@ -95,8 +103,9 @@ rules:
|
||||||
- delete
|
- delete
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
|
||||||
- patch
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
# to resize the filesystem in Spilo pods when increasing volume size
|
# to resize the filesystem in Spilo pods when increasing volume size
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
|
|
@ -126,6 +135,18 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- patch
|
- patch
|
||||||
|
# to CRUD cron jobs for logical backups
|
||||||
|
- apiGroups:
|
||||||
|
- batch
|
||||||
|
resources:
|
||||||
|
- cronjobs
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
# to get namespaces operator resources can run in
|
# to get namespaces operator resources can run in
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- ""
|
- ""
|
||||||
|
|
@ -150,39 +171,35 @@ rules:
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- create
|
- create
|
||||||
# to create role bindings to the operator service account
|
# to create roles and role bindings to the pod service account
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- "rbac.authorization.k8s.io"
|
- rbac.authorization.k8s.io
|
||||||
resources:
|
resources:
|
||||||
- rolebindings
|
- rolebindings
|
||||||
|
- roles
|
||||||
verbs:
|
verbs:
|
||||||
- get
|
- get
|
||||||
- create
|
- create
|
||||||
# to CRUD cron jobs for logical backups
|
# needed to grant to namespaced roles when pods run in privileged mode
|
||||||
- apiGroups:
|
- apiGroups:
|
||||||
- batch
|
- extensions
|
||||||
resources:
|
resources:
|
||||||
- cronjobs
|
- podsecuritypolicies
|
||||||
|
resourceNames:
|
||||||
|
- privileged
|
||||||
verbs:
|
verbs:
|
||||||
- create
|
- use
|
||||||
- delete
|
|
||||||
- get
|
|
||||||
- list
|
|
||||||
- patch
|
|
||||||
- update
|
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
kind: ClusterRoleBinding
|
kind: ClusterRoleBinding
|
||||||
metadata:
|
metadata:
|
||||||
name: zalando-postgres-operator
|
name: postgres-operator
|
||||||
roleRef:
|
roleRef:
|
||||||
apiGroup: rbac.authorization.k8s.io
|
apiGroup: rbac.authorization.k8s.io
|
||||||
kind: ClusterRole
|
kind: ClusterRole
|
||||||
name: zalando-postgres-operator
|
name: postgres-operator
|
||||||
subjects:
|
subjects:
|
||||||
- kind: ServiceAccount
|
- kind: ServiceAccount
|
||||||
# note: the cluster role binding needs to be defined
|
name: postgres-operator
|
||||||
# for every namespace the operator service account lives in.
|
|
||||||
name: zalando-postgres-operator
|
|
||||||
namespace: default
|
namespace: default
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,8 @@ spec:
|
||||||
type: string
|
type: string
|
||||||
pod_service_account_name:
|
pod_service_account_name:
|
||||||
type: string
|
type: string
|
||||||
|
pod_service_account_role_definition:
|
||||||
|
type: string
|
||||||
pod_service_account_role_binding_definition:
|
pod_service_account_role_binding_definition:
|
||||||
type: string
|
type: string
|
||||||
pod_terminate_grace_period:
|
pod_terminate_grace_period:
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ spec:
|
||||||
labels:
|
labels:
|
||||||
name: postgres-operator
|
name: postgres-operator
|
||||||
spec:
|
spec:
|
||||||
serviceAccountName: zalando-postgres-operator
|
serviceAccountName: postgres-operator
|
||||||
containers:
|
containers:
|
||||||
- name: postgres-operator
|
- name: postgres-operator
|
||||||
image: registry.opensource.zalan.do/acid/postgres-operator:v1.3.1
|
image: registry.opensource.zalan.do/acid/postgres-operator:v1.3.1
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
apiVersion: v1
|
||||||
|
kind: ServiceAccount
|
||||||
|
metadata:
|
||||||
|
name: postgres-pod
|
||||||
|
namespace: default
|
||||||
|
|
||||||
|
---
|
||||||
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
kind: ClusterRole
|
||||||
|
metadata:
|
||||||
|
name: postgres-pod
|
||||||
|
rules:
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- endpoints
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- delete
|
||||||
|
- deletecollection
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- pods
|
||||||
|
verbs:
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
- apiGroups:
|
||||||
|
- ""
|
||||||
|
resources:
|
||||||
|
- services
|
||||||
|
verbs:
|
||||||
|
- create
|
||||||
|
- apiGroups:
|
||||||
|
- extensions
|
||||||
|
resources:
|
||||||
|
- podsecuritypolicies
|
||||||
|
resourceNames:
|
||||||
|
- privileged
|
||||||
|
verbs:
|
||||||
|
- use
|
||||||
|
|
@ -45,7 +45,8 @@ configuration:
|
||||||
# pod_priority_class_name: ""
|
# pod_priority_class_name: ""
|
||||||
pod_role_label: spilo-role
|
pod_role_label: spilo-role
|
||||||
# pod_service_account_definition: ""
|
# pod_service_account_definition: ""
|
||||||
pod_service_account_name: zalando-postgres-operator
|
pod_service_account_name: postgres-pod
|
||||||
|
# pod_service_account_role_definition: ""
|
||||||
# pod_service_account_role_binding_definition: ""
|
# pod_service_account_role_binding_definition: ""
|
||||||
pod_terminate_grace_period: 5m
|
pod_terminate_grace_period: 5m
|
||||||
secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
|
secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,14 @@ rules:
|
||||||
- postgresqls
|
- postgresqls
|
||||||
- postgresqls/status
|
- postgresqls/status
|
||||||
verbs:
|
verbs:
|
||||||
- "*"
|
- create
|
||||||
|
- delete
|
||||||
|
- deletecollection
|
||||||
|
- get
|
||||||
|
- list
|
||||||
|
- patch
|
||||||
|
- update
|
||||||
|
- watch
|
||||||
|
|
||||||
---
|
---
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
apiVersion: rbac.authorization.k8s.io/v1
|
||||||
|
|
@ -48,4 +55,3 @@ rules:
|
||||||
- get
|
- get
|
||||||
- list
|
- list
|
||||||
- watch
|
- watch
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -771,6 +771,9 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
|
||||||
"pod_service_account_name": {
|
"pod_service_account_name": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
|
"pod_service_account_role_definition": {
|
||||||
|
Type: "string",
|
||||||
|
},
|
||||||
"pod_service_account_role_binding_definition": {
|
"pod_service_account_role_binding_definition": {
|
||||||
Type: "string",
|
Type: "string",
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ type KubernetesMetaConfiguration struct {
|
||||||
PodServiceAccountName string `json:"pod_service_account_name,omitempty"`
|
PodServiceAccountName string `json:"pod_service_account_name,omitempty"`
|
||||||
// TODO: change it to the proper json
|
// TODO: change it to the proper json
|
||||||
PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"`
|
PodServiceAccountDefinition string `json:"pod_service_account_definition,omitempty"`
|
||||||
|
PodServiceAccountRoleDefinition string `json:"pod_service_account_role_definition,omitempty"`
|
||||||
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
|
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
|
||||||
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
|
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
|
||||||
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
|
SpiloPrivileged bool `json:"spilo_privileged,omitempty"`
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ type Config struct {
|
||||||
RestConfig *rest.Config
|
RestConfig *rest.Config
|
||||||
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
InfrastructureRoles map[string]spec.PgUser // inherited from the controller
|
||||||
PodServiceAccount *v1.ServiceAccount
|
PodServiceAccount *v1.ServiceAccount
|
||||||
|
PodServiceAccountRole *rbacv1.Role
|
||||||
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ type Controller struct {
|
||||||
workerLogs map[uint32]ringlog.RingLogger
|
workerLogs map[uint32]ringlog.RingLogger
|
||||||
|
|
||||||
PodServiceAccount *v1.ServiceAccount
|
PodServiceAccount *v1.ServiceAccount
|
||||||
|
PodServiceAccountRole *rbacv1.Role
|
||||||
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
PodServiceAccountRoleBinding *rbacv1.RoleBinding
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -161,11 +162,12 @@ func (c *Controller) initPodServiceAccount() {
|
||||||
|
|
||||||
if c.opConfig.PodServiceAccountDefinition == "" {
|
if c.opConfig.PodServiceAccountDefinition == "" {
|
||||||
c.opConfig.PodServiceAccountDefinition = `
|
c.opConfig.PodServiceAccountDefinition = `
|
||||||
{ "apiVersion": "v1",
|
{
|
||||||
"kind": "ServiceAccount",
|
"apiVersion": "v1",
|
||||||
"metadata": {
|
"kind": "ServiceAccount",
|
||||||
"name": "operator"
|
"metadata": {
|
||||||
}
|
"name": "postgres-pod"
|
||||||
|
}
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -175,13 +177,13 @@ func (c *Controller) initPodServiceAccount() {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
panic(fmt.Errorf("Unable to parse pod service account definition from the operator config map: %v", err))
|
panic(fmt.Errorf("Unable to parse pod service account definition from the operator configuration: %v", err))
|
||||||
case groupVersionKind.Kind != "ServiceAccount":
|
case groupVersionKind.Kind != "ServiceAccount":
|
||||||
panic(fmt.Errorf("pod service account definition in the operator config map defines another type of resource: %v", groupVersionKind.Kind))
|
panic(fmt.Errorf("pod service account definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
|
||||||
default:
|
default:
|
||||||
c.PodServiceAccount = obj.(*v1.ServiceAccount)
|
c.PodServiceAccount = obj.(*v1.ServiceAccount)
|
||||||
if c.PodServiceAccount.Name != c.opConfig.PodServiceAccountName {
|
if c.PodServiceAccount.Name != c.opConfig.PodServiceAccountName {
|
||||||
c.logger.Warnf("in the operator config map, the pod service account name %v does not match the name %v given in the account definition; using the former for consistency", c.opConfig.PodServiceAccountName, c.PodServiceAccount.Name)
|
c.logger.Warnf("in the operator configuration, the pod service account name %v does not match the name %v given in the account definition; using the former for consistency", c.opConfig.PodServiceAccountName, c.PodServiceAccount.Name)
|
||||||
c.PodServiceAccount.Name = c.opConfig.PodServiceAccountName
|
c.PodServiceAccount.Name = c.opConfig.PodServiceAccountName
|
||||||
}
|
}
|
||||||
c.PodServiceAccount.Namespace = ""
|
c.PodServiceAccount.Namespace = ""
|
||||||
|
|
@ -190,6 +192,100 @@ func (c *Controller) initPodServiceAccount() {
|
||||||
// actual service accounts are deployed at the time of Postgres/Spilo cluster creation
|
// actual service accounts are deployed at the time of Postgres/Spilo cluster creation
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Controller) initRole() {
|
||||||
|
|
||||||
|
// service account on its own lacks any rights starting with k8s v1.8
|
||||||
|
// operator binds it to the namespaced role with sufficient privileges
|
||||||
|
if c.opConfig.PodServiceAccountRoleDefinition == "" {
|
||||||
|
c.opConfig.PodServiceAccountRoleDefinition = fmt.Sprintf(`
|
||||||
|
{
|
||||||
|
"apiVersion": "rbac.authorization.k8s.io/v1",
|
||||||
|
"kind": "Role",
|
||||||
|
"metadata": {
|
||||||
|
"name": "%s"
|
||||||
|
},
|
||||||
|
"rules": [
|
||||||
|
{
|
||||||
|
"apiGroups": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"endpoints"
|
||||||
|
],
|
||||||
|
"verbs": [
|
||||||
|
"create",
|
||||||
|
"delete",
|
||||||
|
"deletecollection",
|
||||||
|
"get",
|
||||||
|
"list",
|
||||||
|
"patch",
|
||||||
|
"update",
|
||||||
|
"watch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiGroups": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"pods"
|
||||||
|
],
|
||||||
|
"verbs": [
|
||||||
|
"get",
|
||||||
|
"list",
|
||||||
|
"patch",
|
||||||
|
"update",
|
||||||
|
"watch"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiGroups": [
|
||||||
|
""
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"services"
|
||||||
|
],
|
||||||
|
"verbs": [
|
||||||
|
"create"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"apiGroups": [
|
||||||
|
"extensions"
|
||||||
|
],
|
||||||
|
"resources": [
|
||||||
|
"podsecuritypolicies"
|
||||||
|
],
|
||||||
|
"resourceNames": [
|
||||||
|
"privileged"
|
||||||
|
],
|
||||||
|
"verbs": [
|
||||||
|
"use"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`, c.PodServiceAccount.Name)
|
||||||
|
}
|
||||||
|
c.logger.Info("Parse roles")
|
||||||
|
// re-uses k8s internal parsing. See k8s client-go issue #193 for explanation
|
||||||
|
decode := scheme.Codecs.UniversalDeserializer().Decode
|
||||||
|
obj, groupVersionKind, err := decode([]byte(c.opConfig.PodServiceAccountRoleDefinition), nil, nil)
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case err != nil:
|
||||||
|
panic(fmt.Errorf("unable to parse the role definition from the operator configuration: %v", err))
|
||||||
|
case groupVersionKind.Kind != "Role":
|
||||||
|
panic(fmt.Errorf("role definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
|
||||||
|
default:
|
||||||
|
c.PodServiceAccountRole = obj.(*rbacv1.Role)
|
||||||
|
c.PodServiceAccountRole.Namespace = ""
|
||||||
|
c.logger.Info("successfully parsed")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// actual roles bindings are deployed at the time of Postgres/Spilo cluster creation
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Controller) initRoleBinding() {
|
func (c *Controller) initRoleBinding() {
|
||||||
|
|
||||||
// service account on its own lacks any rights starting with k8s v1.8
|
// service account on its own lacks any rights starting with k8s v1.8
|
||||||
|
|
@ -205,7 +301,7 @@ func (c *Controller) initRoleBinding() {
|
||||||
},
|
},
|
||||||
"roleRef": {
|
"roleRef": {
|
||||||
"apiGroup": "rbac.authorization.k8s.io",
|
"apiGroup": "rbac.authorization.k8s.io",
|
||||||
"kind": "ClusterRole",
|
"kind": "Role",
|
||||||
"name": "%s"
|
"name": "%s"
|
||||||
},
|
},
|
||||||
"subjects": [
|
"subjects": [
|
||||||
|
|
@ -223,9 +319,9 @@ func (c *Controller) initRoleBinding() {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case err != nil:
|
case err != nil:
|
||||||
panic(fmt.Errorf("Unable to parse the definition of the role binding for the pod service account definition from the operator config map: %v", err))
|
panic(fmt.Errorf("unable to parse the role binding definition from the operator configuration: %v", err))
|
||||||
case groupVersionKind.Kind != "RoleBinding":
|
case groupVersionKind.Kind != "RoleBinding":
|
||||||
panic(fmt.Errorf("role binding definition in the operator config map defines another type of resource: %v", groupVersionKind.Kind))
|
panic(fmt.Errorf("role binding definition in the operator configuration defines another type of resource: %v", groupVersionKind.Kind))
|
||||||
default:
|
default:
|
||||||
c.PodServiceAccountRoleBinding = obj.(*rbacv1.RoleBinding)
|
c.PodServiceAccountRoleBinding = obj.(*rbacv1.RoleBinding)
|
||||||
c.PodServiceAccountRoleBinding.Namespace = ""
|
c.PodServiceAccountRoleBinding.Namespace = ""
|
||||||
|
|
@ -253,6 +349,10 @@ func (c *Controller) initController() {
|
||||||
}
|
}
|
||||||
c.initPodServiceAccount()
|
c.initPodServiceAccount()
|
||||||
c.initRoleBinding()
|
c.initRoleBinding()
|
||||||
|
// init role only if binding references a role
|
||||||
|
if c.PodServiceAccountRoleBinding != nil && c.PodServiceAccountRoleBinding.RoleRef.Kind == "Role" {
|
||||||
|
c.initRole()
|
||||||
|
}
|
||||||
|
|
||||||
c.modifyConfigFromEnvironment()
|
c.modifyConfigFromEnvironment()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
|
||||||
result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
|
result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
|
||||||
result.PodServiceAccountName = fromCRD.Kubernetes.PodServiceAccountName
|
result.PodServiceAccountName = fromCRD.Kubernetes.PodServiceAccountName
|
||||||
result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
|
result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
|
||||||
|
result.PodServiceAccountRoleDefinition = fromCRD.Kubernetes.PodServiceAccountRoleDefinition
|
||||||
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
|
result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
|
||||||
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
|
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
|
||||||
result.PodTerminateGracePeriod = time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod)
|
result.PodTerminateGracePeriod = time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod)
|
||||||
|
|
|
||||||
|
|
@ -493,23 +493,31 @@ func (c *Controller) postgresqlDelete(obj interface{}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Ensures the pod service account and role bindings exists in a namespace
|
Ensures the pod service account, role and role bindings exists in a namespace
|
||||||
before a PG cluster is created there so that a user does not have to deploy
|
before a PG cluster is created there so that a user does not have to deploy
|
||||||
these credentials manually. StatefulSets require the service account to
|
these credentials manually. StatefulSets require the service account to
|
||||||
create pods; Patroni requires relevant RBAC bindings to access endpoints.
|
create pods; Patroni requires relevant RBAC bindings to access endpoints.
|
||||||
|
|
||||||
The operator does not sync accounts/role bindings after creation.
|
The operator does not sync accounts/roles/role bindings after creation.
|
||||||
*/
|
*/
|
||||||
func (c *Controller) submitRBACCredentials(event ClusterEvent) error {
|
func (c *Controller) submitRBACCredentials(event ClusterEvent) error {
|
||||||
|
|
||||||
namespace := event.NewSpec.GetNamespace()
|
namespace := event.NewSpec.GetNamespace()
|
||||||
|
|
||||||
if err := c.createPodServiceAccount(namespace); err != nil {
|
if err := c.createPodServiceAccount(namespace); err != nil {
|
||||||
return fmt.Errorf("could not create pod service account %v : %v", c.opConfig.PodServiceAccountName, err)
|
return fmt.Errorf("could not create pod service account %q : %v", c.opConfig.PodServiceAccountName, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// create role only if binding references a role
|
||||||
|
// if not role is empty and we rely on an existing cluster role
|
||||||
|
if c.PodServiceAccountRole != nil {
|
||||||
|
if err := c.createRole(namespace); err != nil {
|
||||||
|
return fmt.Errorf("could not create role %q : %v", c.PodServiceAccountRole.Name, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.createRoleBindings(namespace); err != nil {
|
if err := c.createRoleBindings(namespace); err != nil {
|
||||||
return fmt.Errorf("could not create role binding %v : %v", c.PodServiceAccountRoleBinding.Name, err)
|
return fmt.Errorf("could not create role binding %q : %v", c.PodServiceAccountRoleBinding.Name, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -517,19 +525,39 @@ func (c *Controller) submitRBACCredentials(event ClusterEvent) error {
|
||||||
func (c *Controller) createPodServiceAccount(namespace string) error {
|
func (c *Controller) createPodServiceAccount(namespace string) error {
|
||||||
|
|
||||||
podServiceAccountName := c.opConfig.PodServiceAccountName
|
podServiceAccountName := c.opConfig.PodServiceAccountName
|
||||||
|
// get a separate copy of service account
|
||||||
|
// to prevent a race condition when setting a namespace for many clusters
|
||||||
|
sa := *c.PodServiceAccount
|
||||||
|
|
||||||
_, err := c.KubeClient.ServiceAccounts(namespace).Get(podServiceAccountName, metav1.GetOptions{})
|
_, err := c.KubeClient.ServiceAccounts(namespace).Get(podServiceAccountName, metav1.GetOptions{})
|
||||||
if k8sutil.ResourceNotFound(err) {
|
if k8sutil.ResourceNotFound(err) {
|
||||||
|
c.logger.Infof(fmt.Sprintf("creating pod service account %q in the %q namespace", podServiceAccountName, namespace))
|
||||||
c.logger.Infof(fmt.Sprintf("creating pod service account in the namespace %v", namespace))
|
|
||||||
|
|
||||||
// get a separate copy of service account
|
|
||||||
// to prevent a race condition when setting a namespace for many clusters
|
|
||||||
sa := *c.PodServiceAccount
|
|
||||||
if _, err = c.KubeClient.ServiceAccounts(namespace).Create(&sa); err != nil {
|
if _, err = c.KubeClient.ServiceAccounts(namespace).Create(&sa); err != nil {
|
||||||
return fmt.Errorf("cannot deploy the pod service account %v defined in the config map to the %v namespace: %v", podServiceAccountName, namespace, err)
|
return fmt.Errorf("cannot deploy the pod service account %q defined in the configuration to the %q namespace: %v", podServiceAccountName, namespace, err)
|
||||||
}
|
}
|
||||||
|
c.logger.Infof("successfully deployed the pod service account %q to the %q namespace", podServiceAccountName, namespace)
|
||||||
|
} else if k8sutil.ResourceAlreadyExists(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
c.logger.Infof("successfully deployed the pod service account %v to the %v namespace", podServiceAccountName, namespace)
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Controller) createRole(namespace string) error {
|
||||||
|
|
||||||
|
podServiceAccountRoleName := c.PodServiceAccountRole.Name
|
||||||
|
// get a separate copy of the role
|
||||||
|
// to prevent a race condition when setting a namespace for many clusters
|
||||||
|
role := *c.PodServiceAccountRole
|
||||||
|
|
||||||
|
_, err := c.KubeClient.Roles(namespace).Get(podServiceAccountRoleName, metav1.GetOptions{})
|
||||||
|
if k8sutil.ResourceNotFound(err) {
|
||||||
|
c.logger.Infof("creating role %q in the %q namespace", podServiceAccountRoleName, namespace)
|
||||||
|
_, err = c.KubeClient.Roles(namespace).Create(&role)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("cannot create role %q in the %q namespace: %v", podServiceAccountRoleName, namespace, err)
|
||||||
|
}
|
||||||
|
c.logger.Infof("successfully deployed role %q to the %q namespace", podServiceAccountRoleName, namespace)
|
||||||
} else if k8sutil.ResourceAlreadyExists(err) {
|
} else if k8sutil.ResourceAlreadyExists(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -541,22 +569,18 @@ func (c *Controller) createRoleBindings(namespace string) error {
|
||||||
|
|
||||||
podServiceAccountName := c.opConfig.PodServiceAccountName
|
podServiceAccountName := c.opConfig.PodServiceAccountName
|
||||||
podServiceAccountRoleBindingName := c.PodServiceAccountRoleBinding.Name
|
podServiceAccountRoleBindingName := c.PodServiceAccountRoleBinding.Name
|
||||||
|
// get a separate copy of role binding
|
||||||
|
// to prevent a race condition when setting a namespace for many clusters
|
||||||
|
rb := *c.PodServiceAccountRoleBinding
|
||||||
|
|
||||||
_, err := c.KubeClient.RoleBindings(namespace).Get(podServiceAccountRoleBindingName, metav1.GetOptions{})
|
_, err := c.KubeClient.RoleBindings(namespace).Get(podServiceAccountRoleBindingName, metav1.GetOptions{})
|
||||||
if k8sutil.ResourceNotFound(err) {
|
if k8sutil.ResourceNotFound(err) {
|
||||||
|
c.logger.Infof("creating the role binding %q in the %q namespace", podServiceAccountRoleBindingName, namespace)
|
||||||
c.logger.Infof("Creating the role binding %v in the namespace %v", podServiceAccountRoleBindingName, namespace)
|
|
||||||
|
|
||||||
// get a separate copy of role binding
|
|
||||||
// to prevent a race condition when setting a namespace for many clusters
|
|
||||||
rb := *c.PodServiceAccountRoleBinding
|
|
||||||
_, err = c.KubeClient.RoleBindings(namespace).Create(&rb)
|
_, err = c.KubeClient.RoleBindings(namespace).Create(&rb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("cannot bind the pod service account %q defined in the config map to the cluster role in the %q namespace: %v", podServiceAccountName, namespace, err)
|
return fmt.Errorf("cannot bind the pod service account %q defined in the configuration to the cluster role in the %q namespace: %v", podServiceAccountName, namespace, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.logger.Infof("successfully deployed the role binding for the pod service account %q to the %q namespace", podServiceAccountName, namespace)
|
c.logger.Infof("successfully deployed the role binding for the pod service account %q to the %q namespace", podServiceAccountName, namespace)
|
||||||
|
|
||||||
} else if k8sutil.ResourceAlreadyExists(err) {
|
} else if k8sutil.ResourceAlreadyExists(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -91,14 +91,14 @@ type Config struct {
|
||||||
Scalyr
|
Scalyr
|
||||||
LogicalBackup
|
LogicalBackup
|
||||||
|
|
||||||
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to'
|
||||||
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
EtcdHost string `name:"etcd_host" default:""` // special values: the empty string "" means Patroni will use K8s as a DCS
|
||||||
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16"`
|
DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16"`
|
||||||
Sidecars map[string]string `name:"sidecar_docker_images"`
|
Sidecars map[string]string `name:"sidecar_docker_images"`
|
||||||
// default name `operator` enables backward compatibility with the older ServiceAccountName field
|
PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"`
|
||||||
PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
|
|
||||||
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
// value of this string must be valid JSON or YAML; see initPodServiceAccount
|
||||||
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
PodServiceAccountDefinition string `name:"pod_service_account_definition" default:""`
|
||||||
|
PodServiceAccountRoleDefinition string `name:"pod_service_account_role_definition" default:""`
|
||||||
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
PodServiceAccountRoleBindingDefinition string `name:"pod_service_account_role_binding_definition" default:""`
|
||||||
MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"`
|
MasterPodMoveTimeout time.Duration `name:"master_pod_move_timeout" default:"20m"`
|
||||||
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
DbHostedZone string `name:"db_hosted_zone" default:"db.example.com"`
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ type KubernetesClient struct {
|
||||||
corev1.NamespacesGetter
|
corev1.NamespacesGetter
|
||||||
corev1.ServiceAccountsGetter
|
corev1.ServiceAccountsGetter
|
||||||
appsv1.StatefulSetsGetter
|
appsv1.StatefulSetsGetter
|
||||||
|
rbacv1.RolesGetter
|
||||||
rbacv1.RoleBindingsGetter
|
rbacv1.RoleBindingsGetter
|
||||||
policyv1beta1.PodDisruptionBudgetsGetter
|
policyv1beta1.PodDisruptionBudgetsGetter
|
||||||
apiextbeta1.CustomResourceDefinitionsGetter
|
apiextbeta1.CustomResourceDefinitionsGetter
|
||||||
|
|
@ -103,6 +104,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) {
|
||||||
kubeClient.StatefulSetsGetter = client.AppsV1()
|
kubeClient.StatefulSetsGetter = client.AppsV1()
|
||||||
kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1()
|
kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1()
|
||||||
kubeClient.RESTClient = client.CoreV1().RESTClient()
|
kubeClient.RESTClient = client.CoreV1().RESTClient()
|
||||||
|
kubeClient.RolesGetter = client.RbacV1()
|
||||||
kubeClient.RoleBindingsGetter = client.RbacV1()
|
kubeClient.RoleBindingsGetter = client.RbacV1()
|
||||||
kubeClient.CronJobsGetter = client.BatchV1beta1()
|
kubeClient.CronJobsGetter = client.BatchV1beta1()
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue