Get rid of TaskDetails in favor of TaskStub

Change I0b406e4662cfd3cb496b71be77ad10a90c178baa introduces a new
class TaskStub. This class is designed to be a subset of what a
task is in the domain layer: it doesn't contain input, message and
result. TaskStub should be used when listing tasks in order to avoid
extra-overhead of transporting these text fields.
As a result, the TaskDetails introduced by change
I0b406e4662cfd3cb496b71be77ad10a90c178baa doesn't need to exist.

With this patch, the domain layer contains Task and TaskStub instead
of Task, TaskStub and TaskDetails.

partially implements bp async-glance-workers

Change-Id: I2a20b0c5033c6920749370355c2d672dec707c28
This commit is contained in:
Arnaud Legendre 2014-03-25 16:43:56 -07:00
parent 2c0d214a18
commit 7f512fd0ac
17 changed files with 353 additions and 429 deletions

View File

@ -67,6 +67,17 @@ def is_task_mutable(context, task):
return task.owner == context.owner
def is_task_stub_mutable(context, task_stub):
"""Return True if the task stub is mutable in this context."""
if context.is_admin:
return True
if context.owner is None:
return False
return task_stub.owner == context.owner
def proxy_task(context, task):
if is_task_mutable(context, task):
return task
@ -74,11 +85,11 @@ def proxy_task(context, task):
return ImmutableTaskProxy(task)
def proxy_task_details(context, task, task_details):
if is_task_mutable(context, task):
return task_details
def proxy_task_stub(context, task_stub):
if is_task_stub_mutable(context, task_stub):
return task_stub
else:
return ImmutableTaskDetailsProxy(task_details)
return ImmutableTaskStubProxy(task_stub)
class ImageRepoProxy(glance.domain.proxy.Repo):
@ -342,6 +353,9 @@ class ImmutableTaskProxy(object):
expires_at = _immutable_attr('base', 'expires_at')
created_at = _immutable_attr('base', 'created_at')
updated_at = _immutable_attr('base', 'updated_at')
input = _immutable_attr('base', 'input')
message = _immutable_attr('base', 'message')
result = _immutable_attr('base', 'result')
def run(self, executor):
self.base.run(executor)
@ -359,13 +373,18 @@ class ImmutableTaskProxy(object):
raise exception.Forbidden(message)
class ImmutableTaskDetailsProxy(object):
class ImmutableTaskStubProxy(object):
def __init__(self, base):
self.base = base
self.resource_name = 'task stub'
input = _immutable_attr('base', 'input')
message = _immutable_attr('base', 'message')
result = _immutable_attr('base', 'result')
task_id = _immutable_attr('base', 'task_id')
type = _immutable_attr('base', 'type')
status = _immutable_attr('base', 'status')
owner = _immutable_attr('base', 'owner')
expires_at = _immutable_attr('base', 'expires_at')
created_at = _immutable_attr('base', 'created_at')
updated_at = _immutable_attr('base', 'updated_at')
class ImageProxy(glance.domain.proxy.Image):
@ -391,13 +410,6 @@ class TaskProxy(glance.domain.proxy.Task):
super(TaskProxy, self).__init__(task)
class TaskDetailsProxy(glance.domain.proxy.TaskDetails):
def __init__(self, task_details):
self.task_details = task_details
super(TaskDetailsProxy, self).__init__(task_details)
class TaskFactoryProxy(glance.domain.proxy.TaskFactory):
def __init__(self, task_factory, context):
@ -405,8 +417,7 @@ class TaskFactoryProxy(glance.domain.proxy.TaskFactory):
self.context = context
super(TaskFactoryProxy, self).__init__(
task_factory,
task_proxy_class=TaskProxy,
task_details_proxy_class=TaskDetailsProxy)
task_proxy_class=TaskProxy)
def new_task(self, **kwargs):
owner = kwargs.get('owner', self.context.owner)
@ -429,12 +440,18 @@ class TaskRepoProxy(glance.domain.proxy.TaskRepo):
self.context = context
super(TaskRepoProxy, self).__init__(task_repo)
def get_task_stub_and_details(self, task_id):
task, task_details = self.task_repo.get_task_stub_and_details(task_id)
return proxy_task(self.context, task), proxy_task_details(self.context,
task,
task_details)
def get(self, task_id):
task = self.task_repo.get(task_id)
return proxy_task(self.context, task)
def list_tasks(self, *args, **kwargs):
tasks = self.task_repo.list_tasks(*args, **kwargs)
return [proxy_task(self.context, t) for t in tasks]
class TaskStubRepoProxy(glance.domain.proxy.TaskStubRepo):
def __init__(self, task_stub_repo, context):
self.task_stub_repo = task_stub_repo
self.context = context
super(TaskStubRepoProxy, self).__init__(task_stub_repo)
def list(self, *args, **kwargs):
task_stubs = self.task_stub_repo.list(*args, **kwargs)
return [proxy_task_stub(self.context, t) for t in task_stubs]

View File

