Repository: aws/aws-cli

Issue:
JSON structures should trim leading whitespace
If you have a space between the opening quote and the opening json element (either `[` or `{`), you'll get an error:

Works:

```
$ aws ec2 run-instances --image-id ami-5675ee3z --min-count 1 --max-count 1 --block-device-mapping '[{"device_name":"/dev/sda1", "ebs":{"volume_size":20} }, {"device_name":"/dev/xvdf", "ebs":{"volume_size":10} } ] ' --region us-east-1
```

Doesn't work (note the space between the opening `'` and the `[`):

```
$ aws ec2 run-instances --image-id ami-5675ee3z --min-count 1 --max-count 1 --block-device-mapping ' [{"device_name":"/dev/sda1", "ebs":{"volume_size":20} }, {"device_name":"/dev/xvdf", "ebs":{"volume_size":10} } ] ' --region us-east-1
Structure option value must be JSON or path to file.
```



Discussion:

Resolving patch:
diff --git a/awscli/argprocess.py b/awscli/argprocess.py
--- a/awscli/argprocess.py
+++ b/awscli/argprocess.py
@@ -348,7 +348,7 @@ def unpack_cli_arg(parameter, value):
 
 def unpack_complex_cli_arg(parameter, value):
     if parameter.type == 'structure' or parameter.type == 'map':
-        if value[0] == '{':
+        if value.lstrip()[0] == '{':
             d = json.loads(value)
         else:
             msg = 'Structure option value must be JSON or path to file.'
@@ -356,10 +356,10 @@ def unpack_complex_cli_arg(parameter, value):
         return d
     elif parameter.type == 'list':
         if isinstance(value, six.string_types):
-            if value[0] == '[':
+            if value.lstrip()[0] == '[':
                 return json.loads(value)
         elif isinstance(value, list) and len(value) == 1:
-            if value[0][0] == '[':
+            if value[0].lstrip()[0] == '[':
                 return json.loads(value[0])
         return [unpack_cli_arg(parameter.members, v) for v in value]
 


Test patch:
diff --git a/tests/unit/ec2/test_run_instances.py b/tests/unit/ec2/test_run_instances.py
new file mode 100644
--- /dev/null
+++ b/tests/unit/ec2/test_run_instances.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"). You
+# may not use this file except in compliance with the License. A copy of
+# the License is located at
+#
+#     http://aws.amazon.com/apache2.0/
+#
+# or in the "license" file accompanying this file. This file is
+# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
+# ANY KIND, either express or implied. See the License for the specific
+# language governing permissions and limitations under the License.
+from tests import BaseCLIDriverTest
+import awscli.clidriver
+
+
+class TestDescribeInstances(BaseCLIDriverTest):
+
+    prefix = 'aws ec2 run-instances'
+
+    def test_block_device_mapping(self):
+        args = ' --image-id ami-foobar --min-count 1 --max-count 1'
+        args_list = (self.prefix + args).split()
+        # We're switching to list form because we need to test
+        # when there's leading spaces.  This is the CLI equivalent
+        # of --block-dev-mapping ' [{"device_name" ...'
+        # (note the space between ``'`` and ``[``)
+        args_list.append('--block-device-mapping')
+        args_list.append(
+            ' [{"device_name":"/dev/sda1","ebs":{"volume_size":20}}]')
+        result = {
+            'BlockDeviceMapping.1.DeviceName': '/dev/sda1',
+            'BlockDeviceMapping.1.Ebs.VolumeSize': '20',
+            'ImageId': 'ami-foobar',
+            'MaxCount': '1',
+            'MinCount': '1'
+        }
+        params = self.driver.test(args_list)
+        self.assertEqual(params, result)
+
+
+if __name__ == "__main__":
+    unittest.main()