github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/runtime/serializer/json/json.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 json
    18  
    19  import (
    20  	"encoding/json"
    21  	"io"
    22  	"strconv"
    23  
    24  	kjson "sigs.k8s.io/json"
    25  	"sigs.k8s.io/yaml"
    26  
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/serializer/recognizer"
    30  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/framer"
    31  	utilyaml "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/yaml"
    32  	"k8s.io/klog/v2"
    33  )
    34  
    35  // NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer
    36  // is not nil, the object has the group, version, and kind fields set.
    37  // Deprecated: use NewSerializerWithOptions instead.
    38  func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, pretty bool) *Serializer {
    39  	return NewSerializerWithOptions(meta, creater, typer, SerializerOptions{false, pretty, false})
    40  }
    41  
    42  // NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer
    43  // is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that
    44  // matches JSON, and will error if constructs are used that do not serialize to JSON.
    45  // Deprecated: use NewSerializerWithOptions instead.
    46  func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper) *Serializer {
    47  	return NewSerializerWithOptions(meta, creater, typer, SerializerOptions{true, false, false})
    48  }
    49  
    50  // NewSerializerWithOptions creates a JSON/YAML serializer that handles encoding versioned objects into the proper JSON/YAML
    51  // form. If typer is not nil, the object has the group, version, and kind fields set. Options are copied into the Serializer
    52  // and are immutable.
    53  func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer {
    54  	return &Serializer{
    55  		meta:       meta,
    56  		creater:    creater,
    57  		typer:      typer,
    58  		options:    options,
    59  		identifier: identifier(options),
    60  	}
    61  }
    62  
    63  // identifier computes Identifier of Encoder based on the given options.
    64  func identifier(options SerializerOptions) runtime.Identifier {
    65  	result := map[string]string{
    66  		"name":   "json",
    67  		"yaml":   strconv.FormatBool(options.Yaml),
    68  		"pretty": strconv.FormatBool(options.Pretty),
    69  		"strict": strconv.FormatBool(options.Strict),
    70  	}
    71  	identifier, err := json.Marshal(result)
    72  	if err != nil {
    73  		klog.Fatalf("Failed marshaling identifier for json Serializer: %v", err)
    74  	}
    75  	return runtime.Identifier(identifier)
    76  }
    77  
    78  // SerializerOptions holds the options which are used to configure a JSON/YAML serializer.
    79  // example:
    80  // (1) To configure a JSON serializer, set `Yaml` to `false`.
    81  // (2) To configure a YAML serializer, set `Yaml` to `true`.
    82  // (3) To configure a strict serializer that can return strictDecodingError, set `Strict` to `true`.
    83  type SerializerOptions struct {
    84  	// Yaml: configures the Serializer to work with JSON(false) or YAML(true).
    85  	// When `Yaml` is enabled, this serializer only supports the subset of YAML that
    86  	// matches JSON, and will error if constructs are used that do not serialize to JSON.
    87  	Yaml bool
    88  
    89  	// Pretty: configures a JSON enabled Serializer(`Yaml: false`) to produce human-readable output.
    90  	// This option is silently ignored when `Yaml` is `true`.
    91  	Pretty bool
    92  
    93  	// Strict: configures the Serializer to return strictDecodingError's when duplicate fields are present decoding JSON or YAML.
    94  	// Note that enabling this option is not as performant as the non-strict variant, and should not be used in fast paths.
    95  	Strict bool
    96  }
    97  
    98  // Serializer handles encoding versioned objects into the proper JSON form
    99  type Serializer struct {
   100  	meta    MetaFactory
   101  	options SerializerOptions
   102  	creater runtime.ObjectCreater
   103  	typer   runtime.ObjectTyper
   104  
   105  	identifier runtime.Identifier
   106  }
   107  
   108  // Serializer implements Serializer
   109  var _ runtime.Serializer = &Serializer{}
   110  var _ recognizer.RecognizingDecoder = &Serializer{}
   111  
   112  // gvkWithDefaults returns group kind and version defaulting from provided default
   113  func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
   114  	if len(actual.Kind) == 0 {
   115  		actual.Kind = defaultGVK.Kind
   116  	}
   117  	if len(actual.Version) == 0 && len(actual.Group) == 0 {
   118  		actual.Group = defaultGVK.Group
   119  		actual.Version = defaultGVK.Version
   120  	}
   121  	if len(actual.Version) == 0 && actual.Group == defaultGVK.Group {
   122  		actual.Version = defaultGVK.Version
   123  	}
   124  	return actual
   125  }
   126  
   127  // Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then
   128  // load that data into an object matching the desired schema kind or the provided into.
   129  // If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed.
   130  // If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling.
   131  // If into is provided and the original data is not fully qualified with kind/version/group, the type of the into will be used to alter the returned gvk.
   132  // If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk)
   133  // On success or most errors, the method will return the calculated schema kind.
   134  // The gvk calculate priority will be originalData > default gvk > into
   135  func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
   136  	data := originalData
   137  	if s.options.Yaml {
   138  		altered, err := yaml.YAMLToJSON(data)
   139  		if err != nil {
   140  			return nil, nil, err
   141  		}
   142  		data = altered
   143  	}
   144  
   145  	actual, err := s.meta.Interpret(data)
   146  	if err != nil {
   147  		return nil, nil, err
   148  	}
   149  
   150  	if gvk != nil {
   151  		*actual = gvkWithDefaults(*actual, *gvk)
   152  	}
   153  
   154  	if unk, ok := into.(*runtime.Unknown); ok && unk != nil {
   155  		unk.Raw = originalData
   156  		unk.ContentType = runtime.ContentTypeJSON
   157  		unk.GetObjectKind().SetGroupVersionKind(*actual)
   158  		return unk, actual, nil
   159  	}
   160  
   161  	if into != nil {
   162  		_, isUnstructured := into.(runtime.Unstructured)
   163  		types, _, err := s.typer.ObjectKinds(into)
   164  		switch {
   165  		case runtime.IsNotRegisteredError(err), isUnstructured:
   166  			strictErrs, err := s.unmarshal(into, data, originalData)
   167  			if err != nil {
   168  				return nil, actual, err
   169  			}
   170  
   171  			// when decoding directly into a provided unstructured object,
   172  			// extract the actual gvk decoded from the provided data,
   173  			// and ensure it is non-empty.
   174  			if isUnstructured {
   175  				*actual = into.GetObjectKind().GroupVersionKind()
   176  				if len(actual.Kind) == 0 {
   177  					return nil, actual, runtime.NewMissingKindErr(string(originalData))
   178  				}
   179  				// TODO(109023): require apiVersion here as well once unstructuredJSONScheme#Decode does
   180  			}
   181  
   182  			if len(strictErrs) > 0 {
   183  				return into, actual, runtime.NewStrictDecodingError(strictErrs)
   184  			}
   185  			return into, actual, nil
   186  		case err != nil:
   187  			return nil, actual, err
   188  		default:
   189  			*actual = gvkWithDefaults(*actual, types[0])
   190  		}
   191  	}
   192  
   193  	if len(actual.Kind) == 0 {
   194  		return nil, actual, runtime.NewMissingKindErr(string(originalData))
   195  	}
   196  	if len(actual.Version) == 0 {
   197  		return nil, actual, runtime.NewMissingVersionErr(string(originalData))
   198  	}
   199  
   200  	// use the target if necessary
   201  	obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
   202  	if err != nil {
   203  		return nil, actual, err
   204  	}
   205  
   206  	strictErrs, err := s.unmarshal(obj, data, originalData)
   207  	if err != nil {
   208  		return nil, actual, err
   209  	} else if len(strictErrs) > 0 {
   210  		return obj, actual, runtime.NewStrictDecodingError(strictErrs)
   211  	}
   212  	return obj, actual, nil
   213  }
   214  
   215  // Encode serializes the provided object to the given writer.
   216  func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
   217  	if co, ok := obj.(runtime.CacheableObject); ok {
   218  		return co.CacheEncode(s.Identifier(), s.doEncode, w)
   219  	}
   220  	return s.doEncode(obj, w)
   221  }
   222  
   223  func (s *Serializer) doEncode(obj runtime.Object, w io.Writer) error {
   224  	if s.options.Yaml {
   225  		json, err := json.Marshal(obj)
   226  		if err != nil {
   227  			return err
   228  		}
   229  		data, err := yaml.JSONToYAML(json)
   230  		if err != nil {
   231  			return err
   232  		}
   233  		_, err = w.Write(data)
   234  		return err
   235  	}
   236  
   237  	if s.options.Pretty {
   238  		data, err := json.MarshalIndent(obj, "", "  ")
   239  		if err != nil {
   240  			return err
   241  		}
   242  		_, err = w.Write(data)
   243  		return err
   244  	}
   245  	encoder := json.NewEncoder(w)
   246  	return encoder.Encode(obj)
   247  }
   248  
   249  // IsStrict indicates whether the serializer
   250  // uses strict decoding or not
   251  func (s *Serializer) IsStrict() bool {
   252  	return s.options.Strict
   253  }
   254  
   255  func (s *Serializer) unmarshal(into runtime.Object, data, originalData []byte) (strictErrs []error, err error) {
   256  	// If the deserializer is non-strict, return here.
   257  	if !s.options.Strict {
   258  		if err := kjson.UnmarshalCaseSensitivePreserveInts(data, into); err != nil {
   259  			return nil, err
   260  		}
   261  		return nil, nil
   262  	}
   263  
   264  	if s.options.Yaml {
   265  		// In strict mode pass the original data through the YAMLToJSONStrict converter.
   266  		// This is done to catch duplicate fields in YAML that would have been dropped in the original YAMLToJSON conversion.
   267  		// TODO: rework YAMLToJSONStrict to return warnings about duplicate fields without terminating so we don't have to do this twice.
   268  		_, err := yaml.YAMLToJSONStrict(originalData)
   269  		if err != nil {
   270  			strictErrs = append(strictErrs, err)
   271  		}
   272  	}
   273  
   274  	var strictJSONErrs []error
   275  	if u, isUnstructured := into.(runtime.Unstructured); isUnstructured {
   276  		// Unstructured is a custom unmarshaler that gets delegated
   277  		// to, so in order to detect strict JSON errors we need
   278  		// to unmarshal directly into the object.
   279  		m := map[string]interface{}{}
   280  		strictJSONErrs, err = kjson.UnmarshalStrict(data, &m)
   281  		u.SetUnstructuredContent(m)
   282  	} else {
   283  		strictJSONErrs, err = kjson.UnmarshalStrict(data, into)
   284  	}
   285  	if err != nil {
   286  		// fatal decoding error, not due to strictness
   287  		return nil, err
   288  	}
   289  	strictErrs = append(strictErrs, strictJSONErrs...)
   290  	return strictErrs, nil
   291  }
   292  
   293  // Identifier implements runtime.Encoder interface.
   294  func (s *Serializer) Identifier() runtime.Identifier {
   295  	return s.identifier
   296  }
   297  
   298  // RecognizesData implements the RecognizingDecoder interface.
   299  func (s *Serializer) RecognizesData(data []byte) (ok, unknown bool, err error) {
   300  	if s.options.Yaml {
   301  		// we could potentially look for '---'
   302  		return false, true, nil
   303  	}
   304  	return utilyaml.IsJSONBuffer(data), false, nil
   305  }
   306  
   307  // Framer is the default JSON framing behavior, with newlines delimiting individual objects.
   308  var Framer = jsonFramer{}
   309  
   310  type jsonFramer struct{}
   311  
   312  // NewFrameWriter implements stream framing for this serializer
   313  func (jsonFramer) NewFrameWriter(w io.Writer) io.Writer {
   314  	// we can write JSON objects directly to the writer, because they are self-framing
   315  	return w
   316  }
   317  
   318  // NewFrameReader implements stream framing for this serializer
   319  func (jsonFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser {
   320  	// we need to extract the JSON chunks of data to pass to Decode()
   321  	return framer.NewJSONFramedReader(r)
   322  }
   323  
   324  // YAMLFramer is the default JSON framing behavior, with newlines delimiting individual objects.
   325  var YAMLFramer = yamlFramer{}
   326  
   327  type yamlFramer struct{}
   328  
   329  // NewFrameWriter implements stream framing for this serializer
   330  func (yamlFramer) NewFrameWriter(w io.Writer) io.Writer {
   331  	return yamlFrameWriter{w}
   332  }
   333  
   334  // NewFrameReader implements stream framing for this serializer
   335  func (yamlFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser {
   336  	// extract the YAML document chunks directly
   337  	return utilyaml.NewDocumentDecoder(r)
   338  }
   339  
   340  type yamlFrameWriter struct {
   341  	w io.Writer
   342  }
   343  
   344  // Write separates each document with the YAML document separator (`---` followed by line
   345  // break). Writers must write well formed YAML documents (include a final line break).
   346  func (w yamlFrameWriter) Write(data []byte) (n int, err error) {
   347  	if _, err := w.w.Write([]byte("---\n")); err != nil {
   348  		return 0, err
   349  	}
   350  	return w.w.Write(data)
   351  }