@ -353,17 +353,14 @@ class TaskProxy(glance.domain.proxy.Task):
self.policy = policy
super(TaskProxy, self).__init__(task)
def run(self, executor):
self.base.run(executor)
class TaskStubProxy(glance.domain.proxy.TaskStub):
class TaskDetailsProxy(glance.domain.proxy.TaskDetails):
def __init__(self, task_details, context, policy):
self.task_details = task_details
def __init__(self, task_stub, context, policy):
self.task_stub = task_stub
self.context = context
self.policy = policy
super(TaskDetailsProxy, self).__init__(task_details)
super(TaskStubProxy, self).__init__(task_stub)
class TaskRepoProxy(glance.domain.proxy.TaskRepo):
@ -376,25 +373,36 @@ class TaskRepoProxy(glance.domain.proxy.TaskRepo):
super(TaskRepoProxy,
self).__init__(task_repo,
task_proxy_class=TaskProxy,
task_proxy_kwargs=proxy_kwargs,
task_details_proxy_class=TaskDetailsProxy,
task_details_proxy_kwargs=proxy_kwargs)
task_proxy_kwargs=proxy_kwargs)
def get_task_stub_and_details(self, task_id):
def get(self, task_id):
self.policy.enforce(self.context, 'get_task', {})
return super(TaskRepoProxy, self).get_task_stub_and_details(task_id)
return super(TaskRepoProxy, self).get(task_id)
def list_tasks(self, *args, **kwargs):
self.policy.enforce(self.context, 'get_tasks', {})
return super(TaskRepoProxy, self).list_tasks(*args, **kwargs)
def add(self, task, task_details=None):
def add(self, task):
self.policy.enforce(self.context, 'add_task', {})
super(TaskRepoProxy, self).add(task, task_details)
super(TaskRepoProxy, self).add(task)
def save(self, task, task_details=None):
def save(self, task):
self.policy.enforce(self.context, 'modify_task', {})
super(TaskRepoProxy, self).save(task, task_details)
super(TaskRepoProxy, self).save(task)
class TaskStubRepoProxy(glance.domain.proxy.TaskStubRepo):
def __init__(self, task_stub_repo, context, task_policy):
self.context = context
self.policy = task_policy
self.task_stub_repo = task_stub_repo
proxy_kwargs = {'context': self.context, 'policy': self.policy}
super(TaskStubRepoProxy,
self).__init__(task_stub_repo,
task_stub_proxy_class=TaskStubProxy,
task_stub_proxy_kwargs=proxy_kwargs)
def list(self, *args, **kwargs):
self.policy.enforce(self.context, 'get_tasks', {})
return super(TaskStubRepoProxy, self).list(*args, **kwargs)
class TaskFactoryProxy(glance.domain.proxy.TaskFactory):
@ -407,6 +415,4 @@ class TaskFactoryProxy(glance.domain.proxy.TaskFactory):
super(TaskFactoryProxy, self).__init__(
task_factory,
task_proxy_class=TaskProxy,
task_proxy_kwargs=proxy_kwargs,
task_details_proxy_class=TaskDetailsProxy,
task_details_proxy_kwargs=proxy_kwargs)
task_proxy_kwargs=proxy_kwargs)

View File

