1
16
17 package proxy
18
19 import (
20 "fmt"
21 "net"
22
23 v1 "k8s.io/api/core/v1"
24 "k8s.io/klog/v2"
25 apiservice "k8s.io/kubernetes/pkg/api/v1/service"
26 proxyutil "k8s.io/kubernetes/pkg/proxy/util"
27 netutils "k8s.io/utils/net"
28 )
29
30
31 type ServicePort interface {
32
33 String() string
34
35 ClusterIP() net.IP
36
37 Port() int
38
39 SessionAffinityType() v1.ServiceAffinity
40
41 StickyMaxAgeSeconds() int
42
43 ExternalIPs() []net.IP
44
45 LoadBalancerVIPs() []net.IP
46
47 Protocol() v1.Protocol
48
49 LoadBalancerSourceRanges() []*net.IPNet
50
51 HealthCheckNodePort() int
52
53 NodePort() int
54
55 ExternalPolicyLocal() bool
56
57 InternalPolicyLocal() bool
58
59 HintsAnnotation() string
60
61
62 ExternallyAccessible() bool
63
64
65 UsesClusterEndpoints() bool
66
67
68 UsesLocalEndpoints() bool
69 }
70
71
72
73
74
75 type BaseServicePortInfo struct {
76 clusterIP net.IP
77 port int
78 protocol v1.Protocol
79 nodePort int
80 loadBalancerVIPs []net.IP
81 sessionAffinityType v1.ServiceAffinity
82 stickyMaxAgeSeconds int
83 externalIPs []net.IP
84 loadBalancerSourceRanges []*net.IPNet
85 healthCheckNodePort int
86 externalPolicyLocal bool
87 internalPolicyLocal bool
88 hintsAnnotation string
89 }
90
91 var _ ServicePort = &BaseServicePortInfo{}
92
93
94 func (bsvcPortInfo *BaseServicePortInfo) String() string {
95 return fmt.Sprintf("%s:%d/%s", bsvcPortInfo.clusterIP, bsvcPortInfo.port, bsvcPortInfo.protocol)
96 }
97
98
99 func (bsvcPortInfo *BaseServicePortInfo) ClusterIP() net.IP {
100 return bsvcPortInfo.clusterIP
101 }
102
103
104 func (bsvcPortInfo *BaseServicePortInfo) Port() int {
105 return bsvcPortInfo.port
106 }
107
108
109 func (bsvcPortInfo *BaseServicePortInfo) SessionAffinityType() v1.ServiceAffinity {
110 return bsvcPortInfo.sessionAffinityType
111 }
112
113
114 func (bsvcPortInfo *BaseServicePortInfo) StickyMaxAgeSeconds() int {
115 return bsvcPortInfo.stickyMaxAgeSeconds
116 }
117
118
119 func (bsvcPortInfo *BaseServicePortInfo) Protocol() v1.Protocol {
120 return bsvcPortInfo.protocol
121 }
122
123
124 func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerSourceRanges() []*net.IPNet {
125 return bsvcPortInfo.loadBalancerSourceRanges
126 }
127
128
129 func (bsvcPortInfo *BaseServicePortInfo) HealthCheckNodePort() int {
130 return bsvcPortInfo.healthCheckNodePort
131 }
132
133
134 func (bsvcPortInfo *BaseServicePortInfo) NodePort() int {
135 return bsvcPortInfo.nodePort
136 }
137
138
139 func (bsvcPortInfo *BaseServicePortInfo) ExternalIPs() []net.IP {
140 return bsvcPortInfo.externalIPs
141 }
142
143
144 func (bsvcPortInfo *BaseServicePortInfo) LoadBalancerVIPs() []net.IP {
145 return bsvcPortInfo.loadBalancerVIPs
146 }
147
148
149 func (bsvcPortInfo *BaseServicePortInfo) ExternalPolicyLocal() bool {
150 return bsvcPortInfo.externalPolicyLocal
151 }
152
153
154 func (bsvcPortInfo *BaseServicePortInfo) InternalPolicyLocal() bool {
155 return bsvcPortInfo.internalPolicyLocal
156 }
157
158
159 func (bsvcPortInfo *BaseServicePortInfo) HintsAnnotation() string {
160 return bsvcPortInfo.hintsAnnotation
161 }
162
163
164 func (bsvcPortInfo *BaseServicePortInfo) ExternallyAccessible() bool {
165 return bsvcPortInfo.nodePort != 0 || len(bsvcPortInfo.loadBalancerVIPs) != 0 || len(bsvcPortInfo.externalIPs) != 0
166 }
167
168
169 func (bsvcPortInfo *BaseServicePortInfo) UsesClusterEndpoints() bool {
170
171
172
173 return !bsvcPortInfo.internalPolicyLocal || bsvcPortInfo.ExternallyAccessible()
174 }
175
176
177 func (bsvcPortInfo *BaseServicePortInfo) UsesLocalEndpoints() bool {
178 return bsvcPortInfo.internalPolicyLocal || (bsvcPortInfo.externalPolicyLocal && bsvcPortInfo.ExternallyAccessible())
179 }
180
181 func newBaseServiceInfo(service *v1.Service, ipFamily v1.IPFamily, port *v1.ServicePort) *BaseServicePortInfo {
182 externalPolicyLocal := apiservice.ExternalPolicyLocal(service)
183 internalPolicyLocal := apiservice.InternalPolicyLocal(service)
184
185 var stickyMaxAgeSeconds int
186 if service.Spec.SessionAffinity == v1.ServiceAffinityClientIP {
187
188 stickyMaxAgeSeconds = int(*service.Spec.SessionAffinityConfig.ClientIP.TimeoutSeconds)
189 }
190
191 clusterIP := proxyutil.GetClusterIPByFamily(ipFamily, service)
192 info := &BaseServicePortInfo{
193 clusterIP: netutils.ParseIPSloppy(clusterIP),
194 port: int(port.Port),
195 protocol: port.Protocol,
196 nodePort: int(port.NodePort),
197 sessionAffinityType: service.Spec.SessionAffinity,
198 stickyMaxAgeSeconds: stickyMaxAgeSeconds,
199 externalPolicyLocal: externalPolicyLocal,
200 internalPolicyLocal: internalPolicyLocal,
201 }
202
203
204 var exists bool
205 info.hintsAnnotation, exists = service.Annotations[v1.DeprecatedAnnotationTopologyAwareHints]
206 if !exists {
207 info.hintsAnnotation = service.Annotations[v1.AnnotationTopologyMode]
208 }
209
210
211
212
213
214 ipFamilyMap := proxyutil.MapIPsByIPFamily(service.Spec.ExternalIPs)
215 info.externalIPs = ipFamilyMap[ipFamily]
216
217
218 if ips, ok := ipFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(ips) > 0 {
219 klog.V(4).InfoS("Service change tracker ignored the following external IPs for given service as they don't match IP Family",
220 "ipFamily", ipFamily, "externalIPs", ips, "service", klog.KObj(service))
221 }
222
223 cidrFamilyMap := proxyutil.MapCIDRsByIPFamily(service.Spec.LoadBalancerSourceRanges)
224 info.loadBalancerSourceRanges = cidrFamilyMap[ipFamily]
225
226 if cidrs, ok := cidrFamilyMap[proxyutil.OtherIPFamily(ipFamily)]; ok && len(cidrs) > 0 {
227 klog.V(4).InfoS("Service change tracker ignored the following load balancer source ranges for given Service as they don't match IP Family",
228 "ipFamily", ipFamily, "loadBalancerSourceRanges", cidrs, "service", klog.KObj(service))
229 }
230
231
232 var invalidIPs []net.IP
233 for _, ing := range service.Status.LoadBalancer.Ingress {
234 if ing.IP == "" {
235 continue
236 }
237
238
239
240
241 if !proxyutil.IsVIPMode(ing) {
242 klog.V(4).InfoS("Service change tracker ignored the following load balancer ingress IP for given Service as it using Proxy mode",
243 "ipFamily", ipFamily, "loadBalancerIngressIP", ing.IP, "service", klog.KObj(service))
244 continue
245 }
246
247
248
249 ip := netutils.ParseIPSloppy(ing.IP)
250 if ingFamily := proxyutil.GetIPFamilyFromIP(ip); ingFamily == ipFamily {
251 info.loadBalancerVIPs = append(info.loadBalancerVIPs, ip)
252 } else {
253 invalidIPs = append(invalidIPs, ip)
254 }
255 }
256 if len(invalidIPs) > 0 {
257 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",
258 "ipFamily", ipFamily, "loadBalancerIngressIPs", invalidIPs, "service", klog.KObj(service))
259 }
260
261 if apiservice.NeedsHealthCheck(service) {
262 p := service.Spec.HealthCheckNodePort
263 if p == 0 {
264 klog.ErrorS(nil, "Service has no healthcheck nodeport", "service", klog.KObj(service))
265 } else {
266 info.healthCheckNodePort = int(p)
267 }
268 }
269
270 return info
271 }
272
View as plain text