Change:
e.g. scale up if CPU usage exceeds 70% of CPU request. Subtasks: [x] Add API changes [x] Add implementation [x] Add support in kubectl to print status with cpu request utilization [x] Add e2e tests
I've sent main PR for review (). During your day/my night please look into kubectl changes and e2e tests.

Discussion:
] }, { <ins> "path": "/apis/extensions/v1beta1/namespaces/{namespace}/replicationcontrollers/{name}/scale", "description": "API at /apis/extensions/v1beta1", "operations": [ { "type": "v1beta1.Scale", "method": "GET", "summary": "read scale of the specified Scale", "nickname": "readNamespacedScaleScale", "parameters": [ { "type": "string", "paramType": "query", "name": "pretty", "description": "If 'true', then the output is pretty printed.", "required": false, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "namespace", "description": "object name and auth scope, such as for teams and projects", "required": true, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "name", "description": "name of the Scale", "required": true, "allowMultiple": false } ], "responseMessages": [ { "code": 200, "message": "OK", "responseModel": "v1beta1.Scale" } ], "produces": [ "application/json" ], "consumes": [ "*/*" ] }, { "type": "v1beta1.Scale", "method": "PUT", "summary": "replace scale of the specified Scale", "nickname": "replaceNamespacedScaleScale", "parameters": [ { "type": "string", "paramType": "query", "name": "pretty", "description": "If 'true', then the output is pretty printed.", "required": false, "allowMultiple": false }, { "type": "v1beta1.Scale", "paramType": "body", "name": "body", "description": "", "required": true, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "namespace", "description": "object name and auth scope, such as for teams and projects", "required": true, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "name", "description": "name of the Scale", "required": true, "allowMultiple": false } ], "responseMessages": [ { "code": 200, "message": "OK", "responseModel": "v1beta1.Scale" } ], "produces": [ "application/json" ], "consumes": [ "*/*" ] }, { "type": "v1beta1.Scale", "method": "PATCH", "summary": "partially update scale of the specified Scale", "nickname": "patchNamespacedScaleScale", "parameters": [ { "type": "string", "paramType": "query", "name": "pretty", "description": "If 'true', then the output is pretty printed.", "required": false, "allowMultiple": false }, { "type": "unversioned.Patch", "paramType": "body", "name": "body", "description": "", "required": true, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "namespace", "description": "object name and auth scope, such as for teams and projects", "required": true, "allowMultiple": false }, { "type": "string", "paramType": "path", "name": "name", "description": "name of the Scale", "required": true, "allowMultiple": false } ], "responseMessages": [ { "code": 200, "message": "OK", "responseModel": "v1beta1.Scale" } ], "produces": [ "application/json" ], "consumes": [ "application/json-patch+json", "application/merge-patch+json", "application/strategic-merge-patch+json" ] } ] }, { </ins> "path": "/apis/extensions/v1beta1", "description": "API at /apis/extensions/v1beta1", "operations": [

"description": "Human readable message indicating details about last transition." } } <ins> }, "v1beta1.Scale": { "id": "v1beta1.Scale", "description": "represents a scaling request for a resource.", "properties": { "kind": { "type": "string", "description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds" }, "apiVersion": { "type": "string", "description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources" }, "metadata": { "$ref": "v1.ObjectMeta", "description": "Standard object metadata; More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata." }, "spec": { "$ref": "v1beta1.ScaleSpec", "description": "defines the behavior of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status." }, "status": { "$ref": "v1beta1.ScaleStatus", "description": "current status of the scale. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status. Read-only." } } }, "v1beta1.ScaleSpec": { "id": "v1beta1.ScaleSpec", "description": "describes the attributes of a scale subresource", "properties": { "replicas": { "type": "integer", "format": "int32", "description": "desired number of instances for the scaled object." } } }, "v1beta1.ScaleStatus": { "id": "v1beta1.ScaleStatus", "description": "represents the current status of a scale subresource.", "required": [ "replicas" ], "properties": { "replicas": { "type": "integer", "format": "int32", "description": "actual number of observed instances of the scaled object." }, "selector": { "type": "any", "description": "label query over pods that should match the replicas count. More info: http://releases.k8s.io/HEAD/docs/user-guide/labels.md#label-selectors" } } </ins> } } }  No newline at end of file

} storage := map[string]rest.Storage{} <del> if isEnabled("replicationcontrollers") { controllerStorage := expcontrolleretcd.NewStorage(c.StorageDestinations.get("", "replicationControllers")) storage["replicationcontrollers"] = controllerStorage.ReplicationController storage["replicationcontrollers/scale"] = controllerStorage.Scale } </del> if isEnabled("horizontalpodautoscalers") { autoscalerStorage, autoscalerStatusStorage := horizontalpodautoscaleretcd.NewREST(dbClient("horizonalpodautoscalers")) storage["horizontalpodautoscalers"] = autoscalerStorage storage["horizontalpodautoscalers/status"] = autoscalerStatusStorage <ins> controllerStorage := expcontrolleretcd.NewStorage(c.StorageDestinations.get("", "replicationControllers")) storage["replicationcontrollers"] = controllerStorage.ReplicationController storage["replicationcontrollers/scale"] = controllerStorage.Scale </ins> } if isEnabled("thirdpartyresources") { thirdPartyResourceStorage := thirdpartyresourceetcd.NewREST(dbClient("thirdpartyresources"))

package e2e import ( <del> "github.com/golang/glog" </del> <ins> "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/apis/extensions" </ins> . "github.com/onsi/ginkgo" )

f := NewFramework("horizontal-pod-autoscaling") // CPU tests <del> It("[Skipped] should scale from 1 pod to 3 pods and from 3 to 5 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 1, 250, 0, 400, 100, f) </del> <ins> It("[Skipped][Autoscaling Suite] should scale from 1 pod to 3 pods and from 3 to 5 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 1, 250, 0, 500, 100, f) </ins> defer rc.CleanUp() <del> createCPUHorizontalPodAutoscaler(rc, "0.1") </del> <ins> createCPUHorizontalPodAutoscaler(rc, 20) </ins> rc.WaitForReplicas(3) rc.ConsumeCPU(700) rc.WaitForReplicas(5) }) <del> It("[Skipped] should scale from 5 pods to 3 pods and from 3 to 1 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 5, 700, 0, 200, 100, f) </del> <ins> It("[Skipped][Autoscaling Suite] should scale from 5 pods to 3 pods and from 3 to 1 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 5, 400, 0, 500, 100, f) </ins> defer rc.CleanUp() <del> createCPUHorizontalPodAutoscaler(rc, "0.3") </del> <ins> createCPUHorizontalPodAutoscaler(rc, 30) </ins> rc.WaitForReplicas(3) rc.ConsumeCPU(100) rc.WaitForReplicas(1) }) <del> // Memory tests It("[Skipped] should scale from 1 pod to 3 pods and from 3 to 5 (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 1, 0, 2200, 100, 2500, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "1000") rc.WaitForReplicas(3) rc.ConsumeMem(4200) rc.WaitForReplicas(5) }) It("[Skipped] should scale from 5 pods to 3 pods and from 3 to 1 (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 5, 0, 2200, 100, 2500, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "1000") rc.WaitForReplicas(3) rc.ConsumeMem(100) rc.WaitForReplicas(1) }) // Backup tests, currently disabled It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to 3 pods (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 1, 700, 0, 800, 100, f) defer rc.CleanUp() createCPUHorizontalPodAutoscaler(rc, "0.3") rc.WaitForReplicas(3) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 3 pods to 1 pod (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 3, 0, 0, 100, 100, f) defer rc.CleanUp() createCPUHorizontalPodAutoscaler(rc, "0.7") rc.WaitForReplicas(1) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to maximum 5 pods (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 1, 700, 0, 800, 100, f) defer rc.CleanUp() createCPUHorizontalPodAutoscaler(rc, "0.1") rc.WaitForReplicas(5) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to 3 pods and from 3 to 1 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 1, 700, 0, 800, 100, f) defer rc.CleanUp() createCPUHorizontalPodAutoscaler(rc, "0.3") rc.WaitForReplicas(3) rc.ConsumeCPU(300) rc.WaitForReplicas(1) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 3 pods to 1 pod and from 1 to 3 (scale resource: CPU)", func() { rc = NewDynamicResourceConsumer("rc", 3, 0, 0, 800, 100, f) defer rc.CleanUp() createCPUHorizontalPodAutoscaler(rc, "0.3") rc.WaitForReplicas(1) rc.ConsumeCPU(700) rc.WaitForReplicas(3) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to 3 pods (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 1, 0, 800, 100, 900, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "300") rc.WaitForReplicas(3) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 3 pods to 1 pod (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 3, 0, 0, 100, 100, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "700") rc.WaitForReplicas(1) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to 3 pods and from 3 to 1 (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 1, 0, 700, 100, 800, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "300") rc.WaitForReplicas(3) rc.ConsumeMem(100) rc.WaitForReplicas(1) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 3 pods to 1 pod and from 1 to 3 (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 3, 0, 0, 100, 800, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "300") rc.WaitForReplicas(1) rc.ConsumeMem(700) rc.WaitForReplicas(3) }) It("[Skipped][Horizontal pod autoscaling Suite] should scale from 1 pod to maximum 5 pods (scale resource: Memory)", func() { rc = NewDynamicResourceConsumer("rc", 1, 0, 700, 100, 800, f) defer rc.CleanUp() createMemoryHorizontalPodAutoscaler(rc, "100") rc.WaitForReplicas(5) }) </del> }) <del> func createCPUHorizontalPodAutoscaler(rc *ResourceConsumer, cpu string) { glog.Fatal("createCPUHorizontalPodAutoscaler not implemented!") // TODO: reimplemente e2e tests for the new API. } // argument memory is in megabytes func createMemoryHorizontalPodAutoscaler(rc *ResourceConsumer, memory string) { glog.Fatal("createMemoryHorizontalPodAutoscaler not implemented!") // TODO: reimplemente e2e tests for the new API. </del> <ins> func createCPUHorizontalPodAutoscaler(rc *ResourceConsumer, cpu int) { minReplicas := 1 hpa := &extensions.HorizontalPodAutoscaler{ ObjectMeta: api.ObjectMeta{ Name:      rc.name, Namespace: rc.framework.Namespace.Name, }, Spec: extensions.HorizontalPodAutoscalerSpec{ ScaleRef: extensions.SubresourceReference{ Kind:        kind, Name:        rc.name, Namespace:   rc.framework.Namespace.Name, Subresource: subresource, }, MinReplicas:    &minReplicas, MaxReplicas:    5, CPUUtilization: &extensions.CPUTargetUtilization{TargetPercentage: cpu}, }, } _, errHPA := rc.framework.Client.Extensions().HorizontalPodAutoscalers(rc.framework.Namespace.Name).Create(hpa) expectNoError(errHPA) </ins> }