diff --git a/LICENSE b/LICENSE index 5d22aa80..c66c8261 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ The MIT License (MIT) Copyright (c) 2023 Zalando SE + 2024 CYBERTEC PostgreSQL International GmbH Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MAINTAINERS b/MAINTAINERS index ea2a29ca..f5db696b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1,6 +1,3 @@ -Sergey Dudoladov -Felix Kunde -Jan Mussler -Jociele Padilha -Ida Novindasari -Polina Bungina \ No newline at end of file +Matthias Grömmer +Ants Aasma +Rafia Sabih diff --git a/README.md b/README.md index 40199353..e4348842 100644 --- a/README.md +++ b/README.md @@ -56,8 +56,7 @@ The Operator project is being driven forward by CYBERTEC and is currently in pro | Release | Postgres versions | pgBackRest versions | Patroni versions | K8s versions | Golang | | :-------- | :---------------: | :-------------------: | :--------------: | :----------------:| :-----: | -| 0.7.0 | 12 → 16 | 2.52.1 | 3.3.1 | 1.21+ | 1.21.7 | -| next rc | 13 → 16 | 2.51 | 3.3.1 | 1.21+ | 1.21.7 | +| 0.8.0 | 13 → 17 | 2.53 | 4.0.2 | 1.21+ | 1.21.7 | ## Getting started @@ -73,4 +72,4 @@ There is a browser-friendly version of this documentation at ## Community -Coming soon \ No newline at end of file +Coming soon diff --git a/SECURITY.md b/SECURITY.md index 52528dc5..1bbabeba 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,3 +1,3 @@ # Security -If you have discovered a security vulnerability, please email tech-security@zalando.de. +If you have discovered a security vulnerability, please email security@cybertec.at. diff --git a/charts/postgres-cluster/.helmignore b/charts/postgres-cluster/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/postgres-cluster/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/postgres-cluster/Chart.yaml b/charts/postgres-cluster/Chart.yaml new file mode 100644 index 00000000..7cca3d73 --- /dev/null +++ b/charts/postgres-cluster/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: postgres-cluster +description: Deploys a PostgreSQL cluster using the PostgreSQL operator + +type: application +version: 0.8.0 + +appVersion: "0.8.0-1" + +maintainers: + - name: CYBERTEC PostgreSQL International GmbH + email: info@cybertec.at + +sources: + - https://github.com/cybertec-postgresql/cybertec-pg-operator + +engine: gotpl diff --git a/charts/postgres-cluster/index.yaml b/charts/postgres-cluster/index.yaml new file mode 100644 index 00000000..907f45ef --- /dev/null +++ b/charts/postgres-cluster/index.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +entries: + postgres-cluster: + - apiVersion: v2 + appVersion: 0.8.0-1 + created: "2024-10-14T00:52:56.851410174+03:00" + description: Deploys a PostgreSQL cluster using the PostgreSQL operator + digest: f275636dc39a2db215633cc3d7449e86fd18d1cc29aed7f06af5006137bebad5 + maintainers: + - email: info@cybertec.at + name: CYBERTEC PostgreSQL International GmbH + name: postgres-cluster + sources: + - https://github.com/cybertec-postgresql/cybertec-pg-operator + type: application + urls: + - postgres-cluster-0.8.0.tgz + version: 0.8.0 +generated: "2024-10-14T00:52:56.850805082+03:00" diff --git a/charts/postgres-cluster/postgres-cluster-0.8.0.tgz b/charts/postgres-cluster/postgres-cluster-0.8.0.tgz new file mode 100644 index 00000000..1c1d7be5 Binary files /dev/null and b/charts/postgres-cluster/postgres-cluster-0.8.0.tgz differ diff --git a/charts/postgres-cluster/templates/NOTES.txt b/charts/postgres-cluster/templates/NOTES.txt new file mode 100644 index 00000000..4d1daa34 --- /dev/null +++ b/charts/postgres-cluster/templates/NOTES.txt @@ -0,0 +1,5 @@ +Database cluster object has been created and the operator should start initializing it now. + +To wait for the initialization to complete you can issue the following command: + + kubectl wait --for=jsonpath='.status.PostgresClusterStatus'=Running -n {{ .Release.Namespace }} pg/{{ .Release.Name }} --timeout=1h diff --git a/charts/postgres-cluster/templates/_helpers.tpl b/charts/postgres-cluster/templates/_helpers.tpl new file mode 100644 index 00000000..263caf13 --- /dev/null +++ b/charts/postgres-cluster/templates/_helpers.tpl @@ -0,0 +1,50 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "postgres-cluster.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "postgres-cluster.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "postgres-cluster.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "postgres-cluster.labels" -}} +helm.sh/chart: {{ include "postgres-cluster.chart" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "postgres-cluster.selectorLabels" -}} +app.kubernetes.io/name: {{ include "postgres-cluster.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} \ No newline at end of file diff --git a/charts/postgres-cluster/templates/postgresql.yaml b/charts/postgres-cluster/templates/postgresql.yaml new file mode 100644 index 00000000..8418107a --- /dev/null +++ b/charts/postgres-cluster/templates/postgresql.yaml @@ -0,0 +1,17 @@ +apiVersion: cpo.opensource.cybertec.at/v1 +kind: postgresql +metadata: + name: {{ .Values.clusterName | default .Release.Name }} + namespace: {{ .Release.Namespace }} + labels: + {{- include "postgres-cluster.labels" . | nindent 4 }} + {{- with .Values.commonLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +{{- with .Values.extraAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} +{{- end}} + +spec: + {{- toYaml .Values.cluster | nindent 2 }} diff --git a/charts/postgres-cluster/values.yaml b/charts/postgres-cluster/values.yaml new file mode 100644 index 00000000..a1fee130 --- /dev/null +++ b/charts/postgres-cluster/values.yaml @@ -0,0 +1,225 @@ +# Name of the cluster, if not specified taken to be Release.Name +#clusterName: + +# Add labels to be set on the cluster resource. If you want to have these labels be set on all resources created +# by the operator, include the name of the label in `configKubernetes.inherited_labels` operator configuration. +#commonLabels: +# my-label: value + +# Include extra annotations on the cluster resource. For setting on all operator created resources include the +# name of the annotation in `configKubernetes.inherited_annotations` operator configuration +#extraAnnotations: +# my-annotation: value + +cluster: + dockerImage: docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2 + numberOfInstances: 2 + + postgresql: + version: "17" + #parameters: + # work_mem: "40MB" + + #env: + #- name: MY_ENV_VARIABLE + # value: custom + + volume: + size: 1Gi + #storageClass: my-storage + #selector: + # matchExpressions: + # - { key: flavour, operator: In, values: [ "banana", "chocolate" ] } + # matchLabels: + # environment: dev + # service: postgres + + #additionalVolumes: + #- name: empty + # mountPath: /opt/empty + # targetContainers: + # - all + # volumeSource: + # emptyDir: {} + # subPath: + + resources: + requests: + cpu: 10m + memory: 100Mi + limits: + cpu: 500m + memory: 500Mi + + # Custom TLS certificate. Disabled unless tls.secretName has a value. + tls: + secretName: "" # should correspond to a Kubernetes Secret resource to load + certificateFile: "tls.crt" + privateKeyFile: "tls.key" + caFile: "" # optionally configure Postgres with a CA certificate + caSecretName: "" # optionally the ca.crt can come from this secret instead. + + patroni: + failsafe_mode: false + initdb: + encoding: "UTF8" + locale: "C.UTF-8" + data-checksums: "true" + pg_hba: + - local all all trust + - local replication standby trust + - host replication standby all scram-sha-256 + - host all all all scram-sha-256 + #slots: + # permanent_physical_1: + # type: physical + # permanent_logical_1: + # type: logical + # database: foo + # plugin: pgoutput + ttl: 30 + loop_wait: 10 + retry_timeout: 10 + #multisite: + # enable: false + # site: dc1 + # etcd: + # hosts: etcd.default.svc.cluster.local,etcd.other.cluster + # user: etcduser + # password: etcdpassword + # protocol: http + # ttl: 90 + # retry_timeout: 40 + synchronous_mode: false + synchronous_mode_strict: false + synchronous_node_count: 1 + maximum_lag_on_failover: 33554432 + + #backup: + # pgbackrest: + # image: "docker.io/cybertecpostgresql/cybertec-pg-container:pgbackrest-17.0-1" + # configuration: + # secret: "xyz" + # protection: + # restore: true + # global: + # repo1-retention-full: '7' + # repo1-retention-full-type: count + # repos: + # - name: repo1 + # storage: pvc + # volume: + # size: "10Gi" + # storageClass: myclass + # schedule: + # full: "" + # incr: "" + # diff: "" + # - name: repo1 + # storage: "s3" + # endpoint: ... + # region: ... + # restore: + # id: "unique restore identifier" + # repo: repo1 + # options: + # target-time: "2023-01-02 03:04:05+06" + + #users: # Application/Robot users + # appadmin: + # - superuser + # - createdb + # flyway: [] + #usersWithSecretRotation: + #- foo_user + #usersWithInPlaceSecretRotation: + #- flyway + #- bar_owner_user + + enableMasterLoadBalancer: false + enableReplicaLoadBalancer: false + enableConnectionPooler: false # enable/disable connection pooler deployment + enableReplicaConnectionPooler: false # set to enable connectionPooler for replica service + enableMasterPoolerLoadBalancer: false + enableReplicaPoolerLoadBalancer: false + + # IP ranges that are allowed to connect to LoadBalancer services + #allowedSourceRanges: 127.0.0.1/32 + + #databases: + # dbname: owner + + #preparedDatabases: + # bar: + # defaultUsers: true + # extensions: + # pg_partman: public + # schemas: + # data: {} + # history: + # defaultRoles: true + # defaultUsers: false + + enableShmVolume: true + # spiloRunAsUser: 101 + # spiloRunAsGroup: 103 + # spiloFSGroup: 103 + # podAnnotations: + # annotation.key: value + # serviceAnnotations: + # annotation.key: value + # podPriorityClassName: "spilo-pod-priority" + # tolerations: + # - key: postgres + # operator: Exists + # effect: NoSchedule + + # Clone existing cluster + #clone: + # cluster: name-of-cluster-to-clone + + #connectionPooler: + # dockerImage: docker.io/cybertecpostgresql/cybertec-pg-container:pgbouncer-1.23-2 + # numberOfInstances: 2 + # mode: transaction # session/transaction mode pooling + # schema: "pooler" + # user: "pooler" + # maxDBConnections: 60 + # resources: + # limits: + # cpu: "1" + # memory: 100Mi + # requests: + # cpu: 300m + # memory: 100Mi + + #sidecars: + # - name: "telegraf-sidecar" + # image: "telegraf:latest" + # ports: + # - name: metrics + # containerPort: 8094 + # protocol: TCP + # resources: + # limits: + # cpu: 500m + # memory: 500Mi + # requests: + # cpu: 100m + # memory: 100Mi + # env: + # - name: "USEFUL_VAR" + # value: "perhaps-true" + + # Add node affinity support by allowing postgres pods to schedule only on nodes that + # have label: "postgres-operator:enabled" set. + #nodeAffinity: + # requiredDuringSchedulingIgnoredDuringExecution: + # nodeSelectorTerms: + # - matchExpressions: + # - key: postgres-operator + # operator: In + # values: + # - enabled + + teamId: default diff --git a/charts/postgres-operator-ui/templates/deployment.yaml b/charts/postgres-operator-ui/templates/deployment.yaml index 61f000d1..8377240f 100644 --- a/charts/postgres-operator-ui/templates/deployment.yaml +++ b/charts/postgres-operator-ui/templates/deployment.yaml @@ -84,6 +84,7 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "17", "16", "15", "14", diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index b36898ca..44abf235 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v2 name: postgres-operator -version: 1.10.1 -appVersion: 1.10.1 +version: 0.8.0 +appVersion: 0.8.0 home: https://github.com/cybertec-postgresql/cybertec-pg-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: @@ -11,8 +11,8 @@ keywords: - patroni - spilo maintainers: -- name: Zalando - email: opensource@zalando.de +- name: CYBERTEC PostgreSQL International GmbH + email: info@cybertec.at sources: - https://github.com/cybertec-postgresql/cybertec-pg-operator engine: gotpl diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 1694f83e..6ff2c556 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -68,7 +68,7 @@ spec: type: string docker_image: type: string - default: "docker.io/cybertecpostgresql/cybertec-pg-container:postgres-16.2-1" + default: "docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2" enable_crd_registration: type: boolean default: true @@ -170,7 +170,7 @@ spec: default: "13" target_major_version: type: string - default: "16" + default: "17" kubernetes: type: object properties: @@ -680,6 +680,28 @@ spec: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' default: "100Mi" + multisite: + type: object + properties: + enable: + type: boolean + site: + type: string + etcd: + type: object + properties: + hosts: + type: string + user: + type: string + password: + type: string + protocol: + type: string + ttl: + type: integer + retry_timeout: + type: integer patroni: type: object properties: diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index 880eaed2..89f25cf2 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -215,6 +215,121 @@ spec: items: type: object x-kubernetes-preserve-unknown-fields: true + topologySpreadConstraints: + description: 'Topology spread constraints of a Dedicated + repo host pod. Changing this value causes the repo host + to restart. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-topology-spread-constraints/' + items: + description: TopologySpreadConstraint specifies how + to spread matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching + pods. Pods that match this label selector are + counted to determine the number of pods in their + corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which + pods may be unevenly distributed. When `whenUnsatisfiable=DoNotSchedule`, + it is the maximum permitted difference between + the number of matching pods in the target topology + and the global minimum. For example, in a 3-zone + cluster, MaxSkew is set to 1, and pods with the + same labelSelector spread as 1/1/0: | zone1 | + zone2 | zone3 | | P | P | | - if + MaxSkew is 1, incoming pod can only be scheduled + to zone3 to become 1/1/1; scheduling it onto zone1(zone2) + would make the ActualSkew(2-0) on zone1(zone2) + violate MaxSkew(1). - if MaxSkew is 2, incoming + pod can be scheduled onto any zone. When `whenUnsatisfiable=ScheduleAnyway`, + it is used to give higher precedence to topologies + that satisfy it. It''s a required field. Default + value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. + Nodes that have a label with this key and identical + values are considered to be in the same topology. + We consider each as a "bucket", and + try to put balanced number of pods into each bucket. + It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to + deal with a pod if it doesn''t satisfy the spread + constraint. - DoNotSchedule (default) tells the + scheduler not to schedule it. - ScheduleAnyway + tells the scheduler to schedule the pod in any + location, but giving higher precedence to topologies + that would help reduce the skew. A constraint + is considered "Unsatisfiable" for an incoming + pod if and only if every possible node assigment + for that pod would violate "MaxSkew" on some topology. + For example, in a 3-zone cluster, MaxSkew is set + to 1, and pods with the same labelSelector spread + as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, + incoming pod can only be scheduled to zone2(zone3) + to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) + satisfies MaxSkew(1). In other words, the cluster + can still be imbalanced, but scheduler won''t + make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array logicalBackupSchedule: type: string pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$' @@ -334,6 +449,28 @@ spec: type: integer maximum_lag_on_failover: type: integer + multisite: + type: object + properties: + enable: + type: boolean + site: + type: string + etcd: + type: object + properties: + hosts: + type: string + user: + type: string + password: + type: string + protocol: + type: string + ttl: + type: integer + retry_timeout: + type: integer pg_hba: type: array items: @@ -375,6 +512,7 @@ spec: - "14" - "15" - "16" + - "17" parameters: type: object additionalProperties: @@ -652,6 +790,146 @@ spec: type: string throughput: type: integer + backup: + type: object + properties: + pgbackrest: + type: object + properties: + image: + type: string + configuration: + type: object + properties: + secret: + type: string + protection: + type: object + properties: + restore: + type: boolean + global: + type: object + additionalProperties: + type: string + repos: + type: array + items: + properties: + name: + type: string + pattern: ^repo[1-4]$ + storage: + type: string + enum: + - "s3" + - "gcs" + - "azure" + - "pvc" + resource: + type: string + endpoint: + type: string + region: + type: string + schedule: + type: object + properties: + full: + type: string + incr: + type: string + diff: + type: string + volume: + type: object + required: + - "size" + properties: + iops: + type: integer + selector: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + enum: + - DoesNotExist + - Exists + - In + - NotIn + values: + type: array + items: + type: string + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + size: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero. + storageClass: + type: string + subPath: + type: string + throughput: + type: integer + required: + - name + - storage + - resource + type: object + minItems: 1 + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map + restore: + type: object + properties: + id: + type: string + repo: + type: string + pattern: ^repo[1-4]$ + options: + type: object + additionalProperties: + type: string + resources: + type: object + properties: + limits: + type: object + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + requests: + type: object + properties: + cpu: + type: string + pattern: '^(\d+m|\d+(\.\d{1,3})?)$' + memory: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + required: + - image + - repos status: type: object additionalProperties: diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index a16c426f..7de794e9 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -2,11 +2,11 @@ apiVersion: v1 entries: postgres-operator: - apiVersion: v2 - appVersion: 1.10.1 - created: "2023-09-07T16:26:25.96185313+02:00" + appVersion: 0.8.0 + created: "2024-10-14T11:32:03.852199097+03:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes - digest: cc3baa41753da92466223d0b334df27e79c882296577b404a8e9071411fcf19c + digest: 306f56b67445d7be8d5e8d26a8eaaa0fd153ca477b03bbc0c238daf10a531792 home: https://github.com/cybertec-postgresql/cybertec-pg-operator keywords: - postgres @@ -15,166 +15,12 @@ entries: - patroni - spilo maintainers: - - email: opensource@zalando.de - name: Zalando + - email: info@cybertec.at + name: CYBERTEC PostgreSQL International GmbH name: postgres-operator sources: - https://github.com/cybertec-postgresql/cybertec-pg-operator urls: - - postgres-operator-1.10.1.tgz - version: 1.10.1 - - apiVersion: v2 - appVersion: 1.10.0 - created: "2023-09-07T16:26:25.960303202+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 60fc5c8059dfed175d14e1034b40997d9c59d33ec8ea158c0597f7228ab04b51 - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.10.0.tgz - version: 1.10.0 - - apiVersion: v2 - appVersion: 1.9.0 - created: "2023-09-07T16:26:25.971662154+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 64df90c898ca591eb3a330328173ffaadfbf9ddd474d8c42ed143edc9e3f4276 - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.9.0.tgz - version: 1.9.0 - - apiVersion: v2 - appVersion: 1.8.2 - created: "2023-09-07T16:26:25.97011158+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: f77ffad2e98b72a621e5527015cf607935d3ed688f10ba4b626435acb9631b5b - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.8.2.tgz - version: 1.8.2 - - apiVersion: v2 - appVersion: 1.8.1 - created: "2023-09-07T16:26:25.968682347+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: ee0c3bb6ba72fa4289ba3b1c6060e5b312dd023faba2a61b4cb7d9e5e2cc57a5 - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.8.1.tgz - version: 1.8.1 - - apiVersion: v1 - appVersion: 1.8.0 - created: "2023-09-07T16:26:25.967242444+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 3ae232cf009e09aa2ad11c171484cd2f1b72e63c59735e58fbe2b6eb842f4c86 - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.8.0.tgz - version: 1.8.0 - - apiVersion: v1 - appVersion: 1.7.1 - created: "2023-09-07T16:26:25.965786379+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: 7262563bec0b058e669ae6bcff0226e33fa9ece9c41ac46a53274046afe7700c - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.7.1.tgz - version: 1.7.1 - - apiVersion: v1 - appVersion: 1.7.0 - created: "2023-09-07T16:26:25.963469658+02:00" - description: Postgres Operator creates and manages PostgreSQL clusters running - in Kubernetes - digest: c3e99fb94305f81484b8b1af18eefb78681f3b5d057d5ad10565e4afb7c65ffe - home: https://github.com/cybertec-postgresql/cybertec-pg-operator - keywords: - - postgres - - operator - - cloud-native - - patroni - - spilo - maintainers: - - email: opensource@zalando.de - name: Zalando - name: postgres-operator - sources: - - https://github.com/cybertec-postgresql/cybertec-pg-operator - urls: - - postgres-operator-1.7.0.tgz - version: 1.7.0 -generated: "2023-09-07T16:26:25.958442963+02:00" + - postgres-operator-0.8.0.tgz + version: 0.8.0 +generated: "2024-10-14T11:32:03.849675216+03:00" diff --git a/charts/postgres-operator/postgres-operator-0.8.0.tgz b/charts/postgres-operator/postgres-operator-0.8.0.tgz new file mode 100644 index 00000000..b477a395 Binary files /dev/null and b/charts/postgres-operator/postgres-operator-0.8.0.tgz differ diff --git a/charts/postgres-operator/postgres-operator-1.10.0.tgz b/charts/postgres-operator/postgres-operator-1.10.0.tgz deleted file mode 100644 index 71f3483f..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.10.0.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.10.1.tgz b/charts/postgres-operator/postgres-operator-1.10.1.tgz deleted file mode 100644 index 5ecb27a5..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.10.1.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.7.0.tgz b/charts/postgres-operator/postgres-operator-1.7.0.tgz deleted file mode 100644 index 2a8bc745..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.7.0.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.7.1.tgz b/charts/postgres-operator/postgres-operator-1.7.1.tgz deleted file mode 100644 index 2d20fe5b..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.7.1.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.8.0.tgz b/charts/postgres-operator/postgres-operator-1.8.0.tgz deleted file mode 100644 index caecd292..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.8.0.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.8.1.tgz b/charts/postgres-operator/postgres-operator-1.8.1.tgz deleted file mode 100644 index 2235cbcd..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.8.1.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.8.2.tgz b/charts/postgres-operator/postgres-operator-1.8.2.tgz deleted file mode 100644 index 61f0f8ae..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.8.2.tgz and /dev/null differ diff --git a/charts/postgres-operator/postgres-operator-1.9.0.tgz b/charts/postgres-operator/postgres-operator-1.9.0.tgz deleted file mode 100644 index 8106bcf1..00000000 Binary files a/charts/postgres-operator/postgres-operator-1.9.0.tgz and /dev/null differ diff --git a/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml index fdccf16d..1a86a646 100644 --- a/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml +++ b/charts/postgres-operator/templates/clusterrole-postgres-pod.yaml @@ -57,6 +57,22 @@ rules: - services verbs: - create +# Create events for multisite status changes +- apiGroups: + - "" + - "events.k8s.io" + resources: + - events + verbs: + - create +# Update multisite status +- apiGroups: + - cpo.opensource.cybertec.at + resources: + - postgresqls/status + verbs: + - patch + - update {{- if toString .Values.configKubernetes.spilo_privileged | eq "true" }} # to run privileged pods - apiGroups: @@ -68,4 +84,11 @@ rules: verbs: - use {{- end }} +# Execute backups +- apiGroups: + - "" + resources: + - pods/exec + verbs: + - create {{ end }} diff --git a/charts/postgres-operator/templates/clusterrole.yaml b/charts/postgres-operator/templates/clusterrole.yaml index b04b1931..4df38fa1 100644 --- a/charts/postgres-operator/templates/clusterrole.yaml +++ b/charts/postgres-operator/templates/clusterrole.yaml @@ -232,6 +232,7 @@ rules: verbs: - get - create + # to create role bindings to the postgres-pod service account - apiGroups: - rbac.authorization.k8s.io @@ -240,6 +241,24 @@ rules: verbs: - get - create +# to create configmap with pgbackrest config +- apiGroups: + - "" + resources: + - configmaps + verbs: + - get + - create + - delete + - patch +# to grant permission to create events to the postgres-pod service account +- apiGroups: + - "" + - "events.k8s.io" + resources: + - events + verbs: + - create {{- if toString .Values.configKubernetes.spilo_privileged | eq "true" }} # to run privileged pods - apiGroups: diff --git a/charts/postgres-operator/templates/operatorconfiguration.yaml b/charts/postgres-operator/templates/operatorconfiguration.yaml index a6ce0766..fc45d070 100644 --- a/charts/postgres-operator/templates/operatorconfiguration.yaml +++ b/charts/postgres-operator/templates/operatorconfiguration.yaml @@ -39,6 +39,10 @@ configuration: logging_rest_api: {{ toYaml .Values.configLoggingRestApi | indent 4 }} connection_pooler: +{{- if .Values.configMultisite.site }} + multisite: +{{ toYaml .Values.configMultisite | indent 4 }} +{{- end }} {{ toYaml .Values.configConnectionPooler | indent 4 }} patroni: {{ toYaml .Values.configPatroni | indent 4 }} diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 4621c9eb..8962567b 100644 --- a/charts/postgres-operator/values.yaml +++ b/charts/postgres-operator/values.yaml @@ -1,7 +1,7 @@ image: - registry: registry.opensource.zalan.do - repository: acid/postgres-operator - tag: v1.10.1 + registry: docker.io + repository: cybertecpostgresql/cybertec-pg-operator + tag: v0.8.0-1 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -37,14 +37,14 @@ configGeneral: enable_team_id_clustername_prefix: false # etcd connection string for Patroni. Empty uses K8s-native DCS. etcd_host: "" - # Spilo docker image - docker_image: ghcr.io/zalando/spilo-15:3.0-p1 + # Database pod docker image + docker_image: docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2 # key name for annotation to ignore globally configured instance limits # ignore_instance_limits_annotation_key: "" # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) - # kubernetes_use_configmaps: false + kubernetes_use_configmaps: true # min number of instances in Postgres cluster. -1 = no limit min_instances: -1 @@ -91,7 +91,7 @@ configMajorVersionUpgrade: # minimal Postgres major version that will not automatically be upgraded minimal_major_version: "13" # target Postgres major version when upgrading clusters automatically - target_major_version: "16" + target_major_version: "17" configKubernetes: # list of additional capabilities for postgres container @@ -395,7 +395,7 @@ configTeamsApi: # pam_configuration: https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees # operator will add all team member roles to this group and add a pg_hba line - pam_role_name: zalandos + pam_role_name: "humans" # List of teams which members need the superuser role in each Postgres cluster postgres_superuser_teams: - postgres_superusers @@ -420,7 +420,7 @@ configConnectionPooler: # db user for pooler to use connection_pooler_user: "pooler" # docker image - connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-27" + connection_pooler_image: "docker.io/cybertecpostgresql/cybertec-pg-container:pgbouncer-1.23-2" # max db connections the pooler should hold connection_pooler_max_db_connections: 60 # default pooling mode @@ -433,6 +433,21 @@ configConnectionPooler: connection_pooler_default_cpu_limit: "1" connection_pooler_default_memory_limit: 100Mi +configMultisite: + # Turn on multisite operation on all cluster by default + enable: false + # Name of this site, if missing multisite operation is disabled. + # Must be unique for each site. + site: "" + # IP address or hostname of shared etcd cluster used for multicluster operation. + etcd_host: "" + etcd_user: "" + etcd_password: "" + # Timeout for cross site failover, and timeout for demoting to read only when accessing shared etcd cluster fails. + # There should be adequate safety margin between the two to allow for demotion to take place. + #ttl: 90 + #retry_timeout: 40 + configPatroni: # enable Patroni DCS failsafe_mode feature enable_patroni_failsafe_mode: false diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 02aa771a..065792c5 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -881,7 +881,7 @@ key. * **pam_role_name** when set, the operator will add all team member roles to this group and add a `pg_hba` line to authenticate members of that role via `pam`. The default is - `zalandos`. + `humans`. * **pam_configuration** when set, should contain a URL to use for authentication against the username diff --git a/go.mod b/go.mod index 9b672d7f..ac25dfdc 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/r3labs/diff v1.1.0 github.com/sirupsen/logrus v1.9.0 github.com/stretchr/testify v1.8.2 + go.etcd.io/etcd/client/v3 v3.5.4 golang.org/x/crypto v0.21.0 golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a gopkg.in/yaml.v2 v2.4.0 @@ -22,6 +23,8 @@ require ( ) require ( + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v4.12.0+incompatible // indirect @@ -48,6 +51,11 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + go.etcd.io/etcd/api/v3 v3.5.4 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.4 // indirect + go.uber.org/atomic v1.7.0 // indirect + go.uber.org/multierr v1.6.0 // indirect + go.uber.org/zap v1.19.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.23.0 // indirect golang.org/x/oauth2 v0.8.0 // indirect @@ -57,6 +65,8 @@ require ( golang.org/x/time v0.3.0 // indirect golang.org/x/tools v0.16.1 // indirect google.golang.org/appengine v1.6.7 // indirect + google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect + google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.33.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 3f1900e3..3c38f40f 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,57 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go v1.54.18 h1:t8DGtN8A2wEiazoJxeDbfPsbxCKtjoRLuO7jBSgJzo4= github.com/aws/aws-sdk-go v1.54.18/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= github.com/evanphx/json-patch v4.12.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= @@ -20,22 +62,45 @@ github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2Kv github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -44,9 +109,12 @@ github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= @@ -55,10 +123,20 @@ github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGw github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -71,37 +149,66 @@ github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI= github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo/v2 v2.9.4 h1:xR7vG4IXt5RWx6FfIjyAtsoMAtnc3C/rFXBBd2AjZwE= github.com/onsi/ginkgo/v2 v2.9.4/go.mod h1:gCQYp2Q+kSoIj7ykSVb9nskRSsR6PUj4AiLywzIhbKM= github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE= github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/r3labs/diff v1.1.0 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M= github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -112,40 +219,93 @@ github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.etcd.io/etcd/api/v3 v3.5.4 h1:OHVyt3TopwtUQ2GKdd5wu3PmmipR4FTwCqoEjSyRdIc= +go.etcd.io/etcd/api/v3 v3.5.4/go.mod h1:5GB2vv4A4AOn3yk7MftYGHkUfGtDHnEraIjym4dYz5A= +go.etcd.io/etcd/client/pkg/v3 v3.5.4 h1:lrneYvz923dvC14R54XcA7FXoZ3mlGZAgmwhfm7HqOg= +go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= +go.etcd.io/etcd/client/v3 v3.5.4 h1:p83BUL3tAYS0OT/r0qglgc3M1JjhM0diV8DSWAhVXv4= +go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.1.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= +go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= +go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= +go.uber.org/zap v1.19.0 h1:mZQZefskPPCMIBCSEH0v2/iUqqLrYtaeqwD6FUGUnFE= +go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a h1:tlXy25amD5A7gOfbXdqCGN5k8ESEed/Ee1E5RcrYnqU= golang.org/x/exp v0.0.0-20230108222341-4b8118a2686a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -155,41 +315,89 @@ golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.28.7 h1:YKIhBxjXKaxuxWJnwohV0aGjRA5l4IU0Eywf/q19AVI= k8s.io/api v0.28.7/go.mod h1:y4RbcjCCMff1930SG/TcP3AUKNfaJUgIeUp58e/2vyY= k8s.io/apiextensions-apiserver v0.25.9 h1:Pycd6lm2auABp9wKQHCFSEPG+NPdFSTJXPST6NJFzB8= diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 397843fa..195946f9 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -34,7 +34,7 @@ data: # default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: ghcr.io/zalando/spilo-15:3.0-p1 + docker_image: docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2 # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_registration: "true" @@ -111,7 +111,7 @@ data: # oauth_token_secret_name: postgresql-operator # pam_configuration: | # https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees - # pam_role_name: zalandos + # pam_role_name: humans patroni_api_check_interval: "1s" patroni_api_check_timeout: "5s" # password_rotation_interval: "90" diff --git a/manifests/operator-service-account-rbac.yaml b/manifests/operator-service-account-rbac.yaml index 9ec389a9..97238c6d 100644 --- a/manifests/operator-service-account-rbac.yaml +++ b/manifests/operator-service-account-rbac.yaml @@ -236,7 +236,14 @@ rules: - create - delete - patch - +# to grant permission to create events to the postgres-pod service account +- apiGroups: + - "" + - "events.k8s.io" + resources: + - events + verbs: + - create --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding @@ -304,3 +311,19 @@ rules: - pods/exec verbs: - create +# Create events for multisite status changes +- apiGroups: + - "" + - "events.k8s.io" + resources: + - events + verbs: + - create + # Update multisite status +- apiGroups: + - cpo.opensource.cybertec.at + resources: + - postgresqls/status + verbs: + - patch + - update diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index f4014fd1..6797846f 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -66,7 +66,7 @@ spec: type: string docker_image: type: string - default: "ghcr.io/zalando/spilo-15:3.0-p1" + default: "docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2" enable_crd_registration: type: boolean default: true @@ -149,7 +149,7 @@ spec: default: 180 replication_username: type: string - default: standby + default: cpo_replication super_username: type: string default: postgres @@ -168,7 +168,7 @@ spec: default: "13" target_major_version: type: string - default: "16" + default: "17" kubernetes: type: object properties: @@ -572,7 +572,7 @@ spec: default: "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees" pam_role_name: type: string - default: "zalandos" + default: "cpo_pam" postgres_superuser_teams: type: array items: @@ -678,6 +678,28 @@ spec: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' default: "100Mi" + multisite: + type: object + properties: + enable: + type: boolean + site: + type: string + etcd: + type: object + properties: + hosts: + type: string + user: + type: string + password: + type: string + protocol: + type: string + ttl: + type: integer + retry_timeout: + type: integer patroni: type: object properties: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 297af0f4..d4991dfb 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -40,7 +40,7 @@ configuration: # major_version_upgrade_team_allow_list: # - acid minimal_major_version: "13" - target_major_version: "16" + target_major_version: "17" kubernetes: # additional_pod_capabilities: # - "SYS_NICE" @@ -186,7 +186,7 @@ configuration: enable_team_superuser: false enable_teams_api: false # pam_configuration: "" - pam_role_name: zalandos + pam_role_name: humans # postgres_superuser_teams: # - postgres_superusers protected_role_names: diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 4c05507f..b7d7ead1 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -259,8 +259,8 @@ spec: type: string type: array required: - - key - - operator + - key + - operator type: object type: array matchLabels: @@ -323,9 +323,9 @@ spec: make it *more* imbalanced. It''s a required field.' type: string required: - - maxSkew - - topologyKey - - whenUnsatisfiable + - maxSkew + - topologyKey + - whenUnsatisfiable type: object type: array logicalBackupSchedule: @@ -447,6 +447,28 @@ spec: type: integer maximum_lag_on_failover: type: integer + multisite: + type: object + properties: + enable: + type: boolean + site: + type: string + etcd: + type: object + properties: + hosts: + type: string + user: + type: string + password: + type: string + protocol: + type: string + ttl: + type: integer + retry_timeout: + type: integer pg_hba: type: array items: @@ -817,16 +839,58 @@ spec: type: string diff: type: string - Pvcsize: - type: string + volume: + type: object + required: + - "size" + properties: + iops: + type: integer + selector: + type: object + properties: + matchExpressions: + type: array + items: + type: object + required: + - key + - operator + properties: + key: + type: string + operator: + type: string + enum: + - DoesNotExist + - Exists + - In + - NotIn + values: + type: array + items: + type: string + matchLabels: + type: object + x-kubernetes-preserve-unknown-fields: true + size: + type: string + pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' + # Note: the value specified here must not be zero. + storageClass: + type: string + subPath: + type: string + throughput: + type: integer required: - - name - - storage - - resource + - name + - storage + - resource type: object minItems: 1 x-kubernetes-list-map-keys: - - name + - name x-kubernetes-list-type: map restore: type: object diff --git a/pkg/apis/cpo.opensource.cybertec.at/v1/crds.go b/pkg/apis/cpo.opensource.cybertec.at/v1/crds.go index 4b07713c..0e9da3d2 100644 --- a/pkg/apis/cpo.opensource.cybertec.at/v1/crds.go +++ b/pkg/apis/cpo.opensource.cybertec.at/v1/crds.go @@ -572,6 +572,40 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ "maximum_lag_on_failover": { Type: "integer", }, + "multisite": { + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "enable": { + Type: "boolean", + }, + "site": { + Type: "string", + }, + "etcd": { + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "hosts": { + Type: "string", + }, + "user": { + Type: "string", + }, + "password": { + Type: "string", + }, + "protocol": { + Type: "string", + }, + }, + }, + "ttl": { + Type: "integer", + }, + "retry_timeout": { + Type: "integer", + }, + }, + }, "pg_hba": { Type: "array", Items: &apiextv1.JSONSchemaPropsOrArray{ @@ -1279,9 +1313,6 @@ var PostgresCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, - "pvcsize": { - Type: "string", - }, }, }, }, @@ -2205,6 +2236,32 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "multisite": { + Type: "object", + Properties: map[string]apiextv1.JSONSchemaProps{ + "enable": { + Type: "boolean", + }, + "site": { + Type: "string", + }, + "etcd_host": { + Type: "string", + }, + "etcd_user": { + Type: "string", + }, + "etcd_password": { + Type: "string", + }, + "ttl": { + Type: "integer", + }, + "retry_timeout": { + Type: "integer", + }, + }, + }, }, }, "status": { diff --git a/pkg/apis/cpo.opensource.cybertec.at/v1/operator_configuration_type.go b/pkg/apis/cpo.opensource.cybertec.at/v1/operator_configuration_type.go index 05601faa..a7603677 100644 --- a/pkg/apis/cpo.opensource.cybertec.at/v1/operator_configuration_type.go +++ b/pkg/apis/cpo.opensource.cybertec.at/v1/operator_configuration_type.go @@ -50,7 +50,7 @@ type MajorVersionUpgradeConfiguration struct { MajorVersionUpgradeMode string `json:"major_version_upgrade_mode" default:"off"` // off - no actions, manual - manifest triggers action, full - manifest and minimal version violation trigger upgrade MajorVersionUpgradeTeamAllowList []string `json:"major_version_upgrade_team_allow_list,omitempty"` MinimalMajorVersion string `json:"minimal_major_version" default:"13"` - TargetMajorVersion string `json:"target_major_version" default:"16"` + TargetMajorVersion string `json:"target_major_version" default:"17"` } // KubernetesMetaConfiguration defines k8s conf required for all Postgres clusters and the operator itself @@ -276,6 +276,7 @@ type OperatorConfigurationData struct { Scalyr ScalyrConfiguration `json:"scalyr"` LogicalBackup OperatorLogicalBackupConfiguration `json:"logical_backup"` ConnectionPooler ConnectionPoolerConfiguration `json:"connection_pooler"` + Multisite Multisite `json:"multisite"` Patroni PatroniConfiguration `json:"patroni"` MinInstances int32 `json:"min_instances,omitempty"` diff --git a/pkg/apis/cpo.opensource.cybertec.at/v1/postgresql_type.go b/pkg/apis/cpo.opensource.cybertec.at/v1/postgresql_type.go index 4582d203..66c94e0c 100644 --- a/pkg/apis/cpo.opensource.cybertec.at/v1/postgresql_type.go +++ b/pkg/apis/cpo.opensource.cybertec.at/v1/postgresql_type.go @@ -180,6 +180,7 @@ type Patroni struct { SynchronousMode bool `json:"synchronous_mode,omitempty"` SynchronousModeStrict bool `json:"synchronous_mode_strict,omitempty"` SynchronousNodeCount uint32 `json:"synchronous_node_count,omitempty" defaults:"1"` + Multisite *Multisite `json:"multisite,omitempty"` FailsafeMode *bool `json:"failsafe_mode,omitempty"` } @@ -313,3 +314,19 @@ type TDE struct { type Monitoring struct { Image string `json:"image,omitempty"` } + +// Multisite enables cross Kubernetes replication coordinated via etcd +type Multisite struct { + Enable *bool `json:"enable,omitempty"` + Site *string `json:"site,omitempty"` + Etcd EtcdConfig `json:"etcd"` + TTL *int32 `json:"ttl,omitempty"` + RetryTimeout *int32 `json:"retry_timeout,omitempty"` +} + +type EtcdConfig struct { + Hosts *string `json:"hosts,omitempty"` + User *string `json:"user,omitempty"` + Password *string `json:"password,omitempty"` + Protocol *string `json:"protocol,omitempty"` +} diff --git a/pkg/apis/cpo.opensource.cybertec.at/v1/zz_generated.deepcopy.go b/pkg/apis/cpo.opensource.cybertec.at/v1/zz_generated.deepcopy.go index 662d4f78..a98323e2 100644 --- a/pkg/apis/cpo.opensource.cybertec.at/v1/zz_generated.deepcopy.go +++ b/pkg/apis/cpo.opensource.cybertec.at/v1/zz_generated.deepcopy.go @@ -187,6 +187,42 @@ func (in *ConnectionPoolerConfiguration) DeepCopy() *ConnectionPoolerConfigurati return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *EtcdConfig) DeepCopyInto(out *EtcdConfig) { + *out = *in + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = new(string) + **out = **in + } + if in.User != nil { + in, out := &in.User, &out.User + *out = new(string) + **out = **in + } + if in.Password != nil { + in, out := &in.Password, &out.Password + *out = new(string) + **out = **in + } + if in.Protocol != nil { + in, out := &in.Protocol, &out.Protocol + *out = new(string) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EtcdConfig. +func (in *EtcdConfig) DeepCopy() *EtcdConfig { + if in == nil { + return nil + } + out := new(EtcdConfig) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfiguration) { *out = *in @@ -411,6 +447,43 @@ func (in *Monitoring) DeepCopy() *Monitoring { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Multisite) DeepCopyInto(out *Multisite) { + *out = *in + if in.Enable != nil { + in, out := &in.Enable, &out.Enable + *out = new(bool) + **out = **in + } + if in.Site != nil { + in, out := &in.Site, &out.Site + *out = new(string) + **out = **in + } + in.Etcd.DeepCopyInto(&out.Etcd) + if in.TTL != nil { + in, out := &in.TTL, &out.TTL + *out = new(int32) + **out = **in + } + if in.RetryTimeout != nil { + in, out := &in.RetryTimeout, &out.RetryTimeout + *out = new(int32) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Multisite. +func (in *Multisite) DeepCopy() *Multisite { + if in == nil { + return nil + } + out := new(Multisite) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *OperatorConfiguration) DeepCopyInto(out *OperatorConfiguration) { *out = *in @@ -488,6 +561,7 @@ func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData out.Scalyr = in.Scalyr out.LogicalBackup = in.LogicalBackup in.ConnectionPooler.DeepCopyInto(&out.ConnectionPooler) + in.Multisite.DeepCopyInto(&out.Multisite) in.Patroni.DeepCopyInto(&out.Patroni) return } @@ -615,6 +689,11 @@ func (in *Patroni) DeepCopyInto(out *Patroni) { (*out)[key] = outVal } } + if in.Multisite != nil { + in, out := &in.Multisite, &out.Multisite + *out = new(Multisite) + (*in).DeepCopyInto(*out) + } if in.FailsafeMode != nil { in, out := &in.FailsafeMode, &out.FailsafeMode *out = new(bool) diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 2171b902..6e4b0398 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -5,6 +5,7 @@ package cluster import ( "context" "database/sql" + "encoding/base64" "encoding/json" "fmt" "reflect" @@ -27,6 +28,7 @@ import ( "github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/teams" "github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/users" "github.com/cybertec-postgresql/cybertec-pg-operator/pkg/util/volumes" + clientv3 "go.etcd.io/etcd/client/v3" appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" policyv1 "k8s.io/api/policy/v1" @@ -98,6 +100,8 @@ type Cluster struct { EBSVolumes map[string]volumes.VolumeProperties VolumeResizer volumes.VolumeResizer currentMajorVersion int + + multisiteClient *clientv3.Client // etcd client for multisite quorum site } type compareStatefulsetResult struct { @@ -228,7 +232,9 @@ func (c *Cluster) initUsers() error { c.systemUsers = map[string]spec.PgUser{} c.pgUsers = map[string]spec.PgUser{} - c.initSystemUsers() + if err := c.initSystemUsers(); err != nil { + return fmt.Errorf("could not init system roles: %v", err) + } if err := c.initInfrastructureRoles(); err != nil { return fmt.Errorf("could not init infrastructure roles: %v", err) @@ -360,6 +366,11 @@ func (c *Cluster) Create() (err error) { c.logger.Info("a monitoring secret was successfully created") } + if c.multisiteEnabled() { + c.logger.Infof("waiting for load balancer IP to be assigned") + c.waitForPrimaryLoadBalancerIp() + } + if c.Statefulset != nil { return fmt.Errorf("statefulset already exists in the cluster") } @@ -1365,25 +1376,35 @@ func (c *Cluster) processPodEventQueue(stopCh <-chan struct{}) { } } -func (c *Cluster) initSystemUsers() { +func (c *Cluster) initSystemUsers() error { // We don't actually use that to create users, delegating this // task to Patroni. Those definitions are only used to create // secrets, therefore, setting flags like SUPERUSER or REPLICATION // is not necessary here + password, err := c.getPasswordForUser(c.OpConfig.SuperUsername) + if err != nil { + return err + } c.systemUsers[constants.SuperuserKeyName] = spec.PgUser{ Origin: spec.RoleOriginSystem, Name: c.OpConfig.SuperUsername, Namespace: c.Namespace, - Password: util.RandomPassword(constants.PasswordLength), + Password: password, + } + password, err = c.getPasswordForUser(c.OpConfig.ReplicationUsername) + if err != nil { + return err } c.systemUsers[constants.ReplicationUserKeyName] = spec.PgUser{ Origin: spec.RoleOriginSystem, Name: c.OpConfig.ReplicationUsername, Namespace: c.Namespace, Flags: []string{constants.RoleFlagLogin}, - Password: util.RandomPassword(constants.PasswordLength), + Password: password, } + // TODO: Check what to do with connection pooler user in multisite mode + // Connection pooler user is an exception // if requested it's going to be created by operator if needConnectionPooler(&c.Spec) { @@ -1419,6 +1440,8 @@ func (c *Cluster) initSystemUsers() { c.systemUsers[constants.EventStreamUserKeyName] = streamUser } } + + return nil } func (c *Cluster) initPreparedDatabaseRoles() error { @@ -1485,6 +1508,28 @@ func (c *Cluster) initPreparedDatabaseRoles() error { return nil } +func (c *Cluster) getPasswordFromSecret(userSecretName spec.NamespacedName) (string, error) { + if userSecretName == (spec.NamespacedName{}) { + return "", nil + } + + userSecret, err := c.KubeClient. + Secrets(userSecretName.Namespace). + Get(context.TODO(), userSecretName.Name, metav1.GetOptions{}) + if err != nil { + msg := "could not get user role secret %s/%s: %v" + return "", fmt.Errorf(msg, userSecretName.Namespace, userSecretName.Name, err) + } + + secretData := userSecret.Data + if pw, present := secretData["password"]; present { + return string(pw), nil + } else { + msg := "password attribute not present in user role secret %s/%s" + return "", fmt.Errorf(msg, userSecretName.Namespace, userSecretName.Name) + } +} + func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix, searchPath, secretNamespace string) error { for defaultRole, inherits := range defaultRoles { @@ -1518,11 +1563,15 @@ func (c *Cluster) initDefaultRoles(defaultRoles map[string]string, admin, prefix adminRole = fmt.Sprintf("%s%s", prefix, constants.OwnerRoleNameSuffix) } + password, err := c.getPasswordForUser(roleName) + if err != nil { + return err + } newRole := spec.PgUser{ Origin: spec.RoleOriginBootstrap, Name: roleName, Namespace: namespace, - Password: util.RandomPassword(constants.PasswordLength), + Password: password, Flags: flags, MemberOf: memberOf, Parameters: map[string]string{"search_path": searchPath}, @@ -1907,3 +1956,79 @@ func (c *Cluster) deletePatroniClusterConfigMaps() { deleteClusterObject(get, deleteConfigMapFn, "configmap", c.Name, c.logger) } + +func (c *Cluster) encryptMultisitePassword(password string) string { + //TODO: add encryption + return base64.StdEncoding.EncodeToString([]byte(password)) +} + +func (c *Cluster) decryptMultisitePassword(value []byte) (string, error) { + result, err := base64.StdEncoding.DecodeString(string(value)) + return string(result), err +} + +func generateEtcdEndpoints(hosts, protocol string) []string { + endpoints := []string{} + + if protocol == "" { + protocol = "http" + } + + for _, host := range strings.Split(hosts, ",") { + hostParts := strings.SplitN(strings.TrimSpace(host), ":", 2) + host = hostParts[0] + port := "2379" + if len(hostParts) == 2 { + port = hostParts[1] + } + endpoints = append(endpoints, fmt.Sprintf("%s://%s:%s", protocol, host, port)) + } + + return endpoints +} + +func (c *Cluster) getPasswordForUser(username string) (string, error) { + if !c.multisiteEnabled() { + return util.RandomPassword(constants.PasswordLength), nil + } + + if c.multisiteClient == nil { + msSpec := c.Spec.Multisite + if msSpec == nil { + msSpec = &cpov1.Multisite{} + } + endpoints := generateEtcdEndpoints( + util.CoalesceStrPtr(msSpec.Etcd.Hosts, c.OpConfig.Multisite.Etcd.Hosts), + util.CoalesceStrPtr(msSpec.Etcd.Protocol, c.OpConfig.Multisite.Etcd.Protocol), + ) + client, err := clientv3.New(clientv3.Config{ + Endpoints: endpoints, + Username: util.CoalesceStrPtr(msSpec.Etcd.User, c.OpConfig.Multisite.Etcd.User), + Password: util.CoalesceStrPtr(msSpec.Etcd.Password, c.OpConfig.Multisite.Etcd.Password), + DialTimeout: time.Duration(2) * time.Second, + }) + if err != nil { + return "", fmt.Errorf("unable to access multisite etcd: %s", err) + } + c.multisiteClient = client + } + reqTimeoutCtx, _ := context.WithTimeout(context.TODO(), time.Duration(2)*time.Second) + credentialsKey := fmt.Sprintf("/multisite/%s/%s/credentials/%s", c.Namespace, c.Name, username) + response, err := c.multisiteClient.Get(reqTimeoutCtx, credentialsKey) + if err != nil { + return "", fmt.Errorf("unable to fetch credentials for %s from multisite etcd: %s", username, err) + } + if len(response.Kvs) == 0 { + password := util.RandomPassword(constants.PasswordLength) + _, err := c.multisiteClient.Put(reqTimeoutCtx, credentialsKey, c.encryptMultisitePassword(password)) + if err != nil { + return "", fmt.Errorf("unable to store credentials for %s from multisite etcd: %s", username, err) + } + return password, nil + } + password, err := c.decryptMultisitePassword(response.Kvs[0].Value) + if err != nil { + return "", fmt.Errorf("error decoding credentials for %s: %s", username, err) + } + return password, nil +} diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 1aab87b2..afcbd393 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -295,7 +295,7 @@ func TestInitHumanUsers(t *testing.T) { cl.OpConfig.EnableTeamSuperuser = true cl.OpConfig.EnableTeamsAPI = true cl.OpConfig.EnableTeamMemberDeprecation = true - cl.OpConfig.PamRoleName = "zalandos" + cl.OpConfig.PamRoleName = "humans" cl.Spec.TeamID = "test" cl.Spec.Users = map[string]cpov1.UserFlags{"bar": []string{}} @@ -383,7 +383,7 @@ func TestInitHumanUsersWithSuperuserTeams(t *testing.T) { cl.OpConfig.EnableTeamSuperuser = false cl.OpConfig.EnableTeamsAPI = true - cl.OpConfig.PamRoleName = "zalandos" + cl.OpConfig.PamRoleName = "humans" teamA := mockTeam{ teamID: "postgres_superusers", diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index 2dd4d198..2a0a04b7 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -456,13 +456,6 @@ PatroniInitDBParams: config.PgLocalConfiguration[patroniPGHBAConfParameterName] = patroni.PgHba } - config.Bootstrap.Users = map[string]pgUser{ - opConfig.PamRoleName: { - Password: "", - Options: []string{constants.RoleFlagCreateDB, constants.RoleFlagNoLogin}, - }, - } - res, err := json.Marshal(config) return string(res), err } @@ -1437,6 +1430,10 @@ func (c *Cluster) generateStatefulSet(spec *cpov1.PostgresSpec) (*appsv1.Statefu }) } + if c.multisiteEnabled() { + spiloEnvVars = appendEnvVars(spiloEnvVars, c.generateMultisiteEnvVars()...) + } + // generate the spilo container spiloContainer := generateContainer(constants.PostgresContainerName, &effectiveDockerImage, @@ -2336,6 +2333,11 @@ func (c *Cluster) shouldCreateLoadBalancerForService(role PostgresRole, spec *cp return c.OpConfig.EnableReplicaLoadBalancer case Master: + // If multisite is enabled at manifest or operator configuration we always + // need a load balancer + if c.multisiteEnabled() { + return true + } if spec.EnableMasterLoadBalancer != nil { return *spec.EnableMasterLoadBalancer @@ -3214,3 +3216,29 @@ func repoNumberFromName(repoName string) int { func (c *Cluster) getPgbackrestJobName(repoName string, backupType string) (jobName string) { return trimCronjobName(fmt.Sprintf("%s-%s-%s-%s", "pgbackrest", c.clusterName().Name, repoName, backupType)) } + +func (c *Cluster) generateMultisiteEnvVars() []v1.EnvVar { + site, err := c.getPrimaryLoadBalancerIp() + if err != nil { + c.logger.Errorf("Error getting primary load balancer IP for %s: %s", c.Name, err) + site = "" + } + clsConf := c.Spec.Multisite + if clsConf == nil { + clsConf = new(cpov1.Multisite) + } + + envVars := []v1.EnvVar{ + {Name: "MULTISITE_SITE", Value: util.CoalesceStrPtr(clsConf.Site, c.OpConfig.Multisite.Site)}, + {Name: "MULTISITE_ETCD_HOSTS", Value: util.CoalesceStrPtr(clsConf.Etcd.Hosts, c.OpConfig.Multisite.Etcd.Hosts)}, + {Name: "MULTISITE_ETCD_USER", Value: util.CoalesceStrPtr(clsConf.Etcd.User, c.OpConfig.Multisite.Etcd.User)}, + {Name: "MULTISITE_ETCD_PASSWORD", Value: util.CoalesceStrPtr(clsConf.Etcd.Password, c.OpConfig.Multisite.Etcd.Password)}, + {Name: "MULTISITE_ETCD_PROTOCOL", Value: util.CoalesceStrPtr(clsConf.Etcd.Protocol, c.OpConfig.Multisite.Etcd.Protocol)}, + {Name: "MULTISITE_TTL", Value: strconv.Itoa(int(*util.CoalesceInt32(clsConf.TTL, c.OpConfig.Multisite.TTL)))}, + {Name: "MULTISITE_RETRY_TIMEOUT", Value: strconv.Itoa(int(*util.CoalesceInt32(clsConf.RetryTimeout, c.OpConfig.Multisite.RetryTimeout)))}, + {Name: "EXTERNAL_HOST", Value: site}, + {Name: "UPDATE_CRD", Value: c.Namespace + "." + c.Name}, + {Name: "CRD_UID", Value: string(c.UID)}, + } + return envVars +} diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index bb7d01f9..40834086 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -77,10 +77,10 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { patroni: &cpov1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ - PamRoleName: "zalandos", + PamRoleName: "humans", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"humans":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{}}}`, }, { subtest: "Patroni configured", @@ -104,10 +104,10 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, opConfig: &config.Config{ Auth: config.Auth{ - PamRoleName: "zalandos", + PamRoleName: "humans", }, }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin","pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin","pg_hba":["hostssl all all 0.0.0.0/0 scram-sha-256","host all all 0.0.0.0/0 scram-sha-256"]},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"},"data-checksums",{"encoding":"UTF8"},{"locale":"en_US.UTF-8"}],"users":{"humans":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"synchronous_mode":true,"synchronous_mode_strict":true,"synchronous_node_count":1,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}},"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally", @@ -115,11 +115,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { patroni: &cpov1.Patroni{}, opConfig: &config.Config{ Auth: config.Auth{ - PamRoleName: "zalandos", + PamRoleName: "humans", }, EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"humans":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, }, { subtest: "Patroni failsafe_mode configured globally, disabled for cluster", @@ -129,11 +129,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, opConfig: &config.Config{ Auth: config.Auth{ - PamRoleName: "zalandos", + PamRoleName: "humans", }, EnablePatroniFailsafeMode: util.True(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":false}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"humans":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":false}}}`, }, { subtest: "Patroni failsafe_mode disabled globally, configured for cluster", @@ -143,11 +143,11 @@ func TestGenerateSpiloJSONConfiguration(t *testing.T) { }, opConfig: &config.Config{ Auth: config.Auth{ - PamRoleName: "zalandos", + PamRoleName: "humans", }, EnablePatroniFailsafeMode: util.False(), }, - result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"zalandos":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, + result: `{"postgresql":{"bin_dir":"/usr/lib/postgresql/15/bin"},"bootstrap":{"initdb":[{"auth-host":"scram-sha-256"},{"auth-local":"trust"}],"users":{"humans":{"password":"","options":["CREATEDB","NOLOGIN"]}},"dcs":{"failsafe_mode":true}}}`, }, } for _, tt := range tests { diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index fc0000b4..49b76f6a 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -18,6 +18,7 @@ var VersionMap = map[string]int{ "14": 140000, "15": 150000, "16": 160000, + "17": 170000, } // IsBiggerPostgresVersion Compare two Postgres version numbers diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index 2c6fb1ba..c04ca223 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -498,6 +498,40 @@ func (c *Cluster) waitStatefulsetPodsReady() error { return nil } +func (c *Cluster) waitForPrimaryLoadBalancerIp() error { + return retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout, + func() (bool, error) { + name := c.serviceName(Master) + svc, err := c.KubeClient.Services(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return false, err + } + + if svc == nil { + return false, fmt.Errorf("primary loadbalancer service is not found") + } + return len(svc.Status.LoadBalancer.Ingress) > 0, nil + }) + + return nil +} + +func (c *Cluster) getPrimaryLoadBalancerIp() (string, error) { + name := c.serviceName(Master) + svc, err := c.KubeClient.Services(c.Namespace).Get(context.TODO(), name, metav1.GetOptions{}) + if err != nil { + return "", err + } + if svc == nil { + return "", fmt.Errorf("primary loadbalancer service is not found") + } + if len(svc.Status.LoadBalancer.Ingress) == 0 { + return "", fmt.Errorf("IP is not assigned") + } + ingress := svc.Status.LoadBalancer.Ingress[0] + return util.Coalesce(ingress.Hostname, ingress.IP), nil +} + // Returns labels used to create or list k8s objects such as pods // For backward compatibility, shouldAddExtraLabels must be false // when listing k8s objects. See operator PR #252 @@ -715,3 +749,13 @@ func parseResourceRequirements(resourcesRequirement v1.ResourceRequirements) (cp } return resources, nil } + +func (c *Cluster) multisiteEnabled() bool { + var enable *bool + if c.Spec.Multisite != nil { + enable = util.CoalesceBool(c.Spec.Multisite.Enable, c.OpConfig.Multisite.Enable) + } else { + enable = c.OpConfig.Multisite.Enable + } + return enable != nil && *enable +} diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 06edb481..12f27a53 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -63,7 +63,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *cpov1.OperatorConfigura result.MajorVersionUpgradeMode = util.Coalesce(fromCRD.MajorVersionUpgrade.MajorVersionUpgradeMode, "off") result.MajorVersionUpgradeTeamAllowList = fromCRD.MajorVersionUpgrade.MajorVersionUpgradeTeamAllowList result.MinimalMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.MinimalMajorVersion, "13") - result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "16") + result.TargetMajorVersion = util.Coalesce(fromCRD.MajorVersionUpgrade.TargetMajorVersion, "17") // kubernetes config result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations @@ -205,7 +205,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *cpov1.OperatorConfigura result.EnableTeamSuperuser = fromCRD.TeamsAPI.EnableTeamSuperuser result.EnableAdminRoleForUsers = fromCRD.TeamsAPI.EnableAdminRoleForUsers result.TeamAdminRole = fromCRD.TeamsAPI.TeamAdminRole - result.PamRoleName = util.Coalesce(fromCRD.TeamsAPI.PamRoleName, "zalandos") + result.PamRoleName = util.Coalesce(fromCRD.TeamsAPI.PamRoleName, "humans") result.PamConfiguration = util.Coalesce(fromCRD.TeamsAPI.PamConfiguration, "https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees") result.ProtectedRoles = util.CoalesceStrArr(fromCRD.TeamsAPI.ProtectedRoles, []string{"admin", "cron_admin"}) result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams @@ -282,5 +282,14 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *cpov1.OperatorConfigura fromCRD.ConnectionPooler.MaxDBConnections, k8sutil.Int32ToPointer(constants.ConnectionPoolerMaxDBConnections)) + result.Multisite.Enable = fromCRD.Multisite.Enable + result.Multisite.Site = util.CoalesceStrPtr(fromCRD.Multisite.Site, "") + result.Multisite.Etcd.Hosts = util.CoalesceStrPtr(fromCRD.Multisite.Etcd.Hosts, "") + result.Multisite.Etcd.User = util.CoalesceStrPtr(fromCRD.Multisite.Etcd.User, "") + result.Multisite.Etcd.Password = util.CoalesceStrPtr(fromCRD.Multisite.Etcd.Password, "") + result.Multisite.Etcd.Protocol = util.CoalesceStrPtr(fromCRD.Multisite.Etcd.Protocol, "") + result.Multisite.TTL = util.CoalesceInt32(fromCRD.Multisite.TTL, k8sutil.Int32ToPointer(90)) + result.Multisite.RetryTimeout = util.CoalesceInt32(fromCRD.Multisite.RetryTimeout, k8sutil.Int32ToPointer(40)) + return result } diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index 8e6875b8..2e37a0b6 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -97,7 +97,7 @@ type InfrastructureRole struct { // Auth describes authentication specific configuration parameters type Auth struct { SecretNameTemplate StringTemplate `name:"secret_name_template" default:"{username}.{cluster}.credentials.{tprkind}.{tprgroup}"` - PamRoleName string `name:"pam_role_name" default:"zalandos"` + PamRoleName string `name:"pam_role_name" default:"humans"` PamConfiguration string `name:"pam_configuration" default:"https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees"` TeamsAPIUrl string `name:"teams_api_url" default:"https://teams.example.com/api/"` OAuthTokenSecretName spec.NamespacedName `name:"oauth_token_secret_name" default:"postgresql-operator"` @@ -160,6 +160,22 @@ type ConnectionPooler struct { ConnectionPoolerDefaultMemoryLimit string `name:"connection_pooler_default_memory_limit" default:"100Mi"` } +type Multisite struct { + Etcd + + Enable *bool `name:"multisite_enable" default:"false"` + Site string `name:"multisite_site" default:""` + TTL *int32 `name:"multisite_ttl" default:"90"` + RetryTimeout *int32 `name:"multisite_retry_timeout" default:"40"` +} + +type Etcd struct { + Hosts string `name:"multisite_etcd_hosts" default:""` + User string `name:"multisite_etcd_user" default:""` + Password string `name:"multisite_etcd_password" default:""` + Protocol string `name:"multisite_etcd_protocol" default:"http"` +} + // Config describes operator config type Config struct { CRD @@ -168,11 +184,12 @@ type Config struct { Scalyr LogicalBackup ConnectionPooler + Multisite WatchedNamespace string `name:"watched_namespace"` // special values: "*" means 'watch all namespaces', the empty string "" means 'watch a namespace where operator is deployed to' KubernetesUseConfigMaps bool `name:"kubernetes_use_configmaps" default:"false"` 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:"ghcr.io/zalando/spilo-15:3.0-p1"` + DockerImage string `name:"docker_image" default:"docker.io/cybertecpostgresql/cybertec-pg-container:postgres-17.0-2"` SidecarImages map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers SidecarContainers []v1.Container `name:"sidecars"` PodServiceAccountName string `name:"pod_service_account_name" default:"postgres-pod"` @@ -242,7 +259,7 @@ type Config struct { MajorVersionUpgradeMode string `name:"major_version_upgrade_mode" default:"off"` MajorVersionUpgradeTeamAllowList []string `name:"major_version_upgrade_team_allow_list" default:""` MinimalMajorVersion string `name:"minimal_major_version" default:"13"` - TargetMajorVersion string `name:"target_major_version" default:"16"` + TargetMajorVersion string `name:"target_major_version" default:"17"` PatroniAPICheckInterval time.Duration `name:"patroni_api_check_interval" default:"1s"` PatroniAPICheckTimeout time.Duration `name:"patroni_api_check_timeout" default:"5s"` EnablePatroniFailsafeMode *bool `name:"enable_patroni_failsafe_mode" default:"false"` diff --git a/pkg/util/util.go b/pkg/util/util.go index 506477d5..db0f2905 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -312,6 +312,14 @@ func CoalesceDuration(val time.Duration, defaultVal string) time.Duration { return val } +// CoalesceStrPtr works like coalesce but for *string +func CoalesceStrPtr(val *string, defaultVal string) string { + if val != nil && *val != "" { + return *val + } + return defaultVal +} + // Test if any of the values is nil func testNil(values ...*int32) bool { for _, v := range values { diff --git a/ui/manifests/deployment.yaml b/ui/manifests/deployment.yaml index 2dc87bac..24f3f20d 100644 --- a/ui/manifests/deployment.yaml +++ b/ui/manifests/deployment.yaml @@ -72,6 +72,7 @@ spec: "limit_iops": 16000, "limit_throughput": 1000, "postgresql_versions": [ + "17", "16", "15", "14",