@@ -34,6 +34,149 @@ type Scheme struct {
3434 raw * conversion.Scheme
3535}
3636
37+ var namedSchemes map [string ]* Scheme
38+
39+ // GetScheme returns the scheme with the given name, creating it if necessary.
40+ // Important: You may not modify the returned *Scheme except from init() functions.
41+ func GetScheme (schemeName string ) * Scheme {
42+ if namedSchemes == nil {
43+ namedSchemes = map [string ]* Scheme {}
44+ }
45+ if s , ok := namedSchemes [schemeName ]; ok {
46+ return s
47+ }
48+ s := NewScheme ("" , "" )
49+ namedSchemes [schemeName ] = s
50+ return s
51+ }
52+
53+ // fromScope gets the input version, desired output version, and desired Scheme
54+ // from a conversion.Scope.
55+ func fromScope (s conversion.Scope ) (inVersion , outVersion string , scheme * Scheme ) {
56+ scheme = DefaultScheme
57+ inVersion = s .Meta ()["srcVersion" ].(string )
58+ outVersion = s .Meta ()["destVersion" ].(string )
59+ // If a scheme tag was provided, use it. Look at the struct tag corresponding
60+ // to version "".
61+ if name := s .SrcTag ().Get ("scheme" ); inVersion == "" && name != "" {
62+ scheme = GetScheme (name )
63+ }
64+ if name := s .DestTag ().Get ("scheme" ); outVersion == "" && name != "" {
65+ scheme = GetScheme (name )
66+ }
67+ return inVersion , outVersion , scheme
68+ }
69+
70+ func init () {
71+ // Set up a generic mapping between RawExtension and EmbeddedObject.
72+ DefaultScheme .AddConversionFuncs (
73+ embeddedObjectToRawExtension ,
74+ rawExtensionToEmbeddedObject ,
75+ )
76+ }
77+
78+ // emptyPlugin is used to copy the Kind field to and from plugin objects.
79+ type emptyPlugin struct {
80+ PluginBase `json:",inline" yaml:",inline"`
81+ }
82+
83+ // embeddedObjectToRawExtension does the conversion you would expect from the name, using the information
84+ // given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
85+ // see the comment for RawExtension.
86+ func embeddedObjectToRawExtension (in * EmbeddedObject , out * RawExtension , s conversion.Scope ) error {
87+ if in .Object == nil {
88+ out .RawJSON = []byte ("null" )
89+ return nil
90+ }
91+
92+ // Figure out the type and kind of the output object.
93+ _ , outVersion , scheme := fromScope (s )
94+ _ , kind , err := scheme .raw .ObjectVersionAndKind (in .Object )
95+ if err != nil {
96+ return err
97+ }
98+
99+ // Manufacture an object of this type and kind.
100+ outObj , err := scheme .New (outVersion , kind )
101+ if err != nil {
102+ return err
103+ }
104+
105+ // Manually do the conversion.
106+ err = s .Convert (in .Object , outObj , 0 )
107+ if err != nil {
108+ return err
109+ }
110+
111+ // Copy the kind field into the ouput object.
112+ err = s .Convert (
113+ & emptyPlugin {PluginBase : PluginBase {Kind : kind }},
114+ outObj ,
115+ conversion .SourceToDest | conversion .IgnoreMissingFields | conversion .AllowDifferentFieldTypeNames ,
116+ )
117+ if err != nil {
118+ return err
119+ }
120+ // Because we provide the correct version, EncodeToVersion will not attempt a conversion.
121+ raw , err := scheme .EncodeToVersion (outObj , outVersion )
122+ if err != nil {
123+ // TODO: if this fails, create an Unknown-- maybe some other
124+ // component will understand it.
125+ return err
126+ }
127+ out .RawJSON = raw
128+ return nil
129+ }
130+
131+ // rawExtensionToEmbeddedObject does the conversion you would expect from the name, using the information
132+ // given in conversion.Scope. It's placed in the DefaultScheme as a ConversionFunc to enable plugins;
133+ // see the comment for RawExtension.
134+ func rawExtensionToEmbeddedObject (in * RawExtension , out * EmbeddedObject , s conversion.Scope ) error {
135+ if len (in .RawJSON ) == 4 && string (in .RawJSON ) == "null" {
136+ out .Object = nil
137+ return nil
138+ }
139+ // Figure out the type and kind of the output object.
140+ inVersion , outVersion , scheme := fromScope (s )
141+ _ , kind , err := scheme .raw .DataVersionAndKind (in .RawJSON )
142+ if err != nil {
143+ return err
144+ }
145+
146+ // We have to make this object ourselves because we don't store the version field for
147+ // plugin objects.
148+ inObj , err := scheme .New (inVersion , kind )
149+ if err != nil {
150+ return err
151+ }
152+
153+ err = scheme .DecodeInto (in .RawJSON , inObj )
154+ if err != nil {
155+ return err
156+ }
157+
158+ // Make the desired internal version, and do the conversion.
159+ outObj , err := scheme .New (outVersion , kind )
160+ if err != nil {
161+ return err
162+ }
163+ err = scheme .Convert (inObj , outObj )
164+ if err != nil {
165+ return err
166+ }
167+ // Last step, clear the Kind field; that should always be blank in memory.
168+ err = s .Convert (
169+ & emptyPlugin {PluginBase : PluginBase {Kind : "" }},
170+ outObj ,
171+ conversion .SourceToDest | conversion .IgnoreMissingFields | conversion .AllowDifferentFieldTypeNames ,
172+ )
173+ if err != nil {
174+ return err
175+ }
176+ out .Object = outObj
177+ return nil
178+ }
179+
37180// NewScheme creates a new Scheme. A default scheme is provided and accessible
38181// as the "DefaultScheme" variable.
39182func NewScheme (internalVersion , externalVersion string ) * Scheme {
@@ -54,6 +197,13 @@ func (s *Scheme) AddKnownTypes(version string, types ...Object) {
54197 s .raw .AddKnownTypes (version , interfaces ... )
55198}
56199
200+ // AddKnownTypeWithName is like AddKnownTypes, but it lets you specify what this type should
201+ // be encoded as. Useful for testing when you don't want to make multiple packages to define
202+ // your structs.
203+ func (s * Scheme ) AddKnownTypeWithName (version , kind string , obj Object ) {
204+ s .raw .AddKnownTypeWithName (version , kind , obj )
205+ }
206+
57207// New returns a new API object of the given version ("" for internal
58208// representation) and name, or an error if it hasn't been registered.
59209func (s * Scheme ) New (versionName , typeName string ) (Object , error ) {
@@ -153,6 +303,11 @@ func (s *Scheme) Encode(obj Object) (data []byte, err error) {
153303 return s .raw .Encode (obj )
154304}
155305
306+ // EncodeToVersion is like Encode, but lets you specify the destination version.
307+ func (s * Scheme ) EncodeToVersion (obj Object , destVersion string ) (data []byte , err error ) {
308+ return s .raw .EncodeToVersion (obj , destVersion )
309+ }
310+
156311// enforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value of the
157312// dereferenced pointer, ensuring that it is settable/addressable.
158313// Returns an error if this is not possible.
0 commit comments