1
16
17 package service
18
19 import (
20 "context"
21 "reflect"
22
23 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/apimachinery/pkg/util/sets"
25 "k8s.io/apimachinery/pkg/util/validation/field"
26 "k8s.io/apiserver/pkg/storage/names"
27 utilfeature "k8s.io/apiserver/pkg/util/feature"
28 "k8s.io/kubernetes/pkg/api/legacyscheme"
29 serviceapi "k8s.io/kubernetes/pkg/api/service"
30 api "k8s.io/kubernetes/pkg/apis/core"
31 "k8s.io/kubernetes/pkg/apis/core/validation"
32 "k8s.io/kubernetes/pkg/features"
33
34 "sigs.k8s.io/structured-merge-diff/v4/fieldpath"
35 )
36
37
38 type svcStrategy struct {
39 runtime.ObjectTyper
40 names.NameGenerator
41 }
42
43
44
45 var Strategy = svcStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
46
47
48 func (svcStrategy) NamespaceScoped() bool {
49 return true
50 }
51
52
53
54 func (svcStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
55 fields := map[fieldpath.APIVersion]*fieldpath.Set{
56 "v1": fieldpath.NewSet(
57 fieldpath.MakePathOrDie("status"),
58 ),
59 }
60
61 return fields
62 }
63
64
65 func (svcStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
66 service := obj.(*api.Service)
67 service.Status = api.ServiceStatus{}
68
69 dropServiceDisabledFields(service, nil)
70 }
71
72
73 func (svcStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
74 newService := obj.(*api.Service)
75 oldService := old.(*api.Service)
76 newService.Status = oldService.Status
77
78 dropServiceDisabledFields(newService, oldService)
79 dropTypeDependentFields(newService, oldService)
80 }
81
82
83 func (svcStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
84 service := obj.(*api.Service)
85 allErrs := validation.ValidateServiceCreate(service)
86 return allErrs
87 }
88
89
90 func (svcStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
91 return serviceapi.GetWarningsForService(obj.(*api.Service), nil)
92 }
93
94
95 func (svcStrategy) Canonicalize(obj runtime.Object) {
96 }
97
98 func (svcStrategy) AllowCreateOnUpdate() bool {
99 return true
100 }
101
102 func (strategy svcStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
103 allErrs := validation.ValidateServiceUpdate(obj.(*api.Service), old.(*api.Service))
104 return allErrs
105 }
106
107
108 func (svcStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
109 return serviceapi.GetWarningsForService(obj.(*api.Service), old.(*api.Service))
110 }
111
112 func (svcStrategy) AllowUnconditionalUpdate() bool {
113 return true
114 }
115
116
117
118
119
120
121
122 func dropServiceDisabledFields(newSvc *api.Service, oldSvc *api.Service) {
123
124 isTrafficDistributionInUse := (oldSvc != nil && oldSvc.Spec.TrafficDistribution != nil)
125 if !utilfeature.DefaultFeatureGate.Enabled(features.ServiceTrafficDistribution) && !isTrafficDistributionInUse {
126 newSvc.Spec.TrafficDistribution = nil
127 }
128 }
129
130 type serviceStatusStrategy struct {
131 svcStrategy
132 }
133
134
135 var StatusStrategy = serviceStatusStrategy{Strategy}
136
137
138
139 func (serviceStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set {
140 fields := map[fieldpath.APIVersion]*fieldpath.Set{
141 "v1": fieldpath.NewSet(
142 fieldpath.MakePathOrDie("spec"),
143 ),
144 }
145
146 return fields
147 }
148
149
150 func (serviceStatusStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
151 newService := obj.(*api.Service)
152 oldService := old.(*api.Service)
153
154 dropServiceStatusDisabledFields(newService, oldService)
155
156 newService.Spec = oldService.Spec
157 }
158
159
160 func (serviceStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
161 return validation.ValidateServiceStatusUpdate(obj.(*api.Service), old.(*api.Service))
162 }
163
164
165 func (serviceStatusStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
166 return nil
167 }
168
169
170
171
172
173
174
175 func dropServiceStatusDisabledFields(newSvc *api.Service, oldSvc *api.Service) {
176 if !utilfeature.DefaultFeatureGate.Enabled(features.LoadBalancerIPMode) && !loadbalancerIPModeInUse(oldSvc) {
177 for i := range newSvc.Status.LoadBalancer.Ingress {
178 newSvc.Status.LoadBalancer.Ingress[i].IPMode = nil
179 }
180 }
181 }
182
183
184 func loadbalancerIPModeInUse(svc *api.Service) bool {
185 if svc == nil {
186 return false
187 }
188 for _, ing := range svc.Status.LoadBalancer.Ingress {
189 if ing.IPMode != nil {
190 return true
191 }
192 }
193 return false
194 }
195
196 func sameStringSlice(a []string, b []string) bool {
197 if len(a) != len(b) {
198 return false
199 }
200 for i := range a {
201 if a[i] != b[i] {
202 return false
203 }
204 }
205 return true
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221 func dropTypeDependentFields(newSvc *api.Service, oldSvc *api.Service) {
222
223
224 if oldSvc == nil {
225 return
226 }
227
228
229
230
231
232
233
234
235
236 if needsClusterIP(oldSvc) && !needsClusterIP(newSvc) {
237 if sameClusterIPs(oldSvc, newSvc) {
238
239 newSvc.Spec.ClusterIP = ""
240 newSvc.Spec.ClusterIPs = nil
241 }
242 if sameIPFamilies(oldSvc, newSvc) {
243 newSvc.Spec.IPFamilies = nil
244 }
245 if sameIPFamilyPolicy(oldSvc, newSvc) {
246 newSvc.Spec.IPFamilyPolicy = nil
247 }
248 }
249
250
251
252
253 if needsNodePort(oldSvc) && !needsNodePort(newSvc) && sameNodePorts(oldSvc, newSvc) {
254 for i := range newSvc.Spec.Ports {
255 newSvc.Spec.Ports[i].NodePort = 0
256 }
257 }
258
259
260
261
262 if needsHCNodePort(oldSvc) && !needsHCNodePort(newSvc) && sameHCNodePort(oldSvc, newSvc) {
263 newSvc.Spec.HealthCheckNodePort = 0
264 }
265
266
267
268 if oldSvc.Spec.Type == api.ServiceTypeLoadBalancer && newSvc.Spec.Type != api.ServiceTypeLoadBalancer {
269 if newSvc.Spec.AllocateLoadBalancerNodePorts != nil && oldSvc.Spec.AllocateLoadBalancerNodePorts != nil {
270 if *oldSvc.Spec.AllocateLoadBalancerNodePorts == *newSvc.Spec.AllocateLoadBalancerNodePorts {
271 newSvc.Spec.AllocateLoadBalancerNodePorts = nil
272 }
273 }
274 }
275
276
277
278 if canSetLoadBalancerClass(oldSvc) && !canSetLoadBalancerClass(newSvc) && sameLoadBalancerClass(oldSvc, newSvc) {
279 newSvc.Spec.LoadBalancerClass = nil
280 }
281
282
283
284 if serviceapi.ExternallyAccessible(oldSvc) && !serviceapi.ExternallyAccessible(newSvc) && sameExternalTrafficPolicy(oldSvc, newSvc) {
285 newSvc.Spec.ExternalTrafficPolicy = api.ServiceExternalTrafficPolicy("")
286 }
287
288
289
290
291
292
293
294
295 if newSvc.Spec.Type != api.ServiceTypeLoadBalancer {
296 newSvc.Status.LoadBalancer = api.LoadBalancerStatus{}
297 }
298 }
299
300 func needsClusterIP(svc *api.Service) bool {
301 if svc.Spec.Type == api.ServiceTypeExternalName {
302 return false
303 }
304 return true
305 }
306
307 func sameClusterIPs(oldSvc, newSvc *api.Service) bool {
308 sameSingular := oldSvc.Spec.ClusterIP == newSvc.Spec.ClusterIP
309 samePlural := sameStringSlice(oldSvc.Spec.ClusterIPs, newSvc.Spec.ClusterIPs)
310 return sameSingular && samePlural
311 }
312
313 func sameIPFamilies(oldSvc, newSvc *api.Service) bool {
314 return reflect.DeepEqual(oldSvc.Spec.IPFamilies, newSvc.Spec.IPFamilies)
315 }
316
317 func getIPFamilyPolicy(svc *api.Service) string {
318 if svc.Spec.IPFamilyPolicy == nil {
319 return ""
320 }
321 return string(*svc.Spec.IPFamilyPolicy)
322 }
323
324 func sameIPFamilyPolicy(oldSvc, newSvc *api.Service) bool {
325 return getIPFamilyPolicy(oldSvc) == getIPFamilyPolicy(newSvc)
326 }
327
328 func needsNodePort(svc *api.Service) bool {
329 if svc.Spec.Type == api.ServiceTypeNodePort || svc.Spec.Type == api.ServiceTypeLoadBalancer {
330 return true
331 }
332 return false
333 }
334
335 func sameNodePorts(oldSvc, newSvc *api.Service) bool {
336
337 allNodePorts := func(svc *api.Service) sets.Int {
338 out := sets.NewInt()
339 for i := range svc.Spec.Ports {
340 if svc.Spec.Ports[i].NodePort != 0 {
341 out.Insert(int(svc.Spec.Ports[i].NodePort))
342 }
343 }
344 return out
345 }
346
347 oldPorts := allNodePorts(oldSvc)
348 newPorts := allNodePorts(newSvc)
349
350
351
352 return oldPorts.IsSuperset(newPorts)
353 }
354
355 func needsHCNodePort(svc *api.Service) bool {
356 if svc.Spec.Type != api.ServiceTypeLoadBalancer {
357 return false
358 }
359 if svc.Spec.ExternalTrafficPolicy != api.ServiceExternalTrafficPolicyLocal {
360 return false
361 }
362 return true
363 }
364
365 func sameHCNodePort(oldSvc, newSvc *api.Service) bool {
366 return oldSvc.Spec.HealthCheckNodePort == newSvc.Spec.HealthCheckNodePort
367 }
368
369 func canSetLoadBalancerClass(svc *api.Service) bool {
370 return svc.Spec.Type == api.ServiceTypeLoadBalancer
371 }
372
373 func sameLoadBalancerClass(oldSvc, newSvc *api.Service) bool {
374 if (oldSvc.Spec.LoadBalancerClass == nil) != (newSvc.Spec.LoadBalancerClass == nil) {
375 return false
376 }
377 if oldSvc.Spec.LoadBalancerClass == nil {
378 return true
379 }
380 return *oldSvc.Spec.LoadBalancerClass == *newSvc.Spec.LoadBalancerClass
381 }
382
383 func sameExternalTrafficPolicy(oldSvc, newSvc *api.Service) bool {
384 return oldSvc.Spec.ExternalTrafficPolicy == newSvc.Spec.ExternalTrafficPolicy
385 }
386
View as plain text