From 005e165809af9799cfeac34b4e5c48668a651f4b Mon Sep 17 00:00:00 2001 From: RustyCoderX Date: Wed, 29 Apr 2026 03:07:26 +0530 Subject: [PATCH 1/2] fix: remove hardcoded cluster.local and add configurable clusterDomain support --- contrib/addons/grafana.yaml | 4 ++-- .../mcp-grafana/templates/toolserver.yaml | 2 +- .../mcp-grafana/tests/toolserver_test.yaml | 4 ++-- .../translator/agent/adk_api_translator.go | 6 +++--- .../agent/adk_translator_golden_test.go | 4 ++-- .../controller/translator/agent/proxy_test.go | 20 +++++++++---------- .../testdata/inputs/agent_with_proxy.yaml | 2 +- .../agent_with_proxy_external_remotemcp.yaml | 2 +- .../inputs/agent_with_proxy_mcpserver.yaml | 2 +- ...t_with_proxy_mcpserver_custom_timeout.yaml | 2 +- .../inputs/agent_with_proxy_service.yaml | 2 +- .../testdata/outputs/agent_with_proxy.json | 8 ++++---- .../outputs/agent_with_proxy_mcpserver.json | 6 +++--- ...t_with_proxy_mcpserver_custom_timeout.json | 6 +++--- .../outputs/agent_with_proxy_service.json | 6 +++--- .../httpserver/auth/proxy_authn_test.go | 2 +- go/core/pkg/app/app.go | 6 ++++-- go/core/pkg/app/app_test.go | 6 +++--- helm/kagent/templates/_helpers.tpl | 14 ++++++++++++- .../templates/controller-deployment.yaml | 6 +++++- helm/kagent/templates/ui-deployment.yaml | 2 +- .../tests/controller-deployment_test.yaml | 8 ++++---- helm/kagent/values.yaml | 10 ++++++++-- .../tests/unittests/test_proxy_integration.py | 8 ++++---- python/samples/langgraph/currency/agent.yaml | 4 ++-- ui/src/lib/__tests__/utils.test.ts | 2 +- ui/src/lib/utils.ts | 2 +- 27 files changed, 85 insertions(+), 61 deletions(-) diff --git a/contrib/addons/grafana.yaml b/contrib/addons/grafana.yaml index 9feeb90f3..06cfbabaf 100644 --- a/contrib/addons/grafana.yaml +++ b/contrib/addons/grafana.yaml @@ -646,7 +646,7 @@ data: destination_workload_namespace=~\\\"$dstns\\\"}[$__rate_interval])) by (destination_workload, destination_workload_namespace), 0.001)\",\"format\":\"time_series\",\"intervalFactor\":1,\"legendFormat\":\"{{ destination_workload }}.{{destination_workload_namespace }}\",\"refId\":\"B\",\"step\":2}],\"title\":\"Bytes - Sent to Incoming TCP Connection\",\"type\":\"timeseries\"}],\"refresh\":\"1m\",\"schemaVersion\":38,\"style\":\"dark\",\"tags\":[],\"templating\":{\"list\":[{\"hide\":0,\"includeAll\":false,\"multi\":false,\"name\":\"datasource\",\"options\":[],\"query\":\"prometheus\",\"queryValue\":\"\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"type\":\"datasource\"},{\"current\":{\"selected\":false,\"text\":\"details.default.svc.cluster.local\",\"value\":\"details.default.svc.cluster.local\"},\"datasource\":{\"type\":\"prometheus\",\"uid\":\"${datasource}\"},\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Service\",\"multi\":false,\"name\":\"service\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{}) + Sent to Incoming TCP Connection\",\"type\":\"timeseries\"}],\"refresh\":\"1m\",\"schemaVersion\":38,\"style\":\"dark\",\"tags\":[],\"templating\":{\"list\":[{\"hide\":0,\"includeAll\":false,\"multi\":false,\"name\":\"datasource\",\"options\":[],\"query\":\"prometheus\",\"queryValue\":\"\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"type\":\"datasource\"},{\"current\":{\"selected\":false,\"text\":\"details.default.svc\",\"value\":\"details.default.svc\"},\"datasource\":{\"type\":\"prometheus\",\"uid\":\"${datasource}\"},\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Service\",\"multi\":false,\"name\":\"service\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{}) by (destination_service) or sum(istio_tcp_sent_bytes_total{}) by (destination_service))\",\"refresh\":1,\"regex\":\"/.*destination_service=\\\"([^\\\"]*).*/\",\"skipUrlSync\":false,\"sort\":0,\"tagValuesQuery\":\"\",\"tagsQuery\":\"\",\"type\":\"query\",\"useTags\":false},{\"current\":{\"selected\":false,\"text\":\"destination\",\"value\":\"destination\"},\"datasource\":\"Prometheus\",\"definition\":\"\",\"hide\":0,\"includeAll\":false,\"label\":\"Reporter\",\"multi\":true,\"name\":\"qrep\",\"options\":[{\"selected\":false,\"text\":\"source\",\"value\":\"source\"},{\"selected\":true,\"text\":\"destination\",\"value\":\"destination\"}],\"query\":\"source,destination\",\"refresh\":1,\"regex\":\"\",\"skipUrlSync\":false,\"sort\":1,\"tagValuesQuery\":\"\",\"tagsQuery\":\"\",\"type\":\"custom\",\"useTags\":false},{\"current\":{\"selected\":false,\"text\":\"All\",\"value\":\"$__all\"},\"datasource\":{\"type\":\"prometheus\",\"uid\":\"${datasource}\"},\"definition\":\"\",\"hide\":0,\"includeAll\":true,\"label\":\"Client Cluster\",\"multi\":true,\"name\":\"srccluster\",\"options\":[],\"query\":\"query_result(sum(istio_requests_total{reporter=~\\\"$qrep\\\", destination_service=\\\"$service\\\"}) by (source_cluster) or sum(istio_tcp_sent_bytes_total{reporter=~\\\"$qrep\\\", @@ -1129,4 +1129,4 @@ kind: ConfigMap metadata: creationTimestamp: null name: istio-services-grafana-dashboards - namespace: kagent \ No newline at end of file + namespace: kagent diff --git a/contrib/tools/mcp-grafana/templates/toolserver.yaml b/contrib/tools/mcp-grafana/templates/toolserver.yaml index c3e708054..26e35c5b1 100644 --- a/contrib/tools/mcp-grafana/templates/toolserver.yaml +++ b/contrib/tools/mcp-grafana/templates/toolserver.yaml @@ -8,6 +8,6 @@ metadata: spec: config: streamableHttp: - url: http://{{ include "mcp-grafana.fullname" . }}.{{ .Release.Namespace }}.svc.cluster.local:{{ .Values.service.port }}/mcp + url: http://{{ include "mcp-grafana.fullname" . }}.{{ .Release.Namespace }}.svc:{{ .Values.service.port }}/mcp timeout: 30s description: "Grafana MCP server for monitoring and dashboard management" diff --git a/contrib/tools/mcp-grafana/tests/toolserver_test.yaml b/contrib/tools/mcp-grafana/tests/toolserver_test.yaml index ca6f7aedb..19c94e8ac 100644 --- a/contrib/tools/mcp-grafana/tests/toolserver_test.yaml +++ b/contrib/tools/mcp-grafana/tests/toolserver_test.yaml @@ -19,7 +19,7 @@ tests: asserts: - equal: path: spec.config.sse.url - value: http://RELEASE-NAME-mcp-grafana.NAMESPACE.svc.cluster.local:8000 + value: http://RELEASE-NAME-mcp-grafana.NAMESPACE.svc:8000 - equal: path: spec.config.sse.timeout value: 5s @@ -43,4 +43,4 @@ tests: value: RELEASE-NAME - equal: path: metadata.labels["app.kubernetes.io/managed-by"] - value: Helm \ No newline at end of file + value: Helm diff --git a/go/core/internal/controller/translator/agent/adk_api_translator.go b/go/core/internal/controller/translator/agent/adk_api_translator.go index b11d030aa..e509f8bcd 100644 --- a/go/core/internal/controller/translator/agent/adk_api_translator.go +++ b/go/core/internal/controller/translator/agent/adk_api_translator.go @@ -940,7 +940,7 @@ func (a *adkApiTranslator) translateRemoteMCPServerTarget(ctx context.Context, a // isInternalK8sURL checks if a URL points to an internal Kubernetes service. // Internal k8s URLs follow the pattern: http://{name}.{namespace}:{port} or -// http://{name}.{namespace}.svc.cluster.local:{port} +// http://{name}.{namespace}.svc:{port} // This method checks if the namespace exists in the cluster to determine if it's internal. func (a *adkApiTranslator) isInternalK8sURL(ctx context.Context, urlStr, namespace string) bool { parsedURL, err := url.Parse(urlStr) @@ -953,8 +953,8 @@ func (a *adkApiTranslator) isInternalK8sURL(ctx context.Context, urlStr, namespa return false } - // Check if it ends with .svc.cluster.local (definitely internal) - if strings.HasSuffix(hostname, ".svc.cluster.local") { + // Check if it's an internal FQDN by looking for .svc or .svc. + if strings.HasSuffix(hostname, ".svc") || strings.Contains(hostname, ".svc.") { return true } diff --git a/go/core/internal/controller/translator/agent/adk_translator_golden_test.go b/go/core/internal/controller/translator/agent/adk_translator_golden_test.go index 31a91dff7..0c8ff0e65 100644 --- a/go/core/internal/controller/translator/agent/adk_translator_golden_test.go +++ b/go/core/internal/controller/translator/agent/adk_translator_golden_test.go @@ -284,14 +284,14 @@ func extractNamespaceFromURL(urlStr string) string { return "" } - // Split hostname by dots: service.namespace or service.namespace.svc.cluster.local + // Split hostname by dots: service.namespace or service.namespace.svc hostname := parsed.Hostname() parts := strings.Split(hostname, ".") // Valid patterns: // - service.namespace (2 parts) // - service.namespace.svc (3 parts) - // - service.namespace.svc.cluster.local (5 parts) + // - service.namespace.svc (5 parts) if len(parts) >= 2 { return parts[1] // namespace is always the second part } diff --git a/go/core/internal/controller/translator/agent/proxy_test.go b/go/core/internal/controller/translator/agent/proxy_test.go index 3c6a843e7..4d19b3c21 100644 --- a/go/core/internal/controller/translator/agent/proxy_test.go +++ b/go/core/internal/controller/translator/agent/proxy_test.go @@ -119,7 +119,7 @@ func TestProxyConfiguration_ThroughTranslateAgent(t *testing.T) { kubeClient, types.NamespacedName{Name: "default-model", Namespace: "test"}, nil, - "http://proxy.kagent.svc.cluster.local:8080", + "http://proxy.kagent.svc:8080", nil, ) @@ -131,14 +131,14 @@ func TestProxyConfiguration_ThroughTranslateAgent(t *testing.T) { // Verify agent tool proxy configuration require.Len(t, result.Config.RemoteAgents, 1) remoteAgent := result.Config.RemoteAgents[0] - assert.Equal(t, "http://proxy.kagent.svc.cluster.local:8080", remoteAgent.Url) + assert.Equal(t, "http://proxy.kagent.svc:8080", remoteAgent.Url) assert.NotNil(t, remoteAgent.Headers) assert.Equal(t, "nested-agent.test", remoteAgent.Headers[agenttranslator.ProxyHostHeader]) // Verify RemoteMCPServer with internal k8s URL DOES use proxy require.Len(t, result.Config.HttpTools, 1) httpTool := result.Config.HttpTools[0] - assert.Equal(t, "http://proxy.kagent.svc.cluster.local:8080/mcp", httpTool.Params.Url) + assert.Equal(t, "http://proxy.kagent.svc:8080/mcp", httpTool.Params.Url) // Proxy header should be set for RemoteMCPServer with internal k8s URL (uses proxy) require.NotNil(t, httpTool.Params.Headers) assert.Equal(t, "test-mcp-server.kagent", httpTool.Params.Headers[agenttranslator.ProxyHostHeader]) @@ -254,14 +254,14 @@ func TestProxyConfiguration_RemoteMCPServer_FallsBackToWatchedNamespacesWhenName []string{"test", "kagent"}, types.NamespacedName{Name: "default-model", Namespace: "test"}, nil, - "http://proxy.kagent.svc.cluster.local:8080", + "http://proxy.kagent.svc:8080", nil, ) result, err := agenttranslator.TranslateAgent(ctx, translator, agent) require.NoError(t, err) require.Len(t, result.Config.HttpTools, 1) - assert.Equal(t, "http://proxy.kagent.svc.cluster.local:8080/mcp", result.Config.HttpTools[0].Params.Url) + assert.Equal(t, "http://proxy.kagent.svc:8080/mcp", result.Config.HttpTools[0].Params.Url) assert.Equal(t, "test-mcp-server.kagent", result.Config.HttpTools[0].Params.Headers[agenttranslator.ProxyHostHeader]) } @@ -336,7 +336,7 @@ func TestProxyConfiguration_RemoteMCPServer_ExternalURL(t *testing.T) { kubeClient, types.NamespacedName{Name: "default-model", Namespace: "test"}, nil, - "http://proxy.kagent.svc.cluster.local:8080", + "http://proxy.kagent.svc:8080", nil, ) @@ -429,7 +429,7 @@ func TestProxyConfiguration_MCPServer(t *testing.T) { kubeClient, types.NamespacedName{Name: "default-model", Namespace: "test"}, nil, - "http://proxy.kagent.svc.cluster.local:8080", + "http://proxy.kagent.svc:8080", nil, ) @@ -441,7 +441,7 @@ func TestProxyConfiguration_MCPServer(t *testing.T) { // Verify MCPServer uses proxy require.Len(t, result.Config.HttpTools, 1) httpTool := result.Config.HttpTools[0] - assert.Equal(t, "http://proxy.kagent.svc.cluster.local:8080/mcp", httpTool.Params.Url) + assert.Equal(t, "http://proxy.kagent.svc:8080/mcp", httpTool.Params.Url) // Proxy header should be set for MCPServer (uses proxy) require.NotNil(t, httpTool.Params.Headers) assert.Equal(t, "test-mcp-server.test", httpTool.Params.Headers[agenttranslator.ProxyHostHeader]) @@ -527,7 +527,7 @@ func TestProxyConfiguration_Service(t *testing.T) { kubeClient, types.NamespacedName{Name: "default-model", Namespace: "test"}, nil, - "http://proxy.kagent.svc.cluster.local:8080", + "http://proxy.kagent.svc:8080", nil, ) @@ -539,7 +539,7 @@ func TestProxyConfiguration_Service(t *testing.T) { // Verify Service uses proxy require.Len(t, result.Config.HttpTools, 1) httpTool := result.Config.HttpTools[0] - assert.Equal(t, "http://proxy.kagent.svc.cluster.local:8080/mcp", httpTool.Params.Url) + assert.Equal(t, "http://proxy.kagent.svc:8080/mcp", httpTool.Params.Url) // Proxy header should be set for Service (uses proxy) require.NotNil(t, httpTool.Params.Headers) assert.Equal(t, "test-service.test", httpTool.Params.Headers[agenttranslator.ProxyHostHeader]) diff --git a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy.yaml b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy.yaml index 6b8c84781..6de316d04 100644 --- a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy.yaml +++ b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy.yaml @@ -1,7 +1,7 @@ operation: translateAgent targetObject: agent-with-proxy namespace: test -proxyURL: http://proxy.kagent.svc.cluster.local:8080 +proxyURL: http://proxy.kagent.svc:8080 objects: - apiVersion: v1 kind: Secret diff --git a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_external_remotemcp.yaml b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_external_remotemcp.yaml index 3f83be46d..7602bbd28 100644 --- a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_external_remotemcp.yaml +++ b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_external_remotemcp.yaml @@ -1,7 +1,7 @@ operation: translateAgent targetObject: agent-with-proxy-external namespace: test -proxyURL: http://proxy.kagent.svc.cluster.local:8080 +proxyURL: http://proxy.kagent.svc:8080 objects: - apiVersion: v1 kind: Secret diff --git a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver.yaml b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver.yaml index 31f80b6be..fad20aa44 100644 --- a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver.yaml +++ b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver.yaml @@ -1,7 +1,7 @@ operation: translateAgent targetObject: agent-with-proxy-mcpserver namespace: test -proxyURL: http://proxy.kagent.svc.cluster.local:8080 +proxyURL: http://proxy.kagent.svc:8080 objects: - apiVersion: v1 kind: Secret diff --git a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver_custom_timeout.yaml b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver_custom_timeout.yaml index 6cf3cf887..b6a4d293d 100644 --- a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver_custom_timeout.yaml +++ b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_mcpserver_custom_timeout.yaml @@ -1,7 +1,7 @@ operation: translateAgent targetObject: agent-with-proxy-mcpserver-timeout namespace: test -proxyURL: http://proxy.kagent.svc.cluster.local:8080 +proxyURL: http://proxy.kagent.svc:8080 objects: - apiVersion: v1 kind: Secret diff --git a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_service.yaml b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_service.yaml index edf2e509d..78db2f95c 100644 --- a/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_service.yaml +++ b/go/core/internal/controller/translator/agent/testdata/inputs/agent_with_proxy_service.yaml @@ -1,7 +1,7 @@ operation: translateAgent targetObject: agent-with-proxy-service namespace: test -proxyURL: http://proxy.kagent.svc.cluster.local:8080 +proxyURL: http://proxy.kagent.svc:8080 objects: - apiVersion: v1 kind: Secret diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json index 901ce9033..607c855a5 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy.json @@ -26,7 +26,7 @@ "headers": { "x-kagent-host": "test-mcp-server.kagent" }, - "url": "http://proxy.kagent.svc.cluster.local:8080/mcp" + "url": "http://proxy.kagent.svc:8080/mcp" }, "tools": [ "test-tool" @@ -45,7 +45,7 @@ "x-kagent-host": "nested-agent.test" }, "name": "test__NS__nested_agent", - "url": "http://proxy.kagent.svc.cluster.local:8080" + "url": "http://proxy.kagent.svc:8080" } ], "stream": false @@ -77,7 +77,7 @@ }, "stringData": { "agent-card.json": "{\"name\":\"agent_with_proxy\",\"description\":\"\",\"url\":\"http://agent-with-proxy.test:8080\",\"version\":\"\",\"capabilities\":{\"streaming\":true,\"pushNotifications\":false,\"stateTransitionHistory\":true},\"defaultInputModes\":[\"text\"],\"defaultOutputModes\":[\"text\"],\"skills\":[],\"preferredTransport\":\"JSONRPC\"}", - "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc.cluster.local:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.kagent\"}},\"tools\":[\"test-tool\"]}],\"remote_agents\":[{\"name\":\"test__NS__nested_agent\",\"url\":\"http://proxy.kagent.svc.cluster.local:8080\",\"headers\":{\"x-kagent-host\":\"nested-agent.test\"}}],\"stream\":false}" + "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.kagent\"}},\"tools\":[\"test-tool\"]}],\"remote_agents\":[{\"name\":\"test__NS__nested_agent\",\"url\":\"http://proxy.kagent.svc:8080\",\"headers\":{\"x-kagent-host\":\"nested-agent.test\"}}],\"stream\":false}" } }, { @@ -305,4 +305,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json index e80a0bf78..4c3e8739c 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver.json @@ -27,7 +27,7 @@ "x-kagent-host": "test-mcp-server.test" }, "timeout": 30, - "url": "http://proxy.kagent.svc.cluster.local:8080/mcp" + "url": "http://proxy.kagent.svc:8080/mcp" }, "tools": [ "test-tool" @@ -69,7 +69,7 @@ }, "stringData": { "agent-card.json": "{\"name\":\"agent_with_proxy_mcpserver\",\"description\":\"\",\"url\":\"http://agent-with-proxy-mcpserver.test:8080\",\"version\":\"\",\"capabilities\":{\"streaming\":true,\"pushNotifications\":false,\"stateTransitionHistory\":true},\"defaultInputModes\":[\"text\"],\"defaultOutputModes\":[\"text\"],\"skills\":[],\"preferredTransport\":\"JSONRPC\"}", - "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc.cluster.local:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.test\"},\"timeout\":30},\"tools\":[\"test-tool\"]}],\"stream\":false}" + "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.test\"},\"timeout\":30},\"tools\":[\"test-tool\"]}],\"stream\":false}" } }, { @@ -297,4 +297,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json index 150bc253d..8eb782826 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_mcpserver_custom_timeout.json @@ -27,7 +27,7 @@ "x-kagent-host": "test-mcp-server.test" }, "timeout": 60, - "url": "http://proxy.kagent.svc.cluster.local:8080/mcp" + "url": "http://proxy.kagent.svc:8080/mcp" }, "tools": [ "test-tool" @@ -69,7 +69,7 @@ }, "stringData": { "agent-card.json": "{\"name\":\"agent_with_proxy_mcpserver_timeout\",\"description\":\"\",\"url\":\"http://agent-with-proxy-mcpserver-timeout.test:8080\",\"version\":\"\",\"capabilities\":{\"streaming\":true,\"pushNotifications\":false,\"stateTransitionHistory\":true},\"defaultInputModes\":[\"text\"],\"defaultOutputModes\":[\"text\"],\"skills\":[],\"preferredTransport\":\"JSONRPC\"}", - "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc.cluster.local:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.test\"},\"timeout\":60},\"tools\":[\"test-tool\"]}],\"stream\":false}" + "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc:8080/mcp\",\"headers\":{\"x-kagent-host\":\"test-mcp-server.test\"},\"timeout\":60},\"tools\":[\"test-tool\"]}],\"stream\":false}" } }, { @@ -297,4 +297,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json index 0010bc16d..31e2f7b37 100644 --- a/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json +++ b/go/core/internal/controller/translator/agent/testdata/outputs/agent_with_proxy_service.json @@ -26,7 +26,7 @@ "headers": { "x-kagent-host": "toolserver.test" }, - "url": "http://proxy.kagent.svc.cluster.local:8080/mcp" + "url": "http://proxy.kagent.svc:8080/mcp" }, "tools": [ "k8s_get_resources" @@ -68,7 +68,7 @@ }, "stringData": { "agent-card.json": "{\"name\":\"agent_with_proxy_service\",\"description\":\"\",\"url\":\"http://agent-with-proxy-service.test:8080\",\"version\":\"\",\"capabilities\":{\"streaming\":true,\"pushNotifications\":false,\"stateTransitionHistory\":true},\"defaultInputModes\":[\"text\"],\"defaultOutputModes\":[\"text\"],\"skills\":[],\"preferredTransport\":\"JSONRPC\"}", - "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc.cluster.local:8080/mcp\",\"headers\":{\"x-kagent-host\":\"toolserver.test\"}},\"tools\":[\"k8s_get_resources\"]}],\"stream\":false}" + "config.json": "{\"model\":{\"type\":\"openai\",\"model\":\"gpt-4o\",\"base_url\":\"\"},\"description\":\"\",\"instruction\":\"You are an agent that uses proxies.\",\"http_tools\":[{\"params\":{\"url\":\"http://proxy.kagent.svc:8080/mcp\",\"headers\":{\"x-kagent-host\":\"toolserver.test\"}},\"tools\":[\"k8s_get_resources\"]}],\"stream\":false}" } }, { @@ -296,4 +296,4 @@ } } ] -} \ No newline at end of file +} diff --git a/go/core/internal/httpserver/auth/proxy_authn_test.go b/go/core/internal/httpserver/auth/proxy_authn_test.go index abd18dd74..340ba9539 100644 --- a/go/core/internal/httpserver/auth/proxy_authn_test.go +++ b/go/core/internal/httpserver/auth/proxy_authn_test.go @@ -171,7 +171,7 @@ func TestProxyAuthenticator_JWTWithAgentHeader(t *testing.T) { name: "extracts agent identity from header when JWT is present", claims: map[string]any{ "sub": "system:serviceaccount:kagent:kebab-agent", - "iss": "https://kubernetes.default.svc.cluster.local", + "iss": "https://kubernetes.default.svc", "aud": []any{"kagent"}, }, agentName: "kagent__NS__kebab_agent", diff --git a/go/core/pkg/app/app.go b/go/core/pkg/app/app.go index eabd6ff05..09308bb73 100644 --- a/go/core/pkg/app/app.go +++ b/go/core/pkg/app/app.go @@ -139,6 +139,7 @@ type Config struct { UrlFile string VectorEnabled bool } + ClusterDomain string } func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { @@ -165,7 +166,7 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.StringVar(&cfg.DefaultModelConfig.Namespace, "default-model-config-namespace", kagentNamespace, "The namespace of the default model config.") commandLine.StringVar(&cfg.HttpServerAddr, "http-server-address", ":8083", "The address the HTTP server binds to.") commandLine.StringVar(&cfg.A2ABaseUrl, "a2a-base-url", "http://127.0.0.1:8083", "The base URL of the A2A Server endpoint, as advertised to clients.") - commandLine.StringVar(&cfg.Database.Url, "postgres-database-url", "postgres://postgres:kagent@kagent-postgresql.kagent.svc.cluster.local:5432/postgres", "The URL of the PostgreSQL database.") + commandLine.StringVar(&cfg.Database.Url, "postgres-database-url", "postgres://postgres:kagent@kagent-postgresql.kagent.svc:5432/postgres", "The URL of the PostgreSQL database.") commandLine.StringVar(&cfg.Database.UrlFile, "postgres-database-url-file", "", "Path to a file containing the PostgreSQL database URL. Takes precedence over --postgres-database-url.") commandLine.BoolVar(&cfg.Database.VectorEnabled, "database-vector-enabled", true, "Enable pgvector extension and memory table. Requires pgvector to be installed on the PostgreSQL server.") @@ -175,7 +176,7 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.Var(&cfg.Streaming.InitialBufSize, "streaming-initial-buf-size", "The initial size of the streaming buffer.") commandLine.DurationVar(&cfg.Streaming.Timeout, "streaming-timeout", 60*time.Second, "The timeout for the streaming connection.") - commandLine.StringVar(&cfg.Proxy.URL, "proxy-url", "", "Proxy URL for internally-built k8s URLs (e.g., http://proxy.kagent.svc.cluster.local:8080)") + commandLine.StringVar(&cfg.Proxy.URL, "proxy-url", "", "Proxy URL for internally-built k8s URLs (e.g., http://proxy.kagent.svc:8080)") commandLine.StringVar(&cfg.Auth.Mode, "auth-mode", "unsecure", "Authentication mode: unsecure or trusted-proxy") commandLine.StringVar(&cfg.Auth.UserIDClaim, "auth-user-id-claim", "sub", "JWT claim name for user identity") @@ -195,6 +196,7 @@ func (cfg *Config) SetFlags(commandLine *flag.FlagSet) { commandLine.Var(&MapValue{Target: &agent_translator.DefaultAgentPodLabels}, "default-agent-pod-labels", "Comma-separated key=value pairs of labels to apply to all agent pod templates (e.g. 'team=platform,env=prod'). Per-agent labels take precedence.") commandLine.StringVar(&agent_translator.DefaultAgentBindHost, "default-agent-bind-host", agent_translator.DefaultAgentBindHost, "Default host address for agent pods to bind to. Use '0.0.0.0' for IPv4 only or '::' for dual-stack (IPv4+IPv6).") + commandLine.StringVar(&cfg.ClusterDomain, "cluster-domain", "", "Optional cluster domain. If provided, internal service URLs will append it (e.g., .svc.cluster.local). If omitted, URLs will use .svc.") } // LoadFromEnv loads configuration values from environment variables. diff --git a/go/core/pkg/app/app_test.go b/go/core/pkg/app/app_test.go index 7b1a558ab..d92323e7a 100644 --- a/go/core/pkg/app/app_test.go +++ b/go/core/pkg/app/app_test.go @@ -399,7 +399,7 @@ func TestLoadFromEnvIntegration(t *testing.T) { "DEFAULT_MODEL_CONFIG_NAMESPACE": "custom-ns", "HTTP_SERVER_ADDRESS": ":9000", "A2A_BASE_URL": "http://example.com:9000", - "PROXY_URL": "http://proxy.kagent.svc.cluster.local:8080", + "PROXY_URL": "http://proxy.kagent.svc:8080", "POSTGRES_DATABASE_URL": "postgres://localhost:5432/testdb", "WATCH_NAMESPACES": "ns1,ns2,ns3", "STREAMING_TIMEOUT": "120s", @@ -444,8 +444,8 @@ func TestLoadFromEnvIntegration(t *testing.T) { if cfg.HttpServerAddr != ":9000" { t.Errorf("HttpServerAddr = %v, want :9000", cfg.HttpServerAddr) } - if cfg.Proxy.URL != "http://proxy.kagent.svc.cluster.local:8080" { - t.Errorf("Proxy.URL = %v, want http://proxy.kagent.svc.cluster.local:8080", cfg.Proxy.URL) + if cfg.Proxy.URL != "http://proxy.kagent.svc:8080" { + t.Errorf("Proxy.URL = %v, want http://proxy.kagent.svc:8080", cfg.Proxy.URL) } if cfg.A2ABaseUrl != "http://example.com:9000" { t.Errorf("A2ABaseUrl = %v, want http://example.com:9000", cfg.A2ABaseUrl) diff --git a/helm/kagent/templates/_helpers.tpl b/helm/kagent/templates/_helpers.tpl index 2c517ba21..e9217d7d9 100644 --- a/helm/kagent/templates/_helpers.tpl +++ b/helm/kagent/templates/_helpers.tpl @@ -154,6 +154,18 @@ Password secret name - returns the chart-managed Secret name for POSTGRES_PASSWO {{- printf "%s-postgresql" (include "kagent.fullname" .) -}} {{- end -}} +{{/* +Cluster Domain Suffix - checks if user has explicitly stated a clusterDomain. +If so, it appends .svc., otherwise it defaults to .svc +*/}} +{{- define "kagent.clusterDomainSuffix" -}} +{{- if .Values.clusterDomain -}} +{{- printf ".svc.%s" .Values.clusterDomain -}} +{{- else -}} +{{- ".svc" -}} +{{- end -}} +{{- end -}} + {{/* A2A Base URL - computes the default URL based on the controller service name if not explicitly set */}} @@ -161,7 +173,7 @@ A2A Base URL - computes the default URL based on the controller service name if {{- if .Values.controller.a2aBaseUrl -}} {{- .Values.controller.a2aBaseUrl -}} {{- else -}} -{{- printf "http://%s-controller.%s.svc.cluster.local:%d" (include "kagent.fullname" .) (include "kagent.namespace" .) (.Values.controller.service.ports.port | int) -}} +{{- printf "http://%s-controller.%s%s:%d" (include "kagent.fullname" .) (include "kagent.namespace" .) (include "kagent.clusterDomainSuffix" .) (.Values.controller.service.ports.port | int) -}} {{- end -}} {{- end -}} diff --git a/helm/kagent/templates/controller-deployment.yaml b/helm/kagent/templates/controller-deployment.yaml index ee7119b8e..fa03d4b2c 100644 --- a/helm/kagent/templates/controller-deployment.yaml +++ b/helm/kagent/templates/controller-deployment.yaml @@ -61,6 +61,10 @@ spec: valueFrom: fieldRef: fieldPath: spec.nodeName + {{- if .Values.clusterDomain }} + - name: CLUSTER_DOMAIN + value: {{ .Values.clusterDomain | quote }} + {{- end }} - name: AUTH_MODE value: {{ .Values.controller.auth.mode | default "unsecure" | quote }} {{- if .Values.controller.auth.userIdClaim }} @@ -80,7 +84,7 @@ spec: name: {{ include "kagent.passwordSecretName" . }} key: POSTGRES_PASSWORD - name: POSTGRES_DATABASE_URL - value: {{ printf "postgres://kagent:$(POSTGRES_PASSWORD)@%s.%s.svc.cluster.local:5432/kagent?sslmode=disable" (include "kagent.postgresqlServiceName" .) (include "kagent.namespace" .) | quote }} + value: {{ printf "postgres://kagent:$(POSTGRES_PASSWORD)@%s.%s%s:5432/kagent?sslmode=disable" (include "kagent.postgresqlServiceName" .) (include "kagent.namespace" .) (include "kagent.clusterDomainSuffix" .) | quote }} {{- else }} {{ fail "No database connection configured. Set database.postgres.url, database.postgres.urlFile, or enable database.postgres.bundled." }} {{- end }} diff --git a/helm/kagent/templates/ui-deployment.yaml b/helm/kagent/templates/ui-deployment.yaml index a5b685b5c..72a84106b 100644 --- a/helm/kagent/templates/ui-deployment.yaml +++ b/helm/kagent/templates/ui-deployment.yaml @@ -57,7 +57,7 @@ spec: imagePullPolicy: {{ .Values.ui.image.pullPolicy | default .Values.imagePullPolicy }} env: - name: NEXT_PUBLIC_BACKEND_URL - value: "http://{{ include "kagent.fullname" . }}-controller.{{ include "kagent.namespace" . }}.svc.cluster.local:{{ .Values.controller.service.ports.port }}/api" + value: "http://{{ include "kagent.fullname" . }}-controller.{{ include "kagent.namespace" . }}{{ include "kagent.clusterDomainSuffix" . }}:{{ .Values.controller.service.ports.port }}/api" {{- if .Values.ui.auth }} - name: SSO_REDIRECT_PATH value: {{ .Values.ui.auth.ssoRedirectPath | default "/oauth2/start" | quote }} diff --git a/helm/kagent/tests/controller-deployment_test.yaml b/helm/kagent/tests/controller-deployment_test.yaml index a35a9c227..e62fbda95 100644 --- a/helm/kagent/tests/controller-deployment_test.yaml +++ b/helm/kagent/tests/controller-deployment_test.yaml @@ -82,7 +82,7 @@ tests: asserts: - equal: path: data.A2A_BASE_URL - value: "http://RELEASE-NAME-controller.NAMESPACE.svc.cluster.local:8083" + value: "http://RELEASE-NAME-controller.NAMESPACE.svc:8083" - it: should set A2A_BASE_URL with custom value when explicitly set template: controller-configmap.yaml @@ -98,11 +98,11 @@ tests: template: controller-configmap.yaml set: proxy: - url: "http://proxy.kagent.svc.cluster.local:8080" + url: "http://proxy.kagent.svc:8080" asserts: - equal: path: data.PROXY_URL - value: "http://proxy.kagent.svc.cluster.local:8080" + value: "http://proxy.kagent.svc:8080" - it: should not set PROXY_URL when not set template: controller-configmap.yaml @@ -371,7 +371,7 @@ tests: path: spec.template.spec.containers[0].env content: name: POSTGRES_DATABASE_URL - value: "postgres://kagent:$(POSTGRES_PASSWORD)@RELEASE-NAME-postgresql.NAMESPACE.svc.cluster.local:5432/kagent?sslmode=disable" + value: "postgres://kagent:$(POSTGRES_PASSWORD)@RELEASE-NAME-postgresql.NAMESPACE.svc:5432/kagent?sslmode=disable" - it: should set POSTGRES_DATABASE_URL with external url when url is set template: controller-deployment.yaml diff --git a/helm/kagent/values.yaml b/helm/kagent/values.yaml index 446cc54e7..6d2540087 100644 --- a/helm/kagent/values.yaml +++ b/helm/kagent/values.yaml @@ -14,6 +14,12 @@ ipv6: enabled: false +# -- Optional cluster domain name (e.g., "cluster.local"). +# If provided, internal service URLs will use ..svc.. +# If omitted, internal URLs will default to ..svc and allow CoreDNS to resolve them. +# @default -- "" +clusterDomain: "" + tag: "" registry: "cr.kagent.dev" @@ -173,7 +179,7 @@ controller: # @default -- "" (controller falls back to "0.0.0.0"; "::" when ipv6.enabled) host: "" # -- The base URL of the A2A Server endpoint, as advertised to clients. - # @default -- `http://-controller..svc.cluster.local:` + # @default -- `http://-controller..svc:` a2aBaseUrl: "" agentImage: registry: "" @@ -404,7 +410,7 @@ kagent-tools: # Note: RemoteMCPServer resources use user-supplied URLs and do not use this proxy unless the URL is an internal k8s URL. proxy: # Proxy URL for internally-built k8s URLs - # Example: "http://proxy.kagent.svc.cluster.local:8080" + # Example: "http://proxy.kagent.svc:8080" or "http://proxy.kagent.svc.cluster.local:8080" url: "" # ============================================================================== diff --git a/python/packages/kagent-adk/tests/unittests/test_proxy_integration.py b/python/packages/kagent-adk/tests/unittests/test_proxy_integration.py index 8566b3db6..83f11e6a0 100644 --- a/python/packages/kagent-adk/tests/unittests/test_proxy_integration.py +++ b/python/packages/kagent-adk/tests/unittests/test_proxy_integration.py @@ -350,7 +350,7 @@ def test_mcp_tool_with_proxy_url(): http_tools=[ HttpMcpServerConfig( params=StreamableHTTPConnectionParams( - url="http://proxy.kagent.svc.cluster.local:8080/mcp", # Proxy URL + url="http://proxy.kagent.svc:8080/mcp", # Proxy URL headers={PROXY_HOST_HEADER: "test-mcp-server.kagent"}, # Proxy host header for proxy routing ), tools=["test-tool"], @@ -374,7 +374,7 @@ def test_mcp_tool_with_proxy_url(): # a public API to verify connection configuration. We're testing our code's configuration logic. connection_params = getattr(mcp_tool, "_connection_params", None) or getattr(mcp_tool, "connection_params", None) assert connection_params is not None - assert connection_params.url == "http://proxy.kagent.svc.cluster.local:8080/mcp" + assert connection_params.url == "http://proxy.kagent.svc:8080/mcp" assert connection_params.headers is not None assert connection_params.headers[PROXY_HOST_HEADER] == "test-mcp-server.kagent" @@ -447,7 +447,7 @@ def test_sse_mcp_tool_with_proxy_url(): sse_tools=[ SseMcpServerConfig( params=SseConnectionParams( - url="http://proxy.kagent.svc.cluster.local:8080/mcp", # Proxy URL + url="http://proxy.kagent.svc:8080/mcp", # Proxy URL headers={PROXY_HOST_HEADER: "test-sse-mcp-server.kagent"}, # Proxy host header for proxy routing ), tools=["test-sse-tool"], @@ -469,7 +469,7 @@ def test_sse_mcp_tool_with_proxy_url(): # Verify connection params are configured correctly connection_params = getattr(mcp_tool, "_connection_params", None) or getattr(mcp_tool, "connection_params", None) assert connection_params is not None - assert connection_params.url == "http://proxy.kagent.svc.cluster.local:8080/mcp" + assert connection_params.url == "http://proxy.kagent.svc:8080/mcp" assert connection_params.headers is not None assert connection_params.headers[PROXY_HOST_HEADER] == "test-sse-mcp-server.kagent" diff --git a/python/samples/langgraph/currency/agent.yaml b/python/samples/langgraph/currency/agent.yaml index e6d9b990d..bf67fe5a9 100644 --- a/python/samples/langgraph/currency/agent.yaml +++ b/python/samples/langgraph/currency/agent.yaml @@ -18,13 +18,13 @@ spec: - name: OTEL_TRACING_ENABLED value: "true" - name: OTEL_EXPORTER_OTLP_ENDPOINT - value: "http://jaeger-collector.jaeger.svc.cluster.local:4317" + value: "http://jaeger-collector.jaeger.svc:4317" - name: LANGSMITH_TRACING value: "true" - name: LANGSMITH_OTEL_ENABLED value: "true" - name: LANGSMITH_ENDPOINT - value: "http://jaeger-collector.jaeger.svc.cluster.local:4317" + value: "http://jaeger-collector.jaeger.svc:4317" - name: LANGSMITH_API_KEY value: "langsmith-api-key" - name: LANGSMITH_WORKSPACE_ID diff --git a/ui/src/lib/__tests__/utils.test.ts b/ui/src/lib/__tests__/utils.test.ts index 39e0fd8ea..16eff3402 100644 --- a/ui/src/lib/__tests__/utils.test.ts +++ b/ui/src/lib/__tests__/utils.test.ts @@ -25,7 +25,7 @@ describe('URL Generation Utilities', () => { value: 'production', configurable: true }); - expect(getBackendUrl()).toBe('http://kagent.kagent.svc.cluster.local/api'); + expect(getBackendUrl()).toBe('http://kagent.kagent.svc/api'); }); it('should use default development URL', () => { diff --git a/ui/src/lib/utils.ts b/ui/src/lib/utils.ts index 67ad340d4..a4a0b25f7 100644 --- a/ui/src/lib/utils.ts +++ b/ui/src/lib/utils.ts @@ -14,7 +14,7 @@ export function getBackendUrl() { if (process.env.NODE_ENV === "production") { // This is more of a fallback; the NEXT_PUBLIC_BACKEND_URL should be set in the Helm chart - return "http://kagent.kagent.svc.cluster.local/api"; + return "http://kagent.kagent.svc/api"; } // Fallback for local development From 758784e637057e13df20886e23292f104e69607c Mon Sep 17 00:00:00 2001 From: Eitan Yarmush Date: Wed, 29 Apr 2026 13:56:28 -0400 Subject: [PATCH 2/2] Apply suggestion from @Copilot Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Eitan Yarmush --- helm/kagent/tests/controller-deployment_test.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/helm/kagent/tests/controller-deployment_test.yaml b/helm/kagent/tests/controller-deployment_test.yaml index e62fbda95..5646dd952 100644 --- a/helm/kagent/tests/controller-deployment_test.yaml +++ b/helm/kagent/tests/controller-deployment_test.yaml @@ -84,6 +84,14 @@ tests: path: data.A2A_BASE_URL value: "http://RELEASE-NAME-controller.NAMESPACE.svc:8083" + - it: should set A2A_BASE_URL with computed custom cluster domain + template: controller-configmap.yaml + set: + clusterDomain: my.domain + asserts: + - equal: + path: data.A2A_BASE_URL + value: "http://RELEASE-NAME-controller.NAMESPACE.svc.my.domain:8083" - it: should set A2A_BASE_URL with custom value when explicitly set template: controller-configmap.yaml set: