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  }