Repository: aws/aws-cli
Pull request: Make configure/set command properly handle spaces in profiles
Fixes #2806
 A big thanks to @gtseres for submitting the fix.

I refactored his fix a little bit to make it a function so we can unit test it and apply it to the `set` command as well.

Problem statement:
Profile does not return region or output format if it contains a SPACE character
The AWS CLI tool allows you to create a profile name with a space character in it, and writes the values to the credentials and config file as expected. However when getting that config using the quoted profile name, it doesn't return the region or default output format.

Setting the profile credentials and config:

    $ aws configure --profile 'Foo Bar'
    AWS Access Key ID [None]: foo
    AWS Secret Access Key [None]: bar
    Default region name [None]: eu-west-1
    Default output format [None]: json

    $ cat .aws/config
    [profile Foo Bar]
    output = json
    region = eu-west-1

    $ cat .aws/credentials
    [Foo Bar]
    aws_access_key_id = foo
    aws_secret_access_key = bar


Checking the profile credentials and config:

    $ aws configure --profile 'Foo Bar'
    AWS Access Key ID [****************foo]:
    AWS Secret Access Key [****************bar]:
    Default region name [None]:
    Default output format [None]:

CLI commands then prompt for a region when using the profile name. The **credentials** do still work, and are retrieved from the profile, but the **config** ,e.g. the region, has to be specified on the command line still:

    $ aws autoscaling describe-auto-scaling-groups --auto-scaling-group-names group_name --profile 'Foo Bar'
    You must specify a region. You can also configure your region by running "aws configure".

When performing the same steps above with a profile name without a space character (Foo_Bar), everything works as expected.

Hints from issue discussion:
So after some debugging. Essentially the `configure` function for profile needs to check if the profile name has spaces and if so add single quotes around them. First off actual config loading happens in botocore and uses `shlex.split` on the profile section name:

https://github.com/boto/botocore/blob/develop/botocore/configloader.py#L254

To get the name of the profile. Unfortunately, this splits on whitespace which leads to your issue:

```
>>> import shlex
>>> test = 'profile Foo Bar'
>>> shlex.split(test)
['profile', 'Foo', 'Bar']
```

 Now given the following profile:

```
[profile 'Foo Bar']
output = json
region = eu-west-1
```

If I run this through the python interactive shell (botocore parses config files through `configparser`):

```
>>> config = configparser.RawConfigParser()
>>> config.read('config.ini')
['config.ini']
>>> config.sections()
["profile 'Foo Bar'"]
>>> shlex.split(config.sections()[0])
['profile', 'Foo Bar']
```

The quoting ensure that the profile name even with spaces comes up properly.
Thanks for looking into this.  So I think a fix for this would be when the CLI is writing out a new profile, check if the profile name has spaces and if so, surround the name in quotes.

I suppose we could always quote the profile name, but I'd prefer a smaller change.
@jamesls I'm thinking of doing the quoting route. It's a mostly small fix with the main part to deal with being testing. This week might end up a bit busy, but at the worst case I might be able to pull together a pull request over the weekend if no one gets to it by then. 
I have sent out a PR for that. Actually there is a bunch of unit tests for the configuration, that cover the change as well. Please let me know if you want me to run any other kind of test.

Commit message: Fix issue #2806: Support for section names that contain spaces

Patch to review:
diff --git a/awscli/customizations/configure/configure.py b/awscli/customizations/configure/configure.py
index 263c8738439a..05b34c69ed6b 100644
--- a/awscli/customizations/configure/configure.py
+++ b/awscli/customizations/configure/configure.py
@@ -115,8 +115,12 @@ def _run_main(self, parsed_args, parsed_globals):
             self._write_out_creds_file_values(new_values,
                                               parsed_globals.profile)
             if parsed_globals.profile is not None:
-                new_values['__section__'] = (
-                    'profile %s' % parsed_globals.profile)
+                if ' ' in parsed_globals.profile:
+                    new_values['__section__'] = (
+                        'profile \'%s\'' % parsed_globals.profile)
+                else:
+                    new_values['__section__'] = (
+                        'profile %s' % parsed_globals.profile)
             self._config_writer.update_config(new_values, config_filename)
 
     def _write_out_creds_file_values(self, new_values, profile_name):


Code review comments:
diff_hunk: @@ -36,3 +36,13 @@ def mask_value(current_value):
         return 'None'
     else:
         return ('*' * 16) + current_value[-4:]
+
+
+def profile_to_section(profile_name):
+    """Converts a profile name to a section header to be used in the config."""
+
+    if ' ' in profile_name:
path: awscli/customizations/configure/__init__.py
text: @user1:
It's probably better to use `string.whitespace` to account for tabs an other weird edge cases.

@author:
I'm okay with making this a little more robust but I'm concerned about what we view as valid whitespace in a section header. A profile name with a newline in it is probably invalid.

@user1:
It's more about being coordinated with what `shlex.split` uses in botocore.

diff_hunk: @@ -36,3 +36,13 @@ def mask_value(current_value):
         return 'None'
     else:
         return ('*' * 16) + current_value[-4:]
+
+
+def profile_to_section(profile_name):
+    """Converts a profile name to a section header to be used in the config."""
+
+    if ' ' in profile_name:
+        section_format = 'profile "%s"'
path: awscli/customizations/configure/__init__.py
text: @user1:
Given botocore uses `shlex.quote` we should probably use the corresponding `six.moves.shlex_quote` to be safe.