Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/test_clidriver.py
1566 views
1
# -*- coding: utf-8 -*-
2
# Copyright 2012-2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
#
4
# Licensed under the Apache License, Version 2.0 (the "License"). You
5
# may not use this file except in compliance with the License. A copy of
6
# the License is located at
7
#
8
# http://aws.amazon.com/apache2.0/
9
#
10
# or in the "license" file accompanying this file. This file is
11
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12
# ANY KIND, either express or implied. See the License for the specific
13
# language governing permissions and limitations under the License.
14
from awscli.testutils import mock
15
from awscli.testutils import unittest
16
from awscli.testutils import BaseAWSCommandParamsTest
17
import logging
18
import io
19
import sys
20
import importlib
21
22
from botocore.awsrequest import AWSResponse
23
from botocore.exceptions import NoCredentialsError
24
from botocore.compat import OrderedDict
25
import botocore.model
26
27
import awscli
28
from awscli.clidriver import CLIDriver
29
from awscli.clidriver import create_clidriver
30
from awscli.clidriver import CustomArgument
31
from awscli.clidriver import CLICommand
32
from awscli.clidriver import ServiceCommand
33
from awscli.clidriver import ServiceOperation
34
from awscli.paramfile import URIArgumentHandler
35
from awscli.customizations.commands import BasicCommand
36
from awscli import formatter
37
from awscli.argparser import HELP_BLURB
38
from awscli.compat import StringIO
39
from botocore.hooks import HierarchicalEmitter
40
41
42
GET_DATA = {
43
'cli': {
44
'description': 'description',
45
'synopsis': 'usage: foo',
46
'options': {
47
"debug": {
48
"action": "store_true",
49
"help": "Turn on debug logging"
50
},
51
"output": {
52
"choices": [
53
"json",
54
"text",
55
"table"
56
],
57
"metavar": "output_format"
58
},
59
"query": {
60
"help": ""
61
},
62
"profile": {
63
"help": "",
64
"metavar": "profile_name"
65
},
66
"region": {
67
"metavar": "region_name"
68
},
69
"endpoint-url": {
70
"help": "",
71
"metavar": "endpoint_url"
72
},
73
"no-verify-ssl": {
74
"action": "store_false",
75
"dest": "verify_ssl",
76
"help": "",
77
},
78
"no-paginate": {
79
"action": "store_false",
80
"help": "",
81
"dest": "paginate"
82
},
83
"page-size": {
84
"type": "int",
85
"help": "",
86
},
87
"read-timeout": {
88
"type": "int",
89
"help": ""
90
},
91
"connect-timeout": {
92
"type": "int",
93
"help": ""
94
}
95
}
96
},
97
}
98
99
GET_VARIABLE = {
100
'provider': 'aws',
101
'output': 'json',
102
'api_versions': {}
103
}
104
105
106
MINI_SERVICE = {
107
"metadata":{
108
"apiVersion":"2006-03-01",
109
"endpointPrefix":"s3",
110
"globalEndpoint":"s3.amazonaws.com",
111
"signatureVersion":"s3",
112
"protocol":"rest-xml"
113
},
114
"operations":{
115
"ListObjects":{
116
"name":"ListObjects",
117
"http":{
118
"method":"GET",
119
"requestUri":"/{Bucket}"
120
},
121
"input":{"shape":"ListObjectsRequest"},
122
"output":{"shape":"ListObjectsOutput"},
123
},
124
"IdempotentOperation":{
125
"name":"IdempotentOperation",
126
"http":{
127
"method":"GET",
128
"requestUri":"/{Bucket}"
129
},
130
"input":{"shape":"IdempotentOperationRequest"},
131
"output":{"shape":"ListObjectsOutput"},
132
},
133
},
134
"shapes":{
135
"ListObjectsOutput":{
136
"type":"structure",
137
"members":{
138
"IsTruncated":{
139
"shape":"IsTruncated",
140
"documentation":""
141
},
142
"NextMarker":{
143
"shape":"NextMarker",
144
},
145
"Contents":{"shape":"Contents"},
146
},
147
},
148
"IdempotentOperationRequest":{
149
"type":"structure",
150
"required": "token",
151
"members":{
152
"token":{
153
"shape":"Token",
154
"idempotencyToken": True,
155
},
156
}
157
},
158
"ListObjectsRequest":{
159
"type":"structure",
160
"required":["Bucket"],
161
"members": OrderedDict([
162
("Bucket", {
163
"shape":"BucketName",
164
"location":"uri",
165
"locationName":"Bucket"
166
}),
167
("Marker", {
168
"shape":"Marker",
169
"location":"querystring",
170
"locationName":"marker",
171
}),
172
("MaxKeys", {
173
"shape":"MaxKeys",
174
"location":"querystring",
175
"locationName":"max-keys",
176
}),
177
]),
178
},
179
"BucketName":{"type":"string"},
180
"MaxKeys":{"type":"integer"},
181
"Marker":{"type":"string"},
182
"IsTruncated":{"type":"boolean"},
183
"NextMarker":{"type":"string"},
184
"Contents":{"type":"string"},
185
"Token":{"type":"string"},
186
}
187
}
188
189
190
class FakeSession(object):
191
def __init__(self, emitter=None):
192
self.operation = None
193
if emitter is None:
194
emitter = HierarchicalEmitter()
195
self.emitter = emitter
196
self.profile = None
197
self.stream_logger_args = None
198
self.credentials = 'fakecredentials'
199
self.session_vars = {}
200
201
def register(self, event_name, handler):
202
self.emitter.register(event_name, handler)
203
204
def emit(self, event_name, **kwargs):
205
return self.emitter.emit(event_name, **kwargs)
206
207
def emit_first_non_none_response(self, event_name, **kwargs):
208
responses = self.emitter.emit(event_name, **kwargs)
209
for _, response in responses:
210
if response is not None:
211
return response
212
213
def get_component(self, name):
214
if name == 'event_emitter':
215
return self.emitter
216
217
def create_client(self, *args, **kwargs):
218
client = mock.Mock()
219
client.list_objects.return_value = {}
220
client.can_paginate.return_value = False
221
return client
222
223
def get_available_services(self):
224
return ['s3']
225
226
def get_data(self, name):
227
return GET_DATA[name]
228
229
def get_config_variable(self, name):
230
if name in GET_VARIABLE:
231
return GET_VARIABLE[name]
232
return self.session_vars[name]
233
234
def get_service_model(self, name, api_version=None):
235
return botocore.model.ServiceModel(
236
MINI_SERVICE, service_name='s3')
237
238
def user_agent(self):
239
return 'user_agent'
240
241
def set_stream_logger(self, *args, **kwargs):
242
self.stream_logger_args = (args, kwargs)
243
244
def get_credentials(self):
245
return self.credentials
246
247
def set_config_variable(self, name, value):
248
if name == 'profile':
249
self.profile = value
250
else:
251
self.session_vars[name] = value
252
253
254
class FakeCommand(BasicCommand):
255
def _run_main(self, args, parsed_globals):
256
# We just return success. If this code is reached, it means that
257
# all the logic in the __call__ method has successfully been run.
258
# We subclass it here because the default implementation raises
259
# an exception and we don't want that behavior.
260
return 0
261
262
263
class FakeCommandVerify(FakeCommand):
264
def _run_main(self, args, parsed_globals):
265
# Verify passed arguments exist and then return success.
266
# This will fail if the expected structure is missing, e.g.
267
# if a string is passed in args instead of the expected
268
# structure from a custom schema.
269
assert args.bar[0]['Name'] == 'test'
270
return 0
271
272
273
class TestCliDriver(unittest.TestCase):
274
def setUp(self):
275
self.session = FakeSession()
276
277
def test_session_can_be_passed_in(self):
278
driver = CLIDriver(session=self.session)
279
self.assertEqual(driver.session, self.session)
280
281
def test_paginate_rc(self):
282
driver = CLIDriver(session=self.session)
283
rc = driver.main('s3 list-objects --bucket foo'.split())
284
self.assertEqual(rc, 0)
285
286
def test_no_profile(self):
287
driver = CLIDriver(session=self.session)
288
driver.main('s3 list-objects --bucket foo'.split())
289
self.assertEqual(driver.session.profile, None)
290
291
def test_profile(self):
292
driver = CLIDriver(session=self.session)
293
driver.main('s3 list-objects --bucket foo --profile foo'.split())
294
self.assertEqual(driver.session.profile, 'foo')
295
296
def test_region_is_set_for_session(self):
297
driver = CLIDriver(session=self.session)
298
driver.main('s3 list-objects --bucket foo --region us-east-2'.split())
299
self.assertEqual(
300
driver.session.get_config_variable('region'), 'us-east-2')
301
302
def test_error_logger(self):
303
driver = CLIDriver(session=self.session)
304
driver.main('s3 list-objects --bucket foo --profile foo'.split())
305
expected = {'log_level': logging.ERROR, 'logger_name': 'awscli'}
306
self.assertEqual(driver.session.stream_logger_args[1], expected)
307
308
def test_ctrl_c_is_handled(self):
309
driver = CLIDriver(session=self.session)
310
fake_client = mock.Mock()
311
fake_client.list_objects.side_effect = KeyboardInterrupt
312
fake_client.can_paginate.return_value = False
313
driver.session.create_client = mock.Mock(return_value=fake_client)
314
rc = driver.main('s3 list-objects --bucket foo'.split())
315
self.assertEqual(rc, 130)
316
317
def test_error_unicode(self):
318
stderr_b = io.BytesIO()
319
stderr = io.TextIOWrapper(stderr_b, encoding="UTF-8")
320
driver = CLIDriver(session=self.session)
321
fake_client = mock.Mock()
322
fake_client.list_objects.side_effect = Exception(u"☃")
323
fake_client.can_paginate.return_value = False
324
driver.session.create_client = mock.Mock(return_value=fake_client)
325
with mock.patch("sys.stderr", stderr):
326
with mock.patch("locale.getpreferredencoding", lambda: "UTF-8"):
327
rc = driver.main('s3 list-objects --bucket foo'.split())
328
stderr.flush()
329
self.assertEqual(rc, 255)
330
self.assertEqual(stderr_b.getvalue().strip(), u"☃".encode("UTF-8"))
331
332
333
class TestCliDriverHooks(unittest.TestCase):
334
# These tests verify the proper hooks are emitted in clidriver.
335
def setUp(self):
336
self.session = FakeSession()
337
self.emitter = mock.Mock()
338
self.emitter.emit.return_value = []
339
self.stdout = StringIO()
340
self.stderr = StringIO()
341
self.stdout_patch = mock.patch('sys.stdout', self.stdout)
342
#self.stdout_patch.start()
343
self.stderr_patch = mock.patch('sys.stderr', self.stderr)
344
self.stderr_patch.start()
345
346
def tearDown(self):
347
#self.stdout_patch.stop()
348
self.stderr_patch.stop()
349
350
def assert_events_fired_in_order(self, events):
351
args = self.emitter.emit.call_args_list
352
actual_events = [arg[0][0] for arg in args]
353
self.assertEqual(actual_events, events)
354
355
def serialize_param(self, param, value, **kwargs):
356
if kwargs['cli_argument'].name == 'bucket':
357
return value + '-altered!'
358
359
def test_expected_events_are_emitted_in_order(self):
360
self.emitter.emit.return_value = []
361
self.session.emitter = self.emitter
362
driver = CLIDriver(session=self.session)
363
driver.main('s3 list-objects --bucket foo'.split())
364
self.assert_events_fired_in_order([
365
# Events fired while parser is being created.
366
'building-command-table.main',
367
'building-top-level-params',
368
'top-level-args-parsed',
369
'session-initialized',
370
'building-command-table.s3',
371
'building-argument-table.s3.list-objects',
372
'before-building-argument-table-parser.s3.list-objects',
373
'operation-args-parsed.s3.list-objects',
374
'load-cli-arg.s3.list-objects.bucket',
375
'process-cli-arg.s3.list-objects',
376
'load-cli-arg.s3.list-objects.marker',
377
'load-cli-arg.s3.list-objects.max-keys',
378
'calling-command.s3.list-objects'
379
])
380
381
def test_create_help_command(self):
382
# When we generate the HTML docs, we don't actually run
383
# commands, we just call the create_help_command methods.
384
# We want to make sure that in this case, the corresponding
385
# building-command-table events are fired.
386
# The test above will prove that is true when running a command.
387
# This test proves it is true when generating the HTML docs.
388
self.emitter.emit.return_value = []
389
self.session.emitter = self.emitter
390
driver = CLIDriver(session=self.session)
391
main_hc = driver.create_help_command()
392
command = main_hc.command_table['s3']
393
command.create_help_command()
394
self.assert_events_fired_in_order([
395
# Events fired while parser is being created.
396
'building-command-table.main',
397
'building-top-level-params',
398
'building-command-table.s3',
399
])
400
401
def test_unknown_command_suggests_help(self):
402
driver = CLIDriver(session=self.session)
403
# We're catching SystemExit here because this is raised from the bowels
404
# of argparser so short of patching the ArgumentParser's exit() method,
405
# we can just catch SystemExit.
406
with self.assertRaises(SystemExit):
407
# Note the typo in 'list-objects'
408
driver.main('s3 list-objecst --bucket foo --unknown-arg foo'.split())
409
# Tell the user what went wrong.
410
self.assertIn("Invalid choice: 'list-objecst'", self.stderr.getvalue())
411
# Offer the user a suggestion.
412
self.assertIn("maybe you meant:\n\n * list-objects", self.stderr.getvalue())
413
414
415
class TestSearchPath(unittest.TestCase):
416
def tearDown(self):
417
importlib.reload(awscli)
418
419
@mock.patch('os.pathsep', ';')
420
@mock.patch('os.environ', {'AWS_DATA_PATH': 'c:\\foo;c:\\bar'})
421
def test_windows_style_search_path(self):
422
driver = CLIDriver()
423
# Because the os.environ patching happens at import time,
424
# we have to force a reimport of the module to test our changes.
425
importlib.reload(awscli)
426
# Our two overrides should be the last two elements in the search path.
427
search_paths = driver.session.get_component(
428
'data_loader').search_paths
429
self.assertIn('c:\\foo', search_paths)
430
self.assertIn('c:\\bar', search_paths)
431
432
433
class TestAWSCommand(BaseAWSCommandParamsTest):
434
# These tests will simulate running actual aws commands
435
# but with the http part mocked out.
436
def setUp(self):
437
super(TestAWSCommand, self).setUp()
438
self.stderr = StringIO()
439
self.stderr_patch = mock.patch('sys.stderr', self.stderr)
440
self.stderr_patch.start()
441
442
def tearDown(self):
443
super(TestAWSCommand, self).tearDown()
444
self.stderr_patch.stop()
445
446
def inject_new_param(self, argument_table, **kwargs):
447
argument = CustomArgument('unknown-arg', {})
448
argument.add_to_arg_table(argument_table)
449
450
def inject_new_param_no_paramfile(self, argument_table, **kwargs):
451
argument = CustomArgument('unknown-arg', no_paramfile=True)
452
argument.add_to_arg_table(argument_table)
453
454
def inject_command(self, command_table, session, **kwargs):
455
command = FakeCommand(session)
456
command.NAME = 'foo'
457
command.ARG_TABLE = [
458
{'name': 'bar', 'action': 'store'}
459
]
460
command_table['foo'] = command
461
462
def inject_command_schema(self, command_table, session, **kwargs):
463
command = FakeCommandVerify(session)
464
command.NAME = 'foo'
465
466
# Build a schema using all the types we are interested in
467
schema = {
468
"type": "array",
469
"items": {
470
"type": "object",
471
"properties": {
472
"Name": {
473
"type": "string",
474
"required": True
475
},
476
"Count": {
477
"type": "integer"
478
}
479
}
480
}
481
}
482
483
command.ARG_TABLE = [
484
{'name': 'bar', 'schema': schema}
485
]
486
487
command_table['foo'] = command
488
489
def test_event_emission_for_top_level_params(self):
490
driver = create_clidriver()
491
# --unknown-foo is an known arg, so we expect a 255 rc.
492
rc = driver.main('ec2 describe-instances --unknown-arg foo'.split())
493
self.assertEqual(rc, 255)
494
self.assertIn('Unknown options: --unknown-arg', self.stderr.getvalue())
495
496
# The argument table is memoized in the CLIDriver object. So
497
# when we call main() above, it will get created and cached
498
# and the argument table won't get created again (and therefore
499
# the building-top-level-params event will not get generated again).
500
# So, for this test we need to create a new driver object.
501
driver = create_clidriver()
502
driver.session.register(
503
'building-top-level-params', self.inject_new_param)
504
driver.session.register(
505
'top-level-args-parsed',
506
lambda parsed_args, **kwargs: args_seen.append(parsed_args))
507
508
args_seen = []
509
510
# Now we should get an rc of 0 as the arg is expected
511
# (though nothing actually does anything with the arg).
512
self.patch_make_request()
513
rc = driver.main('ec2 describe-instances --unknown-arg foo'.split())
514
self.assertEqual(rc, 0)
515
self.assertEqual(len(args_seen), 1)
516
self.assertEqual(args_seen[0].unknown_arg, 'foo')
517
518
@mock.patch('awscli.paramfile.URIArgumentHandler',
519
spec=URIArgumentHandler)
520
def test_custom_arg_paramfile(self, mock_handler):
521
mock_paramfile = mock.Mock(autospec=True)
522
mock_paramfile.return_value = None
523
mock_handler.return_value = mock_paramfile
524
525
driver = create_clidriver()
526
driver.session.register(
527
'building-argument-table', self.inject_new_param)
528
529
self.patch_make_request()
530
rc = driver.main(
531
'ec2 describe-instances --unknown-arg file:///foo'.split())
532
533
self.assertEqual(rc, 0)
534
535
# Make sure uri_param was called
536
mock_paramfile.assert_any_call(
537
event_name='load-cli-arg.ec2.describe-instances.unknown-arg',
538
operation_name='describe-instances',
539
param=mock.ANY,
540
service_name='ec2',
541
value='file:///foo',
542
)
543
# Make sure it was called with our passed-in URI
544
self.assertEqual(
545
'file:///foo',
546
mock_paramfile.call_args_list[-1][1]['value'])
547
548
@mock.patch('awscli.paramfile.URIArgumentHandler',
549
spec=URIArgumentHandler)
550
def test_custom_command_paramfile(self, mock_handler):
551
mock_paramfile = mock.Mock(autospec=True)
552
mock_paramfile.return_value = None
553
mock_handler.return_value = mock_paramfile
554
555
driver = create_clidriver()
556
driver.session.register(
557
'building-command-table', self.inject_command)
558
559
self.patch_make_request()
560
rc = driver.main(
561
'ec2 foo --bar file:///foo'.split())
562
563
self.assertEqual(rc, 0)
564
565
mock_paramfile.assert_any_call(
566
event_name='load-cli-arg.custom.foo.bar',
567
operation_name='foo',
568
param=mock.ANY,
569
service_name='custom',
570
value='file:///foo',
571
)
572
573
def test_custom_arg_no_paramfile(self):
574
driver = create_clidriver()
575
driver.session.register(
576
'building-argument-table', self.inject_new_param_no_paramfile)
577
578
self.patch_make_request()
579
rc = driver.main(
580
'ec2 describe-instances --unknown-arg file:///foo'.split())
581
582
self.assertEqual(rc, 0)
583
584
def test_custom_command_schema(self):
585
driver = create_clidriver()
586
driver.session.register(
587
'building-command-table', self.inject_command_schema)
588
589
self.patch_make_request()
590
591
# Test single shorthand item
592
rc = driver.main(
593
'ec2 foo --bar Name=test,Count=4'.split())
594
595
self.assertEqual(rc, 0)
596
597
# Test shorthand list of items with optional values
598
rc = driver.main(
599
'ec2 foo --bar Name=test,Count=4 Name=another'.split())
600
601
self.assertEqual(rc, 0)
602
603
# Test missing require shorthand item
604
rc = driver.main(
605
'ec2 foo --bar Count=4'.split())
606
607
self.assertEqual(rc, 255)
608
609
# Test extra unknown shorthand item
610
rc = driver.main(
611
'ec2 foo --bar Name=test,Unknown='.split())
612
613
self.assertEqual(rc, 255)
614
615
# Test long form JSON
616
rc = driver.main(
617
'ec2 foo --bar {"Name":"test","Count":4}'.split())
618
619
self.assertEqual(rc, 0)
620
621
# Test malformed long form JSON
622
rc = driver.main(
623
'ec2 foo --bar {"Name":"test",Count:4}'.split())
624
625
self.assertEqual(rc, 255)
626
627
def test_empty_params_gracefully_handled(self):
628
# Simulates the equivalent in bash: --identifies ""
629
cmd = 'ses get-identity-dkim-attributes --identities'.split()
630
cmd.append('')
631
self.assert_params_for_cmd(cmd,expected_rc=0)
632
633
def test_file_param_does_not_exist(self):
634
driver = create_clidriver()
635
rc = driver.main('ec2 describe-instances '
636
'--filters file://does/not/exist.json'.split())
637
self.assertEqual(rc, 255)
638
error_msg = self.stderr.getvalue()
639
self.assertIn("Error parsing parameter '--filters': "
640
"Unable to load paramfile file://does/not/exist.json",
641
error_msg)
642
self.assertIn("No such file or directory", error_msg)
643
644
def test_aws_configure_in_error_message_no_credentials(self):
645
driver = create_clidriver()
646
def raise_exception(*args, **kwargs):
647
raise NoCredentialsError()
648
driver.session.register(
649
'building-command-table',
650
lambda command_table, **kwargs: \
651
command_table.__setitem__('ec2', raise_exception))
652
with mock.patch('sys.stderr') as f:
653
driver.main('ec2 describe-instances'.split())
654
self.assertEqual(
655
f.write.call_args_list[0][0][0],
656
'Unable to locate credentials. '
657
'You can configure credentials by running "aws configure".')
658
659
def test_override_calling_command(self):
660
self.driver = create_clidriver()
661
662
# Make a function that will return an override such that its value
663
# is used over whatever is returned by the invoker which is usually
664
# zero.
665
def override_with_rc(**kwargs):
666
return 20
667
668
self.driver.session.register('calling-command', override_with_rc)
669
rc = self.driver.main('ec2 describe-instances'.split())
670
# Check that the overridden rc is as expected.
671
self.assertEqual(rc, 20)
672
673
def test_override_calling_command_error(self):
674
self.driver = create_clidriver()
675
676
# Make a function that will return an error. The handler will cause
677
# an error to be returned and later raised.
678
def override_with_error(**kwargs):
679
return ValueError()
680
681
self.driver.session.register('calling-command', override_with_error)
682
# An exception should be thrown as a result of the handler, which
683
# will result in 255 rc.
684
rc = self.driver.main('ec2 describe-instances'.split())
685
self.assertEqual(rc, 255)
686
687
def test_help_blurb_in_error_message(self):
688
with self.assertRaises(SystemExit):
689
self.driver.main([])
690
self.assertIn(HELP_BLURB, self.stderr.getvalue())
691
692
def test_help_blurb_in_service_error_message(self):
693
with self.assertRaises(SystemExit):
694
self.driver.main(['ec2'])
695
self.assertIn(HELP_BLURB, self.stderr.getvalue())
696
697
def test_help_blurb_in_operation_error_message(self):
698
with self.assertRaises(SystemExit):
699
self.driver.main(['s3api', 'list-objects'])
700
self.assertIn(HELP_BLURB, self.stderr.getvalue())
701
702
def test_help_blurb_in_unknown_argument_error_message(self):
703
with self.assertRaises(SystemExit):
704
self.driver.main(['s3api', 'list-objects', '--help'])
705
self.assertIn(HELP_BLURB, self.stderr.getvalue())
706
707
def test_idempotency_token_is_not_required_in_help_text(self):
708
with self.assertRaises(SystemExit):
709
self.driver.main(['servicecatalog', 'create-constraint'])
710
self.assertNotIn('--idempotency-token', self.stderr.getvalue())
711
712
class TestHowClientIsCreated(BaseAWSCommandParamsTest):
713
def setUp(self):
714
super(TestHowClientIsCreated, self).setUp()
715
self.endpoint_creator_patch = mock.patch(
716
'botocore.args.EndpointCreator')
717
self.endpoint_creator = self.endpoint_creator_patch.start()
718
self.create_endpoint = \
719
self.endpoint_creator.return_value.create_endpoint
720
self.endpoint = self.create_endpoint.return_value
721
self.endpoint.host = 'https://example.com'
722
# Have the endpoint give a dummy empty response.
723
http_response = AWSResponse(None, 200, {}, None)
724
self.endpoint.make_request.return_value = (
725
http_response, {})
726
727
def tearDown(self):
728
super(TestHowClientIsCreated, self).tearDown()
729
self.endpoint_creator_patch.stop()
730
731
def test_aws_with_endpoint_url(self):
732
self.assert_params_for_cmd(
733
'ec2 describe-instances --endpoint-url https://foobar.com/',
734
expected_rc=0)
735
self.assertEqual(self.create_endpoint.call_args[1]['endpoint_url'],
736
'https://foobar.com/')
737
738
def test_aws_with_region(self):
739
self.assert_params_for_cmd(
740
'ec2 describe-instances --region us-west-2',
741
expected_rc=0)
742
self.assertEqual(
743
self.create_endpoint.call_args[1]['region_name'], 'us-west-2')
744
745
def test_aws_with_verify_false(self):
746
self.assert_params_for_cmd(
747
'ec2 describe-instances --region us-east-1 --no-verify-ssl',
748
expected_rc=0)
749
# Because we used --no-verify-ssl, create_endpoint should be
750
# called with verify=False
751
call_args = self.create_endpoint.call_args
752
self.assertFalse(call_args[1]['verify'])
753
754
def test_aws_with_cacert_env_var(self):
755
self.environ['AWS_CA_BUNDLE'] = '/path/cacert.pem'
756
self.assert_params_for_cmd(
757
'ec2 describe-instances --region us-east-1',
758
expected_rc=0)
759
call_args = self.create_endpoint.call_args
760
self.assertEqual(call_args[1]['verify'], '/path/cacert.pem')
761
762
def test_aws_with_read_timeout(self):
763
self.assert_params_for_cmd(
764
'lambda invoke --function-name foo out.log --cli-read-timeout 90',
765
expected_rc=0)
766
call_args = self.create_endpoint.call_args
767
self.assertEqual(call_args[1]['timeout'][1], 90)
768
769
def test_aws_with_blocking_read_timeout(self):
770
self.assert_params_for_cmd(
771
'lambda invoke --function-name foo out.log --cli-read-timeout 0',
772
expected_rc=0)
773
call_args = self.create_endpoint.call_args
774
self.assertEqual(call_args[1]['timeout'][1], None)
775
776
def test_aws_with_connnect_timeout(self):
777
self.assert_params_for_cmd(
778
'lambda invoke --function-name foo out.log '
779
'--cli-connect-timeout 90',
780
expected_rc=0)
781
call_args = self.create_endpoint.call_args
782
self.assertEqual(call_args[1]['timeout'][0], 90)
783
784
def test_aws_with_blocking_connect_timeout(self):
785
self.assert_params_for_cmd(
786
'lambda invoke --function-name foo out.log '
787
'--cli-connect-timeout 0',
788
expected_rc=0)
789
call_args = self.create_endpoint.call_args
790
self.assertEqual(call_args[1]['timeout'][0], None)
791
792
def test_aws_with_read_and_connnect_timeout(self):
793
self.assert_params_for_cmd(
794
'lambda invoke --function-name foo out.log '
795
'--cli-read-timeout 70 --cli-connect-timeout 90',
796
expected_rc=0)
797
call_args = self.create_endpoint.call_args
798
self.assertEqual(call_args[1]['timeout'], (90, 70))
799
800
801
class TestHTTPParamFileDoesNotExist(BaseAWSCommandParamsTest):
802
803
def setUp(self):
804
super(TestHTTPParamFileDoesNotExist, self).setUp()
805
self.stderr = StringIO()
806
self.stderr_patch = mock.patch('sys.stderr', self.stderr)
807
self.stderr_patch.start()
808
809
def tearDown(self):
810
super(TestHTTPParamFileDoesNotExist, self).tearDown()
811
self.stderr_patch.stop()
812
813
def test_http_file_param_does_not_exist(self):
814
error_msg = ("Error parsing parameter '--filters': "
815
"Unable to retrieve http://does/not/exist.json: "
816
"received non 200 status code of 404")
817
with mock.patch('awscli.paramfile.URLLib3Session.send') as get:
818
get.return_value.status_code = 404
819
self.assert_params_for_cmd(
820
'ec2 describe-instances --filters http://does/not/exist.json',
821
expected_rc=255, stderr_contains=error_msg)
822
823
824
class TestVerifyArgument(BaseAWSCommandParamsTest):
825
def setUp(self):
826
super(TestVerifyArgument, self).setUp()
827
self.driver.session.register('top-level-args-parsed', self.record_args)
828
self.recorded_args = None
829
830
def record_args(self, parsed_args, **kwargs):
831
self.recorded_args = parsed_args
832
833
def test_no_verify_argument(self):
834
self.assert_params_for_cmd('s3api list-buckets --no-verify-ssl'.split())
835
self.assertFalse(self.recorded_args.verify_ssl)
836
837
def test_verify_argument_is_none_by_default(self):
838
self.assert_params_for_cmd('s3api list-buckets'.split())
839
self.assertIsNone(self.recorded_args.verify_ssl)
840
841
842
class TestFormatter(BaseAWSCommandParamsTest):
843
def test_bad_output(self):
844
with self.assertRaises(ValueError):
845
formatter.get_formatter('bad-type', None)
846
847
848
class TestCLICommand(unittest.TestCase):
849
def setUp(self):
850
self.cmd = CLICommand()
851
852
def test_name(self):
853
with self.assertRaises(NotImplementedError):
854
self.cmd.name
855
with self.assertRaises(NotImplementedError):
856
self.cmd.name = 'foo'
857
858
def test_lineage(self):
859
self.assertEqual(self.cmd.lineage, [self.cmd])
860
861
def test_lineage_names(self):
862
with self.assertRaises(NotImplementedError):
863
self.cmd.lineage_names
864
865
def test_arg_table(self):
866
self.assertEqual(self.cmd.arg_table, {})
867
868
869
class TestServiceCommand(unittest.TestCase):
870
def setUp(self):
871
self.name = 'foo'
872
self.session = FakeSession()
873
self.cmd = ServiceCommand(self.name, self.session)
874
875
def test_name(self):
876
self.assertEqual(self.cmd.name, self.name)
877
self.cmd.name = 'bar'
878
self.assertEqual(self.cmd.name, 'bar')
879
880
def test_lineage(self):
881
cmd = CLICommand()
882
self.assertEqual(self.cmd.lineage, [self.cmd])
883
self.cmd.lineage = [cmd]
884
self.assertEqual(self.cmd.lineage, [cmd])
885
886
def test_lineage_names(self):
887
self.assertEqual(self.cmd.lineage_names, ['foo'])
888
889
def test_pass_lineage_to_child(self):
890
# In order to introspect the service command's subcommands
891
# we introspect the subcommand via the help command since
892
# a service command's command table is not public.
893
help_command = self.cmd.create_help_command()
894
child_cmd = help_command.command_table['list-objects']
895
self.assertEqual(child_cmd.lineage,
896
[self.cmd, child_cmd])
897
self.assertEqual(child_cmd.lineage_names, ['foo', 'list-objects'])
898
899
def test_help_event_class(self):
900
# Ensures it sends the right event name to the help command
901
help_command = self.cmd.create_help_command()
902
self.assertEqual(help_command.event_class, 'foo')
903
child_cmd = help_command.command_table['list-objects']
904
# Check the ``ServiceOperation`` class help command as well
905
child_help_cmd = child_cmd.create_help_command()
906
self.assertEqual(child_help_cmd.event_class, 'foo.list-objects')
907
908
909
class TestServiceOperation(unittest.TestCase):
910
def setUp(self):
911
self.name = 'foo'
912
operation = mock.Mock(spec=botocore.model.OperationModel)
913
operation.deprecated = False
914
self.mock_operation = operation
915
self.cmd = ServiceOperation(self.name, None, None, operation, None)
916
917
def test_name(self):
918
self.assertEqual(self.cmd.name, self.name)
919
self.cmd.name = 'bar'
920
self.assertEqual(self.cmd.name, 'bar')
921
922
def test_lineage(self):
923
cmd = CLICommand()
924
self.assertEqual(self.cmd.lineage, [self.cmd])
925
self.cmd.lineage = [cmd]
926
self.assertEqual(self.cmd.lineage, [cmd])
927
928
def test_lineage_names(self):
929
self.assertEqual(self.cmd.lineage_names, ['foo'])
930
931
def test_deprecated_operation(self):
932
self.mock_operation.deprecated = True
933
cmd = ServiceOperation(self.name, None, None, self.mock_operation,
934
None)
935
self.assertTrue(getattr(cmd, '_UNDOCUMENTED'))
936
937
def test_idempotency_token_is_not_required(self):
938
session = FakeSession()
939
name = 'IdempotentOperation'
940
service_model = session.get_service_model('s3')
941
operation_model = service_model.operation_model(name)
942
cmd = ServiceOperation(name, None, None, operation_model, session)
943
arg_table = cmd.arg_table
944
token_argument = arg_table.get('token')
945
self.assertFalse(token_argument.required,
946
'Idempotency tokens should not be required')
947
948
949
if __name__ == '__main__':
950
unittest.main()
951
952