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  }