Path: blob/develop/tests/unit/customizations/test_waiters.py
1567 views
# Copyright 2014 Amazon.com, Inc. or its affiliates. All Rights Reserved.1#2# Licensed under the Apache License, Version 2.0 (the "License"). You3# may not use this file except in compliance with the License. A copy of4# the License is located at5#6# http://aws.amazon.com/apache2.0/7#8# or in the "license" file accompanying this file. This file is9# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF10# ANY KIND, either express or implied. See the License for the specific11# language governing permissions and limitations under the License.12from botocore.waiter import WaiterModel13from botocore.exceptions import DataNotFoundError1415from awscli.testutils import (16mock, unittest,17BaseAWSHelpOutputTest,BaseAWSCommandParamsTest18)19from awscli.customizations.waiters import add_waiters, WaitCommand, \20get_waiter_model_from_service_model, WaiterStateCommand, WaiterCaller, \21WaiterStateDocBuilder, WaiterStateCommandBuilder222324class TestAddWaiters(unittest.TestCase):25def setUp(self):26self.service_model = mock.Mock()27self.session = mock.Mock()2829self.command_object = mock.Mock()30self.command_object.service_model = self.service_model3132# Set up the mock session.33self.session.get_waiter_model.return_value = WaiterModel(34{35'version': 2,36'waiters': {37'FooExists': {},38}39}40)4142def test_add_waiters(self):43command_table = {}44add_waiters(command_table, self.session, self.command_object)45# Make sure a wait command was added.46self.assertIn('wait', command_table)47self.assertIsInstance(command_table['wait'], WaitCommand)4849def test_add_waiters_no_waiter_names(self):50self.session.get_waiter_model.return_value = WaiterModel(51{52'version': 2,53# No waiters are specified.54'waiters': {}55}56)57command_table = {}58add_waiters(command_table, self.session, self.command_object)59# Make sure that no wait command was added since the service object60# has no waiters.61self.assertEqual(command_table, {})6263def test_add_waiters_no_service_object(self):64command_table = {}65self.command_object.service_model = None66add_waiters(command_table, self.session, self.command_object)67# Make sure that no wait command was added since no service object68# was passed in.69self.assertEqual(command_table, {})7071def test_add_waiter_no_waiter_config(self):72self.session.get_waiter_model.side_effect = DataNotFoundError(73data_path='foo')74command_table = {}75add_waiters(command_table, self.session, self.command_object)76self.assertEqual(command_table, {})777879class TestServicetoWaiterModel(unittest.TestCase):80def test_service_object_to_waiter_model(self):81service_model = mock.Mock()82session = mock.Mock()83service_model.service_name = 'service'84service_model.api_version = '2014-01-01'85get_waiter_model_from_service_model(session, service_model)86session.get_waiter_model.assert_called_with('service', '2014-01-01')8788def test_can_handle_data_errors(self):89service_model = mock.Mock()90session = mock.Mock()91service_model.service_name = 'service'92service_model.api_version = '2014-01-01'93session.get_waiter_model.side_effect = DataNotFoundError(94data_path='foo')95self.assertIsNone(96get_waiter_model_from_service_model(session, service_model))979899class TestWaitCommand(unittest.TestCase):100def setUp(self):101self.session = mock.Mock()102self.model = WaiterModel({103'version': 2,104'waiters': {105'Foo': {106'operation': 'foo', 'maxAttempts': 1, 'delay': 1,107'acceptors': [],108}109}110})111self.service_model = mock.Mock()112self.cmd = WaitCommand(self.session, self.model, self.service_model)113114def test_passes_on_lineage(self):115child_cmd = self.cmd.subcommand_table['foo']116self.assertEqual(len(child_cmd.lineage), 2)117self.assertEqual(child_cmd.lineage[0], self.cmd)118self.assertIsInstance(child_cmd.lineage[1], WaiterStateCommand)119120def test_run_main_error(self):121self.parsed_args = mock.Mock()122self.parsed_args.subcommand = None123with self.assertRaises(ValueError):124self.cmd._run_main(self.parsed_args, None)125126127class TestWaitHelpOutput(BaseAWSHelpOutputTest):128def test_wait_command_is_in_list(self):129self.driver.main(['ec2', 'help'])130self.assert_contains('* wait')131132def test_wait_help_command(self):133self.driver.main(['ec2', 'wait', 'help'])134self.assert_contains('.. _cli:aws ec2 wait:')135self.assert_contains('Wait until a particular condition is satisfied.')136self.assert_contains('* instance-running')137self.assert_contains('* vpc-available')138139def test_wait_state_help_command(self):140self.driver.main(['ec2', 'wait', 'instance-running', 'help'])141self.assert_contains('.. _cli:aws ec2 wait instance-running:')142self.assert_contains(143'Wait until JMESPath query Reservations[].Instances[].State.Name')144self.assert_contains('poll every')145self.assert_contains('This will exit with a return code of 255 after')146self.assert_contains('``describe-instances``')147self.assert_contains('[--filters <value>]')148self.assert_contains('``--filters`` (list)')149self.assert_contains('======\nOutput\n======\n\nNone')150151152class TestWait(BaseAWSCommandParamsTest):153""" This is merely a smoke test.154155Its purpose is to test that the wait command can be run proberly for156various services. It is by no means exhaustive.157"""158def test_ec2_instance_running(self):159cmdline = 'ec2 wait instance-running'160cmdline += ' --instance-ids i-12345678 i-87654321'161cmdline += """ --filters {"Name":"group-name","Values":["foobar"]}"""162result = {'Filters': [{'Name': 'group-name',163'Values': ['foobar']}],164'InstanceIds': ['i-12345678', 'i-87654321']}165self.parsed_response = {166'Reservations': [{167'Instances': [{168'State': {169'Name': 'running'170}171}]172}]173}174self.assert_params_for_cmd(cmdline, result)175176def test_dynamodb_table_exists(self):177cmdline = 'dynamodb wait table-exists'178cmdline += ' --table-name mytable'179result = {"TableName": "mytable"}180self.parsed_response = {'Table': {'TableStatus': 'ACTIVE'}}181self.assert_params_for_cmd(cmdline, result)182183def test_elastictranscoder_jobs_complete(self):184cmdline = 'rds wait db-instance-available'185cmdline += ' --db-instance-identifier abc'186result = {'DBInstanceIdentifier': 'abc'}187self.parsed_response = {188'DBInstances': [{189'DBInstanceStatus': 'available'190}]191}192self.assert_params_for_cmd(cmdline, result)193194195class TestWaiterStateCommandBuilder(unittest.TestCase):196def setUp(self):197self.session = mock.Mock()198self.service_model = mock.Mock()199200# Create some waiters.201self.model = WaiterModel({202'version': 2,203'waiters': {204'InstanceRunning': {205'description': 'My waiter description.',206'delay': 1,207'maxAttempts': 10,208'operation': 'MyOperation',209},210'BucketExists': {211'description': 'My waiter description.',212'operation': 'MyOperation',213'delay': 1,214'maxAttempts': 10,215}216}217})218219self.waiter_builder = WaiterStateCommandBuilder(220self.session,221self.model,222self.service_model223)224225def test_build_waiter_state_cmds(self):226subcommand_table = {}227self.waiter_builder.build_all_waiter_state_cmds(subcommand_table)228# Check the commands are in the command table229self.assertEqual(len(subcommand_table), 2)230self.assertIn('instance-running', subcommand_table)231self.assertIn('bucket-exists', subcommand_table)232233# Make sure that the correct operation object was used.234self.service_model.operation_model.assert_called_with('MyOperation')235236# Introspect the commands in the command table237instance_running_cmd = subcommand_table['instance-running']238bucket_exists_cmd = subcommand_table['bucket-exists']239240# Check that the instance type is correct.241self.assertIsInstance(instance_running_cmd, WaiterStateCommand)242self.assertIsInstance(bucket_exists_cmd, WaiterStateCommand)243244# Check the descriptions are set correctly.245self.assertEqual(246instance_running_cmd.DESCRIPTION,247'My waiter description. It will poll every 1 seconds until '248'a successful state has been reached. This will exit with a '249'return code of 255 after 10 failed checks.'250)251self.assertEqual(252bucket_exists_cmd.DESCRIPTION,253'My waiter description. It will poll every 1 seconds until '254'a successful state has been reached. This will exit with a '255'return code of 255 after 10 failed checks.'256)257258259class TestWaiterStateDocBuilder(unittest.TestCase):260def setUp(self):261self.waiter_config = mock.Mock()262self.waiter_config.description = ''263self.waiter_config.operation = 'MyOperation'264self.waiter_config.delay = 5265self.waiter_config.max_attempts = 20266267# Set up the acceptors.268self.success_acceptor = mock.Mock()269self.success_acceptor.state = 'success'270self.fail_acceptor = mock.Mock()271self.fail_acceptor.state = 'failure'272self.error_acceptor = mock.Mock()273self.error_acceptor.state = 'error'274self.waiter_config.acceptors = [275self.fail_acceptor,276self.success_acceptor,277self.error_acceptor278]279280self.doc_builder = WaiterStateDocBuilder(self.waiter_config)281282def test_config_provided_description(self):283# Description is provided by the config file284self.waiter_config.description = 'My description.'285description = self.doc_builder.build_waiter_state_description()286self.assertEqual(287description,288'My description. It will poll every 5 seconds until a '289'successful state has been reached. This will exit with a '290'return code of 255 after 20 failed checks.')291292def test_error_acceptor(self):293self.success_acceptor.matcher = 'error'294self.success_acceptor.expected = 'MyException'295description = self.doc_builder.build_waiter_state_description()296self.assertEqual(297description,298'Wait until MyException is thrown when polling with '299'``my-operation``. It will poll every 5 seconds until a '300'successful state has been reached. This will exit with a '301'return code of 255 after 20 failed checks.'302)303304def test_status_acceptor(self):305self.success_acceptor.matcher = 'status'306self.success_acceptor.expected = 200307description = self.doc_builder.build_waiter_state_description()308self.assertEqual(309description,310'Wait until 200 response is received when polling with '311'``my-operation``. It will poll every 5 seconds until a '312'successful state has been reached. This will exit with a '313'return code of 255 after 20 failed checks.'314)315316def test_path_acceptor(self):317self.success_acceptor.matcher = 'path'318self.success_acceptor.argument = 'MyResource.name'319self.success_acceptor.expected = 'running'320description = self.doc_builder.build_waiter_state_description()321self.assertEqual(322description,323'Wait until JMESPath query MyResource.name returns running when '324'polling with ``my-operation``. It will poll every 5 seconds '325'until a successful state has been reached. This will exit with '326'a return code of 255 after 20 failed checks.'327)328329def test_path_all_acceptor(self):330self.success_acceptor.matcher = 'pathAll'331self.success_acceptor.argument = 'MyResource[].name'332self.success_acceptor.expected = 'running'333description = self.doc_builder.build_waiter_state_description()334self.assertEqual(335description,336'Wait until JMESPath query MyResource[].name returns running for '337'all elements when polling with ``my-operation``. It will poll '338'every 5 seconds until a successful state has been reached. '339'This will exit with a return code of 255 after 20 failed checks.'340)341342def test_path_any_acceptor(self):343self.success_acceptor.matcher = 'pathAny'344self.success_acceptor.argument = 'MyResource[].name'345self.success_acceptor.expected = 'running'346description = self.doc_builder.build_waiter_state_description()347self.assertEqual(348description,349'Wait until JMESPath query MyResource[].name returns running for '350'any element when polling with ``my-operation``. It will poll '351'every 5 seconds until a successful state has been reached. '352'This will exit with a return code of 255 after 20 failed checks.'353)354355356class TestWaiterCaller(unittest.TestCase):357def test_invoke(self):358waiter = mock.Mock()359waiter_name = 'my_waiter'360session = mock.Mock()361session.create_client.return_value.get_waiter.return_value = waiter362363parameters = {'Foo': 'bar', 'Baz': 'biz'}364parsed_globals = mock.Mock()365parsed_globals.region = 'us-east-1'366parsed_globals.endpoint_url = 'myurl'367parsed_globals.verify_ssl = True368369waiter_caller = WaiterCaller(session, waiter_name)370waiter_caller.invoke('myservice', 'MyWaiter', parameters,371parsed_globals)372373# Make sure the client was created properly.374session.create_client.assert_called_with(375'myservice',376region_name=parsed_globals.region,377endpoint_url=parsed_globals.endpoint_url,378verify=parsed_globals.verify_ssl379)380381# Make sure we got the correct waiter.382session.create_client.return_value.get_waiter.assert_called_with(383waiter_name)384385# Ensure the wait command was called properly.386waiter.wait.assert_called_with(387Foo='bar', Baz='biz')388389390