Skip to content

Explicit profile overrides environment variables - #486

Merged
danielgtaylor merged 7 commits into
boto:developfrom
danielgtaylor:explicit-profile
Mar 18, 2015
Merged

danielgtaylor merged 7 commits into
boto:developfrom
danielgtaylor:explicit-profile

Conversation

@danielgtaylor

Copy link
Copy Markdown
Contributor

This changes the behavior of environment variable credential loading to
function as described in aws/aws-cli#113. Here is what this looks like
for both the CLI and Botocore/Boto 3:

CLI:

# Use the environment variables
$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar aws s3 ls

# Ignore the environment variables and use a profile from the config instead
$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar aws --profile dev s3 ls

Python:

$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar python
>>> import boto3
>>> boto3.setup_default_session(profile_name='dev')
>>> # The following will use the profile, not the env vars!
>>> s3dev = boto3.resource('s3')

Added a test and a new log message to ensure it's obvious what is happening.

cc @jamesls @kyleknap

@danielgtaylor danielgtaylor added the bug This issue is a confirmed bug. label Mar 10, 2015
Comment thread botocore/credentials.py Outdated

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 the wrong level of abstraction and couples profiles to retrieving environment variables when they don't need to be. An env provider should just be a simple object that pulls credentials from environment variables and that's it. If something up the call stack wants to either re-arrange cred providers or ignore what this class returns, that's another thing, but this class should just stay minimal.

It also gives this class weird semantics. If you pass a profile_name to this class, then it won't do anything.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Would something along these lines be more like what you were thinking?

https://gist.github.com/danielgtaylor/075fcd07b381f1b73e78

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.

Yeah something along those lines seems better abstracted to me.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I agree. That would look better.

@jamesls

jamesls commented Mar 10, 2015

Copy link
Copy Markdown
Member

We also don't really clarify what precedence the AWS_DEFAULT_PROFILE env var has. Should that be a higher precedence than the AWS_ACCESS*, AWS_SECRET* env vars? I'm thinking it should, but it currently does not.

EDIT: To clarify, what are we saying is the expected behavior for these cases:

Env vars:
AWS_DEFAULT_PROFILE=foo
AWS_ACCESS_KEY_ID=bar
AWS_SECRET_ACCESS_KEY=qux
Cmd line: <nothing>

If we're consistent with --profile, then we should be looking for the foo profile over pulling from the env creds which I don't believe is the current case with the existing PR.

We'd probably also want to double check that given:

Env vars:
AWS_DEFAULT_PROFILE=foo
AWS_ACCESS_KEY_ID=bar
AWS_SECRET_ACCESS_KEY=qux
Cmd line: --profile other-profile

That the end result is that we pull creds from other-profile (or fail). I believe the current PR handles this last case already.

@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage increased (+0.03%) to 94.58% when pulling bb67322b02bc4dd22ef5ccfe7ac87324d9fb2a16 on danielgtaylor:explicit-profile into 2eee0f1 on boto:develop.

@kyleknap

Copy link
Copy Markdown
Contributor

@jamesls @danielgtaylor

Here is the precedence leveling that I think we should be using:

Location CLI Representation Boto3 Representation
Access Keys in Code NA client(access_key_id=..., secret_access_key_id=...)
Profile in Code --profile <profilename> client(profile_name=...)
Access Keys in Environment Variable AWS_ACCESS_KEY_ID AWS_ACCESS_KEY_ID
Profile in Environment Variable AWS_DEFAULT_PROFILE AWS_DEFAULT_PROFILE
Shared Credentials File ~/.aws/credentials ~/.aws/credentials
AWS Config File ~/.aws/config ~/.aws/config
Instance Metadata NA NA

The way I see it is that precedence should be based off of what is the most explicit. Similarly how access key in code is taking precedence of profile_name in code, access key in environment variable should take precedence over profile name in environment variable because the user is being more explicit and it is consistent with the code parallel of precedence.

I honestly don't think AWS_DEFAULT_PROFILE should affect precedence at all. It should just be switching what profile is default when we look into the shared credentials file and/or the config file. Or that is what I expect it would do given the fact that the name of the environment variable seems to imply changing what the default profile is, which is a little different than how it is described in our docs:

screen shot 2015-03-11 at 10 37 30 am

But yet again the docs is a little vague in that it really does not say where its precedence lies when it describes it as "name of the CLI profile to use".

Let me know what you guys think.

@danielgtaylor

