github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/meta.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors 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 conversion 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "path" 23 "reflect" 24 ) 25 26 // MetaFactory is used to store and retrieve the version and kind 27 // information for all objects in a scheme. 28 type MetaFactory interface { 29 // Update sets the given version and kind onto the object. 30 Update(version, kind string, obj interface{}) error 31 // Interpret should return the version and kind of the wire-format of 32 // the object. 33 Interpret(data []byte) (version, kind string, err error) 34 } 35 36 // DefaultMetaFactory is a default factory for versioning objects in JSON. The object 37 // in memory and in the default JSON serialization will use the "kind" and "apiVersion" 38 // fields. 39 var DefaultMetaFactory = SimpleMetaFactory{KindField: "Kind", VersionField: "APIVersion"} 40 41 // SimpleMetaFactory provides default methods for retrieving the type and version of objects 42 // that are identified with an "apiVersion" and "kind" fields in their JSON 43 // serialization. It may be parameterized with the names of the fields in memory, or an 44 // optional list of base structs to search for those fields in memory. 45 type SimpleMetaFactory struct { 46 // The name of the API version field in memory of the struct 47 VersionField string 48 // The name of the kind field in memory of the struct. 49 KindField string 50 // Optional, if set will look in the named inline structs to find the fields to set. 51 BaseFields []string 52 } 53 54 // Interpret will return the APIVersion and Kind of the JSON wire-format 55 // encoding of an object, or an error. 56 func (SimpleMetaFactory) Interpret(data []byte) (version, kind string, err error) { 57 findKind := struct { 58 APIVersion string `json:"apiVersion,omitempty"` 59 Kind string `json:"kind,omitempty"` 60 }{} 61 err = json.Unmarshal(data, &findKind) 62 if err != nil { 63 return "", "", fmt.Errorf("couldn't get version/kind; json parse error: %v", err) 64 } 65 return findKind.APIVersion, findKind.Kind, nil 66 } 67 68 func (f SimpleMetaFactory) Update(version, kind string, obj interface{}) error { 69 return UpdateVersionAndKind(f.BaseFields, f.VersionField, version, f.KindField, kind, obj) 70 } 71 72 // UpdateVersionAndKind uses reflection to find and set the versionField and kindField fields 73 // on a pointer to a struct to version and kind. Provided as a convenience for others 74 // implementing MetaFactory. Pass an array to baseFields to check one or more nested structs 75 // for the named fields. The version field is treated as optional if it is not present in the struct. 76 func UpdateVersionAndKind(baseFields []string, versionField, version, kindField, kind string, obj interface{}) error { 77 v, err := EnforcePtr(obj) 78 if err != nil { 79 return err 80 } 81 pkg := path.Base(v.Type().PkgPath()) 82 t := v.Type() 83 name := t.Name() 84 if v.Kind() != reflect.Struct { 85 return fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), name, v.Interface()) 86 } 87 88 for i := range baseFields { 89 base := v.FieldByName(baseFields[i]) 90 if !base.IsValid() { 91 continue 92 } 93 v = base 94 } 95 96 field := v.FieldByName(kindField) 97 if !field.IsValid() { 98 // Types defined in the unversioned package are allowed to not have a 99 // kindField. Clients will have to know what they are based on the 100 // context. 101 // TODO: add some type trait here, or some way of indicating whether 102 // this feature is allowed on a per-type basis. Using package name is 103 // overly broad and a bit hacky. 104 if pkg == "unversioned" { 105 return nil 106 } 107 return fmt.Errorf("couldn't find %v field in %#v", kindField, v.Interface()) 108 } 109 field.SetString(kind) 110 111 if field := v.FieldByName(versionField); field.IsValid() { 112 field.SetString(version) 113 } 114 115 return nil 116 } 117 118 // EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value 119 // of the dereferenced pointer, ensuring that it is settable/addressable. 120 // Returns an error if this is not possible. 121 func EnforcePtr(obj interface{}) (reflect.Value, error) { 122 v := reflect.ValueOf(obj) 123 if v.Kind() != reflect.Ptr { 124 if v.Kind() == reflect.Invalid { 125 return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind") 126 } 127 return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type()) 128 } 129 if v.IsNil() { 130 return reflect.Value{}, fmt.Errorf("expected pointer, but got nil") 131 } 132 return v.Elem(), nil 133 }