Browse Source

pkg/flags: add "UniqueURLs", "UniqueStrings"

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>

iii

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Gyuho Lee 7 years ago
parent
commit
b426217907

+ 75 - 0
pkg/flags/unique_strings.go

@@ -0,0 +1,75 @@
+// Copyright 2018 The etcd 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 flags
+
+import (
+	"flag"
+	"sort"
+	"strings"
+)
+
+// UniqueStringsValue wraps a list of unique strings.
+// The values are set in order.
+type UniqueStringsValue struct {
+	Values map[string]struct{}
+}
+
+// Set parses a command line set of strings, separated by comma.
+// Implements "flag.Value" interface.
+// The values are set in order.
+func (us *UniqueStringsValue) Set(s string) error {
+	for _, v := range strings.Split(s, ",") {
+		us.Values[v] = struct{}{}
+	}
+	return nil
+}
+
+// String implements "flag.Value" interface.
+func (us *UniqueStringsValue) String() string {
+	return strings.Join(us.stringSlice(), ",")
+}
+
+func (us *UniqueStringsValue) stringSlice() []string {
+	ss := make([]string, 0, len(us.Values))
+	for v := range us.Values {
+		ss = append(ss, v)
+	}
+	sort.Strings(ss)
+	return ss
+}
+
+// NewUniqueStringsValue implements string slice as "flag.Value" interface.
+// Given value is to be separated by comma.
+// The values are set in order.
+func NewUniqueStringsValue(s string) (us *UniqueStringsValue) {
+	us = &UniqueStringsValue{Values: make(map[string]struct{})}
+	if s == "" {
+		return us
+	}
+	if err := us.Set(s); err != nil {
+		plog.Panicf("new UniqueStringsValue should never fail: %v", err)
+	}
+	return us
+}
+
+// UniqueStringsFromFlag returns a string slice from the flag.
+func UniqueStringsFromFlag(fs *flag.FlagSet, flagName string) []string {
+	return []string((*fs.Lookup(flagName).Value.(*UniqueStringsValue)).stringSlice())
+}
+
+// UniqueStringsMapFromFlag returns a map of strings from the flag.
+func UniqueStringsMapFromFlag(fs *flag.FlagSet, flagName string) map[string]struct{} {
+	return (*fs.Lookup(flagName).Value.(*UniqueStringsValue)).Values
+}

+ 68 - 0
pkg/flags/unique_strings_test.go

@@ -0,0 +1,68 @@
+// Copyright 2018 The etcd 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 flags
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestNewUniqueStrings(t *testing.T) {
+	tests := []struct {
+		s   string
+		exp map[string]struct{}
+		rs  string
+	}{
+		{ // non-URL but allowed by exception
+			s:   "*",
+			exp: map[string]struct{}{"*": {}},
+			rs:  "*",
+		},
+		{
+			s:   "",
+			exp: map[string]struct{}{},
+			rs:  "",
+		},
+		{
+			s:   "example.com",
+			exp: map[string]struct{}{"example.com": {}},
+			rs:  "example.com",
+		},
+		{
+			s:   "localhost,localhost",
+			exp: map[string]struct{}{"localhost": {}},
+			rs:  "localhost",
+		},
+		{
+			s:   "b.com,a.com",
+			exp: map[string]struct{}{"a.com": {}, "b.com": {}},
+			rs:  "a.com,b.com",
+		},
+		{
+			s:   "c.com,b.com",
+			exp: map[string]struct{}{"b.com": {}, "c.com": {}},
+			rs:  "b.com,c.com",
+		},
+	}
+	for i := range tests {
+		uv := NewUniqueStringsValue(tests[i].s)
+		if !reflect.DeepEqual(tests[i].exp, uv.Values) {
+			t.Fatalf("#%d: expected %+v, got %+v", i, tests[i].exp, uv.Values)
+		}
+		if uv.String() != tests[i].rs {
+			t.Fatalf("#%d: expected %q, got %q", i, tests[i].rs, uv.String())
+		}
+	}
+}

+ 90 - 0
pkg/flags/unique_urls.go

