From 9b9414dfda87945dda63b939eec8ce34ce87e1d1 Mon Sep 17 00:00:00 2001 From: zhoujinyu <2319109590@qq.com> Date: Wed, 4 Feb 2026 10:56:46 +0800 Subject: [PATCH 1/2] Add a startup parameter for the DNS proxy Signed-off-by: zhoujinyu <2319109590@qq.com> --- ctl/common/common.go | 2 + ctl/dnsproxy/dnsproxy.go | 147 ++++++++++++++++++ daemon/manager/manager.go | 2 +- daemon/options/bpf.go | 2 + .../kmesh-helm/templates/daemonset.yaml | 6 +- deploy/yaml/kmesh.yaml | 4 +- docs/ctl/kmeshctl.md | 1 + docs/ctl/kmeshctl_dnsproxy.md | 31 ++++ pkg/controller/controller.go | 103 +++++++++--- pkg/controller/telemetry/metric.go | 1 + .../workload/workload_controller.go | 10 +- pkg/controller/workload/workload_processor.go | 4 +- pkg/status/status_server.go | 43 ++++- test/e2e/dnsproxy_test.go | 142 +++++++++++++++++ 14 files changed, 460 insertions(+), 38 deletions(-) create mode 100644 ctl/dnsproxy/dnsproxy.go create mode 100644 docs/ctl/kmeshctl_dnsproxy.md create mode 100644 test/e2e/dnsproxy_test.go diff --git a/ctl/common/common.go b/ctl/common/common.go index b804bafc1..6f933a7f5 100644 --- a/ctl/common/common.go +++ b/ctl/common/common.go @@ -20,6 +20,7 @@ import ( "github.com/spf13/cobra" "kmesh.net/kmesh/ctl/authz" + "kmesh.net/kmesh/ctl/dnsproxy" "kmesh.net/kmesh/ctl/dump" logcmd "kmesh.net/kmesh/ctl/log" "kmesh.net/kmesh/ctl/monitoring" @@ -43,6 +44,7 @@ func GetRootCommand() *cobra.Command { rootCmd.AddCommand(waypoint.NewCmd()) rootCmd.AddCommand(version.NewCmd()) rootCmd.AddCommand(monitoring.NewCmd()) + rootCmd.AddCommand(dnsproxy.NewCmd()) rootCmd.AddCommand(authz.NewCmd()) rootCmd.AddCommand(secret.NewCmd()) diff --git a/ctl/dnsproxy/dnsproxy.go b/ctl/dnsproxy/dnsproxy.go new file mode 100644 index 000000000..a6a3097bd --- /dev/null +++ b/ctl/dnsproxy/dnsproxy.go @@ -0,0 +1,147 @@ +/* + * Copyright The Kmesh Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package dnsproxy + +import ( + "context" + "fmt" + "io" + "net/http" + "os" + "strings" + + "github.com/spf13/cobra" + + "kmesh.net/kmesh/ctl/utils" + "kmesh.net/kmesh/pkg/kube" + "kmesh.net/kmesh/pkg/logger" +) + +const patternDnsproxy = "/dnsproxy" + +var log = logger.NewLoggerScope("kmeshctl/dnsproxy") + +func NewCmd() *cobra.Command { + cmd := &cobra.Command{ + Use: "dnsproxy [pod] enable|disable", + Short: "Enable or disable Kmesh's DNS proxy", + Example: `# Enable Kmesh's DNS proxy: +kmeshctl dnsproxy enable + +# Disable Kmesh's DNS proxy: +kmeshctl dnsproxy disable + +# Enable/Disable DNS proxy on all kmesh daemons in the cluster: +kmeshctl dnsproxy enable +kmeshctl dnsproxy disable`, + Args: cobra.RangeArgs(1, 2), + Run: func(cmd *cobra.Command, args []string) { + ControlDnsproxy(cmd, args) + }, + } + return cmd +} + +func ControlDnsproxy(cmd *cobra.Command, args []string) { + client, err := utils.CreateKubeClient() + if err != nil { + log.Errorf("failed to create cli client: %v", err) + os.Exit(1) + } + + var podName string + var enableStr string + if len(args) == 1 { + enableStr = args[0] + podName = "" + } else { + podName = args[0] + enableStr = args[1] + } + + if enableStr != "enable" && enableStr != "disable" { + log.Errorf("Error: Argument must be 'enable' or 'disable'") + os.Exit(1) + } + + if podName != "" && strings.Contains(podName, "--") { + log.Errorf("Error: Invalid pod name") + os.Exit(1) + } + + if podName != "" { + SetDnsproxyPerKmeshDaemon(client, podName, enableStr) + return + } + + // Apply to all kmesh daemons + podList, err := client.PodsForSelector(context.TODO(), utils.KmeshNamespace, utils.KmeshLabel) + if err != nil { + log.Errorf("failed to get kmesh podList: %v", err) + os.Exit(1) + } + for _, pod := range podList.Items { + SetDnsproxyPerKmeshDaemon(client, pod.GetName(), enableStr) + } +} + +func SetDnsproxyPerKmeshDaemon(cli kube.CLIClient, podName, info string) { + var status string + if info == "enable" { + status = "true" + } else { + status = "false" + } + + fw, err := utils.CreateKmeshPortForwarder(cli, podName) + if err != nil { + log.Errorf("failed to create port forwarder for Kmesh daemon pod %s: %v", podName, err) + os.Exit(1) + } + if err := fw.Start(); err != nil { + log.Errorf("failed to start port forwarder for Kmesh daemon pod %s: %v", podName, err) + os.Exit(1) + } + defer fw.Close() + + url := fmt.Sprintf("http://%s%s?enable=%s", fw.Address(), patternDnsproxy, status) + + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + log.Errorf("Error creating request: %v", err) + return + } + + req.Header.Set("Content-Type", "application/json") + httpClient := &http.Client{} + resp, err := httpClient.Do(req) + if err != nil { + log.Errorf("failed to make HTTP request: %v", err) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + log.Errorf("Error: received status code %d", resp.StatusCode) + bodyBytes, readErr := io.ReadAll(resp.Body) + if readErr != nil { + log.Errorf("Error reading response body: %v", readErr) + return + } + log.Errorf("response: %s", string(bodyBytes)) + } +} diff --git a/daemon/manager/manager.go b/daemon/manager/manager.go index 4026d45ad..fcb7a479c 100644 --- a/daemon/manager/manager.go +++ b/daemon/manager/manager.go @@ -100,7 +100,7 @@ func Execute(configs *options.BootstrapConfigs) error { log.Info("controller start successfully") defer c.Stop() - statusServer := status.NewServer(c.GetXdsClient(), configs, bpfLoader) + statusServer := status.NewServer(c, configs, bpfLoader) statusServer.StartServer() defer func() { _ = statusServer.StopServer() diff --git a/daemon/options/bpf.go b/daemon/options/bpf.go index be9dc0dbe..597b1d8a1 100644 --- a/daemon/options/bpf.go +++ b/daemon/options/bpf.go @@ -34,6 +34,7 @@ type BpfConfig struct { EnablePeriodicReport bool EnableProfiling bool EnableIPsec bool + EnableDnsProxy bool } func (c *BpfConfig) AttachFlags(cmd *cobra.Command) { @@ -45,6 +46,7 @@ func (c *BpfConfig) AttachFlags(cmd *cobra.Command) { cmd.PersistentFlags().BoolVar(&c.EnablePeriodicReport, "periodic-report", false, "enable kmesh periodic report in daemon process") cmd.PersistentFlags().BoolVar(&c.EnableProfiling, "profiling", false, "whether to enable profiling or not, default to false") cmd.PersistentFlags().BoolVar(&c.EnableIPsec, "enable-ipsec", false, "enable ipsec encryption and authentication between nodes") + cmd.PersistentFlags().BoolVar(&c.EnableDnsProxy, "enable-dns-proxy", false, "enable dns proxy, a DNS server will be started in kmesh daemon and serve DNS requests") } func (c *BpfConfig) ParseConfig() error { diff --git a/deploy/charts/kmesh-helm/templates/daemonset.yaml b/deploy/charts/kmesh-helm/templates/daemonset.yaml index 9d08dbde9..57833b773 100644 --- a/deploy/charts/kmesh-helm/templates/daemonset.yaml +++ b/deploy/charts/kmesh-helm/templates/daemonset.yaml @@ -22,7 +22,7 @@ spec: prometheus.io/scrape: "true" spec: containers: - - args: ["./start_kmesh.sh {{ .Values.deploy.kmesh.containers.kmeshDaemonArgs }}"] + - args: ["./start_kmesh.sh {{ .Values.deploy.kmesh.containers.kmeshDaemonArgs }}{{ if .Values.features.dnsProxy.enabled }} --enable-dns-proxy{{ end }}"] command: - /bin/sh - -c @@ -51,10 +51,6 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName - {{ if .Values.features.dnsProxy.enabled }} - - name: KMESH_ENABLE_DNS_PROXY - value: {{ .Values.features.dnsProxy.enabled | quote }} - {{- end }} image: {{ .Values.deploy.kmesh.image.repository }}:{{ .Values.deploy.kmesh.image.tag | default .Chart.AppVersion }} imagePullPolicy: {{ .Values.deploy.kmesh.imagePullPolicy }} name: kmesh diff --git a/deploy/yaml/kmesh.yaml b/deploy/yaml/kmesh.yaml index 210f5de06..6949a1e38 100644 --- a/deploy/yaml/kmesh.yaml +++ b/deploy/yaml/kmesh.yaml @@ -78,7 +78,7 @@ spec: command: ["/bin/sh", "-c"] args: [ - "./start_kmesh.sh --mode=dual-engine --enable-bypass=false", + "./start_kmesh.sh --mode=dual-engine --enable-bypass=false --enable-dns-proxy", ] securityContext: privileged: true @@ -107,8 +107,6 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName - - name: KMESH_ENABLE_DNS_PROXY - value: "true" volumeMounts: - name: mnt mountPath: /mnt diff --git a/docs/ctl/kmeshctl.md b/docs/ctl/kmeshctl.md index 2a0c1eadf..0b350d15a 100644 --- a/docs/ctl/kmeshctl.md +++ b/docs/ctl/kmeshctl.md @@ -11,6 +11,7 @@ Kmesh command line tools to operate and debug Kmesh ### SEE ALSO * [kmeshctl authz](kmeshctl_authz.md) - Manage xdp authz eBPF program for Kmesh's authz offloading +* [kmeshctl dnsproxy](kmeshctl_dnsproxy.md) - Enable or disable Kmesh's DNS proxy * [kmeshctl dump](kmeshctl_dump.md) - Dump config of kernel-native or dual-engine mode * [kmeshctl log](kmeshctl_log.md) - Get or set kmesh-daemon's logger level * [kmeshctl monitoring](kmeshctl_monitoring.md) - Control Kmesh's monitoring to be turned on as needed diff --git a/docs/ctl/kmeshctl_dnsproxy.md b/docs/ctl/kmeshctl_dnsproxy.md new file mode 100644 index 000000000..59a231b2c --- /dev/null +++ b/docs/ctl/kmeshctl_dnsproxy.md @@ -0,0 +1,31 @@ +## kmeshctl dnsproxy + +Enable or disable Kmesh's DNS proxy + +```bash +kmeshctl dnsproxy [pod] enable|disable [flags] +``` + +### Examples + +```bash +# Enable Kmesh's DNS proxy: +kmeshctl dnsproxy enable + +# Disable Kmesh's DNS proxy: +kmeshctl dnsproxy disable + +# Enable/Disable DNS proxy on all kmesh daemons in the cluster: +kmeshctl dnsproxy enable +kmeshctl dnsproxy disable +``` + +### Options + +```bash + -h, --help help for dnsproxy +``` + +### SEE ALSO + +* [kmeshctl](kmeshctl.md) - Kmesh command line tools to operate and debug Kmesh diff --git a/pkg/controller/controller.go b/pkg/controller/controller.go index 30ec020b8..c87b337b5 100644 --- a/pkg/controller/controller.go +++ b/pkg/controller/controller.go @@ -19,6 +19,7 @@ package controller import ( "context" "fmt" + "sync" "time" "github.com/cilium/ebpf" @@ -66,6 +67,7 @@ type Controller struct { bpfConfig *options.BpfConfig loader *bpf.BpfLoader dnsServer *dnsclient.LocalDNSServer + dnsProxyMu sync.Mutex } func NewController(opts *options.BootstrapConfigs, bpfLoader *bpf.BpfLoader) *Controller { @@ -172,6 +174,9 @@ func (c *Controller) Start(stopCh <-chan struct{}) error { } if c.client.WorkloadController != nil { + // Startup: flag takes precedence; env KMESH_ENABLE_DNS_PROXY is fallback for backward compatibility + enableDnsProxy := c.bpfConfig.EnableDnsProxy || workload.EnableDNSProxy + c.client.WorkloadController.SetDnsProxyTrigger(enableDnsProxy) if err := c.client.WorkloadController.Run(ctx, stopCh); err != nil { return fmt.Errorf("failed to start workload controller: %+v", err) } @@ -208,35 +213,85 @@ func (c *Controller) GetXdsClient() *XdsClient { return c.client } -func (c *Controller) setupDNSProxy() error { - if workload.EnableDNSProxy { - server, err := dnsclient.NewLocalDNSServer(kmeshNamespace, clusterDomain, ":53", dnsForwardParallel) - if err != nil { - return fmt.Errorf("failed to start local dns server: %v", err) - } - ntb := dns.NewNameTableBuilder(c.client.WorkloadController.Processor.ServiceCache, c.client.WorkloadController.Processor.WorkloadCache) - - debounceTime := time.Second - timer := time.NewTimer(0) - <-timer.C - h := func(rsp *service_discovery_v3.DeltaDiscoveryResponse) error { - // debounce - if timer.Reset(debounceTime) { - return nil - } +func (c *Controller) updateDnsLookupTable() { + c.dnsProxyMu.Lock() + server := c.dnsServer + c.dnsProxyMu.Unlock() + if server == nil { + return + } + ntb := dns.NewNameTableBuilder(c.client.WorkloadController.Processor.ServiceCache, c.client.WorkloadController.Processor.WorkloadCache) + server.UpdateLookupTable(ntb.BuildNameTable()) + log.Debugf("trigger name table update") +} - go func() { - <-timer.C - log.Debugf("trigger name table update") - server.UpdateLookupTable(ntb.BuildNameTable()) - }() +func (c *Controller) setupDNSProxy() error { + if !c.client.WorkloadController.GetDnsProxyTrigger() { + return nil + } + server, err := dnsclient.NewLocalDNSServer(kmeshNamespace, clusterDomain, ":53", dnsForwardParallel) + if err != nil { + return fmt.Errorf("failed to start local dns server: %v", err) + } + debounceTime := time.Second + timer := time.NewTimer(0) + <-timer.C + h := func(rsp *service_discovery_v3.DeltaDiscoveryResponse) error { + if timer.Reset(debounceTime) { return nil } + go func() { + <-timer.C + c.updateDnsLookupTable() + }() + return nil + } + + c.client.WorkloadController.Processor.WithResourceHandlers(workload.AddressType, h) + server.StartDNS() + c.dnsServer = server + return nil +} - c.client.WorkloadController.Processor.WithResourceHandlers(workload.AddressType, h) - server.StartDNS() - c.dnsServer = server +// StartDnsProxy starts the DNS proxy at runtime (e.g. via kmeshctl). +func (c *Controller) StartDnsProxy() error { + if c.client == nil || c.client.WorkloadController == nil { + return fmt.Errorf("dns proxy not supported in this mode") + } + c.dnsProxyMu.Lock() + defer c.dnsProxyMu.Unlock() + if c.dnsServer != nil { + return nil + } + c.client.WorkloadController.SetDnsProxyTrigger(true) + if err := c.client.WorkloadController.Processor.PrepareDNSProxy(true); err != nil { + c.client.WorkloadController.SetDnsProxyTrigger(false) + return err + } + if err := c.setupDNSProxy(); err != nil { + c.client.WorkloadController.SetDnsProxyTrigger(false) + _ = c.client.WorkloadController.Processor.PrepareDNSProxy(false) + return err + } + return nil +} + +// StopDnsProxy stops the DNS proxy at runtime (e.g. via kmeshctl). +func (c *Controller) StopDnsProxy() error { + if c.client == nil || c.client.WorkloadController == nil { + return fmt.Errorf("dns proxy not supported in this mode") + } + c.dnsProxyMu.Lock() + defer c.dnsProxyMu.Unlock() + if c.dnsServer == nil { + c.client.WorkloadController.SetDnsProxyTrigger(false) + _ = c.client.WorkloadController.Processor.PrepareDNSProxy(false) + return nil } + c.client.WorkloadController.SetDnsProxyTrigger(false) + _ = c.client.WorkloadController.Processor.PrepareDNSProxy(false) + c.dnsServer.Close() + c.dnsServer = nil return nil } diff --git a/pkg/controller/telemetry/metric.go b/pkg/controller/telemetry/metric.go index 12168dae5..c955155aa 100644 --- a/pkg/controller/telemetry/metric.go +++ b/pkg/controller/telemetry/metric.go @@ -74,6 +74,7 @@ type MetricController struct { EnableMonitoring atomic.Bool EnableWorkloadMetric atomic.Bool EnableConnectionMetric atomic.Bool + EnableDnsProxy atomic.Bool workloadCache cache.WorkloadCache serviceCache cache.ServiceCache workloadMetricCache map[workloadMetricLabels]*workloadMetricInfo diff --git a/pkg/controller/workload/workload_controller.go b/pkg/controller/workload/workload_controller.go index f83600ca3..79a4db61a 100644 --- a/pkg/controller/workload/workload_controller.go +++ b/pkg/controller/workload/workload_controller.go @@ -82,7 +82,7 @@ func NewController(bpfWorkload *bpfwl.BpfWorkload, enableMonitoring, enablePerfM } func (c *Controller) Run(ctx context.Context, stopCh <-chan struct{}) error { - if err := c.Processor.PrepareDNSProxy(); err != nil { + if err := c.Processor.PrepareDNSProxy(c.GetDnsProxyTrigger()); err != nil { log.Errorf("failed to prepare for dns proxy, err: %+v", err) return err } @@ -215,3 +215,11 @@ func (c *Controller) SetConnectionMetricTrigger(enable bool) { func (c *Controller) GetConnectionMetricTrigger() bool { return c.MetricController.EnableConnectionMetric.Load() } + +func (c *Controller) SetDnsProxyTrigger(enabled bool) { + c.MetricController.EnableDnsProxy.Store(enabled) +} + +func (c *Controller) GetDnsProxyTrigger() bool { + return c.MetricController.EnableDnsProxy.Load() +} diff --git a/pkg/controller/workload/workload_processor.go b/pkg/controller/workload/workload_processor.go index 7557d0990..16d62abe7 100644 --- a/pkg/controller/workload/workload_processor.go +++ b/pkg/controller/workload/workload_processor.go @@ -105,13 +105,13 @@ func (p *Processor) WithResourceHandlers(typeUrl string, h ...func(resp *service return p } -func (p *Processor) PrepareDNSProxy() error { +func (p *Processor) PrepareDNSProxy(enabled bool) error { bk := &bpf.BackendKey{ BackendUid: 0, } // when dns proxy is not enabled, we unregister kmesh daemon from bpf map - if !EnableDNSProxy { + if !enabled { return p.bpf.BackendDelete(bk) } diff --git a/pkg/status/status_server.go b/pkg/status/status_server.go index 6405567e1..222d232a2 100644 --- a/pkg/status/status_server.go +++ b/pkg/status/status_server.go @@ -60,6 +60,7 @@ const ( patternWorkloadMetrics = "/workload_metrics" patternConnectionMetrics = "/connection_metrics" patternAuthz = "/authz" + patternDnsproxy = "/dnsproxy" bpfLoggerName = "bpf" @@ -71,15 +72,17 @@ const ( type Server struct { config *options.BootstrapConfigs xdsClient *controller.XdsClient + ctrl *controller.Controller mux *http.ServeMux server *http.Server loader *bpf.BpfLoader } -func NewServer(c *controller.XdsClient, configs *options.BootstrapConfigs, loader *bpf.BpfLoader) *Server { +func NewServer(ctrl *controller.Controller, configs *options.BootstrapConfigs, loader *bpf.BpfLoader) *Server { s := &Server{ config: configs, - xdsClient: c, + xdsClient: ctrl.GetXdsClient(), + ctrl: ctrl, mux: http.NewServeMux(), loader: loader, } @@ -101,6 +104,7 @@ func NewServer(c *controller.XdsClient, configs *options.BootstrapConfigs, loade s.mux.HandleFunc(patternWorkloadMetrics, s.workloadMetricHandler) s.mux.HandleFunc(patternConnectionMetrics, s.connectionMetricHandler) s.mux.HandleFunc(patternAuthz, s.authzHandler) + s.mux.HandleFunc(patternDnsproxy, s.dnsproxyHandler) // TODO: add dump certificate, authorizationPolicies and services s.mux.HandleFunc(patternReadyProbe, s.readyProbe) @@ -241,6 +245,41 @@ func (s *Server) accesslogHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) } +func (s *Server) dnsproxyHandler(w http.ResponseWriter, r *http.Request) { + if r.Method != http.MethodPost { + http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) + return + } + + info := r.URL.Query().Get("enable") + enabled, err := strconv.ParseBool(info) + if err != nil { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(fmt.Sprintf("invalid dnsproxy enable=%s", info))) + return + } + + if s.ctrl == nil || s.xdsClient.WorkloadController == nil { + http.Error(w, "dns proxy not supported in this mode", http.StatusBadRequest) + return + } + + if enabled { + if err := s.ctrl.StartDnsProxy(); err != nil { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(fmt.Sprintf("failed to start dns proxy: %v", err))) + return + } + } else { + if err := s.ctrl.StopDnsProxy(); err != nil { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte(fmt.Sprintf("failed to stop dns proxy: %v", err))) + return + } + } + w.WriteHeader(http.StatusOK) +} + func (s *Server) monitoringHandler(w http.ResponseWriter, r *http.Request) { if r.Method != http.MethodPost { http.Error(w, "Method Not Allowed", http.StatusMethodNotAllowed) diff --git a/test/e2e/dnsproxy_test.go b/test/e2e/dnsproxy_test.go new file mode 100644 index 000000000..3242be611 --- /dev/null +++ b/test/e2e/dnsproxy_test.go @@ -0,0 +1,142 @@ +//go:build integ +// +build integ + +/* + * Copyright The Kmesh Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package kmesh + +import ( + "fmt" + "io" + "net/http" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "istio.io/istio/pkg/test/framework" + testKube "istio.io/istio/pkg/test/kube" +) + +const ( + kmeshAdminPort = 15200 + dnsproxyPath = "/dnsproxy" +) + +// TestDnsproxyAPI tests enabling and disabling DNS proxy via the status server API (same as kmeshctl dnsproxy). +func TestDnsproxyAPI(t *testing.T) { + framework.NewTest(t).Run(func(t framework.TestContext) { + cls := t.Clusters().Default() + + pods, err := testKube.CheckPodsAreReady(testKube.NewPodFetch(cls, KmeshNamespace, "app=kmesh")) + if err != nil { + t.Fatalf("failed to get kmesh pods: %v", err) + } + if len(pods) == 0 { + t.Fatal("no kmesh pods found") + } + pod := pods[0] + + fw, err := cls.NewPortForwarder(pod.Name, pod.Namespace, "", 0, kmeshAdminPort) + if err != nil { + t.Fatalf("failed to create port forwarder: %v", err) + } + if err := fw.Start(); err != nil { + t.Fatalf("failed to start port forwarder: %v", err) + } + defer fw.Close() + + baseURL := fmt.Sprintf("http://%s", fw.Address()) + + t.NewSubTest("enable dnsproxy").Run(func(t framework.TestContext) { + resp, err := postDnsproxy(baseURL, true) + assert.NoError(t, err) + assert.NotNil(t, resp) + if resp != nil { + _, _ = io.Copy(io.Discard, resp.Body) + resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode, "enable dnsproxy should return 200") + } + }) + + t.NewSubTest("disable dnsproxy").Run(func(t framework.TestContext) { + resp, err := postDnsproxy(baseURL, false) + assert.NoError(t, err) + assert.NotNil(t, resp) + if resp != nil { + _, _ = io.Copy(io.Discard, resp.Body) + resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode, "disable dnsproxy should return 200") + } + }) + + t.NewSubTest("enable again then disable").Run(func(t framework.TestContext) { + resp, err := postDnsproxy(baseURL, true) + assert.NoError(t, err) + assert.NotNil(t, resp) + if resp != nil { + resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + } + + resp, err = postDnsproxy(baseURL, false) + assert.NoError(t, err) + assert.NotNil(t, resp) + if resp != nil { + resp.Body.Close() + assert.Equal(t, http.StatusOK, resp.StatusCode) + } + }) + }) +} + +// postDnsproxy sends POST to /dnsproxy?enable= on the status server. +func postDnsproxy(baseURL string, enable bool) (*http.Response, error) { + enableStr := "false" + if enable { + enableStr = "true" + } + url := fmt.Sprintf("%s%s?enable=%s", baseURL, dnsproxyPath, enableStr) + + req, err := http.NewRequest(http.MethodPost, url, nil) + if err != nil { + return nil, err + } + + client := &http.Client{Timeout: 10 * time.Second} + return client.Do(req) +} + +// TestDnsproxyStartupParameter verifies that DNS proxy can be controlled by startup parameter (--enable-dns-proxy). +// It patches the daemonset to add/remove the env KMESH_ENABLE_DNS_PROXY (backward compat) and ensures +// the daemon still runs; the actual DNS proxy state is driven by flag or env at startup. +func TestDnsproxyStartupParameter(t *testing.T) { + framework.NewTest(t).Run(func(t framework.TestContext) { + // Disable DNS proxy via env (backward compat with env) + configureDNSProxy(t, false) + + // Ensure kmesh pods are still ready after disabling + cls := t.Clusters().Default() + _, err := testKube.CheckPodsAreReady(testKube.NewPodFetch(cls, KmeshNamespace, "app=kmesh")) + assert.NoError(t, err) + + // Re-enable DNS proxy + configureDNSProxy(t, true) + + _, err = testKube.CheckPodsAreReady(testKube.NewPodFetch(cls, KmeshNamespace, "app=kmesh")) + assert.NoError(t, err) + }) +} From f89d640e2a819c1cae806e485c9c4da3c13a1e44 Mon Sep 17 00:00:00 2001 From: zhoujinyu <2319109590@qq.com> Date: Wed, 4 Feb 2026 12:59:29 +0800 Subject: [PATCH 2/2] dnsproxy kmeshctl Signed-off-by: zhoujinyu <2319109590@qq.com> --- test/e2e/dnsproxy_test.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/test/e2e/dnsproxy_test.go b/test/e2e/dnsproxy_test.go index 3242be611..622408bb9 100644 --- a/test/e2e/dnsproxy_test.go +++ b/test/e2e/dnsproxy_test.go @@ -23,6 +23,7 @@ import ( "fmt" "io" "net/http" + "os/exec" "testing" "time" @@ -120,6 +121,34 @@ func postDnsproxy(baseURL string, enable bool) (*http.Response, error) { return client.Do(req) } +// TestDnsproxyKmeshctl tests the ability to autonomously start and stop dnsProxy via kmeshctl. +func TestDnsproxyKmeshctl(t *testing.T) { + framework.NewTest(t).Run(func(t framework.TestContext) { + cls := t.Clusters().Default() + + pods, err := testKube.CheckPodsAreReady(testKube.NewPodFetch(cls, KmeshNamespace, "app=kmesh")) + if err != nil { + t.Fatalf("failed to get kmesh pods: %v", err) + } + if len(pods) == 0 { + t.Fatal("no kmesh pods found") + } + podName := pods[0].Name + + t.NewSubTest("kmeshctl dnsproxy enable").Run(func(t framework.TestContext) { + cmd := exec.Command("kmeshctl", "dnsproxy", podName, "enable") + out, err := cmd.CombinedOutput() + assert.NoError(t, err, "kmeshctl dnsproxy enable should succeed: %s", string(out)) + }) + + t.NewSubTest("kmeshctl dnsproxy disable").Run(func(t framework.TestContext) { + cmd := exec.Command("kmeshctl", "dnsproxy", podName, "disable") + out, err := cmd.CombinedOutput() + assert.NoError(t, err, "kmeshctl dnsproxy disable should succeed: %s", string(out)) + }) + }) +} + // TestDnsproxyStartupParameter verifies that DNS proxy can be controlled by startup parameter (--enable-dns-proxy). // It patches the daemonset to add/remove the env KMESH_ENABLE_DNS_PROXY (backward compat) and ensures // the daemon still runs; the actual DNS proxy state is driven by flag or env at startup.