From 3a4995efb899f02b8e6a1863e36c63f302fd2065 Mon Sep 17 00:00:00 2001 From: nhamza Date: Mon, 4 May 2026 11:51:58 +0300 Subject: [PATCH 1/9] USHIFT-6912: Add DNS deployment resource configuration support Allow users to configure CPU and memory resources for the dns container in the dns-default DaemonSet via dns.resources in config.yaml. Defaults to the current hardcoded values (cpu=50m, memory=70Mi requests, no limits) when not configured. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../openshift-dns/dns/daemonset.yaml | 13 +- .../config/config-openapi-spec.json | 20 +++ docs/user/howto_config.md | 6 + packaging/microshift/config.yaml | 10 ++ pkg/components/controllers.go | 9 +- pkg/config/config.go | 18 +++ pkg/config/config_test.go | 116 ++++++++++++++++++ pkg/config/dns.go | 58 ++++++++- 8 files changed, 245 insertions(+), 5 deletions(-) diff --git a/assets/components/openshift-dns/dns/daemonset.yaml b/assets/components/openshift-dns/dns/daemonset.yaml index 5faae9a3e8..8e8b8b5d8a 100644 --- a/assets/components/openshift-dns/dns/daemonset.yaml +++ b/assets/components/openshift-dns/dns/daemonset.yaml @@ -57,8 +57,17 @@ spec: failureThreshold: 5 resources: requests: - cpu: 50m - memory: 70Mi + cpu: {{ .DNSCPURequest }} + memory: {{ .DNSMemoryRequest }} + {{- if .DNSHasLimits }} + limits: + {{- if .DNSCPULimit }} + cpu: {{ .DNSCPULimit }} + {{- end }} + {{- if .DNSMemoryLimit }} + memory: {{ .DNSMemoryLimit }} + {{- end }} + {{- end }} securityContext: readOnlyRootFilesystem: true image: '{{ .ReleaseImage.coredns }}' diff --git a/cmd/generate-config/config/config-openapi-spec.json b/cmd/generate-config/config/config-openapi-spec.json index 901548610d..7783407006 100755 --- a/cmd/generate-config/config/config-openapi-spec.json +++ b/cmd/generate-config/config/config-openapi-spec.json @@ -266,6 +266,26 @@ "example": "Enabled" } } + }, + "resources": { + "description": "Resources configures the CPU and memory resources for the dns container.", + "type": "object", + "properties": { + "limits": { + "description": "Limits specifies the maximum resources the dns container can use.\nValid keys are \"cpu\" and \"memory\". Values must be valid Kubernetes resource quantities.\nWhen not set, no limits are applied.", + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "requests": { + "description": "Requests specifies the minimum resources required for the dns container.\nValid keys are \"cpu\" and \"memory\". Values must be valid Kubernetes resource quantities.\nWhen not set, defaults to cpu=50m, memory=70Mi.", + "type": "object", + "additionalProperties": { + "type": "string" + } + } + } } } }, diff --git a/docs/user/howto_config.md b/docs/user/howto_config.md index cbed9baa28..16f7183bc9 100644 --- a/docs/user/howto_config.md +++ b/docs/user/howto_config.md @@ -43,6 +43,9 @@ dns: hosts: file: "" status: "" + resources: + limits: {} + requests: {} etcd: memoryLimitMB: 0 genericDevicePlugin: @@ -201,6 +204,9 @@ dns: hosts: file: /etc/hosts status: Disabled + resources: + limits: {} + requests: {} etcd: memoryLimitMB: 0 genericDevicePlugin: diff --git a/packaging/microshift/config.yaml b/packaging/microshift/config.yaml index fd45f1a37a..2239a1e9b4 100644 --- a/packaging/microshift/config.yaml +++ b/packaging/microshift/config.yaml @@ -85,6 +85,16 @@ dns: # example: # Enabled status: Disabled + # Resources configures the CPU and memory resources for the dns container. + resources: + # Limits specifies the maximum resources the dns container can use. + # Valid keys are "cpu" and "memory". Values must be valid Kubernetes resource quantities. + # When not set, no limits are applied. + limits: {} + # Requests specifies the minimum resources required for the dns container. + # Valid keys are "cpu" and "memory". Values must be valid Kubernetes resource quantities. + # When not set, defaults to cpu=50m, memory=70Mi. + requests: {} etcd: # Set a memory limit on the etcd process; etcd will begin paging # memory when it gets to this value. 0 means no limit. diff --git a/pkg/components/controllers.go b/pkg/components/controllers.go index e9bdf7af50..d5113503df 100644 --- a/pkg/components/controllers.go +++ b/pkg/components/controllers.go @@ -304,8 +304,13 @@ func startDNSController(ctx context.Context, cfg *config.Config, kubeconfigPath } extraParams := assets.RenderParams{ - "ClusterIP": cfg.Network.DNS, - "HostsEnabled": cfg.DNS.Hosts.Status == config.HostsStatusEnabled, + "ClusterIP": cfg.Network.DNS, + "HostsEnabled": cfg.DNS.Hosts.Status == config.HostsStatusEnabled, + "DNSCPURequest": cfg.DNS.Resources.Requests["cpu"], + "DNSMemoryRequest": cfg.DNS.Resources.Requests["memory"], + "DNSCPULimit": cfg.DNS.Resources.Limits["cpu"], + "DNSMemoryLimit": cfg.DNS.Resources.Limits["memory"], + "DNSHasLimits": len(cfg.DNS.Resources.Limits) > 0, } if err := assets.ApplyServices(ctx, svc, renderTemplate, renderParamsFromConfig(cfg, extraParams), kubeconfigPath); err != nil { diff --git a/pkg/config/config.go b/pkg/config/config.go index cffb723f2c..54498be5cb 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -439,6 +439,24 @@ func (c *Config) incorporateUserSettings(u *Config) { c.DNS.Hosts.File = u.DNS.Hosts.File } } + + // DNS resource configuration - merge key-by-key to preserve defaults + if u.DNS.Resources.Requests != nil { + if c.DNS.Resources.Requests == nil { + c.DNS.Resources.Requests = make(map[string]string) + } + for k, v := range u.DNS.Resources.Requests { + c.DNS.Resources.Requests[k] = v + } + } + if u.DNS.Resources.Limits != nil { + if c.DNS.Resources.Limits == nil { + c.DNS.Resources.Limits = make(map[string]string) + } + for k, v := range u.DNS.Resources.Limits { + c.DNS.Resources.Limits[k] = v + } + } if u.ApiServer.FeatureGates.FeatureSet != "" { c.ApiServer.FeatureGates.FeatureSet = u.ApiServer.FeatureGates.FeatureSet } diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 523a53c69b..7ec928a506 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -79,6 +79,63 @@ func TestGetActiveConfigFromYAML(t *testing.T) { return c }(), }, + { + name: "dns-resources-requests", + config: dedent(` + dns: + resources: + requests: + cpu: "100m" + memory: "150Mi" + `), + expected: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests = map[string]string{ + "cpu": "100m", + "memory": "150Mi", + } + return c + }(), + }, + { + name: "dns-resources-partial-request", + config: dedent(` + dns: + resources: + requests: + cpu: "100m" + `), + expected: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["cpu"] = "100m" + return c + }(), + }, + { + name: "dns-resources-with-limits", + config: dedent(` + dns: + resources: + requests: + cpu: "100m" + memory: "150Mi" + limits: + cpu: "200m" + memory: "256Mi" + `), + expected: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests = map[string]string{ + "cpu": "100m", + "memory": "150Mi", + } + c.DNS.Resources.Limits = map[string]string{ + "cpu": "200m", + "memory": "256Mi", + } + return c + }(), + }, { name: "network", config: dedent(` @@ -904,6 +961,65 @@ func TestValidate(t *testing.T) { }(), expectErr: true, }, + { + name: "dns-resources-valid-quantities", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests = map[string]string{ + "cpu": "100m", + "memory": "128Mi", + } + c.DNS.Resources.Limits = map[string]string{ + "cpu": "200m", + "memory": "256Mi", + } + return c + }(), + expectErr: false, + }, + { + name: "dns-resources-invalid-request", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["cpu"] = "abc" + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-invalid-limit", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Limits = map[string]string{ + "cpu": "not-a-quantity", + } + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-limit-less-than-request", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["cpu"] = "200m" + c.DNS.Resources.Limits = map[string]string{ + "cpu": "50m", + } + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-limit-without-request", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Limits = map[string]string{ + "cpu": "200m", + } + return c + }(), + expectErr: false, + }, } for _, tt := range ttests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/config/dns.go b/pkg/config/dns.go index d8948449c9..2397242c4a 100644 --- a/pkg/config/dns.go +++ b/pkg/config/dns.go @@ -4,6 +4,8 @@ import ( "fmt" "os" "path/filepath" + + "k8s.io/apimachinery/pkg/api/resource" ) const ( @@ -13,6 +15,20 @@ const ( type HostsStatusEnum string +// DNSResources configures the CPU and memory resources for the dns container +// in the dns-default DaemonSet. +type DNSResources struct { + // Requests specifies the minimum resources required for the dns container. + // Valid keys are "cpu" and "memory". Values must be valid Kubernetes resource quantities. + // When not set, defaults to cpu=50m, memory=70Mi. + Requests map[string]string `json:"requests,omitempty"` + + // Limits specifies the maximum resources the dns container can use. + // Valid keys are "cpu" and "memory". Values must be valid Kubernetes resource quantities. + // When not set, no limits are applied. + Limits map[string]string `json:"limits,omitempty"` +} + type DNS struct { // baseDomain is the base domain of the cluster. All managed DNS records will // be sub-domains of this base. @@ -29,6 +45,9 @@ type DNS struct { // Hosts contains configuration for the hosts file. Hosts HostsConfig `json:"hosts,omitempty"` + + // Resources configures the CPU and memory resources for the dns container. + Resources DNSResources `json:"resources,omitempty"` } // HostsConfig contains configuration for the hosts file . @@ -55,14 +74,27 @@ func dnsDefaults() DNS { File: "/etc/hosts", Status: HostsStatusDisabled, }, + Resources: DNSResources{ + Requests: map[string]string{ + "cpu": "50m", + "memory": "70Mi", + }, + }, } } func (t *DNS) validate() error { + if err := t.validateHosts(); err != nil { + return err + } + return t.validateResources() +} + +func (t *DNS) validateHosts() error { switch t.Hosts.Status { case HostsStatusEnabled: if t.Hosts.File == "" { - break + return nil } cleanPath := filepath.Clean(t.Hosts.File) @@ -94,5 +126,29 @@ func (t *DNS) validate() error { default: return fmt.Errorf("invalid hosts status: %s", t.Hosts.Status) } +} + +func (t *DNS) validateResources() error { + for key, val := range t.Resources.Requests { + if _, err := resource.ParseQuantity(val); err != nil { + return fmt.Errorf("invalid dns resource request %s=%q: %v", key, val, err) + } + } + for key, val := range t.Resources.Limits { + if _, err := resource.ParseQuantity(val); err != nil { + return fmt.Errorf("invalid dns resource limit %s=%q: %v", key, val, err) + } + } + for key, limitVal := range t.Resources.Limits { + reqVal, ok := t.Resources.Requests[key] + if !ok { + continue + } + limit := resource.MustParse(limitVal) + req := resource.MustParse(reqVal) + if limit.Cmp(req) < 0 { + return fmt.Errorf("dns resource limit %s=%q must be greater than or equal to request %s=%q", key, limitVal, key, reqVal) + } + } return nil } From f3a5a9c36026c0e359d02eadb0750b5260452ac5 Mon Sep 17 00:00:00 2001 From: nhamza Date: Mon, 4 May 2026 12:21:56 +0300 Subject: [PATCH 2/9] USHIFT-6912: Restrict DNS resource keys to cpu and memory Reject unsupported resource keys during config validation to prevent typos and ensure only cpu/memory are accepted, matching what the DaemonSet template renders. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/config/config_test.go | 20 ++++++++++++++++++++ pkg/config/dns.go | 10 ++++++++++ 2 files changed, 30 insertions(+) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 7ec928a506..6b0984e408 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1020,6 +1020,26 @@ func TestValidate(t *testing.T) { }(), expectErr: false, }, + { + name: "dns-resources-unsupported-request-key", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["gpu"] = "1" + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-unsupported-limit-key", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Limits = map[string]string{ + "ephemeral-storage": "1Gi", + } + return c + }(), + expectErr: true, + }, } for _, tt := range ttests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/config/dns.go b/pkg/config/dns.go index 2397242c4a..f4f282e873 100644 --- a/pkg/config/dns.go +++ b/pkg/config/dns.go @@ -129,12 +129,22 @@ func (t *DNS) validateHosts() error { } func (t *DNS) validateResources() error { + allowed := map[string]struct{}{ + "cpu": {}, + "memory": {}, + } for key, val := range t.Resources.Requests { + if _, ok := allowed[key]; !ok { + return fmt.Errorf("unsupported dns resource request key %q: allowed keys are cpu, memory", key) + } if _, err := resource.ParseQuantity(val); err != nil { return fmt.Errorf("invalid dns resource request %s=%q: %v", key, val, err) } } for key, val := range t.Resources.Limits { + if _, ok := allowed[key]; !ok { + return fmt.Errorf("unsupported dns resource limit key %q: allowed keys are cpu, memory", key) + } if _, err := resource.ParseQuantity(val); err != nil { return fmt.Errorf("invalid dns resource limit %s=%q: %v", key, val, err) } From a872cea95157c91cb49abc1f3bf4014e1688e9de Mon Sep 17 00:00:00 2001 From: nhamza Date: Mon, 4 May 2026 16:21:43 +0300 Subject: [PATCH 3/9] USHIFT-6914: Add e2e tests for DNS resource configuration RobotFramework tests covering: default resources, custom requests and limits, requests only, partial requests, invalid quantity rejection, and DNS resolution after resource change. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../dns-resource-configuration.robot | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) create mode 100644 test/suites/configuration/dns-resource-configuration.robot diff --git a/test/suites/configuration/dns-resource-configuration.robot b/test/suites/configuration/dns-resource-configuration.robot new file mode 100644 index 0000000000..eb31ff666c --- /dev/null +++ b/test/suites/configuration/dns-resource-configuration.robot @@ -0,0 +1,151 @@ +*** Settings *** +Documentation DNS resource configuration tests + +Resource ../../resources/common.resource +Resource ../../resources/oc.resource +Resource ../../resources/microshift-config.resource +Resource ../../resources/microshift-process.resource +Library ../../resources/journalctl.py + +Suite Setup Setup +Suite Teardown Teardown + +Test Tags slow restart + + +*** Variables *** +${CURSOR} ${EMPTY} +${DNS_DROPIN} 10-dns-resources +${DNS_CUSTOM_RESOURCES} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ requests: +... \ \ \ \ cpu: "100m" +... \ \ \ \ memory: "150Mi" +... \ \ \ limits: +... \ \ \ \ cpu: "200m" +... \ \ \ \ memory: "256Mi" +${DNS_REQUESTS_ONLY} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ requests: +... \ \ \ \ cpu: "100m" +... \ \ \ \ memory: "150Mi" +${DNS_PARTIAL_REQUESTS} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ requests: +... \ \ \ \ cpu: "100m" +${DNS_INVALID_QUANTITY} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ requests: +... \ \ \ \ cpu: "abc" + + +*** Test Cases *** +Default DNS Resources + [Documentation] Verify default DNS resources when no custom config is applied + ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.cpu + ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.memory + Should Be Equal As Strings ${cpu} 50m + Should Be Equal As Strings ${memory} 70Mi + +Custom DNS Resources With Requests And Limits + [Documentation] Configure custom CPU and memory requests and limits via drop-in config + [Setup] Apply DNS Resource Config ${DNS_CUSTOM_RESOURCES} + ${cpu_req}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.cpu + ${mem_req}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.memory + ${cpu_lim}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.limits.cpu + ${mem_lim}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.limits.memory + Should Be Equal As Strings ${cpu_req} 100m + Should Be Equal As Strings ${mem_req} 150Mi + Should Be Equal As Strings ${cpu_lim} 200m + Should Be Equal As Strings ${mem_lim} 256Mi + [Teardown] Remove DNS Resource Config + +Requests Only Without Limits + [Documentation] Configure only requests without limits and verify no limits are injected + [Setup] Apply DNS Resource Config ${DNS_REQUESTS_ONLY} + ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.cpu + ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.memory + ${limits}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.limits + Should Be Equal As Strings ${cpu} 100m + Should Be Equal As Strings ${memory} 150Mi + Should Be Empty ${limits} + [Teardown] Remove DNS Resource Config + +Partial Requests Preserves Defaults + [Documentation] Configure only CPU request and verify memory default is preserved + [Setup] Apply DNS Resource Config ${DNS_PARTIAL_REQUESTS} + ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.cpu + ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.memory + Should Be Equal As Strings ${cpu} 100m + Should Be Equal As Strings ${memory} 70Mi + [Teardown] Remove DNS Resource Config + +Invalid Resource Quantity Prevents Start + [Documentation] Verify MicroShift fails to start with invalid resource quantity + [Setup] Apply Invalid DNS Resource Config ${DNS_INVALID_QUANTITY} + Pattern Should Appear In Log Output ${CURSOR} invalid dns resource request + [Teardown] Restore Valid DNS Config + +DNS Resolution After Resource Change + [Documentation] Verify CoreDNS resolves cluster-local services after resource change + [Setup] Apply DNS Resource Config ${DNS_CUSTOM_RESOURCES} + ${output}= Oc Exec router-default nslookup kubernetes.default.svc.cluster.local + ... openshift-ingress deployment + Should Contain ${output} kubernetes.default.svc.cluster.local + [Teardown] Remove DNS Resource Config + + +*** Keywords *** +Setup + [Documentation] Test suite setup + Check Required Env Variables + Login MicroShift Host + Setup Kubeconfig + +Teardown + [Documentation] Test suite teardown + Remove Kubeconfig + Logout MicroShift Host + +Apply DNS Resource Config + [Documentation] Apply a DNS resource drop-in config and restart MicroShift + [Arguments] ${config} + Drop In MicroShift Config ${config} ${DNS_DROPIN} + Restart MicroShift + +Apply Invalid DNS Resource Config + [Documentation] Apply an invalid DNS resource config that should prevent MicroShift from starting + [Arguments] ${config} + Drop In MicroShift Config ${config} ${DNS_DROPIN} + ${cursor}= Get Journal Cursor + VAR ${CURSOR}= ${cursor} scope=TEST + Run Keyword And Expect Error 0 != 1 Restart MicroShift + +Remove DNS Resource Config + [Documentation] Remove the DNS resource drop-in config and restart MicroShift + Remove Drop In MicroShift Config ${DNS_DROPIN} + Restart MicroShift + +Restore Valid DNS Config + [Documentation] Remove invalid config and restore MicroShift to a working state + Remove Drop In MicroShift Config ${DNS_DROPIN} + Restart MicroShift From e2465ad8eacc6846c5603809be2f82ba6655a4b7 Mon Sep 17 00:00:00 2001 From: nhamza Date: Mon, 4 May 2026 16:28:10 +0300 Subject: [PATCH 4/9] USHIFT-6914: Add limit-less-than-request e2e test case Co-Authored-By: Claude Opus 4.6 (1M context) --- .../configuration/dns-resource-configuration.robot | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/suites/configuration/dns-resource-configuration.robot b/test/suites/configuration/dns-resource-configuration.robot index eb31ff666c..c233288c13 100644 --- a/test/suites/configuration/dns-resource-configuration.robot +++ b/test/suites/configuration/dns-resource-configuration.robot @@ -45,6 +45,14 @@ ${DNS_INVALID_QUANTITY} SEPARATOR=\n ... \ \ resources: ... \ \ \ requests: ... \ \ \ \ cpu: "abc" +${DNS_LIMIT_LESS_THAN_REQUEST} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ requests: +... \ \ \ \ cpu: "200m" +... \ \ \ limits: +... \ \ \ \ cpu: "50m" *** Test Cases *** @@ -105,6 +113,12 @@ Invalid Resource Quantity Prevents Start Pattern Should Appear In Log Output ${CURSOR} invalid dns resource request [Teardown] Restore Valid DNS Config +Limit Less Than Request Prevents Start + [Documentation] Verify MicroShift fails to start when limit is less than request + [Setup] Apply Invalid DNS Resource Config ${DNS_LIMIT_LESS_THAN_REQUEST} + Pattern Should Appear In Log Output ${CURSOR} must be greater than or equal to request + [Teardown] Restore Valid DNS Config + DNS Resolution After Resource Change [Documentation] Verify CoreDNS resolves cluster-local services after resource change [Setup] Apply DNS Resource Config ${DNS_CUSTOM_RESOURCES} From e961d29ebfc963f7e10bbd7db82036e40b77b417 Mon Sep 17 00:00:00 2001 From: nhamza Date: Tue, 5 May 2026 10:36:58 +0300 Subject: [PATCH 5/9] USHIFT-6912: Simplify DNS resource template rendering Pass resource maps directly instead of individual values, use range loops in the template. Reduces params from 7 to 2 and makes the pattern reusable for other components. Co-Authored-By: Claude Opus 4.6 (1M context) --- assets/components/openshift-dns/dns/daemonset.yaml | 14 ++++++-------- pkg/components/controllers.go | 11 ++++------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/assets/components/openshift-dns/dns/daemonset.yaml b/assets/components/openshift-dns/dns/daemonset.yaml index 8e8b8b5d8a..248245b363 100644 --- a/assets/components/openshift-dns/dns/daemonset.yaml +++ b/assets/components/openshift-dns/dns/daemonset.yaml @@ -57,15 +57,13 @@ spec: failureThreshold: 5 resources: requests: - cpu: {{ .DNSCPURequest }} - memory: {{ .DNSMemoryRequest }} - {{- if .DNSHasLimits }} - limits: - {{- if .DNSCPULimit }} - cpu: {{ .DNSCPULimit }} + {{- range $key, $value := .DNSRequests }} + {{ $key }}: {{ $value }} {{- end }} - {{- if .DNSMemoryLimit }} - memory: {{ .DNSMemoryLimit }} + {{- if .DNSLimits }} + limits: + {{- range $key, $value := .DNSLimits }} + {{ $key }}: {{ $value }} {{- end }} {{- end }} securityContext: diff --git a/pkg/components/controllers.go b/pkg/components/controllers.go index d5113503df..9c4e50747d 100644 --- a/pkg/components/controllers.go +++ b/pkg/components/controllers.go @@ -304,13 +304,10 @@ func startDNSController(ctx context.Context, cfg *config.Config, kubeconfigPath } extraParams := assets.RenderParams{ - "ClusterIP": cfg.Network.DNS, - "HostsEnabled": cfg.DNS.Hosts.Status == config.HostsStatusEnabled, - "DNSCPURequest": cfg.DNS.Resources.Requests["cpu"], - "DNSMemoryRequest": cfg.DNS.Resources.Requests["memory"], - "DNSCPULimit": cfg.DNS.Resources.Limits["cpu"], - "DNSMemoryLimit": cfg.DNS.Resources.Limits["memory"], - "DNSHasLimits": len(cfg.DNS.Resources.Limits) > 0, + "ClusterIP": cfg.Network.DNS, + "HostsEnabled": cfg.DNS.Hosts.Status == config.HostsStatusEnabled, + "DNSRequests": cfg.DNS.Resources.Requests, + "DNSLimits": cfg.DNS.Resources.Limits, } if err := assets.ApplyServices(ctx, svc, renderTemplate, renderParamsFromConfig(cfg, extraParams), kubeconfigPath); err != nil { From 5cc88ab57a0ce1aa5d770d9d9d631dceb3c8cca4 Mon Sep 17 00:00:00 2001 From: nhamza Date: Tue, 5 May 2026 12:34:40 +0300 Subject: [PATCH 6/9] USHIFT-6912: Enforce minimum DNS resource requests Reject resource requests below the defaults (cpu: 50m, memory: 70Mi) to prevent unstable clusters from insufficient DNS resources. Co-Authored-By: Claude Opus 4.6 (1M context) --- pkg/config/config_test.go | 28 ++++++++++++++++++++++++++++ pkg/config/dns.go | 16 +++++++++++++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index 6b0984e408..5f3a15a8c6 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -1040,6 +1040,34 @@ func TestValidate(t *testing.T) { }(), expectErr: true, }, + { + name: "dns-resources-cpu-below-minimum", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["cpu"] = "10m" + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-memory-below-minimum", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["memory"] = "30Mi" + return c + }(), + expectErr: true, + }, + { + name: "dns-resources-at-minimum", + config: func() *Config { + c := mkDefaultConfig() + c.DNS.Resources.Requests["cpu"] = "50m" + c.DNS.Resources.Requests["memory"] = "70Mi" + return c + }(), + expectErr: false, + }, } for _, tt := range ttests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/config/dns.go b/pkg/config/dns.go index f4f282e873..c58dcffd7c 100644 --- a/pkg/config/dns.go +++ b/pkg/config/dns.go @@ -128,18 +128,32 @@ func (t *DNS) validateHosts() error { } } +func dnsMinimumRequests() map[string]resource.Quantity { + defaults := dnsDefaults() + mins := make(map[string]resource.Quantity, len(defaults.Resources.Requests)) + for k, v := range defaults.Resources.Requests { + mins[k] = resource.MustParse(v) + } + return mins +} + func (t *DNS) validateResources() error { allowed := map[string]struct{}{ "cpu": {}, "memory": {}, } + mins := dnsMinimumRequests() for key, val := range t.Resources.Requests { if _, ok := allowed[key]; !ok { return fmt.Errorf("unsupported dns resource request key %q: allowed keys are cpu, memory", key) } - if _, err := resource.ParseQuantity(val); err != nil { + qty, err := resource.ParseQuantity(val) + if err != nil { return fmt.Errorf("invalid dns resource request %s=%q: %v", key, val, err) } + if minQty, ok := mins[key]; ok && qty.Cmp(minQty) < 0 { + return fmt.Errorf("dns resource request %s=%q is below minimum %s", key, val, minQty.String()) + } } for key, val := range t.Resources.Limits { if _, ok := allowed[key]; !ok { From d766e575d162808d7a231eb40264ece76ab5854d Mon Sep 17 00:00:00 2001 From: nhamza Date: Tue, 5 May 2026 12:38:26 +0300 Subject: [PATCH 7/9] USHIFT-6914: Add limits-only e2e test case Verify that configuring only limits preserves default requests, covering the corner case raised in enhancement review. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../dns-resource-configuration.robot | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/suites/configuration/dns-resource-configuration.robot b/test/suites/configuration/dns-resource-configuration.robot index c233288c13..c0bf0f4d73 100644 --- a/test/suites/configuration/dns-resource-configuration.robot +++ b/test/suites/configuration/dns-resource-configuration.robot @@ -45,6 +45,13 @@ ${DNS_INVALID_QUANTITY} SEPARATOR=\n ... \ \ resources: ... \ \ \ requests: ... \ \ \ \ cpu: "abc" +${DNS_LIMITS_ONLY} SEPARATOR=\n +... --- +... dns: +... \ \ resources: +... \ \ \ limits: +... \ \ \ \ cpu: "200m" +... \ \ \ \ memory: "256Mi" ${DNS_LIMIT_LESS_THAN_REQUEST} SEPARATOR=\n ... --- ... dns: @@ -107,6 +114,23 @@ Partial Requests Preserves Defaults Should Be Equal As Strings ${memory} 70Mi [Teardown] Remove DNS Resource Config +Limits Only Preserves Default Requests + [Documentation] Configure only limits and verify default requests are preserved + [Setup] Apply DNS Resource Config ${DNS_LIMITS_ONLY} + ${cpu_req}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.cpu + ${mem_req}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.requests.memory + ${cpu_lim}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.limits.cpu + ${mem_lim}= Oc Get JsonPath daemonset openshift-dns dns-default + ... .spec.template.spec.containers[0].resources.limits.memory + Should Be Equal As Strings ${cpu_req} 50m + Should Be Equal As Strings ${mem_req} 70Mi + Should Be Equal As Strings ${cpu_lim} 200m + Should Be Equal As Strings ${mem_lim} 256Mi + [Teardown] Remove DNS Resource Config + Invalid Resource Quantity Prevents Start [Documentation] Verify MicroShift fails to start with invalid resource quantity [Setup] Apply Invalid DNS Resource Config ${DNS_INVALID_QUANTITY} From 2d3fd81f7714bc457a810cf36aabce3c9ed03d5b Mon Sep 17 00:00:00 2001 From: nhamza Date: Tue, 5 May 2026 12:54:03 +0300 Subject: [PATCH 8/9] USHIFT-6914: Fix robocop formatting in RF test Co-Authored-By: Claude Opus 4.6 (1M context) --- test/suites/configuration/dns-resource-configuration.robot | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/suites/configuration/dns-resource-configuration.robot b/test/suites/configuration/dns-resource-configuration.robot index c0bf0f4d73..e5070dd1ab 100644 --- a/test/suites/configuration/dns-resource-configuration.robot +++ b/test/suites/configuration/dns-resource-configuration.robot @@ -45,7 +45,7 @@ ${DNS_INVALID_QUANTITY} SEPARATOR=\n ... \ \ resources: ... \ \ \ requests: ... \ \ \ \ cpu: "abc" -${DNS_LIMITS_ONLY} SEPARATOR=\n +${DNS_LIMITS_ONLY} SEPARATOR=\n ... --- ... dns: ... \ \ resources: From 9861e180415928421ed746b8ef52c4d0903b0d63 Mon Sep 17 00:00:00 2001 From: nhamza Date: Tue, 5 May 2026 17:08:38 +0300 Subject: [PATCH 9/9] USHIFT-6914: Address RF test review comments - Make tests idempotent by cleaning config in setup before checks - Extract duplicate jsonpath into Get DNS Resource Value keyword - Add DNS_RESOURCE_PATH variable for the base container path - Clean existing drop-in before applying new config in Apply keyword Co-Authored-By: Claude Opus 4.6 (1M context) --- .../dns-resource-configuration.robot | 57 +++++++++---------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/test/suites/configuration/dns-resource-configuration.robot b/test/suites/configuration/dns-resource-configuration.robot index e5070dd1ab..e4cd48f0f1 100644 --- a/test/suites/configuration/dns-resource-configuration.robot +++ b/test/suites/configuration/dns-resource-configuration.robot @@ -16,6 +16,7 @@ Test Tags slow restart *** Variables *** ${CURSOR} ${EMPTY} ${DNS_DROPIN} 10-dns-resources +${DNS_RESOURCE_PATH} .spec.template.spec.containers[0].resources ${DNS_CUSTOM_RESOURCES} SEPARATOR=\n ... --- ... dns: @@ -65,24 +66,19 @@ ${DNS_LIMIT_LESS_THAN_REQUEST} SEPARATOR=\n *** Test Cases *** Default DNS Resources [Documentation] Verify default DNS resources when no custom config is applied - ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.cpu - ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.memory + [Setup] Remove DNS Resource Config + ${cpu}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.cpu + ${memory}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.memory Should Be Equal As Strings ${cpu} 50m Should Be Equal As Strings ${memory} 70Mi Custom DNS Resources With Requests And Limits [Documentation] Configure custom CPU and memory requests and limits via drop-in config [Setup] Apply DNS Resource Config ${DNS_CUSTOM_RESOURCES} - ${cpu_req}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.cpu - ${mem_req}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.memory - ${cpu_lim}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.limits.cpu - ${mem_lim}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.limits.memory + ${cpu_req}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.cpu + ${mem_req}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.memory + ${cpu_lim}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.limits.cpu + ${mem_lim}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.limits.memory Should Be Equal As Strings ${cpu_req} 100m Should Be Equal As Strings ${mem_req} 150Mi Should Be Equal As Strings ${cpu_lim} 200m @@ -92,12 +88,9 @@ Custom DNS Resources With Requests And Limits Requests Only Without Limits [Documentation] Configure only requests without limits and verify no limits are injected [Setup] Apply DNS Resource Config ${DNS_REQUESTS_ONLY} - ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.cpu - ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.memory - ${limits}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.limits + ${cpu}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.cpu + ${memory}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.memory + ${limits}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.limits Should Be Equal As Strings ${cpu} 100m Should Be Equal As Strings ${memory} 150Mi Should Be Empty ${limits} @@ -106,10 +99,8 @@ Requests Only Without Limits Partial Requests Preserves Defaults [Documentation] Configure only CPU request and verify memory default is preserved [Setup] Apply DNS Resource Config ${DNS_PARTIAL_REQUESTS} - ${cpu}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.cpu - ${memory}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.memory + ${cpu}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.cpu + ${memory}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.memory Should Be Equal As Strings ${cpu} 100m Should Be Equal As Strings ${memory} 70Mi [Teardown] Remove DNS Resource Config @@ -117,14 +108,10 @@ Partial Requests Preserves Defaults Limits Only Preserves Default Requests [Documentation] Configure only limits and verify default requests are preserved [Setup] Apply DNS Resource Config ${DNS_LIMITS_ONLY} - ${cpu_req}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.cpu - ${mem_req}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.requests.memory - ${cpu_lim}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.limits.cpu - ${mem_lim}= Oc Get JsonPath daemonset openshift-dns dns-default - ... .spec.template.spec.containers[0].resources.limits.memory + ${cpu_req}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.cpu + ${mem_req}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.requests.memory + ${cpu_lim}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.limits.cpu + ${mem_lim}= Get DNS Resource Value ${DNS_RESOURCE_PATH}.limits.memory Should Be Equal As Strings ${cpu_req} 50m Should Be Equal As Strings ${mem_req} 70Mi Should Be Equal As Strings ${cpu_lim} 200m @@ -164,15 +151,23 @@ Teardown Remove Kubeconfig Logout MicroShift Host +Get DNS Resource Value + [Documentation] Get a resource value from the dns-default DaemonSet + [Arguments] ${jsonpath} + ${value}= Oc Get JsonPath daemonset openshift-dns dns-default ${jsonpath} + RETURN ${value} + Apply DNS Resource Config - [Documentation] Apply a DNS resource drop-in config and restart MicroShift + [Documentation] Remove any existing drop-in, apply a new DNS resource config and restart MicroShift [Arguments] ${config} + Remove Drop In MicroShift Config ${DNS_DROPIN} Drop In MicroShift Config ${config} ${DNS_DROPIN} Restart MicroShift Apply Invalid DNS Resource Config [Documentation] Apply an invalid DNS resource config that should prevent MicroShift from starting [Arguments] ${config} + Remove Drop In MicroShift Config ${DNS_DROPIN} Drop In MicroShift Config ${config} ${DNS_DROPIN} ${cursor}= Get Journal Cursor VAR ${CURSOR}= ${cursor} scope=TEST