From b28234fac65ab93b8a0a1c7ecea8e71673d15511 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Thu, 30 Oct 2014 15:04:11 -0700 Subject: [PATCH] Fix self linking of objects returned in lists. --- cmd/integration/integration.go | 58 ++++++++++++++++++++++++++++++++++ pkg/apiserver/resthandler.go | 20 +++++++++++- pkg/runtime/helper.go | 5 +++ pkg/runtime/helper_test.go | 15 +++++++++ 4 files changed, 97 insertions(+), 1 deletion(-) diff --git a/cmd/integration/integration.go b/cmd/integration/integration.go index 607ebe0e5c5..35bdfeba686 100644 --- a/cmd/integration/integration.go +++ b/cmd/integration/integration.go @@ -271,6 +271,63 @@ func runAPIVersionsTest(c *client.Client) { glog.Infof("Version test passed") } +func runSelfLinkTest(c *client.Client) { + var svc api.Service + err := c.Post().Path("services").Body( + &api.Service{ + ObjectMeta: api.ObjectMeta{ + Name: "selflinktest", + Labels: map[string]string{ + "name": "selflinktest", + }, + }, + Port: 12345, + // This is here because validation requires it. + Selector: map[string]string{ + "foo": "bar", + }, + }, + ).Do().Into(&svc) + if err != nil { + glog.Fatalf("Failed creating selflinktest service: %v", err) + } + err = c.Get().AbsPath(svc.SelfLink).Do().Into(&svc) + if err != nil { + glog.Fatalf("Failed listing service with supplied self link '%v': %v", svc.SelfLink, err) + } + + var svcList api.ServiceList + err = c.Get().Path("services").Do().Into(&svcList) + if err != nil { + glog.Fatalf("Failed listing services: %v", err) + } + + err = c.Get().AbsPath(svcList.SelfLink).Do().Into(&svcList) + if err != nil { + glog.Fatalf("Failed listing services with supplied self link '%v': %v", svcList.SelfLink, err) + } + + found := false + for i := range svcList.Items { + item := &svcList.Items[i] + if item.Name != "selflinktest" { + continue + } + found = true + err = c.Get().AbsPath(item.SelfLink).Do().Into(&svc) + if err != nil { + glog.Fatalf("Failed listing service with supplied self link '%v': %v", item.SelfLink, err) + } + break + } + if !found { + glog.Fatalf("never found selflinktest service") + } + glog.Infof("Self link test passed") + + // TODO: Should test PUT at some point, too. +} + func runAtomicPutTest(c *client.Client) { var svc api.Service err := c.Post().Path("services").Body( @@ -509,6 +566,7 @@ func main() { runServiceTest, runAPIVersionsTest, runMasterServiceTest, + runSelfLinkTest, } var wg sync.WaitGroup wg.Add(len(testFuncs)) diff --git a/pkg/apiserver/resthandler.go b/pkg/apiserver/resthandler.go index 822751c8660..673dc3bba7c 100644 --- a/pkg/apiserver/resthandler.go +++ b/pkg/apiserver/resthandler.go @@ -62,7 +62,25 @@ func (h *RESTHandler) setSelfLink(obj runtime.Object, req *http.Request) error { newURL.Path = path.Join(h.canonicalPrefix, req.URL.Path) newURL.RawQuery = "" newURL.Fragment = "" - return h.selfLinker.SetSelfLink(obj, newURL.String()) + err := h.selfLinker.SetSelfLink(obj, newURL.String()) + if err != nil { + return err + } + if !runtime.IsListType(obj) { + return nil + } + + // Set self-link of objects in the list. + items, err := runtime.ExtractList(obj) + if err != nil { + return err + } + for i := range items { + if err := h.setSelfLinkAddName(items[i], req); err != nil { + return err + } + } + return runtime.SetList(obj, items) } // Like setSelfLink, but appends the object's name. diff --git a/pkg/runtime/helper.go b/pkg/runtime/helper.go index a94f8745a01..e7d6e3d46ed 100644 --- a/pkg/runtime/helper.go +++ b/pkg/runtime/helper.go @@ -23,6 +23,11 @@ import ( "github.com/GoogleCloudPlatform/kubernetes/pkg/conversion" ) +func IsListType(obj Object) bool { + _, err := GetItemsPtr(obj) + return err == nil +} + // GetItemsPtr returns a pointer to the list object's Items member. // If 'list' doesn't have an Items member, it's not really a list type // and an error will be returned. diff --git a/pkg/runtime/helper_test.go b/pkg/runtime/helper_test.go index f0af9e11d1a..f2c18de7db4 100644 --- a/pkg/runtime/helper_test.go +++ b/pkg/runtime/helper_test.go @@ -26,6 +26,21 @@ import ( "github.com/google/gofuzz" ) +func TestIsList(t *testing.T) { + tests := []struct { + obj runtime.Object + isList bool + }{ + {&api.PodList{}, true}, + {&api.Pod{}, false}, + } + for _, item := range tests { + if e, a := item.isList, runtime.IsListType(item.obj); e != a { + t.Errorf("%v: Expected %v, got %v", reflect.TypeOf(item.obj), e, a) + } + } +} + func TestExtractList(t *testing.T) { pl := &api.PodList{ Items: []api.Pod{