diff options
Diffstat (limited to 'tests/cloud_tests')
-rw-r--r-- | tests/cloud_tests/__init__.py | 2 | ||||
-rw-r--r-- | tests/cloud_tests/__main__.py | 5 | ||||
-rw-r--r-- | tests/cloud_tests/args.py | 4 | ||||
-rw-r--r-- | tests/cloud_tests/bddeb.py | 19 | ||||
-rw-r--r-- | tests/cloud_tests/collect.py | 3 | ||||
-rw-r--r-- | tests/cloud_tests/config.py | 1 | ||||
-rw-r--r-- | tests/cloud_tests/images/nocloudkvm.py | 88 | ||||
-rw-r--r-- | tests/cloud_tests/instances/base.py | 12 | ||||
-rw-r--r-- | tests/cloud_tests/instances/lxd.py | 10 | ||||
-rw-r--r-- | tests/cloud_tests/instances/nocloudkvm.py | 217 | ||||
-rw-r--r-- | tests/cloud_tests/platforms.yaml | 4 | ||||
-rw-r--r-- | tests/cloud_tests/platforms/__init__.py | 2 | ||||
-rw-r--r-- | tests/cloud_tests/platforms/nocloudkvm.py | 90 | ||||
-rw-r--r-- | tests/cloud_tests/releases.yaml | 19 | ||||
-rw-r--r-- | tests/cloud_tests/setup_image.py | 32 | ||||
-rw-r--r-- | tests/cloud_tests/snapshots/nocloudkvm.py | 74 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/bugs/README.md (renamed from tests/cloud_tests/configs/bugs/README.md) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/bugs/lp1511485.yaml (renamed from tests/cloud_tests/configs/bugs/lp1511485.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/bugs/lp1611074.yaml (renamed from tests/cloud_tests/configs/bugs/lp1611074.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/bugs/lp1628337.yaml (renamed from tests/cloud_tests/configs/bugs/lp1628337.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/README.md (renamed from tests/cloud_tests/configs/examples/README.md) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/TODO.md (renamed from tests/cloud_tests/configs/examples/TODO.md) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/add_apt_repositories.yaml (renamed from tests/cloud_tests/configs/examples/add_apt_repositories.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/alter_completion_message.yaml (renamed from tests/cloud_tests/configs/examples/alter_completion_message.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/configure_instance_trusted_ca_certificates.yaml (renamed from tests/cloud_tests/configs/examples/configure_instance_trusted_ca_certificates.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/configure_instances_ssh_keys.yaml (renamed from tests/cloud_tests/configs/examples/configure_instances_ssh_keys.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/including_user_groups.yaml (renamed from tests/cloud_tests/configs/examples/including_user_groups.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/install_arbitrary_packages.yaml (renamed from tests/cloud_tests/configs/examples/install_arbitrary_packages.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/install_run_chef_recipes.yaml (renamed from tests/cloud_tests/configs/examples/install_run_chef_recipes.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/run_apt_upgrade.yaml (renamed from tests/cloud_tests/configs/examples/run_apt_upgrade.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/run_commands.yaml (renamed from tests/cloud_tests/configs/examples/run_commands.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/run_commands_first_boot.yaml (renamed from tests/cloud_tests/configs/examples/run_commands_first_boot.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/setup_run_puppet.yaml (renamed from tests/cloud_tests/configs/examples/setup_run_puppet.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/examples/writing_out_arbitrary_files.yaml (renamed from tests/cloud_tests/configs/examples/writing_out_arbitrary_files.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/main/README.md (renamed from tests/cloud_tests/configs/main/README.md) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/main/command_output_simple.yaml (renamed from tests/cloud_tests/configs/main/command_output_simple.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/README.md (renamed from tests/cloud_tests/configs/modules/README.md) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/TODO.md (renamed from tests/cloud_tests/configs/modules/TODO.md) | 2 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_conf.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_conf.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_disable_suites.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_disable_suites.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_primary.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_primary.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_proxy.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_proxy.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_security.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_security.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_sources_key.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_sources_key.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_sources_keyserver.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_sources_keyserver.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_sources_list.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_sources_list.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_configure_sources_ppa.yaml (renamed from tests/cloud_tests/configs/modules/apt_configure_sources_ppa.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_pipelining_disable.yaml (renamed from tests/cloud_tests/configs/modules/apt_pipelining_disable.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/apt_pipelining_os.yaml (renamed from tests/cloud_tests/configs/modules/apt_pipelining_os.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/bootcmd.yaml (renamed from tests/cloud_tests/configs/modules/bootcmd.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/byobu.yaml (renamed from tests/cloud_tests/configs/modules/byobu.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ca_certs.yaml (renamed from tests/cloud_tests/configs/modules/ca_certs.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/debug_disable.yaml (renamed from tests/cloud_tests/configs/modules/debug_disable.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/debug_enable.yaml (renamed from tests/cloud_tests/configs/modules/debug_enable.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/final_message.yaml (renamed from tests/cloud_tests/configs/modules/final_message.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/keys_to_console.yaml (renamed from tests/cloud_tests/configs/modules/keys_to_console.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/landscape.yaml (renamed from tests/cloud_tests/configs/modules/landscape.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/locale.yaml (renamed from tests/cloud_tests/configs/modules/locale.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/lxd_bridge.yaml (renamed from tests/cloud_tests/configs/modules/lxd_bridge.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/lxd_dir.yaml (renamed from tests/cloud_tests/configs/modules/lxd_dir.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ntp.yaml (renamed from tests/cloud_tests/configs/modules/ntp.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ntp_pools.yaml (renamed from tests/cloud_tests/configs/modules/ntp_pools.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ntp_servers.yaml (renamed from tests/cloud_tests/configs/modules/ntp_servers.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/package_update_upgrade_install.yaml (renamed from tests/cloud_tests/configs/modules/package_update_upgrade_install.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/runcmd.yaml (renamed from tests/cloud_tests/configs/modules/runcmd.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/salt_minion.yaml (renamed from tests/cloud_tests/configs/modules/salt_minion.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/seed_random_command.yaml (renamed from tests/cloud_tests/configs/modules/seed_random_command.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/seed_random_data.yaml (renamed from tests/cloud_tests/configs/modules/seed_random_data.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_hostname.yaml (renamed from tests/cloud_tests/configs/modules/set_hostname.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_hostname_fqdn.yaml (renamed from tests/cloud_tests/configs/modules/set_hostname_fqdn.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_password.yaml (renamed from tests/cloud_tests/configs/modules/set_password.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_password_expire.yaml (renamed from tests/cloud_tests/configs/modules/set_password_expire.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_password_list.yaml (renamed from tests/cloud_tests/configs/modules/set_password_list.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/set_password_list_string.yaml (renamed from tests/cloud_tests/configs/modules/set_password_list_string.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/snappy.yaml (renamed from tests/cloud_tests/configs/modules/snappy.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_disable.yaml (renamed from tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_disable.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_enable.yaml (renamed from tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_enable.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ssh_import_id.yaml (renamed from tests/cloud_tests/configs/modules/ssh_import_id.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ssh_keys_generate.yaml (renamed from tests/cloud_tests/configs/modules/ssh_keys_generate.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/ssh_keys_provided.yaml (renamed from tests/cloud_tests/configs/modules/ssh_keys_provided.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/timezone.yaml (renamed from tests/cloud_tests/configs/modules/timezone.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/user_groups.yaml (renamed from tests/cloud_tests/configs/modules/user_groups.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/testcases/modules/write_files.yaml (renamed from tests/cloud_tests/configs/modules/write_files.yaml) | 0 | ||||
-rw-r--r-- | tests/cloud_tests/util.py | 43 |
84 files changed, 597 insertions, 30 deletions
diff --git a/tests/cloud_tests/__init__.py b/tests/cloud_tests/__init__.py index 07148c12..98c1d6c7 100644 --- a/tests/cloud_tests/__init__.py +++ b/tests/cloud_tests/__init__.py @@ -7,7 +7,7 @@ import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) TESTCASES_DIR = os.path.join(BASE_DIR, 'testcases') -TEST_CONF_DIR = os.path.join(BASE_DIR, 'configs') +TEST_CONF_DIR = os.path.join(BASE_DIR, 'testcases') TREE_BASE = os.sep.join(BASE_DIR.split(os.sep)[:-2]) diff --git a/tests/cloud_tests/__main__.py b/tests/cloud_tests/__main__.py index 260ddb3f..7ee29cad 100644 --- a/tests/cloud_tests/__main__.py +++ b/tests/cloud_tests/__main__.py @@ -4,6 +4,7 @@ import argparse import logging +import os import sys from tests.cloud_tests import args, bddeb, collect, manage, run_funcs, verify @@ -50,7 +51,7 @@ def main(): return -1 # run handler - LOG.debug('running with args: %s\n', parsed) + LOG.debug('running with args: %s', parsed) return { 'bddeb': bddeb.bddeb, 'collect': collect.collect, @@ -63,6 +64,8 @@ def main(): if __name__ == "__main__": + if os.geteuid() == 0: + sys.exit('Do not run as root') sys.exit(main()) # vi: ts=4 expandtab diff --git a/tests/cloud_tests/args.py b/tests/cloud_tests/args.py index 369d60db..c6c1877b 100644 --- a/tests/cloud_tests/args.py +++ b/tests/cloud_tests/args.py @@ -170,9 +170,9 @@ def normalize_collect_args(args): @param args: parsed args @return_value: updated args, or None if errors occurred """ - # platform should default to all supported + # platform should default to lxd if len(args.platform) == 0: - args.platform = config.ENABLED_PLATFORMS + args.platform = ['lxd'] args.platform = util.sorted_unique(args.platform) # os name should default to all enabled diff --git a/tests/cloud_tests/bddeb.py b/tests/cloud_tests/bddeb.py index 53dbf74e..fba8a0c7 100644 --- a/tests/cloud_tests/bddeb.py +++ b/tests/cloud_tests/bddeb.py @@ -11,7 +11,7 @@ from tests.cloud_tests import (config, LOG) from tests.cloud_tests import (platforms, images, snapshots, instances) from tests.cloud_tests.stage import (PlatformComponent, run_stage, run_single) -build_deps = ['devscripts', 'equivs', 'git', 'tar'] +pre_reqs = ['devscripts', 'equivs', 'git', 'tar'] def _out(cmd_res): @@ -26,13 +26,9 @@ def build_deb(args, instance): @return_value: tuple of results and fail count """ # update remote system package list and install build deps - LOG.debug('installing build deps') - pkgs = ' '.join(build_deps) - cmd = 'apt-get update && apt-get install --yes {}'.format(pkgs) - instance.execute(['/bin/sh', '-c', cmd]) - # TODO Remove this call once we have a ci-deps Makefile target - instance.execute(['mk-build-deps', '--install', '-t', - 'apt-get --no-install-recommends --yes', 'cloud-init']) + LOG.debug('installing pre-reqs') + pkgs = ' '.join(pre_reqs) + instance.execute('apt-get update && apt-get install --yes {}'.format(pkgs)) # local tmpfile that must be deleted local_tarball = tempfile.NamedTemporaryFile().name @@ -40,7 +36,7 @@ def build_deb(args, instance): # paths to use in remote system output_link = '/root/cloud-init_all.deb' remote_tarball = _out(instance.execute(['mktemp'])) - extract_dir = _out(instance.execute(['mktemp', '--directory'])) + extract_dir = '/root' bddeb_path = os.path.join(extract_dir, 'packages', 'bddeb') git_env = {'GIT_DIR': os.path.join(extract_dir, '.git'), 'GIT_WORK_TREE': extract_dir} @@ -56,6 +52,11 @@ def build_deb(args, instance): instance.execute(['git', 'commit', '-a', '-m', 'tmp', '--allow-empty'], env=git_env) + LOG.debug('installing deps') + deps_path = os.path.join(extract_dir, 'tools', 'read-dependencies') + instance.execute([deps_path, '--install', '--test-distro', + '--distro', 'ubuntu', '--python-version', '3']) + LOG.debug('building deb in remote system at: %s', output_link) bddeb_args = args.bddeb_args.split() if args.bddeb_args else [] instance.execute([bddeb_path, '-d'] + bddeb_args, env=git_env) diff --git a/tests/cloud_tests/collect.py b/tests/cloud_tests/collect.py index b44e8bdd..4a2422ed 100644 --- a/tests/cloud_tests/collect.py +++ b/tests/cloud_tests/collect.py @@ -120,6 +120,7 @@ def collect_image(args, platform, os_name): os_config = config.load_os_config( platform.platform_name, os_name, require_enabled=True, feature_overrides=args.feature_override) + LOG.debug('os config: %s', os_config) component = PlatformComponent( partial(images.get_image, platform, os_config)) @@ -144,6 +145,8 @@ def collect_platform(args, platform_name): platform_config = config.load_platform_config( platform_name, require_enabled=True) + platform_config['data_dir'] = args.data_dir + LOG.debug('platform config: %s', platform_config) component = PlatformComponent( partial(platforms.get_platform, platform_name, platform_config)) diff --git a/tests/cloud_tests/config.py b/tests/cloud_tests/config.py index 4d5dc801..52fc2bda 100644 --- a/tests/cloud_tests/config.py +++ b/tests/cloud_tests/config.py @@ -112,6 +112,7 @@ def load_os_config(platform_name, os_name, require_enabled=False, feature_conf = main_conf['features'] feature_groups = conf.get('feature_groups', []) overrides = merge_config(get(conf, 'features'), feature_overrides) + conf['arch'] = c_util.get_architecture() conf['features'] = merge_feature_groups( feature_conf, feature_groups, overrides) diff --git a/tests/cloud_tests/images/nocloudkvm.py b/tests/cloud_tests/images/nocloudkvm.py new file mode 100644 index 00000000..a7af0e59 --- /dev/null +++ b/tests/cloud_tests/images/nocloudkvm.py @@ -0,0 +1,88 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""NoCloud KVM Image Base Class.""" + +from tests.cloud_tests.images import base +from tests.cloud_tests.snapshots import nocloudkvm as nocloud_kvm_snapshot + + +class NoCloudKVMImage(base.Image): + """NoCloud KVM backed image.""" + + platform_name = "nocloud-kvm" + + def __init__(self, platform, config, img_path): + """Set up image. + + @param platform: platform object + @param config: image configuration + @param img_path: path to the image + """ + self.modified = False + self._instance = None + self._img_path = img_path + + super(NoCloudKVMImage, self).__init__(platform, config) + + @property + def instance(self): + """Returns an instance of an image.""" + if not self._instance: + if not self._img_path: + raise RuntimeError() + + self._instance = self.platform.create_image( + self.properties, self.config, self.features, self._img_path, + image_desc=str(self), use_desc='image-modification') + return self._instance + + @property + def properties(self): + """Dictionary containing: 'arch', 'os', 'version', 'release'.""" + return { + 'arch': self.config['arch'], + 'os': self.config['family'], + 'release': self.config['release'], + 'version': self.config['version'], + } + + def execute(self, *args, **kwargs): + """Execute command in image, modifying image.""" + return self.instance.execute(*args, **kwargs) + + def push_file(self, local_path, remote_path): + """Copy file at 'local_path' to instance at 'remote_path'.""" + return self.instance.push_file(local_path, remote_path) + + def run_script(self, *args, **kwargs): + """Run script in image, modifying image. + + @return_value: script output + """ + return self.instance.run_script(*args, **kwargs) + + def snapshot(self): + """Create snapshot of image, block until done.""" + if not self._img_path: + raise RuntimeError() + + instance = self.platform.create_image( + self.properties, self.config, self.features, + self._img_path, image_desc=str(self), use_desc='snapshot') + + return nocloud_kvm_snapshot.NoCloudKVMSnapshot( + self.platform, self.properties, self.config, + self.features, instance) + + def destroy(self): + """Unset path to signal image is no longer used. + + The removal of the images and all other items is handled by the + framework. In some cases we want to keep the images, so let the + framework decide whether to keep or destroy everything. + """ + self._img_path = None + self._instance.destroy() + super(NoCloudKVMImage, self).destroy() + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/instances/base.py b/tests/cloud_tests/instances/base.py index 959e9cce..9bdda608 100644 --- a/tests/cloud_tests/instances/base.py +++ b/tests/cloud_tests/instances/base.py @@ -23,7 +23,7 @@ class Instance(object): self.config = config self.features = features - def execute(self, command, stdout=None, stderr=None, env={}, + def execute(self, command, stdout=None, stderr=None, env=None, rcs=None, description=None): """Execute command in instance, recording output, error and exit code. @@ -31,6 +31,8 @@ class Instance(object): target filesystem being available at /. @param command: the command to execute as root inside the image + if command is a string, then it will be executed as: + ['sh', '-c', command] @param stdout, stderr: file handles to write output and error to @param env: environment variables @param rcs: allowed return codes from command @@ -88,7 +90,7 @@ class Instance(object): return self.execute( ['/bin/bash', script_path], rcs=rcs, description=description) finally: - self.execute(['rm', script_path], rcs=rcs) + self.execute(['rm', '-f', script_path], rcs=rcs) def tmpfile(self): """Get a tmp file in the target. @@ -137,9 +139,9 @@ class Instance(object): tests.append(self.config['cloud_init_ready_script']) formatted_tests = ' && '.join(clean_test(t) for t in tests) - test_cmd = ('for ((i=0;i<{time};i++)); do {test} && exit 0; sleep 1; ' - 'done; exit 1;').format(time=time, test=formatted_tests) - cmd = ['/bin/bash', '-c', test_cmd] + cmd = ('i=0; while [ $i -lt {time} ] && i=$(($i+1)); do {test} && ' + 'exit 0; sleep 1; done; exit 1').format(time=time, + test=formatted_tests) if self.execute(cmd, rcs=(0, 1))[-1] != 0: raise OSError('timeout: after {}s system not started'.format(time)) diff --git a/tests/cloud_tests/instances/lxd.py b/tests/cloud_tests/instances/lxd.py index b9c2cc6b..a43918c2 100644 --- a/tests/cloud_tests/instances/lxd.py +++ b/tests/cloud_tests/instances/lxd.py @@ -31,7 +31,7 @@ class LXDInstance(base.Instance): self._pylxd_container.sync() return self._pylxd_container - def execute(self, command, stdout=None, stderr=None, env={}, + def execute(self, command, stdout=None, stderr=None, env=None, rcs=None, description=None): """Execute command in instance, recording output, error and exit code. @@ -39,6 +39,8 @@ class LXDInstance(base.Instance): target filesystem being available at /. @param command: the command to execute as root inside the image + if command is a string, then it will be executed as: + ['sh', '-c', command] @param stdout: file handler to write output @param stderr: file handler to write error @param env: environment variables @@ -46,6 +48,12 @@ class LXDInstance(base.Instance): @param description: purpose of command @return_value: tuple containing stdout data, stderr data, exit code """ + if env is None: + env = {} + + if isinstance(command, str): + command = ['sh', '-c', command] + # ensure instance is running and execute the command self.start() res = self.pylxd_container.execute(command, environment=env) diff --git a/tests/cloud_tests/instances/nocloudkvm.py b/tests/cloud_tests/instances/nocloudkvm.py new file mode 100644 index 00000000..8a0e5319 --- /dev/null +++ b/tests/cloud_tests/instances/nocloudkvm.py @@ -0,0 +1,217 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base NoCloud KVM instance.""" + +import os +import paramiko +import socket +import subprocess +import time + +from cloudinit import util as c_util +from tests.cloud_tests.instances import base +from tests.cloud_tests import util + + +class NoCloudKVMInstance(base.Instance): + """NoCloud KVM backed instance.""" + + platform_name = "nocloud-kvm" + + def __init__(self, platform, name, properties, config, features, + user_data, meta_data): + """Set up instance. + + @param platform: platform object + @param name: image path + @param properties: dictionary of properties + @param config: dictionary of configuration values + @param features: dictionary of supported feature flags + """ + self.user_data = user_data + self.meta_data = meta_data + self.ssh_key_file = os.path.join(platform.config['data_dir'], + platform.config['private_key']) + self.ssh_port = None + self.pid = None + self.pid_file = None + + super(NoCloudKVMInstance, self).__init__( + platform, name, properties, config, features) + + def destroy(self): + """Clean up instance.""" + if self.pid: + try: + c_util.subp(['kill', '-9', self.pid]) + except util.ProcessExectuionError: + pass + + if self.pid_file: + os.remove(self.pid_file) + + self.pid = None + super(NoCloudKVMInstance, self).destroy() + + def execute(self, command, stdout=None, stderr=None, env=None, + rcs=None, description=None): + """Execute command in instance. + + Assumes functional networking and execution as root with the + target filesystem being available at /. + + @param command: the command to execute as root inside the image + if command is a string, then it will be executed as: + ['sh', '-c', command] + @param stdout, stderr: file handles to write output and error to + @param env: environment variables + @param rcs: allowed return codes from command + @param description: purpose of command + @return_value: tuple containing stdout data, stderr data, exit code + """ + if env is None: + env = {} + + if isinstance(command, str): + command = ['sh', '-c', command] + + if self.pid: + return self.ssh(command) + else: + return self.mount_image_callback(command) + (0,) + + def mount_image_callback(self, cmd): + """Run mount-image-callback.""" + out, err = c_util.subp(['sudo', 'mount-image-callback', + '--system-mounts', '--system-resolvconf', + self.name, '--', 'chroot', + '_MOUNTPOINT_'] + cmd) + + return out, err + + def generate_seed(self, tmpdir): + """Generate nocloud seed from user-data""" + seed_file = os.path.join(tmpdir, '%s_seed.img' % self.name) + user_data_file = os.path.join(tmpdir, '%s_user_data' % self.name) + + with open(user_data_file, "w") as ud_file: + ud_file.write(self.user_data) + + c_util.subp(['cloud-localds', seed_file, user_data_file]) + + return seed_file + + def get_free_port(self): + """Get a free port assigned by the kernel.""" + s = socket.socket() + s.bind(('', 0)) + num = s.getsockname()[1] + s.close() + return num + + def push_file(self, local_path, remote_path): + """Copy file at 'local_path' to instance at 'remote_path'. + + If we have a pid then SSH is up, otherwise, use + mount-image-callback. + + @param local_path: path on local instance + @param remote_path: path on remote instance + """ + if self.pid: + super(NoCloudKVMInstance, self).push_file() + else: + local_file = open(local_path) + p = subprocess.Popen(['sudo', 'mount-image-callback', + '--system-mounts', '--system-resolvconf', + self.name, '--', 'chroot', '_MOUNTPOINT_', + '/bin/sh', '-c', 'cat - > %s' % remote_path], + stdin=local_file, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + p.wait() + + def sftp_put(self, path, data): + """SFTP put a file.""" + client = self._ssh_connect() + sftp = client.open_sftp() + + with sftp.open(path, 'w') as f: + f.write(data) + + client.close() + + def ssh(self, command): + """Run a command via SSH.""" + client = self._ssh_connect() + + try: + _, out, err = client.exec_command(util.shell_pack(command)) + except paramiko.SSHException: + raise util.InTargetExecuteError('', '', -1, command, self.name) + + exit = out.channel.recv_exit_status() + out = ''.join(out.readlines()) + err = ''.join(err.readlines()) + client.close() + + return out, err, exit + + def _ssh_connect(self, hostname='localhost', username='ubuntu', + banner_timeout=120, retry_attempts=30): + """Connect via SSH.""" + private_key = paramiko.RSAKey.from_private_key_file(self.ssh_key_file) + client = paramiko.SSHClient() + client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + while retry_attempts: + try: + client.connect(hostname=hostname, username=username, + port=self.ssh_port, pkey=private_key, + banner_timeout=banner_timeout) + return client + except (paramiko.SSHException, TypeError): + time.sleep(1) + retry_attempts = retry_attempts - 1 + + error_desc = 'Failed command to: %s@%s:%s' % (username, hostname, + self.ssh_port) + raise util.InTargetExecuteError('', '', -1, 'ssh connect', + self.name, error_desc) + + def start(self, wait=True, wait_for_cloud_init=False): + """Start instance.""" + tmpdir = self.platform.config['data_dir'] + seed = self.generate_seed(tmpdir) + self.pid_file = os.path.join(tmpdir, '%s.pid' % self.name) + self.ssh_port = self.get_free_port() + + subprocess.Popen(['./tools/xkvm', + '--disk', '%s,cache=unsafe' % self.name, + '--disk', '%s,cache=unsafe' % seed, + '--netdev', + 'user,hostfwd=tcp::%s-:22' % self.ssh_port, + '--', '-pidfile', self.pid_file, '-vnc', 'none', + '-m', '2G', '-smp', '2'], + close_fds=True, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + + while not os.path.exists(self.pid_file): + time.sleep(1) + + with open(self.pid_file, 'r') as pid_f: + self.pid = pid_f.readlines()[0].strip() + + if wait: + self._wait_for_system(wait_for_cloud_init) + + def write_data(self, remote_path, data): + """Write data to instance filesystem. + + @param remote_path: path in instance + @param data: data to write, either str or bytes + """ + self.sftp_put(remote_path, data) + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/platforms.yaml b/tests/cloud_tests/platforms.yaml index b91834ab..fa4f845e 100644 --- a/tests/cloud_tests/platforms.yaml +++ b/tests/cloud_tests/platforms.yaml @@ -59,6 +59,10 @@ platforms: {{ config_get("user.user-data", properties.default) }} cloud-init-vendor.tpl: | {{ config_get("user.vendor-data", properties.default) }} + nocloud-kvm: + enabled: true + private_key: id_rsa + public_key: id_rsa.pub ec2: {} azure: {} diff --git a/tests/cloud_tests/platforms/__init__.py b/tests/cloud_tests/platforms/__init__.py index 443f6d44..3490fe87 100644 --- a/tests/cloud_tests/platforms/__init__.py +++ b/tests/cloud_tests/platforms/__init__.py @@ -3,8 +3,10 @@ """Main init.""" from tests.cloud_tests.platforms import lxd +from tests.cloud_tests.platforms import nocloudkvm PLATFORMS = { + 'nocloud-kvm': nocloudkvm.NoCloudKVMPlatform, 'lxd': lxd.LXDPlatform, } diff --git a/tests/cloud_tests/platforms/nocloudkvm.py b/tests/cloud_tests/platforms/nocloudkvm.py new file mode 100644 index 00000000..f1f81877 --- /dev/null +++ b/tests/cloud_tests/platforms/nocloudkvm.py @@ -0,0 +1,90 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base NoCloud KVM platform.""" +import glob +import os + +from simplestreams import filters +from simplestreams import mirrors +from simplestreams import objectstores +from simplestreams import util as s_util + +from cloudinit import util as c_util +from tests.cloud_tests.images import nocloudkvm as nocloud_kvm_image +from tests.cloud_tests.instances import nocloudkvm as nocloud_kvm_instance +from tests.cloud_tests.platforms import base +from tests.cloud_tests import util + + +class NoCloudKVMPlatform(base.Platform): + """NoCloud KVM test platform.""" + + platform_name = 'nocloud-kvm' + + def get_image(self, img_conf): + """Get image using specified image configuration. + + @param img_conf: configuration for image + @return_value: cloud_tests.images instance + """ + (url, path) = s_util.path_from_mirror_url(img_conf['mirror_url'], None) + + filter = filters.get_filters(['arch=%s' % c_util.get_architecture(), + 'release=%s' % img_conf['release'], + 'ftype=disk1.img']) + mirror_config = {'filters': filter, + 'keep_items': False, + 'max_items': 1, + 'checksumming_reader': True, + 'item_download': True + } + + def policy(content, path): + return s_util.read_signed(content, keyring=img_conf['keyring']) + + smirror = mirrors.UrlMirrorReader(url, policy=policy) + tstore = objectstores.FileStore(img_conf['mirror_dir']) + tmirror = mirrors.ObjectFilterMirror(config=mirror_config, + objectstore=tstore) + tmirror.sync(smirror, path) + + search_d = os.path.join(img_conf['mirror_dir'], '**', + img_conf['release'], '**', '*.img') + + images = [] + for fname in glob.iglob(search_d, recursive=True): + images.append(fname) + + if len(images) != 1: + raise Exception('No unique images found') + + image = nocloud_kvm_image.NoCloudKVMImage(self, img_conf, images[0]) + if img_conf.get('override_templates', False): + image.update_templates(self.config.get('template_overrides', {}), + self.config.get('template_files', {})) + return image + + def create_image(self, properties, config, features, + src_img_path, image_desc=None, use_desc=None, + user_data=None, meta_data=None): + """Create an image + + @param src_img_path: image path to launch from + @param properties: image properties + @param config: image configuration + @param features: image features + @param image_desc: description of image being launched + @param use_desc: description of container's use + @return_value: cloud_tests.instances instance + """ + name = util.gen_instance_name(image_desc=image_desc, use_desc=use_desc) + img_path = os.path.join(self.config['data_dir'], name + '.qcow2') + c_util.subp(['qemu-img', 'create', '-f', 'qcow2', + '-b', src_img_path, img_path]) + + return nocloud_kvm_instance.NoCloudKVMInstance(self, img_path, + properties, config, + features, user_data, + meta_data) + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/releases.yaml b/tests/cloud_tests/releases.yaml index c8dd1427..ec7e2d5b 100644 --- a/tests/cloud_tests/releases.yaml +++ b/tests/cloud_tests/releases.yaml @@ -27,7 +27,12 @@ default_release_config: # features groups and additional feature settings feature_groups: [] features: {} - + nocloud-kvm: + mirror_url: https://cloud-images.ubuntu.com/daily + mirror_dir: '/srv/citest/nocloud-kvm' + keyring: /usr/share/keyrings/ubuntu-cloudimage-keyring.gpg + setup_overrides: null + override_templates: false # lxd specific default configuration options lxd: # default sstreams server to use for lxd image retrieval @@ -121,6 +126,9 @@ releases: # EOL: Jul 2018 default: enabled: true + release: artful + version: 17.10 + family: ubuntu feature_groups: - base - debian_base @@ -134,6 +142,9 @@ releases: # EOL: Jan 2018 default: enabled: true + release: zesty + version: 17.04 + family: ubuntu feature_groups: - base - debian_base @@ -147,6 +158,9 @@ releases: # EOL: Apr 2021 default: enabled: true + release: xenial + version: 16.04 + family: ubuntu feature_groups: - base - debian_base @@ -160,6 +174,9 @@ releases: # EOL: Apr 2019 default: enabled: true + release: trusty + version: 14.04 + family: ubuntu feature_groups: - base - debian_base diff --git a/tests/cloud_tests/setup_image.py b/tests/cloud_tests/setup_image.py index 8053a093..6672ffb3 100644 --- a/tests/cloud_tests/setup_image.py +++ b/tests/cloud_tests/setup_image.py @@ -5,6 +5,7 @@ from functools import partial import os +from cloudinit import util as c_util from tests.cloud_tests import LOG from tests.cloud_tests import stage, util @@ -19,7 +20,7 @@ def installed_package_version(image, package, ensure_installed=True): """ os_family = util.get_os_family(image.properties['os']) if os_family == 'debian': - cmd = ['dpkg-query', '-W', "--showformat='${Version}'", package] + cmd = ['dpkg-query', '-W', "--showformat=${Version}", package] elif os_family == 'redhat': cmd = ['rpm', '-q', '--queryformat', "'%{VERSION}'", package] else: @@ -49,11 +50,11 @@ def install_deb(args, image): LOG.debug(msg) remote_path = os.path.join('/tmp', os.path.basename(args.deb)) image.push_file(args.deb, remote_path) - cmd = 'dpkg -i {} || apt-get install --yes -f'.format(remote_path) - image.execute(['/bin/sh', '-c', cmd], description=msg) + cmd = 'dpkg -i {}; apt-get install --yes -f'.format(remote_path) + image.execute(cmd, description=msg) # check installed deb version matches package - fmt = ['-W', "--showformat='${Version}'"] + fmt = ['-W', "--showformat=${Version}"] (out, err, exit) = image.execute(['dpkg-deb'] + fmt + [remote_path]) expected_version = out.strip() found_version = installed_package_version(image, 'cloud-init') @@ -113,7 +114,7 @@ def upgrade(args, image): msg = 'upgrading cloud-init' LOG.debug(msg) - image.execute(['/bin/sh', '-c', cmd], description=msg) + image.execute(cmd, description=msg) def upgrade_full(args, image): @@ -134,7 +135,7 @@ def upgrade_full(args, image): msg = 'full system upgrade' LOG.debug(msg) - image.execute(['/bin/sh', '-c', cmd], description=msg) + image.execute(cmd, description=msg) def run_script(args, image): @@ -165,7 +166,7 @@ def enable_ppa(args, image): msg = 'enable ppa: "{}" in target'.format(ppa) LOG.debug(msg) cmd = 'add-apt-repository --yes {} && apt-get update'.format(ppa) - image.execute(['/bin/sh', '-c', cmd], description=msg) + image.execute(cmd, description=msg) def enable_repo(args, image): @@ -188,7 +189,21 @@ def enable_repo(args, image): msg = 'enable repo: "{}" in target'.format(args.repo) LOG.debug(msg) - image.execute(['/bin/sh', '-c', cmd], description=msg) + image.execute(cmd, description=msg) + + +def generate_ssh_keys(data_dir): + """Generate SSH keys to be used with image.""" + LOG.info('generating SSH keys') + filename = os.path.join(data_dir, 'id_rsa') + + if os.path.exists(filename): + c_util.del_file(filename) + + c_util.subp(['ssh-keygen', '-t', 'rsa', '-b', '4096', + '-f', filename, '-P', '', + '-C', 'ubuntu@cloud_test'], + capture=True) def setup_image(args, image): @@ -226,6 +241,7 @@ def setup_image(args, image): 'set up for {}'.format(image), calls, continue_after_error=False) LOG.debug('after setup complete, installed cloud-init version is: %s', installed_package_version(image, 'cloud-init')) + generate_ssh_keys(args.data_dir) return res # vi: ts=4 expandtab diff --git a/tests/cloud_tests/snapshots/nocloudkvm.py b/tests/cloud_tests/snapshots/nocloudkvm.py new file mode 100644 index 00000000..09998349 --- /dev/null +++ b/tests/cloud_tests/snapshots/nocloudkvm.py @@ -0,0 +1,74 @@ +# This file is part of cloud-init. See LICENSE file for license information. + +"""Base NoCloud KVM snapshot.""" +import os + +from tests.cloud_tests.snapshots import base + + +class NoCloudKVMSnapshot(base.Snapshot): + """NoCloud KVM image copy backed snapshot.""" + + platform_name = "nocloud-kvm" + + def __init__(self, platform, properties, config, features, + instance): + """Set up snapshot. + + @param platform: platform object + @param properties: image properties + @param config: image config + @param features: supported feature flags + """ + self.instance = instance + + super(NoCloudKVMSnapshot, self).__init__( + platform, properties, config, features) + + def launch(self, user_data, meta_data=None, block=True, start=True, + use_desc=None): + """Launch instance. + + @param user_data: user-data for the instance + @param instance_id: instance-id for the instance + @param block: wait until instance is created + @param start: start instance and wait until fully started + @param use_desc: description of snapshot instance use + @return_value: an Instance + """ + key_file = os.path.join(self.platform.config['data_dir'], + self.platform.config['public_key']) + user_data = self.inject_ssh_key(user_data, key_file) + + instance = self.platform.create_image( + self.properties, self.config, self.features, + self.instance.name, image_desc=str(self), use_desc=use_desc, + user_data=user_data, meta_data=meta_data) + + if start: + instance.start() + + return instance + + def inject_ssh_key(self, user_data, key_file): + """Inject the authorized key into the user_data.""" + with open(key_file) as f: + value = f.read() + + key = 'ssh_authorized_keys:' + value = ' - %s' % value.strip() + user_data = user_data.split('\n') + if key in user_data: + user_data.insert(user_data.index(key) + 1, '%s' % value) + else: + user_data.insert(-1, '%s' % key) + user_data.insert(-1, '%s' % value) + + return '\n'.join(user_data) + + def destroy(self): + """Clean up snapshot data.""" + self.instance.destroy() + super(NoCloudKVMSnapshot, self).destroy() + +# vi: ts=4 expandtab diff --git a/tests/cloud_tests/configs/bugs/README.md b/tests/cloud_tests/testcases/bugs/README.md index 09ce0765..09ce0765 100644 --- a/tests/cloud_tests/configs/bugs/README.md +++ b/tests/cloud_tests/testcases/bugs/README.md diff --git a/tests/cloud_tests/configs/bugs/lp1511485.yaml b/tests/cloud_tests/testcases/bugs/lp1511485.yaml index ebf9763f..ebf9763f 100644 --- a/tests/cloud_tests/configs/bugs/lp1511485.yaml +++ b/tests/cloud_tests/testcases/bugs/lp1511485.yaml diff --git a/tests/cloud_tests/configs/bugs/lp1611074.yaml b/tests/cloud_tests/testcases/bugs/lp1611074.yaml index 960679d5..960679d5 100644 --- a/tests/cloud_tests/configs/bugs/lp1611074.yaml +++ b/tests/cloud_tests/testcases/bugs/lp1611074.yaml diff --git a/tests/cloud_tests/configs/bugs/lp1628337.yaml b/tests/cloud_tests/testcases/bugs/lp1628337.yaml index e39b3cd8..e39b3cd8 100644 --- a/tests/cloud_tests/configs/bugs/lp1628337.yaml +++ b/tests/cloud_tests/testcases/bugs/lp1628337.yaml diff --git a/tests/cloud_tests/configs/examples/README.md b/tests/cloud_tests/testcases/examples/README.md index 110a223b..110a223b 100644 --- a/tests/cloud_tests/configs/examples/README.md +++ b/tests/cloud_tests/testcases/examples/README.md diff --git a/tests/cloud_tests/configs/examples/TODO.md b/tests/cloud_tests/testcases/examples/TODO.md index 8db0e98e..8db0e98e 100644 --- a/tests/cloud_tests/configs/examples/TODO.md +++ b/tests/cloud_tests/testcases/examples/TODO.md diff --git a/tests/cloud_tests/configs/examples/add_apt_repositories.yaml b/tests/cloud_tests/testcases/examples/add_apt_repositories.yaml index 4b8575f7..4b8575f7 100644 --- a/tests/cloud_tests/configs/examples/add_apt_repositories.yaml +++ b/tests/cloud_tests/testcases/examples/add_apt_repositories.yaml diff --git a/tests/cloud_tests/configs/examples/alter_completion_message.yaml b/tests/cloud_tests/testcases/examples/alter_completion_message.yaml index 9e154f80..9e154f80 100644 --- a/tests/cloud_tests/configs/examples/alter_completion_message.yaml +++ b/tests/cloud_tests/testcases/examples/alter_completion_message.yaml diff --git a/tests/cloud_tests/configs/examples/configure_instance_trusted_ca_certificates.yaml b/tests/cloud_tests/testcases/examples/configure_instance_trusted_ca_certificates.yaml index ad32b088..ad32b088 100644 --- a/tests/cloud_tests/configs/examples/configure_instance_trusted_ca_certificates.yaml +++ b/tests/cloud_tests/testcases/examples/configure_instance_trusted_ca_certificates.yaml diff --git a/tests/cloud_tests/configs/examples/configure_instances_ssh_keys.yaml b/tests/cloud_tests/testcases/examples/configure_instances_ssh_keys.yaml index f3eaf3ce..f3eaf3ce 100644 --- a/tests/cloud_tests/configs/examples/configure_instances_ssh_keys.yaml +++ b/tests/cloud_tests/testcases/examples/configure_instances_ssh_keys.yaml diff --git a/tests/cloud_tests/configs/examples/including_user_groups.yaml b/tests/cloud_tests/testcases/examples/including_user_groups.yaml index 0aa7ad21..0aa7ad21 100644 --- a/tests/cloud_tests/configs/examples/including_user_groups.yaml +++ b/tests/cloud_tests/testcases/examples/including_user_groups.yaml diff --git a/tests/cloud_tests/configs/examples/install_arbitrary_packages.yaml b/tests/cloud_tests/testcases/examples/install_arbitrary_packages.yaml index d3980228..d3980228 100644 --- a/tests/cloud_tests/configs/examples/install_arbitrary_packages.yaml +++ b/tests/cloud_tests/testcases/examples/install_arbitrary_packages.yaml diff --git a/tests/cloud_tests/configs/examples/install_run_chef_recipes.yaml b/tests/cloud_tests/testcases/examples/install_run_chef_recipes.yaml index 0bec305e..0bec305e 100644 --- a/tests/cloud_tests/configs/examples/install_run_chef_recipes.yaml +++ b/tests/cloud_tests/testcases/examples/install_run_chef_recipes.yaml diff --git a/tests/cloud_tests/configs/examples/run_apt_upgrade.yaml b/tests/cloud_tests/testcases/examples/run_apt_upgrade.yaml index 2b7eae4c..2b7eae4c 100644 --- a/tests/cloud_tests/configs/examples/run_apt_upgrade.yaml +++ b/tests/cloud_tests/testcases/examples/run_apt_upgrade.yaml diff --git a/tests/cloud_tests/configs/examples/run_commands.yaml b/tests/cloud_tests/testcases/examples/run_commands.yaml index b0e311ba..b0e311ba 100644 --- a/tests/cloud_tests/configs/examples/run_commands.yaml +++ b/tests/cloud_tests/testcases/examples/run_commands.yaml diff --git a/tests/cloud_tests/configs/examples/run_commands_first_boot.yaml b/tests/cloud_tests/testcases/examples/run_commands_first_boot.yaml index 7bd803db..7bd803db 100644 --- a/tests/cloud_tests/configs/examples/run_commands_first_boot.yaml +++ b/tests/cloud_tests/testcases/examples/run_commands_first_boot.yaml diff --git a/tests/cloud_tests/configs/examples/setup_run_puppet.yaml b/tests/cloud_tests/testcases/examples/setup_run_puppet.yaml index e366c042..e366c042 100644 --- a/tests/cloud_tests/configs/examples/setup_run_puppet.yaml +++ b/tests/cloud_tests/testcases/examples/setup_run_puppet.yaml diff --git a/tests/cloud_tests/configs/examples/writing_out_arbitrary_files.yaml b/tests/cloud_tests/testcases/examples/writing_out_arbitrary_files.yaml index 6f78f994..6f78f994 100644 --- a/tests/cloud_tests/configs/examples/writing_out_arbitrary_files.yaml +++ b/tests/cloud_tests/testcases/examples/writing_out_arbitrary_files.yaml diff --git a/tests/cloud_tests/configs/main/README.md b/tests/cloud_tests/testcases/main/README.md index 60346063..60346063 100644 --- a/tests/cloud_tests/configs/main/README.md +++ b/tests/cloud_tests/testcases/main/README.md diff --git a/tests/cloud_tests/configs/main/command_output_simple.yaml b/tests/cloud_tests/testcases/main/command_output_simple.yaml index 08ca8940..08ca8940 100644 --- a/tests/cloud_tests/configs/main/command_output_simple.yaml +++ b/tests/cloud_tests/testcases/main/command_output_simple.yaml diff --git a/tests/cloud_tests/configs/modules/README.md b/tests/cloud_tests/testcases/modules/README.md index d66101f2..d66101f2 100644 --- a/tests/cloud_tests/configs/modules/README.md +++ b/tests/cloud_tests/testcases/modules/README.md diff --git a/tests/cloud_tests/configs/modules/TODO.md b/tests/cloud_tests/testcases/modules/TODO.md index d496da95..0b933b3b 100644 --- a/tests/cloud_tests/configs/modules/TODO.md +++ b/tests/cloud_tests/testcases/modules/TODO.md @@ -89,8 +89,6 @@ Not applicable to write a test for this as it specifies when something should be ## ssh authkey fingerprints The authkey_hash key does not appear to work. In fact the default claims to be md5, however syslog only shows sha256 -## ubuntu init switch - ## update etc hosts 2016-11-17: Issues with changing /etc/hosts and lxc backend. diff --git a/tests/cloud_tests/configs/modules/apt_configure_conf.yaml b/tests/cloud_tests/testcases/modules/apt_configure_conf.yaml index de453000..de453000 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_conf.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_conf.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_disable_suites.yaml b/tests/cloud_tests/testcases/modules/apt_configure_disable_suites.yaml index 98800673..98800673 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_disable_suites.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_disable_suites.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_primary.yaml b/tests/cloud_tests/testcases/modules/apt_configure_primary.yaml index 41bcf2fd..41bcf2fd 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_primary.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_primary.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_proxy.yaml b/tests/cloud_tests/testcases/modules/apt_configure_proxy.yaml index be6c6f81..be6c6f81 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_proxy.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_proxy.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_security.yaml b/tests/cloud_tests/testcases/modules/apt_configure_security.yaml index 83dd51df..83dd51df 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_security.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_security.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_sources_key.yaml b/tests/cloud_tests/testcases/modules/apt_configure_sources_key.yaml index bde9398a..bde9398a 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_sources_key.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_sources_key.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_sources_keyserver.yaml b/tests/cloud_tests/testcases/modules/apt_configure_sources_keyserver.yaml index 25088135..25088135 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_sources_keyserver.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_sources_keyserver.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_sources_list.yaml b/tests/cloud_tests/testcases/modules/apt_configure_sources_list.yaml index 143cb080..143cb080 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_sources_list.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_sources_list.yaml diff --git a/tests/cloud_tests/configs/modules/apt_configure_sources_ppa.yaml b/tests/cloud_tests/testcases/modules/apt_configure_sources_ppa.yaml index 9efdae52..9efdae52 100644 --- a/tests/cloud_tests/configs/modules/apt_configure_sources_ppa.yaml +++ b/tests/cloud_tests/testcases/modules/apt_configure_sources_ppa.yaml diff --git a/tests/cloud_tests/configs/modules/apt_pipelining_disable.yaml b/tests/cloud_tests/testcases/modules/apt_pipelining_disable.yaml index bd9b5d08..bd9b5d08 100644 --- a/tests/cloud_tests/configs/modules/apt_pipelining_disable.yaml +++ b/tests/cloud_tests/testcases/modules/apt_pipelining_disable.yaml diff --git a/tests/cloud_tests/configs/modules/apt_pipelining_os.yaml b/tests/cloud_tests/testcases/modules/apt_pipelining_os.yaml index cbed3ba3..cbed3ba3 100644 --- a/tests/cloud_tests/configs/modules/apt_pipelining_os.yaml +++ b/tests/cloud_tests/testcases/modules/apt_pipelining_os.yaml diff --git a/tests/cloud_tests/configs/modules/bootcmd.yaml b/tests/cloud_tests/testcases/modules/bootcmd.yaml index 3a73994e..3a73994e 100644 --- a/tests/cloud_tests/configs/modules/bootcmd.yaml +++ b/tests/cloud_tests/testcases/modules/bootcmd.yaml diff --git a/tests/cloud_tests/configs/modules/byobu.yaml b/tests/cloud_tests/testcases/modules/byobu.yaml index a9aa1f3f..a9aa1f3f 100644 --- a/tests/cloud_tests/configs/modules/byobu.yaml +++ b/tests/cloud_tests/testcases/modules/byobu.yaml diff --git a/tests/cloud_tests/configs/modules/ca_certs.yaml b/tests/cloud_tests/testcases/modules/ca_certs.yaml index d939f435..d939f435 100644 --- a/tests/cloud_tests/configs/modules/ca_certs.yaml +++ b/tests/cloud_tests/testcases/modules/ca_certs.yaml diff --git a/tests/cloud_tests/configs/modules/debug_disable.yaml b/tests/cloud_tests/testcases/modules/debug_disable.yaml index 63218b18..63218b18 100644 --- a/tests/cloud_tests/configs/modules/debug_disable.yaml +++ b/tests/cloud_tests/testcases/modules/debug_disable.yaml diff --git a/tests/cloud_tests/configs/modules/debug_enable.yaml b/tests/cloud_tests/testcases/modules/debug_enable.yaml index d44147db..d44147db 100644 --- a/tests/cloud_tests/configs/modules/debug_enable.yaml +++ b/tests/cloud_tests/testcases/modules/debug_enable.yaml diff --git a/tests/cloud_tests/configs/modules/final_message.yaml b/tests/cloud_tests/testcases/modules/final_message.yaml index c9ed6118..c9ed6118 100644 --- a/tests/cloud_tests/configs/modules/final_message.yaml +++ b/tests/cloud_tests/testcases/modules/final_message.yaml diff --git a/tests/cloud_tests/configs/modules/keys_to_console.yaml b/tests/cloud_tests/testcases/modules/keys_to_console.yaml index 5d86e739..5d86e739 100644 --- a/tests/cloud_tests/configs/modules/keys_to_console.yaml +++ b/tests/cloud_tests/testcases/modules/keys_to_console.yaml diff --git a/tests/cloud_tests/configs/modules/landscape.yaml b/tests/cloud_tests/testcases/modules/landscape.yaml index ed2c37c4..ed2c37c4 100644 --- a/tests/cloud_tests/configs/modules/landscape.yaml +++ b/tests/cloud_tests/testcases/modules/landscape.yaml diff --git a/tests/cloud_tests/configs/modules/locale.yaml b/tests/cloud_tests/testcases/modules/locale.yaml index e01518a1..e01518a1 100644 --- a/tests/cloud_tests/configs/modules/locale.yaml +++ b/tests/cloud_tests/testcases/modules/locale.yaml diff --git a/tests/cloud_tests/configs/modules/lxd_bridge.yaml b/tests/cloud_tests/testcases/modules/lxd_bridge.yaml index e6b7e76a..e6b7e76a 100644 --- a/tests/cloud_tests/configs/modules/lxd_bridge.yaml +++ b/tests/cloud_tests/testcases/modules/lxd_bridge.yaml diff --git a/tests/cloud_tests/configs/modules/lxd_dir.yaml b/tests/cloud_tests/testcases/modules/lxd_dir.yaml index f93a3fa7..f93a3fa7 100644 --- a/tests/cloud_tests/configs/modules/lxd_dir.yaml +++ b/tests/cloud_tests/testcases/modules/lxd_dir.yaml diff --git a/tests/cloud_tests/configs/modules/ntp.yaml b/tests/cloud_tests/testcases/modules/ntp.yaml index fbef431b..fbef431b 100644 --- a/tests/cloud_tests/configs/modules/ntp.yaml +++ b/tests/cloud_tests/testcases/modules/ntp.yaml diff --git a/tests/cloud_tests/configs/modules/ntp_pools.yaml b/tests/cloud_tests/testcases/modules/ntp_pools.yaml index 3a93faa2..3a93faa2 100644 --- a/tests/cloud_tests/configs/modules/ntp_pools.yaml +++ b/tests/cloud_tests/testcases/modules/ntp_pools.yaml diff --git a/tests/cloud_tests/configs/modules/ntp_servers.yaml b/tests/cloud_tests/testcases/modules/ntp_servers.yaml index d59d45a8..d59d45a8 100644 --- a/tests/cloud_tests/configs/modules/ntp_servers.yaml +++ b/tests/cloud_tests/testcases/modules/ntp_servers.yaml diff --git a/tests/cloud_tests/configs/modules/package_update_upgrade_install.yaml b/tests/cloud_tests/testcases/modules/package_update_upgrade_install.yaml index 71d24b83..71d24b83 100644 --- a/tests/cloud_tests/configs/modules/package_update_upgrade_install.yaml +++ b/tests/cloud_tests/testcases/modules/package_update_upgrade_install.yaml diff --git a/tests/cloud_tests/configs/modules/runcmd.yaml b/tests/cloud_tests/testcases/modules/runcmd.yaml index 04e5a050..04e5a050 100644 --- a/tests/cloud_tests/configs/modules/runcmd.yaml +++ b/tests/cloud_tests/testcases/modules/runcmd.yaml diff --git a/tests/cloud_tests/configs/modules/salt_minion.yaml b/tests/cloud_tests/testcases/modules/salt_minion.yaml index f20d24f0..f20d24f0 100644 --- a/tests/cloud_tests/configs/modules/salt_minion.yaml +++ b/tests/cloud_tests/testcases/modules/salt_minion.yaml diff --git a/tests/cloud_tests/configs/modules/seed_random_command.yaml b/tests/cloud_tests/testcases/modules/seed_random_command.yaml index 6a9157eb..6a9157eb 100644 --- a/tests/cloud_tests/configs/modules/seed_random_command.yaml +++ b/tests/cloud_tests/testcases/modules/seed_random_command.yaml diff --git a/tests/cloud_tests/configs/modules/seed_random_data.yaml b/tests/cloud_tests/testcases/modules/seed_random_data.yaml index a9b2c885..a9b2c885 100644 --- a/tests/cloud_tests/configs/modules/seed_random_data.yaml +++ b/tests/cloud_tests/testcases/modules/seed_random_data.yaml diff --git a/tests/cloud_tests/configs/modules/set_hostname.yaml b/tests/cloud_tests/testcases/modules/set_hostname.yaml index c96344cf..c96344cf 100644 --- a/tests/cloud_tests/configs/modules/set_hostname.yaml +++ b/tests/cloud_tests/testcases/modules/set_hostname.yaml diff --git a/tests/cloud_tests/configs/modules/set_hostname_fqdn.yaml b/tests/cloud_tests/testcases/modules/set_hostname_fqdn.yaml index daf75931..daf75931 100644 --- a/tests/cloud_tests/configs/modules/set_hostname_fqdn.yaml +++ b/tests/cloud_tests/testcases/modules/set_hostname_fqdn.yaml diff --git a/tests/cloud_tests/configs/modules/set_password.yaml b/tests/cloud_tests/testcases/modules/set_password.yaml index 04d7c58a..04d7c58a 100644 --- a/tests/cloud_tests/configs/modules/set_password.yaml +++ b/tests/cloud_tests/testcases/modules/set_password.yaml diff --git a/tests/cloud_tests/configs/modules/set_password_expire.yaml b/tests/cloud_tests/testcases/modules/set_password_expire.yaml index 789604b0..789604b0 100644 --- a/tests/cloud_tests/configs/modules/set_password_expire.yaml +++ b/tests/cloud_tests/testcases/modules/set_password_expire.yaml diff --git a/tests/cloud_tests/configs/modules/set_password_list.yaml b/tests/cloud_tests/testcases/modules/set_password_list.yaml index a2a89c9d..a2a89c9d 100644 --- a/tests/cloud_tests/configs/modules/set_password_list.yaml +++ b/tests/cloud_tests/testcases/modules/set_password_list.yaml diff --git a/tests/cloud_tests/configs/modules/set_password_list_string.yaml b/tests/cloud_tests/testcases/modules/set_password_list_string.yaml index c2a0f631..c2a0f631 100644 --- a/tests/cloud_tests/configs/modules/set_password_list_string.yaml +++ b/tests/cloud_tests/testcases/modules/set_password_list_string.yaml diff --git a/tests/cloud_tests/configs/modules/snappy.yaml b/tests/cloud_tests/testcases/modules/snappy.yaml index 43f93295..43f93295 100644 --- a/tests/cloud_tests/configs/modules/snappy.yaml +++ b/tests/cloud_tests/testcases/modules/snappy.yaml diff --git a/tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_disable.yaml b/tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_disable.yaml index 746653ec..746653ec 100644 --- a/tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_disable.yaml +++ b/tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_disable.yaml diff --git a/tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_enable.yaml b/tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_enable.yaml index 9f5dc34a..9f5dc34a 100644 --- a/tests/cloud_tests/configs/modules/ssh_auth_key_fingerprints_enable.yaml +++ b/tests/cloud_tests/testcases/modules/ssh_auth_key_fingerprints_enable.yaml diff --git a/tests/cloud_tests/configs/modules/ssh_import_id.yaml b/tests/cloud_tests/testcases/modules/ssh_import_id.yaml index b62d3f69..b62d3f69 100644 --- a/tests/cloud_tests/configs/modules/ssh_import_id.yaml +++ b/tests/cloud_tests/testcases/modules/ssh_import_id.yaml diff --git a/tests/cloud_tests/configs/modules/ssh_keys_generate.yaml b/tests/cloud_tests/testcases/modules/ssh_keys_generate.yaml index 659fd939..659fd939 100644 --- a/tests/cloud_tests/configs/modules/ssh_keys_generate.yaml +++ b/tests/cloud_tests/testcases/modules/ssh_keys_generate.yaml diff --git a/tests/cloud_tests/configs/modules/ssh_keys_provided.yaml b/tests/cloud_tests/testcases/modules/ssh_keys_provided.yaml index 5ceb3623..5ceb3623 100644 --- a/tests/cloud_tests/configs/modules/ssh_keys_provided.yaml +++ b/tests/cloud_tests/testcases/modules/ssh_keys_provided.yaml diff --git a/tests/cloud_tests/configs/modules/timezone.yaml b/tests/cloud_tests/testcases/modules/timezone.yaml index 5112aa9f..5112aa9f 100644 --- a/tests/cloud_tests/configs/modules/timezone.yaml +++ b/tests/cloud_tests/testcases/modules/timezone.yaml diff --git a/tests/cloud_tests/configs/modules/user_groups.yaml b/tests/cloud_tests/testcases/modules/user_groups.yaml index 71cc9da3..71cc9da3 100644 --- a/tests/cloud_tests/configs/modules/user_groups.yaml +++ b/tests/cloud_tests/testcases/modules/user_groups.yaml diff --git a/tests/cloud_tests/configs/modules/write_files.yaml b/tests/cloud_tests/testcases/modules/write_files.yaml index ce936b7b..ce936b7b 100644 --- a/tests/cloud_tests/configs/modules/write_files.yaml +++ b/tests/cloud_tests/testcases/modules/write_files.yaml diff --git a/tests/cloud_tests/util.py b/tests/cloud_tests/util.py index 2bbe21c7..4357fbb0 100644 --- a/tests/cloud_tests/util.py +++ b/tests/cloud_tests/util.py @@ -2,12 +2,14 @@ """Utilities for re-use across integration tests.""" +import base64 import copy import glob import os import random import shutil import string +import subprocess import tempfile import yaml @@ -242,6 +244,47 @@ def update_user_data(user_data, updates, dump_to_yaml=True): if dump_to_yaml else user_data) +def shell_safe(cmd): + """Produce string safe shell string. + + Create a string that can be passed to: + set -- <string> + to produce the same array that cmd represents. + + Internally we utilize 'getopt's ability/knowledge on how to quote + strings to be safe for shell. This implementation could be changed + to be pure python. It is just a matter of correctly escaping + or quoting characters like: ' " ^ & $ ; ( ) ... + + @param cmd: command as a list + """ + out = subprocess.check_output( + ["getopt", "--shell", "sh", "--options", "", "--", "--"] + list(cmd)) + # out contains ' -- <data>\n'. drop the ' -- ' and the '\n' + return out[4:-1].decode() + + +def shell_pack(cmd): + """Return a string that can shuffled through 'sh' and execute cmd. + + In Python subprocess terms: + check_output(cmd) == check_output(shell_pack(cmd), shell=True) + + @param cmd: list or string of command to pack up + """ + + if isinstance(cmd, str): + cmd = [cmd] + else: + cmd = list(cmd) + + stuffed = shell_safe(cmd) + # for whatever reason b64encode returns bytes when it is clearly + # representable as a string by nature of being base64 encoded. + b64 = base64.b64encode(stuffed.encode()).decode() + return 'eval set -- "$(echo %s | base64 --decode)" && exec "$@"' % b64 + + class InTargetExecuteError(c_util.ProcessExecutionError): """Error type for in target commands that fail.""" |