Merge branch 'master' into gh-pages
This commit is contained in:
		
						commit
						edd3fe00b4
					
				| 
						 | 
					@ -9,7 +9,7 @@ assignees: ''
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Please, answer some short questions which should help us to understand your problem / question better?
 | 
					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]
 | 
					- **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]
 | 
					- **Are you running Postgres Operator in production?** [yes | no]
 | 
				
			||||||
- **Type of issue?** [Bug report, question, feature request, etc.]
 | 
					- **Type of issue?** [Bug report, question, feature request, etc.]
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										2
									
								
								Makefile
								
								
								
								
							| 
						 | 
					@ -85,7 +85,7 @@ mocks:
 | 
				
			||||||
	GO111MODULE=on go generate ./...
 | 
						GO111MODULE=on go generate ./...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tools:
 | 
					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 get github.com/golang/mock/mockgen@v1.4.4
 | 
				
			||||||
	GO111MODULE=on go mod tidy
 | 
						GO111MODULE=on go mod tidy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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
 | 
					* Rolling updates on Postgres cluster changes, incl. quick minor version updates
 | 
				
			||||||
* Live volume resize without pod restarts (AWS EBS, PVC)
 | 
					* 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)
 | 
					* Restore and cloning Postgres clusters (incl. major version upgrade)
 | 
				
			||||||
* Additionally logical backups to S3 bucket can be configured
 | 
					* Additionally logical backups to S3 bucket can be configured
 | 
				
			||||||
* Standby cluster from S3 WAL archive
 | 
					* 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
 | 
					* Support for custom TLS certificates
 | 
				
			||||||
* UI to create and edit Postgres cluster manifests
 | 
					* UI to create and edit Postgres cluster manifests
 | 
				
			||||||
* Works well on Amazon AWS, Google Cloud, OpenShift and locally on Kind
 | 
					* 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
 | 
					### PostgreSQL features
 | 
				
			||||||
 | 
					
 | 
				
			||||||
* Supports PostgreSQL 13, starting from 9.5+
 | 
					* Supports PostgreSQL 13, starting from 9.6+
 | 
				
			||||||
* Streaming replication cluster via Patroni
 | 
					* Streaming replication cluster via Patroni
 | 
				
			||||||
* Point-In-Time-Recovery with
 | 
					* Point-In-Time-Recovery with
 | 
				
			||||||
[pg_basebackup](https://www.postgresql.org/docs/11/app-pgbasebackup.html) /
 | 
					[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.
 | 
					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.
 | 
					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`
 | 
					The last Spilo 12 image is: `registry.opensource.zalan.do/acid/spilo-12:1.6-p5`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
name: postgres-operator-ui
 | 
					name: postgres-operator-ui
 | 
				
			||||||
version: 1.6.2
 | 
					version: 1.6.3
 | 
				
			||||||
appVersion: 1.6.2
 | 
					appVersion: 1.6.3
 | 
				
			||||||
home: https://github.com/zalando/postgres-operator
 | 
					home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
					description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
				
			||||||
keywords:
 | 
					keywords:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,31 @@
 | 
				
			||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
entries:
 | 
					entries:
 | 
				
			||||||
  postgres-operator-ui:
 | 
					  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
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.2
 | 
					    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
 | 
					    description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
				
			||||||
    digest: 14d1559bb0bd1e1e828f2daaaa6f6ac9ffc268d79824592c3589b55dd39241f6
 | 
					    digest: 14d1559bb0bd1e1e828f2daaaa6f6ac9ffc268d79824592c3589b55dd39241f6
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -25,7 +47,7 @@ entries:
 | 
				
			||||||
    version: 1.6.2
 | 
					    version: 1.6.2
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.1
 | 
					    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
 | 
					    description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
				
			||||||
    digest: 3d321352f2f1e7bb7450aa8876e3d818aa9f9da9bd4250507386f0490f2c1969
 | 
					    digest: 3d321352f2f1e7bb7450aa8876e3d818aa9f9da9bd4250507386f0490f2c1969
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -47,7 +69,7 @@ entries:
 | 
				
			||||||
    version: 1.6.1
 | 
					    version: 1.6.1
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.0
 | 
					    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
 | 
					    description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
				
			||||||
    digest: 1e0aa1e7db3c1daa96927ffbf6fdbcdb434562f961833cb5241ddbe132220ee4
 | 
					    digest: 1e0aa1e7db3c1daa96927ffbf6fdbcdb434562f961833cb5241ddbe132220ee4
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -69,7 +91,7 @@ entries:
 | 
				
			||||||
    version: 1.6.0
 | 
					    version: 1.6.0
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.5.0
 | 
					    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
 | 
					    description: Postgres Operator UI provides a graphical interface for a convenient database-as-a-service user experience
 | 
				
			||||||
    digest: c91ea39e6d51d57f4048fb1b6ec53b40823f2690eb88e4e4f1a036367b9fdd61
 | 
					    digest: c91ea39e6d51d57f4048fb1b6ec53b40823f2690eb88e4e4f1a036367b9fdd61
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -89,4 +111,4 @@ entries:
 | 
				
			||||||
    urls:
 | 
					    urls:
 | 
				
			||||||
    - postgres-operator-ui-1.5.0.tgz
 | 
					    - postgres-operator-ui-1.5.0.tgz
 | 
				
			||||||
    version: 1.5.0
 | 
					    version: 1.5.0
 | 
				
			||||||
generated: "2021-04-06T16:47:40.991668273+02:00"
 | 
					generated: "2021-05-27T19:04:33.41380858+02:00"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -8,7 +8,7 @@ replicaCount: 1
 | 
				
			||||||
image:
 | 
					image:
 | 
				
			||||||
  registry: registry.opensource.zalan.do
 | 
					  registry: registry.opensource.zalan.do
 | 
				
			||||||
  repository: acid/postgres-operator-ui
 | 
					  repository: acid/postgres-operator-ui
 | 
				
			||||||
  tag: v1.6.2
 | 
					  tag: v1.6.3
 | 
				
			||||||
  pullPolicy: "IfNotPresent"
 | 
					  pullPolicy: "IfNotPresent"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Optionally specify an array of imagePullSecrets.
 | 
					# Optionally specify an array of imagePullSecrets.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
name: postgres-operator
 | 
					name: postgres-operator
 | 
				
			||||||
version: 1.6.2
 | 
					version: 1.6.3
 | 
				
			||||||
appVersion: 1.6.2
 | 
					appVersion: 1.6.3
 | 
				
			||||||
home: https://github.com/zalando/postgres-operator
 | 
					home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
					description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
				
			||||||
keywords:
 | 
					keywords:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -65,7 +65,7 @@ spec:
 | 
				
			||||||
            properties:
 | 
					            properties:
 | 
				
			||||||
              docker_image:
 | 
					              docker_image:
 | 
				
			||||||
                type: string
 | 
					                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:
 | 
					              enable_crd_validation:
 | 
				
			||||||
                type: boolean
 | 
					                type: boolean
 | 
				
			||||||
                default: true
 | 
					                default: true
 | 
				
			||||||
| 
						 | 
					@ -397,7 +397,7 @@ spec:
 | 
				
			||||||
                properties:
 | 
					                properties:
 | 
				
			||||||
                  logical_backup_docker_image:
 | 
					                  logical_backup_docker_image:
 | 
				
			||||||
                    type: string
 | 
					                    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:
 | 
					                  logical_backup_google_application_credentials:
 | 
				
			||||||
                    type: string
 | 
					                    type: string
 | 
				
			||||||
                  logical_backup_job_prefix:
 | 
					                  logical_backup_job_prefix:
 | 
				
			||||||
| 
						 | 
					@ -443,6 +443,9 @@ spec:
 | 
				
			||||||
                  enable_postgres_team_crd_superusers:
 | 
					                  enable_postgres_team_crd_superusers:
 | 
				
			||||||
                    type: boolean
 | 
					                    type: boolean
 | 
				
			||||||
                    default: false
 | 
					                    default: false
 | 
				
			||||||
 | 
					                  enable_team_member_deprecation:
 | 
				
			||||||
 | 
					                    type: boolean
 | 
				
			||||||
 | 
					                    default: false
 | 
				
			||||||
                  enable_team_superuser:
 | 
					                  enable_team_superuser:
 | 
				
			||||||
                    type: boolean
 | 
					                    type: boolean
 | 
				
			||||||
                    default: false
 | 
					                    default: false
 | 
				
			||||||
| 
						 | 
					@ -465,6 +468,9 @@ spec:
 | 
				
			||||||
                      type: string
 | 
					                      type: string
 | 
				
			||||||
                    default:
 | 
					                    default:
 | 
				
			||||||
                    - admin
 | 
					                    - admin
 | 
				
			||||||
 | 
					                  role_deletion_suffix:
 | 
				
			||||||
 | 
					                    type: string
 | 
				
			||||||
 | 
					                    default: "_deleted"
 | 
				
			||||||
                  team_admin_role:
 | 
					                  team_admin_role:
 | 
				
			||||||
                    type: string
 | 
					                    type: string
 | 
				
			||||||
                    default: "admin"
 | 
					                    default: "admin"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -27,7 +27,7 @@ spec:
 | 
				
			||||||
    additionalPrinterColumns:
 | 
					    additionalPrinterColumns:
 | 
				
			||||||
    - name: Team
 | 
					    - name: Team
 | 
				
			||||||
      type: string
 | 
					      type: string
 | 
				
			||||||
      description: Team responsible for Postgres CLuster
 | 
					      description: Team responsible for Postgres cluster
 | 
				
			||||||
      jsonPath: .spec.teamId
 | 
					      jsonPath: .spec.teamId
 | 
				
			||||||
    - name: Version
 | 
					    - name: Version
 | 
				
			||||||
      type: string
 | 
					      type: string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,30 @@
 | 
				
			||||||
apiVersion: v1
 | 
					apiVersion: v1
 | 
				
			||||||
entries:
 | 
					entries:
 | 
				
			||||||
  postgres-operator:
 | 
					  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
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.2
 | 
					    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
 | 
					    description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
				
			||||||
    digest: d886f8a0879ca07d1e5246ee7bc55710e1c872f3977280fe495db6fc2057a7f4
 | 
					    digest: d886f8a0879ca07d1e5246ee7bc55710e1c872f3977280fe495db6fc2057a7f4
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -24,7 +45,7 @@ entries:
 | 
				
			||||||
    version: 1.6.2
 | 
					    version: 1.6.2
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.1
 | 
					    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
 | 
					    description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
				
			||||||
    digest: 4ba5972cd486dcaa2d11c5613a6f97f6b7b831822e610fe9e10a57ea1db23556
 | 
					    digest: 4ba5972cd486dcaa2d11c5613a6f97f6b7b831822e610fe9e10a57ea1db23556
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -45,7 +66,7 @@ entries:
 | 
				
			||||||
    version: 1.6.1
 | 
					    version: 1.6.1
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.6.0
 | 
					    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
 | 
					    description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
				
			||||||
    digest: f52149718ea364f46b4b9eec9a65f6253ad182bb78df541d14cd5277b9c8a8c3
 | 
					    digest: f52149718ea364f46b4b9eec9a65f6253ad182bb78df541d14cd5277b9c8a8c3
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -66,7 +87,7 @@ entries:
 | 
				
			||||||
    version: 1.6.0
 | 
					    version: 1.6.0
 | 
				
			||||||
  - apiVersion: v1
 | 
					  - apiVersion: v1
 | 
				
			||||||
    appVersion: 1.5.0
 | 
					    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
 | 
					    description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
				
			||||||
    digest: 198351d5db52e65cdf383d6f3e1745d91ac1e2a01121f8476f8b1be728b09531
 | 
					    digest: 198351d5db52e65cdf383d6f3e1745d91ac1e2a01121f8476f8b1be728b09531
 | 
				
			||||||
    home: https://github.com/zalando/postgres-operator
 | 
					    home: https://github.com/zalando/postgres-operator
 | 
				
			||||||
| 
						 | 
					@ -85,4 +106,4 @@ entries:
 | 
				
			||||||
    urls:
 | 
					    urls:
 | 
				
			||||||
    - postgres-operator-1.5.0.tgz
 | 
					    - postgres-operator-1.5.0.tgz
 | 
				
			||||||
    version: 1.5.0
 | 
					    version: 1.5.0
 | 
				
			||||||
generated: "2021-03-30T17:00:50.165166707+02:00"
 | 
					generated: "2021-05-27T19:04:25.191897769+02:00"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
image:
 | 
					image:
 | 
				
			||||||
  registry: registry.opensource.zalan.do
 | 
					  registry: registry.opensource.zalan.do
 | 
				
			||||||
  repository: acid/postgres-operator
 | 
					  repository: acid/postgres-operator
 | 
				
			||||||
  tag: v1.6.2
 | 
					  tag: v1.6.3
 | 
				
			||||||
  pullPolicy: "IfNotPresent"
 | 
					  pullPolicy: "IfNotPresent"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Optionally specify an array of imagePullSecrets.
 | 
					# Optionally specify an array of imagePullSecrets.
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ configGeneral:
 | 
				
			||||||
  # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
 | 
					  # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
 | 
				
			||||||
  # kubernetes_use_configmaps: false
 | 
					  # kubernetes_use_configmaps: false
 | 
				
			||||||
  # Spilo docker image
 | 
					  # 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 number of instances in Postgres cluster. -1 = no limit
 | 
				
			||||||
  min_instances: -1
 | 
					  min_instances: -1
 | 
				
			||||||
  # max number of instances in Postgres cluster. -1 = no limit
 | 
					  # max number of instances in Postgres cluster. -1 = no limit
 | 
				
			||||||
| 
						 | 
					@ -263,7 +263,7 @@ configAwsOrGcp:
 | 
				
			||||||
# configure K8s cron job managed by the operator
 | 
					# configure K8s cron job managed by the operator
 | 
				
			||||||
configLogicalBackup:
 | 
					configLogicalBackup:
 | 
				
			||||||
  # image for pods of the logical backup job (example runs pg_dumpall)
 | 
					  # 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
 | 
					  # path of google cloud service account json file
 | 
				
			||||||
  # logical_backup_google_application_credentials: ""
 | 
					  # logical_backup_google_application_credentials: ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -289,13 +289,13 @@ configLogicalBackup:
 | 
				
			||||||
# automate creation of human users with teams API service
 | 
					# automate creation of human users with teams API service
 | 
				
			||||||
configTeamsApi:
 | 
					configTeamsApi:
 | 
				
			||||||
  # team_admin_role will have the rights to grant roles coming from PG manifests
 | 
					  # 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
 | 
					  # operator watches for PostgresTeam CRs to assign additional teams and members to clusters
 | 
				
			||||||
  enable_postgres_team_crd: false
 | 
					  enable_postgres_team_crd: false
 | 
				
			||||||
  # toogle to create additional superuser teams from PostgresTeam CRs
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # List of teams which members need the superuser role in each Postgres cluster
 | 
				
			||||||
  # postgres_superuser_teams:
 | 
					  postgres_superuser_teams:
 | 
				
			||||||
  # - postgres_superusers
 | 
					  - postgres_superusers
 | 
				
			||||||
 | 
					 | 
				
			||||||
  # List of roles that cannot be overwritten by an application, team or infrastructure role
 | 
					  # List of roles that cannot be overwritten by an application, team or infrastructure role
 | 
				
			||||||
  protected_role_names:
 | 
					  protected_role_names:
 | 
				
			||||||
  - admin
 | 
					  - 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
 | 
					  # 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
 | 
					  # postgres config parameters to apply to each team member role
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
image:
 | 
					image:
 | 
				
			||||||
  registry: registry.opensource.zalan.do
 | 
					  registry: registry.opensource.zalan.do
 | 
				
			||||||
  repository: acid/postgres-operator
 | 
					  repository: acid/postgres-operator
 | 
				
			||||||
  tag: v1.6.2
 | 
					  tag: v1.6.3
 | 
				
			||||||
  pullPolicy: "IfNotPresent"
 | 
					  pullPolicy: "IfNotPresent"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Optionally specify an array of imagePullSecrets.
 | 
					# Optionally specify an array of imagePullSecrets.
 | 
				
			||||||
| 
						 | 
					@ -35,7 +35,7 @@ configGeneral:
 | 
				
			||||||
  # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
 | 
					  # Select if setup uses endpoints (default), or configmaps to manage leader (DCS=k8s)
 | 
				
			||||||
  # kubernetes_use_configmaps: "false"
 | 
					  # kubernetes_use_configmaps: "false"
 | 
				
			||||||
  # Spilo docker image
 | 
					  # 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 number of instances in Postgres cluster. -1 = no limit
 | 
				
			||||||
  min_instances: "-1"
 | 
					  min_instances: "-1"
 | 
				
			||||||
  # max number of instances in Postgres cluster. -1 = no limit
 | 
					  # max number of instances in Postgres cluster. -1 = no limit
 | 
				
			||||||
| 
						 | 
					@ -253,7 +253,7 @@ configAwsOrGcp:
 | 
				
			||||||
# configure K8s cron job managed by the operator
 | 
					# configure K8s cron job managed by the operator
 | 
				
			||||||
configLogicalBackup:
 | 
					configLogicalBackup:
 | 
				
			||||||
  # image for pods of the logical backup job (example runs pg_dumpall)
 | 
					  # 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
 | 
					  # path of google cloud service account json file
 | 
				
			||||||
  # logical_backup_google_application_credentials: ""
 | 
					  # logical_backup_google_application_credentials: ""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -280,36 +280,32 @@ configLogicalBackup:
 | 
				
			||||||
# automate creation of human users with teams API service
 | 
					# automate creation of human users with teams API service
 | 
				
			||||||
configTeamsApi:
 | 
					configTeamsApi:
 | 
				
			||||||
  # team_admin_role will have the rights to grant roles coming from PG manifests
 | 
					  # 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
 | 
					  # operator watches for PostgresTeam CRs to assign additional teams and members to clusters
 | 
				
			||||||
  enable_postgres_team_crd: "false"
 | 
					  enable_postgres_team_crd: "false"
 | 
				
			||||||
  # toogle to create additional superuser teams from PostgresTeam CRs
 | 
					  # 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
 | 
					  # 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
 | 
					  # toggles usage of the Teams API by the operator
 | 
				
			||||||
  enable_teams_api: "false"
 | 
					  enable_teams_api: "false"
 | 
				
			||||||
  # should contain a URL to use for authentication (username and token)
 | 
					  # should contain a URL to use for authentication (username and token)
 | 
				
			||||||
  # pam_configuration: https://info.example.com/oauth2/tokeninfo?access_token= uid realm=/employees
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # 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
 | 
					  # URL of the Teams API service
 | 
				
			||||||
  # teams_api_url: http://fake-teams-api.default.svc.cluster.local
 | 
					  # teams_api_url: http://fake-teams-api.default.svc.cluster.local
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -950,7 +950,7 @@ make docker
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# build in image in minikube docker env
 | 
					# build in image in minikube docker env
 | 
				
			||||||
eval $(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
 | 
					# apply UI manifests next to a running Postgres Operator
 | 
				
			||||||
kubectl apply -f manifests/
 | 
					kubectl apply -f manifests/
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -593,7 +593,7 @@ grouped under the `logical_backup` key.
 | 
				
			||||||
  runs `pg_dumpall` on a replica if possible and uploads compressed results to
 | 
					  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`.
 | 
					  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
 | 
					  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**
 | 
					* **logical_backup_google_application_credentials**
 | 
				
			||||||
  Specifies the path of the google cloud service account json file. Default is empty.
 | 
					  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.
 | 
					  cluster to administer Postgres and maintain infrastructure built around it.
 | 
				
			||||||
  The default is empty.
 | 
					  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**
 | 
					* **enable_postgres_team_crd**
 | 
				
			||||||
  toggle to make the operator watch for created or updated `PostgresTeam` CRDs
 | 
					  toggle to make the operator watch for created or updated `PostgresTeam` CRDs
 | 
				
			||||||
  and create roles for specified additional teams and members.
 | 
					  and create roles for specified additional teams and members.
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										17
									
								
								docs/user.md
								
								
								
								
							
							
						
						
									
										17
									
								
								docs/user.md
								
								
								
								
							| 
						 | 
					@ -407,6 +407,23 @@ spec:
 | 
				
			||||||
    - "briggs"
 | 
					    - "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
 | 
					## Prepared databases with roles and default privileges
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The `users` section in the manifests only allows for creating database roles
 | 
					The `users` section in the manifests only allows for creating database roles
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,3 +1,3 @@
 | 
				
			||||||
kubernetes==11.0.0
 | 
					kubernetes==11.0.0
 | 
				
			||||||
timeout_decorator==0.4.1
 | 
					timeout_decorator==0.4.1
 | 
				
			||||||
pyyaml==5.3.1
 | 
					pyyaml==5.4.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -197,7 +197,9 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
        enable_postgres_team_crd = {
 | 
					        enable_postgres_team_crd = {
 | 
				
			||||||
            "data": {
 | 
					            "data": {
 | 
				
			||||||
                "enable_postgres_team_crd": "true",
 | 
					                "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.k8s.update_config(enable_postgres_team_crd)
 | 
				
			||||||
| 
						 | 
					@ -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()
 | 
					        leader = self.k8s.get_cluster_leader_pod()
 | 
				
			||||||
        user_query = """
 | 
					        user_query = """
 | 
				
			||||||
            SELECT usename
 | 
					            SELECT rolname
 | 
				
			||||||
              FROM pg_catalog.pg_user
 | 
					              FROM pg_catalog.pg_roles
 | 
				
			||||||
             WHERE usename IN ('elephant', 'kind');
 | 
					             WHERE rolname IN ('elephant', 'kind');
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        users = self.query_database(leader.metadata.name, "postgres", user_query)
 | 
					        self.eventuallyEqual(lambda: len(self.query_database(leader.metadata.name, "postgres", user_query)), 2,
 | 
				
			||||||
        self.eventuallyEqual(lambda: len(users), 2, 
 | 
					            "Not all additional users found in database", 10, 5)
 | 
				
			||||||
            "Not all additional users found in database: {}".format(users))
 | 
					
 | 
				
			||||||
 | 
					        # 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 config change
 | 
				
			||||||
        revert_resync = {
 | 
					        revert_resync = {
 | 
				
			||||||
| 
						 | 
					@ -407,9 +451,9 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        leader = k8s.get_cluster_leader_pod()
 | 
					        leader = k8s.get_cluster_leader_pod()
 | 
				
			||||||
        schemas_query = """
 | 
					        schemas_query = """
 | 
				
			||||||
            select schema_name
 | 
					            SELECT schema_name
 | 
				
			||||||
            from information_schema.schemata
 | 
					              FROM information_schema.schemata
 | 
				
			||||||
            where schema_name = 'pooler'
 | 
					             WHERE schema_name = 'pooler'
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        db_list = self.list_databases(leader.metadata.name)
 | 
					        db_list = self.list_databases(leader.metadata.name)
 | 
				
			||||||
| 
						 | 
					@ -529,6 +573,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
                            "Parameters": None,
 | 
					                            "Parameters": None,
 | 
				
			||||||
                            "AdminRole": "",
 | 
					                            "AdminRole": "",
 | 
				
			||||||
                            "Origin": 2,
 | 
					                            "Origin": 2,
 | 
				
			||||||
 | 
					                            "Deleted": False
 | 
				
			||||||
                        })
 | 
					                        })
 | 
				
			||||||
                        return True
 | 
					                        return True
 | 
				
			||||||
                except:
 | 
					                except:
 | 
				
			||||||
| 
						 | 
					@ -1417,7 +1462,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
				
			||||||
        k8s = self.k8s
 | 
					        k8s = self.k8s
 | 
				
			||||||
        result_set = []
 | 
					        result_set = []
 | 
				
			||||||
        db_list = []
 | 
					        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 {}"
 | 
					        exec_query = r"psql -tAq -c \"{}\" -d {}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        try:
 | 
					        try:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										11
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										11
									
								
								go.mod
								
								
								
								
							| 
						 | 
					@ -12,11 +12,10 @@ require (
 | 
				
			||||||
	github.com/stretchr/testify v1.6.1
 | 
						github.com/stretchr/testify v1.6.1
 | 
				
			||||||
	golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
 | 
						golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c
 | 
				
			||||||
	golang.org/x/mod v0.4.0 // indirect
 | 
						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
 | 
						gopkg.in/yaml.v2 v2.4.0
 | 
				
			||||||
	k8s.io/api v0.20.2
 | 
						k8s.io/api v0.20.6
 | 
				
			||||||
	k8s.io/apiextensions-apiserver v0.19.4
 | 
						k8s.io/apiextensions-apiserver v0.20.6
 | 
				
			||||||
	k8s.io/apimachinery v0.20.2
 | 
						k8s.io/apimachinery v0.20.6
 | 
				
			||||||
	k8s.io/client-go v0.20.2
 | 
						k8s.io/client-go v0.20.6
 | 
				
			||||||
	k8s.io/code-generator v0.19.4
 | 
						k8s.io/code-generator v0.20.6
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										200
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										200
									
								
								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.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
 | 
				
			||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
 | 
					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.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.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.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
 | 
				
			||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
 | 
					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/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.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/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.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.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
 | 
				
			||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
 | 
					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=
 | 
					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-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 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 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.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/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/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.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/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/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/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/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/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/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/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 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 | 
				
			||||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
					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 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 | 
				
			||||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
					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-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
				
			||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/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-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
				
			||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
					github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
				
			||||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 | 
					github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
 | 
				
			||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
					github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
 | 
				
			||||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
					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/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 h1:lM1G3AF1+7vzFm0n7hfH8r2+750BTo+6Lo6FtPB7kzk=
 | 
				
			||||||
github.com/aws/aws-sdk-go v1.36.29/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
 | 
					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.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
				
			||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 | 
					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/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/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 v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
				
			||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 | 
					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/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/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/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-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.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
				
			||||||
github.com/coreos/go-semver v0.3.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/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/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/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 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
 | 
				
			||||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 | 
					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=
 | 
					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/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 v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
				
			||||||
github.com/ghodss/yaml v1.0.0/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 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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
 | 
				
			||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/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.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 h1:QvGt2nLcHH0WK9orKa+ppBPAxREcH364nPUedEpK0TY=
 | 
				
			||||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
 | 
					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.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 | 
				
			||||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
 | 
					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/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.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
				
			||||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
 | 
					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/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 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
 | 
				
			||||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
 | 
					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.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 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
 | 
				
			||||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
					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/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.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.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
				
			||||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
 | 
					github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
 | 
				
			||||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 | 
					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/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-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
				
			||||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/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/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 h1:DLJCy1n/vrD4HPjOvYcT8aYQXpPIzoRZONaYwyycI+I=
 | 
				
			||||||
github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
 | 
					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 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/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.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-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/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.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
				
			||||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/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.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 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 | 
				
			||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
					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/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 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
				
			||||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
					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=
 | 
					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/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.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/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/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.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/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.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
				
			||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
					github.com/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/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 h1:L8nSXQQzAYByakOFMTwpjRoHsMJklur4Gi59b6VivR8=
 | 
				
			||||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
 | 
					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/magiconair/properties v1.8.1/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/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
					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.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 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
 | 
				
			||||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
					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-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-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/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.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/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-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/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/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=
 | 
					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 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 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 | 
				
			||||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
					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/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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 | 
				
			||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
					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/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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
				
			||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
					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/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.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
 | 
				
			||||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
					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.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.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.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/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 h1:V53xhrbTHrWFWq3gI4b94AjgEJOerO1+1l0xyHOBi8M=
 | 
				
			||||||
github.com/r3labs/diff v1.1.0/go.mod h1:7WjXasNzi0vJetRcB/RqNl5dlIsmXcTTLmF5IoH6Xig=
 | 
					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/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/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/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/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.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
				
			||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
					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.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 | 
				
			||||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
 | 
					github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
 | 
				
			||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
 | 
					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/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/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.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
				
			||||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
					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/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 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/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 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.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
				
			||||||
github.com/spf13/pflag v1.0.3/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 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
				
			||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
					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.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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
				
			||||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
					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.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 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 | 
				
			||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 | 
					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-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/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/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/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.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 | 
				
			||||||
github.com/yuin/goldmark v1.2.1/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.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
				
			||||||
go.etcd.io/bbolt v1.3.3/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/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.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
 | 
				
			||||||
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.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 | 
					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.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
 | 
				
			||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 | 
					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/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
				
			||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
					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-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-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-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-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-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-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-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-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
				
			||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c h1:9HhBz5L/UjnK9XLtiZhYAdue5BVKep3PMmS2LuPDt8k=
 | 
					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-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-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-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-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-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-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-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-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-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-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-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-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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
				
			||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
					golang.org/x/net v0.0.0-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-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-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-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-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 h1:uwuIcX0g4Yl1NC5XAz37xsr2lTtcqevgzYNVt49waME=
 | 
				
			||||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 | 
					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-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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
				
			||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
					golang.org/x/sync v0.0.0-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-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-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-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-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-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-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-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-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-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/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-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-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-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-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 h1:5CtCZbICpIOFdgO940moixOPjc0178IU44m4EjOO5IY=
 | 
				
			||||||
golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
					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/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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-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-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
					golang.org/x/tools v0.0.0-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-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-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-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-20200505023115-26f46d2f7ef8/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20201207204333-a835c872fcea h1:LgKM3cNs8xO6GK1ZVK0nasPn7IN39Sz9EBTwQLyishk=
 | 
					golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
 | 
				
			||||||
golang.org/x/tools v0.0.0-20201207204333-a835c872fcea/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 | 
					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-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-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
				
			||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
					golang.org/x/xerrors v0.0.0-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-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-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-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.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
				
			||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
 | 
					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.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
				
			||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
					google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
				
			||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
 | 
					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/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 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 | 
				
			||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 | 
					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/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/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=
 | 
					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.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-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
 | 
				
			||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
 | 
					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.6 h1:bgdZrW++LqgrLikWYNruIKAtltXbSCX2l5mJu11hrVE=
 | 
				
			||||||
k8s.io/api v0.20.2 h1:y/HR22XDZY3pniu9hIFDLpUCPq2w5eQ6aV/VFQ7uJMw=
 | 
					k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
 | 
				
			||||||
k8s.io/api v0.20.2/go.mod h1:d7n6Ehyzx+S+cE3VhTGfVNNqtGc/oL9DCdYYahlurV8=
 | 
					k8s.io/apiextensions-apiserver v0.20.6 h1:3ZJiXMif7W/32RnfyHN8HygTAZaN7o+FvHxCyuQ7VOo=
 | 
				
			||||||
k8s.io/apiextensions-apiserver v0.19.4 h1:D9ak9T012tb3vcGFWYmbQuj9SCC8YM4zhA4XZqsAQC4=
 | 
					k8s.io/apiextensions-apiserver v0.20.6/go.mod h1:qO8YMqeMmZH+lV21LUNzV41vfpoE9QVAJRA+MNqj0mo=
 | 
				
			||||||
k8s.io/apiextensions-apiserver v0.19.4/go.mod h1:B9rpH/nu4JBCtuUp3zTTk8DEjZUupZTBEec7/2zNRYw=
 | 
					k8s.io/apimachinery v0.20.6 h1:R5p3SlhaABYShQSO6LpPsYHjV05Q+79eBUR0Ut/f4tk=
 | 
				
			||||||
k8s.io/apimachinery v0.19.4/go.mod h1:DnPGDnARWFvYa3pMHgSxtbZb7gpzzAZ1pTfaUNDVlmA=
 | 
					k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
 | 
				
			||||||
k8s.io/apimachinery v0.20.2 h1:hFx6Sbt1oG0n6DZ+g4bFt5f6BoMkOjKWsQFu077M3Vg=
 | 
					k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
 | 
				
			||||||
k8s.io/apimachinery v0.20.2/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 | 
					k8s.io/client-go v0.20.6 h1:nJZOfolnsVtDtbGJNCxzOtKUAu7zvXjB8+pMo9UNxZo=
 | 
				
			||||||
k8s.io/apiserver v0.19.4/go.mod h1:X8WRHCR1UGZDd7HpV0QDc1h/6VbbpAeAGyxSh8yzZXw=
 | 
					k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
 | 
				
			||||||
k8s.io/client-go v0.19.4/go.mod h1:ZrEy7+wj9PjH5VMBCuu/BDlvtUAku0oVFk4MmnW9mWA=
 | 
					k8s.io/code-generator v0.20.6 h1:kp65Y6kF6A4+5PvSNvXWSI5p5vuA9tUxEqEZciPw+7Q=
 | 
				
			||||||
k8s.io/client-go v0.20.2 h1:uuf+iIAbfnCSw8IGAv/Rg0giM+2bOzHLOsbbrwrdhNQ=
 | 
					k8s.io/code-generator v0.20.6/go.mod h1:i6FmG+QxaLxvJsezvZp0q/gAEzzOz3U53KFibghWToU=
 | 
				
			||||||
k8s.io/client-go v0.20.2/go.mod h1:kH5brqWqp7HDxUFKoEgiI4v8G1xzbe9giaCenUWJzgE=
 | 
					k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
 | 
				
			||||||
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/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 | 
					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-20201113003025-83324d819ded h1:JApXBKYyB7l9xx+DK7/+mFjC7A9Bt5A93FPvFD0HIFE=
 | 
				
			||||||
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 | 
					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.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.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 h1:7+X0fUguPyrKEC4WjH8iGDg3laWgMo5tMnRTIGTTxGQ=
 | 
				
			||||||
k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
 | 
					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 h1:sOHNzJIkytDF6qadMNKhhDRpc6ODik8lVC6nOur7B2c=
 | 
				
			||||||
k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
 | 
					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 h1:CbnUZsM497iRC5QMVkHwyl8s2tB3g7yaSHkYPkpgelw=
 | 
				
			||||||
k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
 | 
					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/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/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
 | 
				
			||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
 | 
					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/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
 | 
				
			||||||
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/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
 | 
					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.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 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q=
 | 
				
			||||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 | 
					sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -71,7 +71,7 @@ func addDb(dbName string, dbOwner string, clusterName string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var dbOwnerExists bool
 | 
						var dbOwnerExists bool
 | 
				
			||||||
	dbUsers := postgresql.Spec.Users
 | 
						dbUsers := postgresql.Spec.Users
 | 
				
			||||||
	for key, _ := range dbUsers {
 | 
						for key := range dbUsers {
 | 
				
			||||||
		if key == dbOwner {
 | 
							if key == dbOwner {
 | 
				
			||||||
			dbOwnerExists = true
 | 
								dbOwnerExists = true
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,13 +23,14 @@ THE SOFTWARE.
 | 
				
			||||||
package cmd
 | 
					package cmd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						user "os/user"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes"
 | 
						"k8s.io/client-go/kubernetes"
 | 
				
			||||||
	"k8s.io/client-go/rest"
 | 
						"k8s.io/client-go/rest"
 | 
				
			||||||
	"k8s.io/client-go/tools/remotecommand"
 | 
						"k8s.io/client-go/tools/remotecommand"
 | 
				
			||||||
	"log"
 | 
					 | 
				
			||||||
	"os"
 | 
					 | 
				
			||||||
	user "os/user"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// connectCmd represents the kubectl pg connect command
 | 
					// 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) {
 | 
					func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) {
 | 
				
			||||||
	config := getConfig()
 | 
						config := getConfig()
 | 
				
			||||||
	client, er := kubernetes.NewForConfig(config)
 | 
						client, err := kubernetes.NewForConfig(config)
 | 
				
			||||||
	if er != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(er)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podName := getPodName(clusterName, master, replica)
 | 
						podName := getPodName(clusterName, master, replica)
 | 
				
			||||||
	execRequest := &rest.Request{}
 | 
						var execRequest *rest.Request
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if psql {
 | 
						if psql {
 | 
				
			||||||
		execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
 | 
							execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,9 @@ kubectl pg create -f cluster-manifest.yaml
 | 
				
			||||||
func create(fileName string) {
 | 
					func create(fileName string) {
 | 
				
			||||||
	config := getConfig()
 | 
						config := getConfig()
 | 
				
			||||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
						postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	ymlFile, err := ioutil.ReadFile(fileName)
 | 
						ymlFile, err := ioutil.ReadFile(fileName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							log.Fatal(err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -67,7 +67,7 @@ func extVolume(increasedVolumeSize string, clusterName string) {
 | 
				
			||||||
	namespace := getCurrentNamespace()
 | 
						namespace := getCurrentNamespace()
 | 
				
			||||||
	postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
 | 
						postgresql, err := postgresConfig.Postgresqls(namespace).Get(context.TODO(), clusterName, metav1.GetOptions{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("hii %v", err)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size)
 | 
						oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -31,7 +31,6 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/spf13/cobra"
 | 
						"github.com/spf13/cobra"
 | 
				
			||||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
						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"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	"k8s.io/client-go/kubernetes"
 | 
						"k8s.io/client-go/kubernetes"
 | 
				
			||||||
| 
						 | 
					@ -46,6 +45,9 @@ var scaleCmd = &cobra.Command{
 | 
				
			||||||
Scaling to 0 leads to down time.`,
 | 
					Scaling to 0 leads to down time.`,
 | 
				
			||||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
						Run: func(cmd *cobra.Command, args []string) {
 | 
				
			||||||
		clusterName, err := cmd.Flags().GetString("cluster")
 | 
							clusterName, err := cmd.Flags().GetString("cluster")
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								log.Fatal(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		namespace, err := cmd.Flags().GetString("namespace")
 | 
							namespace, err := cmd.Flags().GetString("namespace")
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			log.Fatal(err)
 | 
								log.Fatal(err)
 | 
				
			||||||
| 
						 | 
					@ -129,8 +131,7 @@ func allowedMinMaxInstances(config *rest.Config) (int32, int32) {
 | 
				
			||||||
		log.Fatal(err)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var operator *v1.Deployment
 | 
						operator := getPostgresOperator(k8sClient)
 | 
				
			||||||
	operator = getPostgresOperator(k8sClient)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	operatorContainer := operator.Spec.Template.Spec.Containers
 | 
						operatorContainer := operator.Spec.Template.Spec.Containers
 | 
				
			||||||
	var configMapName, operatorConfigName string
 | 
						var configMapName, operatorConfigName string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -57,6 +57,9 @@ kubectl pg update -f cluster-manifest.yaml
 | 
				
			||||||
func updatePgResources(fileName string) {
 | 
					func updatePgResources(fileName string) {
 | 
				
			||||||
	config := getConfig()
 | 
						config := getConfig()
 | 
				
			||||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
						postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Fatal(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	ymlFile, err := ioutil.ReadFile(fileName)
 | 
						ymlFile, err := ioutil.ReadFile(fileName)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(err)
 | 
							log.Fatal(err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,9 +99,9 @@ func confirmAction(clusterName string, namespace string) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getPodName(clusterName string, master bool, replicaNumber string) string {
 | 
					func getPodName(clusterName string, master bool, replicaNumber string) string {
 | 
				
			||||||
	config := getConfig()
 | 
						config := getConfig()
 | 
				
			||||||
	client, er := kubernetes.NewForConfig(config)
 | 
						client, err := kubernetes.NewForConfig(config)
 | 
				
			||||||
	if er != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatal(er)
 | 
							log.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
						postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,7 @@ metadata:
 | 
				
			||||||
#    "delete-date": "2020-08-31"  # can only be deleted on that day if "delete-date "key is configured
 | 
					#    "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
 | 
					#    "delete-clustername": "acid-test-cluster"  # can only be deleted when name matches if "delete-clustername" key is configured
 | 
				
			||||||
spec:
 | 
					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"
 | 
					  teamId: "acid"
 | 
				
			||||||
  numberOfInstances: 2
 | 
					  numberOfInstances: 2
 | 
				
			||||||
  users:  # Application/Robot users
 | 
					  users:  # Application/Robot users
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -32,7 +32,7 @@ data:
 | 
				
			||||||
  # default_memory_request: 100Mi
 | 
					  # default_memory_request: 100Mi
 | 
				
			||||||
  # delete_annotation_date_key: delete-date
 | 
					  # delete_annotation_date_key: delete-date
 | 
				
			||||||
  # delete_annotation_name_key: delete-clustername
 | 
					  # 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/*"
 | 
					  # downscaler_annotations: "deployment-time,downscaler/*"
 | 
				
			||||||
  # enable_admin_role_for_users: "true"
 | 
					  # enable_admin_role_for_users: "true"
 | 
				
			||||||
  # enable_crd_validation: "true"
 | 
					  # enable_crd_validation: "true"
 | 
				
			||||||
| 
						 | 
					@ -51,6 +51,7 @@ data:
 | 
				
			||||||
  # enable_shm_volume: "true"
 | 
					  # enable_shm_volume: "true"
 | 
				
			||||||
  # enable_sidecars: "true"
 | 
					  # enable_sidecars: "true"
 | 
				
			||||||
  enable_spilo_wal_path_compat: "true"
 | 
					  enable_spilo_wal_path_compat: "true"
 | 
				
			||||||
 | 
					  enable_team_member_deprecation: "false"
 | 
				
			||||||
  # enable_team_superuser: "false"
 | 
					  # enable_team_superuser: "false"
 | 
				
			||||||
  enable_teams_api: "false"
 | 
					  enable_teams_api: "false"
 | 
				
			||||||
  # etcd_host: ""
 | 
					  # etcd_host: ""
 | 
				
			||||||
| 
						 | 
					@ -63,7 +64,7 @@ data:
 | 
				
			||||||
  # inherited_labels: application,environment
 | 
					  # inherited_labels: application,environment
 | 
				
			||||||
  # kube_iam_role: ""
 | 
					  # kube_iam_role: ""
 | 
				
			||||||
  # log_s3_bucket: ""
 | 
					  # 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_google_application_credentials: ""
 | 
				
			||||||
  logical_backup_job_prefix: "logical-backup-"
 | 
					  logical_backup_job_prefix: "logical-backup-"
 | 
				
			||||||
  logical_backup_provider: "s3"
 | 
					  logical_backup_provider: "s3"
 | 
				
			||||||
| 
						 | 
					@ -111,6 +112,7 @@ data:
 | 
				
			||||||
  resource_check_timeout: 10m
 | 
					  resource_check_timeout: 10m
 | 
				
			||||||
  resync_period: 30m
 | 
					  resync_period: 30m
 | 
				
			||||||
  ring_log_lines: "100"
 | 
					  ring_log_lines: "100"
 | 
				
			||||||
 | 
					  role_deletion_suffix: "_deleted"
 | 
				
			||||||
  secret_name_template: "{username}.{cluster}.credentials"
 | 
					  secret_name_template: "{username}.{cluster}.credentials"
 | 
				
			||||||
  # sidecar_docker_images: ""
 | 
					  # sidecar_docker_images: ""
 | 
				
			||||||
  # set_memory_request_to_limit: "false"
 | 
					  # set_memory_request_to_limit: "false"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -61,7 +61,7 @@ spec:
 | 
				
			||||||
            properties:
 | 
					            properties:
 | 
				
			||||||
              docker_image:
 | 
					              docker_image:
 | 
				
			||||||
                type: string
 | 
					                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:
 | 
					              enable_crd_validation:
 | 
				
			||||||
                type: boolean
 | 
					                type: boolean
 | 
				
			||||||
                default: true
 | 
					                default: true
 | 
				
			||||||
| 
						 | 
					@ -393,7 +393,7 @@ spec:
 | 
				
			||||||
                properties:
 | 
					                properties:
 | 
				
			||||||
                  logical_backup_docker_image:
 | 
					                  logical_backup_docker_image:
 | 
				
			||||||
                    type: string
 | 
					                    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:
 | 
					                  logical_backup_google_application_credentials:
 | 
				
			||||||
                    type: string
 | 
					                    type: string
 | 
				
			||||||
                  logical_backup_job_prefix:
 | 
					                  logical_backup_job_prefix:
 | 
				
			||||||
| 
						 | 
					@ -439,6 +439,9 @@ spec:
 | 
				
			||||||
                  enable_postgres_team_crd_superusers:
 | 
					                  enable_postgres_team_crd_superusers:
 | 
				
			||||||
                    type: boolean
 | 
					                    type: boolean
 | 
				
			||||||
                    default: false
 | 
					                    default: false
 | 
				
			||||||
 | 
					                  enable_team_member_deprecation:
 | 
				
			||||||
 | 
					                    type: boolean
 | 
				
			||||||
 | 
					                    default: false
 | 
				
			||||||
                  enable_team_superuser:
 | 
					                  enable_team_superuser:
 | 
				
			||||||
                    type: boolean
 | 
					                    type: boolean
 | 
				
			||||||
                    default: false
 | 
					                    default: false
 | 
				
			||||||
| 
						 | 
					@ -461,6 +464,9 @@ spec:
 | 
				
			||||||
                      type: string
 | 
					                      type: string
 | 
				
			||||||
                    default:
 | 
					                    default:
 | 
				
			||||||
                    - admin
 | 
					                    - admin
 | 
				
			||||||
 | 
					                  role_deletion_suffix:
 | 
				
			||||||
 | 
					                    type: string
 | 
				
			||||||
 | 
					                    default: "_deleted"
 | 
				
			||||||
                  team_admin_role:
 | 
					                  team_admin_role:
 | 
				
			||||||
                    type: string
 | 
					                    type: string
 | 
				
			||||||
                    default: "admin"
 | 
					                    default: "admin"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,7 +19,7 @@ spec:
 | 
				
			||||||
      serviceAccountName: postgres-operator
 | 
					      serviceAccountName: postgres-operator
 | 
				
			||||||
      containers:
 | 
					      containers:
 | 
				
			||||||
      - name: postgres-operator
 | 
					      - 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
 | 
					        imagePullPolicy: IfNotPresent
 | 
				
			||||||
        resources:
 | 
					        resources:
 | 
				
			||||||
          requests:
 | 
					          requests:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,7 +3,7 @@ kind: OperatorConfiguration
 | 
				
			||||||
metadata:
 | 
					metadata:
 | 
				
			||||||
  name: postgresql-operator-default-configuration
 | 
					  name: postgresql-operator-default-configuration
 | 
				
			||||||
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_crd_validation: true
 | 
				
			||||||
  # enable_lazy_spilo_upgrade: false
 | 
					  # enable_lazy_spilo_upgrade: false
 | 
				
			||||||
  enable_pgversion_env_var: true
 | 
					  enable_pgversion_env_var: true
 | 
				
			||||||
| 
						 | 
					@ -123,7 +123,7 @@ configuration:
 | 
				
			||||||
    # wal_gs_bucket: ""
 | 
					    # wal_gs_bucket: ""
 | 
				
			||||||
    # wal_s3_bucket: ""
 | 
					    # wal_s3_bucket: ""
 | 
				
			||||||
  logical_backup:
 | 
					  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_google_application_credentials: ""
 | 
				
			||||||
    logical_backup_job_prefix: "logical-backup-"
 | 
					    logical_backup_job_prefix: "logical-backup-"
 | 
				
			||||||
    logical_backup_provider: "s3"
 | 
					    logical_backup_provider: "s3"
 | 
				
			||||||
| 
						 | 
					@ -141,6 +141,7 @@ configuration:
 | 
				
			||||||
    # enable_admin_role_for_users: true
 | 
					    # enable_admin_role_for_users: true
 | 
				
			||||||
    # enable_postgres_team_crd: false
 | 
					    # enable_postgres_team_crd: false
 | 
				
			||||||
    # enable_postgres_team_crd_superusers: false
 | 
					    # enable_postgres_team_crd_superusers: false
 | 
				
			||||||
 | 
					    enable_team_member_deprecation: false
 | 
				
			||||||
    enable_team_superuser: false
 | 
					    enable_team_superuser: false
 | 
				
			||||||
    enable_teams_api: false
 | 
					    enable_teams_api: false
 | 
				
			||||||
    # pam_configuration: ""
 | 
					    # pam_configuration: ""
 | 
				
			||||||
| 
						 | 
					@ -149,6 +150,7 @@ configuration:
 | 
				
			||||||
    # - postgres_superusers
 | 
					    # - postgres_superusers
 | 
				
			||||||
    protected_role_names:
 | 
					    protected_role_names:
 | 
				
			||||||
    - admin
 | 
					    - admin
 | 
				
			||||||
 | 
					    role_deletion_suffix: "_deleted"
 | 
				
			||||||
    team_admin_role: admin
 | 
					    team_admin_role: admin
 | 
				
			||||||
    team_api_role_configuration:
 | 
					    team_api_role_configuration:
 | 
				
			||||||
      log_statement: all
 | 
					      log_statement: all
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -23,7 +23,7 @@ spec:
 | 
				
			||||||
    additionalPrinterColumns:
 | 
					    additionalPrinterColumns:
 | 
				
			||||||
    - name: Team
 | 
					    - name: Team
 | 
				
			||||||
      type: string
 | 
					      type: string
 | 
				
			||||||
      description: Team responsible for Postgres CLuster
 | 
					      description: Team responsible for Postgres cluster
 | 
				
			||||||
      jsonPath: .spec.teamId
 | 
					      jsonPath: .spec.teamId
 | 
				
			||||||
    - name: Version
 | 
					    - name: Version
 | 
				
			||||||
      type: string
 | 
					      type: string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1377,6 +1377,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
 | 
				
			||||||
							"enable_postgres_team_crd_superusers": {
 | 
												"enable_postgres_team_crd_superusers": {
 | 
				
			||||||
								Type: "boolean",
 | 
													Type: "boolean",
 | 
				
			||||||
							},
 | 
												},
 | 
				
			||||||
 | 
												"enable_team_member_deprecation": {
 | 
				
			||||||
 | 
													Type: "boolean",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
							"enable_team_superuser": {
 | 
												"enable_team_superuser": {
 | 
				
			||||||
								Type: "boolean",
 | 
													Type: "boolean",
 | 
				
			||||||
							},
 | 
												},
 | 
				
			||||||
| 
						 | 
					@ -1405,6 +1408,9 @@ var OperatorConfigCRDResourceValidation = apiextv1.CustomResourceValidation{
 | 
				
			||||||
									},
 | 
														},
 | 
				
			||||||
								},
 | 
													},
 | 
				
			||||||
							},
 | 
												},
 | 
				
			||||||
 | 
												"role_deletion_suffix": {
 | 
				
			||||||
 | 
													Type: "string",
 | 
				
			||||||
 | 
												},
 | 
				
			||||||
							"team_admin_role": {
 | 
												"team_admin_role": {
 | 
				
			||||||
								Type: "string",
 | 
													Type: "string",
 | 
				
			||||||
							},
 | 
												},
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -159,6 +159,8 @@ type TeamsAPIConfiguration struct {
 | 
				
			||||||
	PostgresSuperuserTeams          []string          `json:"postgres_superuser_teams,omitempty"`
 | 
						PostgresSuperuserTeams          []string          `json:"postgres_superuser_teams,omitempty"`
 | 
				
			||||||
	EnablePostgresTeamCRD           bool              `json:"enable_postgres_team_crd,omitempty"`
 | 
						EnablePostgresTeamCRD           bool              `json:"enable_postgres_team_crd,omitempty"`
 | 
				
			||||||
	EnablePostgresTeamCRDSuperusers bool              `json:"enable_postgres_team_crd_superusers,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
 | 
					// LoggingRESTAPIConfiguration defines Logging API conf
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,6 +74,7 @@ type Cluster struct {
 | 
				
			||||||
	eventRecorder    record.EventRecorder
 | 
						eventRecorder    record.EventRecorder
 | 
				
			||||||
	patroni          patroni.Interface
 | 
						patroni          patroni.Interface
 | 
				
			||||||
	pgUsers          map[string]spec.PgUser
 | 
						pgUsers          map[string]spec.PgUser
 | 
				
			||||||
 | 
						pgUsersCache     map[string]spec.PgUser
 | 
				
			||||||
	systemUsers      map[string]spec.PgUser
 | 
						systemUsers      map[string]spec.PgUser
 | 
				
			||||||
	podSubscribers   map[spec.NamespacedName]chan PodEvent
 | 
						podSubscribers   map[spec.NamespacedName]chan PodEvent
 | 
				
			||||||
	podSubscribersMu sync.RWMutex
 | 
						podSubscribersMu sync.RWMutex
 | 
				
			||||||
| 
						 | 
					@ -129,7 +130,9 @@ func New(cfg Config, kubeClient k8sutil.KubernetesClient, pgSpec acidv1.Postgres
 | 
				
			||||||
			Secrets:   make(map[types.UID]*v1.Secret),
 | 
								Secrets:   make(map[types.UID]*v1.Secret),
 | 
				
			||||||
			Services:  make(map[PostgresRole]*v1.Service),
 | 
								Services:  make(map[PostgresRole]*v1.Service),
 | 
				
			||||||
			Endpoints: make(map[PostgresRole]*v1.Endpoints)},
 | 
								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},
 | 
							deleteOptions:       metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy},
 | 
				
			||||||
		podEventsQueue:      podEventsQueue,
 | 
							podEventsQueue:      podEventsQueue,
 | 
				
			||||||
		KubeClient:          kubeClient,
 | 
							KubeClient:          kubeClient,
 | 
				
			||||||
| 
						 | 
					@ -190,6 +193,17 @@ func (c *Cluster) isNewCluster() bool {
 | 
				
			||||||
func (c *Cluster) initUsers() error {
 | 
					func (c *Cluster) initUsers() error {
 | 
				
			||||||
	c.setProcessName("initializing users")
 | 
						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
 | 
						// clear our the previous state of the cluster users (in case we are
 | 
				
			||||||
	// running a sync).
 | 
						// running a sync).
 | 
				
			||||||
	c.systemUsers = map[string]spec.PgUser{}
 | 
						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) {
 | 
						if !reflect.DeepEqual(c.Statefulset.Annotations, statefulSet.Annotations) {
 | 
				
			||||||
		match = false
 | 
							match = false
 | 
				
			||||||
 | 
							needsReplace = true
 | 
				
			||||||
		reasons = append(reasons, "new statefulset's annotations do not match the current one")
 | 
							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
 | 
						// 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
 | 
						// changing the priority value in an existing class is not supproted
 | 
				
			||||||
	if c.Statefulset.Spec.Template.Spec.PriorityClassName != statefulSet.Spec.Template.Spec.PriorityClassName {
 | 
						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
 | 
						// 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
 | 
						// 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
 | 
							needsReplace = true
 | 
				
			||||||
		reasons = append(reasons, "lazy Spilo update: new statefulset's pod image does not match the current one")
 | 
							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) }),
 | 
								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",
 | 
							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) }),
 | 
								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 {
 | 
						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.
 | 
					// 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 {
 | 
					func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
				
			||||||
	updateFailed := false
 | 
						updateFailed := false
 | 
				
			||||||
	syncStatetfulSet := false
 | 
						syncStatefulSet := false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.mu.Lock()
 | 
						c.mu.Lock()
 | 
				
			||||||
	defer c.mu.Unlock()
 | 
						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()) {
 | 
						if IsBiggerPostgresVersion(oldSpec.Spec.PostgresqlParam.PgVersion, c.GetDesiredMajorVersion()) {
 | 
				
			||||||
		c.logger.Infof("postgresql version increased (%s -> %s), depending on config manual upgrade needed",
 | 
							c.logger.Infof("postgresql version increased (%s -> %s), depending on config manual upgrade needed",
 | 
				
			||||||
			oldSpec.Spec.PostgresqlParam.PgVersion, newSpec.Spec.PostgresqlParam.PgVersion)
 | 
								oldSpec.Spec.PostgresqlParam.PgVersion, newSpec.Spec.PostgresqlParam.PgVersion)
 | 
				
			||||||
		syncStatetfulSet = true
 | 
							syncStatefulSet = true
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		c.logger.Infof("postgresql major version unchanged or smaller, no changes needed")
 | 
							c.logger.Infof("postgresql major version unchanged or smaller, no changes needed")
 | 
				
			||||||
		// sticking with old version, this will also advance GetDesiredVersion next time.
 | 
							// 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) ||
 | 
						needConnectionPooler := needMasterConnectionPoolerWorker(&newSpec.Spec) ||
 | 
				
			||||||
		needReplicaConnectionPoolerWorker(&newSpec.Spec)
 | 
							needReplicaConnectionPoolerWorker(&newSpec.Spec)
 | 
				
			||||||
	if !sameUsers || needConnectionPooler {
 | 
						if !sameUsers || needConnectionPooler {
 | 
				
			||||||
		c.logger.Debugf("syncing secrets")
 | 
							c.logger.Debugf("initialize users")
 | 
				
			||||||
		if err := c.initUsers(); err != nil {
 | 
							if err := c.initUsers(); err != nil {
 | 
				
			||||||
			c.logger.Errorf("could not init users: %v", err)
 | 
								c.logger.Errorf("could not init users: %v", err)
 | 
				
			||||||
			updateFailed = true
 | 
								updateFailed = true
 | 
				
			||||||
| 
						 | 
					@ -688,9 +712,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
				
			||||||
			updateFailed = true
 | 
								updateFailed = true
 | 
				
			||||||
			return
 | 
								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")
 | 
								c.logger.Debugf("syncing statefulsets")
 | 
				
			||||||
			syncStatetfulSet = false
 | 
								syncStatefulSet = false
 | 
				
			||||||
			// TODO: avoid generating the StatefulSet object twice by passing it to syncStatefulSet
 | 
								// TODO: avoid generating the StatefulSet object twice by passing it to syncStatefulSet
 | 
				
			||||||
			if err := c.syncStatefulSet(); err != nil {
 | 
								if err := c.syncStatefulSet(); err != nil {
 | 
				
			||||||
				c.logger.Errorf("could not sync statefulsets: %v", err)
 | 
									c.logger.Errorf("could not sync statefulsets: %v", err)
 | 
				
			||||||
| 
						 | 
					@ -1163,7 +1187,7 @@ func (c *Cluster) initHumanUsers() error {
 | 
				
			||||||
	for _, superuserTeam := range superuserTeams {
 | 
						for _, superuserTeam := range superuserTeams {
 | 
				
			||||||
		err := c.initTeamMembers(superuserTeam, true)
 | 
							err := c.initTeamMembers(superuserTeam, true)
 | 
				
			||||||
		if err != nil {
 | 
							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 {
 | 
							if superuserTeam == c.Spec.TeamID {
 | 
				
			||||||
			clusterIsOwnedBySuperuserTeam = true
 | 
								clusterIsOwnedBySuperuserTeam = true
 | 
				
			||||||
| 
						 | 
					@ -1176,7 +1200,7 @@ func (c *Cluster) initHumanUsers() error {
 | 
				
			||||||
			if !(util.SliceContains(superuserTeams, additionalTeam)) {
 | 
								if !(util.SliceContains(superuserTeams, additionalTeam)) {
 | 
				
			||||||
				err := c.initTeamMembers(additionalTeam, false)
 | 
									err := c.initTeamMembers(additionalTeam, false)
 | 
				
			||||||
				if err != nil {
 | 
									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)
 | 
						err := c.initTeamMembers(c.Spec.TeamID, false)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -420,9 +420,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Clean up the deployment object. If deployment resource we've remembered
 | 
						// Clean up the deployment object. If deployment resource we've remembered
 | 
				
			||||||
	// is somehow empty, try to delete based on what would we generate
 | 
						// 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
 | 
						policy := metav1.DeletePropagationForeground
 | 
				
			||||||
	options := metav1.DeleteOptions{PropagationPolicy: &policy}
 | 
						options := metav1.DeleteOptions{PropagationPolicy: &policy}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -445,8 +443,7 @@ func (c *Cluster) deleteConnectionPooler(role PostgresRole) (err error) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Repeat the same for the service object
 | 
						// Repeat the same for the service object
 | 
				
			||||||
	var service *v1.Service
 | 
						service := c.ConnectionPooler[role].Service
 | 
				
			||||||
	service = c.ConnectionPooler[role].Service
 | 
					 | 
				
			||||||
	if service == nil {
 | 
						if service == nil {
 | 
				
			||||||
		c.logger.Debugf("no connection pooler service object to delete")
 | 
							c.logger.Debugf("no connection pooler service object to delete")
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
| 
						 | 
					@ -566,7 +563,7 @@ func needSyncConnectionPoolerSpecs(oldSpec, newSpec *acidv1.ConnectionPooler, lo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Check if we need to synchronize connection pooler deployment due to new
 | 
					// Check if we need to synchronize connection pooler deployment due to new
 | 
				
			||||||
// defaults, that are different from what we see in the DeploymentSpec
 | 
					// 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{}
 | 
						reasons = []string{}
 | 
				
			||||||
	sync = false
 | 
						sync = false
 | 
				
			||||||
| 
						 | 
					@ -619,14 +616,14 @@ func needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1.ConnectionPoo
 | 
				
			||||||
			ref := env.ValueFrom.SecretKeyRef.LocalObjectReference
 | 
								ref := env.ValueFrom.SecretKeyRef.LocalObjectReference
 | 
				
			||||||
			secretName := Config.OpConfig.SecretNameTemplate.Format(
 | 
								secretName := Config.OpConfig.SecretNameTemplate.Format(
 | 
				
			||||||
				"username", strings.Replace(config.User, "_", "-", -1),
 | 
									"username", strings.Replace(config.User, "_", "-", -1),
 | 
				
			||||||
				"cluster", deployment.ClusterName,
 | 
									"cluster", c.Name,
 | 
				
			||||||
				"tprkind", acidv1.PostgresCRDResourceKind,
 | 
									"tprkind", acidv1.PostgresCRDResourceKind,
 | 
				
			||||||
				"tprgroup", acidzalando.GroupName)
 | 
									"tprgroup", acidzalando.GroupName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			if ref.Name != secretName {
 | 
								if ref.Name != secretName {
 | 
				
			||||||
				sync = true
 | 
									sync = true
 | 
				
			||||||
				msg := fmt.Sprintf("pooler user is different (having %s, required %s)",
 | 
									msg := fmt.Sprintf("pooler user and secret are different (having %s, required %s)",
 | 
				
			||||||
					ref.Name, config.User)
 | 
										ref.Name, secretName)
 | 
				
			||||||
				reasons = append(reasons, msg)
 | 
									reasons = append(reasons, msg)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -747,7 +744,7 @@ func (c *Cluster) syncConnectionPooler(oldSpec, newSpec *acidv1.Postgresql, Look
 | 
				
			||||||
				Deployment:     nil,
 | 
									Deployment:     nil,
 | 
				
			||||||
				Service:        nil,
 | 
									Service:        nil,
 | 
				
			||||||
				Name:           c.connectionPoolerName(role),
 | 
									Name:           c.connectionPoolerName(role),
 | 
				
			||||||
				ClusterName:    c.ClusterName,
 | 
									ClusterName:    c.Name,
 | 
				
			||||||
				Namespace:      c.Namespace,
 | 
									Namespace:      c.Namespace,
 | 
				
			||||||
				LookupFunction: false,
 | 
									LookupFunction: false,
 | 
				
			||||||
				Role:           role,
 | 
									Role:           role,
 | 
				
			||||||
| 
						 | 
					@ -878,7 +875,7 @@ func (c *Cluster) syncConnectionPoolerWorker(oldSpec, newSpec *acidv1.Postgresql
 | 
				
			||||||
			specSync, specReason = needSyncConnectionPoolerSpecs(oldConnectionPooler, newConnectionPooler, c.logger)
 | 
								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...)
 | 
							reason := append(specReason, defaultsReason...)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if specSync || defaultsSync {
 | 
							if specSync || defaultsSync {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -198,6 +198,7 @@ func (c *Cluster) readPgUsersFromDatabase(userNames []string) (users spec.PgUser
 | 
				
			||||||
			rolname, rolpassword                                          string
 | 
								rolname, rolpassword                                          string
 | 
				
			||||||
			rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin bool
 | 
								rolsuper, rolinherit, rolcreaterole, rolcreatedb, rolcanlogin bool
 | 
				
			||||||
			roloptions, memberof                                          []string
 | 
								roloptions, memberof                                          []string
 | 
				
			||||||
 | 
								roldeleted                                                    bool
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
		err := rows.Scan(&rolname, &rolpassword, &rolsuper, &rolinherit,
 | 
							err := rows.Scan(&rolname, &rolpassword, &rolsuper, &rolinherit,
 | 
				
			||||||
			&rolcreaterole, &rolcreatedb, &rolcanlogin, pq.Array(&roloptions), pq.Array(&memberof))
 | 
								&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]
 | 
								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
 | 
						return users, nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -66,10 +66,6 @@ type spiloConfiguration struct {
 | 
				
			||||||
	Bootstrap            pgBootstrap            `json:"bootstrap"`
 | 
						Bootstrap            pgBootstrap            `json:"bootstrap"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) containerName() string {
 | 
					 | 
				
			||||||
	return "postgres"
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (c *Cluster) statefulSetName() string {
 | 
					func (c *Cluster) statefulSetName() string {
 | 
				
			||||||
	return c.Name
 | 
						return c.Name
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -213,10 +209,10 @@ PatroniInitDBParams:
 | 
				
			||||||
	for _, k := range initdbOptionNames {
 | 
						for _, k := range initdbOptionNames {
 | 
				
			||||||
		v := patroni.InitDB[k]
 | 
							v := patroni.InitDB[k]
 | 
				
			||||||
		for i, defaultParam := range config.Bootstrap.Initdb {
 | 
							for i, defaultParam := range config.Bootstrap.Initdb {
 | 
				
			||||||
			switch defaultParam.(type) {
 | 
								switch t := defaultParam.(type) {
 | 
				
			||||||
			case map[string]string:
 | 
								case map[string]string:
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					for k1 := range defaultParam.(map[string]string) {
 | 
										for k1 := range t {
 | 
				
			||||||
						if k1 == k {
 | 
											if k1 == k {
 | 
				
			||||||
							(config.Bootstrap.Initdb[i]).(map[string]string)[k] = v
 | 
												(config.Bootstrap.Initdb[i]).(map[string]string)[k] = v
 | 
				
			||||||
							continue PatroniInitDBParams
 | 
												continue PatroniInitDBParams
 | 
				
			||||||
| 
						 | 
					@ -226,7 +222,7 @@ PatroniInitDBParams:
 | 
				
			||||||
			case string:
 | 
								case string:
 | 
				
			||||||
				{
 | 
									{
 | 
				
			||||||
					/* if the option already occurs in the list */
 | 
										/* if the option already occurs in the list */
 | 
				
			||||||
					if defaultParam.(string) == v {
 | 
										if t == v {
 | 
				
			||||||
						continue PatroniInitDBParams
 | 
											continue PatroniInitDBParams
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
| 
						 | 
					@ -264,7 +260,7 @@ PatroniInitDBParams:
 | 
				
			||||||
	if patroni.SynchronousMode {
 | 
						if patroni.SynchronousMode {
 | 
				
			||||||
		config.Bootstrap.DCS.SynchronousMode = patroni.SynchronousMode
 | 
							config.Bootstrap.DCS.SynchronousMode = patroni.SynchronousMode
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if patroni.SynchronousModeStrict != false {
 | 
						if patroni.SynchronousModeStrict {
 | 
				
			||||||
		config.Bootstrap.DCS.SynchronousModeStrict = 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 {
 | 
						if len(nodeReadinessLabel) == 0 && nodeAffinity == nil {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	nodeAffinityCopy := *&v1.NodeAffinity{}
 | 
						nodeAffinityCopy := v1.NodeAffinity{}
 | 
				
			||||||
	if nodeAffinity != nil {
 | 
						if nodeAffinity != nil {
 | 
				
			||||||
		nodeAffinityCopy = *nodeAffinity.DeepCopy()
 | 
							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("Generating Spilo container, environment variables")
 | 
				
			||||||
	c.logger.Debugf("%v", spiloEnvVars)
 | 
						c.logger.Debugf("%v", spiloEnvVars)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spiloContainer := generateContainer(c.containerName(),
 | 
						spiloContainer := generateContainer(constants.PostgresContainerName,
 | 
				
			||||||
		&effectiveDockerImage,
 | 
							&effectiveDockerImage,
 | 
				
			||||||
		resourceRequirements,
 | 
							resourceRequirements,
 | 
				
			||||||
		deduplicateEnvVars(spiloEnvVars, c.containerName(), c.logger),
 | 
							deduplicateEnvVars(spiloEnvVars, constants.PostgresContainerName, c.logger),
 | 
				
			||||||
		volumeMounts,
 | 
							volumeMounts,
 | 
				
			||||||
		c.OpConfig.Resources.SpiloPrivileged,
 | 
							c.OpConfig.Resources.SpiloPrivileged,
 | 
				
			||||||
		c.OpConfig.Resources.SpiloAllowPrivilegeEscalation,
 | 
							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)
 | 
							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{
 | 
						statefulSet := &appsv1.StatefulSet{
 | 
				
			||||||
		ObjectMeta: metav1.ObjectMeta{
 | 
							ObjectMeta: metav1.ObjectMeta{
 | 
				
			||||||
			Name:        c.statefulSetName(),
 | 
								Name:        c.statefulSetName(),
 | 
				
			||||||
			Namespace:   c.Namespace,
 | 
								Namespace:   c.Namespace,
 | 
				
			||||||
			Labels:      c.labelsSet(true),
 | 
								Labels:      c.labelsSet(true),
 | 
				
			||||||
			Annotations: stsAnnotations,
 | 
								Annotations: c.AnnotationsToPropagate(c.annotationsSet(nil)),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		Spec: appsv1.StatefulSetSpec{
 | 
							Spec: appsv1.StatefulSetSpec{
 | 
				
			||||||
			Replicas:             &numberOfInstances,
 | 
								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
 | 
					// see https://docs.okd.io/latest/dev_guide/shared_memory.html
 | 
				
			||||||
func addShmVolume(podSpec *v1.PodSpec) {
 | 
					func addShmVolume(podSpec *v1.PodSpec) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						postgresContainerIdx := 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	volumes := append(podSpec.Volumes, v1.Volume{
 | 
						volumes := append(podSpec.Volumes, v1.Volume{
 | 
				
			||||||
		Name: constants.ShmVolumeName,
 | 
							Name: constants.ShmVolumeName,
 | 
				
			||||||
		VolumeSource: v1.VolumeSource{
 | 
							VolumeSource: v1.VolumeSource{
 | 
				
			||||||
| 
						 | 
					@ -1404,14 +1400,20 @@ func addShmVolume(podSpec *v1.PodSpec) {
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pgIdx := constants.PostgresContainerIdx
 | 
						for i, container := range podSpec.Containers {
 | 
				
			||||||
	mounts := append(podSpec.Containers[pgIdx].VolumeMounts,
 | 
							if container.Name == constants.PostgresContainerName {
 | 
				
			||||||
 | 
								postgresContainerIdx = i
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mounts := append(podSpec.Containers[postgresContainerIdx].VolumeMounts,
 | 
				
			||||||
		v1.VolumeMount{
 | 
							v1.VolumeMount{
 | 
				
			||||||
			Name:      constants.ShmVolumeName,
 | 
								Name:      constants.ShmVolumeName,
 | 
				
			||||||
			MountPath: constants.ShmVolumePath,
 | 
								MountPath: constants.ShmVolumePath,
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podSpec.Containers[0].VolumeMounts = mounts
 | 
						podSpec.Containers[postgresContainerIdx].VolumeMounts = mounts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podSpec.Volumes = volumes
 | 
						podSpec.Volumes = volumes
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1442,54 +1444,55 @@ func (c *Cluster) addAdditionalVolumes(podSpec *v1.PodSpec,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	volumes := podSpec.Volumes
 | 
						volumes := podSpec.Volumes
 | 
				
			||||||
	mountPaths := map[string]acidv1.AdditionalVolume{}
 | 
						mountPaths := map[string]acidv1.AdditionalVolume{}
 | 
				
			||||||
	for i, v := range additionalVolumes {
 | 
						for i, additionalVolume := range additionalVolumes {
 | 
				
			||||||
		if previousVolume, exist := mountPaths[v.MountPath]; exist {
 | 
							if previousVolume, exist := mountPaths[additionalVolume.MountPath]; exist {
 | 
				
			||||||
			msg := "Volume %+v cannot be mounted to the same path as %+v"
 | 
								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
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if v.MountPath == constants.PostgresDataMount {
 | 
							if additionalVolume.MountPath == constants.PostgresDataMount {
 | 
				
			||||||
			msg := "Cannot mount volume on postgresql data directory, %+v"
 | 
								msg := "Cannot mount volume on postgresql data directory, %+v"
 | 
				
			||||||
			c.logger.Warningf(msg, v)
 | 
								c.logger.Warningf(msg, additionalVolume)
 | 
				
			||||||
			continue
 | 
								continue
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if v.TargetContainers == nil {
 | 
							// if no target container is defined assign it to postgres container
 | 
				
			||||||
			spiloContainer := podSpec.Containers[0]
 | 
							if len(additionalVolume.TargetContainers) == 0 {
 | 
				
			||||||
			additionalVolumes[i].TargetContainers = []string{spiloContainer.Name}
 | 
								postgresContainer := getPostgresContainer(podSpec)
 | 
				
			||||||
 | 
								additionalVolumes[i].TargetContainers = []string{postgresContainer.Name}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for _, target := range v.TargetContainers {
 | 
							for _, target := range additionalVolume.TargetContainers {
 | 
				
			||||||
			if target == "all" && len(v.TargetContainers) != 1 {
 | 
								if target == "all" && len(additionalVolume.TargetContainers) != 1 {
 | 
				
			||||||
				msg := `Target containers could be either "all" or a list
 | 
									msg := `Target containers could be either "all" or a list
 | 
				
			||||||
						of containers, mixing those is not allowed, %+v`
 | 
											of containers, mixing those is not allowed, %+v`
 | 
				
			||||||
				c.logger.Warningf(msg, v)
 | 
									c.logger.Warningf(msg, additionalVolume)
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		volumes = append(volumes,
 | 
							volumes = append(volumes,
 | 
				
			||||||
			v1.Volume{
 | 
								v1.Volume{
 | 
				
			||||||
				Name:         v.Name,
 | 
									Name:         additionalVolume.Name,
 | 
				
			||||||
				VolumeSource: v.VolumeSource,
 | 
									VolumeSource: additionalVolume.VolumeSource,
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		)
 | 
							)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		mountPaths[v.MountPath] = v
 | 
							mountPaths[additionalVolume.MountPath] = additionalVolume
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	c.logger.Infof("Mount additional volumes: %+v", additionalVolumes)
 | 
						c.logger.Infof("Mount additional volumes: %+v", additionalVolumes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i := range podSpec.Containers {
 | 
						for i := range podSpec.Containers {
 | 
				
			||||||
		mounts := podSpec.Containers[i].VolumeMounts
 | 
							mounts := podSpec.Containers[i].VolumeMounts
 | 
				
			||||||
		for _, v := range additionalVolumes {
 | 
							for _, additionalVolume := range additionalVolumes {
 | 
				
			||||||
			for _, target := range v.TargetContainers {
 | 
								for _, target := range additionalVolume.TargetContainers {
 | 
				
			||||||
				if podSpec.Containers[i].Name == target || target == "all" {
 | 
									if podSpec.Containers[i].Name == target || target == "all" {
 | 
				
			||||||
					mounts = append(mounts, v1.VolumeMount{
 | 
										mounts = append(mounts, v1.VolumeMount{
 | 
				
			||||||
						Name:      v.Name,
 | 
											Name:      additionalVolume.Name,
 | 
				
			||||||
						MountPath: v.MountPath,
 | 
											MountPath: additionalVolume.MountPath,
 | 
				
			||||||
						SubPath:   v.SubPath,
 | 
											SubPath:   additionalVolume.SubPath,
 | 
				
			||||||
					})
 | 
										})
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,6 +11,7 @@ import (
 | 
				
			||||||
	"github.com/stretchr/testify/assert"
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
						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/spec"
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util"
 | 
						"github.com/zalando/postgres-operator/pkg/util"
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util/config"
 | 
						"github.com/zalando/postgres-operator/pkg/util/config"
 | 
				
			||||||
| 
						 | 
					@ -24,9 +25,21 @@ import (
 | 
				
			||||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
						metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/types"
 | 
						"k8s.io/apimachinery/pkg/types"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/util/intstr"
 | 
						"k8s.io/apimachinery/pkg/util/intstr"
 | 
				
			||||||
 | 
						"k8s.io/client-go/kubernetes/fake"
 | 
				
			||||||
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
						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
 | 
					// For testing purposes
 | 
				
			||||||
type ExpectedValue struct {
 | 
					type ExpectedValue struct {
 | 
				
			||||||
	envIndex       int
 | 
						envIndex       int
 | 
				
			||||||
| 
						 | 
					@ -412,6 +425,7 @@ func TestShmVolume(t *testing.T) {
 | 
				
			||||||
				Volumes: []v1.Volume{{}},
 | 
									Volumes: []v1.Volume{{}},
 | 
				
			||||||
				Containers: []v1.Container{
 | 
									Containers: []v1.Container{
 | 
				
			||||||
					{
 | 
										{
 | 
				
			||||||
 | 
											Name: "postgres",
 | 
				
			||||||
						VolumeMounts: []v1.VolumeMount{
 | 
											VolumeMounts: []v1.VolumeMount{
 | 
				
			||||||
							{},
 | 
												{},
 | 
				
			||||||
						},
 | 
											},
 | 
				
			||||||
| 
						 | 
					@ -423,9 +437,10 @@ func TestShmVolume(t *testing.T) {
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		addShmVolume(tt.podSpec)
 | 
							addShmVolume(tt.podSpec)
 | 
				
			||||||
 | 
							postgresContainer := getPostgresContainer(tt.podSpec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		volumeName := tt.podSpec.Volumes[tt.shmPos].Name
 | 
							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 {
 | 
							if volumeName != constants.ShmVolumeName {
 | 
				
			||||||
			t.Errorf("%s %s: Expected volume %s was not created, have %s instead",
 | 
								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 {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		additionalSecretMount := "aws-iam-s3-role"
 | 
							additionalSecretMount := "aws-iam-s3-role"
 | 
				
			||||||
		additionalSecretMountPath := "/meta/credentials"
 | 
							additionalSecretMountPath := "/meta/credentials"
 | 
				
			||||||
 | 
							postgresContainer := getPostgresContainer(tt.podSpec)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		numMounts := len(tt.podSpec.Containers[0].VolumeMounts)
 | 
							numMounts := len(postgresContainer.VolumeMounts)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		addSecretVolume(tt.podSpec, additionalSecretMount, additionalSecretMountPath)
 | 
							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 {
 | 
							if numMountsCheck != numMounts+1 {
 | 
				
			||||||
			t.Errorf("Unexpected number of VolumeMounts: got %v instead of %v",
 | 
								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,
 | 
							"CONNECTION_POOLER_PORT": false,
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	envs := podSpec.Spec.Containers[0].Env
 | 
						container := getPostgresContainer(&podSpec.Spec)
 | 
				
			||||||
 | 
						envs := container.Env
 | 
				
			||||||
	for _, env := range envs {
 | 
						for _, env := range envs {
 | 
				
			||||||
		required[env.Name] = true
 | 
							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")
 | 
						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 {
 | 
					func testDeploymentOwnerReference(cluster *Cluster, deployment *appsv1.Deployment) error {
 | 
				
			||||||
	owner := deployment.ObjectMeta.OwnerReferences[0]
 | 
						owner := deployment.ObjectMeta.OwnerReferences[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -962,16 +971,23 @@ func testServiceOwnerReference(cluster *Cluster, service *v1.Service, role Postg
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestTLS(t *testing.T) {
 | 
					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 {
 | 
						client, _ := newFakeK8sTestClient()
 | 
				
			||||||
		return acidv1.PostgresSpec{
 | 
						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,
 | 
								TeamID: "myapp", NumberOfInstances: 1,
 | 
				
			||||||
			Resources: acidv1.Resources{
 | 
								Resources: acidv1.Resources{
 | 
				
			||||||
				ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"},
 | 
									ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"},
 | 
				
			||||||
| 
						 | 
					@ -980,11 +996,24 @@ func TestTLS(t *testing.T) {
 | 
				
			||||||
			Volume: acidv1.Volume{
 | 
								Volume: acidv1.Volume{
 | 
				
			||||||
				Size: "1G",
 | 
									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{
 | 
							Config{
 | 
				
			||||||
			OpConfig: config.Config{
 | 
								OpConfig: config.Config{
 | 
				
			||||||
				PodManagementPolicy: "ordered_ready",
 | 
									PodManagementPolicy: "ordered_ready",
 | 
				
			||||||
| 
						 | 
					@ -999,28 +1028,14 @@ func TestTLS(t *testing.T) {
 | 
				
			||||||
					SpiloFSGroup:    &spiloFSGroup,
 | 
										SpiloFSGroup:    &spiloFSGroup,
 | 
				
			||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
		}, k8sutil.KubernetesClient{}, acidv1.Postgresql{}, logger, eventRecorder)
 | 
							}, client, pg, logger, eventRecorder)
 | 
				
			||||||
	spec = makeSpec(acidv1.TLSDescription{SecretName: "my-secret", CAFile: "ca.crt"})
 | 
					
 | 
				
			||||||
	s, err := cluster.generateStatefulSet(&spec)
 | 
						// create a statefulset
 | 
				
			||||||
	if err != nil {
 | 
						sts, err := cluster.createStatefulSet()
 | 
				
			||||||
	assert.NoError(t, err)
 | 
						assert.NoError(t, err)
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fsGroup := int64(103)
 | 
						fsGroup := int64(103)
 | 
				
			||||||
	assert.Equal(t, &fsGroup, s.Spec.Template.Spec.SecurityContext.FSGroup, "has a default FSGroup assigned")
 | 
						assert.Equal(t, &fsGroup, sts.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,
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	})
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	volume := v1.Volume{
 | 
						volume := v1.Volume{
 | 
				
			||||||
		Name: "my-secret",
 | 
							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",
 | 
							MountPath: "/tls",
 | 
				
			||||||
		Name:      "my-secret",
 | 
							Name:      "my-secret",
 | 
				
			||||||
	}, "the volume gets mounted in /tls")
 | 
						}, "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, postgresContainer.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, postgresContainer.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_CA_FILE", Value: "/tls/ca.crt"})
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func TestAdditionalVolume(t *testing.T) {
 | 
					func TestAdditionalVolume(t *testing.T) {
 | 
				
			||||||
	testName := "TestAdditionalVolume"
 | 
						testName := "TestAdditionalVolume"
 | 
				
			||||||
	tests := []struct {
 | 
					
 | 
				
			||||||
		subTest   string
 | 
						client, _ := newFakeK8sTestClient()
 | 
				
			||||||
		podSpec   *v1.PodSpec
 | 
						clusterName := "acid-test-cluster"
 | 
				
			||||||
		volumePos int
 | 
						namespace := "default"
 | 
				
			||||||
	}{
 | 
						sidecarName := "sidecar"
 | 
				
			||||||
 | 
						additionalVolumes := []acidv1.AdditionalVolume{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			subTest: "empty PodSpec",
 | 
								Name:             "test1",
 | 
				
			||||||
			podSpec: &v1.PodSpec{
 | 
								MountPath:        "/test1",
 | 
				
			||||||
				Volumes: []v1.Volume{},
 | 
								TargetContainers: []string{"all"},
 | 
				
			||||||
				Containers: []v1.Container{
 | 
								VolumeSource: v1.VolumeSource{
 | 
				
			||||||
					{
 | 
									EmptyDir: &v1.EmptyDirVolumeSource{},
 | 
				
			||||||
						VolumeMounts: []v1.VolumeMount{},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			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",
 | 
					 | 
				
			||||||
							},
 | 
					 | 
				
			||||||
						},
 | 
					 | 
				
			||||||
					},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			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",
 | 
								Name:             "test2",
 | 
				
			||||||
						VolumeMounts: []v1.VolumeMount{
 | 
								MountPath:        "/test2",
 | 
				
			||||||
 | 
								TargetContainers: []string{sidecarName},
 | 
				
			||||||
 | 
								VolumeSource: v1.VolumeSource{
 | 
				
			||||||
 | 
									EmptyDir: &v1.EmptyDirVolumeSource{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
								Name:      "data",
 | 
								Name:             "test3",
 | 
				
			||||||
								ReadOnly:  false,
 | 
								MountPath:        "/test3",
 | 
				
			||||||
								MountPath: "/data",
 | 
								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(
 | 
						var cluster = New(
 | 
				
			||||||
		Config{
 | 
							Config{
 | 
				
			||||||
			OpConfig: config.Config{
 | 
								OpConfig: config.Config{
 | 
				
			||||||
				ProtectedRoles: []string{"admin"},
 | 
									PodManagementPolicy: "ordered_ready",
 | 
				
			||||||
				Auth: config.Auth{
 | 
									Resources: config.Resources{
 | 
				
			||||||
					SuperUsername:       superUserName,
 | 
										ClusterLabels:        map[string]string{"application": "spilo"},
 | 
				
			||||||
					ReplicationUsername: replicationUserName,
 | 
										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 {
 | 
						// create a statefulset
 | 
				
			||||||
		// Test with additional volume mounted in all containers
 | 
						sts, err := cluster.createStatefulSet()
 | 
				
			||||||
		additionalVolumeMount := []acidv1.AdditionalVolume{
 | 
						assert.NoError(t, err)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tests := []struct {
 | 
				
			||||||
 | 
							subTest        string
 | 
				
			||||||
 | 
							container      string
 | 
				
			||||||
 | 
							expectedMounts []string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
				Name:             "test",
 | 
								subTest:        "checking volume mounts of postgres container",
 | 
				
			||||||
				MountPath:        "/test",
 | 
								container:      constants.PostgresContainerName,
 | 
				
			||||||
				TargetContainers: []string{"all"},
 | 
								expectedMounts: []string{"pgdata", "test1", "test3", "test4"},
 | 
				
			||||||
				VolumeSource: v1.VolumeSource{
 | 
					 | 
				
			||||||
					EmptyDir: &v1.EmptyDirVolumeSource{},
 | 
					 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								subTest:        "checking volume mounts of sidecar container",
 | 
				
			||||||
 | 
								container:      "sidecar",
 | 
				
			||||||
 | 
								expectedMounts: []string{"pgdata", "test1", "test2"},
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		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)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for _, tt := range tests {
 | 
						for _, tt := range tests {
 | 
				
			||||||
		// Test with additional volume mounted only in first container
 | 
							for _, container := range sts.Spec.Template.Spec.Containers {
 | 
				
			||||||
		additionalVolumeMount := []acidv1.AdditionalVolume{
 | 
								if container.Name != tt.container {
 | 
				
			||||||
			{
 | 
									continue
 | 
				
			||||||
				Name:             "test",
 | 
								}
 | 
				
			||||||
				MountPath:        "/test",
 | 
								mounts := []string{}
 | 
				
			||||||
				TargetContainers: []string{"postgres"},
 | 
								for _, volumeMounts := range container.VolumeMounts {
 | 
				
			||||||
				VolumeSource: v1.VolumeSource{
 | 
									mounts = append(mounts, volumeMounts.Name)
 | 
				
			||||||
					EmptyDir: &v1.EmptyDirVolumeSource{},
 | 
					 | 
				
			||||||
				},
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		numMounts := len(tt.podSpec.Containers[0].VolumeMounts)
 | 
								if !util.IsEqualIgnoreOrder(mounts, tt.expectedMounts) {
 | 
				
			||||||
 | 
									t.Errorf("%s %s: different volume mounts: got %v, epxected %v",
 | 
				
			||||||
		cluster.addAdditionalVolumes(tt.podSpec, additionalVolumeMount)
 | 
										testName, tt.subTest, mounts, tt.expectedMounts)
 | 
				
			||||||
		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)
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -19,8 +19,8 @@ var VersionMap = map[string]int{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsBiggerPostgresVersion Compare two Postgres version numbers
 | 
					// IsBiggerPostgresVersion Compare two Postgres version numbers
 | 
				
			||||||
func IsBiggerPostgresVersion(old string, new string) bool {
 | 
					func IsBiggerPostgresVersion(old string, new string) bool {
 | 
				
			||||||
	oldN, _ := VersionMap[old]
 | 
						oldN := VersionMap[old]
 | 
				
			||||||
	newN, _ := VersionMap[new]
 | 
						newN := VersionMap[new]
 | 
				
			||||||
	return newN > oldN
 | 
						return newN > oldN
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -147,25 +147,6 @@ func (c *Cluster) preScaleDown(newStatefulSet *appsv1.StatefulSet) error {
 | 
				
			||||||
	return nil
 | 
						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 {
 | 
					func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
 | 
				
			||||||
	c.setProcessName("updating statefulset")
 | 
						c.setProcessName("updating statefulset")
 | 
				
			||||||
	if c.Statefulset == nil {
 | 
						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)
 | 
							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
 | 
						c.Statefulset = statefulSet
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -11,7 +11,6 @@ import (
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util"
 | 
						"github.com/zalando/postgres-operator/pkg/util"
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util/constants"
 | 
						"github.com/zalando/postgres-operator/pkg/util/constants"
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | 
						"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | 
				
			||||||
	appsv1 "k8s.io/api/apps/v1"
 | 
					 | 
				
			||||||
	batchv1beta1 "k8s.io/api/batch/v1beta1"
 | 
						batchv1beta1 "k8s.io/api/batch/v1beta1"
 | 
				
			||||||
	v1 "k8s.io/api/core/v1"
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
						policybeta1 "k8s.io/api/policy/v1beta1"
 | 
				
			||||||
| 
						 | 
					@ -260,28 +259,6 @@ func (c *Cluster) syncPodDisruptionBudget(isUpdate bool) error {
 | 
				
			||||||
	return nil
 | 
						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 {
 | 
					func (c *Cluster) syncStatefulSet() error {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	podsToRecreate := make([]v1.Pod, 0)
 | 
						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 {
 | 
							if len(podsToRecreate) == 0 && !c.OpConfig.EnableLazySpiloUpgrade {
 | 
				
			||||||
			// even if the desired and the running statefulsets match
 | 
								// even if the desired and the running statefulsets match
 | 
				
			||||||
			// there still may be not up-to-date pods on condition
 | 
								// there still may be not up-to-date pods on condition
 | 
				
			||||||
| 
						 | 
					@ -382,8 +357,8 @@ func (c *Cluster) syncStatefulSet() error {
 | 
				
			||||||
			// and
 | 
								// and
 | 
				
			||||||
			//  (b) some of the pods were not restarted when the lazy update was still in place
 | 
								//  (b) some of the pods were not restarted when the lazy update was still in place
 | 
				
			||||||
			for _, pod := range pods {
 | 
								for _, pod := range pods {
 | 
				
			||||||
				effectivePodImage := pod.Spec.Containers[0].Image
 | 
									effectivePodImage := getPostgresContainer(&pod.Spec).Image
 | 
				
			||||||
				stsImage := desiredSts.Spec.Template.Spec.Containers[0].Image
 | 
									stsImage := getPostgresContainer(&desiredSts.Spec.Template.Spec).Image
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				if stsImage != effectivePodImage {
 | 
									if stsImage != effectivePodImage {
 | 
				
			||||||
					if err = c.markRollingUpdateFlagForPod(&pod, "pod not yet restarted due to lazy update"); err != nil {
 | 
										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 {
 | 
						for _, u := range c.pgUsers {
 | 
				
			||||||
		userNames = append(userNames, u.Name)
 | 
							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) {
 | 
						if needMasterConnectionPooler(&c.Spec) || needReplicaConnectionPooler(&c.Spec) {
 | 
				
			||||||
		connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName]
 | 
							connectionPoolerUser := c.systemUsers[constants.ConnectionPoolerUserKeyName]
 | 
				
			||||||
		userNames = append(userNames, connectionPoolerUser.Name)
 | 
							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)
 | 
							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)
 | 
						pgSyncRequests := c.userSyncStrategy.ProduceSyncRequests(dbUsers, c.pgUsers)
 | 
				
			||||||
	if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil {
 | 
						if err = c.userSyncStrategy.ExecuteSyncRequests(pgSyncRequests, c.pgDb); err != nil {
 | 
				
			||||||
		return fmt.Errorf("error executing sync statements: %v", err)
 | 
							return fmt.Errorf("error executing sync statements: %v", err)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -227,9 +227,18 @@ func (c *Cluster) logServiceChanges(role PostgresRole, old, new *v1.Service, isU
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (c *Cluster) logVolumeChanges(old, new acidv1.Volume) {
 | 
					func getPostgresContainer(podSpec *v1.PodSpec) (pgContainer v1.Container) {
 | 
				
			||||||
	c.logger.Infof("volume specification has been changed")
 | 
						for _, container := range podSpec.Containers {
 | 
				
			||||||
	logNiceDiff(c.logger, old, new)
 | 
							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) {
 | 
					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 {
 | 
							for team, membership := range *c.Config.PgTeamMap {
 | 
				
			||||||
			if team == teamID {
 | 
								if team == teamID {
 | 
				
			||||||
				additionalMembers = membership.AdditionalMembers
 | 
									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, additionalMembers...)
 | 
				
			||||||
			members = append(members, member)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !c.OpConfig.EnableTeamsAPI {
 | 
						if !c.OpConfig.EnableTeamsAPI {
 | 
				
			||||||
| 
						 | 
					@ -263,14 +270,12 @@ func (c *Cluster) getTeamMembers(teamID string) ([]string, error) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	token, err := c.oauthTokenGetter.getOAuthToken()
 | 
						token, err := c.oauthTokenGetter.getOAuthToken()
 | 
				
			||||||
	if err != nil {
 | 
						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 nil, fmt.Errorf("could not get oauth token to authenticate to team service API: %v", err)
 | 
				
			||||||
		return members, nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	teamInfo, err := c.teamsAPIClient.TeamInfo(teamID, token)
 | 
						teamInfo, err := c.teamsAPIClient.TeamInfo(teamID, token)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		c.logger.Warnf("could not get team info for team %q, only returning %d members: %v", teamID, len(members), err)
 | 
							return nil, fmt.Errorf("could not get team info for team %q: %v", teamID, err)
 | 
				
			||||||
		return members, nil
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, member := range teamInfo.Members {
 | 
						for _, member := range teamInfo.Members {
 | 
				
			||||||
| 
						 | 
					@ -292,7 +297,6 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin
 | 
				
			||||||
	pgCRDAnnotations := c.ObjectMeta.Annotations
 | 
						pgCRDAnnotations := c.ObjectMeta.Annotations
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// allow to inherit certain labels from the 'postgres' object
 | 
						// allow to inherit certain labels from the 'postgres' object
 | 
				
			||||||
	if pgCRDAnnotations != nil {
 | 
					 | 
				
			||||||
	for k, v := range pgCRDAnnotations {
 | 
						for k, v := range pgCRDAnnotations {
 | 
				
			||||||
		for _, match := range c.OpConfig.InheritedAnnotations {
 | 
							for _, match := range c.OpConfig.InheritedAnnotations {
 | 
				
			||||||
			if k == match {
 | 
								if k == match {
 | 
				
			||||||
| 
						 | 
					@ -300,7 +304,6 @@ func (c *Cluster) annotationsSet(annotations map[string]string) map[string]strin
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(annotations) > 0 {
 | 
						if len(annotations) > 0 {
 | 
				
			||||||
		return annotations
 | 
							return annotations
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -74,10 +74,15 @@ func (c *Cluster) syncVolumes() error {
 | 
				
			||||||
func (c *Cluster) syncUnderlyingEBSVolume() error {
 | 
					func (c *Cluster) syncUnderlyingEBSVolume() error {
 | 
				
			||||||
	c.logger.Infof("starting to sync EBS volumes: type, iops, throughput, and size")
 | 
						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
 | 
						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)
 | 
						targetSize := quantityToGigabyte(newSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	awsGp3 := aws.String("gp3")
 | 
						awsGp3 := aws.String("gp3")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -24,6 +24,20 @@ import (
 | 
				
			||||||
	"k8s.io/client-go/kubernetes/fake"
 | 
						"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) {
 | 
					func newFakeK8sPVCclient() (k8sutil.KubernetesClient, *fake.Clientset) {
 | 
				
			||||||
	clientSet := fake.NewSimpleClientset()
 | 
						clientSet := fake.NewSimpleClientset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -189,14 +203,7 @@ func TestMigrateEBS(t *testing.T) {
 | 
				
			||||||
	cluster.Namespace = namespace
 | 
						cluster.Namespace = namespace
 | 
				
			||||||
	filterLabels := cluster.labelsSet(false)
 | 
						filterLabels := cluster.labelsSet(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testVolumes := []testVolume{
 | 
						testVolumes := []testVolume{testVol, testVol}
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
						initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -220,13 +227,6 @@ func TestMigrateEBS(t *testing.T) {
 | 
				
			||||||
	cluster.executeEBSMigration()
 | 
						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) {
 | 
					func initTestVolumesAndPods(client k8sutil.KubernetesClient, namespace, clustername string, labels labels.Set, volumes []testVolume) {
 | 
				
			||||||
	i := 0
 | 
						i := 0
 | 
				
			||||||
	for _, v := range volumes {
 | 
						for _, v := range volumes {
 | 
				
			||||||
| 
						 | 
					@ -305,17 +305,7 @@ func TestMigrateGp3Support(t *testing.T) {
 | 
				
			||||||
	cluster.Namespace = namespace
 | 
						cluster.Namespace = namespace
 | 
				
			||||||
	filterLabels := cluster.labelsSet(false)
 | 
						filterLabels := cluster.labelsSet(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testVolumes := []testVolume{
 | 
						testVolumes := []testVolume{testVol, testVol, testVol}
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
						initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -371,14 +361,7 @@ func TestManualGp2Gp3Support(t *testing.T) {
 | 
				
			||||||
	cluster.Namespace = namespace
 | 
						cluster.Namespace = namespace
 | 
				
			||||||
	filterLabels := cluster.labelsSet(false)
 | 
						filterLabels := cluster.labelsSet(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	testVolumes := []testVolume{
 | 
						testVolumes := []testVolume{testVol, testVol}
 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
		{
 | 
					 | 
				
			||||||
			size: 100,
 | 
					 | 
				
			||||||
		},
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
						initTestVolumesAndPods(cluster.KubeClient, namespace, clusterName, filterLabels, testVolumes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -39,7 +39,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
				
			||||||
	result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat
 | 
						result.EnableSpiloWalPathCompat = fromCRD.EnableSpiloWalPathCompat
 | 
				
			||||||
	result.EtcdHost = fromCRD.EtcdHost
 | 
						result.EtcdHost = fromCRD.EtcdHost
 | 
				
			||||||
	result.KubernetesUseConfigMaps = fromCRD.KubernetesUseConfigMaps
 | 
						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.Workers = util.CoalesceUInt32(fromCRD.Workers, 8)
 | 
				
			||||||
	result.MinInstances = fromCRD.MinInstances
 | 
						result.MinInstances = fromCRD.MinInstances
 | 
				
			||||||
	result.MaxInstances = fromCRD.MaxInstances
 | 
						result.MaxInstances = fromCRD.MaxInstances
 | 
				
			||||||
| 
						 | 
					@ -152,7 +152,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// logical backup config
 | 
						// logical backup config
 | 
				
			||||||
	result.LogicalBackupSchedule = util.Coalesce(fromCRD.LogicalBackup.Schedule, "30 00 * * *")
 | 
						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.LogicalBackupProvider = util.Coalesce(fromCRD.LogicalBackup.BackupProvider, "s3")
 | 
				
			||||||
	result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
 | 
						result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
 | 
				
			||||||
	result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region
 | 
						result.LogicalBackupS3Region = fromCRD.LogicalBackup.S3Region
 | 
				
			||||||
| 
						 | 
					@ -180,6 +180,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
				
			||||||
	result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams
 | 
						result.PostgresSuperuserTeams = fromCRD.TeamsAPI.PostgresSuperuserTeams
 | 
				
			||||||
	result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD
 | 
						result.EnablePostgresTeamCRD = fromCRD.TeamsAPI.EnablePostgresTeamCRD
 | 
				
			||||||
	result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers
 | 
						result.EnablePostgresTeamCRDSuperusers = fromCRD.TeamsAPI.EnablePostgresTeamCRDSuperusers
 | 
				
			||||||
 | 
						result.EnableTeamMemberDeprecation = fromCRD.TeamsAPI.EnableTeamMemberDeprecation
 | 
				
			||||||
 | 
						result.RoleDeletionSuffix = util.Coalesce(fromCRD.TeamsAPI.RoleDeletionSuffix, "_deleted")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// logging REST API config
 | 
						// logging REST API config
 | 
				
			||||||
	result.APIPort = util.CoalesceInt(fromCRD.LoggingRESTAPI.APIPort, 8080)
 | 
						result.APIPort = util.CoalesceInt(fromCRD.LoggingRESTAPI.APIPort, 8080)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -42,6 +42,7 @@ const (
 | 
				
			||||||
	PGSyncUserAdd = iota
 | 
						PGSyncUserAdd = iota
 | 
				
			||||||
	PGsyncUserAlter
 | 
						PGsyncUserAlter
 | 
				
			||||||
	PGSyncAlterSet // handle ALTER ROLE SET parameter = value
 | 
						PGSyncAlterSet // handle ALTER ROLE SET parameter = value
 | 
				
			||||||
 | 
						PGSyncUserRename
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PgUser contains information about a single user.
 | 
					// PgUser contains information about a single user.
 | 
				
			||||||
| 
						 | 
					@ -53,6 +54,7 @@ type PgUser struct {
 | 
				
			||||||
	MemberOf   []string          `yaml:"inrole"`
 | 
						MemberOf   []string          `yaml:"inrole"`
 | 
				
			||||||
	Parameters map[string]string `yaml:"db_parameters"`
 | 
						Parameters map[string]string `yaml:"db_parameters"`
 | 
				
			||||||
	AdminRole  string            `yaml:"admin_role"`
 | 
						AdminRole  string            `yaml:"admin_role"`
 | 
				
			||||||
 | 
						Deleted    bool              `yaml:"deleted"`
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (user *PgUser) Valid() bool {
 | 
					func (user *PgUser) Valid() bool {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,8 +9,6 @@ import (
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	True       = true
 | 
					 | 
				
			||||||
	False      = false
 | 
					 | 
				
			||||||
	pgTeamList = acidv1.PostgresTeamList{
 | 
						pgTeamList = acidv1.PostgresTeamList{
 | 
				
			||||||
		TypeMeta: metav1.TypeMeta{
 | 
							TypeMeta: metav1.TypeMeta{
 | 
				
			||||||
			Kind:       "List",
 | 
								Kind:       "List",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -28,8 +28,8 @@ type Resources struct {
 | 
				
			||||||
	PodLabelWaitTimeout           time.Duration       `name:"pod_label_wait_timeout" default:"10m"`
 | 
						PodLabelWaitTimeout           time.Duration       `name:"pod_label_wait_timeout" default:"10m"`
 | 
				
			||||||
	PodDeletionWaitTimeout        time.Duration       `name:"pod_deletion_wait_timeout" default:"10m"`
 | 
						PodDeletionWaitTimeout        time.Duration       `name:"pod_deletion_wait_timeout" default:"10m"`
 | 
				
			||||||
	PodTerminateGracePeriod       time.Duration       `name:"pod_terminate_grace_period" default:"5m"`
 | 
						PodTerminateGracePeriod       time.Duration       `name:"pod_terminate_grace_period" default:"5m"`
 | 
				
			||||||
	SpiloRunAsUser                *int64              `name:"spilo_runasuser,omitempty"`
 | 
						SpiloRunAsUser                *int64              `name:"spilo_runasuser"`
 | 
				
			||||||
	SpiloRunAsGroup               *int64              `name:"spilo_runasgroup,omitempty"`
 | 
						SpiloRunAsGroup               *int64              `name:"spilo_runasgroup"`
 | 
				
			||||||
	SpiloFSGroup                  *int64              `name:"spilo_fsgroup"`
 | 
						SpiloFSGroup                  *int64              `name:"spilo_fsgroup"`
 | 
				
			||||||
	PodPriorityClassName          string              `name:"pod_priority_class_name"`
 | 
						PodPriorityClassName          string              `name:"pod_priority_class_name"`
 | 
				
			||||||
	ClusterDomain                 string              `name:"cluster_domain" default:"cluster.local"`
 | 
						ClusterDomain                 string              `name:"cluster_domain" default:"cluster.local"`
 | 
				
			||||||
| 
						 | 
					@ -114,7 +114,7 @@ type Scalyr struct {
 | 
				
			||||||
// LogicalBackup defines configuration for logical backup
 | 
					// LogicalBackup defines configuration for logical backup
 | 
				
			||||||
type LogicalBackup struct {
 | 
					type LogicalBackup struct {
 | 
				
			||||||
	LogicalBackupSchedule                     string `name:"logical_backup_schedule" default:"30 00 * * *"`
 | 
						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"`
 | 
						LogicalBackupProvider                     string `name:"logical_backup_provider" default:"s3"`
 | 
				
			||||||
	LogicalBackupS3Bucket                     string `name:"logical_backup_s3_bucket" default:""`
 | 
						LogicalBackupS3Bucket                     string `name:"logical_backup_s3_bucket" default:""`
 | 
				
			||||||
	LogicalBackupS3Region                     string `name:"logical_backup_s3_region" 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'
 | 
						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"`
 | 
						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
 | 
						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
 | 
						SidecarImages           map[string]string `name:"sidecar_docker_images"` // deprecated in favour of SidecarContainers
 | 
				
			||||||
	SidecarContainers       []v1.Container    `name:"sidecars"`
 | 
						SidecarContainers       []v1.Container    `name:"sidecars"`
 | 
				
			||||||
	PodServiceAccountName   string            `name:"pod_service_account_name" default:"postgres-pod"`
 | 
						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"`
 | 
						EnableTeamsAPI                         bool              `name:"enable_teams_api" default:"true"`
 | 
				
			||||||
	EnableTeamSuperuser                    bool              `name:"enable_team_superuser" default:"false"`
 | 
						EnableTeamSuperuser                    bool              `name:"enable_team_superuser" default:"false"`
 | 
				
			||||||
	TeamAdminRole                          string            `name:"team_admin_role" default:"admin"`
 | 
						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"`
 | 
						EnableAdminRoleForUsers                bool              `name:"enable_admin_role_for_users" default:"true"`
 | 
				
			||||||
	EnablePostgresTeamCRD                  bool              `name:"enable_postgres_team_crd" default:"false"`
 | 
						EnablePostgresTeamCRD                  bool              `name:"enable_postgres_team_crd" default:"false"`
 | 
				
			||||||
	EnablePostgresTeamCRDSuperusers        bool              `name:"enable_postgres_team_crd_superusers" default:"false"`
 | 
						EnablePostgresTeamCRDSuperusers        bool              `name:"enable_postgres_team_crd_superusers" default:"false"`
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,7 +5,6 @@ import "time"
 | 
				
			||||||
// General kubernetes-related constants
 | 
					// General kubernetes-related constants
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	PostgresContainerName = "postgres"
 | 
						PostgresContainerName = "postgres"
 | 
				
			||||||
	PostgresContainerIdx  = 0
 | 
					 | 
				
			||||||
	K8sAPIPath            = "/apis"
 | 
						K8sAPIPath            = "/apis"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	QueueResyncPeriodPod  = 5 * time.Minute
 | 
						QueueResyncPeriodPod  = 5 * time.Minute
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,11 +9,13 @@ import (
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/spec"
 | 
						"github.com/zalando/postgres-operator/pkg/spec"
 | 
				
			||||||
	"github.com/zalando/postgres-operator/pkg/util"
 | 
						"github.com/zalando/postgres-operator/pkg/util"
 | 
				
			||||||
 | 
						"github.com/zalando/postgres-operator/pkg/util/constants"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	createUserSQL        = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;`
 | 
						createUserSQL        = `SET LOCAL synchronous_commit = 'local'; CREATE ROLE "%s" %s %s;`
 | 
				
			||||||
	alterUserSQL         = `ALTER ROLE "%s" %s`
 | 
						alterUserSQL         = `ALTER ROLE "%s" %s`
 | 
				
			||||||
 | 
						alterUserRenameSQL   = `ALTER ROLE "%s" RENAME TO "%s%s"`
 | 
				
			||||||
	alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL`
 | 
						alterRoleResetAllSQL = `ALTER ROLE "%s" RESET ALL`
 | 
				
			||||||
	alterRoleSetSQL      = `ALTER ROLE "%s" SET %s TO %s`
 | 
						alterRoleSetSQL      = `ALTER ROLE "%s" SET %s TO %s`
 | 
				
			||||||
	grantToUserSQL       = `GRANT %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.
 | 
					// (except for the NOLOGIN). TODO: process other NOflags, i.e. NOSUPERUSER correctly.
 | 
				
			||||||
type DefaultUserSyncStrategy struct {
 | 
					type DefaultUserSyncStrategy struct {
 | 
				
			||||||
	PasswordEncryption string
 | 
						PasswordEncryption string
 | 
				
			||||||
 | 
						RoleDeletionSuffix string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ProduceSyncRequests figures out the types of changes that need to happen with the given users.
 | 
					// 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 {
 | 
						newUsers spec.PgUserMap) []spec.PgSyncUserRequest {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var reqs []spec.PgSyncUserRequest
 | 
						var reqs []spec.PgSyncUserRequest
 | 
				
			||||||
	// No existing roles are deleted or stripped of role memebership/flags
 | 
					 | 
				
			||||||
	for name, newUser := range newUsers {
 | 
						for name, newUser := range newUsers {
 | 
				
			||||||
 | 
							// do not create user that exists in DB with deletion suffix
 | 
				
			||||||
 | 
							if newUser.Deleted {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		dbUser, exists := dbUsers[name]
 | 
							dbUser, exists := dbUsers[name]
 | 
				
			||||||
		if !exists {
 | 
							if !exists {
 | 
				
			||||||
			reqs = append(reqs, spec.PgSyncUserRequest{Kind: spec.PGSyncUserAdd, User: newUser})
 | 
								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
 | 
						return reqs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -94,6 +119,11 @@ func (strategy DefaultUserSyncStrategy) ExecuteSyncRequests(requests []spec.PgSy
 | 
				
			||||||
				reqretries = append(reqretries, request)
 | 
									reqretries = append(reqretries, request)
 | 
				
			||||||
				errors = append(errors, fmt.Sprintf("could not set custom user %q parameters: %v", request.User.Name, err))
 | 
									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:
 | 
							default:
 | 
				
			||||||
			return fmt.Errorf("unrecognized operation: %v", request.Kind)
 | 
								return fmt.Errorf("unrecognized operation: %v", request.Kind)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
| 
						 | 
					@ -124,6 +154,23 @@ func (strategy DefaultUserSyncStrategy) alterPgUserSet(user spec.PgUser, db *sql
 | 
				
			||||||
	return nil
 | 
						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 {
 | 
					func (strategy DefaultUserSyncStrategy) createPgUser(user spec.PgUser, db *sql.DB) error {
 | 
				
			||||||
	var userFlags []string
 | 
						var userFlags []string
 | 
				
			||||||
	var userPassword string
 | 
						var userPassword string
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -151,6 +151,18 @@ func IsEqualIgnoreOrder(a, b []string) bool {
 | 
				
			||||||
	return reflect.DeepEqual(a_copy, b_copy)
 | 
						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.
 | 
					// 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) {
 | 
					func SubstractStringSlices(a []string, b []string) (result []string, equal bool) {
 | 
				
			||||||
	// Slices are assumed to contain unique elements only
 | 
						// Slices are assumed to contain unique elements only
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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) {
 | 
					func TestSubstractSlices(t *testing.T) {
 | 
				
			||||||
	for _, tt := range substractTest {
 | 
						for _, tt := range substractTest {
 | 
				
			||||||
		actualRes, actualEqual := SubstractStringSlices(tt.inA, tt.inB)
 | 
							actualRes, actualEqual := SubstractStringSlices(tt.inA, tt.inB)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue