Skip to content

Commit c986d09

Browse files
committed
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 <[email protected]>
1 parent 79fa65e commit c986d09

File tree

2 files changed

+53
-12
lines changed

2 files changed

+53
-12
lines changed

cli/compose/loader/loader.go

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
328328
reflect.TypeOf(types.MappingWithEquals{}): transformMappingOrListFunc("=", true),
329329
reflect.TypeOf(types.Labels{}): transformMappingOrListFunc("=", false),
330330
reflect.TypeOf(types.MappingWithColon{}): transformMappingOrListFunc(":", false),
331-
reflect.TypeOf(types.HostsList{}): transformListOrMappingFunc(":", false),
331+
reflect.TypeOf(types.HostsList{}): transformHostsList,
332332
reflect.TypeOf(types.ServiceVolumeConfig{}): transformServiceVolumeConfig,
333333
reflect.TypeOf(types.BuildConfig{}): transformBuildConfig,
334334
reflect.TypeOf(types.Duration(0)): transformStringToDuration,
@@ -808,28 +808,58 @@ var transformStringList TransformerFunc = func(data any) (any, error) {
808808
}
809809
}
810810

811-
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
812-
return func(data any) (any, error) {
813-
return transformMappingOrList(data, sep, allowNil), nil
814-
}
815-
}
811+
var transformHostsList TransformerFunc = func(data any) (any, error) {
812+
hl := transformListOrMapping(data, ":", false, []string{"=", ":"})
816813

817-
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
818-
return func(data any) (any, error) {
819-
return transformListOrMapping(data, sep, allowNil), nil
814+
// Remove brackets from IP addresses if present (for example "[::1]" -> "::1").
815+
result := make([]string, 0, len(hl))
816+
for _, hip := range hl {
817+
host, ip, _ := strings.Cut(hip, ":")
818+
if len(ip) > 2 && ip[0] == '[' && ip[len(ip)-1] == ']' {
819+
ip = ip[1 : len(ip)-1]
820+
}
821+
result = append(result, fmt.Sprintf("%s:%s", host, ip))
820822
}
823+
return result, nil
821824
}
822825

823-
func transformListOrMapping(listOrMapping any, sep string, allowNil bool) any {
826+
// transformListOrMapping transforms pairs of strings that may be represented as
827+
// a map, or a list of '=' or ':' separated strings, into a list of ':' separated
828+
// strings.
829+
func transformListOrMapping(listOrMapping any, sep string, allowNil bool, allowSeps []string) []string {
824830
switch value := listOrMapping.(type) {
825831
case map[string]any:
826832
return toStringList(value, sep, allowNil)
827833
case []any:
828-
return listOrMapping
834+
result := make([]string, 0, len(value))
835+
for _, entry := range value {
836+
for i, allowSep := range allowSeps {
837+
entry := fmt.Sprint(entry)
838+
k, v, ok := strings.Cut(entry, allowSep)
839+
if ok {
840+
// Entry uses this allowed separator. Add it to the result, using
841+
// sep as a separator.
842+
result = append(result, fmt.Sprintf("%s%s%s", k, sep, v))
843+
break
844+
} else if i == len(allowSeps)-1 {
845+
// No more separators to try, keep the entry if allowNil.
846+
if allowNil {
847+
result = append(result, k)
848+
}
849+
}
850+
}
851+
}
852+
return result
829853
}
830854
panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping))
831855
}
832856

857+
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
858+
return func(data any) (any, error) {
859+
return transformMappingOrList(data, sep, allowNil), nil
860+
}
861+
}
862+
833863
func transformMappingOrList(mappingOrList any, sep string, allowNil bool) any {
834864
switch values := mappingOrList.(type) {
835865
case map[string]any:

cli/compose/loader/loader_test.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1302,12 +1302,14 @@ services:
13021302
extra_hosts:
13031303
"zulu": "162.242.195.82"
13041304
"alpha": "50.31.209.229"
1305+
"beta": "[fd20:f8a7:6e5b::2]"
13051306
"host.docker.internal": "host-gateway"
13061307
`)
13071308
assert.NilError(t, err)
13081309

13091310
expected := types.HostsList{
13101311
"alpha:50.31.209.229",
1312+
"beta:fd20:f8a7:6e5b::2",
13111313
"host.docker.internal:host-gateway",
13121314
"zulu:162.242.195.82",
13131315
}
@@ -1324,16 +1326,25 @@ services:
13241326
image: busybox
13251327
extra_hosts:
13261328
- "zulu:162.242.195.82"
1329+
- "whiskey=162.242.195.83"
13271330
- "alpha:50.31.209.229"
13281331
- "zulu:ff02::1"
1329-
- "host.docker.internal:host-gateway"
1332+
- "whiskey=ff02::2"
1333+
- "foxtrot=[ff02::3]"
1334+
- "bravo:[ff02::4]"
1335+
- "host.docker.internal=host-gateway"
1336+
- "noaddress"
13301337
`)
13311338
assert.NilError(t, err)
13321339

13331340
expected := types.HostsList{
13341341
"zulu:162.242.195.82",
1342+
"whiskey:162.242.195.83",
13351343
"alpha:50.31.209.229",
13361344
"zulu:ff02::1",
1345+
"whiskey:ff02::2",
1346+
"foxtrot:ff02::3",
1347+
"bravo:ff02::4",
13371348
"host.docker.internal:host-gateway",
13381349
}
13391350

0 commit comments

Comments
 (0)