@ -56,20 +56,17 @@ class TasksController(object):
task_repo = self.gateway.get_task_repo(req.context)
live_time = CONF.task.task_time_to_live
try:
new_task = task_factory.new_task_stub(task_type=task['type'],
owner=req.context.owner,
task_time_to_live=live_time)
new_task_details = task_factory.new_task_details(new_task.task_id,
task['input'])
task_repo.add(new_task, new_task_details)
new_task = task_factory.new_task(task_type=task['type'],
owner=req.context.owner,
task_time_to_live=live_time,
task_input=task['input'])
task_repo.add(new_task)
except exception.Forbidden as e:
msg = (_("Forbidden to create task. Reason: %(reason)s")
% {'reason': unicode(e)})
LOG.info(msg)
raise webob.exc.HTTPForbidden(explanation=e.msg)
result = {'task': new_task, 'task_details': new_task_details}
return result
return new_task
def index(self, req, marker=None, limit=None, sort_key='created_at',
sort_dir='desc', filters=None):
@ -82,13 +79,10 @@ class TasksController(object):
limit = CONF.limit_param_default
limit = min(CONF.api_limit_max, limit)
task_repo = self.gateway.get_task_repo(req.context)
task_repo = self.gateway.get_task_stub_repo(req.context)
try:
tasks = task_repo.list_tasks(marker,
limit,
sort_key,
sort_dir,
filters)
tasks = task_repo.list(marker, limit, sort_key,
sort_dir, filters)
if len(tasks) != 0 and len(tasks) == limit:
result['next_marker'] = tasks[-1].task_id
except (exception.NotFound, exception.InvalidSortKey,
@ -104,7 +98,7 @@ class TasksController(object):
def get(self, req, task_id):
try:
task_repo = self.gateway.get_task_repo(req.context)
task, task_details = task_repo.get_task_stub_and_details(task_id)
task = task_repo.get(task_id)
except exception.NotFound as e:
msg = (_("Failed to find task %(task_id)s. Reason: %(reason)s") %
{'task_id': task_id, 'reason': unicode(e)})
@ -115,8 +109,7 @@ class TasksController(object):
{'task_id': task_id, 'reason': unicode(e)})
LOG.info(msg)
raise webob.exc.HTTPForbidden(explanation=e.msg)
result = {'task': task, 'task_details': task_details}
return result
return task
def delete(self, req, task_id):
msg = (_("This operation is currently not permitted on Glance Tasks. "
@ -238,16 +231,15 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
def _get_task_location(self, task):
return '/v2/tasks/%s' % task.task_id
def _format_task(self, schema, task, task_details=None):
def _format_task(self, schema, task):
task_view = {}
task_attributes = ['type', 'status', 'owner']
task_details_attributes = ['input', 'result', 'message']
for key in task_attributes:
task_view[key] = getattr(task, key)
if task_details:
for key in task_details_attributes:
task_view[key] = getattr(task_details, key)
task_view['id'] = task.task_id
task_view['input'] = task.task_input
task_view['type'] = task.type
task_view['status'] = task.status
task_view['owner'] = task.owner
task_view['message'] = task.message
task_view['result'] = task.result
if task.expires_at:
task_view['expires_at'] = timeutils.isotime(task.expires_at)
task_view['created_at'] = timeutils.isotime(task.created_at)
@ -257,20 +249,28 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
task_view = schema.filter(task_view) # domain
return task_view
def create(self, response, result):
def _format_task_stub(self, schema, task):
task_view = {}
task_view['id'] = task.task_id
task_view['type'] = task.type
task_view['status'] = task.status
task_view['owner'] = task.owner
if task.expires_at:
task_view['expires_at'] = timeutils.isotime(task.expires_at)
task_view['created_at'] = timeutils.isotime(task.created_at)
task_view['updated_at'] = timeutils.isotime(task.updated_at)
task_view['self'] = self._get_task_location(task)
task_view['schema'] = '/v2/schemas/task'
task_view = schema.filter(task_view) # domain
return task_view
def create(self, response, task):
response.status_int = 201
task = result['task']
task_details = result['task_details']
self._inject_location_header(response, task)
self._get(response, task, task_details)
self.get(response, task)
def get(self, response, result):
task = result['task']
task_details = result['task_details']
self._get(response, task, task_details)
def _get(self, response, task, task_details):
task_view = self._format_task(self.task_schema, task, task_details)
def get(self, response, task):
task_view = self._format_task(self.task_schema, task)
body = json.dumps(task_view, ensure_ascii=False)
response.unicode_body = unicode(body)
response.content_type = 'application/json'
@ -280,7 +280,7 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
params.pop('marker', None)
query = urlparse.urlencode(params)
body = {
'tasks': [self._format_task(self.partial_task_schema, task)
'tasks': [self._format_task_stub(self.partial_task_schema, task)
for task in result['tasks']],
'first': '/v2/tasks',
'schema': '/v2/schemas/tasks',

View File

@ -290,6 +290,20 @@ class TaskRepo(object):
self.context = context
self.db_api = db_api
def _format_task_from_db(self, db_task):
return glance.domain.Task(
task_id=db_task['id'],
task_type=db_task['type'],
status=db_task['status'],
owner=db_task['owner'],
expires_at=db_task['expires_at'],
created_at=db_task['created_at'],
updated_at=db_task['updated_at'],
task_input=db_task['input'],
result=db_task['result'],
message=db_task['message'],
)
def _format_task_stub_from_db(self, db_task):
return glance.domain.TaskStub(
task_id=db_task['id'],
@ -301,50 +315,30 @@ class TaskRepo(object):
updated_at=db_task['updated_at'],
)
def _format_task_details_from_db(self, db_task):
return glance.domain.TaskDetails(
task_id=db_task['id'],
task_input=db_task['input'],
result=db_task['result'],
message=db_task['message'],
)
def _format_task_stub_and_details_to_db(self, task, task_details=None):
def _format_task_to_db(self, task):
task = {'id': task.task_id,
'type': task.type,
'status': task.status,
'input': None,
'result': None,
'input': task.task_input,
'result': task.result,
'owner': task.owner,
'message': None,
'message': task.message,
'expires_at': task.expires_at,
'created_at': task.created_at,
'updated_at': task.updated_at}
if task_details is not None:
task.update({
'input': task_details.input,
'result': task_details.result,
'message': task_details.message,
})
'updated_at': task.updated_at,
}
return task
def get_task_stub_and_details(self, task_id):
def get(self, task_id):
try:
db_api_task = self.db_api.task_get(self.context, task_id)
except (exception.NotFound, exception.Forbidden):
msg = _('Could not find task %s') % task_id
raise exception.NotFound(msg)
return (self._format_task_stub_from_db(db_api_task),
self._format_task_details_from_db(db_api_task))
return self._format_task_from_db(db_api_task)
def list_tasks(self,
marker=None,
limit=None,
sort_key='created_at',
sort_dir='desc',
filters=None):
def list(self, marker=None, limit=None, sort_key='created_at',
sort_dir='desc', filters=None):
db_api_tasks = self.db_api.task_get_all(self.context,
filters=filters,
marker=marker,
@ -353,9 +347,8 @@ class TaskRepo(object):
sort_dir=sort_dir)
return [self._format_task_stub_from_db(task) for task in db_api_tasks]
def save(self, task, task_details=None):
task_values = self._format_task_stub_and_details_to_db(task,
task_details)
def save(self, task):
task_values = self._format_task_to_db(task)
try:
updated_values = self.db_api.task_update(self.context,
task.task_id,
@ -365,15 +358,14 @@ class TaskRepo(object):
raise exception.NotFound(msg)
task.updated_at = updated_values['updated_at']
def add(self, task, task_details=None):
task_values = self._format_task_stub_and_details_to_db(task,
task_details)
def add(self, task):
task_values = self._format_task_to_db(task)
updated_values = self.db_api.task_create(self.context, task_values)
task.created_at = updated_values['created_at']
task.updated_at = updated_values['updated_at']
def remove(self, task):
task_values = self._format_task_stub_and_details_to_db(task)
task_values = self._format_task_to_db(task)
try:
self.db_api.task_update(self.context, task.task_id, task_values)
updated_values = self.db_api.task_delete(self.context,

View File

@ -384,17 +384,19 @@ class Task(object):
self._set_task_status(new_status)
self.expires_at = timeutils.utcnow() + self._time_to_live
def run(self, executor):
pass
class TaskStub(object):
def __init__(self, task_id, task_type, status, owner,
expires_at, created_at, updated_at, task_time_to_live=48):
expires_at, created_at, updated_at):
self.task_id = task_id
self._status = status
self.type = task_type
self.owner = owner
self.expires_at = expires_at
self._time_to_live = datetime.timedelta(hours=task_time_to_live)
self.created_at = created_at
self.updated_at = updated_at
@ -402,25 +404,11 @@ class TaskStub(object):
def status(self):
return self._status
def run(self, executor):
pass
class TaskDetails(object):
def __init__(self, task_id, task_input, message, result):
if task_id is None:
raise exception.TaskException(_('task_id is required to create '
'a new TaskDetails object'))
self.task_id = task_id
self.input = task_input
self.message = message
self.result = result
class TaskFactory(object):
def new_task(self, task_type, owner, task_time_to_live=48):
def new_task(self, task_type, owner, task_time_to_live=48,
task_input=None, **kwargs):
task_id = str(uuid.uuid4())
status = 'pending'
# Note(nikhil): expires_at would be set on the task, only when it
@ -436,30 +424,8 @@ class TaskFactory(object):
expires_at,
created_at,
updated_at,
None, # input
None, # result
None, # message
task_input,
kwargs.get('message'),
kwargs.get('result'),
task_time_to_live
)
def new_task_stub(self, task_type, owner, task_time_to_live=48):
task_id = str(uuid.uuid4())
status = 'pending'
# Note(nikhil): expires_at would be set on the task, only when it
# succeeds or fails.
expires_at = None
created_at = timeutils.utcnow()
updated_at = created_at
return TaskStub(
task_id,
task_type,
status,
owner,
expires_at,
created_at,
updated_at,
task_time_to_live
)
def new_task_details(self, task_id, task_input, message=None, result=None):
return TaskDetails(task_id, task_input, message, result)

View File

@ -45,38 +45,38 @@ class Helper(object):
class TaskRepo(object):
def __init__(self,
base,
task_proxy_class=None, task_proxy_kwargs=None,
task_details_proxy_class=None,
task_details_proxy_kwargs=None):
def __init__(self, base,
task_proxy_class=None, task_proxy_kwargs=None):
self.base = base
self.task_proxy_helper = Helper(task_proxy_class, task_proxy_kwargs)
self.task_details_proxy_helper = Helper(task_details_proxy_class,
task_details_proxy_kwargs)
def get_task_stub_and_details(self, task_id):
task, task_details = self.base.get_task_stub_and_details(task_id)
return (self.task_proxy_helper.proxy(task),
self.task_details_proxy_helper.proxy(task_details))
def get(self, task_id):
task = self.base.get(task_id)
return self.task_proxy_helper.proxy(task)
def list_tasks(self, *args, **kwargs):
tasks = self.base.list_tasks(*args, **kwargs)
return [self.task_proxy_helper.proxy(task) for task in tasks]
def add(self, task):
self.base.add(self.task_proxy_helper.unproxy(task))
def add(self, task, task_details=None):
self.base.add(self.task_proxy_helper.unproxy(task),
self.task_details_proxy_helper.unproxy(task_details))
def save(self, task, task_details=None):
self.base.save(self.task_proxy_helper.unproxy(task),
self.task_details_proxy_helper.unproxy(task_details))
def save(self, task):
self.base.save(self.task_proxy_helper.unproxy(task))
def remove(self, task):
base_task = self.task_proxy_helper.unproxy(task)
self.base.remove(base_task)
class TaskStubRepo(object):
def __init__(self, base, task_stub_proxy_class=None,
task_stub_proxy_kwargs=None):
self.base = base
self.task_stub_proxy_helper = Helper(task_stub_proxy_class,
task_stub_proxy_kwargs)
def list(self, *args, **kwargs):
tasks = self.base.list(*args, **kwargs)
return [self.task_stub_proxy_helper.proxy(task) for task in tasks]
class Repo(object):
def __init__(self, base, item_proxy_class=None, item_proxy_kwargs=None):
self.base = base
@ -191,6 +191,9 @@ class Task(object):
def fail(self, message):
self.base.fail(message)
def run(self, executor):
self.base.run(executor)
class TaskStub(object):
def __init__(self, base):
@ -204,40 +207,15 @@ class TaskStub(object):
created_at = _proxy('base', 'created_at')
updated_at = _proxy('base', 'updated_at')
def run(self, executor):
self.base.run(executor)
class TaskDetails(object):
def __init__(self, base):
self.base = base
task_id = _proxy('base', 'task_id')
input = _proxy('base', 'input')
result = _proxy('base', 'result')
message = _proxy('base', 'message')
class TaskFactory(object):
def __init__(self,
base,
task_proxy_class=None,
task_proxy_kwargs=None,
task_details_proxy_class=None,
task_details_proxy_kwargs=None):
task_proxy_kwargs=None):
self.task_helper = Helper(task_proxy_class, task_proxy_kwargs)
self.task_details_helper = Helper(task_details_proxy_class,
task_details_proxy_kwargs)
self.base = base
def new_task(self, **kwargs):
t = self.base.new_task(**kwargs)
return self.task_helper.proxy(t)
def new_task_stub(self, **kwargs):
t = self.base.new_task_stub(**kwargs)
return self.task_helper.proxy(t)
def new_task_details(self, task_id, task_input, message=None, result=None):
td = self.base.new_task_details(task_id, task_input, message, result)
return self.task_details_helper.proxy(td)

View File

@ -107,3 +107,13 @@ class Gateway(object):
authorized_task_repo = authorization.TaskRepoProxy(
notifier_task_repo, context)
return authorized_task_repo
def get_task_stub_repo(self, context):
task_stub_repo = glance.db.TaskRepo(context, self.db_api)
policy_task_stub_repo = policy.TaskStubRepoProxy(
task_stub_repo, context, self.policy)
notifier_task_stub_repo = glance.notifier.TaskStubRepoProxy(
policy_task_stub_repo, context, self.notifier)
authorized_task_stub_repo = authorization.TaskStubRepoProxy(
notifier_task_stub_repo, context)
return authorized_task_stub_repo

View File

@ -311,14 +311,12 @@ class TaskRepoProxy(glance.domain.proxy.TaskRepo):
super(TaskRepoProxy, self) \
.__init__(task_repo,
task_proxy_class=TaskProxy,
task_proxy_kwargs=proxy_kwargs,
task_details_proxy_class=TaskDetailsProxy,
task_details_proxy_kwargs=proxy_kwargs)
task_proxy_kwargs=proxy_kwargs)
def add(self, task, task_details=None):
def add(self, task):
self.notifier.info('task.create',
format_task_notification(task))
super(TaskRepoProxy, self).add(task, task_details)
super(TaskRepoProxy, self).add(task)
def remove(self, task):
payload = format_task_notification(task)
@ -328,15 +326,26 @@ class TaskRepoProxy(glance.domain.proxy.TaskRepo):
super(TaskRepoProxy, self).remove(task)
class TaskStubRepoProxy(glance.domain.proxy.TaskStubRepo):
def __init__(self, task_stub_repo, context, notifier):
self.task_stub_repo = task_stub_repo
self.context = context
self.notifier = notifier
proxy_kwargs = {'context': self.context, 'notifier': self.notifier}
super(TaskStubRepoProxy, self) \
.__init__(task_stub_repo,
task_stub_proxy_class=TaskStubProxy,
task_stub_proxy_kwargs=proxy_kwargs)
class TaskFactoryProxy(glance.domain.proxy.TaskFactory):
def __init__(self, task_factory, context, notifier):
kwargs = {'context': context, 'notifier': notifier}
super(TaskFactoryProxy, self).__init__(
task_factory,
task_proxy_class=TaskProxy,
task_proxy_kwargs=kwargs,
task_details_proxy_class=TaskDetailsProxy,
task_details_proxy_kwargs=kwargs)
task_proxy_kwargs=kwargs)
class TaskProxy(glance.domain.proxy.Task):
@ -364,6 +373,11 @@ class TaskProxy(glance.domain.proxy.Task):
format_task_notification(self.task))
return super(TaskProxy, self).fail(message)
def run(self, executor):
self.notifier.info('task.run',
format_task_notification(self.task))
return super(TaskProxy, self).run(executor)
class TaskStubProxy(glance.domain.proxy.TaskStub):
@ -372,17 +386,3 @@ class TaskStubProxy(glance.domain.proxy.TaskStub):
self.context = context
self.notifier = notifier
super(TaskStubProxy, self).__init__(task)
def run(self, executor):
self.notifier.info('task.run',
format_task_notification(self.task))
return super(TaskStubProxy, self).run(executor)
class TaskDetailsProxy(glance.domain.proxy.TaskDetails):
def __init__(self, task_details, context, notifier):
self.task_details = task_details
self.context = context
self.notifier = notifier
super(TaskDetailsProxy, self).__init__(task_details)

