From 744b9178057ab033945cb80b48a7716cd94c38cf Mon Sep 17 00:00:00 2001 From: Stephen Hara Date: Tue, 21 Mar 2023 16:49:40 +0900 Subject: [PATCH] Give useful error message on common import issue. This patch addresses #7758 by checking some heuristics on the string provided for import, and if it looks like a file that wasn't given the URI "file://" prefix, tell the user. This also updates the docs for "configure import" to make this requirement more clear. --- awscli/customizations/configure/importer.py | 25 +++++++++++++++++-- .../customizations/configure/test_importer.py | 5 ++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/awscli/customizations/configure/importer.py b/awscli/customizations/configure/importer.py index dc7e6e088ba9..c726741fc86e 100644 --- a/awscli/customizations/configure/importer.py +++ b/awscli/customizations/configure/importer.py @@ -19,6 +19,10 @@ from awscli.customizations.configure.writer import ConfigFileWriter +class ConfigureImportError(Exception): + pass + + class ConfigureImportCommand(BasicCommand): NAME = 'import' DESCRIPTION = ( @@ -36,9 +40,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.' + '"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'}, {'name': 'skip-invalid', @@ -57,6 +63,9 @@ class ConfigureImportCommand(BasicCommand): 'default': '', 'cli_type_name': 'string'}, ] + _POSSIBLE_FILE_ARGUMENT = ( + 'You may be passing a file to import without the "file://" prefix' + ) def __init__(self, session, csv_parser=None, importer=None, out_stream=None): @@ -89,9 +98,21 @@ 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): + """ + Check if the data 1) wouldn't be imported as anything useful, and 2) + looks like it could be a file. + """ + line_count = csv.count('\n') + if line_count <= 1: + comma_count = csv.count(',') + if comma_count == 0 and os.path.isfile(csv): + raise ConfigureImportError(self._POSSIBLE_FILE_ARGUMENT) + def _run_main(self, parsed_args, parsed_globals): self._csv_parser.strict = not parsed_args.skip_invalid self._profile_prefix = parsed_args.profile_prefix + self._check_possible_filepath(parsed_args.csv) self._import_csv(parsed_args.csv) return 0 diff --git a/tests/unit/customizations/configure/test_importer.py b/tests/unit/customizations/configure/test_importer.py index be319d52c2a8..b47601101b3c 100644 --- a/tests/unit/customizations/configure/test_importer.py +++ b/tests/unit/customizations/configure/test_importer.py @@ -22,6 +22,7 @@ ConfigureImportCommand, CSVCredentialParser, CredentialParserError, + ConfigureImportError, ) from awscli.customizations.configure.writer import ConfigFileWriter @@ -88,6 +89,10 @@ def test_import_downloaded_bad_headers(self): with self.assertRaises(CredentialParserError): self.import_command(args=['--csv', content], parsed_globals=None) + def test_import_fails_if_suspected_file_without_uri_prefix(self): + with self.assertRaises(ConfigureImportError): + self.import_command(args=['--csv', './README.rst'], parsed_globals=None) + class TestCSVCredentialParser(unittest.TestCase): def setUp(self):