
Move the ServicePort/BaseServicePortInfo types to serviceport.go. Move the Endpoint/BaseEndpointInfo types to endpoint.go. To avoid confusion with the new filenames, rename service.go to servicechangetracker.go and endpoints.go to endpointschangetracker.go. (No code changes; this just moves some code from types.go and services.go to serviceport.go, and some code from types.go and endpoints.go to endpoint.go.)
277 lines
11 KiB
Go
277 lines
11 KiB
Go
/*
|
|
Copyright 2017 The Kubernetes 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 proxy
|
|
|
|
import (
|
|
"fmt"
|
|
"net"
|
|
"strings"
|
|
|
|
v1 "k8s.io/api/core/v1"
|
|
"k8s.io/klog/v2"
|
|
apiservice "k8s.io/kubernetes/pkg/api/v1/service"
|
|
proxyutil "k8s.io/kubernetes/pkg/proxy/util"
|
|
netutils "k8s.io/utils/net"
|
|
)
|
|
|
|
// ServicePort is an interface which abstracts information about a service.
|
|
type ServicePort interface {
|
|
// String returns service string. An example format can be: `IP:Port/Protocol`.
|
|
String() string
|
|
// ClusterIP returns service cluster IP in net.IP format.
|
|
ClusterIP() net.IP
|
|
// Port returns service port if present. If return 0 means not present.
|
|
Port() int
|
|
// SessionAffinityType returns service session affinity type
|
|
SessionAffinityType() v1.ServiceAffinity
|
|
// StickyMaxAgeSeconds returns service max connection age
|
|
StickyMaxAgeSeconds() int
|
|
// ExternalIPStrings returns service ExternalIPs as a string array.
|
|
ExternalIPStrings() []string
|
|
// LoadBalancerVIPStrings returns service LoadBalancerIPs which are VIP mode as a string array.
|
|
LoadBalancerVIPStrings() []string
|
|
// Protocol returns service protocol.
|
|
Protocol() v1.Protocol
|
|
// LoadBalancerSourceRanges returns service LoadBalancerSourceRanges if present empty array if not
|
|
LoadBalancerSourceRanges() []string
|
|
// HealthCheckNodePort returns service health check node port if present. If return 0, it means not present.
|
|
HealthCheckNodePort() int
|
|
// NodePort returns a service Node port if present. If return 0, it means not present.
|
|
NodePort() int
|
|
// ExternalPolicyLocal returns if a service has only node local endpoints for external traffic.
|
|
ExternalPolicyLocal() bool
|
|
// InternalPolicyLocal returns if a service has only node local endpoints for internal traffic.
|
|
InternalPolicyLocal() bool
|
|
// HintsAnnotation returns the value of the v1.DeprecatedAnnotationTopologyAwareHints annotation.
|
|
HintsAnnotation() string
|
|
// ExternallyAccessible returns true if the service port is reachable via something
|
|
// other than ClusterIP (NodePort/ExternalIP/LoadBalancer)
|
|
ExternallyAccessible() bool
|
|
// UsesClusterEndpoints returns true if the service port ever sends traffic to
|
|
// endpoints based on "Cluster" traffic policy
|
|
UsesClusterEndpoints() bool
|
|
// UsesLocalEndpoints returns true if the service port ever sends traffic to
|
|
// endpoints based on "Local" traffic policy
|
|
UsesLocalEndpoints() bool
|
|
}
|
|
|
|
// BaseServicePortInfo contains base information that defines a service.
|
|
// This could be used directly by proxier while processing services,
|
|
// or can be used for constructing a more specific ServiceInfo struct
|
|
// defined by the proxier if needed.
|
|
type BaseServicePortInfo struct {
|
|
clusterIP net.IP
|
|
port int
|
|
protocol v1.Protocol
|
|
nodePort int
|
|
loadBalancerVIPs []string
|
|
sessionAffinityType v1.ServiceAffinity
|
|
stickyMaxAgeSeconds int
|
|
externalIPs []string
|
|
loadBalancerSourceRanges []string
|
|
healthCheckNodePort int
|
|
externalPolicyLocal bool
|
|
internalPolicyLocal bool
|
|
hintsAnnotation string
|
|
}
|
|
|
|
var _ ServicePort = &BaseServicePortInfo{}
|
|
|
|
// String is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) String() string {
|
|
return fmt.Sprintf("%s:%d/%s", bsvcPortInfo.clusterIP, bsvcPortInfo.port, bsvcPortInfo.protocol)
|
|
}
|
|
|
|
// ClusterIP is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) ClusterIP() net.IP {
|
|
return bsvcPortInfo.clusterIP
|
|
}
|
|
|
|
// Port is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) Port() int {
|
|
return bsvcPortInfo.port
|
|
}
|
|
|
|
// SessionAffinityType is part of the ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) SessionAffinityType() v1.ServiceAffinity {
|
|
return bsvcPortInfo.sessionAffinityType
|
|
}
|
|
|
|
// StickyMaxAgeSeconds is part of the ServicePort interface
|
|
func (bsvcPortInfo *BaseServicePortInfo) StickyMaxAgeSeconds() int {
|
|
return bsvcPortInfo.stickyMaxAgeSeconds
|
|
}
|
|
|
|
// Protocol is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) Protocol() v1.Protocol {
|
|
return bsvcPortInfo.protocol
|
|
}
|
|
|
|
// LoadBalancerSourceRanges is part of ServicePort interface
|
|
func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerSourceRanges() []string {
|
|
return bsvcPortInfo.loadBalancerSourceRanges
|
|
}
|
|
|
|
// HealthCheckNodePort is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) HealthCheckNodePort() int {
|
|
return bsvcPortInfo.healthCheckNodePort
|
|
}
|
|
|
|
// NodePort is part of the ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) NodePort() int {
|
|
return bsvcPortInfo.nodePort
|
|
}
|
|
|
|
// ExternalIPStrings is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) ExternalIPStrings() []string {
|
|
return bsvcPortInfo.externalIPs
|
|
}
|
|
|
|
// LoadBalancerVIPStrings is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerVIPStrings() []string {
|
|
return bsvcPortInfo.loadBalancerVIPs
|
|
}
|
|
|
|
// ExternalPolicyLocal is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) ExternalPolicyLocal() bool {
|
|
return bsvcPortInfo.externalPolicyLocal
|
|
}
|
|
|
|
// InternalPolicyLocal is part of ServicePort interface
|
|
func (bsvcPortInfo *BaseServicePortInfo) InternalPolicyLocal() bool {
|
|
return bsvcPortInfo.internalPolicyLocal
|
|
}
|
|
|
|
// HintsAnnotation is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) HintsAnnotation() string {
|
|
return bsvcPortInfo.hintsAnnotation
|
|
}
|
|
|
|
// ExternallyAccessible is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) ExternallyAccessible() bool {
|
|
return bsvcPortInfo.nodePort != 0 || len(bsvcPortInfo.loadBalancerVIPs) != 0 || len(bsvcPortInfo.externalIPs) != 0
|
|
}
|
|
|
|
// UsesClusterEndpoints is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) UsesClusterEndpoints() bool {
|
|
// The service port uses Cluster endpoints if the internal traffic policy is "Cluster",
|
|
// or if it accepts external traffic at all. (Even if the external traffic policy is
|
|
// "Local", we need Cluster endpoints to implement short circuiting.)
|
|
return !bsvcPortInfo.internalPolicyLocal || bsvcPortInfo.ExternallyAccessible()
|
|
}
|
|
|
|
// UsesLocalEndpoints is part of ServicePort interface.
|
|
func (bsvcPortInfo *BaseServicePortInfo) UsesLocalEndpoints() bool {
|
|
return bsvcPortInfo.internalPolicyLocal || (bsvcPortInfo.externalPolicyLocal && bsvcPortInfo.ExternallyAccessible())
|
|
}
|
|
|
|
func newBaseServiceInfo(service *v1.Service, ipFamily v1.IPFamily, port *v1.ServicePort) *BaseServicePortInfo {
|
|
externalPolicyLocal := apiservice.ExternalPolicyLocal(service)
|
|
internalPolicyLocal := apiservice.InternalPolicyLocal(service)
|
|
|
|
var stickyMaxAgeSeconds int
|
|
if service.Spec.SessionAffinity == v1.ServiceAffinityClientIP {
|
|
// Kube-apiserver side guarantees SessionAffinityConfig won't be nil when session affinity type is ClientIP
|
|
stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
|
|
}
|
|
|
|
clusterIP := proxyutil.GetClusterIPByFamily(ipFamily, service)
|
|
info := &BaseServicePortInfo{
|
|
clusterIP: netutils.ParseIPSloppy(clusterIP),
|
|
port: int(port.Port),
|
|
protocol: port.Protocol,
|
|
nodePort: int(port.NodePort),
|
|
sessionAffinityType: service.Spec.SessionAffinity,
|
|
stickyMaxAgeSeconds: stickyMaxAgeSeconds,
|
|
externalPolicyLocal: externalPolicyLocal,
|
|
internalPolicyLocal: internalPolicyLocal,
|
|
}
|
|
|
|
// v1.DeprecatedAnnotationTopologyAwareHints has precedence over v1.AnnotationTopologyMode.
|
|
var exists bool
|
|
info.hintsAnnotation, exists = service.Annotations[v1.DeprecatedAnnotationTopologyAwareHints]
|
|
if !exists {
|
|
info.hintsAnnotation = service.Annotations[v1.AnnotationTopologyMode]
|
|
}
|
|
|
|
loadBalancerSourceRanges := make([]string, len(service.Spec.LoadBalancerSourceRanges))
|
|
for i, sourceRange := range service.Spec.LoadBalancerSourceRanges {
|
|
loadBalancerSourceRanges[i] = strings.TrimSpace(sourceRange)
|
|
}
|
|
// filter external ips, source ranges and ingress ips
|
|
// prior to dual stack services, this was considered an error, but with dual stack
|
|
// services, this is actually expected. Hence we downgraded from reporting by events
|
|
// to just log lines with high verbosity
|
|
|
|
ipFamilyMap := proxyutil.MapIPsByIPFamily(service.Spec.ExternalIPs)
|
|
info.externalIPs = ipFamilyMap[ipFamily]
|
|
|
|
// Log the IPs not matching the ipFamily
|
|
if ips, ok := ipFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(ips) > 0 {
|
|
klog.V(4).InfoS("Service change tracker ignored the following external IPs for given service as they don't match IP Family",
|
|
"ipFamily", ipFamily, "externalIPs", strings.Join(ips, ", "), "service", klog.KObj(service))
|
|
}
|
|
|
|
ipFamilyMap = proxyutil.MapCIDRsByIPFamily(loadBalancerSourceRanges)
|
|
info.loadBalancerSourceRanges = ipFamilyMap[ipFamily]
|
|
// Log the CIDRs not matching the ipFamily
|
|
if cidrs, ok := ipFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(cidrs) > 0 {
|
|
klog.V(4).InfoS("Service change tracker ignored the following load balancer source ranges for given Service as they don't match IP Family",
|
|
"ipFamily", ipFamily, "loadBalancerSourceRanges", strings.Join(cidrs, ", "), "service", klog.KObj(service))
|
|
}
|
|
|
|
// Obtain Load Balancer Ingress
|
|
var invalidIPs []string
|
|
for _, ing := range service.Status.LoadBalancer.Ingress {
|
|
if ing.IP == "" {
|
|
continue
|
|
}
|
|
|
|
// proxy mode load balancers do not need to track the IPs in the service cache
|
|
// and they can also implement IP family translation, so no need to check if
|
|
// the status ingress.IP and the ClusterIP belong to the same family.
|
|
if !proxyutil.IsVIPMode(ing) {
|
|
klog.V(4).InfoS("Service change tracker ignored the following load balancer ingress IP for given Service as it using Proxy mode",
|
|
"ipFamily", ipFamily, "loadBalancerIngressIP", ing.IP, "service", klog.KObj(service))
|
|
continue
|
|
}
|
|
|
|
// kube-proxy does not implement IP family translation, skip addresses with
|
|
// different IP family
|
|
if ingFamily := proxyutil.GetIPFamilyFromIP(ing.IP); ingFamily == ipFamily {
|
|
info.loadBalancerVIPs = append(info.loadBalancerVIPs, ing.IP)
|
|
} else {
|
|
invalidIPs = append(invalidIPs, ing.IP)
|
|
}
|
|
}
|
|
if len(invalidIPs) > 0 {
|
|
klog.V(4).InfoS("Service change tracker ignored the following load balancer ingress IPs for given Service as they don't match the IP Family",
|
|
"ipFamily", ipFamily, "loadBalancerIngressIPs", strings.Join(invalidIPs, ", "), "service", klog.KObj(service))
|
|
}
|
|
|
|
if apiservice.NeedsHealthCheck(service) {
|
|
p := service.Spec.HealthCheckNodePort
|
|
if p == 0 {
|
|
klog.ErrorS(nil, "Service has no healthcheck nodeport", "service", klog.KObj(service))
|
|
} else {
|
|
info.healthCheckNodePort = int(p)
|
|
}
|
|
}
|
|
|
|
return info
|
|
}
|