diff --git a/pkg/genericapiserver/api/handlers/rest.go b/pkg/genericapiserver/api/handlers/rest.go index ba694752df..ad468d76ae 100644 --- a/pkg/genericapiserver/api/handlers/rest.go +++ b/pkg/genericapiserver/api/handlers/rest.go @@ -334,6 +334,13 @@ func ListResource(r rest.Lister, rw rest.Watcher, scope RequestScope, forceWatch return } trace.Step("Self-linking done") + // Ensure empty lists return a non-nil items slice + if numberOfItems == 0 { + if err := meta.SetList(result, []runtime.Object{}); err != nil { + scope.err(err, res.ResponseWriter, req.Request) + return + } + } responsewriters.WriteObject(http.StatusOK, scope.Kind.GroupVersion(), scope.Serializer, result, w, req.Request) trace.Step(fmt.Sprintf("Writing http response done (%d items)", numberOfItems)) } diff --git a/test/integration/master/master_test.go b/test/integration/master/master_test.go index 9a03897bc4..384d68c835 100644 --- a/test/integration/master/master_test.go +++ b/test/integration/master/master_test.go @@ -76,6 +76,34 @@ func TestExtensionsPrefix(t *testing.T) { testPrefix(t, "/apis/extensions/") } +func TestEmptyList(t *testing.T) { + _, s := framework.RunAMaster(nil) + defer s.Close() + + u := s.URL + "/api/v1/namespaces/default/pods" + resp, err := http.Get(u) + if err != nil { + t.Fatalf("unexpected error getting %s: %v", u, err) + } + if resp.StatusCode != http.StatusOK { + t.Fatalf("got status %v instead of 200 OK", resp.StatusCode) + } + defer resp.Body.Close() + data, _ := ioutil.ReadAll(resp.Body) + decodedData := map[string]interface{}{} + if err := json.Unmarshal(data, &decodedData); err != nil { + t.Logf("body: %s", string(data)) + t.Fatalf("got error decoding data: %v", err) + } + if items, ok := decodedData["items"]; !ok { + t.Logf("body: %s", string(data)) + t.Fatalf("missing items field in empty list (all lists should return an items field)") + } else if items == nil { + t.Logf("body: %s", string(data)) + t.Fatalf("nil items field from empty list (all lists should return non-nil empty items lists)") + } +} + func TestWatchSucceedsWithoutArgs(t *testing.T) { _, s := framework.RunAMaster(nil) defer s.Close()