Add name generation and normalize common create flows

Adds `ObjectMeta.GenerateName`, an optional string field that defines
name generation behavior if a Name is not provided.

Adds `pkg/api/rest`, which defines the default Kubernetes API pattern
for creation (and will cover update as well). Will allow registries
and REST objects to be merged by moving logic on api out of those places.

Add `pkg/api/rest/resttest`, which will be the test suite that verifies
a RESTStorage object follows the Kubernetes API conventions and begin
reducing our duplicated tests.
This commit is contained in:
Clayton Coleman
2015-01-27 18:56:38 -05:00
parent 6e415f760b
commit a7c9a12286
18 changed files with 662 additions and 70 deletions

View File

@@ -21,12 +21,12 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"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"
rc "github.com/GoogleCloudPlatform/kubernetes/pkg/controller"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
)
@@ -55,23 +55,14 @@ func (rs *REST) Create(ctx api.Context, obj runtime.Object) (<-chan apiserver.RE
if !ok {
return nil, 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"))
}
if len(controller.Name) == 0 {
controller.Name = string(util.NewUUID())
if err := rest.BeforeCreate(rest.ReplicationControllers, ctx, obj); err != nil {
return nil, err
}
if errs := validation.ValidateReplicationController(controller); len(errs) > 0 {
return nil, errors.NewInvalid("replicationController", controller.Name, errs)
}
api.FillObjectMetaSystemFields(ctx, &controller.ObjectMeta)
return apiserver.MakeAsync(func() (runtime.Object, error) {
err := rs.registry.CreateController(ctx, controller)
if err != nil {
return nil, err
if err := rs.registry.CreateController(ctx, controller); err != nil {
return apiserver.RESTResult{}, err
}
return rs.registry.GetController(ctx, controller.Name)
}), nil

View File

@@ -27,6 +27,7 @@ import (
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest/resttest"
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/registrytest"
@@ -106,6 +107,7 @@ func TestListControllerList(t *testing.T) {
}
}
// TODO: remove, this is sufficiently covered by other tests
func TestControllerDecode(t *testing.T) {
mockRegistry := registrytest.ControllerRegistry{}
storage := REST{
@@ -140,6 +142,7 @@ func TestControllerDecode(t *testing.T) {
}
}
// TODO: this is sufficiently covered by other tetss
func TestControllerParsing(t *testing.T) {
expectedController := api.ReplicationController{
ObjectMeta: api.ObjectMeta{
@@ -228,6 +231,7 @@ var validPodTemplate = api.PodTemplate{
},
}
// TODO: remove, this is sufficiently covered by other tests
func TestCreateController(t *testing.T) {
mockRegistry := registrytest.ControllerRegistry{}
mockPodRegistry := registrytest.PodRegistry{
@@ -274,6 +278,7 @@ func TestCreateController(t *testing.T) {
}
}
// TODO: remove, covered by TestCreate
func TestControllerStorageValidatesCreate(t *testing.T) {
mockRegistry := registrytest.ControllerRegistry{}
storage := REST{
@@ -376,6 +381,32 @@ func TestFillCurrentState(t *testing.T) {
}
}
// TODO: remove, covered by TestCreate
func TestCreateControllerWithGeneratedName(t *testing.T) {
storage := NewREST(&registrytest.ControllerRegistry{}, nil)
controller := &api.ReplicationController{
ObjectMeta: api.ObjectMeta{
Namespace: api.NamespaceDefault,
GenerateName: "rc-",
},
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Spec,
},
}
ctx := api.NewDefaultContext()
_, err := storage.Create(ctx, controller)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if controller.Name == "rc-" || !strings.HasPrefix(controller.Name, "rc-") {
t.Errorf("unexpected name: %#v", controller)
}
}
// TODO: remove, covered by TestCreate
func TestCreateControllerWithConflictingNamespace(t *testing.T) {
storage := REST{}
controller := &api.ReplicationController{
@@ -389,7 +420,7 @@ func TestCreateControllerWithConflictingNamespace(t *testing.T) {
}
if err == nil {
t.Errorf("Expected an error, but we didn't get one")
} else if strings.Index(err.Error(), "Controller.Namespace does not match the provided context") == -1 {
} else if strings.Contains(err.Error(), "Controller.Namespace does not match the provided context") {
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err.Error())
}
}
@@ -411,3 +442,25 @@ func TestUpdateControllerWithConflictingNamespace(t *testing.T) {
t.Errorf("Expected 'Controller.Namespace does not match the provided context' error, got '%v'", err.Error())
}
}
func TestCreate(t *testing.T) {
test := resttest.New(t, NewREST(&registrytest.ControllerRegistry{}, nil))
test.TestCreate(
// valid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{"a": "b"},
Template: &validPodTemplate.Spec,
},
},
// invalid
&api.ReplicationController{
Spec: api.ReplicationControllerSpec{
Replicas: 2,
Selector: map[string]string{},
Template: &validPodTemplate.Spec,
},
},
)
}