diff --git a/tests/integration/images/__init__.py b/tests/integration/images/__init__.py deleted file mode 100644 index 313e098..0000000 --- a/tests/integration/images/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ - - -def initialize_image(manifest, build_server, bootstrap_info): - if manifest.provider['name'] == 'virtualbox': - import vbox - return vbox.initialize_image(manifest, build_server, bootstrap_info) - if manifest.provider['name'] == 'ec2': - import ami - credentials = {'access-key': build_server.build_settings['ec2-credentials']['access-key'], - 'secret-key': build_server.build_settings['ec2-credentials']['secret-key']} - return ami.initialize_image(manifest, credentials, bootstrap_info) diff --git a/tests/integration/images/ami.py b/tests/integration/images/ami.py deleted file mode 100644 index 895bf9c..0000000 --- a/tests/integration/images/ami.py +++ /dev/null @@ -1,38 +0,0 @@ -from image import Image -import logging -from contextlib import contextmanager -log = logging.getLogger(__name__) - - -def initialize_image(manifest, credentials, bootstrap_info): - image = AmazonMachineImage(manifest, bootstrap_info._ec2['image'], - bootstrap_info._ec2['region'], credentials) - return image - - -class AmazonMachineImage(Image): - - def __init__(self, manifest, image_id, region, credentials): - super(AmazonMachineImage, self).__init__(manifest) - - from boto.ec2 import connect_to_region as ec2_connect - self.ec2_connection = ec2_connect(region, aws_access_key_id=credentials['access-key'], - aws_secret_access_key=credentials['secret-key']) - from boto.vpc import connect_to_region as vpc_connect - self.vpc_connection = vpc_connect(region, aws_access_key_id=credentials['access-key'], - aws_secret_access_key=credentials['secret-key']) - - self.ami = self.ec2_connection.get_image(image_id) - - def destroy(self): - log.debug('Deleting AMI') - self.ami.deregister() - for device, block_device_type in self.ami.block_device_mapping.items(): - self.ec2_connection.delete_snapshot(block_device_type.snapshot_id) - del self.ami - - @contextmanager - def get_instance(self, instance_type): - from ..instances.ec2 import boot_image - with boot_image(self.ami, instance_type, self.ec2_connection, self.vpc_connection) as instance: - yield instance diff --git a/tests/integration/images/image.py b/tests/integration/images/image.py deleted file mode 100644 index 5260afa..0000000 --- a/tests/integration/images/image.py +++ /dev/null @@ -1,6 +0,0 @@ - - -class Image(object): - - def __init__(self, manifest): - self.manifest = manifest diff --git a/tests/integration/images/vbox.py b/tests/integration/images/vbox.py deleted file mode 100644 index f8d7803..0000000 --- a/tests/integration/images/vbox.py +++ /dev/null @@ -1,64 +0,0 @@ -from image import Image -import virtualbox -import logging -from contextlib import contextmanager -log = logging.getLogger(__name__) - - -def initialize_image(manifest, build_server, bootstrap_info): - from bootstrapvz.remote.build_servers.local import LocalBuildServer - if isinstance(build_server, LocalBuildServer): - image_path = bootstrap_info.volume.image_path - else: - import tempfile - handle, image_path = tempfile.mkstemp() - import os - os.close(handle) - try: - build_server.download(bootstrap_info.volume.image_path, image_path) - except (Exception, KeyboardInterrupt): - os.remove(image_path) - raise - finally: - build_server.delete(bootstrap_info.volume.image_path) - image = VirtualBoxImage(manifest, image_path) - return image - - -class VirtualBoxImage(Image): - - def __init__(self, manifest, image_path): - super(VirtualBoxImage, self).__init__(manifest) - self.image_path = image_path - self.vbox = virtualbox.VirtualBox() - - def open(self): - log.debug('Opening vbox medium `{path}\''.format(path=self.image_path)) - self.medium = self.vbox.open_medium(self.image_path, # location - virtualbox.library.DeviceType.hard_disk, # device_type - virtualbox.library.AccessMode.read_only, # access_mode - False) # force_new_uuid - - def close(self): - log.debug('Closing vbox medium `{path}\''.format(path=self.image_path)) - self.medium.close() - - def destroy(self): - log.debug('Deleting vbox image `{path}\''.format(path=self.image_path)) - import os - os.remove(self.image_path) - del self.image_path - - @contextmanager - def get_instance(self): - import hashlib - image_hash = hashlib.sha1(self.image_path).hexdigest() - name = 'bootstrap-vz-{hash}'.format(hash=image_hash[:8]) - - self.open() - try: - from ..instances.vbox import boot_image - with boot_image(name, self) as instance: - yield instance - finally: - self.close() diff --git a/tests/integration/instances/instance.py b/tests/integration/instances/instance.py deleted file mode 100644 index 049fc0f..0000000 --- a/tests/integration/instances/instance.py +++ /dev/null @@ -1,16 +0,0 @@ - - -class Instance(object): - - def __init__(self, name, image): - self.name = name - self.image = image - - def boot(self): - pass - - def shutdown(self): - pass - - def destroy(self): - pass diff --git a/tests/integration/instances/__init__.py b/tests/integration/providers/__init__.py similarity index 100% rename from tests/integration/instances/__init__.py rename to tests/integration/providers/__init__.py diff --git a/tests/integration/instances/ec2.py b/tests/integration/providers/ec2/__init__.py similarity index 58% rename from tests/integration/instances/ec2.py rename to tests/integration/providers/ec2/__init__.py index f658856..62ef7ce 100644 --- a/tests/integration/instances/ec2.py +++ b/tests/integration/providers/ec2/__init__.py @@ -1,11 +1,36 @@ from contextlib import contextmanager -from ..tools import waituntil +from tests.integration.tools import waituntil import logging log = logging.getLogger(__name__) @contextmanager -def boot_image(image, instance_type, ec2_connection, vpc_connection): +def boot_image(manifest, build_server, bootstrap_info, instance_type=None): + + credentials = {'access-key': build_server.build_settings['ec2-credentials']['access-key'], + 'secret-key': build_server.build_settings['ec2-credentials']['secret-key']} + from boto.ec2 import connect_to_region as ec2_connect + ec2_connection = ec2_connect(bootstrap_info._ec2['region'], + aws_access_key_id=credentials['access-key'], + aws_secret_access_key=credentials['secret-key']) + from boto.vpc import connect_to_region as vpc_connect + vpc_connection = vpc_connect(bootstrap_info._ec2['region'], + aws_access_key_id=credentials['access-key'], + aws_secret_access_key=credentials['secret-key']) + + if manifest.volume['backing'] == 'ebs': + from images import EBSImage + image = EBSImage(bootstrap_info._ec2['image'], ec2_connection) + + try: + with run_instance(image, instance_type, ec2_connection, vpc_connection) as instance: + yield instance + finally: + image.destroy() + + +@contextmanager +def run_instance(image, instance_type, ec2_connection, vpc_connection): with create_env(ec2_connection, vpc_connection) as boot_env: @@ -18,8 +43,8 @@ def boot_image(image, instance_type, ec2_connection, vpc_connection): instance = None try: log.debug('Booting ec2 instance') - reservation = image.run(instance_type=instance_type, - subnet_id=boot_env['subnet_id']) + reservation = image.ami.run(instance_type=instance_type, + subnet_id=boot_env['subnet_id']) [instance] = reservation.instances instance.add_tag('Name', 'bootstrap-vz test instance') diff --git a/tests/integration/providers/ec2/images.py b/tests/integration/providers/ec2/images.py new file mode 100644 index 0000000..b2932fc --- /dev/null +++ b/tests/integration/providers/ec2/images.py @@ -0,0 +1,19 @@ +import logging +log = logging.getLogger(__name__) + + +class AmazonMachineImage(object): + + def __init__(self, image_id, ec2_connection): + self.ec2_connection = ec2_connection + self.ami = self.ec2_connection.get_image(image_id) + + +class EBSImage(AmazonMachineImage): + + def destroy(self): + log.debug('Deleting AMI') + self.ami.deregister() + for device, block_device_type in self.ami.block_device_mapping.items(): + self.ec2_connection.delete_snapshot(block_device_type.snapshot_id) + del self.ami diff --git a/tests/integration/providers/virtualbox/__init__.py b/tests/integration/providers/virtualbox/__init__.py new file mode 100644 index 0000000..f94fb26 --- /dev/null +++ b/tests/integration/providers/virtualbox/__init__.py @@ -0,0 +1,55 @@ +from contextlib import contextmanager +import logging +log = logging.getLogger(__name__) + + +@contextmanager +def boot_image(manifest, build_server, bootstrap_info): + from bootstrapvz.remote.build_servers.local import LocalBuildServer + if isinstance(build_server, LocalBuildServer): + image_path = bootstrap_info.volume.image_path + else: + import tempfile + handle, image_path = tempfile.mkstemp() + import os + os.close(handle) + try: + build_server.download(bootstrap_info.volume.image_path, image_path) + except (Exception, KeyboardInterrupt): + os.remove(image_path) + raise + finally: + build_server.delete(bootstrap_info.volume.image_path) + + from image import VirtualBoxImage + image = VirtualBoxImage(image_path) + + import hashlib + image_hash = hashlib.sha1(image_path).hexdigest() + instance_name = 'bootstrap-vz-{hash}'.format(hash=image_hash[:8]) + + try: + image.open() + try: + with run_instance(image, instance_name, manifest) as instance: + yield instance + finally: + image.close() + finally: + image.destroy() + + +@contextmanager +def run_instance(image, instance_name, manifest): + from instance import VirtualBoxInstance + instance = VirtualBoxInstance(image, instance_name, + manifest.system['architecture'], manifest.system['release']) + try: + instance.create() + try: + instance.boot() + yield instance + finally: + instance.shutdown() + finally: + instance.destroy() diff --git a/tests/integration/providers/virtualbox/image.py b/tests/integration/providers/virtualbox/image.py new file mode 100644 index 0000000..7d51eb8 --- /dev/null +++ b/tests/integration/providers/virtualbox/image.py @@ -0,0 +1,27 @@ +import virtualbox +import logging +log = logging.getLogger(__name__) + + +class VirtualBoxImage(object): + + def __init__(self, image_path): + self.image_path = image_path + self.vbox = virtualbox.VirtualBox() + + def open(self): + log.debug('Opening vbox medium `{path}\''.format(path=self.image_path)) + self.medium = self.vbox.open_medium(self.image_path, # location + virtualbox.library.DeviceType.hard_disk, # device_type + virtualbox.library.AccessMode.read_only, # access_mode + False) # force_new_uuid + + def close(self): + log.debug('Closing vbox medium `{path}\''.format(path=self.image_path)) + self.medium.close() + + def destroy(self): + log.debug('Deleting vbox image `{path}\''.format(path=self.image_path)) + import os + os.remove(self.image_path) + del self.image_path diff --git a/tests/integration/instances/vbox.py b/tests/integration/providers/virtualbox/instance.py similarity index 88% rename from tests/integration/instances/vbox.py rename to tests/integration/providers/virtualbox/instance.py index 1ab9927..fa520e6 100644 --- a/tests/integration/instances/vbox.py +++ b/tests/integration/providers/virtualbox/instance.py @@ -1,32 +1,20 @@ -from instance import Instance import virtualbox from contextlib import contextmanager -from ..tools import waituntil +from tests.integration.tools import waituntil import logging log = logging.getLogger(__name__) -@contextmanager -def boot_image(name, image): - instance = VirtualBoxInstance(name, image) - try: - instance.create() - try: - instance.boot() - yield instance - finally: - instance.shutdown() - finally: - instance.destroy() - - -class VirtualBoxInstance(Instance): +class VirtualBoxInstance(object): cpus = 1 memory = 256 - def __init__(self, name, image): - super(VirtualBoxInstance, self).__init__(name, image) + def __init__(self, image, name, arch, release): + self.image = image + self.name = name + self.arch = arch + self.release = release self.vbox = virtualbox.VirtualBox() manager = virtualbox.Manager() self.session = manager.get_session() @@ -35,7 +23,7 @@ class VirtualBoxInstance(Instance): log.debug('Creating vbox machine `{name}\''.format(name=self.name)) # create machine os_type = {'x86': 'Debian', - 'amd64': 'Debian_64'}.get(self.image.manifest.system['architecture']) + 'amd64': 'Debian_64'}.get(self.arch) self.machine = self.vbox.create_machine(settings_file='', name=self.name, groups=[], os_type_id=os_type, flags='') self.machine.cpu_count = self.cpus @@ -71,12 +59,12 @@ class VirtualBoxInstance(Instance): def boot(self): log.debug('Booting vbox machine `{name}\''.format(name=self.name)) self.machine.launch_vm_process(self.session, 'headless').wait_for_completion(-1) - from ..tools import read_from_socket + from tests.integration.tools import read_from_socket # Gotta figure out a more reliable way to check when the system is done booting. # Maybe bootstrapped unit test images should have a startup script that issues # a callback to the host. from bootstrapvz.common.tools import get_codename - if get_codename(self.image.manifest.system['release']) in ['squeeze', 'wheezy']: + if get_codename(self.release) in ['squeeze', 'wheezy']: termination_string = 'INIT: Entering runlevel: 2' else: termination_string = 'Debian GNU/Linux' diff --git a/tests/integration/tools/__init__.py b/tests/integration/tools/__init__.py index 052dcbd..d2c9945 100644 --- a/tests/integration/tools/__init__.py +++ b/tests/integration/tools/__init__.py @@ -26,13 +26,10 @@ def boot_manifest(manifest_data, boot_vars={}): bootstrap_info = connection.run(manifest) log.info('Creating and booting instance') - from ..images import initialize_image - image = initialize_image(manifest, build_server, bootstrap_info) - try: - with image.get_instance(**boot_vars) as instance: - yield instance - finally: - image.destroy() + import importlib + provider_module = importlib.import_module('tests.integration.providers.' + manifest.provider['name']) + with provider_module.boot_image(manifest, build_server, bootstrap_info, **boot_vars) as instance: + yield instance def waituntil(predicate, timeout=5, interval=0.05):