...
1
16
17 package endpointslice
18
19 import (
20 "context"
21 "fmt"
22
23 corev1 "k8s.io/api/core/v1"
24 discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
25 apiequality "k8s.io/apimachinery/pkg/api/equality"
26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
27 "k8s.io/apimachinery/pkg/runtime"
28 "k8s.io/apimachinery/pkg/runtime/schema"
29 "k8s.io/apimachinery/pkg/util/sets"
30 "k8s.io/apimachinery/pkg/util/validation/field"
31 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
32 "k8s.io/apiserver/pkg/storage/names"
33 utilfeature "k8s.io/apiserver/pkg/util/feature"
34 "k8s.io/kubernetes/pkg/api/legacyscheme"
35 apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
36 "k8s.io/kubernetes/pkg/apis/discovery"
37 "k8s.io/kubernetes/pkg/apis/discovery/validation"
38 "k8s.io/kubernetes/pkg/features"
39 )
40
41
42 type endpointSliceStrategy struct {
43 runtime.ObjectTyper
44 names.NameGenerator
45 }
46
47
48 var Strategy = endpointSliceStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
49
50
51 func (endpointSliceStrategy) NamespaceScoped() bool {
52 return true
53 }
54
55
56 func (endpointSliceStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {
57 endpointSlice := obj.(*discovery.EndpointSlice)
58 endpointSlice.Generation = 1
59
60 dropDisabledFieldsOnCreate(endpointSlice)
61 dropTopologyOnV1(ctx, nil, endpointSlice)
62 }
63
64
65 func (endpointSliceStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {
66 newEPS := obj.(*discovery.EndpointSlice)
67 oldEPS := old.(*discovery.EndpointSlice)
68
69
70
71 ogNewMeta := newEPS.ObjectMeta
72 ogOldMeta := oldEPS.ObjectMeta
73 newEPS.ObjectMeta = metav1.ObjectMeta{}
74 oldEPS.ObjectMeta = metav1.ObjectMeta{}
75
76 if !apiequality.Semantic.DeepEqual(newEPS, oldEPS) || !apiequality.Semantic.DeepEqual(ogNewMeta.Labels, ogOldMeta.Labels) {
77 ogNewMeta.Generation = ogOldMeta.Generation + 1
78 }
79
80 newEPS.ObjectMeta = ogNewMeta
81 oldEPS.ObjectMeta = ogOldMeta
82
83 dropDisabledFieldsOnUpdate(oldEPS, newEPS)
84 dropTopologyOnV1(ctx, oldEPS, newEPS)
85 }
86
87
88 func (endpointSliceStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
89 endpointSlice := obj.(*discovery.EndpointSlice)
90 err := validation.ValidateEndpointSliceCreate(endpointSlice)
91 return err
92 }
93
94
95 func (endpointSliceStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
96 eps := obj.(*discovery.EndpointSlice)
97 if eps == nil {
98 return nil
99 }
100 var warnings []string
101 warnings = append(warnings, warnOnDeprecatedAddressType(eps.AddressType)...)
102 return warnings
103 }
104
105
106 func (endpointSliceStrategy) Canonicalize(obj runtime.Object) {
107 }
108
109
110 func (endpointSliceStrategy) AllowCreateOnUpdate() bool {
111 return false
112 }
113
114
115 func (endpointSliceStrategy) ValidateUpdate(ctx context.Context, new, old runtime.Object) field.ErrorList {
116 newEPS := new.(*discovery.EndpointSlice)
117 oldEPS := old.(*discovery.EndpointSlice)
118 return validation.ValidateEndpointSliceUpdate(newEPS, oldEPS)
119 }
120
121
122 func (endpointSliceStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
123 return nil
124 }
125
126
127 func (endpointSliceStrategy) AllowUnconditionalUpdate() bool {
128 return true
129 }
130
131
132 func dropDisabledFieldsOnCreate(endpointSlice *discovery.EndpointSlice) {
133 dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
134
135 if dropHints {
136 for i := range endpointSlice.Endpoints {
137 if dropHints {
138 endpointSlice.Endpoints[i].Hints = nil
139 }
140 }
141 }
142 }
143
144
145
146 func dropDisabledFieldsOnUpdate(oldEPS, newEPS *discovery.EndpointSlice) {
147 dropHints := !utilfeature.DefaultFeatureGate.Enabled(features.TopologyAwareHints)
148 if dropHints {
149 for _, ep := range oldEPS.Endpoints {
150 if ep.Hints != nil {
151 dropHints = false
152 break
153 }
154 }
155 }
156
157 if dropHints {
158 for i := range newEPS.Endpoints {
159 if dropHints {
160 newEPS.Endpoints[i].Hints = nil
161 }
162 }
163 }
164 }
165
166
167
168 func dropTopologyOnV1(ctx context.Context, oldEPS, newEPS *discovery.EndpointSlice) {
169 if info, ok := genericapirequest.RequestInfoFrom(ctx); ok {
170 requestGV := schema.GroupVersion{Group: info.APIGroup, Version: info.APIVersion}
171 if requestGV == discoveryv1beta1.SchemeGroupVersion {
172 return
173 }
174
175
176 if oldEPS != nil && apiequality.Semantic.DeepEqual(oldEPS.Endpoints, newEPS.Endpoints) {
177 return
178 }
179
180
181
182
183 prevNodeNames := getDeprecatedTopologyNodeNames(oldEPS)
184
185 for i := range newEPS.Endpoints {
186 ep := &newEPS.Endpoints[i]
187
188 newTopologyNodeName, ok := ep.DeprecatedTopology[corev1.LabelHostname]
189 if ep.NodeName == nil && ok && prevNodeNames.Has(newTopologyNodeName) && len(apivalidation.ValidateNodeName(newTopologyNodeName, false)) == 0 {
190
191
192 ep.NodeName = &newTopologyNodeName
193 }
194
195 ep.DeprecatedTopology = nil
196 }
197 }
198 }
199
200
201
202 func getDeprecatedTopologyNodeNames(eps *discovery.EndpointSlice) sets.String {
203 if eps == nil {
204 return nil
205 }
206 var names sets.String
207 for _, ep := range eps.Endpoints {
208 if nodeName, ok := ep.DeprecatedTopology[corev1.LabelHostname]; ok && len(nodeName) > 0 {
209 if names == nil {
210 names = sets.NewString()
211 }
212 names.Insert(nodeName)
213 }
214 }
215 return names
216 }
217
218
219 func warnOnDeprecatedAddressType(addressType discovery.AddressType) []string {
220 if addressType == discovery.AddressTypeFQDN {
221 return []string{fmt.Sprintf("%s: FQDN endpoints are deprecated", field.NewPath("spec").Child("addressType"))}
222 }
223 return nil
224 }
225
View as plain text