Skip to content
This repository was archived by the owner on Jan 25, 2022. It is now read-only.

Commit 7e24eee

Browse files
glynChris Brown
authored and
Chris Brown
committed
ContainerSpec environment takes precedence over rootfs environment
Introduce process.Env environment type. [#86540096] Signed-off-by: Chris Brown <[email protected]>
1 parent 5f8f094 commit 7e24eee

File tree

9 files changed

+344
-41
lines changed

9 files changed

+344
-41
lines changed

integration/lifecycle/lifecycle_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,46 @@ var _ = Describe("Creating a container", func() {
133133
Ω(stdout).Should(gbytes.Say("foo"))
134134
})
135135
})
136+
137+
FContext("when the docker image specifies $HOME and $PATH", func() {
138+
BeforeEach(func() {
139+
// dockerfile contains `ENV /usr/local/bin:/usr/bin:/bin:/from-ENV`,
140+
// see diego-dockerfiles/with-volume
141+
rootfs = "docker:///cloudfoundry/with-volume"
142+
})
143+
144+
It("$HOME is taken from the docker image", func() {
145+
stdout := gbytes.NewBuffer()
146+
process, err := container.Run(garden.ProcessSpec{
147+
Path: "/bin/sh",
148+
Args: []string{"-c", "echo $HOME"},
149+
}, garden.ProcessIO{
150+
Stdout: io.MultiWriter(GinkgoWriter, stdout),
151+
Stderr: GinkgoWriter,
152+
})
153+
154+
Ω(err).ShouldNot(HaveOccurred())
155+
156+
process.Wait()
157+
Ω(stdout).Should(gbytes.Say("/home-from-ENV"))
158+
})
159+
160+
PIt("$PATH is taken from the docker image", func() {
161+
stdout := gbytes.NewBuffer()
162+
process, err := container.Run(garden.ProcessSpec{
163+
Path: "/bin/sh",
164+
Args: []string{"-c", "echo $PATH"},
165+
}, garden.ProcessIO{
166+
Stdout: io.MultiWriter(GinkgoWriter, stdout),
167+
Stderr: GinkgoWriter,
168+
})
169+
170+
Ω(err).ShouldNot(HaveOccurred())
171+
172+
process.Wait()
173+
Ω(stdout).Should(gbytes.Say("/usr/local/bin:/usr/bin:/bin:/from-ENV"))
174+
})
175+
})
136176
})
137177

138178
Context("and running a process", func() {

old/linux_backend/container_pool/container_pool.go

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import (
3030
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/uid_pool"
3131
"github.com/cloudfoundry-incubator/garden-linux/old/logging"
3232
"github.com/cloudfoundry-incubator/garden-linux/old/sysconfig"
33+
"github.com/cloudfoundry-incubator/garden-linux/process"
3334
)
3435

3536
var ErrUnknownRootFSProvider = errors.New("unknown rootfs provider")
@@ -256,6 +257,16 @@ func (p *LinuxContainerPool) Create(spec garden.ContainerSpec) (c linux_backend.
256257

257258
pLog.Info("created")
258259

260+
ctrEnv, err := process.NewEnv(rootFSEnvVars)
261+
if err != nil {
262+
return nil, err
263+
}
264+
265+
specEnv, err := process.NewEnv(spec.Env)
266+
if err != nil {
267+
return nil, err
268+
}
269+
259270
return linux_backend.NewLinuxContainer(
260271
pLog,
261272
id,
@@ -270,7 +281,7 @@ func (p *LinuxContainerPool) Create(spec garden.ContainerSpec) (c linux_backend.
270281
p.quotaManager,
271282
bandwidth_manager.New(containerPath, id, p.runner),
272283
process_tracker.New(containerPath, p.runner),
273-
mergeEnv(spec.Env, rootFSEnvVars),
284+
ctrEnv.Merge(specEnv),
274285
p.filterProvider.ProvideFilter(id),
275286
), nil
276287
}
@@ -333,6 +344,11 @@ func (p *LinuxContainerPool) Restore(snapshot io.Reader) (linux_backend.Containe
333344

334345
containerLogger := p.logger.Session(id)
335346

347+
containerEnv, err := process.NewEnv(containerSnapshot.EnvVars)
348+
if err != nil {
349+
return nil, err
350+
}
351+
336352
container := linux_backend.NewLinuxContainer(
337353
containerLogger,
338354
id,
@@ -352,7 +368,7 @@ func (p *LinuxContainerPool) Restore(snapshot io.Reader) (linux_backend.Containe
352368
p.quotaManager,
353369
bandwidthManager,
354370
process_tracker.New(containerPath, p.runner),
355-
containerSnapshot.EnvVars,
371+
containerEnv,
356372
p.filterProvider.ProvideFilter(id),
357373
)
358374

@@ -635,13 +651,6 @@ func getHandle(handle, id string) string {
635651
return id
636652
}
637653

638-
func mergeEnv(env1, env2 []string) []string {
639-
for _, entry := range env2 {
640-
env1 = append(env1, entry)
641-
}
642-
return env1
643-
}
644-
645654
func cleanup(err *error, undo func()) {
646655
if *err != nil {
647656
undo()

old/linux_backend/container_pool/container_pool_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -496,11 +496,10 @@ var _ = Describe("Container pool", func() {
496496
})
497497

498498
Ω(err).ShouldNot(HaveOccurred())
499-
Ω(container.(*linux_backend.LinuxContainer).CurrentEnvVars()).Should(Equal([]string{
499+
Ω(container.(*linux_backend.LinuxContainer).CurrentEnvVars()).Should(ConsistOf([]string{
500+
"var3=rootfs-value-3",
500501
"var1=spec-value1",
501502
"var2=spec-value2",
502-
"var2=rootfs-value-2",
503-
"var3=rootfs-value-3",
504503
}))
505504
})
506505

old/linux_backend/linux_container.go

Lines changed: 21 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/process_tracker"
2222
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/quota_manager"
2323
"github.com/cloudfoundry-incubator/garden-linux/old/logging"
24+
"github.com/cloudfoundry-incubator/garden-linux/process"
2425
"github.com/cloudfoundry/gunk/command_runner"
2526
"github.com/pivotal-golang/lager"
2627
)
@@ -88,7 +89,8 @@ type LinuxContainer struct {
8889

8990
mtu uint32
9091

91-
envvars []string
92+
env process.Env
93+
9294
processIDPool *ProcessIDPool
9395
}
9496

@@ -151,7 +153,7 @@ func NewLinuxContainer(
151153
quotaManager quota_manager.QuotaManager,
152154
bandwidthManager bandwidth_manager.BandwidthManager,
153155
processTracker process_tracker.ProcessTracker,
154-
envvars []string,
156+
env process.Env,
155157
filter network.Filter,
156158
) *LinuxContainer {
157159
return &LinuxContainer{
@@ -182,7 +184,7 @@ func NewLinuxContainer(
182184

183185
filter: filter,
184186

185-
envvars: envvars,
187+
env: env,
186188
processIDPool: &ProcessIDPool{},
187189
}
188190
}
@@ -288,7 +290,7 @@ func (c *LinuxContainer) Snapshot(out io.Writer) error {
288290

289291
Properties: c.Properties(),
290292

291-
EnvVars: c.envvars,
293+
EnvVars: c.env.Array(),
292294
}
293295

294296
var err error
@@ -331,7 +333,14 @@ func (c *LinuxContainer) Restore(snapshot ContainerSnapshot) error {
331333

332334
c.setState(State(snapshot.State))
333335

334-
c.envvars = snapshot.EnvVars
336+
snapshotEnv, err := process.NewEnv(snapshot.EnvVars)
337+
if err != nil {
338+
cLog.Error("restoring-env", err, lager.Data{
339+
"env": snapshot.EnvVars,
340+
})
341+
return err
342+
}
343+
c.env = snapshotEnv
335344

336345
for _, ev := range snapshot.Events {
337346
c.registerEvent(ev)
@@ -365,7 +374,7 @@ func (c *LinuxContainer) Restore(snapshot ContainerSnapshot) error {
365374

366375
net := exec.Command(path.Join(c.path, "net.sh"), "setup")
367376

368-
err := cRunner.Run(net)
377+
err = cRunner.Run(net)
369378
if err != nil {
370379
cLog.Error("failed-to-reenforce-network-rules", err)
371380
return err
@@ -768,11 +777,12 @@ func (c *LinuxContainer) Run(spec garden.ProcessSpec, processIO garden.ProcessIO
768777

769778
args := []string{"--socket", sockPath, "--user", user}
770779

771-
envVars := []string{}
772-
envVars = append(append(envVars, c.envvars...), spec.Env...)
773-
envVars = c.dedup(envVars)
780+
specEnv, err := process.NewEnv(spec.Env)
781+
if err != nil {
782+
return nil, err
783+
}
774784

775-
for _, envVar := range envVars {
785+
for _, envVar := range c.env.Merge(specEnv).Array() {
776786
args = append(args, "--env", envVar)
777787
}
778788

@@ -855,7 +865,7 @@ func (c *LinuxContainer) NetOut(network string, port uint32, portRange string, p
855865
}
856866

857867
func (c *LinuxContainer) CurrentEnvVars() []string {
858-
return c.envvars
868+
return c.env.Array()
859869
}
860870

861871
func (c *LinuxContainer) setState(state State) {
@@ -1089,18 +1099,3 @@ func setRLimitsEnv(cmd *exec.Cmd, rlimits garden.ResourceLimits) {
10891099
cmd.Env = append(cmd.Env, fmt.Sprintf("RLIMIT_STACK=%d", *rlimits.Stack))
10901100
}
10911101
}
1092-
1093-
func (c *LinuxContainer) dedup(envVars []string) []string {
1094-
seenArgs := map[string]string{}
1095-
result := []string{}
1096-
for i := len(envVars) - 1; i >= 0; i-- {
1097-
envVar := envVars[i]
1098-
keyValue := strings.SplitN(envVar, "=", 2)
1099-
_, containsKey := seenArgs[keyValue[0]]
1100-
if len(keyValue) == 2 && !containsKey {
1101-
result = append([]string{envVar}, result...)
1102-
seenArgs[keyValue[0]] = envVar
1103-
}
1104-
}
1105-
return (result)
1106-
}

old/linux_backend/linux_container_test.go

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/process_tracker"
2929
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/process_tracker/fake_process_tracker"
3030
"github.com/cloudfoundry-incubator/garden-linux/old/linux_backend/quota_manager/fake_quota_manager"
31+
"github.com/cloudfoundry-incubator/garden-linux/process"
3132
wfakes "github.com/cloudfoundry-incubator/garden/fakes"
3233
"github.com/cloudfoundry/gunk/command_runner/fake_command_runner"
3334
. "github.com/cloudfoundry/gunk/command_runner/fake_command_runner/matchers"
@@ -94,7 +95,7 @@ var _ = Describe("Linux containers", func() {
9495
fakeQuotaManager,
9596
fakeBandwidthManager,
9697
fakeProcessTracker,
97-
[]string{"env1=env1Value", "env2=env2Value"},
98+
process.Env{"env1": "env1Value", "env2": "env2Value"},
9899
fakeFilter,
99100
)
100101
})
@@ -1019,10 +1020,30 @@ var _ = Describe("Linux containers", func() {
10191020
containerDir + "/bin/wsh",
10201021
"--socket", containerDir + "/run/wshd.sock",
10211022
"--user", "vcap",
1022-
"--env", "env1=env1Value",
1023-
"--env", "env2=env2Value",
10241023
"--env", `ESCAPED=kurt "russell"`,
10251024
"--env", "UNESCAPED=isaac\nhayes",
1025+
"--env", "env1=env1Value",
1026+
"--env", "env2=env2Value",
1027+
"--pidfile", containerDir + "/processes/1.pid",
1028+
"/some/script",
1029+
}))
1030+
})
1031+
1032+
It("runs the script with the environment variables from the run taking precedence over the container environment variables", func() {
1033+
_, err := container.Run(garden.ProcessSpec{
1034+
Path: "/some/script",
1035+
Env: []string{"env1=overridden"},
1036+
}, garden.ProcessIO{})
1037+
1038+
Ω(err).ShouldNot(HaveOccurred())
1039+
1040+
_, ranCmd, _, _, _ := fakeProcessTracker.RunArgsForCall(0)
1041+
Ω(ranCmd.Args).Should(Equal([]string{
1042+
containerDir + "/bin/wsh",
1043+
"--socket", containerDir + "/run/wshd.sock",
1044+
"--user", "vcap",
1045+
"--env", "env1=overridden",
1046+
"--env", "env2=env2Value",
10261047
"--pidfile", containerDir + "/processes/1.pid",
10271048
"/some/script",
10281049
}))

old/linux_backend/src/wsh/wshd.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,29 @@ char **env__add(char **envp, const char *key, const char *value) {
246246
return envp;
247247
}
248248

249+
int env__has(char **envp, const char* key) {
250+
size_t envplen = 0;
251+
252+
if (envp != NULL) {
253+
while(envp[envplen++] != NULL);
254+
255+
int i;
256+
for (i = 0; i < envplen; i++) {
257+
char* eq = strchr(envp[i], '=');
258+
if (eq != NULL) {
259+
size_t keyLen = eq - envp[i];
260+
if (strlen(key) == keyLen) {
261+
if (memcmp(key, envp[i], keyLen) == 0) {
262+
return 1;
263+
}
264+
}
265+
}
266+
}
267+
}
268+
269+
return 0;
270+
}
271+
249272
char **child_setup_environment(struct passwd *pw, char **extra_env_vars) {
250273
int rv;
251274
char **envp = extra_env_vars;

process/environment.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package process
2+
3+
import (
4+
"errors"
5+
"fmt"
6+
"sort"
7+
"strings"
8+
)
9+
10+
type Env map[string]string
11+
12+
func NewEnv(array []string) (Env, error) {
13+
env := make(Env, len(array))
14+
15+
for _, str := range array {
16+
if str == "" {
17+
return nil, errors.New("malformed environment: empty string")
18+
}
19+
20+
tokens := strings.Split(str, "=")
21+
22+
if len(tokens) != 2 {
23+
return nil, errors.New("malformed environment: invalid format (not key=value)")
24+
}
25+
26+
key, value := tokens[0], tokens[1]
27+
28+
if key == "" {
29+
return nil, errors.New("malformed environment: empty key")
30+
}
31+
32+
env[key] = value
33+
}
34+
35+
return env, nil
36+
}
37+
38+
func (env Env) Merge(other Env) Env {
39+
merged := make(Env, len(env)+len(other))
40+
41+
for key, value := range env {
42+
merged[key] = value
43+
}
44+
45+
for key, value := range other {
46+
merged[key] = value
47+
}
48+
49+
return merged
50+
}
51+
52+
func (env Env) Array() []string {
53+
array := make([]string, len(env))
54+
55+
i := 0
56+
for key, value := range env {
57+
array[i] = fmt.Sprintf("%s=%s", key, value)
58+
i++
59+
}
60+
61+
sort.Strings(array)
62+
63+
return array
64+
}
65+
66+
func (env Env) String() string {
67+
return fmt.Sprintf("%#v", env)
68+
}

0 commit comments

Comments
 (0)