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 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 } 213