Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/next-release/enhancement-Configure-43901.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"type": "enhancement",
"category": "Configure",
"description": "Improved error messaging for `aws configure import --csv` command to clarify file"
}
14 changes: 13 additions & 1 deletion awscli/customizations/configure/importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ class ConfigureImportCommand(BasicCommand):
'name': 'csv',
'required': True,
'help_text': (
'The credentials in CSV format generated by the AWS web console.'
'The credentials in CSV format generated by the AWS web console. '
'The CSV file must contain the "User name", "Access key ID", and '
'"Secret access key" headers.'
'If passing a CSV file path instead of a CSV-formatted string, '
'the "file://" prefix is required.'
),
'cli_type_name': 'string',
},
Expand Down Expand Up @@ -86,6 +88,7 @@ def _get_config_path(self):
return os.path.expanduser(config_file)

def _import_csv(self, contents):
self._check_possible_filepath(contents)
config_path = self._get_config_path()
credentials = self._csv_parser.parse_credentials(contents)
for credential in credentials:
Expand All @@ -97,6 +100,15 @@ def _import_csv(self, contents):
import_msg = 'Successfully imported %s profile(s)\n' % len(credentials)
uni_print(import_msg, out_file=self._out_stream)

def _check_possible_filepath(self, csv_data):
if ('\n' not in csv_data and
os.path.exists(csv_data) and
not csv_data.startswith('file://')):
raise ValueError(
"You may be passing a file to import without the 'file://' prefix. "
"To import a CSV file, use --csv file://path/to/file.csv"
)

def _run_main(self, parsed_args, parsed_globals):
self._csv_parser.strict = not parsed_args.skip_invalid
self._profile_prefix = parsed_args.profile_prefix
Expand Down
25 changes: 25 additions & 0 deletions tests/unit/customizations/configure/test_importer.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,31 @@ def test_import_downloaded_bad_headers(self):
with self.assertRaises(CredentialParserError):
self.import_command(args=['--csv', content], parsed_globals=None)

def test_raises_error_when_plain_file_path_passed(self):
with open('temp_creds.csv', 'w') as f:
f.write('User name,Access key ID,Secret access key\nuser,AKID,SAK')
try:
with self.assertRaises(ValueError) as cm:
self.import_command(args=['--csv', 'temp_creds.csv'], parsed_globals=None)
self.assertIn("file://", str(cm.exception))
finally:
os.remove('temp_creds.csv')

def test_inline_csv_succeeds(self):
csv_string = 'User name,Access key ID,Secret access key\nuser,AKID,SAK'
self.import_command(args=['--csv', csv_string], parsed_globals=None)
self.assertIn('Successfully imported 1 profile', self.stdout.getvalue())

def test_csv_content_from_file_succeeds(self):
with open('temp_creds.csv', 'w') as f:
f.write('User name,Access key ID,Secret access key\nuser,AKID,SAK')
try:
with open('temp_creds.csv', 'r') as f:
contents = f.read()
self.import_command(args=['--csv', contents], parsed_globals=None)
self.assertIn('Successfully imported 1 profile', self.stdout.getvalue())
finally:
os.remove('temp_creds.csv')

class TestCSVCredentialParser(unittest.TestCase):
def setUp(self):
Expand Down
Loading