Skip to content

Commit 6d613c8

Browse files
authored
Merge pull request docker#9636 from ulyssessouza/dotenvfile-priority
Environment variables priority
2 parents 78ad525 + e9c8cfc commit 6d613c8

File tree

10 files changed

+150
-82
lines changed

10 files changed

+150
-82
lines changed

cmd/compose/compose.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -354,10 +354,8 @@ func setEnvWithDotEnv(prjOpts *projectOptions) error {
354354
return err
355355
}
356356
for k, v := range envFromFile {
357-
if _, ok := os.LookupEnv(k); !ok {
358-
if err := os.Setenv(k, v); err != nil {
359-
return err
360-
}
357+
if err := os.Setenv(k, v); err != nil { // overwrite the process env with merged OS + env file results
358+
return err
361359
}
362360
}
363361
return nil

go.mod

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,5 @@ replace (
151151
k8s.io/apimachinery => k8s.io/apimachinery v0.22.4
152152
k8s.io/client-go => k8s.io/client-go v0.22.4
153153
)
154+
155+
replace github.com/compose-spec/compose-go => github.com/compose-spec/compose-go v1.2.9-0.20220712021117-dcfe0889b601 // TODO Remove and bump require section when PR is merged

go.sum

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,8 @@ github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h
285285
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
286286
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
287287
github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
288-
github.com/compose-spec/compose-go v1.2.1/go.mod h1:pAy7Mikpeft4pxkFU565/DRHEbDfR84G6AQuiL+Hdg8=
289-
github.com/compose-spec/compose-go v1.2.9 h1:+7q2X8gCd16qUX+FU5EPtepK9lWZSGfc8Xe2cGmZqDc=
290-
github.com/compose-spec/compose-go v1.2.9/go.mod h1:rGaQw1U8uhZhmANQHIocMnQ+qu6ER2+1OwhWjRqQEPI=
288+
github.com/compose-spec/compose-go v1.2.9-0.20220712021117-dcfe0889b601 h1:dG3F1lPuUkmpxGs5qB3dFhqsXp06bpJHd0Btt6K15cU=
289+
github.com/compose-spec/compose-go v1.2.9-0.20220712021117-dcfe0889b601/go.mod h1:rGaQw1U8uhZhmANQHIocMnQ+qu6ER2+1OwhWjRqQEPI=
291290
github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
292291
github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
293292
github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@@ -1007,7 +1006,6 @@ github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:F
10071006
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
10081007
github.com/mitchellh/mapstructure v1.3.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
10091008
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
1010-
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
10111009
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
10121010
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
10131011
github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
@@ -2121,7 +2119,6 @@ gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo=
21212119
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
21222120
gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
21232121
gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
2124-
gotest.tools/v3 v3.1.0/go.mod h1:fHy7eyTmJFO5bQbUsEGQ1v4m2J3Jz9eWL54TP2/ZuYQ=
21252122
gotest.tools/v3 v3.3.0 h1:MfDY1b1/0xN1CyMlQDac0ziEy9zJQd9CXBRRDHw2jJo=
21262123
gotest.tools/v3 v3.3.0/go.mod h1:Mcr9QNxkg0uMvy/YElmo4SpXgJKWgQvYrT7Kw5RzJ1A=
21272124
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=

pkg/compose/run.go

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,15 +114,12 @@ func applyRunOptions(project *types.Project, service *types.ServiceConfig, opts
114114
service.Entrypoint = opts.Entrypoint
115115
}
116116
if len(opts.Environment) > 0 {
117-
env := types.NewMappingWithEquals(opts.Environment)
118-
projectEnv := env.Resolve(func(s string) (string, bool) {
119-
if _, ok := service.Environment[s]; ok {
120-
return "", false
121-
}
117+
cmdEnv := types.NewMappingWithEquals(opts.Environment)
118+
serviceOverrideEnv := cmdEnv.Resolve(func(s string) (string, bool) {
122119
v, ok := project.Environment[s]
123120
return v, ok
124121
}).RemoveEmpty()
125-
service.Environment.OverrideBy(projectEnv)
122+
service.Environment.OverrideBy(serviceOverrideEnv)
126123
}
127124
for k, v := range opts.Labels {
128125
service.Labels = service.Labels.Add(k, v)

pkg/e2e/compose_environment_test.go

Lines changed: 124 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -27,137 +27,196 @@ import (
2727
func TestEnvPriority(t *testing.T) {
2828
c := NewParallelCLI(t)
2929

30-
projectDir := "./fixtures/environment/env-priority"
31-
3230
t.Run("up", func(t *testing.T) {
3331
c.RunDockerOrExitError(t, "rmi", "env-compose-priority")
3432
c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
35-
"--project-directory", projectDir, "up", "-d", "--build")
33+
"up", "-d", "--build")
3634
})
3735

3836
// Full options activated
39-
// 1. Compose file <-- Result expected
40-
// 2. Shell environment variables
41-
// 3. Environment file
42-
// 4. Dockerfile
37+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file)
38+
// 2. Compose File (service::environment section)
39+
// 3. Compose File (service::env_file section file)
40+
// 4. Container Image ENV directive
4341
// 5. Variable is not defined
4442
t.Run("compose file priority", func(t *testing.T) {
4543
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
46-
"--project-directory", projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run",
47-
"--rm", "-e", "WHEREAMI", "env-compose-priority")
44+
"--env-file", "./fixtures/environment/env-priority/.env.override",
45+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
4846
cmd.Env = append(cmd.Env, "WHEREAMI=shell")
4947
res := icmd.RunCmd(cmd)
50-
assert.Equal(t, strings.TrimSpace(res.Stdout()), "Compose File")
48+
assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
49+
})
50+
51+
// Full options activated
52+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected
53+
// 2. Compose File (service::environment section)
54+
// 3. Compose File (service::env_file section file)
55+
// 4. Container Image ENV directive
56+
// 5. Variable is not defined
57+
t.Run("compose file priority", func(t *testing.T) {
58+
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env.yaml",
59+
"--env-file", "./fixtures/environment/env-priority/.env.override",
60+
"run", "--rm", "-e", "WHEREAMI=shell", "env-compose-priority")
61+
res := icmd.RunCmd(cmd)
62+
assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
5163
})
5264

5365
// No Compose file, all other options
54-
// 1. Compose file
55-
// 2. Shell environment variables <-- Result expected
56-
// 3. Environment file
57-
// 4. Dockerfile
66+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file)
67+
// 2. Compose File (service::environment section)
68+
// 3. Compose File (service::env_file section file)
69+
// 4. Container Image ENV directive
5870
// 5. Variable is not defined
5971
t.Run("shell priority", func(t *testing.T) {
60-
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
61-
projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
62-
"WHEREAMI", "env-compose-priority")
72+
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
73+
"--env-file", "./fixtures/environment/env-priority/.env.override",
74+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
75+
cmd.Env = append(cmd.Env, "WHEREAMI=shell")
76+
res := icmd.RunCmd(cmd)
77+
assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
78+
})
79+
80+
// No Compose file, all other options with env variable from OS environment
81+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment)
82+
// 2. Compose File (service::environment section)
83+
// 3. Compose File (service::env_file section file)
84+
// 4. Container Image ENV directive
85+
// 5. Variable is not defined
86+
t.Run("shell priority file with default value", func(t *testing.T) {
87+
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
88+
"--env-file", "./fixtures/environment/env-priority/.env.override.with.default",
89+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
6390
cmd.Env = append(cmd.Env, "WHEREAMI=shell")
6491
res := icmd.RunCmd(cmd)
6592
assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell")
6693
})
6794

