Merge branch 'master' into gh-pages
This commit is contained in:
		
						commit
						049c6399c3
					
				| 
						 | 
				
			
			@ -24,7 +24,6 @@ _testmain.go
 | 
			
		|||
*.test
 | 
			
		||||
*.prof
 | 
			
		||||
/vendor/
 | 
			
		||||
/.glide/
 | 
			
		||||
/build/
 | 
			
		||||
/docker/build/
 | 
			
		||||
.idea
 | 
			
		||||
| 
						 | 
				
			
			@ -87,6 +86,9 @@ coverage.xml
 | 
			
		|||
.hypothesis/
 | 
			
		||||
.pytest_cache/
 | 
			
		||||
 | 
			
		||||
# e2e tests
 | 
			
		||||
e2e/manifests
 | 
			
		||||
 | 
			
		||||
# Translations
 | 
			
		||||
*.mo
 | 
			
		||||
*.pot
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,6 @@ go:
 | 
			
		|||
  - "1.12.x"
 | 
			
		||||
 | 
			
		||||
before_install:
 | 
			
		||||
  - go get github.com/Masterminds/glide
 | 
			
		||||
  - go get github.com/mattn/goveralls
 | 
			
		||||
 | 
			
		||||
install:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										15
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										15
									
								
								Makefile
								
								
								
								
							| 
						 | 
				
			
			@ -78,8 +78,9 @@ scm-source.json: .git
 | 
			
		|||
	echo '{\n "url": "git:$(GITURL)",\n "revision": "$(GITHEAD)",\n "author": "$(USER)",\n "status": "$(GITSTATUS)"\n}' > scm-source.json
 | 
			
		||||
 | 
			
		||||
tools:
 | 
			
		||||
	@go get -u honnef.co/go/tools/cmd/staticcheck
 | 
			
		||||
	@go get -u github.com/Masterminds/glide
 | 
			
		||||
	GO111MODULE=on go get -u honnef.co/go/tools/cmd/staticcheck
 | 
			
		||||
	GO111MODULE=on go get k8s.io/client-go@kubernetes-1.16.3
 | 
			
		||||
	GO111MODULE=on go mod tidy
 | 
			
		||||
 | 
			
		||||
fmt:
 | 
			
		||||
	@gofmt -l -w -s $(DIRS)
 | 
			
		||||
| 
						 | 
				
			
			@ -88,12 +89,12 @@ vet:
 | 
			
		|||
	@go vet $(PKG)
 | 
			
		||||
	@staticcheck $(PKG)
 | 
			
		||||
 | 
			
		||||
deps:
 | 
			
		||||
	@glide install --strip-vendor
 | 
			
		||||
deps: tools
 | 
			
		||||
	GO111MODULE=on go mod vendor
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
	hack/verify-codegen.sh
 | 
			
		||||
	@go test ./...
 | 
			
		||||
	GO111MODULE=on go test ./...
 | 
			
		||||
 | 
			
		||||
e2e:
 | 
			
		||||
	cd e2e; make tools test
 | 
			
		||||
e2e: docker # build operator image to be tested
 | 
			
		||||
	cd e2e; make tools test clean
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
apiVersion: v1
 | 
			
		||||
name: postgres-operator
 | 
			
		||||
version: 1.2.0
 | 
			
		||||
appVersion: 1.2.0
 | 
			
		||||
version: 1.3.0
 | 
			
		||||
appVersion: 1.3.0
 | 
			
		||||
home: https://github.com/zalando/postgres-operator
 | 
			
		||||
description: Postgres Operator creates and manages PostgreSQL clusters running in Kubernetes
 | 
			
		||||
keywords:
 | 
			
		||||
| 
						 | 
				
			
			@ -13,8 +13,6 @@ keywords:
 | 
			
		|||
maintainers:
 | 
			
		||||
- name: Zalando
 | 
			
		||||
  email: opensource@zalando.de
 | 
			
		||||
- name: kimxogus
 | 
			
		||||
  email: kgyoo8232@gmail.com
 | 
			
		||||
sources:
 | 
			
		||||
- https://github.com/zalando/postgres-operator
 | 
			
		||||
engine: gotpl
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,316 @@
 | 
			
		|||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: operatorconfigurations.acid.zalan.do
 | 
			
		||||
  labels:
 | 
			
		||||
    app.kubernetes.io/name: postgres-operator
 | 
			
		||||
  annotations:
 | 
			
		||||
    "helm.sh/hook": crd-install
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: OperatorConfiguration
 | 
			
		||||
    listKind: OperatorConfigurationList
 | 
			
		||||
    plural: operatorconfigurations
 | 
			
		||||
    singular: operatorconfiguration
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - opconfig
 | 
			
		||||
  additionalPrinterColumns:
 | 
			
		||||
  - name: Image
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Spilo image to be used for Pods
 | 
			
		||||
    JSONPath: .configuration.docker_image
 | 
			
		||||
  - name: Cluster-Label
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Label for K8s resources created by operator
 | 
			
		||||
    JSONPath: .configuration.kubernetes.cluster_name_label
 | 
			
		||||
  - name: Service-Account
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Name of service account to be used
 | 
			
		||||
    JSONPath: .configuration.kubernetes.pod_service_account_name
 | 
			
		||||
  - name: Min-Instances
 | 
			
		||||
    type: integer
 | 
			
		||||
    description: Minimum number of instances per Postgres cluster
 | 
			
		||||
    JSONPath: .configuration.min_instances
 | 
			
		||||
  - name: Age
 | 
			
		||||
    type: date
 | 
			
		||||
    JSONPath: .metadata.creationTimestamp
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
  validation:
 | 
			
		||||
    openAPIV3Schema:
 | 
			
		||||
      type: object
 | 
			
		||||
      required:
 | 
			
		||||
        - kind
 | 
			
		||||
        - apiVersion
 | 
			
		||||
        - configuration
 | 
			
		||||
      properties:
 | 
			
		||||
        kind:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - OperatorConfiguration
 | 
			
		||||
        apiVersion:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - acid.zalan.do/v1
 | 
			
		||||
        configuration:
 | 
			
		||||
          type: object
 | 
			
		||||
          properties:
 | 
			
		||||
            docker_image:
 | 
			
		||||
              type: string
 | 
			
		||||
            enable_crd_validation:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enable_shm_volume:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            etcd_host:
 | 
			
		||||
              type: string
 | 
			
		||||
            max_instances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: -1  # -1 = disabled
 | 
			
		||||
            min_instances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: -1  # -1 = disabled
 | 
			
		||||
            resync_period:
 | 
			
		||||
              type: string
 | 
			
		||||
            repair_period:
 | 
			
		||||
              type: string
 | 
			
		||||
            set_memory_request_to_limit:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            sidecar_docker_images:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
            workers:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: 1
 | 
			
		||||
            users:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                replication_username:
 | 
			
		||||
                   type: string
 | 
			
		||||
                super_username:
 | 
			
		||||
                   type: string
 | 
			
		||||
            kubernetes:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                cluster_domain:
 | 
			
		||||
                  type: string
 | 
			
		||||
                cluster_labels:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                cluster_name_label:
 | 
			
		||||
                  type: string
 | 
			
		||||
                custom_pod_annotations:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                enable_init_containers:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_pod_antiaffinity:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_pod_disruption_budget:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_sidecars:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                infrastructure_roles_secret_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                inherited_labels:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                master_pod_move_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                node_readiness_label:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                oauth_token_secret_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pdb_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_antiaffinity_topology_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_environment_configmap:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_management_policy:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                    - "ordered_ready"
 | 
			
		||||
                    - "parallel"
 | 
			
		||||
                pod_priority_class_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_role_label:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_definition:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_role_binding_definition:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_terminate_grace_period:
 | 
			
		||||
                  type: string
 | 
			
		||||
                secret_name_template:
 | 
			
		||||
                  type: string
 | 
			
		||||
                spilo_fsgroup:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                spilo_privileged:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                toleration:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                watched_namespace:
 | 
			
		||||
                  type: string
 | 
			
		||||
            postgres_pod_resources:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                default_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                default_cpu_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                default_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                default_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
            timeouts:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                pod_label_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_deletion_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                ready_wait_interval:
 | 
			
		||||
                  type: string
 | 
			
		||||
                ready_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                resource_check_interval:
 | 
			
		||||
                  type: string
 | 
			
		||||
                resource_check_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
            load_balancer:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                custom_service_annotations:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                db_hosted_zone:
 | 
			
		||||
                  type: string
 | 
			
		||||
                enable_master_load_balancer:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_replica_load_balancer:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                master_dns_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
                replica_dns_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
            aws_or_gcp:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                additional_secret_mount:
 | 
			
		||||
                  type: string
 | 
			
		||||
                additional_secret_mount_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
                aws_region:
 | 
			
		||||
                  type: string
 | 
			
		||||
                kube_iam_role:
 | 
			
		||||
                  type: string
 | 
			
		||||
                log_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
                wal_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
            logical_backup:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                logical_backup_docker_image:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_access_key_id:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_endpoint:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_secret_access_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_sse:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_schedule:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
 | 
			
		||||
            debug:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                debug_logging:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_database_access:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
            teams_api:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                enable_admin_role_for_users:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_team_superuser:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_teams_api:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                pam_configuration:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pam_role_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                postgres_superuser_teams:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                protected_role_names:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                team_admin_role:
 | 
			
		||||
                  type: string
 | 
			
		||||
                team_api_role_configuration:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                teams_api_url:
 | 
			
		||||
                  type: string
 | 
			
		||||
            logging_rest_api:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                api_port:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                cluster_history_entries:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                ring_log_lines:
 | 
			
		||||
                  type: integer
 | 
			
		||||
            scalyr:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                scalyr_api_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                scalyr_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                scalyr_cpu_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                scalyr_image:
 | 
			
		||||
                  type: string
 | 
			
		||||
                scalyr_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                scalyr_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                scalyr_server_url:
 | 
			
		||||
                  type: string
 | 
			
		||||
        status:
 | 
			
		||||
          type: object
 | 
			
		||||
          additionalProperties:
 | 
			
		||||
            type: string
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,363 @@
 | 
			
		|||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgresqls.acid.zalan.do
 | 
			
		||||
  labels:
 | 
			
		||||
    app.kubernetes.io/name: postgres-operator
 | 
			
		||||
  annotations:
 | 
			
		||||
    "helm.sh/hook": crd-install
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: postgresql
 | 
			
		||||
    listKind: postgresqlList
 | 
			
		||||
    plural: postgresqls
 | 
			
		||||
    singular: postgresql
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - pg
 | 
			
		||||
  additionalPrinterColumns:
 | 
			
		||||
  - name: Team
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Team responsible for Postgres CLuster
 | 
			
		||||
    JSONPath: .spec.teamId
 | 
			
		||||
  - name: Version
 | 
			
		||||
    type: string
 | 
			
		||||
    description: PostgreSQL version
 | 
			
		||||
    JSONPath: .spec.postgresql.version
 | 
			
		||||
  - name: Pods
 | 
			
		||||
    type: integer
 | 
			
		||||
    description: Number of Pods per Postgres cluster
 | 
			
		||||
    JSONPath: .spec.numberOfInstances
 | 
			
		||||
  - name: Volume
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Size of the bound volume
 | 
			
		||||
    JSONPath: .spec.volume.size
 | 
			
		||||
  - name: CPU-Request
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Requested CPU for Postgres containers
 | 
			
		||||
    JSONPath: .spec.resources.requests.cpu
 | 
			
		||||
  - name: Memory-Request
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Requested memory for Postgres containers
 | 
			
		||||
    JSONPath: .spec.resources.requests.memory
 | 
			
		||||
  - name: Age
 | 
			
		||||
    type: date
 | 
			
		||||
    JSONPath: .metadata.creationTimestamp
 | 
			
		||||
  - name: Status
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Current sync status of postgresql resource
 | 
			
		||||
    JSONPath: .status.PostgresClusterStatus
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
  validation:
 | 
			
		||||
    openAPIV3Schema:
 | 
			
		||||
      type: object
 | 
			
		||||
      required:
 | 
			
		||||
        - kind
 | 
			
		||||
        - apiVersion
 | 
			
		||||
        - spec
 | 
			
		||||
      properties:
 | 
			
		||||
        kind:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - postgresql
 | 
			
		||||
        apiVersion:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - acid.zalan.do/v1
 | 
			
		||||
        spec:
 | 
			
		||||
          type: object
 | 
			
		||||
          required:
 | 
			
		||||
            - numberOfInstances
 | 
			
		||||
            - teamId
 | 
			
		||||
            - postgresql
 | 
			
		||||
          properties:
 | 
			
		||||
            allowedSourceRanges:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: string
 | 
			
		||||
                pattern: '^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$'
 | 
			
		||||
            clone:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - cluster
 | 
			
		||||
              properties:
 | 
			
		||||
                cluster:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_endpoint:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_access_key_id:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_secret_access_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_force_path_style:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_wal_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
                timestamp:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$'
 | 
			
		||||
                  # The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC
 | 
			
		||||
                  # Example: 1996-12-19T16:39:57-08:00
 | 
			
		||||
                  # Note: this field requires a timezone
 | 
			
		||||
                uid:
 | 
			
		||||
                  format: uuid
 | 
			
		||||
                  type: string
 | 
			
		||||
            databases:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
			
		||||
            dockerImage:
 | 
			
		||||
              type: string
 | 
			
		||||
            enableLogicalBackup:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableMasterLoadBalancer:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableReplicaLoadBalancer:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableShmVolume:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            init_containers:  # deprecated
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            initContainers:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            logicalBackupSchedule:
 | 
			
		||||
              type: string
 | 
			
		||||
              pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
 | 
			
		||||
            maintenanceWindows:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                type: string
 | 
			
		||||
                pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
 | 
			
		||||
            numberOfInstances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: 0
 | 
			
		||||
            patroni:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                initdb:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                pg_hba:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                slots:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: object
 | 
			
		||||
                    additionalProperties:
 | 
			
		||||
                      type: string
 | 
			
		||||
                ttl:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                loop_wait:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                retry_timeout:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                maximum_lag_on_failover:
 | 
			
		||||
                  type: integer
 | 
			
		||||
            podAnnotations:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
            pod_priority_class_name:  # deprecated
 | 
			
		||||
              type: string
 | 
			
		||||
            podPriorityClassName:
 | 
			
		||||
              type: string
 | 
			
		||||
            postgresql:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - version
 | 
			
		||||
              properties:
 | 
			
		||||
                version:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                    - "9.3"
 | 
			
		||||
                    - "9.4"
 | 
			
		||||
                    - "9.5"
 | 
			
		||||
                    - "9.6"
 | 
			
		||||
                    - "10"
 | 
			
		||||
                    - "11"
 | 
			
		||||
                    - "12"
 | 
			
		||||
                parameters:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
            replicaLoadBalancer:  # deprecated
 | 
			
		||||
              type: boolean
 | 
			
		||||
            resources:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - requests
 | 
			
		||||
                - limits
 | 
			
		||||
              properties:
 | 
			
		||||
                limits:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  required:
 | 
			
		||||
                    - cpu
 | 
			
		||||
                    - memory
 | 
			
		||||
                  properties:
 | 
			
		||||
                    cpu:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # Decimal natural followed by m, or decimal natural followed by
 | 
			
		||||
                      # dot followed by up to three decimal digits.
 | 
			
		||||
                      #
 | 
			
		||||
                      # This is because the Kubernetes CPU resource has millis as the
 | 
			
		||||
                      # maximum precision.  The actual values are checked in code
 | 
			
		||||
                      # because the regular expression would be huge and horrible and
 | 
			
		||||
                      # not very helpful in validation error messages; this one checks
 | 
			
		||||
                      # only the format of the given number.
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
 | 
			
		||||
                      pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be lower
 | 
			
		||||
                      # than the corresponding request.
 | 
			
		||||
                    memory:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # You can express memory as a plain integer or as a fixed-point
 | 
			
		||||
                      # integer using one of these suffixes: E, P, T, G, M, k. You can
 | 
			
		||||
                      # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
 | 
			
		||||
                      pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be lower
 | 
			
		||||
                      # than the corresponding request.
 | 
			
		||||
                requests:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  required:
 | 
			
		||||
                    - cpu
 | 
			
		||||
                    - memory
 | 
			
		||||
                  properties:
 | 
			
		||||
                    cpu:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # Decimal natural followed by m, or decimal natural followed by
 | 
			
		||||
                      # dot followed by up to three decimal digits.
 | 
			
		||||
                      #
 | 
			
		||||
                      # This is because the Kubernetes CPU resource has millis as the
 | 
			
		||||
                      # maximum precision.  The actual values are checked in code
 | 
			
		||||
                      # because the regular expression would be huge and horrible and
 | 
			
		||||
                      # not very helpful in validation error messages; this one checks
 | 
			
		||||
                      # only the format of the given number.
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
 | 
			
		||||
                      pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be higher
 | 
			
		||||
                      # than the corresponding limit.
 | 
			
		||||
                    memory:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # You can express memory as a plain integer or as a fixed-point
 | 
			
		||||
                      # integer using one of these suffixes: E, P, T, G, M, k. You can
 | 
			
		||||
                      # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
 | 
			
		||||
                      pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be higher
 | 
			
		||||
                      # than the corresponding limit.
 | 
			
		||||
            sidecars:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            spiloFSGroup:
 | 
			
		||||
              type: integer
 | 
			
		||||
            standby:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - s3_wal_path
 | 
			
		||||
              properties:
 | 
			
		||||
                s3_wal_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
            teamId:
 | 
			
		||||
              type: string
 | 
			
		||||
            tolerations:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                required:
 | 
			
		||||
                  - key
 | 
			
		||||
                  - operator
 | 
			
		||||
                  - effect
 | 
			
		||||
                properties:
 | 
			
		||||
                  key:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  operator:
 | 
			
		||||
                    type: string
 | 
			
		||||
                    enum:
 | 
			
		||||
                      - Equal
 | 
			
		||||
                      - Exists
 | 
			
		||||
                  value:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  effect:
 | 
			
		||||
                    type: string
 | 
			
		||||
                    enum:
 | 
			
		||||
                      - NoExecute
 | 
			
		||||
                      - NoSchedule
 | 
			
		||||
                      - PreferNoSchedule
 | 
			
		||||
                  tolerationSeconds:
 | 
			
		||||
                    type: integer
 | 
			
		||||
            useLoadBalancer:  # deprecated
 | 
			
		||||
              type: boolean
 | 
			
		||||
            users:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: array
 | 
			
		||||
                nullable: true
 | 
			
		||||
                description: "Role flags specified here must not contradict each other"
 | 
			
		||||
                items:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                  - bypassrls
 | 
			
		||||
                  - BYPASSRLS
 | 
			
		||||
                  - nobypassrls
 | 
			
		||||
                  - NOBYPASSRLS
 | 
			
		||||
                  - createdb
 | 
			
		||||
                  - CREATEDB
 | 
			
		||||
                  - nocreatedb
 | 
			
		||||
                  - NOCREATEDB
 | 
			
		||||
                  - createrole
 | 
			
		||||
                  - CREATEROLE
 | 
			
		||||
                  - nocreaterole
 | 
			
		||||
                  - NOCREATEROLE
 | 
			
		||||
                  - inherit
 | 
			
		||||
                  - INHERIT
 | 
			
		||||
                  - noinherit
 | 
			
		||||
                  - NOINHERIT
 | 
			
		||||
                  - login
 | 
			
		||||
                  - LOGIN
 | 
			
		||||
                  - nologin
 | 
			
		||||
                  - NOLOGIN
 | 
			
		||||
                  - replication
 | 
			
		||||
                  - REPLICATION
 | 
			
		||||
                  - noreplication
 | 
			
		||||
                  - NOREPLICATION
 | 
			
		||||
                  - superuser
 | 
			
		||||
                  - SUPERUSER
 | 
			
		||||
                  - nosuperuser
 | 
			
		||||
                  - NOSUPERUSER
 | 
			
		||||
            volume:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - size
 | 
			
		||||
              properties:
 | 
			
		||||
                size:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                  # Note: the value specified here must not be zero.
 | 
			
		||||
                storageClass:
 | 
			
		||||
                  type: string
 | 
			
		||||
                subPath:
 | 
			
		||||
                  type: string
 | 
			
		||||
| 
						 | 
				
			
			@ -1,13 +1,34 @@
 | 
			
		|||
apiVersion: v1
 | 
			
		||||
entries:
 | 
			
		||||
  postgres-operator:
 | 
			
		||||
  - apiVersion: v1
 | 
			
		||||
    appVersion: 1.3.0
 | 
			
		||||
    created: "2019-12-17T12:58:49.477140129+01:00"
 | 
			
		||||
    description: Postgres Operator creates and manages PostgreSQL clusters running
 | 
			
		||||
      in Kubernetes
 | 
			
		||||
    digest: 7e788fd37daec76a01f6d6f9fe5be5b54f5035e4eba0041e80a760d656537325
 | 
			
		||||
    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.3.0.tgz
 | 
			
		||||
    version: 1.3.0
 | 
			
		||||
  - apiVersion: v1
 | 
			
		||||
    appVersion: 1.2.0
 | 
			
		||||
    created: "2019-08-13T17:33:32.735021423+02:00"
 | 
			
		||||
    created: "2019-12-17T12:58:49.475844233+01:00"
 | 
			
		||||
    description: Postgres Operator creates and manages PostgreSQL clusters running
 | 
			
		||||
      in Kubernetes
 | 
			
		||||
    digest: d10710c7cf19f4e266e7704f5d1e98dcfc61bee3919522326c35c22ca7d2f2bf
 | 
			
		||||
    engine: gotpl
 | 
			
		||||
    home: https://github.com/zalando/postgres-operator
 | 
			
		||||
    keywords:
 | 
			
		||||
    - postgres
 | 
			
		||||
| 
						 | 
				
			
			@ -26,4 +47,4 @@ entries:
 | 
			
		|||
    urls:
 | 
			
		||||
    - postgres-operator-1.2.0.tgz
 | 
			
		||||
    version: 1.2.0
 | 
			
		||||
generated: "2019-08-13T17:33:32.734335398+02:00"
 | 
			
		||||
generated: "2019-12-17T12:58:49.474719294+01:00"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											Binary file not shown.
										
									
								
							| 
						 | 
				
			
			@ -87,6 +87,12 @@ rules:
 | 
			
		|||
  - list
 | 
			
		||||
  - watch
 | 
			
		||||
  - patch
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
  - pods/exec
 | 
			
		||||
  verbs:
 | 
			
		||||
  - create
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,6 @@
 | 
			
		|||
{{ if .Values.crd.create }}
 | 
			
		||||
{{- range $path, $bytes := .Files.Glob "crds/*.yaml" }}
 | 
			
		||||
{{ $.Files.Get $path }}
 | 
			
		||||
---
 | 
			
		||||
{{- end }}
 | 
			
		||||
{{- end }}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,101 +0,0 @@
 | 
			
		|||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgresqls.acid.zalan.do
 | 
			
		||||
  labels:
 | 
			
		||||
    app.kubernetes.io/name: {{ template "postgres-operator.name" . }}
 | 
			
		||||
    helm.sh/chart: {{ template "postgres-operator.chart" . }}
 | 
			
		||||
    app.kubernetes.io/managed-by: {{ .Release.Service }}
 | 
			
		||||
    app.kubernetes.io/instance: {{ .Release.Name }}
 | 
			
		||||
  annotations:
 | 
			
		||||
    "helm.sh/hook": crd-install
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: postgresql
 | 
			
		||||
    listKind: postgresqlList
 | 
			
		||||
    plural: postgresqls
 | 
			
		||||
    singular: postgresql
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - pg
 | 
			
		||||
  additionalPrinterColumns:
 | 
			
		||||
  - name: Team
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Team responsible for Postgres CLuster
 | 
			
		||||
    JSONPath: .spec.teamId
 | 
			
		||||
  - name: Version
 | 
			
		||||
    type: string
 | 
			
		||||
    description: PostgreSQL version
 | 
			
		||||
    JSONPath: .spec.postgresql.version
 | 
			
		||||
  - name: Pods
 | 
			
		||||
    type: integer
 | 
			
		||||
    description: Number of Pods per Postgres cluster
 | 
			
		||||
    JSONPath: .spec.numberOfInstances
 | 
			
		||||
  - name: Volume
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Size of the bound volume
 | 
			
		||||
    JSONPath: .spec.volume.size
 | 
			
		||||
  - name: CPU-Request
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Requested CPU for Postgres containers
 | 
			
		||||
    JSONPath: .spec.resources.requests.cpu
 | 
			
		||||
  - name: Memory-Request
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Requested memory for Postgres containers
 | 
			
		||||
    JSONPath: .spec.resources.requests.memory
 | 
			
		||||
  - name: Age
 | 
			
		||||
    type: date
 | 
			
		||||
    JSONPath: .metadata.creationTimestamp
 | 
			
		||||
  - name: Status
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Current sync status of postgresql resource
 | 
			
		||||
    JSONPath: .status.PostgresClusterStatus
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
---
 | 
			
		||||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: operatorconfigurations.acid.zalan.do
 | 
			
		||||
  labels:
 | 
			
		||||
    app.kubernetes.io/name: {{ template "postgres-operator.name" . }}
 | 
			
		||||
    helm.sh/chart: {{ template "postgres-operator.chart" . }}
 | 
			
		||||
    app.kubernetes.io/managed-by: {{ .Release.Service }}
 | 
			
		||||
    app.kubernetes.io/instance: {{ .Release.Name }}
 | 
			
		||||
  annotations:
 | 
			
		||||
    "helm.sh/hook": crd-install
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: OperatorConfiguration
 | 
			
		||||
    listKind: OperatorConfigurationList
 | 
			
		||||
    plural: operatorconfigurations
 | 
			
		||||
    singular: operatorconfiguration
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - opconfig
 | 
			
		||||
  additionalPrinterColumns:
 | 
			
		||||
  - name: Image
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Spilo image to be used for Pods
 | 
			
		||||
    JSONPath: .configuration.docker_image
 | 
			
		||||
  - name: Cluster-Label
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Label for K8s resources created by operator
 | 
			
		||||
    JSONPath: .configuration.kubernetes.cluster_name_label
 | 
			
		||||
  - name: Service-Account
 | 
			
		||||
    type: string
 | 
			
		||||
    description: Name of service account to be used
 | 
			
		||||
    JSONPath: .configuration.kubernetes.pod_service_account_name
 | 
			
		||||
  - name: Min-Instances
 | 
			
		||||
    type: integer
 | 
			
		||||
    description: Minimum number of instances per Postgres cluster
 | 
			
		||||
    JSONPath: .configuration.min_instances
 | 
			
		||||
  - name: Age
 | 
			
		||||
    type: date
 | 
			
		||||
    JSONPath: .metadata.creationTimestamp
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ configuration:
 | 
			
		|||
{{ toYaml .Values.configUsers | indent 4 }}
 | 
			
		||||
  kubernetes:
 | 
			
		||||
    oauth_token_secret_name: {{ template "postgres-operator.fullname" . }}
 | 
			
		||||
    pod_service_account_name: operator
 | 
			
		||||
    pod_service_account_name: {{ include "postgres-operator.serviceAccountName" . }}
 | 
			
		||||
{{ toYaml .Values.configKubernetes | indent 4 }}
 | 
			
		||||
  postgres_pod_resources:
 | 
			
		||||
{{ toYaml .Values.configPostgresPodResources | indent 4 }}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
image:
 | 
			
		||||
  registry: registry.opensource.zalan.do
 | 
			
		||||
  repository: acid/postgres-operator
 | 
			
		||||
  tag: v1.2.0
 | 
			
		||||
  tag: v1.3.0
 | 
			
		||||
  pullPolicy: "IfNotPresent"
 | 
			
		||||
 | 
			
		||||
# Optionally specify an array of imagePullSecrets.
 | 
			
		||||
| 
						 | 
				
			
			@ -17,12 +17,14 @@ configTarget: "OperatorConfigurationCRD"
 | 
			
		|||
 | 
			
		||||
# general top-level configuration parameters
 | 
			
		||||
configGeneral:
 | 
			
		||||
  # choose if deployment creates/updates CRDs with OpenAPIV3Validation
 | 
			
		||||
  enable_crd_validation: true
 | 
			
		||||
  # start any new database pod without limitations on shm memory
 | 
			
		||||
  enable_shm_volume: true
 | 
			
		||||
  # etcd connection string for Patroni. Empty uses K8s-native DCS.
 | 
			
		||||
  etcd_host: ""
 | 
			
		||||
  # Spilo docker image
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-11:1.5-p9
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  # max number of instances in Postgres cluster. -1 = no limit
 | 
			
		||||
  min_instances: -1
 | 
			
		||||
  # min number of instances in Postgres cluster. -1 = no limit
 | 
			
		||||
| 
						 | 
				
			
			@ -56,23 +58,33 @@ configKubernetes:
 | 
			
		|||
    application: spilo
 | 
			
		||||
  # label assigned to Kubernetes objects created by the operator
 | 
			
		||||
  cluster_name_label: cluster-name
 | 
			
		||||
  # additional annotations to add to every database pod
 | 
			
		||||
  # custom_pod_annotations:
 | 
			
		||||
  #   keya: valuea
 | 
			
		||||
  #   keyb: valueb
 | 
			
		||||
 | 
			
		||||
  # enables initContainers to run actions before Spilo is started
 | 
			
		||||
  enable_init_containers: true
 | 
			
		||||
  # toggles pod anti affinity on the Postgres pods
 | 
			
		||||
  enable_pod_antiaffinity: false
 | 
			
		||||
  # toggles PDB to set to MinAvailabe 0 or 1
 | 
			
		||||
  enable_pod_disruption_budget: true
 | 
			
		||||
  # enables sidecar containers to run alongside Spilo in the same pod
 | 
			
		||||
  enable_sidecars: true
 | 
			
		||||
  # name of the secret containing infrastructure roles names and passwords
 | 
			
		||||
  # infrastructure_roles_secret_name: postgresql-infrastructure-roles
 | 
			
		||||
 | 
			
		||||
  # list of labels that can be inherited from the cluster manifest
 | 
			
		||||
  # inherited_labels:
 | 
			
		||||
  # - application
 | 
			
		||||
  # - app
 | 
			
		||||
  # - environment
 | 
			
		||||
 | 
			
		||||
  # timeout for successful migration of master pods from unschedulable node
 | 
			
		||||
  # master_pod_move_timeout: 20m
 | 
			
		||||
 | 
			
		||||
  # set of labels that a running and active node should possess to be considered ready
 | 
			
		||||
  # node_readiness_label: ""
 | 
			
		||||
  # node_readiness_label:
 | 
			
		||||
  #   status: ready
 | 
			
		||||
 | 
			
		||||
  # name of the secret containing the OAuth2 token to pass to the teams API
 | 
			
		||||
  # oauth_token_secret_name: postgresql-operator
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +148,7 @@ configLoadBalancer:
 | 
			
		|||
  #   keya: valuea
 | 
			
		||||
 | 
			
		||||
  # toggles service type load balancer pointing to the master pod of the cluster
 | 
			
		||||
  enable_master_load_balancer: true
 | 
			
		||||
  enable_master_load_balancer: false
 | 
			
		||||
  # toggles service type load balancer pointing to the replica pod of the cluster
 | 
			
		||||
  enable_replica_load_balancer: false
 | 
			
		||||
  # defines the DNS name string template for the master load balancer cluster
 | 
			
		||||
| 
						 | 
				
			
			@ -182,12 +194,20 @@ configAwsOrGcp:
 | 
			
		|||
 | 
			
		||||
# configure K8s cron job managed by the operator
 | 
			
		||||
configLogicalBackup:
 | 
			
		||||
  # backup schedule in the cron format
 | 
			
		||||
  logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
  # image for pods of the logical backup job (example runs pg_dumpall)
 | 
			
		||||
  logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup"
 | 
			
		||||
  # S3 Access Key ID
 | 
			
		||||
  logical_backup_s3_access_key_id: ""
 | 
			
		||||
  # S3 bucket to store backup results
 | 
			
		||||
  logical_backup_s3_bucket: "my-bucket-url"
 | 
			
		||||
  # S3 endpoint url when not using AWS
 | 
			
		||||
  logical_backup_s3_endpoint: ""
 | 
			
		||||
  # S3 Secret Access Key
 | 
			
		||||
  logical_backup_s3_secret_access_key: ""
 | 
			
		||||
  # S3 server side encription
 | 
			
		||||
  logical_backup_s3_sse: "AES256"
 | 
			
		||||
  # backup schedule in the cron format
 | 
			
		||||
  logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
 | 
			
		||||
# automate creation of human users with teams API service
 | 
			
		||||
configTeamsApi:
 | 
			
		||||
| 
						 | 
				
			
			@ -204,7 +224,8 @@ configTeamsApi:
 | 
			
		|||
  # operator will add all team member roles to this group and add a pg_hba line
 | 
			
		||||
  pam_role_name: zalandos
 | 
			
		||||
  # List of teams which members need the superuser role in each Postgres cluster
 | 
			
		||||
  # postgres_superuser_teams: "postgres_superusers"
 | 
			
		||||
  # postgres_superuser_teams:
 | 
			
		||||
  # - postgres_superusers
 | 
			
		||||
 | 
			
		||||
  # List of roles that cannot be overwritten by an application, team or infrastructure role
 | 
			
		||||
  protected_role_names:
 | 
			
		||||
| 
						 | 
				
			
			@ -218,7 +239,7 @@ configTeamsApi:
 | 
			
		|||
  # teams_api_url: http://fake-teams-api.default.svc.cluster.local
 | 
			
		||||
 | 
			
		||||
# Scalyr is a log management tool that Zalando uses as a sidecar
 | 
			
		||||
scalyr:
 | 
			
		||||
configScalyr:
 | 
			
		||||
  # API key for the Scalyr sidecar
 | 
			
		||||
  # scalyr_api_key: ""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -238,14 +259,16 @@ rbac:
 | 
			
		|||
  # Specifies whether RBAC resources should be created
 | 
			
		||||
  create: true
 | 
			
		||||
 | 
			
		||||
crd:
 | 
			
		||||
  # Specifies whether custom resource definitions should be created
 | 
			
		||||
  create: true
 | 
			
		||||
 | 
			
		||||
serviceAccount:
 | 
			
		||||
  # Specifies whether a ServiceAccount should be created
 | 
			
		||||
  create: true
 | 
			
		||||
  # The name of the ServiceAccount to use.
 | 
			
		||||
  # If not set and create is true, a name is generated using the fullname template
 | 
			
		||||
  # When relying solely on the OperatorConfiguration CRD, this value has to be "operator"
 | 
			
		||||
  # Otherwise, the operator tries to use the "default" service account which is forbidden
 | 
			
		||||
  name: operator
 | 
			
		||||
  name:
 | 
			
		||||
 | 
			
		||||
priorityClassName: ""
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
image:
 | 
			
		||||
  registry: registry.opensource.zalan.do
 | 
			
		||||
  repository: acid/postgres-operator
 | 
			
		||||
  tag: v1.2.0
 | 
			
		||||
  tag: v1.3.0
 | 
			
		||||
  pullPolicy: "IfNotPresent"
 | 
			
		||||
 | 
			
		||||
# Optionally specify an array of imagePullSecrets.
 | 
			
		||||
| 
						 | 
				
			
			@ -17,12 +17,14 @@ configTarget: "ConfigMap"
 | 
			
		|||
 | 
			
		||||
# general configuration parameters
 | 
			
		||||
configGeneral:
 | 
			
		||||
  # choose if deployment creates/updates CRDs with OpenAPIV3Validation
 | 
			
		||||
  enable_crd_validation: "true"
 | 
			
		||||
  # start any new database pod without limitations on shm memory
 | 
			
		||||
  enable_shm_volume: "true"
 | 
			
		||||
  # etcd connection string for Patroni. Empty uses K8s-native DCS.
 | 
			
		||||
  etcd_host: ""
 | 
			
		||||
  # Spilo docker image
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-11:1.5-p9
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  # max number of instances in Postgres cluster. -1 = no limit
 | 
			
		||||
  min_instances: "-1"
 | 
			
		||||
  # min number of instances in Postgres cluster. -1 = no limit
 | 
			
		||||
| 
						 | 
				
			
			@ -54,15 +56,22 @@ configKubernetes:
 | 
			
		|||
  cluster_labels: application:spilo
 | 
			
		||||
  # label assigned to Kubernetes objects created by the operator
 | 
			
		||||
  cluster_name_label: version
 | 
			
		||||
  # annotations attached to each database pod
 | 
			
		||||
  # custom_pod_annotations: "keya:valuea,keyb:valueb"
 | 
			
		||||
 | 
			
		||||
  # enables initContainers to run actions before Spilo is started
 | 
			
		||||
  enable_init_containers: "true"
 | 
			
		||||
  # toggles pod anti affinity on the Postgres pods
 | 
			
		||||
  enable_pod_antiaffinity: "false"
 | 
			
		||||
  # toggles PDB to set to MinAvailabe 0 or 1
 | 
			
		||||
  enable_pod_disruption_budget: "true"
 | 
			
		||||
  # enables sidecar containers to run alongside Spilo in the same pod
 | 
			
		||||
  enable_sidecars: "true"
 | 
			
		||||
  # name of the secret containing infrastructure roles names and passwords
 | 
			
		||||
  # infrastructure_roles_secret_name: postgresql-infrastructure-roles
 | 
			
		||||
 | 
			
		||||
  # list of labels that can be inherited from the cluster manifest
 | 
			
		||||
  # inherited_labels: ""
 | 
			
		||||
  # inherited_labels: application,environment
 | 
			
		||||
 | 
			
		||||
  # timeout for successful migration of master pods from unschedulable node
 | 
			
		||||
  # master_pod_move_timeout: 20m
 | 
			
		||||
| 
						 | 
				
			
			@ -127,17 +136,16 @@ configLoadBalancer:
 | 
			
		|||
  # DNS zone for cluster DNS name when load balancer is configured for cluster
 | 
			
		||||
  db_hosted_zone: db.example.com
 | 
			
		||||
  # annotations to apply to service when load balancing is enabled
 | 
			
		||||
  # custom_service_annotations:
 | 
			
		||||
  #   "keyx:valuez,keya:valuea"
 | 
			
		||||
  # custom_service_annotations: "keyx:valuez,keya:valuea"
 | 
			
		||||
 | 
			
		||||
  # toggles service type load balancer pointing to the master pod of the cluster
 | 
			
		||||
  enable_master_load_balancer: "true"
 | 
			
		||||
  enable_master_load_balancer: "false"
 | 
			
		||||
  # toggles service type load balancer pointing to the replica pod of the cluster
 | 
			
		||||
  enable_replica_load_balancer: "false"
 | 
			
		||||
  # defines the DNS name string template for the master load balancer cluster
 | 
			
		||||
  master_dns_name_format: '{cluster}.{team}.staging.{hostedzone}'
 | 
			
		||||
  master_dns_name_format: '{cluster}.{team}.{hostedzone}'
 | 
			
		||||
  # defines the DNS name string template for the replica load balancer cluster
 | 
			
		||||
  replica_dns_name_format: '{cluster}-repl.{team}.staging.{hostedzone}'
 | 
			
		||||
  replica_dns_name_format: '{cluster}-repl.{team}.{hostedzone}'
 | 
			
		||||
 | 
			
		||||
# options to aid debugging of the operator itself
 | 
			
		||||
configDebug:
 | 
			
		||||
| 
						 | 
				
			
			@ -177,12 +185,20 @@ configAwsOrGcp:
 | 
			
		|||
 | 
			
		||||
# configure K8s cron job managed by the operator
 | 
			
		||||
configLogicalBackup:
 | 
			
		||||
  # backup schedule in the cron format
 | 
			
		||||
  logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
  # image for pods of the logical backup job (example runs pg_dumpall)
 | 
			
		||||
  logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup"
 | 
			
		||||
  # S3 Access Key ID
 | 
			
		||||
  logical_backup_s3_access_key_id: ""
 | 
			
		||||
  # S3 bucket to store backup results
 | 
			
		||||
  logical_backup_s3_bucket: "my-bucket-url"
 | 
			
		||||
  # S3 endpoint url when not using AWS
 | 
			
		||||
  logical_backup_s3_endpoint: ""
 | 
			
		||||
  # S3 Secret Access Key
 | 
			
		||||
  logical_backup_s3_secret_access_key: ""
 | 
			
		||||
  # S3 server side encription
 | 
			
		||||
  logical_backup_s3_sse: "AES256"
 | 
			
		||||
  # backup schedule in the cron format
 | 
			
		||||
  logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
 | 
			
		||||
# automate creation of human users with teams API service
 | 
			
		||||
configTeamsApi:
 | 
			
		||||
| 
						 | 
				
			
			@ -219,6 +235,10 @@ rbac:
 | 
			
		|||
  # Specifies whether RBAC resources should be created
 | 
			
		||||
  create: true
 | 
			
		||||
 | 
			
		||||
crd:
 | 
			
		||||
  # Specifies whether custom resource definitions should be created
 | 
			
		||||
  create: true
 | 
			
		||||
 | 
			
		||||
serviceAccount:
 | 
			
		||||
  # Specifies whether a ServiceAccount should be created
 | 
			
		||||
  create: true
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,9 +2,6 @@ version: "2017-09-20"
 | 
			
		|||
pipeline:
 | 
			
		||||
    - id: build-postgres-operator
 | 
			
		||||
      type: script
 | 
			
		||||
      env:
 | 
			
		||||
        GOPATH: /root/go
 | 
			
		||||
        OPERATOR_TOP_DIR: /root/go/src/github.com/zalando
 | 
			
		||||
      commands:
 | 
			
		||||
        - desc: 'Update'
 | 
			
		||||
          cmd: |
 | 
			
		||||
| 
						 | 
				
			
			@ -20,10 +17,6 @@ pipeline:
 | 
			
		|||
            mv go /usr/local
 | 
			
		||||
            ln -s /usr/local/go/bin/go /usr/bin/go
 | 
			
		||||
            go version
 | 
			
		||||
        - desc: 'Symlink sources into the GOPATH'
 | 
			
		||||
          cmd: |
 | 
			
		||||
            mkdir -p $OPERATOR_TOP_DIR
 | 
			
		||||
            ln -s $(pwd) $OPERATOR_TOP_DIR/postgres-operator
 | 
			
		||||
        - desc: 'Build docker image'
 | 
			
		||||
          cmd: |
 | 
			
		||||
            export PATH=$PATH:$HOME/go/bin
 | 
			
		||||
| 
						 | 
				
			
			@ -35,15 +28,13 @@ pipeline:
 | 
			
		|||
              IMAGE=registry-write.opensource.zalan.do/acid/postgres-operator-test
 | 
			
		||||
            fi
 | 
			
		||||
            export IMAGE
 | 
			
		||||
            make tools deps docker
 | 
			
		||||
            make deps docker
 | 
			
		||||
        - desc: 'Run unit tests'
 | 
			
		||||
          cmd: |
 | 
			
		||||
            export PATH=$PATH:$HOME/go/bin
 | 
			
		||||
            cd $OPERATOR_TOP_DIR/postgres-operator
 | 
			
		||||
            go test ./...
 | 
			
		||||
        - desc: 'Run e2e tests'
 | 
			
		||||
          cmd: |
 | 
			
		||||
            cd $OPERATOR_TOP_DIR/postgres-operator
 | 
			
		||||
            make e2e
 | 
			
		||||
        - desc: 'Push docker image'
 | 
			
		||||
          cmd: |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,11 +38,13 @@ function aws_upload {
 | 
			
		|||
    # NB: $LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX already contains the leading "/" when set by the Postgres Operator
 | 
			
		||||
    PATH_TO_BACKUP=s3://$LOGICAL_BACKUP_S3_BUCKET"/spilo/"$SCOPE$LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX"/logical_backups/"$(date +%s).sql.gz
 | 
			
		||||
 | 
			
		||||
    if [ -z "$EXPECTED_SIZE" ]; then
 | 
			
		||||
        aws s3 cp - "$PATH_TO_BACKUP" --debug --sse="AES256"
 | 
			
		||||
    else
 | 
			
		||||
        aws s3 cp - "$PATH_TO_BACKUP" --debug --expected-size "$EXPECTED_SIZE" --sse="AES256"
 | 
			
		||||
    fi;
 | 
			
		||||
    args=()
 | 
			
		||||
 | 
			
		||||
    [[ ! -z "$EXPECTED_SIZE" ]] && args+=("--expected-size=$EXPECTED_SIZE")
 | 
			
		||||
    [[ ! -z "$LOGICAL_BACKUP_S3_ENDPOINT" ]] && args+=("--endpoint-url=$LOGICAL_BACKUP_S3_ENDPOINT")
 | 
			
		||||
    [[ ! "$LOGICAL_BACKUP_S3_SSE" == "" ]] && args+=("--sse=$LOGICAL_BACKUP_S3_SSE")
 | 
			
		||||
 | 
			
		||||
    aws s3 cp - "$PATH_TO_BACKUP" "${args[@]//\'/}" --debug
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_pods {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,15 +68,15 @@ declare -a search_strategy=(
 | 
			
		|||
)
 | 
			
		||||
 | 
			
		||||
function list_all_replica_pods_current_node {
 | 
			
		||||
    get_pods "labelSelector=version%3D${SCOPE},spilo-role%3Dreplica&fieldSelector=spec.nodeName%3D${CURRENT_NODENAME}" | head -n 1
 | 
			
		||||
    get_pods "labelSelector=${CLUSTER_NAME_LABEL}%3D${SCOPE},spilo-role%3Dreplica&fieldSelector=spec.nodeName%3D${CURRENT_NODENAME}" | head -n 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function list_all_replica_pods_any_node {
 | 
			
		||||
    get_pods "labelSelector=version%3D${SCOPE},spilo-role%3Dreplica" | head -n 1
 | 
			
		||||
    get_pods "labelSelector=${CLUSTER_NAME_LABEL}%3D${SCOPE},spilo-role%3Dreplica" | head -n 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function get_master_pod {
 | 
			
		||||
    get_pods "labelSelector=version%3D${SCOPE},spilo-role%3Dmaster" | head -n 1
 | 
			
		||||
    get_pods "labelSelector=${CLUSTER_NAME_LABEL}%3D${SCOPE},spilo-role%3Dmaster" | head -n 1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
CURRENT_NODENAME=$(get_current_pod | jq .items[].spec.nodeName --raw-output)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,50 @@
 | 
			
		|||
Learn how to configure and manage the Postgres Operator in your Kubernetes (K8s)
 | 
			
		||||
environment.
 | 
			
		||||
 | 
			
		||||
## Minor and major version upgrade
 | 
			
		||||
 | 
			
		||||
Minor version upgrades for PostgreSQL are handled via updating the Spilo Docker
 | 
			
		||||
image. The operator will carry out a rolling update of Pods which includes a
 | 
			
		||||
switchover (planned failover) of the master to the Pod with new minor version.
 | 
			
		||||
The switch should usually take less than 5 seconds, still clients have to
 | 
			
		||||
reconnect.
 | 
			
		||||
 | 
			
		||||
Major version upgrades are supported via [cloning](user.md#clone-directly). The
 | 
			
		||||
new cluster manifest must have a higher `version` string than the source cluster
 | 
			
		||||
and will be created from a basebackup. Depending of the cluster size, downtime
 | 
			
		||||
in this case can be significant as writes to the database should be stopped and
 | 
			
		||||
all WAL files should be archived first before cloning is started.
 | 
			
		||||
 | 
			
		||||
Note, that simply changing the version string in the `postgresql` manifest does
 | 
			
		||||
not work at present and leads to errors. Neither Patroni nor Postgres Operator
 | 
			
		||||
can do in place `pg_upgrade`. Still, it can be executed manually in the Postgres
 | 
			
		||||
container, which is tricky (i.e. systems need to be stopped, replicas have to be
 | 
			
		||||
synced) but of course faster than cloning.
 | 
			
		||||
 | 
			
		||||
## CRD Validation
 | 
			
		||||
 | 
			
		||||
[CustomResourceDefinitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions)
 | 
			
		||||
will be registered with schema validation by default when the operator is
 | 
			
		||||
deployed. The `OperatorConfiguration` CRD will only get created if the
 | 
			
		||||
`POSTGRES_OPERATOR_CONFIGURATION_OBJECT` [environment variable](../manifests/postgres-operator.yaml#L36)
 | 
			
		||||
in the deployment yaml is set and not empty.
 | 
			
		||||
 | 
			
		||||
When submitting manifests of [`postgresql`](../manifests/postgresql.crd.yaml) or
 | 
			
		||||
[`OperatorConfiguration`](../manifests/operatorconfiguration.crd.yaml) custom
 | 
			
		||||
resources with kubectl, validation can be bypassed with `--validate=false`. The
 | 
			
		||||
operator can also be configured to not register CRDs with validation on `ADD` or
 | 
			
		||||
`UPDATE` events. Running instances are not affected when enabling the validation
 | 
			
		||||
afterwards unless the manifests is not changed then. Note, that the provided CRD
 | 
			
		||||
manifests contain the validation for users to understand what schema is
 | 
			
		||||
enforced.
 | 
			
		||||
 | 
			
		||||
Once the validation is enabled it can only be disabled manually by editing or
 | 
			
		||||
patching the CRD manifest:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
zk8 patch crd postgresqls.acid.zalan.do -p '{"spec":{"validation": null}}'
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Namespaces
 | 
			
		||||
 | 
			
		||||
### Select the namespace to deploy to
 | 
			
		||||
| 
						 | 
				
			
			@ -32,7 +76,7 @@ By default, the operator watches the namespace it is deployed to. You can
 | 
			
		|||
change this by setting the `WATCHED_NAMESPACE` var in the `env` section of the
 | 
			
		||||
[operator deployment](../manifests/postgres-operator.yaml) manifest or by
 | 
			
		||||
altering the `watched_namespace` field in the operator
 | 
			
		||||
[ConfigMap](../manifests/configmap.yaml#L79).
 | 
			
		||||
[configuration](../manifests/postgresql-operator-default-configuration.yaml#L49).
 | 
			
		||||
In the case both are set, the env var takes the precedence. To make the
 | 
			
		||||
operator listen to all namespaces, explicitly set the field/env var to "`*`".
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,8 +115,6 @@ is used by the operator to connect to the clusters after creation.
 | 
			
		|||
 | 
			
		||||
## Role-based access control for the operator
 | 
			
		||||
 | 
			
		||||
### Service account and cluster roles
 | 
			
		||||
 | 
			
		||||
The manifest [`operator-service-account-rbac.yaml`](../manifests/operator-service-account-rbac.yaml)
 | 
			
		||||
defines the service account, cluster roles and bindings needed for the operator
 | 
			
		||||
to function under access control restrictions. To deploy the operator with this
 | 
			
		||||
| 
						 | 
				
			
			@ -85,6 +127,8 @@ kubectl create -f manifests/postgres-operator.yaml
 | 
			
		|||
kubectl create -f manifests/minimal-postgres-manifest.yaml
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Service account and cluster roles
 | 
			
		||||
 | 
			
		||||
Note that the service account is named `zalando-postgres-operator`. You may have
 | 
			
		||||
to change the `service_account_name` in the operator ConfigMap and
 | 
			
		||||
`serviceAccountName` in the `postgres-operator` deployment appropriately. This
 | 
			
		||||
| 
						 | 
				
			
			@ -92,12 +136,6 @@ is done intentionally to avoid breaking those setups that already work with the
 | 
			
		|||
default `operator` account. In the future the operator should ideally be run
 | 
			
		||||
under the `zalando-postgres-operator` service account.
 | 
			
		||||
 | 
			
		||||
The service account defined in `operator-service-account-rbac.yaml` acquires
 | 
			
		||||
some privileges not used by the operator (i.e. we only need `list` and `watch`
 | 
			
		||||
on `configmaps` resources). This is also done intentionally to avoid breaking
 | 
			
		||||
things if someone decides to configure the same service account in the
 | 
			
		||||
operator's ConfigMap to run Postgres clusters.
 | 
			
		||||
 | 
			
		||||
### Give K8s users access to create/list `postgresqls`
 | 
			
		||||
 | 
			
		||||
By default `postgresql` custom resources can only be listed and changed by
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +153,7 @@ that are aggregated into the K8s [default roles](https://kubernetes.io/docs/refe
 | 
			
		|||
 | 
			
		||||
To ensure Postgres pods are running on nodes without any other application pods,
 | 
			
		||||
you can use [taints and tolerations](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/)
 | 
			
		||||
and configure the required toleration in the operator ConfigMap.
 | 
			
		||||
and configure the required toleration in the operator configuration.
 | 
			
		||||
 | 
			
		||||
As an example you can set following node taint:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +171,20 @@ metadata:
 | 
			
		|||
  name: postgres-operator
 | 
			
		||||
data:
 | 
			
		||||
  toleration: "key:postgres,operator:Exists,effect:NoSchedule"
 | 
			
		||||
  ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For an OperatorConfiguration resource the toleration should be defined like
 | 
			
		||||
this:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: OperatorConfiguration
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgresql-configuration
 | 
			
		||||
configuration:
 | 
			
		||||
  kubernetes:
 | 
			
		||||
    toleration:
 | 
			
		||||
      postgres: "key:postgres,operator:Exists,effect:NoSchedule"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Note that the K8s version 1.13 brings [taint-based eviction](https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/#taint-based-evictions)
 | 
			
		||||
| 
						 | 
				
			
			@ -148,7 +199,7 @@ completely, specify the toleration by leaving out the `tolerationSeconds` value
 | 
			
		|||
 | 
			
		||||
To ensure Postgres pods are running on different topologies, you can use
 | 
			
		||||
[pod anti affinity](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/)
 | 
			
		||||
and configure the required topology in the operator ConfigMap.
 | 
			
		||||
and configure the required topology in the operator configuration.
 | 
			
		||||
 | 
			
		||||
Enable pod anti affinity by adding following line to the operator ConfigMap:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -161,21 +212,22 @@ data:
 | 
			
		|||
  enable_pod_antiaffinity: "true"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
By default the topology key for the pod anti affinity is set to
 | 
			
		||||
`kubernetes.io/hostname`, you can set another topology key e.g.
 | 
			
		||||
`failure-domain.beta.kubernetes.io/zone` by adding following line to the
 | 
			
		||||
operator ConfigMap, see [built-in node labels](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#interlude-built-in-node-labels) for available topology keys:
 | 
			
		||||
Likewise, when using an OperatorConfiguration resource add:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: ConfigMap
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: OperatorConfiguration
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgres-operator
 | 
			
		||||
data:
 | 
			
		||||
  enable_pod_antiaffinity: "true"
 | 
			
		||||
  pod_antiaffinity_topology_key: "failure-domain.beta.kubernetes.io/zone"
 | 
			
		||||
  name: postgresql-configuration
 | 
			
		||||
configuration:
 | 
			
		||||
  kubernetes:
 | 
			
		||||
    enable_pod_antiaffinity: true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
By default the topology key for the pod anti affinity is set to
 | 
			
		||||
`kubernetes.io/hostname`, you can set another topology key e.g.
 | 
			
		||||
`failure-domain.beta.kubernetes.io/zone`. See [built-in node labels](https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#interlude-built-in-node-labels) for available topology keys.
 | 
			
		||||
 | 
			
		||||
## Pod Disruption Budget
 | 
			
		||||
 | 
			
		||||
By default the operator uses a PodDisruptionBudget (PDB) to protect the cluster
 | 
			
		||||
| 
						 | 
				
			
			@ -184,6 +236,7 @@ parameter of the PDB is set to `1` which prevents killing masters in single-node
 | 
			
		|||
clusters and/or the last remaining running instance in a multi-node cluster.
 | 
			
		||||
 | 
			
		||||
The PDB is only relaxed in two scenarios:
 | 
			
		||||
 | 
			
		||||
* If a cluster is scaled down to `0` instances (e.g. for draining nodes)
 | 
			
		||||
* If the PDB is disabled in the configuration (`enable_pod_disruption_budget`)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -200,6 +253,17 @@ Postgres cluster, in order to identify its child objects. The typical use case
 | 
			
		|||
is to add labels that identifies the `Pods` created by the operator, in order
 | 
			
		||||
to implement fine-controlled `NetworkPolicies`.
 | 
			
		||||
 | 
			
		||||
**postgres-operator ConfigMap**
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: v1
 | 
			
		||||
kind: ConfigMap
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgres-operator
 | 
			
		||||
data:
 | 
			
		||||
  inherited_labels: application,environment
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**OperatorConfiguration**
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +276,6 @@ configuration:
 | 
			
		|||
    inherited_labels:
 | 
			
		||||
    - application
 | 
			
		||||
    - environment
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**cluster manifest**
 | 
			
		||||
| 
						 | 
				
			
			@ -226,7 +289,7 @@ metadata:
 | 
			
		|||
    application: my-app
 | 
			
		||||
    environment: demo
 | 
			
		||||
spec:
 | 
			
		||||
...
 | 
			
		||||
  ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**network policy**
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +304,6 @@ spec:
 | 
			
		|||
    matchLabels:
 | 
			
		||||
      application: my-app
 | 
			
		||||
      environment: demo
 | 
			
		||||
...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -264,7 +326,19 @@ metadata:
 | 
			
		|||
data:
 | 
			
		||||
  # referencing config map with custom settings
 | 
			
		||||
  pod_environment_configmap: postgres-pod-config
 | 
			
		||||
  ...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**OperatorConfiguration**
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: OperatorConfiguration
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgresql-operator-configuration
 | 
			
		||||
configuration:
 | 
			
		||||
  kubernetes:
 | 
			
		||||
    # referencing config map with custom settings
 | 
			
		||||
    pod_environment_configmap: postgres-pod-config
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
**referenced ConfigMap `postgres-pod-config`**
 | 
			
		||||
| 
						 | 
				
			
			@ -299,7 +373,7 @@ services: one for the master pod and one for replica pods. To expose these
 | 
			
		|||
services to an outer network, one can attach load balancers to them by setting
 | 
			
		||||
`enableMasterLoadBalancer` and/or `enableReplicaLoadBalancer` to `true` in the
 | 
			
		||||
cluster manifest. In the case any of these variables are omitted from the
 | 
			
		||||
manifest, the operator configmap's settings `enable_master_load_balancer` and
 | 
			
		||||
manifest, the operator configuration settings `enable_master_load_balancer` and
 | 
			
		||||
`enable_replica_load_balancer` apply. Note that the operator settings affect
 | 
			
		||||
all Postgresql services running in all namespaces watched by the operator.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -345,12 +419,12 @@ external systems but defined for an individual Postgres cluster in its manifest.
 | 
			
		|||
A typical example is a role for connections from an application that uses the
 | 
			
		||||
database.
 | 
			
		||||
 | 
			
		||||
* **Human users** originate from the Teams API that returns a list of the team
 | 
			
		||||
members given a team id. The operator differentiates between (a) product teams
 | 
			
		||||
that own a particular Postgres cluster and are granted admin rights to maintain
 | 
			
		||||
it, and (b) Postgres superuser teams that get the superuser access to all
 | 
			
		||||
Postgres databases running in a K8s cluster for the purposes of maintaining and
 | 
			
		||||
troubleshooting.
 | 
			
		||||
* **Human users** originate from the [Teams API](user.md#teams-api-roles) that
 | 
			
		||||
returns a list of the team members given a team id. The operator differentiates
 | 
			
		||||
between (a) product teams that own a particular Postgres cluster and are granted
 | 
			
		||||
admin rights to maintain it, and (b) Postgres superuser teams that get the
 | 
			
		||||
superuser access to all Postgres databases running in a K8s cluster for the
 | 
			
		||||
purposes of maintaining and troubleshooting.
 | 
			
		||||
 | 
			
		||||
## Understanding rolling update of Spilo pods
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -414,7 +488,7 @@ A secret can be pre-provisioned in different ways:
 | 
			
		|||
 | 
			
		||||
With the v1.2 release the Postgres Operator is shipped with a browser-based
 | 
			
		||||
configuration user interface (UI) that simplifies managing Postgres clusters
 | 
			
		||||
with the operator. The UI runs with Node.js and comes with it's own docker
 | 
			
		||||
with the operator. The UI runs with Node.js and comes with it's own Docker
 | 
			
		||||
image.
 | 
			
		||||
 | 
			
		||||
Run NPM to continuously compile `tags/js` code. Basically, it creates an
 | 
			
		||||
| 
						 | 
				
			
			@ -426,14 +500,14 @@ Run NPM to continuously compile `tags/js` code. Basically, it creates an
 | 
			
		|||
 | 
			
		||||
To build the Docker image open a shell and change to the `ui` folder. Then run:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
docker build -t registry.opensource.zalan.do/acid/postgres-operator-ui:v1.2.0 .
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Apply all manifests for the `ui/manifests` folder to deploy the Postgres
 | 
			
		||||
Operator UI on K8s. For local tests you don't need the Ingress resource.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
kubectl apply -f ui/manifests
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -443,6 +517,6 @@ to the K8s and Postgres Operator REST API. You can use the provided
 | 
			
		|||
`run_local.sh` script for this. Make sure it uses the correct URL to your K8s
 | 
			
		||||
API server, e.g. for minikube it would be `https://192.168.99.100:8443`.
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```bash
 | 
			
		||||
./run_local.sh
 | 
			
		||||
```
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,23 +27,20 @@ git clone https://github.com/zalando/postgres-operator.git
 | 
			
		|||
 | 
			
		||||
## Building the operator
 | 
			
		||||
 | 
			
		||||
You need Glide to fetch all dependencies. Install it with:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
make tools
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Next, install dependencies with glide by issuing:
 | 
			
		||||
We use [Go Modules](https://github.com/golang/go/wiki/Modules) for handling
 | 
			
		||||
dependencies. When using Go below v1.13 you need to explicitly enable Go modules
 | 
			
		||||
by setting the `GO111MODULE` environment variable to `on`. The make targets do
 | 
			
		||||
this for you, so simply run
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
make deps
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
This would take a while to complete. You have to redo `make deps` every time
 | 
			
		||||
you dependencies list changes, i.e. after adding a new library dependency.
 | 
			
		||||
your dependencies list changes, i.e. after adding a new library dependency.
 | 
			
		||||
 | 
			
		||||
Build the operator with the `make docker` command. You may define the TAG
 | 
			
		||||
variable to assign an explicit tag to your docker image and the IMAGE to set
 | 
			
		||||
variable to assign an explicit tag to your Docker image and the IMAGE to set
 | 
			
		||||
the image name. By default, the tag is computed with
 | 
			
		||||
`git describe --tags --always --dirty` and the image is
 | 
			
		||||
`registry.opensource.zalan.do/acid/postgres-operator`
 | 
			
		||||
| 
						 | 
				
			
			@ -63,10 +60,10 @@ The binary will be placed into the build directory.
 | 
			
		|||
 | 
			
		||||
## Deploying self build image
 | 
			
		||||
 | 
			
		||||
The fastest way to run and test your docker image locally is to reuse the docker
 | 
			
		||||
from [minikube](https://github.com/kubernetes/minikube/releases) or use the
 | 
			
		||||
`load docker-image` from [kind](https://kind.sigs.k8s.io/). The following steps
 | 
			
		||||
will get you the docker image built and deployed.
 | 
			
		||||
The fastest way to run and test your Docker image locally is to reuse the Docker
 | 
			
		||||
environment from [minikube](https://github.com/kubernetes/minikube/releases)
 | 
			
		||||
or use the `load docker-image` from [kind](https://kind.sigs.k8s.io/). The
 | 
			
		||||
following steps will get you the Docker image built and deployed.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# minikube
 | 
			
		||||
| 
						 | 
				
			
			@ -165,7 +162,7 @@ The operator also supports pprof endpoints listed at the
 | 
			
		|||
* /debug/pprof/trace
 | 
			
		||||
 | 
			
		||||
It's possible to attach a debugger to troubleshoot postgres-operator inside a
 | 
			
		||||
docker container. It's possible with [gdb](https://www.gnu.org/software/gdb/)
 | 
			
		||||
Docker container. It's possible with [gdb](https://www.gnu.org/software/gdb/)
 | 
			
		||||
and [delve](https://github.com/derekparker/delve). Since the latter one is a
 | 
			
		||||
specialized debugger for Go, we will use it as an example. To use it you need:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -214,14 +211,7 @@ dlv connect 127.0.0.1:DLV_PORT
 | 
			
		|||
To run all unit tests, you can simply do:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
go test ./...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For go 1.9 `vendor` directory would be excluded automatically. For previous
 | 
			
		||||
versions you can exclude it manually:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
go test $(glide novendor)
 | 
			
		||||
go test ./pkg/...
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
In case if you need to debug your unit test, it's possible to use delve:
 | 
			
		||||
| 
						 | 
				
			
			@ -294,6 +284,7 @@ manifest files:
 | 
			
		|||
 | 
			
		||||
Postgres manifest parameters are defined in the [api package](../pkg/apis/acid.zalan.do/v1/postgresql_type.go).
 | 
			
		||||
The operator behavior has to be implemented at least in [k8sres.go](../pkg/cluster/k8sres.go).
 | 
			
		||||
Validation of CRD parameters is controlled in [crd.go](../pkg/apis/acid.zalan.do/v1/crds.go).
 | 
			
		||||
Please, reflect your changes in tests, for example in:
 | 
			
		||||
* [config_test.go](../pkg/util/config/config_test.go)
 | 
			
		||||
* [k8sres_test.go](../pkg/cluster/k8sres_test.go)
 | 
			
		||||
| 
						 | 
				
			
			@ -304,6 +295,7 @@ Please, reflect your changes in tests, for example in:
 | 
			
		|||
For the CRD-based configuration, please update the following files:
 | 
			
		||||
* the default [OperatorConfiguration](../manifests/postgresql-operator-default-configuration.yaml)
 | 
			
		||||
* the Helm chart's [values-crd file](../charts/postgres-operator/values.yaml)
 | 
			
		||||
* the CRD's [validation](../manifests/operatorconfiguration.crd.yaml)
 | 
			
		||||
 | 
			
		||||
Reflect the changes in the ConfigMap configuration as well (note that numeric
 | 
			
		||||
and boolean parameters have to use double quotes here):
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -13,7 +13,7 @@ manages PostgreSQL clusters on Kubernetes (K8s):
 | 
			
		|||
 | 
			
		||||
2. The operator also watches updates to [its own configuration](../manifests/configmap.yaml)
 | 
			
		||||
   and alters running Postgres clusters if necessary.  For instance, if the
 | 
			
		||||
   docker image in a pod is changed, the operator carries out the rolling
 | 
			
		||||
   Docker image in a pod is changed, the operator carries out the rolling
 | 
			
		||||
   update, which means it re-spawns pods of each managed StatefulSet one-by-one
 | 
			
		||||
   with the new Docker image.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -55,15 +55,15 @@ kubectl create -f manifests/postgres-operator.yaml  # deployment
 | 
			
		|||
```
 | 
			
		||||
 | 
			
		||||
There is a [Kustomization](https://github.com/kubernetes-sigs/kustomize)
 | 
			
		||||
manifest that [combines the mentioned resources](../manifests/kustomization.yaml) -
 | 
			
		||||
it can be used with kubectl 1.14 or newer as easy as:
 | 
			
		||||
manifest that [combines the mentioned resources](../manifests/kustomization.yaml)
 | 
			
		||||
(except for the CRD) - it can be used with kubectl 1.14 or newer as easy as:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
kubectl apply -k github.com/zalando/postgres-operator/manifests
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
For convenience, we have automated starting the operator with minikube using the
 | 
			
		||||
`run_operator_locally` script. It applies the [`acid-minimal-cluster`](../manifests/minimal-postgres-manifest).
 | 
			
		||||
`run_operator_locally` script. It applies the [`acid-minimal-cluster`](../manifests/minimal-postgres-manifest.yaml).
 | 
			
		||||
manifest.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
| 
						 | 
				
			
			@ -73,22 +73,23 @@ manifest.
 | 
			
		|||
### Helm chart
 | 
			
		||||
 | 
			
		||||
Alternatively, the operator can be installed by using the provided [Helm](https://helm.sh/)
 | 
			
		||||
chart which saves you the manual steps. Therefore, install the helm CLI on your
 | 
			
		||||
machine. After initializing helm (and its server component Tiller) in your local
 | 
			
		||||
cluster you can install the operator chart. You can define a release name that
 | 
			
		||||
is prepended to the operator resource's names.
 | 
			
		||||
 | 
			
		||||
Use `--name zalando` to match with the default service account name as older
 | 
			
		||||
operator versions do not support custom names for service accounts. To use
 | 
			
		||||
CRD-based configuration you need to specify the [values-crd yaml file](../charts/values-crd.yaml).
 | 
			
		||||
chart which saves you the manual steps. Clone this repo and change directory to
 | 
			
		||||
the repo root. With Helm v3 installed you should be able to run:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# 1) initialize helm
 | 
			
		||||
helm init
 | 
			
		||||
# 2) install postgres-operator chart
 | 
			
		||||
helm install --name zalando ./charts/postgres-operator
 | 
			
		||||
helm install postgres-operator ./charts/postgres-operator
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
To use CRD-based configuration you need to specify the [values-crd yaml file](../charts/postgres-operator/values-crd.yaml).
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
helm install postgres-operator ./charts/postgres-operator -f ./charts/postgres-operator/values-crd.yaml
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The chart works with both Helm 2 and Helm 3. The `crd-install` hook from v2 will
 | 
			
		||||
be skipped with warning when using v3. Documentation for installing applications
 | 
			
		||||
with Helm 2 can be found in the [v2 docs](https://v2.helm.sh/docs/).
 | 
			
		||||
 | 
			
		||||
### Operator Lifecycle Manager (OLM)
 | 
			
		||||
 | 
			
		||||
The [Operator Lifecycle Manager (OLM)](https://github.com/operator-framework/operator-lifecycle-manager)
 | 
			
		||||
| 
						 | 
				
			
			@ -119,15 +120,15 @@ kubectl get pod -l app.kubernetes.io/name=postgres-operator
 | 
			
		|||
kubectl create -f manifests/minimal-postgres-manifest.yaml
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
After the cluster manifest is submitted the operator will create Service and
 | 
			
		||||
Endpoint resources and a StatefulSet which spins up new Pod(s) given the number
 | 
			
		||||
of instances specified in the manifest. All resources are named like the
 | 
			
		||||
cluster. The database pods can be identified by their number suffix, starting
 | 
			
		||||
from `-0`. They run the [Spilo](https://github.com/zalando/spilo) container
 | 
			
		||||
image by Zalando. As for the services and endpoints, there will be one for the
 | 
			
		||||
master pod and another one for all the replicas (`-repl` suffix). Check if all
 | 
			
		||||
components are coming up. Use the label `application=spilo` to filter and list
 | 
			
		||||
the label `spilo-role` to see who is currently the master.
 | 
			
		||||
After the cluster manifest is submitted and passed the validation the operator
 | 
			
		||||
will create Service and Endpoint resources and a StatefulSet which spins up new
 | 
			
		||||
Pod(s) given the number of instances specified in the manifest. All resources
 | 
			
		||||
are named like the cluster. The database pods can be identified by their number
 | 
			
		||||
suffix, starting from `-0`. They run the [Spilo](https://github.com/zalando/spilo)
 | 
			
		||||
container image by Zalando. As for the services and endpoints, there will be one
 | 
			
		||||
for the master pod and another one for all the replicas (`-repl` suffix). Check
 | 
			
		||||
if all components are coming up. Use the label `application=spilo` to filter and
 | 
			
		||||
list the label `spilo-role` to see who is currently the master.
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
# check the deployed cluster
 | 
			
		||||
| 
						 | 
				
			
			@ -154,9 +155,12 @@ export PGPORT=$(echo $HOST_PORT | cut -d: -f 2)
 | 
			
		|||
```
 | 
			
		||||
 | 
			
		||||
Retrieve the password from the K8s Secret that is created in your cluster.
 | 
			
		||||
Non-encrypted connections are rejected by default, so set the SSL mode to
 | 
			
		||||
require:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
export PGPASSWORD=$(kubectl get secret postgres.acid-minimal-cluster.credentials -o 'jsonpath={.data.password}' | base64 -d)
 | 
			
		||||
export PGSSLMODE=require
 | 
			
		||||
psql -U postgres
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,9 @@ Individual Postgres clusters are described by the Kubernetes *cluster manifest*
 | 
			
		|||
that has the structure defined by the `postgresql` CRD (custom resource
 | 
			
		||||
definition). The following section describes the structure of the manifest and
 | 
			
		||||
the purpose of individual keys. You can take a look at the examples of the
 | 
			
		||||
[minimal](../manifests/minimal-postgres-manifest.yaml)
 | 
			
		||||
[minimal](../../manifests/minimal-postgres-manifest.yaml)
 | 
			
		||||
and the
 | 
			
		||||
[complete](../manifests/complete-postgres-manifest.yaml)
 | 
			
		||||
[complete](../../manifests/complete-postgres-manifest.yaml)
 | 
			
		||||
cluster manifests.
 | 
			
		||||
 | 
			
		||||
When Kubernetes resources, such as memory, CPU or volumes, are configured,
 | 
			
		||||
| 
						 | 
				
			
			@ -62,7 +62,7 @@ These parameters are grouped directly under  the `spec` key in the manifest.
 | 
			
		|||
  field.
 | 
			
		||||
 | 
			
		||||
* **dockerImage**
 | 
			
		||||
  custom docker image that overrides the **docker_image** operator parameter.
 | 
			
		||||
  custom Docker image that overrides the **docker_image** operator parameter.
 | 
			
		||||
  It should be a [Spilo](https://github.com/zalando/spilo) image. Optional.
 | 
			
		||||
 | 
			
		||||
* **spiloFSGroup**
 | 
			
		||||
| 
						 | 
				
			
			@ -118,8 +118,13 @@ These parameters are grouped directly under  the `spec` key in the manifest.
 | 
			
		|||
   then the default priority class is taken. The priority class itself must be
 | 
			
		||||
   defined in advance. Optional.
 | 
			
		||||
 | 
			
		||||
* **podAnnotations**
 | 
			
		||||
  A map of key value pairs that gets attached as [annotations](https://kubernetes.io/docs/concepts/overview/working-with-objects/annotations/)
 | 
			
		||||
  to each pod created for the database.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
* **enableShmVolume**
 | 
			
		||||
  Start a database pod without limitations on shm memory. By default docker
 | 
			
		||||
  Start a database pod without limitations on shm memory. By default Docker
 | 
			
		||||
  limit `/dev/shm` to `64M` (see e.g. the [docker
 | 
			
		||||
  issue](https://github.com/docker-library/postgres/issues/416), which could be
 | 
			
		||||
  not enough if PostgreSQL uses parallel workers heavily. If this option is
 | 
			
		||||
| 
						 | 
				
			
			@ -180,19 +185,19 @@ explanation of `ttl` and `loop_wait` parameters.
 | 
			
		|||
 | 
			
		||||
* **ttl**
 | 
			
		||||
  Patroni `ttl` parameter value, optional. The default is set by the Spilo
 | 
			
		||||
  docker image. Optional.
 | 
			
		||||
  Docker image. Optional.
 | 
			
		||||
 | 
			
		||||
* **loop_wait**
 | 
			
		||||
  Patroni `loop_wait` parameter value, optional. The default is set by the
 | 
			
		||||
  Spilo docker image. Optional.
 | 
			
		||||
  Spilo Docker image. Optional.
 | 
			
		||||
 | 
			
		||||
* **retry_timeout**
 | 
			
		||||
  Patroni `retry_timeout` parameter value, optional. The default is set by the
 | 
			
		||||
  Spilo docker image. Optional.
 | 
			
		||||
  Spilo Docker image. Optional.
 | 
			
		||||
 | 
			
		||||
* **maximum_lag_on_failover**
 | 
			
		||||
  Patroni `maximum_lag_on_failover` parameter value, optional. The default is
 | 
			
		||||
  set by the Spilo docker image. Optional.
 | 
			
		||||
  set by the Spilo Docker image. Optional.
 | 
			
		||||
 | 
			
		||||
* **slots**
 | 
			
		||||
  permanent replication slots that Patroni preserves after failover by
 | 
			
		||||
| 
						 | 
				
			
			@ -315,7 +320,7 @@ defined in the sidecar dictionary:
 | 
			
		|||
  name of the sidecar. Required.
 | 
			
		||||
 | 
			
		||||
* **image**
 | 
			
		||||
  docker image of the sidecar. Required.
 | 
			
		||||
  Docker image of the sidecar. Required.
 | 
			
		||||
 | 
			
		||||
* **env**
 | 
			
		||||
  a dictionary of environment variables. Use usual Kubernetes definition
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,12 +10,12 @@ configuration.
 | 
			
		|||
  maps. String values containing ':' should be enclosed in quotes. The
 | 
			
		||||
  configuration is flat, parameter group names below are not reflected in the
 | 
			
		||||
  configuration structure. There is an
 | 
			
		||||
  [example](../manifests/configmap.yaml)
 | 
			
		||||
  [example](../../manifests/configmap.yaml)
 | 
			
		||||
 | 
			
		||||
* CRD-based configuration. The configuration is stored in a custom YAML
 | 
			
		||||
  manifest. The manifest is an instance of the custom resource definition (CRD)
 | 
			
		||||
  called `OperatorConfiguration`. The operator registers this CRD during the
 | 
			
		||||
  start and uses it for configuration if the [operator deployment manifest](../manifests/postgres-operator.yaml#L36)
 | 
			
		||||
  start and uses it for configuration if the [operator deployment manifest](../../manifests/postgres-operator.yaml#L36)
 | 
			
		||||
  sets the `POSTGRES_OPERATOR_CONFIGURATION_OBJECT` env variable to a non-empty
 | 
			
		||||
  value. The variable should point to the `postgresql-operator-configuration`
 | 
			
		||||
  object in the operator's namespace.
 | 
			
		||||
| 
						 | 
				
			
			@ -24,26 +24,25 @@ configuration.
 | 
			
		|||
  simply represented in the usual YAML way. There are no default values built-in
 | 
			
		||||
  in the operator, each parameter that is not supplied in the configuration
 | 
			
		||||
  receives an empty value. In order to create your own configuration just copy
 | 
			
		||||
  the [default one](../manifests/postgresql-operator-default-configuration.yaml)
 | 
			
		||||
  the [default one](../../manifests/postgresql-operator-default-configuration.yaml)
 | 
			
		||||
  and change it.
 | 
			
		||||
 | 
			
		||||
  To test the CRD-based configuration locally, use the following
 | 
			
		||||
  ```bash
 | 
			
		||||
  kubectl create -f manifests/operatorconfiguration.crd.yaml # registers the CRD
 | 
			
		||||
  kubectl create -f manifests/postgresql-operator-default-configuration.yaml
 | 
			
		||||
 | 
			
		||||
  kubectl create -f manifests/operator-service-account-rbac.yaml
 | 
			
		||||
  kubectl create -f manifests/postgres-operator.yaml # set the env var as mentioned above
 | 
			
		||||
  kubectl create -f manifests/postgresql-operator-default-configuration.yaml
 | 
			
		||||
 | 
			
		||||
  kubectl get operatorconfigurations postgresql-operator-default-configuration -o yaml
 | 
			
		||||
  ```
 | 
			
		||||
  Note that the operator first attempts to register the CRD of the
 | 
			
		||||
  `OperatorConfiguration` and then waits for an instance to be created. In
 | 
			
		||||
  between these two event the operator pod may be failing since it cannot fetch
 | 
			
		||||
  the not-yet-existing `OperatorConfiguration` instance.
 | 
			
		||||
 | 
			
		||||
The CRD-based configuration is more powerful than the one based on ConfigMaps
 | 
			
		||||
and should be used unless there is a compatibility requirement to use an already
 | 
			
		||||
existing configuration. Even in that case, it should be rather straightforward
 | 
			
		||||
to convert the configmap based configuration into the CRD-based one and restart
 | 
			
		||||
the operator. The ConfigMaps-based configuration will be deprecated and
 | 
			
		||||
to convert the ConfigMap-based configuration into the CRD-based one and restart
 | 
			
		||||
the operator. The ConfigMap-based configuration will be deprecated and
 | 
			
		||||
subsequently removed in future releases.
 | 
			
		||||
 | 
			
		||||
Note that for the CRD-based configuration groups of configuration options below
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +57,11 @@ parameters, those parameters have no effect and are replaced by the
 | 
			
		|||
`CRD_READY_WAIT_INTERVAL` and `CRD_READY_WAIT_TIMEOUT` environment variables.
 | 
			
		||||
They will be deprecated and removed in the future.
 | 
			
		||||
 | 
			
		||||
For the configmap configuration, the [default parameter values](../pkg/util/config/config.go#L14)
 | 
			
		||||
For the configmap configuration, the [default parameter values](../../pkg/util/config/config.go#L14)
 | 
			
		||||
mentioned here are likely to be overwritten in your local operator installation
 | 
			
		||||
via your local version of the operator configmap. In the case you use the
 | 
			
		||||
operator CRD, all the CRD defaults are provided in the
 | 
			
		||||
[operator's default configuration manifest](../manifests/postgresql-operator-default-configuration.yaml)
 | 
			
		||||
[operator's default configuration manifest](../../manifests/postgresql-operator-default-configuration.yaml)
 | 
			
		||||
 | 
			
		||||
Variable names are underscore-separated words.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -71,21 +70,26 @@ Variable names are underscore-separated words.
 | 
			
		|||
 | 
			
		||||
Those are top-level keys, containing both leaf keys and groups.
 | 
			
		||||
 | 
			
		||||
* **enable_crd_validation**
 | 
			
		||||
  toggles if the operator will create or update CRDs with
 | 
			
		||||
  [OpenAPI v3 schema validation](https://kubernetes.io/docs/tasks/access-kubernetes-api/custom-resources/custom-resource-definitions/#validation)
 | 
			
		||||
  The default is `true`.
 | 
			
		||||
 | 
			
		||||
* **etcd_host**
 | 
			
		||||
  Etcd connection string for Patroni defined as `host:port`. Not required when
 | 
			
		||||
  Patroni native Kubernetes support is used. The default is empty (use
 | 
			
		||||
  Kubernetes-native DCS).
 | 
			
		||||
 | 
			
		||||
* **docker_image**
 | 
			
		||||
  Spilo docker image for Postgres instances. For production, don't rely on the
 | 
			
		||||
  Spilo Docker image for Postgres instances. For production, don't rely on the
 | 
			
		||||
  default image, as it might be not the most up-to-date one. Instead, build
 | 
			
		||||
  your own Spilo image from the [github
 | 
			
		||||
  repository](https://github.com/zalando/spilo).
 | 
			
		||||
 | 
			
		||||
* **sidecar_docker_images**
 | 
			
		||||
  a map of sidecar names to docker images for the containers to run alongside
 | 
			
		||||
  Spilo. In case of the name conflict with the definition in the cluster
 | 
			
		||||
  manifest the cluster-specific one is preferred.
 | 
			
		||||
  a map of sidecar names to Docker images to run with Spilo. In case of the name
 | 
			
		||||
  conflict with the definition in the cluster manifest the cluster-specific one
 | 
			
		||||
  is preferred.
 | 
			
		||||
 | 
			
		||||
* **enable_shm_volume**
 | 
			
		||||
  Instruct operator to start any new database pod without limitations on shm
 | 
			
		||||
| 
						 | 
				
			
			@ -122,7 +126,7 @@ Those are top-level keys, containing both leaf keys and groups.
 | 
			
		|||
  containers with high memory limits due to the lack of memory on Kubernetes
 | 
			
		||||
  cluster nodes. This affects all containers created by the operator (Postgres,
 | 
			
		||||
  Scalyr sidecar, and other sidecars); to set resources for the operator's own
 | 
			
		||||
  container, change the [operator deployment manually](../manifests/postgres-operator.yaml#L20).
 | 
			
		||||
  container, change the [operator deployment manually](../../manifests/postgres-operator.yaml#L20).
 | 
			
		||||
  The default is `false`.
 | 
			
		||||
 | 
			
		||||
## Postgres users
 | 
			
		||||
| 
						 | 
				
			
			@ -168,6 +172,11 @@ configuration they are grouped under the `kubernetes` key.
 | 
			
		|||
  Postgres pods are [terminated forcefully](https://kubernetes.io/docs/concepts/workloads/pods/pod/#termination-of-pods)
 | 
			
		||||
  after this timeout. The default is `5m`.
 | 
			
		||||
 | 
			
		||||
* **custom_pod_annotations**
 | 
			
		||||
  This key/value map provides a list of annotations that get attached to each pod
 | 
			
		||||
  of a database created by the operator. If the annotation key is also provided
 | 
			
		||||
  by the database definition, the database definition value is used.
 | 
			
		||||
 | 
			
		||||
* **watched_namespace**
 | 
			
		||||
  The operator watches for Postgres objects in the given namespace. If not
 | 
			
		||||
  specified, the value is taken from the operator namespace. A special `*`
 | 
			
		||||
| 
						 | 
				
			
			@ -187,6 +196,14 @@ configuration they are grouped under the `kubernetes` key.
 | 
			
		|||
  [admin docs](../administrator.md#pod-disruption-budget) for more information.
 | 
			
		||||
  Default is true.
 | 
			
		||||
 | 
			
		||||
* **enable_init_containers**
 | 
			
		||||
  global option to allow for creating init containers to run actions before
 | 
			
		||||
  Spilo is started. Default is true.
 | 
			
		||||
 | 
			
		||||
* **enable_sidecars**
 | 
			
		||||
  global option to allow for creating sidecar containers to run alongside Spilo
 | 
			
		||||
  on the same pod. Default is true.
 | 
			
		||||
 | 
			
		||||
* **secret_name_template**
 | 
			
		||||
  a template for the name of the database user secrets generated by the
 | 
			
		||||
  operator. `{username}` is replaced with name of the secret, `{cluster}` with
 | 
			
		||||
| 
						 | 
				
			
			@ -436,6 +453,19 @@ grouped under the `logical_backup` key.
 | 
			
		|||
  S3 bucket to store backup results. The bucket has to be present and
 | 
			
		||||
  accessible by Postgres pods. Default: empty.
 | 
			
		||||
 | 
			
		||||
* **logical_backup_s3_endpoint**
 | 
			
		||||
  When using non-AWS S3 storage, endpoint can be set as a ENV variable.
 | 
			
		||||
 | 
			
		||||
* **logical_backup_s3_sse**
 | 
			
		||||
  Specify server side encription that S3 storage is using. If empty string
 | 
			
		||||
  is specified, no argument will be passed to `aws s3` command. Default: "AES256".
 | 
			
		||||
 | 
			
		||||
* **logical_backup_s3_access_key_id**
 | 
			
		||||
  When set, value will be in AWS_ACCESS_KEY_ID env variable. The Default is empty.
 | 
			
		||||
 | 
			
		||||
* **logical_backup_s3_secret_access_key**
 | 
			
		||||
  When set, value will be in AWS_SECRET_ACCESS_KEY env variable. The Default is empty.
 | 
			
		||||
 | 
			
		||||
## Debugging the operator
 | 
			
		||||
 | 
			
		||||
Options to aid debugging of the operator itself. Grouped under the `debug` key.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										129
									
								
								docs/user.md
								
								
								
								
							
							
						
						
									
										129
									
								
								docs/user.md
								
								
								
								
							| 
						 | 
				
			
			@ -13,7 +13,7 @@ kind: postgresql
 | 
			
		|||
metadata:
 | 
			
		||||
  name: acid-minimal-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  teamId: "ACID"
 | 
			
		||||
  teamId: "acid"
 | 
			
		||||
  volume:
 | 
			
		||||
    size: 1Gi
 | 
			
		||||
  numberOfInstances: 2
 | 
			
		||||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ spec:
 | 
			
		|||
  databases:
 | 
			
		||||
    foo: zalando
 | 
			
		||||
  postgresql:
 | 
			
		||||
    version: "10"
 | 
			
		||||
    version: "11"
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Once you cloned the Postgres Operator [repository](https://github.com/zalando/postgres-operator)
 | 
			
		||||
| 
						 | 
				
			
			@ -40,6 +40,17 @@ you can find this example also in the manifests folder:
 | 
			
		|||
kubectl create -f manifests/minimal-postgres-manifest.yaml
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
Make sure, the `spec` section of the manifest contains at least a `teamId`, the
 | 
			
		||||
`numberOfInstances` and the `postgresql` object with the `version` specified.
 | 
			
		||||
The minimum volume size to run the `postgresql` resource on Elastic Block
 | 
			
		||||
Storage (EBS) is `1Gi`.
 | 
			
		||||
 | 
			
		||||
Note, that the name of the cluster must start with the `teamId` and `-`. At
 | 
			
		||||
Zalando we use team IDs (nicknames) to lower the chance of duplicate cluster
 | 
			
		||||
names and colliding entities. The team ID would also be used to query an API to
 | 
			
		||||
get all members of a team and create [database roles](#teams-api-roles) for
 | 
			
		||||
them.
 | 
			
		||||
 | 
			
		||||
## Watch pods being created
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
| 
						 | 
				
			
			@ -62,10 +73,12 @@ kubectl port-forward $PGMASTER 6432:5432
 | 
			
		|||
 | 
			
		||||
Open another CLI and connect to the database. Use the generated secret of the
 | 
			
		||||
`postgres` robot user to connect to our `acid-minimal-cluster` master running
 | 
			
		||||
in Minikube:
 | 
			
		||||
in Minikube. As non-encrypted connections are rejected by default set the SSL
 | 
			
		||||
mode to require:
 | 
			
		||||
 | 
			
		||||
```bash
 | 
			
		||||
export PGPASSWORD=$(kubectl get secret postgres.acid-minimal-cluster.credentials -o 'jsonpath={.data.password}' | base64 -d)
 | 
			
		||||
export PGSSLMODE=require
 | 
			
		||||
psql -U postgres -p 6432
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +90,7 @@ cluster. It covers three use-cases:
 | 
			
		|||
* `manifest roles`: create application roles specific to the cluster described
 | 
			
		||||
in the manifest.
 | 
			
		||||
* `infrastructure roles`: create application roles that should be automatically
 | 
			
		||||
created on every
 | 
			
		||||
  cluster managed by the operator.
 | 
			
		||||
created on every cluster managed by the operator.
 | 
			
		||||
* `teams API roles`: automatically create users for every member of the team
 | 
			
		||||
owning the database cluster.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -128,9 +140,9 @@ The infrastructure roles secret is specified by the `infrastructure_roles_secret
 | 
			
		|||
parameter. The role definition looks like this (values are base64 encoded):
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
    user1: ZGJ1c2Vy
 | 
			
		||||
    password1: c2VjcmV0
 | 
			
		||||
    inrole1: b3BlcmF0b3I=
 | 
			
		||||
user1: ZGJ1c2Vy
 | 
			
		||||
password1: c2VjcmV0
 | 
			
		||||
inrole1: b3BlcmF0b3I=
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The block above describes the infrastructure role 'dbuser' with password
 | 
			
		||||
| 
						 | 
				
			
			@ -151,13 +163,13 @@ secret and a ConfigMap. The ConfigMap must have the same name as the secret.
 | 
			
		|||
The secret should contain an entry with 'rolename:rolepassword' for each role.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
    dbuser: c2VjcmV0
 | 
			
		||||
dbuser: c2VjcmV0
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
And the role description for that user should be specified in the ConfigMap.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
    data:
 | 
			
		||||
data:
 | 
			
		||||
  dbuser: |
 | 
			
		||||
    inrole: [operator, admin]  # following roles will be assigned to the new user
 | 
			
		||||
    user_flags:
 | 
			
		||||
| 
						 | 
				
			
			@ -182,6 +194,50 @@ See [infrastructure roles secret](../manifests/infrastructure-roles.yaml)
 | 
			
		|||
and [infrastructure roles configmap](../manifests/infrastructure-roles-configmap.yaml)
 | 
			
		||||
for the examples.
 | 
			
		||||
 | 
			
		||||
### Teams API roles
 | 
			
		||||
 | 
			
		||||
These roles are meant for database activity of human users. It's possible to
 | 
			
		||||
configure the operator to automatically create database roles for lets say all
 | 
			
		||||
employees of one team. They are not listed in the manifest and there are no K8s
 | 
			
		||||
secrets created for them. Instead they would use an OAuth2 token to connect. To
 | 
			
		||||
get all members of the team the operator queries a defined API endpoint that
 | 
			
		||||
returns usernames. A minimal Teams API should work like this:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
/.../<teamname> -> ["name","anothername"]
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
A ["fake" Teams API](../manifests/fake-teams-api.yaml) deployment is provided
 | 
			
		||||
in the manifests folder to set up a basic API around whatever services is used
 | 
			
		||||
for user management. The Teams API's URL is set in the operator's
 | 
			
		||||
[configuration](reference/operator_parameters.md#automatic-creation-of-human-users-in-the-database)
 | 
			
		||||
and `enable_teams_api` must be set to `true`. There are more settings available
 | 
			
		||||
to choose superusers, group roles, [PAM configuration](https://github.com/CyberDem0n/pam-oauth2)
 | 
			
		||||
etc. An OAuth2 token can be passed to the Teams API via a secret. The name for
 | 
			
		||||
this secret is configurable with the `oauth_token_secret_name` parameter.
 | 
			
		||||
 | 
			
		||||
## Resource definition
 | 
			
		||||
 | 
			
		||||
The compute resources to be used for the Postgres containers in the pods can be
 | 
			
		||||
specified in the postgresql cluster manifest.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
spec:
 | 
			
		||||
  resources:
 | 
			
		||||
    requests:
 | 
			
		||||
      cpu: 10m
 | 
			
		||||
      memory: 100Mi
 | 
			
		||||
    limits:
 | 
			
		||||
      cpu: 300m
 | 
			
		||||
      memory: 300Mi
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
The minimum limit to properly run the `postgresql` resource is `256m` for `cpu`
 | 
			
		||||
and `256Mi` for `memory`. If a lower value is set in the manifest the operator
 | 
			
		||||
will cancel ADD or UPDATE events on this resource with an error. If no
 | 
			
		||||
resources are defined in the manifest the operator will obtain the configured
 | 
			
		||||
[default requests](reference/operator_parameters.md#kubernetes-resource-requests).
 | 
			
		||||
 | 
			
		||||
## Use taints and tolerations for dedicated PostgreSQL nodes
 | 
			
		||||
 | 
			
		||||
To ensure Postgres pods are running on nodes without any other application pods,
 | 
			
		||||
| 
						 | 
				
			
			@ -189,12 +245,7 @@ you can use [taints and tolerations](https://kubernetes.io/docs/concepts/configu
 | 
			
		|||
and configure the required toleration in the manifest.
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-minimal-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  teamId: "ACID"
 | 
			
		||||
  tolerations:
 | 
			
		||||
  - key: postgres
 | 
			
		||||
    operator: Exists
 | 
			
		||||
| 
						 | 
				
			
			@ -212,11 +263,6 @@ section in the spec. There are two options here:
 | 
			
		|||
### Clone directly
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-test-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  clone:
 | 
			
		||||
    cluster: "acid-batman"
 | 
			
		||||
| 
						 | 
				
			
			@ -232,11 +278,6 @@ means that you can clone only from clusters within the same namespace.
 | 
			
		|||
### Clone from S3
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-test-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  clone:
 | 
			
		||||
    uid: "efd12e58-5786-11e8-b5a7-06148230260c"
 | 
			
		||||
| 
						 | 
				
			
			@ -265,10 +306,6 @@ For non AWS S3 following settings can be set to support cloning from other S3
 | 
			
		|||
implementations:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-test-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  clone:
 | 
			
		||||
    uid: "efd12e58-5786-11e8-b5a7-06148230260c"
 | 
			
		||||
| 
						 | 
				
			
			@ -305,7 +342,7 @@ Things to note:
 | 
			
		|||
- There is no way to transform a non-standby cluster to a standby cluster
 | 
			
		||||
  through the operator. Adding the standby section to the manifest of a running
 | 
			
		||||
  Postgres cluster will have no effect. However, it can be done through Patroni
 | 
			
		||||
  by adding the [standby_cluster] (https://github.com/zalando/patroni/blob/bd2c54581abb42a7d3a3da551edf0b8732eefd27/docs/replica_bootstrap.rst#standby-cluster)
 | 
			
		||||
  by adding the [standby_cluster](https://github.com/zalando/patroni/blob/bd2c54581abb42a7d3a3da551edf0b8732eefd27/docs/replica_bootstrap.rst#standby-cluster)
 | 
			
		||||
  section using `patronictl edit-config`. Note that the transformed standby
 | 
			
		||||
  cluster will not be doing any streaming. It will be in standby mode and allow
 | 
			
		||||
  read-only transactions only.
 | 
			
		||||
| 
						 | 
				
			
			@ -317,13 +354,7 @@ used for log aggregation, monitoring, backups or other tasks. A sidecar can be
 | 
			
		|||
specified like this:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-minimal-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  ...
 | 
			
		||||
  sidecars:
 | 
			
		||||
    - name: "container-name"
 | 
			
		||||
      image: "company/image:tag"
 | 
			
		||||
| 
						 | 
				
			
			@ -350,6 +381,10 @@ variables are always passed to sidecars:
 | 
			
		|||
The PostgreSQL volume is shared with sidecars and is mounted at
 | 
			
		||||
`/home/postgres/pgdata`.
 | 
			
		||||
 | 
			
		||||
**Note**: The operator will not create a cluster if sidecar containers are
 | 
			
		||||
specified but globally disabled in the configuration. The `enable_sidecars`
 | 
			
		||||
option must be set to `true`.
 | 
			
		||||
 | 
			
		||||
## InitContainers Support
 | 
			
		||||
 | 
			
		||||
Each cluster can specify arbitrary init containers to run. These containers can
 | 
			
		||||
| 
						 | 
				
			
			@ -357,13 +392,7 @@ be used to run custom actions before any normal and sidecar containers start.
 | 
			
		|||
An init container can be specified like this:
 | 
			
		||||
 | 
			
		||||
```yaml
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-minimal-cluster
 | 
			
		||||
spec:
 | 
			
		||||
  ...
 | 
			
		||||
  initContainers:
 | 
			
		||||
    - name: "container-name"
 | 
			
		||||
      image: "company/image:tag"
 | 
			
		||||
| 
						 | 
				
			
			@ -374,18 +403,17 @@ spec:
 | 
			
		|||
 | 
			
		||||
`initContainers` accepts full `v1.Container` definition.
 | 
			
		||||
 | 
			
		||||
**Note**: The operator will not create a cluster if `initContainers` are
 | 
			
		||||
specified but globally disabled in the configuration. The
 | 
			
		||||
`enable_init_containers` option must be set to `true`.
 | 
			
		||||
 | 
			
		||||
## Increase volume size
 | 
			
		||||
 | 
			
		||||
PostgreSQL operator supports statefulset volume resize if you're using the
 | 
			
		||||
Postgres operator supports statefulset volume resize if you're using the
 | 
			
		||||
operator on top of AWS. For that you need to change the size field of the
 | 
			
		||||
volume description in the cluster manifest and apply the change:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
apiVersion: "acid.zalan.do/v1"
 | 
			
		||||
kind: postgresql
 | 
			
		||||
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-test-cluster
 | 
			
		||||
```yaml
 | 
			
		||||
spec:
 | 
			
		||||
  volume:
 | 
			
		||||
    size: 5Gi # new volume size
 | 
			
		||||
| 
						 | 
				
			
			@ -414,7 +442,8 @@ size of volumes that correspond to the previously running pods is not changed.
 | 
			
		|||
You can enable logical backups from the cluster manifest by adding the following
 | 
			
		||||
parameter in the spec section:
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
```yaml
 | 
			
		||||
spec:
 | 
			
		||||
  enableLogicalBackup: true
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,13 +23,12 @@ ifndef GOPATH
 | 
			
		|||
	GOPATH := $(HOME)/go
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
KIND_PATH := $(GOPATH)/bin
 | 
			
		||||
PATH := $(GOPATH)/bin:$(PATH)
 | 
			
		||||
 | 
			
		||||
default: tools
 | 
			
		||||
 | 
			
		||||
clean:
 | 
			
		||||
	rm -fr manifests
 | 
			
		||||
	rm -rf manifests
 | 
			
		||||
 | 
			
		||||
copy: clean
 | 
			
		||||
	mkdir manifests
 | 
			
		||||
| 
						 | 
				
			
			@ -43,10 +42,7 @@ push: docker
 | 
			
		|||
 | 
			
		||||
tools: docker
 | 
			
		||||
	# install pinned version of 'kind'
 | 
			
		||||
	# leave the name as is to avoid overwriting official binary named `kind`
 | 
			
		||||
	wget https://github.com/kubernetes-sigs/kind/releases/download/v0.4.0/kind-linux-amd64
 | 
			
		||||
	chmod +x kind-linux-amd64
 | 
			
		||||
	mv kind-linux-amd64 $(KIND_PATH)
 | 
			
		||||
	GO111MODULE=on go get sigs.k8s.io/kind@v0.5.1
 | 
			
		||||
 | 
			
		||||
test:
 | 
			
		||||
	./run.sh
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										14
									
								
								e2e/run.sh
								
								
								
								
							
							
						
						
									
										14
									
								
								e2e/run.sh
								
								
								
								
							| 
						 | 
				
			
			@ -30,15 +30,15 @@ function pull_images(){
 | 
			
		|||
function start_kind(){
 | 
			
		||||
 | 
			
		||||
  # avoid interference with previous test runs
 | 
			
		||||
  if [[ $(kind-linux-amd64 get clusters | grep "^${cluster_name}*") != "" ]]
 | 
			
		||||
  if [[ $(kind get clusters | grep "^${cluster_name}*") != "" ]]
 | 
			
		||||
  then
 | 
			
		||||
    kind-linux-amd64 delete cluster --name ${cluster_name}
 | 
			
		||||
    kind delete cluster --name ${cluster_name}
 | 
			
		||||
  fi
 | 
			
		||||
 | 
			
		||||
  kind-linux-amd64 create cluster --name ${cluster_name} --config kind-cluster-postgres-operator-e2e-tests.yaml
 | 
			
		||||
  kind-linux-amd64 load docker-image "${operator_image}" --name ${cluster_name}
 | 
			
		||||
  kind-linux-amd64 load docker-image "${e2e_test_image}" --name ${cluster_name}
 | 
			
		||||
  KUBECONFIG="$(kind-linux-amd64 get kubeconfig-path --name=${cluster_name})"
 | 
			
		||||
  kind create cluster --name ${cluster_name} --config kind-cluster-postgres-operator-e2e-tests.yaml
 | 
			
		||||
  kind load docker-image "${operator_image}" --name ${cluster_name}
 | 
			
		||||
  kind load docker-image "${e2e_test_image}" --name ${cluster_name}
 | 
			
		||||
  KUBECONFIG="$(kind get kubeconfig-path --name=${cluster_name})"
 | 
			
		||||
  export KUBECONFIG
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,7 @@ function run_tests(){
 | 
			
		|||
 | 
			
		||||
function clean_up(){
 | 
			
		||||
  unset KUBECONFIG
 | 
			
		||||
  kind-linux-amd64 delete cluster --name ${cluster_name}
 | 
			
		||||
  kind delete cluster --name ${cluster_name}
 | 
			
		||||
  rm -rf ${kubeconfig_path}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -182,17 +182,12 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
 | 
			
		||||
        # update the cluster-wide image of the logical backup pod
 | 
			
		||||
        image = "test-image-name"
 | 
			
		||||
        config_map_patch = {
 | 
			
		||||
        patch_logical_backup_image = {
 | 
			
		||||
            "data": {
 | 
			
		||||
               "logical_backup_docker_image": image,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        k8s.api.core_v1.patch_namespaced_config_map("postgres-operator", "default", config_map_patch)
 | 
			
		||||
 | 
			
		||||
        operator_pod = k8s.api.core_v1.list_namespaced_pod(
 | 
			
		||||
            'default', label_selector="name=postgres-operator").items[0].metadata.name
 | 
			
		||||
        k8s.api.core_v1.delete_namespaced_pod(operator_pod, "default")  # restart reloads the conf
 | 
			
		||||
        k8s.wait_for_operator_pod_start()
 | 
			
		||||
        k8s.update_config(patch_logical_backup_image)
 | 
			
		||||
 | 
			
		||||
        jobs = k8s.get_logical_backup_job().items
 | 
			
		||||
        actual_image = jobs[0].spec.job_template.spec.template.spec.containers[0].image
 | 
			
		||||
| 
						 | 
				
			
			@ -319,6 +314,14 @@ class K8s:
 | 
			
		|||
    def wait_for_logical_backup_job_creation(self):
 | 
			
		||||
        self.wait_for_logical_backup_job(expected_num_of_jobs=1)
 | 
			
		||||
 | 
			
		||||
    def update_config(self, config_map_patch):
 | 
			
		||||
        self.api.core_v1.patch_namespaced_config_map("postgres-operator", "default", config_map_patch)
 | 
			
		||||
 | 
			
		||||
        operator_pod = self.api.core_v1.list_namespaced_pod(
 | 
			
		||||
            'default', label_selector="name=postgres-operator").items[0].metadata.name
 | 
			
		||||
        self.api.core_v1.delete_namespaced_pod(operator_pod, "default")  # restart reloads the conf
 | 
			
		||||
        self.wait_for_operator_pod_start()
 | 
			
		||||
 | 
			
		||||
    def create_with_kubectl(self, path):
 | 
			
		||||
        subprocess.run(["kubectl", "create", "-f", path])
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,308 +0,0 @@
 | 
			
		|||
hash: e4d0d48b4142d5d335e48e030621564207f3aa288ea1e41e0d1e4d29135de3e8
 | 
			
		||||
updated: 2019-02-25T14:54:51.736946406+01:00
 | 
			
		||||
imports:
 | 
			
		||||
- name: github.com/aws/aws-sdk-go
 | 
			
		||||
  version: e8b22c9937cae1fee9bc364a88f3752cc4d1ac2f
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - aws
 | 
			
		||||
  - aws/awserr
 | 
			
		||||
  - aws/awsutil
 | 
			
		||||
  - aws/client
 | 
			
		||||
  - aws/client/metadata
 | 
			
		||||
  - aws/corehandlers
 | 
			
		||||
  - aws/credentials
 | 
			
		||||
  - aws/credentials/ec2rolecreds
 | 
			
		||||
  - aws/credentials/endpointcreds
 | 
			
		||||
  - aws/credentials/processcreds
 | 
			
		||||
  - aws/credentials/stscreds
 | 
			
		||||
  - aws/csm
 | 
			
		||||
  - aws/defaults
 | 
			
		||||
  - aws/ec2metadata
 | 
			
		||||
  - aws/endpoints
 | 
			
		||||
  - aws/request
 | 
			
		||||
  - aws/session
 | 
			
		||||
  - aws/signer/v4
 | 
			
		||||
  - internal/ini
 | 
			
		||||
  - internal/sdkio
 | 
			
		||||
  - internal/sdkrand
 | 
			
		||||
  - internal/sdkuri
 | 
			
		||||
  - internal/shareddefaults
 | 
			
		||||
  - private/protocol
 | 
			
		||||
  - private/protocol/ec2query
 | 
			
		||||
  - private/protocol/query
 | 
			
		||||
  - private/protocol/query/queryutil
 | 
			
		||||
  - private/protocol/rest
 | 
			
		||||
  - private/protocol/xml/xmlutil
 | 
			
		||||
  - service/ec2
 | 
			
		||||
  - service/sts
 | 
			
		||||
- name: github.com/davecgh/go-spew
 | 
			
		||||
  version: 782f4967f2dc4564575ca782fe2d04090b5faca8
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - spew
 | 
			
		||||
- name: github.com/docker/spdystream
 | 
			
		||||
  version: 449fdfce4d962303d702fec724ef0ad181c92528
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - spdy
 | 
			
		||||
- name: github.com/ghodss/yaml
 | 
			
		||||
  version: 73d445a93680fa1a78ae23a5839bad48f32ba1ee
 | 
			
		||||
- name: github.com/gogo/protobuf
 | 
			
		||||
  version: c0656edd0d9eab7c66d1eb0c568f9039345796f7
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - proto
 | 
			
		||||
  - sortkeys
 | 
			
		||||
- name: github.com/golang/glog
 | 
			
		||||
  version: 44145f04b68cf362d9c4df2182967c2275eaefed
 | 
			
		||||
- name: github.com/golang/protobuf
 | 
			
		||||
  version: b4deda0973fb4c70b50d226b1af49f3da59f5265
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - proto
 | 
			
		||||
  - ptypes
 | 
			
		||||
  - ptypes/any
 | 
			
		||||
  - ptypes/duration
 | 
			
		||||
  - ptypes/timestamp
 | 
			
		||||
- name: github.com/google/btree
 | 
			
		||||
  version: 7d79101e329e5a3adf994758c578dab82b90c017
 | 
			
		||||
- name: github.com/google/gofuzz
 | 
			
		||||
  version: 44d81051d367757e1c7c6a5a86423ece9afcf63c
 | 
			
		||||
- name: github.com/googleapis/gnostic
 | 
			
		||||
  version: 0c5108395e2debce0d731cf0287ddf7242066aba
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - OpenAPIv2
 | 
			
		||||
  - compiler
 | 
			
		||||
  - extensions
 | 
			
		||||
- name: github.com/gregjones/httpcache
 | 
			
		||||
  version: 787624de3eb7bd915c329cba748687a3b22666a6
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - diskcache
 | 
			
		||||
- name: github.com/hashicorp/golang-lru
 | 
			
		||||
  version: a0d98a5f288019575c6d1f4bb1573fef2d1fcdc4
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - simplelru
 | 
			
		||||
- name: github.com/imdario/mergo
 | 
			
		||||
  version: 6633656539c1639d9d78127b7d47c622b5d7b6dc
 | 
			
		||||
- name: github.com/jmespath/go-jmespath
 | 
			
		||||
  version: c2b33e8439af944379acbdd9c3a5fe0bc44bd8a5
 | 
			
		||||
- name: github.com/json-iterator/go
 | 
			
		||||
  version: f2b4162afba35581b6d4a50d3b8f34e33c144682
 | 
			
		||||
- name: github.com/konsorten/go-windows-terminal-sequences
 | 
			
		||||
  version: 5c8c8bd35d3832f5d134ae1e1e375b69a4d25242
 | 
			
		||||
- name: github.com/kr/text
 | 
			
		||||
  version: e2ffdb16a802fe2bb95e2e35ff34f0e53aeef34f
 | 
			
		||||
- name: github.com/lib/pq
 | 
			
		||||
  version: 90697d60dd844d5ef6ff15135d0203f65d2f53b8
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - oid
 | 
			
		||||
- name: github.com/modern-go/concurrent
 | 
			
		||||
  version: bacd9c7ef1dd9b15be4a9909b8ac7a4e313eec94
 | 
			
		||||
- name: github.com/modern-go/reflect2
 | 
			
		||||
  version: 05fbef0ca5da472bbf96c9322b84a53edc03c9fd
 | 
			
		||||
- name: github.com/mohae/deepcopy
 | 
			
		||||
  version: c48cc78d482608239f6c4c92a4abd87eb8761c90
 | 
			
		||||
- name: github.com/motomux/pretty
 | 
			
		||||
  version: b2aad2c9a95d14eb978f29baa6e3a5c3c20eef30
 | 
			
		||||
- name: github.com/peterbourgon/diskv
 | 
			
		||||
  version: 5f041e8faa004a95c88a202771f4cc3e991971e6
 | 
			
		||||
- name: github.com/sirupsen/logrus
 | 
			
		||||
  version: e1e72e9de974bd926e5c56f83753fba2df402ce5
 | 
			
		||||
- name: github.com/spf13/pflag
 | 
			
		||||
  version: 583c0c0531f06d5278b7d917446061adc344b5cd
 | 
			
		||||
- name: golang.org/x/crypto
 | 
			
		||||
  version: 49796115aa4b964c318aad4f3084fdb41e9aa067
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - bcrypt
 | 
			
		||||
  - blowfish
 | 
			
		||||
  - ssh/terminal
 | 
			
		||||
- name: golang.org/x/net
 | 
			
		||||
  version: 1c05540f6879653db88113bc4a2b70aec4bd491f
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - context
 | 
			
		||||
  - http2
 | 
			
		||||
  - http2/hpack
 | 
			
		||||
  - idna
 | 
			
		||||
  - lex/httplex
 | 
			
		||||
- name: golang.org/x/sys
 | 
			
		||||
  version: 95c6576299259db960f6c5b9b69ea52422860fce
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - unix
 | 
			
		||||
  - windows
 | 
			
		||||
- name: golang.org/x/text
 | 
			
		||||
  version: b19bf474d317b857955b12035d2c5acb57ce8b01
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - secure/bidirule
 | 
			
		||||
  - transform
 | 
			
		||||
  - unicode/bidi
 | 
			
		||||
  - unicode/norm
 | 
			
		||||
- name: golang.org/x/time
 | 
			
		||||
  version: f51c12702a4d776e4c1fa9b0fabab841babae631
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - rate
 | 
			
		||||
- name: gopkg.in/inf.v0
 | 
			
		||||
  version: 3887ee99ecf07df5b447e9b00d9c0b2adaa9f3e4
 | 
			
		||||
- name: gopkg.in/yaml.v2
 | 
			
		||||
  version: 5420a8b6744d3b0345ab293f6fcba19c978f1183
 | 
			
		||||
- name: k8s.io/api
 | 
			
		||||
  version: 2d6f90ab1293a1fb871cf149423ebb72aa7423aa
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - admission/v1beta1
 | 
			
		||||
  - admissionregistration/v1alpha1
 | 
			
		||||
  - admissionregistration/v1beta1
 | 
			
		||||
  - apps/v1
 | 
			
		||||
  - apps/v1beta1
 | 
			
		||||
  - apps/v1beta2
 | 
			
		||||
  - authentication/v1
 | 
			
		||||
  - authentication/v1beta1
 | 
			
		||||
  - authorization/v1
 | 
			
		||||
  - authorization/v1beta1
 | 
			
		||||
  - autoscaling/v1
 | 
			
		||||
  - autoscaling/v2beta1
 | 
			
		||||
  - batch/v1
 | 
			
		||||
  - batch/v1beta1
 | 
			
		||||
  - batch/v2alpha1
 | 
			
		||||
  - certificates/v1beta1
 | 
			
		||||
  - core/v1
 | 
			
		||||
  - events/v1beta1
 | 
			
		||||
  - extensions/v1beta1
 | 
			
		||||
  - networking/v1
 | 
			
		||||
  - policy/v1beta1
 | 
			
		||||
  - rbac/v1
 | 
			
		||||
  - rbac/v1alpha1
 | 
			
		||||
  - rbac/v1beta1
 | 
			
		||||
  - scheduling/v1alpha1
 | 
			
		||||
  - scheduling/v1beta1
 | 
			
		||||
  - settings/v1alpha1
 | 
			
		||||
  - storage/v1
 | 
			
		||||
  - storage/v1alpha1
 | 
			
		||||
  - storage/v1beta1
 | 
			
		||||
- name: k8s.io/apiextensions-apiserver
 | 
			
		||||
  version: cc9cd5d998df84cc405d398e9030d29c95acff18
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - pkg/apis/apiextensions
 | 
			
		||||
  - pkg/apis/apiextensions/v1beta1
 | 
			
		||||
  - pkg/client/clientset/clientset
 | 
			
		||||
  - pkg/client/clientset/clientset/scheme
 | 
			
		||||
  - pkg/client/clientset/clientset/typed/apiextensions/v1beta1
 | 
			
		||||
- name: k8s.io/apimachinery
 | 
			
		||||
  version: 103fd098999dc9c0c88536f5c9ad2e5da39373ae
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - pkg/api/errors
 | 
			
		||||
  - pkg/api/meta
 | 
			
		||||
  - pkg/api/resource
 | 
			
		||||
  - pkg/apis/meta/internalversion
 | 
			
		||||
  - pkg/apis/meta/v1
 | 
			
		||||
  - pkg/apis/meta/v1/unstructured
 | 
			
		||||
  - pkg/apis/meta/v1beta1
 | 
			
		||||
  - pkg/conversion
 | 
			
		||||
  - pkg/conversion/queryparams
 | 
			
		||||
  - pkg/fields
 | 
			
		||||
  - pkg/labels
 | 
			
		||||
  - pkg/runtime
 | 
			
		||||
  - pkg/runtime/schema
 | 
			
		||||
  - pkg/runtime/serializer
 | 
			
		||||
  - pkg/runtime/serializer/json
 | 
			
		||||
  - pkg/runtime/serializer/protobuf
 | 
			
		||||
  - pkg/runtime/serializer/recognizer
 | 
			
		||||
  - pkg/runtime/serializer/streaming
 | 
			
		||||
  - pkg/runtime/serializer/versioning
 | 
			
		||||
  - pkg/selection
 | 
			
		||||
  - pkg/types
 | 
			
		||||
  - pkg/util/cache
 | 
			
		||||
  - pkg/util/clock
 | 
			
		||||
  - pkg/util/diff
 | 
			
		||||
  - pkg/util/errors
 | 
			
		||||
  - pkg/util/framer
 | 
			
		||||
  - pkg/util/httpstream
 | 
			
		||||
  - pkg/util/httpstream/spdy
 | 
			
		||||
  - pkg/util/intstr
 | 
			
		||||
  - pkg/util/json
 | 
			
		||||
  - pkg/util/mergepatch
 | 
			
		||||
  - pkg/util/net
 | 
			
		||||
  - pkg/util/remotecommand
 | 
			
		||||
  - pkg/util/runtime
 | 
			
		||||
  - pkg/util/sets
 | 
			
		||||
  - pkg/util/strategicpatch
 | 
			
		||||
  - pkg/util/validation
 | 
			
		||||
  - pkg/util/validation/field
 | 
			
		||||
  - pkg/util/wait
 | 
			
		||||
  - pkg/util/yaml
 | 
			
		||||
  - pkg/version
 | 
			
		||||
  - pkg/watch
 | 
			
		||||
  - third_party/forked/golang/json
 | 
			
		||||
  - third_party/forked/golang/netutil
 | 
			
		||||
  - third_party/forked/golang/reflect
 | 
			
		||||
- name: k8s.io/client-go
 | 
			
		||||
  version: 1f13a808da65775f22cbf47862c4e5898d8f4ca1
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - discovery
 | 
			
		||||
  - discovery/fake
 | 
			
		||||
  - kubernetes
 | 
			
		||||
  - kubernetes/scheme
 | 
			
		||||
  - kubernetes/typed/admissionregistration/v1alpha1
 | 
			
		||||
  - kubernetes/typed/admissionregistration/v1beta1
 | 
			
		||||
  - kubernetes/typed/apps/v1
 | 
			
		||||
  - kubernetes/typed/apps/v1beta1
 | 
			
		||||
  - kubernetes/typed/apps/v1beta2
 | 
			
		||||
  - kubernetes/typed/authentication/v1
 | 
			
		||||
  - kubernetes/typed/authentication/v1beta1
 | 
			
		||||
  - kubernetes/typed/authorization/v1
 | 
			
		||||
  - kubernetes/typed/authorization/v1beta1
 | 
			
		||||
  - kubernetes/typed/autoscaling/v1
 | 
			
		||||
  - kubernetes/typed/autoscaling/v2beta1
 | 
			
		||||
  - kubernetes/typed/batch/v1
 | 
			
		||||
  - kubernetes/typed/batch/v1beta1
 | 
			
		||||
  - kubernetes/typed/batch/v2alpha1
 | 
			
		||||
  - kubernetes/typed/certificates/v1beta1
 | 
			
		||||
  - kubernetes/typed/core/v1
 | 
			
		||||
  - kubernetes/typed/events/v1beta1
 | 
			
		||||
  - kubernetes/typed/extensions/v1beta1
 | 
			
		||||
  - kubernetes/typed/networking/v1
 | 
			
		||||
  - kubernetes/typed/policy/v1beta1
 | 
			
		||||
  - kubernetes/typed/rbac/v1
 | 
			
		||||
  - kubernetes/typed/rbac/v1alpha1
 | 
			
		||||
  - kubernetes/typed/rbac/v1beta1
 | 
			
		||||
  - kubernetes/typed/scheduling/v1alpha1
 | 
			
		||||
  - kubernetes/typed/scheduling/v1beta1
 | 
			
		||||
  - kubernetes/typed/settings/v1alpha1
 | 
			
		||||
  - kubernetes/typed/storage/v1
 | 
			
		||||
  - kubernetes/typed/storage/v1alpha1
 | 
			
		||||
  - kubernetes/typed/storage/v1beta1
 | 
			
		||||
  - pkg/apis/clientauthentication
 | 
			
		||||
  - pkg/apis/clientauthentication/v1alpha1
 | 
			
		||||
  - pkg/apis/clientauthentication/v1beta1
 | 
			
		||||
  - pkg/version
 | 
			
		||||
  - plugin/pkg/client/auth/exec
 | 
			
		||||
  - rest
 | 
			
		||||
  - rest/watch
 | 
			
		||||
  - testing
 | 
			
		||||
  - tools/auth
 | 
			
		||||
  - tools/cache
 | 
			
		||||
  - tools/clientcmd
 | 
			
		||||
  - tools/clientcmd/api
 | 
			
		||||
  - tools/clientcmd/api/latest
 | 
			
		||||
  - tools/clientcmd/api/v1
 | 
			
		||||
  - tools/metrics
 | 
			
		||||
  - tools/pager
 | 
			
		||||
  - tools/reference
 | 
			
		||||
  - tools/remotecommand
 | 
			
		||||
  - transport
 | 
			
		||||
  - transport/spdy
 | 
			
		||||
  - util/buffer
 | 
			
		||||
  - util/cert
 | 
			
		||||
  - util/connrotation
 | 
			
		||||
  - util/exec
 | 
			
		||||
  - util/flowcontrol
 | 
			
		||||
  - util/homedir
 | 
			
		||||
  - util/integer
 | 
			
		||||
  - util/retry
 | 
			
		||||
- name: k8s.io/code-generator
 | 
			
		||||
  version: 6702109cc68eb6fe6350b83e14407c8d7309fd1a
 | 
			
		||||
- name: k8s.io/gengo
 | 
			
		||||
  version: 906d99f89cd644eecf75ab547b29bf9f876f0b59
 | 
			
		||||
- name: k8s.io/kube-openapi
 | 
			
		||||
  version: 91cfa479c814065e420cee7ed227db0f63a5854e
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - pkg/builder
 | 
			
		||||
  - pkg/common
 | 
			
		||||
  - pkg/handler
 | 
			
		||||
  - pkg/util
 | 
			
		||||
  - pkg/util/proto
 | 
			
		||||
testImports: []
 | 
			
		||||
							
								
								
									
										23
									
								
								glide.yaml
								
								
								
								
							
							
						
						
									
										23
									
								
								glide.yaml
								
								
								
								
							| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
package: github.com/zalando/postgres-operator
 | 
			
		||||
import:
 | 
			
		||||
- package: github.com/sirupsen/logrus
 | 
			
		||||
  version: ^1.0.1
 | 
			
		||||
- package: github.com/aws/aws-sdk-go
 | 
			
		||||
  version: ^1.8.24
 | 
			
		||||
  subpackages:
 | 
			
		||||
  - aws
 | 
			
		||||
  - aws/session
 | 
			
		||||
  - service/ec2
 | 
			
		||||
- package: github.com/lib/pq
 | 
			
		||||
- package: github.com/motomux/pretty
 | 
			
		||||
- package: k8s.io/apimachinery
 | 
			
		||||
  version: kubernetes-1.11.3-beta.0
 | 
			
		||||
- package: k8s.io/apiextensions-apiserver
 | 
			
		||||
  version: kubernetes-1.11.3-beta.0
 | 
			
		||||
- package: k8s.io/client-go
 | 
			
		||||
  version: kubernetes-1.11.3-beta.0
 | 
			
		||||
- package: k8s.io/code-generator
 | 
			
		||||
  version: kubernetes-1.11.3-beta.0
 | 
			
		||||
- package: k8s.io/gengo
 | 
			
		||||
- package: gopkg.in/yaml.v2
 | 
			
		||||
- package: github.com/mohae/deepcopy
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,25 @@
 | 
			
		|||
module github.com/zalando/postgres-operator
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/aws/aws-sdk-go v1.25.44
 | 
			
		||||
	github.com/emicklei/go-restful v2.9.6+incompatible // indirect
 | 
			
		||||
	github.com/evanphx/json-patch v4.5.0+incompatible // indirect
 | 
			
		||||
	github.com/googleapis/gnostic v0.3.0 // indirect
 | 
			
		||||
	github.com/imdario/mergo v0.3.8 // indirect
 | 
			
		||||
	github.com/lib/pq v1.2.0
 | 
			
		||||
	github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d
 | 
			
		||||
	github.com/sirupsen/logrus v1.4.2
 | 
			
		||||
	golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 // indirect
 | 
			
		||||
	golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 // indirect
 | 
			
		||||
	golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 // indirect
 | 
			
		||||
	golang.org/x/tools v0.0.0-20191209225234-22774f7dae43 // indirect
 | 
			
		||||
	gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
 | 
			
		||||
	gopkg.in/yaml.v2 v2.2.4
 | 
			
		||||
	k8s.io/api v0.0.0-20191121015604-11707872ac1c
 | 
			
		||||
	k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5
 | 
			
		||||
	k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d
 | 
			
		||||
	k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2
 | 
			
		||||
	k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,466 @@
 | 
			
		|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 | 
			
		||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
 | 
			
		||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
 | 
			
		||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
 | 
			
		||||
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/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
 | 
			
		||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
 | 
			
		||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
			
		||||
github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.25.44 h1:n9ahFoiyn66smjF34hYr3tb6/ZdBcLuFz7BCDhHyJ7I=
 | 
			
		||||
github.com/aws/aws-sdk-go v1.25.44/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 | 
			
		||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 | 
			
		||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
			
		||||
github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
			
		||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 | 
			
		||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 | 
			
		||||
github.com/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
 | 
			
		||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 | 
			
		||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
			
		||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e h1:p1yVGRW3nmb85p1Sh1ZJSDm4A4iKLS5QNbvUHMgGu/M=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
			
		||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
			
		||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
			
		||||
github.com/emicklei/go-restful v2.9.6+incompatible h1:tfrHha8zJ01ywiOEC1miGY8st1/igzWB8OmvPgoYX7w=
 | 
			
		||||
github.com/emicklei/go-restful v2.9.6+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
			
		||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
			
		||||
github.com/evanphx/json-patch v4.5.0+incompatible h1:ouOWdg56aJriqS0huScTkVXPC5IcNrDCXZ6OoTAWu7M=
 | 
			
		||||
github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
			
		||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20180905125535-1ca0a4f7cbcb/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/globalsign/mgo v0.0.0-20181015135952-eeefdecb41b8/go.mod h1:xkRDCp4j0OGD1HRkm4kmhM+pmpv3AKq5SU7GMg4oO/Q=
 | 
			
		||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
 | 
			
		||||
github.com/go-openapi/analysis v0.0.0-20180825180245-b006789cd277/go.mod h1:k70tL6pCuVxPJOHXQ+wIac1FUrvNkHolPie/cLEU6hI=
 | 
			
		||||
github.com/go-openapi/analysis v0.17.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.18.0/go.mod h1:IowGgpVeD0vNm45So8nr+IcQ3pxVtpRoBWb8PVZO0ik=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.2/go.mod h1:3P1osvZa9jKjb8ed2TPng3f0i/UY9snX6gxi44djMjk=
 | 
			
		||||
github.com/go-openapi/analysis v0.19.5/go.mod h1:hkEAkxagaIvIP7VTn8ygJNkd4kAYON2rCu0v0ObL0AU=
 | 
			
		||||
github.com/go-openapi/errors v0.17.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.18.0/go.mod h1:LcZQpmvG4wyF5j4IhA73wkLFQg+QJXOQHVjmcZxhka0=
 | 
			
		||||
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.17.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.18.0/go.mod h1:cOnomiV+CVVwFLk0A/MExoFMjwdsUdVpsRhURCKh+3M=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
 | 
			
		||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.17.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.18.0/go.mod h1:g4xxGn04lDIRh0GJb5QlpE3HfopLOL6uZrK/VgnsK9I=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.3 h1:5cxNfTy0UVC3X8JL5ymxzyoUZmo8iZb+jeTWn7tUa8o=
 | 
			
		||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
 | 
			
		||||
github.com/go-openapi/loads v0.17.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.18.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.0/go.mod h1:72tmFy5wsWx89uEVddd0RjRWPZm92WRLhf7AC+0+OOU=
 | 
			
		||||
github.com/go-openapi/loads v0.19.2/go.mod h1:QAskZPMX5V0C2gvfkGZzJlINuP7Hx/4+ix5jWFxsNPs=
 | 
			
		||||
github.com/go-openapi/loads v0.19.4/go.mod h1:zZVHonKd8DXyxyw4yfnVjPzBjIQcLt0CCsn0N0ZrQsk=
 | 
			
		||||
github.com/go-openapi/runtime v0.0.0-20180920151709-4f900dc2ade9/go.mod h1:6v9a6LTXWQCdL8k1AO3cvqx5OtZY/Y9wKTgaoP6YRfA=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.0/go.mod h1:OwNfisksmmaZse4+gpV3Ne9AyMOlP1lt4sK4FXt0O64=
 | 
			
		||||
github.com/go-openapi/runtime v0.19.4/go.mod h1:X277bwSUBxVlCYR3r7xgZZGKVvBd/29gLDlFGtJ8NL4=
 | 
			
		||||
github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
 | 
			
		||||
github.com/go-openapi/spec v0.17.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.18.0/go.mod h1:XkF/MOi14NmjsfZ8VtAKf8pIlbZzyoTvZsdfssdxcBI=
 | 
			
		||||
github.com/go-openapi/spec v0.19.2/go.mod h1:sCxk3jxKgioEJikev4fgkNmwS+3kuYdJtcsZsD5zxMY=
 | 
			
		||||
github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc=
 | 
			
		||||
github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
 | 
			
		||||
github.com/go-openapi/strfmt v0.17.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.18.0/go.mod h1:P82hnJI0CXkErkXi8IKjPbNBM6lV6+5pLP5l494TcyU=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.0/go.mod h1:+uW+93UVvGGq2qGaZxdDeJqSAqBqBdl+ZPMF/cC8nDY=
 | 
			
		||||
github.com/go-openapi/strfmt v0.19.3/go.mod h1:0yX7dbo8mKIvc3XSKp7MNfxw4JytCfCD6+bY1AVL9LU=
 | 
			
		||||
github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
 | 
			
		||||
github.com/go-openapi/swag v0.17.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.18.0/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/kXLo40Tg=
 | 
			
		||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
 | 
			
		||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
 | 
			
		||||
github.com/go-openapi/validate v0.18.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
 | 
			
		||||
github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2KDnRCRMUi7GTA=
 | 
			
		||||
github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4=
 | 
			
		||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d h1:3PaI8p3seN09VjbTYC/QWlUZdZ1qS1zGjy7LH2Wt07I=
 | 
			
		||||
github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903 h1:LbsanbbD6LieFkXbj9YNNBupiGHJgFeLpO0j0Fza1h8=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 | 
			
		||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
			
		||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
 | 
			
		||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
 | 
			
		||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
 | 
			
		||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 | 
			
		||||
github.com/googleapis/gnostic v0.3.0 h1:CcQijm0XKekKjP/YCz28LXVSpgguuB+nCxaSjCe09y0=
 | 
			
		||||
github.com/googleapis/gnostic v0.3.0/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 | 
			
		||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
 | 
			
		||||
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/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
			
		||||
github.com/imdario/mergo v0.3.8 h1:CGgOkSJeqMRmt0D9XLWExdT4m4F1vd3FV3VPt+0VxkQ=
 | 
			
		||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM=
 | 
			
		||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
 | 
			
		||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 | 
			
		||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
 | 
			
		||||
github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
 | 
			
		||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
 | 
			
		||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.7.0 h1:aizVhC/NAAcKWb+5QsU1iNOZb4Yws5UO2I+aIprQITM=
 | 
			
		||||
github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
 | 
			
		||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
 | 
			
		||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
 | 
			
		||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d h1:LznySqW8MqVeFh+pW6rOkFdld9QQ7jRydBKKM6jyPVI=
 | 
			
		||||
github.com/motomux/pretty v0.0.0-20161209205251-b2aad2c9a95d/go.mod h1:u3hJ0kqCQu/cPpsu3RbCOPZ0d7V3IjPjv1adNRleM9I=
 | 
			
		||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
			
		||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 | 
			
		||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
 | 
			
		||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.10.1 h1:q/mM8GF/n0shIN8SaAZ0V+jnLPzen6WIVZdiwrRlMlo=
 | 
			
		||||
github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 | 
			
		||||
github.com/onsi/gomega v1.7.0 h1:XPnZz8VVBHjVsy1vzJmRwIcSwiUO+JFfrv/xGiigmME=
 | 
			
		||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 | 
			
		||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
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/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 v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
 | 
			
		||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
 | 
			
		||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 | 
			
		||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
 | 
			
		||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 | 
			
		||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
			
		||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
			
		||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
 | 
			
		||||
github.com/vektah/gqlparser v1.1.2/go.mod h1:1ycwN7Ij5njmMkPPAOaRFY4rET2Enx7IkVv3vaXspKw=
 | 
			
		||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 | 
			
		||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
 | 
			
		||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
 | 
			
		||||
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.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-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-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413 h1:ULYEB3JvPRE/IfO+9uO7vKV/xzVTO7XPAwm8xbf4w2g=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495 h1:I6A9Ag9FpEKOjcKrRNjQkPHawoXIhKyTGfvvjFAiiAk=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553 h1:efeOvDhwQ29Dj3SdAV/MJf8oukgn+8D8WgaCaRMchF8=
 | 
			
		||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449 h1:gSbV7h1NRL2G1xTg/owz62CST1oJBmxy4QpMMregXVQ=
 | 
			
		||||
golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
 | 
			
		||||
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-20190920225731-5eefd052ad72/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43 h1:NfPq5mgc5ArFgVLCpeS4z07IoxSAqVfV/gQ5vxdgaxI=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191209225234-22774f7dae43/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 | 
			
		||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485 h1:OB/uP/Puiu5vS5QMRPrXCDWUPb+kt8f1KW8oQzFejQw=
 | 
			
		||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e h1:jRyg0XfpwWlhEV8mDfdNGBeSJM2fuyh9Yjrnd8kF2Ts=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
 | 
			
		||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
 | 
			
		||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 | 
			
		||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
k8s.io/api v0.0.0-20191121015604-11707872ac1c h1:Z87my3sF4WhG0OMxzARkWY/IKBtOr+MhXZAb4ts6qFc=
 | 
			
		||||
k8s.io/api v0.0.0-20191121015604-11707872ac1c/go.mod h1:R/s4gKT0V/cWEnbQa9taNRJNbWUK57/Dx6cPj6MD3A0=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5 h1:g+GvnbGqLU1Jxb/9iFm/BFcmkqG9HdsGh52+wHirpsM=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.0.0-20191204090421-cd61debedab5/go.mod h1:CPw0IHz1YrWGy0+8mG/76oTHXvChlgCb3EAezKQKB2I=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20191121015412-41065c7a8c2a/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20191128180518-03184f823e28/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d h1:q+OZmYewHJeMCzwpHkXlNTtk5bvaUMPCikKvf77RBlo=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20191203211716-adc6f4cd9e7d/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
 | 
			
		||||
k8s.io/apiserver v0.0.0-20191204084332-137a9d3b886b/go.mod h1:itgfam5HJbT/4b2BGfpUkkxfheMmDH+Ix+tEAP3uqZk=
 | 
			
		||||
k8s.io/client-go v0.0.0-20191204082517-8c19b9f4a642/go.mod h1:HMVIZ0dPop3WCrPEaJ+v5/94cjt56avdDFshpX0Fjvo=
 | 
			
		||||
k8s.io/client-go v0.0.0-20191204082519-e9644b2e3edc/go.mod h1:5lSG1yeDZVwDYAHe9VK48SCe5zmcnkAcf2Mx59TuhmM=
 | 
			
		||||
k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2 h1:T2HGghBOPAOEjWuIyFSeCsWEwsxa6unkBvy3PHfqonM=
 | 
			
		||||
k8s.io/client-go v0.0.0-20191204082520-bc9b51d240b2/go.mod h1:5lSG1yeDZVwDYAHe9VK48SCe5zmcnkAcf2Mx59TuhmM=
 | 
			
		||||
k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e h1:HB9Zu5ZUvJfNpLiTPhz+CebVKV8C39qTBMQkAgAZLNw=
 | 
			
		||||
k8s.io/code-generator v0.0.0-20191121015212-c4c8f8345c7e/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s=
 | 
			
		||||
k8s.io/component-base v0.0.0-20191204083903-0d4d24e738e4/go.mod h1:8VIh1jErItC4bg9hLBkPneyS77Tin8KwSzbYepHJnQI=
 | 
			
		||||
k8s.io/component-base v0.0.0-20191204083906-3ac1376c73aa/go.mod h1:mECWvHCPhJudDVDMtBl+AIf/YnTMp5r1F947OYFUwP0=
 | 
			
		||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 | 
			
		||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505 h1:ZY6yclUKVbZ+SdWnkfY+Je5vrMpKOxmGeKRbsXVmqYM=
 | 
			
		||||
k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 | 
			
		||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 | 
			
		||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 | 
			
		||||
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
 | 
			
		||||
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a h1:UcxjrRMyNx/i/y8G7kPvLyy7rfbeuf1PYyBf973pgyU=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
 | 
			
		||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f h1:GiPwtSzdP43eI1hpPCbROQCCIgCuiMMNF8YUVLF3vJo=
 | 
			
		||||
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 | 
			
		||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 | 
			
		||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 | 
			
		||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
 | 
			
		||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 | 
			
		||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
 | 
			
		||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 | 
			
		||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
// +build tools
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
Copyright 2019 The Kubernetes Authors.
 | 
			
		||||
Licensed under the Apache License, Version 2.0 (the "License");
 | 
			
		||||
you may not use this file except in compliance with the License.
 | 
			
		||||
You may obtain a copy of the License at
 | 
			
		||||
    http://www.apache.org/licenses/LICENSE-2.0
 | 
			
		||||
Unless required by applicable law or agreed to in writing, software
 | 
			
		||||
distributed under the License is distributed on an "AS IS" BASIS,
 | 
			
		||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
			
		||||
See the License for the specific language governing permissions and
 | 
			
		||||
limitations under the License.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
// This package imports things required by build scripts, to force `go mod` to see them as dependencies
 | 
			
		||||
package tools
 | 
			
		||||
 | 
			
		||||
import _ "k8s.io/code-generator"
 | 
			
		||||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ set -o pipefail
 | 
			
		|||
SCRIPT_ROOT=$(dirname ${BASH_SOURCE})/..
 | 
			
		||||
CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${SCRIPT_ROOT}; ls -d -1 ./vendor/k8s.io/code-generator 2>/dev/null || echo ${GOPATH}/src/k8s.io/code-generator)}
 | 
			
		||||
 | 
			
		||||
vendor/k8s.io/code-generator/generate-groups.sh all \
 | 
			
		||||
bash "${CODEGEN_PKG}/generate-groups.sh" all \
 | 
			
		||||
  github.com/zalando/postgres-operator/pkg/generated github.com/zalando/postgres-operator/pkg/apis \
 | 
			
		||||
  acid.zalan.do:v1 \
 | 
			
		||||
  --go-header-file ${SCRIPT_ROOT}/hack/custom-boilerplate.go.txt
 | 
			
		||||
  "acid.zalan.do:v1" \
 | 
			
		||||
  --go-header-file "${SCRIPT_ROOT}"/hack/custom-boilerplate.go.txt
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
#!/bin/bash
 | 
			
		||||
#!/usr/bin/env bash
 | 
			
		||||
 | 
			
		||||
set -o errexit
 | 
			
		||||
set -o nounset
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,125 @@
 | 
			
		|||
# Kubectl Plugin for Zalando's Postgres Operator
 | 
			
		||||
 | 
			
		||||
## Google Summer of Code 2019
 | 
			
		||||
 | 
			
		||||
This plugin is a prototype developed as a part of GSoC 2019 under the organisation
 | 
			
		||||
**The Postgres Operator**
 | 
			
		||||
 | 
			
		||||
### GSoC Proposal
 | 
			
		||||
 | 
			
		||||
[kubectl pg proposal](https://docs.google.com/document/d/1-WMy9HkfZ1XnnMbzplMe9rCzKrRMGaMz4owLVXXPb7w/edit)
 | 
			
		||||
 | 
			
		||||
### Weekly Reports
 | 
			
		||||
 | 
			
		||||
https://github.com/VineethReddy02/GSoC-Kubectl-Plugin-for-Postgres-Operator-tracker
 | 
			
		||||
 | 
			
		||||
 ### Final Project Report
 | 
			
		||||
 
 | 
			
		||||
 https://gist.github.com/VineethReddy02/159283bd368a710379eaf0f6bd60a40a
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
### Installtion of kubectl pg plugin
 | 
			
		||||
 | 
			
		||||
This project uses Go Modules for dependency management to build locally
 | 
			
		||||
Install go and enable go modules ```export GO111MODULE=on```
 | 
			
		||||
From Go >=1.13 Go modules will be enabled by default
 | 
			
		||||
```
 | 
			
		||||
# Assumes you have a working KUBECONFIG
 | 
			
		||||
$ GO111MODULE="on" 
 | 
			
		||||
# As of now go by default doesn't support Go mods. So explicit enabling is required.
 | 
			
		||||
$ GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg  go mod vendor 
 | 
			
		||||
# This generate a vendor directory with all dependencies needed by the plugin.
 | 
			
		||||
$ $GOPATH/src/github.com/zalando/postgres-operator/kubectl-pg  go install
 | 
			
		||||
# This will place the kubectl-pg binary in your $GOPATH/bin
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Before using the kubectl pg plugin make sure to set KUBECONFIG env varibale
 | 
			
		||||
 | 
			
		||||
Ideally KUBECONFIG is found in $HOME/.kube/config else specify the KUBECONFIG path here.
 | 
			
		||||
 | 
			
		||||
```export KUBECONFIG=$HOME/.kube/config``` 
 | 
			
		||||
 | 
			
		||||
### To list all commands available in kubectl pg
 | 
			
		||||
 | 
			
		||||
```kubectl pg --help``` (or) ```kubectl pg```
 | 
			
		||||
 | 
			
		||||
### This basically means the operator pod managed to start, so our operator is installed.
 | 
			
		||||
 | 
			
		||||
```kubectl pg check```
 | 
			
		||||
 | 
			
		||||
### To create postgresql cluster using manifest file
 | 
			
		||||
 | 
			
		||||
```kubectl pg create -f acid-minimal-cluster.yaml```
 | 
			
		||||
 | 
			
		||||
### To update existing cluster using manifest file
 | 
			
		||||
 | 
			
		||||
```kubectl pg update -f acid-minimal-cluster.yaml```
 | 
			
		||||
 | 
			
		||||
### To delete existing cluster using manifest file
 | 
			
		||||
 | 
			
		||||
```kubectl pg delete -f acid-minimal-cluster.yaml```
 | 
			
		||||
 | 
			
		||||
### To delete existing cluster using cluster name
 | 
			
		||||
 | 
			
		||||
```kubectl pg delete acid-minimal-cluster```
 | 
			
		||||
 | 
			
		||||
--namespace or -n flag to specify namespace if cluster is in other namespace.
 | 
			
		||||
 | 
			
		||||
```kubectl pg delete acid-minimal-cluster -n namespace01```
 | 
			
		||||
 | 
			
		||||
### To list postgres resources for the current namespace
 | 
			
		||||
 | 
			
		||||
```kubectl pg list```
 | 
			
		||||
 | 
			
		||||
### To list postgres resources across namespaces
 | 
			
		||||
 | 
			
		||||
```kubectl pg list all```
 | 
			
		||||
 | 
			
		||||
### To add-user and it's roles to an existing pg cluster
 | 
			
		||||
 | 
			
		||||
```kubectl pg add-user USER01 -p CREATEDB,LOGIN -c acid-minimal-cluster```
 | 
			
		||||
 | 
			
		||||
Privileges can only be [SUPERUSER, REPLICATION, INHERIT, LOGIN, NOLOGIN, CREATEROLE, CREATEDB, BYPASSURL]
 | 
			
		||||
 | 
			
		||||
Note: A login user is created by default unless NOLOGIN is specified, in which case the operator creates a role.
 | 
			
		||||
 | 
			
		||||
### To add-db and it's owner to an existing pg cluster
 | 
			
		||||
 | 
			
		||||
```kubectl pg add-db DB01 -o OWNER01 -c acid-minimal-cluster```
 | 
			
		||||
 | 
			
		||||
### To extend volume for an existing pg cluster
 | 
			
		||||
 | 
			
		||||
```kubectl pg ext-volume 2Gi -c acid-minimal-cluster```
 | 
			
		||||
 | 
			
		||||
### To find the version of postgres operator and kubectl plugin
 | 
			
		||||
 | 
			
		||||
```kubectl pg version (optional -n NAMESPACE allows to know specific to a namespace)```
 | 
			
		||||
 | 
			
		||||
### To connect to the shell of a postgres pod
 | 
			
		||||
 | 
			
		||||
```kubectl pg connect -c CLUSTER``` #This connects to a random pod
 | 
			
		||||
```kubectl pg connect -c CLUSTER -m``` #This connects the master
 | 
			
		||||
```kubectl pg connect -c CLUSTER -r 0``` #This connects to the desired replica
 | 
			
		||||
 | 
			
		||||
### To connect to the psql prompt
 | 
			
		||||
 | 
			
		||||
```kubectl pg connect -c CLUSTER -p -u username``` #This connects to a random pod. Not master
 | 
			
		||||
```kubectl pg connect -c CLUSTER -m -p -u username``` #This connects the master
 | 
			
		||||
```kubectl pg connect -c CLUSTER -r 0 -p -u username``` #This connects to the desired replica
 | 
			
		||||
 | 
			
		||||
Note: -p represents psql prompt
 | 
			
		||||
 | 
			
		||||
### To get the logs of postgres operator
 | 
			
		||||
 | 
			
		||||
```kubectl pg logs -o```
 | 
			
		||||
 | 
			
		||||
### To get the logs of the postgres cluster
 | 
			
		||||
 | 
			
		||||
```kubectl pg logs -c CLUSTER``` #Fetches the logs of a random pod. Not master
 | 
			
		||||
```kubectl pg logs -c CLUSTER -m``` #Fetches the logs of master
 | 
			
		||||
```kubectl pg logs -c CLUSTER -r 2``` #Fecthes the logs of specified replica
 | 
			
		||||
 | 
			
		||||
## Development
 | 
			
		||||
 | 
			
		||||
- When making changes to plugin make sure to change the major or patch version
 | 
			
		||||
of plugin in ```build.sh``` and run ```./build.sh``` 
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,4 @@
 | 
			
		|||
 | 
			
		||||
VERSION=1.0
 | 
			
		||||
sed -i "s/KubectlPgVersion string = \"[^\"]*\"/KubectlPgVersion string = \"${VERSION}\"/" cmd/version.go
 | 
			
		||||
go install
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,112 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// addDbCmd represents the addDb command
 | 
			
		||||
var addDbCmd = &cobra.Command{
 | 
			
		||||
	Use:   "add-db",
 | 
			
		||||
	Short: "Adds a DB and its owner to a Postgres cluster. The owner role is created if it does not exist",
 | 
			
		||||
	Long:  `Adds a new DB to the Postgres cluster. Owner needs to be specified by the -o flag, cluster with -c flag.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		if len(args) > 0 {
 | 
			
		||||
			dbName := args[0]
 | 
			
		||||
			dbOwner, _ := cmd.Flags().GetString("owner")
 | 
			
		||||
			clusterName, _ := cmd.Flags().GetString("cluster")
 | 
			
		||||
			addDb(dbName, dbOwner, clusterName)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Println("database name can't be empty. Use kubectl pg add-db [-h | --help] for more info")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
kubectl pg add-db db01 -o owner01 -c cluster01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// add db and it's owner to the cluster
 | 
			
		||||
func addDb(dbName string, dbOwner string, clusterName string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace := getCurrentNamespace()
 | 
			
		||||
	postgresql, err := postgresConfig.Postgresqls(namespace).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var dbOwnerExists bool
 | 
			
		||||
	dbUsers := postgresql.Spec.Users
 | 
			
		||||
	for key, _ := range dbUsers {
 | 
			
		||||
		if key == dbOwner {
 | 
			
		||||
			dbOwnerExists = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	var patch []byte
 | 
			
		||||
	// validating reserved DB names
 | 
			
		||||
	if dbOwnerExists && dbName != "postgres" && dbName != "template0" && dbName != "template1" {
 | 
			
		||||
		patch = dbPatch(dbName, dbOwner)
 | 
			
		||||
	} else if !dbOwnerExists {
 | 
			
		||||
		log.Fatal("The provided db-owner doesn't exist")
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Fatal("The provided db-name is reserved by postgres")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	updatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(postgresql.Name, types.MergePatchType, patch, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if updatedPostgres.ResourceVersion != postgresql.ResourceVersion {
 | 
			
		||||
		fmt.Printf("Created new database %s with owner %s in PostgreSQL cluster %s.\n", dbName, dbOwner, updatedPostgres.Name)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("postgresql %s is unchanged.\n", updatedPostgres.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func dbPatch(dbname string, owner string) []byte {
 | 
			
		||||
	ins := map[string]map[string]map[string]string{"spec": {"databases": {dbname: owner}}}
 | 
			
		||||
	patchInstances, err := json.Marshal(ins)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err, "unable to parse patch for add-db")
 | 
			
		||||
	}
 | 
			
		||||
	return patchInstances
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	addDbCmd.Flags().StringP("owner", "o", "", "provide owner of the database.")
 | 
			
		||||
	addDbCmd.Flags().StringP("cluster", "c", "", "provide a postgres cluster name.")
 | 
			
		||||
	rootCmd.AddCommand(addDbCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var allowedPrivileges = []string{"SUPERUSER", "REPLICATION", "INHERIT", "LOGIN", "NOLOGIN", "CREATEROLE", "CREATEDB", "BYPASSURL"}
 | 
			
		||||
 | 
			
		||||
// addUserCmd represents the addUser command
 | 
			
		||||
var addUserCmd = &cobra.Command{
 | 
			
		||||
	Use:   "add-user",
 | 
			
		||||
	Short: "Adds a user to the postgres cluster with given privileges",
 | 
			
		||||
	Long:  `Adds a user to the postgres cluster. You can add privileges as well with -p flag.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		clusterName, _ := cmd.Flags().GetString("cluster")
 | 
			
		||||
		privileges, _ := cmd.Flags().GetString("privileges")
 | 
			
		||||
 | 
			
		||||
		if len(args) > 0 {
 | 
			
		||||
			user := args[0]
 | 
			
		||||
			var permissions []string
 | 
			
		||||
			var perms []string
 | 
			
		||||
 | 
			
		||||
			if privileges != "" {
 | 
			
		||||
				parsedRoles := strings.Replace(privileges, ",", " ", -1)
 | 
			
		||||
				parsedRoles = strings.ToUpper(parsedRoles)
 | 
			
		||||
				permissions = strings.Fields(parsedRoles)
 | 
			
		||||
				var invalidPerms []string
 | 
			
		||||
 | 
			
		||||
				for _, userPrivilege := range permissions {
 | 
			
		||||
					validPerm := false
 | 
			
		||||
					for _, privilege := range allowedPrivileges {
 | 
			
		||||
						if privilege == userPrivilege {
 | 
			
		||||
							perms = append(perms, userPrivilege)
 | 
			
		||||
							validPerm = true
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					if !validPerm {
 | 
			
		||||
						invalidPerms = append(invalidPerms, userPrivilege)
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if len(invalidPerms) > 0 {
 | 
			
		||||
					fmt.Printf("Invalid privileges %s\n", invalidPerms)
 | 
			
		||||
					return
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			addUser(user, clusterName, perms)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
kubectl pg add-user user01 -p login,createdb -c cluster01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// add user to the cluster with provided permissions
 | 
			
		||||
func addUser(user string, clusterName string, permissions []string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace := getCurrentNamespace()
 | 
			
		||||
	postgresql, err := postgresConfig.Postgresqls(namespace).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	setUsers := make(map[string]bool)
 | 
			
		||||
	for _, k := range permissions {
 | 
			
		||||
		setUsers[k] = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if existingRoles, key := postgresql.Spec.Users[user]; key {
 | 
			
		||||
		for _, k := range existingRoles {
 | 
			
		||||
			setUsers[k] = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	Privileges := []string{}
 | 
			
		||||
	for keys, values := range setUsers {
 | 
			
		||||
		if values {
 | 
			
		||||
			Privileges = append(Privileges, keys)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	patch := applyUserPatch(user, Privileges)
 | 
			
		||||
	updatedPostgresql, err := postgresConfig.Postgresqls(namespace).Patch(postgresql.Name, types.MergePatchType, patch, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if updatedPostgresql.ResourceVersion != postgresql.ResourceVersion {
 | 
			
		||||
		fmt.Printf("postgresql %s is updated with new user %s and with privileges %s.\n", updatedPostgresql.Name, user, permissions)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("postgresql %s is unchanged.\n", updatedPostgresql.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func applyUserPatch(user string, value []string) []byte {
 | 
			
		||||
	ins := map[string]map[string]map[string][]string{"spec": {"users": {user: value}}}
 | 
			
		||||
	patchInstances, err := json.Marshal(ins)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err, "unable to parse number of instances json")
 | 
			
		||||
	}
 | 
			
		||||
	return patchInstances
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	addUserCmd.Flags().StringP("cluster", "c", "", "add user to the provided cluster.")
 | 
			
		||||
	addUserCmd.Flags().StringP("privileges", "p", "", "add privileges separated by commas without spaces")
 | 
			
		||||
	rootCmd.AddCommand(addUserCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,72 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	postgresConstants "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 | 
			
		||||
	apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// checkCmd represent kubectl pg check.
 | 
			
		||||
var checkCmd = &cobra.Command{
 | 
			
		||||
	Use:   "check",
 | 
			
		||||
	Short: "Checks the Postgres operator is installed in the k8s cluster",
 | 
			
		||||
	Long: `Checks that the Postgres CRD is registered in a k8s cluster. 
 | 
			
		||||
This means that the operator pod was able to start normally.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		check()
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
kubectl pg check
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// check validates postgresql CRD registered or not.
 | 
			
		||||
func check() *v1beta1.CustomResourceDefinition {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	apiExtClient, err := apiextbeta1.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	crdInfo, err := apiExtClient.CustomResourceDefinitions().Get(postgresConstants.PostgresCRDResouceName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if crdInfo.Name == postgresConstants.PostgresCRDResouceName {
 | 
			
		||||
		fmt.Printf("Postgres Operator is installed in the k8s cluster.\n")
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("Postgres Operator is not installed in the k8s cluster.\n")
 | 
			
		||||
	}
 | 
			
		||||
	return crdInfo
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	rootCmd.AddCommand(checkCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,142 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
	"k8s.io/client-go/tools/remotecommand"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	user "os/user"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// connectCmd represents the kubectl pg connect command
 | 
			
		||||
var connectCmd = &cobra.Command{
 | 
			
		||||
	Use:   "connect",
 | 
			
		||||
	Short: "Connects to the shell prompt, psql prompt of postgres cluster",
 | 
			
		||||
	Long:  `Connects to the shell prompt, psql prompt of postgres cluster and also to specified replica or master.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		clusterName, _ := cmd.Flags().GetString("cluster")
 | 
			
		||||
		master, _ := cmd.Flags().GetBool("master")
 | 
			
		||||
		replica, _ := cmd.Flags().GetString("replica")
 | 
			
		||||
		psql, _ := cmd.Flags().GetBool("psql")
 | 
			
		||||
		userName, _ := cmd.Flags().GetString("user")
 | 
			
		||||
		dbName, _ := cmd.Flags().GetString("database")
 | 
			
		||||
 | 
			
		||||
		if psql {
 | 
			
		||||
			if userName == "" {
 | 
			
		||||
				userInfo, err := user.Current()
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatal(err)
 | 
			
		||||
				}
 | 
			
		||||
				userName = userInfo.Username
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if dbName == "" {
 | 
			
		||||
			dbName = userName
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		connect(clusterName, master, replica, psql, userName, dbName)
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#connects to the master of postgres cluster
 | 
			
		||||
kubectl pg connect -c cluster -m
 | 
			
		||||
 | 
			
		||||
#connects to the random replica of postgres cluster
 | 
			
		||||
kubectl pg connect -c cluster
 | 
			
		||||
 | 
			
		||||
#connects to the provided replica number of postgres cluster
 | 
			
		||||
kubectl pg connect -c cluster -r 2
 | 
			
		||||
 | 
			
		||||
#connects to psql prompt of master for provided postgres cluster with current shell user
 | 
			
		||||
kubectl pg connect -c cluster -p -m
 | 
			
		||||
 | 
			
		||||
#connects to psql prompt of random replica for provided postgres cluster with provided user and db
 | 
			
		||||
kubectl pg connect -c cluster -p -u user01 -d db01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func connect(clusterName string, master bool, replica string, psql bool, user string, dbName string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	client, er := kubernetes.NewForConfig(config)
 | 
			
		||||
	if er != nil {
 | 
			
		||||
		log.Fatal(er)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	podName := getPodName(clusterName, master, replica)
 | 
			
		||||
	execRequest := &rest.Request{}
 | 
			
		||||
 | 
			
		||||
	if psql {
 | 
			
		||||
		execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
 | 
			
		||||
			Name(podName).
 | 
			
		||||
			Namespace(getCurrentNamespace()).
 | 
			
		||||
			SubResource("exec").
 | 
			
		||||
			Param("container", "postgres").
 | 
			
		||||
			Param("command", "psql").
 | 
			
		||||
			Param("command", dbName).
 | 
			
		||||
			Param("command", user).
 | 
			
		||||
			Param("stdin", "true").
 | 
			
		||||
			Param("stdout", "true").
 | 
			
		||||
			Param("stderr", "true").
 | 
			
		||||
			Param("tty", "true")
 | 
			
		||||
	} else {
 | 
			
		||||
		execRequest = client.CoreV1().RESTClient().Post().Resource("pods").
 | 
			
		||||
			Name(podName).
 | 
			
		||||
			Namespace(getCurrentNamespace()).
 | 
			
		||||
			SubResource("exec").
 | 
			
		||||
			Param("container", "postgres").
 | 
			
		||||
			Param("command", "su").
 | 
			
		||||
			Param("command", "postgres").
 | 
			
		||||
			Param("stdin", "true").
 | 
			
		||||
			Param("stdout", "true").
 | 
			
		||||
			Param("stderr", "true").
 | 
			
		||||
			Param("tty", "true")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	exec, err := remotecommand.NewSPDYExecutor(config, "POST", execRequest.URL())
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = exec.Stream(remotecommand.StreamOptions{
 | 
			
		||||
		Stdin:  os.Stdin,
 | 
			
		||||
		Stdout: os.Stdout,
 | 
			
		||||
		Stderr: os.Stderr,
 | 
			
		||||
		Tty:    true,
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	connectCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
 | 
			
		||||
	connectCmd.Flags().BoolP("master", "m", false, "connect to master.")
 | 
			
		||||
	connectCmd.Flags().StringP("replica", "r", "", "connect to replica. Specify replica number.")
 | 
			
		||||
	connectCmd.Flags().BoolP("psql", "p", false, "connect to psql prompt.")
 | 
			
		||||
	connectCmd.Flags().StringP("user", "u", "", "provide user.")
 | 
			
		||||
	connectCmd.Flags().StringP("database", "d", "", "provide database name.")
 | 
			
		||||
	rootCmd.AddCommand(connectCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,76 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// createCmd kubectl pg create.
 | 
			
		||||
var createCmd = &cobra.Command{
 | 
			
		||||
	Use:   "create",
 | 
			
		||||
	Short: "Creates postgres object using manifest file",
 | 
			
		||||
	Long:  `Creates postgres custom resource objects from a manifest file.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		fileName, _ := cmd.Flags().GetString("file")
 | 
			
		||||
		create(fileName)
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
kubectl pg create -f cluster-manifest.yaml
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create postgresql resources.
 | 
			
		||||
func create(fileName string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	ymlFile, err := ioutil.ReadFile(fileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decode := scheme.Codecs.UniversalDeserializer().Decode
 | 
			
		||||
	obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postgresSql := obj.(*v1.Postgresql)
 | 
			
		||||
	_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Create(postgresSql)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fmt.Printf("postgresql %s created.\n", postgresSql.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	createCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
 | 
			
		||||
	rootCmd.AddCommand(createCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,132 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// deleteCmd represents kubectl pg delete.
 | 
			
		||||
var deleteCmd = &cobra.Command{
 | 
			
		||||
	Use:   "delete",
 | 
			
		||||
	Short: "Deletes postgresql object by cluster-name/manifest file",
 | 
			
		||||
	Long: `Deletes the postgres objects identified by a manifest file or cluster-name.
 | 
			
		||||
Deleting the manifest is sufficient to delete the cluster.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		namespace, _ := cmd.Flags().GetString("namespace")
 | 
			
		||||
		file, _ := cmd.Flags().GetString("file")
 | 
			
		||||
 | 
			
		||||
		if file != "" {
 | 
			
		||||
			deleteByFile(file)
 | 
			
		||||
		} else if namespace != "" {
 | 
			
		||||
			if len(args) != 0 {
 | 
			
		||||
				clusterName := args[0]
 | 
			
		||||
				deleteByName(clusterName, namespace)
 | 
			
		||||
			} else {
 | 
			
		||||
				fmt.Println("cluster name can't be empty")
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Println("use the flag either -n or -f to delete a resource.")
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Deleting the postgres cluster using manifest file
 | 
			
		||||
kubectl pg delete -f cluster-manifest.yaml
 | 
			
		||||
 | 
			
		||||
#Deleting the postgres cluster using cluster name in current namespace.
 | 
			
		||||
kubectl pg delete cluster01
 | 
			
		||||
 | 
			
		||||
#Deleting the postgres cluster using cluster name in provided namespace
 | 
			
		||||
kubectl pg delete cluster01 -n namespace01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteByFile(file string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ymlFile, err := ioutil.ReadFile(file)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decode := scheme.Codecs.UniversalDeserializer().Decode
 | 
			
		||||
	obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postgresSql := obj.(*v1.Postgresql)
 | 
			
		||||
	_, err = postgresConfig.Postgresqls(postgresSql.Namespace).Get(postgresSql.Name, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", postgresSql.Name, postgresSql.Namespace, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", postgresSql.Namespace, postgresSql.Name)
 | 
			
		||||
 | 
			
		||||
	confirmAction(postgresSql.Name, postgresSql.Namespace)
 | 
			
		||||
	err = postgresConfig.Postgresqls(postgresSql.Namespace).Delete(postgresSql.Name, &metav1.DeleteOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Postgresql %s deleted from %s.\n", postgresSql.Name, postgresSql.Namespace)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func deleteByName(clusterName string, namespace string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = postgresConfig.Postgresqls(namespace).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Postgresql %s not found with the provided namespace %s : %s \n", clusterName, namespace, err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Are you sure you want to remove this PostgreSQL cluster? If so, please type (%s/%s) and hit Enter\n", namespace, clusterName)
 | 
			
		||||
 | 
			
		||||
	confirmAction(clusterName, namespace)
 | 
			
		||||
	err = postgresConfig.Postgresqls(namespace).Delete(clusterName, &metav1.DeleteOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("Postgresql %s deleted from %s.\n", clusterName, namespace)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	namespace := getCurrentNamespace()
 | 
			
		||||
	deleteCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be deleted.")
 | 
			
		||||
	deleteCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
 | 
			
		||||
	rootCmd.AddCommand(deleteCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,117 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// extVolumeCmd represents the extVolume command
 | 
			
		||||
var extVolumeCmd = &cobra.Command{
 | 
			
		||||
	Use:   "ext-volume",
 | 
			
		||||
	Short: "Increases the volume size of a given Postgres cluster",
 | 
			
		||||
	Long:  `Extends the volume of the postgres cluster. But volume cannot be shrinked.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		clusterName, _ := cmd.Flags().GetString("cluster")
 | 
			
		||||
		if len(args) > 0 {
 | 
			
		||||
			volume := args[0]
 | 
			
		||||
			extVolume(volume, clusterName)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Println("please enter the cluster name with -c flag & volume in desired units")
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Extending the volume size of provided cluster 
 | 
			
		||||
kubectl pg ext-volume 2Gi -c cluster01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// extend volume with provided size & cluster name
 | 
			
		||||
func extVolume(increasedVolumeSize string, clusterName string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	namespace := getCurrentNamespace()
 | 
			
		||||
	postgresql, err := postgresConfig.Postgresqls(namespace).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatalf("hii %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	oldSize, err := resource.ParseQuantity(postgresql.Spec.Volume.Size)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newSize, err := resource.ParseQuantity(increasedVolumeSize)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = strconv.Atoi(newSize.String())
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		fmt.Println("provide the valid volume size with respective units i.e Ki, Mi, Gi")
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if newSize.Value() > oldSize.Value() {
 | 
			
		||||
		patchInstances := volumePatch(newSize)
 | 
			
		||||
		response, err := postgresConfig.Postgresqls(namespace).Patch(postgresql.Name, types.MergePatchType, patchInstances, "")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		if postgresql.ResourceVersion != response.ResourceVersion {
 | 
			
		||||
			fmt.Printf("%s volume is extended to %s.\n", response.Name, increasedVolumeSize)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Printf("%s volume %s is unchanged.\n", response.Name, postgresql.Spec.Volume.Size)
 | 
			
		||||
		}
 | 
			
		||||
	} else if newSize.Value() == oldSize.Value() {
 | 
			
		||||
		fmt.Println("volume already has the desired size.")
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("volume %s size cannot be shrinked.\n", postgresql.Spec.Volume.Size)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func volumePatch(volume resource.Quantity) []byte {
 | 
			
		||||
	patchData := map[string]map[string]map[string]resource.Quantity{"spec": {"volume": {"size": volume}}}
 | 
			
		||||
	patch, err := json.Marshal(patchData)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err, "unable to parse patch to extend volume")
 | 
			
		||||
	}
 | 
			
		||||
	return patch
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	extVolumeCmd.Flags().StringP("cluster", "c", "", "provide cluster name.")
 | 
			
		||||
	rootCmd.AddCommand(extVolumeCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,116 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	TrimCreateTimestamp = 6000000000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// listCmd represents kubectl pg list.
 | 
			
		||||
var listCmd = &cobra.Command{
 | 
			
		||||
	Use:   "list",
 | 
			
		||||
	Short: "Lists all the resources of kind postgresql",
 | 
			
		||||
	Long:  `Lists all the info specific to postgresql objects.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		allNamespaces, _ := cmd.Flags().GetBool("all-namespaces")
 | 
			
		||||
		namespace, _ := cmd.Flags().GetString("namespace")
 | 
			
		||||
		if allNamespaces {
 | 
			
		||||
			list(allNamespaces, "")
 | 
			
		||||
		} else {
 | 
			
		||||
			list(allNamespaces, namespace)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Lists postgres cluster in current namespace
 | 
			
		||||
kubectl pg list
 | 
			
		||||
 | 
			
		||||
#Lists postgres clusters in all namespaces
 | 
			
		||||
kubectl pg list -A
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// list command to list postgres.
 | 
			
		||||
func list(allNamespaces bool, namespace string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var listPostgres *v1.PostgresqlList
 | 
			
		||||
	listPostgres, err = postgresConfig.Postgresqls(namespace).List(metav1.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(listPostgres.Items) == 0 {
 | 
			
		||||
		if namespace != "" {
 | 
			
		||||
			fmt.Printf("No Postgresql clusters found in namespace: %v\n", namespace)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Println("No Postgresql clusters found in all namespaces")
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if allNamespaces {
 | 
			
		||||
		listAll(listPostgres)
 | 
			
		||||
	} else {
 | 
			
		||||
		listWithNamespace(listPostgres)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listAll(listPostgres *v1.PostgresqlList) {
 | 
			
		||||
	template := "%-32s%-16s%-12s%-12s%-12s%-12s%-12s\n"
 | 
			
		||||
	fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME", "NAMESPACE")
 | 
			
		||||
	for _, pgObjs := range listPostgres.Items {
 | 
			
		||||
		fmt.Printf(template, pgObjs.Name, pgObjs.Status.PostgresClusterStatus, strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
 | 
			
		||||
			pgObjs.Spec.PgVersion, time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp), pgObjs.Spec.Size, pgObjs.Namespace)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func listWithNamespace(listPostgres *v1.PostgresqlList) {
 | 
			
		||||
	template := "%-32s%-16s%-12s%-12s%-12s%-12s\n"
 | 
			
		||||
	fmt.Printf(template, "NAME", "STATUS", "INSTANCES", "VERSION", "AGE", "VOLUME")
 | 
			
		||||
	for _, pgObjs := range listPostgres.Items {
 | 
			
		||||
		fmt.Printf(template, pgObjs.Name, pgObjs.Status.PostgresClusterStatus, strconv.Itoa(int(pgObjs.Spec.NumberOfInstances)),
 | 
			
		||||
			pgObjs.Spec.PgVersion, time.Since(pgObjs.CreationTimestamp.Time).Truncate(TrimCreateTimestamp), pgObjs.Spec.Size)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	listCmd.Flags().BoolP("all-namespaces", "A", false, "list pg resources across all namespaces.")
 | 
			
		||||
	listCmd.Flags().StringP("namespace", "n", getCurrentNamespace(), "provide the namespace")
 | 
			
		||||
	rootCmd.AddCommand(listCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,141 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"io"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// logsCmd represents the logs command
 | 
			
		||||
var logsCmd = &cobra.Command{
 | 
			
		||||
	Use:   "logs",
 | 
			
		||||
	Short: "This will fetch the logs of the specified postgres cluster & postgres operator",
 | 
			
		||||
	Long:  `Fetches the logs of the postgres cluster (i.e master( with -m flag) & replica with (-r 1 pod number) and without -m or -r connects to random replica`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		opLogs, _ := cmd.Flags().GetBool("operator")
 | 
			
		||||
		clusterName, _ := cmd.Flags().GetString("cluster")
 | 
			
		||||
		master, _ := cmd.Flags().GetBool("master")
 | 
			
		||||
		replica, _ := cmd.Flags().GetString("replica")
 | 
			
		||||
 | 
			
		||||
		if opLogs {
 | 
			
		||||
			operatorLogs()
 | 
			
		||||
		} else {
 | 
			
		||||
			clusterLogs(clusterName, master, replica)
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Fetch the logs of the postgres operator
 | 
			
		||||
kubectl pg logs -o
 | 
			
		||||
 | 
			
		||||
#Fetch the logs of the master for provided cluster
 | 
			
		||||
kubectl pg logs -c cluster01 -m
 | 
			
		||||
 | 
			
		||||
#Fetch the logs of the random replica for provided cluster
 | 
			
		||||
kubectl pg logs -c cluster01
 | 
			
		||||
 | 
			
		||||
#Fetch the logs of the provided replica number of the cluster
 | 
			
		||||
kubectl pg logs -c cluster01 -r 3
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func operatorLogs() {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	client, err := kubernetes.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	operator := getPostgresOperator(client)
 | 
			
		||||
	allPods, err := client.CoreV1().Pods(operator.Namespace).List(metav1.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var operatorPodName string
 | 
			
		||||
	for _, pod := range allPods.Items {
 | 
			
		||||
		for key, value := range pod.Labels {
 | 
			
		||||
			if (key == "name" && value == OperatorName) || (key == "app.kubernetes.io/name" && value == OperatorName) {
 | 
			
		||||
				operatorPodName = pod.Name
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	execRequest := client.CoreV1().RESTClient().Get().Namespace(operator.Namespace).
 | 
			
		||||
		Name(operatorPodName).
 | 
			
		||||
		Resource("pods").
 | 
			
		||||
		SubResource("log").
 | 
			
		||||
		Param("follow", "--follow").
 | 
			
		||||
		Param("container", OperatorName)
 | 
			
		||||
 | 
			
		||||
	readCloser, err := execRequest.Stream()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer readCloser.Close()
 | 
			
		||||
	_, err = io.Copy(os.Stdout, readCloser)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func clusterLogs(clusterName string, master bool, replica string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	client, err := kubernetes.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	podName := getPodName(clusterName, master, replica)
 | 
			
		||||
	execRequest := client.CoreV1().RESTClient().Get().Namespace(getCurrentNamespace()).
 | 
			
		||||
		Name(podName).
 | 
			
		||||
		Resource("pods").
 | 
			
		||||
		SubResource("log").
 | 
			
		||||
		Param("follow", "--follow").
 | 
			
		||||
		Param("container", "postgres")
 | 
			
		||||
 | 
			
		||||
	readCloser, err := execRequest.Stream()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer readCloser.Close()
 | 
			
		||||
	_, err = io.Copy(os.Stdout, readCloser)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	rootCmd.AddCommand(logsCmd)
 | 
			
		||||
	logsCmd.Flags().BoolP("operator", "o", false, "logs of operator")
 | 
			
		||||
	logsCmd.Flags().StringP("cluster", "c", "", "logs for the provided cluster")
 | 
			
		||||
	logsCmd.Flags().BoolP("master", "m", false, "Patroni logs of master")
 | 
			
		||||
	logsCmd.Flags().StringP("replica", "r", "", "Patroni logs of replica. Specify replica number.")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,50 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"github.com/spf13/viper"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var rootCmd = &cobra.Command{
 | 
			
		||||
	Use:   "kubectl-pg",
 | 
			
		||||
	Short: "kubectl plugin for the Zalando Postgres operator.",
 | 
			
		||||
	Long:  `kubectl pg plugin for interaction with Zalando postgres operator.`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Execute adds all child commands to the root command and sets flags appropriately.
 | 
			
		||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
 | 
			
		||||
func Execute() {
 | 
			
		||||
	if err := rootCmd.Execute(); err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		os.Exit(1)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	viper.SetDefault("author", "Vineeth Pothulapati <vineethpothulapati@outlook.com>")
 | 
			
		||||
	viper.SetDefault("license", "mit")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,191 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	v1 "k8s.io/api/apps/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strconv"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// scaleCmd represents the scale command
 | 
			
		||||
var scaleCmd = &cobra.Command{
 | 
			
		||||
	Use:   "scale",
 | 
			
		||||
	Short: "Add/remove pods to a Postgres cluster",
 | 
			
		||||
	Long: `Scales the postgres objects using cluster-name.
 | 
			
		||||
Scaling to 0 leads to down time.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		clusterName, err := cmd.Flags().GetString("cluster")
 | 
			
		||||
		namespace, err := cmd.Flags().GetString("namespace")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if len(args) > 0 {
 | 
			
		||||
			numberOfInstances, err := strconv.Atoi(args[0])
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				log.Fatal(err)
 | 
			
		||||
			}
 | 
			
		||||
			scale(int32(numberOfInstances), clusterName, namespace)
 | 
			
		||||
		} else {
 | 
			
		||||
			fmt.Println("Please enter number of instances to scale.")
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Usage
 | 
			
		||||
kubectl pg scale [NUMBER-OF-INSTANCES] -c [CLUSTER-NAME] -n [NAMESPACE]
 | 
			
		||||
 | 
			
		||||
#Scales the number of instances of the provided cluster
 | 
			
		||||
kubectl pg scale 5 -c cluster01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func scale(numberOfInstances int32, clusterName string, namespace string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postgresql, err := postgresConfig.Postgresqls(namespace).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	minInstances, maxInstances := allowedMinMaxInstances(config)
 | 
			
		||||
 | 
			
		||||
	if minInstances == -1 && maxInstances == -1 {
 | 
			
		||||
		postgresql.Spec.NumberOfInstances = numberOfInstances
 | 
			
		||||
	} else if numberOfInstances <= maxInstances && numberOfInstances >= minInstances {
 | 
			
		||||
		postgresql.Spec.NumberOfInstances = numberOfInstances
 | 
			
		||||
	} else if minInstances == -1 && numberOfInstances < postgresql.Spec.NumberOfInstances ||
 | 
			
		||||
		maxInstances == -1 && numberOfInstances > postgresql.Spec.NumberOfInstances {
 | 
			
		||||
		postgresql.Spec.NumberOfInstances = numberOfInstances
 | 
			
		||||
	} else {
 | 
			
		||||
		log.Fatalf("cannot scale to the provided instances as they don't adhere to MIN_INSTANCES: %v and MAX_INSTANCES: %v provided in configmap or operatorconfiguration", maxInstances, minInstances)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if numberOfInstances == 0 {
 | 
			
		||||
		fmt.Printf("Scaling to zero leads to down time. please type %s/%s and hit Enter this serves to confirm the action\n", namespace, clusterName)
 | 
			
		||||
		confirmAction(clusterName, namespace)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	patchInstances := scalePatch(numberOfInstances)
 | 
			
		||||
	UpdatedPostgres, err := postgresConfig.Postgresqls(namespace).Patch(postgresql.Name, types.MergePatchType, patchInstances, "")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if UpdatedPostgres.ResourceVersion != postgresql.ResourceVersion {
 | 
			
		||||
		fmt.Printf("scaled postgresql %s/%s to %d instances\n", UpdatedPostgres.Namespace, UpdatedPostgres.Name, UpdatedPostgres.Spec.NumberOfInstances)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("postgresql %s is unchanged.\n", postgresql.Name)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func scalePatch(value int32) []byte {
 | 
			
		||||
	instances := map[string]map[string]int32{"spec": {"numberOfInstances": value}}
 | 
			
		||||
	patchInstances, err := json.Marshal(instances)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err, "unable to parse patch for scale")
 | 
			
		||||
	}
 | 
			
		||||
	return patchInstances
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func allowedMinMaxInstances(config *rest.Config) (int32, int32) {
 | 
			
		||||
	k8sClient, err := kubernetes.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var operator *v1.Deployment
 | 
			
		||||
	operator = getPostgresOperator(k8sClient)
 | 
			
		||||
 | 
			
		||||
	operatorContainer := operator.Spec.Template.Spec.Containers
 | 
			
		||||
	var configMapName, operatorConfigName string
 | 
			
		||||
	// -1 indicates no limitations for min/max instances
 | 
			
		||||
	minInstances := -1
 | 
			
		||||
	maxInstances := -1
 | 
			
		||||
	for _, envData := range operatorContainer[0].Env {
 | 
			
		||||
		if envData.Name == "CONFIG_MAP_NAME" {
 | 
			
		||||
			configMapName = envData.Value
 | 
			
		||||
		}
 | 
			
		||||
		if envData.Name == "POSTGRES_OPERATOR_CONFIGURATION_OBJECT" {
 | 
			
		||||
			operatorConfigName = envData.Value
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if operatorConfigName == "" {
 | 
			
		||||
		configMap, err := k8sClient.CoreV1().ConfigMaps(operator.Namespace).Get(configMapName, metav1.GetOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		configMapData := configMap.Data
 | 
			
		||||
		for key, value := range configMapData {
 | 
			
		||||
			if key == "min_instances" {
 | 
			
		||||
				minInstances, err = strconv.Atoi(value)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatalf("invalid min instances in configmap %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if key == "max_instances" {
 | 
			
		||||
				maxInstances, err = strconv.Atoi(value)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					log.Fatalf("invalid max instances in configmap %v", err)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	} else if configMapName == "" {
 | 
			
		||||
		pgClient, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		operatorConfig, err := pgClient.OperatorConfigurations(operator.Namespace).Get(operatorConfigName, metav1.GetOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("unable to read operator configuration %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		minInstances = int(operatorConfig.Configuration.MinInstances)
 | 
			
		||||
		maxInstances = int(operatorConfig.Configuration.MaxInstances)
 | 
			
		||||
	}
 | 
			
		||||
	return int32(minInstances), int32(maxInstances)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	namespace := getCurrentNamespace()
 | 
			
		||||
	scaleCmd.Flags().StringP("namespace", "n", namespace, "namespace of the cluster to be scaled")
 | 
			
		||||
	scaleCmd.Flags().StringP("cluster", "c", "", "provide the cluster name.")
 | 
			
		||||
	rootCmd.AddCommand(scaleCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,91 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/scheme"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// updateCmd represents kubectl pg update
 | 
			
		||||
var updateCmd = &cobra.Command{
 | 
			
		||||
	Use:   "update",
 | 
			
		||||
	Short: "Updates postgresql object using manifest file",
 | 
			
		||||
	Long:  `Updates the state of cluster using manifest file to reflect the changes on the cluster.`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		fileName, _ := cmd.Flags().GetString("file")
 | 
			
		||||
		updatePgResources(fileName)
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#usage
 | 
			
		||||
kubectl pg update -f [File-NAME]
 | 
			
		||||
 | 
			
		||||
#update the postgres cluster with updated manifest file
 | 
			
		||||
kubectl pg update -f cluster-manifest.yaml
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update postgresql resources.
 | 
			
		||||
func updatePgResources(fileName string) {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	ymlFile, err := ioutil.ReadFile(fileName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	decode := scheme.Codecs.UniversalDeserializer().Decode
 | 
			
		||||
	obj, _, err := decode([]byte(ymlFile), nil, &v1.Postgresql{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newPostgresObj := obj.(*v1.Postgresql)
 | 
			
		||||
	oldPostgresObj, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Get(newPostgresObj.Name, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newPostgresObj.ResourceVersion = oldPostgresObj.ResourceVersion
 | 
			
		||||
	response, err := postgresConfig.Postgresqls(newPostgresObj.Namespace).Update(newPostgresObj)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if newPostgresObj.ResourceVersion != response.ResourceVersion {
 | 
			
		||||
		fmt.Printf("postgresql %s updated.\n", response.Name)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("postgresql %s is unchanged.\n", response.Name)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	updateCmd.Flags().StringP("file", "f", "", "manifest file with the cluster definition.")
 | 
			
		||||
	rootCmd.AddCommand(updateCmd)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,170 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"flag"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	PostgresqlLister "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	v1 "k8s.io/api/apps/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	restclient "k8s.io/client-go/rest"
 | 
			
		||||
	"k8s.io/client-go/tools/clientcmd"
 | 
			
		||||
	"k8s.io/client-go/util/homedir"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	OperatorName     = "postgres-operator"
 | 
			
		||||
	DefaultNamespace = "default"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func getConfig() *restclient.Config {
 | 
			
		||||
	var kubeconfig *string
 | 
			
		||||
	var config *restclient.Config
 | 
			
		||||
	envKube := os.Getenv("KUBECONFIG")
 | 
			
		||||
	if envKube != "" {
 | 
			
		||||
		kubeconfig = &envKube
 | 
			
		||||
	} else {
 | 
			
		||||
		if home := homedir.HomeDir(); home != "" {
 | 
			
		||||
			kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "(optional) absolute path to the kubeconfig file")
 | 
			
		||||
		} else {
 | 
			
		||||
			kubeconfig = flag.String("kubeconfig", "", "absolute path to the kubeconfig file")
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	flag.Parse()
 | 
			
		||||
	var err error
 | 
			
		||||
	config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	return config
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getCurrentNamespace() string {
 | 
			
		||||
	namespace, err := exec.Command("kubectl", "config", "view", "--minify", "--output", "jsonpath={..namespace}").CombinedOutput()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	currentNamespace := string(namespace)
 | 
			
		||||
	if currentNamespace == "" {
 | 
			
		||||
		currentNamespace = DefaultNamespace
 | 
			
		||||
	}
 | 
			
		||||
	return currentNamespace
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func confirmAction(clusterName string, namespace string) {
 | 
			
		||||
	for {
 | 
			
		||||
		confirmClusterDetails := ""
 | 
			
		||||
		_, err := fmt.Scan(&confirmClusterDetails)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatalf("couldn't get confirmation from the user %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		clusterDetails := strings.Split(confirmClusterDetails, "/")
 | 
			
		||||
		if clusterDetails[0] != namespace || clusterDetails[1] != clusterName {
 | 
			
		||||
			fmt.Printf("cluster name or namespace doesn't match. Please re-enter %s/%s\nHint: Press (ctrl+c) to exit\n", namespace, clusterName)
 | 
			
		||||
		} else {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPodName(clusterName string, master bool, replicaNumber string) string {
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	client, er := kubernetes.NewForConfig(config)
 | 
			
		||||
	if er != nil {
 | 
			
		||||
		log.Fatal(er)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postgresConfig, err := PostgresqlLister.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	postgresCluster, err := postgresConfig.Postgresqls(getCurrentNamespace()).Get(clusterName, metav1.GetOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	numOfInstances := postgresCluster.Spec.NumberOfInstances
 | 
			
		||||
	var podName string
 | 
			
		||||
	var podRole string
 | 
			
		||||
	replica := clusterName + "-" + replicaNumber
 | 
			
		||||
 | 
			
		||||
	for ins := 0; ins < int(numOfInstances); ins++ {
 | 
			
		||||
		pod, err := client.CoreV1().Pods(getCurrentNamespace()).Get(clusterName+"-"+strconv.Itoa(ins), metav1.GetOptions{})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		podRole = pod.Labels["spilo-role"]
 | 
			
		||||
		if podRole == "master" && master {
 | 
			
		||||
			podName = pod.Name
 | 
			
		||||
			fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
 | 
			
		||||
			break
 | 
			
		||||
		} else if podRole == "replica" && !master && (pod.Name == replica || replicaNumber == "") {
 | 
			
		||||
			podName = pod.Name
 | 
			
		||||
			fmt.Printf("connected to %s with pod name as %s\n", podRole, podName)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if podName == "" {
 | 
			
		||||
		log.Fatal("Provided replica doesn't exist")
 | 
			
		||||
	}
 | 
			
		||||
	return podName
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPostgresOperator(k8sClient *kubernetes.Clientset) *v1.Deployment {
 | 
			
		||||
	var operator *v1.Deployment
 | 
			
		||||
	operator, err := k8sClient.AppsV1().Deployments(getCurrentNamespace()).Get(OperatorName, metav1.GetOptions{})
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		return operator
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	allDeployments := k8sClient.AppsV1().Deployments("")
 | 
			
		||||
	listDeployments, err := allDeployments.List(metav1.ListOptions{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, deployment := range listDeployments.Items {
 | 
			
		||||
		if deployment.Name == OperatorName {
 | 
			
		||||
			operator = deployment.DeepCopy()
 | 
			
		||||
			break
 | 
			
		||||
		} else {
 | 
			
		||||
			for key, value := range deployment.Labels {
 | 
			
		||||
				if key == "app.kubernetes.io/name" && value == OperatorName {
 | 
			
		||||
					operator = deployment.DeepCopy()
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return operator
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package cmd
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"github.com/spf13/cobra"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var KubectlPgVersion string = "1.0"
 | 
			
		||||
 | 
			
		||||
// versionCmd represents the version command
 | 
			
		||||
var versionCmd = &cobra.Command{
 | 
			
		||||
	Use:   "version",
 | 
			
		||||
	Short: "version of kubectl-pg & postgres-operator",
 | 
			
		||||
	Long:  `version of kubectl-pg and current running postgres-operator`,
 | 
			
		||||
	Run: func(cmd *cobra.Command, args []string) {
 | 
			
		||||
		namespace, err := cmd.Flags().GetString("namespace")
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			log.Fatal(err)
 | 
			
		||||
		}
 | 
			
		||||
		version(namespace)
 | 
			
		||||
	},
 | 
			
		||||
	Example: `
 | 
			
		||||
#Lists the version of kubectl pg plugin and postgres operator in current namespace
 | 
			
		||||
kubectl pg version
 | 
			
		||||
 | 
			
		||||
#Lists the version of kubectl pg plugin and postgres operator in provided namespace
 | 
			
		||||
kubectl pg version -n namespace01
 | 
			
		||||
`,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func version(namespace string) {
 | 
			
		||||
	fmt.Printf("kubectl-pg: %s\n", KubectlPgVersion)
 | 
			
		||||
 | 
			
		||||
	config := getConfig()
 | 
			
		||||
	client, err := kubernetes.NewForConfig(config)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	operatorDeployment := getPostgresOperator(client)
 | 
			
		||||
	if operatorDeployment.Name == "" {
 | 
			
		||||
		log.Fatal("make sure zalando's postgres operator is running")
 | 
			
		||||
	}
 | 
			
		||||
	operatorImage := operatorDeployment.Spec.Template.Spec.Containers[0].Image
 | 
			
		||||
	imageDetails := strings.Split(operatorImage, ":")
 | 
			
		||||
	imageSplit := len(imageDetails)
 | 
			
		||||
	imageVersion := imageDetails[imageSplit-1]
 | 
			
		||||
	fmt.Printf("Postgres-Operator: %s\n", imageVersion)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	rootCmd.AddCommand(versionCmd)
 | 
			
		||||
	versionCmd.Flags().StringP("namespace", "n", DefaultNamespace, "provide the namespace.")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,14 @@
 | 
			
		|||
module kubectl-pg
 | 
			
		||||
 | 
			
		||||
go 1.12
 | 
			
		||||
 | 
			
		||||
require (
 | 
			
		||||
	github.com/imdario/mergo v0.3.7 // indirect
 | 
			
		||||
	github.com/kisielk/errcheck v1.2.0 // indirect
 | 
			
		||||
	github.com/spf13/cobra v0.0.5
 | 
			
		||||
	github.com/spf13/viper v1.4.0
 | 
			
		||||
	github.com/zalando/postgres-operator v1.2.0
 | 
			
		||||
	k8s.io/apiextensions-apiserver v0.0.0-20190726024412-102230e288fd
 | 
			
		||||
	k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc
 | 
			
		||||
	k8s.io/client-go v0.0.0-20190726023111-a9c895e7f2ac
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,386 @@
 | 
			
		|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
 | 
			
		||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
 | 
			
		||||
github.com/Azure/go-autorest v11.1.2+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
 | 
			
		||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 | 
			
		||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
 | 
			
		||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
 | 
			
		||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
 | 
			
		||||
github.com/PuerkitoBio/purell v1.1.1/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/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
 | 
			
		||||
github.com/VineethReddy02/postgres-operator v1.1.0 h1:I1CyYLrPbI78blfQY5Dy7m9TNsVVvCNXSM9rEKh888Q=
 | 
			
		||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 | 
			
		||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 | 
			
		||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20180720115003-f9ffefc3facf/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
 | 
			
		||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 | 
			
		||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 | 
			
		||||
github.com/blang/semver v3.5.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
 | 
			
		||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 | 
			
		||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 | 
			
		||||
github.com/coreos/bbolt v1.3.1-coreos.6/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-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
 | 
			
		||||
github.com/coreos/go-oidc v0.0.0-20180117170138-065b426bd416/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
 | 
			
		||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180108230652-97fdf19511ea/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 | 
			
		||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 | 
			
		||||
github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 | 
			
		||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 | 
			
		||||
github.com/dgrijalva/jwt-go v0.0.0-20160705203006-01aeca54ebda/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/docker/docker v0.7.3-0.20190327010347-be7ac8be2ae0/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 | 
			
		||||
github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
 | 
			
		||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg=
 | 
			
		||||
github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
 | 
			
		||||
github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
 | 
			
		||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
			
		||||
github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
 | 
			
		||||
github.com/evanphx/json-patch v0.0.0-20190203023257-5858425f7550/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
			
		||||
github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 | 
			
		||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 | 
			
		||||
github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
 | 
			
		||||
github.com/ghodss/yaml v0.0.0-20180820084758-c7ce16629ff4/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-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
 | 
			
		||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
 | 
			
		||||
github.com/go-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/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/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/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/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/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/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/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/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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 | 
			
		||||
github.com/gogo/protobuf v0.0.0-20171007142547-342cbe0a0415/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v0.0.0-20190410021324-65acae22fc9/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
 | 
			
		||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE=
 | 
			
		||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
 | 
			
		||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 | 
			
		||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 | 
			
		||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
 | 
			
		||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 | 
			
		||||
github.com/google/btree v0.0.0-20160524151835-7d79101e329e/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
 | 
			
		||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
 | 
			
		||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
 | 
			
		||||
github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
			
		||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
 | 
			
		||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
 | 
			
		||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 | 
			
		||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/googleapis/gnostic v0.0.0-20170426233943-68f4ded48ba9/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 | 
			
		||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d h1:7XGaL1e6bYS1yIonGp9761ExpPPV1ui0SAC59Yube9k=
 | 
			
		||||
github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
 | 
			
		||||
github.com/gophercloud/gophercloud v0.0.0-20190126172459-c818fa66e4c8/go.mod h1:3WdhXV3rUYy9p6AUW8d94kr+HS62Y4VL9mBnFxsD8q4=
 | 
			
		||||
github.com/gophercloud/gophercloud v0.1.0/go.mod h1:vxM41WHh5uqHVBMZHzuwNOHh8XEoIEcSTewFxm1c5g8=
 | 
			
		||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 | 
			
		||||
github.com/gregjones/httpcache v0.0.0-20170728041850-787624de3eb7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
 | 
			
		||||
github.com/grpc-ecosystem/go-grpc-middleware v0.0.0-20190222133341-cfaf5686ec79/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-prometheus v0.0.0-20170330212424-2500245aa611/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.3.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
 | 
			
		||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
 | 
			
		||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
 | 
			
		||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
 | 
			
		||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
 | 
			
		||||
github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
			
		||||
github.com/imdario/mergo v0.3.7 h1:Y+UAYTZ7gDEuOfhxKWy+dvb5dRQ6rJjFSdX2HZY1/gI=
 | 
			
		||||
github.com/imdario/mergo v0.3.7/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 | 
			
		||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 | 
			
		||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 | 
			
		||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v0.0.0-20180701071628-ab8a2e0c74be/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
 | 
			
		||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 | 
			
		||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
 | 
			
		||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
 | 
			
		||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
 | 
			
		||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
 | 
			
		||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 | 
			
		||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
 | 
			
		||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
 | 
			
		||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
 | 
			
		||||
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
 | 
			
		||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
 | 
			
		||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
 | 
			
		||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
 | 
			
		||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
 | 
			
		||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
 | 
			
		||||
github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
 | 
			
		||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
 | 
			
		||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
 | 
			
		||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 | 
			
		||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 | 
			
		||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 | 
			
		||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
 | 
			
		||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
 | 
			
		||||
github.com/onsi/gomega v0.0.0-20190113212917-5533ce8a0da3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
 | 
			
		||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
 | 
			
		||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 | 
			
		||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
 | 
			
		||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 | 
			
		||||
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.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
 | 
			
		||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
 | 
			
		||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
 | 
			
		||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
 | 
			
		||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 | 
			
		||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
 | 
			
		||||
github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M=
 | 
			
		||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
 | 
			
		||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 | 
			
		||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
 | 
			
		||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 | 
			
		||||
github.com/soheilhy/cmux v0.1.3/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/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
 | 
			
		||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
 | 
			
		||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
 | 
			
		||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
 | 
			
		||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
 | 
			
		||||
github.com/spf13/cobra v0.0.4/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 | 
			
		||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
 | 
			
		||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
 | 
			
		||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
 | 
			
		||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
 | 
			
		||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 | 
			
		||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
 | 
			
		||||
github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU=
 | 
			
		||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
 | 
			
		||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 | 
			
		||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
 | 
			
		||||
github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 | 
			
		||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
 | 
			
		||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 | 
			
		||||
github.com/xiang90/probing v0.0.0-20160813154853-07dd2e8dfe18/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/zalando/postgres-operator v1.2.0 h1:XV3zM2iON4O8iqLTlSNeekxIqistnUx7Btfk2w7mDaY=
 | 
			
		||||
github.com/zalando/postgres-operator v1.2.0/go.mod h1:0+dT6DbKj6yvytwBpApmSwEbMBqbLS9AzgUZacbG0lY=
 | 
			
		||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 | 
			
		||||
go.uber.org/atomic v0.0.0-20181018215023-8dc6146f7569/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 | 
			
		||||
go.uber.org/multierr v0.0.0-20180122172545-ddea229ff1df/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 | 
			
		||||
go.uber.org/zap v0.0.0-20180814183419-67bc79d13d15/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-20181025213731-e84da0312774/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8 h1:1wopBVtVdWnn03fZelqdXTqk7U7zPQCb+T4rbU9ZEoU=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 | 
			
		||||
golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
 | 
			
		||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 | 
			
		||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
 | 
			
		||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
 | 
			
		||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
 | 
			
		||||
golang.org/x/net v0.0.0-20170114055629-f2499483f923/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-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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190206173232-65e2d4e15006/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 | 
			
		||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
 | 
			
		||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
 | 
			
		||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
 | 
			
		||||
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 | 
			
		||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 | 
			
		||||
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-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-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190209173611-3b5209105503/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f h1:25KHgbfyiSm6vwQLbM3zZIe1v9p/3ea4Rz+nnM5K/i4=
 | 
			
		||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 | 
			
		||||
golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.1-0.20181227161524-e6919f6577db/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/time v0.0.0-20161028155119-f51c12702a4d/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ=
 | 
			
		||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
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-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
 | 
			
		||||
gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
 | 
			
		||||
gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ=
 | 
			
		||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
 | 
			
		||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c=
 | 
			
		||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20170731182057-09f6ed296fc6/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
 | 
			
		||||
google.golang.org/grpc v1.13.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
 | 
			
		||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
 | 
			
		||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
 | 
			
		||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 | 
			
		||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 | 
			
		||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
 | 
			
		||||
gopkg.in/inf.v0 v0.9.0 h1:3zYtXIO92bvsdS3ggAdA8Gb4Azj0YU+TVY1uGYNFA8o=
 | 
			
		||||
gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
 | 
			
		||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
 | 
			
		||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
 | 
			
		||||
gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
 | 
			
		||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
 | 
			
		||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 | 
			
		||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
 | 
			
		||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
 | 
			
		||||
k8s.io/api v0.0.0-20190620084959-7cf5895f2711/go.mod h1:TBhBqb1AWbBQbW3XRusr7n7E4v2+5ZY8r8sAMnyFC5A=
 | 
			
		||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5 h1:vSfC/FjyeuqXC/fjdNqZixNpeec4mEHJ68K3kzetm/M=
 | 
			
		||||
k8s.io/api v0.0.0-20190726022912-69e1bce1dad5/go.mod h1:V6cpJ9D7WqSy0wqcE096gcbj+W//rshgQgmj1Shdwi8=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.0.0-20190726024412-102230e288fd h1:qnJFeJfmqE4nGI+xUjLsSzpl5o08JkRGttSIfzfqj7U=
 | 
			
		||||
k8s.io/apiextensions-apiserver v0.0.0-20190726024412-102230e288fd/go.mod h1:sDyIzs1dBO19o8gtqZK79kPQ+OIyjo34y2Gh2O+2MMo=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20190612205821-1799e75a0719/go.mod h1:I4A+glKBHiTgiEjQiCCQfCAIcIMFGt291SmsvcrFzJA=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20190726022757-641a75999153/go.mod h1:eXR4ljjmbwK6Ng0PKsXRySPXnTUy/qBUa6kPDeckhQ0=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc h1:fi1vG9UrnqoGU/H2HP2rr7GH6vaQeFdLxfocg5uMQmA=
 | 
			
		||||
k8s.io/apimachinery v0.0.0-20190727130956-f97a4e5b4abc/go.mod h1:eXR4ljjmbwK6Ng0PKsXRySPXnTUy/qBUa6kPDeckhQ0=
 | 
			
		||||
k8s.io/apiserver v0.0.0-20190726023815-781c3cd1b3dc/go.mod h1:Gy8ElOsvjzEZF7lUFUffGBuA6Vg4qsN/r+vt05szn6c=
 | 
			
		||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab h1:E8Fecph0qbNsAbijJJQryKu4Oi9QTp5cVpjTE+nqg6g=
 | 
			
		||||
k8s.io/client-go v0.0.0-20190620085101-78d2af792bab/go.mod h1:E95RaSlHr79aHaX0aGSwcPNfygDiPKOVXdmivCIZT0k=
 | 
			
		||||
k8s.io/client-go v0.0.0-20190726023111-a9c895e7f2ac h1:wqRgq2VyWMCJW9mU9MIMAPj5jBOjFFQYbT/DydDUo94=
 | 
			
		||||
k8s.io/client-go v0.0.0-20190726023111-a9c895e7f2ac/go.mod h1:ncT9fCvHnM5BUiZs0RCf9vAEqRrRoJtR2sZ2evompEU=
 | 
			
		||||
k8s.io/client-go v11.0.0+incompatible h1:LBbX2+lOwY9flffWlJM7f1Ct8V2SRNiMRDFeiwnJo9o=
 | 
			
		||||
k8s.io/client-go v11.0.0+incompatible/go.mod h1:7vJpHMYJwNQCWgzmNV+VYUl1zCObLyodBc8nIyt8L5s=
 | 
			
		||||
k8s.io/code-generator v0.0.0-20190726022633-14ba7d03f06f/go.mod h1:kr7tMYxZEaP3mrijPwXnhxOvPyqdJw6TZH87KfFboQ0=
 | 
			
		||||
k8s.io/component-base v0.0.0-20190726023549-042c00bc1f9e/go.mod h1:KiJFR5KR5yaKNXFgCliO2CPcmAI6hdZCcb5XZyl0EhQ=
 | 
			
		||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
 | 
			
		||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 | 
			
		||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 | 
			
		||||
k8s.io/klog v0.3.1 h1:RVgyDHY/kFKtLqh67NvEWIgkMneNoIrdkN0CxDSQc68=
 | 
			
		||||
k8s.io/klog v0.3.1/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20190228160746-b3a7cee44a30/go.mod h1:BXM9ceUBTj2QnfH2MK1odQs778ajze1RxcmP6S8RVVc=
 | 
			
		||||
k8s.io/kube-openapi v0.0.0-20190709113604-33be087ad058/go.mod h1:nfDlWeOsu3pUf4yWGL+ERqohP4YsZcBJXWMK+gkzOA4=
 | 
			
		||||
k8s.io/utils v0.0.0-20190221042446-c2654d5206da/go.mod h1:8k8uAuAQ0rXslZKaEWd0c3oVhZz7sSzSiPnVZayjIX0=
 | 
			
		||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a h1:2jUDc9gJja832Ftp+QbDV0tVhQHMISFn01els+2ZAcw=
 | 
			
		||||
k8s.io/utils v0.0.0-20190607212802-c55fbcfc754a/go.mod h1:sZAwmy6armz5eXlNoLmJcl4F1QuKu7sr+mFQ0byX7Ew=
 | 
			
		||||
modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw=
 | 
			
		||||
modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk=
 | 
			
		||||
modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k=
 | 
			
		||||
modernc.org/strutil v1.0.0/go.mod h1:lstksw84oURvj9y3tn8lGvRxyRC1S2+g5uuIzNfIOBs=
 | 
			
		||||
modernc.org/xc v1.0.0/go.mod h1:mRNCo0bvLjGhHO9WsyuKVU4q0ceiDDDoEeWDJHrNx8I=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 | 
			
		||||
sigs.k8s.io/structured-merge-diff v0.0.0-20190719182312-e94e05bfbbe3/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
 | 
			
		||||
sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
 | 
			
		||||
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,31 @@
 | 
			
		|||
/*
 | 
			
		||||
Copyright © 2019 Vineeth Pothulapati <vineethpothulapati@outlook.com>
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
in the Software without restriction, including without limitation the rights
 | 
			
		||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
furnished to do so, subject to the following conditions:
 | 
			
		||||
 | 
			
		||||
The above copyright notice and this permission notice shall be included in
 | 
			
		||||
all copies or substantial portions of the Software.
 | 
			
		||||
 | 
			
		||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
THE SOFTWARE.
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"kubectl-pg/cmd"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	cmd.Execute()
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -2,13 +2,11 @@ apiVersion: "acid.zalan.do/v1"
 | 
			
		|||
kind: postgresql
 | 
			
		||||
metadata:
 | 
			
		||||
  name: acid-test-cluster
 | 
			
		||||
#  labels:
 | 
			
		||||
#    environment: demo
 | 
			
		||||
spec:
 | 
			
		||||
  dockerImage: registry.opensource.zalan.do/acid/spilo-11:1.5-p9
 | 
			
		||||
  initContainers:
 | 
			
		||||
  - name: date
 | 
			
		||||
    image: busybox
 | 
			
		||||
    command: [ "/bin/date" ]
 | 
			
		||||
  teamId: "ACID"
 | 
			
		||||
  dockerImage: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  teamId: "acid"
 | 
			
		||||
  volume:
 | 
			
		||||
    size: 1Gi
 | 
			
		||||
#    storageClass: my-sc
 | 
			
		||||
| 
						 | 
				
			
			@ -17,23 +15,28 @@ spec:
 | 
			
		|||
    zalando:
 | 
			
		||||
    - superuser
 | 
			
		||||
    - createdb
 | 
			
		||||
  enableMasterLoadBalancer: true
 | 
			
		||||
  enableReplicaLoadBalancer: true
 | 
			
		||||
  enableMasterLoadBalancer: false
 | 
			
		||||
  enableReplicaLoadBalancer: false
 | 
			
		||||
  allowedSourceRanges:  # load balancers' source ranges for both master and replica services
 | 
			
		||||
  - 127.0.0.1/32
 | 
			
		||||
  databases:
 | 
			
		||||
    foo: zalando
 | 
			
		||||
 | 
			
		||||
# Expert section
 | 
			
		||||
 | 
			
		||||
  enableShmVolume: true
 | 
			
		||||
# spiloFSGroup: 103
 | 
			
		||||
  postgresql:
 | 
			
		||||
    version: "10"
 | 
			
		||||
    parameters:
 | 
			
		||||
    version: "11"
 | 
			
		||||
    parameters: # Expert section
 | 
			
		||||
      shared_buffers: "32MB"
 | 
			
		||||
      max_connections: "10"
 | 
			
		||||
      log_statement: "all"
 | 
			
		||||
 | 
			
		||||
  enableShmVolume: true
 | 
			
		||||
#  spiloFSGroup: 103
 | 
			
		||||
#  podAnnotations:
 | 
			
		||||
#    annotation.key: value
 | 
			
		||||
#  podPriorityClassName: "spilo-pod-priority"
 | 
			
		||||
#  tolerations:
 | 
			
		||||
#  - key: postgres
 | 
			
		||||
#    operator: Exists
 | 
			
		||||
#    effect: NoSchedule
 | 
			
		||||
  resources:
 | 
			
		||||
    requests:
 | 
			
		||||
      cpu: 10m
 | 
			
		||||
| 
						 | 
				
			
			@ -50,9 +53,9 @@ spec:
 | 
			
		|||
    - hostssl all all 0.0.0.0/0 md5
 | 
			
		||||
    - host    all all 0.0.0.0/0 md5
 | 
			
		||||
#    slots:
 | 
			
		||||
#     - permanent_physical_1:
 | 
			
		||||
#      permanent_physical_1:
 | 
			
		||||
#        type: physical
 | 
			
		||||
#     - permanent_logical_1:
 | 
			
		||||
#      permanent_logical_1:
 | 
			
		||||
#        type: logical
 | 
			
		||||
#        database: foo
 | 
			
		||||
#        plugin: pgoutput
 | 
			
		||||
| 
						 | 
				
			
			@ -60,6 +63,7 @@ spec:
 | 
			
		|||
    loop_wait: &loop_wait 10
 | 
			
		||||
    retry_timeout: 10
 | 
			
		||||
    maximum_lag_on_failover: 33554432
 | 
			
		||||
 | 
			
		||||
# restore a Postgres DB with point-in-time-recovery
 | 
			
		||||
# with a non-empty timestamp, clone from an S3 bucket using the latest backup before the timestamp
 | 
			
		||||
# with an empty/absent timestamp, clone from an existing alive cluster using pg_basebackup
 | 
			
		||||
| 
						 | 
				
			
			@ -72,9 +76,15 @@ spec:
 | 
			
		|||
# run periodic backups with k8s cron jobs
 | 
			
		||||
#  enableLogicalBackup: true
 | 
			
		||||
#  logicalBackupSchedule: "30 00 * * *"
 | 
			
		||||
  maintenanceWindows:
 | 
			
		||||
  - 01:00-06:00  #UTC
 | 
			
		||||
  - Sat:00:00-04:00
 | 
			
		||||
 | 
			
		||||
#  maintenanceWindows:
 | 
			
		||||
#  - 01:00-06:00  #UTC
 | 
			
		||||
#  - Sat:00:00-04:00
 | 
			
		||||
 | 
			
		||||
  initContainers:
 | 
			
		||||
  - name: date
 | 
			
		||||
    image: busybox
 | 
			
		||||
    command: [ "/bin/date" ]
 | 
			
		||||
#  sidecars:
 | 
			
		||||
#    - name: "telegraf-sidecar"
 | 
			
		||||
#      image: "telegraf:latest"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,33 +11,40 @@ data:
 | 
			
		|||
  cluster_history_entries: "1000"
 | 
			
		||||
  cluster_labels: application:spilo
 | 
			
		||||
  cluster_name_label: version
 | 
			
		||||
  # custom_service_annotations:
 | 
			
		||||
  #   "keyx:valuez,keya:valuea"
 | 
			
		||||
  # custom_service_annotations: "keyx:valuez,keya:valuea"
 | 
			
		||||
  # custom_pod_annotations: "keya:valuea,keyb:valueb"
 | 
			
		||||
  db_hosted_zone: db.example.com
 | 
			
		||||
  debug_logging: "true"
 | 
			
		||||
  # default_cpu_limit: "3"
 | 
			
		||||
  # default_cpu_request: 100m
 | 
			
		||||
  # default_memory_limit: 1Gi
 | 
			
		||||
  # default_memory_request: 100Mi
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-11:1.5-p9
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  # enable_admin_role_for_users: "true"
 | 
			
		||||
  # enable_crd_validation: "true"
 | 
			
		||||
  # enable_database_access: "true"
 | 
			
		||||
  enable_master_load_balancer: "true"
 | 
			
		||||
  # enable_init_containers: "true"
 | 
			
		||||
  enable_master_load_balancer: "false"
 | 
			
		||||
  # enable_pod_antiaffinity: "false"
 | 
			
		||||
  # enable_pod_disruption_budget: "true"
 | 
			
		||||
  enable_replica_load_balancer: "false"
 | 
			
		||||
  # enable_shm_volume: "true"
 | 
			
		||||
  # enable_sidecars: "true"
 | 
			
		||||
  # enable_team_superuser: "false"
 | 
			
		||||
  enable_teams_api: "false"
 | 
			
		||||
  # etcd_host: ""
 | 
			
		||||
  # infrastructure_roles_secret_name: postgresql-infrastructure-roles
 | 
			
		||||
  # inherited_labels: ""
 | 
			
		||||
  # inherited_labels: application,environment
 | 
			
		||||
  # kube_iam_role: ""
 | 
			
		||||
  # log_s3_bucket: ""
 | 
			
		||||
  # logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup"
 | 
			
		||||
  # logical_backup_s3_access_key_id: ""
 | 
			
		||||
  # logical_backup_s3_bucket: "my-bucket-url"
 | 
			
		||||
  # logical_backup_s3_endpoint: ""
 | 
			
		||||
  # logical_backup_s3_secret_access_key: ""
 | 
			
		||||
  # logical_backup_s3_sse: "AES256"
 | 
			
		||||
  # logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
  master_dns_name_format: '{cluster}.{team}.staging.{hostedzone}'
 | 
			
		||||
  master_dns_name_format: "{cluster}.{team}.{hostedzone}"
 | 
			
		||||
  # master_pod_move_timeout: 10m
 | 
			
		||||
  # max_instances: "-1"
 | 
			
		||||
  # min_instances: "-1"
 | 
			
		||||
| 
						 | 
				
			
			@ -60,13 +67,13 @@ data:
 | 
			
		|||
  ready_wait_interval: 3s
 | 
			
		||||
  ready_wait_timeout: 30s
 | 
			
		||||
  repair_period: 5m
 | 
			
		||||
  replica_dns_name_format: '{cluster}-repl.{team}.staging.{hostedzone}'
 | 
			
		||||
  replica_dns_name_format: "{cluster}-repl.{team}.{hostedzone}"
 | 
			
		||||
  replication_username: standby
 | 
			
		||||
  resource_check_interval: 3s
 | 
			
		||||
  resource_check_timeout: 10m
 | 
			
		||||
  resync_period: 5m
 | 
			
		||||
  resync_period: 30m
 | 
			
		||||
  ring_log_lines: "100"
 | 
			
		||||
  secret_name_template: '{username}.{cluster}.credentials'
 | 
			
		||||
  secret_name_template: "{username}.{cluster}.credentials"
 | 
			
		||||
  # sidecar_docker_images: ""
 | 
			
		||||
  # set_memory_request_to_limit: "false"
 | 
			
		||||
  spilo_privileged: "false"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ metadata:
 | 
			
		|||
  name: acid-minimal-cluster
 | 
			
		||||
  namespace: default
 | 
			
		||||
spec:
 | 
			
		||||
  teamId: "ACID"
 | 
			
		||||
  teamId: "acid"
 | 
			
		||||
  volume:
 | 
			
		||||
    size: 1Gi
 | 
			
		||||
  numberOfInstances: 2
 | 
			
		||||
| 
						 | 
				
			
			@ -16,4 +16,4 @@ spec:
 | 
			
		|||
  databases:
 | 
			
		||||
    foo: zalando  # dbname: owner
 | 
			
		||||
  postgresql:
 | 
			
		||||
    version: "10"
 | 
			
		||||
    version: "11"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,6 +10,7 @@ kind: ClusterRole
 | 
			
		|||
metadata:
 | 
			
		||||
  name: zalando-postgres-operator
 | 
			
		||||
rules:
 | 
			
		||||
# all verbs allowed for custom operator resources
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - acid.zalan.do
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -18,6 +19,7 @@ rules:
 | 
			
		|||
  - operatorconfigurations
 | 
			
		||||
  verbs:
 | 
			
		||||
  - "*"
 | 
			
		||||
# to create or get/update CRDs when starting up
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - apiextensions.k8s.io
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -27,12 +29,14 @@ rules:
 | 
			
		|||
  - get
 | 
			
		||||
  - patch
 | 
			
		||||
  - update
 | 
			
		||||
# to read configuration from ConfigMaps
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
  - configmaps
 | 
			
		||||
  verbs:
 | 
			
		||||
  - get
 | 
			
		||||
# to manage endpoints which are also used by Patroni
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -45,6 +49,7 @@ rules:
 | 
			
		|||
  - list
 | 
			
		||||
  - patch
 | 
			
		||||
  - watch  # needed if zalando-postgres-operator account is used for pods as well
 | 
			
		||||
# to CRUD secrets for database access
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -54,6 +59,7 @@ rules:
 | 
			
		|||
  - update
 | 
			
		||||
  - delete
 | 
			
		||||
  - get
 | 
			
		||||
# to check nodes for node readiness label
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -62,6 +68,7 @@ rules:
 | 
			
		|||
  - get
 | 
			
		||||
  - list
 | 
			
		||||
  - watch
 | 
			
		||||
# to read or delete existing PVCs. Creation via StatefulSet
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -70,6 +77,7 @@ rules:
 | 
			
		|||
  - delete
 | 
			
		||||
  - get
 | 
			
		||||
  - list
 | 
			
		||||
 # to read existing PVs. Creation should be done via dynamic provisioning
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -78,6 +86,7 @@ rules:
 | 
			
		|||
  - get
 | 
			
		||||
  - list
 | 
			
		||||
  - update  # only for resizing AWS volumes
 | 
			
		||||
# to watch Spilo pods and do rolling updates. Creation via StatefulSet
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -88,6 +97,14 @@ rules:
 | 
			
		|||
  - list
 | 
			
		||||
  - watch
 | 
			
		||||
  - patch
 | 
			
		||||
# to resize the filesystem in Spilo pods when increasing volume size
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
  - pods/exec
 | 
			
		||||
  verbs:
 | 
			
		||||
  - create
 | 
			
		||||
# to CRUD services to point to Postgres cluster instances
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -97,6 +114,7 @@ rules:
 | 
			
		|||
  - delete
 | 
			
		||||
  - get
 | 
			
		||||
  - patch
 | 
			
		||||
# to CRUD the StatefulSet which controls the Postgres cluster instances
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - apps
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -107,12 +125,14 @@ rules:
 | 
			
		|||
  - get
 | 
			
		||||
  - list
 | 
			
		||||
  - patch
 | 
			
		||||
# to get namespaces operator resources can run in
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
  - namespaces
 | 
			
		||||
  verbs:
 | 
			
		||||
  - get
 | 
			
		||||
# to define PDBs. Update happens via delete/create
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - policy
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -121,6 +141,7 @@ rules:
 | 
			
		|||
  - create
 | 
			
		||||
  - delete
 | 
			
		||||
  - get
 | 
			
		||||
# to create ServiceAccounts in each namespace the operator watches
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - ""
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -128,6 +149,7 @@ rules:
 | 
			
		|||
  verbs:
 | 
			
		||||
  - get
 | 
			
		||||
  - create
 | 
			
		||||
# to create role bindings to the operator service account
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - "rbac.authorization.k8s.io"
 | 
			
		||||
  resources:
 | 
			
		||||
| 
						 | 
				
			
			@ -135,18 +157,11 @@ rules:
 | 
			
		|||
  verbs:
 | 
			
		||||
  - get
 | 
			
		||||
  - create
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - "rbac.authorization.k8s.io"
 | 
			
		||||
  resources:
 | 
			
		||||
  - clusterroles
 | 
			
		||||
  verbs:
 | 
			
		||||
  - bind
 | 
			
		||||
  resourceNames:
 | 
			
		||||
  - zalando-postgres-operator
 | 
			
		||||
# to CRUD cron jobs for logical backups
 | 
			
		||||
- apiGroups:
 | 
			
		||||
  - batch
 | 
			
		||||
  resources:
 | 
			
		||||
  - cronjobs  # enables logical backups
 | 
			
		||||
  - cronjobs
 | 
			
		||||
  verbs:
 | 
			
		||||
  - create
 | 
			
		||||
  - delete
 | 
			
		||||
| 
						 | 
				
			
			@ -154,6 +169,7 @@ rules:
 | 
			
		|||
  - list
 | 
			
		||||
  - patch
 | 
			
		||||
  - update
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
apiVersion: rbac.authorization.k8s.io/v1
 | 
			
		||||
kind: ClusterRoleBinding
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,292 @@
 | 
			
		|||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: operatorconfigurations.acid.zalan.do
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: OperatorConfiguration
 | 
			
		||||
    listKind: OperatorConfigurationList
 | 
			
		||||
    plural: operatorconfigurations
 | 
			
		||||
    singular: operatorconfiguration
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - opconfig
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
  validation:
 | 
			
		||||
    openAPIV3Schema:
 | 
			
		||||
      type: object
 | 
			
		||||
      required:
 | 
			
		||||
        - kind
 | 
			
		||||
        - apiVersion
 | 
			
		||||
        - configuration
 | 
			
		||||
      properties:
 | 
			
		||||
        kind:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - OperatorConfiguration
 | 
			
		||||
        apiVersion:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - acid.zalan.do/v1
 | 
			
		||||
        configuration:
 | 
			
		||||
          type: object
 | 
			
		||||
          properties:
 | 
			
		||||
            docker_image:
 | 
			
		||||
              type: string
 | 
			
		||||
            enable_crd_validation:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enable_shm_volume:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            etcd_host:
 | 
			
		||||
              type: string
 | 
			
		||||
            max_instances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: -1  # -1 = disabled
 | 
			
		||||
            min_instances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: -1  # -1 = disabled
 | 
			
		||||
            resync_period:
 | 
			
		||||
              type: string
 | 
			
		||||
            repair_period:
 | 
			
		||||
              type: string
 | 
			
		||||
            set_memory_request_to_limit:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            sidecar_docker_images:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
            workers:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: 1
 | 
			
		||||
            users:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                replication_username:
 | 
			
		||||
                   type: string
 | 
			
		||||
                super_username:
 | 
			
		||||
                   type: string
 | 
			
		||||
            kubernetes:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                cluster_domain:
 | 
			
		||||
                  type: string
 | 
			
		||||
                cluster_labels:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                cluster_name_label:
 | 
			
		||||
                  type: string
 | 
			
		||||
                custom_pod_annotations:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                enable_init_containers:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_pod_antiaffinity:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_pod_disruption_budget:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_sidecars:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                infrastructure_roles_secret_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                inherited_labels:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                master_pod_move_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                node_readiness_label:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                oauth_token_secret_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pdb_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_antiaffinity_topology_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_environment_configmap:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_management_policy:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                    - "ordered_ready"
 | 
			
		||||
                    - "parallel"
 | 
			
		||||
                pod_priority_class_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_role_label:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_definition:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_service_account_role_binding_definition:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_terminate_grace_period:
 | 
			
		||||
                  type: string
 | 
			
		||||
                secret_name_template:
 | 
			
		||||
                  type: string
 | 
			
		||||
                spilo_fsgroup:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                spilo_privileged:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                toleration:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                watched_namespace:
 | 
			
		||||
                  type: string
 | 
			
		||||
            postgres_pod_resources:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                default_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                default_cpu_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                default_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                default_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
            timeouts:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                pod_label_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pod_deletion_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                ready_wait_interval:
 | 
			
		||||
                  type: string
 | 
			
		||||
                ready_wait_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
                resource_check_interval:
 | 
			
		||||
                  type: string
 | 
			
		||||
                resource_check_timeout:
 | 
			
		||||
                  type: string
 | 
			
		||||
            load_balancer:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                custom_service_annotations:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                db_hosted_zone:
 | 
			
		||||
                  type: string
 | 
			
		||||
                enable_master_load_balancer:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_replica_load_balancer:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                master_dns_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
                replica_dns_name_format:
 | 
			
		||||
                  type: string
 | 
			
		||||
            aws_or_gcp:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                additional_secret_mount:
 | 
			
		||||
                  type: string
 | 
			
		||||
                additional_secret_mount_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
                aws_region:
 | 
			
		||||
                  type: string
 | 
			
		||||
                kube_iam_role:
 | 
			
		||||
                  type: string
 | 
			
		||||
                log_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
                wal_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
            logical_backup:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                logical_backup_docker_image:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_access_key_id:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_bucket:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_endpoint:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_secret_access_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_s3_sse:
 | 
			
		||||
                  type: string
 | 
			
		||||
                logical_backup_schedule:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
 | 
			
		||||
            debug:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                debug_logging:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_database_access:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
            teams_api:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                enable_admin_role_for_users:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_team_superuser:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                enable_teams_api:
 | 
			
		||||
                  type: boolean
 | 
			
		||||
                pam_configuration:
 | 
			
		||||
                  type: string
 | 
			
		||||
                pam_role_name:
 | 
			
		||||
                  type: string
 | 
			
		||||
                postgres_superuser_teams:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                protected_role_names:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                team_admin_role:
 | 
			
		||||
                  type: string
 | 
			
		||||
                team_api_role_configuration:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                teams_api_url:
 | 
			
		||||
                  type: string
 | 
			
		||||
            logging_rest_api:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                api_port:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                cluster_history_entries:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                ring_log_lines:
 | 
			
		||||
                  type: integer
 | 
			
		||||
            scalyr:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                scalyr_api_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                scalyr_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                scalyr_cpu_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                scalyr_image:
 | 
			
		||||
                  type: string
 | 
			
		||||
                scalyr_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                scalyr_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                scalyr_server_url:
 | 
			
		||||
                  type: string
 | 
			
		||||
        status:
 | 
			
		||||
          type: object
 | 
			
		||||
          additionalProperties:
 | 
			
		||||
            type: string
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +15,7 @@ spec:
 | 
			
		|||
      serviceAccountName: zalando-postgres-operator
 | 
			
		||||
      containers:
 | 
			
		||||
      - name: postgres-operator
 | 
			
		||||
        image: registry.opensource.zalan.do/acid/postgres-operator:v1.2.0
 | 
			
		||||
        image: registry.opensource.zalan.do/acid/postgres-operator:v1.3.0
 | 
			
		||||
        imagePullPolicy: IfNotPresent
 | 
			
		||||
        resources:
 | 
			
		||||
          requests:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,8 +3,9 @@ kind: OperatorConfiguration
 | 
			
		|||
metadata:
 | 
			
		||||
  name: postgresql-operator-default-configuration
 | 
			
		||||
configuration:
 | 
			
		||||
  # enable_crd_validation: true
 | 
			
		||||
  etcd_host: ""
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-11:1.5-p9
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  # enable_shm_volume: true
 | 
			
		||||
  max_instances: -1
 | 
			
		||||
  min_instances: -1
 | 
			
		||||
| 
						 | 
				
			
			@ -22,26 +23,36 @@ configuration:
 | 
			
		|||
    cluster_labels:
 | 
			
		||||
      application: spilo
 | 
			
		||||
    cluster_name_label: cluster-name
 | 
			
		||||
    # custom_pod_annotations:
 | 
			
		||||
    #   keya: valuea
 | 
			
		||||
    #   keyb: valueb
 | 
			
		||||
    enable_init_containers: true
 | 
			
		||||
    enable_pod_antiaffinity: false
 | 
			
		||||
    enable_pod_disruption_budget: true
 | 
			
		||||
    # infrastructure_roles_secret_name: ""
 | 
			
		||||
    enable_sidecars: true
 | 
			
		||||
    # infrastructure_roles_secret_name: "postgresql-infrastructure-roles"
 | 
			
		||||
    # inherited_labels:
 | 
			
		||||
    # - application
 | 
			
		||||
    # - app
 | 
			
		||||
    # node_readiness_label: ""
 | 
			
		||||
    # - environment
 | 
			
		||||
    master_pod_move_timeout: 20m
 | 
			
		||||
    # node_readiness_label:
 | 
			
		||||
    #   status: ready
 | 
			
		||||
    oauth_token_secret_name: postgresql-operator
 | 
			
		||||
    pdb_name_format: "postgres-{cluster}-pdb"
 | 
			
		||||
    pod_antiaffinity_topology_key: "kubernetes.io/hostname"
 | 
			
		||||
    # pod_environment_configmap: ""
 | 
			
		||||
    pod_management_policy: "ordered_ready"
 | 
			
		||||
    # pod_priority_class_name: ""
 | 
			
		||||
    pod_role_label: spilo-role
 | 
			
		||||
    pod_service_account_name: operator
 | 
			
		||||
    # pod_service_account_definition: ""
 | 
			
		||||
    pod_service_account_name: zalando-postgres-operator
 | 
			
		||||
    # pod_service_account_role_binding_definition: ""
 | 
			
		||||
    pod_terminate_grace_period: 5m
 | 
			
		||||
    secret_name_template: "{username}.{cluster}.credentials.{tprkind}.{tprgroup}"
 | 
			
		||||
    # spilo_fsgroup: 103
 | 
			
		||||
    spilo_privileged: false
 | 
			
		||||
    # toleration: {}
 | 
			
		||||
    # watched_namespace:""
 | 
			
		||||
    # watched_namespace: ""
 | 
			
		||||
  postgres_pod_resources:
 | 
			
		||||
    default_cpu_limit: "3"
 | 
			
		||||
    default_cpu_request: 100m
 | 
			
		||||
| 
						 | 
				
			
			@ -71,9 +82,13 @@ configuration:
 | 
			
		|||
    # log_s3_bucket: ""
 | 
			
		||||
    # wal_s3_bucket: ""
 | 
			
		||||
  logical_backup:
 | 
			
		||||
    logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
    logical_backup_docker_image: "registry.opensource.zalan.do/acid/logical-backup"
 | 
			
		||||
    # logical_backup_s3_access_key_id: ""
 | 
			
		||||
    logical_backup_s3_bucket: "my-bucket-url"
 | 
			
		||||
    # logical_backup_s3_endpoint: ""
 | 
			
		||||
    # logical_backup_s3_secret_access_key: ""
 | 
			
		||||
    logical_backup_s3_sse: "AES256"
 | 
			
		||||
    logical_backup_schedule: "30 00 * * *"
 | 
			
		||||
  debug:
 | 
			
		||||
    debug_logging: true
 | 
			
		||||
    enable_database_access: true
 | 
			
		||||
| 
						 | 
				
			
			@ -83,7 +98,8 @@ configuration:
 | 
			
		|||
    enable_teams_api: false
 | 
			
		||||
    # pam_configuration: ""
 | 
			
		||||
    pam_role_name: zalandos
 | 
			
		||||
    # postgres_superuser_teams: "postgres_superusers"
 | 
			
		||||
    # postgres_superuser_teams:
 | 
			
		||||
    # - postgres_superusers
 | 
			
		||||
    protected_role_names:
 | 
			
		||||
    - admin
 | 
			
		||||
    team_admin_role: admin
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,331 @@
 | 
			
		|||
apiVersion: apiextensions.k8s.io/v1beta1
 | 
			
		||||
kind: CustomResourceDefinition
 | 
			
		||||
metadata:
 | 
			
		||||
  name: postgresqls.acid.zalan.do
 | 
			
		||||
spec:
 | 
			
		||||
  group: acid.zalan.do
 | 
			
		||||
  names:
 | 
			
		||||
    kind: postgresql
 | 
			
		||||
    listKind: postgresqlList
 | 
			
		||||
    plural: postgresqls
 | 
			
		||||
    singular: postgresql
 | 
			
		||||
    shortNames:
 | 
			
		||||
    - pg
 | 
			
		||||
  scope: Namespaced
 | 
			
		||||
  subresources:
 | 
			
		||||
    status: {}
 | 
			
		||||
  version: v1
 | 
			
		||||
  validation:
 | 
			
		||||
    openAPIV3Schema:
 | 
			
		||||
      type: object
 | 
			
		||||
      required:
 | 
			
		||||
        - kind
 | 
			
		||||
        - apiVersion
 | 
			
		||||
        - spec
 | 
			
		||||
      properties:
 | 
			
		||||
        kind:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - postgresql
 | 
			
		||||
        apiVersion:
 | 
			
		||||
          type: string
 | 
			
		||||
          enum:
 | 
			
		||||
            - acid.zalan.do/v1
 | 
			
		||||
        spec:
 | 
			
		||||
          type: object
 | 
			
		||||
          required:
 | 
			
		||||
            - numberOfInstances
 | 
			
		||||
            - teamId
 | 
			
		||||
            - postgresql
 | 
			
		||||
          properties:
 | 
			
		||||
            allowedSourceRanges:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: string
 | 
			
		||||
                pattern: '^(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\.(\d|[1-9]\d|1\d\d|2[0-4]\d|25[0-5])\/(\d|[1-2]\d|3[0-2])$'
 | 
			
		||||
            clone:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - cluster
 | 
			
		||||
              properties:
 | 
			
		||||
                cluster:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_endpoint:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_access_key_id:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_secret_access_key:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_force_path_style:
 | 
			
		||||
                  type: string
 | 
			
		||||
                s3_wal_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
                timestamp:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\.[0-9]+)?(([Zz])|([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$'
 | 
			
		||||
                  # The regexp matches the date-time format (RFC 3339 Section 5.6) that specifies a timezone as an offset relative to UTC
 | 
			
		||||
                  # Example: 1996-12-19T16:39:57-08:00
 | 
			
		||||
                  # Note: this field requires a timezone
 | 
			
		||||
                uid:
 | 
			
		||||
                  format: uuid
 | 
			
		||||
                  type: string
 | 
			
		||||
            databases:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
              # Note: usernames specified here as database owners must be declared in the users key of the spec key.
 | 
			
		||||
            dockerImage:
 | 
			
		||||
              type: string
 | 
			
		||||
            enableLogicalBackup:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableMasterLoadBalancer:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableReplicaLoadBalancer:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            enableShmVolume:
 | 
			
		||||
              type: boolean
 | 
			
		||||
            init_containers:  # deprecated
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            initContainers:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            logicalBackupSchedule:
 | 
			
		||||
              type: string
 | 
			
		||||
              pattern: '^(\d+|\*)(/\d+)?(\s+(\d+|\*)(/\d+)?){4}$'
 | 
			
		||||
            maintenanceWindows:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                type: string
 | 
			
		||||
                pattern: '^\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\d):([0-5]?\d)|(2[0-3]|[01]?\d):([0-5]?\d))\ *$'
 | 
			
		||||
            numberOfInstances:
 | 
			
		||||
              type: integer
 | 
			
		||||
              minimum: 0
 | 
			
		||||
            patroni:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
                initdb:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
                pg_hba:
 | 
			
		||||
                  type: array
 | 
			
		||||
                  items:
 | 
			
		||||
                    type: string
 | 
			
		||||
                slots:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: object
 | 
			
		||||
                    additionalProperties:
 | 
			
		||||
                      type: string
 | 
			
		||||
                ttl:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                loop_wait:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                retry_timeout:
 | 
			
		||||
                  type: integer
 | 
			
		||||
                maximum_lag_on_failover:
 | 
			
		||||
                  type: integer
 | 
			
		||||
            podAnnotations:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: string
 | 
			
		||||
            pod_priority_class_name:  # deprecated
 | 
			
		||||
              type: string
 | 
			
		||||
            podPriorityClassName:
 | 
			
		||||
              type: string
 | 
			
		||||
            postgresql:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - version
 | 
			
		||||
              properties:
 | 
			
		||||
                version:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                    - "9.3"
 | 
			
		||||
                    - "9.4"
 | 
			
		||||
                    - "9.5"
 | 
			
		||||
                    - "9.6"
 | 
			
		||||
                    - "10"
 | 
			
		||||
                    - "11"
 | 
			
		||||
                    - "12"
 | 
			
		||||
                parameters:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  additionalProperties:
 | 
			
		||||
                    type: string
 | 
			
		||||
            replicaLoadBalancer:  # deprecated
 | 
			
		||||
              type: boolean
 | 
			
		||||
            resources:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - requests
 | 
			
		||||
                - limits
 | 
			
		||||
              properties:
 | 
			
		||||
                limits:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  required:
 | 
			
		||||
                    - cpu
 | 
			
		||||
                    - memory
 | 
			
		||||
                  properties:
 | 
			
		||||
                    cpu:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # Decimal natural followed by m, or decimal natural followed by
 | 
			
		||||
                      # dot followed by up to three decimal digits.
 | 
			
		||||
                      #
 | 
			
		||||
                      # This is because the Kubernetes CPU resource has millis as the
 | 
			
		||||
                      # maximum precision.  The actual values are checked in code
 | 
			
		||||
                      # because the regular expression would be huge and horrible and
 | 
			
		||||
                      # not very helpful in validation error messages; this one checks
 | 
			
		||||
                      # only the format of the given number.
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
 | 
			
		||||
                      pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be lower
 | 
			
		||||
                      # than the corresponding request.
 | 
			
		||||
                    memory:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # You can express memory as a plain integer or as a fixed-point
 | 
			
		||||
                      # integer using one of these suffixes: E, P, T, G, M, k. You can
 | 
			
		||||
                      # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
 | 
			
		||||
                      pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be lower
 | 
			
		||||
                      # than the corresponding request.
 | 
			
		||||
                requests:
 | 
			
		||||
                  type: object
 | 
			
		||||
                  required:
 | 
			
		||||
                    - cpu
 | 
			
		||||
                    - memory
 | 
			
		||||
                  properties:
 | 
			
		||||
                    cpu:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # Decimal natural followed by m, or decimal natural followed by
 | 
			
		||||
                      # dot followed by up to three decimal digits.
 | 
			
		||||
                      #
 | 
			
		||||
                      # This is because the Kubernetes CPU resource has millis as the
 | 
			
		||||
                      # maximum precision.  The actual values are checked in code
 | 
			
		||||
                      # because the regular expression would be huge and horrible and
 | 
			
		||||
                      # not very helpful in validation error messages; this one checks
 | 
			
		||||
                      # only the format of the given number.
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-cpu
 | 
			
		||||
                      pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be higher
 | 
			
		||||
                      # than the corresponding limit.
 | 
			
		||||
                    memory:
 | 
			
		||||
                      type: string
 | 
			
		||||
                      # You can express memory as a plain integer or as a fixed-point
 | 
			
		||||
                      # integer using one of these suffixes: E, P, T, G, M, k. You can
 | 
			
		||||
                      # also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki
 | 
			
		||||
                      #
 | 
			
		||||
                      # https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/#meaning-of-memory
 | 
			
		||||
                      pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                      # Note: the value specified here must not be zero or be higher
 | 
			
		||||
                      # than the corresponding limit.
 | 
			
		||||
            sidecars:
 | 
			
		||||
              type: array
 | 
			
		||||
              nullable: true
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                additionalProperties: true
 | 
			
		||||
            spiloFSGroup:
 | 
			
		||||
              type: integer
 | 
			
		||||
            standby:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - s3_wal_path
 | 
			
		||||
              properties:
 | 
			
		||||
                s3_wal_path:
 | 
			
		||||
                  type: string
 | 
			
		||||
            teamId:
 | 
			
		||||
              type: string
 | 
			
		||||
            tolerations:
 | 
			
		||||
              type: array
 | 
			
		||||
              items:
 | 
			
		||||
                type: object
 | 
			
		||||
                required:
 | 
			
		||||
                  - key
 | 
			
		||||
                  - operator
 | 
			
		||||
                  - effect
 | 
			
		||||
                properties:
 | 
			
		||||
                  key:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  operator:
 | 
			
		||||
                    type: string
 | 
			
		||||
                    enum:
 | 
			
		||||
                      - Equal
 | 
			
		||||
                      - Exists
 | 
			
		||||
                  value:
 | 
			
		||||
                    type: string
 | 
			
		||||
                  effect:
 | 
			
		||||
                    type: string
 | 
			
		||||
                    enum:
 | 
			
		||||
                      - NoExecute
 | 
			
		||||
                      - NoSchedule
 | 
			
		||||
                      - PreferNoSchedule
 | 
			
		||||
                  tolerationSeconds:
 | 
			
		||||
                    type: integer
 | 
			
		||||
            useLoadBalancer:  # deprecated
 | 
			
		||||
              type: boolean
 | 
			
		||||
            users:
 | 
			
		||||
              type: object
 | 
			
		||||
              additionalProperties:
 | 
			
		||||
                type: array
 | 
			
		||||
                nullable: true
 | 
			
		||||
                description: "Role flags specified here must not contradict each other"
 | 
			
		||||
                items:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  enum:
 | 
			
		||||
                  - bypassrls
 | 
			
		||||
                  - BYPASSRLS
 | 
			
		||||
                  - nobypassrls
 | 
			
		||||
                  - NOBYPASSRLS
 | 
			
		||||
                  - createdb
 | 
			
		||||
                  - CREATEDB
 | 
			
		||||
                  - nocreatedb
 | 
			
		||||
                  - NOCREATEDB
 | 
			
		||||
                  - createrole
 | 
			
		||||
                  - CREATEROLE
 | 
			
		||||
                  - nocreaterole
 | 
			
		||||
                  - NOCREATEROLE
 | 
			
		||||
                  - inherit
 | 
			
		||||
                  - INHERIT
 | 
			
		||||
                  - noinherit
 | 
			
		||||
                  - NOINHERIT
 | 
			
		||||
                  - login
 | 
			
		||||
                  - LOGIN
 | 
			
		||||
                  - nologin
 | 
			
		||||
                  - NOLOGIN
 | 
			
		||||
                  - replication
 | 
			
		||||
                  - REPLICATION
 | 
			
		||||
                  - noreplication
 | 
			
		||||
                  - NOREPLICATION
 | 
			
		||||
                  - superuser
 | 
			
		||||
                  - SUPERUSER
 | 
			
		||||
                  - nosuperuser
 | 
			
		||||
                  - NOSUPERUSER
 | 
			
		||||
            volume:
 | 
			
		||||
              type: object
 | 
			
		||||
              required:
 | 
			
		||||
                - size
 | 
			
		||||
              properties:
 | 
			
		||||
                size:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                  # Note: the value specified here must not be zero.
 | 
			
		||||
                storageClass:
 | 
			
		||||
                  type: string
 | 
			
		||||
                subPath:
 | 
			
		||||
                  type: string
 | 
			
		||||
        status:
 | 
			
		||||
          type: object
 | 
			
		||||
          additionalProperties:
 | 
			
		||||
            type: string
 | 
			
		||||
| 
						 | 
				
			
			@ -4,16 +4,12 @@ metadata:
 | 
			
		|||
  name: acid-standby-cluster
 | 
			
		||||
  namespace: default
 | 
			
		||||
spec:
 | 
			
		||||
  teamId: "ACID"
 | 
			
		||||
  teamId: "acid"
 | 
			
		||||
  volume:
 | 
			
		||||
    size: 1Gi
 | 
			
		||||
  numberOfInstances: 1
 | 
			
		||||
  postgresql:
 | 
			
		||||
    version: "10"
 | 
			
		||||
    version: "11"
 | 
			
		||||
# Make this a standby cluster and provide the s3 bucket path of source cluster for continuous streaming.
 | 
			
		||||
  standby:
 | 
			
		||||
    s3_wal_path: "s3://path/to/bucket/containing/wal/of/source/cluster/"
 | 
			
		||||
 | 
			
		||||
  maintenanceWindows:
 | 
			
		||||
  - 01:00-06:00  #UTC
 | 
			
		||||
  - Sat:00:00-04:00
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
package v1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
 | 
			
		||||
	acidzalando "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do"
 | 
			
		||||
	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -103,7 +103,936 @@ var OperatorConfigCRDResourceColumns = []apiextv1beta1.CustomResourceColumnDefin
 | 
			
		|||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomResourceColumnDefinition) *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
var min0 = 0.0
 | 
			
		||||
var min1 = 1.0
 | 
			
		||||
var minDisable = -1.0
 | 
			
		||||
 | 
			
		||||
// PostgresCRDResourceValidation to check applied manifest parameters
 | 
			
		||||
var PostgresCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
 | 
			
		||||
	OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
		Type:     "object",
 | 
			
		||||
		Required: []string{"kind", "apiVersion", "spec"},
 | 
			
		||||
		Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
			"kind": {
 | 
			
		||||
				Type: "string",
 | 
			
		||||
				Enum: []apiextv1beta1.JSON{
 | 
			
		||||
					{
 | 
			
		||||
						Raw: []byte(`"postgresql"`),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"apiVersion": {
 | 
			
		||||
				Type: "string",
 | 
			
		||||
				Enum: []apiextv1beta1.JSON{
 | 
			
		||||
					{
 | 
			
		||||
						Raw: []byte(`"acid.zalan.do/v1"`),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"spec": {
 | 
			
		||||
				Type:     "object",
 | 
			
		||||
				Required: []string{"numberOfInstances", "teamId", "postgresql"},
 | 
			
		||||
				Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
					"allowedSourceRanges": {
 | 
			
		||||
						Type:     "array",
 | 
			
		||||
						Nullable: true,
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\/(\\d|[1-2]\\d|3[0-2])$",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"clone": {
 | 
			
		||||
						Type:     "object",
 | 
			
		||||
						Required: []string{"cluster"},
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"cluster": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"s3_endpoint": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"s3_access_key_id": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"s3_secret_access_key": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"s3_force_path_style": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"s3_wal_path": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"timestamp": {
 | 
			
		||||
								Type:        "string",
 | 
			
		||||
								Description: "Date-time format that specifies a timezone as an offset relative to UTC e.g. 1996-12-19T16:39:57-08:00",
 | 
			
		||||
								Pattern:     "^([0-9]+)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])[Tt]([01][0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]|60)(\\.[0-9]+)?(([Zz])|([+-]([01][0-9]|2[0-3]):[0-5][0-9]))$",
 | 
			
		||||
							},
 | 
			
		||||
							"uid": {
 | 
			
		||||
								Type:   "string",
 | 
			
		||||
								Format: "uuid",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"databases": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type:        "string",
 | 
			
		||||
								Description: "User names specified here as database owners must be declared in the users key of the spec key",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"dockerImage": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"enableLogicalBackup": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"enableMasterLoadBalancer": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"enableReplicaLoadBalancer": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"enableShmVolume": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"init_containers": {
 | 
			
		||||
						Type:        "array",
 | 
			
		||||
						Description: "Deprecated",
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Allows: true,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"initContainers": {
 | 
			
		||||
						Type: "array",
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Allows: true,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"logicalBackupSchedule": {
 | 
			
		||||
						Type:    "string",
 | 
			
		||||
						Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",
 | 
			
		||||
					},
 | 
			
		||||
					"maintenanceWindows": {
 | 
			
		||||
						Type: "array",
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^\\ *((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))-((Mon|Tue|Wed|Thu|Fri|Sat|Sun):(2[0-3]|[01]?\\d):([0-5]?\\d)|(2[0-3]|[01]?\\d):([0-5]?\\d))\\ *$",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"numberOfInstances": {
 | 
			
		||||
						Type:    "integer",
 | 
			
		||||
						Minimum: &min0,
 | 
			
		||||
					},
 | 
			
		||||
					"patroni": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"initdb": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"pg_hba": {
 | 
			
		||||
								Type: "array",
 | 
			
		||||
								Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"slots": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "object",
 | 
			
		||||
										AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
											Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
												Type: "string",
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"ttl": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"loop_wait": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"retry_timeout": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"maximum_lag_on_failover": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"podAnnotations": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"pod_priority_class_name": {
 | 
			
		||||
						Type:        "string",
 | 
			
		||||
						Description: "Deprecated",
 | 
			
		||||
					},
 | 
			
		||||
					"podPriorityClassName": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"postgresql": {
 | 
			
		||||
						Type:     "object",
 | 
			
		||||
						Required: []string{"version"},
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"version": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
								Enum: []apiextv1beta1.JSON{
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"9.3"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"9.4"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"9.5"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"9.6"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"10"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"11"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"12"`),
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"parameters": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"replicaLoadBalancer": {
 | 
			
		||||
						Type:        "boolean",
 | 
			
		||||
						Description: "Deprecated",
 | 
			
		||||
					},
 | 
			
		||||
					"resources": {
 | 
			
		||||
						Type:     "object",
 | 
			
		||||
						Required: []string{"requests", "limits"},
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"limits": {
 | 
			
		||||
								Type:     "object",
 | 
			
		||||
								Required: []string{"cpu", "memory"},
 | 
			
		||||
								Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
									"cpu": {
 | 
			
		||||
										Type:        "string",
 | 
			
		||||
										Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
 | 
			
		||||
										Pattern:     "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
									},
 | 
			
		||||
									"memory": {
 | 
			
		||||
										Type:        "string",
 | 
			
		||||
										Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0",
 | 
			
		||||
										Pattern:     "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"requests": {
 | 
			
		||||
								Type:     "object",
 | 
			
		||||
								Required: []string{"cpu", "memory"},
 | 
			
		||||
								Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
									"cpu": {
 | 
			
		||||
										Type:        "string",
 | 
			
		||||
										Description: "Decimal natural followed by m, or decimal natural followed by dot followed by up to three decimal digits (precision used by Kubernetes). Must be greater than 0",
 | 
			
		||||
										Pattern:     "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
									},
 | 
			
		||||
									"memory": {
 | 
			
		||||
										Type:        "string",
 | 
			
		||||
										Description: "Plain integer or fixed-point integer using one of these suffixes: E, P, T, G, M, k (with or without a tailing i). Must be greater than 0",
 | 
			
		||||
										Pattern:     "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"sidecars": {
 | 
			
		||||
						Type: "array",
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Allows: true,
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"spiloFSGroup": {
 | 
			
		||||
						Type: "integer",
 | 
			
		||||
					},
 | 
			
		||||
					"standby": {
 | 
			
		||||
						Type:     "object",
 | 
			
		||||
						Required: []string{"s3_wal_path"},
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"s3_wal_path": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"teamId": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"tolerations": {
 | 
			
		||||
						Type: "array",
 | 
			
		||||
						Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type:     "object",
 | 
			
		||||
								Required: []string{"key", "operator", "effect"},
 | 
			
		||||
								Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
									"key": {
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
									"operator": {
 | 
			
		||||
										Type: "string",
 | 
			
		||||
										Enum: []apiextv1beta1.JSON{
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"Equal"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"Exists"`),
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
									"value": {
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
									"effect": {
 | 
			
		||||
										Type: "string",
 | 
			
		||||
										Enum: []apiextv1beta1.JSON{
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NoExecute"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NoSchedule"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"PreferNoSchedule"`),
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
									"tolerationSeconds": {
 | 
			
		||||
										Type: "integer",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"useLoadBalancer": {
 | 
			
		||||
						Type:        "boolean",
 | 
			
		||||
						Description: "Deprecated",
 | 
			
		||||
					},
 | 
			
		||||
					"users": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type:        "array",
 | 
			
		||||
								Description: "Role flags specified here must not contradict each other",
 | 
			
		||||
								Nullable:    true,
 | 
			
		||||
								Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
										Enum: []apiextv1beta1.JSON{
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"bypassrls"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"BYPASSRLS"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"nobypassrls"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOBYPASSRLS"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"createdb"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"CREATEDB"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"nocreatedb"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOCREATEDB"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"createrole"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"CREATEROLE"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"nocreaterole"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOCREATEROLE"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"inherit"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"INHERIT"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"noinherit"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOINHERIT"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"login"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"LOGIN"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"nologin"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOLOGIN"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"replication"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"REPLICATION"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"noreplication"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOREPLICATION"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"superuser"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"SUPERUSER"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"nosuperuser"`),
 | 
			
		||||
											},
 | 
			
		||||
											{
 | 
			
		||||
												Raw: []byte(`"NOSUPERUSER"`),
 | 
			
		||||
											},
 | 
			
		||||
										},
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"volume": {
 | 
			
		||||
						Type:     "object",
 | 
			
		||||
						Required: []string{"size"},
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"size": {
 | 
			
		||||
								Type:        "string",
 | 
			
		||||
								Description: "Value must not be zero",
 | 
			
		||||
								Pattern:     "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"storageClass": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"subPath": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"status": {
 | 
			
		||||
				Type: "object",
 | 
			
		||||
				AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
					Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OperatorConfigCRDResourceValidation to check applied manifest parameters
 | 
			
		||||
var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation{
 | 
			
		||||
	OpenAPIV3Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
		Type:     "object",
 | 
			
		||||
		Required: []string{"kind", "apiVersion", "configuration"},
 | 
			
		||||
		Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
			"kind": {
 | 
			
		||||
				Type: "string",
 | 
			
		||||
				Enum: []apiextv1beta1.JSON{
 | 
			
		||||
					{
 | 
			
		||||
						Raw: []byte(`"OperatorConfiguration"`),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"apiVersion": {
 | 
			
		||||
				Type: "string",
 | 
			
		||||
				Enum: []apiextv1beta1.JSON{
 | 
			
		||||
					{
 | 
			
		||||
						Raw: []byte(`"acid.zalan.do/v1"`),
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"configuration": {
 | 
			
		||||
				Type: "object",
 | 
			
		||||
				Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
					"docker_image": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"enable_crd_validation": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"enable_shm_volume": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"etcd_host": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"max_instances": {
 | 
			
		||||
						Type:        "integer",
 | 
			
		||||
						Description: "-1 = disabled",
 | 
			
		||||
						Minimum:     &minDisable,
 | 
			
		||||
					},
 | 
			
		||||
					"min_instances": {
 | 
			
		||||
						Type:        "integer",
 | 
			
		||||
						Description: "-1 = disabled",
 | 
			
		||||
						Minimum:     &minDisable,
 | 
			
		||||
					},
 | 
			
		||||
					"resync_period": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"repair_period": {
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
					"set_memory_request_to_limit": {
 | 
			
		||||
						Type: "boolean",
 | 
			
		||||
					},
 | 
			
		||||
					"sidecar_docker_images": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
							Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"workers": {
 | 
			
		||||
						Type:    "integer",
 | 
			
		||||
						Minimum: &min1,
 | 
			
		||||
					},
 | 
			
		||||
					"users": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"replication_username": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"super_username": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"kubernetes": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"cluster_domain": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"cluster_labels": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"cluster_name_label": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"custom_pod_annotations": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"enable_init_containers": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_pod_antiaffinity": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_pod_disruption_budget": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_sidecars": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"infrastructure_roles_secret_name": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"inherited_labels": {
 | 
			
		||||
								Type: "array",
 | 
			
		||||
								Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"master_pod_move_timeout": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"node_readiness_label": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"oauth_token_secret_name": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pdb_name_format": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_antiaffinity_topology_key": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_environment_configmap": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_management_policy": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
								Enum: []apiextv1beta1.JSON{
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"ordered_ready"`),
 | 
			
		||||
									},
 | 
			
		||||
									{
 | 
			
		||||
										Raw: []byte(`"parallel"`),
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"pod_priority_class_name": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_role_label": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_service_account_definition": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_service_account_name": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_service_account_role_binding_definition": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_terminate_grace_period": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"secret_name_template": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"spilo_fsgroup": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"spilo_privileged": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"toleration": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"watched_namespace": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"postgres_pod_resources": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"default_cpu_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"default_cpu_request": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"default_memory_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"default_memory_request": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"timeouts": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"pod_label_wait_timeout": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pod_deletion_wait_timeout": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"ready_wait_interval": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"ready_wait_timeout": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"resource_check_interval": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"resource_check_timeout": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"load_balancer": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"custom_service_annotations": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"db_hosted_zone": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_master_load_balancer": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_replica_load_balancer": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"master_dns_name_format": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"replica_dns_name_format": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"aws_or_gcp": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"additional_secret_mount": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"additional_secret_mount_path": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"aws_region": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"kube_iam_role": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"log_s3_bucket": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"wal_s3_bucket": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"logical_backup": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"logical_backup_docker_image": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_s3_access_key_id": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_s3_bucket": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_s3_endpoint": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_s3_secret_access_key": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_s3_sse": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"logical_backup_schedule": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+|\\*)(/\\d+)?(\\s+(\\d+|\\*)(/\\d+)?){4}$",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"debug": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"debug_logging": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_database_access": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"teams_api": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"enable_admin_role_for_users": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_team_superuser": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"enable_teams_api": {
 | 
			
		||||
								Type: "boolean",
 | 
			
		||||
							},
 | 
			
		||||
							"pam_configuration": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"pam_role_name": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"postgres_superuser_teams": {
 | 
			
		||||
								Type: "array",
 | 
			
		||||
								Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"protected_role_names": {
 | 
			
		||||
								Type: "array",
 | 
			
		||||
								Items: &apiextv1beta1.JSONSchemaPropsOrArray{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"team_admin_role": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"team_api_role_configuration": {
 | 
			
		||||
								Type: "object",
 | 
			
		||||
								AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
									Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
										Type: "string",
 | 
			
		||||
									},
 | 
			
		||||
								},
 | 
			
		||||
							},
 | 
			
		||||
							"teams_api_url": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"logging_rest_api": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"api_port": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"cluster_history_entries": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
							"ring_log_lines": {
 | 
			
		||||
								Type: "integer",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"scalyr": {
 | 
			
		||||
						Type: "object",
 | 
			
		||||
						Properties: map[string]apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
							"scalyr_api_key": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_cpu_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_cpu_request": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_image": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_memory_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_memory_request": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"scalyr_server_url": {
 | 
			
		||||
								Type: "string",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
			"status": {
 | 
			
		||||
				Type: "object",
 | 
			
		||||
				AdditionalProperties: &apiextv1beta1.JSONSchemaPropsOrBool{
 | 
			
		||||
					Schema: &apiextv1beta1.JSONSchemaProps{
 | 
			
		||||
						Type: "string",
 | 
			
		||||
					},
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomResourceColumnDefinition, validation apiextv1beta1.CustomResourceValidation) *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
	return &apiextv1beta1.CustomResourceDefinition{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name: name,
 | 
			
		||||
| 
						 | 
				
			
			@ -121,24 +1050,39 @@ func buildCRD(name, kind, plural, short string, columns []apiextv1beta1.CustomRe
 | 
			
		|||
				Status: &apiextv1beta1.CustomResourceSubresourceStatus{},
 | 
			
		||||
			},
 | 
			
		||||
			AdditionalPrinterColumns: columns,
 | 
			
		||||
			Validation:               &validation,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostgresCRD returns CustomResourceDefinition built from PostgresCRDResource
 | 
			
		||||
func PostgresCRD() *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
func PostgresCRD(enableValidation *bool) *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
	postgresCRDvalidation := apiextv1beta1.CustomResourceValidation{}
 | 
			
		||||
 | 
			
		||||
	if enableValidation != nil && *enableValidation {
 | 
			
		||||
		postgresCRDvalidation = PostgresCRDResourceValidation
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buildCRD(PostgresCRDResouceName,
 | 
			
		||||
		PostgresCRDResourceKind,
 | 
			
		||||
		PostgresCRDResourcePlural,
 | 
			
		||||
		PostgresCRDResourceShort,
 | 
			
		||||
		PostgresCRDResourceColumns)
 | 
			
		||||
		PostgresCRDResourceColumns,
 | 
			
		||||
		postgresCRDvalidation)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfigurationCRD returns CustomResourceDefinition built from OperatorConfigCRDResource
 | 
			
		||||
func ConfigurationCRD() *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
func ConfigurationCRD(enableValidation *bool) *apiextv1beta1.CustomResourceDefinition {
 | 
			
		||||
	opconfigCRDvalidation := apiextv1beta1.CustomResourceValidation{}
 | 
			
		||||
 | 
			
		||||
	if enableValidation != nil && *enableValidation {
 | 
			
		||||
		opconfigCRDvalidation = OperatorConfigCRDResourceValidation
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return buildCRD(OperatorConfigCRDResourceName,
 | 
			
		||||
		OperatorConfigCRDResouceKind,
 | 
			
		||||
		OperatorConfigCRDResourcePlural,
 | 
			
		||||
		OperatorConfigCRDResourceShort,
 | 
			
		||||
		OperatorConfigCRDResourceColumns)
 | 
			
		||||
		OperatorConfigCRDResourceColumns,
 | 
			
		||||
		opconfigCRDvalidation)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -45,11 +45,13 @@ type KubernetesMetaConfiguration struct {
 | 
			
		|||
	PodServiceAccountDefinition            string                `json:"pod_service_account_definition,omitempty"`
 | 
			
		||||
	PodServiceAccountRoleBindingDefinition string                `json:"pod_service_account_role_binding_definition,omitempty"`
 | 
			
		||||
	PodTerminateGracePeriod                Duration              `json:"pod_terminate_grace_period,omitempty"`
 | 
			
		||||
	SpiloPrivileged                        bool                  `json:"spilo_privileged,omitemty"`
 | 
			
		||||
	SpiloPrivileged                        bool                  `json:"spilo_privileged,omitempty"`
 | 
			
		||||
	SpiloFSGroup                           *int64                `json:"spilo_fsgroup,omitempty"`
 | 
			
		||||
	WatchedNamespace                       string                `json:"watched_namespace,omitempty"`
 | 
			
		||||
	PDBNameFormat                          config.StringTemplate `json:"pdb_name_format,omitempty"`
 | 
			
		||||
	EnablePodDisruptionBudget              *bool                 `json:"enable_pod_disruption_budget,omitempty"`
 | 
			
		||||
	EnableInitContainers                   *bool                 `json:"enable_init_containers,omitempty"`
 | 
			
		||||
	EnableSidecars                         *bool                 `json:"enable_sidecars,omitempty"`
 | 
			
		||||
	SecretNameTemplate                     config.StringTemplate `json:"secret_name_template,omitempty"`
 | 
			
		||||
	ClusterDomain                          string                `json:"cluster_domain"`
 | 
			
		||||
	OAuthTokenSecretName                   spec.NamespacedName   `json:"oauth_token_secret_name,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -59,6 +61,7 @@ type KubernetesMetaConfiguration struct {
 | 
			
		|||
	InheritedLabels                        []string              `json:"inherited_labels,omitempty"`
 | 
			
		||||
	ClusterNameLabel                       string                `json:"cluster_name_label,omitempty"`
 | 
			
		||||
	NodeReadinessLabel                     map[string]string     `json:"node_readiness_label,omitempty"`
 | 
			
		||||
	CustomPodAnnotations                   map[string]string     `json:"custom_pod_annotations,omitempty"`
 | 
			
		||||
	// TODO: use a proper toleration structure?
 | 
			
		||||
	PodToleration map[string]string `json:"toleration,omitempty"`
 | 
			
		||||
	// TODO: use namespacedname
 | 
			
		||||
| 
						 | 
				
			
			@ -115,7 +118,7 @@ type OperatorDebugConfiguration struct {
 | 
			
		|||
	EnableDBAccess bool `json:"enable_database_access,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TeamsAPIConfiguration defines the configration of TeamsAPI
 | 
			
		||||
// TeamsAPIConfiguration defines the configuration of TeamsAPI
 | 
			
		||||
type TeamsAPIConfiguration struct {
 | 
			
		||||
	EnableTeamsAPI           bool              `json:"enable_teams_api,omitempty"`
 | 
			
		||||
	TeamsAPIUrl              string            `json:"teams_api_url,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -147,8 +150,20 @@ type ScalyrConfiguration struct {
 | 
			
		|||
	ScalyrMemoryLimit   string `json:"scalyr_memory_limit,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OperatorLogicalBackupConfiguration defines configuration for logical backup
 | 
			
		||||
type OperatorLogicalBackupConfiguration struct {
 | 
			
		||||
	Schedule          string `json:"logical_backup_schedule,omitempty"`
 | 
			
		||||
	DockerImage       string `json:"logical_backup_docker_image,omitempty"`
 | 
			
		||||
	S3Bucket          string `json:"logical_backup_s3_bucket,omitempty"`
 | 
			
		||||
	S3Endpoint        string `json:"logical_backup_s3_endpoint,omitempty"`
 | 
			
		||||
	S3AccessKeyID     string `json:"logical_backup_s3_access_key_id,omitempty"`
 | 
			
		||||
	S3SecretAccessKey string `json:"logical_backup_s3_secret_access_key,omitempty"`
 | 
			
		||||
	S3SSE             string `json:"logical_backup_s3_sse,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OperatorConfigurationData defines the operation config
 | 
			
		||||
type OperatorConfigurationData struct {
 | 
			
		||||
	EnableCRDValidation        *bool                              `json:"enable_crd_validation,omitempty"`
 | 
			
		||||
	EtcdHost                   string                             `json:"etcd_host,omitempty"`
 | 
			
		||||
	DockerImage                string                             `json:"docker_image,omitempty"`
 | 
			
		||||
	Workers                    uint32                             `json:"workers,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			@ -172,19 +187,5 @@ type OperatorConfigurationData struct {
 | 
			
		|||
	LogicalBackup              OperatorLogicalBackupConfiguration `json:"logical_backup"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OperatorConfigurationUsers defines configration for super user
 | 
			
		||||
type OperatorConfigurationUsers struct {
 | 
			
		||||
	SuperUserName            string            `json:"superuser_name,omitempty"`
 | 
			
		||||
	Replication              string            `json:"replication_user_name,omitempty"`
 | 
			
		||||
	ProtectedRoles           []string          `json:"protected_roles,omitempty"`
 | 
			
		||||
	TeamAPIRoleConfiguration map[string]string `json:"team_api_role_configuration,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//Duration shortens this frequently used name
 | 
			
		||||
type Duration time.Duration
 | 
			
		||||
 | 
			
		||||
type OperatorLogicalBackupConfiguration struct {
 | 
			
		||||
	Schedule    string `json:"logical_backup_schedule,omitempty"`
 | 
			
		||||
	DockerImage string `json:"logical_backup_docker_image,omitempty"`
 | 
			
		||||
	S3Bucket    string `json:"logical_backup_s3_bucket,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,6 +59,7 @@ type PostgresSpec struct {
 | 
			
		|||
	EnableLogicalBackup   bool                 `json:"enableLogicalBackup,omitempty"`
 | 
			
		||||
	LogicalBackupSchedule string               `json:"logicalBackupSchedule,omitempty"`
 | 
			
		||||
	StandbyCluster        *StandbyDescription  `json:"standby"`
 | 
			
		||||
	PodAnnotations        map[string]string    `json:"podAnnotations"`
 | 
			
		||||
 | 
			
		||||
	// deprecated json tags
 | 
			
		||||
	InitContainersOld       []v1.Container `json:"init_containers,omitempty"`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,7 +180,7 @@ var unmarshalCluster = []struct {
 | 
			
		|||
	    "name": "acid-testcluster1"
 | 
			
		||||
	  },
 | 
			
		||||
	  "spec": {
 | 
			
		||||
	    "teamId": "ACID",
 | 
			
		||||
	    "teamId": "acid",
 | 
			
		||||
		"pod_priority_class_name": "spilo-pod-priority",
 | 
			
		||||
	    "volume": {
 | 
			
		||||
	      "size": "5Gi",
 | 
			
		||||
| 
						 | 
				
			
			@ -290,7 +290,7 @@ var unmarshalCluster = []struct {
 | 
			
		|||
					ResourceLimits:   ResourceDescription{CPU: "300m", Memory: "3000Mi"},
 | 
			
		||||
				},
 | 
			
		||||
 | 
			
		||||
				TeamID:              "ACID",
 | 
			
		||||
				TeamID:              "acid",
 | 
			
		||||
				AllowedSourceRanges: []string{"127.0.0.1/32"},
 | 
			
		||||
				NumberOfInstances:   2,
 | 
			
		||||
				Users:               map[string]UserFlags{"zalando": {"superuser", "createdb"}},
 | 
			
		||||
| 
						 | 
				
			
			@ -319,7 +319,7 @@ var unmarshalCluster = []struct {
 | 
			
		|||
			},
 | 
			
		||||
			Error: "",
 | 
			
		||||
		},
 | 
			
		||||
		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host    all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"ACID","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`),
 | 
			
		||||
		marshal: []byte(`{"kind":"Postgresql","apiVersion":"acid.zalan.do/v1","metadata":{"name":"acid-testcluster1","creationTimestamp":null},"spec":{"postgresql":{"version":"9.6","parameters":{"log_statement":"all","max_connections":"10","shared_buffers":"32MB"}},"pod_priority_class_name":"spilo-pod-priority","volume":{"size":"5Gi","storageClass":"SSD", "subPath": "subdir"},"enableShmVolume":false,"patroni":{"initdb":{"data-checksums":"true","encoding":"UTF8","locale":"en_US.UTF-8"},"pg_hba":["hostssl all all 0.0.0.0/0 md5","host    all all 0.0.0.0/0 md5"],"ttl":30,"loop_wait":10,"retry_timeout":10,"maximum_lag_on_failover":33554432,"slots":{"permanent_logical_1":{"database":"foo","plugin":"pgoutput","type":"logical"}}},"resources":{"requests":{"cpu":"10m","memory":"50Mi"},"limits":{"cpu":"300m","memory":"3000Mi"}},"teamId":"acid","allowedSourceRanges":["127.0.0.1/32"],"numberOfInstances":2,"users":{"zalando":["superuser","createdb"]},"maintenanceWindows":["Mon:01:00-06:00","Sat:00:00-04:00","05:00-05:15"],"clone":{"cluster":"acid-batman"}},"status":{"PostgresClusterStatus":""}}`),
 | 
			
		||||
		err:     nil},
 | 
			
		||||
	// example with teamId set in input
 | 
			
		||||
	{
 | 
			
		||||
| 
						 | 
				
			
			@ -437,6 +437,16 @@ var postgresqlList = []struct {
 | 
			
		|||
		PostgresqlList{},
 | 
			
		||||
		errors.New("unexpected end of JSON input")}}
 | 
			
		||||
 | 
			
		||||
var annotations = []struct {
 | 
			
		||||
	in          []byte
 | 
			
		||||
	annotations map[string]string
 | 
			
		||||
	err         error
 | 
			
		||||
}{{
 | 
			
		||||
	in:          []byte(`{"kind": "Postgresql","apiVersion": "acid.zalan.do/v1","metadata": {"name": "acid-testcluster1"}, "spec": {"podAnnotations": {"foo": "bar"},"teamId": "acid", "clone": {"cluster": "team-batman"}}}`),
 | 
			
		||||
	annotations: map[string]string{"foo": "bar"},
 | 
			
		||||
	err:         nil},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func mustParseTime(s string) metav1.Time {
 | 
			
		||||
	v, err := time.Parse("15:04", s)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -482,6 +492,25 @@ func TestWeekdayTime(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestClusterAnnotations(t *testing.T) {
 | 
			
		||||
	for _, tt := range annotations {
 | 
			
		||||
		var cluster Postgresql
 | 
			
		||||
		err := cluster.UnmarshalJSON(tt.in)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if tt.err == nil || err.Error() != tt.err.Error() {
 | 
			
		||||
				t.Errorf("Unable to marshal cluster with annotations: expected %v got %v", tt.err, err)
 | 
			
		||||
			}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		for k, v := range cluster.Spec.PodAnnotations {
 | 
			
		||||
			found, expected := v, tt.annotations[k]
 | 
			
		||||
			if found != expected {
 | 
			
		||||
				t.Errorf("Didn't find correct value for key %v in for podAnnotations:  Expected %v found %v", k, expected, found)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestClusterName(t *testing.T) {
 | 
			
		||||
	for _, tt := range clusterNames {
 | 
			
		||||
		name, err := extractClusterName(tt.in, tt.inTeam)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -81,6 +81,16 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
 | 
			
		|||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.EnableInitContainers != nil {
 | 
			
		||||
		in, out := &in.EnableInitContainers, &out.EnableInitContainers
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.EnableSidecars != nil {
 | 
			
		||||
		in, out := &in.EnableSidecars, &out.EnableSidecars
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	out.OAuthTokenSecretName = in.OAuthTokenSecretName
 | 
			
		||||
	out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName
 | 
			
		||||
	if in.ClusterLabels != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +112,13 @@ func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfigura
 | 
			
		|||
			(*out)[key] = val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if in.CustomPodAnnotations != nil {
 | 
			
		||||
		in, out := &in.CustomPodAnnotations, &out.CustomPodAnnotations
 | 
			
		||||
		*out = make(map[string]string, len(*in))
 | 
			
		||||
		for key, val := range *in {
 | 
			
		||||
			(*out)[key] = val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if in.PodToleration != nil {
 | 
			
		||||
		in, out := &in.PodToleration, &out.PodToleration
 | 
			
		||||
		*out = make(map[string]string, len(*in))
 | 
			
		||||
| 
						 | 
				
			
			@ -209,6 +226,11 @@ func (in *OperatorConfiguration) DeepCopyObject() runtime.Object {
 | 
			
		|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *OperatorConfigurationData) DeepCopyInto(out *OperatorConfigurationData) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.EnableCRDValidation != nil {
 | 
			
		||||
		in, out := &in.EnableCRDValidation, &out.EnableCRDValidation
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.ShmVolume != nil {
 | 
			
		||||
		in, out := &in.ShmVolume, &out.ShmVolume
 | 
			
		||||
		*out = new(bool)
 | 
			
		||||
| 
						 | 
				
			
			@ -249,7 +271,7 @@ func (in *OperatorConfigurationData) DeepCopy() *OperatorConfigurationData {
 | 
			
		|||
func (in *OperatorConfigurationList) DeepCopyInto(out *OperatorConfigurationList) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	out.TypeMeta = in.TypeMeta
 | 
			
		||||
	out.ListMeta = in.ListMeta
 | 
			
		||||
	in.ListMeta.DeepCopyInto(&out.ListMeta)
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		in, out := &in.Items, &out.Items
 | 
			
		||||
		*out = make([]OperatorConfiguration, len(*in))
 | 
			
		||||
| 
						 | 
				
			
			@ -278,34 +300,6 @@ func (in *OperatorConfigurationList) DeepCopyObject() runtime.Object {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *OperatorConfigurationUsers) DeepCopyInto(out *OperatorConfigurationUsers) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	if in.ProtectedRoles != nil {
 | 
			
		||||
		in, out := &in.ProtectedRoles, &out.ProtectedRoles
 | 
			
		||||
		*out = make([]string, len(*in))
 | 
			
		||||
		copy(*out, *in)
 | 
			
		||||
	}
 | 
			
		||||
	if in.TeamAPIRoleConfiguration != nil {
 | 
			
		||||
		in, out := &in.TeamAPIRoleConfiguration, &out.TeamAPIRoleConfiguration
 | 
			
		||||
		*out = make(map[string]string, len(*in))
 | 
			
		||||
		for key, val := range *in {
 | 
			
		||||
			(*out)[key] = val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OperatorConfigurationUsers.
 | 
			
		||||
func (in *OperatorConfigurationUsers) DeepCopy() *OperatorConfigurationUsers {
 | 
			
		||||
	if in == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	out := new(OperatorConfigurationUsers)
 | 
			
		||||
	in.DeepCopyInto(out)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 | 
			
		||||
func (in *OperatorDebugConfiguration) DeepCopyInto(out *OperatorDebugConfiguration) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
| 
						 | 
				
			
			@ -513,6 +507,13 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
 | 
			
		|||
		*out = new(StandbyDescription)
 | 
			
		||||
		**out = **in
 | 
			
		||||
	}
 | 
			
		||||
	if in.PodAnnotations != nil {
 | 
			
		||||
		in, out := &in.PodAnnotations, &out.PodAnnotations
 | 
			
		||||
		*out = make(map[string]string, len(*in))
 | 
			
		||||
		for key, val := range *in {
 | 
			
		||||
			(*out)[key] = val
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if in.InitContainersOld != nil {
 | 
			
		||||
		in, out := &in.InitContainersOld, &out.InitContainersOld
 | 
			
		||||
		*out = make([]corev1.Container, len(*in))
 | 
			
		||||
| 
						 | 
				
			
			@ -597,7 +598,7 @@ func (in *Postgresql) DeepCopyObject() runtime.Object {
 | 
			
		|||
func (in *PostgresqlList) DeepCopyInto(out *PostgresqlList) {
 | 
			
		||||
	*out = *in
 | 
			
		||||
	out.TypeMeta = in.TypeMeta
 | 
			
		||||
	out.ListMeta = in.ListMeta
 | 
			
		||||
	in.ListMeta.DeepCopyInto(&out.ListMeta)
 | 
			
		||||
	if in.Items != nil {
 | 
			
		||||
		in, out := &in.Items, &out.Items
 | 
			
		||||
		*out = make([]Postgresql, len(*in))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -12,7 +12,7 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
| 
						 | 
				
			
			@ -52,7 +52,7 @@ type kubeResources struct {
 | 
			
		|||
	Services            map[PostgresRole]*v1.Service
 | 
			
		||||
	Endpoints           map[PostgresRole]*v1.Endpoints
 | 
			
		||||
	Secrets             map[types.UID]*v1.Secret
 | 
			
		||||
	Statefulset         *v1beta1.StatefulSet
 | 
			
		||||
	Statefulset         *appsv1.StatefulSet
 | 
			
		||||
	PodDisruptionBudget *policybeta1.PodDisruptionBudget
 | 
			
		||||
	//Pods are treated separately
 | 
			
		||||
	//PVCs are treated separately
 | 
			
		||||
| 
						 | 
				
			
			@ -164,11 +164,13 @@ func (c *Cluster) setStatus(status string) {
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// we cannot do a full scale update here without fetching the previous manifest (as the resourceVersion may differ),
 | 
			
		||||
	// however, we could do patch without it. In the future, once /status subresource is there (starting Kubernets 1.11)
 | 
			
		||||
	// however, we could do patch without it. In the future, once /status subresource is there (starting Kubernetes 1.11)
 | 
			
		||||
	// we should take advantage of it.
 | 
			
		||||
	newspec, err := c.KubeClient.AcidV1ClientSet.AcidV1().Postgresqls(c.clusterNamespace()).Patch(c.Name, types.MergePatchType, patch, "status")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		c.logger.Errorf("could not update status: %v", err)
 | 
			
		||||
		// return as newspec is empty, see PR654
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	// update the spec, maintaining the new resourceVersion.
 | 
			
		||||
	c.setSpec(newspec)
 | 
			
		||||
| 
						 | 
				
			
			@ -212,7 +214,7 @@ func (c *Cluster) Create() error {
 | 
			
		|||
 | 
			
		||||
		service *v1.Service
 | 
			
		||||
		ep      *v1.Endpoints
 | 
			
		||||
		ss      *v1beta1.StatefulSet
 | 
			
		||||
		ss      *appsv1.StatefulSet
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -225,6 +227,10 @@ func (c *Cluster) Create() error {
 | 
			
		|||
 | 
			
		||||
	c.setStatus(acidv1.ClusterStatusCreating)
 | 
			
		||||
 | 
			
		||||
	if err = c.validateResources(&c.Spec); err != nil {
 | 
			
		||||
		return fmt.Errorf("insufficient resource limits specified: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, role := range []PostgresRole{Master, Replica} {
 | 
			
		||||
 | 
			
		||||
		if c.Endpoints[role] != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -313,7 +319,7 @@ func (c *Cluster) Create() error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) compareStatefulSetWith(statefulSet *v1beta1.StatefulSet) *compareStatefulsetResult {
 | 
			
		||||
func (c *Cluster) compareStatefulSetWith(statefulSet *appsv1.StatefulSet) *compareStatefulsetResult {
 | 
			
		||||
	reasons := make([]string, 0)
 | 
			
		||||
	var match, needsRollUpdate, needsReplace bool
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -489,6 +495,44 @@ func compareResourcesAssumeFirstNotNil(a *v1.ResourceRequirements, b *v1.Resourc
 | 
			
		|||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) validateResources(spec *acidv1.PostgresSpec) error {
 | 
			
		||||
 | 
			
		||||
	// setting limits too low can cause unnecessary evictions / OOM kills
 | 
			
		||||
	const (
 | 
			
		||||
		cpuMinLimit    = "256m"
 | 
			
		||||
		memoryMinLimit = "256Mi"
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		isSmaller bool
 | 
			
		||||
		err       error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	cpuLimit := spec.Resources.ResourceLimits.CPU
 | 
			
		||||
	if cpuLimit != "" {
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(cpuLimit, cpuMinLimit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("error validating CPU limit: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if isSmaller {
 | 
			
		||||
			return fmt.Errorf("defined CPU limit %s is below required minimum %s to properly run postgresql resource", cpuLimit, cpuMinLimit)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memoryLimit := spec.Resources.ResourceLimits.Memory
 | 
			
		||||
	if memoryLimit != "" {
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(memoryLimit, memoryMinLimit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("error validating memory limit: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if isSmaller {
 | 
			
		||||
			return fmt.Errorf("defined memory limit %s is below required minimum %s to properly run postgresql resource", memoryLimit, memoryMinLimit)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Update changes Kubernetes objects according to the new specification. Unlike the sync case, the missing object
 | 
			
		||||
// (i.e. service) is treated as an error
 | 
			
		||||
// logical backup cron jobs are an exception: a user-initiated Update can enable a logical backup job
 | 
			
		||||
| 
						 | 
				
			
			@ -499,6 +543,7 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
			
		|||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	oldStatus := c.Status
 | 
			
		||||
	c.setStatus(acidv1.ClusterStatusUpdating)
 | 
			
		||||
	c.setSpec(newSpec)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -510,6 +555,22 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
			
		|||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if err := c.validateResources(&newSpec.Spec); err != nil {
 | 
			
		||||
		err = fmt.Errorf("insufficient resource limits specified: %v", err)
 | 
			
		||||
 | 
			
		||||
		// cancel update only when (already too low) pod resources were edited
 | 
			
		||||
		// if cluster was successfully running before the update, continue but log a warning
 | 
			
		||||
		isCPULimitSmaller, err2 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.CPU, oldSpec.Spec.Resources.ResourceLimits.CPU)
 | 
			
		||||
		isMemoryLimitSmaller, err3 := util.IsSmallerQuantity(newSpec.Spec.Resources.ResourceLimits.Memory, oldSpec.Spec.Resources.ResourceLimits.Memory)
 | 
			
		||||
 | 
			
		||||
		if oldStatus.Running() && !isCPULimitSmaller && !isMemoryLimitSmaller && err2 == nil && err3 == nil {
 | 
			
		||||
			c.logger.Warning(err)
 | 
			
		||||
		} else {
 | 
			
		||||
			updateFailed = true
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if oldSpec.Spec.PgVersion != newSpec.Spec.PgVersion { // PG versions comparison
 | 
			
		||||
		c.logger.Warningf("postgresql version change(%q -> %q) has no effect", oldSpec.Spec.PgVersion, newSpec.Spec.PgVersion)
 | 
			
		||||
		//we need that hack to generate statefulset with the old version
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import (
 | 
			
		|||
	"github.com/zalando/postgres-operator/pkg/util/config"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/teams"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
| 
						 | 
				
			
			@ -328,3 +328,57 @@ func TestShouldDeleteSecret(t *testing.T) {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestPodAnnotations(t *testing.T) {
 | 
			
		||||
	testName := "TestPodAnnotations"
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		subTest  string
 | 
			
		||||
		operator map[string]string
 | 
			
		||||
		database map[string]string
 | 
			
		||||
		merged   map[string]string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			subTest:  "No Annotations",
 | 
			
		||||
			operator: make(map[string]string),
 | 
			
		||||
			database: make(map[string]string),
 | 
			
		||||
			merged:   make(map[string]string),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			subTest:  "Operator Config Annotations",
 | 
			
		||||
			operator: map[string]string{"foo": "bar"},
 | 
			
		||||
			database: make(map[string]string),
 | 
			
		||||
			merged:   map[string]string{"foo": "bar"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			subTest:  "Database Config Annotations",
 | 
			
		||||
			operator: make(map[string]string),
 | 
			
		||||
			database: map[string]string{"foo": "bar"},
 | 
			
		||||
			merged:   map[string]string{"foo": "bar"},
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			subTest:  "Database Config overrides Operator Config Annotations",
 | 
			
		||||
			operator: map[string]string{"foo": "bar", "global": "foo"},
 | 
			
		||||
			database: map[string]string{"foo": "baz", "local": "foo"},
 | 
			
		||||
			merged:   map[string]string{"foo": "baz", "global": "foo", "local": "foo"},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		cl.OpConfig.CustomPodAnnotations = tt.operator
 | 
			
		||||
		cl.Postgresql.Spec.PodAnnotations = tt.database
 | 
			
		||||
 | 
			
		||||
		annotations := cl.generatePodAnnotations(&cl.Postgresql.Spec)
 | 
			
		||||
		for k, v := range annotations {
 | 
			
		||||
			if observed, expected := v, tt.merged[k]; observed != expected {
 | 
			
		||||
				t.Errorf("%v expects annotation value %v for key %v, but found %v",
 | 
			
		||||
					testName+"/"+tt.subTest, expected, observed, k)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		for k, v := range tt.merged {
 | 
			
		||||
			if observed, expected := annotations[k], v; observed != expected {
 | 
			
		||||
				t.Errorf("%v expects annotation value %v for key %v, but found %v",
 | 
			
		||||
					testName+"/"+tt.subTest, expected, observed, k)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,7 +7,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
| 
						 | 
				
			
			@ -430,6 +430,7 @@ func mountShmVolumeNeeded(opConfig config.Config, pgSpec *acidv1.PostgresSpec) *
 | 
			
		|||
func generatePodTemplate(
 | 
			
		||||
	namespace string,
 | 
			
		||||
	labels labels.Set,
 | 
			
		||||
	annotations map[string]string,
 | 
			
		||||
	spiloContainer *v1.Container,
 | 
			
		||||
	initContainers []v1.Container,
 | 
			
		||||
	sidecarContainers []v1.Container,
 | 
			
		||||
| 
						 | 
				
			
			@ -487,11 +488,15 @@ func generatePodTemplate(
 | 
			
		|||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Labels:      labels,
 | 
			
		||||
			Namespace:   namespace,
 | 
			
		||||
			Annotations: annotations,
 | 
			
		||||
		},
 | 
			
		||||
		Spec: podSpec,
 | 
			
		||||
	}
 | 
			
		||||
	if kubeIAMRole != "" {
 | 
			
		||||
		template.Annotations = map[string]string{constants.KubeIAmAnnotation: kubeIAMRole}
 | 
			
		||||
		if template.Annotations == nil {
 | 
			
		||||
			template.Annotations = make(map[string]string)
 | 
			
		||||
		}
 | 
			
		||||
		template.Annotations[constants.KubeIAmAnnotation] = kubeIAMRole
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &template, nil
 | 
			
		||||
| 
						 | 
				
			
			@ -711,10 +716,11 @@ func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) acid
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.StatefulSet, error) {
 | 
			
		||||
func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*appsv1.StatefulSet, error) {
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
		err                 error
 | 
			
		||||
		initContainers      []v1.Container
 | 
			
		||||
		sidecarContainers   []v1.Container
 | 
			
		||||
		podTemplate         *v1.PodTemplateSpec
 | 
			
		||||
		volumeClaimTemplate *v1.PersistentVolumeClaim
 | 
			
		||||
| 
						 | 
				
			
			@ -735,7 +741,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
			limit = c.OpConfig.DefaultMemoryLimit
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isSmaller, err := util.RequestIsSmallerThanLimit(request, limit)
 | 
			
		||||
		isSmaller, err := util.IsSmallerQuantity(request, limit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -762,7 +768,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
				limit = c.OpConfig.DefaultMemoryLimit
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			isSmaller, err := util.RequestIsSmallerThanLimit(sidecarRequest, sidecarLimit)
 | 
			
		||||
			isSmaller, err := util.IsSmallerQuantity(sidecarRequest, sidecarLimit)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
			}
 | 
			
		||||
| 
						 | 
				
			
			@ -781,6 +787,13 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
		return nil, fmt.Errorf("could not generate resource requirements: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if spec.InitContainers != nil && len(spec.InitContainers) > 0 {
 | 
			
		||||
		if c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) {
 | 
			
		||||
			c.logger.Warningf("initContainers specified but disabled in configuration - next statefulset creation would fail")
 | 
			
		||||
		}
 | 
			
		||||
		initContainers = spec.InitContainers
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	customPodEnvVarsList := make([]v1.EnvVar, 0)
 | 
			
		||||
 | 
			
		||||
	if c.OpConfig.PodEnvironmentConfigMap != "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -867,10 +880,15 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
	}
 | 
			
		||||
 | 
			
		||||
	// generate sidecar containers
 | 
			
		||||
	if sideCars != nil && len(sideCars) > 0 {
 | 
			
		||||
		if c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) {
 | 
			
		||||
			c.logger.Warningf("sidecars specified but disabled in configuration - next statefulset creation would fail")
 | 
			
		||||
		}
 | 
			
		||||
		if sidecarContainers, err = generateSidecarContainers(sideCars, volumeMounts, defaultResources,
 | 
			
		||||
			c.OpConfig.SuperUsername, c.credentialSecretName(c.OpConfig.SuperUsername), c.logger); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("could not generate sidecar containers: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
 | 
			
		||||
	effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName)
 | 
			
		||||
| 
						 | 
				
			
			@ -881,12 +899,15 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
		effectiveFSGroup = spec.SpiloFSGroup
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	annotations := c.generatePodAnnotations(spec)
 | 
			
		||||
 | 
			
		||||
	// generate pod template for the statefulset, based on the spilo container and sidecars
 | 
			
		||||
	if podTemplate, err = generatePodTemplate(
 | 
			
		||||
		c.Namespace,
 | 
			
		||||
		c.labelsSet(true),
 | 
			
		||||
		annotations,
 | 
			
		||||
		spiloContainer,
 | 
			
		||||
		spec.InitContainers,
 | 
			
		||||
		initContainers,
 | 
			
		||||
		sidecarContainers,
 | 
			
		||||
		&tolerationSpec,
 | 
			
		||||
		effectiveFSGroup,
 | 
			
		||||
| 
						 | 
				
			
			@ -917,25 +938,25 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
	// the operator has domain-specific logic on how to do rolling updates of PG clusters
 | 
			
		||||
	// so we do not use default rolling updates implemented by stateful sets
 | 
			
		||||
	// that leaves the legacy "OnDelete" update strategy as the only option
 | 
			
		||||
	updateStrategy := v1beta1.StatefulSetUpdateStrategy{Type: v1beta1.OnDeleteStatefulSetStrategyType}
 | 
			
		||||
	updateStrategy := appsv1.StatefulSetUpdateStrategy{Type: appsv1.OnDeleteStatefulSetStrategyType}
 | 
			
		||||
 | 
			
		||||
	var podManagementPolicy v1beta1.PodManagementPolicyType
 | 
			
		||||
	var podManagementPolicy appsv1.PodManagementPolicyType
 | 
			
		||||
	if c.OpConfig.PodManagementPolicy == "ordered_ready" {
 | 
			
		||||
		podManagementPolicy = v1beta1.OrderedReadyPodManagement
 | 
			
		||||
		podManagementPolicy = appsv1.OrderedReadyPodManagement
 | 
			
		||||
	} else if c.OpConfig.PodManagementPolicy == "parallel" {
 | 
			
		||||
		podManagementPolicy = v1beta1.ParallelPodManagement
 | 
			
		||||
		podManagementPolicy = appsv1.ParallelPodManagement
 | 
			
		||||
	} else {
 | 
			
		||||
		return nil, fmt.Errorf("could not set the pod management policy to the unknown value: %v", c.OpConfig.PodManagementPolicy)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	statefulSet := &v1beta1.StatefulSet{
 | 
			
		||||
	statefulSet := &appsv1.StatefulSet{
 | 
			
		||||
		ObjectMeta: metav1.ObjectMeta{
 | 
			
		||||
			Name:        c.statefulSetName(),
 | 
			
		||||
			Namespace:   c.Namespace,
 | 
			
		||||
			Labels:      c.labelsSet(true),
 | 
			
		||||
			Annotations: map[string]string{rollingUpdateStatefulsetAnnotationKey: "false"},
 | 
			
		||||
		},
 | 
			
		||||
		Spec: v1beta1.StatefulSetSpec{
 | 
			
		||||
		Spec: appsv1.StatefulSetSpec{
 | 
			
		||||
			Replicas:             &numberOfInstances,
 | 
			
		||||
			Selector:             c.labelsSelector(),
 | 
			
		||||
			ServiceName:          c.serviceName(Master),
 | 
			
		||||
| 
						 | 
				
			
			@ -949,6 +970,24 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
 | 
			
		|||
	return statefulSet, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) generatePodAnnotations(spec *acidv1.PostgresSpec) map[string]string {
 | 
			
		||||
	annotations := make(map[string]string)
 | 
			
		||||
	for k, v := range c.OpConfig.CustomPodAnnotations {
 | 
			
		||||
		annotations[k] = v
 | 
			
		||||
	}
 | 
			
		||||
	if spec != nil || spec.PodAnnotations != nil {
 | 
			
		||||
		for k, v := range spec.PodAnnotations {
 | 
			
		||||
			annotations[k] = v
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(annotations) == 0 {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return annotations
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func generateScalyrSidecarSpec(clusterName, APIKey, serverURL, dockerImage string,
 | 
			
		||||
	containerResources *acidv1.Resources, logger *logrus.Entry) *acidv1.Sidecar {
 | 
			
		||||
	if APIKey == "" || dockerImage == "" {
 | 
			
		||||
| 
						 | 
				
			
			@ -1386,7 +1425,7 @@ func (c *Cluster) generatePodDisruptionBudget() *policybeta1.PodDisruptionBudget
 | 
			
		|||
	pdbEnabled := c.OpConfig.EnablePodDisruptionBudget
 | 
			
		||||
 | 
			
		||||
	// if PodDisruptionBudget is disabled or if there are no DB pods, set the budget to 0.
 | 
			
		||||
	if (pdbEnabled != nil && !*pdbEnabled) || c.Spec.NumberOfInstances <= 0 {
 | 
			
		||||
	if (pdbEnabled != nil && !(*pdbEnabled)) || c.Spec.NumberOfInstances <= 0 {
 | 
			
		||||
		minAvailable = intstr.FromInt(0)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1462,10 +1501,13 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
 | 
			
		|||
			},
 | 
			
		||||
		}}
 | 
			
		||||
 | 
			
		||||
	annotations := c.generatePodAnnotations(&c.Spec)
 | 
			
		||||
 | 
			
		||||
	// re-use the method that generates DB pod templates
 | 
			
		||||
	if podTemplate, err = generatePodTemplate(
 | 
			
		||||
		c.Namespace,
 | 
			
		||||
		labels,
 | 
			
		||||
		annotations,
 | 
			
		||||
		logicalBackupContainer,
 | 
			
		||||
		[]v1.Container{},
 | 
			
		||||
		[]v1.Container{},
 | 
			
		||||
| 
						 | 
				
			
			@ -1479,8 +1521,8 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
 | 
			
		|||
		util.False(),
 | 
			
		||||
		false,
 | 
			
		||||
		"",
 | 
			
		||||
		"",
 | 
			
		||||
		""); err != nil {
 | 
			
		||||
		c.OpConfig.AdditionalSecretMount,
 | 
			
		||||
		c.OpConfig.AdditionalSecretMountPath); err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not generate pod template for logical backup pod: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1528,6 +1570,10 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
 | 
			
		|||
			Name:  "SCOPE",
 | 
			
		||||
			Value: c.Name,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "CLUSTER_NAME_LABEL",
 | 
			
		||||
			Value: c.OpConfig.ClusterNameLabel,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name: "POD_NAMESPACE",
 | 
			
		||||
			ValueFrom: &v1.EnvVarSource{
 | 
			
		||||
| 
						 | 
				
			
			@ -1542,6 +1588,14 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
 | 
			
		|||
			Name:  "LOGICAL_BACKUP_S3_BUCKET",
 | 
			
		||||
			Value: c.OpConfig.LogicalBackup.LogicalBackupS3Bucket,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "LOGICAL_BACKUP_S3_ENDPOINT",
 | 
			
		||||
			Value: c.OpConfig.LogicalBackup.LogicalBackupS3Endpoint,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "LOGICAL_BACKUP_S3_SSE",
 | 
			
		||||
			Value: c.OpConfig.LogicalBackup.LogicalBackupS3SSE,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			Name:  "LOGICAL_BACKUP_S3_BUCKET_SCOPE_SUFFIX",
 | 
			
		||||
			Value: getBucketScopeSuffix(string(c.Postgresql.GetUID())),
 | 
			
		||||
| 
						 | 
				
			
			@ -1580,8 +1634,15 @@ func (c *Cluster) generateLogicalBackupPodEnvVars() []v1.EnvVar {
 | 
			
		|||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.logger.Debugf("Generated logical backup env vars %v", envVars)
 | 
			
		||||
	if c.OpConfig.LogicalBackup.LogicalBackupS3AccessKeyID != "" {
 | 
			
		||||
		envVars = append(envVars, v1.EnvVar{Name: "AWS_ACCESS_KEY_ID", Value: c.OpConfig.LogicalBackup.LogicalBackupS3AccessKeyID})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if c.OpConfig.LogicalBackup.LogicalBackupS3SecretAccessKey != "" {
 | 
			
		||||
		envVars = append(envVars, v1.EnvVar{Name: "AWS_SECRET_ACCESS_KEY", Value: c.OpConfig.LogicalBackup.LogicalBackupS3SecretAccessKey})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.logger.Debugf("Generated logical backup env vars %v", envVars)
 | 
			
		||||
	return envVars
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ package cluster
 | 
			
		|||
import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,12 +4,12 @@ import (
 | 
			
		|||
	"fmt"
 | 
			
		||||
	"math/rand"
 | 
			
		||||
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/spec"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util"
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) listPods() ([]v1.Pod, error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@ func (c *Cluster) MigrateMasterPod(podName spec.NamespacedName) error {
 | 
			
		|||
	}
 | 
			
		||||
	// we must have a statefulset in the cluster for the migration to work
 | 
			
		||||
	if c.Statefulset == nil {
 | 
			
		||||
		var sset *v1beta1.StatefulSet
 | 
			
		||||
		var sset *appsv1.StatefulSet
 | 
			
		||||
		if sset, err = c.KubeClient.StatefulSets(c.Namespace).Get(c.statefulSetName(),
 | 
			
		||||
			metav1.GetOptions{}); err != nil {
 | 
			
		||||
			return fmt.Errorf("could not retrieve cluster statefulset: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,7 @@ import (
 | 
			
		|||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	batchv1beta1 "k8s.io/api/batch/v1beta1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,6 @@ import (
 | 
			
		|||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/constants"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/retryutil"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -64,8 +63,19 @@ func (c *Cluster) listResources() error {
 | 
			
		|||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) createStatefulSet() (*v1beta1.StatefulSet, error) {
 | 
			
		||||
func (c *Cluster) createStatefulSet() (*appsv1.StatefulSet, error) {
 | 
			
		||||
	c.setProcessName("creating statefulset")
 | 
			
		||||
	// check if it's allowed that spec contains initContainers
 | 
			
		||||
	if c.Spec.InitContainers != nil && len(c.Spec.InitContainers) > 0 &&
 | 
			
		||||
		c.OpConfig.EnableInitContainers != nil && !(*c.OpConfig.EnableInitContainers) {
 | 
			
		||||
		return nil, fmt.Errorf("initContainers specified but disabled in configuration")
 | 
			
		||||
	}
 | 
			
		||||
	// check if it's allowed that spec contains sidecars
 | 
			
		||||
	if c.Spec.Sidecars != nil && len(c.Spec.Sidecars) > 0 &&
 | 
			
		||||
		c.OpConfig.EnableSidecars != nil && !(*c.OpConfig.EnableSidecars) {
 | 
			
		||||
		return nil, fmt.Errorf("sidecar containers specified but disabled in configuration")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	statefulSetSpec, err := c.generateStatefulSet(&c.Spec)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not generate statefulset: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -95,7 +105,7 @@ func getPodIndex(podName string) (int32, error) {
 | 
			
		|||
	return int32(res), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) preScaleDown(newStatefulSet *v1beta1.StatefulSet) error {
 | 
			
		||||
func (c *Cluster) preScaleDown(newStatefulSet *appsv1.StatefulSet) error {
 | 
			
		||||
	masterPod, err := c.getRolePods(Master)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("could not get master pod: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -135,7 +145,7 @@ func (c *Cluster) preScaleDown(newStatefulSet *v1beta1.StatefulSet) error {
 | 
			
		|||
 | 
			
		||||
// setRollingUpdateFlagForStatefulSet sets the indicator or the rolling update requirement
 | 
			
		||||
// in the StatefulSet annotation.
 | 
			
		||||
func (c *Cluster) setRollingUpdateFlagForStatefulSet(sset *v1beta1.StatefulSet, val bool) {
 | 
			
		||||
func (c *Cluster) setRollingUpdateFlagForStatefulSet(sset *appsv1.StatefulSet, val bool) {
 | 
			
		||||
	anno := sset.GetAnnotations()
 | 
			
		||||
	if anno == nil {
 | 
			
		||||
		anno = make(map[string]string)
 | 
			
		||||
| 
						 | 
				
			
			@ -160,7 +170,7 @@ func (c *Cluster) applyRollingUpdateFlagforStatefulSet(val bool) error {
 | 
			
		|||
 | 
			
		||||
// getRollingUpdateFlagFromStatefulSet returns the value of the rollingUpdate flag from the passed
 | 
			
		||||
// StatefulSet, reverting to the default value in case of errors
 | 
			
		||||
func (c *Cluster) getRollingUpdateFlagFromStatefulSet(sset *v1beta1.StatefulSet, defaultValue bool) (flag bool) {
 | 
			
		||||
func (c *Cluster) getRollingUpdateFlagFromStatefulSet(sset *appsv1.StatefulSet, defaultValue bool) (flag bool) {
 | 
			
		||||
	anno := sset.GetAnnotations()
 | 
			
		||||
	flag = defaultValue
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -181,7 +191,7 @@ func (c *Cluster) getRollingUpdateFlagFromStatefulSet(sset *v1beta1.StatefulSet,
 | 
			
		|||
// mergeRollingUpdateFlagUsingCache returns the value of the rollingUpdate flag from the passed
 | 
			
		||||
// statefulset, however, the value can be cleared if there is a cached flag in the cluster that
 | 
			
		||||
// is set to false (the discrepancy could be a result of a failed StatefulSet update)
 | 
			
		||||
func (c *Cluster) mergeRollingUpdateFlagUsingCache(runningStatefulSet *v1beta1.StatefulSet) bool {
 | 
			
		||||
func (c *Cluster) mergeRollingUpdateFlagUsingCache(runningStatefulSet *appsv1.StatefulSet) bool {
 | 
			
		||||
	var (
 | 
			
		||||
		cachedStatefulsetExists, clearRollingUpdateFromCache, podsRollingUpdateRequired bool
 | 
			
		||||
	)
 | 
			
		||||
| 
						 | 
				
			
			@ -207,7 +217,7 @@ func (c *Cluster) mergeRollingUpdateFlagUsingCache(runningStatefulSet *v1beta1.S
 | 
			
		|||
	return podsRollingUpdateRequired
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) updateStatefulSetAnnotations(annotations map[string]string) (*v1beta1.StatefulSet, error) {
 | 
			
		||||
func (c *Cluster) updateStatefulSetAnnotations(annotations map[string]string) (*appsv1.StatefulSet, error) {
 | 
			
		||||
	c.logger.Debugf("updating statefulset annotations")
 | 
			
		||||
	patchData, err := metaAnnotationsPatch(annotations)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -223,7 +233,7 @@ func (c *Cluster) updateStatefulSetAnnotations(annotations map[string]string) (*
 | 
			
		|||
	return result, nil
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
func (c *Cluster) updateStatefulSet(newStatefulSet *v1beta1.StatefulSet) error {
 | 
			
		||||
func (c *Cluster) updateStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
 | 
			
		||||
	c.setProcessName("updating statefulset")
 | 
			
		||||
	if c.Statefulset == nil {
 | 
			
		||||
		return fmt.Errorf("there is no statefulset in the cluster")
 | 
			
		||||
| 
						 | 
				
			
			@ -264,7 +274,7 @@ func (c *Cluster) updateStatefulSet(newStatefulSet *v1beta1.StatefulSet) error {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// replaceStatefulSet deletes an old StatefulSet and creates the new using spec in the PostgreSQL CRD.
 | 
			
		||||
func (c *Cluster) replaceStatefulSet(newStatefulSet *v1beta1.StatefulSet) error {
 | 
			
		||||
func (c *Cluster) replaceStatefulSet(newStatefulSet *appsv1.StatefulSet) error {
 | 
			
		||||
	c.setProcessName("replacing statefulset")
 | 
			
		||||
	if c.Statefulset == nil {
 | 
			
		||||
		return fmt.Errorf("there is no statefulset in the cluster")
 | 
			
		||||
| 
						 | 
				
			
			@ -278,7 +288,8 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *v1beta1.StatefulSet) error
 | 
			
		|||
	oldStatefulset := c.Statefulset
 | 
			
		||||
 | 
			
		||||
	options := metav1.DeleteOptions{PropagationPolicy: &deletePropagationPolicy}
 | 
			
		||||
	if err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Delete(oldStatefulset.Name, &options); err != nil {
 | 
			
		||||
	err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Delete(oldStatefulset.Name, &options)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("could not delete statefulset %q: %v", statefulSetName, err)
 | 
			
		||||
	}
 | 
			
		||||
	// make sure we clear the stored statefulset status if the subsequent create fails.
 | 
			
		||||
| 
						 | 
				
			
			@ -286,11 +297,16 @@ func (c *Cluster) replaceStatefulSet(newStatefulSet *v1beta1.StatefulSet) error
 | 
			
		|||
	// wait until the statefulset is truly deleted
 | 
			
		||||
	c.logger.Debugf("waiting for the statefulset to be deleted")
 | 
			
		||||
 | 
			
		||||
	err := retryutil.Retry(constants.StatefulsetDeletionInterval, constants.StatefulsetDeletionTimeout,
 | 
			
		||||
	err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
 | 
			
		||||
		func() (bool, error) {
 | 
			
		||||
			_, err := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(oldStatefulset.Name, metav1.GetOptions{})
 | 
			
		||||
 | 
			
		||||
			return err != nil, nil
 | 
			
		||||
			_, err2 := c.KubeClient.StatefulSets(oldStatefulset.Namespace).Get(oldStatefulset.Name, metav1.GetOptions{})
 | 
			
		||||
			if err2 == nil {
 | 
			
		||||
				return false, nil
 | 
			
		||||
			}
 | 
			
		||||
			if k8sutil.ResourceNotFound(err2) {
 | 
			
		||||
				return true, nil
 | 
			
		||||
			}
 | 
			
		||||
			return false, err2
 | 
			
		||||
		})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("could not delete statefulset: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -329,7 +345,7 @@ func (c *Cluster) deleteStatefulSet() error {
 | 
			
		|||
		return fmt.Errorf("could not delete pods: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.deletePersistenVolumeClaims(); err != nil {
 | 
			
		||||
	if err := c.deletePersistentVolumeClaims(); err != nil {
 | 
			
		||||
		return fmt.Errorf("could not delete PersistentVolumeClaims: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -380,13 +396,27 @@ func (c *Cluster) updateService(role PostgresRole, newService *v1.Service) error
 | 
			
		|||
			return fmt.Errorf("could not delete service %q: %v", serviceName, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Endpoints[role] = nil
 | 
			
		||||
		svc, err := c.KubeClient.Services(serviceName.Namespace).Create(newService)
 | 
			
		||||
		// wait until the service is truly deleted
 | 
			
		||||
		c.logger.Debugf("waiting for service to be deleted")
 | 
			
		||||
 | 
			
		||||
		err = retryutil.Retry(c.OpConfig.ResourceCheckInterval, c.OpConfig.ResourceCheckTimeout,
 | 
			
		||||
			func() (bool, error) {
 | 
			
		||||
				_, err2 := c.KubeClient.Services(serviceName.Namespace).Get(serviceName.Name, metav1.GetOptions{})
 | 
			
		||||
				if err2 == nil {
 | 
			
		||||
					return false, nil
 | 
			
		||||
				}
 | 
			
		||||
				if k8sutil.ResourceNotFound(err2) {
 | 
			
		||||
					return true, nil
 | 
			
		||||
				}
 | 
			
		||||
				return false, err2
 | 
			
		||||
			})
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("could not create service %q: %v", serviceName, err)
 | 
			
		||||
			return fmt.Errorf("could not delete service %q: %v", serviceName, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Services[role] = svc
 | 
			
		||||
		// make sure we clear the stored service and endpoint status if the subsequent create fails.
 | 
			
		||||
		c.Services[role] = nil
 | 
			
		||||
		c.Endpoints[role] = nil
 | 
			
		||||
		if role == Master {
 | 
			
		||||
			// create the new endpoint using the addresses obtained from the previous one
 | 
			
		||||
			endpointSpec := c.generateEndpoint(role, currentEndpoint.Subsets)
 | 
			
		||||
| 
						 | 
				
			
			@ -398,6 +428,13 @@ func (c *Cluster) updateService(role PostgresRole, newService *v1.Service) error
 | 
			
		|||
			c.Endpoints[role] = ep
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		svc, err := c.KubeClient.Services(serviceName.Namespace).Create(newService)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("could not create service %q: %v", serviceName, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		c.Services[role] = svc
 | 
			
		||||
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -676,7 +713,7 @@ func (c *Cluster) GetEndpointReplica() *v1.Endpoints {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// GetStatefulSet returns cluster's kubernetes StatefulSet
 | 
			
		||||
func (c *Cluster) GetStatefulSet() *v1beta1.StatefulSet {
 | 
			
		||||
func (c *Cluster) GetStatefulSet() *appsv1.StatefulSet {
 | 
			
		||||
	return c.Statefulset
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,6 +23,7 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
 | 
			
		|||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	oldStatus := c.Status
 | 
			
		||||
	c.setSpec(newSpec)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -34,6 +35,16 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
 | 
			
		|||
		}
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	if err = c.validateResources(&c.Spec); err != nil {
 | 
			
		||||
		err = fmt.Errorf("insufficient resource limits specified: %v", err)
 | 
			
		||||
		if oldStatus.Running() {
 | 
			
		||||
			c.logger.Warning(err)
 | 
			
		||||
			err = nil
 | 
			
		||||
		} else {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = c.initUsers(); err != nil {
 | 
			
		||||
		err = fmt.Errorf("could not init users: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,8 +4,8 @@ import (
 | 
			
		|||
	"time"
 | 
			
		||||
 | 
			
		||||
	acidv1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
)
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +60,7 @@ type ClusterStatus struct {
 | 
			
		|||
	ReplicaService      *v1.Service
 | 
			
		||||
	MasterEndpoint      *v1.Endpoints
 | 
			
		||||
	ReplicaEndpoint     *v1.Endpoints
 | 
			
		||||
	StatefulSet         *v1beta1.StatefulSet
 | 
			
		||||
	StatefulSet         *appsv1.StatefulSet
 | 
			
		||||
	PodDisruptionBudget *policybeta1.PodDisruptionBudget
 | 
			
		||||
 | 
			
		||||
	CurrentProcess Process
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -11,7 +11,7 @@ import (
 | 
			
		|||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/apps/v1beta1"
 | 
			
		||||
	appsv1 "k8s.io/api/apps/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	policybeta1 "k8s.io/api/policy/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
| 
						 | 
				
			
			@ -168,7 +168,7 @@ func (c *Cluster) logPDBChanges(old, new *policybeta1.PodDisruptionBudget, isUpd
 | 
			
		|||
	c.logger.Debugf("diff\n%s\n", util.PrettyDiff(old.Spec, new.Spec))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) logStatefulSetChanges(old, new *v1beta1.StatefulSet, isUpdate bool, reasons []string) {
 | 
			
		||||
func (c *Cluster) logStatefulSetChanges(old, new *appsv1.StatefulSet, isUpdate bool, reasons []string) {
 | 
			
		||||
	if isUpdate {
 | 
			
		||||
		c.logger.Infof("statefulset %q has been changed", util.NameFromMeta(old.ObjectMeta))
 | 
			
		||||
	} else {
 | 
			
		||||
| 
						 | 
				
			
			@ -365,7 +365,7 @@ func (c *Cluster) waitStatefulsetPodsReady() error {
 | 
			
		|||
	c.setProcessName("waiting for the pods of the statefulset")
 | 
			
		||||
	// TODO: wait for the first Pod only
 | 
			
		||||
	if err := c.waitStatefulsetReady(); err != nil {
 | 
			
		||||
		return fmt.Errorf("statuful set error: %v", err)
 | 
			
		||||
		return fmt.Errorf("stateful set error: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// TODO: wait only for master
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,7 +30,7 @@ func (c *Cluster) listPersistentVolumeClaims() ([]v1.PersistentVolumeClaim, erro
 | 
			
		|||
	return pvcs.Items, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Cluster) deletePersistenVolumeClaims() error {
 | 
			
		||||
func (c *Cluster) deletePersistentVolumeClaims() error {
 | 
			
		||||
	c.logger.Debugln("deleting PVCs")
 | 
			
		||||
	pvcs, err := c.listPersistentVolumeClaims()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,7 +6,7 @@ import (
 | 
			
		|||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	rbacv1beta1 "k8s.io/api/rbac/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
| 
						 | 
				
			
			@ -58,7 +58,6 @@ type Controller struct {
 | 
			
		|||
 | 
			
		||||
	PodServiceAccount            *v1.ServiceAccount
 | 
			
		||||
	PodServiceAccountRoleBinding *rbacv1beta1.RoleBinding
 | 
			
		||||
	namespacesWithDefinedRBAC    sync.Map
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewController creates a new controller
 | 
			
		||||
| 
						 | 
				
			
			@ -112,7 +111,7 @@ func (c *Controller) initOperatorConfig() {
 | 
			
		|||
 | 
			
		||||
	if c.opConfig.SetMemoryRequestToLimit {
 | 
			
		||||
 | 
			
		||||
		isSmaller, err := util.RequestIsSmallerThanLimit(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
 | 
			
		||||
		isSmaller, err := util.IsSmallerQuantity(c.opConfig.DefaultMemoryRequest, c.opConfig.DefaultMemoryLimit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -121,7 +120,7 @@ func (c *Controller) initOperatorConfig() {
 | 
			
		|||
			c.opConfig.DefaultMemoryRequest = c.opConfig.DefaultMemoryLimit
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		isSmaller, err = util.RequestIsSmallerThanLimit(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(c.opConfig.ScalyrMemoryRequest, c.opConfig.ScalyrMemoryLimit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			panic(err)
 | 
			
		||||
		}
 | 
			
		||||
| 
						 | 
				
			
			@ -241,7 +240,7 @@ func (c *Controller) initController() {
 | 
			
		|||
	c.initClients()
 | 
			
		||||
 | 
			
		||||
	if configObjectName := os.Getenv("POSTGRES_OPERATOR_CONFIGURATION_OBJECT"); configObjectName != "" {
 | 
			
		||||
		if err := c.createConfigurationCRD(); err != nil {
 | 
			
		||||
		if err := c.createConfigurationCRD(c.opConfig.EnableCRDValidation); err != nil {
 | 
			
		||||
			c.logger.Fatalf("could not register Operator Configuration CustomResourceDefinition: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if cfg, err := c.readOperatorConfigurationFromCRD(spec.GetOperatorNamespace(), configObjectName); err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -257,7 +256,7 @@ func (c *Controller) initController() {
 | 
			
		|||
 | 
			
		||||
	c.modifyConfigFromEnvironment()
 | 
			
		||||
 | 
			
		||||
	if err := c.createPostgresCRD(); err != nil {
 | 
			
		||||
	if err := c.createPostgresCRD(c.opConfig.EnableCRDValidation); err != nil {
 | 
			
		||||
		c.logger.Fatalf("could not register Postgres CustomResourceDefinition: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
			
		|||
	result := &config.Config{}
 | 
			
		||||
 | 
			
		||||
	// general config
 | 
			
		||||
	result.EnableCRDValidation = fromCRD.EnableCRDValidation
 | 
			
		||||
	result.EtcdHost = fromCRD.EtcdHost
 | 
			
		||||
	result.DockerImage = fromCRD.DockerImage
 | 
			
		||||
	result.Workers = fromCRD.Workers
 | 
			
		||||
| 
						 | 
				
			
			@ -41,6 +42,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
			
		|||
	result.ReplicationUsername = fromCRD.PostgresUsersConfiguration.ReplicationUsername
 | 
			
		||||
 | 
			
		||||
	// kubernetes config
 | 
			
		||||
	result.CustomPodAnnotations = fromCRD.Kubernetes.CustomPodAnnotations
 | 
			
		||||
	result.PodServiceAccountName = fromCRD.Kubernetes.PodServiceAccountName
 | 
			
		||||
	result.PodServiceAccountDefinition = fromCRD.Kubernetes.PodServiceAccountDefinition
 | 
			
		||||
	result.PodServiceAccountRoleBindingDefinition = fromCRD.Kubernetes.PodServiceAccountRoleBindingDefinition
 | 
			
		||||
| 
						 | 
				
			
			@ -52,6 +54,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
			
		|||
	result.WatchedNamespace = fromCRD.Kubernetes.WatchedNamespace
 | 
			
		||||
	result.PDBNameFormat = fromCRD.Kubernetes.PDBNameFormat
 | 
			
		||||
	result.EnablePodDisruptionBudget = fromCRD.Kubernetes.EnablePodDisruptionBudget
 | 
			
		||||
	result.EnableInitContainers = fromCRD.Kubernetes.EnableInitContainers
 | 
			
		||||
	result.EnableSidecars = fromCRD.Kubernetes.EnableSidecars
 | 
			
		||||
	result.SecretNameTemplate = fromCRD.Kubernetes.SecretNameTemplate
 | 
			
		||||
	result.OAuthTokenSecretName = fromCRD.Kubernetes.OAuthTokenSecretName
 | 
			
		||||
	result.InfrastructureRolesSecretName = fromCRD.Kubernetes.InfrastructureRolesSecretName
 | 
			
		||||
| 
						 | 
				
			
			@ -100,6 +104,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
			
		|||
	result.LogicalBackupSchedule = fromCRD.LogicalBackup.Schedule
 | 
			
		||||
	result.LogicalBackupDockerImage = fromCRD.LogicalBackup.DockerImage
 | 
			
		||||
	result.LogicalBackupS3Bucket = fromCRD.LogicalBackup.S3Bucket
 | 
			
		||||
	result.LogicalBackupS3Endpoint = fromCRD.LogicalBackup.S3Endpoint
 | 
			
		||||
	result.LogicalBackupS3AccessKeyID = fromCRD.LogicalBackup.S3AccessKeyID
 | 
			
		||||
	result.LogicalBackupS3SecretAccessKey = fromCRD.LogicalBackup.S3SecretAccessKey
 | 
			
		||||
	result.LogicalBackupS3SSE = fromCRD.LogicalBackup.S3SSE
 | 
			
		||||
 | 
			
		||||
	// debug config
 | 
			
		||||
	result.DebugLogging = fromCRD.OperatorDebug.DebugLogging
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -312,7 +312,7 @@ func (c *Controller) processClusterEventsQueue(idx int, stopCh <-chan struct{},
 | 
			
		|||
	for {
 | 
			
		||||
		obj, err := c.clusterEventQueues[idx].Pop(cache.PopProcessFunc(func(interface{}) error { return nil }))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if err == cache.FIFOClosedError {
 | 
			
		||||
			if err == cache.ErrFIFOClosed {
 | 
			
		||||
				return
 | 
			
		||||
			}
 | 
			
		||||
			c.logger.Errorf("error when processing cluster events queue: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -493,17 +493,16 @@ func (c *Controller) postgresqlDelete(obj interface{}) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
  Ensures the pod service account and role bindings exists in a namespace before a PG cluster is created there so that a user does not have to deploy these credentials manually.
 | 
			
		||||
  StatefulSets require the service account to create pods; Patroni requires relevant RBAC bindings to access endpoints.
 | 
			
		||||
  Ensures the pod service account and role bindings exists in a namespace
 | 
			
		||||
  before a PG cluster is created there so that a user does not have to deploy
 | 
			
		||||
  these credentials manually.  StatefulSets require the service account to
 | 
			
		||||
  create pods; Patroni requires relevant RBAC bindings to access endpoints.
 | 
			
		||||
 | 
			
		||||
  The operator does not sync accounts/role bindings after creation.
 | 
			
		||||
*/
 | 
			
		||||
func (c *Controller) submitRBACCredentials(event ClusterEvent) error {
 | 
			
		||||
 | 
			
		||||
	namespace := event.NewSpec.GetNamespace()
 | 
			
		||||
	if _, ok := c.namespacesWithDefinedRBAC.Load(namespace); ok {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := c.createPodServiceAccount(namespace); err != nil {
 | 
			
		||||
		return fmt.Errorf("could not create pod service account %v : %v", c.opConfig.PodServiceAccountName, err)
 | 
			
		||||
| 
						 | 
				
			
			@ -512,7 +511,6 @@ func (c *Controller) submitRBACCredentials(event ClusterEvent) error {
 | 
			
		|||
	if err := c.createRoleBindings(namespace); err != nil {
 | 
			
		||||
		return fmt.Errorf("could not create role binding %v : %v", c.PodServiceAccountRoleBinding.Name, err)
 | 
			
		||||
	}
 | 
			
		||||
	c.namespacesWithDefinedRBAC.Store(namespace, true)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,7 @@ import (
 | 
			
		|||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	apiextv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/types"
 | 
			
		||||
| 
						 | 
				
			
			@ -91,12 +91,12 @@ func (c *Controller) createOperatorCRD(crd *apiextv1beta1.CustomResourceDefiniti
 | 
			
		|||
	})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Controller) createPostgresCRD() error {
 | 
			
		||||
	return c.createOperatorCRD(acidv1.PostgresCRD())
 | 
			
		||||
func (c *Controller) createPostgresCRD(enableValidation *bool) error {
 | 
			
		||||
	return c.createOperatorCRD(acidv1.PostgresCRD(enableValidation))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Controller) createConfigurationCRD() error {
 | 
			
		||||
	return c.createOperatorCRD(acidv1.ConfigurationCRD())
 | 
			
		||||
func (c *Controller) createConfigurationCRD(enableValidation *bool) error {
 | 
			
		||||
	return c.createOperatorCRD(acidv1.ConfigurationCRD(enableValidation))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readDecodedRole(s string) (*spec.PgUser, error) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,7 +9,7 @@ import (
 | 
			
		|||
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/spec"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/util/k8sutil"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	v1 "k8s.io/api/core/v1"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,8 @@ SOFTWARE.
 | 
			
		|||
package versioned
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	acidv1 "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/typed/acid.zalan.do/v1"
 | 
			
		||||
	discovery "k8s.io/client-go/discovery"
 | 
			
		||||
	rest "k8s.io/client-go/rest"
 | 
			
		||||
| 
						 | 
				
			
			@ -34,8 +36,6 @@ import (
 | 
			
		|||
type Interface interface {
 | 
			
		||||
	Discovery() discovery.DiscoveryInterface
 | 
			
		||||
	AcidV1() acidv1.AcidV1Interface
 | 
			
		||||
	// Deprecated: please explicitly pick a version if possible.
 | 
			
		||||
	Acid() acidv1.AcidV1Interface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Clientset contains the clients for groups. Each group has exactly one
 | 
			
		||||
| 
						 | 
				
			
			@ -50,12 +50,6 @@ func (c *Clientset) AcidV1() acidv1.AcidV1Interface {
 | 
			
		|||
	return c.acidV1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Deprecated: Acid retrieves the default version of AcidClient.
 | 
			
		||||
// Please explicitly pick a version.
 | 
			
		||||
func (c *Clientset) Acid() acidv1.AcidV1Interface {
 | 
			
		||||
	return c.acidV1
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Discovery retrieves the DiscoveryClient
 | 
			
		||||
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
 | 
			
		||||
	if c == nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -65,9 +59,14 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// NewForConfig creates a new Clientset for the given config.
 | 
			
		||||
// If config's RateLimiter is not set and QPS and Burst are acceptable,
 | 
			
		||||
// NewForConfig will generate a rate-limiter in configShallowCopy.
 | 
			
		||||
func NewForConfig(c *rest.Config) (*Clientset, error) {
 | 
			
		||||
	configShallowCopy := *c
 | 
			
		||||
	if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 {
 | 
			
		||||
		if configShallowCopy.Burst <= 0 {
 | 
			
		||||
			return nil, fmt.Errorf("Burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0")
 | 
			
		||||
		}
 | 
			
		||||
		configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst)
 | 
			
		||||
	}
 | 
			
		||||
	var cs Clientset
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -47,7 +47,7 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
 | 
			
		|||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cs := &Clientset{}
 | 
			
		||||
	cs := &Clientset{tracker: o}
 | 
			
		||||
	cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake}
 | 
			
		||||
	cs.AddReactor("*", "*", testing.ObjectReaction(o))
 | 
			
		||||
	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,20 +69,20 @@ func NewSimpleClientset(objects ...runtime.Object) *Clientset {
 | 
			
		|||
type Clientset struct {
 | 
			
		||||
	testing.Fake
 | 
			
		||||
	discovery *fakediscovery.FakeDiscovery
 | 
			
		||||
	tracker   testing.ObjectTracker
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Clientset) Discovery() discovery.DiscoveryInterface {
 | 
			
		||||
	return c.discovery
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Clientset) Tracker() testing.ObjectTracker {
 | 
			
		||||
	return c.tracker
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var _ clientset.Interface = &Clientset{}
 | 
			
		||||
 | 
			
		||||
// AcidV1 retrieves the AcidV1Client
 | 
			
		||||
func (c *Clientset) AcidV1() acidv1.AcidV1Interface {
 | 
			
		||||
	return &fakeacidv1.FakeAcidV1{Fake: &c.Fake}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Acid retrieves the AcidV1Client
 | 
			
		||||
func (c *Clientset) Acid() acidv1.AcidV1Interface {
 | 
			
		||||
	return &fakeacidv1.FakeAcidV1{Fake: &c.Fake}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,15 +30,14 @@ import (
 | 
			
		|||
	runtime "k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	schema "k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var scheme = runtime.NewScheme()
 | 
			
		||||
var codecs = serializer.NewCodecFactory(scheme)
 | 
			
		||||
var parameterCodec = runtime.NewParameterCodec(scheme)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
 | 
			
		||||
	AddToScheme(scheme)
 | 
			
		||||
var localSchemeBuilder = runtime.SchemeBuilder{
 | 
			
		||||
	acidv1.AddToScheme,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
 | 
			
		||||
| 
						 | 
				
			
			@ -51,10 +50,13 @@ func init() {
 | 
			
		|||
//   )
 | 
			
		||||
//
 | 
			
		||||
//   kclientset, _ := kubernetes.NewForConfig(c)
 | 
			
		||||
//   aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
 | 
			
		||||
//   _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
 | 
			
		||||
//
 | 
			
		||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
 | 
			
		||||
// correctly.
 | 
			
		||||
func AddToScheme(scheme *runtime.Scheme) {
 | 
			
		||||
	acidv1.AddToScheme(scheme)
 | 
			
		||||
var AddToScheme = localSchemeBuilder.AddToScheme
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"})
 | 
			
		||||
	utilruntime.Must(AddToScheme(scheme))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,15 +30,14 @@ import (
 | 
			
		|||
	runtime "k8s.io/apimachinery/pkg/runtime"
 | 
			
		||||
	schema "k8s.io/apimachinery/pkg/runtime/schema"
 | 
			
		||||
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Scheme = runtime.NewScheme()
 | 
			
		||||
var Codecs = serializer.NewCodecFactory(Scheme)
 | 
			
		||||
var ParameterCodec = runtime.NewParameterCodec(Scheme)
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
 | 
			
		||||
	AddToScheme(Scheme)
 | 
			
		||||
var localSchemeBuilder = runtime.SchemeBuilder{
 | 
			
		||||
	acidv1.AddToScheme,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddToScheme adds all types of this clientset into the given scheme. This allows composition
 | 
			
		||||
| 
						 | 
				
			
			@ -51,10 +50,13 @@ func init() {
 | 
			
		|||
//   )
 | 
			
		||||
//
 | 
			
		||||
//   kclientset, _ := kubernetes.NewForConfig(c)
 | 
			
		||||
//   aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
 | 
			
		||||
//   _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme)
 | 
			
		||||
//
 | 
			
		||||
// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types
 | 
			
		||||
// correctly.
 | 
			
		||||
func AddToScheme(scheme *runtime.Scheme) {
 | 
			
		||||
	acidv1.AddToScheme(scheme)
 | 
			
		||||
var AddToScheme = localSchemeBuilder.AddToScheme
 | 
			
		||||
 | 
			
		||||
func init() {
 | 
			
		||||
	v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"})
 | 
			
		||||
	utilruntime.Must(AddToScheme(Scheme))
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -27,7 +27,6 @@ package v1
 | 
			
		|||
import (
 | 
			
		||||
	v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	"github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme"
 | 
			
		||||
	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
 | 
			
		||||
	rest "k8s.io/client-go/rest"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +81,7 @@ func setConfigDefaults(config *rest.Config) error {
 | 
			
		|||
	gv := v1.SchemeGroupVersion
 | 
			
		||||
	config.GroupVersion = &gv
 | 
			
		||||
	config.APIPath = "/apis"
 | 
			
		||||
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
 | 
			
		||||
	config.NegotiatedSerializer = scheme.Codecs.WithoutConversion()
 | 
			
		||||
 | 
			
		||||
	if config.UserAgent == "" {
 | 
			
		||||
		config.UserAgent = rest.DefaultKubernetesUserAgent()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -137,7 +137,7 @@ func (c *FakePostgresqls) DeleteCollection(options *v1.DeleteOptions, listOption
 | 
			
		|||
// Patch applies the patch and returns the patched postgresql.
 | 
			
		||||
func (c *FakePostgresqls) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *acidzalandov1.Postgresql, err error) {
 | 
			
		||||
	obj, err := c.Fake.
 | 
			
		||||
		Invokes(testing.NewPatchSubresourceAction(postgresqlsResource, c.ns, name, data, subresources...), &acidzalandov1.Postgresql{})
 | 
			
		||||
		Invokes(testing.NewPatchSubresourceAction(postgresqlsResource, c.ns, name, pt, data, subresources...), &acidzalandov1.Postgresql{})
 | 
			
		||||
 | 
			
		||||
	if obj == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,8 @@ SOFTWARE.
 | 
			
		|||
package v1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	v1 "github.com/zalando/postgres-operator/pkg/apis/acid.zalan.do/v1"
 | 
			
		||||
	scheme "github.com/zalando/postgres-operator/pkg/generated/clientset/versioned/scheme"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
| 
						 | 
				
			
			@ -82,11 +84,16 @@ func (c *postgresqls) Get(name string, options metav1.GetOptions) (result *v1.Po
 | 
			
		|||
 | 
			
		||||
// List takes label and field selectors, and returns the list of Postgresqls that match those selectors.
 | 
			
		||||
func (c *postgresqls) List(opts metav1.ListOptions) (result *v1.PostgresqlList, err error) {
 | 
			
		||||
	var timeout time.Duration
 | 
			
		||||
	if opts.TimeoutSeconds != nil {
 | 
			
		||||
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	result = &v1.PostgresqlList{}
 | 
			
		||||
	err = c.client.Get().
 | 
			
		||||
		Namespace(c.ns).
 | 
			
		||||
		Resource("postgresqls").
 | 
			
		||||
		VersionedParams(&opts, scheme.ParameterCodec).
 | 
			
		||||
		Timeout(timeout).
 | 
			
		||||
		Do().
 | 
			
		||||
		Into(result)
 | 
			
		||||
	return
 | 
			
		||||
| 
						 | 
				
			
			@ -94,11 +101,16 @@ func (c *postgresqls) List(opts metav1.ListOptions) (result *v1.PostgresqlList,
 | 
			
		|||
 | 
			
		||||
// Watch returns a watch.Interface that watches the requested postgresqls.
 | 
			
		||||
func (c *postgresqls) Watch(opts metav1.ListOptions) (watch.Interface, error) {
 | 
			
		||||
	var timeout time.Duration
 | 
			
		||||
	if opts.TimeoutSeconds != nil {
 | 
			
		||||
		timeout = time.Duration(*opts.TimeoutSeconds) * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	opts.Watch = true
 | 
			
		||||
	return c.client.Get().
 | 
			
		||||
		Namespace(c.ns).
 | 
			
		||||
		Resource("postgresqls").
 | 
			
		||||
		VersionedParams(&opts, scheme.ParameterCodec).
 | 
			
		||||
		Timeout(timeout).
 | 
			
		||||
		Watch()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -156,10 +168,15 @@ func (c *postgresqls) Delete(name string, options *metav1.DeleteOptions) error {
 | 
			
		|||
 | 
			
		||||
// DeleteCollection deletes a collection of objects.
 | 
			
		||||
func (c *postgresqls) DeleteCollection(options *metav1.DeleteOptions, listOptions metav1.ListOptions) error {
 | 
			
		||||
	var timeout time.Duration
 | 
			
		||||
	if listOptions.TimeoutSeconds != nil {
 | 
			
		||||
		timeout = time.Duration(*listOptions.TimeoutSeconds) * time.Second
 | 
			
		||||
	}
 | 
			
		||||
	return c.client.Delete().
 | 
			
		||||
		Namespace(c.ns).
 | 
			
		||||
		Resource("postgresqls").
 | 
			
		||||
		VersionedParams(&listOptions, scheme.ParameterCodec).
 | 
			
		||||
		Timeout(timeout).
 | 
			
		||||
		Body(options).
 | 
			
		||||
		Do().
 | 
			
		||||
		Error()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -33,6 +33,7 @@ import (
 | 
			
		|||
	cache "k8s.io/client-go/tools/cache"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer.
 | 
			
		||||
type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer
 | 
			
		||||
 | 
			
		||||
// SharedInformerFactory a small interface to allow for adding an informer without an import cycle
 | 
			
		||||
| 
						 | 
				
			
			@ -41,4 +42,5 @@ type SharedInformerFactory interface {
 | 
			
		|||
	InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TweakListOptionsFunc is a function that transforms a v1.ListOptions.
 | 
			
		||||
type TweakListOptionsFunc func(*v1.ListOptions)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,6 +16,7 @@ type CRD struct {
 | 
			
		|||
	ReadyWaitTimeout    time.Duration `name:"ready_wait_timeout" default:"30s"`
 | 
			
		||||
	ResyncPeriod        time.Duration `name:"resync_period" default:"30m"`
 | 
			
		||||
	RepairPeriod        time.Duration `name:"repair_period" default:"5m"`
 | 
			
		||||
	EnableCRDValidation *bool         `name:"enable_crd_validation" default:"true"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Resources describes kubernetes resource specific configuration parameters
 | 
			
		||||
| 
						 | 
				
			
			@ -68,11 +69,15 @@ type Scalyr struct {
 | 
			
		|||
	ScalyrMemoryLimit   string `name:"scalyr_memory_limit" default:"1Gi"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LogicalBackup
 | 
			
		||||
// LogicalBackup defines configuration for logical backup
 | 
			
		||||
type LogicalBackup struct {
 | 
			
		||||
	LogicalBackupSchedule          string `name:"logical_backup_schedule" default:"30 00 * * *"`
 | 
			
		||||
	LogicalBackupDockerImage       string `name:"logical_backup_docker_image" default:"registry.opensource.zalan.do/acid/logical-backup"`
 | 
			
		||||
	LogicalBackupS3Bucket          string `name:"logical_backup_s3_bucket" default:""`
 | 
			
		||||
	LogicalBackupS3Endpoint        string `name:"logical_backup_s3_endpoint" default:""`
 | 
			
		||||
	LogicalBackupS3AccessKeyID     string `name:"logical_backup_s3_access_key_id" default:""`
 | 
			
		||||
	LogicalBackupS3SecretAccessKey string `name:"logical_backup_s3_secret_access_key" default:""`
 | 
			
		||||
	LogicalBackupS3SSE             string `name:"logical_backup_s3_sse" default:"AES256"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Config describes operator config
 | 
			
		||||
| 
						 | 
				
			
			@ -85,7 +90,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'
 | 
			
		||||
	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-11:1.5-p9"`
 | 
			
		||||
	DockerImage      string            `name:"docker_image" default:"registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16"`
 | 
			
		||||
	Sidecars         map[string]string `name:"sidecar_docker_images"`
 | 
			
		||||
	// default name `operator` enables backward compatibility with the older ServiceAccountName field
 | 
			
		||||
	PodServiceAccountName string `name:"pod_service_account_name" default:"operator"`
 | 
			
		||||
| 
						 | 
				
			
			@ -109,6 +114,7 @@ type Config struct {
 | 
			
		|||
	EnableMasterLoadBalancer               bool              `name:"enable_master_load_balancer" default:"true"`
 | 
			
		||||
	EnableReplicaLoadBalancer              bool              `name:"enable_replica_load_balancer" default:"false"`
 | 
			
		||||
	CustomServiceAnnotations               map[string]string `name:"custom_service_annotations"`
 | 
			
		||||
	CustomPodAnnotations                   map[string]string `name:"custom_pod_annotations"`
 | 
			
		||||
	EnablePodAntiAffinity                  bool              `name:"enable_pod_antiaffinity" default:"false"`
 | 
			
		||||
	PodAntiAffinityTopologyKey             string            `name:"pod_antiaffinity_topology_key" default:"kubernetes.io/hostname"`
 | 
			
		||||
	// deprecated and kept for backward compatibility
 | 
			
		||||
| 
						 | 
				
			
			@ -117,6 +123,8 @@ type Config struct {
 | 
			
		|||
	ReplicaDNSNameFormat      StringTemplate    `name:"replica_dns_name_format" default:"{cluster}-repl.{team}.{hostedzone}"`
 | 
			
		||||
	PDBNameFormat             StringTemplate    `name:"pdb_name_format" default:"postgres-{cluster}-pdb"`
 | 
			
		||||
	EnablePodDisruptionBudget *bool             `name:"enable_pod_disruption_budget" default:"true"`
 | 
			
		||||
	EnableInitContainers      *bool             `name:"enable_init_containers" default:"true"`
 | 
			
		||||
	EnableSidecars            *bool             `name:"enable_sidecars" default:"true"`
 | 
			
		||||
	Workers                   uint32            `name:"workers" default:"4"`
 | 
			
		||||
	APIPort                   int               `name:"api_port" default:"8080"`
 | 
			
		||||
	RingLogLines              int               `name:"ring_log_lines" default:"100"`
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,8 +7,6 @@ const (
 | 
			
		|||
	PostgresContainerName = "postgres"
 | 
			
		||||
	PostgresContainerIdx  = 0
 | 
			
		||||
	K8sAPIPath            = "/apis"
 | 
			
		||||
	StatefulsetDeletionInterval = 1 * time.Second
 | 
			
		||||
	StatefulsetDeletionTimeout  = 30 * time.Second
 | 
			
		||||
 | 
			
		||||
	QueueResyncPeriodPod  = 5 * time.Minute
 | 
			
		||||
	QueueResyncPeriodTPR  = 5 * time.Minute
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,8 +16,8 @@ import (
 | 
			
		|||
	apiextbeta1 "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1"
 | 
			
		||||
	apierrors "k8s.io/apimachinery/pkg/api/errors"
 | 
			
		||||
	"k8s.io/client-go/kubernetes"
 | 
			
		||||
	"k8s.io/client-go/kubernetes/typed/apps/v1beta1"
 | 
			
		||||
	v1core "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
			
		||||
	appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1"
 | 
			
		||||
	corev1 "k8s.io/client-go/kubernetes/typed/core/v1"
 | 
			
		||||
	policyv1beta1 "k8s.io/client-go/kubernetes/typed/policy/v1beta1"
 | 
			
		||||
	rbacv1beta1 "k8s.io/client-go/kubernetes/typed/rbac/v1beta1"
 | 
			
		||||
	"k8s.io/client-go/rest"
 | 
			
		||||
| 
						 | 
				
			
			@ -29,17 +29,17 @@ import (
 | 
			
		|||
 | 
			
		||||
// KubernetesClient describes getters for Kubernetes objects
 | 
			
		||||
type KubernetesClient struct {
 | 
			
		||||
	v1core.SecretsGetter
 | 
			
		||||
	v1core.ServicesGetter
 | 
			
		||||
	v1core.EndpointsGetter
 | 
			
		||||
	v1core.PodsGetter
 | 
			
		||||
	v1core.PersistentVolumesGetter
 | 
			
		||||
	v1core.PersistentVolumeClaimsGetter
 | 
			
		||||
	v1core.ConfigMapsGetter
 | 
			
		||||
	v1core.NodesGetter
 | 
			
		||||
	v1core.NamespacesGetter
 | 
			
		||||
	v1core.ServiceAccountsGetter
 | 
			
		||||
	v1beta1.StatefulSetsGetter
 | 
			
		||||
	corev1.SecretsGetter
 | 
			
		||||
	corev1.ServicesGetter
 | 
			
		||||
	corev1.EndpointsGetter
 | 
			
		||||
	corev1.PodsGetter
 | 
			
		||||
	corev1.PersistentVolumesGetter
 | 
			
		||||
	corev1.PersistentVolumeClaimsGetter
 | 
			
		||||
	corev1.ConfigMapsGetter
 | 
			
		||||
	corev1.NodesGetter
 | 
			
		||||
	corev1.NamespacesGetter
 | 
			
		||||
	corev1.ServiceAccountsGetter
 | 
			
		||||
	appsv1.StatefulSetsGetter
 | 
			
		||||
	rbacv1beta1.RoleBindingsGetter
 | 
			
		||||
	policyv1beta1.PodDisruptionBudgetsGetter
 | 
			
		||||
	apiextbeta1.CustomResourceDefinitionsGetter
 | 
			
		||||
| 
						 | 
				
			
			@ -50,14 +50,14 @@ type KubernetesClient struct {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
type mockSecret struct {
 | 
			
		||||
	v1core.SecretInterface
 | 
			
		||||
	corev1.SecretInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MockSecretGetter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type mockConfigMap struct {
 | 
			
		||||
	v1core.ConfigMapInterface
 | 
			
		||||
	corev1.ConfigMapInterface
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type MockConfigMapsGetter struct {
 | 
			
		||||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ func ResourceNotFound(err error) bool {
 | 
			
		|||
	return apierrors.IsNotFound(err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewFromConfig create Kubernets Interface using REST config
 | 
			
		||||
// NewFromConfig create Kubernetes Interface using REST config
 | 
			
		||||
func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) {
 | 
			
		||||
	kubeClient := KubernetesClient{}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,7 +101,7 @@ func NewFromConfig(cfg *rest.Config) (KubernetesClient, error) {
 | 
			
		|||
	kubeClient.PersistentVolumesGetter = client.CoreV1()
 | 
			
		||||
	kubeClient.NodesGetter = client.CoreV1()
 | 
			
		||||
	kubeClient.NamespacesGetter = client.CoreV1()
 | 
			
		||||
	kubeClient.StatefulSetsGetter = client.AppsV1beta1()
 | 
			
		||||
	kubeClient.StatefulSetsGetter = client.AppsV1()
 | 
			
		||||
	kubeClient.PodDisruptionBudgetsGetter = client.PolicyV1beta1()
 | 
			
		||||
	kubeClient.RESTClient = client.CoreV1().RESTClient()
 | 
			
		||||
	kubeClient.RoleBindingsGetter = client.RbacV1beta1()
 | 
			
		||||
| 
						 | 
				
			
			@ -215,12 +215,12 @@ func (c *mockConfigMap) Get(name string, options metav1.GetOptions) (*v1.ConfigM
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Secrets to be mocked
 | 
			
		||||
func (c *MockSecretGetter) Secrets(namespace string) v1core.SecretInterface {
 | 
			
		||||
func (c *MockSecretGetter) Secrets(namespace string) corev1.SecretInterface {
 | 
			
		||||
	return &mockSecret{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ConfigMaps to be mocked
 | 
			
		||||
func (c *MockConfigMapsGetter) ConfigMaps(namespace string) v1core.ConfigMapInterface {
 | 
			
		||||
func (c *MockConfigMapsGetter) ConfigMaps(namespace string) corev1.ConfigMapInterface {
 | 
			
		||||
	return &mockConfigMap{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,7 +5,9 @@ import (
 | 
			
		|||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
| 
						 | 
				
			
			@ -43,8 +45,19 @@ func New(logger *logrus.Entry) *Patroni {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func apiURL(masterPod *v1.Pod) string {
 | 
			
		||||
	return fmt.Sprintf("http://%s:%d", masterPod.Status.PodIP, apiPort)
 | 
			
		||||
func apiURL(masterPod *v1.Pod) (string, error) {
 | 
			
		||||
	ip := net.ParseIP(masterPod.Status.PodIP)
 | 
			
		||||
	if ip == nil {
 | 
			
		||||
		return "", fmt.Errorf("%s is not a valid IP", masterPod.Status.PodIP)
 | 
			
		||||
	}
 | 
			
		||||
	// Sanity check PodIP
 | 
			
		||||
	if ip.To4() == nil {
 | 
			
		||||
		if ip.To16() == nil {
 | 
			
		||||
			// Shouldn't ever get here, but library states it's possible.
 | 
			
		||||
			return "", fmt.Errorf("%s is not a valid IPv4/IPv6 address", masterPod.Status.PodIP)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("http://%s", net.JoinHostPort(ip.String(), strconv.Itoa(apiPort))), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (p *Patroni) httpPostOrPatch(method string, url string, body *bytes.Buffer) (err error) {
 | 
			
		||||
| 
						 | 
				
			
			@ -88,7 +101,11 @@ func (p *Patroni) Switchover(master *v1.Pod, candidate string) error {
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("could not encode json: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return p.httpPostOrPatch(http.MethodPost, apiURL(master)+failoverPath, buf)
 | 
			
		||||
	apiURLString, err := apiURL(master)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return p.httpPostOrPatch(http.MethodPost, apiURLString+failoverPath, buf)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//TODO: add an option call /patroni to check if it is necessary to restart the server
 | 
			
		||||
| 
						 | 
				
			
			@ -100,5 +117,9 @@ func (p *Patroni) SetPostgresParameters(server *v1.Pod, parameters map[string]st
 | 
			
		|||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("could not encode json: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	return p.httpPostOrPatch(http.MethodPatch, apiURL(server)+configPath, buf)
 | 
			
		||||
	apiURLString, err := apiURL(server)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return p.httpPostOrPatch(http.MethodPatch, apiURLString+configPath, buf)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,74 @@
 | 
			
		|||
package patroni
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"k8s.io/api/core/v1"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newMockPod(ip string) *v1.Pod {
 | 
			
		||||
	return &v1.Pod{
 | 
			
		||||
		Status: v1.PodStatus{
 | 
			
		||||
			PodIP: ip,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestApiURL(t *testing.T) {
 | 
			
		||||
	var testTable = []struct {
 | 
			
		||||
		podIP            string
 | 
			
		||||
		expectedResponse string
 | 
			
		||||
		expectedError    error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			"127.0.0.1",
 | 
			
		||||
			fmt.Sprintf("http://127.0.0.1:%d", apiPort),
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"0000:0000:0000:0000:0000:0000:0000:0001",
 | 
			
		||||
			fmt.Sprintf("http://[::1]:%d", apiPort),
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"::1",
 | 
			
		||||
			fmt.Sprintf("http://[::1]:%d", apiPort),
 | 
			
		||||
			nil,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"",
 | 
			
		||||
			"",
 | 
			
		||||
			errors.New(" is not a valid IP"),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"foobar",
 | 
			
		||||
			"",
 | 
			
		||||
			errors.New("foobar is not a valid IP"),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			"127.0.1",
 | 
			
		||||
			"",
 | 
			
		||||
			errors.New("127.0.1 is not a valid IP"),
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			":::",
 | 
			
		||||
			"",
 | 
			
		||||
			errors.New("::: is not a valid IP"),
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	for _, test := range testTable {
 | 
			
		||||
		resp, err := apiURL(newMockPod(test.podIP))
 | 
			
		||||
		if resp != test.expectedResponse {
 | 
			
		||||
			t.Errorf("expected response %v does not match the actual %v", test.expectedResponse, resp)
 | 
			
		||||
		}
 | 
			
		||||
		if err != test.expectedError {
 | 
			
		||||
			if err == nil || test.expectedError == nil {
 | 
			
		||||
				t.Errorf("expected error '%v' does not match the actual error '%v'", test.expectedError, err)
 | 
			
		||||
			}
 | 
			
		||||
			if err != nil && test.expectedError != nil && err.Error() != test.expectedError.Error() {
 | 
			
		||||
				t.Errorf("expected error '%v' does not match the actual error '%v'", test.expectedError, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -24,7 +24,7 @@ var teamsAPItc = []struct {
 | 
			
		|||
	{`{
 | 
			
		||||
"dn": "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net",
 | 
			
		||||
"id": "acid",
 | 
			
		||||
"id_name": "ACID",
 | 
			
		||||
"id_name": "acid",
 | 
			
		||||
"team_id": "111222",
 | 
			
		||||
"type": "official",
 | 
			
		||||
"name": "Acid team name",
 | 
			
		||||
| 
						 | 
				
			
			@ -70,7 +70,7 @@ var teamsAPItc = []struct {
 | 
			
		|||
		&Team{
 | 
			
		||||
			Dn:           "cn=100100,ou=official,ou=foobar,dc=zalando,dc=net",
 | 
			
		||||
			ID:           "acid",
 | 
			
		||||
			TeamName:     "ACID",
 | 
			
		||||
			TeamName:     "acid",
 | 
			
		||||
			TeamID:       "111222",
 | 
			
		||||
			Type:         "official",
 | 
			
		||||
			FullName:     "Acid team name",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -141,17 +141,17 @@ func Coalesce(val, defaultVal string) string {
 | 
			
		|||
	return val
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestIsSmallerThanLimit : ...
 | 
			
		||||
func RequestIsSmallerThanLimit(requestStr, limitStr string) (bool, error) {
 | 
			
		||||
// IsSmallerQuantity : checks if first resource is of a smaller quantity than the second
 | 
			
		||||
func IsSmallerQuantity(requestStr, limitStr string) (bool, error) {
 | 
			
		||||
 | 
			
		||||
	request, err := resource.ParseQuantity(requestStr)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return false, fmt.Errorf("could not parse memory request %v : %v", requestStr, err)
 | 
			
		||||
		return false, fmt.Errorf("could not parse request %v : %v", requestStr, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	limit, err2 := resource.ParseQuantity(limitStr)
 | 
			
		||||
	if err2 != nil {
 | 
			
		||||
		return false, fmt.Errorf("could not parse memory limit %v : %v", limitStr, err2)
 | 
			
		||||
		return false, fmt.Errorf("could not parse limit %v : %v", limitStr, err2)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return request.Cmp(limit) == -1, nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,7 +69,7 @@ var substringMatch = []struct {
 | 
			
		|||
	{regexp.MustCompile(`aaaa (\d+) bbbb`), "aaaa 123 bbbb", nil},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var requestIsSmallerThanLimitTests = []struct {
 | 
			
		||||
var requestIsSmallerQuantityTests = []struct {
 | 
			
		||||
	request string
 | 
			
		||||
	limit   string
 | 
			
		||||
	out     bool
 | 
			
		||||
| 
						 | 
				
			
			@ -155,14 +155,14 @@ func TestMapContains(t *testing.T) {
 | 
			
		|||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRequestIsSmallerThanLimit(t *testing.T) {
 | 
			
		||||
	for _, tt := range requestIsSmallerThanLimitTests {
 | 
			
		||||
		res, err := RequestIsSmallerThanLimit(tt.request, tt.limit)
 | 
			
		||||
func TestIsSmallerQuantity(t *testing.T) {
 | 
			
		||||
	for _, tt := range requestIsSmallerQuantityTests {
 | 
			
		||||
		res, err := IsSmallerQuantity(tt.request, tt.limit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			t.Errorf("RequestIsSmallerThanLimit returned unexpected error: %#v", err)
 | 
			
		||||
			t.Errorf("IsSmallerQuantity returned unexpected error: %#v", err)
 | 
			
		||||
		}
 | 
			
		||||
		if res != tt.out {
 | 
			
		||||
			t.Errorf("RequestIsSmallerThanLimit expected: %#v, got: %#v", tt.out, res)
 | 
			
		||||
			t.Errorf("IsSmallerQuantity expected: %#v, got: %#v", tt.out, res)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,7 +98,7 @@ function build_operator_binary(){
 | 
			
		|||
 | 
			
		||||
    # redirecting stderr greatly reduces non-informative output during normal builds
 | 
			
		||||
    echo "Build operator binary (stderr redirected to /dev/null)..."
 | 
			
		||||
    make clean tools deps local test > /dev/null 2>&1
 | 
			
		||||
    make clean deps local test > /dev/null 2>&1
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,7 +82,7 @@ def request_delete(cluster, path, **kwargs):
 | 
			
		|||
def resource_api_version(resource_type):
 | 
			
		||||
    return {
 | 
			
		||||
        'postgresqls': 'apis/acid.zalan.do/v1',
 | 
			
		||||
        'statefulsets': 'apis/apps/v1beta1',
 | 
			
		||||
        'statefulsets': 'apis/apps/v1',
 | 
			
		||||
    }.get(resource_type, 'api/v1')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue