make minimum limits boundaries configurable
This commit is contained in:
		
							parent
							
								
									fddaf0fb73
								
							
						
					
					
						commit
						eb24d729f2
					
				| 
						 | 
				
			
			@ -179,6 +179,12 @@ spec:
 | 
			
		|||
                default_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                min_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                min_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
            timeouts:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -115,13 +115,17 @@ configKubernetes:
 | 
			
		|||
# configure resource requests for the Postgres pods
 | 
			
		||||
configPostgresPodResources:
 | 
			
		||||
  # CPU limits for the postgres containers
 | 
			
		||||
  default_cpu_limit: "3"
 | 
			
		||||
  # cpu request value for the postgres containers
 | 
			
		||||
  default_cpu_limit: "1"
 | 
			
		||||
  # CPU request value for the postgres containers
 | 
			
		||||
  default_cpu_request: 100m
 | 
			
		||||
  # memory limits for the postgres containers
 | 
			
		||||
  default_memory_limit: 1Gi
 | 
			
		||||
  default_memory_limit: 500Mi
 | 
			
		||||
  # memory request value for the postgres containers
 | 
			
		||||
  default_memory_request: 100Mi
 | 
			
		||||
  # hard CPU minimum required to properly run a Postgres cluster
 | 
			
		||||
  min_cpu_limit: 250m
 | 
			
		||||
  # hard memory minimum required to properly run a Postgres cluster
 | 
			
		||||
  min_memory_limit: 250Mi
 | 
			
		||||
 | 
			
		||||
# timeouts related to some operator actions
 | 
			
		||||
configTimeouts:
 | 
			
		||||
| 
						 | 
				
			
			@ -251,7 +255,7 @@ configScalyr:
 | 
			
		|||
  # CPU rquest value for the Scalyr sidecar
 | 
			
		||||
  scalyr_cpu_request: 100m
 | 
			
		||||
  # Memory limit value for the Scalyr sidecar
 | 
			
		||||
  scalyr_memory_limit: 1Gi
 | 
			
		||||
  scalyr_memory_limit: 500Mi
 | 
			
		||||
  # Memory request value for the Scalyr sidecar
 | 
			
		||||
  scalyr_memory_request: 50Mi
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -272,13 +276,13 @@ serviceAccount:
 | 
			
		|||
 | 
			
		||||
priorityClassName: ""
 | 
			
		||||
 | 
			
		||||
resources: {}
 | 
			
		||||
  # limits:
 | 
			
		||||
  #   cpu: 100m
 | 
			
		||||
  #   memory: 300Mi
 | 
			
		||||
  # requests:
 | 
			
		||||
  #   cpu: 100m
 | 
			
		||||
  #   memory: 300Mi
 | 
			
		||||
resources:
 | 
			
		||||
  limits:
 | 
			
		||||
    cpu: 500m
 | 
			
		||||
    memory: 500Mi
 | 
			
		||||
  requests:
 | 
			
		||||
   cpu: 100m
 | 
			
		||||
   memory: 250Mi
 | 
			
		||||
 | 
			
		||||
# Affinity for pod assignment
 | 
			
		||||
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,13 +108,17 @@ configKubernetes:
 | 
			
		|||
# configure resource requests for the Postgres pods
 | 
			
		||||
configPostgresPodResources:
 | 
			
		||||
  # CPU limits for the postgres containers
 | 
			
		||||
  default_cpu_limit: "3"
 | 
			
		||||
  # cpu request value for the postgres containers
 | 
			
		||||
  default_cpu_limit: "1"
 | 
			
		||||
  # CPU request value for the postgres containers
 | 
			
		||||
  default_cpu_request: 100m
 | 
			
		||||
  # memory limits for the postgres containers
 | 
			
		||||
  default_memory_limit: 1Gi
 | 
			
		||||
  default_memory_limit: 500Mi
 | 
			
		||||
  # memory request value for the postgres containers
 | 
			
		||||
  default_memory_request: 100Mi
 | 
			
		||||
  # hard CPU minimum required to properly run a Postgres cluster
 | 
			
		||||
  min_cpu_limit: 250m
 | 
			
		||||
  # hard memory minimum required to properly run a Postgres cluster
 | 
			
		||||
  min_memory_limit: 250Mi
 | 
			
		||||
 | 
			
		||||
