RESTStorage should not need to know about async behavior

Also make sure all POST operations return 201 by default.
Removes the remainder of the asych logic in RESTStorage and
leaves it up to the API server to expose that behavior.
This commit is contained in:
Clayton Coleman 2015-02-10 12:49:56 -05:00
parent 79cb93002e
commit 26f08b7807
19 changed files with 333 additions and 442 deletions

View File

@ -18,9 +18,9 @@ package binding
import (
"fmt"
"net/http"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
@ -44,15 +44,13 @@ func (*REST) New() runtime.Object {
}
// Create attempts to make the assignment indicated by the binding it recieves.
func (b *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (b *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
binding, ok := obj.(*api.Binding)
if !ok {
return nil, fmt.Errorf("incorrect type: %#v", obj)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := b.registry.ApplyBinding(ctx, binding); err != nil {
return nil, err
}
return &api.Status{Status: api.StatusSuccess}, nil
}), nil
if err := b.registry.ApplyBinding(ctx, binding); err != nil {
return nil, err
}
return &api.Status{Status: api.StatusSuccess, Code: http.StatusCreated}, nil
}

View File

@ -71,22 +71,20 @@ func TestRESTPost(t *testing.T) {
}
ctx := api.NewContext()
b := NewREST(mockRegistry)
resultChan, err := b.Create(ctx, item.b)
if err != nil {
result, err := b.Create(ctx, item.b)
if err != nil && item.err == nil {
t.Errorf("Unexpected error %v", err)
continue
}
var expect *api.Status
if item.err == nil {
expect = &api.Status{Status: api.StatusSuccess}
} else {
expect = &api.Status{
Status: api.StatusFailure,
Code: http.StatusInternalServerError,
Message: item.err.Error(),
}
if err == nil && item.err != nil {
t.Errorf("Unexpected error %v", err)
continue
}
if e, a := expect, (<-resultChan).Object; !reflect.DeepEqual(e, a) {
var expect interface{}
if item.err == nil {
expect = &api.Status{Status: api.StatusSuccess, Code: http.StatusCreated}
}
if e, a := expect, result; !reflect.DeepEqual(e, a) {
t.Errorf("%v: expected %#v, got %#v", i, e, a)
}
}

View File

@ -50,7 +50,7 @@ func NewREST(registry Registry, podLister PodLister) *REST {
}
// Create registers the given ReplicationController.
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
controller, ok := obj.(*api.ReplicationController)
if !ok {
return nil, fmt.Errorf("not a replication controller: %#v", obj)
@ -60,20 +60,16 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
return nil, err
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := rs.registry.CreateController(ctx, controller); err != nil {
err = rest.CheckGeneratedNameError(rest.ReplicationControllers, err, controller)
return apiserver.RESTResult{}, err
}
return rs.registry.GetController(ctx, controller.Name)
}), nil
if err := rs.registry.CreateController(ctx, controller); err != nil {
err = rest.CheckGeneratedNameError(rest.ReplicationControllers, err, controller)
return apiserver.RESTResult{}, err
}
return rs.registry.GetController(ctx, controller.Name)
}
// Delete asynchronously deletes the ReplicationController specified by its id.
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteController(ctx, id)
}), nil
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteController(ctx, id)
}
// Get obtains the ReplicationController specified by its id.
@ -117,24 +113,23 @@ func (*REST) NewList() runtime.Object {
// Update replaces a given ReplicationController instance with an existing
// instance in storage.registry.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
controller, ok := obj.(*api.ReplicationController)
if !ok {
return nil, fmt.Errorf("not a replication controller: %#v", obj)
return nil, false, fmt.Errorf("not a replication controller: %#v", obj)
}
if !api.ValidNamespace(ctx, &controller.ObjectMeta) {
return nil, errors.NewConflict("controller", controller.Namespace, fmt.Errorf("Controller.Namespace does not match the provided context"))
return nil, false, errors.NewConflict("controller", controller.Namespace, fmt.Errorf("Controller.Namespace does not match the provided context"))
}
if errs := validation.ValidateReplicationController(controller); len(errs) > 0 {
return nil, errors.NewInvalid("replicationController", controller.Name, errs)
return nil, false, errors.NewInvalid("replicationController", controller.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.UpdateController(ctx, controller)
if err != nil {
return nil, err
}
return rs.registry.GetController(ctx, controller.Name)
}), nil
err := rs.registry.UpdateController(ctx, controller)
if err != nil {
return nil, false, err
}
out, err := rs.registry.GetController(ctx, controller.Name)
return out, false, err
}
// Watch returns ReplicationController events via a watch.Interface.

View File

