From b934808cce62361c1ec9f829507373301de73287 Mon Sep 17 00:00:00 2001 From: Jonh Wendell Date: Mon, 6 Apr 2015 11:47:15 -0300 Subject: [PATCH 1/3] ec2_launch: Simplify the logic for getting the IP address Use the function waituntil() available in the integration-test branch to simplify the code that retrieves the IP of the instance. A 'TODO' note was also added to remember us to merge this function once it gets merged. --- bootstrapvz/plugins/ec2_launch/tasks.py | 53 +++++++++++++------------ 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/bootstrapvz/plugins/ec2_launch/tasks.py b/bootstrapvz/plugins/ec2_launch/tasks.py index 53190ef..df159db 100644 --- a/bootstrapvz/plugins/ec2_launch/tasks.py +++ b/bootstrapvz/plugins/ec2_launch/tasks.py @@ -1,10 +1,20 @@ from bootstrapvz.base import Task from bootstrapvz.common import phases from bootstrapvz.providers.ec2.tasks import ami -import time import logging +# TODO: Merge with the method available in wip-integration-tests branch +def waituntil(predicate, timeout=5, interval=0.05): + import time + threshhold = time.time() + timeout + while time.time() < threshhold: + if predicate(): + return True + time.sleep(interval) + return False + + class LaunchEC2Instance(Task): description = 'Launching EC2 instance' phase = phases.image_registration @@ -40,31 +50,22 @@ class PrintPublicIPAddress(Task): filename = '/dev/null' f = open(filename, 'w') - i = 0 - instance = None - while True: - logger.debug('Waiting a bit to get instance metadata...') - time.sleep(5) - - i += 1 - if i > 10: - logger.error('Waited too much, giving up') - break - - r = ec2['connection'].get_only_instances([ec2['instance_id']]) - if not r and not r[0]: - logger.error('Could not get instance metadata') - break - - instance = r[0] - if instance.ip_address: - break - - if instance and instance.ip_address: - logger.info('******* EC2 IP ADDRESS: %s *******' % instance.ip_address) - f.write(instance.ip_address) - else: - logger.error('Could not get IP address for the instance') + r = ec2['connection'].get_only_instances([ec2['instance_id']]) + if not r and not r[0]: + logger.error('Could not get instance metadata') f.write('') + else: + instance = r[0] + + def instance_has_ip(): + instance.update() + return instance.ip_address + + if waituntil(instance_has_ip, timeout=120, interval=5): + logger.info('******* EC2 IP ADDRESS: %s *******' % instance.ip_address) + f.write(instance.ip_address) + else: + logger.error('Could not get IP address for the instance') + f.write('') f.close() From 50d61c735dfc0b390997c3f9384866b7636b3716 Mon Sep 17 00:00:00 2001 From: Jonh Wendell Date: Mon, 6 Apr 2015 14:18:14 -0300 Subject: [PATCH 2/3] ec2_launch: Store the instance object directly in the info dictionary Instead of storing just its ID. This gives quick access to the recently created instance, which allows us to simplify the code that needs to fetch the instance object every time it was necessary. --- bootstrapvz/plugins/ec2_launch/tasks.py | 29 ++++++++++--------------- 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/bootstrapvz/plugins/ec2_launch/tasks.py b/bootstrapvz/plugins/ec2_launch/tasks.py index df159db..c597254 100644 --- a/bootstrapvz/plugins/ec2_launch/tasks.py +++ b/bootstrapvz/plugins/ec2_launch/tasks.py @@ -26,14 +26,14 @@ class LaunchEC2Instance(Task): r = conn.run_instances(info._ec2['image'], security_group_ids=info.manifest.plugins['ec2_launch'].get('security_group_ids'), instance_type=info.manifest.plugins['ec2_launch'].get('instance_type', 't2.micro')) - info._ec2['instance_id'] = r.instances[0].id + info._ec2['instance'] = r.instances[0] if 'tags' in info.manifest.plugins['ec2_launch']: def apply_format(v): return v.format(**info.manifest_vars) tags = info.manifest.plugins['ec2_launch']['tags'] r = {k: apply_format(v) for k, v in tags.items()} - conn.create_tags([info._ec2['instance_id']], r) + conn.create_tags([info._ec2['instance'].id], r) class PrintPublicIPAddress(Task): @@ -50,22 +50,15 @@ class PrintPublicIPAddress(Task): filename = '/dev/null' f = open(filename, 'w') - r = ec2['connection'].get_only_instances([ec2['instance_id']]) - if not r and not r[0]: - logger.error('Could not get instance metadata') - f.write('') + def instance_has_ip(): + ec2['instance'].update() + return ec2['instance'].ip_address + + if waituntil(instance_has_ip, timeout=120, interval=5): + logger.info('******* EC2 IP ADDRESS: %s *******' % ec2['instance'].ip_address) + f.write(ec2['instance'].ip_address) else: - instance = r[0] - - def instance_has_ip(): - instance.update() - return instance.ip_address - - if waituntil(instance_has_ip, timeout=120, interval=5): - logger.info('******* EC2 IP ADDRESS: %s *******' % instance.ip_address) - f.write(instance.ip_address) - else: - logger.error('Could not get IP address for the instance') - f.write('') + logger.error('Could not get IP address for the instance') + f.write('') f.close() From a5cd6e077d57ccace73af65404c7acc38d8bfea9 Mon Sep 17 00:00:00 2001 From: Jonh Wendell Date: Mon, 6 Apr 2015 14:23:36 -0300 Subject: [PATCH 3/3] ec2_launch: Allow to deregister the AMI after launching image If all you want is to test an image or product and to achieve this you need to generate several images a day, you will end up with lots of AMI's and snapshots that have no use in the end of the day. This commit adds the new boolean manifest option "deregister_ami" that, if True, deletes the recently created AMI and snapshot. So, the final result will be only the running instance, nothing else. --- bootstrapvz/plugins/ec2_launch/__init__.py | 2 ++ .../plugins/ec2_launch/manifest-schema.yml | 2 ++ bootstrapvz/plugins/ec2_launch/tasks.py | 21 +++++++++++++++++++ 3 files changed, 25 insertions(+) diff --git a/bootstrapvz/plugins/ec2_launch/__init__.py b/bootstrapvz/plugins/ec2_launch/__init__.py index c1e8caf..69c29c7 100644 --- a/bootstrapvz/plugins/ec2_launch/__init__.py +++ b/bootstrapvz/plugins/ec2_launch/__init__.py @@ -9,3 +9,5 @@ def resolve_tasks(taskset, manifest): taskset.add(tasks.LaunchEC2Instance) if 'print_public_ip' in manifest.plugins['ec2_launch']: taskset.add(tasks.PrintPublicIPAddress) + if manifest.plugins['ec2_launch'].get('deregister_ami', False): + taskset.add(tasks.DeregisterAMI) diff --git a/bootstrapvz/plugins/ec2_launch/manifest-schema.yml b/bootstrapvz/plugins/ec2_launch/manifest-schema.yml index faba1e8..9d7992e 100644 --- a/bootstrapvz/plugins/ec2_launch/manifest-schema.yml +++ b/bootstrapvz/plugins/ec2_launch/manifest-schema.yml @@ -16,3 +16,5 @@ properties: instance_type: {type: string} print_public_ip: {type: string} tags: {type: object} + deregister_ami: {type: boolean} + additionalProperties: false diff --git a/bootstrapvz/plugins/ec2_launch/tasks.py b/bootstrapvz/plugins/ec2_launch/tasks.py index c597254..5d4abc5 100644 --- a/bootstrapvz/plugins/ec2_launch/tasks.py +++ b/bootstrapvz/plugins/ec2_launch/tasks.py @@ -62,3 +62,24 @@ class PrintPublicIPAddress(Task): f.write('') f.close() + + +class DeregisterAMI(Task): + description = 'Deregistering AMI' + phase = phases.image_registration + predecessors = [LaunchEC2Instance] + + @classmethod + def run(cls, info): + ec2 = info._ec2 + logger = logging.getLogger(__name__) + + def instance_running(): + ec2['instance'].update() + return ec2['instance'].state == 'running' + + if waituntil(instance_running, timeout=120, interval=5): + info._ec2['connection'].deregister_image(info._ec2['image']) + info._ec2['snapshot'].delete() + else: + logger.error('Timeout while booting instance')