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