Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aws
GitHub Repository: aws/aws-cli
Path: blob/develop/tests/unit/customizations/configure/test_configure.py
1569 views
1
# Copyright 2013 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
#
3
# Licensed under the Apache License, Version 2.0 (the "License"). You
4
# may not use this file except in compliance with the License. A copy of
5
# the License is located at
6
#
7
# http://aws.amazon.com/apache2.0/
8
#
9
# or in the "license" file accompanying this file. This file is
10
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
11
# ANY KIND, either express or implied. See the License for the specific
12
# language governing permissions and limitations under the License.
13
import os
14
15
from awscli.customizations.configure import configure, ConfigValue, NOT_SET
16
from awscli.customizations.configure import profile_to_section
17
from awscli.testutils import mock, unittest
18
from awscli.compat import StringIO
19
20
from . import FakeSession
21
22
23
class TestConfigureCommand(unittest.TestCase):
24
25
def setUp(self):
26
self.writer = mock.Mock()
27
self.global_args = mock.Mock()
28
self.global_args.profile = None
29
self.precanned = PrecannedPrompter(value='new_value')
30
self.session = FakeSession({'config_file': 'myconfigfile'})
31
self.configure = configure.ConfigureCommand(self.session,
32
prompter=self.precanned,
33
config_writer=self.writer)
34
35
def assert_credentials_file_updated_with(self, new_values):
36
called_args = self.writer.update_config.call_args_list
37
credentials_file_call = called_args[0]
38
expected_creds_file = os.path.expanduser('~/fake_credentials_filename')
39
self.assertEqual(credentials_file_call,
40
mock.call(new_values, expected_creds_file))
41
42
def test_configure_command_sends_values_to_writer(self):
43
self.configure(args=[], parsed_globals=self.global_args)
44
# Credentials are always written to the shared credentials file.
45
self.assert_credentials_file_updated_with(
46
{'aws_access_key_id': 'new_value',
47
'aws_secret_access_key': 'new_value'})
48
49
# Non-credentials config is written to the config file.
50
self.writer.update_config.assert_called_with(
51
{'region': 'new_value',
52
'output': 'new_value'}, 'myconfigfile')
53
54
def test_same_values_are_not_changed(self):
55
# If the user enters the same value as the current value, we don't need
56
# to write anything to the config.
57
self.configure = configure.ConfigureCommand(self.session,
58
prompter=EchoPrompter(),
59
config_writer=self.writer)
60
self.configure(args=[], parsed_globals=self.global_args)
61
self.assertFalse(self.writer.update_config.called)
62
63
def test_none_values_are_not_changed(self):
64
# If a user hits enter, this will result in a None value which means
65
# don't change the existing values. In this case, we don't need
66
# to write anything out to the config.
67
user_presses_enter = None
68
precanned = PrecannedPrompter(value=user_presses_enter)
69
self.configure = configure.ConfigureCommand(self.session,
70
prompter=precanned,
71
config_writer=self.writer)
72
self.configure(args=[], parsed_globals=self.global_args)
73
self.assertFalse(self.writer.update_config.called)
74
75
def test_create_configure_cmd_session_only(self):
76
self.configure = configure.ConfigureCommand(self.session)
77
self.assertIsInstance(self.configure, configure.ConfigureCommand)
78
79
def test_some_values_changed(self):
80
# Test the case where the user only wants to change a single_value.
81
responses = {
82
"AWS Access Key ID": None,
83
"AWS Secert Access Key": None,
84
"Default region name": None,
85
"Default output format": "NEW OUTPUT FORMAT",
86
}
87
prompter = KeyValuePrompter(responses)
88
self.configure = configure.ConfigureCommand(self.session, prompter=prompter,
89
config_writer=self.writer)
90
self.configure(args=[], parsed_globals=self.global_args)
91
92
# We only need to write out the default output format.
93
self.writer.update_config.assert_called_with(
94
{'output': 'NEW OUTPUT FORMAT'}, 'myconfigfile')
95
96
def test_section_name_can_be_changed_for_profiles(self):
97
# If the user specifies a profile we need to write this out to
98
# the [profile myname] section.
99
self.session.profile = 'myname'
100
self.configure(args=[], parsed_globals=self.global_args)
101
# Note the __section__ key name.
102
self.assert_credentials_file_updated_with(
103
{'aws_access_key_id': 'new_value',
104
'aws_secret_access_key': 'new_value',
105
'__section__': 'myname'})
106
self.writer.update_config.assert_called_with(
107
{'__section__': 'profile myname',
108
'region': 'new_value',
109
'output': 'new_value'}, 'myconfigfile')
110
111
def test_session_says_profile_does_not_exist(self):
112
# Whenever you try to get a config value from botocore,
113
# it will raise an exception complaining about ProfileNotFound.
114
# We should handle this case, and write out a new profile section
115
# in the config file.
116
session = FakeSession({'config_file': 'myconfigfile'},
117
profile_does_not_exist=True,
118
profile='profile-does-not-exist')
119
self.configure = configure.ConfigureCommand(session,
120
prompter=self.precanned,
121
config_writer=self.writer)
122
self.configure(args=[], parsed_globals=self.global_args)
123
self.assert_credentials_file_updated_with(
124
{'aws_access_key_id': 'new_value',
125
'aws_secret_access_key': 'new_value',
126
'__section__': 'profile-does-not-exist'})
127
self.writer.update_config.assert_called_with(
128
{'__section__': 'profile profile-does-not-exist',
129
'region': 'new_value',
130
'output': 'new_value'}, 'myconfigfile')
131
132
133
class TestInteractivePrompter(unittest.TestCase):
134
135
def setUp(self):
136
self.input_patch = mock.patch('awscli.compat.raw_input')
137
self.mock_raw_input = self.input_patch.start()
138
self.stdout = StringIO()
139
self.stdout_patch = mock.patch('sys.stdout', self.stdout)
140
self.stdout_patch.start()
141
142
def tearDown(self):
143
self.input_patch.stop()
144
self.stdout_patch.stop()
145
146
def test_access_key_is_masked(self):
147
self.mock_raw_input.return_value = 'foo'
148
prompter = configure.InteractivePrompter()
149
response = prompter.get_value(
150
current_value='myaccesskey', config_name='aws_access_key_id',
151
prompt_text='Access key')
152
# First we should return the value from raw_input.
153
self.assertEqual(response, 'foo')
154
# We should also not display the entire access key.
155
prompt_text = self.stdout.getvalue()
156
self.assertNotIn('myaccesskey', prompt_text)
157
self.assertRegex(prompt_text, r'\[\*\*\*\*.*\]')
158
159
def test_access_key_not_masked_when_none(self):
160
self.mock_raw_input.return_value = 'foo'
161
prompter = configure.InteractivePrompter()
162
response = prompter.get_value(
163
current_value=None, config_name='aws_access_key_id',
164
prompt_text='Access key')
165
# First we should return the value from raw_input.
166
self.assertEqual(response, 'foo')
167
prompt_text = self.stdout.getvalue()
168
self.assertIn('[None]', prompt_text)
169
170
def test_secret_key_is_masked(self):
171
prompter = configure.InteractivePrompter()
172
prompter.get_value(
173
current_value='mysupersecretkey',
174
config_name='aws_secret_access_key',
175
prompt_text='Secret Key')
176
# We should also not display the entire secret key.
177
prompt_text = self.stdout.getvalue()
178
self.assertNotIn('mysupersecretkey', prompt_text)
179
self.assertRegex(prompt_text, r'\[\*\*\*\*.*\]')
180
181
def test_non_secret_keys_are_not_masked(self):
182
prompter = configure.InteractivePrompter()
183
prompter.get_value(
184
current_value='mycurrentvalue', config_name='not_a_secret_key',
185
prompt_text='Enter value')
186
# We should also not display the entire secret key.
187
prompt_text = self.stdout.getvalue()
188
self.assertIn('mycurrentvalue', prompt_text)
189
self.assertRegex(prompt_text, r'\[mycurrentvalue\]')
190
191
def test_user_hits_enter_returns_none(self):
192
# If a user hits enter, then raw_input returns the empty string.
193
self.mock_raw_input.return_value = ''
194
195
prompter = configure.InteractivePrompter()
196
response = prompter.get_value(
197
current_value=None, config_name='aws_access_key_id',
198
prompt_text='Access key')
199
# We convert the empty string to None to indicate that there
200
# was no input.
201
self.assertIsNone(response)
202
203
def test_compat_input_flushes_after_each_prompt(self):
204
# Clear out the default patch
205
self.stdout_patch.stop()
206
207
# Create a mock stdout to record flush calls and replace stdout_patch
208
self.stdout = mock.Mock()
209
self.stdout_patch = mock.patch('sys.stdout', self.stdout)
210
self.stdout_patch.start()
211
212
# Make sure flush called at least once
213
prompter = configure.InteractivePrompter()
214
prompter.get_value(current_value='foo', config_name='bar',
215
prompt_text='baz')
216
self.assertTrue(self.stdout.flush.called)
217
218
# Make sure flush is called after *every* prompt
219
self.stdout.reset_mock()
220
prompter.get_value(current_value='foo2', config_name='bar2',
221
prompt_text='baz2')
222
self.assertTrue(self.stdout.flush.called)
223
224
225
class TestConfigValueMasking(unittest.TestCase):
226
227
def test_config_value_is_masked(self):
228
config_value = ConfigValue(
229
'fake_access_key', 'config_file', 'aws_access_key_id')
230
self.assertEqual(config_value.value, 'fake_access_key')
231
config_value.mask_value()
232
self.assertEqual(config_value.value, '****************_key')
233
234
def test_dont_mask_unset_value(self):
235
no_config = ConfigValue(NOT_SET, None, None)
236
self.assertEqual(no_config.value, NOT_SET)
237
no_config.mask_value()
238
self.assertEqual(no_config.value, NOT_SET)
239
240
241
class TestProfileToSection(unittest.TestCase):
242
243
def test_normal_profile(self):
244
profile = 'my-profile'
245
section = profile_to_section(profile)
246
self.assertEqual('profile my-profile', section)
247
248
def test_profile_with_spaces(self):
249
profile = 'my spaced profile'
250
section = profile_to_section(profile)
251
self.assertEqual('profile \'my spaced profile\'', section)
252
253
def test_profile_with_tab(self):
254
profile = 'tab\ts'
255
section = profile_to_section(profile)
256
self.assertEqual('profile \'tab\ts\'', section)
257
258
def test_profile_with_consecutive_spaces(self):
259
profile = ' '
260
section = profile_to_section(profile)
261
self.assertEqual('profile \' \'', section)
262
263
264
class PrecannedPrompter(object):
265
266
def __init__(self, value):
267
self._value = value
268
269
def get_value(self, current_value, logical_name, prompt_text=''):
270
return self._value
271
272
273
class EchoPrompter(object):
274
275
def get_value(self, current_value, logical_name, prompt_text=''):
276
return current_value
277
278
279
class KeyValuePrompter(object):
280
281
def __init__(self, mapping):
282
self.mapping = mapping
283
284
def get_value(self, current_value, config_name, prompt_text=''):
285
return self.mapping.get(prompt_text)
286
287