Repository: aws/aws-cli

Issue:
storagegateway commands having to do with volumes fail
It can't decide whether it wants `--volume-arns` or `--volume-ar-ns`

```
% aws --version                                                                                                                                                                                           
aws-cli/1.10.1 Python/2.7.10 Darwin/15.2.0 botocore/1.3.23
% aws storagegateway describe-stored-iscsi-volumes --volume-arns 'arn:aws:storagegateway:us-west-2:xxxxxxxxxx:gateway/sgw-xxxxxxxxxx/volume/vol-xxxxxxx'
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws: error: argument --volume-ar-ns is required
% aws storagegateway describe-stored-iscsi-volumes --volume-ar-ns 'arn:aws:storagegateway:us-west-2:xxxxxxxxxx:gateway/sgw-xxxxxxx/volume/vol-xxxxxxx'                                                
usage: aws [options] <command> <subcommand> [<subcommand> ...] [parameters]
To see help text, you can run:

  aws help
  aws <command> help
  aws <command> <subcommand> help
aws: error: argument --volume-arns is required
```



Discussion:

Resolving patch:
diff --git a/awscli/clidocs.py b/awscli/clidocs.py
--- a/awscli/clidocs.py
+++ b/awscli/clidocs.py
@@ -127,7 +127,8 @@ def doc_synopsis_option(self, arg_name, help_command, **kwargs):
             self._documented_arg_groups.append(argument.group_name)
         else:
             option_str = '%s <value>' % argument.cli_name
-        if not argument.required:
+        if not (argument.required
+                or getattr(argument, '_DOCUMENT_AS_REQUIRED', False)):
             option_str = '[%s]' % option_str
         doc.writeln('%s' % option_str)
 
diff --git a/awscli/customizations/utils.py b/awscli/customizations/utils.py
--- a/awscli/customizations/utils.py
+++ b/awscli/customizations/utils.py
@@ -42,6 +42,14 @@ def make_hidden_alias(argument_table, existing_name, alias_name):
     copy_arg = copy.copy(current)
     copy_arg._UNDOCUMENTED = True
     copy_arg.name = alias_name
+    if current.required:
+        # If the current argument is required, then
+        # we'll mark both as not required, but
+        # flag _DOCUMENT_AS_REQUIRED so our doc gen
+        # knows to still document this argument as required.
+        copy_arg.required = False
+        current.required = False
+        current._DOCUMENT_AS_REQUIRED = True
     argument_table[alias_name] = copy_arg
 
 


Test patch:
diff --git a/tests/functional/docs/test_help_output.py b/tests/functional/docs/test_help_output.py
--- a/tests/functional/docs/test_help_output.py
+++ b/tests/functional/docs/test_help_output.py
@@ -375,6 +375,19 @@ def test_hidden_params_are_not_documented(self):
         self.assert_not_contains('--starting-sequence-number')
 
 
+class TestCanDocumentAsRequired(BaseAWSHelpOutputTest):
+    def test_can_doc_as_required(self):
+        # This param is already marked as required, but to be
+        # explicit this is repeated here to make it more clear.
+        def doc_as_required(argument_table, **kwargs):
+            arg = argument_table['volume-arns']
+        self.driver.session.register('building-argument-table',
+                                     doc_as_required)
+        self.driver.main(['storagegateway', 'describe-cached-iscsi-volumes',
+                          'help'])
+        self.assert_not_contains('[--volume-arns <value>]')
+
+
 class TestEC2AuthorizeSecurityGroupNotRendered(BaseAWSHelpOutputTest):
     def test_deprecated_args_not_documented(self):
         self.driver.main(['ec2', 'authorize-security-group-ingress', 'help'])
diff --git a/tests/functional/storagegateway/test_describe_cached_iscsi_volumes.py b/tests/functional/storagegateway/test_describe_cached_iscsi_volumes.py
new file mode 100644
--- /dev/null
+++ b/tests/functional/storagegateway/test_describe_cached_iscsi_volumes.py
@@ -0,0 +1,33 @@
+# Copyright 2016 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 awscli.testutils import BaseAWSCommandParamsTest
+
+
+class TestDescribeCachedISCSIVolumes(BaseAWSCommandParamsTest):
+
+    PREFIX = 'storagegateway describe-cached-iscsi-volumes'
+    VOLUME_ARN = 'a' * 50
+
+    def test_accepts_old_argname(self):
+        cmdline = (self.PREFIX + '  --volume-ar-ns %s') % (self.VOLUME_ARN,)
+        params = {
+            'VolumeARNs': [self.VOLUME_ARN],
+        }
+        self.assert_params_for_cmd(cmdline, params)
+
+    def test_accepts_fixed_param_name(self):
+        cmdline = (self.PREFIX + '  --volume-arns %s') % (self.VOLUME_ARN,)
+        params = {
+            'VolumeARNs': [self.VOLUME_ARN],
+        }
+        self.assert_params_for_cmd(cmdline, params)
diff --git a/tests/unit/customizations/test_utils.py b/tests/unit/customizations/test_utils.py
--- a/tests/unit/customizations/test_utils.py
+++ b/tests/unit/customizations/test_utils.py
@@ -40,6 +40,29 @@ def test_rename_command_table(self):
         self.assert_contains('run-instances')
 
 
+class TestHiddenAlias(unittest.TestCase):
+    def test_not_marked_as_required_if_not_needed(self):
+        original_arg_required = mock.Mock()
+        original_arg_required.required = False
+        arg_table = {'original': original_arg_required}
+        utils.make_hidden_alias(arg_table, 'original', 'new-name')
+        self.assertIn('new-name', arg_table)
+        # Note: the _DOCUMENT_AS_REQUIRED is tested as a functional
+        # test because it only affects how the doc synopsis is
+        # rendered.
+        self.assertFalse(arg_table['original'].required)
+        self.assertFalse(arg_table['new-name'].required)
+
+    def test_hidden_alias_marks_as_not_required(self):
+        original_arg_required = mock.Mock()
+        original_arg_required.required = True
+        arg_table = {'original': original_arg_required}
+        utils.make_hidden_alias(arg_table, 'original', 'new-name')
+        self.assertIn('new-name', arg_table)
+        self.assertFalse(arg_table['original'].required)
+        self.assertFalse(arg_table['new-name'].required)
+
+
 class TestValidateMututuallyExclusiveGroups(unittest.TestCase):
     def test_two_single_groups(self):
         # The most basic example of mutually exclusive args.
@@ -54,7 +77,6 @@ def test_two_single_groups(self):
         with self.assertRaises(ValueError):
             utils.validate_mutually_exclusive(parsed, ['foo'], ['bar'])
 
-
     def test_multiple_groups(self):
         groups = (['one', 'two', 'three'], ['foo', 'bar', 'baz'],
                   ['qux', 'bad', 'morebad'])
@@ -99,6 +121,7 @@ def test_bucket_exists_with_non_404(self):
         self.assertTrue(
             utils.s3_bucket_exists(self.s3_client, self.bucket_name))
 
+
 class TestClientCreationFromGlobals(unittest.TestCase):
     def setUp(self):
         self.fake_client = {}