@ -22,7 +22,6 @@ import (
"io/ioutil"
"strings"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
@ -268,23 +267,17 @@ func TestCreateController(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
channel, err := storage.Create(ctx, controller)
obj, err := storage.Create(ctx, controller)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err != nil {
t.Errorf("unexpected error: %v", err)
if obj == nil {
t.Errorf("unexpected object")
}
if !api.HasObjectMetaSystemFieldValues(&controller.ObjectMeta) {
t.Errorf("storage did not populate object meta field values")
}
select {
case <-channel:
// expected case
case <-time.After(time.Millisecond * 100):
t.Error("Unexpected timeout from async channel")
}
}
// TODO: remove, covered by TestCreate
@ -338,9 +331,9 @@ func TestControllerStorageValidatesUpdate(t *testing.T) {
}
ctx := api.NewDefaultContext()
for _, failureCase := range failureCases {
c, err := storage.Update(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
c, created, err := storage.Update(ctx, &failureCase)
if c != nil || created {
t.Errorf("Expected nil object and not created")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)
@ -441,9 +434,9 @@ func TestUpdateControllerWithConflictingNamespace(t *testing.T) {
}
ctx := api.NewDefaultContext()
channel, err := storage.Update(ctx, controller)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
obj, created, err := storage.Update(ctx, controller)
if obj != nil || created {
t.Error("Expected a nil object, but we got a value or created was true")
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")

View File

@ -21,7 +21,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -59,7 +58,7 @@ func (rs *REST) Watch(ctx api.Context, label, field labels.Selector, resourceVer
}
// Create satisfies the RESTStorage interface.
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, fmt.Errorf("not an endpoints: %#v", obj)
@ -72,28 +71,25 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
api.FillObjectMetaSystemFields(ctx, &endpoints.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.UpdateEndpoints(ctx, endpoints)
if err != nil {
return nil, err
}
return rs.registry.GetEndpoints(ctx, endpoints.Name)
}), nil
err := rs.registry.UpdateEndpoints(ctx, endpoints)
if err != nil {
return nil, err
}
return rs.registry.GetEndpoints(ctx, endpoints.Name)
}
// Update satisfies the RESTStorage interface.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
endpoints, ok := obj.(*api.Endpoints)
if !ok {
return nil, fmt.Errorf("not an endpoints: %#v", obj)
return nil, false, fmt.Errorf("not an endpoints: %#v", obj)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.UpdateEndpoints(ctx, endpoints)
if err != nil {
return nil, err
}
return rs.registry.GetEndpoints(ctx, endpoints.Name)
}), nil
err := rs.registry.UpdateEndpoints(ctx, endpoints)
if err != nil {
return nil, false, err
}
out, err := rs.registry.GetEndpoints(ctx, endpoints.Name)
return out, false, err
}
// New implements the RESTStorage interface.

View File

@ -22,7 +22,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -42,7 +41,7 @@ func NewREST(registry generic.Registry) *REST {
}
}
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
event, ok := obj.(*api.Event)
if !ok {
return nil, fmt.Errorf("invalid object type")
@ -57,41 +56,38 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
api.FillObjectMetaSystemFields(ctx, &event.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Create(ctx, event.Name, event)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, event.Name)
}), nil
err := rs.registry.Create(ctx, event.Name, event)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, event.Name)
}
// Update replaces an existing Event instance in storage.registry, with the given instance.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
event, ok := obj.(*api.Event)
if !ok {
return nil, fmt.Errorf("not an event object: %#v", obj)
return nil, false, fmt.Errorf("not an event object: %#v", obj)
}
if api.NamespaceValue(ctx) != "" {
if !api.ValidNamespace(ctx, &event.ObjectMeta) {
return nil, errors.NewConflict("event", event.Namespace, fmt.Errorf("event.namespace does not match the provided context"))
return nil, false, errors.NewConflict("event", event.Namespace, fmt.Errorf("event.namespace does not match the provided context"))
}
}
if errs := validation.ValidateEvent(event); len(errs) > 0 {
return nil, errors.NewInvalid("event", event.Name, errs)
return nil, false, errors.NewInvalid("event", event.Name, errs)
}
api.FillObjectMetaSystemFields(ctx, &event.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Update(ctx, event.Name, event)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, event.Name)
}), nil
err := rs.registry.Update(ctx, event.Name, event)
if err != nil {
return nil, false, err
}
out, err := rs.registry.Get(ctx, event.Name)
return out, false, err
}
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
obj, err := rs.registry.Get(ctx, id)
if err != nil {
return nil, err
@ -100,9 +96,7 @@ func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult,
if !ok {
return nil, fmt.Errorf("invalid object type")
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, id)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, id)
}
func (rs *REST) Get(ctx api.Context, id string) (runtime.Object, error) {

View File

@ -89,7 +89,7 @@ func TestRESTCreate(t *testing.T) {
if !api.HasObjectMetaSystemFieldValues(&item.event.ObjectMeta) {
t.Errorf("storage did not populate object meta field values")
}
if e, a := item.event, (<-c).Object; !reflect.DeepEqual(e, a) {
if e, a := item.event, c; !reflect.DeepEqual(e, a) {
t.Errorf("diff: %s", util.ObjectDiff(e, a))
}
// Ensure we implement the interface
@ -100,11 +100,10 @@ func TestRESTCreate(t *testing.T) {
func TestRESTUpdate(t *testing.T) {
_, rest := NewTestREST()
eventA := testEvent("foo")
c, err := rest.Create(api.NewDefaultContext(), eventA)
_, err := rest.Create(api.NewDefaultContext(), eventA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
got, err := rest.Get(api.NewDefaultContext(), eventA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
@ -113,11 +112,10 @@ func TestRESTUpdate(t *testing.T) {
t.Errorf("diff: %s", util.ObjectDiff(e, a))
}
eventB := testEvent("bar")
u, err := rest.Update(api.NewDefaultContext(), eventB)
_, _, err = rest.Update(api.NewDefaultContext(), eventB)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-u
got2, err := rest.Get(api.NewDefaultContext(), eventB.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
@ -131,16 +129,15 @@ func TestRESTUpdate(t *testing.T) {
func TestRESTDelete(t *testing.T) {
_, rest := NewTestREST()
eventA := testEvent("foo")
c, err := rest.Create(api.NewDefaultContext(), eventA)
_, err := rest.Create(api.NewDefaultContext(), eventA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
c, err = rest.Delete(api.NewDefaultContext(), eventA.Name)
c, err := rest.Delete(api.NewDefaultContext(), eventA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if stat := (<-c).Object.(*api.Status); stat.Status != api.StatusSuccess {
if stat := c.(*api.Status); stat.Status != api.StatusSuccess {
t.Errorf("unexpected status: %v", stat)
}
}
@ -148,11 +145,10 @@ func TestRESTDelete(t *testing.T) {
func TestRESTGet(t *testing.T) {
_, rest := NewTestREST()
eventA := testEvent("foo")
c, err := rest.Create(api.NewDefaultContext(), eventA)
_, err := rest.Create(api.NewDefaultContext(), eventA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
got, err := rest.Get(api.NewDefaultContext(), eventA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)

View File

@ -22,7 +22,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -44,7 +43,7 @@ func NewREST(registry generic.Registry) *REST {
}
// Create a LimitRange object
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
limitRange, ok := obj.(*api.LimitRange)
if !ok {
return nil, fmt.Errorf("invalid object type")
@ -63,29 +62,27 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
api.FillObjectMetaSystemFields(ctx, &limitRange.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Create(ctx, limitRange.Name, limitRange)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, limitRange.Name)
}), nil
err := rs.registry.Create(ctx, limitRange.Name, limitRange)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, limitRange.Name)
}
// Update updates a LimitRange object.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
limitRange, ok := obj.(*api.LimitRange)
if !ok {
return nil, fmt.Errorf("invalid object type")
return nil, false, fmt.Errorf("invalid object type")
}
if !api.ValidNamespace(ctx, &limitRange.ObjectMeta) {
return nil, errors.NewConflict("limitRange", limitRange.Namespace, fmt.Errorf("LimitRange.Namespace does not match the provided context"))
return nil, false, errors.NewConflict("limitRange", limitRange.Namespace, fmt.Errorf("LimitRange.Namespace does not match the provided context"))
}
oldObj, err := rs.registry.Get(ctx, limitRange.Name)
if err != nil {
return nil, err
return nil, false, err
}
editLimitRange := oldObj.(*api.LimitRange)
@ -97,20 +94,18 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
editLimitRange.Spec = limitRange.Spec
if errs := validation.ValidateLimitRange(editLimitRange); len(errs) > 0 {
return nil, errors.NewInvalid("limitRange", editLimitRange.Name, errs)
return nil, false, errors.NewInvalid("limitRange", editLimitRange.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Update(ctx, editLimitRange.Name, editLimitRange)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, editLimitRange.Name)
}), nil
if err := rs.registry.Update(ctx, editLimitRange.Name, editLimitRange); err != nil {
return nil, false, err
}
out, err := rs.registry.Get(ctx, editLimitRange.Name)
return out, false, err
}
// Delete deletes the LimitRange with the specified name
func (rs *REST) Delete(ctx api.Context, name string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, name string) (runtime.Object, error) {
obj, err := rs.registry.Get(ctx, name)
if err != nil {
return nil, err
@ -119,9 +114,7 @@ func (rs *REST) Delete(ctx api.Context, name string) (<-chan apiserver.RESTResul
if !ok {
return nil, fmt.Errorf("invalid object type")
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, name)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, name)
}
// Get gets a LimitRange with the specified name

View File

@ -26,7 +26,6 @@ import (
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/master/ports"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -49,7 +48,7 @@ var ErrDoesNotExist = errors.New("The requested resource does not exist.")
var ErrNotHealty = errors.New("The requested minion is not healthy.")
// Create satisfies the RESTStorage interface.
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
minion, ok := obj.(*api.Node)
if !ok {
return nil, fmt.Errorf("not a minion: %#v", obj)
@ -59,17 +58,15 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
return nil, err
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := rs.registry.CreateMinion(ctx, minion); err != nil {
err = rest.CheckGeneratedNameError(rest.Nodes, err, minion)
return nil, err
}
return minion, nil
}), nil
if err := rs.registry.CreateMinion(ctx, minion); err != nil {
err = rest.CheckGeneratedNameError(rest.Nodes, err, minion)
return nil, err
}
return minion, nil
}
// Delete satisfies the RESTStorage interface.
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
minion, err := rs.registry.GetMinion(ctx, id)
if minion == nil {
return nil, ErrDoesNotExist
@ -77,9 +74,7 @@ func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult,
if err != nil {
return nil, err
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteMinion(ctx, id)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteMinion(ctx, id)
}
// Get satisfies the RESTStorage interface.
@ -108,10 +103,10 @@ func (*REST) NewList() runtime.Object {
}
// Update satisfies the RESTStorage interface.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
minion, ok := obj.(*api.Node)
if !ok {
return nil, fmt.Errorf("not a minion: %#v", obj)
return nil, false, fmt.Errorf("not a minion: %#v", obj)
}
// This is hacky, but minions don't really have a namespace, but kubectl currently automatically
// stuffs one in there. Fix it here temporarily until we fix kubectl
@ -123,7 +118,7 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
oldMinion, err := rs.registry.GetMinion(ctx, minion.Name)
if err != nil {
return nil, err
return nil, false, err
}
// This is hacky, but minion HostIP has been moved from spec to status since v1beta2. When updating
@ -134,16 +129,14 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
if errs := validation.ValidateMinionUpdate(oldMinion, minion); len(errs) > 0 {
return nil, kerrors.NewInvalid("minion", minion.Name, errs)
return nil, false, kerrors.NewInvalid("minion", minion.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.UpdateMinion(ctx, minion)
if err != nil {
return nil, err
}
return rs.registry.GetMinion(ctx, minion.Name)
}), nil
if err := rs.registry.UpdateMinion(ctx, minion); err != nil {
return nil, false, err
}
out, err := rs.registry.GetMinion(ctx, minion.Name)
return out, false, err
}
// Watch returns Minions events via a watch.Interface.

View File

@ -39,27 +39,25 @@ func TestMinionRegistryREST(t *testing.T) {
t.Errorf("has unexpected error: %v", err)
}
c, err := ms.Create(ctx, &api.Node{ObjectMeta: api.ObjectMeta{Name: "baz"}})
obj, err := ms.Create(ctx, &api.Node{ObjectMeta: api.ObjectMeta{Name: "baz"}})
if err != nil {
t.Fatalf("insert failed: %v", err)
}
obj := <-c
if !api.HasObjectMetaSystemFieldValues(&obj.Object.(*api.Node).ObjectMeta) {
if !api.HasObjectMetaSystemFieldValues(&obj.(*api.Node).ObjectMeta) {
t.Errorf("storage did not populate object meta field values")
}
if m, ok := obj.Object.(*api.Node); !ok || m.Name != "baz" {
if m, ok := obj.(*api.Node); !ok || m.Name != "baz" {
t.Errorf("insert return value was weird: %#v", obj)
}
if obj, err := ms.Get(ctx, "baz"); err != nil || obj.(*api.Node).Name != "baz" {
t.Errorf("insert didn't actually insert")
}
c, err = ms.Delete(ctx, "bar")
obj, err = ms.Delete(ctx, "bar")
if err != nil {
t.Fatalf("delete failed")
}
obj = <-c
if s, ok := obj.Object.(*api.Status); !ok || s.Status != api.StatusSuccess {
if s, ok := obj.(*api.Status); !ok || s.Status != api.StatusSuccess {
t.Errorf("delete return value was weird: %#v", obj)
}
if _, err := ms.Get(ctx, "bar"); !errors.IsNotFound(err) {
@ -103,7 +101,7 @@ func TestMinionRegistryValidUpdate(t *testing.T) {
"foo": "bar",
"baz": "home",
}
if _, err = storage.Update(ctx, minion); err != nil {
if _, _, err = storage.Update(ctx, minion); err != nil {
t.Errorf("Unexpected error: %v", err)
}
}
@ -136,7 +134,7 @@ func TestMinionRegistryValidatesCreate(t *testing.T) {
for _, failureCase := range failureCases {
c, err := storage.Create(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
t.Errorf("Expected nil object")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)

View File

@ -23,7 +23,6 @@ import (
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -44,48 +43,44 @@ func NewREST(registry generic.Registry) *REST {
}
// Create creates a Namespace object
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
namespace := obj.(*api.Namespace)
if err := rest.BeforeCreate(rest.Namespaces, ctx, obj); err != nil {
return nil, err
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := rs.registry.Create(ctx, namespace.Name, namespace); err != nil {
err = rest.CheckGeneratedNameError(rest.Namespaces, err, namespace)
return nil, err
}
return rs.registry.Get(ctx, namespace.Name)
}), nil
if err := rs.registry.Create(ctx, namespace.Name, namespace); err != nil {
err = rest.CheckGeneratedNameError(rest.Namespaces, err, namespace)
return nil, err
}
return rs.registry.Get(ctx, namespace.Name)
}
// Update updates a Namespace object.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
namespace, ok := obj.(*api.Namespace)
if !ok {
return nil, fmt.Errorf("not a namespace: %#v", obj)
return nil, false, fmt.Errorf("not a namespace: %#v", obj)
}
oldObj, err := rs.registry.Get(ctx, namespace.Name)
if err != nil {
return nil, err
return nil, false, err
}
oldNamespace := oldObj.(*api.Namespace)
if errs := validation.ValidateNamespaceUpdate(oldNamespace, namespace); len(errs) > 0 {
return nil, kerrors.NewInvalid("namespace", namespace.Name, errs)
return nil, false, kerrors.NewInvalid("namespace", namespace.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Update(ctx, oldNamespace.Name, oldNamespace)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, oldNamespace.Name)
}), nil
if err := rs.registry.Update(ctx, oldNamespace.Name, oldNamespace); err != nil {
return nil, false, err
}
out, err := rs.registry.Get(ctx, oldNamespace.Name)
return out, false, err
}
// Delete deletes the Namespace with the specified name
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
obj, err := rs.registry.Get(ctx, id)
if err != nil {
return nil, err
@ -94,10 +89,7 @@ func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult,
if !ok {
return nil, fmt.Errorf("invalid object type")
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, id)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, id)
}
func (rs *REST) Get(ctx api.Context, id string) (runtime.Object, error) {

View File

@ -78,7 +78,7 @@ func TestRESTCreate(t *testing.T) {
if !api.HasObjectMetaSystemFieldValues(&item.namespace.ObjectMeta) {
t.Errorf("storage did not populate object meta field values")
}
if e, a := item.namespace, (<-c).Object; !reflect.DeepEqual(e, a) {
if e, a := item.namespace, c; !reflect.DeepEqual(e, a) {
t.Errorf("diff: %s", util.ObjectDiff(e, a))
}
// Ensure we implement the interface
@ -89,11 +89,10 @@ func TestRESTCreate(t *testing.T) {
func TestRESTUpdate(t *testing.T) {
_, rest := NewTestREST()
namespaceA := testNamespace("foo")
c, err := rest.Create(api.NewDefaultContext(), namespaceA)
_, err := rest.Create(api.NewDefaultContext(), namespaceA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
got, err := rest.Get(api.NewDefaultContext(), namespaceA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
@ -102,11 +101,10 @@ func TestRESTUpdate(t *testing.T) {
t.Errorf("diff: %s", util.ObjectDiff(e, a))
}
namespaceB := testNamespace("foo")
u, err := rest.Update(api.NewDefaultContext(), namespaceB)
_, _, err = rest.Update(api.NewDefaultContext(), namespaceB)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-u
got2, err := rest.Get(api.NewDefaultContext(), namespaceB.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
@ -120,16 +118,15 @@ func TestRESTUpdate(t *testing.T) {
func TestRESTDelete(t *testing.T) {
_, rest := NewTestREST()
namespaceA := testNamespace("foo")
c, err := rest.Create(api.NewContext(), namespaceA)
_, err := rest.Create(api.NewContext(), namespaceA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
c, err = rest.Delete(api.NewContext(), namespaceA.Name)
c, err := rest.Delete(api.NewContext(), namespaceA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if stat := (<-c).Object.(*api.Status); stat.Status != api.StatusSuccess {
if stat := c.(*api.Status); stat.Status != api.StatusSuccess {
t.Errorf("unexpected status: %v", stat)
}
}
@ -137,11 +134,10 @@ func TestRESTDelete(t *testing.T) {
func TestRESTGet(t *testing.T) {
_, rest := NewTestREST()
namespaceA := testNamespace("foo")
c, err := rest.Create(api.NewContext(), namespaceA)
_, err := rest.Create(api.NewContext(), namespaceA)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
<-c
got, err := rest.Get(api.NewContext(), namespaceA.Name)
if err != nil {
t.Fatalf("Unexpected error %v", err)

View File

@ -25,7 +25,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
@ -55,32 +54,28 @@ func NewREST(config *RESTConfig) *REST {
}
}
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
pod := obj.(*api.Pod)
if err := rest.BeforeCreate(rest.Pods, ctx, obj); err != nil {
return nil, err
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := rs.registry.CreatePod(ctx, pod); err != nil {
err = rest.CheckGeneratedNameError(rest.Pods, err, pod)
return nil, err
}
return rs.registry.GetPod(ctx, pod.Name)
}), nil
if err := rs.registry.CreatePod(ctx, pod); err != nil {
err = rest.CheckGeneratedNameError(rest.Pods, err, pod)
return nil, err
}
return rs.registry.GetPod(ctx, pod.Name)
}
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
return apiserver.MakeAsync(func() (runtime.Object, error) {
namespace, found := api.NamespaceFrom(ctx)
if !found {
return &api.Status{Status: api.StatusFailure}, nil
}
rs.podCache.ClearPodStatus(namespace, id)
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
namespace, found := api.NamespaceFrom(ctx)
if !found {
return &api.Status{Status: api.StatusFailure}, nil
}
rs.podCache.ClearPodStatus(namespace, id)
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeletePod(ctx, id)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeletePod(ctx, id)
}
func (rs *REST) Get(ctx api.Context, id string) (runtime.Object, error) {
@ -167,20 +162,19 @@ func (*REST) NewList() runtime.Object {
return &api.PodList{}
}
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
pod := obj.(*api.Pod)
if !api.ValidNamespace(ctx, &pod.ObjectMeta) {
return nil, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context"))
return nil, false, errors.NewConflict("pod", pod.Namespace, fmt.Errorf("Pod.Namespace does not match the provided context"))
}
if errs := validation.ValidatePod(pod); len(errs) > 0 {
return nil, errors.NewInvalid("pod", pod.Name, errs)
return nil, false, errors.NewInvalid("pod", pod.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := rs.registry.UpdatePod(ctx, pod); err != nil {
return nil, err
}
return rs.registry.GetPod(ctx, pod.Name)
}), nil
if err := rs.registry.UpdatePod(ctx, pod); err != nil {
return nil, false, err
}
out, err := rs.registry.GetPod(ctx, pod.Name)
return out, false, err
}
// ResourceLocation returns a URL to which one can send traffic for the specified pod.

View File

@ -21,7 +21,6 @@ import (
"reflect"
"strings"
"testing"
"time"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
@ -31,6 +30,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
)
@ -55,9 +55,8 @@ func (f *fakeCache) ClearPodStatus(namespace, name string) {
f.clearedName = name
}
func expectApiStatusError(t *testing.T, ch <-chan apiserver.RESTResult, msg string) {
out := <-ch
status, ok := out.Object.(*api.Status)
func expectApiStatusError(t *testing.T, out runtime.Object, msg string) {
status, ok := out.(*api.Status)
if !ok {
t.Errorf("Expected an api.Status object, was %#v", out)
return
@ -67,9 +66,8 @@ func expectApiStatusError(t *testing.T, ch <-chan apiserver.RESTResult, msg stri
}
}
func expectPod(t *testing.T, ch <-chan apiserver.RESTResult) (*api.Pod, bool) {
out := <-ch
pod, ok := out.Object.(*api.Pod)
func expectPod(t *testing.T, out runtime.Object) (*api.Pod, bool) {
pod, ok := out.(*api.Pod)
if !ok || pod == nil {
t.Errorf("Expected an api.Pod object, was %#v", out)
return nil, false
@ -94,11 +92,10 @@ func TestCreatePodRegistryError(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
ch, err := storage.Create(ctx, pod)
if err != nil {
_, err := storage.Create(ctx, pod)
if err != podRegistry.Err {
t.Fatalf("unexpected error: %v", err)
}
expectApiStatusError(t, ch, podRegistry.Err.Error())
}
func TestCreatePodSetsIds(t *testing.T) {
@ -118,11 +115,10 @@ func TestCreatePodSetsIds(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
ch, err := storage.Create(ctx, pod)
if err != nil {
_, err := storage.Create(ctx, pod)
if err != podRegistry.Err {
t.Fatalf("unexpected error: %v", err)
}
expectApiStatusError(t, ch, podRegistry.Err.Error())
if len(podRegistry.Pod.Name) == 0 {
t.Errorf("Expected pod ID to be set, Got %#v", pod)
@ -149,11 +145,10 @@ func TestCreatePodSetsUID(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
ch, err := storage.Create(ctx, pod)
if err != nil {
_, err := storage.Create(ctx, pod)
if err != podRegistry.Err {
t.Fatalf("unexpected error: %v", err)
}
expectApiStatusError(t, ch, podRegistry.Err.Error())
if len(podRegistry.Pod.UID) == 0 {
t.Errorf("Expected pod UID to be set, Got %#v", pod)
@ -471,15 +466,12 @@ func TestCreatePod(t *testing.T) {
}
pod.Name = "foo"
ctx := api.NewDefaultContext()
channel, err := storage.Create(ctx, pod)
obj, err := storage.Create(ctx, pod)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
select {
case <-channel:
// Do nothing, this is expected.
case <-time.After(time.Millisecond * 100):
t.Error("Unexpected timeout on async channel")
if obj == nil {
t.Fatalf("unexpected object: %#v", obj)
}
if !api.HasObjectMetaSystemFieldValues(&podRegistry.Pod.ObjectMeta) {
t.Errorf("Expected ObjectMeta field values were populated")
@ -520,9 +512,9 @@ func TestUpdatePodWithConflictingNamespace(t *testing.T) {
}
ctx := api.NewDefaultContext()
channel, err := storage.Update(ctx, pod)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
obj, created, err := storage.Update(ctx, pod)
if obj != nil || created {
t.Error("Expected a nil channel, but we got a value or created")
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
@ -648,19 +640,12 @@ func TestDeletePod(t *testing.T) {
podCache: fakeCache,
}
ctx := api.NewDefaultContext()
channel, err := storage.Delete(ctx, "foo")
result, err := storage.Delete(ctx, "foo")
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var result apiserver.RESTResult
select {
case result = <-channel:
// Do nothing, this is expected.
case <-time.After(time.Millisecond * 100):
t.Error("Unexpected timeout on async channel")
}
if fakeCache.clearedNamespace != "default" || fakeCache.clearedName != "foo" {
t.Errorf("Unexpeceted cache delete: %s %s %#v", fakeCache.clearedName, fakeCache.clearedNamespace, result.Object)
t.Errorf("Unexpeceted cache delete: %s %s %#v", fakeCache.clearedName, fakeCache.clearedNamespace, result)
}
}

View File

@ -22,7 +22,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
@ -44,7 +43,7 @@ func NewREST(registry generic.Registry) *REST {
}
// Create a ResourceQuota object
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
resourceQuota, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, fmt.Errorf("invalid object type")
@ -66,29 +65,27 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
api.FillObjectMetaSystemFields(ctx, &resourceQuota.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Create(ctx, resourceQuota.Name, resourceQuota)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, resourceQuota.Name)
}), nil
err := rs.registry.Create(ctx, resourceQuota.Name, resourceQuota)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, resourceQuota.Name)
}
// Update updates a ResourceQuota object.
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
resourceQuota, ok := obj.(*api.ResourceQuota)
if !ok {
return nil, fmt.Errorf("invalid object type")
return nil, false, fmt.Errorf("invalid object type")
}
if !api.ValidNamespace(ctx, &resourceQuota.ObjectMeta) {
return nil, errors.NewConflict("resourceQuota", resourceQuota.Namespace, fmt.Errorf("ResourceQuota.Namespace does not match the provided context"))
return nil, false, errors.NewConflict("resourceQuota", resourceQuota.Namespace, fmt.Errorf("ResourceQuota.Namespace does not match the provided context"))
}
oldObj, err := rs.registry.Get(ctx, resourceQuota.Name)
if err != nil {
return nil, err
return nil, false, err
}
editResourceQuota := oldObj.(*api.ResourceQuota)
@ -100,20 +97,18 @@ func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
editResourceQuota.Spec = resourceQuota.Spec
if errs := validation.ValidateResourceQuota(editResourceQuota); len(errs) > 0 {
return nil, errors.NewInvalid("resourceQuota", editResourceQuota.Name, errs)
return nil, false, errors.NewInvalid("resourceQuota", editResourceQuota.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.Update(ctx, editResourceQuota.Name, editResourceQuota)
if err != nil {
return nil, err
}
return rs.registry.Get(ctx, editResourceQuota.Name)
}), nil
if err := rs.registry.Update(ctx, editResourceQuota.Name, editResourceQuota); err != nil {
return nil, false, err
}
out, err := rs.registry.Get(ctx, editResourceQuota.Name)
return out, false, err
}
// Delete deletes the ResourceQuota with the specified name
func (rs *REST) Delete(ctx api.Context, name string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, name string) (runtime.Object, error) {
obj, err := rs.registry.Get(ctx, name)
if err != nil {
return nil, err
@ -122,9 +117,7 @@ func (rs *REST) Delete(ctx api.Context, name string) (<-chan apiserver.RESTResul
if !ok {
return nil, fmt.Errorf("invalid object type")
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, name)
}), nil
return &api.Status{Status: api.StatusSuccess}, rs.registry.Delete(ctx, name)
}
// Get gets a ResourceQuota with the specified name

View File

@ -20,7 +20,6 @@ import (
"fmt"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
)
@ -42,15 +41,13 @@ func (*REST) New() runtime.Object {
}
// Create takes the incoming ResourceQuotaUsage and applies the latest status atomically to a ResourceQuota
func (b *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (b *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
resourceQuotaUsage, ok := obj.(*api.ResourceQuotaUsage)
if !ok {
return nil, fmt.Errorf("incorrect type: %#v", obj)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
if err := b.registry.ApplyStatus(ctx, resourceQuotaUsage); err != nil {
return nil, err
}
return &api.Status{Status: api.StatusSuccess}, nil
}), nil
if err := b.registry.ApplyStatus(ctx, resourceQuotaUsage); err != nil {
return nil, err
}
return &api.Status{Status: api.StatusSuccess}, nil
}

View File

@ -25,7 +25,6 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
"github.com/GoogleCloudPlatform/kubernetes/pkg/apiserver"
"github.com/GoogleCloudPlatform/kubernetes/pkg/cloudprovider"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/minion"
@ -80,7 +79,7 @@ func reloadIPsFromStorage(ipa *ipAllocator, registry Registry) {
}
}
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Create(ctx api.Context, obj runtime.Object) (runtime.Object, error) {
service := obj.(*api.Service)
if err := rest.BeforeCreate(rest.Services, ctx, obj); err != nil {
@ -102,61 +101,59 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
}
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
// TODO: Move this to post-creation rectification loop, so that we make/remove external load balancers
// correctly no matter what http operations happen.
if service.Spec.CreateExternalLoadBalancer {
if rs.cloud == nil {
return nil, fmt.Errorf("requested an external service, but no cloud provider supplied.")
}
if service.Spec.Protocol != api.ProtocolTCP {
// TODO: Support UDP here too.
return nil, fmt.Errorf("external load balancers for non TCP services are not currently supported.")
}
balancer, ok := rs.cloud.TCPLoadBalancer()
if !ok {
return nil, fmt.Errorf("the cloud provider does not support external TCP load balancers.")
}
zones, ok := rs.cloud.Zones()
if !ok {
return nil, fmt.Errorf("the cloud provider does not support zone enumeration.")
}
hosts, err := rs.machines.ListMinions(ctx)
if err != nil {
return nil, err
}
zone, err := zones.GetZone()
if err != nil {
return nil, err
}
// TODO: We should be able to rely on valid input, and not do defaulting here.
var affinityType api.AffinityType = service.Spec.SessionAffinity
if affinityType == "" {
affinityType = api.AffinityTypeNone
}
if len(service.Spec.PublicIPs) > 0 {
for _, publicIP := range service.Spec.PublicIPs {
_, err = balancer.CreateTCPLoadBalancer(service.Name, zone.Region, net.ParseIP(publicIP), service.Spec.Port, hostsFromMinionList(hosts), affinityType)
if err != nil {
// TODO: have to roll-back any successful calls.
return nil, err
}
}
} else {
ip, err := balancer.CreateTCPLoadBalancer(service.Name, zone.Region, nil, service.Spec.Port, hostsFromMinionList(hosts), affinityType)
if err != nil {
return nil, err
}
service.Spec.PublicIPs = []string{ip.String()}
}
// TODO: Move this to post-creation rectification loop, so that we make/remove external load balancers
// correctly no matter what http operations happen.
if service.Spec.CreateExternalLoadBalancer {
if rs.cloud == nil {
return nil, fmt.Errorf("requested an external service, but no cloud provider supplied.")
}
if err := rs.registry.CreateService(ctx, service); err != nil {
err = rest.CheckGeneratedNameError(rest.Services, err, service)
if service.Spec.Protocol != api.ProtocolTCP {
// TODO: Support UDP here too.
return nil, fmt.Errorf("external load balancers for non TCP services are not currently supported.")
}
balancer, ok := rs.cloud.TCPLoadBalancer()
if !ok {
return nil, fmt.Errorf("the cloud provider does not support external TCP load balancers.")
}
zones, ok := rs.cloud.Zones()
if !ok {
return nil, fmt.Errorf("the cloud provider does not support zone enumeration.")
}
hosts, err := rs.machines.ListMinions(ctx)
if err != nil {
return nil, err
}
return rs.registry.GetService(ctx, service.Name)
}), nil
zone, err := zones.GetZone()
if err != nil {
return nil, err
}
// TODO: We should be able to rely on valid input, and not do defaulting here.
var affinityType api.AffinityType = service.Spec.SessionAffinity
if affinityType == "" {
affinityType = api.AffinityTypeNone
}
if len(service.Spec.PublicIPs) > 0 {
for _, publicIP := range service.Spec.PublicIPs {
_, err = balancer.CreateTCPLoadBalancer(service.Name, zone.Region, net.ParseIP(publicIP), service.Spec.Port, hostsFromMinionList(hosts), affinityType)
if err != nil {
// TODO: have to roll-back any successful calls.
return nil, err
}
}
} else {
ip, err := balancer.CreateTCPLoadBalancer(service.Name, zone.Region, nil, service.Spec.Port, hostsFromMinionList(hosts), affinityType)
if err != nil {
return nil, err
}
service.Spec.PublicIPs = []string{ip.String()}
}
}
if err := rs.registry.CreateService(ctx, service); err != nil {
err = rest.CheckGeneratedNameError(rest.Services, err, service)
return nil, err
}
return rs.registry.GetService(ctx, service.Name)
}
func hostsFromMinionList(list *api.NodeList) []string {
@ -167,16 +164,14 @@ func hostsFromMinionList(list *api.NodeList) []string {
return result
}
func (rs *REST) Delete(ctx api.Context, id string) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Delete(ctx api.Context, id string) (runtime.Object, error) {
service, err := rs.registry.GetService(ctx, id)
if err != nil {
return nil, err
}
rs.portalMgr.Release(net.ParseIP(service.Spec.PortalIP))
return apiserver.MakeAsync(func() (runtime.Object, error) {
rs.deleteExternalLoadBalancer(service)
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteService(ctx, id)
}), nil
rs.deleteExternalLoadBalancer(service)
return &api.Status{Status: api.StatusSuccess}, rs.registry.DeleteService(ctx, id)
}
func (rs *REST) Get(ctx api.Context, id string) (runtime.Object, error) {
@ -217,30 +212,29 @@ func (*REST) NewList() runtime.Object {
return &api.Service{}
}
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (<-chan apiserver.RESTResult, error) {
func (rs *REST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
service := obj.(*api.Service)
if !api.ValidNamespace(ctx, &service.ObjectMeta) {
return nil, errors.NewConflict("service", service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context"))
return nil, false, errors.NewConflict("service", service.Namespace, fmt.Errorf("Service.Namespace does not match the provided context"))
}
oldService, err := rs.registry.GetService(ctx, service.Name)
if err != nil {
return nil, err
return nil, false, err
}
// Copy over non-user fields
// TODO: make this a merge function
if errs := validation.ValidateServiceUpdate(oldService, service); len(errs) > 0 {
return nil, errors.NewInvalid("service", service.Name, errs)
return nil, false, errors.NewInvalid("service", service.Name, errs)
}
return apiserver.MakeAsync(func() (runtime.Object, error) {
// TODO: check to see if external load balancer status changed
err = rs.registry.UpdateService(ctx, service)
if err != nil {
return nil, err
}
return rs.registry.GetService(ctx, service.Name)
}), nil
// TODO: check to see if external load balancer status changed
err = rs.registry.UpdateService(ctx, service)
if err != nil {
return nil, false, err
}
out, err := rs.registry.GetService(ctx, service.Name)
return out, false, err
}
// ResourceLocation returns a URL to which one can send traffic for the specified service.

View File

@ -56,9 +56,8 @@ func TestServiceRegistryCreate(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c, _ := storage.Create(ctx, svc)
created_svc := <-c
created_service := created_svc.Object.(*api.Service)
created_svc, _ := storage.Create(ctx, svc)
created_service := created_svc.(*api.Service)
if !api.HasObjectMetaSystemFieldValues(&created_service.ObjectMeta) {
t.Errorf("storage did not populate object meta field values")
}
@ -109,7 +108,7 @@ func TestServiceStorageValidatesCreate(t *testing.T) {
for _, failureCase := range failureCases {
c, err := storage.Create(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
t.Errorf("Expected nil object")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)
@ -129,7 +128,7 @@ func TestServiceRegistryUpdate(t *testing.T) {
},
})
storage := NewREST(registry, nil, nil, makeIPNet(t))
c, err := storage.Update(ctx, &api.Service{
updated_svc, created, err := storage.Update(ctx, &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo"},
Spec: api.ServiceSpec{
Port: 6502,
@ -141,11 +140,13 @@ func TestServiceRegistryUpdate(t *testing.T) {
if err != nil {
t.Fatalf("Expected no error: %v", err)
}
if c == nil {
t.Errorf("Expected non-nil channel")
if updated_svc == nil {
t.Errorf("Expected non-nil object")
}
updated_svc := <-c
updated_service := updated_svc.Object.(*api.Service)
if created {
t.Errorf("expected not created")
}
updated_service := updated_svc.(*api.Service)
if updated_service.Name != "foo" {
t.Errorf("Expected foo, but got %v", updated_service.Name)
}
@ -186,9 +187,9 @@ func TestServiceStorageValidatesUpdate(t *testing.T) {
},
}
for _, failureCase := range failureCases {
c, err := storage.Update(ctx, &failureCase)
if c != nil {
t.Errorf("Expected nil channel")
c, created, err := storage.Update(ctx, &failureCase)
if c != nil || created {
t.Errorf("Expected nil object or created false")
}
if !errors.IsInvalid(err) {
t.Errorf("Expected to get an invalid resource error, got %v", err)
@ -212,8 +213,7 @@ func TestServiceRegistryExternalService(t *testing.T) {
SessionAffinity: api.AffinityTypeNone,
},
}
c, _ := storage.Create(ctx, svc)
<-c
storage.Create(ctx, svc)
if len(fakeCloud.Calls) != 2 || fakeCloud.Calls[0] != "get-zone" || fakeCloud.Calls[1] != "create" {
t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
}
@ -244,8 +244,7 @@ func TestServiceRegistryExternalServiceError(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c, _ := storage.Create(ctx, svc)
<-c
storage.Create(ctx, svc)
if len(fakeCloud.Calls) != 1 || fakeCloud.Calls[0] != "get-zone" {
t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
}
@ -269,8 +268,7 @@ func TestServiceRegistryDelete(t *testing.T) {
},
}
registry.CreateService(ctx, svc)
c, _ := storage.Delete(ctx, svc.Name)
<-c
storage.Delete(ctx, svc.Name)
if len(fakeCloud.Calls) != 0 {
t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
}
@ -295,8 +293,7 @@ func TestServiceRegistryDeleteExternal(t *testing.T) {
},
}
registry.CreateService(ctx, svc)
c, _ := storage.Delete(ctx, svc.Name)
<-c
storage.Delete(ctx, svc.Name)
if len(fakeCloud.Calls) != 2 || fakeCloud.Calls[0] != "get-zone" || fakeCloud.Calls[1] != "delete" {
t.Errorf("Unexpected call(s): %#v", fakeCloud.Calls)
}
@ -413,9 +410,8 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c1, _ := rest.Create(ctx, svc1)
created_svc1 := <-c1
created_service_1 := created_svc1.Object.(*api.Service)
created_svc1, _ := rest.Create(ctx, svc1)
created_service_1 := created_svc1.(*api.Service)
if created_service_1.Name != "foo" {
t.Errorf("Expected foo, but got %v", created_service_1.Name)
}
@ -432,9 +428,8 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
SessionAffinity: api.AffinityTypeNone,
}}
ctx = api.NewDefaultContext()
c2, _ := rest.Create(ctx, svc2)
created_svc2 := <-c2
created_service_2 := created_svc2.Object.(*api.Service)
created_svc2, _ := rest.Create(ctx, svc2)
created_service_2 := created_svc2.(*api.Service)
if created_service_2.Name != "bar" {
t.Errorf("Expected bar, but got %v", created_service_2.Name)
}
@ -453,9 +448,8 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
},
}
ctx = api.NewDefaultContext()
c3, _ := rest.Create(ctx, svc3)
created_svc3 := <-c3
created_service_3 := created_svc3.Object.(*api.Service)
created_svc3, _ := rest.Create(ctx, svc3)
created_service_3 := created_svc3.(*api.Service)
if created_service_3.Spec.PortalIP != "1.2.3.93" { // specific IP
t.Errorf("Unexpected PortalIP: %s", created_service_3.Spec.PortalIP)
}
@ -478,9 +472,8 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c1, _ := rest.Create(ctx, svc1)
created_svc1 := <-c1
created_service_1 := created_svc1.Object.(*api.Service)
created_svc1, _ := rest.Create(ctx, svc1)
created_service_1 := created_svc1.(*api.Service)
if created_service_1.Name != "foo" {
t.Errorf("Expected foo, but got %v", created_service_1.Name)
}
@ -488,8 +481,7 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
t.Errorf("Unexpected PortalIP: %s", created_service_1.Spec.PortalIP)
}
c, _ := rest.Delete(ctx, created_service_1.Name)
<-c
rest.Delete(ctx, created_service_1.Name)
svc2 := &api.Service{
ObjectMeta: api.ObjectMeta{Name: "bar"},
@ -501,9 +493,8 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
},
}
ctx = api.NewDefaultContext()
c2, _ := rest.Create(ctx, svc2)
created_svc2 := <-c2
created_service_2 := created_svc2.Object.(*api.Service)
created_svc2, _ := rest.Create(ctx, svc2)
created_service_2 := created_svc2.(*api.Service)
if created_service_2.Name != "bar" {
t.Errorf("Expected bar, but got %v", created_service_2.Name)
}
@ -529,9 +520,8 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c, _ := rest.Create(ctx, svc)
created_svc := <-c
created_service := created_svc.Object.(*api.Service)
created_svc, _ := rest.Create(ctx, svc)
created_service := created_svc.(*api.Service)
if created_service.Spec.Port != 6502 {
t.Errorf("Expected port 6502, but got %v", created_service.Spec.Port)
}
@ -543,9 +533,8 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
*update = *created_service
update.Spec.Port = 6503
c, _ = rest.Update(ctx, update)
updated_svc := <-c
updated_service := updated_svc.Object.(*api.Service)
updated_svc, _, _ := rest.Update(ctx, update)
updated_service := updated_svc.(*api.Service)
if updated_service.Spec.Port != 6503 {
t.Errorf("Expected port 6503, but got %v", updated_service.Spec.Port)
}
@ -554,7 +543,7 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
update.Spec.Port = 6503
update.Spec.PortalIP = "1.2.3.76" // error
_, err := rest.Update(ctx, update)
_, _, err := rest.Update(ctx, update)
if err == nil || !errors.IsInvalid(err) {
t.Error("Unexpected error type: %v", err)
}
@ -578,9 +567,8 @@ func TestServiceRegistryIPExternalLoadBalancer(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c, _ := rest.Create(ctx, svc)
created_svc := <-c
created_service := created_svc.Object.(*api.Service)
created_svc, _ := rest.Create(ctx, svc)
created_service := created_svc.(*api.Service)
if created_service.Spec.Port != 6502 {
t.Errorf("Expected port 6502, but got %v", created_service.Spec.Port)
}
@ -591,7 +579,7 @@ func TestServiceRegistryIPExternalLoadBalancer(t *testing.T) {
update := new(api.Service)
*update = *created_service
_, err := rest.Update(ctx, update)
_, _, err := rest.Update(ctx, update)
if err != nil {
t.Errorf("Unexpected error %v", err)
}
@ -614,8 +602,7 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
},
}
ctx := api.NewDefaultContext()
c, _ := rest1.Create(ctx, svc)
<-c
rest1.Create(ctx, svc)
svc = &api.Service{
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
Spec: api.ServiceSpec{
@ -625,8 +612,7 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
SessionAffinity: api.AffinityTypeNone,
},
}
c, _ = rest1.Create(ctx, svc)
<-c
rest1.Create(ctx, svc)
// This will reload from storage, finding the previous 2
rest2 := NewREST(registry, fakeCloud, registrytest.NewMinionRegistry(machines, api.NodeResources{}), makeIPNet(t))
@ -641,9 +627,8 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
SessionAffinity: api.AffinityTypeNone,
},
}
c, _ = rest2.Create(ctx, svc)
created_svc := <-c
created_service := created_svc.Object.(*api.Service)
created_svc, _ := rest2.Create(ctx, svc)
created_service := created_svc.(*api.Service)
if created_service.Spec.PortalIP != "1.2.3.3" {
t.Errorf("Unexpected PortalIP: %s", created_service.Spec.PortalIP)
}
@ -657,9 +642,9 @@ func TestCreateServiceWithConflictingNamespace(t *testing.T) {
}
ctx := api.NewDefaultContext()
channel, err := storage.Create(ctx, service)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
obj, err := storage.Create(ctx, service)
if obj != nil {
t.Error("Expected a nil object, but we got a value")
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
@ -675,9 +660,9 @@ func TestUpdateServiceWithConflictingNamespace(t *testing.T) {
}
ctx := api.NewDefaultContext()
channel, err := storage.Update(ctx, service)
if channel != nil {
t.Error("Expected a nil channel, but we got a value")
obj, created, err := storage.Update(ctx, service)
if obj != nil || created {
t.Error("Expected a nil object, but we got a value or created was true")
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")

View File

@ -175,6 +175,7 @@ var timeoutFlag = "?timeout=60s"
// Requests to try. Each one should be forbidden or not forbidden
// depending on the authentication and authorization setup of the master.
var code200 = map[int]bool{200: true}
var code201 = map[int]bool{201: true}
var code400 = map[int]bool{400: true}
var code403 = map[int]bool{403: true}
var code404 = map[int]bool{404: true}
@ -197,7 +198,7 @@ func getTestRequests() []struct {
}{
// Normal methods on pods
{"GET", "/api/v1beta1/pods", "", code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code201},
{"PUT", "/api/v1beta1/pods/a" + timeoutFlag, aPod, code200},
{"GET", "/api/v1beta1/pods", "", code200},
{"GET", "/api/v1beta1/pods/a", "", code200},
@ -217,7 +218,7 @@ func getTestRequests() []struct {
// Normal methods on services
{"GET", "/api/v1beta1/services", "", code200},
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code200},
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code201},
{"PUT", "/api/v1beta1/services/a" + timeoutFlag, aService, code200},
{"GET", "/api/v1beta1/services", "", code200},
{"GET", "/api/v1beta1/services/a", "", code200},
@ -225,7 +226,7 @@ func getTestRequests() []struct {
// Normal methods on replicationControllers
{"GET", "/api/v1beta1/replicationControllers", "", code200},
{"POST", "/api/v1beta1/replicationControllers" + timeoutFlag, aRC, code200},
{"POST", "/api/v1beta1/replicationControllers" + timeoutFlag, aRC, code201},
{"PUT", "/api/v1beta1/replicationControllers/a" + timeoutFlag, aRC, code200},
{"GET", "/api/v1beta1/replicationControllers", "", code200},
{"GET", "/api/v1beta1/replicationControllers/a", "", code200},
@ -233,7 +234,7 @@ func getTestRequests() []struct {
// Normal methods on endpoints
{"GET", "/api/v1beta1/endpoints", "", code200},
{"POST", "/api/v1beta1/endpoints" + timeoutFlag, aEndpoints, code200},
{"POST", "/api/v1beta1/endpoints" + timeoutFlag, aEndpoints, code201},
{"PUT", "/api/v1beta1/endpoints/a" + timeoutFlag, aEndpoints, code200},
{"GET", "/api/v1beta1/endpoints", "", code200},
{"GET", "/api/v1beta1/endpoints/a", "", code200},
@ -241,7 +242,7 @@ func getTestRequests() []struct {
// Normal methods on minions
{"GET", "/api/v1beta1/minions", "", code200},
{"POST", "/api/v1beta1/minions" + timeoutFlag, aMinion, code200},
{"POST", "/api/v1beta1/minions" + timeoutFlag, aMinion, code201},
{"PUT", "/api/v1beta1/minions/a" + timeoutFlag, aMinion, code409}, // See #2115 about why 409
{"GET", "/api/v1beta1/minions", "", code200},
{"GET", "/api/v1beta1/minions/a", "", code200},
@ -249,7 +250,7 @@ func getTestRequests() []struct {
// Normal methods on events
{"GET", "/api/v1beta1/events", "", code200},
{"POST", "/api/v1beta1/events" + timeoutFlag, aEvent, code200},
{"POST", "/api/v1beta1/events" + timeoutFlag, aEvent, code201},
{"PUT", "/api/v1beta1/events/a" + timeoutFlag, aEvent, code200},
{"GET", "/api/v1beta1/events", "", code200},
{"GET", "/api/v1beta1/events", "", code200},
@ -258,8 +259,8 @@ func getTestRequests() []struct {
// Normal methods on bindings
{"GET", "/api/v1beta1/bindings", "", code405}, // Bindings are write-only
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code200}, // Need a pod to bind or you get a 404
{"POST", "/api/v1beta1/bindings" + timeoutFlag, aBinding, code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag, aPod, code201}, // Need a pod to bind or you get a 404
{"POST", "/api/v1beta1/bindings" + timeoutFlag, aBinding, code201},
{"PUT", "/api/v1beta1/bindings/a" + timeoutFlag, aBinding, code404},
{"GET", "/api/v1beta1/bindings", "", code405},
{"GET", "/api/v1beta1/bindings/a", "", code404}, // No bindings instances
@ -727,7 +728,7 @@ func TestNamespaceAuthorization(t *testing.T) {
statusCodes map[int]bool // allowed status codes.
}{
{"POST", "/api/v1beta1/pods" + timeoutFlag + "&namespace=foo", "foo", aPod, code200},
{"POST", "/api/v1beta1/pods" + timeoutFlag + "&namespace=foo", "foo", aPod, code201},
{"GET", "/api/v1beta1/pods?namespace=foo", "foo", "", code200},
{"GET", "/api/v1beta1/pods/a?namespace=foo", "foo", "", code200},
{"DELETE", "/api/v1beta1/pods/a" + timeoutFlag + "&namespace=foo", "foo", "", code200},
@ -838,7 +839,7 @@ func TestKindAuthorization(t *testing.T) {
body string
statusCodes map[int]bool // allowed status codes.
}{
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code200},
{"POST", "/api/v1beta1/services" + timeoutFlag, aService, code201},
{"GET", "/api/v1beta1/services", "", code200},
{"GET", "/api/v1beta1/services/a", "", code200},
{"DELETE", "/api/v1beta1/services/a" + timeoutFlag, "", code200},