Add status subresource to HorizontalPodAutoscaler
This commit is contained in:
		@@ -51,6 +51,19 @@ func ValidateHorizontalPodAutoscalerName(name string, prefix bool) (bool, string
 | 
			
		||||
	return apivalidation.ValidateReplicationControllerName(name, prefix)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateResourceConsumption(consumption *extensions.ResourceConsumption, fieldName string) errs.ValidationErrorList {
 | 
			
		||||
	allErrs := errs.ValidationErrorList{}
 | 
			
		||||
	resource := consumption.Resource.String()
 | 
			
		||||
	if resource != string(api.ResourceMemory) && resource != string(api.ResourceCPU) {
 | 
			
		||||
		allErrs = append(allErrs, errs.NewFieldInvalid(fieldName+".resource", resource, "resource not supported by autoscaler"))
 | 
			
		||||
	}
 | 
			
		||||
	quantity := consumption.Quantity.Value()
 | 
			
		||||
	if quantity < 0 {
 | 
			
		||||
		allErrs = append(allErrs, errs.NewFieldInvalid(fieldName+".quantity", quantity, "must be non-negative"))
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validateHorizontalPodAutoscalerSpec(autoscaler extensions.HorizontalPodAutoscalerSpec) errs.ValidationErrorList {
 | 
			
		||||
	allErrs := errs.ValidationErrorList{}
 | 
			
		||||
	if autoscaler.MinReplicas < 0 {
 | 
			
		||||
@@ -87,6 +100,19 @@ func ValidateHorizontalPodAutoscalerUpdate(newAutoscler, oldAutoscaler *extensio
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ValidateHorizontalPodAutoscalerStatusUpdate(controller, oldController *extensions.HorizontalPodAutoscaler) errs.ValidationErrorList {
 | 
			
		||||
	allErrs := errs.ValidationErrorList{}
 | 
			
		||||
	allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&controller.ObjectMeta, &oldController.ObjectMeta).Prefix("metadata")...)
 | 
			
		||||
 | 
			
		||||
	status := controller.Status
 | 
			
		||||
	allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.CurrentReplicas), "currentReplicas")...)
 | 
			
		||||
	allErrs = append(allErrs, apivalidation.ValidatePositiveField(int64(status.DesiredReplicas), "desiredReplicas")...)
 | 
			
		||||
	if status.CurrentConsumption != nil {
 | 
			
		||||
		allErrs = append(allErrs, validateResourceConsumption(status.CurrentConsumption, "currentConsumption")...)
 | 
			
		||||
	}
 | 
			
		||||
	return allErrs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ValidateThirdPartyResourceUpdate(old, update *extensions.ThirdPartyResource) errs.ValidationErrorList {
 | 
			
		||||
	return ValidateThirdPartyResource(update)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -36,6 +36,7 @@ type HorizontalPodAutoscalerInterface interface {
 | 
			
		||||
	Delete(name string, options *api.DeleteOptions) error
 | 
			
		||||
	Create(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error)
 | 
			
		||||
	Update(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error)
 | 
			
		||||
	UpdateStatus(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error)
 | 
			
		||||
	Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -94,6 +95,13 @@ func (c *horizontalPodAutoscalers) Update(horizontalPodAutoscaler *extensions.Ho
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// UpdateStatus takes the representation of a horizontalPodAutoscaler and updates it.  Returns the server's representation of the horizontalPodAutoscaler, and an error, if it occurs.
 | 
			
		||||
func (c *horizontalPodAutoscalers) UpdateStatus(horizontalPodAutoscaler *extensions.HorizontalPodAutoscaler) (result *extensions.HorizontalPodAutoscaler, err error) {
 | 
			
		||||
	result = &extensions.HorizontalPodAutoscaler{}
 | 
			
		||||
	err = c.client.Put().Namespace(c.ns).Resource("horizontalPodAutoscalers").Name(horizontalPodAutoscaler.Name).SubResource("status").Body(horizontalPodAutoscaler).Do().Into(result)
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Watch returns a watch.Interface that watches the requested horizontalPodAutoscalers.
 | 
			
		||||
func (c *horizontalPodAutoscalers) Watch(label labels.Selector, field fields.Selector, resourceVersion string) (watch.Interface, error) {
 | 
			
		||||
	return c.client.Get().
 | 
			
		||||
 
 | 
			
		||||
@@ -120,6 +120,23 @@ func TestHorizontalPodAutoscalerUpdate(t *testing.T) {
 | 
			
		||||
	c.Validate(t, response, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHorizontalPodAutoscalerUpdateStatus(t *testing.T) {
 | 
			
		||||
	ns := api.NamespaceDefault
 | 
			
		||||
	horizontalPodAutoscaler := &extensions.HorizontalPodAutoscaler{
 | 
			
		||||
		ObjectMeta: api.ObjectMeta{
 | 
			
		||||
			Name:            "abc",
 | 
			
		||||
			Namespace:       ns,
 | 
			
		||||
			ResourceVersion: "1",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	c := &testClient{
 | 
			
		||||
		Request:  testRequest{Method: "PUT", Path: testapi.Extensions.ResourcePath(getHorizontalPodAutoscalersResoureName(), ns, "abc") + "/status", Query: buildQueryValues(nil)},
 | 
			
		||||
		Response: Response{StatusCode: 200, Body: horizontalPodAutoscaler},
 | 
			
		||||
	}
 | 
			
		||||
	response, err := c.Setup(t).Experimental().HorizontalPodAutoscalers(ns).UpdateStatus(horizontalPodAutoscaler)
 | 
			
		||||
	c.Validate(t, response, err)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestHorizontalPodAutoscalerDelete(t *testing.T) {
 | 
			
		||||
	ns := api.NamespaceDefault
 | 
			
		||||
	c := &testClient{
 | 
			
		||||
 
 | 
			
		||||
@@ -72,6 +72,14 @@ func (c *FakeHorizontalPodAutoscalers) Update(a *extensions.HorizontalPodAutosca
 | 
			
		||||
	return obj.(*extensions.HorizontalPodAutoscaler), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *FakeHorizontalPodAutoscalers) UpdateStatus(a *extensions.HorizontalPodAutoscaler) (*extensions.HorizontalPodAutoscaler, error) {
 | 
			
		||||
	obj, err := c.Fake.Invokes(NewUpdateSubresourceAction("horizontalpodautoscalers", "status", c.Namespace, a), &extensions.HorizontalPodAutoscaler{})
 | 
			
		||||
	if obj == nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return obj.(*extensions.HorizontalPodAutoscaler), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *FakeHorizontalPodAutoscalers) Delete(name string, options *api.DeleteOptions) error {
 | 
			
		||||
	_, err := c.Fake.Invokes(NewDeleteAction("horizontalpodautoscalers", c.Namespace, name), &extensions.HorizontalPodAutoscaler{})
 | 
			
		||||
	return err
 | 
			
		||||
 
 | 
			
		||||
@@ -147,7 +147,7 @@ func (a *HorizontalController) reconcileAutoscaler(hpa extensions.HorizontalPodA
 | 
			
		||||
		hpa.Status.LastScaleTimestamp = &now
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, err = a.client.Experimental().HorizontalPodAutoscalers(hpa.Namespace).Update(&hpa)
 | 
			
		||||
	_, err = a.client.Experimental().HorizontalPodAutoscalers(hpa.Namespace).UpdateStatus(&hpa)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		a.eventRecorder.Event(&hpa, "FailedUpdateStatus", err.Error())
 | 
			
		||||
		return fmt.Errorf("failed to update status for %s: %v", hpa.Name, err)
 | 
			
		||||
 
 | 
			
		||||
@@ -1042,7 +1042,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
 | 
			
		||||
	dbClient := func(resource string) storage.Interface {
 | 
			
		||||
		return c.StorageDestinations.get("extensions", resource)
 | 
			
		||||
	}
 | 
			
		||||
	autoscalerStorage := horizontalpodautoscaleretcd.NewREST(dbClient("horizonalpodautoscalers"))
 | 
			
		||||
	autoscalerStorage, autoscalerStatusStorage := horizontalpodautoscaleretcd.NewREST(dbClient("horizonalpodautoscalers"))
 | 
			
		||||
	thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(dbClient("thirdpartyresources"))
 | 
			
		||||
	daemonSetStorage, daemonSetStatusStorage := daemonetcd.NewREST(dbClient("daemonsets"))
 | 
			
		||||
	deploymentStorage := deploymentetcd.NewStorage(dbClient("deployments"))
 | 
			
		||||
@@ -1064,6 +1064,7 @@ func (m *Master) experimental(c *Config) *apiserver.APIGroupVersion {
 | 
			
		||||
		strings.ToLower("replicationControllers"):          controllerStorage.ReplicationController,
 | 
			
		||||
		strings.ToLower("replicationControllers/scale"):    controllerStorage.Scale,
 | 
			
		||||
		strings.ToLower("horizontalpodautoscalers"):        autoscalerStorage,
 | 
			
		||||
		strings.ToLower("horizontalpodautoscalers/status"): autoscalerStatusStorage,
 | 
			
		||||
		strings.ToLower("thirdpartyresources"):             thirdPartyResourceStorage,
 | 
			
		||||
		strings.ToLower("daemonsets"):                      daemonSetStorage,
 | 
			
		||||
		strings.ToLower("daemonsets/status"):               daemonSetStatusStorage,
 | 
			
		||||
 
 | 
			
		||||
@@ -33,7 +33,7 @@ type REST struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewREST returns a RESTStorage object that will work against horizontal pod autoscalers.
 | 
			
		||||
func NewREST(s storage.Interface) *REST {
 | 
			
		||||
func NewREST(s storage.Interface) (*REST, *StatusREST) {
 | 
			
		||||
	prefix := "/horizontalpodautoscalers"
 | 
			
		||||
	store := &etcdgeneric.Etcd{
 | 
			
		||||
		NewFunc: func() runtime.Object { return &extensions.HorizontalPodAutoscaler{} },
 | 
			
		||||
@@ -67,5 +67,21 @@ func NewREST(s storage.Interface) *REST {
 | 
			
		||||
 | 
			
		||||
		Storage: s,
 | 
			
		||||
	}
 | 
			
		||||
	return &REST{store}
 | 
			
		||||
	statusStore := *store
 | 
			
		||||
	statusStore.UpdateStrategy = horizontalpodautoscaler.StatusStrategy
 | 
			
		||||
	return &REST{store}, &StatusREST{store: &statusStore}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StatusREST implements the REST endpoint for changing the status of a daemonset
 | 
			
		||||
type StatusREST struct {
 | 
			
		||||
	store *etcdgeneric.Etcd
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *StatusREST) New() runtime.Object {
 | 
			
		||||
	return &extensions.HorizontalPodAutoscaler{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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)
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -31,9 +31,10 @@ import (
 | 
			
		||||
	"k8s.io/kubernetes/pkg/tools"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newStorage(t *testing.T) (*REST, *tools.FakeEtcdClient) {
 | 
			
		||||
func newStorage(t *testing.T) (*REST, *StatusREST, *tools.FakeEtcdClient) {
 | 
			
		||||
	etcdStorage, fakeClient := registrytest.NewEtcdStorage(t, "extensions")
 | 
			
		||||
	return NewREST(etcdStorage), fakeClient
 | 
			
		||||
	storage, statusStorage := NewREST(etcdStorage)
 | 
			
		||||
	return storage, statusStorage, fakeClient
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func validNewHorizontalPodAutoscaler(name string) *extensions.HorizontalPodAutoscaler {
 | 
			
		||||
@@ -54,7 +55,7 @@ func validNewHorizontalPodAutoscaler(name string) *extensions.HorizontalPodAutos
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCreate(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	autoscaler := validNewHorizontalPodAutoscaler("foo")
 | 
			
		||||
	autoscaler.ObjectMeta = api.ObjectMeta{}
 | 
			
		||||
@@ -67,7 +68,7 @@ func TestCreate(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestUpdate(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	test.TestUpdate(
 | 
			
		||||
		// valid
 | 
			
		||||
@@ -82,25 +83,25 @@ func TestUpdate(t *testing.T) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestDelete(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	test.TestDelete(validNewHorizontalPodAutoscaler("foo"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGet(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	test.TestGet(validNewHorizontalPodAutoscaler("foo"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestList(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	test.TestList(validNewHorizontalPodAutoscaler("foo"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestWatch(t *testing.T) {
 | 
			
		||||
	storage, fakeClient := newStorage(t)
 | 
			
		||||
	storage, _, fakeClient := newStorage(t)
 | 
			
		||||
	test := registrytest.New(t, fakeClient, storage.Etcd)
 | 
			
		||||
	test.TestWatch(
 | 
			
		||||
		validNewHorizontalPodAutoscaler("foo"),
 | 
			
		||||
@@ -119,3 +120,5 @@ func TestWatch(t *testing.T) {
 | 
			
		||||
		},
 | 
			
		||||
	)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO TestUpdateStatus
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,10 @@ func (autoscalerStrategy) NamespaceScoped() bool {
 | 
			
		||||
 | 
			
		||||
// PrepareForCreate clears fields that are not allowed to be set by end users on creation.
 | 
			
		||||
func (autoscalerStrategy) PrepareForCreate(obj runtime.Object) {
 | 
			
		||||
	_ = obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	newHPA := obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
 | 
			
		||||
	// create cannot set status
 | 
			
		||||
	newHPA.Status = extensions.HorizontalPodAutoscalerStatus{}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Validate validates a new autoscaler.
 | 
			
		||||
@@ -62,7 +65,10 @@ func (autoscalerStrategy) AllowCreateOnUpdate() bool {
 | 
			
		||||
 | 
			
		||||
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
 | 
			
		||||
func (autoscalerStrategy) PrepareForUpdate(obj, old runtime.Object) {
 | 
			
		||||
	_ = obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	newHPA := obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	oldHPA := obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	// Update is not allowed to set status
 | 
			
		||||
	newHPA.Status = oldHPA.Status
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ValidateUpdate is the default update validation for an end user.
 | 
			
		||||
@@ -91,3 +97,20 @@ func MatchAutoscaler(label labels.Selector, field fields.Selector) generic.Match
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type autoscalerStatusStrategy struct {
 | 
			
		||||
	autoscalerStrategy
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var StatusStrategy = autoscalerStatusStrategy{Strategy}
 | 
			
		||||
 | 
			
		||||
func (autoscalerStatusStrategy) PrepareForUpdate(obj, old runtime.Object) {
 | 
			
		||||
	newAutoscaler := obj.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	oldAutoscaler := old.(*extensions.HorizontalPodAutoscaler)
 | 
			
		||||
	// status changes are not allowed to update spec
 | 
			
		||||
	newAutoscaler.Spec = oldAutoscaler.Spec
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (autoscalerStatusStrategy) ValidateUpdate(ctx api.Context, obj, old runtime.Object) errs.ValidationErrorList {
 | 
			
		||||
	return validation.ValidateHorizontalPodAutoscalerStatusUpdate(obj.(*extensions.HorizontalPodAutoscaler), old.(*extensions.HorizontalPodAutoscaler))
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user