build source
This commit is contained in:
commit
ee1fec43ed
4171 changed files with 1351288 additions and 0 deletions
383
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/cbor.go
generated
vendored
Normal file
383
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/cbor.go
generated
vendored
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
|
||||
util "k8s.io/apimachinery/pkg/util/runtime"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
type metaFactory interface {
|
||||
// Interpret should return the version and kind of the wire-format of the object.
|
||||
Interpret(data []byte) (*schema.GroupVersionKind, error)
|
||||
}
|
||||
|
||||
type defaultMetaFactory struct{}
|
||||
|
||||
func (mf *defaultMetaFactory) Interpret(data []byte) (*schema.GroupVersionKind, error) {
|
||||
var tm metav1.TypeMeta
|
||||
// The input is expected to include additional map keys besides apiVersion and kind, so use
|
||||
// lax mode for decoding into TypeMeta.
|
||||
if err := modes.DecodeLax.Unmarshal(data, &tm); err != nil {
|
||||
return nil, fmt.Errorf("unable to determine group/version/kind: %w", err)
|
||||
}
|
||||
actual := tm.GetObjectKind().GroupVersionKind()
|
||||
return &actual, nil
|
||||
}
|
||||
|
||||
type Serializer interface {
|
||||
runtime.Serializer
|
||||
runtime.NondeterministicEncoder
|
||||
recognizer.RecognizingDecoder
|
||||
|
||||
// NewSerializer returns a value of this interface type rather than exporting the serializer
|
||||
// type and returning one of those because the zero value of serializer isn't ready to
|
||||
// use. Users aren't intended to implement cbor.Serializer themselves, and this unexported
|
||||
// interface method is here to prevent that (https://go.dev/blog/module-compatibility).
|
||||
private()
|
||||
}
|
||||
|
||||
var _ Serializer = &serializer{}
|
||||
|
||||
type options struct {
|
||||
strict bool
|
||||
transcode bool
|
||||
}
|
||||
|
||||
type Option func(*options)
|
||||
|
||||
// Strict configures a serializer to return a strict decoding error when it encounters map keys that
|
||||
// do not correspond to a field in the target object of a decode operation. This option is disabled
|
||||
// by default.
|
||||
func Strict(s bool) Option {
|
||||
return func(opts *options) {
|
||||
opts.strict = s
|
||||
}
|
||||
}
|
||||
|
||||
// Transcode configures a serializer to transcode the "raw" bytes of a decoded runtime.RawExtension
|
||||
// or metav1.FieldsV1 object to JSON. This is enabled by default to support existing programs that
|
||||
// depend on the assumption that objects of either type contain valid JSON.
|
||||
func Transcode(s bool) Option {
|
||||
return func(opts *options) {
|
||||
opts.transcode = s
|
||||
}
|
||||
}
|
||||
|
||||
type serializer struct {
|
||||
metaFactory metaFactory
|
||||
creater runtime.ObjectCreater
|
||||
typer runtime.ObjectTyper
|
||||
options options
|
||||
}
|
||||
|
||||
func (serializer) private() {}
|
||||
|
||||
// NewSerializer creates and returns a serializer configured with the provided options. The default
|
||||
// options are equivalent to explicitly passing Strict(false) and Transcode(true).
|
||||
func NewSerializer(creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) Serializer {
|
||||
return newSerializer(&defaultMetaFactory{}, creater, typer, options...)
|
||||
}
|
||||
|
||||
func newSerializer(metaFactory metaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options ...Option) *serializer {
|
||||
s := &serializer{
|
||||
metaFactory: metaFactory,
|
||||
creater: creater,
|
||||
typer: typer,
|
||||
}
|
||||
s.options.transcode = true
|
||||
for _, o := range options {
|
||||
o(&s.options)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
func (s *serializer) Identifier() runtime.Identifier {
|
||||
return "cbor"
|
||||
}
|
||||
|
||||
// Encode writes a CBOR representation of the given object.
|
||||
//
|
||||
// Because the CBOR data item written by a call to Encode is always enclosed in the "self-described
|
||||
// CBOR" tag, its encoded form always has the prefix 0xd9d9f7. This prefix is suitable for use as a
|
||||
// "magic number" for distinguishing encoded CBOR from other protocols.
|
||||
//
|
||||
// The default serialization behavior for any given object replicates the behavior of the JSON
|
||||
// serializer as far as it is necessary to allow the CBOR serializer to be used as a drop-in
|
||||
// replacement for the JSON serializer, with limited exceptions. For example, the distinction
|
||||
// between integers and floating-point numbers is preserved in CBOR due to its distinct
|
||||
// representations for each type.
|
||||
//
|
||||
// Objects implementing runtime.Unstructured will have their unstructured content encoded rather
|
||||
// than following the default behavior for their dynamic type.
|
||||
func (s *serializer) Encode(obj runtime.Object, w io.Writer) error {
|
||||
return s.encode(modes.Encode, obj, w)
|
||||
}
|
||||
|
||||
func (s *serializer) EncodeNondeterministic(obj runtime.Object, w io.Writer) error {
|
||||
return s.encode(modes.EncodeNondeterministic, obj, w)
|
||||
}
|
||||
|
||||
func (s *serializer) encode(mode modes.EncMode, obj runtime.Object, w io.Writer) error {
|
||||
var v interface{} = obj
|
||||
if u, ok := obj.(runtime.Unstructured); ok {
|
||||
v = u.UnstructuredContent()
|
||||
}
|
||||
|
||||
if _, err := w.Write(selfDescribedCBOR); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return mode.MarshalTo(v, w)
|
||||
}
|
||||
|
||||
// gvkWithDefaults returns group kind and version defaulting from provided default
|
||||
func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
|
||||
if len(actual.Kind) == 0 {
|
||||
actual.Kind = defaultGVK.Kind
|
||||
}
|
||||
if len(actual.Version) == 0 && len(actual.Group) == 0 {
|
||||
actual.Group = defaultGVK.Group
|
||||
actual.Version = defaultGVK.Version
|
||||
}
|
||||
if len(actual.Version) == 0 && actual.Group == defaultGVK.Group {
|
||||
actual.Version = defaultGVK.Version
|
||||
}
|
||||
return actual
|
||||
}
|
||||
|
||||
// diagnose returns the diagnostic encoding of a well-formed CBOR data item.
|
||||
func diagnose(data []byte) string {
|
||||
diag, err := modes.Diagnostic.Diagnose(data)
|
||||
if err != nil {
|
||||
// Since the input must already be well-formed CBOR, converting it to diagnostic
|
||||
// notation should not fail.
|
||||
util.HandleError(err)
|
||||
|
||||
return hex.EncodeToString(data)
|
||||
}
|
||||
return diag
|
||||
}
|
||||
|
||||
// unmarshal unmarshals CBOR data from the provided byte slice into a Go object. If the decoder is
|
||||
// configured to report strict errors, the first error return value may be a non-nil strict decoding
|
||||
// error. If the last error return value is non-nil, then the unmarshal failed entirely and the
|
||||
// state of the destination object should not be relied on.
|
||||
func (s *serializer) unmarshal(data []byte, into interface{}) (strict, lax error) {
|
||||
if u, ok := into.(runtime.Unstructured); ok {
|
||||
var content map[string]interface{}
|
||||
defer func() {
|
||||
switch u := u.(type) {
|
||||
case *unstructured.UnstructuredList:
|
||||
// UnstructuredList's implementation of SetUnstructuredContent
|
||||
// produces different objects than those produced by a decode using
|
||||
// UnstructuredJSONScheme:
|
||||
//
|
||||
// 1. SetUnstructuredContent retains the "items" key in the list's
|
||||
// Object field. It is omitted from Object when decoding with
|
||||
// UnstructuredJSONScheme.
|
||||
// 2. SetUnstructuredContent does not populate "apiVersion" and
|
||||
// "kind" on each entry of its Items
|
||||
// field. UnstructuredJSONScheme does, inferring the singular
|
||||
// Kind from the list Kind.
|
||||
// 3. SetUnstructuredContent ignores entries of "items" that are
|
||||
// not JSON objects or are objects without
|
||||
// "kind". UnstructuredJSONScheme returns an error in either
|
||||
// case.
|
||||
//
|
||||
// UnstructuredJSONScheme's behavior is replicated here.
|
||||
var items []interface{}
|
||||
if uncast, present := content["items"]; present {
|
||||
var cast bool
|
||||
items, cast = uncast.([]interface{})
|
||||
if !cast {
|
||||
strict, lax = nil, fmt.Errorf("items field of UnstructuredList must be encoded as an array or null if present")
|
||||
return
|
||||
}
|
||||
}
|
||||
apiVersion, _ := content["apiVersion"].(string)
|
||||
kind, _ := content["kind"].(string)
|
||||
kind = strings.TrimSuffix(kind, "List")
|
||||
var unstructureds []unstructured.Unstructured
|
||||
if len(items) > 0 {
|
||||
unstructureds = make([]unstructured.Unstructured, len(items))
|
||||
}
|
||||
for i := range items {
|
||||
object, cast := items[i].(map[string]interface{})
|
||||
if !cast {
|
||||
strict, lax = nil, fmt.Errorf("elements of the items field of UnstructuredList must be encoded as a map")
|
||||
return
|
||||
}
|
||||
|
||||
// As in UnstructuredJSONScheme, only set the heuristic
|
||||
// singular GVK when both "apiVersion" and "kind" are either
|
||||
// missing, non-string, or empty.
|
||||
object["apiVersion"], _ = object["apiVersion"].(string)
|
||||
object["kind"], _ = object["kind"].(string)
|
||||
if object["apiVersion"] == "" && object["kind"] == "" {
|
||||
object["apiVersion"] = apiVersion
|
||||
object["kind"] = kind
|
||||
}
|
||||
|
||||
if object["kind"] == "" {
|
||||
strict, lax = nil, runtime.NewMissingKindErr(diagnose(data))
|
||||
return
|
||||
}
|
||||
if object["apiVersion"] == "" {
|
||||
strict, lax = nil, runtime.NewMissingVersionErr(diagnose(data))
|
||||
return
|
||||
}
|
||||
|
||||
unstructureds[i].Object = object
|
||||
}
|
||||
delete(content, "items")
|
||||
u.Object = content
|
||||
u.Items = unstructureds
|
||||
default:
|
||||
u.SetUnstructuredContent(content)
|
||||
}
|
||||
}()
|
||||
into = &content
|
||||
}
|
||||
|
||||
if !s.options.strict {
|
||||
return nil, modes.DecodeLax.Unmarshal(data, into)
|
||||
}
|
||||
|
||||
err := modes.Decode.Unmarshal(data, into)
|
||||
// TODO: UnknownFieldError is ambiguous. It only provides the index of the first problematic
|
||||
// map entry encountered and does not indicate which map the index refers to.
|
||||
var unknownField *cbor.UnknownFieldError
|
||||
if errors.As(err, &unknownField) {
|
||||
// Unlike JSON, there are no strict errors in CBOR for duplicate map keys. CBOR maps
|
||||
// with duplicate keys are considered invalid according to the spec and are rejected
|
||||
// entirely.
|
||||
return runtime.NewStrictDecodingError([]error{unknownField}), modes.DecodeLax.Unmarshal(data, into)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (s *serializer) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
|
||||
// A preliminary pass over the input to obtain the actual GVK is redundant on a successful
|
||||
// decode into Unstructured.
|
||||
if _, ok := into.(runtime.Unstructured); ok {
|
||||
if _, unmarshalErr := s.unmarshal(data, into); unmarshalErr != nil {
|
||||
actual, interpretErr := s.metaFactory.Interpret(data)
|
||||
if interpretErr != nil {
|
||||
return nil, nil, interpretErr
|
||||
}
|
||||
|
||||
if gvk != nil {
|
||||
*actual = gvkWithDefaults(*actual, *gvk)
|
||||
}
|
||||
|
||||
return nil, actual, unmarshalErr
|
||||
}
|
||||
|
||||
actual := into.GetObjectKind().GroupVersionKind()
|
||||
if len(actual.Kind) == 0 {
|
||||
return nil, &actual, runtime.NewMissingKindErr(diagnose(data))
|
||||
}
|
||||
if len(actual.Version) == 0 {
|
||||
return nil, &actual, runtime.NewMissingVersionErr(diagnose(data))
|
||||
}
|
||||
|
||||
return into, &actual, nil
|
||||
}
|
||||
|
||||
actual, err := s.metaFactory.Interpret(data)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if gvk != nil {
|
||||
*actual = gvkWithDefaults(*actual, *gvk)
|
||||
}
|
||||
|
||||
if into != nil {
|
||||
types, _, err := s.typer.ObjectKinds(into)
|
||||
if err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
*actual = gvkWithDefaults(*actual, types[0])
|
||||
}
|
||||
|
||||
if len(actual.Kind) == 0 {
|
||||
return nil, actual, runtime.NewMissingKindErr(diagnose(data))
|
||||
}
|
||||
if len(actual.Version) == 0 {
|
||||
return nil, actual, runtime.NewMissingVersionErr(diagnose(data))
|
||||
}
|
||||
|
||||
obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
|
||||
if err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
|
||||
strict, err := s.unmarshal(data, obj)
|
||||
if err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
|
||||
if s.options.transcode {
|
||||
if err := transcodeRawTypes(obj); err != nil {
|
||||
return nil, actual, err
|
||||
}
|
||||
}
|
||||
|
||||
return obj, actual, strict
|
||||
}
|
||||
|
||||
// selfDescribedCBOR is the CBOR encoding of the head of tag number 55799. This tag, specified in
|
||||
// RFC 8949 Section 3.4.6 "Self-Described CBOR", encloses all output from the encoder, has no
|
||||
// special semantics, and is used as a magic number to recognize CBOR-encoded data items.
|
||||
//
|
||||
// See https://www.rfc-editor.org/rfc/rfc8949.html#name-self-described-cbor.
|
||||
var selfDescribedCBOR = []byte{0xd9, 0xd9, 0xf7}
|
||||
|
||||
func (s *serializer) RecognizesData(data []byte) (ok, unknown bool, err error) {
|
||||
return bytes.HasPrefix(data, selfDescribedCBOR), false, nil
|
||||
}
|
||||
|
||||
// NewSerializerInfo returns a default SerializerInfo for CBOR using the given creater and typer.
|
||||
func NewSerializerInfo(creater runtime.ObjectCreater, typer runtime.ObjectTyper) runtime.SerializerInfo {
|
||||
return runtime.SerializerInfo{
|
||||
MediaType: "application/cbor",
|
||||
MediaTypeType: "application",
|
||||
MediaTypeSubType: "cbor",
|
||||
Serializer: NewSerializer(creater, typer),
|
||||
StrictSerializer: NewSerializer(creater, typer, Strict(true)),
|
||||
StreamSerializer: &runtime.StreamSerializerInfo{
|
||||
Framer: NewFramer(),
|
||||
Serializer: NewSerializer(creater, typer, Transcode(false)),
|
||||
},
|
||||
}
|
||||
}
|
||||
43
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go
generated
vendored
Normal file
43
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/direct/direct.go
generated
vendored
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package direct provides functions for marshaling and unmarshaling between arbitrary Go values and
|
||||
// CBOR data, with behavior that is compatible with that of the CBOR serializer. In particular,
|
||||
// types that implement cbor.Marshaler and cbor.Unmarshaler should use these functions.
|
||||
package direct
|
||||
|
||||
import (
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes"
|
||||
)
|
||||
|
||||
// Marshal serializes a value to CBOR. If there is more than one way to encode the value, it will
|
||||
// make the same choice as the CBOR implementation of runtime.Serializer.
|
||||
func Marshal(src any) ([]byte, error) {
|
||||
return modes.Encode.Marshal(src)
|
||||
}
|
||||
|
||||
// Unmarshal deserializes from CBOR into an addressable value. If there is more than one way to
|
||||
// unmarshal a value, it will make the same choice as the CBOR implementation of runtime.Serializer.
|
||||
func Unmarshal(src []byte, dst any) error {
|
||||
return modes.Decode.Unmarshal(src, dst)
|
||||
}
|
||||
|
||||
// Diagnose accepts well-formed CBOR bytes and returns a string representing the same data item in
|
||||
// human-readable diagnostic notation (RFC 8949 Section 8). The diagnostic notation is not meant to
|
||||
// be parsed.
|
||||
func Diagnose(src []byte) (string, error) {
|
||||
return modes.Diagnostic.Diagnose(src)
|
||||
}
|
||||
90
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/framer.go
generated
vendored
Normal file
90
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/framer.go
generated
vendored
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
// NewFramer returns a runtime.Framer based on RFC 8742 CBOR Sequences. Each frame contains exactly
|
||||
// one encoded CBOR data item.
|
||||
func NewFramer() runtime.Framer {
|
||||
return framer{}
|
||||
}
|
||||
|
||||
var _ runtime.Framer = framer{}
|
||||
|
||||
type framer struct{}
|
||||
|
||||
func (framer) NewFrameReader(rc io.ReadCloser) io.ReadCloser {
|
||||
return &frameReader{
|
||||
decoder: cbor.NewDecoder(rc),
|
||||
closer: rc,
|
||||
}
|
||||
}
|
||||
|
||||
func (framer) NewFrameWriter(w io.Writer) io.Writer {
|
||||
// Each data item in a CBOR sequence is self-delimiting (like JSON objects).
|
||||
return w
|
||||
}
|
||||
|
||||
type frameReader struct {
|
||||
decoder *cbor.Decoder
|
||||
closer io.Closer
|
||||
|
||||
overflow []byte
|
||||
}
|
||||
|
||||
func (fr *frameReader) Read(dst []byte) (int, error) {
|
||||
if len(fr.overflow) > 0 {
|
||||
// We read a frame that was too large for the destination slice in a previous call
|
||||
// to Read and have bytes left over.
|
||||
n := copy(dst, fr.overflow)
|
||||
if n < len(fr.overflow) {
|
||||
fr.overflow = fr.overflow[n:]
|
||||
return n, io.ErrShortBuffer
|
||||
}
|
||||
fr.overflow = nil
|
||||
return n, nil
|
||||
}
|
||||
|
||||
// The Reader contract allows implementations to use all of dst[0:len(dst)] as scratch
|
||||
// space, even if n < len(dst), but it does not allow implementations to use
|
||||
// dst[len(dst):cap(dst)]. Slicing it up-front allows us to append to it without worrying
|
||||
// about overwriting dst[len(dst):cap(dst)].
|
||||
m := cbor.RawMessage(dst[0:0:len(dst)])
|
||||
if err := fr.decoder.Decode(&m); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if len(m) > len(dst) {
|
||||
// The frame was too big, m has a newly-allocated underlying array to accommodate
|
||||
// it.
|
||||
fr.overflow = m[len(dst):]
|
||||
return copy(dst, m), io.ErrShortBuffer
|
||||
}
|
||||
|
||||
return len(m), nil
|
||||
}
|
||||
|
||||
func (fr *frameReader) Close() error {
|
||||
return fr.closer.Close()
|
||||
}
|
||||
65
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/buffers.go
generated
vendored
Normal file
65
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/buffers.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package modes
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
)
|
||||
|
||||
var buffers = BufferProvider{p: new(sync.Pool)}
|
||||
|
||||
type buffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
type pool interface {
|
||||
Get() interface{}
|
||||
Put(interface{})
|
||||
}
|
||||
|
||||
type BufferProvider struct {
|
||||
p pool
|
||||
}
|
||||
|
||||
func (b *BufferProvider) Get() *buffer {
|
||||
if buf, ok := b.p.Get().(*buffer); ok {
|
||||
return buf
|
||||
}
|
||||
return &buffer{}
|
||||
}
|
||||
|
||||
func (b *BufferProvider) Put(buf *buffer) {
|
||||
if buf.Cap() > 3*1024*1024 /* Default MaxRequestBodyBytes */ {
|
||||
// Objects in a sync.Pool are assumed to be fungible. This is not a good assumption
|
||||
// for pools of *bytes.Buffer because a *bytes.Buffer's underlying array grows as
|
||||
// needed to accommodate writes. In Kubernetes, apiservers tend to encode "small"
|
||||
// objects very frequently and much larger objects (especially large lists) only
|
||||
// occasionally. Under steady load, pooled buffers tend to be borrowed frequently
|
||||
// enough to prevent them from being released. Over time, each buffer is used to
|
||||
// encode a large object and its capacity increases accordingly. The result is that
|
||||
// practically all buffers in the pool retain much more capacity than needed to
|
||||
// encode most objects.
|
||||
|
||||
// As a basic mitigation for the worst case, buffers with more capacity than the
|
||||
// default max request body size are never returned to the pool.
|
||||
// TODO: Optimize for higher buffer utilization.
|
||||
return
|
||||
}
|
||||
buf.Reset()
|
||||
b.p.Put(buf)
|
||||
}
|
||||
178
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/decode.go
generated
vendored
Normal file
178
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/decode.go
generated
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package modes
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
var simpleValues *cbor.SimpleValueRegistry = func() *cbor.SimpleValueRegistry {
|
||||
var opts []func(*cbor.SimpleValueRegistry) error
|
||||
for sv := 0; sv <= 255; sv++ {
|
||||
// Reject simple values 0-19, 23, and 32-255. The simple values 24-31 are reserved
|
||||
// and considered ill-formed by the CBOR specification. We only accept false (20),
|
||||
// true (21), and null (22).
|
||||
switch sv {
|
||||
case 20: // false
|
||||
case 21: // true
|
||||
case 22: // null
|
||||
case 24, 25, 26, 27, 28, 29, 30, 31: // reserved
|
||||
default:
|
||||
opts = append(opts, cbor.WithRejectedSimpleValue(cbor.SimpleValue(sv)))
|
||||
}
|
||||
}
|
||||
simpleValues, err := cbor.NewSimpleValueRegistryFromDefaults(opts...)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return simpleValues
|
||||
}()
|
||||
|
||||
// decode is the basis for the Decode mode, with no JSONUnmarshalerTranscoder
|
||||
// configured. TranscodeToJSON uses this directly rather than Decode to avoid an initialization
|
||||
// cycle between the two. Everything else should use one of the exported DecModes.
|
||||
var decode cbor.DecMode = func() cbor.DecMode {
|
||||
decode, err := cbor.DecOptions{
|
||||
// Maps with duplicate keys are well-formed but invalid according to the CBOR spec
|
||||
// and never acceptable. Unlike the JSON serializer, inputs containing duplicate map
|
||||
// keys are rejected outright and not surfaced as a strict decoding error.
|
||||
DupMapKey: cbor.DupMapKeyEnforcedAPF,
|
||||
|
||||
// For JSON parity, decoding an RFC3339 string into time.Time needs to be accepted
|
||||
// with or without tagging. If a tag number is present, it must be valid.
|
||||
TimeTag: cbor.DecTagOptional,
|
||||
|
||||
// Observed depth up to 16 in fuzzed batch/v1 CronJobList. JSON implementation limit
|
||||
// is 10000.
|
||||
MaxNestedLevels: 64,
|
||||
|
||||
MaxArrayElements: 1024,
|
||||
MaxMapPairs: 1024,
|
||||
|
||||
// Indefinite-length sequences aren't produced by this serializer, but other
|
||||
// implementations can.
|
||||
IndefLength: cbor.IndefLengthAllowed,
|
||||
|
||||
// Accept inputs that contain CBOR tags.
|
||||
TagsMd: cbor.TagsAllowed,
|
||||
|
||||
// Decode type 0 (unsigned integer) as int64.
|
||||
// TODO: IntDecConvertSignedOrFail errors on overflow, JSON will try to fall back to float64.
|
||||
IntDec: cbor.IntDecConvertSignedOrFail,
|
||||
|
||||
// Disable producing map[cbor.ByteString]interface{}, which is not acceptable for
|
||||
// decodes into interface{}.
|
||||
MapKeyByteString: cbor.MapKeyByteStringForbidden,
|
||||
|
||||
// Error on map keys that don't map to a field in the destination struct.
|
||||
ExtraReturnErrors: cbor.ExtraDecErrorUnknownField,
|
||||
|
||||
// Decode maps into concrete type map[string]interface{} when the destination is an
|
||||
// interface{}.
|
||||
DefaultMapType: reflect.TypeOf(map[string]interface{}(nil)),
|
||||
|
||||
// A CBOR text string whose content is not a valid UTF-8 sequence is well-formed but
|
||||
// invalid according to the CBOR spec. Reject invalid inputs. Encoders are
|
||||
// responsible for ensuring that all text strings they produce contain valid UTF-8
|
||||
// sequences and may use the byte string major type to encode strings that have not
|
||||
// been validated.
|
||||
UTF8: cbor.UTF8RejectInvalid,
|
||||
|
||||
// Never make a case-insensitive match between a map key and a struct field.
|
||||
FieldNameMatching: cbor.FieldNameMatchingCaseSensitive,
|
||||
|
||||
// Produce string concrete values when decoding a CBOR byte string into interface{}.
|
||||
DefaultByteStringType: reflect.TypeOf(""),
|
||||
|
||||
// Allow CBOR byte strings to be decoded into string destination values. If a byte
|
||||
// string is enclosed in an "expected later encoding" tag
|
||||
// (https://www.rfc-editor.org/rfc/rfc8949.html#section-3.4.5.2), then the text
|
||||
// encoding indicated by that tag (e.g. base64) will be applied to the contents of
|
||||
// the byte string.
|
||||
ByteStringToString: cbor.ByteStringToStringAllowedWithExpectedLaterEncoding,
|
||||
|
||||
// Allow CBOR byte strings to match struct fields when appearing as a map key.
|
||||
FieldNameByteString: cbor.FieldNameByteStringAllowed,
|
||||
|
||||
// When decoding an unrecognized tag to interface{}, return the decoded tag content
|
||||
// instead of the default, a cbor.Tag representing a (number, content) pair.
|
||||
UnrecognizedTagToAny: cbor.UnrecognizedTagContentToAny,
|
||||
|
||||
// Decode time tags to interface{} as strings containing RFC 3339 timestamps.
|
||||
TimeTagToAny: cbor.TimeTagToRFC3339Nano,
|
||||
|
||||
// For parity with JSON, strings can be decoded into time.Time if they are RFC 3339
|
||||
// timestamps.
|
||||
ByteStringToTime: cbor.ByteStringToTimeAllowed,
|
||||
|
||||
// Reject NaN and infinite floating-point values since they don't have a JSON
|
||||
// representation (RFC 8259 Section 6).
|
||||
NaN: cbor.NaNDecodeForbidden,
|
||||
Inf: cbor.InfDecodeForbidden,
|
||||
|
||||
// When unmarshaling a byte string into a []byte, assume that the byte string
|
||||
// contains base64-encoded bytes, unless explicitly counterindicated by an "expected
|
||||
// later encoding" tag. This is consistent with the because of unmarshaling a JSON
|
||||
// text into a []byte.
|
||||
ByteStringExpectedFormat: cbor.ByteStringExpectedBase64,
|
||||
|
||||
// Reject the arbitrary-precision integer tags because they can't be faithfully
|
||||
// roundtripped through the allowable Unstructured types.
|
||||
BignumTag: cbor.BignumTagForbidden,
|
||||
|
||||
// Reject anything other than the simple values true, false, and null.
|
||||
SimpleValues: simpleValues,
|
||||
|
||||
// Disable default recognition of types implementing encoding.BinaryUnmarshaler,
|
||||
// which is not recognized for JSON decoding.
|
||||
BinaryUnmarshaler: cbor.BinaryUnmarshalerNone,
|
||||
|
||||
// Marshal types that implement encoding.TextMarshaler by calling their MarshalText
|
||||
// method and encoding the result to a CBOR text string.
|
||||
TextUnmarshaler: cbor.TextUnmarshalerTextString,
|
||||
}.DecMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return decode
|
||||
}()
|
||||
|
||||
var Decode cbor.DecMode = func() cbor.DecMode {
|
||||
opts := decode.DecOptions()
|
||||
// When decoding into a value of a type that implements json.Unmarshaler (and does not
|
||||
// implement cbor.Unmarshaler), transcode the input to JSON and pass it to the value's
|
||||
// UnmarshalJSON method.
|
||||
opts.JSONUnmarshalerTranscoder = TranscodeFunc(TranscodeToJSON)
|
||||
dm, err := opts.DecMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dm
|
||||
}()
|
||||
|
||||
// DecodeLax is derived from Decode, but does not complain about unknown fields in the input.
|
||||
var DecodeLax cbor.DecMode = func() cbor.DecMode {
|
||||
opts := Decode.DecOptions()
|
||||
opts.ExtraReturnErrors &^= cbor.ExtraDecErrorUnknownField // clear bit
|
||||
dm, err := opts.DecMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dm
|
||||
}()
|
||||
36
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/diagnostic.go
generated
vendored
Normal file
36
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/diagnostic.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package modes
|
||||
|
||||
import (
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
var Diagnostic cbor.DiagMode = func() cbor.DiagMode {
|
||||
opts := Decode.DecOptions()
|
||||
diagnostic, err := cbor.DiagOptions{
|
||||
ByteStringText: true,
|
||||
|
||||
MaxNestedLevels: opts.MaxNestedLevels,
|
||||
MaxArrayElements: opts.MaxArrayElements,
|
||||
MaxMapPairs: opts.MaxMapPairs,
|
||||
}.DiagMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return diagnostic
|
||||
}()
|
||||
177
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/encode.go
generated
vendored
Normal file
177
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/encode.go
generated
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package modes
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/fxamacker/cbor/v2"
|
||||
)
|
||||
|
||||
// encode is the basis for the Encode mode, with no JSONMarshalerTranscoder
|
||||
// configured. TranscodeFromJSON uses this directly rather than Encode to avoid an initialization
|
||||
// cycle between the two. Everything else should use one of the exported EncModes.
|
||||
var encode = EncMode{
|
||||
delegate: func() cbor.UserBufferEncMode {
|
||||
encode, err := cbor.EncOptions{
|
||||
// Map keys need to be sorted to have deterministic output, and this is the order
|
||||
// defined in RFC 8949 4.2.1 "Core Deterministic Encoding Requirements".
|
||||
Sort: cbor.SortBytewiseLexical,
|
||||
|
||||
// CBOR supports distinct types for IEEE-754 float16, float32, and float64. Store
|
||||
// floats in the smallest width that preserves value so that equivalent float32 and
|
||||
// float64 values encode to identical bytes, as they do in a JSON
|
||||
// encoding. Satisfies one of the "Core Deterministic Encoding Requirements".
|
||||
ShortestFloat: cbor.ShortestFloat16,
|
||||
|
||||
// Error on attempt to encode NaN and infinite values. This is what the JSON
|
||||
// serializer does.
|
||||
NaNConvert: cbor.NaNConvertReject,
|
||||
InfConvert: cbor.InfConvertReject,
|
||||
|
||||
// Error on attempt to encode math/big.Int values, which can't be faithfully
|
||||
// roundtripped through Unstructured in general (the dynamic numeric types allowed
|
||||
// in Unstructured are limited to float64 and int64).
|
||||
BigIntConvert: cbor.BigIntConvertReject,
|
||||
|
||||
// MarshalJSON for time.Time writes RFC3339 with nanos.
|
||||
Time: cbor.TimeRFC3339Nano,
|
||||
|
||||
// The decoder must be able to accept RFC3339 strings with or without tag 0 (e.g. by
|
||||
// the end of time.Time -> JSON -> Unstructured -> CBOR, the CBOR encoder has no
|
||||
// reliable way of knowing that a particular string originated from serializing a
|
||||
// time.Time), so producing tag 0 has little use.
|
||||
TimeTag: cbor.EncTagNone,
|
||||
|
||||
// Indefinite-length items have multiple encodings and aren't being used anyway, so
|
||||
// disable to avoid an opportunity for nondeterminism.
|
||||
IndefLength: cbor.IndefLengthForbidden,
|
||||
|
||||
// Preserve distinction between nil and empty for slices and maps.
|
||||
NilContainers: cbor.NilContainerAsNull,
|
||||
|
||||
// OK to produce tags.
|
||||
TagsMd: cbor.TagsAllowed,
|
||||
|
||||
// Use the same definition of "empty" as encoding/json.
|
||||
OmitEmpty: cbor.OmitEmptyGoValue,
|
||||
|
||||
// The CBOR types text string and byte string are structurally equivalent, with the
|
||||
// semantic difference that a text string whose content is an invalid UTF-8 sequence
|
||||
// is itself invalid. We reject all invalid text strings at decode time and do not
|
||||
// validate or sanitize all Go strings at encode time. Encoding Go strings to the
|
||||
// byte string type is comparable to the existing Protobuf behavior and cheaply
|
||||
// ensures that the output is valid CBOR.
|
||||
String: cbor.StringToByteString,
|
||||
|
||||
// Encode struct field names to the byte string type rather than the text string
|
||||
// type.
|
||||
FieldName: cbor.FieldNameToByteString,
|
||||
|
||||
// Marshal Go byte arrays to CBOR arrays of integers (as in JSON) instead of byte
|
||||
// strings.
|
||||
ByteArray: cbor.ByteArrayToArray,
|
||||
|
||||
// Marshal []byte to CBOR byte string enclosed in tag 22 (expected later base64
|
||||
// encoding, https://www.rfc-editor.org/rfc/rfc8949.html#section-3.4.5.2), to
|
||||
// interoperate with the existing JSON behavior. This indicates to the decoder that,
|
||||
// when decoding into a string (or unstructured), the resulting value should be the
|
||||
// base64 encoding of the original bytes. No base64 encoding or decoding needs to be
|
||||
// performed for []byte-to-CBOR-to-[]byte roundtrips.
|
||||
ByteSliceLaterFormat: cbor.ByteSliceLaterFormatBase64,
|
||||
|
||||
// Disable default recognition of types implementing encoding.BinaryMarshaler, which
|
||||
// is not recognized for JSON encoding.
|
||||
BinaryMarshaler: cbor.BinaryMarshalerNone,
|
||||
|
||||
// Unmarshal into types that implement encoding.TextUnmarshaler by passing
|
||||
// the contents of a CBOR string to their UnmarshalText method.
|
||||
TextMarshaler: cbor.TextMarshalerTextString,
|
||||
}.UserBufferEncMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return encode
|
||||
}(),
|
||||
}
|
||||
|
||||
var Encode = EncMode{
|
||||
delegate: func() cbor.UserBufferEncMode {
|
||||
opts := encode.options()
|
||||
// To encode a value of a type that implements json.Marshaler (and does not
|
||||
// implement cbor.Marshaler), transcode the result of calling its MarshalJSON method
|
||||
// directly to CBOR.
|
||||
opts.JSONMarshalerTranscoder = TranscodeFunc(TranscodeFromJSON)
|
||||
em, err := opts.UserBufferEncMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return em
|
||||
}(),
|
||||
}
|
||||
|
||||
var EncodeNondeterministic = EncMode{
|
||||
delegate: func() cbor.UserBufferEncMode {
|
||||
opts := Encode.options()
|
||||
opts.Sort = cbor.SortFastShuffle
|
||||
em, err := opts.UserBufferEncMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return em
|
||||
}(),
|
||||
}
|
||||
|
||||
type EncMode struct {
|
||||
delegate cbor.UserBufferEncMode
|
||||
}
|
||||
|
||||
func (em EncMode) options() cbor.EncOptions {
|
||||
return em.delegate.EncOptions()
|
||||
}
|
||||
|
||||
func (em EncMode) MarshalTo(v interface{}, w io.Writer) error {
|
||||
if buf, ok := w.(*buffer); ok {
|
||||
return em.delegate.MarshalToBuffer(v, &buf.Buffer)
|
||||
}
|
||||
|
||||
buf := buffers.Get()
|
||||
defer buffers.Put(buf)
|
||||
if err := em.delegate.MarshalToBuffer(v, &buf.Buffer); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(w, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (em EncMode) Marshal(v interface{}) ([]byte, error) {
|
||||
buf := buffers.Get()
|
||||
defer buffers.Put(buf)
|
||||
|
||||
if err := em.MarshalTo(v, &buf.Buffer); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clone := make([]byte, buf.Len())
|
||||
copy(clone, buf.Bytes())
|
||||
|
||||
return clone, nil
|
||||
}
|
||||
108
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/transcoding.go
generated
vendored
Normal file
108
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/transcoding.go
generated
vendored
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
Copyright 2025 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package modes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
kjson "sigs.k8s.io/json"
|
||||
)
|
||||
|
||||
type TranscodeFunc func(dst io.Writer, src io.Reader) error
|
||||
|
||||
func (f TranscodeFunc) Transcode(dst io.Writer, src io.Reader) error {
|
||||
return f(dst, src)
|
||||
}
|
||||
|
||||
func TranscodeFromJSON(dst io.Writer, src io.Reader) error {
|
||||
var tmp any
|
||||
dec := kjson.NewDecoderCaseSensitivePreserveInts(src)
|
||||
if err := dec.Decode(&tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dec.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
|
||||
return errors.New("extraneous data")
|
||||
}
|
||||
|
||||
return encode.MarshalTo(tmp, dst)
|
||||
}
|
||||
|
||||
func TranscodeToJSON(dst io.Writer, src io.Reader) error {
|
||||
var tmp any
|
||||
dec := decode.NewDecoder(src)
|
||||
if err := dec.Decode(&tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dec.Decode(&struct{}{}); !errors.Is(err, io.EOF) {
|
||||
return errors.New("extraneous data")
|
||||
}
|
||||
|
||||
// Use an Encoder to avoid the extra []byte allocated by Marshal. Encode, unlike Marshal,
|
||||
// appends a trailing newline to separate consecutive encodings of JSON values that aren't
|
||||
// self-delimiting, like numbers. Strip the newline to avoid the assumption that every
|
||||
// json.Unmarshaler implementation will accept trailing whitespace.
|
||||
enc := json.NewEncoder(&trailingLinefeedSuppressor{delegate: dst})
|
||||
enc.SetIndent("", "")
|
||||
return enc.Encode(tmp)
|
||||
}
|
||||
|
||||
// trailingLinefeedSuppressor is an io.Writer that wraps another io.Writer, suppressing a single
|
||||
// trailing linefeed if it is the last byte written by the latest call to Write.
|
||||
type trailingLinefeedSuppressor struct {
|
||||
lf bool
|
||||
delegate io.Writer
|
||||
}
|
||||
|
||||
func (w *trailingLinefeedSuppressor) Write(p []byte) (int, error) {
|
||||
if len(p) == 0 {
|
||||
// Avoid flushing a buffered linefeeds on an empty write.
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
if w.lf {
|
||||
// The previous write had a trailing linefeed that was buffered. That wasn't the
|
||||
// last Write call, so flush the buffered linefeed before continuing.
|
||||
n, err := w.delegate.Write([]byte{'\n'})
|
||||
if n > 0 {
|
||||
w.lf = false
|
||||
}
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if p[len(p)-1] != '\n' {
|
||||
return w.delegate.Write(p)
|
||||
}
|
||||
|
||||
p = p[:len(p)-1]
|
||||
|
||||
if len(p) == 0 { // []byte{'\n'}
|
||||
w.lf = true
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
n, err := w.delegate.Write(p)
|
||||
if n == len(p) {
|
||||
// Everything up to the trailing linefeed has been flushed. Eat the linefeed.
|
||||
w.lf = true
|
||||
n++
|
||||
}
|
||||
return n, err
|
||||
}
|
||||
236
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/raw.go
generated
vendored
Normal file
236
vendor/k8s.io/apimachinery/pkg/runtime/serializer/cbor/raw.go
generated
vendored
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
Copyright 2024 The Kubernetes Authors.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package cbor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"sync"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
||||
var sharedTranscoders transcoders
|
||||
|
||||
var rawTypeTranscodeFuncs = map[reflect.Type]func(reflect.Value) error{
|
||||
reflect.TypeFor[runtime.RawExtension](): func(rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return nil
|
||||
}
|
||||
re := rv.Addr().Interface().(*runtime.RawExtension)
|
||||
if re.Raw == nil {
|
||||
// When Raw is nil it encodes to null. Don't change nil Raw values during
|
||||
// transcoding, they would have unmarshalled from JSON as nil too.
|
||||
return nil
|
||||
}
|
||||
j, err := re.MarshalJSON()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to transcode RawExtension to JSON: %w", err)
|
||||
}
|
||||
re.Raw = j
|
||||
return nil
|
||||
},
|
||||
reflect.TypeFor[metav1.FieldsV1](): func(rv reflect.Value) error {
|
||||
if !rv.CanAddr() {
|
||||
return nil
|
||||
}
|
||||
fields := rv.Addr().Interface().(*metav1.FieldsV1)
|
||||
if fields.Raw == nil {
|
||||
// When Raw is nil it encodes to null. Don't change nil Raw values during
|
||||
// transcoding, they would have unmarshalled from JSON as nil too.
|
||||
return nil
|
||||
}
|
||||
j, err := fields.MarshalJSON()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to transcode FieldsV1 to JSON: %w", err)
|
||||
}
|
||||
fields.Raw = j
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func transcodeRawTypes(v interface{}) error {
|
||||
if v == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
rv := reflect.ValueOf(v)
|
||||
return sharedTranscoders.getTranscoder(rv.Type()).fn(rv)
|
||||
}
|
||||
|
||||
type transcoder struct {
|
||||
fn func(rv reflect.Value) error
|
||||
}
|
||||
|
||||
var noop = transcoder{
|
||||
fn: func(reflect.Value) error {
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
type transcoders struct {
|
||||
lock sync.RWMutex
|
||||
m map[reflect.Type]**transcoder
|
||||
}
|
||||
|
||||
func (ts *transcoders) getTranscoder(rt reflect.Type) transcoder {
|
||||
ts.lock.RLock()
|
||||
tpp, ok := ts.m[rt]
|
||||
ts.lock.RUnlock()
|
||||
if ok {
|
||||
return **tpp
|
||||
}
|
||||
|
||||
ts.lock.Lock()
|
||||
defer ts.lock.Unlock()
|
||||
tp := ts.getTranscoderLocked(rt)
|
||||
return *tp
|
||||
}
|
||||
|
||||
func (ts *transcoders) getTranscoderLocked(rt reflect.Type) *transcoder {
|
||||
if tpp, ok := ts.m[rt]; ok {
|
||||
// A transcoder for this type was cached while waiting to acquire the lock.
|
||||
return *tpp
|
||||
}
|
||||
|
||||
// Cache the transcoder now, before populating fn, so that circular references between types
|
||||
// don't overflow the call stack.
|
||||
t := new(transcoder)
|
||||
if ts.m == nil {
|
||||
ts.m = make(map[reflect.Type]**transcoder)
|
||||
}
|
||||
ts.m[rt] = &t
|
||||
|
||||
for rawType, fn := range rawTypeTranscodeFuncs {
|
||||
if rt == rawType {
|
||||
t = &transcoder{fn: fn}
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
switch rt.Kind() {
|
||||
case reflect.Array:
|
||||
te := ts.getTranscoderLocked(rt.Elem())
|
||||
rtlen := rt.Len()
|
||||
if rtlen == 0 || te == &noop {
|
||||
t = &noop
|
||||
break
|
||||
}
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
for i := 0; i < rtlen; i++ {
|
||||
if err := te.fn(rv.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case reflect.Interface:
|
||||
// Any interface value might have a dynamic type involving RawExtension. It needs to
|
||||
// be checked.
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
if rv.IsNil() {
|
||||
return nil
|
||||
}
|
||||
rv = rv.Elem()
|
||||
// The interface element's type is dynamic so its transcoder can't be
|
||||
// determined statically.
|
||||
return ts.getTranscoder(rv.Type()).fn(rv)
|
||||
}
|
||||
case reflect.Map:
|
||||
rtk := rt.Key()
|
||||
tk := ts.getTranscoderLocked(rtk)
|
||||
rte := rt.Elem()
|
||||
te := ts.getTranscoderLocked(rte)
|
||||
if tk == &noop && te == &noop {
|
||||
t = &noop
|
||||
break
|
||||
}
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
iter := rv.MapRange()
|
||||
rvk := reflect.New(rtk).Elem()
|
||||
rve := reflect.New(rte).Elem()
|
||||
for iter.Next() {
|
||||
rvk.SetIterKey(iter)
|
||||
if err := tk.fn(rvk); err != nil {
|
||||
return err
|
||||
}
|
||||
rve.SetIterValue(iter)
|
||||
if err := te.fn(rve); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case reflect.Pointer:
|
||||
te := ts.getTranscoderLocked(rt.Elem())
|
||||
if te == &noop {
|
||||
t = &noop
|
||||
break
|
||||
}
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
if rv.IsNil() {
|
||||
return nil
|
||||
}
|
||||
return te.fn(rv.Elem())
|
||||
}
|
||||
case reflect.Slice:
|
||||
te := ts.getTranscoderLocked(rt.Elem())
|
||||
if te == &noop {
|
||||
t = &noop
|
||||
break
|
||||
}
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
if err := te.fn(rv.Index(i)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
case reflect.Struct:
|
||||
type fieldTranscoder struct {
|
||||
Index int
|
||||
Transcoder *transcoder
|
||||
}
|
||||
var fieldTranscoders []fieldTranscoder
|
||||
for i := 0; i < rt.NumField(); i++ {
|
||||
f := rt.Field(i)
|
||||
tf := ts.getTranscoderLocked(f.Type)
|
||||
if tf == &noop {
|
||||
continue
|
||||
}
|
||||
fieldTranscoders = append(fieldTranscoders, fieldTranscoder{Index: i, Transcoder: tf})
|
||||
}
|
||||
if len(fieldTranscoders) == 0 {
|
||||
t = &noop
|
||||
break
|
||||
}
|
||||
t.fn = func(rv reflect.Value) error {
|
||||
for _, ft := range fieldTranscoders {
|
||||
if err := ft.Transcoder.fn(rv.Field(ft.Index)); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
default:
|
||||
t = &noop
|
||||
}
|
||||
|
||||
return t
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue