diff --git a/murano_tempest_tests/extras/MockApp/Classes/mock_muranopl.yaml b/murano_tempest_tests/extras/MockApp/Classes/mock_muranopl.yaml index df29caac3..c89de41aa 100644 --- a/murano_tempest_tests/extras/MockApp/Classes/mock_muranopl.yaml +++ b/murano_tempest_tests/extras/MockApp/Classes/mock_muranopl.yaml @@ -26,7 +26,7 @@ Methods: credentials: uri: localhost deploy: - Body: + Body: - $this.find(std:Environment).reporter.report($this, 'Follow the white rabbit') staticAction: diff --git a/murano_tempest_tests/extras/MockApp/UI/ui.yaml b/murano_tempest_tests/extras/MockApp/UI/ui.yaml index e7a775770..6f1cda4a7 100644 --- a/murano_tempest_tests/extras/MockApp/UI/ui.yaml +++ b/murano_tempest_tests/extras/MockApp/UI/ui.yaml @@ -19,4 +19,4 @@ Forms: type: string description: Apache License, Version 2.0 hidden: false - required: false \ No newline at end of file + required: false diff --git a/murano_tempest_tests/tests/api/application_catalog/artifacts/base.py b/murano_tempest_tests/tests/api/application_catalog/artifacts/base.py index 82ff42940..7e36d8da1 100644 --- a/murano_tempest_tests/tests/api/application_catalog/artifacts/base.py +++ b/murano_tempest_tests/tests/api/application_catalog/artifacts/base.py @@ -19,6 +19,7 @@ from tempest import config from tempest.lib import base from murano_tempest_tests import clients +from murano_tempest_tests import utils CONF = config.CONF @@ -85,6 +86,7 @@ class BaseArtifactsTest(base.BaseTestCase): creds = cls.get_configured_isolated_creds(type_of_creds='primary') cls.os = clients.Manager(credentials=creds) cls.artifacts_client = cls.os.artifacts_client + cls.application_catalog_client = cls.os.application_catalog_client @classmethod def resource_cleanup(cls): @@ -94,3 +96,24 @@ class BaseArtifactsTest(base.BaseTestCase): def clear_isolated_creds(cls): if hasattr(cls, "dynamic_cred"): cls.dynamic_cred.clear_creds() + + @classmethod + def upload_package(cls, application_name, version=None, require=None): + abs_archive_path, dir_with_archive, archive_name = \ + utils.prepare_package(application_name, version=version, + add_class_name=True, require=require) + package = cls.artifacts_client.upload_package( + application_name, archive_name, dir_with_archive, + {"categories": [], "tags": [], 'is_public': False}) + return package, abs_archive_path + + @staticmethod + def create_obj_model(package): + return { + "name": package['display_name'], + "?": { + "type": package['name'], + "id": utils.generate_uuid(), + "classVersion": package['version'] + } + } diff --git a/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning.py b/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning.py new file mode 100644 index 000000000..ba704ef40 --- /dev/null +++ b/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning.py @@ -0,0 +1,164 @@ +# 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. + +import os +import six +import testtools + +from tempest import config + +from murano_tempest_tests.tests.api.application_catalog.artifacts import base +from murano_tempest_tests import utils + +CONF = config.CONF + + +class TestVersioning(base.BaseArtifactsTest): + + @classmethod + def resource_setup(cls): + if not CONF.application_catalog.glare_backend: + msg = ("Murano is not using GLARE backend. " + "Skipping GLARE tests.") + raise cls.skipException(msg) + super(TestVersioning, cls).resource_setup() + + application_name = utils.generate_name('package_test') + # create first package + version1 = '1.0.0' + package1, _ = cls.upload_package(application_name, + version=version1) + + # create second package + version2 = '2.0.0' + package2, path1 = cls.upload_package(application_name, + version=version2) + + # create package with require >=2.0.0 for 2.0.0 package + expected_version = '>=2.0.0' + main_app_name = utils.generate_name('main_package_test') + require = [(package2['name'], expected_version)] + package3, path2 = cls.upload_package(main_app_name, require=require) + + cls.packages = { + '1.0.0': package1, + '2.0.0': package2, + 'require_for_2.0.0': package3, + } + cls.abs_archive_paths = [path1, path2] + + @classmethod + def resource_cleanup(cls): + for pkg in six.itervalues(cls.packages): + cls.artifacts_client.delete_package(pkg['id']) + map(os.remove, cls.abs_archive_paths) + super(TestVersioning, cls).resource_cleanup() + + @testtools.testcase.attr('smoke') + def test_availability_of_packages_with_different_versions(self): + """Test availability of packages with different versions. + + 1) Check two packages to have the same names. + 2) Check two packages to have different ids. + 3) Check two packages to be in repository. + """ + self.assertEqual(self.packages['1.0.0']['name'], + self.packages['2.0.0']['name']) + self.assertNotEqual(self.packages['1.0.0']['id'], + self.packages['2.0.0']['id']) + + # check packages availability + artifact_packages = {pkg['id'] for pkg in + self.artifacts_client.get_list_packages()} + + self.assertIn(self.packages['1.0.0']['id'], artifact_packages) + self.assertIn(self.packages['2.0.0']['id'], artifact_packages) + + @testtools.testcase.attr('smoke') + def test_deploy_packages_with_different_versions(self): + """Test deployment of packages with different versions. + + 1) Create environment. + 2) Add package with version 1.0.0 to the environment. + 3) Add package with version 2.0.0 to the environment. + 4) Deploy environment. + 5) Check if deployment status ok. + """ + + # create environment + environment_name = utils.generate_name('create_environment') + environment = self.application_catalog_client.create_environment( + environment_name) + self.addCleanup(self.application_catalog_client.delete_environment, + environment['id']) + + # create session + session = self.application_catalog_client.create_session( + environment['id']) + + # add first application + object_model = self.create_obj_model(self.packages['1.0.0']) + + self.application_catalog_client.create_service( + environment['id'], session['id'], object_model) + + # add second application + object_model = self.create_obj_model(self.packages['2.0.0']) + + self.application_catalog_client.create_service( + environment['id'], session['id'], object_model) + + self.application_catalog_client.deploy_session( + environment['id'], session['id']) + + deploy_result = utils.wait_for_environment_deploy( + self.application_catalog_client, environment['id'])['status'] + + self.assertEqual(deploy_result, 'ready') + + @testtools.testcase.attr('smoke') + def test_deploy_package_with_required_package_version(self): + """Test deployment of package which requires package with present version. + + 1) Create environment. + 2) Add to the environment package which requires version 2.0.0 of the + package, which is present with versions 1.0.0 and 2.0.0 in repository. + 3) Deploy environment. + 4) Check if deployment status ok. + """ + + # create environment + environment_name = utils.generate_name('create_environment') + environment = self.application_catalog_client.create_environment( + environment_name) + self.addCleanup(self.application_catalog_client.delete_environment, + environment['id']) + + # create session + session = self.application_catalog_client.create_session( + environment['id']) + + object_model = self.create_obj_model( + self.packages['require_for_2.0.0']) + + self.application_catalog_client.create_service( + environment['id'], session['id'], object_model) + + self.application_catalog_client.deploy_session( + environment['id'], session['id']) + + deploy_result = utils.wait_for_environment_deploy( + self.application_catalog_client, environment['id'])['status'] + + self.assertEqual(deploy_result, 'ready') diff --git a/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning_negative.py b/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning_negative.py new file mode 100644 index 000000000..298e3b1af --- /dev/null +++ b/murano_tempest_tests/tests/api/application_catalog/artifacts/test_versioning_negative.py @@ -0,0 +1,97 @@ +# 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. + +import os +import six +import testtools + +from tempest import config + +from murano_tempest_tests.tests.api.application_catalog.artifacts import base +from murano_tempest_tests import utils + +CONF = config.CONF + + +class TestVersioningNegative(base.BaseArtifactsTest): + + @classmethod + def resource_setup(cls): + if not CONF.application_catalog.glare_backend: + msg = ("Murano is not using GLARE backend. " + "Skipping GLARE tests.") + raise cls.skipException(msg) + super(TestVersioningNegative, cls).resource_setup() + + # create package with version 1.0.0 + application_name = utils.generate_name('package_test') + provided_version = '1.0.0' + package1, path1 = cls.upload_package( + application_name, version=provided_version) + + # main application + expected_version = '2.0.0' + main_app_name = utils.generate_name('main_package_test') + require = [(package1['name'], expected_version)] + package2, path2 = cls.upload_package(main_app_name, require=require) + + cls.packages = { + '1.0.0': package1, + 'require_for_1.0.0': package2 + } + cls.abs_archive_paths = [path1, path2] + + @classmethod + def resource_cleanup(cls): + for pkg in six.itervalues(cls.packages): + cls.artifacts_client.delete_package(pkg['id']) + map(os.remove, cls.abs_archive_paths) + super(TestVersioningNegative, cls).resource_cleanup() + + @testtools.testcase.attr('negative') + @testtools.testcase.attr('smoke') + def test_deploy_package_with_no_required_package_version(self): + """Test deployment of package which requires package with absent version. + + 1) Create environment. + 2) Add to the environment package which requires version 2.0.0 of the + package, which is present with version 1.0.0 only in repository. + 3) Deploy environment. + 4) Check if deployment status failure. + """ + + # create environment + environment_name = utils.generate_name('create_environment') + environment = self.application_catalog_client.create_environment( + environment_name) + self.addCleanup(self.application_catalog_client.delete_environment, + environment['id']) + + # create session + session = self.application_catalog_client.create_session( + environment['id']) + + object_model = self.create_obj_model( + self.packages['require_for_1.0.0']) + + self.application_catalog_client.create_service( + environment['id'], session['id'], object_model) + + self.application_catalog_client.deploy_session( + environment['id'], session['id']) + + deploy_result = utils.wait_for_environment_deploy( + self.application_catalog_client, environment['id'])['status'] + + self.assertEqual(deploy_result, 'deploy failure') diff --git a/murano_tempest_tests/utils.py b/murano_tempest_tests/utils.py index aee4785e4..822e8e72d 100644 --- a/murano_tempest_tests/utils.py +++ b/murano_tempest_tests/utils.py @@ -16,6 +16,7 @@ import collections import os import shutil import tempfile +import time import uuid import yaml import zipfile @@ -35,7 +36,7 @@ MANIFEST = {'Format': 'MuranoPL/1.0', def compose_package(app_name, manifest, package_dir, require=None, archive_dir=None, add_class_name=False, - manifest_required=True): + manifest_required=True, version=None): """Composes a murano package Composes package `app_name` with `manifest` file as a template for the @@ -44,6 +45,13 @@ def compose_package(app_name, manifest, package_dir, Puts the resulting .zip file into `acrhive_dir` if present or in the `package_dir`. """ + class_file_changes = add_class_name or require + # store class file before changes + if class_file_changes: + class_path = os.path.join(package_dir, 'Classes', 'mock_muranopl.yaml') + with open(class_path, 'r') as f: + class_store = f.read() + if manifest_required: with open(manifest, 'w') as f: fqn = 'io.murano.apps.' + app_name @@ -52,7 +60,10 @@ def compose_package(app_name, manifest, package_dir, mfest_copy['Name'] = app_name mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'} if require: - mfest_copy['Require'] = require + mfest_copy['Require'] = {str(name): version + for name, version in require} + if version: + mfest_copy['Version'] = version f.write(yaml.dump(mfest_copy, default_flow_style=False)) if add_class_name: @@ -66,6 +77,20 @@ def compose_package(app_name, manifest, package_dir, with open(class_file, 'w') as f: f.write(contents) + if require: + class_file = os.path.join(package_dir, 'Classes', 'mock_muranopl.yaml') + with open(class_file, 'r') as f: + content = f.read() + + index_string = 'deploy:\n Body:\n ' + index = content.index(index_string) + len(index_string) + class_names = [req[0][req[0].rfind('.') + 1:] for req in require] + addition = "".join(["- new({})\n".format(name) + ' ' * 6 + for name in class_names]) + content = content[:index] + addition + content[index:] + with open(class_file, 'w') as f: + f.write(content) + name = app_name + '.zip' if not archive_dir: @@ -80,11 +105,17 @@ def compose_package(app_name, manifest, package_dir, arcname=os.path.join(os.path.relpath(root, package_dir), f) ) + # restore class file after changes + if class_file_changes: + with open(class_path, 'w') as f: + f.write(class_store) + return archive_path, name def prepare_package(name, require=None, add_class_name=False, - app='MockApp', manifest_required=True): + app='MockApp', manifest_required=True, + version=None): """Prepare package. :param name: Package name to compose @@ -98,7 +129,8 @@ def prepare_package(name, require=None, add_class_name=False, arc_path, filename = compose_package( name, os.path.join(app_dir, 'manifest.yaml'), app_dir, require=require, archive_dir=target_arc_path, - add_class_name=add_class_name, manifest_required=manifest_required) + add_class_name=add_class_name, manifest_required=manifest_required, + version=version) return arc_path, target_arc_path, filename @@ -477,3 +509,13 @@ def get_local_inheritance(classes): base_fqn = base_class result.setdefault(base_fqn, []).append(class_name) return result + + +def wait_for_environment_deploy(client, environment_id, + timeout=1800, interval=10): + start_time = time.time() + while client.get_environment(environment_id)['status'] == 'deploying': + if time.time() - start_time > timeout: + break + time.sleep(interval) + return client.get_environment(environment_id)