Copy link
Copy Markdown
Contributor Author

Spoke with @kyleknap this morning about this and I think we both agree. To put this into more concrete examples, if these are set:

Code / arguments:

  • aws_access_key_id='abc123' <----- takes precedence
  • profile_name='dev'

Environment

  • AWS_ACCESS_KEY_ID=abc123 <------ takes precedence
  • AWS_DEFAULT_PROFILE=dev

The case we really need to get working is this one (which this PR solves):

  • AWS_ACCESS_KEY_ID=abc123
  • --profile <------ takes precedence
$ AWS_ACCESS_KEY_ID=abc123 aws --profile dev s3 ls

Thoughts?

@jamesls

jamesls commented Mar 12, 2015

Copy link
Copy Markdown
Member

Sorry, just catching up on this now. To summarize, it sounds like we're saying:

For any given precedence level (i.e config file, env vars, cmdline args, etc), the akid/sak will always trump a profile value if all three are specified at the same precedence value.

A consequence of this is that in order to get a "profile" to take, you have to specify it at the next precedence level (or higher) of whatever is explicitly specifying the akid/skid. So if akid/sak are in env vars, profile has to be as a cmd line option or in boto3 via a method arg.

In which case, I think that makes sense. Thanks for looking into this.

I would suggest adding integration tests for these cases as well.

This changes the behavior of environment variable credential loading to
function as described in aws/aws-cli#113. Here is what this looks like
for both the CLI and Botocore/Boto 3:

CLI:
```bash
$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar aws s3 ls

$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar aws --profile dev s3 ls
```

Python:
```python
$ AWS_ACCESS_KEY_ID=foo AWS_SECRET_ACCESS_KEY=bar python
>>> import boto3
>>> boto3.setup_default_session(profile_name='dev')
>>> # The following will use the profile, not the env vars!
>>> s3dev = boto3.resource('s3')
```

Added a test and a new log message to ensure it's obvious what is happening.
@danielgtaylor
danielgtaylor force-pushed the explicit-profile branch 2 times, most recently from 841f54c to f3114d7 Compare March 16, 2015 19:56
@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage increased (+0.02%) to 94.6% when pulling 856eaec on danielgtaylor:explicit-profile into 73b59f1 on boto:develop.

@danielgtaylor

Copy link
Copy Markdown
Contributor Author

@jamesls @kyleknap please take another look.

Comment thread tests/integration/test_credentials.py Outdated

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.

This is general test case best practice advice: Do not leave global state modified outside the scope of a test. It's ok to mess with global state in a test if there's no better way to test what you need to test, but you have to put the state back to the way it initially was.

Here, if the vars you're deleting were defined in os.environ before the test, this setUp() is deleting these values and not restoring them after the test executes. This can lead to all sorts of hard to track down problems where the test order may affect whether or not a test passes depending on if it passes before/after the global state modification.

That being, said, just use BaseEnvVar to handle the patching of env vars for you.

@jamesls

jamesls commented Mar 16, 2015

Copy link
Copy Markdown
Member

I think your integ tests cover a good set of cases, I just have some comments on how the tests themselves are implemented.

Also, there are no unit tests for this. While I think we want the integ tests to ensure we don't regress as we refactor some of the internals, I still think this change should have a unit test. We should be able to call into create_credential_resolver and assert how the cred resolver is being created.

The actual code change itself looks good.

Comment thread botocore/credentials.py Outdated

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.

This seems more like a debug message. We'll still log which provider ultimately gives creds, but the fact that we're disabling a check seems mostly of interest to a dev.

@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage increased (+0.02%) to 94.6% when pulling d17c52d on danielgtaylor:explicit-profile into 73b59f1 on boto:develop.

@danielgtaylor

Copy link
Copy Markdown
Contributor Author

@jamesls please take another look.

Comment thread tests/unit/test_credentials.py Outdated

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.

Or to assert that all the providers are not an instance of the env provider, you can just use: self.assertTrue(all(not isinstance(p, EnvProvider) for p in p.providers))

@jamesls

jamesls commented Mar 17, 2015

Copy link
Copy Markdown
Member

:shipit: Looks good, just had a small suggestion. Thanks for updating.

@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage increased (+0.03%) to 94.61% when pulling d0e7add on danielgtaylor:explicit-profile into 73b59f1 on boto:develop.

danielgtaylor added a commit that referenced this pull request Mar 18, 2015
Explicit profile overrides environment variables.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug This issue is a confirmed bug.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants