From b9f13ded6f27195fff22d743d34869da53bbfe8c Mon Sep 17 00:00:00 2001 From: Marcin Wielgus Date: Mon, 5 Oct 2015 22:22:01 +0200 Subject: [PATCH] Experimental Cluster Autoscaler configuration object --- pkg/apis/experimental/deep_copy_generated.go | 59 ++++++ pkg/apis/experimental/register.go | 4 + pkg/apis/experimental/types.go | 61 ++++++ .../v1alpha1/conversion_generated.go | 174 ++++++++++++++++++ .../v1alpha1/deep_copy_generated.go | 59 ++++++ pkg/apis/experimental/v1alpha1/register.go | 4 + pkg/apis/experimental/v1alpha1/types.go | 61 ++++++ .../v1alpha1/types_swagger_doc_generated.go | 37 ++++ .../experimental/validation/validation.go | 37 ++++ .../validation/validation_test.go | 114 ++++++++++++ 10 files changed, 610 insertions(+) diff --git a/pkg/apis/experimental/deep_copy_generated.go b/pkg/apis/experimental/deep_copy_generated.go index 056784d5384..78557cf898c 100644 --- a/pkg/apis/experimental/deep_copy_generated.go +++ b/pkg/apis/experimental/deep_copy_generated.go @@ -836,6 +836,55 @@ func deepCopy_experimental_APIVersion(in APIVersion, out *APIVersion, c *convers return nil } +func deepCopy_experimental_ClusterAutoscaler(in ClusterAutoscaler, out *ClusterAutoscaler, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_api_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_experimental_ClusterAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + return nil +} + +func deepCopy_experimental_ClusterAutoscalerList(in ClusterAutoscalerList, out *ClusterAutoscalerList, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]ClusterAutoscaler, len(in.Items)) + for i := range in.Items { + if err := deepCopy_experimental_ClusterAutoscaler(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_experimental_ClusterAutoscalerSpec(in ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, c *conversion.Cloner) error { + out.MinNodes = in.MinNodes + out.MaxNodes = in.MaxNodes + if in.TargetUtilization != nil { + out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) + for i := range in.TargetUtilization { + if err := deepCopy_experimental_NodeUtilization(in.TargetUtilization[i], &out.TargetUtilization[i], c); err != nil { + return err + } + } + } else { + out.TargetUtilization = nil + } + return nil +} + func deepCopy_experimental_DaemonSet(in DaemonSet, out *DaemonSet, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1284,6 +1333,12 @@ func deepCopy_experimental_JobStatus(in JobStatus, out *JobStatus, c *conversion return nil } +func deepCopy_experimental_NodeUtilization(in NodeUtilization, out *NodeUtilization, c *conversion.Cloner) error { + out.Resource = in.Resource + out.Value = in.Value + return nil +} + func deepCopy_experimental_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1487,6 +1542,9 @@ func init() { deepCopy_unversioned_Time, deepCopy_unversioned_TypeMeta, deepCopy_experimental_APIVersion, + deepCopy_experimental_ClusterAutoscaler, + deepCopy_experimental_ClusterAutoscalerList, + deepCopy_experimental_ClusterAutoscalerSpec, deepCopy_experimental_DaemonSet, deepCopy_experimental_DaemonSetList, deepCopy_experimental_DaemonSetSpec, @@ -1514,6 +1572,7 @@ func init() { deepCopy_experimental_JobList, deepCopy_experimental_JobSpec, deepCopy_experimental_JobStatus, + deepCopy_experimental_NodeUtilization, deepCopy_experimental_ReplicationControllerDummy, deepCopy_experimental_ResourceConsumption, deepCopy_experimental_RollingUpdateDeployment, diff --git a/pkg/apis/experimental/register.go b/pkg/apis/experimental/register.go index 74e718e0175..882e4e76235 100644 --- a/pkg/apis/experimental/register.go +++ b/pkg/apis/experimental/register.go @@ -28,6 +28,8 @@ func init() { // Adds the list of known types to api.Scheme. func addKnownTypes() { api.Scheme.AddKnownTypes("", + &ClusterAutoscaler{}, + &ClusterAutoscalerList{}, &Deployment{}, &DeploymentList{}, &HorizontalPodAutoscaler{}, @@ -47,6 +49,8 @@ func addKnownTypes() { ) } +func (*ClusterAutoscaler) IsAnAPIObject() {} +func (*ClusterAutoscalerList) IsAnAPIObject() {} func (*Deployment) IsAnAPIObject() {} func (*DeploymentList) IsAnAPIObject() {} func (*HorizontalPodAutoscaler) IsAnAPIObject() {} diff --git a/pkg/apis/experimental/types.go b/pkg/apis/experimental/types.go index ff06ec72b4e..7db4d885dd4 100644 --- a/pkg/apis/experimental/types.go +++ b/pkg/apis/experimental/types.go @@ -578,3 +578,64 @@ type IngressBackend struct { // Specifies the port of the referenced service. ServicePort util.IntOrString `json:"servicePort"` } + +type NodeResource string + +const ( + // Percentage of node's CPUs that is currently used. + CpuConsumption NodeResource = "CpuConsumption" + + // Percentage of node's CPUs that is currently requested for pods. + CpuRequest NodeResource = "CpuRequest" + + // Percentage od node's memory that is currently used. + MemConsumption NodeResource = "MemConsumption" + + // Percentage of node's CPUs that is currently requested for pods. + MemRequest NodeResource = "MemRequest" +) + +// NodeUtilization describes what percentage of a particular resource is used on a node. +type NodeUtilization struct { + Resource NodeResource `json:"resource"` + + // The accepted values are from 0 to 1. + Value float64 `json:"value"` +} + +// Configuration of the Cluster Autoscaler +type ClusterAutoscalerSpec struct { + // Minimum number of nodes that the cluster should have. + MinNodes int `json:"minNodes"` + + // Maximum number of nodes that the cluster should have. + MaxNodes int `json:"maxNodes"` + + // Target average utilization of the cluster nodes. New nodes will be added if one of the + // targets is exceeded. Cluster size will be decreased if the current utilization is too low + // for all targets. + TargetUtilization []NodeUtilization `json:"target"` +} + +type ClusterAutoscaler struct { + unversioned.TypeMeta `json:",inline"` + + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // For now (experimental api) it is required that the name is set to "ClusterAutoscaler" and namespace is "default". + api.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired behavior of this daemon set. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + Spec ClusterAutoscalerSpec `json:"spec,omitempty"` +} + +// There will be just one (or none) ClusterAutoscaler. +type ClusterAutoscalerList struct { + unversioned.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + unversioned.ListMeta `json:"metadata,omitempty"` + + Items []ClusterAutoscaler `json:"items"` +} diff --git a/pkg/apis/experimental/v1alpha1/conversion_generated.go b/pkg/apis/experimental/v1alpha1/conversion_generated.go index 463c91237aa..f74edd922ff 100644 --- a/pkg/apis/experimental/v1alpha1/conversion_generated.go +++ b/pkg/apis/experimental/v1alpha1/conversion_generated.go @@ -2134,6 +2134,76 @@ func convert_experimental_APIVersion_To_v1alpha1_APIVersion(in *experimental.API return autoconvert_experimental_APIVersion_To_v1alpha1_APIVersion(in, out, s) } +func autoconvert_experimental_ClusterAutoscaler_To_v1alpha1_ClusterAutoscaler(in *experimental.ClusterAutoscaler, out *ClusterAutoscaler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*experimental.ClusterAutoscaler))(in) + } + if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { + return err + } + if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := convert_experimental_ClusterAutoscalerSpec_To_v1alpha1_ClusterAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func convert_experimental_ClusterAutoscaler_To_v1alpha1_ClusterAutoscaler(in *experimental.ClusterAutoscaler, out *ClusterAutoscaler, s conversion.Scope) error { + return autoconvert_experimental_ClusterAutoscaler_To_v1alpha1_ClusterAutoscaler(in, out, s) +} + +func autoconvert_experimental_ClusterAutoscalerList_To_v1alpha1_ClusterAutoscalerList(in *experimental.ClusterAutoscalerList, out *ClusterAutoscalerList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*experimental.ClusterAutoscalerList))(in) + } + if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { + return err + } + if err := s.Convert(&in.ListMeta, &out.ListMeta, 0); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]ClusterAutoscaler, len(in.Items)) + for i := range in.Items { + if err := convert_experimental_ClusterAutoscaler_To_v1alpha1_ClusterAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func convert_experimental_ClusterAutoscalerList_To_v1alpha1_ClusterAutoscalerList(in *experimental.ClusterAutoscalerList, out *ClusterAutoscalerList, s conversion.Scope) error { + return autoconvert_experimental_ClusterAutoscalerList_To_v1alpha1_ClusterAutoscalerList(in, out, s) +} + +func autoconvert_experimental_ClusterAutoscalerSpec_To_v1alpha1_ClusterAutoscalerSpec(in *experimental.ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*experimental.ClusterAutoscalerSpec))(in) + } + out.MinNodes = in.MinNodes + out.MaxNodes = in.MaxNodes + if in.TargetUtilization != nil { + out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) + for i := range in.TargetUtilization { + if err := convert_experimental_NodeUtilization_To_v1alpha1_NodeUtilization(&in.TargetUtilization[i], &out.TargetUtilization[i], s); err != nil { + return err + } + } + } else { + out.TargetUtilization = nil + } + return nil +} + +func convert_experimental_ClusterAutoscalerSpec_To_v1alpha1_ClusterAutoscalerSpec(in *experimental.ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, s conversion.Scope) error { + return autoconvert_experimental_ClusterAutoscalerSpec_To_v1alpha1_ClusterAutoscalerSpec(in, out, s) +} + func autoconvert_experimental_DaemonSet_To_v1alpha1_DaemonSet(in *experimental.DaemonSet, out *DaemonSet, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*experimental.DaemonSet))(in) @@ -2764,6 +2834,19 @@ func convert_experimental_JobStatus_To_v1alpha1_JobStatus(in *experimental.JobSt return autoconvert_experimental_JobStatus_To_v1alpha1_JobStatus(in, out, s) } +func autoconvert_experimental_NodeUtilization_To_v1alpha1_NodeUtilization(in *experimental.NodeUtilization, out *NodeUtilization, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*experimental.NodeUtilization))(in) + } + out.Resource = NodeResource(in.Resource) + out.Value = in.Value + return nil +} + +func convert_experimental_NodeUtilization_To_v1alpha1_NodeUtilization(in *experimental.NodeUtilization, out *NodeUtilization, s conversion.Scope) error { + return autoconvert_experimental_NodeUtilization_To_v1alpha1_NodeUtilization(in, out, s) +} + func autoconvert_experimental_ReplicationControllerDummy_To_v1alpha1_ReplicationControllerDummy(in *experimental.ReplicationControllerDummy, out *ReplicationControllerDummy, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*experimental.ReplicationControllerDummy))(in) @@ -2993,6 +3076,76 @@ func convert_v1alpha1_APIVersion_To_experimental_APIVersion(in *APIVersion, out return autoconvert_v1alpha1_APIVersion_To_experimental_APIVersion(in, out, s) } +func autoconvert_v1alpha1_ClusterAutoscaler_To_experimental_ClusterAutoscaler(in *ClusterAutoscaler, out *experimental.ClusterAutoscaler, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ClusterAutoscaler))(in) + } + if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { + return err + } + if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil { + return err + } + if err := convert_v1alpha1_ClusterAutoscalerSpec_To_experimental_ClusterAutoscalerSpec(&in.Spec, &out.Spec, s); err != nil { + return err + } + return nil +} + +func convert_v1alpha1_ClusterAutoscaler_To_experimental_ClusterAutoscaler(in *ClusterAutoscaler, out *experimental.ClusterAutoscaler, s conversion.Scope) error { + return autoconvert_v1alpha1_ClusterAutoscaler_To_experimental_ClusterAutoscaler(in, out, s) +} + +func autoconvert_v1alpha1_ClusterAutoscalerList_To_experimental_ClusterAutoscalerList(in *ClusterAutoscalerList, out *experimental.ClusterAutoscalerList, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ClusterAutoscalerList))(in) + } + if err := s.Convert(&in.TypeMeta, &out.TypeMeta, 0); err != nil { + return err + } + if err := s.Convert(&in.ListMeta, &out.ListMeta, 0); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]experimental.ClusterAutoscaler, len(in.Items)) + for i := range in.Items { + if err := convert_v1alpha1_ClusterAutoscaler_To_experimental_ClusterAutoscaler(&in.Items[i], &out.Items[i], s); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func convert_v1alpha1_ClusterAutoscalerList_To_experimental_ClusterAutoscalerList(in *ClusterAutoscalerList, out *experimental.ClusterAutoscalerList, s conversion.Scope) error { + return autoconvert_v1alpha1_ClusterAutoscalerList_To_experimental_ClusterAutoscalerList(in, out, s) +} + +func autoconvert_v1alpha1_ClusterAutoscalerSpec_To_experimental_ClusterAutoscalerSpec(in *ClusterAutoscalerSpec, out *experimental.ClusterAutoscalerSpec, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*ClusterAutoscalerSpec))(in) + } + out.MinNodes = in.MinNodes + out.MaxNodes = in.MaxNodes + if in.TargetUtilization != nil { + out.TargetUtilization = make([]experimental.NodeUtilization, len(in.TargetUtilization)) + for i := range in.TargetUtilization { + if err := convert_v1alpha1_NodeUtilization_To_experimental_NodeUtilization(&in.TargetUtilization[i], &out.TargetUtilization[i], s); err != nil { + return err + } + } + } else { + out.TargetUtilization = nil + } + return nil +} + +func convert_v1alpha1_ClusterAutoscalerSpec_To_experimental_ClusterAutoscalerSpec(in *ClusterAutoscalerSpec, out *experimental.ClusterAutoscalerSpec, s conversion.Scope) error { + return autoconvert_v1alpha1_ClusterAutoscalerSpec_To_experimental_ClusterAutoscalerSpec(in, out, s) +} + func autoconvert_v1alpha1_DaemonSet_To_experimental_DaemonSet(in *DaemonSet, out *experimental.DaemonSet, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*DaemonSet))(in) @@ -3603,6 +3756,19 @@ func convert_v1alpha1_JobStatus_To_experimental_JobStatus(in *JobStatus, out *ex return autoconvert_v1alpha1_JobStatus_To_experimental_JobStatus(in, out, s) } +func autoconvert_v1alpha1_NodeUtilization_To_experimental_NodeUtilization(in *NodeUtilization, out *experimental.NodeUtilization, s conversion.Scope) error { + if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { + defaulting.(func(*NodeUtilization))(in) + } + out.Resource = experimental.NodeResource(in.Resource) + out.Value = in.Value + return nil +} + +func convert_v1alpha1_NodeUtilization_To_experimental_NodeUtilization(in *NodeUtilization, out *experimental.NodeUtilization, s conversion.Scope) error { + return autoconvert_v1alpha1_NodeUtilization_To_experimental_NodeUtilization(in, out, s) +} + func autoconvert_v1alpha1_ReplicationControllerDummy_To_experimental_ReplicationControllerDummy(in *ReplicationControllerDummy, out *experimental.ReplicationControllerDummy, s conversion.Scope) error { if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found { defaulting.(func(*ReplicationControllerDummy))(in) @@ -3859,6 +4025,9 @@ func init() { autoconvert_api_VolumeSource_To_v1_VolumeSource, autoconvert_api_Volume_To_v1_Volume, autoconvert_experimental_APIVersion_To_v1alpha1_APIVersion, + autoconvert_experimental_ClusterAutoscalerList_To_v1alpha1_ClusterAutoscalerList, + autoconvert_experimental_ClusterAutoscalerSpec_To_v1alpha1_ClusterAutoscalerSpec, + autoconvert_experimental_ClusterAutoscaler_To_v1alpha1_ClusterAutoscaler, autoconvert_experimental_DaemonSetList_To_v1alpha1_DaemonSetList, autoconvert_experimental_DaemonSetSpec_To_v1alpha1_DaemonSetSpec, autoconvert_experimental_DaemonSetStatus_To_v1alpha1_DaemonSetStatus, @@ -3886,6 +4055,7 @@ func init() { autoconvert_experimental_JobSpec_To_v1alpha1_JobSpec, autoconvert_experimental_JobStatus_To_v1alpha1_JobStatus, autoconvert_experimental_Job_To_v1alpha1_Job, + autoconvert_experimental_NodeUtilization_To_v1alpha1_NodeUtilization, autoconvert_experimental_ReplicationControllerDummy_To_v1alpha1_ReplicationControllerDummy, autoconvert_experimental_ResourceConsumption_To_v1alpha1_ResourceConsumption, autoconvert_experimental_RollingUpdateDeployment_To_v1alpha1_RollingUpdateDeployment, @@ -3939,6 +4109,9 @@ func init() { autoconvert_v1_VolumeSource_To_api_VolumeSource, autoconvert_v1_Volume_To_api_Volume, autoconvert_v1alpha1_APIVersion_To_experimental_APIVersion, + autoconvert_v1alpha1_ClusterAutoscalerList_To_experimental_ClusterAutoscalerList, + autoconvert_v1alpha1_ClusterAutoscalerSpec_To_experimental_ClusterAutoscalerSpec, + autoconvert_v1alpha1_ClusterAutoscaler_To_experimental_ClusterAutoscaler, autoconvert_v1alpha1_DaemonSetList_To_experimental_DaemonSetList, autoconvert_v1alpha1_DaemonSetSpec_To_experimental_DaemonSetSpec, autoconvert_v1alpha1_DaemonSetStatus_To_experimental_DaemonSetStatus, @@ -3965,6 +4138,7 @@ func init() { autoconvert_v1alpha1_JobSpec_To_experimental_JobSpec, autoconvert_v1alpha1_JobStatus_To_experimental_JobStatus, autoconvert_v1alpha1_Job_To_experimental_Job, + autoconvert_v1alpha1_NodeUtilization_To_experimental_NodeUtilization, autoconvert_v1alpha1_ReplicationControllerDummy_To_experimental_ReplicationControllerDummy, autoconvert_v1alpha1_ResourceConsumption_To_experimental_ResourceConsumption, autoconvert_v1alpha1_RollingUpdateDeployment_To_experimental_RollingUpdateDeployment, diff --git a/pkg/apis/experimental/v1alpha1/deep_copy_generated.go b/pkg/apis/experimental/v1alpha1/deep_copy_generated.go index cfb2033a3d1..91148b91be6 100644 --- a/pkg/apis/experimental/v1alpha1/deep_copy_generated.go +++ b/pkg/apis/experimental/v1alpha1/deep_copy_generated.go @@ -838,6 +838,55 @@ func deepCopy_v1alpha1_APIVersion(in APIVersion, out *APIVersion, c *conversion. return nil } +func deepCopy_v1alpha1_ClusterAutoscaler(in ClusterAutoscaler, out *ClusterAutoscaler, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_v1_ObjectMeta(in.ObjectMeta, &out.ObjectMeta, c); err != nil { + return err + } + if err := deepCopy_v1alpha1_ClusterAutoscalerSpec(in.Spec, &out.Spec, c); err != nil { + return err + } + return nil +} + +func deepCopy_v1alpha1_ClusterAutoscalerList(in ClusterAutoscalerList, out *ClusterAutoscalerList, c *conversion.Cloner) error { + if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { + return err + } + if err := deepCopy_unversioned_ListMeta(in.ListMeta, &out.ListMeta, c); err != nil { + return err + } + if in.Items != nil { + out.Items = make([]ClusterAutoscaler, len(in.Items)) + for i := range in.Items { + if err := deepCopy_v1alpha1_ClusterAutoscaler(in.Items[i], &out.Items[i], c); err != nil { + return err + } + } + } else { + out.Items = nil + } + return nil +} + +func deepCopy_v1alpha1_ClusterAutoscalerSpec(in ClusterAutoscalerSpec, out *ClusterAutoscalerSpec, c *conversion.Cloner) error { + out.MinNodes = in.MinNodes + out.MaxNodes = in.MaxNodes + if in.TargetUtilization != nil { + out.TargetUtilization = make([]NodeUtilization, len(in.TargetUtilization)) + for i := range in.TargetUtilization { + if err := deepCopy_v1alpha1_NodeUtilization(in.TargetUtilization[i], &out.TargetUtilization[i], c); err != nil { + return err + } + } + } else { + out.TargetUtilization = nil + } + return nil +} + func deepCopy_v1alpha1_DaemonSet(in DaemonSet, out *DaemonSet, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1296,6 +1345,12 @@ func deepCopy_v1alpha1_JobStatus(in JobStatus, out *JobStatus, c *conversion.Clo return nil } +func deepCopy_v1alpha1_NodeUtilization(in NodeUtilization, out *NodeUtilization, c *conversion.Cloner) error { + out.Resource = in.Resource + out.Value = in.Value + return nil +} + func deepCopy_v1alpha1_ReplicationControllerDummy(in ReplicationControllerDummy, out *ReplicationControllerDummy, c *conversion.Cloner) error { if err := deepCopy_unversioned_TypeMeta(in.TypeMeta, &out.TypeMeta, c); err != nil { return err @@ -1509,6 +1564,9 @@ func init() { deepCopy_v1_VolumeMount, deepCopy_v1_VolumeSource, deepCopy_v1alpha1_APIVersion, + deepCopy_v1alpha1_ClusterAutoscaler, + deepCopy_v1alpha1_ClusterAutoscalerList, + deepCopy_v1alpha1_ClusterAutoscalerSpec, deepCopy_v1alpha1_DaemonSet, deepCopy_v1alpha1_DaemonSetList, deepCopy_v1alpha1_DaemonSetSpec, @@ -1536,6 +1594,7 @@ func init() { deepCopy_v1alpha1_JobList, deepCopy_v1alpha1_JobSpec, deepCopy_v1alpha1_JobStatus, + deepCopy_v1alpha1_NodeUtilization, deepCopy_v1alpha1_ReplicationControllerDummy, deepCopy_v1alpha1_ResourceConsumption, deepCopy_v1alpha1_RollingUpdateDeployment, diff --git a/pkg/apis/experimental/v1alpha1/register.go b/pkg/apis/experimental/v1alpha1/register.go index c1291881078..eef557bf95a 100644 --- a/pkg/apis/experimental/v1alpha1/register.go +++ b/pkg/apis/experimental/v1alpha1/register.go @@ -32,6 +32,8 @@ func init() { // Adds the list of known types to api.Scheme. func addKnownTypes() { api.Scheme.AddKnownTypes("experimental/v1alpha1", + &ClusterAutoscaler{}, + &ClusterAutoscalerList{}, &Deployment{}, &DeploymentList{}, &HorizontalPodAutoscaler{}, @@ -51,6 +53,8 @@ func addKnownTypes() { ) } +func (*ClusterAutoscaler) IsAnAPIObject() {} +func (*ClusterAutoscalerList) IsAnAPIObject() {} func (*Deployment) IsAnAPIObject() {} func (*DeploymentList) IsAnAPIObject() {} func (*HorizontalPodAutoscaler) IsAnAPIObject() {} diff --git a/pkg/apis/experimental/v1alpha1/types.go b/pkg/apis/experimental/v1alpha1/types.go index 872409fc5d0..9f702614afc 100644 --- a/pkg/apis/experimental/v1alpha1/types.go +++ b/pkg/apis/experimental/v1alpha1/types.go @@ -580,3 +580,64 @@ type IngressBackend struct { // Specifies the port of the referenced service. ServicePort util.IntOrString `json:"servicePort"` } + +type NodeResource string + +const ( + // Percentage of node's CPUs that is currently used. + CpuConsumption NodeResource = "CpuConsumption" + + // Percentage of node's CPUs that is currently requested for pods. + CpuRequest NodeResource = "CpuRequest" + + // Percentage od node's memory that is currently used. + MemConsumption NodeResource = "MemConsumption" + + // Percentage of node's CPUs that is currently requested for pods. + MemRequest NodeResource = "MemRequest" +) + +// NodeUtilization describes what percentage of a particular resource is used on a node. +type NodeUtilization struct { + Resource NodeResource `json:"resource"` + + // The accepted values are from 0 to 1. + Value float64 `json:"value"` +} + +// Configuration of the Cluster Autoscaler +type ClusterAutoscalerSpec struct { + // Minimum number of nodes that the cluster should have. + MinNodes int `json:"minNodes"` + + // Maximum number of nodes that the cluster should have. + MaxNodes int `json:"maxNodes"` + + // Target average utilization of the cluster nodes. New nodes will be added if one of the + // targets is exceeded. Cluster size will be decreased if the current utilization is too low + // for all targets. + TargetUtilization []NodeUtilization `json:"target"` +} + +type ClusterAutoscaler struct { + unversioned.TypeMeta `json:",inline"` + + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + // For now (experimental api) it is required that the name is set to "ClusterAutoscaler" and namespace is "default". + v1.ObjectMeta `json:"metadata,omitempty"` + + // Spec defines the desired behavior of this daemon set. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status + Spec ClusterAutoscalerSpec `json:"spec,omitempty"` +} + +// There will be just one (or none) ClusterAutoscaler. +type ClusterAutoscalerList struct { + unversioned.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata + unversioned.ListMeta `json:"metadata,omitempty"` + + Items []ClusterAutoscaler `json:"items"` +} diff --git a/pkg/apis/experimental/v1alpha1/types_swagger_doc_generated.go b/pkg/apis/experimental/v1alpha1/types_swagger_doc_generated.go index c278dc668cc..34b8fe578e5 100644 --- a/pkg/apis/experimental/v1alpha1/types_swagger_doc_generated.go +++ b/pkg/apis/experimental/v1alpha1/types_swagger_doc_generated.go @@ -37,6 +37,35 @@ func (APIVersion) SwaggerDoc() map[string]string { return map_APIVersion } +var map_ClusterAutoscaler = map[string]string{ + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata For now (experimental api) it is required that the name is set to \"ClusterAutoscaler\" and namespace is \"default\".", + "spec": "Spec defines the desired behavior of this daemon set. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status", +} + +func (ClusterAutoscaler) SwaggerDoc() map[string]string { + return map_ClusterAutoscaler +} + +var map_ClusterAutoscalerList = map[string]string{ + "": "There will be just one (or none) ClusterAutoscaler.", + "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", +} + +func (ClusterAutoscalerList) SwaggerDoc() map[string]string { + return map_ClusterAutoscalerList +} + +var map_ClusterAutoscalerSpec = map[string]string{ + "": "Configuration of the Cluster Autoscaler", + "minNodes": "Minimum number of nodes that the cluster should have.", + "maxNodes": "Maximum number of nodes that the cluster should have.", + "target": "Target average utilization of the cluster nodes. New nodes will be added if one of the targets is exceeded. Cluster size will be decreased if the current utilization is too low for all targets.", +} + +func (ClusterAutoscalerSpec) SwaggerDoc() map[string]string { + return map_ClusterAutoscalerSpec +} + var map_DaemonSet = map[string]string{ "": "DaemonSet represents the configuration of a daemon set.", "metadata": "Standard object's metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata", @@ -326,6 +355,14 @@ func (JobStatus) SwaggerDoc() map[string]string { return map_JobStatus } +var map_NodeUtilization = map[string]string{ + "": "NodeUtilization describes what percentage of a particular resource is used on a node.", +} + +func (NodeUtilization) SwaggerDoc() map[string]string { + return map_NodeUtilization +} + var map_ReplicationControllerDummy = map[string]string{ "": "Dummy definition", } diff --git a/pkg/apis/experimental/validation/validation.go b/pkg/apis/experimental/validation/validation.go index e72577d9cd1..7765d71748a 100644 --- a/pkg/apis/experimental/validation/validation.go +++ b/pkg/apis/experimental/validation/validation.go @@ -501,3 +501,40 @@ func validateIngressBackend(backend *experimental.IngressBackend) errs.Validatio } return allErrs } + +func validateClusterAutoscalerSpec(spec experimental.ClusterAutoscalerSpec) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + if spec.MinNodes < 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("minNodes", spec.MinNodes, `must be non-negative`)) + } + if spec.MaxNodes < spec.MinNodes { + allErrs = append(allErrs, errs.NewFieldInvalid("maxNodes", spec.MaxNodes, `must be bigger or equal to minNodes`)) + } + if len(spec.TargetUtilization) == 0 { + allErrs = append(allErrs, errs.NewFieldRequired("targetUtilization")) + } + for _, target := range spec.TargetUtilization { + if len(target.Resource) == 0 { + allErrs = append(allErrs, errs.NewFieldRequired("targetUtilization.resource")) + } + if target.Value <= 0 { + allErrs = append(allErrs, errs.NewFieldInvalid("targetUtilization.value", target.Value, "must be greater than 0")) + } + if target.Value > 1 { + allErrs = append(allErrs, errs.NewFieldInvalid("targetUtilization.value", target.Value, "must be less or equal 1")) + } + } + return allErrs +} + +func ValidateClusterAutoscaler(autoscaler *experimental.ClusterAutoscaler) errs.ValidationErrorList { + allErrs := errs.ValidationErrorList{} + if autoscaler.Name != "ClusterAutoscaler" { + allErrs = append(allErrs, errs.NewFieldInvalid("name", autoscaler.Name, `name must be ClusterAutoscaler`)) + } + if autoscaler.Namespace != api.NamespaceDefault { + allErrs = append(allErrs, errs.NewFieldInvalid("namespace", autoscaler.Namespace, `namespace must be default`)) + } + allErrs = append(allErrs, validateClusterAutoscalerSpec(autoscaler.Spec)...) + return allErrs +} diff --git a/pkg/apis/experimental/validation/validation_test.go b/pkg/apis/experimental/validation/validation_test.go index dcc65e97ad3..54620d0d217 100644 --- a/pkg/apis/experimental/validation/validation_test.go +++ b/pkg/apis/experimental/validation/validation_test.go @@ -950,3 +950,117 @@ func TestValidateIngress(t *testing.T) { } } } + +func TestValidateClusterAutoscaler(t *testing.T) { + successCases := []experimental.ClusterAutoscaler{ + { + ObjectMeta: api.ObjectMeta{ + Name: "ClusterAutoscaler", + Namespace: api.NamespaceDefault, + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: 1, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{ + { + Resource: experimental.CpuRequest, + Value: 0.7, + }, + }, + }, + }, + } + for _, successCase := range successCases { + if errs := ValidateClusterAutoscaler(&successCase); len(errs) != 0 { + t.Errorf("expected success: %v", errs) + } + } + + errorCases := map[string]experimental.ClusterAutoscaler{ + "name must be ClusterAutoscaler": { + ObjectMeta: api.ObjectMeta{ + Name: "TestClusterAutoscaler", + Namespace: api.NamespaceDefault, + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: 1, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{ + { + Resource: experimental.CpuRequest, + Value: 0.7, + }, + }, + }, + }, + "namespace must be default": { + ObjectMeta: api.ObjectMeta{ + Name: "ClusterAutoscaler", + Namespace: "test", + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: 1, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{ + { + Resource: experimental.CpuRequest, + Value: 0.7, + }, + }, + }, + }, + + `must be non-negative`: { + ObjectMeta: api.ObjectMeta{ + Name: "ClusterAutoscaler", + Namespace: api.NamespaceDefault, + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: -1, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{ + { + Resource: experimental.CpuRequest, + Value: 0.7, + }, + }, + }, + }, + `must be bigger or equal to minNodes`: { + ObjectMeta: api.ObjectMeta{ + Name: "ClusterAutoscaler", + Namespace: api.NamespaceDefault, + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: 10, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{ + { + Resource: experimental.CpuRequest, + Value: 0.7, + }, + }, + }, + }, + "required value": { + ObjectMeta: api.ObjectMeta{ + Name: "ClusterAutoscaler", + Namespace: api.NamespaceDefault, + }, + Spec: experimental.ClusterAutoscalerSpec{ + MinNodes: 1, + MaxNodes: 5, + TargetUtilization: []experimental.NodeUtilization{}, + }, + }, + } + + for k, v := range errorCases { + errs := ValidateClusterAutoscaler(&v) + if len(errs) == 0 { + t.Errorf("expected failure for %s", k) + } else if !strings.Contains(errs[0].Error(), k) { + t.Errorf("unexpected error: %v, expected: %s", errs[0], k) + } + } +}