memory manager: provide new flag var to parse reserved-memory parameter
The new flag will parse the `--reserved-memory` flag straight forward to the []kubeletconfig.MemoryReservation variable instead of parsing it to the middle map representation. It gives us possibility to get rid of a lot of unneeded code and use the single presentation for the reserved-memory. Signed-off-by: Artyom Lukianov <alukiano@redhat.com>
This commit is contained in:
@@ -11,6 +11,10 @@ go_library(
|
||||
srcs = ["flags.go"],
|
||||
importpath = "k8s.io/kubernetes/pkg/util/flag",
|
||||
deps = [
|
||||
"//pkg/apis/core/v1/helper:go_default_library",
|
||||
"//pkg/kubelet/apis/config:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
"//vendor/k8s.io/utils/net:go_default_library",
|
||||
@@ -34,5 +38,11 @@ go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["flags_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//vendor/github.com/spf13/pflag:go_default_library"],
|
||||
deps = [
|
||||
"//pkg/kubelet/apis/config:go_default_library",
|
||||
"//staging/src/k8s.io/api/core/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/equality:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
|
||||
"//vendor/github.com/spf13/pflag:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@@ -19,10 +19,17 @@ package flag
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
corev1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
utilsnet "k8s.io/utils/net"
|
||||
)
|
||||
|
||||
@@ -32,6 +39,7 @@ var (
|
||||
_ pflag.Value = &IPVar{}
|
||||
_ pflag.Value = &IPPortVar{}
|
||||
_ pflag.Value = &PortRangeVar{}
|
||||
_ pflag.Value = &ReservedMemoryVar{}
|
||||
)
|
||||
|
||||
// IPVar is used for validating a command line option that represents an IP. It implements the pflag.Value interface
|
||||
@@ -151,3 +159,99 @@ func (v PortRangeVar) String() string {
|
||||
func (v PortRangeVar) Type() string {
|
||||
return "port-range"
|
||||
}
|
||||
|
||||
// ReservedMemoryVar is used for validating a command line option that represents a reserved memory. It implements the pflag.Value interface
|
||||
type ReservedMemoryVar struct {
|
||||
Value *[]kubeletconfig.MemoryReservation
|
||||
initialized bool // set to true after the first Set call
|
||||
}
|
||||
|
||||
// Set sets the flag value
|
||||
func (v *ReservedMemoryVar) Set(s string) error {
|
||||
if v.Value == nil {
|
||||
return fmt.Errorf("no target (nil pointer to *[]MemoryReservation")
|
||||
}
|
||||
|
||||
if s == "" {
|
||||
v.Value = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
if !v.initialized || *v.Value == nil {
|
||||
*v.Value = make([]kubeletconfig.MemoryReservation, 0)
|
||||
v.initialized = true
|
||||
}
|
||||
|
||||
if s == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
numaNodeReservation := strings.Split(s, ":")
|
||||
if len(numaNodeReservation) != 2 {
|
||||
return fmt.Errorf("the reserved memory has incorrect format, expected numaNodeID:type=quantity[,type=quantity...], got %s", s)
|
||||
}
|
||||
|
||||
memoryTypeReservations := strings.Split(numaNodeReservation[1], ",")
|
||||
if len(memoryTypeReservations) < 1 {
|
||||
return fmt.Errorf("the reserved memory has incorrect format, expected numaNodeID:type=quantity[,type=quantity...], got %s", s)
|
||||
}
|
||||
|
||||
numaNodeID, err := strconv.Atoi(numaNodeReservation[0])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to convert the NUMA node ID, exptected integer, got %s", numaNodeReservation[0])
|
||||
}
|
||||
|
||||
memoryReservation := kubeletconfig.MemoryReservation{
|
||||
NumaNode: int32(numaNodeID),
|
||||
Limits: map[v1.ResourceName]resource.Quantity{},
|
||||
}
|
||||
|
||||
for _, reservation := range memoryTypeReservations {
|
||||
limit := strings.Split(reservation, "=")
|
||||
if len(limit) != 2 {
|
||||
return fmt.Errorf("the reserved limit has incorrect value, expected type=quantatity, got %s", reservation)
|
||||
}
|
||||
|
||||
resourceName := v1.ResourceName(limit[0])
|
||||
if resourceName != v1.ResourceMemory && !corev1helper.IsHugePageResourceName(resourceName) {
|
||||
return fmt.Errorf("memory type conversion error, unknown type: %q", resourceName)
|
||||
}
|
||||
|
||||
q, err := resource.ParseQuantity(limit[1])
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse the quantatity, expected quantatity, got %s", limit[1])
|
||||
}
|
||||
|
||||
memoryReservation.Limits[v1.ResourceName(limit[0])] = q
|
||||
}
|
||||
|
||||
*v.Value = append(*v.Value, memoryReservation)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the flag value
|
||||
func (v *ReservedMemoryVar) String() string {
|
||||
if v == nil || v.Value == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var slices []string
|
||||
for _, reservedMemory := range *v.Value {
|
||||
var limits []string
|
||||
for resourceName, q := range reservedMemory.Limits {
|
||||
limits = append(limits, fmt.Sprintf("%s=%s", resourceName, q.String()))
|
||||
}
|
||||
|
||||
sort.Strings(limits)
|
||||
slices = append(slices, fmt.Sprintf("%d:%s", reservedMemory.NumaNode, strings.Join(limits, ",")))
|
||||
}
|
||||
|
||||
sort.Strings(slices)
|
||||
return strings.Join(slices, ",")
|
||||
}
|
||||
|
||||
// Type gets the flag type
|
||||
func (v *ReservedMemoryVar) Type() string {
|
||||
return "reserved-memory"
|
||||
}
|
||||
|
@@ -17,10 +17,16 @@ limitations under the License.
|
||||
package flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
kubeletconfig "k8s.io/kubernetes/pkg/kubelet/apis/config"
|
||||
)
|
||||
|
||||
func TestIPVar(t *testing.T) {
|
||||
@@ -163,3 +169,121 @@ func TestIPPortVar(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReservedMemoryVar(t *testing.T) {
|
||||
resourceNameHugepages1Gi := v1.ResourceName(fmt.Sprintf("%s1Gi", v1.ResourceHugePagesPrefix))
|
||||
memory1Gi := resource.MustParse("1Gi")
|
||||
testCases := []struct {
|
||||
desc string
|
||||
argc string
|
||||
expectErr bool
|
||||
expectVal []kubeletconfig.MemoryReservation
|
||||
}{
|
||||
{
|
||||
desc: "valid input",
|
||||
argc: "blah --reserved-memory=0:memory=1Gi",
|
||||
expectVal: []kubeletconfig.MemoryReservation{
|
||||
{
|
||||
NumaNode: 0,
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceMemory: memory1Gi,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid input with multiple memory types",
|
||||
argc: "blah --reserved-memory=0:memory=1Gi,hugepages-1Gi=1Gi",
|
||||
expectVal: []kubeletconfig.MemoryReservation{
|
||||
{
|
||||
NumaNode: 0,
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceMemory: memory1Gi,
|
||||
resourceNameHugepages1Gi: memory1Gi,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "valid input with multiple reserved-memory arguments",
|
||||
argc: "blah --reserved-memory=0:memory=1Gi,hugepages-1Gi=1Gi --reserved-memory=1:memory=1Gi",
|
||||
expectVal: []kubeletconfig.MemoryReservation{
|
||||
{
|
||||
NumaNode: 0,
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceMemory: memory1Gi,
|
||||
resourceNameHugepages1Gi: memory1Gi,
|
||||
},
|
||||
},
|
||||
{
|
||||
NumaNode: 1,
|
||||
Limits: v1.ResourceList{
|
||||
v1.ResourceMemory: memory1Gi,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
desc: "invalid input",
|
||||
argc: "blah --reserved-memory=bad-input",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid input without memory types",
|
||||
argc: "blah --reserved-memory=0:",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid input with non-integer NUMA node",
|
||||
argc: "blah --reserved-memory=a:memory=1Gi",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid input with invalid limit",
|
||||
argc: "blah --reserved-memory=0:memory=",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid input with invalid memory type",
|
||||
argc: "blah --reserved-memory=0:type=1Gi",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
{
|
||||
desc: "invalid input with invalid quantity",
|
||||
argc: "blah --reserved-memory=0:memory=1Be",
|
||||
expectVal: nil,
|
||||
expectErr: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
fs := pflag.NewFlagSet("blah", pflag.PanicOnError)
|
||||
|
||||
var reservedMemory []kubeletconfig.MemoryReservation
|
||||
fs.Var(&ReservedMemoryVar{Value: &reservedMemory}, "reserved-memory", "--reserved-memory 0:memory=1Gi,hugepages-1M=2Gi")
|
||||
|
||||
var err error
|
||||
func() {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
err = r.(error)
|
||||
}
|
||||
}()
|
||||
fs.Parse(strings.Split(tc.argc, " "))
|
||||
}()
|
||||
|
||||
if tc.expectErr && err == nil {
|
||||
t.Fatalf("%q: Did not observe an expected error", tc.desc)
|
||||
}
|
||||
if !tc.expectErr && err != nil {
|
||||
t.Fatalf("%q: Observed an unexpected error: %v", tc.desc, err)
|
||||
}
|
||||
if !apiequality.Semantic.DeepEqual(reservedMemory, tc.expectVal) {
|
||||
t.Fatalf("%q: Unexpected reserved-error: expected %v, saw %v", tc.desc, tc.expectVal, reservedMemory)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user