68-
// No Compose file and env variable pass to the run command
69-
// 1. Compose file
70-
// 2. Shell environment variables <-- Result expected
71-
// 3. Environment file
72-
// 4. Dockerfile
95+
// No Compose file, all other options with env variable from OS environment
96+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment default value from file in --env-file)
97+
// 2. Compose File (service::environment section)
98+
// 3. Compose File (service::env_file section file)
99+
// 4. Container Image ENV directive
100+
// 5. Variable is not defined
101+
t.Run("shell priority implicitly set", func(t *testing.T) {
102+
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
103+
"--env-file", "./fixtures/environment/env-priority/.env.override.with.default",
104+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
105+
res := icmd.RunCmd(cmd)
106+
assert.Equal(t, strings.TrimSpace(res.Stdout()), "EnvFileDefaultValue")
107+
})
108+
109+
// No Compose file and env variable pass to the run command
110+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected
111+
// 2. Compose File (service::environment section)
112+
// 3. Compose File (service::env_file section file)
113+
// 4. Container Image ENV directive
73114
// 5. Variable is not defined
74115
t.Run("shell priority from run command", func(t *testing.T) {
75-
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
76-
projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
77-
"WHEREAMI=shell-run", "env-compose-priority")
116+
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
117+
"--env-file", "./fixtures/environment/env-priority/.env.override",
118+
"run", "--rm", "-e", "WHEREAMI=shell-run", "env-compose-priority")
78119
assert.Equal(t, strings.TrimSpace(res.Stdout()), "shell-run")
79120
})
80121

81-
// No Compose file & no env variable but override env file
82-
// 1. Compose file
83-
// 2. Shell environment variables
84-
// 3. Environment file <-- Result expected
85-
// 4. Dockerfile
122+
// No Compose file & no env variable but override env file
123+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by .env as a default --env-file value)
124+
// 2. Compose File (service::environment section)
125+
// 3. Compose File (service::env_file section file)
126+
// 4. Container Image ENV directive
127+
// 5. Variable is not defined
128+
t.Run("override env file from compose", func(t *testing.T) {
129+
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose-with-env-file.yaml",
130+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
131+
assert.Equal(t, strings.TrimSpace(res.Stdout()), "Env File")
132+
})
133+
134+
// No Compose file & no env variable but override by default env file
135+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file value)
136+
// 2. Compose File (service::environment section)
137+
// 3. Compose File (service::env_file section file)
138+
// 4. Container Image ENV directive
86139
// 5. Variable is not defined
87140
t.Run("override env file", func(t *testing.T) {
88-
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
89-
projectDir, "--env-file", "./fixtures/environment/env-priority/.env.override", "run", "--rm", "-e",
90-
"WHEREAMI", "env-compose-priority")
141+
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
142+
"--env-file", "./fixtures/environment/env-priority/.env.override",
143+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
91144
assert.Equal(t, strings.TrimSpace(res.Stdout()), "override")
92145
})
93146

94-
// No Compose file & no env variable but override env file
95-
// 1. Compose file
96-
// 2. Shell environment variables
97-
// 3. Environment file <-- Result expected
98-
// 4. Dockerfile
147+
// No Compose file & no env variable but override env file
148+
// 1. Command Line (docker compose run --env <KEY[=VAL]>) <-- Result expected (From environment patched by --env-file value)
149+
// 2. Compose File (service::environment section)
150+
// 3. Compose File (service::env_file section file)
151+
// 4. Container Image ENV directive
99152
// 5. Variable is not defined
100153
t.Run("env file", func(t *testing.T) {
101-
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
102-
projectDir, "run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
154+
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
155+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
103156
assert.Equal(t, strings.TrimSpace(res.Stdout()), "Env File")
104157
})
105158

106-
// No Compose file & no env variable, using an empty override env file
107-
// 1. Compose file
108-
// 2. Shell environment variables
109-
// 3. Environment file
110-
// 4. Dockerfile <-- Result expected
159+
// No Compose file & no env variable, using an empty override env file
160+
// 1. Command Line (docker compose run --env <KEY[=VAL]>)
161+
// 2. Compose File (service::environment section)
162+
// 3. Compose File (service::env_file section file)
163+
// 4. Container Image ENV directive <-- Result expected
111164
// 5. Variable is not defined
112165
t.Run("use Dockerfile", func(t *testing.T) {
113-
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml", "--project-directory",
114-
projectDir, "--env-file", "./fixtures/environment/env-priority/.env.empty", "run", "--rm", "-e", "WHEREAMI",
115-
"env-compose-priority")
166+
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-priority/compose.yaml",
167+
"--env-file", "./fixtures/environment/env-priority/.env.empty",
168+
"run", "--rm", "-e", "WHEREAMI", "env-compose-priority")
116169
assert.Equal(t, strings.TrimSpace(res.Stdout()), "Dockerfile")
117170
})
118171

