1
16
17 package versioning
18
19 import (
20 "encoding/json"
21 "io"
22 "reflect"
23 "sync"
24
25 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26 "k8s.io/apimachinery/pkg/runtime"
27 "k8s.io/apimachinery/pkg/runtime/schema"
28 "k8s.io/klog/v2"
29 )
30
31
32 func NewDefaultingCodecForScheme(
33
34 scheme *runtime.Scheme,
35 encoder runtime.Encoder,
36 decoder runtime.Decoder,
37 encodeVersion runtime.GroupVersioner,
38 decodeVersion runtime.GroupVersioner,
39 ) runtime.Codec {
40 return NewCodec(encoder, decoder, runtime.UnsafeObjectConvertor(scheme), scheme, scheme, scheme, encodeVersion, decodeVersion, scheme.Name())
41 }
42
43
44
45
46 func NewCodec(
47 encoder runtime.Encoder,
48 decoder runtime.Decoder,
49 convertor runtime.ObjectConvertor,
50 creater runtime.ObjectCreater,
51 typer runtime.ObjectTyper,
52 defaulter runtime.ObjectDefaulter,
53 encodeVersion runtime.GroupVersioner,
54 decodeVersion runtime.GroupVersioner,
55 originalSchemeName string,
56 ) runtime.Codec {
57 internal := &codec{
58 encoder: encoder,
59 decoder: decoder,
60 convertor: convertor,
61 creater: creater,
62 typer: typer,
63 defaulter: defaulter,
64
65 encodeVersion: encodeVersion,
66 decodeVersion: decodeVersion,
67
68 identifier: identifier(encodeVersion, encoder),
69
70 originalSchemeName: originalSchemeName,
71 }
72 return internal
73 }
74
75 type codec struct {
76 encoder runtime.Encoder
77 decoder runtime.Decoder
78 convertor runtime.ObjectConvertor
79 creater runtime.ObjectCreater
80 typer runtime.ObjectTyper
81 defaulter runtime.ObjectDefaulter
82
83 encodeVersion runtime.GroupVersioner
84 decodeVersion runtime.GroupVersioner
85
86 identifier runtime.Identifier
87
88
89 originalSchemeName string
90 }
91
92 var _ runtime.EncoderWithAllocator = &codec{}
93
94 var identifiersMap sync.Map
95
96 type codecIdentifier struct {
97 EncodeGV string `json:"encodeGV,omitempty"`
98 Encoder string `json:"encoder,omitempty"`
99 Name string `json:"name,omitempty"`
100 }
101
102
103 func identifier(encodeGV runtime.GroupVersioner, encoder runtime.Encoder) runtime.Identifier {
104 result := codecIdentifier{
105 Name: "versioning",
106 }
107
108 if encodeGV != nil {
109 result.EncodeGV = encodeGV.Identifier()
110 }
111 if encoder != nil {
112 result.Encoder = string(encoder.Identifier())
113 }
114 if id, ok := identifiersMap.Load(result); ok {
115 return id.(runtime.Identifier)
116 }
117 identifier, err := json.Marshal(result)
118 if err != nil {
119 klog.Fatalf("Failed marshaling identifier for codec: %v", err)
120 }
121 identifiersMap.Store(result, runtime.Identifier(identifier))
122 return runtime.Identifier(identifier)
123 }
124
125
126
127
128 func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
129
130
131 decodeInto := into
132 if into != nil {
133 if _, ok := into.(runtime.Unstructured); ok && !into.GetObjectKind().GroupVersionKind().GroupVersion().Empty() {
134 decodeInto = reflect.New(reflect.TypeOf(into).Elem()).Interface().(runtime.Object)
135 }
136 }
137
138 var strictDecodingErrs []error
139 obj, gvk, err := c.decoder.Decode(data, defaultGVK, decodeInto)
140 if err != nil {
141 if strictErr, ok := runtime.AsStrictDecodingError(err); obj != nil && ok {
142
143 strictDecodingErrs = append(strictDecodingErrs, strictErr.Errors()...)
144 } else {
145 return nil, gvk, err
146 }
147 }
148
149 if d, ok := obj.(runtime.NestedObjectDecoder); ok {
150 if err := d.DecodeNestedObjects(runtime.WithoutVersionDecoder{Decoder: c.decoder}); err != nil {
151 if strictErr, ok := runtime.AsStrictDecodingError(err); ok {
152
153 strictDecodingErrs = append(strictDecodingErrs, strictErr.Errors()...)
154 } else {
155 return nil, gvk, err
156
157 }
158 }
159 }
160
161
162 var strictDecodingErr error
163 if len(strictDecodingErrs) > 0 {
164 strictDecodingErr = runtime.NewStrictDecodingError(strictDecodingErrs)
165 }
166
167 if into != nil {
168
169 if c.defaulter != nil {
170 c.defaulter.Default(obj)
171 }
172
173
174 if into == obj {
175 return into, gvk, strictDecodingErr
176 }
177
178 if err := c.convertor.Convert(obj, into, c.decodeVersion); err != nil {
179 return nil, gvk, err
180 }
181
182 return into, gvk, strictDecodingErr
183 }
184
185
186 if c.defaulter != nil {
187 c.defaulter.Default(obj)
188 }
189
190 out, err := c.convertor.ConvertToVersion(obj, c.decodeVersion)
191 if err != nil {
192 return nil, gvk, err
193 }
194 return out, gvk, strictDecodingErr
195 }
196
197
198
199
200 func (c *codec) EncodeWithAllocator(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
201 return c.encode(obj, w, memAlloc)
202 }
203
204
205
206 func (c *codec) Encode(obj runtime.Object, w io.Writer) error {
207 return c.encode(obj, w, nil)
208 }
209
210 func (c *codec) encode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
211 if co, ok := obj.(runtime.CacheableObject); ok {
212 return co.CacheEncode(c.Identifier(), func(obj runtime.Object, w io.Writer) error { return c.doEncode(obj, w, memAlloc) }, w)
213 }
214 return c.doEncode(obj, w, memAlloc)
215 }
216
217 func (c *codec) doEncode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error {
218 encodeFn := c.encoder.Encode
219 if memAlloc != nil {
220 if encoder, supportsAllocator := c.encoder.(runtime.EncoderWithAllocator); supportsAllocator {
221 encodeFn = func(obj runtime.Object, w io.Writer) error {
222 return encoder.EncodeWithAllocator(obj, w, memAlloc)
223 }
224 } else {
225 klog.V(6).Infof("a memory allocator was provided but the encoder %s doesn't implement the runtime.EncoderWithAllocator, using regular encoder.Encode method", c.encoder.Identifier())
226 }
227 }
228 switch obj := obj.(type) {
229 case *runtime.Unknown:
230 return encodeFn(obj, w)
231 case runtime.Unstructured:
232
233
234
235 if _, ok := obj.(*unstructured.UnstructuredList); !ok {
236
237 objGVK := obj.GetObjectKind().GroupVersionKind()
238 if len(objGVK.Version) == 0 {
239 return encodeFn(obj, w)
240 }
241 targetGVK, ok := c.encodeVersion.KindForGroupVersionKinds([]schema.GroupVersionKind{objGVK})
242 if !ok {
243 return runtime.NewNotRegisteredGVKErrForTarget(c.originalSchemeName, objGVK, c.encodeVersion)
244 }
245 if targetGVK == objGVK {
246 return encodeFn(obj, w)
247 }
248 }
249 }
250
251 gvks, isUnversioned, err := c.typer.ObjectKinds(obj)
252 if err != nil {
253 return err
254 }
255
256 objectKind := obj.GetObjectKind()
257 old := objectKind.GroupVersionKind()
258
259 defer objectKind.SetGroupVersionKind(old)
260
261 if c.encodeVersion == nil || isUnversioned {
262 if e, ok := obj.(runtime.NestedObjectEncoder); ok {
263 if err := e.EncodeNestedObjects(runtime.WithVersionEncoder{Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
264 return err
265 }
266 }
267 objectKind.SetGroupVersionKind(gvks[0])
268 return encodeFn(obj, w)
269 }
270
271
272 out, err := c.convertor.ConvertToVersion(obj, c.encodeVersion)
273 if err != nil {
274 return err
275 }
276
277 if e, ok := out.(runtime.NestedObjectEncoder); ok {
278 if err := e.EncodeNestedObjects(runtime.WithVersionEncoder{Version: c.encodeVersion, Encoder: c.encoder, ObjectTyper: c.typer}); err != nil {
279 return err
280 }
281 }
282
283
284 return encodeFn(out, w)
285 }
286
287
288 func (c *codec) Identifier() runtime.Identifier {
289 return c.identifier
290 }
291
View as plain text