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