Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ports --env-file in minimalistic fashion #695

Merged
merged 1 commit into from Aug 4, 2015
Merged

Conversation

vpetersson
Copy link
Contributor

Refs #565. Adds support for providing environment variables from a file instead of manually providing them one-by-one.

@aanand
Copy link
Contributor

aanand commented Jul 28, 2015

Thanks for the contribution!

  1. This needs tests, and they should at least cover the cases covered by the Docker client's env file parser tests (although the "too long file" is less important).
  2. The Docker client performs very straightforward string splitting. Is there a compelling reason to use csv here, rather than just do the same?

@aanand
Copy link
Contributor

aanand commented Jul 28, 2015

Also, it's not clear what happens when a variable is specified both in a file and in the environment key. Indeed, the developer might want to decide that for themselves (I can say from experience working on Compose that opinions differ on what should take precedence).

Arguably, docker-py should just provide a utility method to parse a dict of environment variables from a file, and leave the job of building the combined dict to the developer.

@vpetersson
Copy link
Contributor Author

This needs tests, and they should at least cover the cases covered
by the Docker client's env file parser tests (although the "too long file" is less important).

Ok, let me take a look at that.

The Docker client performs very straightforward string splitting. Is there a compelling
reason to use csv here, rather than just do the same?

None really other than I figured that it was better to use a built-in feature instead of re-inventing the wheel (even if a simple .split('=') is likely sufficient).

it's not clear what happens when a variable is specified both in a file and in the environment key.

It works fine:

In [4]: docker.utils.create_container_config('1.18', '_mongodb', 'foo',  environment={'john': 'doe', 'steve': 'smith'}, env_file='/Users/mvip/tmp/foo')['Env']
Out[4]: 
[u'steve=smith',
 u'john=doe',
 'BROKER_HOST=localhost',
 'BROKER_PASSWORD=somePassword',
 'BROKER_PORT=123']

In [5]: docker.utils.create_container_config('1.18', '_mongodb', 'foo',  environment='FOO=BAR', env_file='/Users/mvip/tmp/foo')['Env']                                                                 
Out[5]: 
['FOO=BAR',
 'BROKER_HOST=localhost',
 'BROKER_PASSWORD=somePassword',
 'BROKER_PORT=123']

But you're right, the environment file from disk would override the manual one.

@aanand
Copy link
Contributor

aanand commented Jul 28, 2015

Sorry, I meant: what happens when the same variable (e.g. FOO) is specified in both places with a different value? Which one wins?

I figured that it was better to use a built-in feature instead of re-inventing the wheel

Fair enough - I'm just concerned that it could have all kinds of unexpected extra behaviour. For example, does it have special ways of dealing with quotes, or escaped delimiters? Even if those are useful things, we shouldn't deviate from the Docker client's behaviour.

@vpetersson
Copy link
Contributor Author

Sorry, I meant: what happens when the same variable (e.g. FOO) is specified in both
places with a different value? Which one wins?

Yeah, in that case the environment file would override. Not sure if this makes sense or not, but it would be easy to add logic if that isn't the expected behavior.

For example, does it have special ways of dealing with quotes, or escaped delimiters?
Even if those are useful things, we shouldn't deviate from the Docker client's behaviour.

Yeah, I was thinking about that too when I wrote it. I could not find any documentation for the escape character (and didn't find the code at first glance). The CSV library does have a feature for explicitly set this though so if you have the escape character, I can easily add that.

@vpetersson
Copy link
Contributor Author

It looks like the entire 'create_container_config' function lacks testing. I'm not sure if I'm well suited to write a test for this entire function.

@aanand
Copy link
Contributor

aanand commented Jul 28, 2015

The problem is that the "expected behaviour" differs for different people. For example, with the Docker client, values specified with -e take precedence over those specified in environment files, but lots of Compose users would prefer environment files to take precedence over values specified in the environment section of a Compose file.

This is why I think that, as a first step, it'd be preferable to simply provide a utility function for parsing environment files and leave it to the user to construct an environment dict to pass into create_container. That way docker-py isn't dictating the order of precedence.

@vpetersson
Copy link
Contributor Author

I see your point. Yeah, there are benefits with that, but for the use case I have in mind for this it would actually complicate things further, as you'd then have to re-implement the same stuff there (unless I'm missing something).

@aanand
Copy link
Contributor

aanand commented Jul 28, 2015

You wouldn't be duplicating any logic - the logic for parsing files would live in docker-py, and the logic for building the final environment dict would live in your project.

from docker.utils import parse_env_file

env = #... get env option
env_files = #... get env_file option

for filename in env_files:
    env.update(parse_env_file(filename))

@vpetersson
Copy link
Contributor Author

@aanand Let me know what you think.

@aanand
Copy link
Contributor

aanand commented Jul 29, 2015

Looks much better, thanks.

  1. After skimming the docs for the csv module, I'm convinced we shouldn't use it. There's way too much logic in there for dealing with actual CSV that might result in weird bugs for environment files. It would be best to follow Docker's ParseEnvFile method as closely as possible.
  2. The if isinstance(env_file, str) and if os.path.isfile(env_file) checks are unnecessary and will swallow errors that should bubble up to the developer.
  3. After that, just needs tests I think.

