merge with master

This commit is contained in:
Felix Kunde 2019-12-16 12:05:18 +01:00
commit 36bb4e77df
41 changed files with 378 additions and 163 deletions

View File

@ -79,7 +79,8 @@ scm-source.json: .git
tools:
GO111MODULE=on go get -u honnef.co/go/tools/cmd/staticcheck
GO111MODULE=on go get k8s.io/client-go@kubernetes-1.16.0
GO111MODULE=on go get k8s.io/client-go@kubernetes-1.16.3
GO111MODULE=on go mod tidy
fmt:
@gofmt -l -w -s $(DIRS)

View File

@ -107,10 +107,14 @@ spec:
type: object
additionalProperties:
type: string
enable_init_containers:
type: boolean
enable_pod_antiaffinity:
type: boolean
enable_pod_disruption_budget:
type: boolean
enable_sidecars:
type: boolean
infrastructure_roles_secret_name:
type: string
inherited_labels:
@ -298,3 +302,7 @@ spec:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
scalyr_server_url:
type: string
status:
type: object
additionalProperties:
type: string

View File

@ -222,7 +222,7 @@ spec:
# only the format of the given number.
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
pattern: '^(\d+m|\d+\.\d{1,3})$'
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
# Note: the value specified here must not be zero or be lower
# than the corresponding request.
memory:
@ -253,7 +253,7 @@ spec:
# only the format of the given number.
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
pattern: '^(\d+m|\d+\.\d{1,3})$'
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
# Note: the value specified here must not be zero or be higher
# than the corresponding limit.
memory:

View File

@ -59,10 +59,14 @@ configKubernetes:
# annotations attached to each database pod
# custom_pod_annotations: keya:valuea,keyb:valueb
# enables initContainers to run actions before Spilo is started
enable_init_containers: "true"
# toggles pod anti affinity on the Postgres pods
enable_pod_antiaffinity: "false"
# toggles PDB to set to MinAvailabe 0 or 1
enable_pod_disruption_budget: "true"
# enables sidecar containers to run alongside Spilo in the same pod
enable_sidecars: "true"
# name of the secret containing infrastructure roles names and passwords
# infrastructure_roles_secret_name: postgresql-infrastructure-roles

View File

@ -63,10 +63,14 @@ configKubernetes:
# keya: valuea
# keyb: valueb
# enables initContainers to run actions before Spilo is started
enable_init_containers: true
# toggles pod anti affinity on the Postgres pods
enable_pod_antiaffinity: false
# toggles PDB to set to MinAvailabe 0 or 1
enable_pod_disruption_budget: true
# enables sidecar containers to run alongside Spilo in the same pod
enable_sidecars: true
# name of the secret containing infrastructure roles names and passwords
# infrastructure_roles_secret_name: postgresql-infrastructure-roles

View File

@ -41,8 +41,8 @@ function aws_upload {
args=()
[[ ! -z "$EXPECTED_SIZE" ]] && args+=("--expected-size=$EXPECTED_SIZE")
[[ ! -z "$LOGICAL_BACKUP_S3_ENDPOINT" ]] && args+=("--endpoint-url=\"$LOGICAL_BACKUP_S3_ENDPOINT\"")
[[ ! "$LOGICAL_BACKUP_S3_SSE" == "" ]] && args+=("--sse=\"$LOGICAL_BACKUP_S3_SSE\"")
[[ ! -z "$LOGICAL_BACKUP_S3_ENDPOINT" ]] && args+=("--endpoint-url=$LOGICAL_BACKUP_S3_ENDPOINT")
[[ ! "$LOGICAL_BACKUP_S3_SSE" == "" ]] && args+=("--sse=$LOGICAL_BACKUP_S3_SSE")
aws s3 cp - "$PATH_TO_BACKUP" "${args[@]//\'/}" --debug
}

View File

