mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
Merge pull request #3841 from vishh/api_resources
Adding an extensible resource spec to the API.
This commit is contained in:
commit
ce164f67fd
42
pkg/api/resource_helpers.go
Normal file
42
pkg/api/resource_helpers.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2014 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Returns string version of ResourceName.
|
||||||
|
func (self ResourceName) String() string {
|
||||||
|
return string(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the CPU limit if specified.
|
||||||
|
func (self *ResourceList) Cpu() *resource.Quantity {
|
||||||
|
if val, ok := (*self)[ResourceCPU]; ok {
|
||||||
|
return &val
|
||||||
|
}
|
||||||
|
return &resource.Quantity{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the Memory limit if specified.
|
||||||
|
func (self *ResourceList) Memory() *resource.Quantity {
|
||||||
|
if val, ok := (*self)[ResourceMemory]; ok {
|
||||||
|
return &val
|
||||||
|
}
|
||||||
|
return &resource.Quantity{}
|
||||||
|
}
|
53
pkg/api/resource_helpers_test.go
Normal file
53
pkg/api/resource_helpers_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2015 Google Inc. All rights reserved.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestResourceHelpers(t *testing.T) {
|
||||||
|
cpuLimit := resource.MustParse("10")
|
||||||
|
memoryLimit := resource.MustParse("10G")
|
||||||
|
resourceSpec := ResourceRequirementSpec{
|
||||||
|
Limits: ResourceList{
|
||||||
|
"cpu": cpuLimit,
|
||||||
|
"memory": memoryLimit,
|
||||||
|
"kube.io/storage": memoryLimit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if res := resourceSpec.Limits.Cpu(); *res != cpuLimit {
|
||||||
|
t.Errorf("expected cpulimit %d, got %d", cpuLimit, res)
|
||||||
|
}
|
||||||
|
if res := resourceSpec.Limits.Memory(); *res != memoryLimit {
|
||||||
|
t.Errorf("expected memorylimit %d, got %d", memoryLimit, res)
|
||||||
|
}
|
||||||
|
resourceSpec = ResourceRequirementSpec{
|
||||||
|
Limits: ResourceList{
|
||||||
|
"memory": memoryLimit,
|
||||||
|
"kube.io/storage": memoryLimit,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
if res := resourceSpec.Limits.Cpu(); res.Value() != 0 {
|
||||||
|
t.Errorf("expected cpulimit %d, got %d", 0, res)
|
||||||
|
}
|
||||||
|
if res := resourceSpec.Limits.Memory(); *res != memoryLimit {
|
||||||
|
t.Errorf("expected memorylimit %d, got %d", memoryLimit, res)
|
||||||
|
}
|
||||||
|
}
|
@ -304,6 +304,12 @@ type Capabilities struct {
|
|||||||
Drop []CapabilityType `json:"drop,omitempty"`
|
Drop []CapabilityType `json:"drop,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRequirementSpec describes the compute resource requirements.
|
||||||
|
type ResourceRequirementSpec struct {
|
||||||
|
// Limits describes the maximum amount of compute resources required.
|
||||||
|
Limits ResourceList `json:"limits,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Container represents a single container that is expected to be run on the host.
|
// Container represents a single container that is expected to be run on the host.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
// Required: This must be a DNS_LABEL. Each container in a pod must
|
// Required: This must be a DNS_LABEL. Each container in a pod must
|
||||||
@ -317,10 +323,8 @@ type Container struct {
|
|||||||
WorkingDir string `json:"workingDir,omitempty"`
|
WorkingDir string `json:"workingDir,omitempty"`
|
||||||
Ports []Port `json:"ports,omitempty"`
|
Ports []Port `json:"ports,omitempty"`
|
||||||
Env []EnvVar `json:"env,omitempty"`
|
Env []EnvVar `json:"env,omitempty"`
|
||||||
// Optional: Defaults to unlimited.
|
// Compute resource requirements.
|
||||||
Memory resource.Quantity `json:"memory,omitempty"`
|
Resources ResourceRequirementSpec `json:"resources,omitempty"`
|
||||||
// Optional: Defaults to unlimited.
|
|
||||||
CPU resource.Quantity `json:"cpu,omitempty"`
|
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
||||||
LivenessProbe *Probe `json:"livenessProbe,omitempty"`
|
LivenessProbe *Probe `json:"livenessProbe,omitempty"`
|
||||||
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
||||||
@ -775,8 +779,6 @@ type NodeResources struct {
|
|||||||
type ResourceName string
|
type ResourceName string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// The default compute resource namespace for all standard resource types.
|
|
||||||
DefaultResourceNamespace = "kubernetes.io"
|
|
||||||
// CPU, in cores. (500m = .5 cores)
|
// CPU, in cores. (500m = .5 cores)
|
||||||
ResourceCPU ResourceName = "cpu"
|
ResourceCPU ResourceName = "cpu"
|
||||||
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
|
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
|
||||||
|
@ -444,7 +444,140 @@ func init() {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
// Converts internal Container to v1beta1.Container.
|
||||||
|
// Fields 'CPU' and 'Memory' are not present in the internal Container object.
|
||||||
|
// Hence the need for a custom conversion function.
|
||||||
|
func(in *newer.Container, out *Container, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Name, &out.Name, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Ports, &out.Ports, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Env, &out.Env, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(in.Resources.Limits.Cpu(), &out.CPU, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(in.Resources.Limits.Memory(), &out.Memory, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.VolumeMounts, &out.VolumeMounts, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.LivenessProbe, &out.LivenessProbe, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Lifecycle, &out.Lifecycle, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Internal API does not support CPU to be specified via an explicit field.
|
||||||
|
// Hence it must be stored in Container.Resources.
|
||||||
|
func(in *int, out *newer.ResourceList, s conversion.Scope) error {
|
||||||
|
if *in <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
quantity := resource.Quantity{}
|
||||||
|
if err := s.Convert(in, &quantity, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
(*out)[newer.ResourceCPU] = quantity
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Internal API does not support Memory to be specified via an explicit field.
|
||||||
|
// Hence it must be stored in Container.Resources.
|
||||||
|
func(in *int64, out *newer.ResourceList, s conversion.Scope) error {
|
||||||
|
if *in <= 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
quantity := resource.Quantity{}
|
||||||
|
if err := s.Convert(in, &quantity, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
(*out)[newer.ResourceMemory] = quantity
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Converts v1beta1.Container to internal Container.
|
||||||
|
// Fields 'CPU' and 'Memory' are not present in the internal Container object.
|
||||||
|
// Hence the need for a custom conversion function.
|
||||||
|
func(in *Container, out *newer.Container, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Name, &out.Name, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Ports, &out.Ports, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Env, &out.Env, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.CPU, &out.Resources.Limits, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Memory, &out.Resources.Limits, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.VolumeMounts, &out.VolumeMounts, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.LivenessProbe, &out.LivenessProbe, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Lifecycle, &out.Lifecycle, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error {
|
func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error {
|
||||||
if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil {
|
if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -22,7 +22,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
current "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
current "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Convert = newer.Scheme.Convert
|
var Convert = newer.Scheme.Convert
|
||||||
@ -316,3 +318,67 @@ func TestPullPolicyConversion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getResourceRequirements(cpu, memory resource.Quantity) current.ResourceRequirementSpec {
|
||||||
|
res := current.ResourceRequirementSpec{}
|
||||||
|
res.Limits = current.ResourceList{}
|
||||||
|
if cpu.Value() > 0 {
|
||||||
|
res.Limits[current.ResourceCPU] = util.NewIntOrStringFromInt(int(cpu.Value()))
|
||||||
|
}
|
||||||
|
if memory.Value() > 0 {
|
||||||
|
res.Limits[current.ResourceMemory] = util.NewIntOrStringFromInt(int(memory.Value()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainerConversion(t *testing.T) {
|
||||||
|
cpuLimit := resource.MustParse("10")
|
||||||
|
memoryLimit := resource.MustParse("10M")
|
||||||
|
null := resource.Quantity{}
|
||||||
|
testCases := []current.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Resources: getResourceRequirements(cpuLimit, memoryLimit),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Resources: getResourceRequirements(null, memoryLimit),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
Resources: getResourceRequirements(cpuLimit, null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
Resources: getResourceRequirements(cpuLimit, resource.MustParse("100M")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Resources: getResourceRequirements(resource.MustParse("500"), memoryLimit),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
got := newer.Container{}
|
||||||
|
if err := Convert(&tc, &got); err != nil {
|
||||||
|
t.Errorf("[Case: %d] Unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cpu := got.Resources.Limits.Cpu(); cpu.Value() != cpuLimit.Value() {
|
||||||
|
t.Errorf("[Case: %d] Expected cpu: %v, got: %v", i, cpuLimit, *cpu)
|
||||||
|
}
|
||||||
|
if memory := got.Resources.Limits.Memory(); memory.Value() != memoryLimit.Value() {
|
||||||
|
t.Errorf("[Case: %d] Expected memory: %v, got: %v", i, memoryLimit, *memory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -252,6 +252,11 @@ type Capabilities struct {
|
|||||||
Drop []CapabilityType `json:"drop,omitempty" description:"droped capabilities"`
|
Drop []CapabilityType `json:"drop,omitempty" description:"droped capabilities"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResourceRequirementSpec struct {
|
||||||
|
// Limits describes the maximum amount of compute resources required.
|
||||||
|
Limits ResourceList `json:"limits,omitempty" description:"Maximum amount of compute resources allowed"`
|
||||||
|
}
|
||||||
|
|
||||||
// Container represents a single container that is expected to be run on the host.
|
// Container represents a single container that is expected to be run on the host.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
// Required: This must be a DNS_LABEL. Each container in a pod must
|
// Required: This must be a DNS_LABEL. Each container in a pod must
|
||||||
@ -265,10 +270,11 @@ type Container struct {
|
|||||||
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default"`
|
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default"`
|
||||||
Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"`
|
Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"`
|
||||||
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"`
|
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"`
|
||||||
// Optional: Defaults to unlimited.
|
Resources ResourceRequirementSpec `json:"resources,omitempty" description:"Compute Resources required by this container"`
|
||||||
Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"`
|
|
||||||
// Optional: Defaults to unlimited.
|
// Optional: Defaults to unlimited.
|
||||||
CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"`
|
CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"`
|
||||||
|
// Optional: Defaults to unlimited.
|
||||||
|
Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"`
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"`
|
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"`
|
||||||
LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty" description:"periodic probe of container liveness; container will be restarted if the probe fails"`
|
LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty" description:"periodic probe of container liveness; container will be restarted if the probe fails"`
|
||||||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events"`
|
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events"`
|
||||||
|
@ -295,7 +295,143 @@ func init() {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
// Converts internal Container to v1beta1.Container.
|
||||||
|
// Fields 'CPU' and 'Memory' are not present in the internal Container object.
|
||||||
|
// Hence the need for a custom conversion function.
|
||||||
|
func(in *newer.Container, out *Container, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Name, &out.Name, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := s.Convert(&in.Ports, &out.Ports, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Env, &out.Env, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(in.Resources.Limits.Cpu(), &out.CPU, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(in.Resources.Limits.Memory(), &out.Memory, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.VolumeMounts, &out.VolumeMounts, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.LivenessProbe, &out.LivenessProbe, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Lifecycle, &out.Lifecycle, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Internal API does not support CPU to be specified via an explicit field.
|
||||||
|
// Hence it must be stored in Container.Resources.
|
||||||
|
func(in *int, out *newer.ResourceList, s conversion.Scope) error {
|
||||||
|
if *in == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
quantity := resource.Quantity{}
|
||||||
|
if err := s.Convert(in, &quantity, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
(*out)[newer.ResourceCPU] = quantity
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Internal API does not support Memory to be specified via an explicit field.
|
||||||
|
// Hence it must be stored in Container.Resources.
|
||||||
|
func(in *int64, out *newer.ResourceList, s conversion.Scope) error {
|
||||||
|
if *in == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
quantity := resource.Quantity{}
|
||||||
|
if err := s.Convert(in, &quantity, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
(*out)[newer.ResourceMemory] = quantity
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
// Converts v1beta1.Container to internal newer.Container.
|
||||||
|
// Fields 'CPU' and 'Memory' are not present in the internal newer.Container object.
|
||||||
|
// Hence the need for a custom conversion function.
|
||||||
|
func(in *Container, out *newer.Container, s conversion.Scope) error {
|
||||||
|
if err := s.Convert(&in.Name, &out.Name, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Image, &out.Image, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Command, &out.Command, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.WorkingDir, &out.WorkingDir, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Ports, &out.Ports, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Env, &out.Env, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Resources, &out.Resources, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.CPU, &out.Resources.Limits, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Memory, &out.Resources.Limits, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.VolumeMounts, &out.VolumeMounts, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.LivenessProbe, &out.LivenessProbe, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Lifecycle, &out.Lifecycle, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.TerminationMessagePath, &out.TerminationMessagePath, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Privileged, &out.Privileged, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.ImagePullPolicy, &out.ImagePullPolicy, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := s.Convert(&in.Capabilities, &out.Capabilities, 0); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error {
|
func(in *newer.PodSpec, out *ContainerManifest, s conversion.Scope) error {
|
||||||
if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil {
|
if err := s.Convert(&in.Volumes, &out.Volumes, 0); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -21,7 +21,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
newer "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
current "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
current "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta2"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestServiceEmptySelector(t *testing.T) {
|
func TestServiceEmptySelector(t *testing.T) {
|
||||||
@ -146,3 +148,67 @@ func TestPullPolicyConversion(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getResourceRequirements(cpu, memory resource.Quantity) current.ResourceRequirementSpec {
|
||||||
|
res := current.ResourceRequirementSpec{}
|
||||||
|
res.Limits = current.ResourceList{}
|
||||||
|
if cpu.Value() > 0 {
|
||||||
|
res.Limits[current.ResourceCPU] = util.NewIntOrStringFromInt(int(cpu.Value()))
|
||||||
|
}
|
||||||
|
if memory.Value() > 0 {
|
||||||
|
res.Limits[current.ResourceMemory] = util.NewIntOrStringFromInt(int(memory.Value()))
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestContainerConversion(t *testing.T) {
|
||||||
|
cpuLimit := resource.MustParse("10")
|
||||||
|
memoryLimit := resource.MustParse("10M")
|
||||||
|
null := resource.Quantity{}
|
||||||
|
testCases := []current.Container{
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Resources: getResourceRequirements(cpuLimit, memoryLimit),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Resources: getResourceRequirements(null, memoryLimit),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
Resources: getResourceRequirements(cpuLimit, null),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
Memory: memoryLimit.Value(),
|
||||||
|
Resources: getResourceRequirements(cpuLimit, resource.MustParse("100M")),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name: "container",
|
||||||
|
CPU: int(cpuLimit.MilliValue()),
|
||||||
|
Resources: getResourceRequirements(resource.MustParse("500"), memoryLimit),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tc := range testCases {
|
||||||
|
got := newer.Container{}
|
||||||
|
if err := newer.Scheme.Convert(&tc, &got); err != nil {
|
||||||
|
t.Errorf("[Case: %d] Unexpected error: %v", i, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cpu := got.Resources.Limits.Cpu(); cpu.Value() != cpuLimit.Value() {
|
||||||
|
t.Errorf("[Case: %d] Expected cpu: %v, got: %v", i, cpuLimit, *cpu)
|
||||||
|
}
|
||||||
|
if memory := got.Resources.Limits.Memory(); memory.Value() != memoryLimit.Value() {
|
||||||
|
t.Errorf("[Case: %d] Expected memory: %v, got: %v", i, memoryLimit, *memory)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -216,6 +216,11 @@ type Capabilities struct {
|
|||||||
Drop []CapabilityType `json:"drop,omitempty" description:"droped capabilities"`
|
Drop []CapabilityType `json:"drop,omitempty" description:"droped capabilities"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ResourceRequirementSpec struct {
|
||||||
|
// Limits describes the maximum amount of compute resources required.
|
||||||
|
Limits ResourceList `json:"limits,omitempty" description:"Maximum amount of compute resources allowed"`
|
||||||
|
}
|
||||||
|
|
||||||
// Container represents a single container that is expected to be run on the host.
|
// Container represents a single container that is expected to be run on the host.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
// Required: This must be a DNS_LABEL. Each container in a pod must
|
// Required: This must be a DNS_LABEL. Each container in a pod must
|
||||||
@ -229,10 +234,11 @@ type Container struct {
|
|||||||
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default"`
|
WorkingDir string `json:"workingDir,omitempty" description:"container's working directory; defaults to image's default"`
|
||||||
Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"`
|
Ports []Port `json:"ports,omitempty" description:"list of ports to expose from the container"`
|
||||||
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"`
|
Env []EnvVar `json:"env,omitempty" description:"list of environment variables to set in the container"`
|
||||||
// Optional: Defaults to unlimited.
|
Resources ResourceRequirementSpec `json:"resources,omitempty" description:"Compute Resources required by this container"`
|
||||||
Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"`
|
|
||||||
// Optional: Defaults to unlimited.
|
// Optional: Defaults to unlimited.
|
||||||
CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"`
|
CPU int `json:"cpu,omitempty" description:"CPU share in thousandths of a core"`
|
||||||
|
// Optional: Defaults to unlimited.
|
||||||
|
Memory int64 `json:"memory,omitempty" description:"memory limit in bytes; defaults to unlimited"`
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"`
|
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty" description:"pod volumes to mount into the container's filesystem"`
|
||||||
LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty" description:"periodic probe of container liveness; container will be restarted if the probe fails"`
|
LivenessProbe *LivenessProbe `json:"livenessProbe,omitempty" description:"periodic probe of container liveness; container will be restarted if the probe fails"`
|
||||||
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events"`
|
Lifecycle *Lifecycle `json:"lifecycle,omitempty" description:"actions that the management system should take in response to container lifecycle events"`
|
||||||
|
@ -322,6 +322,12 @@ type Capabilities struct {
|
|||||||
Drop []CapabilityType `json:"drop,omitempty"`
|
Drop []CapabilityType `json:"drop,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResourceRequirementSpec describes the compute resource requirements.
|
||||||
|
type ResourceRequirementSpec struct {
|
||||||
|
// Limits describes the maximum amount of compute resources required.
|
||||||
|
Limits ResourceList `json:"limits,omitempty" description:"Maximum amount of compute resources allowed"`
|
||||||
|
}
|
||||||
|
|
||||||
// Container represents a single container that is expected to be run on the host.
|
// Container represents a single container that is expected to be run on the host.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
// Required: This must be a DNS_LABEL. Each container in a pod must
|
// Required: This must be a DNS_LABEL. Each container in a pod must
|
||||||
@ -335,10 +341,7 @@ type Container struct {
|
|||||||
WorkingDir string `json:"workingDir,omitempty"`
|
WorkingDir string `json:"workingDir,omitempty"`
|
||||||
Ports []Port `json:"ports,omitempty"`
|
Ports []Port `json:"ports,omitempty"`
|
||||||
Env []EnvVar `json:"env,omitempty"`
|
Env []EnvVar `json:"env,omitempty"`
|
||||||
// Optional: Defaults to unlimited. Units: bytes.
|
Resources ResourceRequirementSpec `json:"resources,omitempty" description:"Compute Resources required by this container"`
|
||||||
Memory resource.Quantity `json:"memory,omitempty"`
|
|
||||||
// Optional: Defaults to unlimited. Units: Cores. (500m == 1/2 core)
|
|
||||||
CPU resource.Quantity `json:"cpu,omitempty"`
|
|
||||||
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
VolumeMounts []VolumeMount `json:"volumeMounts,omitempty"`
|
||||||
LivenessProbe *Probe `json:"livenessProbe,omitempty"`
|
LivenessProbe *Probe `json:"livenessProbe,omitempty"`
|
||||||
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
Lifecycle *Lifecycle `json:"lifecycle,omitempty"`
|
||||||
@ -796,8 +799,6 @@ type NodeCondition struct {
|
|||||||
type ResourceName string
|
type ResourceName string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// The default compute resource namespace for all standard resource types.
|
|
||||||
DefaultResourceNamespace = "kubernetes.io"
|
|
||||||
// CPU, in cores. (500m = .5 cores)
|
// CPU, in cores. (500m = .5 cores)
|
||||||
ResourceCPU ResourceName = "cpu"
|
ResourceCPU ResourceName = "cpu"
|
||||||
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
|
// Memory, in bytes. (500Gi = 500GiB = 500 * 1024 * 1024 * 1024)
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||||
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
errs "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
|
||||||
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/capabilities"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
|
||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||||
@ -395,6 +396,7 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs
|
|||||||
cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...)
|
cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...)
|
||||||
cErrs = append(cErrs, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...)
|
cErrs = append(cErrs, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...)
|
||||||
cErrs = append(cErrs, validatePullPolicyWithDefault(ctr).Prefix("pullPolicy")...)
|
cErrs = append(cErrs, validatePullPolicyWithDefault(ctr).Prefix("pullPolicy")...)
|
||||||
|
cErrs = append(cErrs, validateResourceRequirements(ctr).Prefix("resources")...)
|
||||||
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
||||||
}
|
}
|
||||||
// Check for colliding ports across all containers.
|
// Check for colliding ports across all containers.
|
||||||
@ -696,28 +698,17 @@ func ValidateMinionUpdate(oldMinion *api.Node, minion *api.Node) errs.Validation
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// Typename is a generic representation for all compute resource typenames.
|
// Validate compute resource typename.
|
||||||
// Refer to docs/resources.md for more details.
|
// Refer to docs/resources.md for more details.
|
||||||
func ValidateResourceName(str string) errs.ValidationErrorList {
|
func validateResourceName(str string) errs.ValidationErrorList {
|
||||||
if !util.IsQualifiedName(str) {
|
if !util.IsQualifiedName(str) {
|
||||||
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename format %q", str)}
|
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename format %q", str)}
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(str, "/")
|
if len(strings.Split(str, "/")) == 1 {
|
||||||
switch len(parts) {
|
if !api.IsStandardResourceName(str) {
|
||||||
case 1:
|
|
||||||
if !api.IsStandardResourceName(parts[0]) {
|
|
||||||
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename. %q is neither a standard resource type nor is fully qualified", str)}
|
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename. %q is neither a standard resource type nor is fully qualified", str)}
|
||||||
}
|
}
|
||||||
break
|
|
||||||
case 2:
|
|
||||||
if parts[0] == api.DefaultResourceNamespace {
|
|
||||||
if !api.IsStandardResourceName(parts[1]) {
|
|
||||||
return errs.ValidationErrorList{fmt.Errorf("invalid compute resource typename. %q contains a compute resource type not supported", str)}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return errs.ValidationErrorList{}
|
return errs.ValidationErrorList{}
|
||||||
@ -740,15 +731,37 @@ func ValidateLimitRange(limitRange *api.LimitRange) errs.ValidationErrorList {
|
|||||||
for i := range limitRange.Spec.Limits {
|
for i := range limitRange.Spec.Limits {
|
||||||
limit := limitRange.Spec.Limits[i]
|
limit := limitRange.Spec.Limits[i]
|
||||||
for k := range limit.Max {
|
for k := range limit.Max {
|
||||||
allErrs = append(allErrs, ValidateResourceName(string(k))...)
|
allErrs = append(allErrs, validateResourceName(string(k))...)
|
||||||
}
|
}
|
||||||
for k := range limit.Min {
|
for k := range limit.Min {
|
||||||
allErrs = append(allErrs, ValidateResourceName(string(k))...)
|
allErrs = append(allErrs, validateResourceName(string(k))...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func validateBasicResource(quantity resource.Quantity) errs.ValidationErrorList {
|
||||||
|
if quantity.Value() < 0 {
|
||||||
|
return errs.ValidationErrorList{fmt.Errorf("%v is not a valid resource quantity", quantity.Value())}
|
||||||
|
}
|
||||||
|
return errs.ValidationErrorList{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validates resource requirement spec.
|
||||||
|
func validateResourceRequirements(container *api.Container) errs.ValidationErrorList {
|
||||||
|
allErrs := errs.ValidationErrorList{}
|
||||||
|
for resourceName, quantity := range container.Resources.Limits {
|
||||||
|
// Validate resource name.
|
||||||
|
errs := validateResourceName(resourceName.String())
|
||||||
|
if api.IsStandardResourceName(resourceName.String()) {
|
||||||
|
errs = append(errs, validateBasicResource(quantity).Prefix(fmt.Sprintf("Resource %s: ", resourceName))...)
|
||||||
|
}
|
||||||
|
allErrs = append(allErrs, errs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return allErrs
|
||||||
|
}
|
||||||
|
|
||||||
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
|
// ValidateResourceQuota tests if required fields in the ResourceQuota are set.
|
||||||
func ValidateResourceQuota(resourceQuota *api.ResourceQuota) errs.ValidationErrorList {
|
func ValidateResourceQuota(resourceQuota *api.ResourceQuota) errs.ValidationErrorList {
|
||||||
allErrs := errs.ValidationErrorList{}
|
allErrs := errs.ValidationErrorList{}
|
||||||
@ -763,13 +776,13 @@ func ValidateResourceQuota(resourceQuota *api.ResourceQuota) errs.ValidationErro
|
|||||||
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", resourceQuota.Namespace, ""))
|
allErrs = append(allErrs, errs.NewFieldInvalid("namespace", resourceQuota.Namespace, ""))
|
||||||
}
|
}
|
||||||
for k := range resourceQuota.Spec.Hard {
|
for k := range resourceQuota.Spec.Hard {
|
||||||
allErrs = append(allErrs, ValidateResourceName(string(k))...)
|
allErrs = append(allErrs, validateResourceName(string(k))...)
|
||||||
}
|
}
|
||||||
for k := range resourceQuota.Status.Hard {
|
for k := range resourceQuota.Status.Hard {
|
||||||
allErrs = append(allErrs, ValidateResourceName(string(k))...)
|
allErrs = append(allErrs, validateResourceName(string(k))...)
|
||||||
}
|
}
|
||||||
for k := range resourceQuota.Status.Used {
|
for k := range resourceQuota.Status.Used {
|
||||||
allErrs = append(allErrs, ValidateResourceName(string(k))...)
|
allErrs = append(allErrs, validateResourceName(string(k))...)
|
||||||
}
|
}
|
||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
@ -268,6 +268,13 @@ func TestValidatePullPolicy(t *testing.T) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getResourceLimits(cpu, memory string) api.ResourceList {
|
||||||
|
res := api.ResourceList{}
|
||||||
|
res[api.ResourceCPU] = resource.MustParse(cpu)
|
||||||
|
res[api.ResourceMemory] = resource.MustParse(memory)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func TestValidateContainers(t *testing.T) {
|
func TestValidateContainers(t *testing.T) {
|
||||||
volumes := util.StringSet{}
|
volumes := util.StringSet{}
|
||||||
capabilities.SetForTests(capabilities.Capabilities{
|
capabilities.SetForTests(capabilities.Capabilities{
|
||||||
@ -287,6 +294,17 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "resources-test",
|
||||||
|
Image: "image",
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
api.ResourceName(api.ResourceCPU): resource.MustParse("10"),
|
||||||
|
api.ResourceName(api.ResourceMemory): resource.MustParse("10G"),
|
||||||
|
api.ResourceName("my.org/resource"): resource.MustParse("10m"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{Name: "abc-1234", Image: "image", Privileged: true},
|
{Name: "abc-1234", Image: "image", Privileged: true},
|
||||||
}
|
}
|
||||||
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
|
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
|
||||||
@ -349,6 +367,35 @@ func TestValidateContainers(t *testing.T) {
|
|||||||
"privilege disabled": {
|
"privilege disabled": {
|
||||||
{Name: "abc", Image: "image", Privileged: true},
|
{Name: "abc", Image: "image", Privileged: true},
|
||||||
},
|
},
|
||||||
|
"invalid compute resource": {
|
||||||
|
{
|
||||||
|
Name: "abc-123",
|
||||||
|
Image: "image",
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
"disk": resource.MustParse("10G"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Resource CPU invalid": {
|
||||||
|
{
|
||||||
|
Name: "abc-123",
|
||||||
|
Image: "image",
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: getResourceLimits("-10", "0"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"Resource Memory invalid": {
|
||||||
|
{
|
||||||
|
Name: "abc-123",
|
||||||
|
Image: "image",
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: getResourceLimits("0", "-10"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for k, v := range errorCases {
|
for k, v := range errorCases {
|
||||||
if errs := validateContainers(v, volumes); len(errs) == 0 {
|
if errs := validateContainers(v, volumes); len(errs) == 0 {
|
||||||
@ -422,8 +469,12 @@ func TestValidateManifest(t *testing.T) {
|
|||||||
Image: "image",
|
Image: "image",
|
||||||
Command: []string{"foo", "bar"},
|
Command: []string{"foo", "bar"},
|
||||||
WorkingDir: "/tmp",
|
WorkingDir: "/tmp",
|
||||||
Memory: resource.MustParse("1"),
|
Resources: api.ResourceRequirementSpec{
|
||||||
CPU: resource.MustParse("1"),
|
Limits: api.ResourceList{
|
||||||
|
"cpu": resource.MustParse("1"),
|
||||||
|
"memory": resource.MustParse("1"),
|
||||||
|
},
|
||||||
|
},
|
||||||
Ports: []api.Port{
|
Ports: []api.Port{
|
||||||
{Name: "p1", ContainerPort: 80, HostPort: 8080},
|
{Name: "p1", ContainerPort: 80, HostPort: 8080},
|
||||||
{Name: "p2", ContainerPort: 81},
|
{Name: "p2", ContainerPort: 81},
|
||||||
@ -711,7 +762,9 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "foo:V1",
|
Image: "foo:V1",
|
||||||
CPU: resource.MustParse("100m"),
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: getResourceLimits("100m", "0"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -722,7 +775,9 @@ func TestValidatePodUpdate(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "foo:V2",
|
Image: "foo:V2",
|
||||||
CPU: resource.MustParse("1000m"),
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: getResourceLimits("1000m", "0"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -1675,8 +1730,6 @@ func TestValidateResourceNames(t *testing.T) {
|
|||||||
{"", false},
|
{"", false},
|
||||||
{".", false},
|
{".", false},
|
||||||
{"..", false},
|
{"..", false},
|
||||||
{"kubernetes.io/cpu", true},
|
|
||||||
{"kubernetes.io/disk", false},
|
|
||||||
{"my.favorite.app.co/12345", true},
|
{"my.favorite.app.co/12345", true},
|
||||||
{"my.favorite.app.co/_12345", false},
|
{"my.favorite.app.co/_12345", false},
|
||||||
{"my.favorite.app.co/12345_", false},
|
{"my.favorite.app.co/12345_", false},
|
||||||
@ -1687,7 +1740,7 @@ func TestValidateResourceNames(t *testing.T) {
|
|||||||
{"kubernetes.io/will/not/work/", false},
|
{"kubernetes.io/will/not/work/", false},
|
||||||
}
|
}
|
||||||
for _, item := range table {
|
for _, item := range table {
|
||||||
err := ValidateResourceName(item.input)
|
err := validateResourceName(item.input)
|
||||||
if len(err) != 0 && item.success {
|
if len(err) != 0 && item.success {
|
||||||
t.Errorf("expected no failure for input %q", item.input)
|
t.Errorf("expected no failure for input %q", item.input)
|
||||||
} else if len(err) == 0 && !item.success {
|
} else if len(err) == 0 && !item.success {
|
||||||
|
@ -649,8 +649,8 @@ func (kl *Kubelet) runContainer(pod *api.BoundPod, container *api.Container, pod
|
|||||||
ExposedPorts: exposedPorts,
|
ExposedPorts: exposedPorts,
|
||||||
Hostname: pod.Name,
|
Hostname: pod.Name,
|
||||||
Image: container.Image,
|
Image: container.Image,
|
||||||
Memory: container.Memory.Value(),
|
Memory: container.Resources.Limits.Memory().Value(),
|
||||||
CPUShares: milliCPUToShares(container.CPU.MilliValue()),
|
CPUShares: milliCPUToShares(container.Resources.Limits.Cpu().MilliValue()),
|
||||||
WorkingDir: container.WorkingDir,
|
WorkingDir: container.WorkingDir,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -179,7 +179,7 @@ func (rm *ResourceQuotaManager) syncResourceQuota(quota api.ResourceQuota) (err
|
|||||||
func PodCPU(pod *api.Pod) *resource.Quantity {
|
func PodCPU(pod *api.Pod) *resource.Quantity {
|
||||||
val := int64(0)
|
val := int64(0)
|
||||||
for j := range pod.Spec.Containers {
|
for j := range pod.Spec.Containers {
|
||||||
val = val + pod.Spec.Containers[j].CPU.MilliValue()
|
val = val + pod.Spec.Containers[j].Resources.Limits.Cpu().MilliValue()
|
||||||
}
|
}
|
||||||
return resource.NewMilliQuantity(int64(val), resource.DecimalSI)
|
return resource.NewMilliQuantity(int64(val), resource.DecimalSI)
|
||||||
}
|
}
|
||||||
@ -188,7 +188,7 @@ func PodCPU(pod *api.Pod) *resource.Quantity {
|
|||||||
func PodMemory(pod *api.Pod) *resource.Quantity {
|
func PodMemory(pod *api.Pod) *resource.Quantity {
|
||||||
val := int64(0)
|
val := int64(0)
|
||||||
for j := range pod.Spec.Containers {
|
for j := range pod.Spec.Containers {
|
||||||
val = val + pod.Spec.Containers[j].Memory.Value()
|
val = val + pod.Spec.Containers[j].Resources.Limits.Memory().Value()
|
||||||
}
|
}
|
||||||
return resource.NewQuantity(int64(val), resource.DecimalSI)
|
return resource.NewQuantity(int64(val), resource.DecimalSI)
|
||||||
}
|
}
|
||||||
|
@ -95,8 +95,9 @@ type resourceRequest struct {
|
|||||||
func getResourceRequest(pod *api.Pod) resourceRequest {
|
func getResourceRequest(pod *api.Pod) resourceRequest {
|
||||||
result := resourceRequest{}
|
result := resourceRequest{}
|
||||||
for ix := range pod.Spec.Containers {
|
for ix := range pod.Spec.Containers {
|
||||||
result.memory += pod.Spec.Containers[ix].Memory.Value()
|
limits := pod.Spec.Containers[ix].Resources.Limits
|
||||||
result.milliCPU += pod.Spec.Containers[ix].CPU.MilliValue()
|
result.memory += limits.Memory().Value()
|
||||||
|
result.milliCPU += limits.Cpu().MilliValue()
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -120,8 +121,8 @@ func (r *ResourceFit) PodFitsResources(pod api.Pod, existingPods []api.Pod, node
|
|||||||
memoryRequested += existingRequest.memory
|
memoryRequested += existingRequest.memory
|
||||||
}
|
}
|
||||||
|
|
||||||
totalMilliCPU := info.Spec.Capacity.Get(api.ResourceCPU).MilliValue()
|
totalMilliCPU := info.Spec.Capacity.Cpu().MilliValue()
|
||||||
totalMemory := info.Spec.Capacity.Get(api.ResourceMemory).Value()
|
totalMemory := info.Spec.Capacity.Memory().Value()
|
||||||
|
|
||||||
fitsCPU := totalMilliCPU == 0 || (totalMilliCPU-milliCPURequested) >= podRequest.milliCPU
|
fitsCPU := totalMilliCPU == 0 || (totalMilliCPU-milliCPURequested) >= podRequest.milliCPU
|
||||||
fitsMemory := totalMemory == 0 || (totalMemory-memoryRequested) >= podRequest.memory
|
fitsMemory := totalMemory == 0 || (totalMemory-memoryRequested) >= podRequest.memory
|
||||||
|
@ -46,8 +46,8 @@ func (nodes FakeNodeListInfo) GetNodeInfo(nodeName string) (*api.Node, error) {
|
|||||||
func makeResources(milliCPU int64, memory int64) api.NodeResources {
|
func makeResources(milliCPU int64, memory int64) api.NodeResources {
|
||||||
return api.NodeResources{
|
return api.NodeResources{
|
||||||
Capacity: api.ResourceList{
|
Capacity: api.ResourceList{
|
||||||
api.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
"cpu": *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
||||||
api.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
|
"memory": *resource.NewQuantity(memory, resource.BinarySI),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -56,8 +56,12 @@ func newResourcePod(usage ...resourceRequest) api.Pod {
|
|||||||
containers := []api.Container{}
|
containers := []api.Container{}
|
||||||
for _, req := range usage {
|
for _, req := range usage {
|
||||||
containers = append(containers, api.Container{
|
containers = append(containers, api.Container{
|
||||||
Memory: *resource.NewQuantity(req.memory, resource.BinarySI),
|
Resources: api.ResourceRequirementSpec{
|
||||||
CPU: *resource.NewMilliQuantity(req.milliCPU, resource.DecimalSI),
|
Limits: api.ResourceList{
|
||||||
|
"cpu": *resource.NewMilliQuantity(req.milliCPU, resource.DecimalSI),
|
||||||
|
"memory": *resource.NewQuantity(req.memory, resource.BinarySI),
|
||||||
|
},
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return api.Pod{
|
return api.Pod{
|
||||||
|
@ -42,19 +42,19 @@ func calculateOccupancy(pod api.Pod, node api.Node, pods []api.Pod) HostPriority
|
|||||||
totalMemory := int64(0)
|
totalMemory := int64(0)
|
||||||
for _, existingPod := range pods {
|
for _, existingPod := range pods {
|
||||||
for _, container := range existingPod.Spec.Containers {
|
for _, container := range existingPod.Spec.Containers {
|
||||||
totalMilliCPU += container.CPU.MilliValue()
|
totalMilliCPU += container.Resources.Limits.Cpu().MilliValue()
|
||||||
totalMemory += container.Memory.Value()
|
totalMemory += container.Resources.Limits.Memory().Value()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add the resources requested by the current pod being scheduled.
|
// Add the resources requested by the current pod being scheduled.
|
||||||
// This also helps differentiate between differently sized, but empty, minions.
|
// This also helps differentiate between differently sized, but empty, minions.
|
||||||
for _, container := range pod.Spec.Containers {
|
for _, container := range pod.Spec.Containers {
|
||||||
totalMilliCPU += container.CPU.MilliValue()
|
totalMilliCPU += container.Resources.Limits.Cpu().MilliValue()
|
||||||
totalMemory += container.Memory.Value()
|
totalMemory += container.Resources.Limits.Memory().Value()
|
||||||
}
|
}
|
||||||
|
|
||||||
capacityMilliCPU := node.Spec.Capacity.Get(api.ResourceCPU).MilliValue()
|
capacityMilliCPU := node.Spec.Capacity.Cpu().MilliValue()
|
||||||
capacityMemory := node.Spec.Capacity.Get(api.ResourceMemory).Value()
|
capacityMemory := node.Spec.Capacity.Memory().Value()
|
||||||
|
|
||||||
cpuScore := calculateScore(totalMilliCPU, capacityMilliCPU, node.Name)
|
cpuScore := calculateScore(totalMilliCPU, capacityMilliCPU, node.Name)
|
||||||
memoryScore := calculateScore(totalMemory, capacityMemory, node.Name)
|
memoryScore := calculateScore(totalMemory, capacityMemory, node.Name)
|
||||||
|
@ -30,8 +30,8 @@ func makeMinion(node string, milliCPU, memory int64) api.Node {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: node},
|
ObjectMeta: api.ObjectMeta{Name: node},
|
||||||
Spec: api.NodeSpec{
|
Spec: api.NodeSpec{
|
||||||
Capacity: api.ResourceList{
|
Capacity: api.ResourceList{
|
||||||
api.ResourceCPU: *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
"cpu": *resource.NewMilliQuantity(milliCPU, resource.DecimalSI),
|
||||||
api.ResourceMemory: *resource.NewQuantity(memory, resource.BinarySI),
|
"memory": *resource.NewQuantity(memory, resource.BinarySI),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -57,14 +57,40 @@ func TestLeastRequested(t *testing.T) {
|
|||||||
}
|
}
|
||||||
cpuOnly := api.PodSpec{
|
cpuOnly := api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{CPU: resource.MustParse("1000m")},
|
{
|
||||||
{CPU: resource.MustParse("2000m")},
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
"cpu": resource.MustParse("1000m"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
"cpu": resource.MustParse("2000m"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
cpuAndMemory := api.PodSpec{
|
cpuAndMemory := api.PodSpec{
|
||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{CPU: resource.MustParse("1000m"), Memory: resource.MustParse("2000")},
|
{
|
||||||
{CPU: resource.MustParse("2000m"), Memory: resource.MustParse("3000")},
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
"cpu": resource.MustParse("1000m"),
|
||||||
|
"memory": resource.MustParse("2000"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Resources: api.ResourceRequirementSpec{
|
||||||
|
Limits: api.ResourceList{
|
||||||
|
"cpu": resource.MustParse("2000m"),
|
||||||
|
"memory": resource.MustParse("3000"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
@ -103,8 +103,8 @@ func PodLimitFunc(limitRange *api.LimitRange, kind string, obj runtime.Object) e
|
|||||||
|
|
||||||
for i := range pod.Spec.Containers {
|
for i := range pod.Spec.Containers {
|
||||||
container := pod.Spec.Containers[i]
|
container := pod.Spec.Containers[i]
|
||||||
containerCPU := container.CPU.MilliValue()
|
containerCPU := container.Resources.Limits.Cpu().MilliValue()
|
||||||
containerMem := container.Memory.Value()
|
containerMem := container.Resources.Limits.Memory().Value()
|
||||||
|
|
||||||
if i == 0 {
|
if i == 0 {
|
||||||
minContainerCPU = containerCPU
|
minContainerCPU = containerCPU
|
||||||
@ -113,8 +113,8 @@ func PodLimitFunc(limitRange *api.LimitRange, kind string, obj runtime.Object) e
|
|||||||
maxContainerMem = containerMem
|
maxContainerMem = containerMem
|
||||||
}
|
}
|
||||||
|
|
||||||
podCPU = podCPU + container.CPU.MilliValue()
|
podCPU = podCPU + container.Resources.Limits.Cpu().MilliValue()
|
||||||
podMem = podMem + container.Memory.Value()
|
podMem = podMem + container.Resources.Limits.Memory().Value()
|
||||||
|
|
||||||
minContainerCPU = Min(containerCPU, minContainerCPU)
|
minContainerCPU = Min(containerCPU, minContainerCPU)
|
||||||
minContainerMem = Min(containerMem, minContainerMem)
|
minContainerMem = Min(containerMem, minContainerMem)
|
||||||
|
@ -23,6 +23,19 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/resource"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getResourceRequirements(cpu, memory string) api.ResourceRequirementSpec {
|
||||||
|
res := api.ResourceRequirementSpec{}
|
||||||
|
res.Limits = api.ResourceList{}
|
||||||
|
if cpu != "" {
|
||||||
|
res.Limits[api.ResourceCPU] = resource.MustParse(cpu)
|
||||||
|
}
|
||||||
|
if memory != "" {
|
||||||
|
res.Limits[api.ResourceMemory] = resource.MustParse(memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func TestPodLimitFunc(t *testing.T) {
|
func TestPodLimitFunc(t *testing.T) {
|
||||||
limitRange := &api.LimitRange{
|
limitRange := &api.LimitRange{
|
||||||
ObjectMeta: api.ObjectMeta{
|
ObjectMeta: api.ObjectMeta{
|
||||||
@ -32,25 +45,13 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Limits: []api.LimitRangeItem{
|
Limits: []api.LimitRangeItem{
|
||||||
{
|
{
|
||||||
Type: api.LimitTypePod,
|
Type: api.LimitTypePod,
|
||||||
Max: api.ResourceList{
|
Max: getResourceRequirements("200m", "4Gi").Limits,
|
||||||
api.ResourceCPU: resource.MustParse("200m"),
|
Min: getResourceRequirements("50m", "2Mi").Limits,
|
||||||
api.ResourceMemory: resource.MustParse("4Gi"),
|
|
||||||
},
|
|
||||||
Min: api.ResourceList{
|
|
||||||
api.ResourceCPU: resource.MustParse("50m"),
|
|
||||||
api.ResourceMemory: resource.MustParse("2Mi"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Type: api.LimitTypeContainer,
|
Type: api.LimitTypeContainer,
|
||||||
Max: api.ResourceList{
|
Max: getResourceRequirements("100m", "2Gi").Limits,
|
||||||
api.ResourceCPU: resource.MustParse("100m"),
|
Min: getResourceRequirements("25m", "1Mi").Limits,
|
||||||
api.ResourceMemory: resource.MustParse("2Gi"),
|
|
||||||
},
|
|
||||||
Min: api.ResourceList{
|
|
||||||
api.ResourceCPU: resource.MustParse("25m"),
|
|
||||||
api.ResourceMemory: resource.MustParse("1Mi"),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -63,13 +64,11 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "foo:V1",
|
Image: "foo:V1",
|
||||||
CPU: resource.MustParse("100m"),
|
Resources: getResourceRequirements("100m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("100m"),
|
Resources: getResourceRequirements("100m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -80,8 +79,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("100m"),
|
Resources: getResourceRequirements("100m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -95,8 +93,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("25m"),
|
Resources: getResourceRequirements("25m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -107,8 +104,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("110m"),
|
Resources: getResourceRequirements("110m", "1Gi"),
|
||||||
Memory: resource.MustParse("1Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -119,8 +115,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("30m"),
|
Resources: getResourceRequirements("30m", "0"),
|
||||||
Memory: resource.MustParse("0"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -131,8 +126,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("30m"),
|
Resources: getResourceRequirements("30m", "3Gi"),
|
||||||
Memory: resource.MustParse("3Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -143,8 +137,7 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("40m"),
|
Resources: getResourceRequirements("40m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -155,23 +148,19 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "1Mi"),
|
||||||
Memory: resource.MustParse("1Mi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V2",
|
Image: "boo:V2",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "1Mi"),
|
||||||
Memory: resource.MustParse("1Mi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V3",
|
Image: "boo:V3",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "1Mi"),
|
||||||
Memory: resource.MustParse("1Mi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V4",
|
Image: "boo:V4",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "1Mi"),
|
||||||
Memory: resource.MustParse("1Mi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -182,18 +171,15 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V2",
|
Image: "boo:V2",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V3",
|
Image: "boo:V3",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "2Gi"),
|
||||||
Memory: resource.MustParse("2Gi"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -204,18 +190,15 @@ func TestPodLimitFunc(t *testing.T) {
|
|||||||
Containers: []api.Container{
|
Containers: []api.Container{
|
||||||
{
|
{
|
||||||
Image: "boo:V1",
|
Image: "boo:V1",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "0"),
|
||||||
Memory: resource.MustParse("0"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V2",
|
Image: "boo:V2",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "0"),
|
||||||
Memory: resource.MustParse("0"),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Image: "boo:V3",
|
Image: "boo:V3",
|
||||||
CPU: resource.MustParse("60m"),
|
Resources: getResourceRequirements("60m", "0"),
|
||||||
Memory: resource.MustParse("0"),
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -55,11 +55,12 @@ func (resourceDefaults) Admit(a admission.Attributes) (err error) {
|
|||||||
obj := a.GetObject()
|
obj := a.GetObject()
|
||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
for index := range pod.Spec.Containers {
|
for index := range pod.Spec.Containers {
|
||||||
if pod.Spec.Containers[index].Memory.Value() == 0 {
|
pod.Spec.Containers[index].Resources.Limits = api.ResourceList{}
|
||||||
pod.Spec.Containers[index].Memory = resource.MustParse(defaultMemory)
|
if pod.Spec.Containers[index].Resources.Limits.Memory().Value() == 0 {
|
||||||
|
pod.Spec.Containers[index].Resources.Limits[api.ResourceMemory] = resource.MustParse(defaultMemory)
|
||||||
}
|
}
|
||||||
if pod.Spec.Containers[index].CPU.Value() == 0 {
|
if pod.Spec.Containers[index].Resources.Limits.Cpu().Value() == 0 {
|
||||||
pod.Spec.Containers[index].CPU = resource.MustParse(defaultCPU)
|
pod.Spec.Containers[index].Resources.Limits[api.ResourceCPU] = resource.MustParse(defaultCPU)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -41,11 +41,13 @@ func TestAdmission(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for i := range pod.Spec.Containers {
|
for i := range pod.Spec.Containers {
|
||||||
if pod.Spec.Containers[i].Memory.String() != "512Mi" {
|
memory := pod.Spec.Containers[i].Resources.Limits.Memory().String()
|
||||||
t.Errorf("Unexpected memory value %s", pod.Spec.Containers[i].Memory.String())
|
cpu := pod.Spec.Containers[i].Resources.Limits.Cpu().String()
|
||||||
|
if memory != "512Mi" {
|
||||||
|
t.Errorf("Unexpected memory value %s", memory)
|
||||||
}
|
}
|
||||||
if pod.Spec.Containers[i].CPU.String() != "1" {
|
if cpu != "1" {
|
||||||
t.Errorf("Unexpected cpu value %s", pod.Spec.Containers[i].CPU.String())
|
t.Errorf("Unexpected cpu value %s", cpu)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,19 @@ import (
|
|||||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getResourceRequirements(cpu, memory string) api.ResourceRequirementSpec {
|
||||||
|
res := api.ResourceRequirementSpec{}
|
||||||
|
res.Limits = api.ResourceList{}
|
||||||
|
if cpu != "" {
|
||||||
|
res.Limits[api.ResourceCPU] = resource.MustParse(cpu)
|
||||||
|
}
|
||||||
|
if memory != "" {
|
||||||
|
res.Limits[api.ResourceMemory] = resource.MustParse(memory)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
func TestAdmissionIgnoresDelete(t *testing.T) {
|
func TestAdmissionIgnoresDelete(t *testing.T) {
|
||||||
namespace := "default"
|
namespace := "default"
|
||||||
handler := NewResourceQuota(&client.Fake{})
|
handler := NewResourceQuota(&client.Fake{})
|
||||||
@ -43,7 +56,7 @@ func TestIncrementUsagePods(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -78,7 +91,7 @@ func TestIncrementUsageMemory(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -96,7 +109,7 @@ func TestIncrementUsageMemory(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
}}
|
}}
|
||||||
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -121,7 +134,7 @@ func TestExceedUsageMemory(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -139,7 +152,7 @@ func TestExceedUsageMemory(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("3Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "3Gi")}},
|
||||||
}}
|
}}
|
||||||
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -156,7 +169,7 @@ func TestIncrementUsageCPU(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -174,7 +187,7 @@ func TestIncrementUsageCPU(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
}}
|
}}
|
||||||
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
dirty, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -199,7 +212,7 @@ func TestExceedUsageCPU(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@ -217,7 +230,7 @@ func TestExceedUsageCPU(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("500m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("500m", "1Gi")}},
|
||||||
}}
|
}}
|
||||||
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
_, err := IncrementUsage(admission.NewAttributesRecord(newPod, namespace, "pods", "CREATE"), status, client)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -234,7 +247,7 @@ func TestExceedUsagePods(t *testing.T) {
|
|||||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: namespace},
|
||||||
Spec: api.PodSpec{
|
Spec: api.PodSpec{
|
||||||
Volumes: []api.Volume{{Name: "vol"}},
|
Volumes: []api.Volume{{Name: "vol"}},
|
||||||
Containers: []api.Container{{Name: "ctr", Image: "image", Memory: resource.MustParse("1Gi"), CPU: resource.MustParse("100m")}},
|
Containers: []api.Container{{Name: "ctr", Image: "image", Resources: getResourceRequirements("100m", "1Gi")}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loading…
Reference in New Issue
Block a user