2017-11-20 09:30:52 -05:00
/ *
Copyright 2014 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 json
import (
"encoding/json"
"io"
"strconv"
"unsafe"
"github.com/ghodss/yaml"
jsoniter "github.com/json-iterator/go"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
"k8s.io/apimachinery/pkg/util/framer"
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
)
// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer
// is not nil, the object has the group, version, and kind fields set.
func NewSerializer ( meta MetaFactory , creater runtime . ObjectCreater , typer runtime . ObjectTyper , pretty bool ) * Serializer {
return & Serializer {
meta : meta ,
creater : creater ,
typer : typer ,
yaml : false ,
pretty : pretty ,
}
}
// NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer
// is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that
// matches JSON, and will error if constructs are used that do not serialize to JSON.
func NewYAMLSerializer ( meta MetaFactory , creater runtime . ObjectCreater , typer runtime . ObjectTyper ) * Serializer {
return & Serializer {
meta : meta ,
creater : creater ,
typer : typer ,
yaml : true ,
}
}
type Serializer struct {
meta MetaFactory
creater runtime . ObjectCreater
typer runtime . ObjectTyper
yaml bool
pretty bool
}
// Serializer implements Serializer
var _ runtime . Serializer = & Serializer { }
var _ recognizer . RecognizingDecoder = & Serializer { }
func init ( ) {
// Force jsoniter to decode number to interface{} via ints, if possible.
decodeNumberAsInt64IfPossible := func ( ptr unsafe . Pointer , iter * jsoniter . Iterator ) {
switch iter . WhatIsNext ( ) {
case jsoniter . NumberValue :
var number json . Number
iter . ReadVal ( & number )
i64 , err := strconv . ParseInt ( string ( number ) , 10 , 64 )
if err == nil {
* ( * interface { } ) ( ptr ) = i64
return
}
f64 , err := strconv . ParseFloat ( string ( number ) , 64 )
if err == nil {
* ( * interface { } ) ( ptr ) = f64
return
}
// Not much we can do here.
default :
* ( * interface { } ) ( ptr ) = iter . Read ( )
}
}
jsoniter . RegisterTypeDecoderFunc ( "interface {}" , decodeNumberAsInt64IfPossible )
}
2018-07-25 04:17:02 -04:00
// CaseSensitiveJsonIterator returns a jsoniterator API that's configured to be
// case-sensitive when unmarshalling, and otherwise compatible with
// the encoding/json standard library.
func CaseSensitiveJsonIterator ( ) jsoniter . API {
return jsoniter . Config {
EscapeHTML : true ,
SortMapKeys : true ,
ValidateJsonRawMessage : true ,
CaseSensitive : true ,
} . Froze ( )
}
var caseSensitiveJsonIterator = CaseSensitiveJsonIterator ( )
// 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
}
2017-11-20 09:30:52 -05:00
// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then
2018-07-25 04:17:02 -04:00
// load that data into an object matching the desired schema kind or the provided into.
// If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed.
// If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling.
// 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.
// If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk)
// On success or most errors, the method will return the calculated schema kind.
// The gvk calculate priority will be originalData > default gvk > into
2017-11-20 09:30:52 -05:00
func ( s * Serializer ) Decode ( originalData [ ] byte , gvk * schema . GroupVersionKind , into runtime . Object ) ( runtime . Object , * schema . GroupVersionKind , error ) {
if versioned , ok := into . ( * runtime . VersionedObjects ) ; ok {
into = versioned . Last ( )
obj , actual , err := s . Decode ( originalData , gvk , into )
if err != nil {
return nil , actual , err
}
versioned . Objects = [ ] runtime . Object { obj }
return versioned , actual , nil
}
data := originalData
if s . yaml {
altered , err := yaml . YAMLToJSON ( data )
if err != nil {
return nil , nil , err
}
data = altered
}
actual , err := s . meta . Interpret ( data )
if err != nil {
return nil , nil , err
}
if gvk != nil {
2018-07-25 04:17:02 -04:00
* actual = gvkWithDefaults ( * actual , * gvk )
2017-11-20 09:30:52 -05:00
}
if unk , ok := into . ( * runtime . Unknown ) ; ok && unk != nil {
unk . Raw = originalData
unk . ContentType = runtime . ContentTypeJSON
unk . GetObjectKind ( ) . SetGroupVersionKind ( * actual )
return unk , actual , nil
}
if into != nil {
2018-07-25 04:17:02 -04:00
_ , isUnstructured := into . ( runtime . Unstructured )
2017-11-20 09:30:52 -05:00
types , _ , err := s . typer . ObjectKinds ( into )
switch {
2018-07-25 04:17:02 -04:00
case runtime . IsNotRegisteredError ( err ) , isUnstructured :
if err := caseSensitiveJsonIterator . Unmarshal ( data , into ) ; err != nil {
2017-11-20 09:30:52 -05:00
return nil , actual , err
}
return into , actual , nil
case err != nil :
return nil , actual , err
default :
2018-07-25 04:17:02 -04:00
* actual = gvkWithDefaults ( * actual , types [ 0 ] )
2017-11-20 09:30:52 -05:00
}
}
if len ( actual . Kind ) == 0 {
return nil , actual , runtime . NewMissingKindErr ( string ( originalData ) )
}
if len ( actual . Version ) == 0 {
return nil , actual , runtime . NewMissingVersionErr ( string ( originalData ) )
}
// use the target if necessary
obj , err := runtime . UseOrCreateObject ( s . typer , s . creater , * actual , into )
if err != nil {
return nil , actual , err
}
2018-07-25 04:17:02 -04:00
if err := caseSensitiveJsonIterator . Unmarshal ( data , obj ) ; err != nil {
2017-11-20 09:30:52 -05:00
return nil , actual , err
}
return obj , actual , nil
}
// Encode serializes the provided object to the given writer.
func ( s * Serializer ) Encode ( obj runtime . Object , w io . Writer ) error {
if s . yaml {
2018-07-25 04:17:02 -04:00
json , err := caseSensitiveJsonIterator . Marshal ( obj )
2017-11-20 09:30:52 -05:00
if err != nil {
return err
}
data , err := yaml . JSONToYAML ( json )
if err != nil {
return err
}
_ , err = w . Write ( data )
return err
}
if s . pretty {
2018-07-25 04:17:02 -04:00
data , err := caseSensitiveJsonIterator . MarshalIndent ( obj , "" , " " )
2017-11-20 09:30:52 -05:00
if err != nil {
return err
}
_ , err = w . Write ( data )
return err
}
encoder := json . NewEncoder ( w )
return encoder . Encode ( obj )
}
// RecognizesData implements the RecognizingDecoder interface.
func ( s * Serializer ) RecognizesData ( peek io . Reader ) ( ok , unknown bool , err error ) {
if s . yaml {
// we could potentially look for '---'
return false , true , nil
}
_ , _ , ok = utilyaml . GuessJSONStream ( peek , 2048 )
return ok , false , nil
}
// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
var Framer = jsonFramer { }
type jsonFramer struct { }
// NewFrameWriter implements stream framing for this serializer
func ( jsonFramer ) NewFrameWriter ( w io . Writer ) io . Writer {
// we can write JSON objects directly to the writer, because they are self-framing
return w
}
// NewFrameReader implements stream framing for this serializer
func ( jsonFramer ) NewFrameReader ( r io . ReadCloser ) io . ReadCloser {
// we need to extract the JSON chunks of data to pass to Decode()
return framer . NewJSONFramedReader ( r )
}
// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
var YAMLFramer = yamlFramer { }
type yamlFramer struct { }
// NewFrameWriter implements stream framing for this serializer
func ( yamlFramer ) NewFrameWriter ( w io . Writer ) io . Writer {
return yamlFrameWriter { w }
}
// NewFrameReader implements stream framing for this serializer
func ( yamlFramer ) NewFrameReader ( r io . ReadCloser ) io . ReadCloser {
// extract the YAML document chunks directly
return utilyaml . NewDocumentDecoder ( r )
}
type yamlFrameWriter struct {
w io . Writer
}
// Write separates each document with the YAML document separator (`---` followed by line
// break). Writers must write well formed YAML documents (include a final line break).
func ( w yamlFrameWriter ) Write ( data [ ] byte ) ( n int , err error ) {
if _ , err := w . w . Write ( [ ] byte ( "---\n" ) ) ; err != nil {
return 0 , err
}
return w . w . Write ( data )
}