blob: 01f56c9871e05785ec79fcc8ce869581fdf71426 [file] [log] [blame]
Zack Williamse940c7a2019-08-21 14:25:39 -07001/*
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 serializer
18
19import (
David Bainbridge86971522019-09-26 22:09:39 +000020 "mime"
21 "strings"
22
Zack Williamse940c7a2019-08-21 14:25:39 -070023 "k8s.io/apimachinery/pkg/runtime"
24 "k8s.io/apimachinery/pkg/runtime/schema"
25 "k8s.io/apimachinery/pkg/runtime/serializer/json"
David Bainbridge86971522019-09-26 22:09:39 +000026 "k8s.io/apimachinery/pkg/runtime/serializer/protobuf"
Zack Williamse940c7a2019-08-21 14:25:39 -070027 "k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
28 "k8s.io/apimachinery/pkg/runtime/serializer/versioning"
29)
30
31// serializerExtensions are for serializers that are conditionally compiled in
32var serializerExtensions = []func(*runtime.Scheme) (serializerType, bool){}
33
34type serializerType struct {
35 AcceptContentTypes []string
36 ContentType string
37 FileExtensions []string
38 // EncodesAsText should be true if this content type can be represented safely in UTF-8
39 EncodesAsText bool
40
41 Serializer runtime.Serializer
42 PrettySerializer runtime.Serializer
43
44 AcceptStreamContentTypes []string
45 StreamContentType string
46
47 Framer runtime.Framer
48 StreamSerializer runtime.Serializer
49}
50
51func newSerializersForScheme(scheme *runtime.Scheme, mf json.MetaFactory) []serializerType {
52 jsonSerializer := json.NewSerializer(mf, scheme, scheme, false)
53 jsonPrettySerializer := json.NewSerializer(mf, scheme, scheme, true)
54 yamlSerializer := json.NewYAMLSerializer(mf, scheme, scheme)
David Bainbridge86971522019-09-26 22:09:39 +000055 serializer := protobuf.NewSerializer(scheme, scheme)
56 raw := protobuf.NewRawSerializer(scheme, scheme)
Zack Williamse940c7a2019-08-21 14:25:39 -070057
58 serializers := []serializerType{
59 {
60 AcceptContentTypes: []string{"application/json"},
61 ContentType: "application/json",
62 FileExtensions: []string{"json"},
63 EncodesAsText: true,
64 Serializer: jsonSerializer,
65 PrettySerializer: jsonPrettySerializer,
66
67 Framer: json.Framer,
68 StreamSerializer: jsonSerializer,
69 },
70 {
71 AcceptContentTypes: []string{"application/yaml"},
72 ContentType: "application/yaml",
73 FileExtensions: []string{"yaml"},
74 EncodesAsText: true,
75 Serializer: yamlSerializer,
76 },
David Bainbridge86971522019-09-26 22:09:39 +000077 {
78 AcceptContentTypes: []string{runtime.ContentTypeProtobuf},
79 ContentType: runtime.ContentTypeProtobuf,
80 FileExtensions: []string{"pb"},
81 Serializer: serializer,
82
83 Framer: protobuf.LengthDelimitedFramer,
84 StreamSerializer: raw,
85 },
Zack Williamse940c7a2019-08-21 14:25:39 -070086 }
87
88 for _, fn := range serializerExtensions {
89 if serializer, ok := fn(scheme); ok {
90 serializers = append(serializers, serializer)
91 }
92 }
93 return serializers
94}
95
96// CodecFactory provides methods for retrieving codecs and serializers for specific
97// versions and content types.
98type CodecFactory struct {
99 scheme *runtime.Scheme
100 serializers []serializerType
101 universal runtime.Decoder
102 accepts []runtime.SerializerInfo
103
104 legacySerializer runtime.Serializer
105}
106
107// NewCodecFactory provides methods for retrieving serializers for the supported wire formats
108// and conversion wrappers to define preferred internal and external versions. In the future,
109// as the internal version is used less, callers may instead use a defaulting serializer and
110// only convert objects which are shared internally (Status, common API machinery).
111// TODO: allow other codecs to be compiled in?
112// TODO: accept a scheme interface
113func NewCodecFactory(scheme *runtime.Scheme) CodecFactory {
114 serializers := newSerializersForScheme(scheme, json.DefaultMetaFactory)
115 return newCodecFactory(scheme, serializers)
116}
117
118// newCodecFactory is a helper for testing that allows a different metafactory to be specified.
119func newCodecFactory(scheme *runtime.Scheme, serializers []serializerType) CodecFactory {
120 decoders := make([]runtime.Decoder, 0, len(serializers))
121 var accepts []runtime.SerializerInfo
122 alreadyAccepted := make(map[string]struct{})
123
124 var legacySerializer runtime.Serializer
125 for _, d := range serializers {
126 decoders = append(decoders, d.Serializer)
127 for _, mediaType := range d.AcceptContentTypes {
128 if _, ok := alreadyAccepted[mediaType]; ok {
129 continue
130 }
131 alreadyAccepted[mediaType] = struct{}{}
132 info := runtime.SerializerInfo{
133 MediaType: d.ContentType,
134 EncodesAsText: d.EncodesAsText,
135 Serializer: d.Serializer,
136 PrettySerializer: d.PrettySerializer,
137 }
David Bainbridge86971522019-09-26 22:09:39 +0000138
139 mediaType, _, err := mime.ParseMediaType(info.MediaType)
140 if err != nil {
141 panic(err)
142 }
143 parts := strings.SplitN(mediaType, "/", 2)
144 info.MediaTypeType = parts[0]
145 info.MediaTypeSubType = parts[1]
146
Zack Williamse940c7a2019-08-21 14:25:39 -0700147 if d.StreamSerializer != nil {
148 info.StreamSerializer = &runtime.StreamSerializerInfo{
149 Serializer: d.StreamSerializer,
150 EncodesAsText: d.EncodesAsText,
151 Framer: d.Framer,
152 }
153 }
154 accepts = append(accepts, info)
155 if mediaType == runtime.ContentTypeJSON {
156 legacySerializer = d.Serializer
157 }
158 }
159 }
160 if legacySerializer == nil {
161 legacySerializer = serializers[0].Serializer
162 }
163
164 return CodecFactory{
165 scheme: scheme,
166 serializers: serializers,
167 universal: recognizer.NewDecoder(decoders...),
168
169 accepts: accepts,
170
171 legacySerializer: legacySerializer,
172 }
173}
174
David Bainbridge86971522019-09-26 22:09:39 +0000175// WithoutConversion returns a NegotiatedSerializer that performs no conversion, even if the
176// caller requests it.
177func (f CodecFactory) WithoutConversion() runtime.NegotiatedSerializer {
178 return WithoutConversionCodecFactory{f}
179}
180
Zack Williamse940c7a2019-08-21 14:25:39 -0700181// SupportedMediaTypes returns the RFC2046 media types that this factory has serializers for.
182func (f CodecFactory) SupportedMediaTypes() []runtime.SerializerInfo {
183 return f.accepts
184}
185
186// LegacyCodec encodes output to a given API versions, and decodes output into the internal form from
187// any recognized source. The returned codec will always encode output to JSON. If a type is not
188// found in the list of versions an error will be returned.
189//
190// This method is deprecated - clients and servers should negotiate a serializer by mime-type and
191// invoke CodecForVersions. Callers that need only to read data should use UniversalDecoder().
192//
193// TODO: make this call exist only in pkg/api, and initialize it with the set of default versions.
194// All other callers will be forced to request a Codec directly.
195func (f CodecFactory) LegacyCodec(version ...schema.GroupVersion) runtime.Codec {
196 return versioning.NewDefaultingCodecForScheme(f.scheme, f.legacySerializer, f.universal, schema.GroupVersions(version), runtime.InternalGroupVersioner)
197}
198
199// UniversalDeserializer can convert any stored data recognized by this factory into a Go object that satisfies
200// runtime.Object. It does not perform conversion. It does not perform defaulting.
201func (f CodecFactory) UniversalDeserializer() runtime.Decoder {
202 return f.universal
203}
204
205// UniversalDecoder returns a runtime.Decoder capable of decoding all known API objects in all known formats. Used
206// by clients that do not need to encode objects but want to deserialize API objects stored on disk. Only decodes
207// objects in groups registered with the scheme. The GroupVersions passed may be used to select alternate
208// versions of objects to return - by default, runtime.APIVersionInternal is used. If any versions are specified,
209// unrecognized groups will be returned in the version they are encoded as (no conversion). This decoder performs
210// defaulting.
211//
212// TODO: the decoder will eventually be removed in favor of dealing with objects in their versioned form
213// TODO: only accept a group versioner
214func (f CodecFactory) UniversalDecoder(versions ...schema.GroupVersion) runtime.Decoder {
215 var versioner runtime.GroupVersioner
216 if len(versions) == 0 {
217 versioner = runtime.InternalGroupVersioner
218 } else {
219 versioner = schema.GroupVersions(versions)
220 }
221 return f.CodecForVersions(nil, f.universal, nil, versioner)
222}
223
224// CodecForVersions creates a codec with the provided serializer. If an object is decoded and its group is not in the list,
225// it will default to runtime.APIVersionInternal. If encode is not specified for an object's group, the object is not
226// converted. If encode or decode are nil, no conversion is performed.
227func (f CodecFactory) CodecForVersions(encoder runtime.Encoder, decoder runtime.Decoder, encode runtime.GroupVersioner, decode runtime.GroupVersioner) runtime.Codec {
228 // TODO: these are for backcompat, remove them in the future
229 if encode == nil {
230 encode = runtime.DisabledGroupVersioner
231 }
232 if decode == nil {
233 decode = runtime.InternalGroupVersioner
234 }
235 return versioning.NewDefaultingCodecForScheme(f.scheme, encoder, decoder, encode, decode)
236}
237
238// DecoderToVersion returns a decoder that targets the provided group version.
239func (f CodecFactory) DecoderToVersion(decoder runtime.Decoder, gv runtime.GroupVersioner) runtime.Decoder {
240 return f.CodecForVersions(nil, decoder, nil, gv)
241}
242
243// EncoderForVersion returns an encoder that targets the provided group version.
244func (f CodecFactory) EncoderForVersion(encoder runtime.Encoder, gv runtime.GroupVersioner) runtime.Encoder {
245 return f.CodecForVersions(encoder, nil, gv, nil)
246}
247
David Bainbridge86971522019-09-26 22:09:39 +0000248// WithoutConversionCodecFactory is a CodecFactory that will explicitly ignore requests to perform conversion.
249// This wrapper is used while code migrates away from using conversion (such as external clients) and in the future
250// will be unnecessary when we change the signature of NegotiatedSerializer.
251type WithoutConversionCodecFactory struct {
Zack Williamse940c7a2019-08-21 14:25:39 -0700252 CodecFactory
253}
254
David Bainbridge86971522019-09-26 22:09:39 +0000255// EncoderForVersion returns an encoder that does not do conversion, but does set the group version kind of the object
256// when serialized.
257func (f WithoutConversionCodecFactory) EncoderForVersion(serializer runtime.Encoder, version runtime.GroupVersioner) runtime.Encoder {
258 return runtime.WithVersionEncoder{
Zack Williamse940c7a2019-08-21 14:25:39 -0700259 Version: version,
260 Encoder: serializer,
261 ObjectTyper: f.CodecFactory.scheme,
262 }
263}
264
David Bainbridge86971522019-09-26 22:09:39 +0000265// DecoderToVersion returns an decoder that does not do conversion.
266func (f WithoutConversionCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder {
267 return runtime.WithoutVersionDecoder{
Zack Williamse940c7a2019-08-21 14:25:39 -0700268 Decoder: serializer,
269 }
270}
David Bainbridge86971522019-09-26 22:09:39 +0000271
272// DirectCodecFactory was renamed to WithoutConversionCodecFactory in 1.15.
273// TODO: remove in 1.16.
274type DirectCodecFactory = WithoutConversionCodecFactory