Skip to content

compose: fix environment interpolation from the client#30781

Merged
vdemeester merged 3 commits intomoby:masterfrom
AkihiroSuda:fix-stack-env
Mar 17, 2017
Merged

compose: fix environment interpolation from the client#30781
vdemeester merged 3 commits intomoby:masterfrom
AkihiroSuda:fix-stack-env

Conversation

@AkihiroSuda
Copy link
Copy Markdown
Member

@AkihiroSuda AkihiroSuda commented Feb 7, 2017

- What I did

Fix (newly support?) environment interpolation from the client

e.g.

services:
  x:
    environment:
# needs to be propagated from the client
      - FOO

- How I did it

Please refer to the code.

- How to verify it
Prepare following files:

docker-compose.yaml:

version: "3"
services:
  x:
    image: "busybox"
    command: ["sh", "-c", "env; tail -f /dev/null"]
    environment:
      - FOO
      - BAR
      - QUX
      - QUUX=quux_from_yaml
    env_file: a.env

a.env:

FOO=foo_from_file
BAR=bar_from_file
BAZ=baz_from_file

on Docker Compose:

$ FOO=foo_from_env QUUX=quux_from_env docker-compose up

on Docker Swarm-mode:

$ FOO=foo_from_env QUUX=quux_from_env docker stack deploy -c docker-compose.yaml stack01
$ docker service logs stack01_x

Result:

Composer FOO BAR BAZ QUX QUUX
Compose 1.11.1,
Swarm-mode with this PR
"foo_from_env" unset (not "") "baz_from_file" unset (not "") "quux_from_yaml"
Swarm-mode 1.13.1 "" "" "baz_from_file" "" "quux_from_yaml"

For BAR, I wonder Compose's result (unset) is unexpected and should be "", but not sure. (Could not find any doc 😅)

- Description for the changelog

compose: support environment interpolation from the client

- A picture of a cute animal (not mandatory but encouraged)

446374156

Signed-off-by: Akihiro Suda suda.akihiro@lab.ntt.co.jp

@AkihiroSuda
Copy link
Copy Markdown
Member Author

cc @dnephin

Copy link
Copy Markdown
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks great, thanks!

Just a couple small issues

home, ok := lookupEnv("HOME")
if !ok {
panic(errors.New("cannot expand '~', because the environment lacks HOME"))
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this should be a panic. At most a warning.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@dnephin What should we do with warning?
Keep unparsed '~' as-is? Or Ignore the entry?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Keep it as-is with the ~ I think

@@ -358,10 +363,18 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string) e

serviceConfig.Environment = environment
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: To be consistent I think this line should be at the end of the function, right before return nil


