Skip to content

Commit bbaaa6a

Browse files
authored
config: case-insensitive env vars on Windows (docker#9438)
Signed-off-by: IKEDA Yasuyuki <[email protected]>
1 parent d0d06d4 commit bbaaa6a

File tree

4 files changed

+186
-9
lines changed

4 files changed

+186
-9
lines changed

pkg/compose/build.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
5050
opts := map[string]build.Options{}
5151
var imagesToBuild []string
5252

53-
args := flatten(options.Args.Resolve(func(s string) (string, bool) {
54-
s, ok := project.Environment[s]
55-
return s, ok
56-
}))
53+
args := flatten(options.Args.Resolve(envResolver(project.Environment)))
5754

5855
services, err := project.GetServices(options.Services...)
5956
if err != nil {
@@ -214,10 +211,7 @@ func (s *composeService) toBuildOptions(project *types.Project, service types.Se
214211
var tags []string
215212
tags = append(tags, imageTag)
216213

217-
buildArgs := flatten(service.Build.Args.Resolve(func(s string) (string, bool) {
218-
s, ok := project.Environment[s]
219-
return s, ok
220-
}))
214+
buildArgs := flatten(service.Build.Args.Resolve(envResolver(project.Environment)))
221215

222216
var plats []specs.Platform
223217
if platform, ok := project.Environment["DOCKER_DEFAULT_PLATFORM"]; ok {

pkg/compose/envresolver.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"runtime"
21+
"strings"
22+
)
23+
24+
var (
25+
// isCaseInsensitiveEnvVars is true on platforms where environment variable names are treated case-insensitively.
26+
isCaseInsensitiveEnvVars = (runtime.GOOS == "windows")
27+
)
28+
29+
// envResolver returns resolver for environment variables suitable for the current platform.
30+
// Expected to be used with `MappingWithEquals.Resolve`.
31+
// Updates in `environment` may not be reflected.
32+
func envResolver(environment map[string]string) func(string) (string, bool) {
33+
return envResolverWithCase(environment, isCaseInsensitiveEnvVars)
34+
}
35+
36+
// envResolverWithCase returns resolver for environment variables with the specified case-sensitive condition.
37+
// Expected to be used with `MappingWithEquals.Resolve`.
38+
// Updates in `environment` may not be reflected.
39+
func envResolverWithCase(environment map[string]string, caseInsensitive bool) func(string) (string, bool) {
40+
if environment == nil {
41+
return func(s string) (string, bool) {
42+
return "", false
43+
}
44+
}
45+
if !caseInsensitive {
46+
return func(s string) (string, bool) {
47+
v, ok := environment[s]
48+
return v, ok
49+
}
50+
}
51+
// variable names must be treated case-insensitively.
52+
// Resolves in this way:
53+
// * Return the value if its name matches with the passed name case-sensitively.
54+
// * Otherwise, return the value if its lower-cased name matches lower-cased passed name.
55+
// * The value is indefinite if multiple variable matches.
56+
loweredEnvironment := make(map[string]string, len(environment))
57+
for k, v := range environment {
58+
loweredEnvironment[strings.ToLower(k)] = v
59+
}
60+
return func(s string) (string, bool) {
61+
v, ok := environment[s]
62+
if ok {
63+
return v, ok
64+
}
65+
v, ok = loweredEnvironment[strings.ToLower(s)]
66+
return v, ok
67+
}
68+
}

pkg/compose/envresolver_test.go

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
/*
2+
Copyright 2020 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"testing"
21+
22+
"gotest.tools/assert"
23+
)
24+
25+
func Test_EnvResolverWithCase(t *testing.T) {
26+
tests := []struct {
27+
name string
28+
environment map[string]string
29+
caseInsensitive bool
30+
search string
31+
expectedValue string
32+
expectedOk bool
33+
}{
34+
{
35+
name: "case sensitive/case match",
36+
environment: map[string]string{
37+
"Env1": "Value1",
38+
"Env2": "Value2",
39+
},
40+
caseInsensitive: false,
41+
search: "Env1",
42+
expectedValue: "Value1",
43+
expectedOk: true,
44+
},
45+
{
46+
name: "case sensitive/case unmatch",
47+
environment: map[string]string{
48+
"Env1": "Value1",
49+
"Env2": "Value2",
50+
},
51+
caseInsensitive: false,
52+
search: "ENV1",
53+
expectedValue: "",
54+
expectedOk: false,
55+
},
56+
{
57+
name: "case sensitive/nil environment",
58+
environment: nil,
59+
caseInsensitive: false,
60+
search: "Env1",
61+
expectedValue: "",
62+
expectedOk: false,
63+
},
64+
{
65+
name: "case insensitive/case match",
66+
environment: map[string]string{
67+
"Env1": "Value1",
68+
"Env2": "Value2",
69+
},
70+
caseInsensitive: true,
71+
search: "Env1",
72+
expectedValue: "Value1",
73+
expectedOk: true,
74+
},
75+
{
76+
name: "case insensitive/case unmatch",
77+
environment: map[string]string{
78+
"Env1": "Value1",
79+
"Env2": "Value2",
80+
},
81+
caseInsensitive: true,
82+
search: "ENV1",
83+
expectedValue: "Value1",
84+
expectedOk: true,
85+
},
86+
{
87+
name: "case insensitive/unmatch",
88+
environment: map[string]string{
89+
"Env1": "Value1",
90+
"Env2": "Value2",
91+
},
92+
caseInsensitive: true,
93+
search: "Env3",
94+
expectedValue: "",
95+
expectedOk: false,
96+
},
97+
{
98+
name: "case insensitive/nil environment",
99+
environment: nil,
100+
caseInsensitive: true,
101+
search: "Env1",
102+
expectedValue: "",
103+
expectedOk: false,
104+
},
105+
}
106+
107+
for _, test := range tests {
108+
t.Run(test.name, func(t *testing.T) {
109+
f := envResolverWithCase(test.environment, test.caseInsensitive)
110+
v, ok := f(test.search)
111+
assert.Equal(t, v, test.expectedValue)
112+
assert.Equal(t, ok, test.expectedOk)
113+
})
114+
}
115+
}

pkg/compose/run.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts
116116
if len(opts.Environment) > 0 {
117117
cmdEnv := types.NewMappingWithEquals(opts.Environment)
118118
serviceOverrideEnv := cmdEnv.Resolve(func(s string) (string, bool) {
119-
v, ok := project.Environment[s]
119+
v, ok := envResolver(project.Environment)(s)
120120
return v, ok
121121
}).RemoveEmpty()
122122
service.Environment.OverrideBy(serviceOverrideEnv)

0 commit comments

Comments
 (0)