mirror of https://github.com/docker/cli.git
docker stack: allow '=' separator in extra_hosts
extra_hosts in the compose file format allows '=' as a separator, and brackets
around IP addresses, the engine API doesn't.
So, transform the values when reading a compose file for 'docker stack'.
Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit c986d09bca
)
Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
parent
a5937c6043
commit
1cddb2b03d
|
@ -328,7 +328,7 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
|
||||||
reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true),
|
reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true),
|
||||||
reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false),
|
reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false),
|
||||||
reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false),
|
reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false),
|
||||||
reflect.TypeOf(types.HostsList{}): transformListOrMappingFunc(":", false),
|
reflect.TypeOf(types.HostsList{}): transformHostsList,
|
||||||
reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig,
|
reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig,
|
||||||
reflect.TypeOf(types.BuildConfig{}): transformBuildConfig,
|
reflect.TypeOf(types.BuildConfig{}): transformBuildConfig,
|
||||||
reflect.TypeOf(types.Duration(0)): transformStringToDuration,
|
reflect.TypeOf(types.Duration(0)): transformStringToDuration,
|
||||||
|
@ -808,28 +808,58 @@ var transformStringList TransformerFunc = func(data any) (any, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
|
var transformHostsList TransformerFunc = func(data any) (any, error) {
|
||||||
return func(data any) (any, error) {
|
hl := transformListOrMapping(data, ":", false, []string{"=", ":"})
|
||||||
return transformMappingOrList(data, sep, allowNil), nil
|
|
||||||
|
// Remove brackets from IP addresses if present (for example "[::1]" -> "::1").
|
||||||
|
result := make([]string, 0, len(hl))
|
||||||
|
for _, hip := range hl {
|
||||||
|
host, ip, _ := strings.Cut(hip, ":")
|
||||||
|
if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
|
||||||
|
ip = ip[1 : len(ip)-1]
|
||||||
|
}
|
||||||
|
result = append(result, fmt.Sprintf("%s:%s", host, ip))
|
||||||
}
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
|
// transformListOrMapping transforms pairs of strings that may be represented as
|
||||||
return func(data any) (any, error) {
|
// a map, or a list of '=' or ':' separated strings, into a list of ':' separated
|
||||||
return transformListOrMapping(data, sep, allowNil), nil
|
// strings.
|
||||||
}
|
func transformListOrMapping(listOrMapping any, sep string, allowNil bool, allowSeps []string) []string {
|
||||||
}
|
|
||||||
|
|
||||||
func transformListOrMapping(listOrMapping any, sep string, allowNil bool) any {
|
|
||||||
switch value := listOrMapping.(type) {
|
switch value := listOrMapping.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
return toStringList(value, sep, allowNil)
|
return toStringList(value, sep, allowNil)
|
||||||
case []any:
|
case []any:
|
||||||
return listOrMapping
|
result := make([]string, 0, len(value))
|
||||||
|
for _, entry := range value {
|
||||||
|
for i, allowSep := range allowSeps {
|
||||||
|
entry := fmt.Sprint(entry)
|
||||||
|
k, v, ok := strings.Cut(entry, allowSep)
|
||||||
|
if ok {
|
||||||
|
// Entry uses this allowed separator. Add it to the result, using
|
||||||
|
// sep as a separator.
|
||||||
|
result = append(result, fmt.Sprintf("%s%s%s", k, sep, v))
|
||||||
|
break
|
||||||
|
} else if i == len(allowSeps)-1 {
|
||||||
|
// No more separators to try, keep the entry if allowNil.
|
||||||
|
if allowNil {
|
||||||
|
result = append(result, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping))
|
panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
|
||||||
|
return func(data any) (any, error) {
|
||||||
|
return transformMappingOrList(data, sep, allowNil), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func transformMappingOrList(mappingOrList any, sep string, allowNil bool) any {
|
func transformMappingOrList(mappingOrList any, sep string, allowNil bool) any {
|
||||||
switch values := mappingOrList.(type) {
|
switch values := mappingOrList.(type) {
|
||||||
case map[string]any:
|
case map[string]any:
|
||||||
|
|
|
@ -1302,12 +1302,14 @@ services:
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
"zulu": "162.242.195.82"
|
"zulu": "162.242.195.82"
|
||||||
"alpha": "50.31.209.229"
|
"alpha": "50.31.209.229"
|
||||||
|
"beta": "[fd20:f8a7:6e5b::2]"
|
||||||
"host.docker.internal": "host-gateway"
|
"host.docker.internal": "host-gateway"
|
||||||
`)
|
`)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expected := types.HostsList{
|
expected := types.HostsList{
|
||||||
"alpha:50.31.209.229",
|
"alpha:50.31.209.229",
|
||||||
|
"beta:fd20:f8a7:6e5b::2",
|
||||||
"host.docker.internal:host-gateway",
|
"host.docker.internal:host-gateway",
|
||||||
"zulu:162.242.195.82",
|
"zulu:162.242.195.82",
|
||||||
}
|
}
|
||||||
|
@ -1324,16 +1326,25 @@ services:
|
||||||
image: busybox
|
image: busybox
|
||||||
extra_hosts:
|
extra_hosts:
|
||||||
- "zulu:162.242.195.82"
|
- "zulu:162.242.195.82"
|
||||||
|
- "whiskey=162.242.195.83"
|
||||||
- "alpha:50.31.209.229"
|
- "alpha:50.31.209.229"
|
||||||
- "zulu:ff02::1"
|
- "zulu:ff02::1"
|
||||||
- "host.docker.internal:host-gateway"
|
- "whiskey=ff02::2"
|
||||||
|
- "foxtrot=[ff02::3]"
|
||||||
|
- "bravo:[ff02::4]"
|
||||||
|
- "host.docker.internal=host-gateway"
|
||||||
|
- "noaddress"
|
||||||
`)
|
`)
|
||||||
assert.NilError(t, err)
|
assert.NilError(t, err)
|
||||||
|
|
||||||
expected := types.HostsList{
|
expected := types.HostsList{
|
||||||
"zulu:162.242.195.82",
|
"zulu:162.242.195.82",
|
||||||
|
"whiskey:162.242.195.83",
|
||||||
"alpha:50.31.209.229",
|
"alpha:50.31.209.229",
|
||||||
"zulu:ff02::1",
|
"zulu:ff02::1",
|
||||||
|
"whiskey:ff02::2",
|
||||||
|
"foxtrot:ff02::3",
|
||||||
|
"bravo:ff02::4",
|
||||||
"host.docker.internal:host-gateway",
|
"host.docker.internal:host-gateway",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue