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// CaseSensitiveJsonIterator returns a jsoniterator API that's configured to be
97// case-sensitive when unmarshalling, and otherwise compatible with
98// the encoding/json standard library.
99func CaseSensitiveJsonIterator() jsoniter.API {
100	return jsoniter.Config{
101		EscapeHTML:             true,
102		SortMapKeys:            true,
103		ValidateJsonRawMessage: true,
104		CaseSensitive:          true,
105	}.Froze()
106}
107
108var caseSensitiveJsonIterator = CaseSensitiveJsonIterator()
109
110// gvkWithDefaults returns group kind and version defaulting from provided default
111func gvkWithDefaults(actual, defaultGVK schema.GroupVersionKind) schema.GroupVersionKind {
112	if len(actual.Kind) == 0 {
113		actual.Kind = defaultGVK.Kind
114	}
115	if len(actual.Version) == 0 && len(actual.Group) == 0 {
116		actual.Group = defaultGVK.Group
117		actual.Version = defaultGVK.Version
118	}
119	if len(actual.Version) == 0 && actual.Group == defaultGVK.Group {
120		actual.Version = defaultGVK.Version
121	}
122	return actual
123}
124
125// Decode attempts to convert the provided data into YAML or JSON, extract the stored schema kind, apply the provided default gvk, and then
126// load that data into an object matching the desired schema kind or the provided into.
127// If into is *runtime.Unknown, the raw data will be extracted and no decoding will be performed.
128// If into is not registered with the typer, then the object will be straight decoded using normal JSON/YAML unmarshalling.
129// 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.
130// If into is nil or data's gvk different from into's gvk, it will generate a new Object with ObjectCreater.New(gvk)
131// On success or most errors, the method will return the calculated schema kind.
132// The gvk calculate priority will be originalData > default gvk > into
133func (s *Serializer) Decode(originalData []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
134	if versioned, ok := into.(*runtime.VersionedObjects); ok {
135		into = versioned.Last()
136		obj, actual, err := s.Decode(originalData, gvk, into)
137		if err != nil {
138			return nil, actual, err
139		}
140		versioned.Objects = []runtime.Object{obj}
141		return versioned, actual, nil
142	}
143
144	data := originalData
145	if s.yaml {
146		altered, err := yaml.YAMLToJSON(data)
147		if err != nil {
148			return nil, nil, err
149		}
150		data = altered
151	}
152
153	actual, err := s.meta.Interpret(data)
154	if err != nil {
155		return nil, nil, err
156	}
157
158	if gvk != nil {
159		*actual = gvkWithDefaults(*actual, *gvk)
160	}
161
162	if unk, ok := into.(*runtime.Unknown); ok && unk != nil {
163		unk.Raw = originalData
164		unk.ContentType = runtime.ContentTypeJSON
165		unk.GetObjectKind().SetGroupVersionKind(*actual)
166		return unk, actual, nil
167	}
168
169	if into != nil {
170		_, isUnstructured := into.(runtime.Unstructured)
171		types, _, err := s.typer.ObjectKinds(into)
172		switch {
173		case runtime.IsNotRegisteredError(err), isUnstructured:
174			if err := caseSensitiveJsonIterator.Unmarshal(data, into); err != nil {
175				return nil, actual, err
176			}
177			return into, actual, nil
178		case err != nil:
179			return nil, actual, err
180		default:
181			*actual = gvkWithDefaults(*actual, types[0])
182		}
183	}
184
185	if len(actual.Kind) == 0 {
186		return nil, actual, runtime.NewMissingKindErr(string(originalData))
187	}
188	if len(actual.Version) == 0 {
189		return nil, actual, runtime.NewMissingVersionErr(string(originalData))
190	}
191
192	// use the target if necessary
193	obj, err := runtime.UseOrCreateObject(s.typer, s.creater, *actual, into)
194	if err != nil {
195		return nil, actual, err
196	}
197
198	if err := caseSensitiveJsonIterator.Unmarshal(data, obj); err != nil {
199		return nil, actual, err
200	}
201	return obj, actual, nil
202}
203
204// Encode serializes the provided object to the given writer.
205func (s *Serializer) Encode(obj runtime.Object, w io.Writer) error {
206	if s.yaml {
207		json, err := caseSensitiveJsonIterator.Marshal(obj)
208		if err != nil {
209			return err
210		}
211		data, err := yaml.JSONToYAML(json)
212		if err != nil {
213			return err
214		}
215		_, err = w.Write(data)
216		return err
217	}
218
219	if s.pretty {
220		data, err := caseSensitiveJsonIterator.MarshalIndent(obj, "", "  ")
221		if err != nil {
222			return err
223		}
224		_, err = w.Write(data)
225		return err
226	}
227	encoder := json.NewEncoder(w)
228	return encoder.Encode(obj)
229}
230
231// RecognizesData implements the RecognizingDecoder interface.
232func (s *Serializer) RecognizesData(peek io.Reader) (ok, unknown bool, err error) {
233	if s.yaml {
234		// we could potentially look for '---'
235		return false, true, nil
236	}
237	_, _, ok = utilyaml.GuessJSONStream(peek, 2048)
238	return ok, false, nil
239}
240
241// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
242var Framer = jsonFramer{}
243
244type jsonFramer struct{}
245
246// NewFrameWriter implements stream framing for this serializer
247func (jsonFramer) NewFrameWriter(w io.Writer) io.Writer {
248	// we can write JSON objects directly to the writer, because they are self-framing
249	return w
250}
251
252// NewFrameReader implements stream framing for this serializer
253func (jsonFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser {
254	// we need to extract the JSON chunks of data to pass to Decode()
255	return framer.NewJSONFramedReader(r)
256}
257
258// Framer is the default JSON framing behavior, with newlines delimiting individual objects.
259var YAMLFramer = yamlFramer{}
260
261type yamlFramer struct{}
262
263// NewFrameWriter implements stream framing for this serializer
264func (yamlFramer) NewFrameWriter(w io.Writer) io.Writer {
265	return yamlFrameWriter{w}
266}
267
268// NewFrameReader implements stream framing for this serializer
269func (yamlFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser {
270	// extract the YAML document chunks directly
271	return utilyaml.NewDocumentDecoder(r)
272}
273
274type yamlFrameWriter struct {
275	w io.Writer
276}
277
278// Write separates each document with the YAML document separator (`---` followed by line
279// break). Writers must write well formed YAML documents (include a final line break).
280func (w yamlFrameWriter) Write(data []byte) (n int, err error) {
281	if _, err := w.w.Write([]byte("---\n")); err != nil {
282		return 0, err
283	}
284	return w.w.Write(data)
285}
286