From b6155f47a0c01a7dbc9a7a98f8eab65864595e5c Mon Sep 17 00:00:00 2001 From: Sean Marlow Date: Thu, 10 May 2018 10:52:24 -0600 Subject: [PATCH 1/2] Add inject option for ipa testing. Provides the following options for injection: :inject_packages: an rpm path or list of rpm paths which will be copied and installed on instance. :inject_archives: an archive or list of archives which will be copied and extracted on instance. :inject_files: a file path or list of file paths which will be copied to instance. :execute: a command or list of commands to run. :install: a package name or list of package names to install from an existing repo. The injection happens in that order. --- ipa/ipa_azure.py | 2 + ipa/ipa_controller.py | 2 + ipa/ipa_ec2.py | 2 + ipa/ipa_gce.py | 2 + ipa/ipa_provider.py | 70 ++++++++++++++++++++++++ ipa/scripts/cli.py | 7 +++ tests/data/injection/test_injection.yaml | 5 ++ tests/test_ipa_provider.py | 38 +++++++++++++ 8 files changed, 128 insertions(+) create mode 100644 tests/data/injection/test_injection.yaml diff --git a/ipa/ipa_azure.py b/ipa/ipa_azure.py index b198e57c..f486dde6 100644 --- a/ipa/ipa_azure.py +++ b/ipa/ipa_azure.py @@ -46,6 +46,7 @@ def __init__(self, early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=30, no_default_test_dirs=False, @@ -72,6 +73,7 @@ def __init__(self, early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff --git a/ipa/ipa_controller.py b/ipa/ipa_controller.py index 0141ffd5..aa2f5061 100644 --- a/ipa/ipa_controller.py +++ b/ipa/ipa_controller.py @@ -43,6 +43,7 @@ def test_image(provider_name, early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=None, no_default_test_dirs=None, @@ -83,6 +84,7 @@ def test_image(provider_name, early_exit=early_exit, history_log=history_log, image_id=image_id, + inject=inject, instance_type=instance_type, log_level=log_level, no_default_test_dirs=no_default_test_dirs, diff --git a/ipa/ipa_ec2.py b/ipa/ipa_ec2.py index 54c23d13..ba06b88a 100644 --- a/ipa/ipa_ec2.py +++ b/ipa/ipa_ec2.py @@ -48,6 +48,7 @@ def __init__(self, early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=30, no_default_test_dirs=False, @@ -75,6 +76,7 @@ def __init__(self, early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff --git a/ipa/ipa_gce.py b/ipa/ipa_gce.py index afdf53d9..202a7e32 100644 --- a/ipa/ipa_gce.py +++ b/ipa/ipa_gce.py @@ -49,6 +49,7 @@ def __init__(self, early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=30, no_default_test_dirs=False, @@ -75,6 +76,7 @@ def __init__(self, early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff --git a/ipa/ipa_provider.py b/ipa/ipa_provider.py index dea724b3..7ea376e0 100644 --- a/ipa/ipa_provider.py +++ b/ipa/ipa_provider.py @@ -67,6 +67,7 @@ def __init__(self, early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=None, no_default_test_dirs=False, @@ -102,6 +103,7 @@ def __init__(self, self.distro_name = self._get_value(distro_name) self.early_exit = self._get_value(early_exit) self.image_id = self._get_value(image_id) + self.inject = self._get_value(inject) self.instance_type = self._get_value(instance_type) self.running_instance_id = self._get_value(running_instance_id) self.test_files = list(self._get_value(test_files, default=[])) @@ -447,6 +449,71 @@ def install_package(self, client, package): log_file.write('\n') log_file.write(out) + def process_injection_file(self, client): + """ + Load yaml file and process injection configuration. + + There are 5 injection options: + + :inject_packages: an rpm path or list of rpm paths which will be + copied and installed on instance. + :inject_archives: an archive or list of archives which will + be copied and extracted on instance. + :inject_files: a file path or list of file paths which + will be copied to instance. + :execute: a command or list of commands to run. + :install: a package name or list of package names to + install from an existing repo. + """ + configuration = ipa_utils.get_yaml_config(self.inject) + + if configuration.get('inject_packages'): + inject_packages = configuration['inject_packages'] + + if not isinstance(inject_packages, list): + inject_packages = [inject_packages] + + for package in inject_packages: + package_path = self.put_file(client, package) + self.install_package(client, package_path) + + if configuration.get('inject_archives'): + inject_archives = configuration['inject_archives'] + + if not isinstance(inject_archives, list): + inject_archives = [inject_archives] + + for archive in inject_archives: + archive_path = self.put_file(client, archive) + self.extract_archive(client, archive_path) + + if configuration.get('inject_files'): + inject_files = configuration['inject_files'] + + if not isinstance(inject_files, list): + inject_files = [inject_files] + + for file_path in inject_files: + self.put_file(client, file_path) + + if configuration.get('execute'): + execute = configuration['execute'] + + if not isinstance(execute, list): + execute = [execute] + + for command in execute: + self.execute_ssh_command(client, command) + + if configuration.get('install'): + install = configuration['install'] + + if not isinstance(install, list): + install = [install] + + for package in install: + self.install_package(client, package) + def put_file(self, client, source_file): """ Put file on instance in default SSH directory. @@ -507,6 +574,9 @@ def test_image(self): self._set_distro() self._log_info() + if self.inject: + self.process_injection_file(self._get_ssh_client()) + status = 0 with ipa_utils.ssh_config(self.ssh_user, self.ssh_private_key)\ as ssh_config: diff --git a/ipa/scripts/cli.py b/ipa/scripts/cli.py index adde54df..326864e5 100644 --- a/ipa/scripts/cli.py +++ b/ipa/scripts/cli.py @@ -126,6 +126,11 @@ def main(context, no_color): '--image-id', help='The ID of the image used for instance.' ) +@click.option( + '--inject', + help='Path to an injection yaml config file.', + type=click.Path(exists=True) +) @click.option( '-t', '--instance-type', @@ -228,6 +233,7 @@ def test(context, early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, @@ -260,6 +266,7 @@ def test(context, early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff --git a/tests/data/injection/test_injection.yaml b/tests/data/injection/test_injection.yaml new file mode 100644 index 00000000..fc96a178 --- /dev/null +++ b/tests/data/injection/test_injection.yaml @@ -0,0 +1,5 @@ +inject_packages: /home/user/test.noarch.rpm +inject_archives: /home/user/test.tar.xz +install: python3 +inject_files: /home/user/test.py +execute: python test.py diff --git a/tests/test_ipa_provider.py b/tests/test_ipa_provider.py index cd063124..c591c461 100644 --- a/tests/test_ipa_provider.py +++ b/tests/test_ipa_provider.py @@ -322,6 +322,44 @@ def test_provider_install_package(self): call('package install successful!') ]) + @patch.object(IpaProvider, 'execute_ssh_command') + @patch.object(IpaProvider, 'extract_archive') + @patch.object(IpaProvider, 'install_package') + @patch.object(IpaProvider, 'put_file') + def test_process_injection_file(self, + mock_put_file, + mock_install_package, + mock_extract_archive, + mock_execute_command): + client = MagicMock() + mock_put_file.side_effect = [ + 'test.noarch.rpm', 'test.tar.xz', 'test.py' + ] + + provider = IpaProvider(*args, **self.kwargs) + provider.inject = 'tests/data/injection/test_injection.yaml' + + provider.process_injection_file(client) + + mock_put_file.assert_has_calls([ + call(client, '/home/user/test.noarch.rpm'), + call(client, '/home/user/test.tar.xz'), + call(client, '/home/user/test.py') + ]) + + mock_install_package.assert_has_calls([ + call(client, 'test.noarch.rpm'), + call(client, 'python3') + ]) + + mock_extract_archive.assert_called_once_with( + client, 'test.tar.xz' + ) + + mock_execute_command.assert_called_once_with( + client, 'python test.py' + ) + @patch.object(IpaProvider, '_set_instance_ip') @patch.object(IpaProvider, '_set_image_id') @patch.object(IpaProvider, '_start_instance_if_stopped') From cfc88b3940035360a0121ef70d1fb57585cbd937 Mon Sep 17 00:00:00 2001 From: Sean Marlow Date: Tue, 15 May 2018 10:32:18 -0600 Subject: [PATCH 2/2] Update docstring add order of execution. --- ipa/ipa_provider.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ipa/ipa_provider.py b/ipa/ipa_provider.py index 7ea376e0..0aa4e9bb 100644 --- a/ipa/ipa_provider.py +++ b/ipa/ipa_provider.py @@ -456,14 +456,17 @@ def process_injection_file(self, client): There are 5 injection options: :inject_packages: an rpm path or list of rpm paths which will be - copied and installed on instance. + copied and installed on the test instance. :inject_archives: an archive or list of archives which will - be copied and extracted on instance. + be copied and extracted on the test instance. :inject_files: a file path or list of file paths which - will be copied to instance. - :execute: a command or list of commands to run. + will be copied to the test instance. + :execute: a command or list of commands to run on the test instance. :install: a package name or list of package names to - install from an existing repo. + install from an existing repo on the test instance. + + The order of processing is as follows: inject_packages, + inject_archives, inject_files, execute, install. """ configuration = ipa_utils.get_yaml_config(self.inject)