// env is prioritized over the file
for k := range serviceConfig.Environment {
v, ok := lookupEnv(k)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is missing a check to see if v == "". If v is not the empty string we should not override from the client environment.

env := os.Environ()
details.Environment = make(map[string]string, len(env))
for _, s := range env {
// if value is empty, s is like "K=", not "K".
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is probably a reasonable assumption, but it couldn't hurt to check.

@AkihiroSuda
Copy link
Copy Markdown
Member Author

@dnephin @aaronlehmann
Updated PR (sorry for being late)

Now this PR is much more compatible with Docker Compose, but maybe breaking compatibility with Docker Swarm-mode 1.13.1. (Please refer to changes in UT)

cc @shin-

if ok {
environment[k] = interpolatedV
} else {
delete(environment, k)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think you ever want to delete things from the environment. What's this about?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the environment variable is specified to be interpolated in the YAML but its value is unset in the actual environment, it should be deleted (in Docker Compose's implementation).

Please refer to the example table listed in the description text of this PR.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think it's actually deleted. I don't see that in the Compose code anywhere. I think that's handled by the server when starting a container. It creates the variable unset.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, you are right, I didn't know that 😅

Opened #31634 for clarifying this behavior in the doc.

I'll update this PR as well in short.

environment[k] = interpolatedV
} else {
environment[k] = v
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the logic from Compose: https://github.com/docker/compose/blob/master/compose/config/config.py#L589-L598

I think we can reduce the size of this function quite a bit by matching the logic.

Once delete() is removed from below, i think these blocks will be nearly identical. So we could have a function like this:

func updateEnvironment(environment, map[string]string, vars map[string]string, lookupEnv template.Mapping) map[string[string {
    ...
}

environment = updateEnvironment(environment, runconfigopts.ConvertKVStringsToMap(envVars), lookupEnv)
serviceConfig.Environment = updateEnvironment(environment, serviceConfig.Environment, lookupEnv)
return nil

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the pointer, I'll look into that.

Copy link
Copy Markdown
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Design looks good :)

@cpuguy83
Copy link
Copy Markdown
Member

cpuguy83 commented Mar 7, 2017

Ping, any update here?

@AkihiroSuda
Copy link
Copy Markdown
Member Author

updated PR, sorry for delay

@AkihiroSuda
Copy link
Copy Markdown
Member Author

oops, still something is wrong... (expected: BAR, got BAR=)

$ docker inspect task_container_of_the_example_above
            "Env": [
                "BAR=",
                "BAZ=baz_from_file",
                "FOO=foo_from_env",
                "QUUX=quux_from_env",
                "QUX=",
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],

@AkihiroSuda
Copy link
Copy Markdown
Member Author

trying to fix the issue 👆 by defining MappingWithEquals as map[string]*string rather than map[string]string, but getting stuck due to reflection ... 😭 https://github.com/AkihiroSuda/docker/commits/fix-stack-env.wip.20170308

For an environment variable defined in the yaml without value,
the value needs to be propagated from the client, as in Docker Compose.

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
Load from env should only happen if the value is unset.
Extract a buildEnvironment function and revert some changes to tests.

Signed-off-by: Daniel Nephin <dnephin@docker.com>
@dnephin
Copy link
Copy Markdown
Member

dnephin commented Mar 14, 2017

I've rebased this PR, and fixed the remaining issue with empty values.

@AkihiroSuda
Copy link
Copy Markdown
Member Author

@dnephin Thank you a lot 😃

@@ -135,7 +135,10 @@ type StringOrNumberList []string

// MappingWithEquals is a mapping type that can be converted from a list of
// key=value strings
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// MappingWithEquals is a mapping type that can be converted from a list of
// key[=value] strings.
// For the key with an empty value (`key=`), the mapped value is set to a pointer to `""`.
// For the key without value (`key`), the mapped value is set to nil.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

committed as 2cdc9c4

Copy link
Copy Markdown
Member

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🦁

Signed-off-by: Akihiro Suda <suda.akihiro@lab.ntt.co.jp>
@AkihiroSuda
Copy link
Copy Markdown
Member Author

LGTM for @dnephin 's work 😃

Copy link
Copy Markdown
Member

@dnephin dnephin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@vdemeester vdemeester merged commit 8bc7038 into moby:master Mar 17, 2017
@GordonTheTurtle GordonTheTurtle added this to the 17.05.0 milestone Mar 17, 2017
@AkihiroSuda
Copy link
Copy Markdown
Member Author

@vieux @dnephin

I'm unsure this is a new feature of bug-fix 😅, but can we cherry-pick this for 17.04?

@dnephin
Copy link
Copy Markdown
Member

dnephin commented Mar 22, 2017

I think it's a bug fix

@thaJeztah
Copy link
Copy Markdown
Member

ping @AkihiroSuda could you prepare a cherry-pick for this?

@AkihiroSuda
Copy link
Copy Markdown
Member Author

sure, opened #32033

dnephin pushed a commit to dnephin/docker that referenced this pull request Apr 17, 2017
compose: fix environment interpolation from the client
@bitsofinfo
Copy link
Copy Markdown

When is this slated to be released?

@thaJeztah
Copy link
Copy Markdown
Member

This has been released quite a while ago already

@thaJeztah
Copy link
Copy Markdown
Member

Docker 17.04 and up should have this change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants