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 serializer 18 19import ( 20 "mime" 21 "strings" 22 23 "k8s.io/apimachinery/pkg/runtime" 24 "k8s.io/apimachinery/pkg/runtime/schema" 25 "k8s.io/apimachinery/pkg/runtime/serializer/json" 26 "k8s.io/apimachinery/pkg/runtime/serializer/protobuf" 27 "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) 55 serializer := protobuf.NewSerializer(scheme, scheme) 56 raw := protobuf.NewRawSerializer(scheme, scheme) 57 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 }, 77 { 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 }, 86 } 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 } 138 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 147 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 175// 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 181// 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 248// 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 { 252 CodecFactory 253} 254 255// 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{ 259 Version: version, 260 Encoder: serializer, 261 ObjectTyper: f.CodecFactory.scheme, 262 } 263} 264 265// DecoderToVersion returns an decoder that does not do conversion. 266func (f WithoutConversionCodecFactory) DecoderToVersion(serializer runtime.Decoder, _ runtime.GroupVersioner) runtime.Decoder { 267 return runtime.WithoutVersionDecoder{ 268 Decoder: serializer, 269 } 270} 271 272// DirectCodecFactory was renamed to WithoutConversionCodecFactory in 1.15. 273// TODO: remove in 1.16. 274type DirectCodecFactory = WithoutConversionCodecFactory 275