Skip to content

Commit d4fbd6f

Browse files
committed
add mapping for ssh property in build section
Signed-off-by: Guillaume Lours <[email protected]>
1 parent 98c14dd commit d4fbd6f

File tree

6 files changed

+121
-20
lines changed

6 files changed

+121
-20
lines changed

loader/full-example.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ services:
77
dockerfile: Dockerfile
88
args:
99
foo: bar
10+
ssh: default
1011
target: foo
1112
network: foo
1213
cache_from:

loader/full-struct_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ func services(workingDir, homeDir string) []types.ServiceConfig {
5353
Context: "./dir",
5454
Dockerfile: "Dockerfile",
5555
Args: map[string]*string{"foo": strPtr("bar")},
56+
SSH: map[string]string{"default": ""},
5657
Target: "foo",
5758
Network: "foo",
5859
CacheFrom: []string{"foo", "bar"},
@@ -569,6 +570,8 @@ services:
569570
dockerfile: Dockerfile
570571
args:
571572
foo: bar
573+
ssh:
574+
default: ""
572575
labels:
573576
FOO: BAR
574577
cache_from:
@@ -1082,6 +1085,9 @@ func fullExampleJSON(workingDir, homeDir string) string {
10821085
"args": {
10831086
"foo": "bar"
10841087
},
1088+
"ssh": {
1089+
"default": ""
1090+
},
10851091
"labels": {
10861092
"FOO": "BAR"
10871093
},

loader/loader.go

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ func createTransformHook(additionalTransformers ...Transformer) mapstructure.Dec
390390
reflect.TypeOf(types.DependsOnConfig{}): transformDependsOnConfig,
391391
reflect.TypeOf(types.ExtendsConfig{}): transformExtendsConfig,
392392
reflect.TypeOf(types.DeviceRequest{}): transformServiceDeviceRequest,
393+
reflect.TypeOf(types.SSHConfig{}): transformSSHConfig,
393394
}
394395

395396
for _, transformer := range additionalTransformers {
@@ -978,6 +979,24 @@ var transformServiceNetworkMap TransformerFunc = func(value interface{}) (interf
978979
return value, nil
979980
}
980981

982+
var transformSSHConfig TransformerFunc = func(data interface{}) (interface{}, error) {
983+
switch value := data.(type) {
984+
case map[string]interface{}:
985+
return toMapStringString(value, true), nil
986+
case []interface{}:
987+
return transformMappingOrList(value, "=", true)
988+
case string:
989+
result := make(map[string]interface{})
990+
if value == "" {
991+
value = "default"
992+
}
993+
key, val := transformValueToMapEntry(value, "=", true)
994+
result[key] = val
995+
return result, nil
996+
}
997+
return nil, errors.Errorf("expected a sting, map or a list, got %T: %#v", data, data)
998+
}
999+
9811000
var transformStringOrNumberList TransformerFunc = func(value interface{}) (interface{}, error) {
9821001
list := value.([]interface{})
9831002
result := make([]string, len(list))
@@ -1000,47 +1019,52 @@ var transformStringList TransformerFunc = func(data interface{}) (interface{}, e
10001019

10011020
func transformMappingOrListFunc(sep string, allowNil bool) TransformerFunc {
10021021
return func(data interface{}) (interface{}, error) {
1003-
return transformMappingOrList(data, sep, allowNil), nil
1022+
return transformMappingOrList(data, sep, allowNil)
10041023
}
10051024
}
10061025

10071026
func transformListOrMappingFunc(sep string, allowNil bool) TransformerFunc {
10081027
return func(data interface{}) (interface{}, error) {
1009-
return transformListOrMapping(data, sep, allowNil), nil
1028+
return transformListOrMapping(data, sep, allowNil)
10101029
}
10111030
}
10121031

1013-
func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) interface{} {
1032+
func transformListOrMapping(listOrMapping interface{}, sep string, allowNil bool) (interface{}, error) {
10141033
switch value := listOrMapping.(type) {
10151034
case map[string]interface{}:
1016-
return toStringList(value, sep, allowNil)
1035+
return toStringList(value, sep, allowNil), nil
10171036
case []interface{}:
1018-
return listOrMapping
1037+
return listOrMapping, nil
10191038
}
1020-
panic(errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping))
1039+
return nil, errors.Errorf("expected a map or a list, got %T: %#v", listOrMapping, listOrMapping)
10211040
}
10221041

1023-
func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) interface{} {
1042+
func transformMappingOrList(mappingOrList interface{}, sep string, allowNil bool) (interface{}, error) {
10241043
switch value := mappingOrList.(type) {
10251044
case map[string]interface{}:
1026-
return toMapStringString(value, allowNil)
1045+
return toMapStringString(value, allowNil), nil
10271046
case []interface{}:
10281047
result := make(map[string]interface{})
10291048
for _, value := range value {
1030-
parts := strings.SplitN(value.(string), sep, 2)
1031-
key := parts[0]
1032-
switch {
1033-
case len(parts) == 1 && allowNil:
1034-
result[key] = nil
1035-
case len(parts) == 1 && !allowNil:
1036-
result[key] = ""
1037-
default:
1038-
result[key] = parts[1]
1039-
}
1049+
key, val := transformValueToMapEntry(value.(string), sep, allowNil)
1050+
result[key] = val
10401051
}
1041-
return result
1052+
return result, nil
1053+
}
1054+
return nil, errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList)
1055+
}
1056+
1057+
func transformValueToMapEntry(value string, separator string, allowNil bool) (string, interface{}) {
1058+
parts := strings.SplitN(value, separator, 2)
1059+
key := parts[0]
1060+
switch {
1061+
case len(parts) == 1 && allowNil:
1062+
return key, nil
1063+
case len(parts) == 1 && !allowNil:
1064+
return key, ""
1065+
default:
1066+
return key, parts[1]
10421067
}
1043-
panic(errors.Errorf("expected a map or a list, got %T: %#v", mappingOrList, mappingOrList))
10441068
}
10451069

10461070
var transformShellCommand TransformerFunc = func(value interface{}) (interface{}, error) {

loader/loader_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1867,3 +1867,59 @@ func TestLoadServiceWithVolumes(t *testing.T) {
18671867
assert.Equal(t, len(s.Secrets), 1)
18681868
assert.Equal(t, "/path 4", s.Secrets[0].Target)
18691869
}
1870+
1871+
func TestLoadNoSSHInBuildConfig(t *testing.T) {
1872+
actual, err := loadYAML(`
1873+
services:
1874+
test:
1875+
build:
1876+
context: .
1877+
`)
1878+
assert.NilError(t, err)
1879+
svc, err := actual.GetService("test")
1880+
assert.NilError(t, err)
1881+
assert.Check(t, nil == svc.Build.SSH)
1882+
}
1883+
1884+
func TestLoadSSHWithoutValueInBuildConfig(t *testing.T) {
1885+
_, err := loadYAML(`
1886+
services:
1887+
test:
1888+
build:
1889+
context: .
1890+
ssh:
1891+
`)
1892+
assert.ErrorContains(t, err, "services.test.build.ssh must be a mapping")
1893+
}
1894+
1895+
func TestLoadSSHWithDefaultValueInBuildConfig(t *testing.T) {
1896+
actual, err := loadYAML(`
1897+
services:
1898+
test:
1899+
build:
1900+
context: .
1901+
ssh: ssh-agent
1902+
`)
1903+
assert.NilError(t, err)
1904+
svc, err := actual.GetService("test")
1905+
assert.NilError(t, err)
1906+
assert.Assert(t, is.Contains(svc.Build.SSH, "ssh-agent"))
1907+
assert.Equal(t, "", svc.Build.SSH["ssh-agent"])
1908+
}
1909+
1910+
func TestLoadSSHWithKeysValuesInBuildConfig(t *testing.T) {
1911+
actual, err := loadYAML(`
1912+
services:
1913+
test:
1914+
build:
1915+
context: .
1916+
ssh:
1917+
- key1=value1
1918+
- key2=value2
1919+
`)
1920+
assert.NilError(t, err)
1921+
svc, err := actual.GetService("test")
1922+
assert.NilError(t, err)
1923+
assert.Equal(t, "value1", svc.Build.SSH["key1"])
1924+
assert.Equal(t, "value2", svc.Build.SSH["key2"])
1925+
}

schema/compose-spec.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,16 @@
9191
"context": {"type": "string"},
9292
"dockerfile": {"type": "string"},
9393
"args": {"$ref": "#/definitions/list_or_dict"},
94+
"ssh": {
95+
"oneOf": [
96+
{
97+
"type": "string"
98+
},
99+
{
100+
"$ref": "#/definitions/list_or_dict"
101+
}
102+
]
103+
},
94104
"labels": {"$ref": "#/definitions/list_or_dict"},
95105
"cache_from": {"type": "array", "items": {"type": "string"}},
96106
"cache_to": {"type": "array", "items": {"type": "string"}},

types/types.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ type BuildConfig struct {
294294
Context string `yaml:",omitempty" json:"context,omitempty"`
295295
Dockerfile string `yaml:",omitempty" json:"dockerfile,omitempty"`
296296
Args MappingWithEquals `yaml:",omitempty" json:"args,omitempty"`
297+
SSH SSHConfig `yaml:"ssh,omitempty" json:"ssh,omitempty"`
297298
Labels Labels `yaml:",omitempty" json:"labels,omitempty"`
298299
CacheFrom StringList `mapstructure:"cache_from" yaml:"cache_from,omitempty" json:"cache_from,omitempty"`
299300
CacheTo StringList `mapstructure:"cache_to" yaml:"cache_to,omitempty" json:"cache_to,omitempty"`
@@ -426,6 +427,9 @@ func (l Labels) Add(key, value string) Labels {
426427
return l
427428
}
428429

430+
// SSHConfig is a mapping type for SSH build config
431+
type SSHConfig map[string]string
432+
429433
// MappingWithColon is a mapping type that can be converted from a list of
430434
// 'key: value' strings
431435
type MappingWithColon map[string]string

0 commit comments

Comments
 (0)