github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/protobuf/protobuf.go (about) 1 /* 2 Copyright 2015 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 protobuf 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "net/http" 24 "reflect" 25 26 "github.com/gogo/protobuf/proto" 27 28 metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1" 29 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime" 30 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema" 31 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/serializer/recognizer" 32 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/framer" 33 "k8s.io/klog/v2" 34 ) 35 36 var ( 37 // protoEncodingPrefix serves as a magic number for an encoded protobuf message on this serializer. All 38 // proto messages serialized by this schema will be preceded by the bytes 0x6b 0x38 0x73, with the fourth 39 // byte being reserved for the encoding style. The only encoding style defined is 0x00, which means that 40 // the rest of the byte stream is a message of type k8s.io.kubernetes.pkg.runtime.Unknown (proto2). 41 // 42 // See k8s.io/apimachinery/pkg/runtime/generated.proto for details of the runtime.Unknown message. 43 // 44 // This encoding scheme is experimental, and is subject to change at any time. 45 protoEncodingPrefix = []byte{0x6b, 0x38, 0x73, 0x00} 46 ) 47 48 type errNotMarshalable struct { 49 t reflect.Type 50 } 51 52 func (e errNotMarshalable) Error() string { 53 return fmt.Sprintf("object %v does not implement the protobuf marshalling interface and cannot be encoded to a protobuf message", e.t) 54 } 55 56 func (e errNotMarshalable) Status() metav1.Status { 57 return metav1.Status{ 58 Status: metav1.StatusFailure, 59 Code: http.StatusNotAcceptable, 60 Reason: metav1.StatusReason("NotAcceptable"), 61 Message: e.Error(), 62 } 63 } 64 65 // IsNotMarshalable checks the type of error, returns a boolean true if error is not nil and not marshalable false otherwise 66 func IsNotMarshalable(err error) bool { 67 _, ok := err.(errNotMarshalable) 68 return err != nil && ok 69 } 70 71 // NewSerializer creates a Protobuf serializer that handles encoding versioned objects into the proper wire form. If a typer 72 // is passed, the encoded object will have group, version, and kind fields set. If typer is nil, the objects will be written 73 // as-is (any type info passed with the object will be used). 74 func NewSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper) *Serializer { 75 return &Serializer{ 76 prefix: protoEncodingPrefix, 77 creater: creater, 78 typer: typer, 79 } 80 } 81 82 // Serializer handles encoding versioned objects into the proper wire form 83 type Serializer struct { 84 prefix []byte 85 creater runtime.ObjectCreater 86 typer runtime.ObjectTyper 87 } 88 89 var _ runtime.Serializer = &Serializer{} 90 var _ runtime.EncoderWithAllocator = &Serializer{} 91 var _ recognizer.RecognizingDecoder = &Serializer{} 92 93 const serializerIdentifier runtime.Identifier = "protobuf" 94 95 // Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default 96 // gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, 97 // the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will 98 // be straight decoded using normal protobuf unmarshalling (the MarshalTo interface). If into is provided and the original data is 99 // not fully qualified with kind/version/group, the type of the into will be used to alter the returned gvk. On success or most 100 // errors, the method will return the calculated schema kind. 101 func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { 102 prefixLen := len(s.prefix) 103 switch { 104 case len(originalData) == 0: 105 // TODO: treat like decoding {} from JSON with defaulting 106 return nil, nil, fmt.Errorf("empty data") 107 case len(originalData) < prefixLen || !bytes.Equal(s.prefix, originalData[:prefixLen]): 108 return nil, nil, fmt.Errorf("provided data does not appear to be a protobuf message, expected prefix %v", s.prefix) 109 case len(originalData) == prefixLen: 110 // TODO: treat like decoding {} from JSON with defaulting 111 return nil, nil, fmt.Errorf("empty body") 112 } 113 114 data := originalData[prefixLen:] 115 unk := runtime.Unknown{} 116 if err := unk.Unmarshal(data); err != nil { 117 return nil, nil, err 118 } 119 120 actual := unk.GroupVersionKind() 121 copyKindDefaults(&actual, gvk) 122 123 if intoUnknown, ok := into.(*runtime.Unknown); ok && intoUnknown != nil { 124 *intoUnknown = unk 125 if ok, _, _ := s.RecognizesData(unk.Raw); ok { 126 intoUnknown.ContentType = runtime.ContentTypeProtobuf 127 } 128 return intoUnknown, &actual, nil 129 } 130 131 if into != nil { 132 types, _, err := s.typer.ObjectKinds(into) 133 switch { 134 case runtime.IsNotRegisteredError(err): 135 pb, ok := into.(proto.Message) 136 if !ok { 137 return nil, &actual, errNotMarshalable{reflect.TypeOf(into)} 138 } 139 if err := proto.Unmarshal(unk.Raw, pb); err != nil { 140 return nil, &actual, err 141 } 142 return into, &actual, nil 143 case err != nil: 144 return nil, &actual, err 145 default: 146 copyKindDefaults(&actual, &types[0]) 147 // if the result of defaulting did not set a version or group, ensure that at least group is set 148 // (copyKindDefaults will not assign Group if version is already set). This guarantees that the group 149 // of into is set if there is no better information from the caller or object. 150 if len(actual.Version) == 0 && len(actual.Group) == 0 { 151 actual.Group = types[0].Group 152 } 153 } 154 } 155 156 if len(actual.Kind) == 0 { 157 return nil, &actual, runtime.NewMissingKindErr(fmt.Sprintf("%#v", unk.TypeMeta)) 158 } 159 if len(actual.Version) == 0 { 160 return nil, &actual, runtime.NewMissingVersionErr(fmt.Sprintf("%#v", unk.TypeMeta)) 161 } 162 163 return unmarshalToObject(s.typer, s.creater, &actual, into, unk.Raw) 164 } 165 166 // EncodeWithAllocator writes an object to the provided writer. 167 // In addition, it allows for providing a memory allocator for efficient memory usage during object serialization. 168 func (s *Serializer) EncodeWithAllocator(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 169 return s.encode(obj, w, memAlloc) 170 } 171 172 // Encode serializes the provided object to the given writer. 173 func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error { 174 return s.encode(obj, w, &runtime.SimpleAllocator{}) 175 } 176 177 func (s *Serializer) encode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 178 if co, ok := obj.(runtime.CacheableObject); ok { 179 return co.CacheEncode(s.Identifier(), func(obj runtime.Object, w io.Writer) error { return s.doEncode(obj, w, memAlloc) }, w) 180 } 181 return s.doEncode(obj, w, memAlloc) 182 } 183 184 func (s *Serializer) doEncode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 185 if memAlloc == nil { 186 klog.Error("a mandatory memory allocator wasn't provided, this might have a negative impact on performance, check invocations of EncodeWithAllocator method, falling back on runtime.SimpleAllocator") 187 memAlloc = &runtime.SimpleAllocator{} 188 } 189 prefixSize := uint64(len(s.prefix)) 190 191 var unk runtime.Unknown 192 switch t := obj.(type) { 193 case *runtime.Unknown: 194 estimatedSize := prefixSize + uint64(t.Size()) 195 data := memAlloc.Allocate(estimatedSize) 196 i, err := t.MarshalTo(data[prefixSize:]) 197 if err != nil { 198 return err 199 } 200 copy(data, s.prefix) 201 _, err = w.Write(data[:prefixSize+uint64(i)]) 202 return err 203 default: 204 kind := obj.GetObjectKind().GroupVersionKind() 205 unk = runtime.Unknown{ 206 TypeMeta: runtime.TypeMeta{ 207 Kind: kind.Kind, 208 APIVersion: kind.GroupVersion().String(), 209 }, 210 } 211 } 212 213 switch t := obj.(type) { 214 case bufferedMarshaller: 215 // this path performs a single allocation during write only when the Allocator wasn't provided 216 // it also requires the caller to implement the more efficient Size and MarshalToSizedBuffer methods 217 encodedSize := uint64(t.Size()) 218 estimatedSize := prefixSize + estimateUnknownSize(&unk, encodedSize) 219 data := memAlloc.Allocate(estimatedSize) 220 221 i, err := unk.NestedMarshalTo(data[prefixSize:], t, encodedSize) 222 if err != nil { 223 return err 224 } 225 226 copy(data, s.prefix) 227 228 _, err = w.Write(data[:prefixSize+uint64(i)]) 229 return err 230 231 case proto.Marshaler: 232 // this path performs extra allocations 233 data, err := t.Marshal() 234 if err != nil { 235 return err 236 } 237 unk.Raw = data 238 239 estimatedSize := prefixSize + uint64(unk.Size()) 240 data = memAlloc.Allocate(estimatedSize) 241 242 i, err := unk.MarshalTo(data[prefixSize:]) 243 if err != nil { 244 return err 245 } 246 247 copy(data, s.prefix) 248 249 _, err = w.Write(data[:prefixSize+uint64(i)]) 250 return err 251 252 default: 253 // TODO: marshal with a different content type and serializer (JSON for third party objects) 254 return errNotMarshalable{reflect.TypeOf(obj)} 255 } 256 } 257 258 // Identifier implements runtime.Encoder interface. 259 func (s *Serializer) Identifier() runtime.Identifier { 260 return serializerIdentifier 261 } 262 263 // RecognizesData implements the RecognizingDecoder interface. 264 func (s *Serializer) RecognizesData(data []byte) (bool, bool, error) { 265 return bytes.HasPrefix(data, s.prefix), false, nil 266 } 267 268 // copyKindDefaults defaults dst to the value in src if dst does not have a value set. 269 func copyKindDefaults(dst, src *schema.GroupVersionKind) { 270 if src == nil { 271 return 272 } 273 // apply kind and version defaulting from provided default 274 if len(dst.Kind) == 0 { 275 dst.Kind = src.Kind 276 } 277 if len(dst.Version) == 0 && len(src.Version) > 0 { 278 dst.Group = src.Group 279 dst.Version = src.Version 280 } 281 } 282 283 // bufferedMarshaller describes a more efficient marshalling interface that can avoid allocating multiple 284 // byte buffers by pre-calculating the size of the final buffer needed. 285 type bufferedMarshaller interface { 286 proto.Sizer 287 runtime.ProtobufMarshaller 288 } 289 290 // Like bufferedMarshaller, but is able to marshal backwards, which is more efficient since it doesn't call Size() as frequently. 291 type bufferedReverseMarshaller interface { 292 proto.Sizer 293 runtime.ProtobufReverseMarshaller 294 } 295 296 // estimateUnknownSize returns the expected bytes consumed by a given runtime.Unknown 297 // object with a nil RawJSON struct and the expected size of the provided buffer. The 298 // returned size will not be correct if RawJSOn is set on unk. 299 func estimateUnknownSize(unk *runtime.Unknown, byteSize uint64) uint64 { 300 size := uint64(unk.Size()) 301 // protobuf uses 1 byte for the tag, a varint for the length of the array (at most 8 bytes - uint64 - here), 302 // and the size of the array. 303 size += 1 + 8 + byteSize 304 return size 305 } 306 307 // NewRawSerializer creates a Protobuf serializer that handles encoding versioned objects into the proper wire form. If typer 308 // is not nil, the object has the group, version, and kind fields set. This serializer does not provide type information for the 309 // encoded object, and thus is not self describing (callers must know what type is being described in order to decode). 310 // 311 // This encoding scheme is experimental, and is subject to change at any time. 312 func NewRawSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper) *RawSerializer { 313 return &RawSerializer{ 314 creater: creater, 315 typer: typer, 316 } 317 } 318 319 // RawSerializer encodes and decodes objects without adding a runtime.Unknown wrapper (objects are encoded without identifying 320 // type). 321 type RawSerializer struct { 322 creater runtime.ObjectCreater 323 typer runtime.ObjectTyper 324 } 325 326 var _ runtime.Serializer = &RawSerializer{} 327 328 const rawSerializerIdentifier runtime.Identifier = "raw-protobuf" 329 330 // Decode attempts to convert the provided data into a protobuf message, extract the stored schema kind, apply the provided default 331 // gvk, and then load that data into an object matching the desired schema kind or the provided into. If into is *runtime.Unknown, 332 // the raw data will be extracted and no decoding will be performed. If into is not registered with the typer, then the object will 333 // be straight decoded using normal protobuf unmarshalling (the MarshalTo interface). If into is provided and the original data is 334 // not fully qualified with kind/version/group, the type of the into will be used to alter the returned gvk. On success or most 335 // errors, the method will return the calculated schema kind. 336 func (s *RawSerializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { 337 if into == nil { 338 return nil, nil, fmt.Errorf("this serializer requires an object to decode into: %#v", s) 339 } 340 341 if len(originalData) == 0 { 342 // TODO: treat like decoding {} from JSON with defaulting 343 return nil, nil, fmt.Errorf("empty data") 344 } 345 data := originalData 346 347 actual := &schema.GroupVersionKind{} 348 copyKindDefaults(actual, gvk) 349 350 if intoUnknown, ok := into.(*runtime.Unknown); ok && intoUnknown != nil { 351 intoUnknown.Raw = data 352 intoUnknown.ContentEncoding = "" 353 intoUnknown.ContentType = runtime.ContentTypeProtobuf 354 intoUnknown.SetGroupVersionKind(*actual) 355 return intoUnknown, actual, nil 356 } 357 358 types, _, err := s.typer.ObjectKinds(into) 359 switch { 360 case runtime.IsNotRegisteredError(err): 361 pb, ok := into.(proto.Message) 362 if !ok { 363 return nil, actual, errNotMarshalable{reflect.TypeOf(into)} 364 } 365 if err := proto.Unmarshal(data, pb); err != nil { 366 return nil, actual, err 367 } 368 return into, actual, nil 369 case err != nil: 370 return nil, actual, err 371 default: 372 copyKindDefaults(actual, &types[0]) 373 // if the result of defaulting did not set a version or group, ensure that at least group is set 374 // (copyKindDefaults will not assign Group if version is already set). This guarantees that the group 375 // of into is set if there is no better information from the caller or object. 376 if len(actual.Version) == 0 && len(actual.Group) == 0 { 377 actual.Group = types[0].Group 378 } 379 } 380 381 if len(actual.Kind) == 0 { 382 return nil, actual, runtime.NewMissingKindErr("<protobuf encoded body - must provide default type>") 383 } 384 if len(actual.Version) == 0 { 385 return nil, actual, runtime.NewMissingVersionErr("<protobuf encoded body - must provide default type>") 386 } 387 388 return unmarshalToObject(s.typer, s.creater, actual, into, data) 389 } 390 391 // unmarshalToObject is the common code between decode in the raw and normal serializer. 392 func unmarshalToObject(typer runtime.ObjectTyper, creater runtime.ObjectCreater, actual *schema.GroupVersionKind, into runtime.Object, data []byte) (runtime.Object, *schema.GroupVersionKind, error) { 393 // use the target if necessary 394 obj, err := runtime.UseOrCreateObject(typer, creater, *actual, into) 395 if err != nil { 396 return nil, actual, err 397 } 398 399 pb, ok := obj.(proto.Message) 400 if !ok { 401 return nil, actual, errNotMarshalable{reflect.TypeOf(obj)} 402 } 403 if err := proto.Unmarshal(data, pb); err != nil { 404 return nil, actual, err 405 } 406 if actual != nil { 407 obj.GetObjectKind().SetGroupVersionKind(*actual) 408 } 409 return obj, actual, nil 410 } 411 412 // Encode serializes the provided object to the given writer. Overrides is ignored. 413 func (s *RawSerializer) Encode(obj runtime.Object, w io.Writer) error { 414 return s.encode(obj, w, &runtime.SimpleAllocator{}) 415 } 416 417 // EncodeWithAllocator writes an object to the provided writer. 418 // In addition, it allows for providing a memory allocator for efficient memory usage during object serialization. 419 func (s *RawSerializer) EncodeWithAllocator(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 420 return s.encode(obj, w, memAlloc) 421 } 422 423 func (s *RawSerializer) encode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 424 if co, ok := obj.(runtime.CacheableObject); ok { 425 return co.CacheEncode(s.Identifier(), func(obj runtime.Object, w io.Writer) error { return s.doEncode(obj, w, memAlloc) }, w) 426 } 427 return s.doEncode(obj, w, memAlloc) 428 } 429 430 func (s *RawSerializer) doEncode(obj runtime.Object, w io.Writer, memAlloc runtime.MemoryAllocator) error { 431 if memAlloc == nil { 432 klog.Error("a mandatory memory allocator wasn't provided, this might have a negative impact on performance, check invocations of EncodeWithAllocator method, falling back on runtime.SimpleAllocator") 433 memAlloc = &runtime.SimpleAllocator{} 434 } 435 switch t := obj.(type) { 436 case bufferedReverseMarshaller: 437 // this path performs a single allocation during write only when the Allocator wasn't provided 438 // it also requires the caller to implement the more efficient Size and MarshalToSizedBuffer methods 439 encodedSize := uint64(t.Size()) 440 data := memAlloc.Allocate(encodedSize) 441 442 n, err := t.MarshalToSizedBuffer(data) 443 if err != nil { 444 return err 445 } 446 _, err = w.Write(data[:n]) 447 return err 448 449 case bufferedMarshaller: 450 // this path performs a single allocation during write only when the Allocator wasn't provided 451 // it also requires the caller to implement the more efficient Size and MarshalTo methods 452 encodedSize := uint64(t.Size()) 453 data := memAlloc.Allocate(encodedSize) 454 455 n, err := t.MarshalTo(data) 456 if err != nil { 457 return err 458 } 459 _, err = w.Write(data[:n]) 460 return err 461 462 case proto.Marshaler: 463 // this path performs extra allocations 464 data, err := t.Marshal() 465 if err != nil { 466 return err 467 } 468 _, err = w.Write(data) 469 return err 470 471 default: 472 return errNotMarshalable{reflect.TypeOf(obj)} 473 } 474 } 475 476 // Identifier implements runtime.Encoder interface. 477 func (s *RawSerializer) Identifier() runtime.Identifier { 478 return rawSerializerIdentifier 479 } 480 481 // LengthDelimitedFramer is exported variable of type lengthDelimitedFramer 482 var LengthDelimitedFramer = lengthDelimitedFramer{} 483 484 // Provides length delimited frame reader and writer methods 485 type lengthDelimitedFramer struct{} 486 487 // NewFrameWriter implements stream framing for this serializer 488 func (lengthDelimitedFramer) NewFrameWriter(w io.Writer) io.Writer { 489 return framer.NewLengthDelimitedFrameWriter(w) 490 } 491 492 // NewFrameReader implements stream framing for this serializer 493 func (lengthDelimitedFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { 494 return framer.NewLengthDelimitedFrameReader(r) 495 }