diff --git a/charts/postgres-operator/crds/operatorconfigurations.yaml b/charts/postgres-operator/crds/operatorconfigurations.yaml index f9258b597..b0d3966f6 100644 --- a/charts/postgres-operator/crds/operatorconfigurations.yaml +++ b/charts/postgres-operator/crds/operatorconfigurations.yaml @@ -365,19 +365,15 @@ spec: default_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "1" default_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "100m" default_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "500Mi" default_memory_request: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" max_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' @@ -387,11 +383,9 @@ spec: min_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "250m" min_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "250Mi" timeouts: type: object properties: @@ -672,19 +666,15 @@ spec: connection_pooler_default_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "1" connection_pooler_default_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "500m" connection_pooler_default_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" connection_pooler_default_memory_request: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" patroni: type: object properties: diff --git a/docs/reference/operator_parameters.md b/docs/reference/operator_parameters.md index 240e5f378..a453e8343 100644 --- a/docs/reference/operator_parameters.md +++ b/docs/reference/operator_parameters.md @@ -549,19 +549,19 @@ CRD-based configuration. * **default_cpu_request** CPU request value for the Postgres containers, unless overridden by - cluster-specific settings. The default is `100m`. + cluster-specific settings. Empty string or `0` disables the default. * **default_memory_request** memory request value for the Postgres containers, unless overridden by - cluster-specific settings. The default is `100Mi`. + cluster-specific settings. Empty string or `0` disables the default. * **default_cpu_limit** CPU limits for the Postgres containers, unless overridden by cluster-specific - settings. The default is `1`. + settings. Empty string or `0` disables the default. * **default_memory_limit** memory limits for the Postgres containers, unless overridden by cluster-specific - settings. The default is `500Mi`. + settings. Empty string or `0` disables the default. * **max_cpu_request** optional upper boundary for CPU request @@ -571,11 +571,11 @@ CRD-based configuration. * **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`. + clusters with Patroni on Kubernetes. * **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`. + clusters with Patroni on Kubernetes. ## Patroni options @@ -1026,5 +1026,4 @@ operator being able to provide some reasonable defaults. **connection_pooler_default_memory_reques** **connection_pooler_default_cpu_limit** **connection_pooler_default_memory_limit** - Default resource configuration for connection pooler deployment. The internal - default for memory request and limit is `100Mi`, for CPU it is `500m` and `1`. + Default resource configuration for connection pooler deployment. diff --git a/docs/user.md b/docs/user.md index 59b0729e0..2c5fea995 100644 --- a/docs/user.md +++ b/docs/user.md @@ -689,6 +689,9 @@ The minimum limits to properly run the `postgresql` resource are configured to 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). +If neither defaults nor minimum limits are configured the operator will not +specify any resources and it's up to K8s (or your own) admission hooks to +handle it. ### HugePages support diff --git a/manifests/configmap.yaml b/manifests/configmap.yaml index 918e22ac7..6a10ade2c 100644 --- a/manifests/configmap.yaml +++ b/manifests/configmap.yaml @@ -13,10 +13,10 @@ data: cluster_history_entries: "1000" cluster_labels: application:spilo cluster_name_label: cluster-name - # connection_pooler_default_cpu_limit: "1" - # connection_pooler_default_cpu_request: "500m" - # connection_pooler_default_memory_limit: 100Mi - # connection_pooler_default_memory_request: 100Mi + connection_pooler_default_cpu_limit: "1" + connection_pooler_default_cpu_request: "500m" + connection_pooler_default_memory_limit: 100Mi + connection_pooler_default_memory_request: 100Mi connection_pooler_image: "registry.opensource.zalan.do/acid/pgbouncer:master-32" # connection_pooler_max_db_connections: 60 # connection_pooler_mode: "transaction" @@ -28,10 +28,10 @@ data: # custom_pod_annotations: "keya:valuea,keyb:valueb" db_hosted_zone: db.example.com debug_logging: "true" - # default_cpu_limit: "1" - # default_cpu_request: 100m - # default_memory_limit: 500Mi - # default_memory_request: 100Mi + default_cpu_limit: "1" + default_cpu_request: 100m + default_memory_limit: 500Mi + default_memory_request: 100Mi # delete_annotation_date_key: delete-date # delete_annotation_name_key: delete-clustername docker_image: ghcr.io/zalando/spilo-15:3.0-p1 diff --git a/manifests/operatorconfiguration.crd.yaml b/manifests/operatorconfiguration.crd.yaml index dd34c3a85..73e667821 100644 --- a/manifests/operatorconfiguration.crd.yaml +++ b/manifests/operatorconfiguration.crd.yaml @@ -363,19 +363,15 @@ spec: default_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "1" default_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "100m" default_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "500Mi" default_memory_request: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" max_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' @@ -385,11 +381,9 @@ spec: min_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "250m" min_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "250Mi" timeouts: type: object properties: @@ -670,19 +664,15 @@ spec: connection_pooler_default_cpu_limit: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "1" connection_pooler_default_cpu_request: type: string pattern: '^(\d+m|\d+(\.\d{1,3})?)$' - default: "500m" connection_pooler_default_memory_limit: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" connection_pooler_default_memory_request: type: string pattern: '^(\d+(e\d+)?|\d+(\.\d+)?(e\d+)?[EPTGMK]i?)$' - default: "100Mi" patroni: type: object properties: diff --git a/pkg/apis/acid.zalan.do/v1/postgresql_type.go b/pkg/apis/acid.zalan.do/v1/postgresql_type.go index edce944f1..266c0c63c 100644 --- a/pkg/apis/acid.zalan.do/v1/postgresql_type.go +++ b/pkg/apis/acid.zalan.do/v1/postgresql_type.go @@ -153,10 +153,10 @@ type PostgresqlParam struct { // ResourceDescription describes CPU and memory resources defined for a cluster. type ResourceDescription struct { - CPU string `json:"cpu"` - Memory string `json:"memory"` - HugePages2Mi *string `json:"hugepages-2Mi"` - HugePages1Gi *string `json:"hugepages-1Gi"` + CPU *string `json:"cpu,omitempty"` + Memory *string `json:"memory,omitempty"` + HugePages2Mi *string `json:"hugepages-2Mi,omitempty"` + HugePages1Gi *string `json:"hugepages-1Gi,omitempty"` } // Resources describes requests and limits for the cluster resouces. @@ -260,7 +260,7 @@ type Stream struct { // StreamTable defines properties of outbox tables for FabricEventStreams type StreamTable struct { EventType string `json:"eventType"` - RecoveryEventType string `json:"recoveryEventType"` + RecoveryEventType string `json:"recoveryEventType,omitempty"` IdColumn *string `json:"idColumn,omitempty"` PayloadColumn *string `json:"payloadColumn,omitempty"` } diff --git a/pkg/apis/acid.zalan.do/v1/util_test.go b/pkg/apis/acid.zalan.do/v1/util_test.go index 71a629f31..2b1e6a6b2 100644 --- a/pkg/apis/acid.zalan.do/v1/util_test.go +++ b/pkg/apis/acid.zalan.do/v1/util_test.go @@ -26,6 +26,10 @@ var parseTimeTests = []struct { {"expect error as minute is out of range", "23:69", metav1.Now(), errors.New(`parsing time "23:69": minute out of range`)}, } +func stringToPointer(str string) *string { + return &str +} + var parseWeekdayTests = []struct { about string in string @@ -301,8 +305,8 @@ var unmarshalCluster = []struct { Slots: map[string]map[string]string{"permanent_logical_1": {"type": "logical", "database": "foo", "plugin": "pgoutput"}}, }, Resources: &Resources{ - ResourceRequests: ResourceDescription{CPU: "10m", Memory: "50Mi"}, - ResourceLimits: ResourceDescription{CPU: "300m", Memory: "3000Mi"}, + ResourceRequests: ResourceDescription{CPU: stringToPointer("10m"), Memory: stringToPointer("50Mi")}, + ResourceLimits: ResourceDescription{CPU: stringToPointer("300m"), Memory: stringToPointer("3000Mi")}, }, TeamID: "acid", diff --git a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go index 69ce4fc9a..ba887f882 100644 --- a/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go +++ b/pkg/apis/acid.zalan.do/v1/zz_generated.deepcopy.go @@ -1165,6 +1165,16 @@ func (in *PreparedSchema) DeepCopy() *PreparedSchema { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceDescription) DeepCopyInto(out *ResourceDescription) { *out = *in + if in.CPU != nil { + in, out := &in.CPU, &out.CPU + *out = new(string) + **out = **in + } + if in.Memory != nil { + in, out := &in.Memory, &out.Memory + *out = new(string) + **out = **in + } if in.HugePages2Mi != nil { in, out := &in.HugePages2Mi, &out.HugePages2Mi *out = new(string) diff --git a/pkg/cluster/cluster_test.go b/pkg/cluster/cluster_test.go index 0e0dba30b..0a2fe2fd4 100644 --- a/pkg/cluster/cluster_test.go +++ b/pkg/cluster/cluster_test.go @@ -161,8 +161,8 @@ func TestStatefulSetAnnotations(t *testing.T) { spec := acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -184,8 +184,8 @@ func TestStatefulSetUpdateWithEnv(t *testing.T) { oldSpec := &acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", diff --git a/pkg/cluster/connection_pooler.go b/pkg/cluster/connection_pooler.go index 1c01cd0c1..7a97497d7 100644 --- a/pkg/cluster/connection_pooler.go +++ b/pkg/cluster/connection_pooler.go @@ -821,12 +821,12 @@ func (c *Cluster) needSyncConnectionPoolerDefaults(Config *Config, spec *acidv1. func makeDefaultConnectionPoolerResources(config *config.Config) acidv1.Resources { defaultRequests := acidv1.ResourceDescription{ - CPU: config.ConnectionPooler.ConnectionPoolerDefaultCPURequest, - Memory: config.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest, + CPU: &config.ConnectionPooler.ConnectionPoolerDefaultCPURequest, + Memory: &config.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest, } defaultLimits := acidv1.ResourceDescription{ - CPU: config.ConnectionPooler.ConnectionPoolerDefaultCPULimit, - Memory: config.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit, + CPU: &config.ConnectionPooler.ConnectionPoolerDefaultCPULimit, + Memory: &config.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit, } return acidv1.Resources{ diff --git a/pkg/cluster/connection_pooler_test.go b/pkg/cluster/connection_pooler_test.go index f83d369b5..f7f2e2cb0 100644 --- a/pkg/cluster/connection_pooler_test.go +++ b/pkg/cluster/connection_pooler_test.go @@ -2,7 +2,6 @@ package cluster import ( "context" - "errors" "fmt" "strings" "testing" @@ -711,47 +710,42 @@ func TestConnectionPoolerPodSpec(t *testing.T) { noCheck := func(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error { return nil } tests := []struct { - subTest string - spec *acidv1.PostgresSpec - expected error - cluster *Cluster - check func(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error + subTest string + spec *acidv1.PostgresSpec + cluster *Cluster + check func(cluster *Cluster, podSpec *v1.PodTemplateSpec, role PostgresRole) error }{ { subTest: "default configuration", spec: &acidv1.PostgresSpec{ ConnectionPooler: &acidv1.ConnectionPooler{}, }, - expected: nil, - cluster: cluster, - check: noCheck, + cluster: cluster, + check: noCheck, }, { subTest: "pooler uses pod service account", spec: &acidv1.PostgresSpec{ ConnectionPooler: &acidv1.ConnectionPooler{}, }, - expected: nil, - cluster: cluster, - check: testServiceAccount, + cluster: cluster, + check: testServiceAccount, }, { subTest: "no default resources", spec: &acidv1.PostgresSpec{ ConnectionPooler: &acidv1.ConnectionPooler{}, }, - expected: errors.New(`could not generate resource requirements: could not fill resource requests: could not parse default CPU quantity: quantities must match the regular expression '^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$'`), - cluster: clusterNoDefaultRes, - check: noCheck, + cluster: clusterNoDefaultRes, + check: noCheck, }, { subTest: "default resources are set", spec: &acidv1.PostgresSpec{ ConnectionPooler: &acidv1.ConnectionPooler{}, }, - expected: nil, - cluster: cluster, - check: testResources, + cluster: cluster, + check: testResources, }, { subTest: "labels for service", @@ -759,30 +753,23 @@ func TestConnectionPoolerPodSpec(t *testing.T) { ConnectionPooler: &acidv1.ConnectionPooler{}, EnableReplicaConnectionPooler: boolToPointer(true), }, - expected: nil, - cluster: cluster, - check: testLabels, + cluster: cluster, + check: testLabels, }, { subTest: "required envs", spec: &acidv1.PostgresSpec{ ConnectionPooler: &acidv1.ConnectionPooler{}, }, - expected: nil, - cluster: cluster, - check: testEnvs, + cluster: cluster, + check: testEnvs, }, } for _, role := range [2]PostgresRole{Master, Replica} { for _, tt := range tests { - podSpec, err := tt.cluster.generateConnectionPoolerPodTemplate(role) + podSpec, _ := tt.cluster.generateConnectionPoolerPodTemplate(role) - if err != tt.expected && err.Error() != tt.expected.Error() { - t.Errorf("%s [%s]: Could not generate pod template,\n %+v, expected\n %+v", - testName, tt.subTest, err, tt.expected) - } - - err = tt.check(cluster, podSpec, role) + err := tt.check(cluster, podSpec, role) if err != nil { t.Errorf("%s [%s]: Pod spec is incorrect, %+v", testName, tt.subTest, err) @@ -973,8 +960,8 @@ func TestPoolerTLS(t *testing.T) { TeamID: "myapp", NumberOfInstances: 1, EnableConnectionPooler: util.True(), Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", diff --git a/pkg/cluster/k8sres.go b/pkg/cluster/k8sres.go index b60c4ecb3..2cd1a96e3 100644 --- a/pkg/cluster/k8sres.go +++ b/pkg/cluster/k8sres.go @@ -21,6 +21,7 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "golang.org/x/exp/maps" + "golang.org/x/exp/slices" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/labels" @@ -126,12 +127,12 @@ func (c *Cluster) podDisruptionBudgetName() string { func makeDefaultResources(config *config.Config) acidv1.Resources { defaultRequests := acidv1.ResourceDescription{ - CPU: config.Resources.DefaultCPURequest, - Memory: config.Resources.DefaultMemoryRequest, + CPU: &config.Resources.DefaultCPURequest, + Memory: &config.Resources.DefaultMemoryRequest, } defaultLimits := acidv1.ResourceDescription{ - CPU: config.Resources.DefaultCPULimit, - Memory: config.Resources.DefaultMemoryLimit, + CPU: &config.Resources.DefaultCPULimit, + Memory: &config.Resources.DefaultMemoryLimit, } return acidv1.Resources{ @@ -143,12 +144,12 @@ func makeDefaultResources(config *config.Config) acidv1.Resources { func makeLogicalBackupResources(config *config.Config) acidv1.Resources { logicalBackupResourceRequests := acidv1.ResourceDescription{ - CPU: config.LogicalBackup.LogicalBackupCPURequest, - Memory: config.LogicalBackup.LogicalBackupMemoryRequest, + CPU: &config.LogicalBackup.LogicalBackupCPURequest, + Memory: &config.LogicalBackup.LogicalBackupMemoryRequest, } logicalBackupResourceLimits := acidv1.ResourceDescription{ - CPU: config.LogicalBackup.LogicalBackupCPULimit, - Memory: config.LogicalBackup.LogicalBackupMemoryLimit, + CPU: &config.LogicalBackup.LogicalBackupCPULimit, + Memory: &config.LogicalBackup.LogicalBackupMemoryLimit, } return acidv1.Resources{ @@ -214,7 +215,9 @@ func (c *Cluster) enforceMaxResourceRequests(resources *v1.ResourceRequirements) return fmt.Errorf("could not compare defined CPU request %s for %q container with configured maximum value %s: %v", cpuRequest.String(), constants.PostgresContainerName, maxCPURequest, err) } - resources.Requests[v1.ResourceCPU] = maxCPU + if !maxCPU.IsZero() { + resources.Requests[v1.ResourceCPU] = maxCPU + } memoryRequest := resources.Requests[v1.ResourceMemory] maxMemoryRequest := c.OpConfig.MaxMemoryRequest @@ -223,7 +226,9 @@ func (c *Cluster) enforceMaxResourceRequests(resources *v1.ResourceRequirements) return fmt.Errorf("could not compare defined memory request %s for %q container with configured maximum value %s: %v", memoryRequest.String(), constants.PostgresContainerName, maxMemoryRequest, err) } - resources.Requests[v1.ResourceMemory] = maxMemory + if !maxMemory.IsZero() { + resources.Requests[v1.ResourceMemory] = maxMemory + } return nil } @@ -240,30 +245,53 @@ func setMemoryRequestToLimit(resources *v1.ResourceRequirements, containerName s } } +func matchLimitsWithRequestsIfSmaller(resources *v1.ResourceRequirements, containerName string, logger *logrus.Entry) { + requests := resources.Requests + limits := resources.Limits + requestCPU, cpuRequestsExists := requests[v1.ResourceCPU] + limitCPU, cpuLimitExists := limits[v1.ResourceCPU] + if cpuRequestsExists && cpuLimitExists && limitCPU.Cmp(requestCPU) == -1 { + logger.Warningf("CPU limit of %s for %q container is increased to match CPU requests of %s", limitCPU.String(), containerName, requestCPU.String()) + resources.Limits[v1.ResourceCPU] = requestCPU + } + + requestMemory, memoryRequestsExists := requests[v1.ResourceMemory] + limitMemory, memoryLimitExists := limits[v1.ResourceMemory] + if memoryRequestsExists && memoryLimitExists && limitMemory.Cmp(requestMemory) == -1 { + logger.Warningf("memory limit of %s for %q container is increased to match memory requests of %s", limitMemory.String(), containerName, requestMemory.String()) + resources.Limits[v1.ResourceMemory] = requestMemory + } +} + func fillResourceList(spec acidv1.ResourceDescription, defaults acidv1.ResourceDescription) (v1.ResourceList, error) { var err error requests := v1.ResourceList{} + emptyResourceExamples := []string{"", "0", "null"} - if spec.CPU != "" { - requests[v1.ResourceCPU], err = resource.ParseQuantity(spec.CPU) + if spec.CPU != nil && !slices.Contains(emptyResourceExamples, *spec.CPU) { + requests[v1.ResourceCPU], err = resource.ParseQuantity(*spec.CPU) if err != nil { return nil, fmt.Errorf("could not parse CPU quantity: %v", err) } } else { - requests[v1.ResourceCPU], err = resource.ParseQuantity(defaults.CPU) - if err != nil { - return nil, fmt.Errorf("could not parse default CPU quantity: %v", err) + if defaults.CPU != nil && !slices.Contains(emptyResourceExamples, *defaults.CPU) { + requests[v1.ResourceCPU], err = resource.ParseQuantity(*defaults.CPU) + if err != nil { + return nil, fmt.Errorf("could not parse default CPU quantity: %v", err) + } } } - if spec.Memory != "" { - requests[v1.ResourceMemory], err = resource.ParseQuantity(spec.Memory) + if spec.Memory != nil && !slices.Contains(emptyResourceExamples, *spec.Memory) { + requests[v1.ResourceMemory], err = resource.ParseQuantity(*spec.Memory) if err != nil { return nil, fmt.Errorf("could not parse memory quantity: %v", err) } } else { - requests[v1.ResourceMemory], err = resource.ParseQuantity(defaults.Memory) - if err != nil { - return nil, fmt.Errorf("could not parse default memory quantity: %v", err) + if defaults.Memory != nil && !slices.Contains(emptyResourceExamples, *defaults.Memory) { + requests[v1.ResourceMemory], err = resource.ParseQuantity(*defaults.Memory) + if err != nil { + return nil, fmt.Errorf("could not parse default memory quantity: %v", err) + } } } @@ -314,6 +342,10 @@ func (c *Cluster) generateResourceRequirements( } } + // make sure after reflecting default and enforcing min limit values we don't have requests > limits + matchLimitsWithRequestsIfSmaller(&result, containerName, c.logger) + + // vice versa set memory requests to limit if option is enabled if c.OpConfig.SetMemoryRequestToLimit { setMemoryRequestToLimit(&result, containerName, c.logger) } @@ -1208,12 +1240,12 @@ func getBucketScopeSuffix(uid string) string { func makeResources(cpuRequest, memoryRequest, cpuLimit, memoryLimit string) acidv1.Resources { return acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{ - CPU: cpuRequest, - Memory: memoryRequest, + CPU: &cpuRequest, + Memory: &memoryRequest, }, ResourceLimits: acidv1.ResourceDescription{ - CPU: cpuLimit, - Memory: memoryLimit, + CPU: &cpuLimit, + Memory: &memoryLimit, }, } } diff --git a/pkg/cluster/k8sres_test.go b/pkg/cluster/k8sres_test.go index 483463c21..ff75cd6d4 100644 --- a/pkg/cluster/k8sres_test.go +++ b/pkg/cluster/k8sres_test.go @@ -1421,8 +1421,8 @@ func TestNodeAffinity(t *testing.T) { return acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -1514,8 +1514,8 @@ func TestPodAffinity(t *testing.T) { Spec: acidv1.PostgresSpec{ NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -1658,8 +1658,8 @@ func TestTLS(t *testing.T) { Spec: acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -1899,8 +1899,8 @@ func TestAdditionalVolume(t *testing.T) { Spec: acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -1975,8 +1975,8 @@ func TestVolumeSelector(t *testing.T) { TeamID: "myapp", NumberOfInstances: 0, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: volume, } @@ -2107,8 +2107,8 @@ func TestSidecars(t *testing.T) { }, TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -2120,8 +2120,8 @@ func TestSidecars(t *testing.T) { acidv1.Sidecar{ Name: "cluster-specific-sidecar-with-resources", Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "210m", Memory: "0.8Gi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "510m", Memory: "1.4Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, }, }, acidv1.Sidecar{ @@ -2419,8 +2419,8 @@ func TestGenerateService(t *testing.T) { spec = acidv1.PostgresSpec{ TeamID: "myapp", NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, Volume: acidv1.Volume{ Size: "1G", @@ -2432,8 +2432,8 @@ func TestGenerateService(t *testing.T) { acidv1.Sidecar{ Name: "cluster-specific-sidecar-with-resources", Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "210m", Memory: "0.8Gi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "510m", Memory: "1.4Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("210m"), Memory: k8sutil.StringToPointer("0.8Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("510m"), Memory: k8sutil.StringToPointer("1.4Gi")}, }, }, acidv1.Sidecar{ @@ -2646,8 +2646,8 @@ func TestEnableLoadBalancers(t *testing.T) { EnableReplicaPoolerLoadBalancer: util.False(), NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2693,8 +2693,8 @@ func TestEnableLoadBalancers(t *testing.T) { EnableReplicaPoolerLoadBalancer: util.True(), NumberOfInstances: 1, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "10"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("10")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2786,8 +2786,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, }, { @@ -2815,8 +2815,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, }, { @@ -2833,7 +2833,7 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "50m", Memory: "50Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("50m"), Memory: k8sutil.StringToPointer("50Mi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2842,8 +2842,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "50m", Memory: "50Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("50m"), Memory: k8sutil.StringToPointer("50Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, }, { @@ -2860,8 +2860,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{Memory: "1Gi"}, + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("1Gi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2870,8 +2870,97 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "1Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("1Gi")}, + }, + }, + { + subTest: "test generation of resources when default is not defined", + config: config.Config{ + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: clusterNameLabel, + DefaultCPURequest: "100m", + DefaultMemoryRequest: "100Mi", + PodRoleLabel: "spilo-role", + }, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + }, + }, + { + subTest: "test matchLimitsWithRequestsIfSmaller", + config: config.Config{ + Resources: configResources, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("750Mi")}, + ResourceLimits: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("300Mi")}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("750Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("750Mi")}, + }, + }, + { + subTest: "defaults are not defined but minimum limit is", + config: config.Config{ + Resources: config.Resources{ + ClusterLabels: map[string]string{"application": "spilo"}, + ClusterNameLabel: clusterNameLabel, + MinMemoryLimit: "250Mi", + PodRoleLabel: "spilo-role", + }, + PodManagementPolicy: "ordered_ready", + SetMemoryRequestToLimit: false, + }, + pgSpec: acidv1.Postgresql{ + ObjectMeta: metav1.ObjectMeta{ + Name: clusterName, + Namespace: namespace, + }, + Spec: acidv1.PostgresSpec{ + Resources: &acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("500Mi")}, + }, + TeamID: "acid", + Volume: acidv1.Volume{ + Size: "1G", + }, + }, + }, + expectedResources: acidv1.Resources{ + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("500Mi")}, + ResourceLimits: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("500Mi")}, }, }, { @@ -2888,8 +2977,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{Memory: "200Mi"}, - ResourceLimits: acidv1.ResourceDescription{Memory: "300Mi"}, + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("200Mi")}, + ResourceLimits: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("300Mi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2898,8 +2987,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "300Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "300Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("300Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("300Mi")}, }, }, { @@ -2919,8 +3008,8 @@ func TestGenerateResourceRequirements(t *testing.T) { acidv1.Sidecar{ Name: sidecarName, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "10Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, }, }, }, @@ -2931,8 +3020,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, }, }, { @@ -2949,8 +3038,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "250Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "400m", Memory: "800Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("250Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("400m"), Memory: k8sutil.StringToPointer("800Mi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2959,8 +3048,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "250Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "400m", Memory: "800Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("250Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("400m"), Memory: k8sutil.StringToPointer("800Mi")}, }, }, { @@ -2977,8 +3066,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "200m", Memory: "200Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("200m"), Memory: k8sutil.StringToPointer("200Mi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -2987,8 +3076,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "250m", Memory: "250Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("250m"), Memory: k8sutil.StringToPointer("250Mi")}, }, }, { @@ -3008,8 +3097,8 @@ func TestGenerateResourceRequirements(t *testing.T) { acidv1.Sidecar{ Name: sidecarName, Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "10Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, }, }, }, @@ -3020,8 +3109,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "10Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("10Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, }, }, { @@ -3038,8 +3127,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("2Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("2"), Memory: k8sutil.StringToPointer("4Gi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -3048,8 +3137,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "500m", Memory: "1Gi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "2", Memory: "4Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("500m"), Memory: k8sutil.StringToPointer("1Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("2"), Memory: k8sutil.StringToPointer("4Gi")}, }, }, { @@ -3066,8 +3155,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, Spec: acidv1.PostgresSpec{ Resources: &acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{Memory: "500Mi"}, - ResourceLimits: acidv1.ResourceDescription{Memory: "2Gi"}, + ResourceRequests: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("500Mi")}, + ResourceLimits: acidv1.ResourceDescription{Memory: k8sutil.StringToPointer("2Gi")}, }, TeamID: "acid", Volume: acidv1.Volume{ @@ -3076,8 +3165,8 @@ func TestGenerateResourceRequirements(t *testing.T) { }, }, expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "1Gi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "2Gi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("1Gi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("2Gi")}, }, }, { @@ -3104,12 +3193,12 @@ func TestGenerateResourceRequirements(t *testing.T) { }, expectedResources: acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{ - CPU: "100m", - Memory: "100Mi", + CPU: k8sutil.StringToPointer("100m"), + Memory: k8sutil.StringToPointer("100Mi"), }, ResourceLimits: acidv1.ResourceDescription{ - CPU: "1", - Memory: "500Mi", + CPU: k8sutil.StringToPointer("1"), + Memory: k8sutil.StringToPointer("500Mi"), }, }, }, @@ -3143,14 +3232,14 @@ func TestGenerateResourceRequirements(t *testing.T) { }, expectedResources: acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{ - CPU: "100m", - Memory: "100Mi", + CPU: k8sutil.StringToPointer("100m"), + Memory: k8sutil.StringToPointer("100Mi"), HugePages2Mi: k8sutil.StringToPointer("128Mi"), HugePages1Gi: k8sutil.StringToPointer("1Gi"), }, ResourceLimits: acidv1.ResourceDescription{ - CPU: "1", - Memory: "500Mi", + CPU: k8sutil.StringToPointer("1"), + Memory: k8sutil.StringToPointer("500Mi"), HugePages2Mi: k8sutil.StringToPointer("256Mi"), HugePages1Gi: k8sutil.StringToPointer("2Gi"), }, @@ -3192,14 +3281,14 @@ func TestGenerateResourceRequirements(t *testing.T) { }, expectedResources: acidv1.Resources{ ResourceRequests: acidv1.ResourceDescription{ - CPU: "100m", - Memory: "100Mi", + CPU: k8sutil.StringToPointer("100m"), + Memory: k8sutil.StringToPointer("100Mi"), HugePages2Mi: k8sutil.StringToPointer("128Mi"), HugePages1Gi: k8sutil.StringToPointer("1Gi"), }, ResourceLimits: acidv1.ResourceDescription{ - CPU: "1", - Memory: "500Mi", + CPU: k8sutil.StringToPointer("1"), + Memory: k8sutil.StringToPointer("500Mi"), HugePages2Mi: k8sutil.StringToPointer("256Mi"), HugePages1Gi: k8sutil.StringToPointer("2Gi"), }, @@ -3269,8 +3358,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedSchedule: "30 00 * * *", expectedJobName: "logical-backup-acid-test-cluster", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, @@ -3294,8 +3383,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedSchedule: "30 00 * * 7", expectedJobName: "lb-acid-test-cluster", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "10m", Memory: "50Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "300m", Memory: "300Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("10m"), Memory: k8sutil.StringToPointer("50Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("300m"), Memory: k8sutil.StringToPointer("300Mi")}, }, expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, @@ -3317,8 +3406,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedSchedule: "30 00 * * *", expectedJobName: "acid-test-cluster", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "50m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "250m", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("50m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("250m"), Memory: k8sutil.StringToPointer("500Mi")}, }, expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, @@ -3340,8 +3429,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedSchedule: "30 00 * * *", expectedJobName: "test-long-prefix-so-name-must-be-trimmed-acid-test-c", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "200Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "200Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("200Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("200Mi")}, }, expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: nil, @@ -3362,8 +3451,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedJobName: "acid-test-cluster", expectedSchedule: "", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, expectedLabel: map[string]string{"labelKey": "labelValue", "cluster-name": clusterName, "team": teamId}, expectedAnnotation: nil, @@ -3384,8 +3473,8 @@ func TestGenerateLogicalBackupJob(t *testing.T) { expectedJobName: "acid-test-cluster", expectedSchedule: "", expectedResources: acidv1.Resources{ - ResourceRequests: acidv1.ResourceDescription{CPU: "100m", Memory: "100Mi"}, - ResourceLimits: acidv1.ResourceDescription{CPU: "1", Memory: "500Mi"}, + ResourceRequests: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("100m"), Memory: k8sutil.StringToPointer("100Mi")}, + ResourceLimits: acidv1.ResourceDescription{CPU: k8sutil.StringToPointer("1"), Memory: k8sutil.StringToPointer("500Mi")}, }, expectedLabel: map[string]string{configResources.ClusterNameLabel: clusterName, "team": teamId}, expectedAnnotation: map[string]string{"annotationKey": "annotationValue"}, diff --git a/pkg/controller/operator_config.go b/pkg/controller/operator_config.go index 7c964e292..328307d97 100644 --- a/pkg/controller/operator_config.go +++ b/pkg/controller/operator_config.go @@ -130,12 +130,12 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur result.PodToleration = fromCRD.Kubernetes.PodToleration // Postgres Pod resources - result.DefaultCPURequest = util.Coalesce(fromCRD.PostgresPodResources.DefaultCPURequest, "100m") - result.DefaultMemoryRequest = util.Coalesce(fromCRD.PostgresPodResources.DefaultMemoryRequest, "100Mi") - result.DefaultCPULimit = util.Coalesce(fromCRD.PostgresPodResources.DefaultCPULimit, "1") - result.DefaultMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.DefaultMemoryLimit, "500Mi") - result.MinCPULimit = util.Coalesce(fromCRD.PostgresPodResources.MinCPULimit, "250m") - result.MinMemoryLimit = util.Coalesce(fromCRD.PostgresPodResources.MinMemoryLimit, "250Mi") + result.DefaultCPURequest = fromCRD.PostgresPodResources.DefaultCPURequest + 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 result.MaxCPURequest = fromCRD.PostgresPodResources.MaxCPURequest result.MaxMemoryRequest = fromCRD.PostgresPodResources.MaxMemoryRequest @@ -265,21 +265,10 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur fromCRD.ConnectionPooler.Mode, constants.ConnectionPoolerDefaultMode) - result.ConnectionPooler.ConnectionPoolerDefaultCPURequest = util.Coalesce( - fromCRD.ConnectionPooler.DefaultCPURequest, - constants.ConnectionPoolerDefaultCpuRequest) - - result.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest = util.Coalesce( - fromCRD.ConnectionPooler.DefaultMemoryRequest, - constants.ConnectionPoolerDefaultMemoryRequest) - - result.ConnectionPooler.ConnectionPoolerDefaultCPULimit = util.Coalesce( - fromCRD.ConnectionPooler.DefaultCPULimit, - constants.ConnectionPoolerDefaultCpuLimit) - - result.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit = util.Coalesce( - fromCRD.ConnectionPooler.DefaultMemoryLimit, - constants.ConnectionPoolerDefaultMemoryLimit) + result.ConnectionPooler.ConnectionPoolerDefaultCPURequest = fromCRD.ConnectionPooler.DefaultCPURequest + result.ConnectionPooler.ConnectionPoolerDefaultMemoryRequest = fromCRD.ConnectionPooler.DefaultMemoryRequest + result.ConnectionPooler.ConnectionPoolerDefaultCPULimit = fromCRD.ConnectionPooler.DefaultCPULimit + result.ConnectionPooler.ConnectionPoolerDefaultMemoryLimit = fromCRD.ConnectionPooler.DefaultMemoryLimit result.ConnectionPooler.MaxDBConnections = util.CoalesceInt32( fromCRD.ConnectionPooler.MaxDBConnections, diff --git a/pkg/util/config/config.go b/pkg/util/config/config.go index c0fda940b..7ea9f9e29 100644 --- a/pkg/util/config/config.go +++ b/pkg/util/config/config.go @@ -48,12 +48,12 @@ type Resources struct { DeleteAnnotationNameKey string `name:"delete_annotation_name_key"` PodRoleLabel string `name:"pod_role_label" default:"spilo-role"` 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:"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"` + DefaultCPURequest string `name:"default_cpu_request"` + DefaultMemoryRequest string `name:"default_memory_request"` + DefaultCPULimit string `name:"default_cpu_limit"` + DefaultMemoryLimit string `name:"default_memory_limit"` + MinCPULimit string `name:"min_cpu_limit"` + MinMemoryLimit string `name:"min_memory_limit"` MaxCPURequest string `name:"max_cpu_request"` MaxMemoryRequest string `name:"max_memory_request"` PodEnvironmentConfigMap spec.NamespacedName `name:"pod_environment_configmap"` @@ -155,10 +155,10 @@ type ConnectionPooler struct { Image string `name:"connection_pooler_image" default:"registry.opensource.zalan.do/acid/pgbouncer"` Mode string `name:"connection_pooler_mode" default:"transaction"` MaxDBConnections *int32 `name:"connection_pooler_max_db_connections" default:"60"` - ConnectionPoolerDefaultCPURequest string `name:"connection_pooler_default_cpu_request" default:"500m"` - ConnectionPoolerDefaultMemoryRequest string `name:"connection_pooler_default_memory_request" default:"100Mi"` - ConnectionPoolerDefaultCPULimit string `name:"connection_pooler_default_cpu_limit" default:"1"` - ConnectionPoolerDefaultMemoryLimit string `name:"connection_pooler_default_memory_limit" default:"100Mi"` + ConnectionPoolerDefaultCPURequest string `name:"connection_pooler_default_cpu_request"` + ConnectionPoolerDefaultMemoryRequest string `name:"connection_pooler_default_memory_request"` + ConnectionPoolerDefaultCPULimit string `name:"connection_pooler_default_cpu_limit"` + ConnectionPoolerDefaultMemoryLimit string `name:"connection_pooler_default_memory_limit"` } // Config describes operator config