From efe33ec0257b165b39c90ffd24d3b5dd0ff4665d Mon Sep 17 00:00:00 2001 From: Pedro Coutinho Date: Tue, 24 Feb 2026 19:55:12 -0800 Subject: [PATCH 1/2] Split ServiceEndpoint into host-network and pod-network fields Rename Host/Port to HostNetworkHost/HostNetworkPort and add new PodNetworkHost/PodNetworkPort fields to ServiceEndpoint. This allows the operator to configure different K8s API server endpoints for host-networked vs pod-networked pods. For host-networked pods, EnvVars() uses HostNetworkHost/HostNetworkPort. For pod-networked pods, EnvVars() uses PodNetworkHost/PodNetworkPort. Both inject the standard KUBERNETES_SERVICE_HOST/PORT env var names. The pod-network values are read from the kubernetes-services-endpoint ConfigMap using the KUBERNETES_SERVICE_HOST_POD_NETWORK and KUBERNETES_SERVICE_PORT_POD_NETWORK keys. Co-Authored-By: Claude Opus 4.6 --- .../installation/core_controller.go | 8 +-- pkg/controller/installation/validation.go | 2 +- .../installation/validation_test.go | 8 +-- pkg/controller/k8sapi/k8s-endpoint.go | 56 ++++++++++--------- pkg/controller/utils/utils.go | 6 +- pkg/controller/utils/utils_test.go | 4 +- pkg/render/apiserver_test.go | 34 +++++------ pkg/render/common/test/testing.go | 4 +- .../kubecontrollers/kube-controllers_test.go | 12 ++-- pkg/render/node.go | 4 +- pkg/render/node_test.go | 8 +-- pkg/render/windows_test.go | 4 +- 12 files changed, 79 insertions(+), 71 deletions(-) diff --git a/pkg/controller/installation/core_controller.go b/pkg/controller/installation/core_controller.go index 9ebf347be0..5c4c83c971 100644 --- a/pkg/controller/installation/core_controller.go +++ b/pkg/controller/installation/core_controller.go @@ -2047,8 +2047,8 @@ func serviceIPsAndPorts(svc *corev1.Service) []k8sapi.ServiceEndpoint { for _, ip := range ips { for _, port := range svc.Spec.Ports { endpoints = append(endpoints, k8sapi.ServiceEndpoint{ - Host: ip, - Port: fmt.Sprintf("%d", port.Port), + HostNetworkHost: ip, + HostNetworkPort: fmt.Sprintf("%d", port.Port), }) } } @@ -2071,8 +2071,8 @@ func serviceEndpointSlice(endpointSliceList *discoveryv1.EndpointSliceList) []k8 } endpoints = append(endpoints, k8sapi.ServiceEndpoint{ - Host: ip, - Port: fmt.Sprintf("%d", *port.Port), + HostNetworkHost: ip, + HostNetworkPort: fmt.Sprintf("%d", *port.Port), }) } } diff --git a/pkg/controller/installation/validation.go b/pkg/controller/installation/validation.go index 30fe58e99c..efdd8c7927 100644 --- a/pkg/controller/installation/validation.go +++ b/pkg/controller/installation/validation.go @@ -384,7 +384,7 @@ func validateCustomResource(instance *operatorv1.Installation) error { } if common.WindowsEnabled(instance.Spec) { - if k8sapi.Endpoint.Host == "" || k8sapi.Endpoint.Port == "" { + if k8sapi.Endpoint.HostNetworkHost == "" || k8sapi.Endpoint.HostNetworkPort == "" { return fmt.Errorf("services endpoint configmap '%s' does not have all required information for Calico Windows daemonset configuration", render.K8sSvcEndpointConfigMapName) } if instance.Spec.CNI.Type == operatorv1.PluginCalico { diff --git a/pkg/controller/installation/validation_test.go b/pkg/controller/installation/validation_test.go index b7859bdcb5..39f7b61c11 100644 --- a/pkg/controller/installation/validation_test.go +++ b/pkg/controller/installation/validation_test.go @@ -823,8 +823,8 @@ var _ = Describe("Installation validation tests", func() { } // Populate the k8s service endpoint (required for windows) k8sapi.Endpoint = k8sapi.ServiceEndpoint{ - Host: "1.2.3.4", - Port: "6443", + HostNetworkHost: "1.2.3.4", + HostNetworkPort: "6443", } Expect(fillDefaults(instance, nil)).NotTo(HaveOccurred()) err := validateCustomResource(instance) @@ -1010,8 +1010,8 @@ var _ = Describe("Installation validation tests", func() { } instance.Spec.CalicoNetwork.BGP = ptr.To(operator.BGPDisabled) k8sapi.Endpoint = k8sapi.ServiceEndpoint{ - Host: "1.2.3.4", - Port: "6443", + HostNetworkHost: "1.2.3.4", + HostNetworkPort: "6443", } }) Context("Windows disabled", func() { diff --git a/pkg/controller/k8sapi/k8s-endpoint.go b/pkg/controller/k8sapi/k8s-endpoint.go index 5a9ddcac2c..d44070b5d3 100644 --- a/pkg/controller/k8sapi/k8s-endpoint.go +++ b/pkg/controller/k8sapi/k8s-endpoint.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2024 Tigera, Inc. All rights reserved. +// Copyright (c) 2020-2026 Tigera, Inc. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -35,47 +35,53 @@ func init() { // We read whatever is in the variable. We would read "" if they were not set. // We decide at the point of usage what to do with the values. Endpoint = ServiceEndpoint{ - Host: os.Getenv("KUBERNETES_SERVICE_HOST"), - Port: os.Getenv("KUBERNETES_SERVICE_PORT"), + HostNetworkHost: os.Getenv("KUBERNETES_SERVICE_HOST"), + HostNetworkPort: os.Getenv("KUBERNETES_SERVICE_PORT"), } } // ServiceEndpoint is the Host/Port of the K8s endpoint. +// HostNetworkHost/HostNetworkPort are used for host-networked pods, while +// PodNetworkHost/PodNetworkPort are used for pod-networked pods. type ServiceEndpoint struct { - Host string - Port string + HostNetworkHost string + HostNetworkPort string + PodNetworkHost string + PodNetworkPort string } -// EnvVars returns a slice of v1.EnvVars KUBERNETES_SERVICE_HOST/PORT if the Host and Port +// EnvVars returns a slice of v1.EnvVars with the K8s service endpoint if the Host and Port // of the ServiceEndpoint were set. It returns a nil slice if either was empty as both -// need to be set. +// need to be set. For host-networked pods, it returns KUBERNETES_SERVICE_HOST/PORT. +// For pod-networked pods, it returns KUBERNETES_SERVICE_HOST_POD_NETWORK/PORT_POD_NETWORK. func (k8s ServiceEndpoint) EnvVars(hostNetworked bool, provider operator.Provider) []v1.EnvVar { - if k8s.Host == "" || k8s.Port == "" { - return nil + if !hostNetworked { + if k8s.PodNetworkHost == "" || k8s.PodNetworkPort == "" { + return nil + } + return []v1.EnvVar{ + {Name: "KUBERNETES_SERVICE_HOST", Value: k8s.PodNetworkHost}, + {Name: "KUBERNETES_SERVICE_PORT", Value: k8s.PodNetworkPort}, + } } - if provider == operator.ProviderDockerEE && !hostNetworked && k8s.Host == dockerEEProxyLocal { - // Special case: Docker EE (now MKE) has a proxy on each host that is only accessible from the host - // namespace. Don't try to use it from non-host network pods. - // - // It's also possible for the user to configure a different route to the API server; we let those through. + if k8s.HostNetworkHost == "" || k8s.HostNetworkPort == "" { return nil } - return []v1.EnvVar{ - {Name: "KUBERNETES_SERVICE_HOST", Value: k8s.Host}, - {Name: "KUBERNETES_SERVICE_PORT", Value: k8s.Port}, + {Name: "KUBERNETES_SERVICE_HOST", Value: k8s.HostNetworkHost}, + {Name: "KUBERNETES_SERVICE_PORT", Value: k8s.HostNetworkPort}, } } -// DestinationEntityRule returns an EntityRule to match the Host and Port +// DestinationEntityRule returns an EntityRule to match the HostNetworkHost and HostNetworkPort // if the ServiceEndpoint was set. It returns nil if either was empty. func (k8s ServiceEndpoint) DestinationEntityRule() (*calicov3.EntityRule, error) { - if k8s.Host == "" || k8s.Port == "" { + if k8s.HostNetworkHost == "" || k8s.HostNetworkPort == "" { return nil, nil } - p, err := numorstring.PortFromString(k8s.Port) + p, err := numorstring.PortFromString(k8s.HostNetworkPort) if err != nil { return nil, err } @@ -84,9 +90,9 @@ func (k8s ServiceEndpoint) DestinationEntityRule() (*calicov3.EntityRule, error) Ports: []numorstring.Port{p}, } - ip := net.ParseIP(k8s.Host) + ip := net.ParseIP(k8s.HostNetworkHost) if ip == nil { - rule.Domains = []string{k8s.Host} + rule.Domains = []string{k8s.HostNetworkHost} } else { var netSuffix string if ip.To4() != nil { @@ -101,12 +107,12 @@ func (k8s ServiceEndpoint) DestinationEntityRule() (*calicov3.EntityRule, error) } func (k8s ServiceEndpoint) CNIAPIRoot() string { - if k8s.Host == "" || k8s.Port == "" { + if k8s.HostNetworkHost == "" || k8s.HostNetworkPort == "" { return "" } - host := k8s.Host + host := k8s.HostNetworkHost if strings.Contains(host, ":") { host = "[" + host + "]" } - return fmt.Sprintf("https://%s:%s", host, k8s.Port) + return fmt.Sprintf("https://%s:%s", host, k8s.HostNetworkPort) } diff --git a/pkg/controller/utils/utils.go b/pkg/controller/utils/utils.go index 057d4fbfe4..5bbf0d4dee 100644 --- a/pkg/controller/utils/utils.go +++ b/pkg/controller/utils/utils.go @@ -449,8 +449,10 @@ func PopulateK8sServiceEndPoint(client client.Client) error { return fmt.Errorf("failed to read ConfigMap %q: %s", render.K8sSvcEndpointConfigMapName, err) } } else { - k8sapi.Endpoint.Host = cm.Data["KUBERNETES_SERVICE_HOST"] - k8sapi.Endpoint.Port = cm.Data["KUBERNETES_SERVICE_PORT"] + k8sapi.Endpoint.HostNetworkHost = cm.Data["KUBERNETES_SERVICE_HOST"] + k8sapi.Endpoint.HostNetworkPort = cm.Data["KUBERNETES_SERVICE_PORT"] + k8sapi.Endpoint.PodNetworkHost = cm.Data["KUBERNETES_SERVICE_HOST_POD_NETWORK"] + k8sapi.Endpoint.PodNetworkPort = cm.Data["KUBERNETES_SERVICE_PORT_POD_NETWORK"] } return nil } diff --git a/pkg/controller/utils/utils_test.go b/pkg/controller/utils/utils_test.go index b53f335b1f..e70b0e91a1 100644 --- a/pkg/controller/utils/utils_test.go +++ b/pkg/controller/utils/utils_test.go @@ -262,8 +262,8 @@ var _ = Describe("PopulateK8sServiceEndPoint", func() { Expect(err).To(BeNil()) - Expect(k8sapi.Endpoint.Host).To(Equal("1.2.3.4")) - Expect(k8sapi.Endpoint.Port).To(Equal("5678")) + Expect(k8sapi.Endpoint.HostNetworkHost).To(Equal("1.2.3.4")) + Expect(k8sapi.Endpoint.HostNetworkPort).To(Equal("5678")) }) It("does not return error if ConfigMap is not found.", func() { diff --git a/pkg/render/apiserver_test.go b/pkg/render/apiserver_test.go index c3e716b0bb..0d0595d1ca 100644 --- a/pkg/render/apiserver_test.go +++ b/pkg/render/apiserver_test.go @@ -735,8 +735,8 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { }) It("should set KUBERENETES_SERVICE_... variables if host networked", func() { - cfg.K8SServiceEndpoint.Host = "k8shost" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.HostNetworkHost = "k8shost" + cfg.K8SServiceEndpoint.HostNetworkPort = "1234" cfg.ForceHostNetwork = true component, err := render.APIServer(cfg) Expect(err).To(BeNil(), "Expected APIServer to create successfully %s", err) @@ -760,8 +760,8 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { }) It("should add egress policy with Enterprise variant and K8SServiceEndpoint defined", func() { - cfg.K8SServiceEndpoint.Host = "k8shost" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.HostNetworkHost = "k8shost" + cfg.K8SServiceEndpoint.HostNetworkPort = "1234" cfg.ForceHostNetwork = true component := render.APIServerPolicy(cfg) @@ -782,8 +782,8 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { }) It("should add egress policy with Enterprise variant and K8SServiceEndpoint as IP defined", func() { - cfg.K8SServiceEndpoint.Host = "169.169.169.169" - cfg.K8SServiceEndpoint.Port = "4321" + cfg.K8SServiceEndpoint.HostNetworkHost = "169.169.169.169" + cfg.K8SServiceEndpoint.HostNetworkPort = "4321" cfg.ForceHostNetwork = false component := render.APIServerPolicy(cfg) @@ -804,8 +804,8 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { }) It("should not set KUBERENETES_SERVICE_... variables if not host networked on Docker EE with proxy.local", func() { - cfg.K8SServiceEndpoint.Host = "proxy.local" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.HostNetworkHost = "proxy.local" + cfg.K8SServiceEndpoint.HostNetworkPort = "1234" cfg.Installation.KubernetesProvider = operatorv1.ProviderDockerEE component, err := render.APIServer(cfg) @@ -821,8 +821,8 @@ var _ = Describe("API server rendering tests (Calico Enterprise)", func() { }) It("should set KUBERENETES_SERVICE_... variables if not host networked on Docker EE with non-proxy address", func() { - cfg.K8SServiceEndpoint.Host = "k8shost" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.PodNetworkHost = "k8shost" + cfg.K8SServiceEndpoint.PodNetworkPort = "1234" cfg.Installation.KubernetesProvider = operatorv1.ProviderDockerEE component, err := render.APIServer(cfg) @@ -2014,8 +2014,8 @@ var _ = Describe("API server rendering tests (Calico)", func() { }) It("should set KUBERNETES_SERVICE_... variables if host networked", func() { - cfg.K8SServiceEndpoint.Host = "k8shost" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.HostNetworkHost = "k8shost" + cfg.K8SServiceEndpoint.HostNetworkPort = "1234" cfg.Installation.KubernetesProvider = operatorv1.ProviderDockerEE cfg.ForceHostNetwork = true @@ -2041,8 +2041,8 @@ var _ = Describe("API server rendering tests (Calico)", func() { }) It("should not set KUBERNETES_SERVICE_... variables if Docker EE using proxy.local", func() { - cfg.K8SServiceEndpoint.Host = "proxy.local" - cfg.K8SServiceEndpoint.Port = "1234" + cfg.K8SServiceEndpoint.HostNetworkHost = "proxy.local" + cfg.K8SServiceEndpoint.HostNetworkPort = "1234" cfg.Installation.KubernetesProvider = operatorv1.ProviderDockerEE component, err := render.APIServer(cfg) @@ -2057,9 +2057,9 @@ var _ = Describe("API server rendering tests (Calico)", func() { rtest.ExpectNoK8sServiceEpEnvVars(deployment.Spec.Template.Spec) }) - It("should not set KUBERNETES_SERVICE_... variables if Docker EE using non-proxy address", func() { - cfg.K8SServiceEndpoint.Host = "k8shost" - cfg.K8SServiceEndpoint.Port = "1234" + It("should set KUBERNETES_SERVICE_... variables if Docker EE using non-proxy address", func() { + cfg.K8SServiceEndpoint.PodNetworkHost = "k8shost" + cfg.K8SServiceEndpoint.PodNetworkPort = "1234" cfg.Installation.KubernetesProvider = operatorv1.ProviderDockerEE component, err := render.APIServer(cfg) diff --git a/pkg/render/common/test/testing.go b/pkg/render/common/test/testing.go index 396393a6f6..2530fb9ed9 100644 --- a/pkg/render/common/test/testing.go +++ b/pkg/render/common/test/testing.go @@ -44,13 +44,13 @@ func ExpectK8sServiceEpEnvVars(podSpec corev1.PodSpec, host, port string) { ExpectWithOffset(1, c.Env).To(ContainElements( corev1.EnvVar{Name: "KUBERNETES_SERVICE_HOST", Value: host}, corev1.EnvVar{Name: "KUBERNETES_SERVICE_PORT", Value: port}, - ), fmt.Sprintf("Container %s did not have KUBERENETES_SERVICE_... env vars", c.Name)) + ), fmt.Sprintf("Container %s did not have KUBERNETES_SERVICE_... env vars", c.Name)) } for _, c := range podSpec.InitContainers { ExpectWithOffset(1, c.Env).To(ContainElements( corev1.EnvVar{Name: "KUBERNETES_SERVICE_HOST", Value: host}, corev1.EnvVar{Name: "KUBERNETES_SERVICE_PORT", Value: port}, - ), fmt.Sprintf("Init container %s did not have KUBERENETES_SERVICE_... env vars", c.Name)) + ), fmt.Sprintf("Init container %s did not have KUBERNETES_SERVICE_... env vars", c.Name)) } } diff --git a/pkg/render/kubecontrollers/kube-controllers_test.go b/pkg/render/kubecontrollers/kube-controllers_test.go index e8325d8152..dd435971be 100644 --- a/pkg/render/kubecontrollers/kube-controllers_test.go +++ b/pkg/render/kubecontrollers/kube-controllers_test.go @@ -977,8 +977,8 @@ var _ = Describe("kube-controllers rendering tests", func() { }) It("should add the KUBERNETES_SERVICE_... variables", func() { - k8sServiceEp.Host = "k8shost" - k8sServiceEp.Port = "1234" + k8sServiceEp.PodNetworkHost = "k8shost" + k8sServiceEp.PodNetworkPort = "1234" cfg.K8sServiceEp = k8sServiceEp component := kubecontrollers.NewCalicoKubeControllers(&cfg) @@ -992,8 +992,8 @@ var _ = Describe("kube-controllers rendering tests", func() { }) It("should not add the KUBERNETES_SERVICE_... variables on docker EE using proxy.local", func() { - k8sServiceEp.Host = "proxy.local" - k8sServiceEp.Port = "1234" + k8sServiceEp.HostNetworkHost = "proxy.local" + k8sServiceEp.HostNetworkPort = "1234" instance.KubernetesProvider = operatorv1.ProviderDockerEE component := kubecontrollers.NewCalicoKubeControllers(&cfg) @@ -1149,8 +1149,8 @@ var _ = Describe("kube-controllers rendering tests", func() { }) It("should add egress policy with Enterprise variant and K8SServiceEndpoint defined", func() { - cfg.K8sServiceEp.Host = "k8shost" - cfg.K8sServiceEp.Port = "1234" + cfg.K8sServiceEp.HostNetworkHost = "k8shost" + cfg.K8sServiceEp.HostNetworkPort = "1234" objects, _ := kubecontrollers.NewCalicoKubeControllersPolicy(&cfg, nil).Objects() Expect(objects).To(HaveLen(1)) policy, ok := objects[0].(*v3.NetworkPolicy) diff --git a/pkg/render/node.go b/pkg/render/node.go index 45174fd668..261fb4e66d 100644 --- a/pkg/render/node.go +++ b/pkg/render/node.go @@ -1275,10 +1275,10 @@ func JoinServiceEndpoints(endpoints []k8sapi.ServiceEndpoint) string { var parts []string for _, ep := range endpoints { - if ep.Host == "" || ep.Port == "" { + if ep.HostNetworkHost == "" || ep.HostNetworkPort == "" { continue } - parts = append(parts, net.JoinHostPort(ep.Host, ep.Port)) + parts = append(parts, net.JoinHostPort(ep.HostNetworkHost, ep.HostNetworkPort)) } return strings.Join(parts, ",") } diff --git a/pkg/render/node_test.go b/pkg/render/node_test.go index 4f7dc49ace..000c3b9444 100644 --- a/pkg/render/node_test.go +++ b/pkg/render/node_test.go @@ -2596,8 +2596,8 @@ var _ = Describe("Node rendering tests", func() { }) It("should render cni config with k8s endpoint", func() { - k8sServiceEp.Host = "k8shost" - k8sServiceEp.Port = "1234" + k8sServiceEp.HostNetworkHost = "k8shost" + k8sServiceEp.HostNetworkPort = "1234" cfg.K8sServiceEp = k8sServiceEp component := render.Node(&cfg) Expect(component.ResolveImages(nil)).To(BeNil()) @@ -2892,8 +2892,8 @@ var _ = Describe("Node rendering tests", func() { Context("with k8s overrides set", func() { It("should override k8s endpoints", func() { cfg.K8sServiceEp = k8sapi.ServiceEndpoint{ - Host: "k8shost", - Port: "1234", + HostNetworkHost: "k8shost", + HostNetworkPort: "1234", } component := render.Node(&cfg) Expect(component.ResolveImages(nil)).To(BeNil()) diff --git a/pkg/render/windows_test.go b/pkg/render/windows_test.go index 922c512490..1cb36bf6e7 100644 --- a/pkg/render/windows_test.go +++ b/pkg/render/windows_test.go @@ -101,8 +101,8 @@ var _ = Describe("Windows rendering tests", func() { // Dummy service endpoint for k8s API. k8sServiceEp = k8sapi.ServiceEndpoint{ - Host: "1.2.3.4", - Port: "6443", + HostNetworkHost: "1.2.3.4", + HostNetworkPort: "6443", } // Create a default configuration. From a24a266d631c364015cd8c29f4f13c19aef40701 Mon Sep 17 00:00:00 2001 From: Pedro Coutinho Date: Wed, 25 Feb 2026 10:57:11 -0800 Subject: [PATCH 2/2] fix static check --- pkg/controller/k8sapi/k8s-endpoint.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkg/controller/k8sapi/k8s-endpoint.go b/pkg/controller/k8sapi/k8s-endpoint.go index d44070b5d3..3de42196d0 100644 --- a/pkg/controller/k8sapi/k8s-endpoint.go +++ b/pkg/controller/k8sapi/k8s-endpoint.go @@ -26,8 +26,6 @@ import ( v1 "k8s.io/api/core/v1" ) -const dockerEEProxyLocal = "proxy.local" - // Endpoint is the default ServiceEndpoint learned from environment variables. var Endpoint ServiceEndpoint