Lift embedded structure out of ManifestURLHeader field

Kubernetes-commit: 7cb21746c09cd7dd8e2beaaa03f17ba41563a318
This commit is contained in:
Michael Taufen 2017-10-19 15:42:07 -07:00 committed by Kubernetes Publisher
parent 703a17eb2a
commit ce50eca6d7
3 changed files with 208 additions and 0 deletions

View File

@ -9,6 +9,7 @@ load(
go_test(
name = "go_default_test",
srcs = [
"colon_separated_multimap_string_string_test.go",
"map_string_bool_test.go",
"namedcertkey_flag_test.go",
],
@ -24,6 +25,7 @@ go_test(
go_library(
name = "go_default_library",
srcs = [
"colon_separated_multimap_string_string.go",
"configuration_map.go",
"flags.go",
"map_string_bool.go",

View File

@ -0,0 +1,80 @@
/*
Copyright 2017 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"
)
// ColonSeparatedMultimapStringString supports setting a map[string][]string from an encoding
// that separates keys from values with ':' and separates key-value pairs with ','.
// A key can be repeated multiple times, in which case the values are appended to a
// slice of strings associated with that key. Items in the list associated with a given
// key will appear in the order provided.
// For example: `a:hello,b:again,c:world,b:beautiful` results in `{"a": ["hello"], "b": ["again", "beautiful"], "c": ["world"]}`
type ColonSeparatedMultimapStringString map[string][]string
// Set implements github.com/spf13/pflag.Value
func (m ColonSeparatedMultimapStringString) Set(value string) error {
// clear old values
for k := range m {
delete(m, k)
}
for _, pair := range strings.Split(value, ",") {
if len(pair) == 0 {
continue
}
kv := strings.SplitN(pair, ":", 2)
if len(kv) != 2 {
return fmt.Errorf("malformed pair, expect string:string")
}
k := strings.TrimSpace(kv[0])
v := strings.TrimSpace(kv[1])
m[k] = append(m[k], v)
}
return nil
}
// String implements github.com/spf13/pflag.Value
func (m ColonSeparatedMultimapStringString) String() string {
type kv struct {
k string
v string
}
kvs := make([]kv, 0, len(m))
for k, vs := range m {
for i := range vs {
kvs = append(kvs, kv{k: k, v: vs[i]})
}
}
// stable sort by keys, order of values should be preserved
sort.SliceStable(kvs, func(i, j int) bool {
return kvs[i].k < kvs[j].k
})
pairs := make([]string, 0, len(kvs))
for i := range kvs {
pairs = append(pairs, fmt.Sprintf("%s:%s", kvs[i].k, kvs[i].v))
}
return strings.Join(pairs, ",")
}
// Type implements github.com/spf13/pflag.Value
func (m ColonSeparatedMultimapStringString) Type() string {
return "colonSeparatedMultimapStringString"
}

View File

@ -0,0 +1,126 @@
/*
Copyright 2017 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 TestStringColonSeparatedMultimapStringString(t *testing.T) {
cases := []struct {
desc string
m ColonSeparatedMultimapStringString
expect string
}{
{"empty", ColonSeparatedMultimapStringString{}, ""},
{"empty key", ColonSeparatedMultimapStringString{"": []string{"foo"}}, ":foo"},
{"one key", ColonSeparatedMultimapStringString{"one": []string{"foo"}}, "one:foo"},
{"two keys", ColonSeparatedMultimapStringString{"one": []string{"foo"}, "two": []string{"bar"}}, "one:foo,two:bar"},
{"two keys, multiple items in one key", ColonSeparatedMultimapStringString{"one": []string{"foo", "baz"}, "two": []string{"bar"}}, "one:foo,one:baz,two:bar"},
{"three keys, multiple items in one key", ColonSeparatedMultimapStringString{"a": []string{"hello"}, "b": []string{"again", "beautiful"}, "c": []string{"world"}}, "a:hello,b:again,b:beautiful,c:world"},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
str := c.m.String()
if c.expect != str {
t.Fatalf("expect %q but got %q", c.expect, str)
}
})
}
}
func TestSetColonSeparatedMultimapStringString(t *testing.T) {
cases := []struct {
desc string
val string
expect ColonSeparatedMultimapStringString
err string
}{
{"empty", "", ColonSeparatedMultimapStringString{}, ""},
{"empty key", ":foo", ColonSeparatedMultimapStringString{
"": []string{"foo"},
}, ""},
{"one key", "one:foo", ColonSeparatedMultimapStringString{
"one": []string{"foo"}}, ""},
{"two keys", "one:foo,two:bar", ColonSeparatedMultimapStringString{
"one": []string{"foo"},
"two": []string{"bar"},
}, ""},
{"two keys with space", "one:foo, two:bar", ColonSeparatedMultimapStringString{
"one": []string{"foo"},
"two": []string{"bar"},
}, ""},
{"two keys, multiple items in one key", "one: foo, two:bar, one:baz", ColonSeparatedMultimapStringString{
"one": []string{"foo", "baz"},
"two": []string{"bar"},
}, ""},
{"three keys, multiple items in one key", "a:hello,b:again,c:world,b:beautiful", ColonSeparatedMultimapStringString{
"a": []string{"hello"},
"b": []string{"again", "beautiful"},
"c": []string{"world"},
}, ""},
{"missing value", "one", ColonSeparatedMultimapStringString{}, "malformed pair, expect string:string"},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
// we initialize the map with a default key that should be cleared by Set (no test cases specify "default")
m := ColonSeparatedMultimapStringString{"default": []string{}}
err := m.Set(c.val)
if c.err != "" {
if err.Error() != c.err {
t.Fatalf("expect error %s but got %v", c.err, err)
}
return
} else if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(c.expect, m) {
t.Fatalf("expect %#v but got %#v", c.expect, m)
}
})
}
}
func TestRoundTripColonSeparatedMultimapStringString(t *testing.T) {
cases := []struct {
desc string
val string
expect string
}{
{"empty", "", ""},
{"empty key", ":foo", ":foo"},
{"one key", "one:foo", "one:foo"},
{"two keys", "one:foo,two:bar", "one:foo,two:bar"},
{"two keys, multiple items in one key", "one:foo, two:bar, one:baz", "one:foo,one:baz,two:bar"},
{"three keys, multiple items in one key", "a:hello,b:again,c:world,b:beautiful", "a:hello,b:again,b:beautiful,c:world"},
}
for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
m := ColonSeparatedMultimapStringString{}
if err := m.Set(c.val); err != nil {
t.Fatalf("unexpected error: %v", err)
}
str := m.String()
if c.expect != str {
t.Fatalf("expect %q but got %q", c.expect, str)
}
})
}
}