View File

@ -105,7 +105,7 @@ class TestTasks(functional.FunctionalTest):
u'status',
u'type',
u'updated_at'])
self.assertEqual(set(task.keys()), checked_keys)
self.assertEqual(checked_keys, set(task.keys()))
expected_task = {
'status': 'pending',
'type': 'import',

View File

@ -207,7 +207,7 @@ class TestTasksApi(base.ApiTest):
# 1. POST /tasks
# Create a new task with invalid input for type 'import'
# Expect BadRequest(400) Error as response
task_data = _new_task_fixture(input='{something: invalid}')
task_data = _new_task_fixture(task_input='{something: invalid}')
task_owner = 'tenant1'
body_content = json.dumps(task_data)

View File

@ -925,6 +925,53 @@ class TestImmutableTask(utils.BaseTestCase):
)
class TestImmutableTaskStub(utils.BaseTestCase):
def setUp(self):
super(TestImmutableTaskStub, self).setUp()
task_factory = glance.domain.TaskFactory()
self.context = glance.context.RequestContext(tenant=TENANT2)
task_type = 'import'
owner = TENANT2
task = task_factory.new_task(task_type, owner)
self.task = authorization.ImmutableTaskStubProxy(task)
def _test_change(self, attr, value):
self.assertRaises(
exception.Forbidden,
setattr,
self.task,
attr,
value
)
self.assertRaises(
exception.Forbidden,
delattr,
self.task,
attr
)
def test_change_id(self):
self._test_change('task_id', UUID2)
def test_change_type(self):
self._test_change('type', 'fake')
def test_change_status(self):
self._test_change('status', 'success')
def test_change_owner(self):
self._test_change('owner', 'fake')
def test_change_expires_at(self):
self._test_change('expires_at', 'fake')
def test_change_created_at(self):
self._test_change('created_at', 'fake')
def test_change_updated_at(self):
self._test_change('updated_at', 'fake')
class TestTaskFactoryProxy(utils.BaseTestCase):
def setUp(self):
super(TestTaskFactoryProxy, self).setUp()
@ -977,14 +1024,18 @@ class TestTaskRepoProxy(utils.BaseTestCase):
def __init__(self, fixtures):
self.fixtures = fixtures
def get_task_stub_and_details(self, task_id):
def get(self, task_id):
for f in self.fixtures:
if f.task_id == task_id:
return f, None
return f
else:
raise ValueError(task_id)
def list_tasks(self, *args, **kwargs):
class TaskStubRepoStub(object):
def __init__(self, fixtures):
self.fixtures = fixtures
def list(self, *args, **kwargs):
return self.fixtures
def setUp(self):
@ -999,27 +1050,28 @@ class TestTaskRepoProxy(utils.BaseTestCase):
]
self.context = glance.context.RequestContext(tenant=TENANT1)
task_repo = self.TaskRepoStub(self.fixtures)
task_stub_repo = self.TaskStubRepoStub(self.fixtures)
self.task_repo = authorization.TaskRepoProxy(
task_repo,
self.context
)
self.task_stub_repo = authorization.TaskStubRepoProxy(
task_stub_repo,
self.context
)
def test_get_mutable_task(self):
task, _ = self.task_repo.get_task_stub_and_details(
self.fixtures[0].task_id)
task = self.task_repo.get(self.fixtures[0].task_id)
self.assertEqual(task.task_id, self.fixtures[0].task_id)
def test_get_immutable_task(self):
task_id = self.fixtures[1].task_id
task, task_details = self.task_repo.get_task_stub_and_details(task_id)
task = self.task_repo.get(task_id)
self.assertRaises(exception.Forbidden,
setattr,
task_details,
'input',
'foo')
setattr, task, 'input', 'foo')
def test_list(self):
tasks = self.task_repo.list_tasks()
tasks = self.task_stub_repo.list()
self.assertEqual(tasks[0].task_id, self.fixtures[0].task_id)
self.assertRaises(exception.Forbidden,
setattr,

View File

@ -585,97 +585,92 @@ class TestTaskRepo(test_utils.BaseTestCase):
[self.db.task_create(None, task) for task in self.tasks]
def test_get(self):
task, task_details = self.task_repo.get_task_stub_and_details(UUID1)
task = self.task_repo.get(UUID1)
self.assertEqual(task.task_id, UUID1)
self.assertEqual(task.type, 'import')
self.assertEqual(task.status, 'pending')
self.assertEqual(task.task_id, task_details.task_id)
self.assertEqual(task_details.input, self.fake_task_input)
self.assertEqual(task_details.result, '')
self.assertEqual(task.task_input, self.fake_task_input)
self.assertEqual(task.result, '')
self.assertEqual(task.message, '')
self.assertEqual(task.owner, TENANT1)
def test_get_not_found(self):
self.assertRaises(exception.NotFound,
self.task_repo.get_task_stub_and_details,
self.task_repo.get,
str(uuid.uuid4()))
def test_get_forbidden(self):
self.assertRaises(exception.NotFound,
self.task_repo.get_task_stub_and_details,
self.task_repo.get,
UUID4)
def test_list(self):
tasks = self.task_repo.list_tasks()
tasks = self.task_repo.list()
task_ids = set([i.task_id for i in tasks])
self.assertEqual(set([UUID1, UUID2, UUID3]), task_ids)
def test_list_with_type(self):
filters = {'type': 'import'}
tasks = self.task_repo.list_tasks(filters=filters)
tasks = self.task_repo.list(filters=filters)
task_ids = set([i.task_id for i in tasks])
self.assertEqual(set([UUID1, UUID2, UUID3]), task_ids)
def test_list_with_status(self):
filters = {'status': 'failure'}
tasks = self.task_repo.list_tasks(filters=filters)
tasks = self.task_repo.list(filters=filters)
task_ids = set([i.task_id for i in tasks])
self.assertEqual(set([UUID3]), task_ids)
def test_list_with_marker(self):
full_tasks = self.task_repo.list_tasks()
full_tasks = self.task_repo.list()
full_ids = [i.task_id for i in full_tasks]
marked_tasks = self.task_repo.list_tasks(marker=full_ids[0])
marked_tasks = self.task_repo.list(marker=full_ids[0])
actual_ids = [i.task_id for i in marked_tasks]
self.assertEqual(actual_ids, full_ids[1:])
def test_list_with_last_marker(self):
tasks = self.task_repo.list_tasks()
marked_tasks = self.task_repo.list_tasks(marker=tasks[-1].task_id)
tasks = self.task_repo.list()
marked_tasks = self.task_repo.list(marker=tasks[-1].task_id)
self.assertEqual(len(marked_tasks), 0)
def test_limited_list(self):
limited_tasks = self.task_repo.list_tasks(limit=2)
limited_tasks = self.task_repo.list(limit=2)
self.assertEqual(len(limited_tasks), 2)
def test_list_with_marker_and_limit(self):
full_tasks = self.task_repo.list_tasks()
full_tasks = self.task_repo.list()
full_ids = [i.task_id for i in full_tasks]
marked_tasks = self.task_repo.list_tasks(marker=full_ids[0], limit=1)
marked_tasks = self.task_repo.list(marker=full_ids[0], limit=1)
actual_ids = [i.task_id for i in marked_tasks]
self.assertEqual(actual_ids, full_ids[1:2])
def test_sorted_list(self):
tasks = self.task_repo.list_tasks(sort_key='status', sort_dir='desc')
tasks = self.task_repo.list(sort_key='status', sort_dir='desc')
task_ids = [i.task_id for i in tasks]
self.assertEqual([UUID2, UUID1, UUID3], task_ids)
def test_add_task(self):
task_type = 'import'
task = self.task_factory.new_task(task_type, None)
task = self.task_factory.new_task(task_type, None,
task_input=self.fake_task_input)
self.assertEqual(task.updated_at, task.created_at)
task_details = self.task_factory.new_task_details(task.task_id,
self.fake_task_input)
self.task_repo.add(task, task_details)
retrieved_task, retrieved_task_details = \
self.task_repo.get_task_stub_and_details(task.task_id)
self.assertEqual(retrieved_task.updated_at, task.updated_at)
self.assertEqual(retrieved_task_details.task_id,
retrieved_task.task_id)
self.assertEqual(retrieved_task_details.input, task_details.input)
self.task_repo.add(task)
retrieved_task = self.task_repo.get(task.task_id)
self.assertEqual(task.updated_at, retrieved_task.updated_at)
self.assertEqual(self.fake_task_input, retrieved_task.task_input)
def test_save_task(self):
task, task_details = self.task_repo.get_task_stub_and_details(UUID1)
task = self.task_repo.get(UUID1)
original_update_time = task.updated_at
self.task_repo.save(task)
current_update_time = task.updated_at
self.assertTrue(current_update_time > original_update_time)
task, task_details = self.task_repo.get_task_stub_and_details(UUID1)
task = self.task_repo.get(UUID1)
self.assertEqual(task.updated_at, current_update_time)
def test_remove_task(self):
task, task_details = self.task_repo.get_task_stub_and_details(UUID1)
task = self.task_repo.get(UUID1)
self.task_repo.remove(task)
self.assertRaises(exception.NotFound,
self.task_repo.get_task_stub_and_details,
self.task_repo.get,
task.task_id)

View File

@ -306,29 +306,19 @@ class TestTaskFactory(test_utils.BaseTestCase):
def test_new_task(self):
task_type = 'import'
owner = TENANT1
task = self.task_factory.new_task(task_type, owner)
task_input = 'input'
task = self.task_factory.new_task(task_type, owner,
task_input=task_input)
self.assertIsNotNone(task.task_id)
self.assertEqual('pending', task.status)
self.assertEqual(task_type, task.type)
self.assertEqual(owner, task.owner)
self.assertIsNone(task.expires_at)
self.assertIsNotNone(task.created_at)
self.assertEqual(task_type, task.type)
self.assertEqual(task.created_at, task.updated_at)
self.assertIsNone(task.task_input)
self.assertIsNone(task.result)
self.assertEqual('pending', task.status)
self.assertIsNone(task.expires_at)
self.assertEqual(owner, task.owner)
self.assertEqual(task_input, task.task_input)
self.assertIsNone(task.message)
def test_new_task_stub(self):
task_type = 'import'
owner = TENANT1
task = self.task_factory.new_task_stub(task_type, owner)
self.assertIsNotNone(task.task_id)
self.assertEqual('pending', task.status)
self.assertEqual(task_type, task.type)
self.assertEqual(owner, task.owner)
self.assertIsNone(task.expires_at)
self.assertIsNotNone(task.created_at)
self.assertEqual(task.created_at, task.updated_at)
self.assertIsNone(task.result)
def test_new_task_invalid_type(self):
task_type = 'blah'
@ -340,20 +330,6 @@ class TestTaskFactory(test_utils.BaseTestCase):
owner,
)
def test_new_task_details(self):
task_id = 'fake_task_id'
task_input = '{"import_from": "fake"}'
result = '{"result": "success"}'
message = 'fake message'
task_details = self.task_factory.new_task_details(task_id,
task_input,
message,
result)
self.assertEqual(task_details.task_id, task_id)
self.assertEqual(task_details.input, task_input)
self.assertEqual(task_details.result, result)
self.assertEqual(task_details.message, message)
class TestTask(test_utils.BaseTestCase):
@ -381,8 +357,8 @@ class TestTask(test_utils.BaseTestCase):
created_at=timeutils.utcnow(),
updated_at=timeutils.utcnow(),
task_input=None,
result=None,
message=None
message=None,
result=None
)
def test_validate_status_transition_from_pending(self):
@ -489,8 +465,7 @@ class TestTaskStub(test_utils.BaseTestCase):
self.owner,
'expires_at',
'created_at',
'updated_at',
task_time_to_live=self.task_ttl
'updated_at'
)
self.assertEqual(self.task_id, task.task_id)
self.assertEqual(self.task_type, task.type)
@ -509,32 +484,6 @@ class TestTaskStub(test_utils.BaseTestCase):
self.owner,
'expires_at',
'created_at',
'updated_at',
task_time_to_live=self.task_ttl
'updated_at'
)
self.assertEqual(status, task.status)
class TestTaskDetails(test_utils.BaseTestCase):
def setUp(self):
super(TestTaskDetails, self).setUp()
self.task_input = ('{"import_from": "file:///home/a.img",'
' "import_from_format": "qcow2"}')
def test_task_details_init(self):
task_details_values = ['task_id_1',
self.task_input,
'result',
'None']
task_details = domain.TaskDetails(*task_details_values)
self.assertIsNotNone(task_details)
def test_task_details_with_no_task_id(self):
task_id = None
task_details_values = [task_id,
self.task_input,
'result',
'None']
self.assertRaises(exception.TaskException,
domain.TaskDetails,
*task_details_values)

View File

@ -305,24 +305,13 @@ class TestTaskFactory(test_utils.BaseTestCase):
owner=self.fake_owner
)
proxy_factory.new_task_details("task_01", "input")
self.factory.new_task_details.assert_called_once_with(
"task_01",
"input",
None, None
)
def test_proxy_wrapping(self):
proxy_factory = proxy.TaskFactory(
self.factory,
task_proxy_class=FakeProxy,
task_proxy_kwargs={'dog': 'bark'},
task_details_proxy_class=FakeProxy,
task_details_proxy_kwargs={'dog': 'bark'})
task_proxy_kwargs={'dog': 'bark'})
self.factory.new_task.return_value = 'fake_task'
self.factory.new_task_details.return_value = 'fake_task_detail'
task = proxy_factory.new_task(
type=self.fake_type,
@ -335,14 +324,3 @@ class TestTaskFactory(test_utils.BaseTestCase):
)
self.assertIsInstance(task, FakeProxy)
self.assertEqual(task.base, 'fake_task')
task_details = proxy_factory.new_task_details('task_01', "input")
self.factory.new_task_details.assert_called_once_with(
'task_01',
"input",
None, None
)
self.assertIsInstance(task_details, FakeProxy)
self.assertEqual(task_details.base, 'fake_task_detail')