119172
t.Run("down", func(t *testing.T) {
120-
c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down")
173+
c.RunDockerComposeCmd(t, "--project-name", "env-priority", "down")
121174
})
122175
}
123176

124177
func TestEnvInterpolation(t *testing.T) {
125178
c := NewParallelCLI(t)
126179

127-
projectDir := "./fixtures/environment/env-interpolation"
128-
129-
// No variable defined in the Compose file and env variable pass to the run command
130-
// 1. Compose file
131-
// 2. Shell environment variables <-- Result expected
132-
// 3. Environment file
133-
// 4. Dockerfile
180+
// No variable defined in the Compose file and nor env variable pass to the run command
181+
// 1. Command Line (docker compose run --env <KEY[=VAL]>)
182+
// 2. Compose File (service::environment section) <-- Result expected (From environment patched by .env as a default --env-file value)
183+
// 3. Compose File (service::env_file section file)
184+
// 4. Container Image ENV directive
134185
// 5. Variable is not defined
135186
t.Run("shell priority from run command", func(t *testing.T) {
136-
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation/compose.yaml",
137-
"--project-directory", projectDir, "config")
187+
cmd := c.NewDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation/compose.yaml", "config")
138188
cmd.Env = append(cmd.Env, "WHEREAMI=shell")
139189
res := icmd.RunCmd(cmd)
140-
res.Assert(t, icmd.Expected{Out: `IMAGE: default_env:shell`})
190+
res.Assert(t, icmd.Expected{Out: `IMAGE: default_env:EnvFile`})
191+
})
192+
193+
// No variable defined in the Compose file and env variable pass to the run command
194+
// 1. Command Line (docker compose run --env <KEY[=VAL]>)
195+
// 2. Compose File (service::environment section) <-- Result expected (From environment patched by .env as a default --env-file value.
196+
// This variable has a default value in case of an absent variable in the OS environment)
197+
// 3. Compose File (service::env_file section file)
198+
// 4. Container Image ENV directive
199+
// 5. Variable is not defined
200+
t.Run("shell priority from run command using default value fallback", func(t *testing.T) {
201+
c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-interpolation-default-value/compose.yaml", "config").
202+
Assert(t, icmd.Expected{Out: `IMAGE: default_env:EnvFileDefaultValue`})
141203
})
142204
}
143205

144206
func TestCommentsInEnvFile(t *testing.T) {
145207
c := NewParallelCLI(t)
146208

147-
projectDir := "./fixtures/environment/env-file-comments"
148-
149209
t.Run("comments in env files", func(t *testing.T) {
150210
c.RunDockerOrExitError(t, "rmi", "env-file-comments")
151211

152-
c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml", "--project-directory",
153-
projectDir, "up", "-d", "--build")
212+
c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml", "up", "-d", "--build")
154213

155214
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/environment/env-file-comments/compose.yaml",
156-
"--project-directory", projectDir, "run", "--rm", "-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")
215+
"run", "--rm", "-e", "COMMENT", "-e", "NO_COMMENT", "env-file-comments")
157216

158217
res.Assert(t, icmd.Expected{Out: `COMMENT=1234`})
159218
res.Assert(t, icmd.Expected{Out: `NO_COMMENT=1234#5`})
160219

161-
c.RunDockerComposeCmd(t, "--project-directory", projectDir, "down", "--rmi", "all")
220+
c.RunDockerComposeCmd(t, "--project-name", "env-file-comments", "down", "--rmi", "all")
162221
})
163222
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
IMAGE=default_env:${WHEREAMI:-EnvFileDefaultValue}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
services:
2+
env-interpolation:
3+
image: bash
4+
environment:
5+
IMAGE: ${IMAGE}
6+
command: echo "$IMAGE"
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
WHEREAMI=Env File
2-
IMAGE=default_env:${WHEREAMI}
1+
WHEREAMI=EnvFile
2+
IMAGE=default_env:${WHEREAMI}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
WHEREAMI=${WHEREAMI:-EnvFileDefaultValue}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
services:
2+
env-compose-priority:
3+
image: env-compose-priority
4+
build:
5+
context: .
6+
env_file:
7+
- .env.override

0 commit comments

Comments
 (0)