github.com/timstclair/heapster@v0.20.0-alpha1/Godeps/_workspace/src/k8s.io/kubernetes/pkg/conversion/encode.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 "bytes" 21 "encoding/json" 22 "fmt" 23 "io" 24 "path" 25 ) 26 27 // EncodeToVersion turns the given api object into an appropriate JSON string. 28 // Obj may be a pointer to a struct, or a struct. If a struct, a copy 29 // will be made, therefore it's recommended to pass a pointer to a 30 // struct. The type must have been registered. 31 // 32 // Memory/wire format differences: 33 // * Having to keep track of the Kind and Version fields makes tests 34 // very annoying, so the rule is that they are set only in wire format 35 // (json), not when in native (memory) format. This is possible because 36 // both pieces of information are implicit in the go typed object. 37 // * An exception: note that, if there are embedded API objects of known 38 // type, for example, PodList{... Items []Pod ...}, these embedded 39 // objects must be of the same version of the object they are embedded 40 // within, and their Version and Kind must both be empty. 41 // * Note that the exception does not apply to a generic APIObject type 42 // which recursively does Encode()/Decode(), and is capable of 43 // expressing any API object. 44 // * Only versioned objects should be encoded. This means that, if you pass 45 // a native object, Encode will convert it to a versioned object. For 46 // example, an api.Pod will get converted to a v1.Pod. However, if 47 // you pass in an object that's already versioned (v1.Pod), Encode 48 // will not modify it. 49 // 50 // The purpose of the above complex conversion behavior is to allow us to 51 // change the memory format yet not break compatibility with any stored 52 // objects, whether they be in our storage layer (e.g., etcd), or in user's 53 // config files. 54 // 55 func (s *Scheme) EncodeToVersion(obj interface{}, destVersion string) (data []byte, err error) { 56 buff := &bytes.Buffer{} 57 if err := s.EncodeToVersionStream(obj, destVersion, buff); err != nil { 58 return nil, err 59 } 60 return buff.Bytes(), nil 61 } 62 63 func (s *Scheme) EncodeToVersionStream(obj interface{}, destVersion string, stream io.Writer) error { 64 obj = maybeCopy(obj) 65 v, _ := EnforcePtr(obj) // maybeCopy guarantees a pointer 66 67 // Don't encode an object defined in the unversioned package, unless if the 68 // destVersion is v1, encode it to v1 for backward compatibility. 69 pkg := path.Base(v.Type().PkgPath()) 70 if pkg == "unversioned" && destVersion != "v1" { 71 // TODO: convert this to streaming too 72 data, err := s.encodeUnversionedObject(obj) 73 if err != nil { 74 return err 75 } 76 _, err = stream.Write(data) 77 return err 78 } 79 80 if _, registered := s.typeToVersion[v.Type()]; !registered { 81 return fmt.Errorf("type %v is not registered for %q and it will be impossible to Decode it, therefore Encode will refuse to encode it.", v.Type(), destVersion) 82 } 83 84 objVersion, objKind, err := s.ObjectVersionAndKind(obj) 85 if err != nil { 86 return err 87 } 88 89 // Perform a conversion if necessary. 90 if objVersion != destVersion { 91 objOut, err := s.NewObject(destVersion, objKind) 92 if err != nil { 93 return err 94 } 95 flags, meta := s.generateConvertMeta(objVersion, destVersion, obj) 96 err = s.converter.Convert(obj, objOut, flags, meta) 97 if err != nil { 98 return err 99 } 100 obj = objOut 101 } 102 103 // ensure the output object name comes from the destination type 104 _, objKind, err = s.ObjectVersionAndKind(obj) 105 if err != nil { 106 return err 107 } 108 109 // Version and Kind should be set on the wire. 110 err = s.SetVersionAndKind(destVersion, objKind, obj) 111 if err != nil { 112 return err 113 } 114 115 // To add metadata, do some simple surgery on the JSON. 116 encoder := json.NewEncoder(stream) 117 if err := encoder.Encode(obj); err != nil { 118 return err 119 } 120 121 // Version and Kind should be blank in memory. Reset them, since it's 122 // possible that we modified a user object and not a copy above. 123 err = s.SetVersionAndKind("", "", obj) 124 if err != nil { 125 return err 126 } 127 128 return nil 129 } 130 131 func (s *Scheme) encodeUnversionedObject(obj interface{}) (data []byte, err error) { 132 _, objKind, err := s.ObjectVersionAndKind(obj) 133 if err != nil { 134 return nil, err 135 } 136 if err = s.SetVersionAndKind("", objKind, obj); err != nil { 137 return nil, err 138 } 139 data, err = json.Marshal(obj) 140 if err != nil { 141 return nil, err 142 } 143 // Version and Kind should be blank in memory. Reset them, since it's 144 // possible that we modified a user object and not a copy above. 145 err = s.SetVersionAndKind("", "", obj) 146 return data, nil 147 }