github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/versioning/versioning.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package versioning 18 19 import ( 20 "encoding/json" 21 "io" 22 "reflect" 23 "sync" 24 25 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1/unstructured" 26 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 27 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 28 "k8s.io/klog/v2" 29 ) 30 31 // NewDefaultingCodecForScheme is a convenience method for callers that are using a scheme. 32 func NewDefaultingCodecForScheme( 33 // TODO: I should be a scheme interface? 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 // NewCodec takes objects in their internal versions and converts them to external versions before 44 // serializing them. It assumes the serializer provided to it only deals with external versions. 45 // This class is also a serializer, but is generally used with a specific version. 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 // originalSchemeName is optional, but when filled in it holds the name of the scheme from which this codec originates 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 // identifier computes Identifier of Encoder based on codec parameters. 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 // Decode attempts a decode of the object, then tries to convert it to the internal version. If into is provided and the decoding is 126 // successful, the returned runtime.Object will be the value passed as into. Note that this may bypass conversion if you pass an 127 // into that matches the serialized version. 128 func (c *codec) Decode(data []byte, defaultGVK *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { 129 // If the into object is unstructured and expresses an opinion about its group/version, 130 // create a new instance of the type so we always exercise the conversion path (skips short-circuiting on `into == obj`) 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 // save the strictDecodingError and let the caller decide what to do with it 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{c.decoder}); err != nil { 151 if strictErr, ok := runtime.AsStrictDecodingError(err); ok { 152 // save the strictDecodingError let and the caller decide what to do with it 153 strictDecodingErrs = append(strictDecodingErrs, strictErr.Errors()...) 154 } else { 155 return nil, gvk, err 156 157 } 158 } 159 } 160 161 // aggregate the strict decoding errors into one 162 var strictDecodingErr error 163 if len(strictDecodingErrs) > 0 { 164 strictDecodingErr = runtime.NewStrictDecodingError(strictDecodingErrs) 165 } 166 // if we specify a target, use generic conversion. 167 if into != nil { 168 // perform defaulting if requested 169 if c.defaulter != nil { 170 c.defaulter.Default(obj) 171 } 172 173 // Short-circuit conversion if the into object is same object 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 // perform defaulting if requested 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 // EncodeWithAllocator ensures the provided object is output in the appropriate group and version, invoking 198 // conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is. 199 // In addition, it allows for providing a memory allocator for efficient memory usage during object serialization. 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 // Encode ensures the provided object is output in the appropriate group and version, invoking 205 // conversion if necessary. Unversioned objects (according to the ObjectTyper) are output as is. 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 // An unstructured list can contain objects of multiple group version kinds. don't short-circuit just 233 // because the top-level type matches our desired destination type. actually send the object to the converter 234 // to give it a chance to convert the list items if needed. 235 if _, ok := obj.(*unstructured.UnstructuredList); !ok { 236 // avoid conversion roundtrip if GVK is the right one already or is empty (yes, this is a hack, but the old behaviour we rely on in kubectl) 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 // restore the old GVK after encoding 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 // Perform a conversion if necessary 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 // Conversion is responsible for setting the proper group, version, and kind onto the outgoing object 284 return encodeFn(out, w) 285 } 286 287 // Identifier implements runtime.Encoder interface. 288 func (c *codec) Identifier() runtime.Identifier { 289 return c.identifier 290 }