diff --git a/bridge/types_test.go b/bridge/types_test.go index fe2265e8e..0e3e63ef8 100644 --- a/bridge/types_test.go +++ b/bridge/types_test.go @@ -23,3 +23,6 @@ func (f *fakeAdapter) Deregister(service *Service) error { func (f *fakeAdapter) Refresh(service *Service) error { return nil } +func (f *fakeAdapter) Services() ([]*Service, error) { + return nil, nil +} diff --git a/bridge/util.go b/bridge/util.go index 3b450faff..e3c295cfb 100644 --- a/bridge/util.go +++ b/bridge/util.go @@ -20,12 +20,36 @@ func mapDefault(m map[string]string, key, default_ string) string { return v } +// Golang regexp module does not support /(?!\\),/ syntax for spliting by not escaped comma +// Then this function is reproducing it +func recParseEscapedComma(str string) []string { + if len(str) == 0 { + return []string{} + } else if str[0] == ',' { + return recParseEscapedComma(str[1:]) + } + + offset := 0 + for len(str[offset:]) > 0 { + index := strings.Index(str[offset:], ",") + + if index == -1 { + break + } else if str[offset+index-1:offset+index] != "\\" { + return append(recParseEscapedComma(str[offset+index+1:]), str[:offset+index]) + } + + str = str[:offset+index-1] + str[offset+index:] + offset += index + } + + return []string{str} +} + func combineTags(tagParts ...string) []string { tags := make([]string, 0) for _, element := range tagParts { - if element != "" { - tags = append(tags, strings.Split(element, ",")...) - } + tags = append(tags, recParseEscapedComma(element)...) } return tags } diff --git a/bridge/util_test.go b/bridge/util_test.go new file mode 100644 index 000000000..149186a9b --- /dev/null +++ b/bridge/util_test.go @@ -0,0 +1,59 @@ +package bridge + +import ( + "sort" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestEscapedComma(t *testing.T) { + cases := []struct { + Tag string + Expected []string + }{ + { + Tag: "", + Expected: []string{}, + }, + { + Tag: "foobar", + Expected: []string{"foobar"}, + }, + { + Tag: "foo,bar", + Expected: []string{"foo", "bar"}, + }, + { + Tag: "foo\\,bar", + Expected: []string{"foo,bar"}, + }, + { + Tag: "foo,bar\\,baz", + Expected: []string{"foo", "bar,baz"}, + }, + { + Tag: "\\,foobar\\,", + Expected: []string{",foobar,"}, + }, + { + Tag: ",,,,foo,,,bar,,,", + Expected: []string{"foo", "bar"}, + }, + { + Tag: ",,,,", + Expected: []string{}, + }, + { + Tag: ",,\\,,", + Expected: []string{","}, + }, + } + + for _, c := range cases { + results := recParseEscapedComma(c.Tag) + sort.Strings(c.Expected) + sort.Strings(results) + assert.EqualValues(t, c.Expected, results) + } +} diff --git a/docs/user/services.md b/docs/user/services.md index d7a187c1e..5065b9739 100644 --- a/docs/user/services.md +++ b/docs/user/services.md @@ -166,6 +166,13 @@ Results in `Service`: Keep in mind not all of the `Service` object may be used by the registry backend. For example, currently none of them support registering arbitrary attributes. This field is there for future use. +The comma can be escaped by adding a backslash, such as the following example: + + $ docker run -d --name redis.0 -p 10000:6379 \ + -e "SERVICE_NAME=db" \ + -e "SERVICE_TAGS=/(;\\,:-_)/" \ + -e "SERVICE_REGION=us2" progrium/redis + ### Multiple services with defaults $ docker run -d --name nginx.0 -p 4443:443 -p 8000:80 progrium/nginx