1/* 2Copyright 2014 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package json 18 19import ( 20 "encoding/json" 21 "io" 22 "strconv" 23 "unsafe" 24 25 jsoniter "github.com/json-iterator/go" 26 "github.com/modern-go/reflect2" 27 "sigs.k8s.io/yaml" 28 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/runtime/serializer/recognizer" 32 "k8s.io/apimachinery/pkg/util/framer" 33 utilyaml "k8s.io/apimachinery/pkg/util/yaml" 34) 35 36// NewSerializer creates a JSON serializer that handles encoding versioned objects into the proper JSON form. If typer 37// is not nil, the object has the group, version, and kind fields set. 38// Deprecated: use NewSerializerWithOptions instead. 39func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, pretty bool) *Serializer { 40 return NewSerializerWithOptions(meta, creater, typer, SerializerOptions{false, pretty, false}) 41} 42 43// NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer 44// is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that 45// matches JSON, and will error if constructs are used that do not serialize to JSON. 46// Deprecated: use NewSerializerWithOptions instead. 47func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper) *Serializer { 48 return NewSerializerWithOptions(meta, creater, typer, SerializerOptions{true, false, false}) 49} 50 51// NewSerializerWithOptions creates a JSON/YAML serializer that handles encoding versioned objects into the proper JSON/YAML 52// form. If typer is not nil, the object has the group, version, and kind fields set. Options are copied into the Serializer 53// and are immutable. 54func NewSerializerWithOptions(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, options SerializerOptions) *Serializer { 55 return &Serializer{ 56 meta: meta, 57 creater: creater, 58 typer: typer, 59 options: options, 60 } 61} 62 63// SerializerOptions holds the options which are used to configure a JSON/YAML serializer. 64// example: 65// (1) To configure a JSON serializer, set `Yaml` to `false`. 66// (2) To configure a YAML serializer, set `Yaml` to `true`. 67// (3) To configure a strict serializer that can return strictDecodingError, set `Strict` to `true`. 68type SerializerOptions struct { 69 // Yaml: configures the Serializer to work with JSON(false) or YAML(true). 70 // When `Yaml` is enabled, this serializer only supports the subset of YAML that 71 // matches JSON, and will error if constructs are used that do not serialize to JSON. 72 Yaml bool 73 74 // Pretty: configures a JSON enabled Serializer(`Yaml: false`) to produce human-readable output. 75 // This option is silently ignored when `Yaml` is `true`. 76 Pretty bool 77 78 // Strict: configures the Serializer to return strictDecodingError's when duplicate fields are present decoding JSON or YAML. 79 // Note that enabling this option is not as performant as the non-strict variant, and should not be used in fast paths. 80 Strict bool 81} 82 83type Serializer struct { 84 meta MetaFactory 85 options SerializerOptions 86 creater runtime.ObjectCreater 87 typer runtime.ObjectTyper 88} 89 90// Serializer implements Serializer 91var _ runtime.Serializer = &Serializer{} 92var _ recognizer.RecognizingDecoder = &Serializer{} 93 94type customNumberExtension struct { 95 jsoniter.DummyExtension 96} 97 98func (cne *customNumberExtension) CreateDecoder(typ reflect2.Type) jsoniter.ValDecoder { 99 if typ.String() == "interface {}" { 100 return customNumberDecoder{} 101 } 102 return nil 103} 104 105type customNumberDecoder struct { 106} 107 108func (customNumberDecoder) Decode(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 109 switch iter.WhatIsNext() { 110 case jsoniter.NumberValue: 111 var number jsoniter.Number 112 iter.ReadVal(&number) 113 i64, err := strconv.ParseInt(string(number), 10, 64) 114 if err == nil { 115 *(*interface{})(ptr) = i64 116 return 117 } 118 f64, err := strconv.ParseFloat(string(number), 64) 119 if err == nil { 120 *(*interface{})(ptr) = f64 121 return 122 } 123 iter.ReportError("DecodeNumber", err.Error()) 124 default: 125 // init depth, if needed 126 if iter.Attachment == nil { 127 iter.Attachment = int(1) 128 } 129 130 // remember current depth 131 originalAttachment := iter.Attachment 132 133 // increment depth before descending 134 if i, ok := iter.Attachment.(int); ok { 135 iter.Attachment = i + 1 136 if i > 10000 { 137 iter.ReportError("parse", "exceeded max depth") 138 return 139 } 140 } 141 142 *(*interface{})(ptr) = iter.Read() 143 144 // restore current depth 145 iter.Attachment = originalAttachment 146 } 147} 148 149// CaseSensitiveJsonIterator returns a jsoniterator API that's configured to be 150// case-sensitive when unmarshalling, and otherwise compatible with 151// the encoding/json standard library. 152func CaseSensitiveJsonIterator() jsoniter.API { 153 config := jsoniter.Config{ 154 EscapeHTML: true, 155 SortMapKeys: true, 156 ValidateJsonRawMessage: true, 157 CaseSensitive: true, 158 }.Froze() 159 // Force jsoniter to decode number to interface{} via int64/float64, if possible. 160 config.RegisterExtension(&customNumberExtension{}) 161 return config 162} 163 164// StrictCaseSensitiveJsonIterator returns a jsoniterator API that's configured to be 165// case-sensitive, but also disallows unknown fields when unmarshalling. It is compatible with 166// the encoding/json standard library. 167func StrictCaseSensitiveJsonIterator() jsoniter.API { 168 config := jsoniter.Config{ 169 EscapeHTML: true, 170 SortMapKeys: true, 171 ValidateJsonRawMessage: true, 172 CaseSensitive: true, 173 DisallowUnknownFields: true, 174 }.Froze() 175 // Force jsoniter to decode number to interface{} via int64/float64, if possible. 176 config.RegisterExtension(&customNumberExtension{}) 177 return config 178} 179 180// Private copies of jsoniter to try to shield against possible mutations 181// from outside. Still does not protect from package level jsoniter.Register*() functions - someone calling them 182// in some other library will mess with every usage of the jsoniter library in the whole program. 183// See https://github.com/json-iterator/go/issues/265 184var caseSensitiveJsonIterator = CaseSensitiveJsonIterator() 185var strictCaseSensitiveJsonIterator = StrictCaseSensitiveJsonIterator() 186 187// gvkWithDefaults returns group kind and version defaulting from provided default 188func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind { 189 if len(actual.Kind) == 0 { 190 actual.Kind = defaultGVK.Kind 191 } 192 if len(actual.Version) == 0 && len(actual.Group) == 0 { 193 actual.Group = defaultGVK.Group 194 actual.Version = defaultGVK.Version 195 } 196 if len(actual.Version) == 0 && actual.Group == defaultGVK.Group { 197 actual.Version = defaultGVK.Version 198 } 199 return actual 200} 201 202// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then 203// load that data into an object matching the desired schema kind or the provided into. 204// If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed. 205// If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling. 206// 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. 207// If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk) 208// On success or most errors, the method will return the calculated schema kind. 209// The gvk calculate priority will be originalData > default gvk > into 210func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { 211 if versioned, ok := into.(*runtime.VersionedObjects); ok { 212 into = versioned.Last() 213 obj, actual, err := s.Decode(originalData, gvk, into) 214 if err != nil { 215 return nil, actual, err 216 } 217 versioned.Objects = []runtime.Object{obj} 218 return versioned, actual, nil 219 } 220 221 data := originalData 222 if s.options.Yaml { 223 altered, err := yaml.YAMLToJSON(data) 224 if err != nil { 225 return nil, nil, err 226 } 227 data = altered 228 } 229 230 actual, err := s.meta.Interpret(data) 231 if err != nil { 232 return nil, nil, err 233 } 234 235 if gvk != nil { 236 *actual = gvkWithDefaults(*actual, *gvk) 237 } 238 239 if unk, ok := into.(*runtime.Unknown); ok && unk != nil { 240 unk.Raw = originalData 241 unk.ContentType = runtime.ContentTypeJSON 242 unk.GetObjectKind().SetGroupVersionKind(*actual) 243 return unk, actual, nil 244 } 245 246 if into != nil { 247 _, isUnstructured := into.(runtime.Unstructured) 248 types, _, err := s.typer.ObjectKinds(into) 249 switch { 250 case runtime.IsNotRegisteredError(err), isUnstructured: 251 if err := caseSensitiveJsonIterator.Unmarshal(data, into); err != nil { 252 return nil, actual, err 253 } 254 return into, actual, nil 255 case err != nil: 256 return nil, actual, err 257 default: 258 *actual = gvkWithDefaults(*actual, types[0]) 259 } 260 } 261 262 if len(actual.Kind) == 0 { 263 return nil, actual, runtime.NewMissingKindErr(string(originalData)) 264 } 265 if len(actual.Version) == 0 { 266 return nil, actual, runtime.NewMissingVersionErr(string(originalData)) 267 } 268 269 // use the target if necessary 270 obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into) 271 if err != nil { 272 return nil, actual, err 273 } 274 275 if err := caseSensitiveJsonIterator.Unmarshal(data, obj); err != nil { 276 return nil, actual, err 277 } 278 279 // If the deserializer is non-strict, return successfully here. 280 if !s.options.Strict { 281 return obj, actual, nil 282 } 283 284 // In strict mode pass the data trough the YAMLToJSONStrict converter. 285 // This is done to catch duplicate fields regardless of encoding (JSON or YAML). For JSON data, 286 // the output would equal the input, unless there is a parsing error such as duplicate fields. 287 // As we know this was successful in the non-strict case, the only error that may be returned here 288 // is because of the newly-added strictness. hence we know we can return the typed strictDecoderError 289 // the actual error is that the object contains duplicate fields. 290 altered, err := yaml.YAMLToJSONStrict(originalData) 291 if err != nil { 292 return nil, actual, runtime.NewStrictDecodingError(err.Error(), string(originalData)) 293 } 294 // As performance is not an issue for now for the strict deserializer (one has regardless to do 295 // the unmarshal twice), we take the sanitized, altered data that is guaranteed to have no duplicated 296 // fields, and unmarshal this into a copy of the already-populated obj. Any error that occurs here is 297 // due to that a matching field doesn't exist in the object. hence we can return a typed strictDecoderError, 298 // the actual error is that the object contains unknown field. 299 strictObj := obj.DeepCopyObject() 300 if err := strictCaseSensitiveJsonIterator.Unmarshal(altered, strictObj); err != nil { 301 return nil, actual, runtime.NewStrictDecodingError(err.Error(), string(originalData)) 302 } 303 // Always return the same object as the non-strict serializer to avoid any deviations. 304 return obj, actual, nil 305} 306 307// Encode serializes the provided object to the given writer. 308func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error { 309 if s.options.Yaml { 310 json, err := caseSensitiveJsonIterator.Marshal(obj) 311 if err != nil { 312 return err 313 } 314 data, err := yaml.JSONToYAML(json) 315 if err != nil { 316 return err 317 } 318 _, err = w.Write(data) 319 return err 320 } 321 322 if s.options.Pretty { 323 data, err := caseSensitiveJsonIterator.MarshalIndent(obj, "", " ") 324 if err != nil { 325 return err 326 } 327 _, err = w.Write(data) 328 return err 329 } 330 encoder := json.NewEncoder(w) 331 return encoder.Encode(obj) 332} 333 334// RecognizesData implements the RecognizingDecoder interface. 335func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) { 336 if s.options.Yaml { 337 // we could potentially look for '---' 338 return false, true, nil 339 } 340 _, _, ok = utilyaml.GuessJSONStream(peek, 2048) 341 return ok, false, nil 342} 343 344// Framer is the default JSON framing behavior, with newlines delimiting individual objects. 345var Framer = jsonFramer{} 346 347type jsonFramer struct{} 348 349// NewFrameWriter implements stream framing for this serializer 350func (jsonFramer) NewFrameWriter(w io.Writer) io.Writer { 351 // we can write JSON objects directly to the writer, because they are self-framing 352 return w 353} 354 355// NewFrameReader implements stream framing for this serializer 356func (jsonFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { 357 // we need to extract the JSON chunks of data to pass to Decode() 358 return framer.NewJSONFramedReader(r) 359} 360 361// YAMLFramer is the default JSON framing behavior, with newlines delimiting individual objects. 362var YAMLFramer = yamlFramer{} 363 364type yamlFramer struct{} 365 366// NewFrameWriter implements stream framing for this serializer 367func (yamlFramer) NewFrameWriter(w io.Writer) io.Writer { 368 return yamlFrameWriter{w} 369} 370 371// NewFrameReader implements stream framing for this serializer 372func (yamlFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { 373 // extract the YAML document chunks directly 374 return utilyaml.NewDocumentDecoder(r) 375} 376 377type yamlFrameWriter struct { 378 w io.Writer 379} 380 381// Write separates each document with the YAML document separator (`---` followed by line 382// break). Writers must write well formed YAML documents (include a final line break). 383func (w yamlFrameWriter) Write(data []byte) (n int, err error) { 384 if _, err := w.w.Write([]byte("---\n")); err != nil { 385 return 0, err 386 } 387 return w.w.Write(data) 388} 389