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 "github.com/ghodss/yaml" 26 jsoniter "github.com/json-iterator/go" 27 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/runtime/schema" 30 "k8s.io/apimachinery/pkg/runtime/serializer/recognizer" 31 "k8s.io/apimachinery/pkg/util/framer" 32 utilyaml "k8s.io/apimachinery/pkg/util/yaml" 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. 37func NewSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper, pretty bool) *Serializer { 38 return &Serializer{ 39 meta: meta, 40 creater: creater, 41 typer: typer, 42 yaml: false, 43 pretty: pretty, 44 } 45} 46 47// NewYAMLSerializer creates a YAML serializer that handles encoding versioned objects into the proper YAML form. If typer 48// is not nil, the object has the group, version, and kind fields set. This serializer supports only the subset of YAML that 49// matches JSON, and will error if constructs are used that do not serialize to JSON. 50func NewYAMLSerializer(meta MetaFactory, creater runtime.ObjectCreater, typer runtime.ObjectTyper) *Serializer { 51 return &Serializer{ 52 meta: meta, 53 creater: creater, 54 typer: typer, 55 yaml: true, 56 } 57} 58 59type Serializer struct { 60 meta MetaFactory 61 creater runtime.ObjectCreater 62 typer runtime.ObjectTyper 63 yaml bool 64 pretty bool 65} 66 67// Serializer implements Serializer 68var _ runtime.Serializer = &Serializer{} 69var _ recognizer.RecognizingDecoder = &Serializer{} 70 71func init() { 72 // Force jsoniter to decode number to interface{} via ints, if possible. 73 decodeNumberAsInt64IfPossible := func(ptr unsafe.Pointer, iter *jsoniter.Iterator) { 74 switch iter.WhatIsNext() { 75 case jsoniter.NumberValue: 76 var number json.Number 77 iter.ReadVal(&number) 78 i64, err := strconv.ParseInt(string(number), 10, 64) 79 if err == nil { 80 *(*interface{})(ptr) = i64 81 return 82 } 83 f64, err := strconv.ParseFloat(string(number), 64) 84 if err == nil { 85 *(*interface{})(ptr) = f64 86 return 87 } 88 // Not much we can do here. 89 default: 90 *(*interface{})(ptr) = iter.Read() 91 } 92 } 93 jsoniter.RegisterTypeDecoderFunc("interface {}", decodeNumberAsInt64IfPossible) 94} 95 96// gvkWithDefaults returns group kind and version defaulting from provided default 97func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind { 98 if len(actual.Kind) == 0 { 99 actual.Kind = defaultGVK.Kind 100 } 101 if len(actual.Version) == 0 && len(actual.Group) == 0 { 102 actual.Group = defaultGVK.Group 103 actual.Version = defaultGVK.Version 104 } 105 if len(actual.Version) == 0 && actual.Group == defaultGVK.Group { 106 actual.Version = defaultGVK.Version 107 } 108 return actual 109} 110 111// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then 112// load that data into an object matching the desired schema kind or the provided into. 113// If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed. 114// If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling. 115// 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. 116// If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk) 117// On success or most errors, the method will return the calculated schema kind. 118// The gvk calculate priority will be originalData > default gvk > into 119func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) { 120 if versioned, ok := into.(*runtime.VersionedObjects); ok { 121 into = versioned.Last() 122 obj, actual, err := s.Decode(originalData, gvk, into) 123 if err != nil { 124 return nil, actual, err 125 } 126 versioned.Objects = []runtime.Object{obj} 127 return versioned, actual, nil 128 } 129 130 data := originalData 131 if s.yaml { 132 altered, err := yaml.YAMLToJSON(data) 133 if err != nil { 134 return nil, nil, err 135 } 136 data = altered 137 } 138 139 actual, err := s.meta.Interpret(data) 140 if err != nil { 141 return nil, nil, err 142 } 143 144 if gvk != nil { 145 *actual = gvkWithDefaults(*actual, *gvk) 146 } 147 148 if unk, ok := into.(*runtime.Unknown); ok && unk != nil { 149 unk.Raw = originalData 150 unk.ContentType = runtime.ContentTypeJSON 151 unk.GetObjectKind().SetGroupVersionKind(*actual) 152 return unk, actual, nil 153 } 154 155 if into != nil { 156 _, isUnstructured := into.(runtime.Unstructured) 157 types, _, err := s.typer.ObjectKinds(into) 158 switch { 159 case runtime.IsNotRegisteredError(err), isUnstructured: 160 if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, into); err != nil { 161 return nil, actual, err 162 } 163 return into, actual, nil 164 case err != nil: 165 return nil, actual, err 166 default: 167 *actual = gvkWithDefaults(*actual, types[0]) 168 } 169 } 170 171 if len(actual.Kind) == 0 { 172 return nil, actual, runtime.NewMissingKindErr(string(originalData)) 173 } 174 if len(actual.Version) == 0 { 175 return nil, actual, runtime.NewMissingVersionErr(string(originalData)) 176 } 177 178 // use the target if necessary 179 obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into) 180 if err != nil { 181 return nil, actual, err 182 } 183 184 if err := jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal(data, obj); err != nil { 185 return nil, actual, err 186 } 187 return obj, actual, nil 188} 189 190// Encode serializes the provided object to the given writer. 191func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error { 192 if s.yaml { 193 json, err := jsoniter.ConfigCompatibleWithStandardLibrary.Marshal(obj) 194 if err != nil { 195 return err 196 } 197 data, err := yaml.JSONToYAML(json) 198 if err != nil { 199 return err 200 } 201 _, err = w.Write(data) 202 return err 203 } 204 205 if s.pretty { 206 data, err := jsoniter.ConfigCompatibleWithStandardLibrary.MarshalIndent(obj, "", " ") 207 if err != nil { 208 return err 209 } 210 _, err = w.Write(data) 211 return err 212 } 213 encoder := json.NewEncoder(w) 214 return encoder.Encode(obj) 215} 216 217// RecognizesData implements the RecognizingDecoder interface. 218func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) { 219 if s.yaml { 220 // we could potentially look for '---' 221 return false, true, nil 222 } 223 _, _, ok = utilyaml.GuessJSONStream(peek, 2048) 224 return ok, false, nil 225} 226 227// Framer is the default JSON framing behavior, with newlines delimiting individual objects. 228var Framer = jsonFramer{} 229 230type jsonFramer struct{} 231 232// NewFrameWriter implements stream framing for this serializer 233func (jsonFramer) NewFrameWriter(w io.Writer) io.Writer { 234 // we can write JSON objects directly to the writer, because they are self-framing 235 return w 236} 237 238// NewFrameReader implements stream framing for this serializer 239func (jsonFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { 240 // we need to extract the JSON chunks of data to pass to Decode() 241 return framer.NewJSONFramedReader(r) 242} 243 244// Framer is the default JSON framing behavior, with newlines delimiting individual objects. 245var YAMLFramer = yamlFramer{} 246 247type yamlFramer struct{} 248 249// NewFrameWriter implements stream framing for this serializer 250func (yamlFramer) NewFrameWriter(w io.Writer) io.Writer { 251 return yamlFrameWriter{w} 252} 253 254// NewFrameReader implements stream framing for this serializer 255func (yamlFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { 256 // extract the YAML document chunks directly 257 return utilyaml.NewDocumentDecoder(r) 258} 259 260type yamlFrameWriter struct { 261 w io.Writer 262} 263 264// Write separates each document with the YAML document separator (`---` followed by line 265// break). Writers must write well formed YAML documents (include a final line break). 266func (w yamlFrameWriter) Write(data []byte) (n int, err error) { 267 if _, err := w.w.Write([]byte("---\n")); err != nil { 268 return 0, err 269 } 270 return w.w.Write(data) 271} 272