mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
memory manager: add new flag type BracketSeparatedSliceMapStringString
Add BracketSeparatedSliceMapStringString to parse config like the below {numa-node=0,type=memory,limit=1Gi},{numa-node=1,type=memory,limit=1Gi} Signed-off-by: Byonggon Chun <bg.chun@samsung.com>
This commit is contained in:
parent
95f81372e2
commit
b95d45e803
@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 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 braket-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 {
|
||||
Value *[]map[string]string
|
||||
initialized bool // set to true after the first Set call
|
||||
}
|
||||
|
||||
// NewBracketSeparatedSliceMapStringString takes a pointer to a []map[string]string and returns the
|
||||
// BracketSeparatedSliceMapStringString flag parsing shim for that map
|
||||
func NewBracketSeparatedSliceMapStringString(m *[]map[string]string) *BracketSeparatedSliceMapStringString {
|
||||
return &BracketSeparatedSliceMapStringString{Value: m}
|
||||
}
|
||||
|
||||
|
||||
// Set implements github.com/spf13/pflag.Value
|
||||
func (m *BracketSeparatedSliceMapStringString) Set(value string) error {
|
||||
if m.Value == nil {
|
||||
return fmt.Errorf("no target (nil pointer to []map[string]string)")
|
||||
}
|
||||
if !m.initialized || *m.Value == nil {
|
||||
*m.Value = make([]map[string]string, 0)
|
||||
m.initialized = true
|
||||
}
|
||||
|
||||
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, ",{") {
|
||||
//split = strings.TrimRight(split, ",")
|
||||
split = strings.TrimLeft(split, "{")
|
||||
split = strings.TrimRight(split, "}")
|
||||
|
||||
if len(split) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// now we have "numa-node=1,memory-type=memory,limit=1Gi"
|
||||
tmpRawMap := make(map[string]string)
|
||||
|
||||
tmpMap:= NewMapStringString(&tmpRawMap)
|
||||
|
||||
if err := tmpMap.Set(split); err != nil {
|
||||
return fmt.Errorf("could not parse String: (%s): %v", value, err)
|
||||
}
|
||||
|
||||
*m.Value = append(*m.Value, tmpRawMap)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// String implements github.com/spf13/pflag.Value
|
||||
func (m *BracketSeparatedSliceMapStringString) String() string {
|
||||
if m == nil || m.Value == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
var slices []string
|
||||
|
||||
for _, configMap := range *m.Value {
|
||||
var tmpPairs []string
|
||||
|
||||
var keys []string
|
||||
for key := range configMap {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, key := range keys {
|
||||
tmpPairs = append(tmpPairs, fmt.Sprintf("%s=%s", key, configMap[key]))
|
||||
}
|
||||
|
||||
if len(tmpPairs) != 0 {
|
||||
slices = append(slices, "{" + strings.Join(tmpPairs, ",") + "}")
|
||||
}
|
||||
}
|
||||
sort.Strings(slices)
|
||||
return strings.Join(slices, ",")
|
||||
}
|
||||
|
||||
// Type implements github.com/spf13/pflag.Value
|
||||
func (*BracketSeparatedSliceMapStringString) Type() string {
|
||||
return "BracketSeparatedSliceMapStringString"
|
||||
}
|
||||
|
||||
// Empty implements OmitEmpty
|
||||
func (m *BracketSeparatedSliceMapStringString) Empty() bool {
|
||||
return !m.initialized || m.Value == nil || len(*m.Value) == 0
|
||||
}
|
@ -0,0 +1,178 @@
|
||||
/*
|
||||
Copyright 2020 The Kubernetes Authors.
|
||||
|
||||
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 flag
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringBracketSeparatedSliceMapStringString(t *testing.T) {
|
||||
var nilSliceMap []map[string]string
|
||||
testCases := []struct {
|
||||
desc string
|
||||
m *BracketSeparatedSliceMapStringString
|
||||
expect string
|
||||
}{
|
||||
{"nill", NewBracketSeparatedSliceMapStringString(&nilSliceMap), ""},
|
||||
{"empty", NewBracketSeparatedSliceMapStringString(&[]map[string]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}"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
str := tc.m.String()
|
||||
if tc.expect != str {
|
||||
t.Fatalf("expect %q but got %q", tc.expect, str)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetBracketSeparatedSliceMapStringString(t *testing.T) {
|
||||
var nilMap []map[string]string
|
||||
testCases := []struct {
|
||||
desc string
|
||||
vals []string
|
||||
start *BracketSeparatedSliceMapStringString
|
||||
expect *BracketSeparatedSliceMapStringString
|
||||
err string
|
||||
}{
|
||||
// we initialize the map with a default key that should be cleared by Set
|
||||
{"clears defaults", []string{""},
|
||||
NewBracketSeparatedSliceMapStringString(&[]map[string]string{{"default": ""}}),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{},
|
||||
}, ""},
|
||||
// make sure we still allocate for "initialized" multimaps where Multimap was initially set to a nil map
|
||||
{"allocates map if currently nil", []string{""},
|
||||
&BracketSeparatedSliceMapStringString{initialized: true, Value: &nilMap},
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{},
|
||||
}, ""},
|
||||
// for most cases, we just reuse nilMap, which should be allocated by Set, and is reset before each test case
|
||||
{"empty", []string{""},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{},
|
||||
}, ""},
|
||||
{"empty braket", []string{"{}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{},
|
||||
}, ""},
|
||||
{"missing braket", []string{"a=string, b=string"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string", "b": "string"}},
|
||||
}, ""},
|
||||
{"empty key", []string{"{=string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"": "string"}},
|
||||
}, ""},
|
||||
{"one key", []string{"{a=string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string"}},
|
||||
}, ""},
|
||||
{"two keys", []string{"{a=string,b=string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string", "b": "string"}},
|
||||
}, ""},
|
||||
{"two duplecated keys", []string{"{a=string,a=string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string"}},
|
||||
}, ""},
|
||||
{"two keys with space", []string{"{a = string, b = string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string", "b": "string"}},
|
||||
}, ""},
|
||||
{"two keys, multiple Set invocations", []string{"{a=string, b=string}", "{a=string, b=string}"},
|
||||
NewBracketSeparatedSliceMapStringString(&nilMap),
|
||||
&BracketSeparatedSliceMapStringString{
|
||||
initialized: true,
|
||||
Value: &[]map[string]string{{"a": "string", "b": "string"}, {"a": "string", "b": "string"}},
|
||||
}, ""},
|
||||
{"no target", []string{""},
|
||||
NewBracketSeparatedSliceMapStringString(nil),
|
||||
nil,
|
||||
"no target (nil pointer to []map[string]string)"},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
nilMap = nil
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
var err error
|
||||
for _, val := range tc.vals {
|
||||
err = tc.start.Set(val)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if tc.err != "" {
|
||||
if err == nil || err.Error() != tc.err {
|
||||
t.Fatalf("expect error %s but got %v", tc.err, err)
|
||||
}
|
||||
return
|
||||
} else if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !reflect.DeepEqual(tc.expect, tc.start) {
|
||||
t.Fatalf("expect %#v but got %#v", tc.expect, tc.start)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyBracketSeparatedSliceMapStringString(t *testing.T) {
|
||||
var nilSliceMap []map[string]string
|
||||
notEmpty := &BracketSeparatedSliceMapStringString{
|
||||
Value: &[]map[string]string{{"a": "int", "b": "string", "c": "string"}},
|
||||
initialized: true,
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
desc string
|
||||
m *BracketSeparatedSliceMapStringString
|
||||
expect bool
|
||||
}{
|
||||
{"nil", NewBracketSeparatedSliceMapStringString(&nilSliceMap), true},
|
||||
{"empty", NewBracketSeparatedSliceMapStringString(&[]map[string]string{}), true},
|
||||
{"populated", notEmpty, false},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.desc, func(t *testing.T) {
|
||||
ret := tc.m.Empty()
|
||||
if ret != tc.expect {
|
||||
t.Fatalf("expect %t but got %t", tc.expect, ret)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user