diff --git a/.changes/next-release/enhancement-Configure-43901.json b/.changes/next-release/enhancement-Configure-43901.json new file mode 100644 index 000000000000..0e4a7a0b0203 --- /dev/null +++ b/.changes/next-release/enhancement-Configure-43901.json @@ -0,0 +1,5 @@ +{ + "type": "enhancement", + "category": "configure", + "description": "Improved error messaging for `aws configure import --csv` command to clarify file" +} diff --git a/awscli/customizations/configure/importer.py b/awscli/customizations/configure/importer.py index 3fd2d381257e..fa7a73a651bb 100644 --- a/awscli/customizations/configure/importer.py +++ b/awscli/customizations/configure/importer.py @@ -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', }, @@ -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: @@ -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 diff --git a/tests/unit/customizations/configure/test_importer.py b/tests/unit/customizations/configure/test_importer.py index 34d009d8e62f..53e2b45fe569 100644 --- a/tests/unit/customizations/configure/test_importer.py +++ b/tests/unit/customizations/configure/test_importer.py @@ -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("without the 'file://' prefix", 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):