@@ -0,0 +1,90 @@
+// Copyright 2018 The etcd 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 flags
+
+import (
+	"flag"
+	"net/url"
+	"sort"
+	"strings"
+
+	"github.com/coreos/etcd/pkg/types"
+)
+
+// UniqueURLs contains unique URLs
+// with non-URL exceptions.
+type UniqueURLs struct {
+	Values  map[string]struct{}
+	uss     []url.URL
+	Allowed map[string]struct{}
+}
+
+// Set parses a command line set of URLs formatted like:
+// http://127.0.0.1:2380,http://10.1.1.2:80
+// Implements "flag.Value" interface.
+func (us *UniqueURLs) Set(s string) error {
+	if _, ok := us.Values[s]; ok {
+		return nil
+	}
+	if _, ok := us.Allowed[s]; ok {
+		us.Values[s] = struct{}{}
+		return nil
+	}
+	ss, err := types.NewURLs(strings.Split(s, ","))
+	if err != nil {
+		return err
+	}
+	for _, v := range ss {
+		us.Values[v.String()] = struct{}{}
+		us.uss = append(us.uss, v)
+	}
+	return nil
+}
+
+// String implements "flag.Value" interface.
+func (us *UniqueURLs) String() string {
+	all := make([]string, 0, len(us.Values))
+	for u := range us.Values {
+		all = append(all, u)
+	}
+	sort.Strings(all)
+	return strings.Join(all, ",")
+}
+
+// NewUniqueURLsWithExceptions implements "url.URL" slice as flag.Value interface.
+// Given value is to be separated by comma.
+func NewUniqueURLsWithExceptions(s string, exceptions ...string) *UniqueURLs {
+	us := &UniqueURLs{Values: make(map[string]struct{}), Allowed: make(map[string]struct{})}
+	for _, v := range exceptions {
+		us.Allowed[v] = struct{}{}
+	}
+	if s == "" {
+		return us
+	}
+	if err := us.Set(s); err != nil {
+		plog.Panicf("new UniqueURLs should never fail: %v", err)
+	}
+	return us
+}
+
+// UniqueURLsFromFlag returns a slice from urls got from the flag.
+func UniqueURLsFromFlag(fs *flag.FlagSet, urlsFlagName string) []url.URL {
+	return (*fs.Lookup(urlsFlagName).Value.(*UniqueURLs)).uss
+}
+
+// UniqueURLsMapFromFlag returns a map from url strings got from the flag.
+func UniqueURLsMapFromFlag(fs *flag.FlagSet, urlsFlagName string) map[string]struct{} {
+	return (*fs.Lookup(urlsFlagName).Value.(*UniqueURLs)).Values
+}

+ 93 - 0
pkg/flags/unique_urls_test.go

@@ -0,0 +1,93 @@
+// Copyright 2018 The etcd 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 flags
+
+import (
+	"reflect"
+	"testing"
+)
+
+func TestNewUniqueURLsWithExceptions(t *testing.T) {
+	tests := []struct {
+		s         string
+		exp       map[string]struct{}
+		rs        string
+		exception string
+	}{
+		{ // non-URL but allowed by exception
+			s:         "*",
+			exp:       map[string]struct{}{"*": {}},
+			rs:        "*",
+			exception: "*",
+		},
+		{
+			s:         "",
+			exp:       map[string]struct{}{},
+			rs:        "",
+			exception: "*",
+		},
+		{
+			s:         "https://1.2.3.4:8080",
+			exp:       map[string]struct{}{"https://1.2.3.4:8080": {}},
+			rs:        "https://1.2.3.4:8080",
+			exception: "*",
+		},
+		{
+			s:         "https://1.2.3.4:8080,https://1.2.3.4:8080",
+			exp:       map[string]struct{}{"https://1.2.3.4:8080": {}},
+			rs:        "https://1.2.3.4:8080",
+			exception: "*",
+		},
+		{
+			s:         "http://10.1.1.1:80",
+			exp:       map[string]struct{}{"http://10.1.1.1:80": {}},
+			rs:        "http://10.1.1.1:80",
+			exception: "*",
+		},
+		{
+			s:         "http://localhost:80",
+			exp:       map[string]struct{}{"http://localhost:80": {}},
+			rs:        "http://localhost:80",
+			exception: "*",
+		},
+		{
+			s:         "http://:80",
+			exp:       map[string]struct{}{"http://:80": {}},
+			rs:        "http://:80",
+			exception: "*",
+		},
+		{
+			s:         "https://localhost:5,https://localhost:3",
+			exp:       map[string]struct{}{"https://localhost:3": {}, "https://localhost:5": {}},
+			rs:        "https://localhost:3,https://localhost:5",
+			exception: "*",
+		},
+		{
+			s:         "http://localhost:5,https://localhost:3",
+			exp:       map[string]struct{}{"https://localhost:3": {}, "http://localhost:5": {}},
+			rs:        "http://localhost:5,https://localhost:3",
+			exception: "*",
+		},
+	}
+	for i := range tests {
+		uv := NewUniqueURLsWithExceptions(tests[i].s, tests[i].exception)
+		if !reflect.DeepEqual(tests[i].exp, uv.Values) {
+			t.Fatalf("#%d: expected %+v, got %+v", i, tests[i].exp, uv.Values)
+		}
+		if uv.String() != tests[i].rs {
+			t.Fatalf("#%d: expected %q, got %q", i, tests[i].rs, uv.String())
+		}
+	}
+}