From 5dd80b0d4b8a122d166ae9ad5dddc098c9e2d741 Mon Sep 17 00:00:00 2001 From: Mike Fedosin Date: Wed, 4 Jan 2017 15:39:41 +0300 Subject: [PATCH] Add simple db for unit testing Change-Id: I166a35add924276ef17711e5231658816af1bcc2 --- glare/db/simple_api.py | 222 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 glare/db/simple_api.py diff --git a/glare/db/simple_api.py b/glare/db/simple_api.py new file mode 100644 index 0000000..9960a15 --- /dev/null +++ b/glare/db/simple_api.py @@ -0,0 +1,222 @@ +# Copyright (c) 2016 Mirantis, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +"""Simple Database API for testing artifact types""" + +from oslo_log import log as logging +from oslo_utils import timeutils +import semantic_version +import six + +import glare.common.exception as glare_exc +from glare.common import utils +from glare.db import api +from glare.i18n import _ +from glare import locking + + +LOG = logging.getLogger(__name__) + +DATA = { + 'artifacts': {}, + 'locks': {} +} + +error_map = [{"catch": KeyError, "raise": glare_exc.NotFound}] + + +class SimpleAPI(api.BaseDBAPI): + + @utils.error_handler(error_map) + def create(self, context, values): + global DATA + values['created_at'] = values['updated_at'] = timeutils.utcnow() + artifact_id = values['id'] + if artifact_id in DATA['artifacts']: + msg = _("Artifact with id '%s' already exists") % artifact_id + raise glare_exc.BadRequest(msg) + values['type_name'] = self.type + + DATA['artifacts'][artifact_id] = values + return values + + @utils.error_handler(error_map) + def update(self, context, artifact_id, values): + global DATA + af = DATA['artifacts'][artifact_id] + if af['owner'] != context.tenant and not context.is_admin: + if af['visibility'] == 'private': + raise glare_exc.BadRequest + else: + raise glare_exc.Forbidden + af.update(values) + for key, val in six.iteritems(values): + if val is None: + del af[key] + else: + af[key] = val + if 'status' in values and values['status'] == self.cls.STATUS.ACTIVE: + af['activated_at'] = timeutils.utcnow() + af['updated_at'] = timeutils.utcnow() + DATA['artifacts'][artifact_id] = af + return af + + @utils.error_handler(error_map) + def delete(self, context, artifact_id): + global DATA + af = DATA['artifacts'][artifact_id] + if af['owner'] != context.tenant and not context.is_admin: + if af['visibility'] == 'private': + raise glare_exc.BadRequest + else: + raise glare_exc.Forbidden + del DATA['artifacts'][artifact_id] + + @utils.error_handler(error_map) + def get(self, context, artifact_id): + global DATA + af = DATA['artifacts'][artifact_id] + if af['owner'] != context.tenant and \ + not context.is_admin and af['visibility'] == 'private': + raise KeyError + return af + + @utils.error_handler(error_map) + def list(self, context, filters, marker, limit, sort, latest): + global DATA + afs = list(DATA['artifacts'].values()) + if self.type != 'all': + filters.append(('type_name', None, 'eq', None, self.type)) + + for field_name, key_name, op, field_type, value in filters: + if field_name == 'tags': + values = utils.split_filter_value_for_quotes(value) + for af in afs[:]: + if not set(values).issubset(af['tags']): + afs.remove(af) + elif field_name == 'tags-any': + values = utils.split_filter_value_for_quotes(value) + for af in afs[:]: + for tag in values: + if tag in af['tags']: + break + else: + afs.remove(af) + # filter by dict field + elif key_name is not None: + for af in afs[:]: + if key_name not in af[field_name]: + afs.remove(af) + elif value is None: + continue + elif op == 'in': + for val in value: + if af[field_name][key_name] == val: + break + else: + afs.remove(af) + elif not utils.evaluate_filter_op( + af[field_name][key_name], op, value): + afs.remove(af) + # filter by common field + else: + for af in afs[:]: + if op == 'in': + for val in value: + if field_name == 'version': + val = semantic_version.Version.coerce(val) + af_version = semantic_version.Version.coerce( + af[field_name]) + if af_version == val: + break + elif af[field_name] == val: + break + else: + afs.remove(af) + else: + if field_name == 'version': + af_version = semantic_version.Version.coerce( + af[field_name]) + if not utils.evaluate_filter_op( + af_version, op, + semantic_version.Version.coerce(value)): + afs.remove(af) + elif isinstance(af.get(field_name), list): + if value not in af[field_name]: + afs.remove(af) + elif not utils.evaluate_filter_op( + af[field_name], op, value): + afs.remove(af) + + if not context.is_admin: + for af in afs[:]: + if af['visibility'] != 'public' and \ + af['owner'] != context.tenant: + afs.remove(af) + + if latest: + afs.sort(cmp=version_cmp, reverse=True) + afs.sort(key=lambda x: x['name']) + new_afs = [] + new_afs_names = [] + for af in afs: + if af['name'] not in new_afs_names: + new_afs_names.append(af['name']) + new_afs.append(af) + afs = new_afs + + for key, dir, prop_type in reversed(sort): + reverse = dir == 'desc' + if key == 'version': + afs.sort(cmp=version_cmp, reverse=reverse) + else: + afs.sort(key=lambda x: x.get(key, None), reverse=reverse) + + if marker: + for af in afs[:]: + afs.remove(af) + if af['id'] == marker: + break + + if limit: + afs = afs[:limit] + + return afs + + +def version_cmp(af1, af2): + return semantic_version.compare( + af1['version'], af2['version']) + + +class SimpleLockApi(locking.LockApiBase): + def create_lock(self, context, lock_key): + global DATA + item_lock = DATA['locks'].get(lock_key) + if item_lock: + msg = _("Cannot lock an item with key %s. " + "Lock already acquired by other request.") % lock_key + raise glare_exc.Conflict(msg) + # TODO(kairat) Log user data in the log so we can identify who + # acquired the lock + else: + DATA['locks'][lock_key] = lock_key + return lock_key + + def delete_lock(self, context, lock_id): + global DATA + item_lock = DATA['locks'][lock_id] + if item_lock: + del DATA['locks'][lock_id]