Update github.com/hashicorp/go-discover to pull in new config parser
This patch updates the go-discover library to use the new config parser which uses a different encoding scheme for the go-discover config DSL. Values are no longer URL encoded but taken literally unless they contain spaces, backslashes or double quotes. To support keys or values with these special characters the string needs to be double quoted and usual escaping rules apply. Fixes #3417
This commit is contained in:
parent
64adc44583
commit
3d4fa7f65d
|
@ -5,16 +5,20 @@
|
|||
ip addresses of nodes in cloud environments based on meta information
|
||||
like tags provided by the environment.
|
||||
|
||||
The configuration for the providers is provided as a list of `key=val
|
||||
key=val ...` tuples where the values can be URL encoded. The provider is
|
||||
determined through the `provider` key. Effectively, only spaces have to
|
||||
be encoded with a `+` and on the command line you have to observe
|
||||
quoting rules with your shell.
|
||||
The configuration for the providers is provided as a list of `key=val key=val
|
||||
...` tuples. If either the key or the value contains a space (` `), a backslash
|
||||
(`\`) or double quotes (`"`) then it needs to be quoted with double quotes.
|
||||
Within a quoted string you can use the backslash to escape double quotes or the
|
||||
backslash itself, e.g. `key=val "some key"="some value"`
|
||||
|
||||
Duplicate keys are reported as error and the provider is determined through the
|
||||
`provider` key.
|
||||
|
||||
### Supported Providers
|
||||
|
||||
The following cloud providers have implementations in the go-discover/provider
|
||||
sub packages. Additional providers can be added through the [Register](https://godoc.org/github.com/hashicorp/go-discover#Register)
|
||||
sub packages. Additional providers can be added through the
|
||||
[Register](https://godoc.org/github.com/hashicorp/go-discover#Register)
|
||||
function.
|
||||
|
||||
* Amazon AWS [Config options](http://godoc.org/github.com/hashicorp/go-discover/provider/aws)
|
||||
|
|
|
@ -2,8 +2,8 @@ package discover
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net/url"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
@ -11,28 +11,12 @@ import (
|
|||
// functions to use.
|
||||
type Config map[string]string
|
||||
|
||||
// Parse parses a "key=val key=val ..." string into
|
||||
// a config map. Values are URL escaped.
|
||||
// Parse parses a "key=val key=val ..." string into a config map. Keys
|
||||
// and values which contain spaces, backslashes or double-quotes must be
|
||||
// quoted with double quotes. Use the backslash to escape special
|
||||
// characters within quoted strings, e.g. "some key"="some \"value\"".
|
||||
func Parse(s string) (Config, error) {
|
||||
s = strings.TrimSpace(s)
|
||||
if s == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
c := Config{}
|
||||
for _, v := range strings.Fields(s) {
|
||||
p := strings.SplitN(v, "=", 2)
|
||||
if len(p) != 2 {
|
||||
return nil, fmt.Errorf("invalid format: %s", v)
|
||||
}
|
||||
key := p[0]
|
||||
val, err := url.QueryUnescape(p[1])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid format: %s", v)
|
||||
}
|
||||
c[key] = val
|
||||
}
|
||||
return c, nil
|
||||
return parse(s)
|
||||
}
|
||||
|
||||
// String formats a config map into the "key=val key=val ..."
|
||||
|
@ -48,14 +32,195 @@ func (c Config) String() string {
|
|||
sort.Strings(keys)
|
||||
keys = append([]string{"provider"}, keys...)
|
||||
|
||||
quote := func(s string) string {
|
||||
if strings.ContainsAny(s, ` "\`) {
|
||||
return strconv.Quote(s)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
var vals []string
|
||||
for _, k := range keys {
|
||||
v := c[k]
|
||||
if v == "" {
|
||||
continue
|
||||
}
|
||||
v = k + "=" + url.QueryEscape(v)
|
||||
vals = append(vals, v)
|
||||
vals = append(vals, quote(k)+"="+quote(v))
|
||||
}
|
||||
return strings.Join(vals, " ")
|
||||
}
|
||||
|
||||
func parse(in string) (Config, error) {
|
||||
m := Config{}
|
||||
s := []rune(strings.TrimSpace(in))
|
||||
state := stateKey
|
||||
key := ""
|
||||
for {
|
||||
// exit condition
|
||||
if len(s) == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// get the next token
|
||||
item, val, n := lex(s)
|
||||
s = s[n:]
|
||||
// fmt.Printf("parse: state: %q item: %q val: '%s' n: %d rest: '%s'\n", state, item, val, n, string(s))
|
||||
|
||||
switch state {
|
||||
|
||||
case stateKey:
|
||||
switch item {
|
||||
case itemText:
|
||||
key = val
|
||||
if _, exists := m[key]; exists {
|
||||
return nil, fmt.Errorf("%s: duplicate key", key)
|
||||
}
|
||||
state = stateEqual
|
||||
default:
|
||||
return nil, fmt.Errorf("%s: %s", key, val)
|
||||
}
|
||||
|
||||
case stateEqual:
|
||||
switch item {
|
||||
case itemEqual:
|
||||
state = stateVal
|
||||
default:
|
||||
return nil, fmt.Errorf("%s: missing '='", key)
|
||||
}
|
||||
|
||||
case stateVal:
|
||||
switch item {
|
||||
case itemText:
|
||||
m[key] = val
|
||||
state = stateKey
|
||||
case itemError:
|
||||
return nil, fmt.Errorf("%s: %s", key, val)
|
||||
default:
|
||||
return nil, fmt.Errorf("%s: missing value", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//fmt.Printf("parse: state: %q rest: '%s'\n", state, string(s))
|
||||
switch state {
|
||||
case stateEqual:
|
||||
return nil, fmt.Errorf("%s: missing '='", key)
|
||||
case stateVal:
|
||||
return nil, fmt.Errorf("%s: missing value", key)
|
||||
}
|
||||
if len(m) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
type itemType string
|
||||
|
||||
const (
|
||||
itemText itemType = "TEXT"
|
||||
itemEqual = "EQUAL"
|
||||
itemError = "ERROR"
|
||||
)
|
||||
|
||||
func (t itemType) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
type state string
|
||||
|
||||
const (
|
||||
|
||||
// lexer states
|
||||
stateStart state = "start"
|
||||
stateEqual = "equal"
|
||||
stateText = "text"
|
||||
stateQText = "qtext"
|
||||
stateQTextEnd = "qtextend"
|
||||
stateQTextEsc = "qtextesc"
|
||||
|
||||
// parser states
|
||||
stateKey = "key"
|
||||
stateVal = "val"
|
||||
)
|
||||
|
||||
func lex(s []rune) (itemType, string, int) {
|
||||
isEqual := func(r rune) bool { return r == '=' }
|
||||
isEscape := func(r rune) bool { return r == '\\' }
|
||||
isQuote := func(r rune) bool { return r == '"' }
|
||||
isSpace := func(r rune) bool { return r == ' ' }
|
||||
|
||||
unquote := func(r []rune) (string, error) {
|
||||
v := strings.TrimSpace(string(r))
|
||||
return strconv.Unquote(v)
|
||||
}
|
||||
|
||||
var quote rune
|
||||
state := stateStart
|
||||
for i, r := range s {
|
||||
// fmt.Println("lex:", "i:", i, "r:", string(r), "state:", string(state), "head:", string(s[:i]), "tail:", string(s[i:]))
|
||||
switch state {
|
||||
case stateStart:
|
||||
switch {
|
||||
case isSpace(r):
|
||||
// state = stateStart
|
||||
case isEqual(r):
|
||||
state = stateEqual
|
||||
case isQuote(r):
|
||||
quote = r
|
||||
state = stateQText
|
||||
default:
|
||||
state = stateText
|
||||
}
|
||||
|
||||
case stateEqual:
|
||||
return itemEqual, "", i
|
||||
|
||||
case stateText:
|
||||
switch {
|
||||
case isEqual(r) || isSpace(r):
|
||||
v := strings.TrimSpace(string(s[:i]))
|
||||
return itemText, v, i
|
||||
default:
|
||||
// state = stateText
|
||||
}
|
||||
|
||||
case stateQText:
|
||||
switch {
|
||||
case r == quote:
|
||||
state = stateQTextEnd
|
||||
case isEscape(r):
|
||||
state = stateQTextEsc
|
||||
default:
|
||||
// state = stateQText
|
||||
}
|
||||
|
||||
case stateQTextEsc:
|
||||
state = stateQText
|
||||
|
||||
case stateQTextEnd:
|
||||
v, err := unquote(s[:i])
|
||||
if err != nil {
|
||||
return itemError, err.Error(), i
|
||||
}
|
||||
return itemText, v, i
|
||||
}
|
||||
}
|
||||
|
||||
// fmt.Println("lex:", "state:", string(state))
|
||||
switch state {
|
||||
case stateEqual:
|
||||
return itemEqual, "", len(s)
|
||||
case stateQText:
|
||||
return itemError, "unbalanced quotes", len(s)
|
||||
case stateQTextEsc:
|
||||
return itemError, "unterminated escape sequence", len(s)
|
||||
case stateQTextEnd:
|
||||
v, err := unquote(s)
|
||||
if err != nil {
|
||||
return itemError, err.Error(), len(s)
|
||||
}
|
||||
return itemText, v, len(s)
|
||||
default:
|
||||
return itemText, strings.TrimSpace(string(s)), len(s)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
{"path":"github.com/hashicorp/errwrap","checksumSHA1":"cdOCt0Yb+hdErz8NAQqayxPmRsY=","revision":"7554cd9344cec97297fa6649b055a8c98c2a1e55","revisionTime":"2014-10-28T05:47:10Z"},
|
||||
{"path":"github.com/hashicorp/go-checkpoint","checksumSHA1":"nd3S1qkFv7zZxA9be0bw4nT0pe0=","revision":"e4b2dc34c0f698ee04750bf2035d8b9384233e1b","revisionTime":"2015-10-22T18:15:14Z"},
|
||||
{"path":"github.com/hashicorp/go-cleanhttp","checksumSHA1":"b8F628srIitj5p7Y130xc9k0QWs=","revision":"3573b8b52aa7b37b9358d966a898feb387f62437","revisionTime":"2017-02-11T01:34:15Z"},
|
||||
{"path":"github.com/hashicorp/go-discover","checksumSHA1":"uNoQWG5h2hzWHjaLi376ZXVaCr4=","revision":"21b26b722865b64ae5809a532d2c18f3a3800129","revisionTime":"2017-08-16T16:53:52Z"},
|
||||
{"path":"github.com/hashicorp/go-discover","checksumSHA1":"tWQmb13hmUBoWi0ZHnc4CLBqRc0=","revision":"745a463b035b25a4d0c12d056e208e3850fdd1be","revisionTime":"2017-10-04T16:46:45Z"},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/aws","checksumSHA1":"lyPRg8aZKgGiNkMILk/VKwOqMy4=","revision":"25e4565347de14cea0a0e0730374c9fcffa7bab0","revisionTime":"2017-09-25T01:06:15Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/azure","checksumSHA1":"r97P32e+VmNMh2vwLkZa1zPEDQU=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
{"path":"github.com/hashicorp/go-discover/provider/gce","checksumSHA1":"KC/MepQsQF17904UShiM61jmaEs=","revision":"b518491d039b6782035b8881502b4f5e9fcc887b","revisionTime":"2017-08-01T15:32:04Z","tree":true},
|
||||
|
|
Loading…
Reference in New Issue