@ -3,6 +3,26 @@
Learn how to configure and manage the Postgres Operator in your Kubernetes (K8s)
environment.
## Minor and major version upgrade
Minor version upgrades for PostgreSQL are handled via updating the Spilo Docker
image. The operator will carry out a rolling update of Pods which includes a
switchover (planned failover) of the master to the Pod with new minor version.
The switch should usually take less than 5 seconds, still clients have to
reconnect.
Major version upgrades are supported via [cloning](user.md#clone-directly). The
new cluster manifest must have a higher `version` string than the source cluster
and will be created from a basebackup. Depending of the cluster size, downtime
in this case can be significant as writes to the database should be stopped and
all WAL files should be archived first before cloning is started.
Note, that simply changing the version string in the `postgresql` manifest does
not work at present and leads to errors. Neither Patroni nor Postgres Operator
can do in place `pg_upgrade`. Still, it can be executed manually in the Postgres
container, which is tricky (i.e. systems need to be stopped, replicas have to be
synced) but of course faster than cloning.
## CRD Validation
[CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions)
@ -95,8 +115,6 @@ is used by the operator to connect to the clusters after creation.
## Role-based access control for the operator
### Service account and cluster roles
The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml)
defines the service account, cluster roles and bindings needed for the operator
to function under access control restrictions. To deploy the operator with this
@ -110,6 +128,8 @@ kubectl create -f manifests/postgres-operator.yaml
kubectl create -f manifests/minimal-postgres-manifest.yaml
```
### Service account and cluster roles
Note that the service account is named `zalando-postgres-operator`. You may have
to change the `service_account_name` in the operator ConfigMap and
`serviceAccountName` in the `postgres-operator` deployment appropriately. This
@ -117,12 +137,6 @@ is done intentionally to avoid breaking those setups that already work with the
default `operator` account. In the future the operator should ideally be run
under the `zalando-postgres-operator` service account.
The service account defined in `operator-service-account-rbac.yaml` acquires
some privileges not used by the operator (i.e. we only need `list` and `watch`
on `configmaps` resources). This is also done intentionally to avoid breaking
things if someone decides to configure the same service account in the
operator's ConfigMap to run Postgres clusters.
### Give K8s users access to create/list `postgresqls`
By default `postgresql` custom resources can only be listed and changed by
@ -158,7 +172,6 @@ metadata:
name: postgres-operator
data:
toleration: "key:postgres,operator:Exists,effect:NoSchedule"
...
```
For an OperatorConfiguration resource the toleration should be defined like
@ -173,7 +186,6 @@ configuration:
kubernetes:
toleration:
postgres: "key:postgres,operator:Exists,effect:NoSchedule"
...
```
Note that the K8s version 1.13 brings [taint-based eviction](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/#taint-based-evictions)
@ -251,7 +263,6 @@ metadata:
name: postgres-operator
data:
inherited_labels: application,environment
...
```
**OperatorConfiguration**
@ -266,7 +277,6 @@ configuration:
inherited_labels:
- application
- environment
...
```
**cluster manifest**
@ -280,7 +290,7 @@ metadata:
application: my-app
environment: demo
spec:
...
...
```
**network policy**
@ -295,7 +305,6 @@ spec:
matchLabels:
application: my-app
environment: demo
...
```
@ -318,7 +327,6 @@ metadata:
data:
# referencing config map with custom settings
pod_environment_configmap: postgres-pod-config
...
```
**OperatorConfiguration**
@ -332,7 +340,6 @@ configuration:
kubernetes:
# referencing config map with custom settings
pod_environment_configmap: postgres-pod-config
...
```
**referenced ConfigMap `postgres-pod-config`**
@ -413,12 +420,12 @@ external systems but defined for an individual Postgres cluster in its manifest.
A typical example is a role for connections from an application that uses the
database.
* **Human users** originate from the Teams API that returns a list of the team
members given a team id. The operator differentiates between (a) product teams
that own a particular Postgres cluster and are granted admin rights to maintain
it, and (b) Postgres superuser teams that get the superuser access to all
Postgres databases running in a K8s cluster for the purposes of maintaining and
troubleshooting.
* **Human users** originate from the [Teams API](user.md#teams-api-roles) that
returns a list of the team members given a team id. The operator differentiates
between (a) product teams that own a particular Postgres cluster and are granted
admin rights to maintain it, and (b) Postgres superuser teams that get the
superuser access to all Postgres databases running in a K8s cluster for the
purposes of maintaining and troubleshooting.
## Understanding rolling update of Spilo pods
@ -482,7 +489,7 @@ A secret can be pre-provisioned in different ways:
With the v1.2 release the Postgres Operator is shipped with a browser-based
configuration user interface (UI) that simplifies managing Postgres clusters
with the operator. The UI runs with Node.js and comes with it's own docker
with the operator. The UI runs with Node.js and comes with it's own Docker
image.
Run NPM to continuously compile `tags/js` code. Basically, it creates an
@ -494,14 +501,14 @@ Run NPM to continuously compile `tags/js` code. Basically, it creates an
To build the Docker image open a shell and change to the `ui` folder. Then run:
```
```bash
docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.2.0 .
```
Apply all manifests for the `ui/manifests` folder to deploy the Postgres
Operator UI on K8s. For local tests you don't need the Ingress resource.
```
```bash
kubectl apply -f ui/manifests
```
@ -511,6 +518,6 @@ to the K8s and Postgres Operator REST API. You can use the provided
`run_local.sh` script for this. Make sure it uses the correct URL to your K8s
API server, e.g. for minikube it would be `https://192.168.99.100:8443`.
```
```bash
./run_local.sh
```

View File

@ -40,7 +40,7 @@ This would take a while to complete. You have to redo `make deps` every time
your dependencies list changes, i.e. after adding a new library dependency.
Build the operator with the `make docker` command. You may define the TAG
variable to assign an explicit tag to your docker image and the IMAGE to set
variable to assign an explicit tag to your Docker image and the IMAGE to set
the image name. By default, the tag is computed with
`git describe --tags --always --dirty` and the image is
`registry.opensource.zalan.do/acid/postgres-operator`
@ -60,10 +60,10 @@ The binary will be placed into the build directory.
## Deploying self build image
The fastest way to run and test your docker image locally is to reuse the docker
from [minikube](https://github.com/kubernetes/minikube/releases) or use the
`load docker-image` from [kind](https://kind.sigs.k8s.io/). The following steps
will get you the docker image built and deployed.
The fastest way to run and test your Docker image locally is to reuse the Docker
environment from [minikube](https://github.com/kubernetes/minikube/releases)
or use the `load docker-image` from [kind](https://kind.sigs.k8s.io/). The
following steps will get you the Docker image built and deployed.
```bash
# minikube
@ -163,7 +163,7 @@ The operator also supports pprof endpoints listed at the
* /debug/pprof/trace
It's possible to attach a debugger to troubleshoot postgres-operator inside a
docker container. It's possible with [gdb](https://www.gnu.org/software/gdb/)
Docker container. It's possible with [gdb](https://www.gnu.org/software/gdb/)
and [delve](https://github.com/derekparker/delve). Since the latter one is a
specialized debugger for Go, we will use it as an example. To use it you need:

View File

@ -13,7 +13,7 @@ manages PostgreSQL clusters on Kubernetes (K8s):
2. The operator also watches updates to [its own configuration](../manifests/configmap.yaml)
and alters running Postgres clusters if necessary. For instance, if the
docker image in a pod is changed, the operator carries out the rolling
Docker image in a pod is changed, the operator carries out the rolling
update, which means it re-spawns pods of each managed StatefulSet one-by-one
with the new Docker image.

View File

@ -157,9 +157,12 @@ export PGPORT=$(echo $HOST_PORT | cut -d: -f 2)
```
Retrieve the password from the K8s Secret that is created in your cluster.
Non-encrypted connections are rejected by default, so set the SSL mode to
require:
```bash
export PGPASSWORD=$(kubectl get secret postgres.acid-minimal-cluster.credentials -o 'jsonpath={.data.password}' | base64 -d)
export PGSSLMODE=require
psql -U postgres
```

View File

@ -62,7 +62,7 @@ These parameters are grouped directly under the `spec` key in the manifest.
field.
* **dockerImage**
custom docker image that overrides the **docker_image** operator parameter.
custom Docker image that overrides the **docker_image** operator parameter.
It should be a [Spilo](https://github.com/zalando/spilo) image. Optional.
* **spiloFSGroup**
@ -124,7 +124,7 @@ These parameters are grouped directly under the `spec` key in the manifest.
* **enableShmVolume**
Start a database pod without limitations on shm memory. By default docker
Start a database pod without limitations on shm memory. By default Docker
limit `/dev/shm` to `64M` (see e.g. the [docker
issue](https://github.com/docker-library/postgres/issues/416), which could be
not enough if PostgreSQL uses parallel workers heavily. If this option is
@ -185,19 +185,19 @@ explanation of `ttl` and `loop_wait` parameters.
* **ttl**
Patroni `ttl` parameter value, optional. The default is set by the Spilo
docker image. Optional.
Docker image. Optional.
* **loop_wait**
Patroni `loop_wait` parameter value, optional. The default is set by the
Spilo docker image. Optional.
Spilo Docker image. Optional.
* **retry_timeout**
Patroni `retry_timeout` parameter value, optional. The default is set by the
Spilo docker image. Optional.
Spilo Docker image. Optional.
* **maximum_lag_on_failover**
Patroni `maximum_lag_on_failover` parameter value, optional. The default is
set by the Spilo docker image. Optional.
set by the Spilo Docker image. Optional.
* **slots**
permanent replication slots that Patroni preserves after failover by
@ -320,7 +320,7 @@ defined in the sidecar dictionary:
name of the sidecar. Required.
* **image**
docker image of the sidecar. Required.
Docker image of the sidecar. Required.
* **env**
a dictionary of environment variables. Use usual Kubernetes definition

View File

@ -81,15 +81,15 @@ Those are top-level keys, containing both leaf keys and groups.
Kubernetes-native DCS).
* **docker_image**
Spilo docker image for Postgres instances. For production, don't rely on the
Spilo Docker image for Postgres instances. For production, don't rely on the
default image, as it might be not the most up-to-date one. Instead, build
your own Spilo image from the [github
repository](https://github.com/zalando/spilo).
* **sidecar_docker_images**
a map of sidecar names to docker images for the containers to run alongside
Spilo. In case of the name conflict with the definition in the cluster
manifest the cluster-specific one is preferred.
a map of sidecar names to Docker images to run with Spilo. In case of the name
conflict with the definition in the cluster manifest the cluster-specific one
is preferred.
* **enable_shm_volume**
Instruct operator to start any new database pod without limitations on shm
@ -196,6 +196,14 @@ configuration they are grouped under the `kubernetes` key.
[admin docs](../administrator.md#pod-disruption-budget) for more information.
Default is true.
* **enable_init_containers**
global option to allow for creating init containers to run actions before
Spilo is started. Default is true.
* **enable_sidecars**
global option to allow for creating sidecar containers to run alongside Spilo
on the same pod. Default is true.
* **secret_name_template**
a template for the name of the database user secrets generated by the
operator. `{username}` is replaced with name of the secret, `{cluster}` with

View File

@ -13,7 +13,7 @@ kind: postgresql
metadata:
name: acid-minimal-cluster
spec:
teamId: "ACID"
teamId: "acid"
volume:
size: 1Gi
numberOfInstances: 2
@ -30,7 +30,7 @@ spec:
databases:
foo: zalando
postgresql:
version: "10"
version: "11"
```
Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator)
@ -40,6 +40,17 @@ you can find this example also in the manifests folder:
kubectl create -f manifests/minimal-postgres-manifest.yaml
```
Make sure, the `spec` section of the manifest contains at least a `teamId`, the
`numberOfInstances` and the `postgresql` object with the `version` specified.
The minimum volume size to run the `postgresql` resource on Elastic Block
Storage (EBS) is `1Gi`.
Note, that the name of the cluster must start with the `teamId` and `-`. At
Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster
names and colliding entities. The team ID would also be used to query an API to
get all members of a team and create [database roles](#teams-api-roles) for
them.
## Watch pods being created
```bash
@ -62,10 +73,12 @@ kubectl port-forward $PGMASTER 6432:5432
Open another CLI and connect to the database. Use the generated secret of the
`postgres` robot user to connect to our `acid-minimal-cluster` master running
in Minikube:
in Minikube. As non-encrypted connections are rejected by default set the SSL
mode to require:
```bash
export PGPASSWORD=$(kubectl get secret postgres.acid-minimal-cluster.credentials -o 'jsonpath={.data.password}' | base64 -d)
export PGSSLMODE=require
psql -U postgres -p 6432
```
@ -77,8 +90,7 @@ cluster. It covers three use-cases:
* `manifest roles`: create application roles specific to the cluster described
in the manifest.
* `infrastructure roles`: create application roles that should be automatically
created on every
cluster managed by the operator.
created on every cluster managed by the operator.
* `teams API roles`: automatically create users for every member of the team
owning the database cluster.
@ -128,9 +140,9 @@ The infrastructure roles secret is specified by the `infrastructure_roles_secret
parameter. The role definition looks like this (values are base64 encoded):
```yaml
user1: ZGJ1c2Vy
password1: c2VjcmV0
inrole1: b3BlcmF0b3I=
user1: ZGJ1c2Vy
password1: c2VjcmV0
inrole1: b3BlcmF0b3I=
```
The block above describes the infrastructure role 'dbuser' with password
@ -151,13 +163,13 @@ secret and a ConfigMap. The ConfigMap must have the same name as the secret.
The secret should contain an entry with 'rolename:rolepassword' for each role.
```yaml
dbuser: c2VjcmV0
dbuser: c2VjcmV0
```
And the role description for that user should be specified in the ConfigMap.
```yaml
data:
data:
dbuser: |
inrole: [operator, admin] # following roles will be assigned to the new user
user_flags:
@ -182,6 +194,50 @@ See [infrastructure roles secret](../manifests/infrastructure-roles.yaml)
and [infrastructure roles configmap](../manifests/infrastructure-roles-configmap.yaml)
for the examples.
### Teams API roles
These roles are meant for database activity of human users. It's possible to
configure the operator to automatically create database roles for lets say all
employees of one team. They are not listed in the manifest and there are no K8s
secrets created for them. Instead they would use an OAuth2 token to connect. To
get all members of the team the operator queries a defined API endpoint that
returns usernames. A minimal Teams API should work like this:
```
/.../<teamname> -> ["name","anothername"]
```
A ["fake" Teams API](../manifests/fake-teams-api.yaml) deployment is provided
in the manifests folder to set up a basic API around whatever services is used
for user management. The Teams API's URL is set in the operator's
[configuration](reference/operator_parameters.md#automatic-creation-of-human-users-in-the-database)
and `enable_teams_api` must be set to `true`. There are more settings available
to choose superusers, group roles, [PAM configuration](https://github.com/CyberDem0n/pam-oauth2)
etc. An OAuth2 token can be passed to the Teams API via a secret. The name for
this secret is configurable with the `oauth_token_secret_name` parameter.
## Resource definition
The compute resources to be used for the Postgres containers in the pods can be
specified in the postgresql cluster manifest.
```yaml
spec:
resources:
requests:
cpu: 10m
memory: 100Mi
limits:
cpu: 300m
memory: 300Mi
```
The minimum limit to properly run the `postgresql` resource is `256m` for `cpu`
and `256Mi` for `memory`. If a lower value is set in the manifest the operator
will cancel ADD or UPDATE events on this resource with an error. If no
resources are defined in the manifest the operator will obtain the configured
[default requests](reference/operator_parameters.md#kubernetes-resource-requests).
## Use taints and tolerations for dedicated PostgreSQL nodes
To ensure Postgres pods are running on nodes without any other application pods,
@ -189,12 +245,7 @@ you can use [taints and tolerations](https://kubernetes.io/docs/concepts/configu
and configure the required toleration in the manifest.
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-minimal-cluster
spec:
teamId: "ACID"
tolerations:
- key: postgres
operator: Exists
@ -212,11 +263,6 @@ section in the spec. There are two options here:
### Clone directly
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-test-cluster
spec:
clone:
cluster: "acid-batman"
@ -232,11 +278,6 @@ means that you can clone only from clusters within the same namespace.
### Clone from S3
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-test-cluster
spec:
clone:
uid: "efd12e58-5786-11e8-b5a7-06148230260c"
@ -265,10 +306,6 @@ For non AWS S3 following settings can be set to support cloning from other S3
implementations:
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-test-cluster
spec:
clone:
uid: "efd12e58-5786-11e8-b5a7-06148230260c"
@ -305,7 +342,7 @@ Things to note:
- There is no way to transform a non-standby cluster to a standby cluster
through the operator. Adding the standby section to the manifest of a running
Postgres cluster will have no effect. However, it can be done through Patroni
by adding the [standby_cluster] (https://github.com/zalando/patroni/blob/bd2c54581abb42a7d3a3da551edf0b8732eefd27/docs/replica_bootstrap.rst#standby-cluster)
by adding the [standby_cluster](https://github.com/zalando/patroni/blob/bd2c54581abb42a7d3a3da551edf0b8732eefd27/docs/replica_bootstrap.rst#standby-cluster)
section using `patronictl edit-config`. Note that the transformed standby
cluster will not be doing any streaming. It will be in standby mode and allow
read-only transactions only.
@ -317,13 +354,7 @@ used for log aggregation, monitoring, backups or other tasks. A sidecar can be
specified like this:
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-minimal-cluster
spec:
...
sidecars:
- name: "container-name"
image: "company/image:tag"
@ -350,6 +381,10 @@ variables are always passed to sidecars:
The PostgreSQL volume is shared with sidecars and is mounted at
`/home/postgres/pgdata`.
**Note**: The operator will not create a cluster if sidecar containers are
specified but globally disabled in the configuration. The `enable_sidecars`
option must be set to `true`.
## InitContainers Support
Each cluster can specify arbitrary init containers to run. These containers can
@ -357,13 +392,7 @@ be used to run custom actions before any normal and sidecar containers start.
An init container can be specified like this:
```yaml
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-minimal-cluster
spec:
...
initContainers:
- name: "container-name"
image: "company/image:tag"
@ -374,18 +403,17 @@ spec:
`initContainers` accepts full `v1.Container` definition.
**Note**: The operator will not create a cluster if `initContainers` are
specified but globally disabled in the configuration. The
`enable_init_containers` option must be set to `true`.
## Increase volume size
PostgreSQL operator supports statefulset volume resize if you're using the
Postgres operator supports statefulset volume resize if you're using the
operator on top of AWS. For that you need to change the size field of the
volume description in the cluster manifest and apply the change:
```
apiVersion: "acid.zalan.do/v1"
kind: postgresql
metadata:
name: acid-test-cluster
```yaml
spec:
volume:
size: 5Gi # new volume size
@ -414,7 +442,8 @@ size of volumes that correspond to the previously running pods is not changed.
You can enable logical backups from the cluster manifest by adding the following
parameter in the spec section:
```
```yaml
spec:
enableLogicalBackup: true
```

View File

@ -179,20 +179,14 @@ class EndToEndTestCase(unittest.TestCase):
# update the cluster-wide image of the logical backup pod
image = "test-image-name"
config_patch = {
patch_logical_backup_image = {
"configuration": {
"logical_backup": {
"logical_backup_docker_image": image,
}
}
}
k8s.api.custom_objects_api.patch_namespaced_custom_object(
"acid.zalan.do", "v1", "default", "operatorconfigurations", "postgresql-operator-default-configuration", config_patch)
operator_pod = k8s.api.core_v1.list_namespaced_pod(
'default', label_selector="name=postgres-operator").items[0].metadata.name
k8s.api.core_v1.delete_namespaced_pod(operator_pod, "default") # restart reloads the conf
k8s.wait_for_operator_pod_start()
k8s.update_config(patch_logical_backup_image)
jobs = k8s.get_logical_backup_job().items
actual_image = jobs[0].spec.job_template.spec.template.spec.containers[0].image
@ -319,12 +313,21 @@ class K8s:
def wait_for_logical_backup_job_creation(self):
self.wait_for_logical_backup_job(expected_num_of_jobs=1)
def apply_kustomization(self, path):
subprocess.run(["kubectl", "apply", "-k", path])
def update_config(self, patch):
k8s.api.custom_objects_api.patch_namespaced_custom_object(
"acid.zalan.do", "v1", "default", "operatorconfigurations", "postgresql-operator-default-configuration", patch)
operator_pod = self.api.core_v1.list_namespaced_pod(
'default', label_selector="name=postgres-operator").items[0].metadata.name
self.api.core_v1.delete_namespaced_pod(operator_pod, "default") # restart reloads the conf
self.wait_for_operator_pod_start()
def create_with_kubectl(self, path):
subprocess.run(["kubectl", "create", "-f", path])
def apply_kustomization(self, path):
subprocess.run(["kubectl", "apply", "-k", path])
if __name__ == '__main__':
unittest.main()

20
go.mod
View File

@ -3,23 +3,23 @@ module github.com/zalando/postgres-operator
go 1.12
require (
github.com/aws/aws-sdk-go v1.25.1
github.com/aws/aws-sdk-go v1.25.44
github.com/emicklei/go-restful v2.9.6+incompatible // indirect
github.com/evanphx/json-patch v4.5.0+incompatible // indirect
github.com/googleapis/gnostic v0.3.0 // indirect
github.com/imdario/mergo v0.3.7 // indirect
github.com/imdario/mergo v0.3.8 // indirect
github.com/lib/pq v1.2.0
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d
github.com/sirupsen/logrus v1.4.2
golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c // indirect
golang.org/x/net v0.0.0-20191126235420-ef20fe5d7933 // indirect
golang.org/x/sys v0.0.0-20191128015809-6d18c012aee9 // indirect
golang.org/x/tools v0.0.0-20191127201027-ecd32218bd7f // indirect
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 // indirect
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.5
gopkg.in/yaml.v2 v2.2.4
k8s.io/api v0.0.0-20191121015604-11707872ac1c
k8s.io/apiextensions-apiserver v0.0.0-20191121021419-88daf26ec3b8
k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a
k8s.io/client-go v11.0.0+incompatible
k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5
k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d
k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2
k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e
)

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -o errexit
set -o nounset

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/usr/bin/env bash
set -o errexit
set -o nounset

View File

@ -10,7 +10,7 @@ spec:
- name: date
image: busybox
command: [ "/bin/date" ]
teamId: "ACID"
teamId: "acid"
volume:
size: 1Gi
# storageClass: my-sc

View File

@ -23,11 +23,13 @@ data:
# enable_admin_role_for_users: "true"
# enable_crd_validation: "true"
# enable_database_access: "true"
# enable_init_containers: "true"
enable_master_load_balancer: "false"
# enable_pod_antiaffinity: "false"
# enable_pod_disruption_budget: "true"
enable_replica_load_balancer: "false"
# enable_shm_volume: "true"
# enable_sidecars: "true"
# enable_team_superuser: "false"
enable_teams_api: "false"
# etcd_host: ""

View File

@ -4,7 +4,7 @@ metadata:
name: acid-minimal-cluster
namespace: default
spec:
teamId: "ACID"
teamId: "acid"
volume:
size: 1Gi
numberOfInstances: 2

View File

@ -83,10 +83,14 @@ spec:
type: object
additionalProperties:
type: string
enable_init_containers:
type: boolean
enable_pod_antiaffinity:
type: boolean
enable_pod_disruption_budget:
type: boolean
enable_sidecars:
type: boolean
infrastructure_roles_secret_name:
type: string
inherited_labels:
@ -274,3 +278,7 @@ spec:
pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
scalyr_server_url:
type: string
status:
type: object
additionalProperties:
type: string

View File

@ -26,9 +26,11 @@ configuration:
# custom_pod_annotations:
# keya: valuea
# keyb: valueb
enable_init_containers: true
enable_pod_antiaffinity: false
enable_pod_disruption_budget: true
# infrastructure_roles_secret_name: postgresql-infrastructure-roles
enable_sidecars: true
# infrastructure_roles_secret_name: "postgresql-infrastructure-roles"
# inherited_labels:
# - application
# - environment

View File

@ -186,7 +186,7 @@ spec:
# only the format of the given number.
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
pattern: '^(\d+m|\d+\.\d{1,3})$'
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
# Note: the value specified here must not be zero or be lower
# than the corresponding request.
memory:
@ -217,7 +217,7 @@ spec:
# only the format of the given number.
#
# https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
pattern: '^(\d+m|\d+\.\d{1,3})$'
pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
# Note: the value specified here must not be zero or be higher
# than the corresponding limit.
memory:
@ -325,3 +325,7 @@ spec:
type: string
subPath:
type: string
status:
type: object
additionalProperties:
type: string

View File

@ -4,7 +4,7 @@ metadata:
name: acid-standby-cluster
namespace: default
spec:
teamId: "ACID"
teamId: "acid"
volume:
size: 1Gi
numberOfInstances: 1

View File

@ -356,7 +356,7 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
"cpu": {
Type: "string",
Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
Pattern: "^(\\d+m|\\d+\\.\\d{1,3})$",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"memory": {
Type: "string",
@ -372,7 +372,7 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
"cpu": {
Type: "string",
Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
Pattern: "^(\\d+m|\\d+\\.\\d{1,3})$",
Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
},
"memory": {
Type: "string",
@ -578,6 +578,14 @@ var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
},
},
},
"status": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
},
},
}
@ -686,12 +694,18 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
},
},
},
"enable_init_containers": {
Type: "boolean",
},
"enable_pod_antiaffinity": {
Type: "boolean",
},
"enable_pod_disruption_budget": {
Type: "boolean",
},
"enable_sidecars": {
Type: "boolean",
},
"infrastructure_roles_secret_name": {
Type: "string",
},
@ -994,6 +1008,14 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
},
},
},
"status": {
Type: "object",
AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
Schema: &apiextv1beta1.JSONSchemaProps{
Type: "string",
},
},
},
},
},
}

