mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-11-03 19:58:17 +00:00 
			
		
		
		
	memory manager: validate reserved-memory against Node Allocatable
Reserved memory of all kinds (and over all NUMA nodes) must be equal to the values determined by Node Allocatable feature. Signed-off-by: Cezary Zukowski <c.zukowski@samsung.com>
This commit is contained in:
		
				
					committed by
					
						
						Artyom Lukianov
					
				
			
			
				
	
			
			
			
						parent
						
							711e85af24
						
					
				
				
					commit
					4a64102918
				
			@@ -703,6 +703,7 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
 | 
				
			|||||||
	ExpandInUsePersistentVolumes:                   {Default: true, PreRelease: featuregate.Beta},
 | 
						ExpandInUsePersistentVolumes:                   {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
	ExpandCSIVolumes:                               {Default: true, PreRelease: featuregate.Beta},
 | 
						ExpandCSIVolumes:                               {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
	CPUManager:                                     {Default: true, PreRelease: featuregate.Beta},
 | 
						CPUManager:                                     {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
 | 
						MemoryManager:                                  {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
	CPUCFSQuotaPeriod:                              {Default: false, PreRelease: featuregate.Alpha},
 | 
						CPUCFSQuotaPeriod:                              {Default: false, PreRelease: featuregate.Alpha},
 | 
				
			||||||
	TopologyManager:                                {Default: true, PreRelease: featuregate.Beta},
 | 
						TopologyManager:                                {Default: true, PreRelease: featuregate.Beta},
 | 
				
			||||||
	ServiceNodeExclusion:                           {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22
 | 
						ServiceNodeExclusion:                           {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.22
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,6 +28,7 @@ import (
 | 
				
			|||||||
	"k8s.io/apimachinery/pkg/api/resource"
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
	runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
 | 
						runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"
 | 
				
			||||||
	"k8s.io/klog/v2"
 | 
						"k8s.io/klog/v2"
 | 
				
			||||||
 | 
						corev1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/containermap"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/memorymanager/state"
 | 
				
			||||||
	"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
 | 
						"k8s.io/kubernetes/pkg/kubelet/cm/topologymanager"
 | 
				
			||||||
@@ -101,8 +102,6 @@ type manager struct {
 | 
				
			|||||||
	// for all containers a pod
 | 
						// for all containers a pod
 | 
				
			||||||
	containerMap containermap.ContainerMap
 | 
						containerMap containermap.ContainerMap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	nodeAllocatableReservation v1.ResourceList
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// sourcesReady provides the readiness of kubelet configuration sources such as apiserver update readiness.
 | 
						// sourcesReady provides the readiness of kubelet configuration sources such as apiserver update readiness.
 | 
				
			||||||
	// We use it to determine when we can purge inactive pods from checkpointed state.
 | 
						// We use it to determine when we can purge inactive pods from checkpointed state.
 | 
				
			||||||
	sourcesReady config.SourcesReady
 | 
						sourcesReady config.SourcesReady
 | 
				
			||||||
@@ -123,12 +122,12 @@ func NewManager(policyName string, machineInfo *cadvisorapi.MachineInfo, nodeAll
 | 
				
			|||||||
		policy = NewPolicyNone()
 | 
							policy = NewPolicyNone()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	case policyTypeStatic:
 | 
						case policyTypeStatic:
 | 
				
			||||||
		reserved, err := getReservedMemory(machineInfo, nodeAllocatableReservation, reservedMemory)
 | 
							systemReserved, err := getSystemReservedMemory(machineInfo, nodeAllocatableReservation, reservedMemory)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		policy, err = NewPolicyStatic(machineInfo, reserved, affinity)
 | 
							policy, err = NewPolicyStatic(machineInfo, systemReserved, affinity)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -138,9 +137,8 @@ func NewManager(policyName string, machineInfo *cadvisorapi.MachineInfo, nodeAll
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	manager := &manager{
 | 
						manager := &manager{
 | 
				
			||||||
		policy:                     policy,
 | 
							policy:             policy,
 | 
				
			||||||
		nodeAllocatableReservation: nodeAllocatableReservation,
 | 
							stateFileDirectory: stateFileDirectory,
 | 
				
			||||||
		stateFileDirectory:         stateFileDirectory,
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	manager.sourcesReady = &sourcesReadyStub{}
 | 
						manager.sourcesReady = &sourcesReadyStub{}
 | 
				
			||||||
	return manager, nil
 | 
						return manager, nil
 | 
				
			||||||
@@ -309,35 +307,86 @@ func (m *manager) policyRemoveContainerByRef(podUID string, containerName string
 | 
				
			|||||||
	return err
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getTotalMemoryTypeReserved(preReservedMemory map[int]map[v1.ResourceName]resource.Quantity) map[v1.ResourceName]resource.Quantity {
 | 
				
			||||||
 | 
						totalMemoryType := map[v1.ResourceName]resource.Quantity{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, node := range preReservedMemory {
 | 
				
			||||||
 | 
							for memType, memVal := range node {
 | 
				
			||||||
 | 
								if totalMem, exists := totalMemoryType[memType]; exists {
 | 
				
			||||||
 | 
									memVal.Add(totalMem)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								totalMemoryType[memType] = memVal
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return totalMemoryType
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateReservedMemory(nodeAllocatableReservation v1.ResourceList, reservedMemory map[int]map[v1.ResourceName]resource.Quantity) error {
 | 
					func validateReservedMemory(nodeAllocatableReservation v1.ResourceList, reservedMemory map[int]map[v1.ResourceName]resource.Quantity) error {
 | 
				
			||||||
	// TODO: this will check equality of total reserved memory by node allocatable feature and total pre-reserved memory
 | 
						totalMemoryType := getTotalMemoryTypeReserved(reservedMemory)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						commonMemoryTypeSet := make(map[v1.ResourceName]bool)
 | 
				
			||||||
 | 
						for resourceType := range totalMemoryType {
 | 
				
			||||||
 | 
							if !(corev1helper.IsHugePageResourceName(resourceType) || resourceType == v1.ResourceMemory) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							commonMemoryTypeSet[resourceType] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for resourceType := range nodeAllocatableReservation {
 | 
				
			||||||
 | 
							if !(corev1helper.IsHugePageResourceName(resourceType) || resourceType == v1.ResourceMemory) {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							commonMemoryTypeSet[resourceType] = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for resourceType := range commonMemoryTypeSet {
 | 
				
			||||||
 | 
							nodeAllocatableMemory := resource.NewQuantity(0, resource.DecimalSI)
 | 
				
			||||||
 | 
							if memValue, set := nodeAllocatableReservation[resourceType]; set {
 | 
				
			||||||
 | 
								nodeAllocatableMemory.Add(memValue)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							reservedMemory := resource.NewQuantity(0, resource.DecimalSI)
 | 
				
			||||||
 | 
							if memValue, set := totalMemoryType[resourceType]; set {
 | 
				
			||||||
 | 
								reservedMemory.Add(memValue)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if !(*nodeAllocatableMemory).Equal(*reservedMemory) {
 | 
				
			||||||
 | 
								return fmt.Errorf("the total amount of memory of type \"%s\" is not equal to the value determined by Node Allocatable feature", resourceType)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getReservedMemory(machineInfo *cadvisorapi.MachineInfo, nodeAllocatableReservation v1.ResourceList, reservedMemory map[int]map[v1.ResourceName]resource.Quantity) (systemReservedMemory, error) {
 | 
					func convertReserved(machineInfo *cadvisorapi.MachineInfo, reservedMemory map[int]map[v1.ResourceName]resource.Quantity) (systemReservedMemory, error) {
 | 
				
			||||||
	// TODO: we should add new kubelet parameter, and to get reserved memory per NUMA node from it
 | 
						preReservedMemoryConverted := make(map[int]map[v1.ResourceName]uint64)
 | 
				
			||||||
	// currently we use kube-reserved + system-reserved + eviction reserve for each NUMA node, that creates memory over-consumption
 | 
						for _, node := range machineInfo.Topology {
 | 
				
			||||||
	// and no reservation for huge pages
 | 
							preReservedMemoryConverted[node.Id] = make(map[v1.ResourceName]uint64)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := validateReservedMemory(nodeAllocatableReservation, reservedMemory); err != nil {
 | 
						for numaIndex := range reservedMemory {
 | 
				
			||||||
 | 
							for memoryType := range reservedMemory[numaIndex] {
 | 
				
			||||||
 | 
								tmp := reservedMemory[numaIndex][memoryType]
 | 
				
			||||||
 | 
								if val, success := tmp.AsInt64(); success {
 | 
				
			||||||
 | 
									preReservedMemoryConverted[numaIndex][memoryType] = uint64(val)
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return nil, fmt.Errorf("could not covert a variable of type Quantity to int64")
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return preReservedMemoryConverted, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getSystemReservedMemory(machineInfo *cadvisorapi.MachineInfo, nodeAllocatableReservation v1.ResourceList, preReservedMemory map[int]map[v1.ResourceName]resource.Quantity) (systemReservedMemory, error) {
 | 
				
			||||||
 | 
						if err := validateReservedMemory(nodeAllocatableReservation, preReservedMemory); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	reserved := systemReservedMemory{}
 | 
						reservedMemoryConverted, err := convertReserved(machineInfo, preReservedMemory)
 | 
				
			||||||
	for _, node := range machineInfo.Topology {
 | 
						if err != nil {
 | 
				
			||||||
		memory := nodeAllocatableReservation[v1.ResourceMemory]
 | 
							return nil, err
 | 
				
			||||||
		if memory.IsZero() {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		value, succeeded := memory.AsInt64()
 | 
					 | 
				
			||||||
		if !succeeded {
 | 
					 | 
				
			||||||
			return nil, fmt.Errorf("failed to represent reserved memory as int64")
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		reserved[node.Id] = map[v1.ResourceName]uint64{
 | 
					 | 
				
			||||||
			v1.ResourceMemory: uint64(value),
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return reserved, nil
 | 
					
 | 
				
			||||||
 | 
						return reservedMemoryConverted, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										181
									
								
								pkg/kubelet/cm/memorymanager/memory_manager_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								pkg/kubelet/cm/memorymanager/memory_manager_test.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					package memorymanager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/stretchr/testify/assert"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						info "github.com/google/cadvisor/info/v1"
 | 
				
			||||||
 | 
						v1 "k8s.io/api/core/v1"
 | 
				
			||||||
 | 
						"k8s.io/apimachinery/pkg/api/resource"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						hugepages2M = "hugepages-2Mi"
 | 
				
			||||||
 | 
						hugepages1G = "hugepages-1Gi"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type nodeResources map[v1.ResourceName]resource.Quantity
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// validateReservedMemory
 | 
				
			||||||
 | 
					func TestValidatePreReservedMemory(t *testing.T) {
 | 
				
			||||||
 | 
						const msgNotEqual = "the total amount of memory of type \"%s\" is not equal to the value determined by Node Allocatable feature"
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							description                string
 | 
				
			||||||
 | 
							nodeAllocatableReservation v1.ResourceList
 | 
				
			||||||
 | 
							preReservedMemory          map[int]map[v1.ResourceName]resource.Quantity
 | 
				
			||||||
 | 
							expectedError              string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Node Allocatable not set, pre-reserved not set",
 | 
				
			||||||
 | 
								v1.ResourceList{},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Node Allocatable set to zero, pre-reserved set to zero",
 | 
				
			||||||
 | 
								v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI)},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(0, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Node Allocatable not set (equal zero), pre-reserved set",
 | 
				
			||||||
 | 
								v1.ResourceList{},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fmt.Sprintf(msgNotEqual, v1.ResourceMemory),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Node Allocatable set, pre-reserved not set",
 | 
				
			||||||
 | 
								v1.ResourceList{hugepages2M: *resource.NewQuantity(5, resource.DecimalSI)},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{},
 | 
				
			||||||
 | 
								fmt.Sprintf(msgNotEqual, hugepages2M),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Pre-reserved not equal to Node Allocatable",
 | 
				
			||||||
 | 
								v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI)},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fmt.Sprintf(msgNotEqual, v1.ResourceMemory),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Pre-reserved total equal to Node Allocatable",
 | 
				
			||||||
 | 
								v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
 | 
				
			||||||
 | 
									hugepages2M: *resource.NewQuantity(77, resource.DecimalSI),
 | 
				
			||||||
 | 
									hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
									1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Pre-reserved total hugapages-2M not equal to Node Allocatable",
 | 
				
			||||||
 | 
								v1.ResourceList{v1.ResourceMemory: *resource.NewQuantity(17, resource.DecimalSI),
 | 
				
			||||||
 | 
									hugepages2M: *resource.NewQuantity(14, resource.DecimalSI),
 | 
				
			||||||
 | 
									hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
									1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								fmt.Sprintf(msgNotEqual, hugepages2M),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range testCases {
 | 
				
			||||||
 | 
							t.Run(tc.description, func(t *testing.T) {
 | 
				
			||||||
 | 
								err := validateReservedMemory(tc.nodeAllocatableReservation, tc.preReservedMemory)
 | 
				
			||||||
 | 
								if strings.TrimSpace(tc.expectedError) != "" {
 | 
				
			||||||
 | 
									assert.Error(t, err)
 | 
				
			||||||
 | 
									assert.Equal(t, err.Error(), tc.expectedError)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestConvertPreReserved(t *testing.T) {
 | 
				
			||||||
 | 
						machineInfo := info.MachineInfo{
 | 
				
			||||||
 | 
							Topology: []info.Node{
 | 
				
			||||||
 | 
								info.Node{Id: 0},
 | 
				
			||||||
 | 
								info.Node{Id: 1},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						testCases := []struct {
 | 
				
			||||||
 | 
							description      string
 | 
				
			||||||
 | 
							reserved         map[int]map[v1.ResourceName]resource.Quantity
 | 
				
			||||||
 | 
							reservedExpected reservedMemory
 | 
				
			||||||
 | 
							expectedError    string
 | 
				
			||||||
 | 
						}{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Empty",
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{},
 | 
				
			||||||
 | 
								reservedMemory{
 | 
				
			||||||
 | 
									0: map[v1.ResourceName]uint64{},
 | 
				
			||||||
 | 
									1: map[v1.ResourceName]uint64{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Single NUMA node is pre-reserved",
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								reservedMemory{
 | 
				
			||||||
 | 
									0: map[v1.ResourceName]uint64{
 | 
				
			||||||
 | 
										v1.ResourceMemory: 12,
 | 
				
			||||||
 | 
										hugepages2M:       70,
 | 
				
			||||||
 | 
										hugepages1G:       13,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									1: map[v1.ResourceName]uint64{},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								"Both NUMA nodes are pre-reserved",
 | 
				
			||||||
 | 
								map[int]map[v1.ResourceName]resource.Quantity{
 | 
				
			||||||
 | 
									0: nodeResources{v1.ResourceMemory: *resource.NewQuantity(12, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(70, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages1G: *resource.NewQuantity(13, resource.DecimalSI)},
 | 
				
			||||||
 | 
									1: nodeResources{v1.ResourceMemory: *resource.NewQuantity(5, resource.DecimalSI),
 | 
				
			||||||
 | 
										hugepages2M: *resource.NewQuantity(7, resource.DecimalSI)},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								reservedMemory{
 | 
				
			||||||
 | 
									0: map[v1.ResourceName]uint64{
 | 
				
			||||||
 | 
										v1.ResourceMemory: 12,
 | 
				
			||||||
 | 
										hugepages2M:       70,
 | 
				
			||||||
 | 
										hugepages1G:       13,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									1: map[v1.ResourceName]uint64{
 | 
				
			||||||
 | 
										v1.ResourceMemory: 5,
 | 
				
			||||||
 | 
										hugepages2M:       7,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								"",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, tc := range testCases {
 | 
				
			||||||
 | 
							t.Run(tc.description, func(t *testing.T) {
 | 
				
			||||||
 | 
								reserved, _ := convertReserved(&machineInfo, tc.reserved)
 | 
				
			||||||
 | 
								if !reflect.DeepEqual(reserved, tc.reservedExpected) {
 | 
				
			||||||
 | 
									t.Errorf("got %v, expected %v", reserved, tc.reservedExpected)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -23,10 +23,10 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// BracketSeparatedSliceMapStringString can be set from the command line with the format `--flag {key=value, ...}, {...}`.
 | 
					// BracketSeparatedSliceMapStringString can be set from the command line with the format `--flag {key=value, ...}, {...}`.
 | 
				
			||||||
// Multiple comma-separated key-value pairs in a braket(`{}`) in a single invocation are supported. For example: `--flag {key=value, key=value, ...}`.
 | 
					// Multiple comma-separated key-value pairs in brackets (`{}`) in a single invocation are supported. For example: `--flag {key=value, key=value, ...}`.
 | 
				
			||||||
// Multiple braket-separated list of key-value pairs in a single invocation are supported. For example: `--flag {key=value, key=value}, {key=value, key=value}`.
 | 
					// Multiple bracket-separated list of key-value pairs in a single invocation are supported. For example: `--flag {key=value, key=value}, {key=value, key=value}`.
 | 
				
			||||||
type BracketSeparatedSliceMapStringString struct {
 | 
					type BracketSeparatedSliceMapStringString struct {
 | 
				
			||||||
	Value    *[]map[string]string
 | 
						Value       *[]map[string]string
 | 
				
			||||||
	initialized bool // set to true after the first Set call
 | 
						initialized bool // set to true after the first Set call
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -36,7 +36,6 @@ func NewBracketSeparatedSliceMapStringString(m *[]map[string]string) *BracketSep
 | 
				
			|||||||
	return &BracketSeparatedSliceMapStringString{Value: m}
 | 
						return &BracketSeparatedSliceMapStringString{Value: m}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
// Set implements github.com/spf13/pflag.Value
 | 
					// Set implements github.com/spf13/pflag.Value
 | 
				
			||||||
func (m *BracketSeparatedSliceMapStringString) Set(value string) error {
 | 
					func (m *BracketSeparatedSliceMapStringString) Set(value string) error {
 | 
				
			||||||
	if m.Value == nil {
 | 
						if m.Value == nil {
 | 
				
			||||||
@@ -49,13 +48,7 @@ func (m *BracketSeparatedSliceMapStringString) Set(value string) error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	value = strings.TrimSpace(value)
 | 
						value = strings.TrimSpace(value)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// split here
 | 
					 | 
				
			||||||
	//{numa-node=0,memory-type=memory,limit=1Gi},{numa-node=1,memory-type=memory,limit=1Gi},{numa-node=1,memory-type=memory,limit=1Gi}
 | 
					 | 
				
			||||||
//	for _, split := range strings.Split(value, "{") {
 | 
					 | 
				
			||||||
//		split = strings.TrimRight(split, ",")
 | 
					 | 
				
			||||||
//		split = strings.TrimRight(split, "}")
 | 
					 | 
				
			||||||
	for _, split := range strings.Split(value, ",{") {
 | 
						for _, split := range strings.Split(value, ",{") {
 | 
				
			||||||
		//split = strings.TrimRight(split, ",")
 | 
					 | 
				
			||||||
		split = strings.TrimLeft(split, "{")
 | 
							split = strings.TrimLeft(split, "{")
 | 
				
			||||||
		split = strings.TrimRight(split, "}")
 | 
							split = strings.TrimRight(split, "}")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -66,7 +59,7 @@ func (m *BracketSeparatedSliceMapStringString) Set(value string) error {
 | 
				
			|||||||
		// now we have "numa-node=1,memory-type=memory,limit=1Gi"
 | 
							// now we have "numa-node=1,memory-type=memory,limit=1Gi"
 | 
				
			||||||
		tmpRawMap := make(map[string]string)
 | 
							tmpRawMap := make(map[string]string)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		tmpMap:= NewMapStringString(&tmpRawMap)
 | 
							tmpMap := NewMapStringString(&tmpRawMap)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if err := tmpMap.Set(split); err != nil {
 | 
							if err := tmpMap.Set(split); err != nil {
 | 
				
			||||||
			return fmt.Errorf("could not parse String: (%s): %v", value, err)
 | 
								return fmt.Errorf("could not parse String: (%s): %v", value, err)
 | 
				
			||||||
@@ -100,7 +93,7 @@ func (m *BracketSeparatedSliceMapStringString) String() string {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if len(tmpPairs) != 0 {
 | 
							if len(tmpPairs) != 0 {
 | 
				
			||||||
			slices = append(slices, "{" + strings.Join(tmpPairs, ",") + "}")
 | 
								slices = append(slices, "{"+strings.Join(tmpPairs, ",")+"}")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	sort.Strings(slices)
 | 
						sort.Strings(slices)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@ func TestStringBracketSeparatedSliceMapStringString(t *testing.T) {
 | 
				
			|||||||
		m      *BracketSeparatedSliceMapStringString
 | 
							m      *BracketSeparatedSliceMapStringString
 | 
				
			||||||
		expect string
 | 
							expect string
 | 
				
			||||||
	}{
 | 
						}{
 | 
				
			||||||
		{"nill", NewBracketSeparatedSliceMapStringString(&nilSliceMap), ""},
 | 
							{"nil", NewBracketSeparatedSliceMapStringString(&nilSliceMap), ""},
 | 
				
			||||||
		{"empty", NewBracketSeparatedSliceMapStringString(&[]map[string]string{}), ""},
 | 
							{"empty", NewBracketSeparatedSliceMapStringString(&[]map[string]string{}), ""},
 | 
				
			||||||
		{"one key", NewBracketSeparatedSliceMapStringString(&[]map[string]string{{"a": "string"}}), "{a=string}"},
 | 
							{"one key", NewBracketSeparatedSliceMapStringString(&[]map[string]string{{"a": "string"}}), "{a=string}"},
 | 
				
			||||||
		{"two keys", NewBracketSeparatedSliceMapStringString(&[]map[string]string{{"a": "string", "b": "string"}}), "{a=string,b=string}"},
 | 
							{"two keys", NewBracketSeparatedSliceMapStringString(&[]map[string]string{{"a": "string", "b": "string"}}), "{a=string,b=string}"},
 | 
				
			||||||
@@ -73,13 +73,13 @@ func TestSetBracketSeparatedSliceMapStringString(t *testing.T) {
 | 
				
			|||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
				Value:       &[]map[string]string{},
 | 
									Value:       &[]map[string]string{},
 | 
				
			||||||
			}, ""},
 | 
								}, ""},
 | 
				
			||||||
		{"empty braket", []string{"{}"},
 | 
							{"empty bracket", []string{"{}"},
 | 
				
			||||||
			NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
								NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
				
			||||||
			&BracketSeparatedSliceMapStringString{
 | 
								&BracketSeparatedSliceMapStringString{
 | 
				
			||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
				Value:       &[]map[string]string{},
 | 
									Value:       &[]map[string]string{},
 | 
				
			||||||
			}, ""},
 | 
								}, ""},
 | 
				
			||||||
		{"missing braket", []string{"a=string, b=string"},
 | 
							{"missing bracket", []string{"a=string, b=string"},
 | 
				
			||||||
			NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
								NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
				
			||||||
			&BracketSeparatedSliceMapStringString{
 | 
								&BracketSeparatedSliceMapStringString{
 | 
				
			||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
@@ -103,13 +103,13 @@ func TestSetBracketSeparatedSliceMapStringString(t *testing.T) {
 | 
				
			|||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
				Value:       &[]map[string]string{{"a": "string", "b": "string"}},
 | 
									Value:       &[]map[string]string{{"a": "string", "b": "string"}},
 | 
				
			||||||
			}, ""},
 | 
								}, ""},
 | 
				
			||||||
		{"two duplecated keys", []string{"{a=string,a=string}"},
 | 
							{"two duplicated keys", []string{"{a=string,a=string}"},
 | 
				
			||||||
			NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
								NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
				
			||||||
			&BracketSeparatedSliceMapStringString{
 | 
								&BracketSeparatedSliceMapStringString{
 | 
				
			||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
				Value:       &[]map[string]string{{"a": "string"}},
 | 
									Value:       &[]map[string]string{{"a": "string"}},
 | 
				
			||||||
			}, ""},
 | 
								}, ""},
 | 
				
			||||||
		{"two keys with space", []string{"{a = string, b = string}"},
 | 
							{"two keys with spaces", []string{"{a = string, b = string}"},
 | 
				
			||||||
			NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
								NewBracketSeparatedSliceMapStringString(&nilMap),
 | 
				
			||||||
			&BracketSeparatedSliceMapStringString{
 | 
								&BracketSeparatedSliceMapStringString{
 | 
				
			||||||
				initialized: true,
 | 
									initialized: true,
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user