@vpetersson
Copy link
Contributor Author

Addressed the feedback, but still working on the tests. I'm having some issues there and have posted it on StackOverflow.

@aanand
Copy link
Contributor

aanand commented Jul 29, 2015

  1. If you remove the isfile check, you can remove the first patch from your test.
  2. Rather than patching open(), just write the contents to a temporary file (using tempfile) and open() that directly.

@vpetersson
Copy link
Contributor Author

@aanand Tests added. Let me know what you think.

@aanand
Copy link
Contributor

aanand commented Jul 30, 2015

Thanks!

  1. The Docker client raises an error if there are bad lines in the file, so we should do that too.
  2. It'd be more useful if parse_env_file returned a dict, so that multiple env files can be combined using the update() method on dict
  3. I'm not sure about the change to .gitignore, but that should definitely be done in a separate PR.

@vpetersson
Copy link
Contributor Author

The Docker client raises an error if there are bad lines in the file, so we should do that too.

Done.

It'd be more useful if parse_env_file returned a dict, so that multiple env files can be
combined using the update() method on dict

Sure. Fixed.

I'm not sure about the change to .gitignore, but that should definitely be done in a separate PR.

Yeah, that was an accidental commit.

@vpetersson
Copy link
Contributor Author

Also, I'm not sure if I should commit the whole htmlcov folder as well.

environment[k] = v
elif line[0] != '#':
raise errors.DockerException(
'Invalid env_file line:\n{0}'.format(line))
Copy link
Contributor

Choose a reason for hiding this comment

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

Rather than checking twice for line[0] != '#', I think this could be simplified to:

if line[0] == '#':
    continue

parse_line = line.strip().split('=')
if len(parse_line) == 2:
    # ...
else:
    # ...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Nice! Yes, that's cleaner. Pushed.

@aanand
Copy link
Contributor

aanand commented Jul 30, 2015

Great stuff. Two last things:

  1. Could you add a test for the "invalid line" case?
  2. Could you change the error message wording to:
Invalid line in environment file /path/to/file.env:
(offending line goes here)

@vpetersson
Copy link
Contributor Author

Could you add a test for the "invalid line" case?

There's already a test for that here or what do you mean?

Could you change the error message wording to...

Done.

@aanand
Copy link
Contributor

aanand commented Jul 30, 2015

Great. Needs squashing to one commit, then LGTM.

commit 4f053a06c1e9e3f63fd5afde60322f676acbdf45
Merge: 9177380 07a99ea
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Thu Jul 30 14:37:16 2015 +0100

    Merge branch 'master' into fixes

commit 9177380
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Thu Jul 30 14:00:51 2015 +0100

    Tweaks exception message.

commit 6a5832e
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Thu Jul 30 13:17:32 2015 +0100

    Simplifies logic as per feedback.

commit f750edd
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Thu Jul 30 11:09:14 2015 +0100

    Move return from list to dict. Adds exception handling.

commit 8e50f57
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Thu Jul 30 10:15:58 2015 +0100

    Reverts change to .gitignore.

commit 5ba2c1b
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 21:15:21 2015 +0100

    Fixes feedback. Adds three unittests.

commit e1c719e
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 17:00:16 2015 +0100

    WIP Adds test for parse_env_file

commit 4448ae7
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 16:42:49 2015 +0100

    Excludes coverage files.

commit 19a5d01
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 16:42:42 2015 +0100

    Switch fixes logic.

commit a8094c6
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 11:45:56 2015 +0100

    Implements logic for envfile parsing from Docker-cli

    Ref: https://github.com/docker/docker/blob/master/opts/envfile.go#L19-L51

commit ea9bfd9
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 11:41:23 2015 +0100

    Replaces CSV module with manual splitting.

commit a001d28
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Wed Jul 29 11:35:37 2015 +0100

    Removes isinstance on filename.

commit 419d596
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Tue Jul 28 22:39:33 2015 +0100

    Reflects @aanand's feedback.

commit e81e3c8
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Tue Jul 28 15:43:32 2015 +0100

    Typo fix.

commit 2898389
Author: Viktor Petersson <vpetersson@wireload.net>
Date:   Tue Jul 28 15:41:08 2015 +0100

    Refs docker#565. Adds minimal implementation of env_file client-side support.
@vpetersson
Copy link
Contributor Author

Done like dinner!

@vpetersson
Copy link
Contributor Author

@aanand can we land this? Would like to start working on the Ansible side of things :)

@aanand
Copy link
Contributor

aanand commented Jul 31, 2015

It's got my LGTM, but another maintainer needs to approve and merge it.

@shin- shin- merged commit d400717 into docker:master Aug 4, 2015
@shin-
Copy link
Contributor

shin- commented Aug 4, 2015

Merged - thanks!

@shin- shin- added this to the 1.4.0 milestone Sep 9, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants