Hello community, here is the log from the commit of package python3-ipa for openSUSE:Factory checked in at 2018-06-02 12:14:36 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python3-ipa (Old) and /work/SRC/openSUSE:Factory/.python3-ipa.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "python3-ipa" Sat Jun 2 12:14:36 2018 rev:4 rq:613452 version:1.1.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python3-ipa/python3-ipa.changes 2018-04-04 11:04:14.494599198 +0200 +++ /work/SRC/openSUSE:Factory/.python3-ipa.new/python3-ipa.changes 2018-06-02 12:14:52.957972322 +0200 @@ -1,0 +2,12 @@ +Wed May 16 21:03:53 UTC 2018 - sean.marlow@suse.com + +- Update to v1.1.1 (2018-05-16) + + Cleanup typo in docs. + + Explicitly close SSH connections. +- Update to v1.1.0 (2018-05-15) + + Added the --inject option. + + Adds the option to inject packages, archives and files. + + Also provides the ability to execute commands and install + packages from an existing repository. + +------------------------------------------------------------------- Old: ---- python3-ipa-1.0.0.tar.gz New: ---- python3-ipa-1.1.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python3-ipa.spec ++++++ --- /var/tmp/diff_new_pack.ROOzkw/_old 2018-06-02 12:14:53.833940192 +0200 +++ /var/tmp/diff_new_pack.ROOzkw/_new 2018-06-02 12:14:53.837940046 +0200 @@ -18,7 +18,7 @@ %bcond_without test Name: python3-ipa -Version: 1.0.0 +Version: 1.1.1 Release: 0 Summary: Command line and API for testing custom images License: GPL-3.0-or-later ++++++ python3-ipa-1.0.0.tar.gz -> python3-ipa-1.1.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/CHANGES.asciidoc new/python3-ipa-1.1.1/CHANGES.asciidoc --- old/python3-ipa-1.0.0/CHANGES.asciidoc 2018-03-30 17:48:40.000000000 +0200 +++ new/python3-ipa-1.1.1/CHANGES.asciidoc 2018-05-16 22:43:59.000000000 +0200 @@ -1,5 +1,19 @@ = Changelog +== v1.1.1 (2018-05-16) + +* Cleanup typo in docs. +* Explicitly close SSH connections. + link:https://github.com/SUSE/ipa/pull/79[#79] + +== v1.1.0 (2018-05-15) + +* Add requirements and external test injection with the `--inject` option. + link:https://github.com/SUSE/ipa/pull/78[#78] +** Adds the option to inject packages, archives and + files. Also provides the ability to execute commands + and install packages from an existing repository. + == v1.0.0 (2018-03-30) * Tests argument is now optional. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/PKG-INFO new/python3-ipa-1.1.1/PKG-INFO --- old/python3-ipa-1.0.0/PKG-INFO 2018-03-30 18:23:36.000000000 +0200 +++ new/python3-ipa-1.1.1/PKG-INFO 2018-05-16 22:55:56.000000000 +0200 @@ -1,12 +1,11 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: python3-ipa -Version: 1.0.0 +Version: 1.1.1 Summary: Package for automated testing of cloud images. Home-page: https://github.com/SUSE/pubcloud/ipa Author: SUSE Author-email: public-cloud-dev@susecloud.net License: GPLv3+ -Description-Content-Type: UNKNOWN Description: = ipa image:https://travis-ci.org/SUSE/ipa.svg?branch=master["Build Status", link="https://travis-ci.org/SUSE/ipa"] @@ -78,8 +77,11 @@ === Azure Configuration - There is no additional configuration file used for Azure. All options - should be placed in the *ipa* config file. + Azure uses service principals for authentication. See + link:https://docs.microsoft.com/en-us/python/azure/python-sdk-azure-authenticate?view=azure-python#mgmt-auth-file[Azure docs] + for more info on creating a service principal json file. Additional + configuration options can be placed in the `azure` section of the + `ipa` configuration file. === EC2 Configuration @@ -166,3 +168,6 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Requires-Python: >=3.4 +Provides-Extra: tox +Provides-Extra: test +Provides-Extra: dev diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/README.asciidoc new/python3-ipa-1.1.1/README.asciidoc --- old/python3-ipa-1.0.0/README.asciidoc 2018-03-30 18:05:40.000000000 +0200 +++ new/python3-ipa-1.1.1/README.asciidoc 2018-04-25 23:05:09.000000000 +0200 @@ -69,8 +69,11 @@ === Azure Configuration -There is no additional configuration file used for Azure. All options -should be placed in the *ipa* config file. +Azure uses service principals for authentication. See +link:https://docs.microsoft.com/en-us/python/azure/python-sdk-azure-authenticate?view=azure-python#mgmt-auth-file[Azure docs] +for more info on creating a service principal json file. Additional +configuration options can be placed in the `azure` section of the +`ipa` configuration file. === EC2 Configuration diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/docs/start.asciidoc new/python3-ipa-1.1.1/docs/start.asciidoc --- old/python3-ipa-1.0.0/docs/start.asciidoc 2018-03-30 18:02:18.000000000 +0200 +++ new/python3-ipa-1.1.1/docs/start.asciidoc 2018-05-16 17:54:33.000000000 +0200 @@ -63,8 +63,12 @@ === Azure -There is no additional configuration file used for Azure. All options -should be placed in the *ipa* config file. +Azure uses service principals for authentication. See +link:https://docs.microsoft.com/en-us/python/azure/python-sdk-azure-authenticate?view=azure-python#mgmt-auth-file[Azure docs] +for more info on creating a service principal json file. Additional +configuration options can be placed in the `azure` section of the +`ipa` configuration file. + === EC2 @@ -181,6 +185,61 @@ link:https://docs.pytest.org/en/latest/usage.html#stopping-after-the-first-or-n-failures[Pytest docs] for more info. +==== Requirements and external test injection + +Using the `--inject` option; packages, archives and files can be injected +on the test instance. This also provides the ability to install packages +in an existing repository and run commands on the test instance. The +following sections may be provided in a YAML style config file. Each +section can be a single item or a list of items. All files are copied +and extracted to the default SSH location for the test instance. This +is generally the user's home directory. + +*_inject_packages_*:: +an rpm path or list of rpm paths which will be copied and installed on +the test instance. + +*_inject_archives_*:: +an archive or list of archives which will be copied and extracted on the +test instance. + +*_inject_files_*:: +a file path or list of file paths which 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 on +the test instance. + +The order of processing for the sections is as follows: + +1. inject_packages +1. inject_archives +1. inject_files +1. execute +1. install + +===== Example + +[source] +.testing_injection.yaml +---- +inject_packages: /home/user/test.noarch.rpm +inject_archives: /home/user/test.tar.xz +inject_files: /home/user/test.py +install: + - python3 + - python3-Django +execute: python test.py +---- + +[source] +---- +> ipa test ... --inject testing_injection.yaml +---- + === Code *ipa* primarily provides a CLI tool for testing images. However, the endpoints diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/__init__.py new/python3-ipa-1.1.1/ipa/__init__.py --- old/python3-ipa-1.0.0/ipa/__init__.py 2018-03-30 18:11:43.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/__init__.py 2018-05-16 22:44:47.000000000 +0200 @@ -22,4 +22,4 @@ __author__ = """SUSE""" __email__ = 'public-cloud-dev@susecloud.net' -__version__ = '1.0.0' +__version__ = '1.1.1' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_azure.py new/python3-ipa-1.1.1/ipa/ipa_azure.py --- old/python3-ipa-1.0.0/ipa/ipa_azure.py 2018-03-28 15:36:07.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_azure.py 2018-05-15 21:01:19.000000000 +0200 @@ -46,6 +46,7 @@ early_exit=None, history_log=None, image_id=None, + inject=None, instance_type=None, log_level=30, no_default_test_dirs=False, @@ -54,7 +55,7 @@ results_dir=None, running_instance_id=None, secret_access_key=None, # Not used in Azure - service_account_file=None, # Not used in Azure + service_account_file=None, ssh_key_name=None, # Not used in Azure ssh_private_key=None, ssh_user=None, @@ -72,6 +73,7 @@ early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_controller.py new/python3-ipa-1.1.1/ipa/ipa_controller.py --- old/python3-ipa-1.0.0/ipa/ipa_controller.py 2018-03-28 15:36:07.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_controller.py 2018-05-15 21:01:19.000000000 +0200 @@ -43,6 +43,7 @@ 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 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_distro.py new/python3-ipa-1.1.1/ipa/ipa_distro.py --- old/python3-ipa-1.0.0/ipa/ipa_distro.py 2017-08-15 16:50:28.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_distro.py 2018-05-10 18:21:09.000000000 +0200 @@ -37,6 +37,10 @@ """Determine the init system of distribution.""" raise NotImplementedError(NOT_IMPLEMENTED) + def get_install_cmd(self): + """Return install package command for distribution.""" + raise NotImplementedError(NOT_IMPLEMENTED) + def get_reboot_cmd(self): """Return reboot command for given distribution.""" return 'shutdown -r now' @@ -57,6 +61,30 @@ """Return command to update instance.""" raise NotImplementedError(NOT_IMPLEMENTED) + def install_package(self, client, package): + """Install package on instance.""" + install_cmd = "{sudo} '{install} {package}'".format( + sudo=self.get_sudo_exec_wrapper(), + install=self.get_install_cmd(), + package=package + ) + + try: + out = ipa_utils.execute_ssh_command( + client, + install_cmd + ) + except Exception as error: + raise IpaDistroException( + 'An error occurred installing package {package} ' + 'on instance: {error}'.format( + package=package, + error=error + ) + ) + else: + return out + def reboot(self, client): """Execute reboot command on instance.""" if not self.init_system: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_ec2.py new/python3-ipa-1.1.1/ipa/ipa_ec2.py --- old/python3-ipa-1.0.0/ipa/ipa_ec2.py 2018-03-28 15:42:08.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_ec2.py 2018-05-15 21:01:19.000000000 +0200 @@ -48,6 +48,7 @@ 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 @@ early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_gce.py new/python3-ipa-1.1.1/ipa/ipa_gce.py --- old/python3-ipa-1.0.0/ipa/ipa_gce.py 2018-03-28 15:36:07.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_gce.py 2018-05-15 21:01:19.000000000 +0200 @@ -49,6 +49,7 @@ 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 @@ early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_provider.py new/python3-ipa-1.1.1/ipa/ipa_provider.py --- old/python3-ipa-1.0.0/ipa/ipa_provider.py 2018-03-30 16:39:26.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_provider.py 2018-05-15 21:01:19.000000000 +0200 @@ -67,6 +67,7 @@ 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 @@ 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=[])) @@ -391,6 +393,37 @@ ) ) + def execute_ssh_command(self, client, command): + """Execute the provided command and log output.""" + try: + out = ipa_utils.execute_ssh_command(client, command) + except Exception as error: + raise IpaProviderException( + 'Command: "{0}", failed execution: {1}.'.format( + command, error + ) + ) + else: + with open(self.log_file, 'a') as log_file: + log_file.write('\n') + log_file.write(out) + + def extract_archive(self, client, archive_path, extract_path=None): + """Extract the archive files using the client in the current path.""" + try: + out = ipa_utils.extract_archive(client, archive_path, extract_path) + except Exception as error: + raise IpaProviderException( + 'Failed to extract archive, "{0}": {1}.'.format( + archive_path, error + ) + ) + + else: + with open(self.log_file, 'a') as log_file: + log_file.write('\n') + log_file.write(out) + def hard_reboot_instance(self): """Stop then start the instance.""" self._stop_instance() @@ -399,6 +432,107 @@ self.logger.debug('IP of instance: %s' % self.instance_ip) ipa_utils.clear_cache() + def install_package(self, client, package): + """ + Install package using distro specific install method. + """ + try: + out = self.distro.install_package(client, package) + except Exception as error: + raise IpaProviderException( + 'Failed installing package, "{0}"; {1}.'.format( + package, error + ) + ) + else: + with open(self.log_file, 'a') as log_file: + 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 the test instance. + :inject_archives: an archive or list of archives which will + be copied and extracted on the test instance. + :inject_files: a file path or list of file paths which + 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 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) + + 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. + """ + try: + file_name = os.path.basename(source_file) + ipa_utils.put_file(client, source_file, file_name) + except Exception as error: + raise IpaProviderException( + 'Failed copying file, "{0}"; {1}.'.format( + source_file, error + ) + ) + else: + return file_name + def test_image(self): """ The entry point for testing an image. @@ -443,6 +577,9 @@ 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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_sles.py new/python3-ipa-1.1.1/ipa/ipa_sles.py --- old/python3-ipa-1.0.0/ipa/ipa_sles.py 2017-08-15 16:50:28.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_sles.py 2018-05-10 16:28:27.000000000 +0200 @@ -43,6 +43,10 @@ if out: self.init_system = out.strip() + def get_install_cmd(self): + """Return install package command for SLES.""" + return 'zypper -n --no-gpg-checks in -y' + def get_refresh_repo_cmd(self): """Return refresh repo command for SLES.""" return 'zypper -n refresh' diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/ipa_utils.py new/python3-ipa-1.1.1/ipa/ipa_utils.py --- old/python3-ipa-1.0.0/ipa/ipa_utils.py 2018-03-30 16:39:26.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/ipa_utils.py 2018-05-16 22:36:17.000000000 +0200 @@ -47,9 +47,14 @@ def clear_cache(ip=None): """Clear the client cache or remove key matching the given ip.""" if ip: - with ignored(KeyError): + with ignored(Exception): + client = CLIENT_CACHE[ip] del CLIENT_CACHE[ip] + client.close() else: + for client in CLIENT_CACHE.values(): + with ignored(Exception): + client.close() CLIENT_CACHE.clear() @@ -142,6 +147,21 @@ return parse_sync_points(expanded_names, tests) +def extract_archive(client, archive_path, extract_path=None): + """ + Extract the archive in current path using the provided client. + + If extract_path is provided extract the archive there. + """ + command = 'tar -xf {path}'.format(path=archive_path) + + if extract_path: + command += ' -C {extract_path}'.format(extract_path=extract_path) + + out = execute_ssh_command(client, command) + return out + + def find_test_file(name, tests): """ Find test file by name, given a list of tests. @@ -457,6 +477,22 @@ return '::'.join(filter(None, [test_file, test_class, test_case])) +def put_file(client, source_file, destination_file): + """ + Copy file to instance using Paramiko client connection. + """ + try: + sftp_client = client.open_sftp() + sftp_client.put(source_file, destination_file) + except Exception as error: + raise IpaUtilsException( + 'Error copying file to instance: {0}.'.format(error) + ) + finally: + with ignored(Exception): + sftp_client.close() + + @contextmanager def redirect_output(fileobj): """Redirect standard out to file.""" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/ipa/scripts/cli.py new/python3-ipa-1.1.1/ipa/scripts/cli.py --- old/python3-ipa-1.0.0/ipa/scripts/cli.py 2018-03-30 16:39:26.000000000 +0200 +++ new/python3-ipa-1.1.1/ipa/scripts/cli.py 2018-05-15 21:01:19.000000000 +0200 @@ -127,6 +127,11 @@ 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', help='Instance type to use for launching machine.' @@ -228,6 +233,7 @@ early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, @@ -260,6 +266,7 @@ early_exit, history_log, image_id, + inject, instance_type, log_level, no_default_test_dirs, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/package/python3-ipa.spec new/python3-ipa-1.1.1/package/python3-ipa.spec --- old/python3-ipa-1.0.0/package/python3-ipa.spec 2018-03-30 18:11:43.000000000 +0200 +++ new/python3-ipa-1.1.1/package/python3-ipa.spec 2018-05-16 22:44:47.000000000 +0200 @@ -17,7 +17,7 @@ %bcond_without test Name: python3-ipa -Version: 1.0.0 +Version: 1.1.1 Release: 0 Summary: Command line and API for testing custom images License: GPL-3.0+ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/python3_ipa.egg-info/PKG-INFO new/python3-ipa-1.1.1/python3_ipa.egg-info/PKG-INFO --- old/python3-ipa-1.0.0/python3_ipa.egg-info/PKG-INFO 2018-03-30 18:23:36.000000000 +0200 +++ new/python3-ipa-1.1.1/python3_ipa.egg-info/PKG-INFO 2018-05-16 22:55:56.000000000 +0200 @@ -1,12 +1,11 @@ -Metadata-Version: 1.2 +Metadata-Version: 2.1 Name: python3-ipa -Version: 1.0.0 +Version: 1.1.1 Summary: Package for automated testing of cloud images. Home-page: https://github.com/SUSE/pubcloud/ipa Author: SUSE Author-email: public-cloud-dev@susecloud.net License: GPLv3+ -Description-Content-Type: UNKNOWN Description: = ipa image:https://travis-ci.org/SUSE/ipa.svg?branch=master["Build Status", link="https://travis-ci.org/SUSE/ipa"] @@ -78,8 +77,11 @@ === Azure Configuration - There is no additional configuration file used for Azure. All options - should be placed in the *ipa* config file. + Azure uses service principals for authentication. See + link:https://docs.microsoft.com/en-us/python/azure/python-sdk-azure-authenticate?view=azure-python#mgmt-auth-file[Azure docs] + for more info on creating a service principal json file. Additional + configuration options can be placed in the `azure` section of the + `ipa` configuration file. === EC2 Configuration @@ -166,3 +168,6 @@ Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Requires-Python: >=3.4 +Provides-Extra: tox +Provides-Extra: test +Provides-Extra: dev diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/python3_ipa.egg-info/SOURCES.txt new/python3-ipa-1.1.1/python3_ipa.egg-info/SOURCES.txt --- old/python3-ipa-1.0.0/python3_ipa.egg-info/SOURCES.txt 2018-03-30 18:23:36.000000000 +0200 +++ new/python3-ipa-1.1.1/python3_ipa.egg-info/SOURCES.txt 2018-05-16 22:55:56.000000000 +0200 @@ -70,6 +70,7 @@ tests/data/ida_test tests/data/invalid.history tests/data/test.results +tests/data/injection/test_injection.yaml tests/data/tests/test_broken.py tests/data/tests/test_image.py tests/data/tests/test_image_desc.yaml diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/setup.cfg new/python3-ipa-1.1.1/setup.cfg --- old/python3-ipa-1.0.0/setup.cfg 2018-03-30 18:23:36.000000000 +0200 +++ new/python3-ipa-1.1.1/setup.cfg 2018-05-16 22:55:56.000000000 +0200 @@ -1,5 +1,5 @@ [bumpversion] -current_version = 1.0.0 +current_version = 1.1.1 commit = True tag = False diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/setup.py new/python3-ipa-1.1.1/setup.py --- old/python3-ipa-1.0.0/setup.py 2018-03-30 18:11:43.000000000 +0200 +++ new/python3-ipa-1.1.1/setup.py 2018-05-16 22:44:47.000000000 +0200 @@ -60,7 +60,7 @@ setup( name='python3-ipa', - version='1.0.0', + version='1.1.1', description="Package for automated testing of cloud images.", long_description=readme, author="SUSE", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/tests/data/injection/test_injection.yaml new/python3-ipa-1.1.1/tests/data/injection/test_injection.yaml --- old/python3-ipa-1.0.0/tests/data/injection/test_injection.yaml 1970-01-01 01:00:00.000000000 +0100 +++ new/python3-ipa-1.1.1/tests/data/injection/test_injection.yaml 2018-05-15 21:01:19.000000000 +0200 @@ -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 -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/tests/test_ipa_distro.py new/python3-ipa-1.1.1/tests/test_ipa_distro.py --- old/python3-ipa-1.0.0/tests/test_ipa_distro.py 2017-08-23 00:00:09.000000000 +0200 +++ new/python3-ipa-1.1.1/tests/test_ipa_distro.py 2018-05-10 18:17:37.000000000 +0200 @@ -27,22 +27,33 @@ import pytest +methods = [ + 'get_install_cmd', + 'get_refresh_repo_cmd', + 'get_stop_ssh_service_cmd', + 'get_update_cmd' +] -def test_distro_not_implemented_methods(): + +@pytest.mark.parametrize( + "method", + methods, + ids=methods +) +def test_distro_not_implemented_methods(method): """Confirm methods raise not implemented exception.""" distro = Distro() - methods = [ - 'get_refresh_repo_cmd', - 'get_stop_ssh_service_cmd', - 'get_update_cmd' - ] - for method in methods: - pytest.raises( - NotImplementedError, - getattr(distro, method) - ) + pytest.raises( + NotImplementedError, + getattr(distro, method) + ) + +def test_distro_set_init_system(): + """Test set init system raises not implemented exception.""" + distro = Distro() client = MagicMock() + pytest.raises( NotImplementedError, getattr(distro, '_set_init_system'), diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/tests/test_ipa_provider.py new/python3-ipa-1.1.1/tests/test_ipa_provider.py --- old/python3-ipa-1.0.0/tests/test_ipa_provider.py 2018-01-31 20:21:42.000000000 +0100 +++ new/python3-ipa-1.1.1/tests/test_ipa_provider.py 2018-05-15 21:01:19.000000000 +0200 @@ -21,6 +21,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see http://www.gnu.org/licenses/. +import io import pytest from ipa import ipa_utils @@ -28,7 +29,7 @@ from ipa.ipa_exceptions import IpaProviderException, IpaSSHException from ipa.ipa_provider import IpaProvider -from unittest.mock import MagicMock, patch +from unittest.mock import call, MagicMock, patch args = ['ec2'] @@ -222,6 +223,49 @@ assert mock_instance_running.call_count == 1 assert mock_start_instance.call_count == 1 + @patch('ipa.ipa_utils.execute_ssh_command') + def test_provider_execute_ssh_command(self, mock_exec_cmd): + client = MagicMock() + mock_exec_cmd.return_value = 'command executed successfully!' + + provider = IpaProvider(*args, **self.kwargs) + provider.log_file = 'fake_file.name' + + with patch('builtins.open', create=True) as mock_open: + mock_open.return_value = MagicMock(spec=io.IOBase) + file_handle = mock_open.return_value.__enter__.return_value + + provider.execute_ssh_command(client, 'python test.py') + + file_handle.write.assert_has_calls([ + call('\n'), + call('command executed successfully!') + ]) + + @patch('ipa.ipa_utils.extract_archive') + def test_provider_extract_archive(self, mock_extract_archive): + client = MagicMock() + + mock_extract_archive.return_value = 'archive extracted successfully!' + + provider = IpaProvider(*args, **self.kwargs) + provider.log_file = 'fake_file.name' + + with patch('builtins.open', create=True) as mock_open: + mock_open.return_value = MagicMock(spec=io.IOBase) + file_handle = mock_open.return_value.__enter__.return_value + + provider.extract_archive(client, 'archive.tar.xz') + + file_handle.write.assert_has_calls([ + call('\n'), + call('archive extracted successfully!') + ]) + + mock_extract_archive.assert_called_once_with( + client, 'archive.tar.xz', None + ) + @patch.object(IpaProvider, '_set_instance_ip') @patch.object(IpaProvider, '_stop_instance') @patch.object(IpaProvider, '_start_instance') @@ -242,6 +286,80 @@ assert mock_start_instance.call_count == 1 assert mock_set_instance_ip.call_count == 1 + @patch('ipa.ipa_utils.put_file') + def test_provider_put_file(self, mock_put_file): + client = MagicMock() + + file_path = '/home/user/test.file' + basename = 'test.file' + + provider = IpaProvider(*args, **self.kwargs) + out = provider.put_file(client, file_path) + + assert out == basename + + mock_put_file.assert_called_once_with( + client, file_path, basename + ) + + def test_provider_install_package(self): + client = MagicMock() + distro = MagicMock() + distro.install_package.return_value = 'package install successful!' + + provider = IpaProvider(*args, **self.kwargs) + provider.log_file = 'fake_file.name' + provider.distro = distro + + with patch('builtins.open', create=True) as mock_open: + mock_open.return_value = MagicMock(spec=io.IOBase) + file_handle = mock_open.return_value.__enter__.return_value + + provider.install_package(client, 'python') + + file_handle.write.assert_has_calls([ + call('\n'), + 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') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/tests/test_ipa_sles_distro.py new/python3-ipa-1.1.1/tests/test_ipa_sles_distro.py --- old/python3-ipa-1.0.0/tests/test_ipa_sles_distro.py 2017-08-23 00:00:09.000000000 +0200 +++ new/python3-ipa-1.1.1/tests/test_ipa_sles_distro.py 2018-05-10 18:17:37.000000000 +0200 @@ -62,6 +62,21 @@ 'The init system for SUSE distribution cannot be determined.' +def test_sles_install_package(): + """Test install package method for SLES distro.""" + client = MagicMock() + sles = SLES() + + with patch('ipa.ipa_utils.execute_ssh_command', + MagicMock(return_value='')) as mocked: + sles.install_package(client, 'python') + + mocked.assert_called_once_with( + client, + "sudo sh -c 'zypper -n --no-gpg-checks in -y python'" + ) + + def test_sles_reboot(): """Test soft reboot method for SLES distro.""" client = MagicMock() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/python3-ipa-1.0.0/tests/test_ipa_utils.py new/python3-ipa-1.1.1/tests/test_ipa_utils.py --- old/python3-ipa-1.0.0/tests/test_ipa_utils.py 2018-01-31 20:21:42.000000000 +0100 +++ new/python3-ipa-1.1.1/tests/test_ipa_utils.py 2018-05-10 18:17:37.000000000 +0200 @@ -174,6 +174,20 @@ assert expanded[2] == 'test_hard_reboot' +@patch('ipa.ipa_utils.execute_ssh_command') +def test_utils_extract_archive(mock_exec_ssh_command): + client = MagicMock() + + mock_exec_ssh_command.return_value = 'archive successfully extracted!' + + # Test gzip + ipa_utils.extract_archive(client, 'archive.tar.gz', extract_path='/tmp/') + mock_exec_ssh_command.assert_called_once_with( + client, 'tar -xf archive.tar.gz -C /tmp/' + ) + mock_exec_ssh_command.reset_mock() + + def test_utils_duplicate_files(): """Test exception raised if duplicate test files exist.""" test_dirs = ['tests/data/tests', 'tests/data/tests2'] @@ -262,6 +276,20 @@ assert isinstance(rand_string, str) +def test_utils_put_file(): + client = MagicMock() + sftp_client = MagicMock() + client.open_sftp.return_value = sftp_client + + source_file = '/home/user/temp.file' + destination_file = 'temp.file' + + ipa_utils.put_file(client, source_file, destination_file) + + sftp_client.put.assert_called_once_with(source_file, destination_file) + sftp_client.close.assert_called_once_with() + + def test_utils_redirect_output(): """Test redirect output context manager.""" temp_file = NamedTemporaryFile(mode='w+', delete=False)