blob: 33f11eb10d64e982d6f2b7f82bd5aaead4c23f3a [file] [log] [blame]
Scott Bakere7144bc2019-10-01 14:16:47 -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 runtime
18
19import (
20 "fmt"
21 "io"
22 "reflect"
23
24 "k8s.io/apimachinery/pkg/conversion"
25 "k8s.io/apimachinery/pkg/runtime/schema"
26 "k8s.io/apimachinery/pkg/util/errors"
27)
28
29// unsafeObjectConvertor implements ObjectConvertor using the unsafe conversion path.
30type unsafeObjectConvertor struct {
31 *Scheme
32}
33
34var _ ObjectConvertor = unsafeObjectConvertor{}
35
36// ConvertToVersion converts in to the provided outVersion without copying the input first, which
37// is only safe if the output object is not mutated or reused.
38func (c unsafeObjectConvertor) ConvertToVersion(in Object, outVersion GroupVersioner) (Object, error) {
39 return c.Scheme.UnsafeConvertToVersion(in, outVersion)
40}
41
42// UnsafeObjectConvertor performs object conversion without copying the object structure,
43// for use when the converted object will not be reused or mutated. Primarily for use within
44// versioned codecs, which use the external object for serialization but do not return it.
45func UnsafeObjectConvertor(scheme *Scheme) ObjectConvertor {
46 return unsafeObjectConvertor{scheme}
47}
48
49// SetField puts the value of src, into fieldName, which must be a member of v.
50// The value of src must be assignable to the field.
51func SetField(src interface{}, v reflect.Value, fieldName string) error {
52 field := v.FieldByName(fieldName)
53 if !field.IsValid() {
54 return fmt.Errorf("couldn't find %v field in %#v", fieldName, v.Interface())
55 }
56 srcValue := reflect.ValueOf(src)
57 if srcValue.Type().AssignableTo(field.Type()) {
58 field.Set(srcValue)
59 return nil
60 }
61 if srcValue.Type().ConvertibleTo(field.Type()) {
62 field.Set(srcValue.Convert(field.Type()))
63 return nil
64 }
65 return fmt.Errorf("couldn't assign/convert %v to %v", srcValue.Type(), field.Type())
66}
67
68// Field puts the value of fieldName, which must be a member of v, into dest,
69// which must be a variable to which this field's value can be assigned.
70func Field(v reflect.Value, fieldName string, dest interface{}) error {
71 field := v.FieldByName(fieldName)
72 if !field.IsValid() {
73 return fmt.Errorf("couldn't find %v field in %#v", fieldName, v.Interface())
74 }
75 destValue, err := conversion.EnforcePtr(dest)
76 if err != nil {
77 return err
78 }
79 if field.Type().AssignableTo(destValue.Type()) {
80 destValue.Set(field)
81 return nil
82 }
83 if field.Type().ConvertibleTo(destValue.Type()) {
84 destValue.Set(field.Convert(destValue.Type()))
85 return nil
86 }
87 return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), destValue.Type())
88}
89
90// FieldPtr puts the address of fieldName, which must be a member of v,
91// into dest, which must be an address of a variable to which this field's
92// address can be assigned.
93func FieldPtr(v reflect.Value, fieldName string, dest interface{}) error {
94 field := v.FieldByName(fieldName)
95 if !field.IsValid() {
96 return fmt.Errorf("couldn't find %v field in %#v", fieldName, v.Interface())
97 }
98 v, err := conversion.EnforcePtr(dest)
99 if err != nil {
100 return err
101 }
102 field = field.Addr()
103 if field.Type().AssignableTo(v.Type()) {
104 v.Set(field)
105 return nil
106 }
107 if field.Type().ConvertibleTo(v.Type()) {
108 v.Set(field.Convert(v.Type()))
109 return nil
110 }
111 return fmt.Errorf("couldn't assign/convert %v to %v", field.Type(), v.Type())
112}
113
114// EncodeList ensures that each object in an array is converted to a Unknown{} in serialized form.
115// TODO: accept a content type.
116func EncodeList(e Encoder, objects []Object) error {
117 var errs []error
118 for i := range objects {
119 data, err := Encode(e, objects[i])
120 if err != nil {
121 errs = append(errs, err)
122 continue
123 }
124 // TODO: Set ContentEncoding and ContentType.
125 objects[i] = &Unknown{Raw: data}
126 }
127 return errors.NewAggregate(errs)
128}
129
130func decodeListItem(obj *Unknown, decoders []Decoder) (Object, error) {
131 for _, decoder := range decoders {
132 // TODO: Decode based on ContentType.
133 obj, err := Decode(decoder, obj.Raw)
134 if err != nil {
135 if IsNotRegisteredError(err) {
136 continue
137 }
138 return nil, err
139 }
140 return obj, nil
141 }
142 // could not decode, so leave the object as Unknown, but give the decoders the
143 // chance to set Unknown.TypeMeta if it is available.
144 for _, decoder := range decoders {
145 if err := DecodeInto(decoder, obj.Raw, obj); err == nil {
146 return obj, nil
147 }
148 }
149 return obj, nil
150}
151
152// DecodeList alters the list in place, attempting to decode any objects found in
153// the list that have the Unknown type. Any errors that occur are returned
154// after the entire list is processed. Decoders are tried in order.
155func DecodeList(objects []Object, decoders ...Decoder) []error {
156 errs := []error(nil)
157 for i, obj := range objects {
158 switch t := obj.(type) {
159 case *Unknown:
160 decoded, err := decodeListItem(t, decoders)
161 if err != nil {
162 errs = append(errs, err)
163 break
164 }
165 objects[i] = decoded
166 }
167 }
168 return errs
169}
170
171// MultiObjectTyper returns the types of objects across multiple schemes in order.
172type MultiObjectTyper []ObjectTyper
173
174var _ ObjectTyper = MultiObjectTyper{}
175
176func (m MultiObjectTyper) ObjectKinds(obj Object) (gvks []schema.GroupVersionKind, unversionedType bool, err error) {
177 for _, t := range m {
178 gvks, unversionedType, err = t.ObjectKinds(obj)
179 if err == nil {
180 return
181 }
182 }
183 return
184}
185
186func (m MultiObjectTyper) Recognizes(gvk schema.GroupVersionKind) bool {
187 for _, t := range m {
188 if t.Recognizes(gvk) {
189 return true
190 }
191 }
192 return false
193}
194
195// SetZeroValue would set the object of objPtr to zero value of its type.
196func SetZeroValue(objPtr Object) error {
197 v, err := conversion.EnforcePtr(objPtr)
198 if err != nil {
199 return err
200 }
201 v.Set(reflect.Zero(v.Type()))
202 return nil
203}
204
205// DefaultFramer is valid for any stream that can read objects serially without
206// any separation in the stream.
207var DefaultFramer = defaultFramer{}
208
209type defaultFramer struct{}
210
211func (defaultFramer) NewFrameReader(r io.ReadCloser) io.ReadCloser { return r }
212func (defaultFramer) NewFrameWriter(w io.Writer) io.Writer { return w }