View File

@ -50,6 +50,8 @@ type KubernetesMetaConfiguration struct {
WatchedNamespace string `json:"watched_namespace,omitempty"`
PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"`
EnablePodDisruptionBudget *bool `json:"enable_pod_disruption_budget,omitempty"`
EnableInitContainers *bool `json:"enable_init_containers,omitempty"`
EnableSidecars *bool `json:"enable_sidecars,omitempty"`
SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"`
ClusterDomain string `json:"cluster_domain"`
OAuthTokenSecretName spec.NamespacedName `json:"oauth_token_secret_name,omitempty"`

View File

@ -180,7 +180,7 @@ var unmarshalCluster = []struct {
"name": "acid-testcluster1"
},
"spec": {
"teamId": "ACID",
"teamId": "acid",
"pod_priority_class_name": "spilo-pod-priority",
"volume": {
"size": "5Gi",
@ -290,7 +290,7 @@ var unmarshalCluster = []struct {
ResourceLimits: ResourceDescription{CPU: "300m", Memory: "3000Mi"},
},
TeamID: "ACID",
TeamID: "acid",
AllowedSourceRanges: []string{"127.0.0.1/32"},
NumberOfInstances: 2,
Users: map[string]UserFlags{"zalando": {"superuser", "createdb"}},
@ -319,7 +319,7 @@ var unmarshalCluster = []struct {
},
Error: "",
},
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"ACID","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`),
marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`),
err: nil},
// example with teamId set in input
{

View File

@ -81,6 +81,16 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
*out = new(bool)
**out = **in
}
if in.EnableInitContainers != nil {
in, out := &in.EnableInitContainers, &out.EnableInitContainers
*out = new(bool)
**out = **in
}
if in.EnableSidecars != nil {
in, out := &in.EnableSidecars, &out.EnableSidecars
*out = new(bool)
**out = **in
}
out.OAuthTokenSecretName = in.OAuthTokenSecretName
out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName
if in.ClusterLabels != nil {

View File

@ -227,6 +227,10 @@ func (c *Cluster) Create() error {
c.setStatus(acidv1.ClusterStatusCreating)
if err = c.validateResources(&c.Spec); err != nil {
return fmt.Errorf("insufficient resource limits specified: %v", err)
}
for _, role := range []PostgresRole{Master, Replica} {
if c.Endpoints[role] != nil {
@ -491,6 +495,44 @@ func compareResourcesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.Resourc
}
func (c *Cluster) validateResources(spec *acidv1.PostgresSpec) error {
// setting limits too low can cause unnecessary evictions / OOM kills
const (
cpuMinLimit = "256m"
memoryMinLimit = "256Mi"
)
var (
isSmaller bool
err error
)
cpuLimit := spec.Resources.ResourceLimits.CPU
if cpuLimit != "" {
isSmaller, err = util.IsSmallerQuantity(cpuLimit, cpuMinLimit)
if err != nil {
return fmt.Errorf("error validating CPU limit: %v", err)
}
if isSmaller {
return fmt.Errorf("defined CPU limit %s is below required minimum %s to properly run postgresql resource", cpuLimit, cpuMinLimit)
}
}
memoryLimit := spec.Resources.ResourceLimits.Memory
if memoryLimit != "" {
isSmaller, err = util.IsSmallerQuantity(memoryLimit, memoryMinLimit)
if err != nil {
return fmt.Errorf("error validating memory limit: %v", err)
}
if isSmaller {
return fmt.Errorf("defined memory limit %s is below required minimum %s to properly run postgresql resource", memoryLimit, memoryMinLimit)
}
}
return nil
}
// Update changes Kubernetes objects according to the new specification. Unlike the sync case, the missing object
// (i.e. service) is treated as an error
// logical backup cron jobs are an exception: a user-initiated Update can enable a logical backup job
@ -501,6 +543,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
c.mu.Lock()
defer c.mu.Unlock()
oldStatus := c.Status
c.setStatus(acidv1.ClusterStatusUpdating)
c.setSpec(newSpec)
@ -512,6 +555,22 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
}
}()
if err := c.validateResources(&newSpec.Spec); err != nil {
err = fmt.Errorf("insufficient resource limits specified: %v", err)
// cancel update only when (already too low) pod resources were edited
// if cluster was successfully running before the update, continue but log a warning
isCPULimitSmaller, err2 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.CPU, oldSpec.Spec.Resources.ResourceLimits.CPU)
isMemoryLimitSmaller, err3 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.Memory, oldSpec.Spec.Resources.ResourceLimits.Memory)
if oldStatus.Running() && !isCPULimitSmaller && !isMemoryLimitSmaller && err2 == nil && err3 == nil {
c.logger.Warning(err)
} else {
updateFailed = true
return err
}
}
if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
//we need that hack to generate statefulset with the old version

View File

@ -720,6 +720,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
var (
err error
initContainers []v1.Container
sidecarContainers []v1.Container
podTemplate *v1.PodTemplateSpec
volumeClaimTemplate *v1.PersistentVolumeClaim
@ -740,7 +741,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
limit = c.OpConfig.DefaultMemoryLimit
}
isSmaller, err := util.RequestIsSmallerThanLimit(request, limit)
isSmaller, err := util.IsSmallerQuantity(request, limit)
if err != nil {
return nil, err
}
@ -767,7 +768,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
limit = c.OpConfig.DefaultMemoryLimit
}
isSmaller, err := util.RequestIsSmallerThanLimit(sidecarRequest, sidecarLimit)
isSmaller, err := util.IsSmallerQuantity(sidecarRequest, sidecarLimit)
if err != nil {
return nil, err
}
@ -786,6 +787,13 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
return nil, fmt.Errorf("could not generate resource requirements: %v", err)
}
if spec.InitContainers != nil && len(spec.InitContainers) > 0 {
if c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) {
c.logger.Warningf("initContainers specified but disabled in configuration - next statefulset creation would fail")
}
initContainers = spec.InitContainers
}
customPodEnvVarsList := make([]v1.EnvVar, 0)
if c.OpConfig.PodEnvironmentConfigMap != "" {
@ -872,10 +880,15 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
}
// generate sidecar containers
if sideCars != nil && len(sideCars) > 0 {
if c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) {
c.logger.Warningf("sidecars specified but disabled in configuration - next statefulset creation would fail")
}
if sidecarContainers, err = generateSidecarContainers(sideCars, volumeMounts, defaultResources,
c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername), c.logger); err != nil {
return nil, fmt.Errorf("could not generate sidecar containers: %v", err)
}
}
tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName)
@ -894,7 +907,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef
c.labelsSet(true),
annotations,
spiloContainer,
spec.InitContainers,
initContainers,
sidecarContainers,
&tolerationSpec,
effectiveFSGroup,
@ -1412,7 +1425,7 @@ func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget
pdbEnabled := c.OpConfig.EnablePodDisruptionBudget
// if PodDisruptionBudget is disabled or if there are no DB pods, set the budget to 0.
if (pdbEnabled != nil && !*pdbEnabled) || c.Spec.NumberOfInstances <= 0 {
if (pdbEnabled != nil && !(*pdbEnabled)) || c.Spec.NumberOfInstances <= 0 {
minAvailable = intstr.FromInt(0)
}

View File

@ -3,7 +3,7 @@ package cluster
import (
"reflect"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
"testing"

View File

@ -65,6 +65,17 @@ func (c *Cluster) listResources() error {
func (c *Cluster) createStatefulSet() (*appsv1.StatefulSet, error) {
c.setProcessName("creating statefulset")
// check if it's allowed that spec contains initContainers
if c.Spec.InitContainers != nil && len(c.Spec.InitContainers) > 0 &&
c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) {
return nil, fmt.Errorf("initContainers specified but disabled in configuration")
}
// check if it's allowed that spec contains sidecars
if c.Spec.Sidecars != nil && len(c.Spec.Sidecars) > 0 &&
c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) {
return nil, fmt.Errorf("sidecar containers specified but disabled in configuration")
}
statefulSetSpec, err := c.generateStatefulSet(&c.Spec)
if err != nil {
return nil, fmt.Errorf("could not generate statefulset: %v", err)

View File

@ -23,6 +23,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
c.mu.Lock()
defer c.mu.Unlock()
oldStatus := c.Status
c.setSpec(newSpec)
defer func() {
@ -34,6 +35,16 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
}
}()
if err = c.validateResources(&c.Spec); err != nil {
err = fmt.Errorf("insufficient resource limits specified: %v", err)
if oldStatus.Running() {
c.logger.Warning(err)
err = nil
} else {
return err
}
}
if err = c.initUsers(); err != nil {
err = fmt.Errorf("could not init users: %v", err)
return err

View File

@ -5,7 +5,7 @@ import (
acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
appsv1 "k8s.io/api/apps/v1"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
policybeta1 "k8s.io/api/policy/v1beta1"
"k8s.io/apimachinery/pkg/types"
)

View File

@ -111,7 +111,7 @@ func (c *Controller) initOperatorConfig() {
if c.opConfig.SetMemoryRequestToLimit {
isSmaller, err := util.RequestIsSmallerThanLimit(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
isSmaller, err := util.IsSmallerQuantity(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
if err != nil {
panic(err)
}
@ -120,7 +120,7 @@ func (c *Controller) initOperatorConfig() {
c.opConfig.DefaultMemoryRequest = c.opConfig.DefaultMemoryLimit
}
isSmaller, err = util.RequestIsSmallerThanLimit(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
isSmaller, err = util.IsSmallerQuantity(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
if err != nil {
panic(err)
}

View File

@ -54,6 +54,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.WatchedNamespace = fromCRD.Kubernetes.WatchedNamespace
result.PDBNameFormat = fromCRD.Kubernetes.PDBNameFormat
result.EnablePodDisruptionBudget = fromCRD.Kubernetes.EnablePodDisruptionBudget
result.EnableInitContainers = fromCRD.Kubernetes.EnableInitContainers
result.EnableSidecars = fromCRD.Kubernetes.EnableSidecars
result.SecretNameTemplate = fromCRD.Kubernetes.SecretNameTemplate
result.OAuthTokenSecretName = fromCRD.Kubernetes.OAuthTokenSecretName
result.InfrastructureRolesSecretName = fromCRD.Kubernetes.InfrastructureRolesSecretName

View File

@ -9,7 +9,7 @@ import (
"github.com/zalando/postgres-operator/pkg/spec"
"github.com/zalando/postgres-operator/pkg/util/k8sutil"
"k8s.io/api/core/v1"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

View File

@ -123,6 +123,8 @@ type Config struct {
ReplicaDNSNameFormat StringTemplate `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"`
PDBNameFormat StringTemplate `name:"pdb_name_format" default:"postgres-{cluster}-pdb"`
EnablePodDisruptionBudget *bool `name:"enable_pod_disruption_budget" default:"true"`
EnableInitContainers *bool `name:"enable_init_containers" default:"true"`
EnableSidecars *bool `name:"enable_sidecars" default:"true"`
Workers uint32 `name:"workers" default:"4"`
APIPort int `name:"api_port" default:"8080"`
RingLogLines int `name:"ring_log_lines" default:"100"`

View File

@ -24,7 +24,7 @@ var teamsAPItc = []struct {
{`{
"dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net",
"id": "acid",
"id_name": "ACID",
"id_name": "acid",
"team_id": "111222",
"type": "official",
"name": "Acid team name",
@ -70,7 +70,7 @@ var teamsAPItc = []struct {
&Team{
Dn: "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net",
ID: "acid",
TeamName: "ACID",
TeamName: "acid",
TeamID: "111222",
Type: "official",
FullName: "Acid team name",

View File

@ -141,17 +141,17 @@ func Coalesce(val, defaultVal string) string {
return val
}
// RequestIsSmallerThanLimit : ...
func RequestIsSmallerThanLimit(requestStr, limitStr string) (bool, error) {
// IsSmallerQuantity : checks if first resource is of a smaller quantity than the second
func IsSmallerQuantity(requestStr, limitStr string) (bool, error) {
request, err := resource.ParseQuantity(requestStr)
if err != nil {
return false, fmt.Errorf("could not parse memory request %v : %v", requestStr, err)
return false, fmt.Errorf("could not parse request %v : %v", requestStr, err)
}
limit, err2 := resource.ParseQuantity(limitStr)
if err2 != nil {
return false, fmt.Errorf("could not parse memory limit %v : %v", limitStr, err2)
return false, fmt.Errorf("could not parse limit %v : %v", limitStr, err2)
}
return request.Cmp(limit) == -1, nil

View File

@ -69,7 +69,7 @@ var substringMatch = []struct {
{regexp.MustCompile(`aaaa (\d+) bbbb`), "aaaa 123 bbbb", nil},
}
var requestIsSmallerThanLimitTests = []struct {
var requestIsSmallerQuantityTests = []struct {
request string
limit string
out bool
@ -155,14 +155,14 @@ func TestMapContains(t *testing.T) {
}
}
func TestRequestIsSmallerThanLimit(t *testing.T) {
for _, tt := range requestIsSmallerThanLimitTests {
res, err := RequestIsSmallerThanLimit(tt.request, tt.limit)
func TestIsSmallerQuantity(t *testing.T) {
for _, tt := range requestIsSmallerQuantityTests {
res, err := IsSmallerQuantity(tt.request, tt.limit)
if err != nil {
t.Errorf("RequestIsSmallerThanLimit returned unexpected error: %#v", err)
t.Errorf("IsSmallerQuantity returned unexpected error: %#v", err)
}
if res != tt.out {
t.Errorf("RequestIsSmallerThanLimit expected: %#v, got: %#v", tt.out, res)
t.Errorf("IsSmallerQuantity expected: %#v, got: %#v", tt.out, res)
}
}
}