Merge pull request #196 from lavalamp/marshal

Make api able to marshal its types correctly
pull/6/head
brendandburns 2014-06-23 13:22:52 -07:00
commit 49c25a4e28
16 changed files with 373 additions and 134 deletions

View File

@ -169,7 +169,7 @@ func executeAPIRequest(method string, auth *kube_client.AuthInfo) bool {
printer = &cloudcfg.HumanReadablePrinter{}
}
var body string
var body []byte
if body, err = cloudcfg.DoRequest(request, auth); err == nil {
if err = printer.Print(body, os.Stdout); err != nil {
log.Fatalf("Failed to print: %#v\nRaw received text:\n%v\n", err, string(body))

154
pkg/api/helper.go Normal file
View File

@ -0,0 +1,154 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"encoding/json"
"fmt"
"reflect"
"gopkg.in/v1/yaml"
)
var knownTypes = map[string]reflect.Type{}
func init() {
AddKnownTypes(
PodList{},
Pod{},
ReplicationControllerList{},
ReplicationController{},
ServiceList{},
Service{},
Status{},
)
}
func AddKnownTypes(types ...interface{}) {
for _, obj := range types {
t := reflect.TypeOf(obj)
knownTypes[t.Name()] = t
}
}
// Encode turns the given api object into an appropriate JSON string.
// Will return an error if the object doesn't have an embedded JSONBase.
// Obj may be a pointer to a struct, or a struct. If a struct, a copy
// will be made so that the object's Kind field can be set. If a pointer,
// we change the Kind field, marshal, and then set the kind field back to
// "". Having to keep track of the kind field makes tests very annoying,
// so the rule is it's set only in wire format (json), not when in native
// format.
func Encode(obj interface{}) (data []byte, err error) {
obj = checkPtr(obj)
jsonBase, err := prepareEncode(obj)
if err != nil {
return nil, err
}
data, err = json.MarshalIndent(obj, "", " ")
jsonBase.Kind = ""
return data, err
}
func checkPtr(obj interface{}) interface{} {
v := reflect.ValueOf(obj)
if v.Kind() == reflect.Ptr {
return obj
}
v2 := reflect.New(v.Type())
v2.Elem().Set(v)
return v2.Interface()
}
func prepareEncode(obj interface{}) (*JSONBase, error) {
name, jsonBase, err := nameAndJSONBase(obj)
if err != nil {
return nil, err
}
if _, contains := knownTypes[name]; !contains {
return nil, fmt.Errorf("struct %v won't be unmarshalable because it's not in knownTypes", name)
}
jsonBase.Kind = name
return jsonBase, nil
}
// Returns the name of the type (sans pointer), and its kind field. Takes pointer-to-struct..
func nameAndJSONBase(obj interface{}) (string, *JSONBase, error) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr {
return "", nil, fmt.Errorf("expected pointer, but got %v", v.Type().Name())
}
v = v.Elem()
name := v.Type().Name()
if v.Kind() != reflect.Struct {
return "", nil, fmt.Errorf("expected struct, but got %v", name)
}
jsonBase := v.FieldByName("JSONBase")
if !jsonBase.IsValid() {
return "", nil, fmt.Errorf("struct %v lacks embedded JSON type", name)
}
return name, jsonBase.Addr().Interface().(*JSONBase), nil
}
// Decode converts a JSON string back into a pointer to an api object. Deduces the type
// based upon the Kind field (set by encode).
func Decode(data []byte) (interface{}, error) {
findKind := struct {
Kind string `json:"kind,omitempty" yaml:"kind,omitempty"`
}{}
// yaml is a superset of json, so we use it to decode here. That way, we understand both.
err := yaml.Unmarshal(data, &findKind)
if err != nil {
return nil, fmt.Errorf("Couldn't get kind: %#v", err)
}
objType, found := knownTypes[findKind.Kind]
if !found {
return nil, fmt.Errorf("%v is not a known type", findKind.Kind)
}
obj := reflect.New(objType).Interface()
err = yaml.Unmarshal(data, obj)
if err != nil {
return nil, err
}
_, jsonBase, err := nameAndJSONBase(obj)
if err != nil {
return nil, err
}
// Don't leave these set. Track type with go's type.
jsonBase.Kind = ""
return obj, nil
}
// DecodeInto parses a JSON string and stores it in obj. Returns an error
// if data.Kind is set and doesn't match the type of obj. Obj should be a
// pointer to an api type.
func DecodeInto(data []byte, obj interface{}) error {
err := yaml.Unmarshal(data, obj)
if err != nil {
return err
}
name, jsonBase, err := nameAndJSONBase(obj)
if err != nil {
return err
}
if jsonBase.Kind != "" && jsonBase.Kind != name {
return fmt.Errorf("data had kind %v, but passed object was of type %v", jsonBase.Kind, name)
}
// Don't leave these set. Track type with go's type.
jsonBase.Kind = ""
return nil
}

