add status subresource for deployment

This commit is contained in:
Mike Danese 2015-10-16 18:01:08 -07:00
parent 0884214fe0
commit 8acf01d620
8 changed files with 121 additions and 6 deletions

View File

@ -34,8 +34,9 @@ type DeploymentInterface interface {
List(label labels.Selector, field fields.Selector) (*extensions.DeploymentList, error)
Get(name string) (*extensions.Deployment, error)
Delete(name string, options *api.DeleteOptions) error
Create(Deployment *extensions.Deployment) (*extensions.Deployment, error)
Update(Deployment *extensions.Deployment) (*extensions.Deployment, error)
Create(*extensions.Deployment) (*extensions.Deployment, error)
Update(*extensions.Deployment) (*extensions.Deployment, error)
UpdateStatus(*extensions.Deployment) (*extensions.Deployment, error)
Watch(label labels.Selector, field fields.Selector, opts api.ListOptions) (watch.Interface, error)
}
@ -93,6 +94,12 @@ func (c *deployments) Update(deployment *extensions.Deployment) (result *extensi
return
}
func (c *deployments) UpdateStatus(deployment *extensions.Deployment) (result *extensions.Deployment, err error) {
result = &extensions.Deployment{}
err = c.client.Put().Namespace(c.ns).Resource("deployments").Name(deployment.Name).SubResource("status").Body(deployment).Do().Into(result)
return
}
// Watch returns a watch.Interface that watches the requested deployments.
func (c *deployments) Watch(label labels.Selector, field fields.Selector, opts api.ListOptions) (watch.Interface, error) {
return c.client.Get().

View File

@ -124,6 +124,27 @@ func TestDeploymentUpdate(t *testing.T) {
c.Validate(t, response, err)
}
func TestDeploymentUpdateStatus(t *testing.T) {
ns := api.NamespaceDefault
deployment := &extensions.Deployment{
ObjectMeta: api.ObjectMeta{
Name: "abc",
Namespace: ns,
ResourceVersion: "1",
},
}
c := &testClient{
Request: testRequest{
Method: "PUT",
Path: testapi.Extensions.ResourcePath(getDeploymentsResoureName(), ns, "abc") + "/status",
Query: buildQueryValues(nil),
},
Response: Response{StatusCode: 200, Body: deployment},
}
response, err := c.Setup(t).Deployments(ns).UpdateStatus(deployment)
c.Validate(t, response, err)
}
func TestDeploymentDelete(t *testing.T) {
ns := api.NamespaceDefault
c := &testClient{

View File

@ -72,6 +72,15 @@ func (c *FakeDeployments) Update(deployment *extensions.Deployment) (*extensions
return obj.(*extensions.Deployment), err
}
func (c *FakeDeployments) UpdateStatus(deployment *extensions.Deployment) (*extensions.Deployment, error) {
obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("deployments", "status", c.Namespace, deployment), deployment)
if obj == nil {
return nil, err
}
return obj.(*extensions.Deployment), err
}
func (c *FakeDeployments) Delete(name string, options *api.DeleteOptions) error {
_, err := c.Fake.Invokes(NewDeleteAction("deployments", c.Namespace, name), &extensions.Deployment{})
return err

View File

@ -251,7 +251,7 @@ func (d *DeploymentController) updateDeploymentStatus(allRCs []*api.ReplicationC
Replicas: totalReplicas,
UpdatedReplicas: updatedReplicas,
}
_, err := d.updateDeployment(&newDeployment)
_, err := d.client.Extensions().Deployments(deployment.ObjectMeta.Namespace).UpdateStatus(&newDeployment)
return err
}

View File

@ -1081,6 +1081,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
if isEnabled("deployments") {
deploymentStorage := deploymentetcd.NewStorage(dbClient("deployments"))
storage["deployments"] = deploymentStorage.Deployment
storage["deployments/status"] = deploymentStorage.Status
storage["deployments/scale"] = deploymentStorage.Scale
}
if isEnabled("jobs") {

View File

@ -35,15 +35,17 @@ import (
// DeploymentStorage includes dummy storage for Deployments and for Scale subresource.
type DeploymentStorage struct {
Deployment *REST
Status *StatusREST
Scale *ScaleREST
}
func NewStorage(s storage.Interface) DeploymentStorage {
deploymentRest := NewREST(s)
deploymentRest, deploymentStatusRest := NewREST(s)
deploymentRegistry := deployment.NewRegistry(deploymentRest)
return DeploymentStorage{
Deployment: deploymentRest,
Status: deploymentStatusRest,
Scale: &ScaleREST{registry: &deploymentRegistry},
}
}
@ -53,7 +55,7 @@ type REST struct {
}
// NewREST returns a RESTStorage object that will work against deployments.
func NewREST(s storage.Interface) *REST {
func NewREST(s storage.Interface) (*REST, *StatusREST) {
prefix := "/deployments"
store := &etcdgeneric.Etcd{
NewFunc: func() runtime.Object { return &extensions.Deployment{} },
@ -87,7 +89,23 @@ func NewREST(s storage.Interface) *REST {
Storage: s,
}
return &REST{store}
statusStore := *store
statusStore.UpdateStrategy = deployment.StatusStrategy
return &REST{store}, &StatusREST{store: &statusStore}
}
// StatusREST implements the REST endpoint for changing the status of a deployment
type StatusREST struct {
store *etcdgeneric.Etcd
}
func (r *StatusREST) New() runtime.Object {
return &extensions.Deployment{}
}
// Update alters the status subset of an object.
func (r *StatusREST) Update(ctx api.Context, obj runtime.Object) (runtime.Object, bool, error) {
return r.store.Update(ctx, obj)
}
type ScaleREST struct {

View File

@ -236,3 +236,39 @@ func TestScaleUpdate(t *testing.T) {
t.Errorf("wrong replicas count expected: %d got: %d", replicas, deployment.Spec.Replicas)
}
}
func TestStatusUpdate(t *testing.T) {
storage, fakeClient := newStorage(t)
ctx := api.WithNamespace(api.NewContext(), namespace)
key := etcdtest.AddPrefix("/deployments/" + namespace + "/" + name)
if _, err := fakeClient.Set(key, runtime.EncodeOrDie(testapi.Extensions.Codec(), &validDeployment), 0); err != nil {
t.Fatalf("unexpected error: %v", err)
}
update := extensions.Deployment{
ObjectMeta: validDeployment.ObjectMeta,
Spec: extensions.DeploymentSpec{
Replicas: 100,
},
Status: extensions.DeploymentStatus{
Replicas: 100,
},
}
if _, _, err := storage.Status.Update(ctx, &update); err != nil {
t.Fatalf("unexpected error: %v", err)
}
response, err := fakeClient.Get(key, false, false)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
var deployment extensions.Deployment
testapi.Extensions.Codec().DecodeInto([]byte(response.Node.Value), &deployment)
if deployment.Spec.Replicas != 7 {
t.Errorf("we expected .spec.replicas to not be updated but it was updated to %v", deployment.Spec.Replicas)
}
if deployment.Status.Replicas != 100 {
t.Errorf("we expected .status.replicas to be updated to 100 but it was %v", deployment.Status.Replicas)
}
}

View File

@ -46,6 +46,8 @@ func (deploymentStrategy) NamespaceScoped() bool {
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
func (deploymentStrategy) PrepareForCreate(obj runtime.Object) {
deployment := obj.(*extensions.Deployment)
deployment.Status = extensions.DeploymentStatus{}
}
// Validate validates a new deployment.
@ -61,6 +63,9 @@ func (deploymentStrategy) AllowCreateOnUpdate() bool {
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (deploymentStrategy) PrepareForUpdate(obj, old runtime.Object) {
newDeployment := obj.(*extensions.Deployment)
oldDeployment := old.(*extensions.Deployment)
newDeployment.Status = oldDeployment.Status
}
// ValidateUpdate is the default update validation for an end user.
@ -72,6 +77,24 @@ func (deploymentStrategy) AllowUnconditionalUpdate() bool {
return true
}
type deploymentStatusStrategy struct {
deploymentStrategy
}
var StatusStrategy = deploymentStatusStrategy{Strategy}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update of status
func (deploymentStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
newDeployment := obj.(*extensions.Deployment)
oldDeployment := old.(*extensions.Deployment)
newDeployment.Spec = oldDeployment.Spec
}
// ValidateUpdate is the default update validation for an end user updating status
func (deploymentStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) errs.ValidationErrorList {
return validation.ValidateDeploymentUpdate(old.(*extensions.Deployment), obj.(*extensions.Deployment))
}
// DeploymentToSelectableFields returns a field set that represents the object.
func DeploymentToSelectableFields(deployment *extensions.Deployment) fields.Set {
return generic.ObjectMetaFieldsSet(deployment.ObjectMeta)