diff --git a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md index f53be1319..93392a68e 100644 --- a/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md +++ b/.github/ISSUE_TEMPLATE/postgres-operator-issue-template.md @@ -9,7 +9,7 @@ assignees: '' Please, answer some short questions which should help us to understand your problem / question better? -- **Which image of the operator are you using?** e.g. registry.opensource.zalan.do/acid/postgres-operator:v1.6.2 +- **Which image of the operator are you using?** e.g. registry.opensource.zalan.do/acid/postgres-operator:v1.6.3 - **Where do you run it - cloud or metal? Kubernetes or OpenShift?** [AWS K8s | GCP ... | Bare Metal K8s] - **Are you running Postgres Operator in production?** [yes | no] - **Type of issue?** [Bug report, question, feature request, etc.] diff --git a/Makefile b/Makefile index b9a654dd8..5d0d960f0 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ mocks: GO111MODULE=on go generate ./... tools: - GO111MODULE=on go get k8s.io/client-go@kubernetes-1.20.2 + GO111MODULE=on go get k8s.io/client-go@kubernetes-1.20.6 GO111MODULE=on go get github.com/golang/mock/mockgen@v1.4.4 GO111MODULE=on go mod tidy diff --git a/README.md b/README.md index 3035e109d..ebe84bf5f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,8 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Rolling updates on Postgres cluster changes, incl. quick minor version updates * Live volume resize without pod restarts (AWS EBS, PVC) -* Database connection pooler with PGBouncer +* Database connection pooling with PGBouncer +* Support fast in place major version upgrade to PG13. Supports global upgrade of all clusters. * Restore and cloning Postgres clusters (incl. major version upgrade) * Additionally logical backups to S3 bucket can be configured * Standby cluster from S3 WAL archive @@ -24,11 +25,11 @@ pipelines with no access to Kubernetes API directly, promoting infrastructure as * Support for custom TLS certificates * UI to create and edit Postgres cluster manifests * Works well on Amazon AWS, Google Cloud, OpenShift and locally on Kind -* Support for AWS EBS gp3 migration +* Support for AWS EBS gp2 to gp3 migration, supporting iops and throughput configuration ### PostgreSQL features -* Supports PostgreSQL 13, starting from 9.5+ +* Supports PostgreSQL 13, starting from 9.6+ * Streaming replication cluster via Patroni * Point-In-Time-Recovery with [pg_basebackup](https://www.postgresql.org/docs/11/app-pgbasebackup.html) / @@ -65,7 +66,7 @@ We introduce the major version into the backup path to smoothen the [major versi The new operator configuration can set a compatibility flag *enable_spilo_wal_path_compat* to make Spilo look for wal segments in the current path but also old format paths. This comes at potential performance costs and should be disabled after a few days. -The newest Spilo 13 image is: `registry.opensource.zalan.do/acid/spilo-13:2.0-p6` +The newest Spilo 13 image is: `registry.opensource.zalan.do/acid/spilo-13:2.0-p7` The last Spilo 12 image is: `registry.opensource.zalan.do/acid/spilo-12:1.6-p5` diff --git a/charts/postgres-operator-ui/Chart.yaml b/charts/postgres-operator-ui/Chart.yaml index 4ad4de5e6..e9c10868c 100644 --- a/charts/postgres-operator-ui/Chart.yaml +++ b/charts/postgres-operator-ui/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: postgres-operator-ui -version: 1.6.2 -appVersion: 1.6.2 +version: 1.6.3 +appVersion: 1.6.3 home: https://github.com/zalando/postgres-operator description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience keywords: diff --git a/charts/postgres-operator-ui/index.yaml b/charts/postgres-operator-ui/index.yaml index 19db0b74f..f76bfcbb4 100644 --- a/charts/postgres-operator-ui/index.yaml +++ b/charts/postgres-operator-ui/index.yaml @@ -1,9 +1,31 @@ apiVersion: v1 entries: postgres-operator-ui: + - apiVersion: v1 + appVersion: 1.6.3 + created: "2021-05-27T19:04:33.425637932+02:00" + description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience + digest: 08b810aa632dcc719e4785ef184e391267f7c460caa99677f2d00719075aac78 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - ui + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator-ui + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-ui-1.6.3.tgz + version: 1.6.3 - apiVersion: v1 appVersion: 1.6.2 - created: "2021-04-06T16:47:40.993908218+02:00" + created: "2021-05-27T19:04:33.422124263+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 14d1559bb0bd1e1e828f2daaaa6f6ac9ffc268d79824592c3589b55dd39241f6 home: https://github.com/zalando/postgres-operator @@ -25,7 +47,7 @@ entries: version: 1.6.2 - apiVersion: v1 appVersion: 1.6.1 - created: "2021-04-06T16:47:40.993378451+02:00" + created: "2021-05-27T19:04:33.419640902+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 3d321352f2f1e7bb7450aa8876e3d818aa9f9da9bd4250507386f0490f2c1969 home: https://github.com/zalando/postgres-operator @@ -47,7 +69,7 @@ entries: version: 1.6.1 - apiVersion: v1 appVersion: 1.6.0 - created: "2021-04-06T16:47:40.992871656+02:00" + created: "2021-05-27T19:04:33.41788193+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: 1e0aa1e7db3c1daa96927ffbf6fdbcdb434562f961833cb5241ddbe132220ee4 home: https://github.com/zalando/postgres-operator @@ -69,7 +91,7 @@ entries: version: 1.6.0 - apiVersion: v1 appVersion: 1.5.0 - created: "2021-04-06T16:47:40.992346484+02:00" + created: "2021-05-27T19:04:33.416056821+02:00" description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience digest: c91ea39e6d51d57f4048fb1b6ec53b40823f2690eb88e4e4f1a036367b9fdd61 home: https://github.com/zalando/postgres-operator @@ -89,4 +111,4 @@ entries: urls: - postgres-operator-ui-1.5.0.tgz version: 1.5.0 -generated: "2021-04-06T16:47:40.991668273+02:00" +generated: "2021-05-27T19:04:33.41380858+02:00" diff --git a/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz b/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz new file mode 100644 index 000000000..45e3d00b2 Binary files /dev/null and b/charts/postgres-operator-ui/postgres-operator-ui-1.6.3.tgz differ diff --git a/charts/postgres-operator-ui/values.yaml b/charts/postgres-operator-ui/values.yaml index db44ce375..c9f521464 100644 --- a/charts/postgres-operator-ui/values.yaml +++ b/charts/postgres-operator-ui/values.yaml @@ -8,7 +8,7 @@ replicaCount: 1 image: registry: registry.opensource.zalan.do repository: acid/postgres-operator-ui - tag: v1.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. diff --git a/charts/postgres-operator/Chart.yaml b/charts/postgres-operator/Chart.yaml index 94dea3a15..28d0f811d 100644 --- a/charts/postgres-operator/Chart.yaml +++ b/charts/postgres-operator/Chart.yaml @@ -1,7 +1,7 @@ apiVersion: v1 name: postgres-operator -version: 1.6.2 -appVersion: 1.6.2 +version: 1.6.3 +appVersion: 1.6.3 home: https://github.com/zalando/postgres-operator description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes keywords: diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index 0ac5651c3..82a737ae6 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -65,7 +65,7 @@ spec: properties: docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p6" + default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p7" enable_crd_validation: type: boolean default: true @@ -397,7 +397,7 @@ spec: properties: logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: @@ -443,6 +443,9 @@ spec: enable_postgres_team_crd_superusers: type: boolean default: false + enable_team_member_deprecation: + type: boolean + default: false enable_team_superuser: type: boolean default: false @@ -465,6 +468,9 @@ spec: type: string default: - admin + role_deletion_suffix: + type: string + default: "_deleted" team_admin_role: type: string default: "admin" diff --git a/charts/postgres-operator/crds/postgresqls.yaml b/charts/postgres-operator/crds/postgresqls.yaml index ad11f6407..aead7fe69 100644 --- a/charts/postgres-operator/crds/postgresqls.yaml +++ b/charts/postgres-operator/crds/postgresqls.yaml @@ -27,7 +27,7 @@ spec: additionalPrinterColumns: - name: Team type: string - description: Team responsible for Postgres CLuster + description: Team responsible for Postgres cluster jsonPath: .spec.teamId - name: Version type: string diff --git a/charts/postgres-operator/index.yaml b/charts/postgres-operator/index.yaml index e59cc7629..bbd762104 100644 --- a/charts/postgres-operator/index.yaml +++ b/charts/postgres-operator/index.yaml @@ -1,9 +1,30 @@ apiVersion: v1 entries: postgres-operator: + - apiVersion: v1 + appVersion: 1.6.3 + created: "2021-05-27T19:04:25.199523943+02:00" + description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes + digest: ea08f991bf23c9ad114bca98ebcbe3e2fa15beab163061399394905eaee89b35 + home: https://github.com/zalando/postgres-operator + keywords: + - postgres + - operator + - cloud-native + - patroni + - spilo + maintainers: + - email: opensource@zalando.de + name: Zalando + name: postgres-operator + sources: + - https://github.com/zalando/postgres-operator + urls: + - postgres-operator-1.6.3.tgz + version: 1.6.3 - apiVersion: v1 appVersion: 1.6.2 - created: "2021-03-30T17:00:50.171986449+02:00" + created: "2021-05-27T19:04:25.198182197+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: d886f8a0879ca07d1e5246ee7bc55710e1c872f3977280fe495db6fc2057a7f4 home: https://github.com/zalando/postgres-operator @@ -24,7 +45,7 @@ entries: version: 1.6.2 - apiVersion: v1 appVersion: 1.6.1 - created: "2021-03-30T17:00:50.170294515+02:00" + created: "2021-05-27T19:04:25.19687586+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 4ba5972cd486dcaa2d11c5613a6f97f6b7b831822e610fe9e10a57ea1db23556 home: https://github.com/zalando/postgres-operator @@ -45,7 +66,7 @@ entries: version: 1.6.1 - apiVersion: v1 appVersion: 1.6.0 - created: "2021-03-30T17:00:50.168493689+02:00" + created: "2021-05-27T19:04:25.195600766+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: f52149718ea364f46b4b9eec9a65f6253ad182bb78df541d14cd5277b9c8a8c3 home: https://github.com/zalando/postgres-operator @@ -66,7 +87,7 @@ entries: version: 1.6.0 - apiVersion: v1 appVersion: 1.5.0 - created: "2021-03-30T17:00:50.166722286+02:00" + created: "2021-05-27T19:04:25.193985032+02:00" description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes digest: 198351d5db52e65cdf383d6f3e1745d91ac1e2a01121f8476f8b1be728b09531 home: https://github.com/zalando/postgres-operator @@ -85,4 +106,4 @@ entries: urls: - postgres-operator-1.5.0.tgz version: 1.5.0 -generated: "2021-03-30T17:00:50.165166707+02:00" +generated: "2021-05-27T19:04:25.191897769+02:00" diff --git a/charts/postgres-operator/postgres-operator-1.6.3.tgz b/charts/postgres-operator/postgres-operator-1.6.3.tgz new file mode 100644 index 000000000..af84bf57b Binary files /dev/null and b/charts/postgres-operator/postgres-operator-1.6.3.tgz differ diff --git a/charts/postgres-operator/values-crd.yaml b/charts/postgres-operator/values-crd.yaml index 6110fe8bc..b1fc43261 100644 --- a/charts/postgres-operator/values-crd.yaml +++ b/charts/postgres-operator/values-crd.yaml @@ -1,7 +1,7 @@ image: registry: registry.opensource.zalan.do repository: acid/postgres-operator - tag: v1.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -32,7 +32,7 @@ configGeneral: # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: false # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # min number of instances in Postgres cluster. -1 = no limit min_instances: -1 # max number of instances in Postgres cluster. -1 = no limit @@ -263,7 +263,7 @@ configAwsOrGcp: # configure K8s cron job managed by the operator configLogicalBackup: # image for pods of the logical backup job (example runs pg_dumpall) - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # path of google cloud service account json file # logical_backup_google_application_credentials: "" @@ -289,13 +289,13 @@ configLogicalBackup: # automate creation of human users with teams API service configTeamsApi: # team_admin_role will have the rights to grant roles coming from PG manifests - # enable_admin_role_for_users: true - + enable_admin_role_for_users: true # operator watches for PostgresTeam CRs to assign additional teams and members to clusters enable_postgres_team_crd: false # toogle to create additional superuser teams from PostgresTeam CRs - # enable_postgres_team_crd_superusers: false - + enable_postgres_team_crd_superusers: false + # toggle to automatically rename roles of former team members and deny LOGIN + enable_team_member_deprecation: false # toggle to grant superuser to team members created from the Teams API enable_team_superuser: false # toggles usage of the Teams API by the operator @@ -306,12 +306,13 @@ configTeamsApi: # operator will add all team member roles to this group and add a pg_hba line pam_role_name: zalandos # List of teams which members need the superuser role in each Postgres cluster - # postgres_superuser_teams: - # - postgres_superusers - + postgres_superuser_teams: + - postgres_superusers # List of roles that cannot be overwritten by an application, team or infrastructure role protected_role_names: - admin + # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD + role_deletion_suffix: "_deleted" # role name to grant to team members created from the Teams API team_admin_role: admin # postgres config parameters to apply to each team member role diff --git a/charts/postgres-operator/values.yaml b/charts/postgres-operator/values.yaml index 1cf84ca87..fde4e203d 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.6.2 + tag: v1.6.3 pullPolicy: "IfNotPresent" # Optionally specify an array of imagePullSecrets. @@ -35,7 +35,7 @@ configGeneral: # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s) # kubernetes_use_configmaps: "false" # Spilo docker image - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # min number of instances in Postgres cluster. -1 = no limit min_instances: "-1" # max number of instances in Postgres cluster. -1 = no limit @@ -253,7 +253,7 @@ configAwsOrGcp: # configure K8s cron job managed by the operator configLogicalBackup: # image for pods of the logical backup job (example runs pg_dumpall) - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # path of google cloud service account json file # logical_backup_google_application_credentials: "" @@ -280,36 +280,32 @@ configLogicalBackup: # automate creation of human users with teams API service configTeamsApi: # team_admin_role will have the rights to grant roles coming from PG manifests - # enable_admin_role_for_users: "true" - + enable_admin_role_for_users: "true" # operator watches for PostgresTeam CRs to assign additional teams and members to clusters enable_postgres_team_crd: "false" # toogle to create additional superuser teams from PostgresTeam CRs - # enable_postgres_team_crd_superusers: "false" - + enable_postgres_team_crd_superusers: "false" + # toggle to automatically rename roles of former team members and deny LOGIN + enable_team_member_deprecation: "false" # toggle to grant superuser to team members created from the Teams API - # enable_team_superuser: "false" - + enable_team_superuser: "false" # toggles usage of the Teams API by the operator enable_teams_api: "false" # should contain a URL to use for authentication (username and token) # 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: "zalandos" # List of teams which members need the superuser role in each Postgres cluster - # postgres_superuser_teams: "postgres_superusers" - + postgres_superuser_teams: "postgres_superusers" # List of roles that cannot be overwritten by an application, team or infrastructure role - # protected_role_names: "admin" - + protected_role_names: "admin" + # Suffix to add if members are removed from TeamsAPI or PostgresTeam CRD + role_deletion_suffix: "_deleted" # role name to grant to team members created from the Teams API - # team_admin_role: "admin" - + team_admin_role: "admin" # postgres config parameters to apply to each team member role - # team_api_role_configuration: "log_statement:all" - + team_api_role_configuration: "log_statement:all" # URL of the Teams API service # teams_api_url: http://fake-teams-api.default.svc.cluster.local diff --git a/docs/administrator.md b/docs/administrator.md index 2b65fe3ea..db3bae6b9 100644 --- a/docs/administrator.md +++ b/docs/administrator.md @@ -950,7 +950,7 @@ make docker # build in image in minikube docker env eval $(minikube docker-env) -docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.6.2 . +docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.6.3 . # apply UI manifests next to a running Postgres Operator kubectl apply -f manifests/ diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 80d0e5b8c..395007c91 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -593,7 +593,7 @@ grouped under the `logical_backup` key. runs `pg_dumpall` on a replica if possible and uploads compressed results to an S3 bucket under the key `/spilo/pg_cluster_name/cluster_k8s_uuid/logical_backups`. The default image is the same image built with the Zalando-internal CI - pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + pipeline. Default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" * **logical_backup_google_application_credentials** Specifies the path of the google cloud service account json file. Default is empty. @@ -704,6 +704,19 @@ key. cluster to administer Postgres and maintain infrastructure built around it. The default is empty. +* **role_deletion_suffix** + defines a suffix that - when `enable_team_member_deprecation` is set to + `true` - will be appended to database role names of team members that were + removed from either the team in the Teams API or a `PostgresTeam` custom + resource (additionalMembers). When re-added, the operator will rename roles + with the defined suffix back to the original role name. + The default is `_deleted`. + +* **enable_team_member_deprecation** + if `true` database roles of former team members will be renamed by appending + the configured `role_deletion_suffix` and `LOGIN` privilege will be revoked. + The default is `false`. + * **enable_postgres_team_crd** toggle to make the operator watch for created or updated `PostgresTeam` CRDs and create roles for specified additional teams and members. diff --git a/docs/user.md b/docs/user.md index 3342913c8..8e406ec00 100644 --- a/docs/user.md +++ b/docs/user.md @@ -407,6 +407,23 @@ spec: - "briggs" ``` +#### Removed members + +The Postgres Operator does not delete database roles when users are removed +from manifests. But, using the `PostgresTeam` custom resource or Teams API it +is very easy to add roles to many clusters. Manually reverting such a change +is cumbersome. Therefore, if members are removed from a `PostgresTeam` or the +Teams API the operator can rename roles appending a configured suffix to the +name (see `role_deletion_suffix` option) and revoke the `LOGIN` privilege. +The suffix makes it easy then for a cleanup script to remove those deprecated +roles completely. Switch `enable_team_member_deprecation` to `true` to enable +this behavior. + +When a role is re-added to a `PostgresTeam` manifest (or to the source behind +the Teams API) the operator will check for roles with the configured suffix +and if found, rename the role back to the original name and grant `LOGIN` +again. + ## Prepared databases with roles and default privileges The `users` section in the manifests only allows for creating database roles diff --git a/e2e/requirements.txt b/e2e/requirements.txt index 4f6f5ac5f..b276d2537 100644 --- a/e2e/requirements.txt +++ b/e2e/requirements.txt @@ -1,3 +1,3 @@ kubernetes==11.0.0 timeout_decorator==0.4.1 -pyyaml==5.3.1 +pyyaml==5.4.1 diff --git a/e2e/tests/test_e2e.py b/e2e/tests/test_e2e.py index 114f881c4..2b9e3ad28 100644 --- a/e2e/tests/test_e2e.py +++ b/e2e/tests/test_e2e.py @@ -197,13 +197,15 @@ class EndToEndTestCase(unittest.TestCase): enable_postgres_team_crd = { "data": { "enable_postgres_team_crd": "true", - "resync_period": "15s", + "enable_team_member_deprecation": "true", + "role_deletion_suffix": "_delete_me", + "resync_period": "15s" }, } self.k8s.update_config(enable_postgres_team_crd) self.eventuallyEqual(lambda: self.k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") - + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( 'acid.zalan.do', 'v1', 'default', 'postgresteams', 'custom-team-membership', @@ -222,18 +224,60 @@ class EndToEndTestCase(unittest.TestCase): } }) - # make sure we let one sync pass and the new user being added - time.sleep(15) - leader = self.k8s.get_cluster_leader_pod() user_query = """ - SELECT usename - FROM pg_catalog.pg_user - WHERE usename IN ('elephant', 'kind'); + SELECT rolname + FROM pg_catalog.pg_roles + WHERE rolname IN ('elephant', 'kind'); """ - users = self.query_database(leader.metadata.name, "postgres", user_query) - self.eventuallyEqual(lambda: len(users), 2, - "Not all additional users found in database: {}".format(users)) + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Not all additional users found in database", 10, 5) + + # replace additional member and check if the removed member's role is renamed + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( + 'acid.zalan.do', 'v1', 'default', + 'postgresteams', 'custom-team-membership', + { + 'spec': { + 'additionalMembers': { + 'e2e': [ + 'tester' + ] + }, + } + }) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE (rolname = 'tester' AND rolcanlogin) + OR (rolname = 'kind_delete_me' AND NOT rolcanlogin); + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Database role of replaced member in PostgresTeam not renamed", 10, 5) + + # re-add additional member and check if the role is renamed back + self.k8s.api.custom_objects_api.patch_namespaced_custom_object( + 'acid.zalan.do', 'v1', 'default', + 'postgresteams', 'custom-team-membership', + { + 'spec': { + 'additionalMembers': { + 'e2e': [ + 'kind' + ] + }, + } + }) + + user_query = """ + SELECT rolname + FROM pg_catalog.pg_roles + WHERE (rolname = 'kind' AND rolcanlogin) + OR (rolname = 'tester_delete_me' AND NOT rolcanlogin); + """ + self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2, + "Database role of recreated member in PostgresTeam not renamed back to original name", 10, 5) # revert config change revert_resync = { @@ -407,14 +451,14 @@ class EndToEndTestCase(unittest.TestCase): leader = k8s.get_cluster_leader_pod() schemas_query = """ - select schema_name - from information_schema.schemata - where schema_name = 'pooler' + SELECT schema_name + FROM information_schema.schemata + WHERE schema_name = 'pooler' """ db_list = self.list_databases(leader.metadata.name) for db in db_list: - self.eventuallyNotEqual(lambda: len(self.query_database(leader.metadata.name, db, schemas_query)), 0, + self.eventuallyNotEqual(lambda: len(self.query_database(leader.metadata.name, db, schemas_query)), 0, "Pooler schema not found in database {}".format(db)) # remove config section to make test work next time @@ -529,6 +573,7 @@ class EndToEndTestCase(unittest.TestCase): "Parameters": None, "AdminRole": "", "Origin": 2, + "Deleted": False }) return True except: @@ -744,7 +789,7 @@ class EndToEndTestCase(unittest.TestCase): } } k8s.api.custom_objects_api.patch_namespaced_custom_object( - "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources) + "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources) self.eventuallyEqual(lambda: k8s.get_operator_state(), {"0": "idle"}, "Operator does not get in sync") self.eventuallyEqual(lambda: k8s.count_running_pods(), 2, "No two pods running after lazy rolling upgrade") @@ -1417,7 +1462,7 @@ class EndToEndTestCase(unittest.TestCase): k8s = self.k8s result_set = [] db_list = [] - db_list_query = "select datname from pg_database" + db_list_query = "SELECT datname FROM pg_database" exec_query = r"psql -tAq -c \"{}\" -d {}" try: diff --git a/go.mod b/go.mod index b46ebd089..1759d9573 100644 --- a/go.mod +++ b/go.mod @@ -12,11 +12,10 @@ require ( github.com/stretchr/testify v1.6.1 golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c golang.org/x/mod v0.4.0 // indirect - golang.org/x/tools v0.0.0-20201207204333-a835c872fcea // indirect gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.20.2 - k8s.io/apiextensions-apiserver v0.19.4 - k8s.io/apimachinery v0.20.2 - k8s.io/client-go v0.20.2 - k8s.io/code-generator v0.19.4 + k8s.io/api v0.20.6 + k8s.io/apiextensions-apiserver v0.20.6 + k8s.io/apimachinery v0.20.6 + k8s.io/client-go v0.20.6 + k8s.io/code-generator v0.20.6 ) diff --git a/go.sum b/go.sum index eb83c8d38..81b242b7a 100644 --- a/go.sum +++ b/go.sum @@ -6,7 +6,6 @@ cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxK cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.51.0/go.mod h1:hWtGJ6gnXH+KgDv+V0zFGDvpi07n3z8ZNj3T1RW0Gcw= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= @@ -15,6 +14,7 @@ cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNF cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -24,44 +24,29 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI= -github.com/Azure/go-autorest/autorest v0.9.6/go.mod h1:/FALq9T/kS7b5J5qsQ+RSTUdAmGFqi0vUdVNNx8q630= github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= -github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0= -github.com/Azure/go-autorest/autorest/adal v0.8.2/go.mod h1:ZjhuQClTqx435SRJ2iMlOxPYt3d2C/T/7TiQCVZSn3Q= github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA= -github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g= github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0= -github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM= github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= 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/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/aws/aws-sdk-go v1.36.29 h1:lM1G3AF1+7vzFm0n7hfH8r2+750BTo+6Lo6FtPB7kzk= github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= @@ -69,7 +54,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -79,7 +65,7 @@ github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMn github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -94,8 +80,6 @@ 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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= @@ -117,8 +101,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= -github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -129,58 +111,22 @@ github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI= -github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik= -github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk= -github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU= -github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0= -github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= -github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= -github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= -github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= -github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= -github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU= -github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs= -github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk= -github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA= -github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64= -github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4= -github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= -github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI= -github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY= github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU= -github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY= -github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU= -github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= -github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= -github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg= github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4= -github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA= -github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= -github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +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-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -236,18 +182,36 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gnostic v0.4.1 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= +github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= +github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= +github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -265,9 +229,10 @@ github.com/json-iterator/go v1.1.10 h1:Kz6Cvnvv2wGdaG/V8yMvfkmNiXq9Ya2KUv4rouJJr github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +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= @@ -281,20 +246,25 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.9.0 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= +github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -318,7 +288,7 @@ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -327,6 +297,7 @@ 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= @@ -344,33 +315,37 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 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.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= 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 v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 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.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= 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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= @@ -380,23 +355,17 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200819165624-17cef6e3e9d5/go.mod h1:skWido08r9w6Lq/w70DO5XYIKMu4QFu1+4VsqLQuJy8= -go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.1/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= -go.mongodb.org/mongo-driver v1.1.2/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -406,14 +375,12 @@ go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/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-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190617133340-57b3e21c3d56/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k= @@ -453,17 +420,16 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 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= @@ -477,7 +443,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/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-20201110031124-69a78807bb2b h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -494,14 +459,15 @@ golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJ 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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 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-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -525,7 +491,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/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-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY= golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -546,26 +511,24 @@ golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e h1:EHBhcS0mlXEAVwNyO2dLfjTo golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/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-20190125232054-d66bd3c5d5a6/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190617190820-da514acc4774/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -581,9 +544,10 @@ golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapK golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200616133436-c1934b75d054/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20201207204333-a835c872fcea h1:LgKM3cNs8xO6GK1ZVK0nasPn7IN39Sz9EBTwQLyishk= -golang.org/x/tools v0.0.0-20201207204333-a835c872fcea/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +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 h1:CB3a9Nez8M13wwlr/E2YtwoU+qYHKfC+JrDa45RXXoQ= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 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= @@ -624,9 +588,9 @@ google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -654,6 +618,7 @@ gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= @@ -677,41 +642,36 @@ honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.19.4/go.mod h1:SbtJ2aHCItirzdJ36YslycFNzWADYH3tgOhvBEFtZAk= -k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw= -k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8= -k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4= -k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw= -k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA= -k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg= -k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= -k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw= -k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA= -k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ= -k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE= -k8s.io/code-generator v0.19.4 h1:c8IL7RgTgJaYgr2bYMgjN0WikHnohbBhEgajfIkuP5I= -k8s.io/code-generator v0.19.4/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk= -k8s.io/component-base v0.19.4/go.mod h1:ZzuSLlsWhajIDEkKF73j64Gz/5o0AgON08FgRbEPI70= +k8s.io/api v0.20.6 h1:bgdZrW++LqgrLikWYNruIKAtltXbSCX2l5mJu11hrVE= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apiextensions-apiserver v0.20.6 h1:3ZJiXMif7W/32RnfyHN8HygTAZaN7o+FvHxCyuQ7VOo= +k8s.io/apiextensions-apiserver v0.20.6/go.mod h1:qO8YMqeMmZH+lV21LUNzV41vfpoE9QVAJRA+MNqj0mo= +k8s.io/apimachinery v0.20.6 h1:R5p3SlhaABYShQSO6LpPsYHjV05Q+79eBUR0Ut/f4tk= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.6 h1:nJZOfolnsVtDtbGJNCxzOtKUAu7zvXjB8+pMo9UNxZo= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/code-generator v0.20.6 h1:kp65Y6kF6A4+5PvSNvXWSI5p5vuA9tUxEqEZciPw+7Q= +k8s.io/code-generator v0.20.6/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14 h1:t4L10Qfx/p7ASH3gXCdIUtPbbIuegCoUJf3TMSFekjw= -k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded h1:JApXBKYyB7l9xx+DK7/+mFjC7A9Bt5A93FPvFD0HIFE= +k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= k8s.io/klog/v2 v2.4.0 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ= k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/kube-openapi v0.0.0-20200805222855-6aeccd4b50c6/go.mod h1:UuqjUnNftUyPE5H64/qeyjQoUZhGpeFDVdxjTeEVN2o= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c= k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= -k8s.io/utils v0.0.0-20200729134348-d5654de09c73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20201110183641-67b214c5f920 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw= k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.9/go.mod h1:dzAXnQbTRyDlZPJX2SUPEqvnB+j7AJjtlox7PEwigU0= -sigs.k8s.io/structured-merge-diff/v4 v4.0.1/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2 h1:YHQV7Dajm86OuqnIR6zAelnDWBRjo+YhYV9PmGrh1s8= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3 h1:4oyYo8NREp49LBBhKxEqCulFjg26rawYKrnCmg+Sr6c= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/kubectl-pg/cmd/addDb.go b/kubectl-pg/cmd/addDb.go index cd45ea974..1c33579d9 100644 --- a/kubectl-pg/cmd/addDb.go +++ b/kubectl-pg/cmd/addDb.go @@ -71,7 +71,7 @@ func addDb(dbName string, dbOwner string, clusterName string) { var dbOwnerExists bool dbUsers := postgresql.Spec.Users - for key, _ := range dbUsers { + for key := range dbUsers { if key == dbOwner { dbOwnerExists = true } diff --git a/kubectl-pg/cmd/connect.go b/kubectl-pg/cmd/connect.go index 2f1500639..2c6d87835 100644 --- a/kubectl-pg/cmd/connect.go +++ b/kubectl-pg/cmd/connect.go @@ -23,13 +23,14 @@ THE SOFTWARE. package cmd import ( + "log" + "os" + user "os/user" + "github.com/spf13/cobra" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" - "log" - "os" - user "os/user" ) // connectCmd represents the kubectl pg connect command @@ -80,13 +81,13 @@ kubectl pg connect -c cluster -p -u user01 -d db01 func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) { config := getConfig() - client, er := kubernetes.NewForConfig(config) - if er != nil { - log.Fatal(er) + client, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatal(err) } podName := getPodName(clusterName, master, replica) - execRequest := &rest.Request{} + var execRequest *rest.Request if psql { execRequest = client.CoreV1().RESTClient().Post().Resource("pods"). diff --git a/kubectl-pg/cmd/create.go b/kubectl-pg/cmd/create.go index 4d1bc75fb..00ee7ac24 100644 --- a/kubectl-pg/cmd/create.go +++ b/kubectl-pg/cmd/create.go @@ -53,6 +53,9 @@ kubectl pg create -f cluster-manifest.yaml func create(fileName string) { config := getConfig() postgresConfig, err := PostgresqlLister.NewForConfig(config) + if err != nil { + log.Fatal(err) + } ymlFile, err := ioutil.ReadFile(fileName) if err != nil { log.Fatal(err) diff --git a/kubectl-pg/cmd/extVolume.go b/kubectl-pg/cmd/extVolume.go index 58a9eef67..02ccc372d 100644 --- a/kubectl-pg/cmd/extVolume.go +++ b/kubectl-pg/cmd/extVolume.go @@ -67,7 +67,7 @@ func extVolume(increasedVolumeSize string, clusterName string) { namespace := getCurrentNamespace() postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{}) if err != nil { - log.Fatalf("hii %v", err) + log.Fatal(err) } oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size) diff --git a/kubectl-pg/cmd/scale.go b/kubectl-pg/cmd/scale.go index 5e8848de6..0a7bdc60f 100644 --- a/kubectl-pg/cmd/scale.go +++ b/kubectl-pg/cmd/scale.go @@ -31,7 +31,6 @@ import ( "github.com/spf13/cobra" PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1" - v1 "k8s.io/api/apps/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" @@ -46,6 +45,9 @@ var scaleCmd = &cobra.Command{ Scaling to 0 leads to down time.`, Run: func(cmd *cobra.Command, args []string) { clusterName, err := cmd.Flags().GetString("cluster") + if err != nil { + log.Fatal(err) + } namespace, err := cmd.Flags().GetString("namespace") if err != nil { log.Fatal(err) @@ -129,8 +131,7 @@ func allowedMinMaxInstances(config *rest.Config) (int32, int32) { log.Fatal(err) } - var operator *v1.Deployment - operator = getPostgresOperator(k8sClient) + operator := getPostgresOperator(k8sClient) operatorContainer := operator.Spec.Template.Spec.Containers var configMapName, operatorConfigName string diff --git a/kubectl-pg/cmd/update.go b/kubectl-pg/cmd/update.go index 604d613fc..6a5f4e36d 100644 --- a/kubectl-pg/cmd/update.go +++ b/kubectl-pg/cmd/update.go @@ -57,6 +57,9 @@ kubectl pg update -f cluster-manifest.yaml func updatePgResources(fileName string) { config := getConfig() postgresConfig, err := PostgresqlLister.NewForConfig(config) + if err != nil { + log.Fatal(err) + } ymlFile, err := ioutil.ReadFile(fileName) if err != nil { log.Fatal(err) diff --git a/kubectl-pg/cmd/util.go b/kubectl-pg/cmd/util.go index a6bc10296..fa0eb6d42 100644 --- a/kubectl-pg/cmd/util.go +++ b/kubectl-pg/cmd/util.go @@ -99,9 +99,9 @@ func confirmAction(clusterName string, namespace string) { func getPodName(clusterName string, master bool, replicaNumber string) string { config := getConfig() - client, er := kubernetes.NewForConfig(config) - if er != nil { - log.Fatal(er) + client, err := kubernetes.NewForConfig(config) + if err != nil { + log.Fatal(err) } postgresConfig, err := PostgresqlLister.NewForConfig(config) diff --git a/manifests/complete-postgres-manifest.yaml b/manifests/complete-postgres-manifest.yaml index 75b873321..6e2acbdd3 100644 --- a/manifests/complete-postgres-manifest.yaml +++ b/manifests/complete-postgres-manifest.yaml @@ -9,7 +9,7 @@ metadata: # "delete-date": "2020-08-31" # can only be deleted on that day if "delete-date "key is configured # "delete-clustername": "acid-test-cluster" # can only be deleted when name matches if "delete-clustername" key is configured spec: - dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + dockerImage: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 teamId: "acid" numberOfInstances: 2 users: # Application/Robot users diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 29390a1f0..7a05135ab 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -32,7 +32,7 @@ data: # default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # downscaler_annotations: "deployment-time,downscaler/*" # enable_admin_role_for_users: "true" # enable_crd_validation: "true" @@ -51,6 +51,7 @@ data: # enable_shm_volume: "true" # enable_sidecars: "true" enable_spilo_wal_path_compat: "true" + enable_team_member_deprecation: "false" # enable_team_superuser: "false" enable_teams_api: "false" # etcd_host: "" @@ -63,7 +64,7 @@ data: # inherited_labels: application,environment # kube_iam_role: "" # log_s3_bucket: "" - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" @@ -111,6 +112,7 @@ data: resource_check_timeout: 10m resync_period: 30m ring_log_lines: "100" + role_deletion_suffix: "_deleted" secret_name_template: "{username}.{cluster}.credentials" # sidecar_docker_images: "" # set_memory_request_to_limit: "false" diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index 47244e5d7..806acc8da 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -61,7 +61,7 @@ spec: properties: docker_image: type: string - default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p6" + default: "registry.opensource.zalan.do/acid/spilo-13:2.0-p7" enable_crd_validation: type: boolean default: true @@ -393,7 +393,7 @@ spec: properties: logical_backup_docker_image: type: string - default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + default: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" logical_backup_google_application_credentials: type: string logical_backup_job_prefix: @@ -439,6 +439,9 @@ spec: enable_postgres_team_crd_superusers: type: boolean default: false + enable_team_member_deprecation: + type: boolean + default: false enable_team_superuser: type: boolean default: false @@ -461,6 +464,9 @@ spec: type: string default: - admin + role_deletion_suffix: + type: string + default: "_deleted" team_admin_role: type: string default: "admin" diff --git a/manifests/postgres-operator.yaml b/manifests/postgres-operator.yaml index 5e189c819..3dbe3acb9 100644 --- a/manifests/postgres-operator.yaml +++ b/manifests/postgres-operator.yaml @@ -19,7 +19,7 @@ spec: serviceAccountName: postgres-operator containers: - name: postgres-operator - image: registry.opensource.zalan.do/acid/postgres-operator:v1.6.2 + image: registry.opensource.zalan.do/acid/postgres-operator:v1.6.3 imagePullPolicy: IfNotPresent resources: requests: diff --git a/manifests/postgresql-operator-default-configuration.yaml b/manifests/postgresql-operator-default-configuration.yaml index 3a8a79c8d..bd6f321dd 100644 --- a/manifests/postgresql-operator-default-configuration.yaml +++ b/manifests/postgresql-operator-default-configuration.yaml @@ -3,7 +3,7 @@ kind: OperatorConfiguration metadata: name: postgresql-operator-default-configuration configuration: - docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p6 + docker_image: registry.opensource.zalan.do/acid/spilo-13:2.0-p7 # enable_crd_validation: true # enable_lazy_spilo_upgrade: false enable_pgversion_env_var: true @@ -123,7 +123,7 @@ configuration: # wal_gs_bucket: "" # wal_s3_bucket: "" logical_backup: - logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.2" + logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup:v1.6.3" # logical_backup_google_application_credentials: "" logical_backup_job_prefix: "logical-backup-" logical_backup_provider: "s3" @@ -141,6 +141,7 @@ configuration: # enable_admin_role_for_users: true # enable_postgres_team_crd: false # enable_postgres_team_crd_superusers: false + enable_team_member_deprecation: false enable_team_superuser: false enable_teams_api: false # pam_configuration: "" @@ -149,6 +150,7 @@ configuration: # - postgres_superusers protected_role_names: - admin + role_deletion_suffix: "_deleted" team_admin_role: admin team_api_role_configuration: log_statement: all diff --git a/manifests/postgresql.crd.yaml b/manifests/postgresql.crd.yaml index 61a04144c..30b41d392 100644 --- a/manifests/postgresql.crd.yaml +++ b/manifests/postgresql.crd.yaml @@ -23,7 +23,7 @@ spec: additionalPrinterColumns: - name: Team type: string - description: Team responsible for Postgres CLuster + description: Team responsible for Postgres cluster jsonPath: .spec.teamId - name: Version type: string diff --git a/pkg/apis/acid.zalan.do/v1/crds.go b/pkg/apis/acid.zalan.do/v1/crds.go index 89d71eef5..83e7273e4 100644 --- a/pkg/apis/acid.zalan.do/v1/crds.go +++ b/pkg/apis/acid.zalan.do/v1/crds.go @@ -1377,6 +1377,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ "enable_postgres_team_crd_superusers": { Type: "boolean", }, + "enable_team_member_deprecation": { + Type: "boolean", + }, "enable_team_superuser": { Type: "boolean", }, @@ -1405,6 +1408,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{ }, }, }, + "role_deletion_suffix": { + Type: "string", + }, "team_admin_role": { Type: "string", }, diff --git a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go index 78b618b78..cf581431b 100644 --- a/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go +++ b/pkg/apis/acid.zalan.do/v1/operator_configuration_type.go @@ -159,6 +159,8 @@ type TeamsAPIConfiguration struct { PostgresSuperuserTeams []string `json:"postgres_superuser_teams,omitempty"` EnablePostgresTeamCRD bool `json:"enable_postgres_team_crd,omitempty"` EnablePostgresTeamCRDSuperusers bool `json:"enable_postgres_team_crd_superusers,omitempty"` + EnableTeamMemberDeprecation bool `json:"enable_team_member_deprecation,omitempty"` + RoleDeletionSuffix string `json:"role_deletion_suffix,omitempty"` } // LoggingRESTAPIConfiguration defines Logging API conf diff --git a/pkg/cluster/cluster.go b/pkg/cluster/cluster.go index 424c8e89a..ff474884c 100644 --- a/pkg/cluster/cluster.go +++ b/pkg/cluster/cluster.go @@ -74,6 +74,7 @@ type Cluster struct { eventRecorder record.EventRecorder patroni patroni.Interface pgUsers map[string]spec.PgUser + pgUsersCache map[string]spec.PgUser systemUsers map[string]spec.PgUser podSubscribers map[spec.NamespacedName]chan PodEvent podSubscribersMu sync.RWMutex @@ -129,7 +130,9 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres Secrets: make(map[types.UID]*v1.Secret), Services: make(map[PostgresRole]*v1.Service), Endpoints: make(map[PostgresRole]*v1.Endpoints)}, - userSyncStrategy: users.DefaultUserSyncStrategy{PasswordEncryption: passwordEncryption}, + userSyncStrategy: users.DefaultUserSyncStrategy{ + PasswordEncryption: passwordEncryption, + RoleDeletionSuffix: cfg.OpConfig.RoleDeletionSuffix}, deleteOptions: metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy}, podEventsQueue: podEventsQueue, KubeClient: kubeClient, @@ -190,6 +193,17 @@ func (c *Cluster) isNewCluster() bool { func (c *Cluster) initUsers() error { c.setProcessName("initializing users") + // if team member deprecation is enabled save current state of pgUsers + // to check for deleted roles + c.pgUsersCache = map[string]spec.PgUser{} + if c.OpConfig.EnableTeamMemberDeprecation { + for k, v := range c.pgUsers { + if v.Origin == spec.RoleOriginTeamsAPI { + c.pgUsersCache[k] = v + } + } + } + // clear our the previous state of the cluster users (in case we are // running a sync). c.systemUsers = map[string]spec.PgUser{} @@ -361,6 +375,7 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa } if !reflect.DeepEqual(c.Statefulset.Annotations, statefulSet.Annotations) { match = false + needsReplace = true reasons = append(reasons, "new statefulset's annotations do not match the current one") } @@ -445,6 +460,11 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa } } + if len(c.Statefulset.Spec.Template.Spec.Volumes) != len(statefulSet.Spec.Template.Spec.Volumes) { + needsReplace = true + reasons = append(reasons, "new statefulset's volumes contains different number of volumes to the old one") + } + // we assume any change in priority happens by rolling out a new priority class // changing the priority value in an existing class is not supproted if c.Statefulset.Spec.Template.Spec.PriorityClassName != statefulSet.Spec.Template.Spec.PriorityClassName { @@ -456,7 +476,9 @@ func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compa // lazy Spilo update: modify the image in the statefulset itself but let its pods run with the old image // until they are re-created for other reasons, for example node rotation - if c.OpConfig.EnableLazySpiloUpgrade && !reflect.DeepEqual(c.Statefulset.Spec.Template.Spec.Containers[0].Image, statefulSet.Spec.Template.Spec.Containers[0].Image) { + effectivePodImage := getPostgresContainer(&c.Statefulset.Spec.Template.Spec).Image + desiredImage := getPostgresContainer(&statefulSet.Spec.Template.Spec).Image + if c.OpConfig.EnableLazySpiloUpgrade && !reflect.DeepEqual(effectivePodImage, desiredImage) { needsReplace = true reasons = append(reasons, "lazy Spilo update: new statefulset's pod image does not match the current one") } @@ -502,6 +524,8 @@ func (c *Cluster) compareContainers(description string, setA, setB []v1.Containe func(a, b v1.Container) bool { return !reflect.DeepEqual(a.EnvFrom, b.EnvFrom) }), newCheck("new statefulset %s's %s (index %d) security context does not match the current one", func(a, b v1.Container) bool { return !reflect.DeepEqual(a.SecurityContext, b.SecurityContext) }), + newCheck("new statefulset %s's %s (index %d) volume mounts do not match the current one", + func(a, b v1.Container) bool { return !reflect.DeepEqual(a.VolumeMounts, b.VolumeMounts) }), } if !c.OpConfig.EnableLazySpiloUpgrade { @@ -598,7 +622,7 @@ func (c *Cluster) enforceMinResourceLimits(spec *acidv1.PostgresSpec) error { // for a cluster that had no such job before. In this case a missing job is not an error. func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed := false - syncStatetfulSet := false + syncStatefulSet := false c.mu.Lock() defer c.mu.Unlock() @@ -619,7 +643,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { if IsBiggerPostgresVersion(oldSpec.Spec.PostgresqlParam.PgVersion, c.GetDesiredMajorVersion()) { c.logger.Infof("postgresql version increased (%s -> %s), depending on config manual upgrade needed", oldSpec.Spec.PostgresqlParam.PgVersion, newSpec.Spec.PostgresqlParam.PgVersion) - syncStatetfulSet = true + syncStatefulSet = true } else { c.logger.Infof("postgresql major version unchanged or smaller, no changes needed") // sticking with old version, this will also advance GetDesiredVersion next time. @@ -642,7 +666,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { needConnectionPooler := needMasterConnectionPoolerWorker(&newSpec.Spec) || needReplicaConnectionPoolerWorker(&newSpec.Spec) if !sameUsers || needConnectionPooler { - c.logger.Debugf("syncing secrets") + c.logger.Debugf("initialize users") if err := c.initUsers(); err != nil { c.logger.Errorf("could not init users: %v", err) updateFailed = true @@ -688,9 +712,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error { updateFailed = true return } - if syncStatetfulSet || !reflect.DeepEqual(oldSs, newSs) || !reflect.DeepEqual(oldSpec.Annotations, newSpec.Annotations) { + if syncStatefulSet || !reflect.DeepEqual(oldSs, newSs) || !reflect.DeepEqual(oldSpec.Annotations, newSpec.Annotations) { c.logger.Debugf("syncing statefulsets") - syncStatetfulSet = false + syncStatefulSet = false // TODO: avoid generating the StatefulSet object twice by passing it to syncStatefulSet if err := c.syncStatefulSet(); err != nil { c.logger.Errorf("could not sync statefulsets: %v", err) @@ -1163,7 +1187,7 @@ func (c *Cluster) initHumanUsers() error { for _, superuserTeam := range superuserTeams { err := c.initTeamMembers(superuserTeam, true) if err != nil { - return fmt.Errorf("Cannot initialize members for team %q of Postgres superusers: %v", superuserTeam, err) + return fmt.Errorf("cannot initialize members for team %q of Postgres superusers: %v", superuserTeam, err) } if superuserTeam == c.Spec.TeamID { clusterIsOwnedBySuperuserTeam = true @@ -1176,7 +1200,7 @@ func (c *Cluster) initHumanUsers() error { if !(util.SliceContains(superuserTeams, additionalTeam)) { err := c.initTeamMembers(additionalTeam, false) if err != nil { - return fmt.Errorf("Cannot initialize members for additional team %q for cluster owned by %q: %v", additionalTeam, c.Spec.TeamID, err) + return fmt.Errorf("cannot initialize members for additional team %q for cluster owned by %q: %v", additionalTeam, c.Spec.TeamID, err) } } } @@ -1189,7 +1213,7 @@ func (c *Cluster) initHumanUsers() error { err := c.initTeamMembers(c.Spec.TeamID, false) if err != nil { - return fmt.Errorf("Cannot initialize members for team %q who owns the Postgres cluster: %v", c.Spec.TeamID, err) + return fmt.Errorf("cannot initialize members for team %q who owns the Postgres cluster: %v", c.Spec.TeamID, err) } return nil diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index db4f1f56d..f579b446e 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -420,9 +420,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { // Clean up the deployment object. If deployment resource we've remembered // is somehow empty, try to delete based on what would we generate - var deployment *appsv1.Deployment - deployment = c.ConnectionPooler[role].Deployment - + deployment := c.ConnectionPooler[role].Deployment policy := metav1.DeletePropagationForeground options := metav1.DeleteOptions{PropagationPolicy: &policy} @@ -445,8 +443,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) { } // Repeat the same for the service object - var service *v1.Service - service = c.ConnectionPooler[role].Service + service := c.ConnectionPooler[role].Service if service == nil { c.logger.Debugf("no connection pooler service object to delete") } else { @@ -566,7 +563,7 @@ func needSyncConnectionPoolerSpecs(oldSpec, newSpec *acidv1.ConnectionPooler, lo // Check if we need to synchronize connection pooler deployment due to new // defaults, that are different from what we see in the DeploymentSpec -func needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPooler, deployment *appsv1.Deployment) (sync bool, reasons []string) { +func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPooler, deployment *appsv1.Deployment) (sync bool, reasons []string) { reasons = []string{} sync = false @@ -619,14 +616,14 @@ func needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPoo ref := env.ValueFrom.SecretKeyRef.LocalObjectReference secretName := Config.OpConfig.SecretNameTemplate.Format( "username", strings.Replace(config.User, "_", "-", -1), - "cluster", deployment.ClusterName, + "cluster", c.Name, "tprkind", acidv1.PostgresCRDResourceKind, "tprgroup", acidzalando.GroupName) if ref.Name != secretName { sync = true - msg := fmt.Sprintf("pooler user is different (having %s, required %s)", - ref.Name, config.User) + msg := fmt.Sprintf("pooler user and secret are different (having %s, required %s)", + ref.Name, secretName) reasons = append(reasons, msg) } } @@ -747,7 +744,7 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look Deployment: nil, Service: nil, Name: c.connectionPoolerName(role), - ClusterName: c.ClusterName, + ClusterName: c.Name, Namespace: c.Namespace, LookupFunction: false, Role: role, @@ -878,7 +875,7 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql specSync, specReason = needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler, c.logger) } - defaultsSync, defaultsReason := needSyncConnectionPoolerDefaults(&c.Config, newConnectionPooler, deployment) + defaultsSync, defaultsReason := c.needSyncConnectionPoolerDefaults(&c.Config, newConnectionPooler, deployment) reason := append(specReason, defaultsReason...) if specSync || defaultsSync { diff --git a/pkg/cluster/database.go b/pkg/cluster/database.go index 760b68d72..829c2e5c7 100644 --- a/pkg/cluster/database.go +++ b/pkg/cluster/database.go @@ -198,6 +198,7 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser rolname, rolpassword string rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin bool roloptions, memberof []string + roldeleted bool ) err := rows.Scan(&rolname, &rolpassword, &rolsuper, &rolinherit, &rolcreaterole, &rolcreatedb, &rolcanlogin, pq.Array(&roloptions), pq.Array(&memberof)) @@ -216,7 +217,11 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser parameters[fields[0]] = fields[1] } - users[rolname] = spec.PgUser{Name: rolname, Password: rolpassword, Flags: flags, MemberOf: memberof, Parameters: parameters} + if strings.HasSuffix(rolname, c.OpConfig.RoleDeletionSuffix) { + roldeleted = true + } + + users[rolname] = spec.PgUser{Name: rolname, Password: rolpassword, Flags: flags, MemberOf: memberof, Parameters: parameters, Deleted: roldeleted} } return users, nil diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index ace665fb0..cb11170d6 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -66,10 +66,6 @@ type spiloConfiguration struct { Bootstrap pgBootstrap `json:"bootstrap"` } -func (c *Cluster) containerName() string { - return "postgres" -} - func (c *Cluster) statefulSetName() string { return c.Name } @@ -213,10 +209,10 @@ PatroniInitDBParams: for _, k := range initdbOptionNames { v := patroni.InitDB[k] for i, defaultParam := range config.Bootstrap.Initdb { - switch defaultParam.(type) { + switch t := defaultParam.(type) { case map[string]string: { - for k1 := range defaultParam.(map[string]string) { + for k1 := range t { if k1 == k { (config.Bootstrap.Initdb[i]).(map[string]string)[k] = v continue PatroniInitDBParams @@ -226,7 +222,7 @@ PatroniInitDBParams: case string: { /* if the option already occurs in the list */ - if defaultParam.(string) == v { + if t == v { continue PatroniInitDBParams } } @@ -264,7 +260,7 @@ PatroniInitDBParams: if patroni.SynchronousMode { config.Bootstrap.DCS.SynchronousMode = patroni.SynchronousMode } - if patroni.SynchronousModeStrict != false { + if patroni.SynchronousModeStrict { config.Bootstrap.DCS.SynchronousModeStrict = patroni.SynchronousModeStrict } @@ -336,7 +332,7 @@ func nodeAffinity(nodeReadinessLabel map[string]string, nodeAffinity *v1.NodeAff if len(nodeReadinessLabel) == 0 && nodeAffinity == nil { return nil } - nodeAffinityCopy := *&v1.NodeAffinity{} + nodeAffinityCopy := v1.NodeAffinity{} if nodeAffinity != nil { nodeAffinityCopy = *nodeAffinity.DeepCopy() } @@ -1157,10 +1153,10 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef c.logger.Debugf("Generating Spilo container, environment variables") c.logger.Debugf("%v", spiloEnvVars) - spiloContainer := generateContainer(c.containerName(), + spiloContainer := generateContainer(constants.PostgresContainerName, &effectiveDockerImage, resourceRequirements, - deduplicateEnvVars(spiloEnvVars, c.containerName(), c.logger), + deduplicateEnvVars(spiloEnvVars, constants.PostgresContainerName, c.logger), volumeMounts, c.OpConfig.Resources.SpiloPrivileged, c.OpConfig.Resources.SpiloAllowPrivilegeEscalation, @@ -1279,15 +1275,12 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.Statef return nil, fmt.Errorf("could not set the pod management policy to the unknown value: %v", c.OpConfig.PodManagementPolicy) } - stsAnnotations := make(map[string]string) - stsAnnotations = c.AnnotationsToPropagate(c.annotationsSet(nil)) - statefulSet := &appsv1.StatefulSet{ ObjectMeta: metav1.ObjectMeta{ Name: c.statefulSetName(), Namespace: c.Namespace, Labels: c.labelsSet(true), - Annotations: stsAnnotations, + Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)), }, Spec: appsv1.StatefulSetSpec{ Replicas: &numberOfInstances, @@ -1395,6 +1388,9 @@ func (c *Cluster) getNumberOfInstances(spec *acidv1.PostgresSpec) int32 { // // see https://docs.okd.io/latest/dev_guide/shared_memory.html func addShmVolume(podSpec *v1.PodSpec) { + + postgresContainerIdx := 0 + volumes := append(podSpec.Volumes, v1.Volume{ Name: constants.ShmVolumeName, VolumeSource: v1.VolumeSource{ @@ -1404,14 +1400,20 @@ func addShmVolume(podSpec *v1.PodSpec) { }, }) - pgIdx := constants.PostgresContainerIdx - mounts := append(podSpec.Containers[pgIdx].VolumeMounts, + for i, container := range podSpec.Containers { + if container.Name == constants.PostgresContainerName { + postgresContainerIdx = i + } + } + + mounts := append(podSpec.Containers[postgresContainerIdx].VolumeMounts, v1.VolumeMount{ Name: constants.ShmVolumeName, MountPath: constants.ShmVolumePath, }) - podSpec.Containers[0].VolumeMounts = mounts + podSpec.Containers[postgresContainerIdx].VolumeMounts = mounts + podSpec.Volumes = volumes } @@ -1442,54 +1444,55 @@ func (c *Cluster) addAdditionalVolumes(podSpec *v1.PodSpec, volumes := podSpec.Volumes mountPaths := map[string]acidv1.AdditionalVolume{} - for i, v := range additionalVolumes { - if previousVolume, exist := mountPaths[v.MountPath]; exist { + for i, additionalVolume := range additionalVolumes { + if previousVolume, exist := mountPaths[additionalVolume.MountPath]; exist { msg := "Volume %+v cannot be mounted to the same path as %+v" - c.logger.Warningf(msg, v, previousVolume) + c.logger.Warningf(msg, additionalVolume, previousVolume) continue } - if v.MountPath == constants.PostgresDataMount { + if additionalVolume.MountPath == constants.PostgresDataMount { msg := "Cannot mount volume on postgresql data directory, %+v" - c.logger.Warningf(msg, v) + c.logger.Warningf(msg, additionalVolume) continue } - if v.TargetContainers == nil { - spiloContainer := podSpec.Containers[0] - additionalVolumes[i].TargetContainers = []string{spiloContainer.Name} + // if no target container is defined assign it to postgres container + if len(additionalVolume.TargetContainers) == 0 { + postgresContainer := getPostgresContainer(podSpec) + additionalVolumes[i].TargetContainers = []string{postgresContainer.Name} } - for _, target := range v.TargetContainers { - if target == "all" && len(v.TargetContainers) != 1 { + for _, target := range additionalVolume.TargetContainers { + if target == "all" && len(additionalVolume.TargetContainers) != 1 { msg := `Target containers could be either "all" or a list of containers, mixing those is not allowed, %+v` - c.logger.Warningf(msg, v) + c.logger.Warningf(msg, additionalVolume) continue } } volumes = append(volumes, v1.Volume{ - Name: v.Name, - VolumeSource: v.VolumeSource, + Name: additionalVolume.Name, + VolumeSource: additionalVolume.VolumeSource, }, ) - mountPaths[v.MountPath] = v + mountPaths[additionalVolume.MountPath] = additionalVolume } c.logger.Infof("Mount additional volumes: %+v", additionalVolumes) for i := range podSpec.Containers { mounts := podSpec.Containers[i].VolumeMounts - for _, v := range additionalVolumes { - for _, target := range v.TargetContainers { + for _, additionalVolume := range additionalVolumes { + for _, target := range additionalVolume.TargetContainers { if podSpec.Containers[i].Name == target || target == "all" { mounts = append(mounts, v1.VolumeMount{ - Name: v.Name, - MountPath: v.MountPath, - SubPath: v.SubPath, + Name: additionalVolume.Name, + MountPath: additionalVolume.MountPath, + SubPath: additionalVolume.SubPath, }) } } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index cf0441f98..5acd4a159 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -11,6 +11,7 @@ import ( "github.com/stretchr/testify/assert" acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/config" @@ -24,9 +25,21 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/client-go/kubernetes/fake" v1core "k8s.io/client-go/kubernetes/typed/core/v1" ) +func newFakeK8sTestClient() (k8sutil.KubernetesClient, *fake.Clientset) { + acidClientSet := fakeacidv1.NewSimpleClientset() + clientSet := fake.NewSimpleClientset() + + return k8sutil.KubernetesClient{ + PodsGetter: clientSet.CoreV1(), + PostgresqlsGetter: acidClientSet.AcidV1(), + StatefulSetsGetter: clientSet.AppsV1(), + }, clientSet +} + // For testing purposes type ExpectedValue struct { envIndex int @@ -412,6 +425,7 @@ func TestShmVolume(t *testing.T) { Volumes: []v1.Volume{{}}, Containers: []v1.Container{ { + Name: "postgres", VolumeMounts: []v1.VolumeMount{ {}, }, @@ -423,9 +437,10 @@ func TestShmVolume(t *testing.T) { } for _, tt := range tests { addShmVolume(tt.podSpec) + postgresContainer := getPostgresContainer(tt.podSpec) volumeName := tt.podSpec.Volumes[tt.shmPos].Name - volumeMountName := tt.podSpec.Containers[0].VolumeMounts[tt.shmPos].Name + volumeMountName := postgresContainer.VolumeMounts[tt.shmPos].Name if volumeName != constants.ShmVolumeName { t.Errorf("%s %s: Expected volume %s was not created, have %s instead", @@ -597,8 +612,9 @@ func TestSecretVolume(t *testing.T) { for _, tt := range tests { additionalSecretMount := "aws-iam-s3-role" additionalSecretMountPath := "/meta/credentials" + postgresContainer := getPostgresContainer(tt.podSpec) - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) + numMounts := len(postgresContainer.VolumeMounts) addSecretVolume(tt.podSpec, additionalSecretMount, additionalSecretMountPath) @@ -618,7 +634,8 @@ func TestSecretVolume(t *testing.T) { } } - numMountsCheck := len(tt.podSpec.Containers[0].VolumeMounts) + postgresContainer = getPostgresContainer(tt.podSpec) + numMountsCheck := len(postgresContainer.VolumeMounts) if numMountsCheck != numMounts+1 { t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", @@ -850,7 +867,8 @@ func testEnvs(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) "CONNECTION_POOLER_PORT": false, } - envs := podSpec.Spec.Containers[0].Env + container := getPostgresContainer(&podSpec.Spec) + envs := container.Env for _, env := range envs { required[env.Name] = true } @@ -930,15 +948,6 @@ func TestNodeAffinity(t *testing.T) { assert.Equal(t, s.Spec.Template.Spec.Affinity.NodeAffinity, nodeAff, "cluster template has correct node affinity") } -func testCustomPodTemplate(cluster *Cluster, podSpec *v1.PodTemplateSpec) error { - if podSpec.ObjectMeta.Name != "test-pod-template" { - return fmt.Errorf("Custom pod template is not used, current spec %+v", - podSpec) - } - - return nil -} - func testDeploymentOwnerReference(cluster *Cluster, deployment *appsv1.Deployment) error { owner := deployment.ObjectMeta.OwnerReferences[0] @@ -962,16 +971,23 @@ func testServiceOwnerReference(cluster *Cluster, service *v1.Service, role Postg } func TestTLS(t *testing.T) { - var err error - var spec acidv1.PostgresSpec - var cluster *Cluster - var spiloRunAsUser = int64(101) - var spiloRunAsGroup = int64(103) - var spiloFSGroup = int64(103) - var additionalVolumes = spec.AdditionalVolumes - makeSpec := func(tls acidv1.TLSDescription) acidv1.PostgresSpec { - return acidv1.PostgresSpec{ + client, _ := newFakeK8sTestClient() + clusterName := "acid-test-cluster" + namespace := "default" + tlsSecretName := "my-secret" + spiloRunAsUser := int64(101) + spiloRunAsGroup := int64(103) + spiloFSGroup := int64(103) + defaultMode := int32(0640) + mountPath := "/tls" + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, @@ -980,11 +996,24 @@ func TestTLS(t *testing.T) { Volume: acidv1.Volume{ Size: "1G", }, - TLS: &tls, - } + TLS: &acidv1.TLSDescription{ + SecretName: tlsSecretName, CAFile: "ca.crt"}, + AdditionalVolumes: []acidv1.AdditionalVolume{ + acidv1.AdditionalVolume{ + Name: tlsSecretName, + MountPath: mountPath, + VolumeSource: v1.VolumeSource{ + Secret: &v1.SecretVolumeSource{ + SecretName: tlsSecretName, + DefaultMode: &defaultMode, + }, + }, + }, + }, + }, } - cluster = New( + var cluster = New( Config{ OpConfig: config.Config{ PodManagementPolicy: "ordered_ready", @@ -999,28 +1028,14 @@ func TestTLS(t *testing.T) { SpiloFSGroup: &spiloFSGroup, }, }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) - spec = makeSpec(acidv1.TLSDescription{SecretName: "my-secret", CAFile: "ca.crt"}) - s, err := cluster.generateStatefulSet(&spec) - if err != nil { - assert.NoError(t, err) - } + }, client, pg, logger, eventRecorder) + + // create a statefulset + sts, err := cluster.createStatefulSet() + assert.NoError(t, err) fsGroup := int64(103) - assert.Equal(t, &fsGroup, s.Spec.Template.Spec.SecurityContext.FSGroup, "has a default FSGroup assigned") - - defaultMode := int32(0640) - mountPath := "/tls" - additionalVolumes = append(additionalVolumes, acidv1.AdditionalVolume{ - Name: spec.TLS.SecretName, - MountPath: mountPath, - VolumeSource: v1.VolumeSource{ - Secret: &v1.SecretVolumeSource{ - SecretName: spec.TLS.SecretName, - DefaultMode: &defaultMode, - }, - }, - }) + assert.Equal(t, &fsGroup, sts.Spec.Template.Spec.SecurityContext.FSGroup, "has a default FSGroup assigned") volume := v1.Volume{ Name: "my-secret", @@ -1031,182 +1046,134 @@ func TestTLS(t *testing.T) { }, }, } - assert.Contains(t, s.Spec.Template.Spec.Volumes, volume, "the pod gets a secret volume") + assert.Contains(t, sts.Spec.Template.Spec.Volumes, volume, "the pod gets a secret volume") - assert.Contains(t, s.Spec.Template.Spec.Containers[0].VolumeMounts, v1.VolumeMount{ + postgresContainer := getPostgresContainer(&sts.Spec.Template.Spec) + assert.Contains(t, postgresContainer.VolumeMounts, v1.VolumeMount{ MountPath: "/tls", Name: "my-secret", }, "the volume gets mounted in /tls") - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) - assert.Contains(t, s.Spec.Template.Spec.Containers[0].Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_CERTIFICATE_FILE", Value: "/tls/tls.crt"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_PRIVATE_KEY_FILE", Value: "/tls/tls.key"}) + assert.Contains(t, postgresContainer.Env, v1.EnvVar{Name: "SSL_CA_FILE", Value: "/tls/ca.crt"}) } func TestAdditionalVolume(t *testing.T) { testName := "TestAdditionalVolume" - tests := []struct { - subTest string - podSpec *v1.PodSpec - volumePos int - }{ + + client, _ := newFakeK8sTestClient() + clusterName := "acid-test-cluster" + namespace := "default" + sidecarName := "sidecar" + additionalVolumes := []acidv1.AdditionalVolume{ { - subTest: "empty PodSpec", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{}, - Containers: []v1.Container{ - { - VolumeMounts: []v1.VolumeMount{}, - }, - }, + Name: "test1", + MountPath: "/test1", + TargetContainers: []string{"all"}, + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, }, - volumePos: 0, }, { - subTest: "non empty PodSpec", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{{}}, - Containers: []v1.Container{ - { - Name: "postgres", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, - }, + Name: "test2", + MountPath: "/test2", + TargetContainers: []string{sidecarName}, + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, }, - volumePos: 1, }, { - subTest: "non empty PodSpec with sidecar", - podSpec: &v1.PodSpec{ - Volumes: []v1.Volume{{}}, - Containers: []v1.Container{ - { - Name: "postgres", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, - { - Name: "sidecar", - VolumeMounts: []v1.VolumeMount{ - { - Name: "data", - ReadOnly: false, - MountPath: "/data", - }, - }, - }, + Name: "test3", + MountPath: "/test3", + TargetContainers: []string{}, // should mount only to postgres + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + { + Name: "test4", + MountPath: "/test4", + TargetContainers: nil, // should mount only to postgres + VolumeSource: v1.VolumeSource{ + EmptyDir: &v1.EmptyDirVolumeSource{}, + }, + }, + } + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + TeamID: "myapp", NumberOfInstances: 1, + Resources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + }, + Volume: acidv1.Volume{ + Size: "1G", + }, + AdditionalVolumes: additionalVolumes, + Sidecars: []acidv1.Sidecar{ + { + Name: sidecarName, }, }, - volumePos: 1, }, } var cluster = New( Config{ OpConfig: config.Config{ - ProtectedRoles: []string{"admin"}, - Auth: config.Auth{ - SuperUsername: superUserName, - ReplicationUsername: replicationUserName, + PodManagementPolicy: "ordered_ready", + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + PodRoleLabel: "spilo-role", }, }, - }, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder) + }, client, pg, logger, eventRecorder) - for _, tt := range tests { - // Test with additional volume mounted in all containers - additionalVolumeMount := []acidv1.AdditionalVolume{ - { - Name: "test", - MountPath: "/test", - TargetContainers: []string{"all"}, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, - }, - }, - } + // create a statefulset + sts, err := cluster.createStatefulSet() + assert.NoError(t, err) - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) - - cluster.addAdditionalVolumes(tt.podSpec, additionalVolumeMount) - volumeName := tt.podSpec.Volumes[tt.volumePos].Name - - if volumeName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected volume %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeName) - } - - for i := range tt.podSpec.Containers { - volumeMountName := tt.podSpec.Containers[i].VolumeMounts[tt.volumePos].Name - - if volumeMountName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected mount %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeMountName) - } - - } - - numMountsCheck := len(tt.podSpec.Containers[0].VolumeMounts) - - if numMountsCheck != numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts+1) - } + tests := []struct { + subTest string + container string + expectedMounts []string + }{ + { + subTest: "checking volume mounts of postgres container", + container: constants.PostgresContainerName, + expectedMounts: []string{"pgdata", "test1", "test3", "test4"}, + }, + { + subTest: "checking volume mounts of sidecar container", + container: "sidecar", + expectedMounts: []string{"pgdata", "test1", "test2"}, + }, } for _, tt := range tests { - // Test with additional volume mounted only in first container - additionalVolumeMount := []acidv1.AdditionalVolume{ - { - Name: "test", - MountPath: "/test", - TargetContainers: []string{"postgres"}, - VolumeSource: v1.VolumeSource{ - EmptyDir: &v1.EmptyDirVolumeSource{}, - }, - }, - } + for _, container := range sts.Spec.Template.Spec.Containers { + if container.Name != tt.container { + continue + } + mounts := []string{} + for _, volumeMounts := range container.VolumeMounts { + mounts = append(mounts, volumeMounts.Name) + } - numMounts := len(tt.podSpec.Containers[0].VolumeMounts) - - cluster.addAdditionalVolumes(tt.podSpec, additionalVolumeMount) - volumeName := tt.podSpec.Volumes[tt.volumePos].Name - - if volumeName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected volume %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeName) - } - - for _, container := range tt.podSpec.Containers { - if container.Name == "postgres" { - volumeMountName := container.VolumeMounts[tt.volumePos].Name - - if volumeMountName != additionalVolumeMount[0].Name { - t.Errorf("%s %s: Expected mount %v was not created, have %s instead", - testName, tt.subTest, additionalVolumeMount, volumeMountName) - } - - numMountsCheck := len(container.VolumeMounts) - if numMountsCheck != numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts+1) - } - } else { - numMountsCheck := len(container.VolumeMounts) - if numMountsCheck == numMounts+1 { - t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v", - numMountsCheck, numMounts) - } + if !util.IsEqualIgnoreOrder(mounts, tt.expectedMounts) { + t.Errorf("%s %s: different volume mounts: got %v, epxected %v", + testName, tt.subTest, mounts, tt.expectedMounts) } } } diff --git a/pkg/cluster/majorversionupgrade.go b/pkg/cluster/majorversionupgrade.go index 06dd979b2..c997a675a 100644 --- a/pkg/cluster/majorversionupgrade.go +++ b/pkg/cluster/majorversionupgrade.go @@ -19,8 +19,8 @@ var VersionMap = map[string]int{ // IsBiggerPostgresVersion Compare two Postgres version numbers func IsBiggerPostgresVersion(old string, new string) bool { - oldN, _ := VersionMap[old] - newN, _ := VersionMap[new] + oldN := VersionMap[old] + newN := VersionMap[new] return newN > oldN } diff --git a/pkg/cluster/resources.go b/pkg/cluster/resources.go index ed8b4099c..48b17f532 100644 --- a/pkg/cluster/resources.go +++ b/pkg/cluster/resources.go @@ -147,25 +147,6 @@ func (c *Cluster) preScaleDown(newStatefulSet *appsv1.StatefulSet) error { return nil } -func (c *Cluster) updateStatefulSetAnnotations(annotations map[string]string) (*appsv1.StatefulSet, error) { - c.logger.Debugf("patching statefulset annotations") - patchData, err := metaAnnotationsPatch(annotations) - if err != nil { - return nil, fmt.Errorf("could not form patch for the statefulset metadata: %v", err) - } - result, err := c.KubeClient.StatefulSets(c.Statefulset.Namespace).Patch( - context.TODO(), - c.Statefulset.Name, - types.MergePatchType, - []byte(patchData), - metav1.PatchOptions{}, - "") - if err != nil { - return nil, fmt.Errorf("could not patch statefulset annotations %q: %v", patchData, err) - } - return result, nil -} - func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error { c.setProcessName("updating statefulset") if c.Statefulset == nil { @@ -197,13 +178,6 @@ func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error { return fmt.Errorf("could not patch statefulset spec %q: %v", statefulSetName, err) } - if newStatefulSet.Annotations != nil { - statefulSet, err = c.updateStatefulSetAnnotations(newStatefulSet.Annotations) - if err != nil { - return err - } - } - c.Statefulset = statefulSet return nil diff --git a/pkg/cluster/sync.go b/pkg/cluster/sync.go index c82e528fd..94e930290 100644 --- a/pkg/cluster/sync.go +++ b/pkg/cluster/sync.go @@ -11,7 +11,6 @@ import ( "github.com/zalando/postgres-operator/pkg/util" "github.com/zalando/postgres-operator/pkg/util/constants" "github.com/zalando/postgres-operator/pkg/util/k8sutil" - appsv1 "k8s.io/api/apps/v1" batchv1beta1 "k8s.io/api/batch/v1beta1" v1 "k8s.io/api/core/v1" policybeta1 "k8s.io/api/policy/v1beta1" @@ -260,28 +259,6 @@ func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error { return nil } -func (c *Cluster) mustUpdatePodsAfterLazyUpdate(desiredSset *appsv1.StatefulSet) (bool, error) { - - pods, err := c.listPods() - if err != nil { - return false, fmt.Errorf("could not list pods of the statefulset: %v", err) - } - - for _, pod := range pods { - - effectivePodImage := pod.Spec.Containers[0].Image - ssImage := desiredSset.Spec.Template.Spec.Containers[0].Image - - if ssImage != effectivePodImage { - c.logger.Infof("not all pods were re-started when the lazy upgrade was enabled; forcing the rolling upgrade now") - return true, nil - } - - } - - return false, nil -} - func (c *Cluster) syncStatefulSet() error { podsToRecreate := make([]v1.Pod, 0) @@ -373,8 +350,6 @@ func (c *Cluster) syncStatefulSet() error { } } - c.updateStatefulSetAnnotations(c.AnnotationsToPropagate(c.annotationsSet(c.Statefulset.Annotations))) - if len(podsToRecreate) == 0 && !c.OpConfig.EnableLazySpiloUpgrade { // even if the desired and the running statefulsets match // there still may be not up-to-date pods on condition @@ -382,8 +357,8 @@ func (c *Cluster) syncStatefulSet() error { // and // (b) some of the pods were not restarted when the lazy update was still in place for _, pod := range pods { - effectivePodImage := pod.Spec.Containers[0].Image - stsImage := desiredSts.Spec.Template.Spec.Containers[0].Image + effectivePodImage := getPostgresContainer(&pod.Spec).Image + stsImage := getPostgresContainer(&desiredSts.Spec.Template.Spec).Image if stsImage != effectivePodImage { if err = c.markRollingUpdateFlagForPod(&pod, "pod not yet restarted due to lazy update"); err != nil { @@ -576,10 +551,29 @@ func (c *Cluster) syncRoles() (err error) { } }() + // mapping between original role name and with deletion suffix + deletedUsers := map[string]string{} + + // create list of database roles to query for _, u := range c.pgUsers { userNames = append(userNames, u.Name) + // add team member role name with rename suffix in case we need to rename it back + if u.Origin == spec.RoleOriginTeamsAPI && c.OpConfig.EnableTeamMemberDeprecation { + deletedUsers[u.Name+c.OpConfig.RoleDeletionSuffix] = u.Name + userNames = append(userNames, u.Name+c.OpConfig.RoleDeletionSuffix) + } } + // add team members that exist only in cache + // to trigger a rename of the role in ProduceSyncRequests + for _, cachedUser := range c.pgUsersCache { + if _, exists := c.pgUsers[cachedUser.Name]; !exists { + userNames = append(userNames, cachedUser.Name) + } + } + + // add pooler user to list of pgUsers, too + // to check if the pooler user exists or has to be created if needMasterConnectionPooler(&c.Spec) || needReplicaConnectionPooler(&c.Spec) { connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName] userNames = append(userNames, connectionPoolerUser.Name) @@ -594,6 +588,16 @@ func (c *Cluster) syncRoles() (err error) { return fmt.Errorf("error getting users from the database: %v", err) } + // update pgUsers where a deleted role was found + // so that they are skipped in ProduceSyncRequests + for _, dbUser := range dbUsers { + if originalUser, exists := deletedUsers[dbUser.Name]; exists { + recreatedUser := c.pgUsers[originalUser] + recreatedUser.Deleted = true + c.pgUsers[originalUser] = recreatedUser + } + } + pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, c.pgUsers) if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil { return fmt.Errorf("error executing sync statements: %v", err) diff --git a/pkg/cluster/sync_test.go b/pkg/cluster/sync_test.go new file mode 100644 index 000000000..e6f23914b --- /dev/null +++ b/pkg/cluster/sync_test.go @@ -0,0 +1,115 @@ +package cluster + +import ( + "testing" + "time" + + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + + "github.com/stretchr/testify/assert" + acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1" + fakeacidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/fake" + "github.com/zalando/postgres-operator/pkg/util/config" + "github.com/zalando/postgres-operator/pkg/util/k8sutil" + "k8s.io/client-go/kubernetes/fake" +) + +func newFakeK8sSyncClient() (k8sutil.KubernetesClient, *fake.Clientset) { + acidClientSet := fakeacidv1.NewSimpleClientset() + clientSet := fake.NewSimpleClientset() + + return k8sutil.KubernetesClient{ + PodsGetter: clientSet.CoreV1(), + PostgresqlsGetter: acidClientSet.AcidV1(), + StatefulSetsGetter: clientSet.AppsV1(), + }, clientSet +} + +func TestSyncStatefulSetsAnnotations(t *testing.T) { + testName := "test syncing statefulsets annotations" + client, _ := newFakeK8sSyncClient() + clusterName := "acid-test-cluster" + namespace := "default" + inheritedAnnotation := "environment" + + pg := acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + Annotations: map[string]string{inheritedAnnotation: "test"}, + }, + Spec: acidv1.PostgresSpec{ + Volume: acidv1.Volume{ + Size: "1Gi", + }, + }, + } + + var cluster = New( + Config{ + OpConfig: config.Config{ + PodManagementPolicy: "ordered_ready", + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: "cluster-name", + DefaultCPURequest: "300m", + DefaultCPULimit: "300m", + DefaultMemoryRequest: "300Mi", + DefaultMemoryLimit: "300Mi", + InheritedAnnotations: []string{inheritedAnnotation}, + PodRoleLabel: "spilo-role", + ResourceCheckInterval: time.Duration(3), + ResourceCheckTimeout: time.Duration(10), + }, + }, + }, client, pg, logger, eventRecorder) + + cluster.Name = clusterName + cluster.Namespace = namespace + + // create a statefulset + _, err := cluster.createStatefulSet() + assert.NoError(t, err) + + // patch statefulset and add annotation + patchData, err := metaAnnotationsPatch(map[string]string{"test-anno": "true"}) + assert.NoError(t, err) + + newSts, err := cluster.KubeClient.StatefulSets(namespace).Patch( + context.TODO(), + clusterName, + types.MergePatchType, + []byte(patchData), + metav1.PatchOptions{}, + "") + assert.NoError(t, err) + + cluster.Statefulset = newSts + + // first compare running with desired statefulset - they should not match + // because no inherited annotations or downscaler annotations are configured + desiredSts, err := cluster.generateStatefulSet(&cluster.Postgresql.Spec) + assert.NoError(t, err) + + cmp := cluster.compareStatefulSetWith(desiredSts) + if cmp.match { + t.Errorf("%s: match between current and desired statefulsets albeit differences: %#v", testName, cmp) + } + + // now sync statefulset - the diff will trigger a replacement of the statefulset + cluster.syncStatefulSet() + + // compare again after the SYNC - must be identical to the desired state + cmp = cluster.compareStatefulSetWith(desiredSts) + if !cmp.match { + t.Errorf("%s: current and desired statefulsets are not matching %#v", testName, cmp) + } + + // check if inherited annotation exists + if _, exists := desiredSts.Annotations[inheritedAnnotation]; !exists { + t.Errorf("%s: inherited annotation not found in desired statefulset: %#v", testName, desiredSts.Annotations) + } +} diff --git a/pkg/cluster/util.go b/pkg/cluster/util.go index fa8a52a1b..4350f9d56 100644 --- a/pkg/cluster/util.go +++ b/pkg/cluster/util.go @@ -227,9 +227,18 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU } } -func (c *Cluster) logVolumeChanges(old, new acidv1.Volume) { - c.logger.Infof("volume specification has been changed") - logNiceDiff(c.logger, old, new) +func getPostgresContainer(podSpec *v1.PodSpec) (pgContainer v1.Container) { + for _, container := range podSpec.Containers { + if container.Name == constants.PostgresContainerName { + pgContainer = container + } + } + + // if no postgres container was found, take the first one in the podSpec + if reflect.DeepEqual(pgContainer, v1.Container{}) && len(podSpec.Containers) > 0 { + pgContainer = podSpec.Containers[0] + } + return pgContainer } func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { @@ -247,13 +256,11 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { for team, membership := range *c.Config.PgTeamMap { if team == teamID { additionalMembers = membership.AdditionalMembers - c.logger.Debugf("found %d additional members for team %q", len(members), teamID) + c.logger.Debugf("found %d additional members for team %q", len(additionalMembers), teamID) } } - for _, member := range additionalMembers { - members = append(members, member) - } + members = append(members, additionalMembers...) } if !c.OpConfig.EnableTeamsAPI { @@ -263,14 +270,12 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) { token, err := c.oauthTokenGetter.getOAuthToken() if err != nil { - c.logger.Warnf("could not get oauth token to authenticate to team service API, only returning %d members for team %q: %v", len(members), teamID, err) - return members, nil + return nil, fmt.Errorf("could not get oauth token to authenticate to team service API: %v", err) } teamInfo, err := c.teamsAPIClient.TeamInfo(teamID, token) if err != nil { - c.logger.Warnf("could not get team info for team %q, only returning %d members: %v", teamID, len(members), err) - return members, nil + return nil, fmt.Errorf("could not get team info for team %q: %v", teamID, err) } for _, member := range teamInfo.Members { @@ -292,12 +297,10 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin pgCRDAnnotations := c.ObjectMeta.Annotations // allow to inherit certain labels from the 'postgres' object - if pgCRDAnnotations != nil { - for k, v := range pgCRDAnnotations { - for _, match := range c.OpConfig.InheritedAnnotations { - if k == match { - annotations[k] = v - } + for k, v := range pgCRDAnnotations { + for _, match := range c.OpConfig.InheritedAnnotations { + if k == match { + annotations[k] = v } } } diff --git a/pkg/cluster/volumes.go b/pkg/cluster/volumes.go index e07d453ec..9a41f5f05 100644 --- a/pkg/cluster/volumes.go +++ b/pkg/cluster/volumes.go @@ -74,10 +74,15 @@ func (c *Cluster) syncVolumes() error { func (c *Cluster) syncUnderlyingEBSVolume() error { c.logger.Infof("starting to sync EBS volumes: type, iops, throughput, and size") - var err error + var ( + err error + newSize resource.Quantity + ) targetValue := c.Spec.Volume - newSize, err := resource.ParseQuantity(targetValue.Size) + if newSize, err = resource.ParseQuantity(targetValue.Size); err != nil { + return fmt.Errorf("could not parse volume size: %v", err) + } targetSize := quantityToGigabyte(newSize) awsGp3 := aws.String("gp3") diff --git a/pkg/cluster/volumes_test.go b/pkg/cluster/volumes_test.go index aea7711af..204ea8aab 100644 --- a/pkg/cluster/volumes_test.go +++ b/pkg/cluster/volumes_test.go @@ -24,6 +24,20 @@ import ( "k8s.io/client-go/kubernetes/fake" ) +type testVolume struct { + size int64 + iops int64 + throughtput int64 + volType string +} + +var testVol = testVolume{ + size: 100, + iops: 300, + throughtput: 125, + volType: "gp2", +} + func newFakeK8sPVCclient() (k8sutil.KubernetesClient, *fake.Clientset) { clientSet := fake.NewSimpleClientset() @@ -189,14 +203,7 @@ func TestMigrateEBS(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) @@ -220,13 +227,6 @@ func TestMigrateEBS(t *testing.T) { cluster.executeEBSMigration() } -type testVolume struct { - iops int64 - throughtput int64 - size int64 - volType string -} - func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustername string, labels labels.Set, volumes []testVolume) { i := 0 for _, v := range volumes { @@ -305,17 +305,7 @@ func TestMigrateGp3Support(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) @@ -371,14 +361,7 @@ func TestManualGp2Gp3Support(t *testing.T) { cluster.Namespace = namespace filterLabels := cluster.labelsSet(false) - testVolumes := []testVolume{ - { - size: 100, - }, - { - size: 100, - }, - } + testVolumes := []testVolume{testVol, testVol} initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes) diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 3c0302cab..761cf1b60 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat result.EtcdHost = fromCRD.EtcdHost result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps - result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-13:2.0-p6") + result.DockerImage = util.Coalesce(fromCRD.DockerImage, "registry.opensource.zalan.do/acid/spilo-13:2.0-p7") result.Workers = util.CoalesceUInt32(fromCRD.Workers, 8) result.MinInstances = fromCRD.MinInstances result.MaxInstances = fromCRD.MaxInstances @@ -152,7 +152,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur // logical backup config result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *") - result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.6.2") + result.LogicalBackupDockerImage = util.Coalesce(fromCRD.LogicalBackup.DockerImage, "registry.opensource.zalan.do/acid/logical-backup:v1.6.3") result.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3") result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region @@ -180,6 +180,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers + result.EnableTeamMemberDeprecation = fromCRD.TeamsAPI.EnableTeamMemberDeprecation + result.RoleDeletionSuffix = util.Coalesce(fromCRD.TeamsAPI.RoleDeletionSuffix, "_deleted") // logging REST API config result.APIPort = util.CoalesceInt(fromCRD.LoggingRESTAPI.APIPort, 8080) diff --git a/pkg/spec/types.go b/pkg/spec/types.go index 78c79e1b3..5d7794b42 100644 --- a/pkg/spec/types.go +++ b/pkg/spec/types.go @@ -42,6 +42,7 @@ const ( PGSyncUserAdd = iota PGsyncUserAlter PGSyncAlterSet // handle ALTER ROLE SET parameter = value + PGSyncUserRename ) // PgUser contains information about a single user. @@ -53,6 +54,7 @@ type PgUser struct { MemberOf []string `yaml:"inrole"` Parameters map[string]string `yaml:"db_parameters"` AdminRole string `yaml:"admin_role"` + Deleted bool `yaml:"deleted"` } func (user *PgUser) Valid() bool { diff --git a/pkg/teams/postgres_team_test.go b/pkg/teams/postgres_team_test.go index f138ec124..dec020c7d 100644 --- a/pkg/teams/postgres_team_test.go +++ b/pkg/teams/postgres_team_test.go @@ -9,8 +9,6 @@ import ( ) var ( - True = true - False = false pgTeamList = acidv1.PostgresTeamList{ TypeMeta: metav1.TypeMeta{ Kind: "List", diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index a5d144051..52530fb12 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -28,8 +28,8 @@ type Resources struct { PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"` PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` - SpiloRunAsUser *int64 `name:"spilo_runasuser,omitempty"` - SpiloRunAsGroup *int64 `name:"spilo_runasgroup,omitempty"` + SpiloRunAsUser *int64 `name:"spilo_runasuser"` + SpiloRunAsGroup *int64 `name:"spilo_runasgroup"` SpiloFSGroup *int64 `name:"spilo_fsgroup"` PodPriorityClassName string `name:"pod_priority_class_name"` ClusterDomain string `name:"cluster_domain" default:"cluster.local"` @@ -114,7 +114,7 @@ type Scalyr struct { // LogicalBackup defines configuration for logical backup type LogicalBackup struct { LogicalBackupSchedule string `name:"logical_backup_schedule" default:"30 00 * * *"` - LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.6.2"` + LogicalBackupDockerImage string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup:v1.6.3"` LogicalBackupProvider string `name:"logical_backup_provider" default:"s3"` LogicalBackupS3Bucket string `name:"logical_backup_s3_bucket" default:""` LogicalBackupS3Region string `name:"logical_backup_s3_region" default:""` @@ -152,7 +152,7 @@ type Config struct { 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:"registry.opensource.zalan.do/acid/spilo-13:2.0-p6"` + DockerImage string `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-13:2.0-p7"` 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"` @@ -176,6 +176,8 @@ type Config struct { EnableTeamsAPI bool `name:"enable_teams_api" default:"true"` EnableTeamSuperuser bool `name:"enable_team_superuser" default:"false"` TeamAdminRole string `name:"team_admin_role" default:"admin"` + RoleDeletionSuffix string `name:"role_deletion_suffix" default:"_deleted"` + EnableTeamMemberDeprecation bool `name:"enable_team_member_deprecation" default:"false"` EnableAdminRoleForUsers bool `name:"enable_admin_role_for_users" default:"true"` EnablePostgresTeamCRD bool `name:"enable_postgres_team_crd" default:"false"` EnablePostgresTeamCRDSuperusers bool `name:"enable_postgres_team_crd_superusers" default:"false"` diff --git a/pkg/util/constants/kubernetes.go b/pkg/util/constants/kubernetes.go index be79687eb..fd2712acc 100644 --- a/pkg/util/constants/kubernetes.go +++ b/pkg/util/constants/kubernetes.go @@ -5,7 +5,6 @@ import "time" // General kubernetes-related constants const ( PostgresContainerName = "postgres" - PostgresContainerIdx = 0 K8sAPIPath = "/apis" QueueResyncPeriodPod = 5 * time.Minute diff --git a/pkg/util/users/users.go b/pkg/util/users/users.go index 231cf2a89..3da933644 100644 --- a/pkg/util/users/users.go +++ b/pkg/util/users/users.go @@ -9,11 +9,13 @@ import ( "github.com/zalando/postgres-operator/pkg/spec" "github.com/zalando/postgres-operator/pkg/util" + "github.com/zalando/postgres-operator/pkg/util/constants" ) const ( createUserSQL = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;` alterUserSQL = `ALTER ROLE "%s" %s` + alterUserRenameSQL = `ALTER ROLE "%s" RENAME TO "%s%s"` alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL` alterRoleSetSQL = `ALTER ROLE "%s" SET %s TO %s` grantToUserSQL = `GRANT %s TO "%s"` @@ -29,6 +31,7 @@ const ( // (except for the NOLOGIN). TODO: process other NOflags, i.e. NOSUPERUSER correctly. type DefaultUserSyncStrategy struct { PasswordEncryption string + RoleDeletionSuffix string } // ProduceSyncRequests figures out the types of changes that need to happen with the given users. @@ -36,8 +39,11 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM newUsers spec.PgUserMap) []spec.PgSyncUserRequest { var reqs []spec.PgSyncUserRequest - // No existing roles are deleted or stripped of role memebership/flags for name, newUser := range newUsers { + // do not create user that exists in DB with deletion suffix + if newUser.Deleted { + continue + } dbUser, exists := dbUsers[name] if !exists { reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser}) @@ -70,6 +76,25 @@ func (strategy DefaultUserSyncStrategy) ProduceSyncRequests(dbUsers spec.PgUserM } } + // No existing roles are deleted or stripped of role membership/flags + // but team roles will be renamed and denied from LOGIN + for name, dbUser := range dbUsers { + if _, exists := newUsers[name]; !exists { + // toggle LOGIN flag based on role deletion + userFlags := make([]string, len(dbUser.Flags)) + userFlags = append(userFlags, dbUser.Flags...) + if dbUser.Deleted { + dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagNoLogin, constants.RoleFlagLogin) + } else { + dbUser.Flags = util.StringSliceReplaceElement(dbUser.Flags, constants.RoleFlagLogin, constants.RoleFlagNoLogin) + } + if !util.IsEqualIgnoreOrder(userFlags, dbUser.Flags) { + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGsyncUserAlter, User: dbUser}) + } + + reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserRename, User: dbUser}) + } + } return reqs } @@ -94,6 +119,11 @@ func (strategy DefaultUserSyncStrategy) ExecuteSyncRequests(requests []spec.PgSy reqretries = append(reqretries, request) errors = append(errors, fmt.Sprintf("could not set custom user %q parameters: %v", request.User.Name, err)) } + case spec.PGSyncUserRename: + if err := strategy.alterPgUserRename(request.User, db); err != nil { + reqretries = append(reqretries, request) + errors = append(errors, fmt.Sprintf("could not rename custom user %q: %v", request.User.Name, err)) + } default: return fmt.Errorf("unrecognized operation: %v", request.Kind) } @@ -124,6 +154,23 @@ func (strategy DefaultUserSyncStrategy) alterPgUserSet(user spec.PgUser, db *sql return nil } +func (strategy DefaultUserSyncStrategy) alterPgUserRename(user spec.PgUser, db *sql.DB) error { + var query string + + // append or trim deletion suffix depending if the user has the suffix or not + if user.Deleted { + newName := strings.TrimSuffix(user.Name, strategy.RoleDeletionSuffix) + query = fmt.Sprintf(alterUserRenameSQL, user.Name, newName, "") + } else { + query = fmt.Sprintf(alterUserRenameSQL, user.Name, user.Name, strategy.RoleDeletionSuffix) + } + + if _, err := db.Exec(query); err != nil { + return err + } + return nil +} + func (strategy DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) error { var userFlags []string var userPassword string diff --git a/pkg/util/util.go b/pkg/util/util.go index bebb9f8da..a52925583 100644 --- a/pkg/util/util.go +++ b/pkg/util/util.go @@ -151,6 +151,18 @@ func IsEqualIgnoreOrder(a, b []string) bool { return reflect.DeepEqual(a_copy, b_copy) } +// SliceReplaceElement +func StringSliceReplaceElement(s []string, a, b string) (result []string) { + tmp := make([]string, 0, len(s)) + for _, str := range s { + if str == a { + str = b + } + tmp = append(tmp, str) + } + return tmp +} + // SubstractStringSlices finds elements in a that are not in b and return them as a result slice. func SubstractStringSlices(a []string, b []string) (result []string, equal bool) { // Slices are assumed to contain unique elements only diff --git a/pkg/util/util_test.go b/pkg/util/util_test.go index c02d2c075..75853c3d6 100644 --- a/pkg/util/util_test.go +++ b/pkg/util/util_test.go @@ -166,6 +166,14 @@ func TestIsEqualIgnoreOrder(t *testing.T) { } } +func TestStringSliceReplaceElement(t *testing.T) { + testSlice := []string{"a", "b", "c"} + testSlice = StringSliceReplaceElement(testSlice, "b", "d") + if !SliceContains(testSlice, "d") { + t.Errorf("testSlide item not replaced: %v", testSlice) + } +} + func TestSubstractSlices(t *testing.T) { for _, tt := range substractTest { actualRes, actualEqual := SubstractStringSlices(tt.inA, tt.inB)