116
pkg/api/helper_test.go Normal file
View File

@ -0,0 +1,116 @@
/*
Copyright 2014 Google Inc. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"reflect"
"testing"
)
func runTest(t *testing.T, source interface{}) {
name := reflect.TypeOf(source).Name()
data, err := Encode(source)
if err != nil {
t.Errorf("%v: %v", name, err)
return
}
obj2, err := Decode(data)
if err != nil {
t.Errorf("%v: %v", name, err)
return
}
if !reflect.DeepEqual(source, obj2) {
t.Errorf("%v: wanted %#v, got %#v", name, source, obj2)
return
}
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
err = DecodeInto(data, obj3)
if err != nil {
t.Errorf("%v: %v", name, err)
return
}
if !reflect.DeepEqual(source, obj3) {
t.Errorf("%v: wanted %#v, got %#v", name, source, obj3)
return
}
}
func TestTypes(t *testing.T) {
// TODO: auto-fill all fields.
table := []interface{}{
&Pod{
JSONBase: JSONBase{
ID: "mylittlepod",
},
Labels: map[string]string{
"name": "pinky",
},
},
&Service{},
&ServiceList{
Items: []Service{
{
Labels: map[string]string{
"foo": "bar",
},
}, {
Labels: map[string]string{
"foo": "baz",
},
},
},
},
&ReplicationControllerList{},
&ReplicationController{},
&PodList{},
}
for _, item := range table {
runTest(t, item)
}
}
func TestNonPtr(t *testing.T) {
obj := interface{}(Pod{Labels: map[string]string{"name": "foo"}})
data, err := Encode(obj)
obj2, err2 := Decode(data)
if err != nil || err2 != nil {
t.Errorf("Failure: %v %v", err2, err2)
}
if _, ok := obj2.(*Pod); !ok {
t.Errorf("Got wrong type")
}
if !reflect.DeepEqual(obj2, &Pod{Labels: map[string]string{"name": "foo"}}) {
t.Errorf("Something changed: %#v", obj2)
}
}
func TestPtr(t *testing.T) {
obj := interface{}(&Pod{Labels: map[string]string{"name": "foo"}})
data, err := Encode(obj)
obj2, err2 := Decode(data)
if err != nil || err2 != nil {
t.Errorf("Failure: %v %v", err2, err2)
}
if _, ok := obj2.(*Pod); !ok {
t.Errorf("Got wrong type")
}
if !reflect.DeepEqual(obj2, &Pod{Labels: map[string]string{"name": "foo"}}) {
t.Errorf("Something changed: %#v", obj2)
}
}
// TODO: test rejection of bad JSON.

View File

@ -174,3 +174,21 @@ type Endpoints struct {
Name string
Endpoints []string
}
// Status is a return value for calls that don't return other objects.
// Arguably, this could go in apiserver, but I'm including it here so clients needn't
// import both.
type Status struct {
JSONBase `json:",inline" yaml:",inline"`
// One of: "success", "failure", "working" (for operations not yet completed)
// TODO: if "working", include an operation identifier so final status can be
// checked.
Status string `json:"status,omitempty" yaml:"status,omitempty"`
}
// Values of Status.Status
const (
StatusSuccess = "success"
StatusFailure = "failure"
StatusWorking = "working"
)

View File

@ -17,7 +17,6 @@ limitations under the License.
package apiserver
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
@ -27,6 +26,7 @@ import (
"strings"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -36,7 +36,7 @@ type RESTStorage interface {
List(labels.Selector) (interface{}, error)
Get(id string) (interface{}, error)
Delete(id string) (<-chan interface{}, error)
Extract(body string) (interface{}, error)
Extract(body []byte) (interface{}, error)
Create(interface{}) (<-chan interface{}, error)
Update(interface{}) (<-chan interface{}, error)
}
@ -50,11 +50,6 @@ func MakeAsync(fn func() interface{}) <-chan interface{} {
return channel
}
// Status is a return value for calls that don't return other objects
type Status struct {
Success bool
}
// ApiServer is an HTTPHandler that delegates to RESTStorage objects.
// It handles URLs of the form:
// ${prefix}/${storage_key}[/${object_name}]
@ -130,7 +125,7 @@ func (server *ApiServer) notFound(req *http.Request, w http.ResponseWriter) {
func (server *ApiServer) write(statusCode int, object interface{}, w http.ResponseWriter) {
w.WriteHeader(statusCode)
output, err := json.MarshalIndent(object, "", " ")
output, err := api.Encode(object)
if err != nil {
server.error(err, w)
return
@ -143,10 +138,10 @@ func (server *ApiServer) error(err error, w http.ResponseWriter) {
fmt.Fprintf(w, "Internal Error: %#v", err)
}
func (server *ApiServer) readBody(req *http.Request) (string, error) {
func (server *ApiServer) readBody(req *http.Request) ([]byte, error) {
defer req.Body.Close()
body, err := ioutil.ReadAll(req.Body)
return string(body), err
return body, err
}
func (server *ApiServer) waitForObject(out <-chan interface{}, timeout time.Duration) (interface{}, error) {
@ -248,7 +243,7 @@ func (server *ApiServer) handleREST(parts []string, requestUrl *url.URL, req *ht
}
out, err := storage.Delete(parts[1])
var obj interface{}
obj = Status{Success: true}
obj = api.Status{Status: api.StatusSuccess}
if err == nil && sync {
obj, err = server.waitForObject(out, timeout)
}

View File

@ -27,9 +27,14 @@ import (
"sync"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
)
func init() {
api.AddKnownTypes(Simple{}, SimpleList{})
}
// TODO: This doesn't reduce typing enough to make it worth the less readable errors. Remove.
func expectNoError(t *testing.T, err error) {
if err != nil {
@ -38,11 +43,13 @@ func expectNoError(t *testing.T, err error) {
}
type Simple struct {
Name string
api.JSONBase `yaml:",inline" json:",inline"`
Name string `yaml:"name,omitempty" json:"name,omitempty"`
}
type SimpleList struct {
Items []Simple
api.JSONBase `yaml:",inline" json:",inline"`
Items []Simple `yaml:"items,omitempty" json:"items,omitempty"`
}
type SimpleRESTStorage struct {
@ -55,7 +62,7 @@ type SimpleRESTStorage struct {
}
func (storage *SimpleRESTStorage) List(labels.Selector) (interface{}, error) {
result := SimpleList{
result := &SimpleList{
Items: storage.list,
}
return result, storage.err
@ -70,9 +77,9 @@ func (storage *SimpleRESTStorage) Delete(id string) (<-chan interface{}, error)
return storage.channel, storage.err
}
func (storage *SimpleRESTStorage) Extract(body string) (interface{}, error) {
func (storage *SimpleRESTStorage) Extract(body []byte) (interface{}, error) {
var item Simple
json.Unmarshal([]byte(body), &item)
api.DecodeInto(body, &item)
return item, storage.err
}
@ -91,7 +98,7 @@ func extractBody(response *http.Response, object interface{}) (string, error) {
if err != nil {
return string(body), err
}
err = json.Unmarshal(body, object)
err = api.DecodeInto(body, object)
return string(body), err
}
@ -149,8 +156,10 @@ func TestNonEmptyList(t *testing.T) {
var listOut SimpleList
body, err := extractBody(resp, &listOut)
expectNoError(t, err)
if len(listOut.Items) != 1 {
t.Errorf("Unexpected response: %#v", listOut)
return
}
if listOut.Items[0].Name != simpleStorage.list[0].Name {
t.Errorf("Unexpected data: %#v, %s", listOut.Items[0], string(body))

View File

@ -113,7 +113,7 @@ func RequestWithBodyData(data []byte, url, method string) (*http.Request, error)
}
// Execute a request, adds authentication (if auth != nil), and HTTPS cert ignoring.
func DoRequest(request *http.Request, auth *client.AuthInfo) (string, error) {
func DoRequest(request *http.Request, auth *client.AuthInfo) ([]byte, error) {
if auth != nil {
request.SetBasicAuth(auth.User, auth.Password)
}
@ -123,11 +123,11 @@ func DoRequest(request *http.Request, auth *client.AuthInfo) (string, error) {
client := &http.Client{Transport: tr}
response, err := client.Do(request)
if err != nil {
return "", err
return []byte{}, err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
return string(body), err
return body, err
}
// StopController stops a controller named 'name' by setting replicas to zero

View File

@ -164,7 +164,7 @@ func TestDoRequest(t *testing.T) {
if err != nil {
t.Error("Unexpected error")
}
if body != expectedBody {
if string(body) != expectedBody {
t.Errorf("Expected body: '%s', saw: '%s'", expectedBody, body)
}
fakeHandler.ValidateRequest(t, "/foo/bar", "GET", nil)

View File

@ -17,12 +17,10 @@ limitations under the License.
package cloudcfg
import (
"encoding/json"
"fmt"
"reflect"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"gopkg.in/v1/yaml"
)
var storageToType = map[string]reflect.Type{
@ -41,9 +39,9 @@ func ToWireFormat(data []byte, storage string) ([]byte, error) {
}
obj := reflect.New(prototypeType).Interface()
err := yaml.Unmarshal(data, obj)
err := api.DecodeInto(data, obj)
if err != nil {
return nil, err
}
return json.Marshal(obj)
return api.Encode(obj)
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package cloudcfg
import (
"encoding/json"
"testing"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
@ -32,7 +31,7 @@ func TestParseBadStorage(t *testing.T) {
}
func DoParseTest(t *testing.T, storage string, obj interface{}) {
json_data, _ := json.Marshal(obj)
json_data, _ := api.Encode(obj)
yaml_data, _ := yaml.Marshal(obj)
t.Logf("Intermediate yaml:\n%v\n", string(yaml_data))

View File

@ -31,23 +31,23 @@ import (
// ResourcePrinter is an interface that knows how to print API resources
type ResourcePrinter interface {
// Print receives an arbitrary JSON body, formats it and prints it to a writer
Print(string, io.Writer) error
Print([]byte, io.Writer) error
}
// Identity printer simply copies the body out to the output stream
type IdentityPrinter struct{}
func (i *IdentityPrinter) Print(data string, w io.Writer) error {
_, err := fmt.Fprint(w, data)
func (i *IdentityPrinter) Print(data []byte, w io.Writer) error {
_, err := w.Write(data)
return err
}
// YAMLPrinter parses JSON, and re-formats as YAML
type YAMLPrinter struct{}
func (y *YAMLPrinter) Print(data string, w io.Writer) error {
func (y *YAMLPrinter) Print(data []byte, w io.Writer) error {
var obj interface{}
if err := json.Unmarshal([]byte(data), &obj); err != nil {
if err := json.Unmarshal(data, &obj); err != nil {
return err
}
output, err := yaml.Marshal(obj)
@ -64,9 +64,10 @@ type HumanReadablePrinter struct{}
var podColumns = []string{"Name", "Image(s)", "Host", "Labels"}
var replicationControllerColumns = []string{"Name", "Image(s)", "Selector", "Replicas"}
var serviceColumns = []string{"Name", "Labels", "Selector", "Port"}
var statusColumns = []string{"Status"}
func (h *HumanReadablePrinter) unknown(data string, w io.Writer) error {
_, err := fmt.Fprintf(w, "Unknown object: %s", data)
func (h *HumanReadablePrinter) unknown(data []byte, w io.Writer) error {
_, err := fmt.Fprintf(w, "Unknown object: %s", string(data))
return err
}
@ -90,97 +91,62 @@ func (h *HumanReadablePrinter) makeImageList(manifest api.ContainerManifest) str
return strings.Join(images, ",")
}
func (h *HumanReadablePrinter) printPod(pod api.Pod, w io.Writer) error {
func (h *HumanReadablePrinter) printPod(pod *api.Pod, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n",
pod.ID, h.makeImageList(pod.DesiredState.Manifest), pod.CurrentState.Host+"/"+pod.CurrentState.HostIP, labels.Set(pod.Labels))
return err
}
func (h *HumanReadablePrinter) printPodList(podList api.PodList, w io.Writer) error {
func (h *HumanReadablePrinter) printPodList(podList *api.PodList, w io.Writer) error {
for _, pod := range podList.Items {
if err := h.printPod(pod, w); err != nil {
if err := h.printPod(&pod, w); err != nil {
return err
}
}
return nil
}
func (h *HumanReadablePrinter) printReplicationController(ctrl api.ReplicationController, w io.Writer) error {
func (h *HumanReadablePrinter) printReplicationController(ctrl *api.ReplicationController, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n",
ctrl.ID, h.makeImageList(ctrl.DesiredState.PodTemplate.DesiredState.Manifest), labels.Set(ctrl.DesiredState.ReplicaSelector), ctrl.DesiredState.Replicas)
return err
}
func (h *HumanReadablePrinter) printReplicationControllerList(list api.ReplicationControllerList, w io.Writer) error {
func (h *HumanReadablePrinter) printReplicationControllerList(list *api.ReplicationControllerList, w io.Writer) error {
for _, ctrl := range list.Items {
if err := h.printReplicationController(ctrl, w); err != nil {
if err := h.printReplicationController(&ctrl, w); err != nil {
return err
}
}
return nil
}
func (h *HumanReadablePrinter) printService(svc api.Service, w io.Writer) error {
func (h *HumanReadablePrinter) printService(svc *api.Service, w io.Writer) error {
_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%d\n", svc.ID, labels.Set(svc.Labels), labels.Set(svc.Selector), svc.Port)
return err
}
func (h *HumanReadablePrinter) printServiceList(list api.ServiceList, w io.Writer) error {
func (h *HumanReadablePrinter) printServiceList(list *api.ServiceList, w io.Writer) error {
for _, svc := range list.Items {
if err := h.printService(svc, w); err != nil {
if err := h.printService(&svc, w); err != nil {
return err
}
}
return nil
}
// TODO replace this with something that returns a concrete printer object, rather than
// having the secondary switch below.
func (h *HumanReadablePrinter) extractObject(data, kind string) (interface{}, error) {
// TODO: I think this can be replaced with some reflection and a map[string]type
switch kind {
case "cluster#pod":
var obj api.Pod
if err := json.Unmarshal([]byte(data), &obj); err != nil {
return nil, err
}
return obj, nil
case "cluster#podList":
var list api.PodList
if err := json.Unmarshal([]byte(data), &list); err != nil {
return nil, err
}
return list, nil
case "cluster#replicationController":
var ctrl api.ReplicationController
if err := json.Unmarshal([]byte(data), &ctrl); err != nil {
return nil, err
}
return ctrl, nil
case "cluster#replicationControllerList":
var list api.ReplicationControllerList
if err := json.Unmarshal([]byte(data), &list); err != nil {
return nil, err
}
return list, nil
case "cluster#service":
var ctrl api.Service
if err := json.Unmarshal([]byte(data), &ctrl); err != nil {
return nil, err
}
return ctrl, nil
case "cluster#serviceList":
var list api.ServiceList
if err := json.Unmarshal([]byte(data), &list); err != nil {
return nil, err
}
return list, nil
default:
return nil, fmt.Errorf("unknown kind: %s", kind)
func (h *HumanReadablePrinter) printStatus(status *api.Status, w io.Writer) error {
err := h.printHeader(statusColumns, w)
if err != nil {
return err
}
_, err = fmt.Fprintf(w, "%v\n", status.Status)
return err
}
func (h *HumanReadablePrinter) Print(data string, output io.Writer) error {
// TODO replace this with something that returns a concrete printer object, rather than
// having the secondary switch below.
func (h *HumanReadablePrinter) Print(data []byte, output io.Writer) error {
w := tabwriter.NewWriter(output, 20, 5, 3, ' ', 0)
defer w.Flush()
var mapObj map[string]interface{}
@ -198,30 +164,31 @@ func (h *HumanReadablePrinter) Print(data string, output io.Writer) error {
return fmt.Errorf("unexpected object with no 'kind' field: %s", data)
}
kind := (mapObj["kind"]).(string)
obj, err := h.extractObject(data, kind)
obj, err := api.Decode(data)
if err != nil {
return err
}
switch obj.(type) {
case api.Pod:
switch o := obj.(type) {
case *api.Pod:
h.printHeader(podColumns, w)
return h.printPod(obj.(api.Pod), w)
case api.PodList:
return h.printPod(o, w)
case *api.PodList:
h.printHeader(podColumns, w)
return h.printPodList(obj.(api.PodList), w)
case api.ReplicationController:
return h.printPodList(o, w)
case *api.ReplicationController:
h.printHeader(replicationControllerColumns, w)
return h.printReplicationController(obj.(api.ReplicationController), w)
case api.ReplicationControllerList:
return h.printReplicationController(o, w)
case *api.ReplicationControllerList:
h.printHeader(replicationControllerColumns, w)
return h.printReplicationControllerList(obj.(api.ReplicationControllerList), w)
case api.Service:
return h.printReplicationControllerList(o, w)
case *api.Service:
h.printHeader(serviceColumns, w)
return h.printService(obj.(api.Service), w)
case api.ServiceList:
return h.printService(o, w)
case *api.ServiceList:
h.printHeader(serviceColumns, w)
return h.printServiceList(obj.(api.ServiceList), w)
return h.printServiceList(o, w)
case *api.Status:
return h.printStatus(o, w)
default:
return h.unknown(data, w)
}

View File

@ -17,8 +17,6 @@ limitations under the License.
package registry
import (
"encoding/json"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
@ -36,7 +34,7 @@ func MakeControllerRegistryStorage(registry ControllerRegistry) apiserver.RESTSt
}
func (storage *ControllerRegistryStorage) List(selector labels.Selector) (interface{}, error) {
result := api.ReplicationControllerList{JSONBase: api.JSONBase{Kind: "cluster#replicationControllerList"}}
result := api.ReplicationControllerList{}
controllers, err := storage.registry.ListControllers()
if err == nil {
for _, controller := range controllers {
@ -53,18 +51,16 @@ func (storage *ControllerRegistryStorage) Get(id string) (interface{}, error) {
if err != nil {
return nil, err
}
controller.Kind = "cluster#replicationController"
return controller, err
}
func (storage *ControllerRegistryStorage) Delete(id string) (<-chan interface{}, error) {
return apiserver.MakeAsync(func() interface{} { return apiserver.Status{Success: true} }), storage.registry.DeleteController(id)
return apiserver.MakeAsync(func() interface{} { return api.Status{Status: api.StatusSuccess} }), storage.registry.DeleteController(id)
}
func (storage *ControllerRegistryStorage) Extract(body string) (interface{}, error) {
func (storage *ControllerRegistryStorage) Extract(body []byte) (interface{}, error) {
result := api.ReplicationController{}
err := json.Unmarshal([]byte(body), &result)
result.Kind = "cluster#replicationController"
err := api.DecodeInto(body, &result)
return result, err
}

View File

@ -122,12 +122,10 @@ func TestExtractControllerJson(t *testing.T) {
ID: "foo",
},
}
body, err := json.Marshal(controller)
body, err := api.Encode(&controller)
expectNoError(t, err)
controllerOut, err := storage.Extract(string(body))
controllerOut, err := storage.Extract(body)
expectNoError(t, err)
// Extract adds a Kind
controller.Kind = "cluster#replicationController"
if !reflect.DeepEqual(controller, controllerOut) {
t.Errorf("Expected %#v, found %#v", controller, controllerOut)
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package registry
import (
"encoding/json"
"fmt"
"log"
"strings"
@ -74,7 +73,6 @@ func (storage *PodRegistryStorage) List(selector labels.Selector) (interface{},
}
}
result.Kind = "cluster#podList"
return result, err
}
@ -129,18 +127,16 @@ func (storage *PodRegistryStorage) Get(id string) (interface{}, error) {
}
pod.CurrentState.HostIP = getInstanceIP(storage.cloud, pod.CurrentState.Host)
pod.Kind = "cluster#pod"
return pod, err
}
func (storage *PodRegistryStorage) Delete(id string) (<-chan interface{}, error) {
return apiserver.MakeAsync(func() interface{} { return apiserver.Status{Success: true} }), storage.registry.DeletePod(id)
return apiserver.MakeAsync(func() interface{} { return api.Status{Status: api.StatusSuccess} }), storage.registry.DeletePod(id)
}
func (storage *PodRegistryStorage) Extract(body string) (interface{}, error) {
func (storage *PodRegistryStorage) Extract(body []byte) (interface{}, error) {
pod := api.Pod{}
err := json.Unmarshal([]byte(body), &pod)
pod.Kind = "cluster#pod"
err := api.DecodeInto(body, &pod)
return pod, err
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package registry
import (
"encoding/json"
"fmt"
"reflect"
"testing"
@ -103,12 +102,10 @@ func TestExtractJson(t *testing.T) {
ID: "foo",
},
}
body, err := json.Marshal(pod)
body, err := api.Encode(&pod)
expectNoError(t, err)
podOut, err := storage.Extract(string(body))
podOut, err := storage.Extract(body)
expectNoError(t, err)
// Extract adds in a kind
pod.Kind = "cluster#pod"
if !reflect.DeepEqual(pod, podOut) {
t.Errorf("Expected %#v, found %#v", pod, podOut)
}

View File

@ -17,7 +17,6 @@ limitations under the License.
package registry
import (
"encoding/json"
"fmt"
"strconv"
"strings"
@ -64,7 +63,6 @@ func (sr *ServiceRegistryStorage) List(selector labels.Selector) (interface{}, e
if err != nil {
return nil, err
}
list.Kind = "cluster#serviceList"
var filtered []api.Service
for _, service := range list.Items {
if selector.Matches(labels.Set(service.Labels)) {
@ -80,7 +78,6 @@ func (sr *ServiceRegistryStorage) Get(id string) (interface{}, error) {
if err != nil {
return nil, err
}
service.Kind = "cluster#service"
return service, err
}
@ -102,13 +99,12 @@ func (sr *ServiceRegistryStorage) Delete(id string) (<-chan interface{}, error)
}
}
}
return apiserver.MakeAsync(func() interface{} { return apiserver.Status{Success: true} }), sr.registry.DeleteService(id)
return apiserver.MakeAsync(func() interface{} { return api.Status{Status: api.StatusSuccess} }), sr.registry.DeleteService(id)
}
func (sr *ServiceRegistryStorage) Extract(body string) (interface{}, error) {
func (sr *ServiceRegistryStorage) Extract(body []byte) (interface{}, error) {
var svc api.Service
err := json.Unmarshal([]byte(body), &svc)
svc.Kind = "cluster#service"
err := api.DecodeInto(body, &svc)
return svc, err
}