From de388f5e64d887ff6b20790fb540c05ab43dacf7 Mon Sep 17 00:00:00 2001 From: efedorova Date: Thu, 17 Oct 2013 19:18:08 +0400 Subject: [PATCH] Refactoring API and Archiver module * Set uo manifest.in and tox.ini * Add test module Change-Id: I265401658922c75b50db4d1232af17215ee1b6fc --- .gitreview | 4 + ChangeLog | 38 +++ MANIFEST.in | 15 +- etc/murano-repository.conf | 2 +- muranorepository/__init__.py | 2 +- muranorepository/api/__init__.py | 2 +- muranorepository/api/v1.py | 217 ++++++++++-------- muranorepository/cmd/__init__.py | 2 +- muranorepository/cmd/run.py | 6 +- muranorepository/consts.py | 2 +- muranorepository/manifest.py | 2 +- .../openstack/common/eventlet_backdoor.py | 3 +- muranorepository/openstack/common/log.py | 3 +- muranorepository/openstack/common/test.py | 3 +- muranorepository/service_metadata.tar | Bin 51200 -> 0 bytes .../tests/__init__.py | 0 muranorepository/tests/fixtures/__init__.py | 0 muranorepository/tests/fixtures/consts.py | 42 ++++ muranorepository/tests/test.conf | 28 +++ muranorepository/tests/test_main.py | 87 +++++++ muranorepository/utils/__init__.py | 2 +- muranorepository/utils/archiver.py | 66 +++--- setup.cfg | 8 +- test-requirements.txt | 9 +- tools/config/generate_sample.sh | 69 ------ tox.ini | 46 ++-- 26 files changed, 394 insertions(+), 264 deletions(-) create mode 100644 .gitreview delete mode 100644 muranorepository/service_metadata.tar rename "muranorepository/config.example\\" => muranorepository/tests/__init__.py (100%) create mode 100644 muranorepository/tests/fixtures/__init__.py create mode 100644 muranorepository/tests/fixtures/consts.py create mode 100644 muranorepository/tests/test.conf create mode 100644 muranorepository/tests/test_main.py delete mode 100755 tools/config/generate_sample.sh diff --git a/.gitreview b/.gitreview new file mode 100644 index 0000000..ac42f31 --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=stackforge/murano-repository.git diff --git a/ChangeLog b/ChangeLog index 3006463..3bd941a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,41 @@ +commit d259b6c33a4379c12dd673cb8cabc769545739d0 +Author: efedorova +Date: Thu Oct 17 19:18:08 2013 +0400 + + Refactoring API and Archiver module + + * Set uo manifest.in and tox.ini + * Add test module + + Change-Id: I265401658922c75b50db4d1232af17215ee1b6fc + +commit e4b9fe765e912a13ff02a394b329ecd117a0edb7 +Author: efedorova +Date: Tue Oct 15 19:03:09 2013 +0400 + + Fix buf with manifests listing + +commit b24d260524d16dce96a682fd32629951e2ac17ee +Author: efedorova +Date: Tue Oct 15 18:47:27 2013 +0400 + + Update api + +commit ecb0c93b3077755192929fa8ddf045ef43d43112 +Author: efedorova +Date: Mon Oct 14 16:54:00 2013 +0400 + + Fix typos + +commit c46bfbde86728d689677a2d7d484b2db04893186 +Author: efedorova +Date: Mon Oct 14 16:34:33 2013 +0400 + + Remove service to muranorepository + + Add venv support + Update requirements + commit 28cbd261871038cf1fc4fa67d36c26ae9e9a4747 Author: efedorova Date: Mon Oct 14 13:27:09 2013 +0400 diff --git a/MANIFEST.in b/MANIFEST.in index 0720919..fdf902d 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,10 +1,13 @@ -include AUTHORS -include README.rst -include ChangeLog -include LICENSE - recursive-include Services * - +include ChangeLog +include README.rst +include MANIFEST.in +include AUTHORS +include LICENSE +include ChangeLog +include babel.cfg +include tox.ini +include muranorepository/tests/test.conf exclude .gitignore exclude .gitreview diff --git a/etc/murano-repository.conf b/etc/murano-repository.conf index 6b0ea01..8078e89 100644 --- a/etc/murano-repository.conf +++ b/etc/murano-repository.conf @@ -1,6 +1,6 @@ [DEFAULT] # Address to bind the server to -host = 172.18.10.111 +host = localhost #Port the bind the server to port = 5000 diff --git a/muranorepository/__init__.py b/muranorepository/__init__.py index 207fa15..7d93825 100644 --- a/muranorepository/__init__.py +++ b/muranorepository/__init__.py @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# under the License. diff --git a/muranorepository/api/__init__.py b/muranorepository/api/__init__.py index 207fa15..7d93825 100644 --- a/muranorepository/api/__init__.py +++ b/muranorepository/api/__init__.py @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# under the License. diff --git a/muranorepository/api/v1.py b/muranorepository/api/v1.py index 5ace695..9ab52df 100644 --- a/muranorepository/api/v1.py +++ b/muranorepository/api/v1.py @@ -1,19 +1,20 @@ # Copyright (c) 2013 Mirantis, Inc. # -# Licensed under the Apache License, Version 2.0 (the "License"); you may +# 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 +# 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. import os -from flask import Blueprint, make_response, send_file + +from flask import Blueprint, send_file from flask import jsonify, request, abort from werkzeug import secure_filename @@ -26,116 +27,132 @@ CONF = cfg.CONF v1_api = Blueprint('v1', __name__) -@v1_api.route('/client/ui') -def get_ui_data(): +def get_archive(client): parser = ManifestParser(CONF.manifests) manifests = parser.parse() - archive_name = Archiver().create(manifests, "ui") + if client == 'conductor': + return Archiver().create(manifests, + 'heat', + 'agent', + 'scripts') + else: + return Archiver().create(manifests, client) - return send_file(archive_name) + +def get_locations(data_type, result_path): + locations = [] + if data_type == MANIFEST: + for item in os.listdir(result_path): + if '-manifest' in item and \ + os.path.isfile(os.path.join(result_path, item)): + locations.append(item) + else: + for path, subdirs, files in os.walk(result_path): + for name in files: + if path != result_path: + base, diff = path.rsplit(result_path, 2) + # split base path and remove slash + name = os.path.join(diff[1:], name) + locations.append(name) + return jsonify({data_type: locations}) + + +def save_file(request, result_path): + file_to_upload = request.files.get('file') + if file_to_upload: + filename = secure_filename(file_to_upload.filename) + file_to_upload.save(os.path.join(result_path, filename)) + return jsonify(result='success') + else: + abort(400) + + +def compose_path(data_type, path=None): + if path: + return os.path.join(CONF.manifests, getattr(CONF, data_type), path) + else: + return os.path.join(CONF.manifests, getattr(CONF, data_type)) + + +def check_data_type(data_type): + if data_type not in DATA_TYPES: + abort(404) + + +@v1_api.route('/client/ui') +def get_ui_data(): + return send_file(get_archive('ui')) @v1_api.route('/client/conductor') def get_conductor_data(): - parser = ManifestParser(CONF.manifests) - manifests = parser.parse() - archive_name = Archiver().create(manifests, - "heat", - "agent", - "scripts") - return send_file(archive_name) + return send_file(get_archive('conductor')) -@v1_api.route('/admin/', methods=['GET', 'POST']) +@v1_api.route('/admin/') def get_data_type_locations(data_type): - ####### validation ######## - if data_type not in DATA_TYPES: - abort(404) - result_path = os.path.join(CONF.manifests, getattr(CONF, data_type)) - ####### end validation ######## - if request.method == 'GET': - locations = [] - if data_type == MANIFEST: - for item in os.listdir(result_path): - if '-manifest' in item: - locations.append(item) - else: - for path, subdirs, files in os.walk(result_path): - for name in files: - locations.append(name) - result = {data_type: locations} - return jsonify(result) - - if request.method == 'POST': - try: - file_to_upload = request.files.get('files') - if file_to_upload: - filename = secure_filename(file_to_upload.filename) - file_to_upload.save(os.path.join(result_path, filename)) - return jsonify(result="success") - except: - abort(403) + check_data_type(data_type) + result_path = compose_path(data_type) + return get_locations(data_type, result_path) -@v1_api.route('/admin//', methods=['GET', 'POST']) -def get_data_type_locations_by_path_or_get_file(data_type, path): - if data_type not in DATA_TYPES: - abort(404) - result_path = os.path.join(os.path.join(CONF.manifests, - getattr(CONF, data_type), - path)) +@v1_api.route('/admin/', methods=['POST']) +def upload_file(data_type): + check_data_type(data_type) + result_path = compose_path(data_type) + try: + return save_file(request, result_path) + except: + abort(403) + + +@v1_api.route('/admin//') +def get_locations_in_nested_path_or_get_file(data_type, path): + check_data_type(data_type) + result_path = compose_path(data_type, path) + if os.path.isfile(result_path): + return send_file(result_path) + else: + return get_locations(data_type, result_path) + + +@v1_api.route('/admin//', methods=['POST']) +def upload_file_in_nested_path(data_type, path): + check_data_type(data_type) + result_path = compose_path(data_type, path) + return save_file(request, result_path) + + +@v1_api.route('/admin//', methods=['PUT']) +def create_dirs(data_type, path): + check_data_type(data_type) + result_path = compose_path(data_type, path) + resp = jsonify(result='success') + if os.path.exists(result_path): + return resp + if data_type == MANIFEST: + abort(403) + try: + os.makedirs(result_path) + except Exception: + abort(403) + return resp + + +@v1_api.route('/admin//', methods=['DELETE']) +def delete_dirictory_or_file(data_type, path): + check_data_type(data_type) + result_path = compose_path(data_type, path) if not os.path.exists(result_path): abort(404) - - if request.method == 'GET': - locations = [] - if os.path.isfile(result_path): - return send_file(result_path) - else: - for file in os.listdir(result_path): - locations.append(file) - result = {data_type: locations} - return jsonify(result) - - if request.method == 'POST': - file_to_upload = request.files.get('files') - if file_to_upload: - filename = secure_filename(file_to_upload.filename) - file_to_upload.save(os.path.join(result_path, filename)) - return jsonify(result="success") - else: - abort(403) - - -@v1_api.route('/admin//', methods=['PUT', 'DELETE']) -def create_dirs(data_type, path): - if data_type not in DATA_TYPES: - abort(404) - result_path = os.path.join(CONF.manifests, getattr(CONF, data_type), path) - if request.method == 'PUT': - resp = make_response() - if os.path.exists(result_path): - return resp - if data_type == MANIFEST: - abort(403) + if os.path.isfile(result_path): try: - os.makedirs(result_path) - except Exception as e: - abort(403) - return resp - - if request.method == 'DELETE': - if not os.path.exists(result_path): + os.remove(result_path) + except Exception: abort(404) - if os.path.isfile(result_path): - try: - os.remove(result_path) - except Exception as e: - abort(404) - else: - try: - os.rmdir(result_path) - except Exception as e: - abort(403) - resp = make_response() - return resp + else: + try: + os.rmdir(result_path) + except Exception: + abort(403) + return jsonify(result='success') diff --git a/muranorepository/cmd/__init__.py b/muranorepository/cmd/__init__.py index 207fa15..7d93825 100644 --- a/muranorepository/cmd/__init__.py +++ b/muranorepository/cmd/__init__.py @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# under the License. diff --git a/muranorepository/cmd/run.py b/muranorepository/cmd/run.py index 761c9de..f31548f 100644 --- a/muranorepository/cmd/run.py +++ b/muranorepository/cmd/run.py @@ -28,8 +28,8 @@ possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir, os.pardir)) if os.path.exists(os.path.join(possible_topdir, - 'muranorepository', - '__init__.py')): + 'muranorepository', + '__init__.py')): sys.path.insert(0, possible_topdir) from muranorepository import config @@ -43,7 +43,7 @@ LOG = log.getLogger(__name__) def main(): dev_conf = os.path.join(possible_topdir, 'etc', - 'muranorepository.conf') + 'murano-repository.conf') config_files = None if os.path.exists(dev_conf): config_files = [dev_conf] diff --git a/muranorepository/consts.py b/muranorepository/consts.py index 026e756..a306d29 100644 --- a/muranorepository/consts.py +++ b/muranorepository/consts.py @@ -20,4 +20,4 @@ HEAT = 'heat' AGENT = 'agent' SCRIPTS = 'scripts' -DATA_TYPES = [UI, WORKFLOW, HEAT, AGENT, SCRIPTS, MANIFEST] \ No newline at end of file +DATA_TYPES = [UI, WORKFLOW, HEAT, AGENT, SCRIPTS, MANIFEST] diff --git a/muranorepository/manifest.py b/muranorepository/manifest.py index a6410f3..14d69eb 100644 --- a/muranorepository/manifest.py +++ b/muranorepository/manifest.py @@ -16,4 +16,4 @@ class Manifest(object): def __init__(self, initial_data): for key in initial_data: - setattr(self, key, initial_data[key]) \ No newline at end of file + setattr(self, key, initial_data[key]) diff --git a/muranorepository/openstack/common/eventlet_backdoor.py b/muranorepository/openstack/common/eventlet_backdoor.py index c64b9d0..b141fd8 100644 --- a/muranorepository/openstack/common/eventlet_backdoor.py +++ b/muranorepository/openstack/common/eventlet_backdoor.py @@ -104,8 +104,7 @@ def _listen(host, start_port, end_port, listen_func): try: return listen_func((host, try_port)) except socket.error as exc: - if (exc.errno != errno.EADDRINUSE or - try_port >= end_port): + if (exc.errno != errno.EADDRINUSE or try_port >= end_port): raise try_port += 1 diff --git a/muranorepository/openstack/common/log.py b/muranorepository/openstack/common/log.py index e9f3f4b..d41a86c 100644 --- a/muranorepository/openstack/common/log.py +++ b/muranorepository/openstack/common/log.py @@ -425,7 +425,8 @@ def _setup_logging_from_conf(): if CONF.publish_errors: handler = importutils.import_object( - "muranorepository.openstack.common.log_handler.PublishErrorsHandler", + "muranorepository.openstack.common.log_handler." + "PublishErrorsHandler", logging.ERROR) log_root.addHandler(handler) diff --git a/muranorepository/openstack/common/test.py b/muranorepository/openstack/common/test.py index 955f10b..dfef9a9 100644 --- a/muranorepository/openstack/common/test.py +++ b/muranorepository/openstack/common/test.py @@ -29,7 +29,8 @@ class BaseTestCase(testtools.TestCase): super(BaseTestCase, self).setUp() self._set_timeout() self._fake_output() - self.useFixture(fixtures.FakeLogger('muranorepository.openstack.common')) + self.useFixture( + fixtures.FakeLogger('muranorepository.openstack.common')) self.useFixture(fixtures.NestedTempfile()) def _set_timeout(self): diff --git a/muranorepository/service_metadata.tar b/muranorepository/service_metadata.tar deleted file mode 100644 index 33903f8978921ab74af47794897c8f384281f6b0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51200 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 G6!-' +' This goes a description' + +'full_service_name: test_service' +'author: Mirantis Inc.' +'service_version: 1.0' +'enabled: True' + +'ui:' +' - test1.yaml' + +'workflows:' +' - test1.xml' + +'heat:' +' - Windows.template' + +'agents:' +' - test1.template' + + +'scripts:' +' - test1.sh' diff --git a/muranorepository/tests/test.conf b/muranorepository/tests/test.conf new file mode 100644 index 0000000..cb76842 --- /dev/null +++ b/muranorepository/tests/test.conf @@ -0,0 +1,28 @@ +[DEFAULT] +# Address to bind the server to +host = localhost +#Port the bind the server to +port = 5000 + + +# Provide information about data types +# absolute path to manifest location(root directory) +#manifests = /home/fervent/Projects/my_repo/muranorepository/tests/var +manifests = /bin/server + +# Parameter name corresponds to section in manifest file +# Parameter value corresponds to relative path to data type +ui = ui +workflows = workflows +heat = heat +agent = agent +scripts = scripts + +# Configure archive structure +# data_type = desired folder +[output] +ui = service_forms +workflows = workflows +heat = templates/cf +agent = templates/agent +scripts = templates/agent/script diff --git a/muranorepository/tests/test_main.py b/muranorepository/tests/test_main.py new file mode 100644 index 0000000..3ba1b6f --- /dev/null +++ b/muranorepository/tests/test_main.py @@ -0,0 +1,87 @@ +import sys +import os +from flask.ext.testing import TestCase as FlaskTestCase +import shutil +from StringIO import StringIO +import mockfs +possible_topdir = os.path.normpath(os.path.join(os.path.abspath(__file__), + os.pardir, + os.pardir, + os.pardir)) +if os.path.exists(os.path.join(possible_topdir, + 'muranorepository', + '__init__.py')): + sys.path.insert(0, possible_topdir) +from muranorepository.consts import MANIFEST +from muranorepository.tests.fixtures.consts import MANIFEST_FILE +from muranorepository import config +from muranorepository.main import make_app + + +class TestAdminAPI(FlaskTestCase): + url = "/v1/admin/{0}" + url_with_path = "/v1/admin/{0}/{1}" + + def create_app(self): + test_app = make_app() + test_app.config['TESTING'] = True + return test_app + + def setUp(self): + config_files = [os.path.join(possible_topdir, + 'muranorepository', + 'tests', + 'test.conf')] + + config.parse_configs(None, config_files) + self.mfs = mockfs.replace_builtins() + self.mfs.add_entries( + { + '/bin/server': { + 'test-manifest.yaml': MANIFEST_FILE, + 'ui': {'test1.yaml': ''}, + 'heat': + {'Windows.template': '', + 'folder_to_delete': {} + } + } + }) + + def tearDown(self): + mockfs.restore_builtins() + + def test_list_manifests(self): + response = self.client.get(self.url.format(MANIFEST)) + expected_result = {MANIFEST: ['test-manifest.yaml']} + self.assert200(response) + self.assertEquals(response.json, expected_result) + + def test_list_ui(self): + response = self.client.get(self.url.format('ui')) + expected_result = {'ui': ['test1.yaml']} + self.assert200(response) + self.assertEquals(response.json, expected_result) + + def test_create_ui_subfolder(self): + response = self.client.put(self.url_with_path.format('ui', 'test')) + expected_result = {'result': 'success'} + self.assert200(response) + self.assertEquals(response.json, expected_result) + shutil.rmtree('bin/server/ui/test') + + def test_delete_heat_subfolder(self): + url = self.url_with_path.format('heat', + 'folder_to_delete') + response = self.client.delete(url) + self.assert200(response) + expected_result = {'result': 'success'} + self.assertEquals(response.json, expected_result) + + def test_upload_ui_file(self): + upload_data = {'file': (StringIO('content'), 'test.yaml')} + response = self.client.post(self.url.format('ui'), + data=upload_data) + 'test.yaml' in os.listdir('bin/server/ui') + self.assert200(response) + expected_result = {'result': 'success'} + self.assertEquals(response.json, expected_result) diff --git a/muranorepository/utils/__init__.py b/muranorepository/utils/__init__.py index 207fa15..7d93825 100644 --- a/muranorepository/utils/__init__.py +++ b/muranorepository/utils/__init__.py @@ -10,4 +10,4 @@ # 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. \ No newline at end of file +# under the License. diff --git a/muranorepository/utils/archiver.py b/muranorepository/utils/archiver.py index 76d0e8a..e893281 100644 --- a/muranorepository/utils/archiver.py +++ b/muranorepository/utils/archiver.py @@ -18,16 +18,44 @@ import shutil import logging as log from oslo.config import cfg from muranorepository.consts import DATA_TYPES -OUTPUT_CONF = cfg.CONF.output CONF = cfg.CONF class Archiver(object): + def _copy_data(self, file_lists, src, dst): + if not os.path.exists(dst): + os.makedirs(dst) + + for path in file_lists: + source = os.path.join(src, path) + destination = os.path.join(dst, path) + base_dir = os.path.dirname(destination) + + if (base_dir != dst) and (not os.path.exists(base_dir)): + os.makedirs(os.path.dirname(destination)) + try: + shutil.copyfile(source, destination) + except IOError: + log.error("Unable to copy file " + "{0}".format(file)) + + def _compose_archive(self, path): + target_archive = "service_metadata.tar" + with tarfile.open(target_archive, "w") as tar: + for item in os.listdir(path): + tar.add(os.path.join(path, item), item) + try: + shutil.rmtree(path, ignore_errors=True) + except Exception as e: + log.error("Unable to delete temp directory: {0}".format(e)) + return os.path.abspath(target_archive) def create(self, manifests, *types): """ manifests -- list of Manifest objects *types - desired data types to be added to archive + + return: absolute path to created archive """ temp_dir = tempfile.mkdtemp() for data_type in types: @@ -38,44 +66,18 @@ class Archiver(object): for manifest in manifests: if not manifest.enabled and not manifest.valid: continue + if hasattr(manifest, data_type): + file_list = getattr(manifest, data_type) dst_directory = os.path.join(temp_dir, - getattr(OUTPUT_CONF, + getattr(CONF.output, data_type)) scr_directory = os.path.join(CONF.manifests, getattr(CONF, data_type)) - - if not os.path.exists(dst_directory): - os.makedirs(dst_directory) - - for path in getattr(manifest, data_type): - source = os.path.join(scr_directory, path) - destination = os.path.join(dst_directory, path) - base_dir = os.path.dirname(destination) - - if (base_dir != dst_directory) \ - and (not os.path.exists(base_dir)): - os.makedirs(os.path.dirname(destination)) - try: - shutil.copyfile(source, destination) - except IOError: - log.error("Unable to copy file " - "{0}".format(file)) + self._copy_data(file_list, scr_directory, dst_directory) else: log.info( "Manifest for {0} service has no file definitions for " "{1}".format(manifest.service_display_name, data_type)) - target_archive = "service_metadata.tar" - with tarfile.open(target_archive, "w") as tar: - for item in os.listdir(temp_dir): - tar.add(os.path.join(temp_dir, item), item) - try: - shutil.rmtree(temp_dir, ignore_errors=True) - except Exception as e: - log.error("Unable to delete temp directory: {0}".format(e)) - return os.path.abspath(target_archive) - - - - + return self._compose_archive(temp_dir) diff --git a/setup.cfg b/setup.cfg index c3ecfbe..1a3825d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,7 +32,8 @@ author-email = openstack-dev@lists.openstack.org home-page = https://launchpad.net/murano [global] -setup-hooks = pbr.hooks.setup_hook +setup-hooks = + pbr.hooks.setup_hook [files] packages = @@ -54,8 +55,9 @@ tag_date = 0 tag_svn_revision = 0 [compile_catalog] -directory = murano-repository/locale -domain = murano-repository +directory = muranorepository/locale +domain = muranorepository + [extract_messages] keywords = _ gettext ngettext l_ lazy_gettext diff --git a/test-requirements.txt b/test-requirements.txt index 67d9663..dd15aa8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -2,16 +2,11 @@ pep8==1.4.5 pyflakes>=0.7.2,<0.7.4 flake8==2.0 -hacking>=0.5.6,<0.8 - -coverage>=3.6 +flask-testing docutils==0.9.1 fixtures>=0.3.14 mock>=1.0 -nose -openstack.nose_plugin>=0.7 +mockfs oslo.sphinx pylint==0.25.2 -sphinx>=1.1.2 -sphinxcontrib-httpdomain unittest2 diff --git a/tools/config/generate_sample.sh b/tools/config/generate_sample.sh deleted file mode 100755 index 4c6d101..0000000 --- a/tools/config/generate_sample.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env bash - -print_hint() { - echo "Try \`${0##*/} --help' for more information." >&2 -} - -PARSED_OPTIONS=$(getopt -n "${0##*/}" -o hb:p:o: \ - --long help,base-dir:,package-name:,output-dir: -- "$@") - -if [ $? != 0 ] ; then print_hint ; exit 1 ; fi - -eval set -- "$PARSED_OPTIONS" - -while true; do - case "$1" in - -h|--help) - echo "${0##*/} [options]" - echo "" - echo "options:" - echo "-h, --help show brief help" - echo "-b, --base-dir=DIR Project base directory (required)" - echo "-p, --package-name=NAME Project package name" - echo "-o, --output-dir=DIR File output directory" - exit 0 - ;; - -b|--base-dir) - shift - BASEDIR=`echo $1 | sed -e 's/\/*$//g'` - shift - ;; - -p|--package-name) - shift - PACKAGENAME=`echo $1` - shift - ;; - -o|--output-dir) - shift - OUTPUTDIR=`echo $1 | sed -e 's/\/*$//g'` - shift - ;; - --) - break - ;; - esac -done - -if [ -z $BASEDIR ] || ! [ -d $BASEDIR ] -then - echo "${0##*/}: missing project base directory" >&2 ; print_hint ; exit 1 -fi - -PACKAGENAME=${PACKAGENAME:-${BASEDIR##*/}} - -OUTPUTDIR=${OUTPUTDIR:-$BASEDIR/etc} -if ! [ -d $OUTPUTDIR ] -then - echo "${0##*/}: cannot access \`$OUTPUTDIR': No such file or directory" >&2 - exit 1 -fi - -BASEDIRESC=`echo $BASEDIR | sed -e 's/\//\\\\\//g'` -FILES=$(find $BASEDIR/$PACKAGENAME -type f -name "*.py" ! -path "*/tests/*" \ - -exec grep -l "Opt(" {} + | sed -e "s/^$BASEDIRESC\///g" | sort -u) - -export EVENTLET_NO_GREENDNS=yes - -MODULEPATH=murano-repository.openstack.common.config.generator -OUTPUTFILE=$OUTPUTDIR/$PACKAGENAME.conf.sample -python -m $MODULEPATH $FILES > $OUTPUTFILE diff --git a/tox.ini b/tox.ini index b6c0ff6..96186f2 100644 --- a/tox.ini +++ b/tox.ini @@ -10,49 +10,29 @@ setenv = VIRTUAL_ENV={envdir} NOSE_OPENSTACK_SHOW_ELAPSED=1 deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = nosetests [testenv:pep8] deps = pep8==1.3.3 -commands = pep8 --repeat --show-source murano-repository setup.py - -[testenv:venv] -commands = {posargs} - -[testenv:cover] -commands = nosetests --cover-erase --cover-package=murano-repository --with-xcoverage - -[tox:jenkins] -downloadcache = ~/cache/pip - -[testenv:jenkins26] -basepython = python2.6 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkins27] -basepython = python2.7 -setenv = NOSE_WITH_XUNIT=1 -deps = file://{toxinidir}/.cache.bundle - -[testenv:jenkinscover] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = nosetests --cover-erase --cover-package=muranoapi --with-xcoverage - -[testenv:jenkinsvenv] -deps = file://{toxinidir}/.cache.bundle -setenv = NOSE_WITH_XUNIT=1 -commands = {posargs} +commands = pep8 --repeat --show-source muranorepository setup.py [testenv:pyflakes] deps = flake8 commands = flake8 +[testenv:venv] +commands = {posargs} + +[testenv:cover] +commands = nosetests --cover-erase --cover-package=muranorepository --with-xcoverage + +[tox:jenkins] +downloadcache = ~/cache/pip + [flake8] # H301 one import per line # H302 import only modules -ignore = H301,H302 +# H201 no 'except:' at least use 'except Exception:' +ignore = H301,H302,F403,H201 show-source = true builtins = _ -exclude=.venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,tools \ No newline at end of file +exclude=.build,.venv,.git,.tox,dist,doc,*/openstack,*lib/python*,*egg,tools