View File

@ -21,7 +21,6 @@ import webob
from glance.common import exception
import glance.context
from glance import domain
from glance import notifier
from glance.openstack.common import timeutils
import glance.tests.unit.utils as unit_test_utils
@ -427,10 +426,6 @@ class TestTaskNotifications(utils.BaseTestCase):
result='res',
message='blah'
)
self.task_details = domain.TaskDetails(task_id=self.task.task_id,
task_input=task_input,
result='',
message='')
self.context = glance.context.RequestContext(
tenant=TENANT2,
user=USER1
@ -452,9 +447,6 @@ class TestTaskNotifications(utils.BaseTestCase):
self.context,
self.notifier
)
self.task_details_proxy = notifier.TaskDetailsProxy(self.task_details,
self.context,
self.notifier)
self.patcher = mock.patch.object(timeutils, 'utcnow')
mock_utcnow = self.patcher.start()
mock_utcnow.return_value = datetime.datetime.utcnow()
@ -464,8 +456,7 @@ class TestTaskNotifications(utils.BaseTestCase):
self.patcher.stop()
def test_task_create_notification(self):
self.task_repo_proxy.add(self.task_stub_proxy,
self.task_details_proxy)
self.task_repo_proxy.add(self.task_stub_proxy)
output_logs = self.notifier.get_logs()
self.assertEqual(len(output_logs), 1)
output_log = output_logs[0]
@ -508,7 +499,7 @@ class TestTaskNotifications(utils.BaseTestCase):
self.fail('Notification contained location field.')
def test_task_run_notification(self):
self.task_stub_proxy.run(executor=None)
self.task_proxy.run(executor=None)
output_logs = self.notifier.get_logs()
self.assertEqual(len(output_logs), 1)
output_log = output_logs[0]

View File

@ -85,13 +85,13 @@ class ImageMembershipStub(object):
class TaskRepoStub(object):
def get_task_stub_and_details(self, *args, **kwargs):
return 'task_from_get', 'task_details_from_get'
def get(self, *args, **kwargs):
return 'task_from_get'
def add(self, *args, **kwargs):
return 'task_from_add'
def list_tasks(self, *args, **kwargs):
def list(self, *args, **kwargs):
return ['task_from_list_0', 'task_from_list_1']
@ -386,7 +386,7 @@ class TestTaskPolicy(test_utils.BaseTestCase):
self.policy
)
self.assertRaises(exception.Forbidden,
task_repo.get_task_stub_and_details,
task_repo.get,
UUID1)
def test_get_task_allowed(self):
@ -397,32 +397,32 @@ class TestTaskPolicy(test_utils.BaseTestCase):
{},
self.policy
)
task, task_details = task_repo.get_task_stub_and_details(UUID1)
task = task_repo.get(UUID1)
self.assertIsInstance(task, glance.api.policy.TaskProxy)
self.assertEqual(task.task, 'task_from_get')
def test_get_tasks_not_allowed(self):
rules = {"get_tasks": False}
self.policy.set_rules(rules)
task_repo = glance.api.policy.TaskRepoProxy(
task_repo = glance.api.policy.TaskStubRepoProxy(
self.task_repo_stub,
{},
self.policy
)
self.assertRaises(exception.Forbidden, task_repo.list_tasks)
self.assertRaises(exception.Forbidden, task_repo.list)
def test_get_tasks_allowed(self):
rules = {"get_task": True}
self.policy.set_rules(rules)
task_repo = glance.api.policy.TaskRepoProxy(
task_repo = glance.api.policy.TaskStubRepoProxy(
self.task_repo_stub,
{},
self.policy
)
tasks = task_repo.list_tasks()
tasks = task_repo.list()
for i, task in enumerate(tasks):
self.assertIsInstance(task, glance.api.policy.TaskProxy)
self.assertEqual(task.task, 'task_from_list_%d' % i)
self.assertIsInstance(task, glance.api.policy.TaskStubProxy)
self.assertEqual(task.task_stub, 'task_from_list_%d' % i)
def test_add_task_not_allowed(self):
rules = {"add_task": False}

View File

@ -71,13 +71,12 @@ def _domain_fixture(task_id, **kwargs):
'expires_at': kwargs.get('expires_at', None),
'created_at': kwargs.get('created_at', default_datetime),
'updated_at': kwargs.get('updated_at', default_datetime),
'task_input': kwargs.get('task_input', {}),
'message': kwargs.get('message', None),
'result': kwargs.get('result', None)
}
task = glance.domain.TaskStub(**task_properties)
task_details = glance.domain.TaskDetails(task_id,
kwargs.get('input', {}),
kwargs.get('message', None),
kwargs.get('result', None))
return {'task': task, 'task_details': task_details}
task = glance.domain.Task(**task_properties)
return task
class TestTasksController(test_utils.BaseTestCase):
@ -267,11 +266,8 @@ class TestTasksController(test_utils.BaseTestCase):
def test_get(self):
request = unit_test_utils.get_fake_request()
output = self.controller.get(request, task_id=UUID1)
task = output['task']
task_details = output['task_details']
task = self.controller.get(request, task_id=UUID1)
self.assertEqual(UUID1, task.task_id)
self.assertEqual(UUID1, task_details.task_id)
self.assertEqual('import', task.type)
def test_get_non_existent(self):
@ -292,13 +288,11 @@ class TestTasksController(test_utils.BaseTestCase):
"import_from": "swift://cloud.foo/myaccount/mycontainer/path",
"image_from_format": "qcow2"}
}
output = self.controller.create(request, task=task)
task = output['task']
task_details = output['task_details']
task = self.controller.create(request, task=task)
self.assertEqual('import', task.type)
self.assertEqual({
"import_from": "swift://cloud.foo/myaccount/mycontainer/path",
"image_from_format": "qcow2"}, task_details.input)
"image_from_format": "qcow2"}, task.task_input)
output_logs = [nlog for nlog in self.notifier.get_logs()
if nlog['event_type'] == 'task.create']
self.assertEqual(len(output_logs), 1)
@ -495,21 +489,23 @@ class TestTasksSerializer(test_utils.BaseTestCase):
self.serializer = glance.api.v2.tasks.ResponseSerializer()
self.fixtures = [
_domain_fixture(UUID1, type='import', status='pending',
input={'loc': 'fake'}, result={}, owner=TENANT1,
message='', created_at=DATETIME,
task_input={'loc': 'fake'}, result={},
owner=TENANT1, message='', created_at=DATETIME,
updated_at=DATETIME),
_domain_fixture(UUID2, type='import', status='processing',
input={'loc': 'bake'}, owner=TENANT2, message='',
created_at=DATETIME, updated_at=DATETIME,
result={}),
task_input={'loc': 'bake'}, owner=TENANT2,
message='', created_at=DATETIME,
updated_at=DATETIME, result={}),
_domain_fixture(UUID3, type='import', status='success',
input={'loc': 'foo'}, owner=TENANT3, message='',
created_at=DATETIME, updated_at=DATETIME,
result={}, expires_at=DATETIME),
task_input={'loc': 'foo'}, owner=TENANT3,
message='', created_at=DATETIME,
updated_at=DATETIME, result={},
expires_at=DATETIME),
_domain_fixture(UUID4, type='import', status='failure',
input={'loc': 'boo'}, owner=TENANT4, message='',
created_at=DATETIME, updated_at=DATETIME,
result={}, expires_at=DATETIME),
task_input={'loc': 'boo'}, owner=TENANT4,
message='', created_at=DATETIME,
updated_at=DATETIME, result={},
expires_at=DATETIME),
]
def test_index(self):
@ -563,7 +559,7 @@ class TestTasksSerializer(test_utils.BaseTestCase):
}
request = webob.Request.blank('/v2/tasks')
response = webob.Response(request=request)
task_fixtures = [f['task'] for f in self.fixtures]
task_fixtures = [f for f in self.fixtures]
result = {'tasks': task_fixtures}
self.serializer.index(response, result)
actual = jsonutils.loads(response.body)
@ -573,7 +569,7 @@ class TestTasksSerializer(test_utils.BaseTestCase):
def test_index_next_marker(self):
request = webob.Request.blank('/v2/tasks')
response = webob.Response(request=request)
task_fixtures = [f['task'] for f in self.fixtures]
task_fixtures = [f for f in self.fixtures]
result = {'tasks': task_fixtures, 'next_marker': UUID2}
self.serializer.index(response, result)
output = jsonutils.loads(response.body)
@ -583,7 +579,7 @@ class TestTasksSerializer(test_utils.BaseTestCase):
url = '/v2/tasks?limit=10&sort_key=id&sort_dir=asc'
request = webob.Request.blank(url)
response = webob.Response(request=request)
task_fixtures = [f['task'] for f in self.fixtures]
task_fixtures = [f for f in self.fixtures]
result = {'tasks': task_fixtures, 'next_marker': UUID2}
self.serializer.index(response, result)
output = jsonutils.loads(response.body)
@ -661,11 +657,9 @@ class TestTasksSerializer(test_utils.BaseTestCase):
serialized_task = jsonutils.loads(response.body)
self.assertEqual(response.status_int, 201)
self.assertEqual(self.fixtures[3]['task'].task_id,
self.assertEqual(self.fixtures[3].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[3]['task_details'].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[3]['task_details'].input,
self.assertEqual(self.fixtures[3].task_input,
serialized_task['input'])
self.assertTrue('expires_at' in serialized_task)
self.assertEqual('application/json', response.content_type)
@ -677,11 +671,9 @@ class TestTasksSerializer(test_utils.BaseTestCase):
serialized_task = jsonutils.loads(response.body)
self.assertEqual(response.status_int, 201)
self.assertEqual(self.fixtures[0]['task'].task_id,
self.assertEqual(self.fixtures[0].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[0]['task_details'].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[0]['task_details'].input,
self.assertEqual(self.fixtures[0].task_input,
serialized_task['input'])
self.assertFalse('expires_at' in serialized_task)
self.assertEqual('application/json', response.content_type)
@ -692,11 +684,9 @@ class TestTasksSerializer(test_utils.BaseTestCase):
serialized_task = jsonutils.loads(response.body)
self.assertEqual(response.status_int, 201)
self.assertEqual(self.fixtures[1]['task'].task_id,
self.assertEqual(self.fixtures[1].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[1]['task_details'].task_id,
serialized_task['id'])
self.assertEqual(self.fixtures[1]['task_details'].input,
self.assertEqual(self.fixtures[1].task_input,
serialized_task['input'])
self.assertFalse('expires_at' in serialized_task)
self.assertEqual('application/json', response.content_type)