Improve tests for the ephemeral build manager.

This commit is contained in:
Jake Moshenko 2014-12-22 16:22:07 -05:00
parent e53b6b0e21
commit 2b6c2a2a50
2 changed files with 115 additions and 35 deletions

View file

@ -2,9 +2,11 @@ import logging
import etcd
import uuid
import calendar
import os.path
from datetime import datetime, timedelta
from trollius import From, coroutine, Return
from trollius import From, coroutine, Return, async
from concurrent.futures import ThreadPoolExecutor
from buildman.manager.basemanager import BaseManager
from buildman.manager.executor import PopenExecutor, EC2Executor
@ -16,25 +18,11 @@ logger = logging.getLogger(__name__)
ETCD_BUILDER_PREFIX = 'building/'
def clear_etcd(client):
""" Debugging method used to clear out the section of etcd we are using to track jobs in flight.
"""
try:
building = client.read(ETCD_BUILDER_PREFIX, recursive=True)
for child in building.leaves:
if not child.dir:
logger.warning('Deleting key: %s', child.key)
client.delete(child.key)
except KeyError:
pass
ETCD_EXPIRE_RESULT = 'expire'
class EphemeralBuilderManager(BaseManager):
""" Build manager implementation for the Enterprise Registry. """
shutting_down = False
_executors = {
'popen': PopenExecutor,
'ec2': EC2Executor,
@ -43,7 +31,10 @@ class EphemeralBuilderManager(BaseManager):
_etcd_client_klass = etcd.Client
def __init__(self, *args, **kwargs):
self._shutting_down = False
self._manager_config = None
self._async_thread_executor = None
self._etcd_client = None
self._component_to_job = {}
@ -51,8 +42,35 @@ class EphemeralBuilderManager(BaseManager):
self._executor = None
self._worker_watch_task = None
super(EphemeralBuilderManager, self).__init__(*args, **kwargs)
def _watch_builders(self):
""" Watch the builders key for expirations.
"""
if not self._shutting_down:
workers_future = self._etcd_client.watch(ETCD_BUILDER_PREFIX, recursive=True)
workers_future.add_done_callback(self._handle_key_expiration)
logger.debug('Scheduling watch task.')
self._worker_watch_task = async(workers_future)
def _handle_key_expiration(self, changed_key_future):
""" Handle when a builder expires
"""
if self._worker_watch_task is None or self._worker_watch_task.done():
self._watch_builders()
if changed_key_future.cancelled():
# Due to lack of interest, tomorrow has been cancelled
return
etcd_result = changed_key_future.result()
if etcd_result.action == ETCD_EXPIRE_RESULT:
# Handle the expiration
logger.debug('Builder expired, clean up the old build node')
async(self._clean_up_old_builder(etcd_result.key, etcd_result._prev_node.value))
def initialize(self, manager_config):
logger.debug('Calling initialize')
self._manager_config = manager_config
@ -65,7 +83,11 @@ class EphemeralBuilderManager(BaseManager):
etcd_port = self._manager_config.get('ETCD_PORT', 2379)
logger.debug('Connecting to etcd on %s:%s', etcd_host, etcd_port)
self._etcd_client = AsyncWrapper(self._etcd_client_klass(host=etcd_host, port=etcd_port))
self._async_thread_executor = ThreadPoolExecutor(self._manager_config.get('ETCD_WORKERS', 5))
self._etcd_client = AsyncWrapper(self._etcd_client_klass(host=etcd_host, port=etcd_port),
executor=self._async_thread_executor)
self._watch_builders()
def setup_time(self):
setup_time = self._manager_config.get('MACHINE_SETUP_TIME', 300)
@ -73,8 +95,17 @@ class EphemeralBuilderManager(BaseManager):
return setup_time
def shutdown(self):
logger.debug('Calling shutdown.')
raise NotImplementedError
logger.debug('Shutting down worker.')
self._shutting_down = True
if self._worker_watch_task is not None:
logger.debug('Canceling watch task.')
self._worker_watch_task.cancel()
self._worker_watch_task = None
if self._async_thread_executor is not None:
logger.debug('Shutting down thread pool executor.')
self._async_thread_executor.shutdown()
@coroutine
def schedule(self, build_job, loop):
@ -161,9 +192,8 @@ class EphemeralBuilderManager(BaseManager):
yield From(self._etcd_client.delete(job_key))
@staticmethod
def _etcd_job_key(build_job):
""" Create a key which is used to track a job in etcd.
"""
return '{0}{1}'.format(ETCD_BUILDER_PREFIX, build_job.job_details['build_uuid'])
return os.path.join(ETCD_BUILDER_PREFIX, build_job.job_details['build_uuid'])