diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 59b990d8bdfa..a158311a24c5 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -2,6 +2,15 @@ CHANGELOG ========= +1.3.20 +====== + +* feature:``aws cloudsearchdomain``: Add support for the + Amazon CloudSearch Domain command. +* feature:``aws cloudfront``: Update the Amazon CloudFront + command to the latest version + + 1.3.19 ====== diff --git a/awscli/__init__.py b/awscli/__init__.py index b32446081050..bcc14bf96417 100644 --- a/awscli/__init__.py +++ b/awscli/__init__.py @@ -17,7 +17,7 @@ """ import os -__version__ = '1.3.19' +__version__ = '1.3.20' # # Get our data path to be added to botocore's search path diff --git a/awscli/clidriver.py b/awscli/clidriver.py index 235e2186f121..57052006c603 100644 --- a/awscli/clidriver.py +++ b/awscli/clidriver.py @@ -186,8 +186,12 @@ def main(self, args=None): parser = self._create_parser() command_table = self._get_command_table() parsed_args, remaining = parser.parse_known_args(args) - self._handle_top_level_args(parsed_args) try: + # Because _handle_top_level_args emits events, it's possible + # that exceptions can be raised, which should have the same + # general exception handling logic as calling into the + # command table. This is why it's in the try/except clause. + self._handle_top_level_args(parsed_args) return command_table[parsed_args.command](remaining, parsed_args) except UnknownArgumentError as e: sys.stderr.write(str(e) + '\n') diff --git a/awscli/customizations/argrename.py b/awscli/customizations/argrename.py index f24fa4530820..e9e50ae81d3d 100644 --- a/awscli/customizations/argrename.py +++ b/awscli/customizations/argrename.py @@ -33,6 +33,7 @@ 'datapipeline.*.query': 'objects-query', 'emr.*.job-flow-ids': 'cluster-ids', 'emr.*.job-flow-id': 'cluster-id', + 'cloudsearchdomain.search.query': 'search-query', } diff --git a/awscli/customizations/cloudsearchdomain.py b/awscli/customizations/cloudsearchdomain.py new file mode 100644 index 000000000000..533c6fe8a512 --- /dev/null +++ b/awscli/customizations/cloudsearchdomain.py @@ -0,0 +1,29 @@ +# Copyright 2014 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. +"""Customizations for the cloudsearchdomain command. + +This module customizes the cloudsearchdomain command: + + * Add validation that --endpoint-url is required. + +""" + +def register_cloudsearchdomain(cli): + cli.register('top-level-args-parsed', validate_endpoint_url) + + +def validate_endpoint_url(parsed_args, **kwargs): + if parsed_args.command == 'cloudsearchdomain' and \ + parsed_args.endpoint_url is None: + raise ValueError( + "--endpoint-url is required for cloudsearchdomain commands") diff --git a/awscli/handlers.py b/awscli/handlers.py index 795fbd671e4c..2badab18244f 100644 --- a/awscli/handlers.py +++ b/awscli/handlers.py @@ -45,6 +45,7 @@ from awscli.customizations.globalargs import register_parse_global_args from awscli.customizations.cloudsearch import initialize as cloudsearch_init from awscli.customizations.emr.emr import emr_initialize +from awscli.customizations.cloudsearchdomain import register_cloudsearchdomain def awscli_initialize(event_handlers): @@ -92,3 +93,4 @@ def awscli_initialize(event_handlers): datapipeline.register_customizations(event_handlers) cloudsearch_init(event_handlers) emr_initialize(event_handlers) + register_cloudsearchdomain(event_handlers) diff --git a/awscli/testutils.py b/awscli/testutils.py index 0f619fd23daf..0484e63d3120 100644 --- a/awscli/testutils.py +++ b/awscli/testutils.py @@ -291,7 +291,14 @@ def run_cmd(self, cmd, expected_rc=0): captured_stdout = six.StringIO() with mock.patch('sys.stderr', captured_stderr): with mock.patch('sys.stdout', captured_stdout): - rc = self.driver.main(cmdlist) + try: + rc = self.driver.main(cmdlist) + except SystemExit as e: + # We need to catch SystemExit so that we + # can get a proper rc and still present the + # stdout/stderr to the test runner so we can + # figure out what went wrong. + rc = e.code stderr = captured_stderr.getvalue() stdout = captured_stdout.getvalue() self.assertEqual( diff --git a/doc/source/conf.py b/doc/source/conf.py index 34d4161f139d..648996a20b2d 100644 --- a/doc/source/conf.py +++ b/doc/source/conf.py @@ -52,7 +52,7 @@ # The short X.Y version. version = '1.3.' # The full version, including alpha/beta/rc tags. -release = '1.3.19' +release = '1.3.20' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/setup.py b/setup.py index 242054c42a62..ec1e88c7cd1d 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ import awscli -requires = ['botocore>=0.53.0,<0.54.0', +requires = ['botocore>=0.54.0,<0.55.0', 'bcdoc>=0.12.0,<0.13.0', 'six>=1.1.0', 'colorama==0.2.5', diff --git a/tests/unit/customizations/test_cloudsearchdomain.py b/tests/unit/customizations/test_cloudsearchdomain.py new file mode 100644 index 000000000000..8ba25137c980 --- /dev/null +++ b/tests/unit/customizations/test_cloudsearchdomain.py @@ -0,0 +1,62 @@ +# Copyright 2014 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 unittest +from awscli.testutils import BaseAWSCommandParamsTest +from awscli.customizations.cloudsearchdomain import validate_endpoint_url + +import mock + + +class TestSearchCommand(BaseAWSCommandParamsTest): + prefix = 'cloudsearchdomain search ' + + def test_search_with_query(self): + cmd = self.prefix.split() + cmd += [ + '--endpoint-url', 'http://example.com/', + # Note we're also verifying that --query is renamed to + # --search-query from argrename.py. + '--search-query', 'George Lucas', + '--query-options', + '{"defaultOperator":"and","fields":["directors^10"]}'] + + result = { + 'headers': {}, + 'uri_params': { + 'query': 'George Lucas', + 'queryOptions': + '{"defaultOperator":"and","fields":["directors^10"]}'}} + + self.assert_params_for_cmd(cmd, result, ignore_params=['payload']) + # We ignore'd the paylod, but we can verify that there should be + # no payload for this request. + self.assertIsNone(self.last_params['payload'].getvalue()) + + def test_endpoint_is_required(self): + cmd = self.prefix.split() + cmd += ['--search-query', 'foo'] + stderr = self.run_cmd(cmd, expected_rc=255)[1] + self.assertIn('--endpoint-url is required', stderr) + + +class TestCloudsearchDomainHandler(unittest.TestCase): + def test_validate_endpoint_url_is_none(self): + parsed_args = mock.Mock() + parsed_args.endpoint_url = None + parsed_args.command = 'cloudsearchdomain' + with self.assertRaises(ValueError): + validate_endpoint_url(parsed_args) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/unit/test_completer.py b/tests/unit/test_completer.py index 6203c30db841..6cfce44be92d 100644 --- a/tests/unit/test_completer.py +++ b/tests/unit/test_completer.py @@ -28,15 +28,15 @@ COMPLETIONS = [ ('aws ', -1, set(['autoscaling', 'cloudformation', 'cloudsearch', - 'cloudtrail', 'cloudwatch', 'configure', 'datapipeline', - 'directconnect', 'dynamodb', 'ec2', - 'elasticache', 'elasticbeanstalk', 'elastictranscoder', - 'elb', 'iam', 'importexport', 'kinesis', - 'opsworks', 'rds', 'redshift', 'route53', 's3', 's3api', - 'ses', 'sns', 'sqs', 'storagegateway', 'sts', - 'support', 'swf'])), + 'cloudsearchdomain', 'cloudtrail', 'cloudwatch', + 'configure', 'datapipeline', 'directconnect', 'dynamodb', + 'ec2', 'elasticache', 'elasticbeanstalk', + 'elastictranscoder', 'elb', 'iam', 'importexport', + 'kinesis', 'opsworks', 'rds', 'redshift', 'route53', + 's3', 's3api', 'ses', 'sns', 'sqs', 'storagegateway', + 'sts', 'support', 'swf'])), ('aws cloud', -1, set(['cloudformation', 'cloudsearch', - 'cloudtrail', 'cloudwatch'])), + 'cloudsearchdomain', 'cloudtrail', 'cloudwatch'])), ('aws cloudf', -1, set(['cloudformation'])), ('aws cloudfr', -1, set([])), ('aws foobar', -1, set([])),