mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	Merge pull request #39678 from resouer/extract-resource
Automatic merge from submit-queue (batch tested with PRs 41775, 39678, 42629, 42524, 43028) Extract resources functions belongs to api/util Address: extract kubelet resources functions belongs to `pkg/api/v1/resource_helpers.go`
This commit is contained in:
		@@ -178,6 +178,7 @@ pkg/controller/volume/attachdetach/reconciler
 | 
			
		||||
pkg/conversion
 | 
			
		||||
pkg/conversion/queryparams
 | 
			
		||||
pkg/credentialprovider/aws
 | 
			
		||||
pkg/fieldpath
 | 
			
		||||
pkg/fields
 | 
			
		||||
pkg/hyperkube
 | 
			
		||||
pkg/kubelet/api
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,9 @@ limitations under the License.
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
@@ -227,3 +230,41 @@ func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, li
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceCPUToString converts cpu value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
 | 
			
		||||
	return strconv.FormatInt(c, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceMemoryToString converts memory value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
 | 
			
		||||
	return strconv.FormatInt(m, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -86,6 +86,7 @@ go_test(
 | 
			
		||||
    library = ":go_default_library",
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//vendor:github.com/stretchr/testify/assert",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/api/equality",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/api/resource",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,14 @@ limitations under the License.
 | 
			
		||||
package v1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Returns string version of ResourceName.
 | 
			
		||||
@@ -255,3 +259,100 @@ func GetResourceRequest(pod *Pod, resource ResourceName) int64 {
 | 
			
		||||
	}
 | 
			
		||||
	return totalResources
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractResourceValueByContainerName extracts the value of a resource
 | 
			
		||||
// by providing container name
 | 
			
		||||
func ExtractResourceValueByContainerName(fs *ResourceFieldSelector, pod *Pod, containerName string) (string, error) {
 | 
			
		||||
	container, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractResourceValueByContainerNameAndNodeAllocatable extracts the value of a resource
 | 
			
		||||
// by providing container name and node allocatable
 | 
			
		||||
func ExtractResourceValueByContainerNameAndNodeAllocatable(fs *ResourceFieldSelector, pod *Pod, containerName string, nodeAllocatable ResourceList) (string, error) {
 | 
			
		||||
	realContainer, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerCopy, err := api.Scheme.DeepCopy(realContainer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("failed to perform a deep copy of container object: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	container, ok := containerCopy.(*Container)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", fmt.Errorf("unexpected type returned from deep copy of container object")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MergeContainerResourceLimits(container, nodeAllocatable)
 | 
			
		||||
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceCPUToString converts cpu value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
 | 
			
		||||
	return strconv.FormatInt(c, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceMemoryToString converts memory value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
 | 
			
		||||
	return strconv.FormatInt(m, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findContainerInPod finds a container by its name in the provided pod
 | 
			
		||||
func findContainerInPod(pod *Pod, containerName string) (*Container, error) {
 | 
			
		||||
	for _, container := range pod.Spec.Containers {
 | 
			
		||||
		if container.Name == containerName {
 | 
			
		||||
			return &container, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("container %s not found", containerName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MergeContainerResourceLimits checks if a limit is applied for
 | 
			
		||||
// the container, and if not, it sets the limit to the passed resource list.
 | 
			
		||||
func MergeContainerResourceLimits(container *Container,
 | 
			
		||||
	allocatable ResourceList) {
 | 
			
		||||
	if container.Resources.Limits == nil {
 | 
			
		||||
		container.Resources.Limits = make(ResourceList)
 | 
			
		||||
	}
 | 
			
		||||
	for _, resource := range []ResourceName{ResourceCPU, ResourceMemory} {
 | 
			
		||||
		if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
 | 
			
		||||
			if cap, exists := allocatable[resource]; exists {
 | 
			
		||||
				container.Resources.Limits[resource] = *cap.Copy()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,8 @@ import (
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
)
 | 
			
		||||
@@ -118,3 +120,119 @@ func TestIsPodAvailable(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExtractResourceValue(t *testing.T) {
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		fs            *ResourceFieldSelector
 | 
			
		||||
		pod           *Pod
 | 
			
		||||
		cName         string
 | 
			
		||||
		expectedValue string
 | 
			
		||||
		expectedError error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "limits.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "9", "", ""),
 | 
			
		||||
			expectedValue: "9",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "", ""),
 | 
			
		||||
			expectedValue: "0",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "8", "", "", ""),
 | 
			
		||||
			expectedValue: "8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "100m", "", "", ""),
 | 
			
		||||
			expectedValue: "1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
				Divisor:  resource.MustParse("100m"),
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "1200m", "", "", ""),
 | 
			
		||||
			expectedValue: "12",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.memory",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "100Mi", ""),
 | 
			
		||||
			expectedValue: "104857600",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.memory",
 | 
			
		||||
				Divisor:  resource.MustParse("1Mi"),
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "100Mi", "1Gi"),
 | 
			
		||||
			expectedValue: "100",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &ResourceFieldSelector{
 | 
			
		||||
				Resource: "limits.memory",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "10Mi", "100Mi"),
 | 
			
		||||
			expectedValue: "104857600",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	as := assert.New(t)
 | 
			
		||||
	for idx, tc := range cases {
 | 
			
		||||
		actual, err := ExtractResourceValueByContainerName(tc.fs, tc.pod, tc.cName)
 | 
			
		||||
		if tc.expectedError != nil {
 | 
			
		||||
			as.Equal(tc.expectedError, err, "expected test case [%d] to fail with error %v; got %v", idx, tc.expectedError, err)
 | 
			
		||||
		} else {
 | 
			
		||||
			as.Nil(err, "expected test case [%d] to not return an error; got %v", idx, err)
 | 
			
		||||
			as.Equal(tc.expectedValue, actual, "expected test case [%d] to return %q; got %q instead", idx, tc.expectedValue, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPod(cname, cpuRequest, cpuLimit, memoryRequest, memoryLimit string) *Pod {
 | 
			
		||||
	resources := ResourceRequirements{
 | 
			
		||||
		Limits:   make(ResourceList),
 | 
			
		||||
		Requests: make(ResourceList),
 | 
			
		||||
	}
 | 
			
		||||
	if cpuLimit != "" {
 | 
			
		||||
		resources.Limits[ResourceCPU] = resource.MustParse(cpuLimit)
 | 
			
		||||
	}
 | 
			
		||||
	if memoryLimit != "" {
 | 
			
		||||
		resources.Limits[ResourceMemory] = resource.MustParse(memoryLimit)
 | 
			
		||||
	}
 | 
			
		||||
	if cpuRequest != "" {
 | 
			
		||||
		resources.Requests[ResourceCPU] = resource.MustParse(cpuRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if memoryRequest != "" {
 | 
			
		||||
		resources.Requests[ResourceMemory] = resource.MustParse(memoryRequest)
 | 
			
		||||
	}
 | 
			
		||||
	return &Pod{
 | 
			
		||||
		Spec: PodSpec{
 | 
			
		||||
			Containers: []Container{
 | 
			
		||||
				{
 | 
			
		||||
					Name:      cname,
 | 
			
		||||
					Resources: resources,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,12 +15,7 @@ go_library(
 | 
			
		||||
        "fieldpath.go",
 | 
			
		||||
    ],
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/api:go_default_library",
 | 
			
		||||
        "//pkg/api/v1:go_default_library",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/api/meta",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/api/resource",
 | 
			
		||||
    ],
 | 
			
		||||
    deps = ["//vendor:k8s.io/apimachinery/pkg/api/meta"],
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
go_test(
 | 
			
		||||
@@ -30,8 +25,6 @@ go_test(
 | 
			
		||||
    tags = ["automanaged"],
 | 
			
		||||
    deps = [
 | 
			
		||||
        "//pkg/api/v1:go_default_library",
 | 
			
		||||
        "//vendor:github.com/stretchr/testify/assert",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/api/resource",
 | 
			
		||||
        "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1",
 | 
			
		||||
    ],
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,14 +18,9 @@ package fieldpath
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/v1"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FormatMap formats map[string]string to a string.
 | 
			
		||||
@@ -60,126 +55,3 @@ func ExtractFieldPathAsString(obj interface{}, fieldPath string) (string, error)
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("unsupported fieldPath: %v", fieldPath)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: move the functions below to pkg/api/util/resources
 | 
			
		||||
// ExtractResourceValueByContainerName extracts the value of a resource
 | 
			
		||||
// by providing container name
 | 
			
		||||
func ExtractResourceValueByContainerName(fs *v1.ResourceFieldSelector, pod *v1.Pod, containerName string) (string, error) {
 | 
			
		||||
	container, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractResourceValueByContainerNameAndNodeAllocatable extracts the value of a resource
 | 
			
		||||
// by providing container name and node allocatable
 | 
			
		||||
func ExtractResourceValueByContainerNameAndNodeAllocatable(fs *v1.ResourceFieldSelector, pod *v1.Pod, containerName string, nodeAllocatable v1.ResourceList) (string, error) {
 | 
			
		||||
	realContainer, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerCopy, err := api.Scheme.DeepCopy(realContainer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("failed to perform a deep copy of container object: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	container, ok := containerCopy.(*v1.Container)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", fmt.Errorf("unexpected type returned from deep copy of container object")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MergeContainerResourceLimits(container, nodeAllocatable)
 | 
			
		||||
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func ExtractContainerResourceValue(fs *v1.ResourceFieldSelector, container *v1.Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO: remove this duplicate
 | 
			
		||||
// InternalExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func InternalExtractContainerResourceValue(fs *api.ResourceFieldSelector, container *api.Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findContainerInPod finds a container by its name in the provided pod
 | 
			
		||||
func findContainerInPod(pod *v1.Pod, containerName string) (*v1.Container, error) {
 | 
			
		||||
	for _, container := range pod.Spec.Containers {
 | 
			
		||||
		if container.Name == containerName {
 | 
			
		||||
			return &container, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("container %s not found", containerName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceCPUToString converts cpu value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
 | 
			
		||||
	return strconv.FormatInt(c, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceMemoryToString converts memory value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
 | 
			
		||||
	return strconv.FormatInt(m, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MergeContainerResourceLimits checks if a limit is applied for
 | 
			
		||||
// the container, and if not, it sets the limit to the passed resource list.
 | 
			
		||||
func MergeContainerResourceLimits(container *v1.Container,
 | 
			
		||||
	allocatable v1.ResourceList) {
 | 
			
		||||
	if container.Resources.Limits == nil {
 | 
			
		||||
		container.Resources.Limits = make(v1.ResourceList)
 | 
			
		||||
	}
 | 
			
		||||
	for _, resource := range []v1.ResourceName{v1.ResourceCPU, v1.ResourceMemory} {
 | 
			
		||||
		if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
 | 
			
		||||
			if cap, exists := allocatable[resource]; exists {
 | 
			
		||||
				container.Resources.Limits[resource] = *cap.Copy()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -20,9 +20,6 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"github.com/stretchr/testify/assert"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/v1"
 | 
			
		||||
)
 | 
			
		||||
@@ -119,119 +116,3 @@ func TestExtractFieldPathAsString(t *testing.T) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getPod(cname, cpuRequest, cpuLimit, memoryRequest, memoryLimit string) *v1.Pod {
 | 
			
		||||
	resources := v1.ResourceRequirements{
 | 
			
		||||
		Limits:   make(v1.ResourceList),
 | 
			
		||||
		Requests: make(v1.ResourceList),
 | 
			
		||||
	}
 | 
			
		||||
	if cpuLimit != "" {
 | 
			
		||||
		resources.Limits[v1.ResourceCPU] = resource.MustParse(cpuLimit)
 | 
			
		||||
	}
 | 
			
		||||
	if memoryLimit != "" {
 | 
			
		||||
		resources.Limits[v1.ResourceMemory] = resource.MustParse(memoryLimit)
 | 
			
		||||
	}
 | 
			
		||||
	if cpuRequest != "" {
 | 
			
		||||
		resources.Requests[v1.ResourceCPU] = resource.MustParse(cpuRequest)
 | 
			
		||||
	}
 | 
			
		||||
	if memoryRequest != "" {
 | 
			
		||||
		resources.Requests[v1.ResourceMemory] = resource.MustParse(memoryRequest)
 | 
			
		||||
	}
 | 
			
		||||
	return &v1.Pod{
 | 
			
		||||
		Spec: v1.PodSpec{
 | 
			
		||||
			Containers: []v1.Container{
 | 
			
		||||
				{
 | 
			
		||||
					Name:      cname,
 | 
			
		||||
					Resources: resources,
 | 
			
		||||
				},
 | 
			
		||||
			},
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestExtractResourceValue(t *testing.T) {
 | 
			
		||||
	cases := []struct {
 | 
			
		||||
		fs            *v1.ResourceFieldSelector
 | 
			
		||||
		pod           *v1.Pod
 | 
			
		||||
		cName         string
 | 
			
		||||
		expectedValue string
 | 
			
		||||
		expectedError error
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "limits.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "9", "", ""),
 | 
			
		||||
			expectedValue: "9",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "", ""),
 | 
			
		||||
			expectedValue: "0",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "8", "", "", ""),
 | 
			
		||||
			expectedValue: "8",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "100m", "", "", ""),
 | 
			
		||||
			expectedValue: "1",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.cpu",
 | 
			
		||||
				Divisor:  resource.MustParse("100m"),
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "1200m", "", "", ""),
 | 
			
		||||
			expectedValue: "12",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.memory",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "100Mi", ""),
 | 
			
		||||
			expectedValue: "104857600",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "requests.memory",
 | 
			
		||||
				Divisor:  resource.MustParse("1Mi"),
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "100Mi", "1Gi"),
 | 
			
		||||
			expectedValue: "100",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			fs: &v1.ResourceFieldSelector{
 | 
			
		||||
				Resource: "limits.memory",
 | 
			
		||||
			},
 | 
			
		||||
			cName:         "foo",
 | 
			
		||||
			pod:           getPod("foo", "", "", "10Mi", "100Mi"),
 | 
			
		||||
			expectedValue: "104857600",
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
	as := assert.New(t)
 | 
			
		||||
	for idx, tc := range cases {
 | 
			
		||||
		actual, err := ExtractResourceValueByContainerName(tc.fs, tc.pod, tc.cName)
 | 
			
		||||
		if tc.expectedError != nil {
 | 
			
		||||
			as.Equal(tc.expectedError, err, "expected test case [%d] to fail with error %v; got %v", idx, tc.expectedError, err)
 | 
			
		||||
		} else {
 | 
			
		||||
			as.Nil(err, "expected test case [%d] to not return an error; got %v", idx, err)
 | 
			
		||||
			as.Equal(tc.expectedValue, actual, "expected test case [%d] to return %q; got %q instead", idx, tc.expectedValue, actual)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -654,9 +654,9 @@ func (kl *Kubelet) podFieldSelectorRuntimeValue(fs *v1.ObjectFieldSelector, pod
 | 
			
		||||
func containerResourceRuntimeValue(fs *v1.ResourceFieldSelector, pod *v1.Pod, container *v1.Container) (string, error) {
 | 
			
		||||
	containerName := fs.ContainerName
 | 
			
		||||
	if len(containerName) == 0 {
 | 
			
		||||
		return fieldpath.ExtractContainerResourceValue(fs, container)
 | 
			
		||||
		return v1.ExtractContainerResourceValue(fs, container)
 | 
			
		||||
	} else {
 | 
			
		||||
		return fieldpath.ExtractResourceValueByContainerName(fs, pod, containerName)
 | 
			
		||||
		return v1.ExtractResourceValueByContainerName(fs, pod, containerName)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ import (
 | 
			
		||||
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/api/v1"
 | 
			
		||||
	"k8s.io/kubernetes/pkg/fieldpath"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// defaultPodLimitsForDownwardApi copies the input pod, and optional container,
 | 
			
		||||
@@ -53,7 +52,7 @@ func (kl *Kubelet) defaultPodLimitsForDownwardApi(pod *v1.Pod, container *v1.Con
 | 
			
		||||
		return nil, nil, fmt.Errorf("unexpected type returned from deep copy of pod object")
 | 
			
		||||
	}
 | 
			
		||||
	for idx := range outputPod.Spec.Containers {
 | 
			
		||||
		fieldpath.MergeContainerResourceLimits(&outputPod.Spec.Containers[idx], allocatable)
 | 
			
		||||
		v1.MergeContainerResourceLimits(&outputPod.Spec.Containers[idx], allocatable)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var outputContainer *v1.Container
 | 
			
		||||
@@ -66,7 +65,7 @@ func (kl *Kubelet) defaultPodLimitsForDownwardApi(pod *v1.Pod, container *v1.Con
 | 
			
		||||
		if !ok {
 | 
			
		||||
			return nil, nil, fmt.Errorf("unexpected type returned from deep copy of container object")
 | 
			
		||||
		}
 | 
			
		||||
		fieldpath.MergeContainerResourceLimits(outputContainer, allocatable)
 | 
			
		||||
		v1.MergeContainerResourceLimits(outputContainer, allocatable)
 | 
			
		||||
	}
 | 
			
		||||
	return outputPod, outputContainer, nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1100,7 +1100,7 @@ func describeContainerEnvVars(container api.Container, resolverFn EnvVarResolver
 | 
			
		||||
			}
 | 
			
		||||
			w.Write(LEVEL_3, "%s:\t%s (%s:%s)\n", e.Name, valueFrom, e.ValueFrom.FieldRef.APIVersion, e.ValueFrom.FieldRef.FieldPath)
 | 
			
		||||
		case e.ValueFrom.ResourceFieldRef != nil:
 | 
			
		||||
			valueFrom, err := fieldpath.InternalExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container)
 | 
			
		||||
			valueFrom, err := api.ExtractContainerResourceValue(e.ValueFrom.ResourceFieldRef, &container)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				valueFrom = ""
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -244,7 +244,7 @@ func CollectData(items []v1.DownwardAPIVolumeFile, pod *v1.Pod, host volume.Volu
 | 
			
		||||
			nodeAllocatable, err := host.GetNodeAllocatable()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				errlist = append(errlist, err)
 | 
			
		||||
			} else if values, err := fieldpath.ExtractResourceValueByContainerNameAndNodeAllocatable(fileInfo.ResourceFieldRef, pod, containerName, nodeAllocatable); err != nil {
 | 
			
		||||
			} else if values, err := v1.ExtractResourceValueByContainerNameAndNodeAllocatable(fileInfo.ResourceFieldRef, pod, containerName, nodeAllocatable); err != nil {
 | 
			
		||||
				glog.Errorf("Unable to extract field %s: %s", fileInfo.ResourceFieldRef.Resource, err.Error())
 | 
			
		||||
				errlist = append(errlist, err)
 | 
			
		||||
			} else {
 | 
			
		||||
 
 | 
			
		||||
@@ -17,6 +17,9 @@ limitations under the License.
 | 
			
		||||
package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
@@ -227,3 +230,41 @@ func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, li
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceCPUToString converts cpu value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
 | 
			
		||||
	return strconv.FormatInt(c, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceMemoryToString converts memory value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
 | 
			
		||||
	return strconv.FormatInt(m, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -17,10 +17,14 @@ limitations under the License.
 | 
			
		||||
package v1
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
			
		||||
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 | 
			
		||||
	"k8s.io/client-go/pkg/api"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Returns string version of ResourceName.
 | 
			
		||||
@@ -255,3 +259,100 @@ func GetResourceRequest(pod *Pod, resource ResourceName) int64 {
 | 
			
		||||
	}
 | 
			
		||||
	return totalResources
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractResourceValueByContainerName extracts the value of a resource
 | 
			
		||||
// by providing container name
 | 
			
		||||
func ExtractResourceValueByContainerName(fs *ResourceFieldSelector, pod *Pod, containerName string) (string, error) {
 | 
			
		||||
	container, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractResourceValueByContainerNameAndNodeAllocatable extracts the value of a resource
 | 
			
		||||
// by providing container name and node allocatable
 | 
			
		||||
func ExtractResourceValueByContainerNameAndNodeAllocatable(fs *ResourceFieldSelector, pod *Pod, containerName string, nodeAllocatable ResourceList) (string, error) {
 | 
			
		||||
	realContainer, err := findContainerInPod(pod, containerName)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containerCopy, err := api.Scheme.DeepCopy(realContainer)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return "", fmt.Errorf("failed to perform a deep copy of container object: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	container, ok := containerCopy.(*Container)
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return "", fmt.Errorf("unexpected type returned from deep copy of container object")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	MergeContainerResourceLimits(container, nodeAllocatable)
 | 
			
		||||
 | 
			
		||||
	return ExtractContainerResourceValue(fs, container)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ExtractContainerResourceValue extracts the value of a resource
 | 
			
		||||
// in an already known container
 | 
			
		||||
func ExtractContainerResourceValue(fs *ResourceFieldSelector, container *Container) (string, error) {
 | 
			
		||||
	divisor := resource.Quantity{}
 | 
			
		||||
	if divisor.Cmp(fs.Divisor) == 0 {
 | 
			
		||||
		divisor = resource.MustParse("1")
 | 
			
		||||
	} else {
 | 
			
		||||
		divisor = fs.Divisor
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	switch fs.Resource {
 | 
			
		||||
	case "limits.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Limits.Cpu(), divisor)
 | 
			
		||||
	case "limits.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Limits.Memory(), divisor)
 | 
			
		||||
	case "requests.cpu":
 | 
			
		||||
		return convertResourceCPUToString(container.Resources.Requests.Cpu(), divisor)
 | 
			
		||||
	case "requests.memory":
 | 
			
		||||
		return convertResourceMemoryToString(container.Resources.Requests.Memory(), divisor)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return "", fmt.Errorf("Unsupported container resource : %v", fs.Resource)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceCPUToString converts cpu value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceCPUToString(cpu *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	c := int64(math.Ceil(float64(cpu.MilliValue()) / float64(divisor.MilliValue())))
 | 
			
		||||
	return strconv.FormatInt(c, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// convertResourceMemoryToString converts memory value to the format of divisor and returns
 | 
			
		||||
// ceiling of the value.
 | 
			
		||||
func convertResourceMemoryToString(memory *resource.Quantity, divisor resource.Quantity) (string, error) {
 | 
			
		||||
	m := int64(math.Ceil(float64(memory.Value()) / float64(divisor.Value())))
 | 
			
		||||
	return strconv.FormatInt(m, 10), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findContainerInPod finds a container by its name in the provided pod
 | 
			
		||||
func findContainerInPod(pod *Pod, containerName string) (*Container, error) {
 | 
			
		||||
	for _, container := range pod.Spec.Containers {
 | 
			
		||||
		if container.Name == containerName {
 | 
			
		||||
			return &container, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil, fmt.Errorf("container %s not found", containerName)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MergeContainerResourceLimits checks if a limit is applied for
 | 
			
		||||
// the container, and if not, it sets the limit to the passed resource list.
 | 
			
		||||
func MergeContainerResourceLimits(container *Container,
 | 
			
		||||
	allocatable ResourceList) {
 | 
			
		||||
	if container.Resources.Limits == nil {
 | 
			
		||||
		container.Resources.Limits = make(ResourceList)
 | 
			
		||||
	}
 | 
			
		||||
	for _, resource := range []ResourceName{ResourceCPU, ResourceMemory} {
 | 
			
		||||
		if quantity, exists := container.Resources.Limits[resource]; !exists || quantity.IsZero() {
 | 
			
		||||
			if cap, exists := allocatable[resource]; exists {
 | 
			
		||||
				container.Resources.Limits[resource] = *cap.Copy()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user