Skip to content
This repository was archived by the owner on Feb 8, 2021. It is now read-only.

Commit 52d2c22

Browse files
committed
pkg/runtime now has a well defined api pluggability model.
1 parent 71e5471 commit 52d2c22

File tree

5 files changed

+532
-88
lines changed

5 files changed

+532
-88
lines changed

pkg/runtime/extension.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
Copyright 2014 Google Inc. All rights reserved.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package runtime
18+
19+
import (
20+
"gopkg.in/v1/yaml"
21+
)
22+
23+
func (re *RawExtension) UnmarshalJSON(in []byte) error {
24+
re.RawJSON = in
25+
return nil
26+
}
27+
28+
func (re *RawExtension) MarshalJSON() ([]byte, error) {
29+
return re.RawJSON, nil
30+
}
31+
32+
// SetYAML implements the yaml.Setter interface.
33+
func (re *RawExtension) SetYAML(tag string, value interface{}) bool {
34+
if value == nil {
35+
re.RawJSON = []byte("null")
36+
return true
37+
}
38+
// Why does the yaml package send value as a map[interface{}]interface{}?
39+
// It's especially frustrating because encoding/json does the right thing
40+
// by giving a []byte. So here we do the embarrasing thing of re-encode and
41+
// de-encode the right way.
42+
// TODO: Write a version of Decode that uses reflect to turn this value
43+
// into an API object.
44+
b, err := yaml.Marshal(value)
45+
if err != nil {
46+
panic("yaml can't reverse its own object")
47+
}
48+
re.RawJSON = b
49+
return true
50+
}
51+
52+
// GetYAML implements the yaml.Getter interface.
53+
func (re *RawExtension) GetYAML() (tag string, value interface{}) {
54+
return tag, re.RawJSON
55+
}

pkg/runtime/helper_test.go

Lines changed: 0 additions & 81 deletions
This file was deleted.
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
39182
func 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.
59209
func (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

Comments
 (0)