# timeouts related to some operator actions
 | 
			
		||||
configTimeouts:
 | 
			
		||||
| 
						 | 
				
			
			@ -248,13 +252,13 @@ serviceAccount:
 | 
			
		|||
 | 
			
		||||
priorityClassName: ""
 | 
			
		||||
 | 
			
		||||
resources: {}
 | 
			
		||||
  # limits:
 | 
			
		||||
  #   cpu: 100m
 | 
			
		||||
  #   memory: 300Mi
 | 
			
		||||
  # requests:
 | 
			
		||||
  #   cpu: 100m
 | 
			
		||||
  #   memory: 300Mi
 | 
			
		||||
resources:
 | 
			
		||||
  limits:
 | 
			
		||||
    cpu: 500m
 | 
			
		||||
    memory: 500Mi
 | 
			
		||||
  requests:
 | 
			
		||||
   cpu: 100m
 | 
			
		||||
   memory: 250Mi
 | 
			
		||||
 | 
			
		||||
# Affinity for pod assignment
 | 
			
		||||
# Ref: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/#affinity-and-anti-affinity
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -318,11 +318,19 @@ CRD-based configuration.
 | 
			
		|||
 | 
			
		||||
* **default_cpu_limit**
 | 
			
		||||
  CPU limits for the Postgres containers, unless overridden by cluster-specific
 | 
			
		||||
  settings. The default is `3`.
 | 
			
		||||
  settings. The default is `1`.
 | 
			
		||||
 | 
			
		||||
* **default_memory_limit**
 | 
			
		||||
  memory limits for the Postgres containers, unless overridden by cluster-specific
 | 
			
		||||
  settings. The default is `1Gi`.
 | 
			
		||||
  settings. The default is `500Mi`.
 | 
			
		||||
 | 
			
		||||
* **min_cpu_limit**
 | 
			
		||||
  hard CPU minimum what we consider to be required to properly run Postgres
 | 
			
		||||
  clusters with Patroni on Kubernetes. The default is `250m`.
 | 
			
		||||
 | 
			
		||||
* **min_memory_limit**
 | 
			
		||||
  hard memory minimum what we consider to be required to properly run Postgres
 | 
			
		||||
  clusters with Patroni on Kubernetes. The default is `250Mi`.
 | 
			
		||||
 | 
			
		||||
## Operator timeouts
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -579,4 +587,4 @@ scalyr sidecar. In the CRD-based configuration they are grouped under the
 | 
			
		|||
  CPU limit value for the Scalyr sidecar. The default is `1`.
 | 
			
		||||
 | 
			
		||||
* **scalyr_memory_limit**
 | 
			
		||||
  Memory limit value for the Scalyr sidecar. The default is `1Gi`.
 | 
			
		||||
  Memory limit value for the Scalyr sidecar. The default is `500Mi`.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										10
									
								
								docs/user.md
								
								
								
								
							
							
						
						
									
										10
									
								
								docs/user.md
								
								
								
								
							| 
						 | 
				
			
			@ -232,11 +232,11 @@ spec:
 | 
			
		|||
      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).
 | 
			
		||||
The minimum limits to properly run the `postgresql` resource are configured to
 | 
			
		||||
`250m` for `cpu` and `250Mi` for `memory`. If a lower value is set in the
 | 
			
		||||
manifest the operator will raise the limits to the configured minimum values.
 | 
			
		||||
If no resources are defined in the manifest they will be obtained from the
 | 
			
		||||
configured [default requests](reference/operator_parameters.md#kubernetes-resource-requests).
 | 
			
		||||
 | 
			
		||||
## Use taints and tolerations for dedicated PostgreSQL nodes
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -58,6 +58,57 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
        k8s.create_with_kubectl("manifests/minimal-postgres-manifest.yaml")
 | 
			
		||||
        k8s.wait_for_pod_start('spilo-role=master')
 | 
			
		||||
 | 
			
		||||
    @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
 | 
			
		||||
    def test_min_resource_limits(self):
 | 
			
		||||
        '''
 | 
			
		||||
        Lower resource limits below configured minimum and let operator fix it
 | 
			
		||||
        '''
 | 
			
		||||
        k8s = self.k8s
 | 
			
		||||
        cluster_label = 'version=acid-minimal-cluster'
 | 
			
		||||
        _, failover_targets = k8s.get_pg_nodes(cluster_label)
 | 
			
		||||
 | 
			
		||||
        # configure minimum boundaries for CPU and memory limits
 | 
			
		||||
        minCPULimit = '250m'
 | 
			
		||||
        minMemoryLimit = '250Mi'
 | 
			
		||||
        patch_min_resource_limits = {
 | 
			
		||||
            "data": {
 | 
			
		||||
                "min_cpu_limit": minCPULimit,
 | 
			
		||||
                "min_memory_limit": minMemoryLimit
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        k8s.update_config(patch_min_resource_limits)
 | 
			
		||||
 | 
			
		||||
        # lower resource limits below minimum
 | 
			
		||||
        pg_patch_resources = {
 | 
			
		||||
            "spec": {
 | 
			
		||||
                "resources": {
 | 
			
		||||
                    "requests": {
 | 
			
		||||
                        "cpu": "10m",
 | 
			
		||||
                        "memory": "50Mi"
 | 
			
		||||
                    },
 | 
			
		||||
                    "limits": {
 | 
			
		||||
                        "cpu": "80m",
 | 
			
		||||
                        "memory": "200Mi"
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        k8s.api.custom_objects_api.patch_namespaced_custom_object(
 | 
			
		||||
            "acid.zalan.do", "v1", "default", "postgresqls", "acid-minimal-cluster", pg_patch_resources)
 | 
			
		||||
        k8s.wait_for_master_failover(failover_targets)
 | 
			
		||||
 | 
			
		||||
        pods = self.api.core_v1.list_namespaced_pod(
 | 
			
		||||
            'default', label_selector='spilo-role=master,' + cluster_label).items
 | 
			
		||||
        self.assert_master_is_unique()
 | 
			
		||||
        masterPod = pods[0]
 | 
			
		||||
 | 
			
		||||
        self.assertEqual(masterPod.spec.resources.limits.cpu, minCPULimit,
 | 
			
		||||
                         "Expected CPU limit {}, found {}"
 | 
			
		||||
                         .format(minCPULimit, masterPod.spec.resources.limits.cpu))
 | 
			
		||||
        self.assertEqual(masterPod.spec.resources.limits.memory, "250Mi",
 | 
			
		||||
                         "Expected memory limit {}, found {}"
 | 
			
		||||
                         .format(minMemoryLimit, masterPod.spec.resources.limits.memory))
 | 
			
		||||
 | 
			
		||||
    @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
 | 
			
		||||
    def test_multi_namespace_support(self):
 | 
			
		||||
        '''
 | 
			
		||||
| 
						 | 
				
			
			@ -76,9 +127,9 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
 | 
			
		||||
    @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
 | 
			
		||||
    def test_scaling(self):
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
           Scale up from 2 to 3 and back to 2 pods by updating the Postgres manifest at runtime.
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        k8s = self.k8s
 | 
			
		||||
        labels = "version=acid-minimal-cluster"
 | 
			
		||||
| 
						 | 
				
			
			@ -93,9 +144,9 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
 | 
			
		||||
    @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
 | 
			
		||||
    def test_taint_based_eviction(self):
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
           Add taint "postgres=:NoExecute" to node with master. This must cause a failover.
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
        k8s = self.k8s
 | 
			
		||||
        cluster_label = 'version=acid-minimal-cluster'
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -145,7 +196,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
 | 
			
		||||
    @timeout_decorator.timeout(TEST_TIMEOUT_SEC)
 | 
			
		||||
    def test_logical_backup_cron_job(self):
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
        Ensure we can (a) create the cron job at user request for a specific PG cluster
 | 
			
		||||
                      (b) update the cluster-wide image for the logical backup pod
 | 
			
		||||
                      (c) delete the job at user request
 | 
			
		||||
| 
						 | 
				
			
			@ -153,7 +204,7 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
        Limitations:
 | 
			
		||||
        (a) Does not run the actual batch job because there is no S3 mock to upload backups to
 | 
			
		||||
        (b) Assumes 'acid-minimal-cluster' exists as defined in setUp
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        k8s = self.k8s
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -208,10 +259,10 @@ class EndToEndTestCase(unittest.TestCase):
 | 
			
		|||
                         "Expected 0 logical backup jobs, found {}".format(len(jobs)))
 | 
			
		||||
 | 
			
		||||
    def assert_master_is_unique(self, namespace='default', version="acid-minimal-cluster"):
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
           Check that there is a single pod in the k8s cluster with the label "spilo-role=master"
 | 
			
		||||
           To be called manually after operations that affect pods
 | 
			
		||||
        """
 | 
			
		||||
        '''
 | 
			
		||||
 | 
			
		||||
        k8s = self.k8s
 | 
			
		||||
        labels = 'spilo-role=master,version=' + version
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,8 +42,8 @@ spec:
 | 
			
		|||
      cpu: 10m
 | 
			
		||||
      memory: 100Mi
 | 
			
		||||
    limits:
 | 
			
		||||
      cpu: 300m
 | 
			
		||||
      memory: 300Mi
 | 
			
		||||
      cpu: 500m
 | 
			
		||||
      memory: 500Mi
 | 
			
		||||
  patroni:
 | 
			
		||||
    initdb:
 | 
			
		||||
      encoding: "UTF8"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,9 +15,9 @@ data:
 | 
			
		|||
  # custom_pod_annotations: "keya:valuea,keyb:valueb"
 | 
			
		||||
  db_hosted_zone: db.example.com
 | 
			
		||||
  debug_logging: "true"
 | 
			
		||||
  # default_cpu_limit: "3"
 | 
			
		||||
  # default_cpu_limit: "1"
 | 
			
		||||
  # default_cpu_request: 100m
 | 
			
		||||
  # default_memory_limit: 1Gi
 | 
			
		||||
  # default_memory_limit: 500Mi
 | 
			
		||||
  # default_memory_request: 100Mi
 | 
			
		||||
  docker_image: registry.opensource.zalan.do/acid/spilo-cdp-12:1.6-p16
 | 
			
		||||
  # enable_admin_role_for_users: "true"
 | 
			
		||||
| 
						 | 
				
			
			@ -48,6 +48,8 @@ data:
 | 
			
		|||
  # master_pod_move_timeout: 10m
 | 
			
		||||
  # max_instances: "-1"
 | 
			
		||||
  # min_instances: "-1"
 | 
			
		||||
  # min_cpu_limit: 250m
 | 
			
		||||
  # min_memory_limit: 250Mi
 | 
			
		||||
  # node_readiness_label: ""
 | 
			
		||||
  # oauth_token_secret_name: postgresql-operator
 | 
			
		||||
  # pam_configuration: |
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -155,6 +155,12 @@ spec:
 | 
			
		|||
                default_memory_request:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
                min_cpu_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+m|\d+(\.\d{1,3})?)$'
 | 
			
		||||
                min_memory_limit:
 | 
			
		||||
                  type: string
 | 
			
		||||
                  pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$'
 | 
			
		||||
            timeouts:
 | 
			
		||||
              type: object
 | 
			
		||||
              properties:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -19,10 +19,10 @@ spec:
 | 
			
		|||
        imagePullPolicy: IfNotPresent
 | 
			
		||||
        resources:
 | 
			
		||||
          requests:
 | 
			
		||||
            cpu: 500m
 | 
			
		||||
            cpu: 100m
 | 
			
		||||
            memory: 250Mi
 | 
			
		||||
          limits:
 | 
			
		||||
            cpu: 2000m
 | 
			
		||||
            cpu: 500m
 | 
			
		||||
            memory: 500Mi
 | 
			
		||||
        securityContext:
 | 
			
		||||
          runAsUser: 1000
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,10 +54,12 @@ configuration:
 | 
			
		|||
    # toleration: {}
 | 
			
		||||
    # watched_namespace: ""
 | 
			
		||||
  postgres_pod_resources:
 | 
			
		||||
    default_cpu_limit: "3"
 | 
			
		||||
    default_cpu_limit: "1"
 | 
			
		||||
    default_cpu_request: 100m
 | 
			
		||||
    default_memory_limit: 1Gi
 | 
			
		||||
    default_memory_limit: 500Mi
 | 
			
		||||
    default_memory_request: 100Mi
 | 
			
		||||
    # min_cpu_limit: 250m
 | 
			
		||||
    # min_memory_limit: 250Mi
 | 
			
		||||
  timeouts:
 | 
			
		||||
    pod_label_wait_timeout: 10m
 | 
			
		||||
    pod_deletion_wait_timeout: 10m
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +117,6 @@ configuration:
 | 
			
		|||
    scalyr_cpu_limit: "1"
 | 
			
		||||
    scalyr_cpu_request: 100m
 | 
			
		||||
    # scalyr_image: ""
 | 
			
		||||
    scalyr_memory_limit: 1Gi
 | 
			
		||||
    scalyr_memory_limit: 500Mi
 | 
			
		||||
    scalyr_memory_request: 50Mi
 | 
			
		||||
    # scalyr_server_url: ""
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -810,6 +810,14 @@ var OperatorConfigCRDResourceValidation = apiextv1beta1.CustomResourceValidation
 | 
			
		|||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"min_cpu_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+m|\\d+(\\.\\d{1,3})?)$",
 | 
			
		||||
							},
 | 
			
		||||
							"min_memory_limit": {
 | 
			
		||||
								Type:    "string",
 | 
			
		||||
								Pattern: "^(\\d+(e\\d+)?|\\d+(\\.\\d+)?(e\\d+)?[EPTGMK]i?)$",
 | 
			
		||||
							},
 | 
			
		||||
						},
 | 
			
		||||
					},
 | 
			
		||||
					"timeouts": {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -79,6 +79,8 @@ type PostgresPodResourcesDefaults struct {
 | 
			
		|||
	DefaultMemoryRequest string `json:"default_memory_request,omitempty"`
 | 
			
		||||
	DefaultCPULimit      string `json:"default_cpu_limit,omitempty"`
 | 
			
		||||
	DefaultMemoryLimit   string `json:"default_memory_limit,omitempty"`
 | 
			
		||||
	MinCPULimit          string `json:"min_cpu_limit,omitempty"`
 | 
			
		||||
	MinMemoryLimit       string `json:"min_memory_limit,omitempty"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// OperatorTimeouts defines the timeout of ResourceCheck, PodWait, ReadyWait
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -228,7 +228,7 @@ 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)
 | 
			
		||||
		return fmt.Errorf("could not validate postgresql resources: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, role := range []PostgresRole{Master, Replica} {
 | 
			
		||||
| 
						 | 
				
			
			@ -497,36 +497,36 @@ 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
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	// setting limits too low can cause unnecessary evictions / OOM kills
 | 
			
		||||
	minCPULimit := c.OpConfig.MinCPULimit
 | 
			
		||||
	minMemoryLimit := c.OpConfig.MinMemoryLimit
 | 
			
		||||
 | 
			
		||||
	cpuLimit := spec.Resources.ResourceLimits.CPU
 | 
			
		||||
	if cpuLimit != "" {
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(cpuLimit, cpuMinLimit)
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(cpuLimit, minCPULimit)
 | 
			
		||||
		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)
 | 
			
		||||
			c.logger.Warningf("defined CPU limit %s is below required minimum %s and will be set to it", cpuLimit, minCPULimit)
 | 
			
		||||
			spec.Resources.ResourceLimits.CPU = minCPULimit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memoryLimit := spec.Resources.ResourceLimits.Memory
 | 
			
		||||
	if memoryLimit != "" {
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(memoryLimit, memoryMinLimit)
 | 
			
		||||
		isSmaller, err = util.IsSmallerQuantity(memoryLimit, minMemoryLimit)
 | 
			
		||||
		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)
 | 
			
		||||
			c.logger.Warningf("defined memory limit %s is below required minimum %s and will be set to it", memoryLimit, minMemoryLimit)
 | 
			
		||||
			spec.Resources.ResourceLimits.Memory = minMemoryLimit
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -543,7 +543,6 @@ 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)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -555,22 +554,6 @@ 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
 | 
			
		||||
| 
						 | 
				
			
			@ -616,6 +599,12 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
			
		|||
 | 
			
		||||
	// Statefulset
 | 
			
		||||
	func() {
 | 
			
		||||
		if err := c.validateResources(&c.Spec); err != nil {
 | 
			
		||||
			c.logger.Errorf("could not sync resources: %v", err)
 | 
			
		||||
			updateFailed = true
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		oldSs, err := c.generateStatefulSet(&oldSpec.Spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.logger.Errorf("could not generate old statefulset spec: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			@ -623,6 +612,9 @@ func (c *Cluster) Update(oldSpec, newSpec *acidv1.Postgresql) error {
 | 
			
		|||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// update newSpec to for latter comparison with oldSpec
 | 
			
		||||
		c.validateResources(&newSpec.Spec)
 | 
			
		||||
 | 
			
		||||
		newSs, err := c.generateStatefulSet(&newSpec.Spec)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			c.logger.Errorf("could not generate new statefulset spec: %v", err)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -23,7 +23,6 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
 | 
			
		|||
	c.mu.Lock()
 | 
			
		||||
	defer c.mu.Unlock()
 | 
			
		||||
 | 
			
		||||
	oldStatus := c.Status
 | 
			
		||||
	c.setSpec(newSpec)
 | 
			
		||||
 | 
			
		||||
	defer func() {
 | 
			
		||||
| 
						 | 
				
			
			@ -35,16 +34,6 @@ 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
 | 
			
		||||
| 
						 | 
				
			
			@ -76,6 +65,11 @@ func (c *Cluster) Sync(newSpec *acidv1.Postgresql) error {
 | 
			
		|||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err = c.validateResources(&c.Spec); err != nil {
 | 
			
		||||
		err = fmt.Errorf("could not validate postgresql resources: %v", err)
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c.logger.Debugf("syncing statefulsets")
 | 
			
		||||
	if err = c.syncStatefulSet(); err != nil {
 | 
			
		||||
		if !k8sutil.ResourceAlreadyExists(err) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -75,6 +75,8 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
 | 
			
		|||
	result.DefaultMemoryRequest = fromCRD.PostgresPodResources.DefaultMemoryRequest
 | 
			
		||||
	result.DefaultCPULimit = fromCRD.PostgresPodResources.DefaultCPULimit
 | 
			
		||||
	result.DefaultMemoryLimit = fromCRD.PostgresPodResources.DefaultMemoryLimit
 | 
			
		||||
	result.MinCPULimit = fromCRD.PostgresPodResources.MinCPULimit
 | 
			
		||||
	result.MinMemoryLimit = fromCRD.PostgresPodResources.MinMemoryLimit
 | 
			
		||||
 | 
			
		||||
	// timeout config
 | 
			
		||||
	result.ResourceCheckInterval = time.Duration(fromCRD.Timeouts.ResourceCheckInterval)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,8 +37,10 @@ type Resources struct {
 | 
			
		|||
	PodToleration           map[string]string `name:"toleration" default:""`
 | 
			
		||||
	DefaultCPURequest       string            `name:"default_cpu_request" default:"100m"`
 | 
			
		||||
	DefaultMemoryRequest    string            `name:"default_memory_request" default:"100Mi"`
 | 
			
		||||
	DefaultCPULimit         string            `name:"default_cpu_limit" default:"3"`
 | 
			
		||||
	DefaultMemoryLimit      string            `name:"default_memory_limit" default:"1Gi"`
 | 
			
		||||
	DefaultCPULimit         string            `name:"default_cpu_limit" default:"1"`
 | 
			
		||||
	DefaultMemoryLimit      string            `name:"default_memory_limit" default:"500Mi"`
 | 
			
		||||
	MinCPULimit             string            `name:"min_cpu_limit" default:"250m"`
 | 
			
		||||
	MinMemoryLimit          string            `name:"min_memory_limit" default:"250Mi"`
 | 
			
		||||
	PodEnvironmentConfigMap string            `name:"pod_environment_configmap" default:""`
 | 
			
		||||
	NodeReadinessLabel      map[string]string `name:"node_readiness_label" default:""`
 | 
			
		||||
	MaxInstances            int32             `name:"max_instances" default:"-1"`
 | 
			
		||||
| 
						 | 
				
			
			@ -66,7 +68,7 @@ type Scalyr struct {
 | 
			
		|||
	ScalyrCPURequest    string `name:"scalyr_cpu_request" default:"100m"`
 | 
			
		||||
	ScalyrMemoryRequest string `name:"scalyr_memory_request" default:"50Mi"`
 | 
			
		||||
	ScalyrCPULimit      string `name:"scalyr_cpu_limit" default:"1"`
 | 
			
		||||
	ScalyrMemoryLimit   string `name:"scalyr_memory_limit" default:"1Gi"`
 | 
			
		||||
	ScalyrMemoryLimit   string `name:"scalyr_memory_limit" default:"500Mi"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LogicalBackup defines configuration for logical backup
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue