diff --git a/.changes/unreleased/chart-redpanda-Added-20250108-153147.yaml b/.changes/unreleased/chart-redpanda-Added-20250108-153147.yaml new file mode 100644 index 000000000..6cd595327 --- /dev/null +++ b/.changes/unreleased/chart-redpanda-Added-20250108-153147.yaml @@ -0,0 +1,22 @@ +project: chart-redpanda +kind: Added +body: | + Added `resources.limits` and `resources.requests` as an alternative method of managing the redpanda container's resources. + + When both `resources.limits` and `resources.requests` are specified, the + redpanda container's `resources` will be set to the provided values and all + other keys of `resources` will be ignored. Instead, all other values will be + inferred from the limits and requests. + + This allows fine grain control of resources. i.e. It is now possible to set + CPU requests without setting limits: + + ```yaml + resources: + limits: {} # Specified but no cpu or memory values provided + requests: + cpu: 5 # Only CPU requests + ``` + + For more details see [redpanda's values.yaml](./charts/redpanda/values.yaml). +time: 2025-01-08T15:31:47.946476-05:00 diff --git a/charts/redpanda/CHANGELOG.md b/charts/redpanda/CHANGELOG.md index 6514803df..73d18b5c8 100644 --- a/charts/redpanda/CHANGELOG.md +++ b/charts/redpanda/CHANGELOG.md @@ -5,6 +5,28 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and is generated by [Changie](https://github.com/miniscruff/changie). +## Unreleased +### Added +* Added `resources.limits` and `resources.requests` as an alternative method of managing the redpanda container's resources. + + When both `resources.limits` and `resources.requests` are specified, the + redpanda container's `resources` will be set to the provided values and all + other keys of `resources` will be ignored. Instead, all other values will be + inferred from the limits and requests. + + This allows fine grain control of resources. i.e. It is now possible to set + CPU requests without setting limits: + + ```yaml + resources: + limits: {} # Specified but no cpu or memory values provided + requests: + cpu: 5 # Only CPU requests + ``` + + For more details see [redpanda's values.yaml](./charts/redpanda/values.yaml). + + ### [5.9.18](https://github.com/redpanda-data/helm-charts/releases/tag/redpanda-5.9.18) - 2024-12-20 #### Added #### Changed diff --git a/charts/redpanda/README.md b/charts/redpanda/README.md index f0a1a5d31..bc5c6d4cd 100644 --- a/charts/redpanda/README.md +++ b/charts/redpanda/README.md @@ -621,7 +621,42 @@ Enable for features that need extra privileges. If you use the Redpanda Operator ### [resources](https://artifacthub.io/packages/helm/redpanda-data/redpanda?modal=values&path=resources) -Pod resource management. This section simplifies resource allocation by providing a single location where resources are defined. Helm sets these resource values within the `statefulset.yaml` and `configmap.yaml` templates. The default values are for a development environment. Production-level values and other considerations are documented, where those values are different from the default. For details, see the [Pod resources documentation](https://docs.redpanda.com/docs/manage/kubernetes/manage-resources/). +Pod resource management. +This section simplifies resource allocation for the redpanda container by +providing a single location where resources are defined. + +Resources may be specified by either setting `resources.cpu` and +`resources.memory` (the default) or by setting `resources.requests` and +`resources.limits`. + +For details on `resources.cpu` and `resources.memory`, see their respective +documentation below. + +When `resources.limits` and `resources.requests` are set, the redpanda +container's resources will be set to exactly the provided values. This allows +users to granularly control limits and requests to best suit their use case. +For example: `resources.requests.cpu` may be set without setting +`resources.limits.cpu` to avoid the potential of CPU throttling. + +Redpanda's resource related CLI flags will then be calculated as follows: +* `--smp max(1, floor(coalesce(resources.requests.cpu, resources.limits.cpu)))` +* `--memory coalesce(resources.requests.memory, resources.limits.memory) * 90%` +* `--reserve-memory 0` +* `--overprovisioned coalesce(resources.requests.cpu, resources.limits.cpu) < 1000m` + +If neither a request nor a limit is provided for cpu or memory, the +corresponding flag will be omitted. As a result, setting `resources.limits` +and `resources.requests` to `{}` will result in redpanda being run without +`--smp` or `--memory`. (This is not recommended). + +If the computed CLI flags are undesirable, they may be overridden by +specifying the desired value through `statefulset.additionalRedpandaCmdFlags`. + +The default values are for a development environment. +Production-level values and other considerations are documented, +where those values are different from the default. +For details, +see the [Pod resources documentation](https://docs.redpanda.com/docs/manage/kubernetes/manage-resources/). **Default:** diff --git a/charts/redpanda/configmap.tpl.go b/charts/redpanda/configmap.tpl.go index 51393eb0f..e135a4d49 100644 --- a/charts/redpanda/configmap.tpl.go +++ b/charts/redpanda/configmap.tpl.go @@ -359,10 +359,12 @@ func rpkNodeConfig(dot *helmette.Dot) map[string]any { schemaRegistryTLS = tls } + lockMemory, overprovisioned, flags := RedpandaAdditionalStartFlags(&values) + result := map[string]any{ - "overprovisioned": values.Resources.GetOverProvisionValue(), - "enable_memory_locking": ptr.Deref(values.Resources.Memory.EnableMemoryLocking, false), - "additional_start_flags": RedpandaAdditionalStartFlags(dot), + "additional_start_flags": flags, + "enable_memory_locking": lockMemory, + "overprovisioned": overprovisioned, "kafka_api": map[string]any{ "brokers": brokerList, "tls": brokerTLS, @@ -610,39 +612,60 @@ func createInternalListenerCfg(port int32) map[string]any { } } -// RedpandaAdditionalStartFlags returns a string list of flags suitable for use +// RedpandaAdditionalStartFlags returns a string slice of flags suitable for use // as `additional_start_flags`. User provided flags will override any of those // set by default. -func RedpandaAdditionalStartFlags(dot *helmette.Dot) []string { - values := helmette.Unwrap[Values](dot.Values) - +func RedpandaAdditionalStartFlags(values *Values) (bool, bool, []string) { // All `additional_start_flags` that are set by the chart. - chartFlags := values.Resources.GetRedpandaStartFlags() - chartFlags["default-log-level"] = values.Logging.LogLevel + flags := values.Resources.GetRedpandaFlags() + flags["--default-log-level"] = values.Logging.LogLevel - // If in developer_mode, don't set reserve-memory. + // Unclear why this is done aside from historical reasons. + // Legacy comment: If in developer_mode, don't set reserve-memory. if values.Config.Node["developer_mode"] == true { - delete(chartFlags, "reserve-memory") + delete(flags, "--reserve-memory") } - // Check to see if there are any flags overriding the defaults set by the - // chart. - for flag := range chartFlags { - for _, userFlag := range values.Statefulset.AdditionalRedpandaCmdFlags { - if helmette.RegexMatch(fmt.Sprintf("^--%s", flag), userFlag) { - delete(chartFlags, flag) - } - } + for key, value := range ParseCLIArgs(values.Statefulset.AdditionalRedpandaCmdFlags) { + flags[key] = value + } + + enabledOptions := map[string]bool{ + "true": true, + "1": true, + "": true, + } + + // Due to a buglet in rpk, we need to set lock-memory and overprovisioned + // via their fields in redpanda.yaml instead of additional_start_flags. + // https://github.com/redpanda-data/helm-charts/pull/1622#issuecomment-2577922409 + lockMemory := false + if value, ok := flags["--lock-memory"]; ok { + lockMemory = enabledOptions[value] + delete(flags, "--lock-memory") + } + + overprovisioned := false + if value, ok := flags["--overprovisioned"]; ok { + overprovisioned = enabledOptions[value] + delete(flags, "--overprovisioned") } // Deterministically order out list and add in values supplied flags. - keys := helmette.Keys(chartFlags) - helmette.SortAlpha(keys) + keys := helmette.Keys(flags) + keys = helmette.SortAlpha(keys) - flags := []string{} + var rendered []string for _, key := range keys { - flags = append(flags, fmt.Sprintf("--%s=%s", key, chartFlags[key])) + value := flags[key] + // Support flags that don't have values (`--overprovisioned`) by + // letting them be specified as key: "" + if value == "" { + rendered = append(rendered, key) + } else { + rendered = append(rendered, fmt.Sprintf("%s=%s", key, value)) + } } - return append(flags, values.Statefulset.AdditionalRedpandaCmdFlags...) + return lockMemory, overprovisioned, rendered } diff --git a/charts/redpanda/helpers.go b/charts/redpanda/helpers.go index 3eba68895..5346fb399 100644 --- a/charts/redpanda/helpers.go +++ b/charts/redpanda/helpers.go @@ -568,3 +568,54 @@ func mergeContainer(original corev1.Container, override applycorev1.ContainerApp merged.VolumeMounts = mergeSliceBy(original.VolumeMounts, override.VolumeMounts, "name", mergeVolumeMount) return merged } + +// ParseCLIArgs parses a slice of strings intended for rpk's +// `additional_start_flags` field into a map to allow merging slices of flags +// or introspection thereof. +// Flags without values are not differentiated between flags with values of an +// empty string. e.g. --value and --value=” are represented the same way. +func ParseCLIArgs(args []string) map[string]string { + parsed := map[string]string{} + + // NB: templates/gotohelm don't supported c style for loops (or ++) which + // is the ideal for this situation. The janky code you see is a rough + // equivalent for the following: + // for i := 0; i < len(args); i++ { + i := -1 // Start at -1 so our increment can be at the start of the loop. + for range args { // Range needs to range over something and we'll always have < len(args) iterations. + i = i + 1 + if i >= len(args) { + break + } + + // All flags should start with - or --. + // If not present, skip this value. + if !strings.HasPrefix(args[i], "-") { + continue + } + + flag := args[i] + + // Handle values like: `--flag value` or `--flag=value` + // There's no strings.Index in sprig, so RegexSplit is the next best + // option. + spl := helmette.RegexSplit(" |=", flag, 2) + if len(spl) == 2 { + parsed[spl[0]] = spl[1] + continue + } + + // If no ' ' or =, consume the next value if it's not formatted like a + // flag: `--flag`, `value` + if i+1 < len(args) && !strings.HasPrefix(args[i+1], "-") { + parsed[flag] = args[i+1] + i = i + 1 + continue + } + + // Otherwise, assume this is a bare flag and assign it an empty string. + parsed[flag] = "" + } + + return parsed +} diff --git a/charts/redpanda/helpers_test.go b/charts/redpanda/helpers_test.go index e535d4be2..685a655d4 100644 --- a/charts/redpanda/helpers_test.go +++ b/charts/redpanda/helpers_test.go @@ -210,3 +210,58 @@ func TestStrategicMergePatch_nopanic(t *testing.T) { }) }) } + +func TestParseCLIArgs(t *testing.T) { + testCases := []struct { + In []string + Out map[string]string + }{ + { + In: []string{}, + Out: map[string]string{}, + }, + { + In: []string{ + "-foo=bar", + "-baz 1", + "--help", "topic", + }, + Out: map[string]string{ + "-foo": "bar", + "-baz": "1", + "--help": "topic", + }, + }, + { + In: []string{ + "invalid", + "-valid=bar", + "--trailing spaces ", + "--bare=", + "ignored-perhaps-confusingly", + "--final", + }, + Out: map[string]string{ + "-valid": "bar", + "--trailing": "spaces ", + "--bare": "", + "--final": "", + }, + }, + } + + for _, tc := range testCases { + assert.Equal(t, tc.Out, redpanda.ParseCLIArgs(tc.In)) + } + + t.Run("NotPanics", rapid.MakeCheck(func(t *rapid.T) { + // We could certainly be more inventive with + // the inputs here but this is more of a + // fuzz test than a property test. + in := rapid.SliceOf(rapid.String()).Draw(t, "input") + + assert.NotPanics(t, func() { + redpanda.ParseCLIArgs(in) + }) + })) +} diff --git a/charts/redpanda/servicemonitor.go b/charts/redpanda/servicemonitor.go index 625090b85..19d036b2d 100644 --- a/charts/redpanda/servicemonitor.go +++ b/charts/redpanda/servicemonitor.go @@ -29,7 +29,7 @@ func ServiceMonitor(dot *helmette.Dot) *monitoringv1.ServiceMonitor { Interval: values.Monitoring.ScrapeInterval, Path: "/public_metrics", Port: "admin", - EnableHttp2: values.Monitoring.EnableHttp2, + EnableHttp2: values.Monitoring.EnableHTTP2, Scheme: "http", } diff --git a/charts/redpanda/templates/_configmap.go.tpl b/charts/redpanda/templates/_configmap.go.tpl index 227de82a0..c5a75fa7f 100644 --- a/charts/redpanda/templates/_configmap.go.tpl +++ b/charts/redpanda/templates/_configmap.go.tpl @@ -313,7 +313,11 @@ {{- if (gt ((get (fromJson (include "_shims.len" (dict "a" (list $tls_8) ))) "r") | int) (0 | int)) -}} {{- $schemaRegistryTLS = $tls_8 -}} {{- end -}} -{{- $result := (dict "overprovisioned" (get (fromJson (include "redpanda.RedpandaResources.GetOverProvisionValue" (dict "a" (list $values.resources) ))) "r") "enable_memory_locking" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $values.resources.memory.enable_memory_locking false) ))) "r") "additional_start_flags" (get (fromJson (include "redpanda.RedpandaAdditionalStartFlags" (dict "a" (list $dot) ))) "r") "kafka_api" (dict "brokers" $brokerList "tls" $brokerTLS ) "admin_api" (dict "addresses" (get (fromJson (include "redpanda.Listeners.AdminList" (dict "a" (list $values.listeners ($values.statefulset.replicas | int) (get (fromJson (include "redpanda.Fullname" (dict "a" (list $dot) ))) "r") (get (fromJson (include "redpanda.InternalDomain" (dict "a" (list $dot) ))) "r")) ))) "r") "tls" $adminTLS ) "schema_registry" (dict "addresses" (get (fromJson (include "redpanda.Listeners.SchemaRegistryList" (dict "a" (list $values.listeners ($values.statefulset.replicas | int) (get (fromJson (include "redpanda.Fullname" (dict "a" (list $dot) ))) "r") (get (fromJson (include "redpanda.InternalDomain" (dict "a" (list $dot) ))) "r")) ))) "r") "tls" $schemaRegistryTLS ) ) -}} +{{- $_370_lockMemory_overprovisioned_flags := (get (fromJson (include "redpanda.RedpandaAdditionalStartFlags" (dict "a" (list $values) ))) "r") -}} +{{- $lockMemory := (index $_370_lockMemory_overprovisioned_flags 0) -}} +{{- $overprovisioned := (index $_370_lockMemory_overprovisioned_flags 1) -}} +{{- $flags := (index $_370_lockMemory_overprovisioned_flags 2) -}} +{{- $result := (dict "additional_start_flags" $flags "enable_memory_locking" $lockMemory "overprovisioned" $overprovisioned "kafka_api" (dict "brokers" $brokerList "tls" $brokerTLS ) "admin_api" (dict "addresses" (get (fromJson (include "redpanda.Listeners.AdminList" (dict "a" (list $values.listeners ($values.statefulset.replicas | int) (get (fromJson (include "redpanda.Fullname" (dict "a" (list $dot) ))) "r") (get (fromJson (include "redpanda.InternalDomain" (dict "a" (list $dot) ))) "r")) ))) "r") "tls" $adminTLS ) "schema_registry" (dict "addresses" (get (fromJson (include "redpanda.Listeners.SchemaRegistryList" (dict "a" (list $values.listeners ($values.statefulset.replicas | int) (get (fromJson (include "redpanda.Fullname" (dict "a" (list $dot) ))) "r") (get (fromJson (include "redpanda.InternalDomain" (dict "a" (list $dot) ))) "r")) ))) "r") "tls" $schemaRegistryTLS ) ) -}} {{- $result = (merge (dict ) $result (get (fromJson (include "redpanda.Tuning.Translate" (dict "a" (list $values.tuning) ))) "r")) -}} {{- $result = (merge (dict ) $result (get (fromJson (include "redpanda.Config.CreateRPKConfiguration" (dict "a" (list $values.config) ))) "r")) -}} {{- $_is_returning = true -}} @@ -540,39 +544,53 @@ {{- end -}} {{- define "redpanda.RedpandaAdditionalStartFlags" -}} -{{- $dot := (index .a 0) -}} +{{- $values := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $values := $dot.Values.AsMap -}} -{{- $chartFlags := (get (fromJson (include "redpanda.RedpandaResources.GetRedpandaStartFlags" (dict "a" (list $values.resources) ))) "r") -}} -{{- $_ := (set $chartFlags "default-log-level" $values.logging.logLevel) -}} +{{- $flags := (get (fromJson (include "redpanda.RedpandaResources.GetRedpandaFlags" (dict "a" (list $values.resources) ))) "r") -}} +{{- $_ := (set $flags "--default-log-level" $values.logging.logLevel) -}} {{- if (eq (index $values.config.node "developer_mode") true) -}} -{{- $_ := (unset $chartFlags "reserve-memory") -}} -{{- end -}} -{{- range $flag, $_ := $chartFlags -}} -{{- range $_, $userFlag := $values.statefulset.additionalRedpandaCmdFlags -}} -{{- if (regexMatch (printf "^--%s" $flag) $userFlag) -}} -{{- $_ := (unset $chartFlags $flag) -}} -{{- end -}} -{{- end -}} -{{- if $_is_returning -}} -{{- break -}} +{{- $_ := (unset $flags "--reserve-memory") -}} {{- end -}} +{{- range $key, $value := (get (fromJson (include "redpanda.ParseCLIArgs" (dict "a" (list $values.statefulset.additionalRedpandaCmdFlags) ))) "r") -}} +{{- $_ := (set $flags $key $value) -}} {{- end -}} {{- if $_is_returning -}} {{- break -}} {{- end -}} -{{- $keys := (keys $chartFlags) -}} -{{- $_ := (sortAlpha $keys) -}} -{{- $flags := (list ) -}} +{{- $enabledOptions := (dict "true" true "1" true "" true ) -}} +{{- $lockMemory := false -}} +{{- $_655_value_14_ok_15 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $flags "--lock-memory" "") ))) "r") -}} +{{- $value_14 := (index $_655_value_14_ok_15 0) -}} +{{- $ok_15 := (index $_655_value_14_ok_15 1) -}} +{{- if $ok_15 -}} +{{- $lockMemory = (ternary (index $enabledOptions $value_14) false (hasKey $enabledOptions $value_14)) -}} +{{- $_ := (unset $flags "--lock-memory") -}} +{{- end -}} +{{- $overprovisioned := false -}} +{{- $_662_value_16_ok_17 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $flags "--overprovisioned" "") ))) "r") -}} +{{- $value_16 := (index $_662_value_16_ok_17 0) -}} +{{- $ok_17 := (index $_662_value_16_ok_17 1) -}} +{{- if $ok_17 -}} +{{- $overprovisioned = (ternary (index $enabledOptions $value_16) false (hasKey $enabledOptions $value_16)) -}} +{{- $_ := (unset $flags "--overprovisioned") -}} +{{- end -}} +{{- $keys := (keys $flags) -}} +{{- $keys = (sortAlpha $keys) -}} +{{- $rendered := (coalesce nil) -}} {{- range $_, $key := $keys -}} -{{- $flags = (concat (default (list ) $flags) (list (printf "--%s=%s" $key (ternary (index $chartFlags $key) "" (hasKey $chartFlags $key))))) -}} +{{- $value := (ternary (index $flags $key) "" (hasKey $flags $key)) -}} +{{- if (eq $value "") -}} +{{- $rendered = (concat (default (list ) $rendered) (list $key)) -}} +{{- else -}} +{{- $rendered = (concat (default (list ) $rendered) (list (printf "%s=%s" $key $value))) -}} +{{- end -}} {{- end -}} {{- if $_is_returning -}} {{- break -}} {{- end -}} {{- $_is_returning = true -}} -{{- (dict "r" (concat (default (list ) $flags) (default (list ) $values.statefulset.additionalRedpandaCmdFlags))) | toJson -}} +{{- (dict "r" (list $lockMemory $overprovisioned $rendered)) | toJson -}} {{- break -}} {{- end -}} {{- end -}} diff --git a/charts/redpanda/templates/_helpers.go.tpl b/charts/redpanda/templates/_helpers.go.tpl index 86ffe6e52..d14ea79bb 100644 --- a/charts/redpanda/templates/_helpers.go.tpl +++ b/charts/redpanda/templates/_helpers.go.tpl @@ -625,3 +625,39 @@ {{- end -}} {{- end -}} +{{- define "redpanda.ParseCLIArgs" -}} +{{- $args := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- $parsed := (dict ) -}} +{{- $i := -1 -}} +{{- range $_, $_ := $args -}} +{{- $i = ((add $i (1 | int)) | int) -}} +{{- if (ge $i ((get (fromJson (include "_shims.len" (dict "a" (list $args) ))) "r") | int)) -}} +{{- break -}} +{{- end -}} +{{- if (not (hasPrefix "-" (index $args $i))) -}} +{{- continue -}} +{{- end -}} +{{- $flag := (index $args $i) -}} +{{- $spl := (mustRegexSplit " |=" $flag (2 | int)) -}} +{{- if (eq ((get (fromJson (include "_shims.len" (dict "a" (list $spl) ))) "r") | int) (2 | int)) -}} +{{- $_ := (set $parsed (index $spl (0 | int)) (index $spl (1 | int))) -}} +{{- continue -}} +{{- end -}} +{{- if (and (lt ((add $i (1 | int)) | int) ((get (fromJson (include "_shims.len" (dict "a" (list $args) ))) "r") | int)) (not (hasPrefix "-" (index $args ((add $i (1 | int)) | int))))) -}} +{{- $_ := (set $parsed $flag (index $args ((add $i (1 | int)) | int))) -}} +{{- $i = ((add $i (1 | int)) | int) -}} +{{- continue -}} +{{- end -}} +{{- $_ := (set $parsed $flag "") -}} +{{- end -}} +{{- if $_is_returning -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $parsed) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + diff --git a/charts/redpanda/templates/_values.go.tpl b/charts/redpanda/templates/_values.go.tpl index 7833e6c91..7cd62c909 100644 --- a/charts/redpanda/templates/_values.go.tpl +++ b/charts/redpanda/templates/_values.go.tpl @@ -106,6 +106,11 @@ {{- $rr := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} +{{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} +{{- $_is_returning = true -}} +{{- (dict "r" (mustMergeOverwrite (dict ) (dict "limits" $rr.limits "requests" $rr.requests ))) | toJson -}} +{{- break -}} +{{- end -}} {{- $reqs := (mustMergeOverwrite (dict ) (dict "limits" (dict "cpu" $rr.cpu.cores "memory" $rr.memory.container.max ) )) -}} {{- if (ne (toJson $rr.memory.container.min) "null") -}} {{- $_ := (set $reqs "requests" (dict "cpu" $rr.cpu.cores "memory" $rr.memory.container.min )) -}} @@ -116,19 +121,25 @@ {{- end -}} {{- end -}} -{{- define "redpanda.RedpandaResources.GetRedpandaStartFlags" -}} +{{- define "redpanda.RedpandaResources.GetRedpandaFlags" -}} {{- $rr := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $flags := (dict ) -}} -{{- $coresInMillies_2 := ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $rr.cpu.cores) ))) "r") | int64) -}} -{{- if (lt $coresInMillies_2 (1000 | int64)) -}} -{{- $_ := (set $flags "smp" (printf "%d" (1 | int))) -}} -{{- else -}} -{{- $_ := (set $flags "smp" (printf "%d" ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rr.cpu.cores) ))) "r") | int64))) -}} +{{- $flags := (dict "--reserve-memory" (printf "%dM" ((get (fromJson (include "redpanda.RedpandaResources.reserveMemory" (dict "a" (list $rr) ))) "r") | int64)) ) -}} +{{- $smp_2 := (get (fromJson (include "redpanda.RedpandaResources.smp" (dict "a" (list $rr) ))) "r") -}} +{{- if (ne (toJson $smp_2) "null") -}} +{{- $_ := (set $flags "--smp" (printf "%d" ($smp_2 | int64))) -}} +{{- end -}} +{{- $memory_3 := (get (fromJson (include "redpanda.RedpandaResources.memory" (dict "a" (list $rr) ))) "r") -}} +{{- if (ne (toJson $memory_3) "null") -}} +{{- $_ := (set $flags "--memory" (printf "%dM" ($memory_3 | int64))) -}} +{{- end -}} +{{- if (and (eq (toJson $rr.limits) "null") (eq (toJson $rr.requests) "null")) -}} +{{- $_ := (set $flags "--lock-memory" (printf "%v" (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $rr.memory.enable_memory_locking false) ))) "r"))) -}} +{{- end -}} +{{- if (get (fromJson (include "redpanda.RedpandaResources.GetOverProvisionValue" (dict "a" (list $rr) ))) "r") -}} +{{- $_ := (set $flags "--overprovisioned" "") -}} {{- end -}} -{{- $_ := (set $flags "memory" (printf "%dM" ((get (fromJson (include "redpanda.RedpandaResources.memory" (dict "a" (list $rr) ))) "r") | int64))) -}} -{{- $_ := (set $flags "reserve-memory" (printf "%dM" ((get (fromJson (include "redpanda.RedpandaResources.reserveMemory" (dict "a" (list $rr) ))) "r") | int64))) -}} {{- $_is_returning = true -}} {{- (dict "r" $flags) | toJson -}} {{- break -}} @@ -139,6 +150,24 @@ {{- $rr := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} +{{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} +{{- $_449_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0") ))) "r") -}} +{{- $cpuReq := (index $_449_cpuReq_ok 0) -}} +{{- $ok := (index $_449_cpuReq_ok 1) -}} +{{- if (not $ok) -}} +{{- $_451_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0") ))) "r") -}} +{{- $cpuReq = (index $_451_cpuReq_ok 0) -}} +{{- $ok = (index $_451_cpuReq_ok 1) -}} +{{- end -}} +{{- if (and $ok (lt ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $cpuReq) ))) "r") | int64) (1000 | int64))) -}} +{{- $_is_returning = true -}} +{{- (dict "r" true) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" false) | toJson -}} +{{- break -}} +{{- end -}} {{- if (lt ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $rr.cpu.cores) ))) "r") | int64) (1000 | int64)) -}} {{- $_is_returning = true -}} {{- (dict "r" true) | toJson -}} @@ -150,15 +179,72 @@ {{- end -}} {{- end -}} +{{- define "redpanda.RedpandaResources.smp" -}} +{{- $rr := (index .a 0) -}} +{{- range $_ := (list 1) -}} +{{- $_is_returning := false -}} +{{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} +{{- $_475_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "cpu" "0") ))) "r") -}} +{{- $cpuReq := (index $_475_cpuReq_ok 0) -}} +{{- $ok := (index $_475_cpuReq_ok 1) -}} +{{- if (not $ok) -}} +{{- $_477_cpuReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "cpu" "0") ))) "r") -}} +{{- $cpuReq = (index $_477_cpuReq_ok 0) -}} +{{- $ok = (index $_477_cpuReq_ok 1) -}} +{{- end -}} +{{- if (not $ok) -}} +{{- $_is_returning = true -}} +{{- (dict "r" (coalesce nil)) | toJson -}} +{{- break -}} +{{- end -}} +{{- $smp := ((div ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $cpuReq) ))) "r") | int64) (1000 | int64)) | int64) -}} +{{- if (lt $smp (1 | int64)) -}} +{{- $smp = (1 | int64) -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" $smp) | toJson -}} +{{- break -}} +{{- end -}} +{{- $coresInMillies_4 := ((get (fromJson (include "_shims.resource_MilliValue" (dict "a" (list $rr.cpu.cores) ))) "r") | int64) -}} +{{- if (lt $coresInMillies_4 (1000 | int64)) -}} +{{- $_is_returning = true -}} +{{- (dict "r" ((1 | int64) | int64)) | toJson -}} +{{- break -}} +{{- end -}} +{{- $_is_returning = true -}} +{{- (dict "r" (((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rr.cpu.cores) ))) "r") | int64) | int64)) | toJson -}} +{{- break -}} +{{- end -}} +{{- end -}} + {{- define "redpanda.RedpandaResources.memory" -}} {{- $rr := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} +{{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} +{{- $_534_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.requests) "memory" "0") ))) "r") -}} +{{- $memReq := (index $_534_memReq_ok 0) -}} +{{- $ok := (index $_534_memReq_ok 1) -}} +{{- if (not $ok) -}} +{{- $_536_memReq_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list ($rr.limits) "memory" "0") ))) "r") -}} +{{- $memReq = (index $_536_memReq_ok 0) -}} +{{- $ok = (index $_536_memReq_ok 1) -}} +{{- end -}} +{{- if (not $ok) -}} +{{- $_is_returning = true -}} +{{- (dict "r" (coalesce nil)) | toJson -}} +{{- break -}} +{{- end -}} +{{- $memory := (((mulf (((get (fromJson (include "_shims.resource_Value" (dict "a" (list $memReq) ))) "r") | int64) | float64) 0.90) | float64) | int64) -}} +{{- $_is_returning = true -}} +{{- (dict "r" ((div $memory ((mul (1024 | int) (1024 | int)))) | int64)) | toJson -}} +{{- break -}} +{{- end -}} {{- $memory := ((0 | int64) | int64) -}} {{- $containerMemory := ((get (fromJson (include "redpanda.RedpandaResources.containerMemory" (dict "a" (list $rr) ))) "r") | int64) -}} -{{- $rpMem_3 := $rr.memory.redpanda -}} -{{- if (and (ne (toJson $rpMem_3) "null") (ne (toJson $rpMem_3.memory) "null")) -}} -{{- $memory = ((div ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rpMem_3.memory) ))) "r") | int64) ((mul (1024 | int) (1024 | int)))) | int64) -}} +{{- $rpMem_5 := $rr.memory.redpanda -}} +{{- if (and (ne (toJson $rpMem_5) "null") (ne (toJson $rpMem_5.memory) "null")) -}} +{{- $memory = ((div ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rpMem_5.memory) ))) "r") | int64) ((mul (1024 | int) (1024 | int)))) | int64) -}} {{- else -}} {{- $memory = (((mulf ($containerMemory | float64) 0.8) | float64) | int64) -}} {{- end -}} @@ -168,7 +254,7 @@ {{- if (lt $memory (256 | int64)) -}} {{- $_ := (fail (printf "%d is below the minimum value for Redpanda" $memory)) -}} {{- end -}} -{{- if (gt ((add $memory ((get (fromJson (include "redpanda.RedpandaResources.reserveMemory" (dict "a" (list $rr) ))) "r") | int64)) | int64) $containerMemory) -}} +{{- if (gt ((add $memory (((get (fromJson (include "redpanda.RedpandaResources.reserveMemory" (dict "a" (list $rr) ))) "r") | int64) | int64)) | int64) $containerMemory) -}} {{- $_ := (fail (printf "Not enough container memory for Redpanda memory values where Redpanda: %d, reserve: %d, container: %d" $memory ((get (fromJson (include "redpanda.RedpandaResources.reserveMemory" (dict "a" (list $rr) ))) "r") | int64) $containerMemory)) -}} {{- end -}} {{- $_is_returning = true -}} @@ -181,10 +267,15 @@ {{- $rr := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $rpMem_4 := $rr.memory.redpanda -}} -{{- if (and (ne (toJson $rpMem_4) "null") (ne (toJson $rpMem_4.reserveMemory) "null")) -}} +{{- if (and (ne (toJson $rr.limits) "null") (ne (toJson $rr.requests) "null")) -}} +{{- $_is_returning = true -}} +{{- (dict "r" (0 | int64)) | toJson -}} +{{- break -}} +{{- end -}} +{{- $rpMem_6 := $rr.memory.redpanda -}} +{{- if (and (ne (toJson $rpMem_6) "null") (ne (toJson $rpMem_6.reserveMemory) "null")) -}} {{- $_is_returning = true -}} -{{- (dict "r" ((div ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rpMem_4.reserveMemory) ))) "r") | int64) ((mul (1024 | int) (1024 | int)))) | int64)) | toJson -}} +{{- (dict "r" ((div ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $rpMem_6.reserveMemory) ))) "r") | int64) ((mul (1024 | int) (1024 | int)))) | int64)) | toJson -}} {{- break -}} {{- end -}} {{- $_is_returning = true -}} @@ -213,9 +304,9 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- $conf := (get (fromJson (include "redpanda.Storage.GetTieredStorageConfig" (dict "a" (list $s) ))) "r") -}} -{{- $_514_b_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $conf "cloud_storage_enabled" (coalesce nil)) ))) "r") -}} -{{- $b := (index $_514_b_ok 0) -}} -{{- $ok := (index $_514_b_ok 1) -}} +{{- $_654_b_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $conf "cloud_storage_enabled" (coalesce nil)) ))) "r") -}} +{{- $b := (index $_654_b_ok 0) -}} +{{- $ok := (index $_654_b_ok 1) -}} {{- $_is_returning = true -}} {{- (dict "r" (and $ok (get (fromJson (include "_shims.typeassertion" (dict "a" (list "bool" $b) ))) "r"))) | toJson -}} {{- break -}} @@ -260,21 +351,21 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- $values := $dot.Values.AsMap -}} -{{- $_543_dir_5_ok_6 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $values.config.node "cloud_storage_cache_directory") "") ))) "r") -}} -{{- $dir_5 := (index $_543_dir_5_ok_6 0) -}} -{{- $ok_6 := (index $_543_dir_5_ok_6 1) -}} -{{- if $ok_6 -}} +{{- $_683_dir_7_ok_8 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $values.config.node "cloud_storage_cache_directory") "") ))) "r") -}} +{{- $dir_7 := (index $_683_dir_7_ok_8 0) -}} +{{- $ok_8 := (index $_683_dir_7_ok_8 1) -}} +{{- if $ok_8 -}} {{- $_is_returning = true -}} -{{- (dict "r" $dir_5) | toJson -}} +{{- (dict "r" $dir_7) | toJson -}} {{- break -}} {{- end -}} {{- $tieredConfig := (get (fromJson (include "redpanda.Storage.GetTieredStorageConfig" (dict "a" (list $values.storage) ))) "r") -}} -{{- $_552_dir_7_ok_8 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $tieredConfig "cloud_storage_cache_directory") "") ))) "r") -}} -{{- $dir_7 := (index $_552_dir_7_ok_8 0) -}} -{{- $ok_8 := (index $_552_dir_7_ok_8 1) -}} -{{- if $ok_8 -}} +{{- $_692_dir_9_ok_10 := (get (fromJson (include "_shims.typetest" (dict "a" (list "string" (index $tieredConfig "cloud_storage_cache_directory") "") ))) "r") -}} +{{- $dir_9 := (index $_692_dir_9_ok_10 0) -}} +{{- $ok_10 := (index $_692_dir_9_ok_10 1) -}} +{{- if $ok_10 -}} {{- $_is_returning = true -}} -{{- (dict "r" $dir_7) | toJson -}} +{{- (dict "r" $dir_9) | toJson -}} {{- break -}} {{- end -}} {{- $_is_returning = true -}} @@ -371,9 +462,9 @@ {{- $result := (dict ) -}} {{- $s := (toJson $t) -}} {{- $tune := (fromJson $s) -}} -{{- $_778_m_ok := (get (fromJson (include "_shims.typetest" (dict "a" (list (printf "map[%s]%s" "string" "interface {}") $tune (coalesce nil)) ))) "r") -}} -{{- $m := (index $_778_m_ok 0) -}} -{{- $ok := (index $_778_m_ok 1) -}} +{{- $_918_m_ok := (get (fromJson (include "_shims.typetest" (dict "a" (list (printf "map[%s]%s" "string" "interface {}") $tune (coalesce nil)) ))) "r") -}} +{{- $m := (index $_918_m_ok 0) -}} +{{- $ok := (index $_918_m_ok 1) -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} {{- (dict "r" (dict )) | toJson -}} @@ -509,10 +600,10 @@ {{- $seen := (dict ) -}} {{- $deduped := (coalesce nil) -}} {{- range $_, $item := $items -}} -{{- $_895___ok_9 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $seen $item.key false) ))) "r") -}} -{{- $_ := (index $_895___ok_9 0) -}} -{{- $ok_9 := (index $_895___ok_9 1) -}} -{{- if $ok_9 -}} +{{- $_1035___ok_11 := (get (fromJson (include "_shims.dicttest" (dict "a" (list $seen $item.key false) ))) "r") -}} +{{- $_ := (index $_1035___ok_11 0) -}} +{{- $ok_11 := (index $_1035___ok_11 1) -}} +{{- if $ok_11 -}} {{- continue -}} {{- end -}} {{- $deduped = (concat (default (list ) $deduped) (list $item)) -}} @@ -564,9 +655,9 @@ {{- $name := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_986_cert_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $m $name (dict "enabled" (coalesce nil) "caEnabled" false "applyInternalDNSNames" (coalesce nil) "duration" "" "issuerRef" (coalesce nil) "secretRef" (coalesce nil) "clientSecretRef" (coalesce nil) )) ))) "r") -}} -{{- $cert := (index $_986_cert_ok 0) -}} -{{- $ok := (index $_986_cert_ok 1) -}} +{{- $_1126_cert_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $m $name (dict "enabled" (coalesce nil) "caEnabled" false "applyInternalDNSNames" (coalesce nil) "duration" "" "issuerRef" (coalesce nil) "secretRef" (coalesce nil) "clientSecretRef" (coalesce nil) )) ))) "r") -}} +{{- $cert := (index $_1126_cert_ok 0) -}} +{{- $ok := (index $_1126_cert_ok 1) -}} {{- if (not $ok) -}} {{- $_ := (fail (printf "Certificate %q referenced, but not found in the tls.certs map" $name)) -}} {{- end -}} @@ -914,9 +1005,9 @@ {{- if $saslEnabled -}} {{- $_ := (set $internal "authentication_method" "http_basic") -}} {{- end -}} -{{- $am_10 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_10 "") -}} -{{- $_ := (set $internal "authentication_method" $am_10) -}} +{{- $am_12 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_12 "") -}} +{{- $_ := (set $internal "authentication_method" $am_12) -}} {{- end -}} {{- $result := (list $internal) -}} {{- range $k, $l := $l.external -}} @@ -927,9 +1018,9 @@ {{- if $saslEnabled -}} {{- $_ := (set $listener "authentication_method" "http_basic") -}} {{- end -}} -{{- $am_11 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_11 "") -}} -{{- $_ := (set $listener "authentication_method" $am_11) -}} +{{- $am_13 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_13 "") -}} +{{- $_ := (set $listener "authentication_method" $am_13) -}} {{- end -}} {{- $result = (concat (default (list ) $result) (list $listener)) -}} {{- end -}} @@ -1012,9 +1103,9 @@ {{- if (get (fromJson (include "redpanda.Auth.IsSASLEnabled" (dict "a" (list $auth) ))) "r") -}} {{- $_ := (set $internal "authentication_method" "sasl") -}} {{- end -}} -{{- $am_12 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_12 "") -}} -{{- $_ := (set $internal "authentication_method" $am_12) -}} +{{- $am_14 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_14 "") -}} +{{- $_ := (set $internal "authentication_method" $am_14) -}} {{- end -}} {{- $kafka := (list $internal) -}} {{- range $k, $l := $l.external -}} @@ -1025,9 +1116,9 @@ {{- if (get (fromJson (include "redpanda.Auth.IsSASLEnabled" (dict "a" (list $auth) ))) "r") -}} {{- $_ := (set $listener "authentication_method" "sasl") -}} {{- end -}} -{{- $am_13 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_13 "") -}} -{{- $_ := (set $listener "authentication_method" $am_13) -}} +{{- $am_15 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_15 "") -}} +{{- $_ := (set $listener "authentication_method" $am_15) -}} {{- end -}} {{- $kafka = (concat (default (list ) $kafka) (list $listener)) -}} {{- end -}} @@ -1159,9 +1250,9 @@ {{- if $saslEnabled -}} {{- $_ := (set $internal "authentication_method" "http_basic") -}} {{- end -}} -{{- $am_14 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_14 "") -}} -{{- $_ := (set $internal "authentication_method" $am_14) -}} +{{- $am_16 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_16 "") -}} +{{- $_ := (set $internal "authentication_method" $am_16) -}} {{- end -}} {{- $result := (list $internal) -}} {{- range $k, $l := $l.external -}} @@ -1172,9 +1263,9 @@ {{- if $saslEnabled -}} {{- $_ := (set $listener "authentication_method" "http_basic") -}} {{- end -}} -{{- $am_15 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} -{{- if (ne $am_15 "") -}} -{{- $_ := (set $listener "authentication_method" $am_15) -}} +{{- $am_17 := (get (fromJson (include "_shims.ptr_Deref" (dict "a" (list $l.authenticationMethod "") ))) "r") -}} +{{- if (ne $am_17 "") -}} +{{- $_ := (set $listener "authentication_method" $am_17) -}} {{- end -}} {{- $result = (concat (default (list ) $result) (list $listener)) -}} {{- end -}} @@ -1309,10 +1400,10 @@ {{- $result := (dict ) -}} {{- range $k, $v := $c -}} {{- if (not (empty $v)) -}} -{{- $_1820___ok_16 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v) ))) "r") -}} -{{- $_ := ((index $_1820___ok_16 0) | float64) -}} -{{- $ok_16 := (index $_1820___ok_16 1) -}} -{{- if $ok_16 -}} +{{- $_1960___ok_18 := (get (fromJson (include "_shims.asnumeric" (dict "a" (list $v) ))) "r") -}} +{{- $_ := ((index $_1960___ok_18 0) | float64) -}} +{{- $ok_18 := (index $_1960___ok_18 1) -}} +{{- if $ok_18 -}} {{- $_ := (set $result $k $v) -}} {{- else -}}{{- if (kindIs "bool" $v) -}} {{- $_ := (set $result $k $v) -}} @@ -1337,11 +1428,11 @@ {{- $_is_returning := false -}} {{- $result := (dict ) -}} {{- range $k, $v := $c -}} -{{- $_1840_b_17_ok_18 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false) ))) "r") -}} -{{- $b_17 := (index $_1840_b_17_ok_18 0) -}} -{{- $ok_18 := (index $_1840_b_17_ok_18 1) -}} -{{- if $ok_18 -}} -{{- $_ := (set $result $k $b_17) -}} +{{- $_1980_b_19_ok_20 := (get (fromJson (include "_shims.typetest" (dict "a" (list "bool" $v false) ))) "r") -}} +{{- $b_19 := (index $_1980_b_19_ok_20 0) -}} +{{- $ok_20 := (index $_1980_b_19_ok_20 1) -}} +{{- if $ok_20 -}} +{{- $_ := (set $result $k $b_19) -}} {{- continue -}} {{- end -}} {{- if (not (empty $v)) -}} @@ -1382,15 +1473,15 @@ {{- $config := (index .a 1) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1885___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil)) ))) "r") -}} -{{- $_ := (index $_1885___hasAccessKey 0) -}} -{{- $hasAccessKey := (index $_1885___hasAccessKey 1) -}} -{{- $_1886___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil)) ))) "r") -}} -{{- $_ := (index $_1886___hasSecretKey 0) -}} -{{- $hasSecretKey := (index $_1886___hasSecretKey 1) -}} -{{- $_1887___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil)) ))) "r") -}} -{{- $_ := (index $_1887___hasSharedKey 0) -}} -{{- $hasSharedKey := (index $_1887___hasSharedKey 1) -}} +{{- $_2025___hasAccessKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_access_key" (coalesce nil)) ))) "r") -}} +{{- $_ := (index $_2025___hasAccessKey 0) -}} +{{- $hasAccessKey := (index $_2025___hasAccessKey 1) -}} +{{- $_2026___hasSecretKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_secret_key" (coalesce nil)) ))) "r") -}} +{{- $_ := (index $_2026___hasSecretKey 0) -}} +{{- $hasSecretKey := (index $_2026___hasSecretKey 1) -}} +{{- $_2027___hasSharedKey := (get (fromJson (include "_shims.dicttest" (dict "a" (list $config "cloud_storage_azure_shared_key" (coalesce nil)) ))) "r") -}} +{{- $_ := (index $_2027___hasSharedKey 0) -}} +{{- $hasSharedKey := (index $_2027___hasSharedKey 1) -}} {{- $envvars := (coalesce nil) -}} {{- if (and (not $hasAccessKey) (get (fromJson (include "redpanda.SecretRef.IsValid" (dict "a" (list $tsc.accessKey) ))) "r")) -}} {{- $envvars = (concat (default (list ) $envvars) (list (mustMergeOverwrite (dict "name" "" ) (dict "name" "REDPANDA_CLOUD_STORAGE_ACCESS_KEY" "valueFrom" (get (fromJson (include "redpanda.SecretRef.AsSource" (dict "a" (list $tsc.accessKey) ))) "r") )))) -}} @@ -1413,12 +1504,12 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1923___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil)) ))) "r") -}} -{{- $_ := (index $_1923___containerExists 0) -}} -{{- $containerExists := (index $_1923___containerExists 1) -}} -{{- $_1924___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil)) ))) "r") -}} -{{- $_ := (index $_1924___accountExists 0) -}} -{{- $accountExists := (index $_1924___accountExists 1) -}} +{{- $_2063___containerExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_container" (coalesce nil)) ))) "r") -}} +{{- $_ := (index $_2063___containerExists 0) -}} +{{- $containerExists := (index $_2063___containerExists 1) -}} +{{- $_2064___accountExists := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c "cloud_storage_azure_storage_account" (coalesce nil)) ))) "r") -}} +{{- $_ := (index $_2064___accountExists 0) -}} +{{- $accountExists := (index $_2064___accountExists 1) -}} {{- $_is_returning = true -}} {{- (dict "r" (and $containerExists $accountExists)) | toJson -}} {{- break -}} @@ -1429,9 +1520,9 @@ {{- $c := (index .a 0) -}} {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} -{{- $_1929_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil)) ))) "r") -}} -{{- $value := (index $_1929_value_ok 0) -}} -{{- $ok := (index $_1929_value_ok 1) -}} +{{- $_2069_value_ok := (get (fromJson (include "_shims.dicttest" (dict "a" (list $c `cloud_storage_cache_size` (coalesce nil)) ))) "r") -}} +{{- $value := (index $_2069_value_ok 0) -}} +{{- $ok := (index $_2069_value_ok 1) -}} {{- if (not $ok) -}} {{- $_is_returning = true -}} {{- (dict "r" (coalesce nil)) | toJson -}} @@ -1456,9 +1547,9 @@ {{- if $_is_returning -}} {{- break -}} {{- end -}} -{{- $size_19 := (get (fromJson (include "redpanda.TieredStorageConfig.CloudStorageCacheSize" (dict "a" (list (deepCopy $c)) ))) "r") -}} -{{- if (ne (toJson $size_19) "null") -}} -{{- $_ := (set $config "cloud_storage_cache_size" ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $size_19) ))) "r") | int64)) -}} +{{- $size_21 := (get (fromJson (include "redpanda.TieredStorageConfig.CloudStorageCacheSize" (dict "a" (list (deepCopy $c)) ))) "r") -}} +{{- if (ne (toJson $size_21) "null") -}} +{{- $_ := (set $config "cloud_storage_cache_size" ((get (fromJson (include "_shims.resource_Value" (dict "a" (list $size_21) ))) "r") | int64)) -}} {{- end -}} {{- $_is_returning = true -}} {{- (dict "r" $config) | toJson -}} diff --git a/charts/redpanda/testdata/template-cases.golden.txtar b/charts/redpanda/testdata/template-cases.golden.txtar index e86faa10e..443daebdd 100644 --- a/charts/redpanda/testdata/template-cases.golden.txtar +++ b/charts/redpanda/testdata/template-cases.golden.txtar @@ -42784,8 +42784,8 @@ data: additional_start_flags: - --default-log-level=info - --memory=2048M - - --smp=99 - --reserve-memory=9999 + - --smp=99 admin_api: addresses: - redpanda-0.redpanda.default.svc.cluster.local.:9644 @@ -43178,7 +43178,7 @@ spec: template: metadata: annotations: - config.redpanda.com/checksum: 154bbf670b25a84e084af1f1a6777450206fdb2f3014a881f8350fe0371cdc54 + config.redpanda.com/checksum: 254fcfce7b5ac22dbb5c85515910cfa4a9a3ce14c1453ff29f044e4ff9b60160 creationTimestamp: null labels: app.kubernetes.io/component: redpanda-statefulset @@ -74737,12 +74737,12 @@ data: port: 33145 rpk: additional_start_flags: + - --abort-on-seastar-bad-alloc - --default-log-level=info - - --smp=1 + - --dump-memory-diagnostics-on-alloc-failure-kind=all - --memory=5G - --reserve-memory=1G - - --abort-on-seastar-bad-alloc - - --dump-memory-diagnostics-on-alloc-failure-kind=all + - --smp=1 admin_api: addresses: - redpanda-0.redpanda.default.svc.cluster.local.:9644 @@ -74997,7 +74997,7 @@ spec: template: metadata: annotations: - config.redpanda.com/checksum: 69e9dd514b47e871ffed51f91c705708156ff083623eccd0d138847850a6858b + config.redpanda.com/checksum: a1ecaf6efd1c355f483958b8c2eeb52695110b1b65366d9d0a0571b067fa6283 creationTimestamp: null labels: app.kubernetes.io/component: redpanda-statefulset diff --git a/charts/redpanda/testdata/template-cases.txtar b/charts/redpanda/testdata/template-cases.txtar index a16904fe9..cad21b9e2 100644 --- a/charts/redpanda/testdata/template-cases.txtar +++ b/charts/redpanda/testdata/template-cases.txtar @@ -1123,4 +1123,41 @@ tls: issuerRef: kind: ClusterIssuer name: letsencrypt-production - enabled: true \ No newline at end of file + enabled: true + +-- direct-resources-examples -- +# ASSERT-NO-ERROR +# ASSERT-FIELD-EQUALS ["apps/v1/StatefulSet", "default/redpanda", "{.spec.template.spec.containers[0].resources.limits}", {"memory": "10Gi"}] +# ASSERT-FIELD-EQUALS ["apps/v1/StatefulSet", "default/redpanda", "{.spec.template.spec.containers[0].resources.requests}", {"cpu": "5500m"}] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--smp=5"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--memory=9216M"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--reserve-memory=0M"] +resources: + limits: + memory: 10Gi + requests: + cpu: 5500m + +-- direct-resources-none-provided -- +# ASSERT-NO-ERROR +# ASSERT-FIELD-EQUALS ["apps/v1/StatefulSet", "default/redpanda", "{.spec.template.spec.containers[0].resources.limits}", {}] +# ASSERT-FIELD-EQUALS ["apps/v1/StatefulSet", "default/redpanda", "{.spec.template.spec.containers[0].resources.requests}", {}] +# ASSERT-FIELD-NOT-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--smp"] +# ASSERT-FIELD-NOT-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--memory"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--reserve-memory=0M"] +resources: + limits: {} + requests: {} + +-- flag-overrides -- +# ASSERT-NO-ERROR +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--smp=67"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "enable_memory_locking: false"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "overprovisioned: true"] +# ASSERT-FIELD-CONTAINS ["v1/ConfigMap", "default/redpanda", "{.data.redpanda\\.yaml}", "--default-log-level=warn"] +statefulset: + additionalRedpandaCmdFlags: + - --smp=67 + - --lock-memory=false + - --overprovisioned + - --default-log-level warn diff --git a/charts/redpanda/values.go b/charts/redpanda/values.go index 88ef451b3..6eda868c0 100644 --- a/charts/redpanda/values.go +++ b/charts/redpanda/values.go @@ -302,11 +302,34 @@ type Monitoring struct { ScrapeInterval monitoringv1.Duration `json:"scrapeInterval" jsonschema:"required"` Labels map[string]string `json:"labels"` TLSConfig *monitoringv1.TLSConfig `json:"tlsConfig"` - //nolint:stylecheck - EnableHttp2 *bool `json:"enableHttp2"` + EnableHTTP2 *bool `json:"enableHttp2"` } +// RedpandaResources encapsulates the calculation of the redpanda container's +// [corev1.ResourceRequirements] and parameters such as `--memory`, +// `--reserve-memory`, and `--smp`. +// This calculation supports two modes: +// +// - Explicit mode (recommended): Activated when `Limits` and `Requests` are +// set. In this mode, the CLI flags are calculated directly based on the +// provided `Limits` and `Requests`. This mode ensures predictable resource +// allocation and is recommended for production environments. If additional +// tuning is required, the CLI flags can be manually overridden using +// `statefulset.additionalRedpandaCmdFlags`. +// +// - Legacy mode (default): Used when `Limits` and `Requests` are not set. +// In this mode, the container resources and CLI flags are calculated using +// built-in default logic, where 80% of the container's memory is allocated +// to Redpanda and the rest is reserved for system overhead. Legacy mode is +// intended for backward compatibility and less controlled environments. +// +// Explicit mode offers better control and aligns with Kubernetes best +// practices. Legacy mode is a fallback for users who have not defined `Limits` +// and `Requests`. type RedpandaResources struct { + Limits *corev1.ResourceList `json:"limits,omitempty"` + Requests *corev1.ResourceList `json:"requests,omitempty"` + CPU struct { Cores resource.Quantity `json:"cores" jsonschema:"required"` Overprovisioned *bool `json:"overprovisioned"` @@ -350,13 +373,6 @@ type RedpandaResources struct { // based on container memory. // Uncommenting this section and setting memory and reserveMemory values will disable // automatic calculation. - // - // If you are setting the following values manually, keep in mind the following guidelines. - // Getting this wrong may lead to performance issues, instability, and loss of data: - // The amount of memory to allocate to a container is determined by the sum of three values: - // 1. Redpanda (at least 2Gi per core, ~80% of the container's total memory) - // 2. Seastar subsystem (200Mi * 0.2% of the container's total memory, 200Mi < x < 1Gi) - // 3. Other container processes (whatever small amount remains) Redpanda *struct { // Memory for the Redpanda process. // This must be lower than the container's memory (resources.memory.container.min if provided, otherwise @@ -374,6 +390,14 @@ type RedpandaResources struct { } func (rr *RedpandaResources) GetResourceRequirements() corev1.ResourceRequirements { + // If Limits and Requests are specified, use them as is. + if rr.Limits != nil && rr.Requests != nil { + return corev1.ResourceRequirements{ + Limits: *rr.Limits, + Requests: *rr.Requests, + } + } + // Otherwise fallback to the historical behavior. reqs := corev1.ResourceRequirements{ Limits: corev1.ResourceList{ @@ -392,22 +416,49 @@ func (rr *RedpandaResources) GetResourceRequirements() corev1.ResourceRequiremen return reqs } -func (rr *RedpandaResources) GetRedpandaStartFlags() map[string]string { - flags := map[string]string{} +func (rr *RedpandaResources) GetRedpandaFlags() map[string]string { + flags := map[string]string{ + "--reserve-memory": fmt.Sprintf("%dM", rr.reserveMemory()), + } - if coresInMillies := rr.CPU.Cores.MilliValue(); coresInMillies < 1000 { - flags["smp"] = fmt.Sprintf("%d", 1) - } else { - flags["smp"] = fmt.Sprintf("%d", rr.CPU.Cores.Value()) + if smp := rr.smp(); smp != nil { + flags["--smp"] = fmt.Sprintf("%d", int64(*smp)) + } + + if memory := rr.memory(); memory != nil { + flags["--memory"] = fmt.Sprintf("%dM", int64(*memory)) + } + + // Only set lock-memory if Limits and Requests are NOT specified. It should + // otherwise be set through additionalRedpandaCmdFlags. + if rr.Limits == nil && rr.Requests == nil { + flags["--lock-memory"] = fmt.Sprintf("%v", ptr.Deref(rr.Memory.EnableMemoryLocking, false)) } - flags["memory"] = fmt.Sprintf("%dM", rr.memory()) - flags["reserve-memory"] = fmt.Sprintf("%dM", rr.reserveMemory()) + if rr.GetOverProvisionValue() { + flags["--overprovisioned"] = "" + } return flags } func (rr *RedpandaResources) GetOverProvisionValue() bool { + if rr.Limits != nil && rr.Requests != nil { + // Get CPU prioritizing requests, falling back to limits if not + // specified as kube-scheduler does. + cpuReq, ok := (*rr.Requests)[corev1.ResourceCPU] + if !ok { + cpuReq, ok = (*rr.Limits)[corev1.ResourceCPU] + } + + // If redpanda has been allocated less than 1 full CPU, set + // overprovisioned to true. + if ok && cpuReq.MilliValue() < 1000 { + return true + } + return false + } + if rr.CPU.Cores.MilliValue() < 1000 { return true } @@ -415,22 +466,113 @@ func (rr *RedpandaResources) GetOverProvisionValue() bool { return ptr.Deref(rr.CPU.Overprovisioned, false) } +func (rr *RedpandaResources) smp() *int64 { + if rr.Limits != nil && rr.Requests != nil { + // Get CPU prioritizing requests, falling back to limits if not + // specified as kube-scheduler does. This ordering also forces --smp to + // be <= the containers CPU limits. The other way around (limits + // fallback to requests; therefore --smp >= CPU limits) isn't useful. + cpuReq, ok := (*rr.Requests)[corev1.ResourceCPU] + if !ok { + cpuReq, ok = (*rr.Limits)[corev1.ResourceCPU] + } + + // If neither requests nor limits are set, don't set --smp. + if !ok { + return nil + } + + // If CPU limits/requests are defined, set --smp to max(1, floor(cpu)). + // + // Due to redpanda/seastar's per core model, we can't do much with + // fractional CPU values we need to round either up or down. Rounding + // up would result in utilizing too much CPU from the CRI perspective + // and cause throttling, so we round down and potentially waste some + // quota. + smp := cpuReq.MilliValue() / 1000 + if smp < 1 { + smp = 1 + } + return ptr.To(smp) + } + + if coresInMillies := rr.CPU.Cores.MilliValue(); coresInMillies < 1000 { + return ptr.To(int64(1)) + } + return ptr.To(int64(rr.CPU.Cores.Value())) +} + // memory returns the amount of memory for Redpanda process. It should be // passed to the `--memory` argument of the Redpanda process, see // RedpandaAdditionalStartFlags and rpk redpanda start documentation. // // https://docs.redpanda.com/current/reference/rpk/rpk-redpanda/rpk-redpanda-start/ -func (rr *RedpandaResources) memory() int64 { +func (rr *RedpandaResources) memory() *MebiBytes { + if rr.Limits != nil && rr.Requests != nil { + // `--memory` will be set to something < the container's + // resources.memory.limits value. + // We want to allocate seastar < memory than our limit for several reasons: + // 1. Seastar may slightly exceed this limit due to page tables and + // non-heap memory that's still accounted by cgroups. + // 2. resources.limits.memory applies to the entire container. We want + // to keep headroom to allow exec'ing into the container and for any + // exec probes. + // 3. emptyDir's storage is counted against the container's memory + // limits. We use these to store rendered versions of config files + // and therefore need to account for them. + // https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#memory-backed-emptydir + // The memory reservation is done by subtracting from `--memory` and + // always setting `--reserve-memory` to 0 rather than setting + // `--reserve-memory`. This is an easier mental model to follow as the + // `--reserve-memory` flag is exceptionally nuanced in practice and is + // meant to aid seastar running on an entire VM rather than in a + // container. + + // If either memory limit or requests are set, we take the minimum of + // the two (relying on the invariant enforced by Kubernetes that + // requests must be <= limits). + memReq, ok := (*rr.Requests)[corev1.ResourceMemory] + if !ok { + memReq, ok = (*rr.Limits)[corev1.ResourceMemory] + } + + // If neither requests nor limits are set, don't set --memory. + if !ok { + return nil + } + + // Here we perform our memory reservation. Historically, 80% of + // container memory was provided to Redpanda and some additional amount + // was removed due to the usage of `--reserve-memory`. We're largely + // blind to the lower limit of what's tolerated. + // This calculation, therefore, is a complete split ball. We expect + // this to change over time; ideally trending towards providing + // redpanda more memory. + // We intentionally err on the conservative side as we'd prefer to + // "waste" a few megs of memory rather than risking OOM kills. + // For simplicity, we're using a % as a static reservation would need + // to handle weird edge cases. + // + // redpanda get's 90% of the container limit. (It's better than the + // historic 80%). + memory := int64(float64(memReq.Value()) * 0.90) + + // Cast to Membibytes. + return ptr.To(memory / (1024 * 1024)) + } + + // Below we perform the calculations for the legacy resource mode. This + // calculation appears to be based on an incorrect understanding of the + // (admittedly convoluted) `--reserve-memory` seastar flag and is preserved + // solely for backwards compatibility. + // + // It segments out memory for: + // * Seastar/Redpanda (`--memory`) - .Memory.Redpanda OR 80% of memory + // * Seastar's "subsystem" (`--reserve-memory`) - .Memory.Reserve OR 200Mi + 0.2% of memory + // * Container processes (execing, hooks, probes, etc) - The leftovers from the above (if any) memory := int64(0) containerMemory := rr.containerMemory() - // This optional `redpanda` object allows you to specify the memory size for both the Redpanda - // process and the underlying reserved memory used by Seastar. - // - // The amount of memory to allocate to a container is determined by the sum of three values: - // 1. Redpanda (at least 2Gi per core, ~80% of the container's total memory) - // 2. Seastar subsystem (200Mi * 0.2% of the container's total memory, 200Mi < x < 1Gi) - // 3. Other container processes (whatever small amount remains) if rpMem := rr.Memory.Redpanda; rpMem != nil && rpMem.Memory != nil { memory = rpMem.Memory.Value() / (1024 * 1024) } else { @@ -445,11 +587,13 @@ func (rr *RedpandaResources) memory() int64 { panic(fmt.Sprintf("%d is below the minimum value for Redpanda", memory)) } - if memory+rr.reserveMemory() > containerMemory { + // NB: int64's are working around a bug in gotohelm's BinaryExpr detection + // with Alias types. + if memory+int64(rr.reserveMemory()) > containerMemory { panic(fmt.Sprintf("Not enough container memory for Redpanda memory values where Redpanda: %d, reserve: %d, container: %d", memory, rr.reserveMemory(), containerMemory)) } - return memory + return ptr.To(memory) } // reserveMemory returns the amount of memory that the Redpanda process will @@ -459,20 +603,17 @@ func (rr *RedpandaResources) memory() int64 { // start documentation. // // https://docs.redpanda.com/current/reference/rpk/rpk-redpanda/rpk-redpanda-start/ -func (rr *RedpandaResources) reserveMemory() int64 { - // This optional `redpanda` object allows you to specify the memory size for both the Redpanda - // process and the underlying reserved memory used by Seastar. - // - // The amount of memory to allocate to a container is determined by the sum of three values: - // 1. Redpanda (at least 2Gi per core, ~80% of the container's total memory) - // 2. Seastar subsystem (200Mi * 0.2% of the container's total memory, 200Mi < x < 1Gi) - // 3. Other container processes (whatever small amount remains) +func (rr *RedpandaResources) reserveMemory() MebiBytes { + if rr.Limits != nil && rr.Requests != nil { + // See [RedpandaResources.memory] for details here. + return 0 + } + + // See [RedpandaResources.memory] for details here. if rpMem := rr.Memory.Redpanda; rpMem != nil && rpMem.ReserveMemory != nil { return rpMem.ReserveMemory.Value() / (1024 * 1024) } - // If Redpanda is omitted (default behavior), memory sizes are calculated automatically - // based on 0.2% of container memory plus 200 Mi. return int64(float64(rr.containerMemory())*0.002) + 200 } diff --git a/charts/redpanda/values.schema.json b/charts/redpanda/values.schema.json index 81af69a1c..233361d29 100644 --- a/charts/redpanda/values.schema.json +++ b/charts/redpanda/values.schema.json @@ -14019,6 +14019,20 @@ ], "type": "object" }, + "limits": { + "additionalProperties": { + "oneOf": [ + { + "type": "integer" + }, + { + "pattern": "^[0-9]+(\\.[0-9]){0,1}(m|k|M|G|T|P|Ki|Mi|Gi|Ti|Pi)?$", + "type": "string" + } + ] + }, + "type": "object" + }, "memory": { "properties": { "container": { @@ -14086,6 +14100,20 @@ "container" ], "type": "object" + }, + "requests": { + "additionalProperties": { + "oneOf": [ + { + "type": "integer" + }, + { + "pattern": "^[0-9]+(\\.[0-9]){0,1}(m|k|M|G|T|P|Ki|Mi|Gi|Ti|Pi)?$", + "type": "string" + } + ] + }, + "type": "object" } }, "required": [ diff --git a/charts/redpanda/values.yaml b/charts/redpanda/values.yaml index f9128fafc..5ce371686 100644 --- a/charts/redpanda/values.yaml +++ b/charts/redpanda/values.yaml @@ -346,9 +346,36 @@ monitoring: # keyFile: /etc/prom-certs/key.pem # -- Pod resource management. -# This section simplifies resource allocation -# by providing a single location where resources are defined. -# Helm sets these resource values within the `statefulset.yaml` and `configmap.yaml` templates. +# @raw +# This section simplifies resource allocation for the redpanda container by +# providing a single location where resources are defined. +# +# Resources may be specified by either setting `resources.cpu` and +# `resources.memory` (the default) or by setting `resources.requests` and +# `resources.limits`. +# +# For details on `resources.cpu` and `resources.memory`, see their respective +# documentation below. +# +# When `resources.limits` and `resources.requests` are set, the redpanda +# container's resources will be set to exactly the provided values. This allows +# users to granularly control limits and requests to best suit their use case. +# For example: `resources.requests.cpu` may be set without setting +# `resources.limits.cpu` to avoid the potential of CPU throttling. +# +# Redpanda's resource related CLI flags will then be calculated as follows: +# * `--smp max(1, floor(coalesce(resources.requests.cpu, resources.limits.cpu)))` +# * `--memory coalesce(resources.requests.memory, resources.limits.memory) * 90%` +# * `--reserve-memory 0` +# * `--overprovisioned coalesce(resources.requests.cpu, resources.limits.cpu) < 1000m` +# +# If neither a request nor a limit is provided for cpu or memory, the +# corresponding flag will be omitted. As a result, setting `resources.limits` +# and `resources.requests` to `{}` will result in redpanda being run without +# `--smp` or `--memory`. (This is not recommended). +# +# If the computed CLI flags are undesirable, they may be overridden by +# specifying the desired value through `statefulset.additionalRedpandaCmdFlags`. # # The default values are for a development environment. # Production-level values and other considerations are documented, @@ -356,6 +383,8 @@ monitoring: # For details, # see the [Pod resources documentation](https://docs.redpanda.com/docs/manage/kubernetes/manage-resources/). resources: + # limits: null + # requests: null # # -- CPU resources. # For details, diff --git a/charts/redpanda/values_partial.gen.go b/charts/redpanda/values_partial.gen.go index fbb4c7206..9f20ef771 100644 --- a/charts/redpanda/values_partial.gen.go +++ b/charts/redpanda/values_partial.gen.go @@ -127,11 +127,13 @@ type PartialMonitoring struct { ScrapeInterval *monitoringv1.Duration "json:\"scrapeInterval,omitempty\" jsonschema:\"required\"" Labels map[string]string "json:\"labels,omitempty\"" TLSConfig *monitoringv1.TLSConfig "json:\"tlsConfig,omitempty\"" - EnableHttp2 *bool "json:\"enableHttp2,omitempty\"" + EnableHTTP2 *bool "json:\"enableHttp2,omitempty\"" } type PartialRedpandaResources struct { - CPU *struct { + Limits *corev1.ResourceList "json:\"limits,omitempty\"" + Requests *corev1.ResourceList "json:\"requests,omitempty\"" + CPU *struct { Cores *resource.Quantity "json:\"cores,omitempty\" jsonschema:\"required\"" Overprovisioned *bool "json:\"overprovisioned,omitempty\"" } "json:\"cpu,omitempty\" jsonschema:\"required\"" diff --git a/charts/redpanda/values_test.go b/charts/redpanda/values_test.go index c6f0115bc..0fbd7a947 100644 --- a/charts/redpanda/values_test.go +++ b/charts/redpanda/values_test.go @@ -13,8 +13,10 @@ import ( "strings" "testing" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/utils/ptr" ) @@ -618,3 +620,87 @@ func TestTieredStorageConfigCreds(t *testing.T) { }) } } + +func TestRedpandaResources_RedpandaFlags(t *testing.T) { + cases := []struct { + Resources RedpandaResources + Expected map[string]string + }{ + { + Resources: RedpandaResources{ + Limits: &corev1.ResourceList{}, + Requests: &corev1.ResourceList{}, + }, + Expected: map[string]string{ + "--reserve-memory": "0M", // Always set when Limits && Requests != nil. + // No other flags set as there's nothing to base them off of (Not recommended). + }, + }, + { + // overprovisioned is only set if CPU < 1000m. + Resources: RedpandaResources{ + Limits: &corev1.ResourceList{}, + Requests: &corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("500m"), + }, + }, + Expected: map[string]string{ + "--reserve-memory": "0M", // Always set when Limits && Requests != nil. + "--smp": "1", + "--overprovisioned": "", + }, + }, + { + Resources: RedpandaResources{ + Limits: &corev1.ResourceList{}, + Requests: &corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("2500m"), + corev1.ResourceMemory: resource.MustParse("10Gi"), + }, + }, + Expected: map[string]string{ + "--reserve-memory": "0M", + "--smp": "2", // floor(CPU) + "--memory": "9216M", // memory * 90% + }, + }, + { + // Limits are taken if requests aren't specified. + Resources: RedpandaResources{ + Limits: &corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("3"), + corev1.ResourceMemory: resource.MustParse("20Gi"), + }, + Requests: &corev1.ResourceList{}, + }, + Expected: map[string]string{ + "--reserve-memory": "0M", + "--smp": "3", // floor(CPU) + "--memory": "18432M", // memory * 90% + }, + }, + { + // Showcase that Requests are taken for CLI params in favor of limits. + Resources: RedpandaResources{ + Limits: &corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("10"), + corev1.ResourceMemory: resource.MustParse("200Gi"), + }, + Requests: &corev1.ResourceList{ + corev1.ResourceCPU: resource.MustParse("5"), + corev1.ResourceMemory: resource.MustParse("100Gi"), + }, + }, + Expected: map[string]string{ + "--reserve-memory": "0M", + "--smp": "5", // floor(CPU) + "--memory": "92160M", // memory * 90% + }, + }, + } + + for _, tc := range cases { + flags := tc.Resources.GetRedpandaFlags() + assert.Equal(t, tc.Expected, flags) + } +}