From 139c835a2e6050e94240c60560a2fcbb1854c11f Mon Sep 17 00:00:00 2001 From: zhurong Date: Sat, 28 Jul 2018 15:41:21 +0800 Subject: [PATCH] Try to move functional test to murano-tempest-plugin Change-Id: I19debb3ca8cceb99d21a5bc500264b18e6bce8ce --- .../tests/functional/__init__.py | 0 .../tests/functional/common/__init__.py | 0 .../tests/functional/common/tempest_utils.py | 49 ++ .../tests/functional/common/utils.py | 553 ++++++++++++++++++ .../functional/common/zip_utils_mixin.py | 30 + .../tests/functional/engine/__init__.py | 0 .../functional/engine/config.conf.sample | 45 ++ .../tests/functional/engine/config.py | 88 +++ .../Classes/ApacheHttpServer.yaml | 81 +++ .../LICENSE | 175 ++++++ .../Resources/index.html | 8 + .../manifest.yaml | 28 + .../Classes/Lighttpd.yaml | 55 ++ .../io.murano.apps.test.Lighttpd/LICENSE | 175 ++++++ .../Resources/DeployLighttpd.template | 27 + .../Resources/scripts/deployLighttpd.sh | 14 + .../manifest.yaml | 24 + .../Classes/UpdateExecutor.yaml | 47 ++ .../LICENSE | 175 ++++++ .../Resources/Update.template | 30 + .../Resources/scripts/update.sh | 14 + .../manifest.yaml | 22 + .../Classes/ExampleChef.yaml | 48 ++ .../Resources/DeployExampleChef.template | 22 + .../Resources/scripts/test/README.rdoc | 7 + .../Resources/scripts/test/metadata.rb | 8 + .../Resources/scripts/test/recipes/install.rb | 10 + .../manifest.yaml | 10 + .../Classes/ExamplePuppet.yaml | 46 ++ .../Resources/DeployExamplePuppet.template | 20 + .../scripts/test/manifests/install.pp | 4 + .../manifest.yaml | 10 + .../tests/functional/engine/manager.py | 238 ++++++++ .../engine/test_conf_language_support.py | 87 +++ .../functional/engine/test_deployment.py | 138 +++++ .../tests/functional/integration/__init__.py | 0 .../integration/integration_base.py | 157 +++++ .../Classes/MistralShowcaseApp.yaml | 32 + .../Resources/TestEcho_MistralWorkflow.yaml | 24 + .../manifest.yaml | 10 + .../Classes/PolicyEnforcementTestApp.yaml | 48 ++ .../manifest.yaml | 10 + .../integration/rules_murano_action.txt | 18 + .../integration/rules_murano_system.txt | 7 + .../functional/integration/test_mistral.py | 62 ++ .../functional/integration/test_policy_enf.py | 89 +++ requirements.txt | 9 + 47 files changed, 2754 insertions(+) create mode 100644 murano_tempest_tests/tests/functional/__init__.py create mode 100644 murano_tempest_tests/tests/functional/common/__init__.py create mode 100644 murano_tempest_tests/tests/functional/common/tempest_utils.py create mode 100644 murano_tempest_tests/tests/functional/common/utils.py create mode 100644 murano_tempest_tests/tests/functional/common/zip_utils_mixin.py create mode 100644 murano_tempest_tests/tests/functional/engine/__init__.py create mode 100644 murano_tempest_tests/tests/functional/engine/config.conf.sample create mode 100644 murano_tempest_tests/tests/functional/engine/config.py create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/LICENSE create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/LICENSE create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/LICENSE create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/Update.template create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Classes/ExampleChef.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/DeployExampleChef.template create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/README.rdoc create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/metadata.rb create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/recipes/install.rb create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Classes/ExamplePuppet.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/DeployExamplePuppet.template create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/scripts/test/manifests/install.pp create mode 100644 murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/engine/manager.py create mode 100644 murano_tempest_tests/tests/functional/engine/test_conf_language_support.py create mode 100644 murano_tempest_tests/tests/functional/engine/test_deployment.py create mode 100644 murano_tempest_tests/tests/functional/integration/__init__.py create mode 100644 murano_tempest_tests/tests/functional/integration/integration_base.py create mode 100644 murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Classes/MistralShowcaseApp.yaml create mode 100644 murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Resources/TestEcho_MistralWorkflow.yaml create mode 100644 murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/Classes/PolicyEnforcementTestApp.yaml create mode 100644 murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/manifest.yaml create mode 100644 murano_tempest_tests/tests/functional/integration/rules_murano_action.txt create mode 100644 murano_tempest_tests/tests/functional/integration/rules_murano_system.txt create mode 100644 murano_tempest_tests/tests/functional/integration/test_mistral.py create mode 100644 murano_tempest_tests/tests/functional/integration/test_policy_enf.py diff --git a/murano_tempest_tests/tests/functional/__init__.py b/murano_tempest_tests/tests/functional/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/murano_tempest_tests/tests/functional/common/__init__.py b/murano_tempest_tests/tests/functional/common/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/murano_tempest_tests/tests/functional/common/tempest_utils.py b/murano_tempest_tests/tests/functional/common/tempest_utils.py new file mode 100644 index 0000000..3675eac --- /dev/null +++ b/murano_tempest_tests/tests/functional/common/tempest_utils.py @@ -0,0 +1,49 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 congressclient.v1.client as cclient +from keystoneauth1 import identity +from keystoneauth1 import session as ksasession +import keystoneclient.v3 as ksclient +from tempest import config + +import murano_tempest_tests.tests.functional.common.utils as common_utils + +CONF = config.CONF + + +class TempestDeployTestMixin(common_utils.DeployTestMixin): + """Overrides methods to use tempest configuration.""" + + @staticmethod + @common_utils.memoize + def keystone_client(): + return ksclient.Client(username=CONF.auth.admin_username, + password=CONF.auth.admin_password, + tenant_name=CONF.auth.admin_project_name, + auth_url=CONF.identity.uri_v3) + + @staticmethod + @common_utils.memoize + def congress_client(): + auth = identity.v3.Password( + auth_url=CONF.identity.uri_v3, + username=CONF.auth.admin_username, + password=CONF.auth.admin_password, + project_name=CONF.auth.admin_project_name, + user_domain_name=CONF.auth.admin_domain_name, + project_domain_name=CONF.auth.admin_domain_name) + session = ksasession.Session(auth=auth) + return cclient.Client(session=session, + service_type='policy') diff --git a/murano_tempest_tests/tests/functional/common/utils.py b/murano_tempest_tests/tests/functional/common/utils.py new file mode 100644 index 0000000..ad774b4 --- /dev/null +++ b/murano_tempest_tests/tests/functional/common/utils.py @@ -0,0 +1,553 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 collections +import contextlib +import json +import os +import random +import re +import socket +import telnetlib +import time + +from heatclient import client as heatclient +from keystoneclient import exceptions as ks_exceptions +import keystoneclient.v3 as ksclient +from muranoclient import client as mclient +import muranoclient.common.exceptions as exceptions +from muranoclient.glance import client as glare_client +from oslo_log import log as logging +from tempest import config +import yaml + +import murano_tempest_tests.tests.functional.common.zip_utils_mixin \ + as zip_utils +import murano_tempest_tests.tests.functional.engine.config as cfg + + +CONF = config.CONF + +LOG = logging.getLogger(__name__) + + +SessionState = collections.namedtuple('SessionState', [ + 'OPENED', 'DEPLOYING', 'DEPLOYED', 'DEPLOY_FAILURE', 'DELETING', + 'DELETE_FAILURE' +])( + OPENED='opened', + DEPLOYING='deploying', + DEPLOYED='deployed', + DEPLOY_FAILURE='deploy failure', + DELETING='deleting', + DELETE_FAILURE='delete failure' +) + + +@contextlib.contextmanager +def ignored(*exceptions): + try: + yield + except exceptions: + pass + + +def memoize(f): + """Saves result of decorated function to cache + + Decorator, which saves result of a decorated function + to cache. + TTL for cache is 1800 sec + + :param f: decorated function + :return: saved result of a decorated function + """ + cache = {} + + def decorated_function(*args): + if args in cache: + if time.time() - cache[args][1] < 1800: + return cache[args][0] + else: + cache[args] = (f(*args), time.time()) + return cache[args][0] + else: + cache[args] = (f(*args), time.time()) + return cache[args][0] + + return decorated_function + + +class DeployTestMixin(zip_utils.ZipUtilsMixin): + +# -----------------------------Clients methods--------------------------------- + @staticmethod + @memoize + def keystone_client(): + return ksclient.Client(username=CONF.auth.admin_username, + password=CONF.auth.admin_password, + tenant_name=CONF.auth.admin_project_name, + auth_url=CONF.identity.uri_v3) + + @classmethod + @memoize + def heat_client(cls): + heat_url = cls.keystone_client().service_catalog.url_for( + service_type='orchestration', endpoint_type='publicURL') + return heatclient.Client('1', + endpoint=heat_url, + token=cls.keystone_client().auth_token) + + @classmethod + @memoize + def murano_client(cls): + murano_url = cls.get_murano_url() + if CONF.murano.packages_service == "glare": + glare_endpoint = "http://127.0.0.1:9494" + artifacts_client = glare_client.Client( + endpoint=glare_endpoint, + token=cls.keystone_client().auth_token, + insecure=False, key_file=None, ca_file=None, cert_file=None, + type_name="murano", type_version=1) + else: + artifacts_client = None + return mclient.Client('1', + artifacts_client=artifacts_client, + endpoint=murano_url, + token=cls.keystone_client().auth_token) + +# --------------------------Specific test methods------------------------------ + + @classmethod + def deploy_apps(cls, name, *apps): + """Create and deploy environment. + + :param name: Murano environment name + :param apps: App(s), described in JSON format + :return: Murano environment + """ + environment = cls.murano_client().environments.create({'name': name}) + cls.init_list("_environments") + cls._environments.append(environment) + session = cls.murano_client().sessions.configure(environment.id) + for app in apps: + cls.murano_client().services.post( + environment.id, + path='/', + data=app, + session_id=session.id) + cls.murano_client().sessions.deploy(environment.id, session.id) + return environment + + @classmethod + def wait_for_final_status(cls, environment, timeout=300): + """Function for wait final status of environment. + + :param environment: Murano environment. + :param timeout: Timeout for waiting environment to get any status + excluding DEPLOYING state + """ + start_time = time.time() + status = environment.manager.get(environment.id).status + while SessionState.DEPLOYING == status: + if time.time() - start_time > timeout: + err_msg = ('Deployment not finished in {amount} seconds' + .format(amount=timeout)) + LOG.error(err_msg) + raise RuntimeError(err_msg) + time.sleep(5) + status = environment.manager.get(environment.id).status + dep = cls.murano_client().deployments.list(environment.id) + reports = cls.murano_client().deployments.reports(environment.id, + dep[0].id) + return status, ", ".join([r.text for r in reports]) + +# -----------------------------Reports methods--------------------------------- + + @classmethod + def get_last_deployment(cls, environment): + """Gets last deployment of Murano environment. + + :param environment: Murano environment + :return: + """ + deployments = cls.murano_client().deployments.list(environment.id) + return deployments[0] + + @classmethod + def get_deployment_report(cls, environment, deployment): + """Gets reports for environment with specific deployment. + + :param environment: Murano environment. + :param deployment: Murano deployment for certain environment + :return: + """ + history = '' + report = cls.murano_client().deployments.reports( + environment.id, deployment.id) + for status in report: + history += '\t{0} - {1}\n'.format(status.created, status.text) + return history + + @classmethod + def _log_report(cls, environment): + """Used for logging reports on failures. + + :param environment: Murano environment. + """ + deployment = cls.get_last_deployment(environment) + try: + details = deployment.result['result']['details'] + LOG.warning('Details:\n {details}'.format(details=details)) + except Exception as e: + LOG.error(e) + report = cls.get_deployment_report(environment, deployment) + LOG.debug('Report:\n {report}\n'.format(report=report)) + +# -----------------------------Service methods--------------------------------- + + @classmethod + def add_service(cls, environment, data, session, to_dict=False): + """This function adds a specific service to environment. + + :param environment: Murano environment + :param data: JSON with specific servive to add into + :param session: Session that is open for environment + :param to_dict: If True - returns a JSON object with service + If False - returns a specific class + """ + + LOG.debug('Added service:\n {data}'.format(data=data)) + service = cls.murano_client().services.post(environment.id, + path='/', data=data, + session_id=session.id) + if to_dict: + return cls._convert_service(service) + else: + return service + + @classmethod + def services_list(cls, environment): + """Get a list of environment services. + + :param environment: Murano environment + :return: List of objects + """ + return cls.murano_client().services.list(environment.id) + + @classmethod + def get_service(cls, environment, service_name, to_dict=True): + """Get a service with specific name from environment. + + :param to_dict: Convert service to JSON or not to convert + :param environment: Murano environment + :param service_name: Service name + :return: JSON or object + """ + for service in cls.services_list(environment): + if service.name == service_name: + return cls._convert_service(service) if to_dict else service + + @classmethod + def _convert_service(cls, service): + """Converts a to JSON object. + + :param service: object + :return: JSON object + """ + component = service.to_dict() + component = json.dumps(component) + return yaml.safe_load(component) + + @classmethod + def get_service_id(cls, service): + """Gets id on object. + + :param service: object + :return: ID of the Service + """ + serv = cls._convert_service(service) + serv_id = serv['?']['id'] + return serv_id + + @classmethod + def delete_service(cls, environment, session, service): + """This function removes a specific service from environment. + + :param environment: Murano environment + :param session: Session fir urano environment + :param service: object + :return: Updated murano environment + """ + cls.murano_client().services.delete( + environment.id, path='/{0}'.format(cls.get_service_id(service)), + session_id=session.id) + LOG.debug('Service with name {0} from environment {1} successfully ' + 'removed'.format(environment.name, service.name)) + updated_env = cls.get_environment(environment) + return updated_env + + +# -----------------------------Packages methods-------------------------------- + + @classmethod + def upload_package(cls, package_name, body, app): + """Uploads a .zip package with parameters to Murano. + + :param package_name: Package name in Murano repository + :param body: Categories, tags, etc. + e.g. { + "categories": ["Application Servers"], + "tags": ["tag"] + } + :param app: Correct .zip archive with the application + :return: Package + """ + files = {'{0}'.format(package_name): open(app, 'rb')} + package = cls.murano_client().packages.create(body, files) + cls.init_list("_packages") + cls._packages.append(package) + return package + +# ------------------------------Common methods--------------------------------- + + @classmethod + def rand_name(cls, name='murano'): + """Generates random string. + + :param name: Basic name + :return: + """ + return name + str(random.randint(1, 0x7fffffff)) + + @classmethod + def init_list(cls, list_name): + if not hasattr(cls, list_name): + setattr(cls, list_name, []) + + @classmethod + def get_murano_url(cls): + try: + url = cls.keystone_client().service_catalog.url_for( + service_type='application-catalog', endpoint_type='publicURL') + except ks_exceptions.EndpointNotFound: + url = CONF.murano.murano_url + LOG.warning("Murano endpoint not found in Keystone. " + "Using CONF.") + return url if 'v1' not in url else "/".join( + url.split('/')[:url.split('/').index('v1')]) + + @classmethod + def verify_connection(cls, ip, port): + """Try to connect to specific ip:port with telnet. + + :param ip: Ip that you want to check + :param port: Port that you want to check + :return: :raise RuntimeError: + """ + tn = telnetlib.Telnet(ip, port) + tn.write('GET / HTTP/1.0\n\n') + try: + buf = tn.read_all() + LOG.debug('Data:\n {data}'.format(data=buf)) + if len(buf) != 0: + tn.sock.sendall(telnetlib.IAC + telnetlib.NOP) + return + else: + raise RuntimeError('Resource at {0}:{1} not exist'. + format(ip, port)) + except socket.error as e: + LOG.error('Socket Error: {error}'.format(error=e)) + + @classmethod + def get_ip_by_appname(cls, environment, appname): + """Returns ip of instance with a deployed application using app name. + + :param environment: Murano environment + :param appname: Application name or substring of application name + :return: + """ + for service in environment.services: + if appname in service['name']: + return service['instance']['floatingIpAddress'] + + @classmethod + def get_ip_by_instance_name(cls, environment, inst_name): + """Returns ip of instance using instance name. + + :param environment: Murano environment + :param name: String, which is substring of name of instance or name of + instance + :return: + """ + for service in environment.services: + if inst_name in service['instance']['name']: + return service['instance']['floatingIpAddress'] + + @classmethod + def get_k8s_ip_by_instance_name(cls, environment, inst_name, service_name): + """Returns ip of specific kubernetes node (gateway, master, minion). + + Search depends on service name of kubernetes and names of spawned + instances + :param environment: Murano environment + :param inst_name: Name of instance or substring of instance name + :param service_name: Name of Kube Cluster application in Murano + environment + :return: Ip of Kubernetes instances + """ + for service in environment.services: + if service_name in service['name']: + if "gateway" in inst_name: + for gateway in service['gatewayNodes']: + if inst_name in gateway['instance']['name']: + LOG.debug(gateway['instance']['floatingIpAddress']) + return gateway['instance']['floatingIpAddress'] + elif "master" in inst_name: + LOG.debug(service['masterNode']['instance'][ + 'floatingIpAddress']) + return service['masterNode']['instance'][ + 'floatingIpAddress'] + elif "minion" in inst_name: + for minion in service['minionNodes']: + if inst_name in minion['instance']['name']: + LOG.debug(minion['instance']['floatingIpAddress']) + return minion['instance']['floatingIpAddress'] + +# -----------------------------Cleanup methods--------------------------------- + + @classmethod + def purge_uploaded_packages(cls): + """Cleanup for uploaded packages.""" + cls.init_list("_packages") + try: + for pkg in cls._packages: + with ignored(Exception): + cls.murano_client().packages.delete(pkg.id) + finally: + cls._packages = [] + cls.init_list("_package_files") + try: + for pkg_file in cls._package_files: + os.remove(pkg_file) + finally: + cls._package_files = [] + + @classmethod + def purge_environments(cls): + """Cleanup for created environments.""" + cls.init_list("_environments") + try: + for env in cls._environments: + with ignored(Exception): + LOG.debug('Processing cleanup for environment {0} ({1})'. + format(env.name, env.id)) + cls.environment_delete(env.id) + cls.purge_stacks(env.id) + time.sleep(5) + finally: + cls._environments = [] + + @classmethod + def purge_stacks(cls, environment_id): + stack = cls._get_stack(environment_id) + if not stack: + return + else: + cls.heat_client().stacks.delete(stack.id) + +# -----------------------Methods for environment CRUD-------------------------- + + @classmethod + def create_environment(cls, name=None): + """Creates Murano environment with random name. + + + :param name: Environment name + :return: Murano environment + """ + if not name: + name = cls.rand_name('MuranoTe') + environment = cls.murano_client().environments.create({'name': name}) + cls._environments.append(environment) + return environment + + @classmethod + def get_environment(cls, environment): + """Refresh variable. + + :param environment: Murano environment. + :return: Murano environment. + """ + return cls.murano_client().environments.get(environment.id) + + @classmethod + def environment_delete(cls, environment_id, timeout=180): + """Remove Murano environment. + + :param environment_id: ID of Murano environment + :param timeout: Timeout to environment get deleted + :return: :raise RuntimeError: + """ + try: + cls.murano_client().environments.delete(environment_id) + + start_time = time.time() + while time.time() - start_time < timeout: + try: + cls.murano_client().environments.get(environment_id) + except exceptions.HTTPNotFound: + LOG.debug('Environment with id {0} successfully deleted.'. + format(environment_id)) + return + err_msg = ('Environment {0} was not deleted in {1} seconds'. + format(environment_id, timeout)) + LOG.error(err_msg) + raise RuntimeError(err_msg) + except Exception as exc: + LOG.debug('Environment with id {0} going to be abandoned.'. + format(environment_id)) + LOG.exception(exc) + cls.murano_client().environments.delete(environment_id, + abandon=True) + +# -----------------------Methods for session actions--------------------------- + + @classmethod + def create_session(cls, environment): + return cls.murano_client().sessions.configure(environment.id) + + @classmethod + def delete_session(cls, environment, session): + return cls.murano_client().sessions.delete(environment.id, session.id) + + +# -------------------------------Heat methods---------------------------------- + + @classmethod + def _get_stack(cls, environment_id): + + for stack in cls.heat_client().stacks.list(): + stack_description = ( + cls.heat_client().stacks.get(stack.id).description) + if not stack_description: + err_msg = ("Stack {0} description is empty".format(stack.id)) + LOG.error(err_msg) + raise RuntimeError(err_msg) + if environment_id in stack_description: + return stack + + @classmethod + def get_stack_template(cls, stack): + return cls.heat_client().stacks.template(stack.stack_name) diff --git a/murano_tempest_tests/tests/functional/common/zip_utils_mixin.py b/murano_tempest_tests/tests/functional/common/zip_utils_mixin.py new file mode 100644 index 0000000..464f398 --- /dev/null +++ b/murano_tempest_tests/tests/functional/common/zip_utils_mixin.py @@ -0,0 +1,30 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 zipfile + + +class ZipUtilsMixin(object): + @staticmethod + def zip_dir(parent_dir, dir): + abs_path = os.path.join(parent_dir, dir) + path_len = len(abs_path) + 1 + zip_file = abs_path + ".zip" + with zipfile.ZipFile(zip_file, "w") as zf: + for dir_name, _, files in os.walk(abs_path): + for filename in files: + fn = os.path.join(dir_name, filename) + zf.write(fn, fn[path_len:]) + return zip_file diff --git a/murano_tempest_tests/tests/functional/engine/__init__.py b/murano_tempest_tests/tests/functional/engine/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/murano_tempest_tests/tests/functional/engine/config.conf.sample b/murano_tempest_tests/tests/functional/engine/config.conf.sample new file mode 100644 index 0000000..7bbe847 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/config.conf.sample @@ -0,0 +1,45 @@ +[murano] +# keystone url +# auth_url = http://127.0.0.1:5000/v2.0/ + +# keystone user +# user = admin + +# password for keystone user +# password = admin + +# keystone tenant +# tenant = admin + +# keyname - used for debugging murano-agent +# keyname = keyname + +# murano url +# murano_url = http://127.0.0.1:8082 + +# Flavor for sanity checks +# standard_flavor = m1.medium + +# Flavor for advanced checks +# advanced_flavor = m1.medium + +# image for linux services +# linux_image = default_linux + +# murano instance type +# instance_type = io.murano.resources.LinuxMuranoInstance + +# image for docker applications +# docker_image = debian-8-docker.qcow2 + +# image for kubernetes applications +# kubernetes_image = ubuntu14.04-x64-kubernetes.qcow2 + +# image for windows services +# windows_image = default_windows + +# image for hdp sandbox +# hdp_image = hdp-sandbox + +# region name for services +# region_name = None diff --git a/murano_tempest_tests/tests/functional/engine/config.py b/murano_tempest_tests/tests/functional/engine/config.py new file mode 100644 index 0000000..14a40ec --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/config.py @@ -0,0 +1,88 @@ +# Copyright (c) 2015 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 + +from oslo_config import cfg + + +murano_group = cfg.OptGroup(name='murano', title="murano") + +MuranoGroup = [ + cfg.StrOpt('auth_url', + default='http://127.0.0.1:5000', + help="keystone url"), + cfg.StrOpt('user', + default='admin', + help="keystone user"), + cfg.StrOpt('password', + default='pass', + help="password for keystone user"), + cfg.StrOpt('tenant', + default='admin', + help='keystone tenant'), + cfg.StrOpt('keyname', + default='', + help='name of keypair for debugging'), + cfg.StrOpt('murano_url', + default='http://127.0.0.1:8082/v1/', + help="murano url"), + cfg.StrOpt('standard_flavor', + default='m1.medium', + help="flavor for sanity tests"), + cfg.StrOpt('advanced_flavor', + default='m1.large', + help="flavor for advanced tests"), + cfg.StrOpt('linux_image', + default='default_linux', + help="image for linux services"), + cfg.StrOpt('instance_type', + default='io.murano.resources.LinuxMuranoInstance', + help="murano instance type"), + cfg.StrOpt('docker_image', + default='ubuntu14.04-x64-docker', + help="image for docker applications"), + cfg.StrOpt('windows_image', + default='default_windows', + help="image for windows services"), + cfg.StrOpt('hdp_image', + default="hdp-sandbox", + help="image for hdp-sandbox"), + cfg.StrOpt('kubernetes_image', + default="ubuntu14.04-x64-kubernetes", + help="image for kubernetes"), + cfg.StrOpt('region_name', help="region name for services"), + cfg.StrOpt('packages_service', + default='murano', + help='murano packages service, either "murano" or "glare"') +] + +CONF = cfg.CONF + + +def register_config(config, config_group, config_opts): + + config.register_group(config_group) + config.register_opts(config_opts, config_group) + + +def load_config(): + __location = os.path.realpath(os.path.join(os.getcwd(), + os.path.dirname(__file__))) + path = os.path.join(__location, "config.conf") + + if os.path.exists(path): + CONF([], project='muranointegration', default_config_files=[path]) + + register_config(CONF, murano_group, MuranoGroup) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml new file mode 100644 index 0000000..cd13ff9 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Classes/ApacheHttpServer.yaml @@ -0,0 +1,81 @@ +# 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. + +Namespaces: + =: io.murano.apps.test + std: io.murano + res: io.murano.resources + sys: io.murano.system + conf: io.murano.configuration + + +Name: ApacheHttpServerCustom + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + instance: + Contract: $.class(res:Instance).notNull() + + userName: + Contract: $.string() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM for Apache Server.') + - $securityGroupIngress: + - ToPort: 80 + FromPort: 80 + IpProtocol: tcp + External: true + - ToPort: 443 + FromPort: 443 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $.instance.deploy() + - $._environment.reporter.report($this, 'Instance is created. Deploying Apache') + + - $resources: new(sys:Resources) + - $linux: new(conf:Linux) + + - $linux.runCommand($.instance.agent, 'apt-get -y install apache2') + - $linux.runCommand($.instance.agent, 'iptables -I INPUT 1 -p tcp --dport 443 -j ACCEPT') + - $linux.runCommand($.instance.agent, 'iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT') + - $._environment.reporter.report($this, 'Apache is installed.') + + - If: $.userName != '' + Then: + - $linux.runCommand($.instance.agent, 'service apache2 stop') + - $fileReplacements: + "%USER_NAME%": $.userName + - $fileContent: $resources.string('index.html').replace($fileReplacements) + - $linux.putFile($.instance.agent, $fileContent, '/var/www/html/index.html') + - $linux.runCommand($.instance.agent, 'service apache2 start') + + - If: $.instance.assignFloatingIp + Then: + - $host: $.instance.floatingIpAddress + Else: + - $host: $.instance.ipAddresses[0] + - $._environment.reporter.report($this, format('Apache is available at http://{0}', $host)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/LICENSE b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html new file mode 100644 index 0000000..6843697 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/Resources/index.html @@ -0,0 +1,8 @@ + + + + Hello World + +Hello world. This is my first web page. My name is %USER_NAME%. + + diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml new file mode 100644 index 0000000..b0ab7c3 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.ApacheHttpServerCustom/manifest.yaml @@ -0,0 +1,28 @@ +# 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. + +Format: 1.0 +Type: Application +FullName: io.murano.test.apache.ApacheHttpServerCustom +Name: Apache HTTP Server Custom +Description: | + The Apache HTTP Server Project is an effort to develop and maintain an + open-source HTTP server for modern operating systems including UNIX and + Windows NT. The goal of this project is to provide a secure, efficient and + extensible server that provides HTTP services in sync with the current HTTP + standards. + Apache httpd has been the most popular web server on the Internet since + April 1996, and celebrated its 17th birthday as a project this February. +Author: 'Mirantis, Inc' +Tags: [HTTP, Server, WebServer, HTML, Apache] +Classes: + io.murano.apps.test.ApacheHttpServerCustom: ApacheHttpServer.yaml diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml new file mode 100644 index 0000000..c719f12 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Classes/Lighttpd.yaml @@ -0,0 +1,55 @@ +# 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. + +Namespaces: + =: io.murano.apps.test + std: io.murano + sys: io.murano.system + +Name: Lighttpd + +Extends: std:Application + +Properties: + updater: + Contract: $.class(UpdateExecutor).notNull() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $securityGroupIngress: + - ToPort: 80 + FromPort: 80 + IpProtocol: tcp + External: true + - ToPort: 443 + FromPort: 443 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $._environment.reporter.report($this, 'Ensuring Updater is deployed.') + - $.updater.deploy() + - $resources: new(sys:Resources) + - $template: $resources.yaml('DeployLighttpd.template') + - $.updater.instance.agent.call($template, $resources) + + - If: $.updater.instance.assignFloatingIp + Then: + - $address: $.updater.instance.floatingIpAddress + - $._environment.reporter.report($this, format('Running at http://{0}', $address)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/LICENSE b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template new file mode 100644 index 0000000..7eba0d5 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/DeployLighttpd.template @@ -0,0 +1,27 @@ +# 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. + +FormatVersion: 2.0.0 +Version: 1.0.0 +Name: Deploy Lighttpd + +Body: | + deploy() + +Scripts: + deploy: + Type: Application + Version: 1.0.0 + EntryPoint: deployLighttpd.sh + Options: + captureStdout: true + captureStderr: true diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh new file mode 100644 index 0000000..09dcd46 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/Resources/scripts/deployLighttpd.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# 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. + +sudo apt-get -y -q install lighttpd diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/manifest.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/manifest.yaml new file mode 100644 index 0000000..5bdb902 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.Lighttpd/manifest.yaml @@ -0,0 +1,24 @@ +# 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. + +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.Lighttpd +Name: Lighttpd +Description: | + Lighttpd... :) +Author: 'Mirantis, Inc' +Tags: [Web] +Classes: + io.murano.apps.test.Lighttpd: Lighttpd.yaml +Require: + io.murano.apps.test.UpdateExecutor: diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml new file mode 100644 index 0000000..dcf95ea --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Classes/UpdateExecutor.yaml @@ -0,0 +1,47 @@ +# 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. + +Namespaces: + =: io.murano.apps.test + std: io.murano + res: io.murano.resources + sys: io.murano.system + + +Name: UpdateExecutor + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + instance: + Contract: $.class(res:Instance).notNull() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM.') + - $.instance.deploy() + - $resources: new(sys:Resources) + - $template: $resources.yaml('Update.template') + - $._environment.reporter.report($this, 'Starting packages updating.') + - $.instance.agent.call($template, $resources) + - $._environment.reporter.report($this, 'Update completed.') + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/LICENSE b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/LICENSE new file mode 100644 index 0000000..67db858 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/LICENSE @@ -0,0 +1,175 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/Update.template b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/Update.template new file mode 100644 index 0000000..e1b0132 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/Update.template @@ -0,0 +1,30 @@ +# 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. + +FormatVersion: 2.0.0 +Version: 1.0.0 +Name: Update + +Parameters: + +Body: | + update() + +Scripts: + update: + Type: Application + Version: 1.0.0 + EntryPoint: update.sh + Files: [] + Options: + captureStdout: true + captureStderr: true diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh new file mode 100644 index 0000000..28cddd5 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/Resources/scripts/update.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# 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. + +sudo apt-get update diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/manifest.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/manifest.yaml new file mode 100644 index 0000000..49137b4 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.apps.test.UpdateExecutor/manifest.yaml @@ -0,0 +1,22 @@ +# 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. + +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.UpdateExecutor +Name: Update Executor +Description: | + Test application, which updates packages on VM +Author: 'Mirantis, Inc' +Tags: [application] +Classes: + io.murano.apps.test.UpdateExecutor: UpdateExecutor.yaml diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Classes/ExampleChef.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Classes/ExampleChef.yaml new file mode 100644 index 0000000..97e0fe5 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Classes/ExampleChef.yaml @@ -0,0 +1,48 @@ +Namespaces: + =: io.murano.conflang.chef + std: io.murano + res: io.murano.resources + sys: io.murano.system + + +Name: ExampleChef + +Extends: std:Application + +Properties: + + instance: + Contract: $.class(res:Instance).notNull() + + + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM for Chef example ') + - $securityGroupIngress: + - ToPort: 22 + FromPort: 22 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $.instance.deploy() + - $resources: new(sys:Resources) + # Deploy Chef example + - $template: $resources.yaml('DeployExampleChef.template') + + - $._environment.reporter.report($this, 'Instance is created. Deploying Chef example') + - $.instance.agent.call($template, $resources) + - If: $.instance.assignFloatingIp + Then: + - $host: $.instance.floatingIpAddress + Else: + - $host: $.instance.ipAddresses[0] + - $._environment.reporter.report($this, format('Chef example is installed at {0}', $host)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/DeployExampleChef.template b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/DeployExampleChef.template new file mode 100644 index 0000000..554a166 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/DeployExampleChef.template @@ -0,0 +1,22 @@ +FormatVersion: 2.1.0 +Version: 1.0.0 +Name: Deploy Example Chef + +Parameters: + port: $port + +Body: | + return executeRecipe(args).stdout + +Scripts: + executeRecipe: + Type: Chef + Version: 1.0.0 + EntryPoint: test::install + Files: + - test/recipes/install.rb + - test/metadata.rb + - test/README.rdoc + Options: + captureStdout: true + captureStderr: true diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/README.rdoc b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/README.rdoc new file mode 100644 index 0000000..131c9eb --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/README.rdoc @@ -0,0 +1,7 @@ += DESCRIPTION: + += REQUIREMENTS: + += ATTRIBUTES: + += USAGE: \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/metadata.rb b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/metadata.rb new file mode 100644 index 0000000..1f866d3 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/metadata.rb @@ -0,0 +1,8 @@ +#comment +maintainer "Telefonica I+D" +maintainer_email "henar@tid.es" +name "test" +license "All rights reserved" +description "Default cookbook for testing" +long_description IO.read(File.join(File.dirname(__FILE__), 'README.rdoc')) +version "0.1.0" \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/recipes/install.rb b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/recipes/install.rb new file mode 100644 index 0000000..653968a --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/Resources/scripts/test/recipes/install.rb @@ -0,0 +1,10 @@ +script "install" do + interpreter "bash" + user "root" + cwd "/opt" + code <<-EOH + echo test install + EOH +end + +node.normal['test']['action_test'] = "install" \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/manifest.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/manifest.yaml new file mode 100644 index 0000000..a3a223e --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.chef.ExampleChef/manifest.yaml @@ -0,0 +1,10 @@ +Format: 1.0 +Type: Application +FullName: io.murano.conflang.chef.ExampleChef +Name: ExampleChef +Description: | + Example Chef. +Author: 'TID' +Tags: [Test, Chef] +Classes: + io.murano.conflang.chef.ExampleChef: ExampleChef.yaml diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Classes/ExamplePuppet.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Classes/ExamplePuppet.yaml new file mode 100644 index 0000000..015569e --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Classes/ExamplePuppet.yaml @@ -0,0 +1,46 @@ +Namespaces: + =: io.murano.conflang.puppet + std: io.murano + res: io.murano.resources + sys: io.murano.system + + +Name: ExamplePuppet + +Extends: std:Application + +Properties: + + instance: + Contract: $.class(res:Instance).notNull() + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM for Example Chef example ') + - $securityGroupIngress: + - ToPort: 22 + FromPort: 22 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $.instance.deploy() + - $resources: new(sys:Resources) + # Deploy Puppet example + - $template: $resources.yaml('DeployExamplePuppet.template') + + - $._environment.reporter.report($this, 'Instance is created. Deploying Example Puppet') + - $.instance.agent.call($template, $resources) + - If: $.instance.assignFloatingIp + Then: + - $host: $.instance.floatingIpAddress + Else: + - $host: $.instance.ipAddresses[0] + - $._environment.reporter.report($this, format('Example Puppet is installed at {0}', $host)) + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/DeployExamplePuppet.template b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/DeployExamplePuppet.template new file mode 100644 index 0000000..17119f5 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/DeployExamplePuppet.template @@ -0,0 +1,20 @@ +FormatVersion: 2.1.0 +Version: 1.0.0 +Name: Deploy Example Puppet + +Parameters: + port: $port + +Body: | + return executeRecipe(args).stdout + +Scripts: + executeRecipe: + Type: Puppet + Version: 1.0.0 + EntryPoint: test::install + Files: + - test/manifests/install.pp + Options: + captureStdout: true + captureStderr: true diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/scripts/test/manifests/install.pp b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/scripts/test/manifests/install.pp new file mode 100644 index 0000000..58a3594 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/Resources/scripts/test/manifests/install.pp @@ -0,0 +1,4 @@ +class test::install($version='default_version'){ + + notify {"version: test":} +} diff --git a/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/manifest.yaml b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/manifest.yaml new file mode 100644 index 0000000..cb309d0 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/io.murano.conflang.puppet.ExamplePuppet/manifest.yaml @@ -0,0 +1,10 @@ +Format: 1.0 +Type: Application +FullName: io.murano.conflang.puppet.ExamplePuppet +Name: ExamplePuppet +Description: | + Example Chef +Author: 'TID' +Tags: [Test, Puppet] +Classes: + io.murano.conflang.puppet.ExamplePuppet: ExamplePuppet.yaml diff --git a/murano_tempest_tests/tests/functional/engine/manager.py b/murano_tempest_tests/tests/functional/engine/manager.py new file mode 100644 index 0000000..8960dd0 --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/manager.py @@ -0,0 +1,238 @@ +# Copyright (c) 2015 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 socket +import time +import uuid + +from oslo_log import log as logging +import requests +import testresources +import testtools + +import murano_tempest_tests.tests.functional.common.utils as utils +import murano_tempest_tests.tests.functional.engine.config as cfg + + +CONF = cfg.cfg.CONF + +LOG = logging.getLogger(__name__) + + +class MuranoTestsCore(testtools.TestCase, testtools.testcase.WithAttributes, + testresources.ResourcedTestCase, utils.DeployTestMixin): + """This manager provides access to Murano-api service.""" + @classmethod + def setUpClass(cls): + super(MuranoTestsCore, cls).setUpClass() + + cfg.load_config() + cls._environments = [] + + def tearDown(self): + super(MuranoTestsCore, self).tearDown() + self.purge_environments() + +# --------------------------Specific test methods------------------------------ + + def wait_for_environment_deploy(self, environment): + """Wait for successful deployment of Murano environment. + + Logging deployments and reports of failure. + :param environment: Murano environment + :return: Murano environment + """ + start_time = time.time() + status = environment.manager.get(environment.id).status + while status != 'ready': + status = environment.manager.get(environment.id).status + if time.time() - start_time > 1800: + time.sleep(60) + self._log_report(environment) + self.fail( + 'Environment deployment is not finished in 1200 seconds') + elif status == 'deploy failure': + self._log_report(environment) + time.sleep(60) + self.fail('Environment has incorrect status {0}'. + format(status)) + time.sleep(5) + LOG.debug('Environment {env_name} is ready'.format( + env_name=environment.name)) + return environment.manager.get(environment.id) + + def status_check(self, environment, configurations, kubernetes=False): + """Function which gives opportunity to check any count of instances. + + :param environment: Murano environment + :param configurations: Array of configurations. + :param kubernetes: Used for parsing multiple instances in one service + False by default. + Example: [[instance_name, *ports], [instance_name, *ports]] ... + Example k8s: [[cluster['name'], instance_name, *ports], [...], ...] + """ + for configuration in configurations: + if kubernetes: + service_name = configuration[0] + LOG.debug('Service: {service_name}'.format( + service_name=service_name)) + inst_name = configuration[1] + LOG.debug('Instance: {instance_name}'.format( + instance_name=inst_name)) + ports = configuration[2:] + LOG.debug('Acquired ports: {ports}'.format(ports=ports)) + ip = self.get_k8s_ip_by_instance_name(environment, inst_name, + service_name) + if ip and ports: + for port in ports: + self.check_port_access(ip, port) + self.check_k8s_deployment(ip, port) + else: + self.fail('Instance does not have floating IP') + else: + inst_name = configuration[0] + ports = configuration[1:] + ip = self.get_ip_by_instance_name(environment, inst_name) + if ip and ports: + for port in ports: + self.check_port_access(ip, port) + else: + self.fail('Instance does not have floating IP') + + def deployment_success_check(self, environment, *ports): + """Old style deployment check. + + Checks that environment deployment successfully. Only one instance in + environment for this function is permitted for using this function. + :param environment: Murano environment + :param ports: + """ + deployment = self.murano_client().deployments.list(environment.id)[-1] + + self.assertEqual('success', deployment.state, + 'Deployment status is {0}'.format(deployment.state)) + + ip = environment.services[0]['instance']['floatingIpAddress'] + + if ip: + for port in ports: + self.check_port_access(ip, port) + else: + self.fail('Instance does not have floating IP') + + def check_port_access(self, ip, port): + """Check that ports are opened on specific instances. + + :param ip: Instance's ip address + :param port: Port that you want to check + """ + result = 1 + start_time = time.time() + while time.time() - start_time < 600: + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + result = sock.connect_ex((str(ip), port)) + sock.close() + if result == 0: + break + time.sleep(5) + self.assertEqual(0, result, '%s port is closed on instance' % port) + + def check_k8s_deployment(self, ip, port): + start_time = time.time() + while time.time() - start_time < 600: + try: + LOG.debug('Checking: {ip}:{port}'.format(ip=ip, port=port)) + self.verify_connection(ip, port) + return + except RuntimeError as e: + time.sleep(10) + LOG.debug(e) + self.fail('Containers are not ready') + + def check_path(self, env, path, inst_name=None): + """Check path of deployed application using requests method 'GET'. + + :param env: Murano environment. + :param path: Path to check + Example: wordpress. e.g. function will check http:///wordpress + :param inst_name: If defined, function will search through environment + for instance ip and after check path. + """ + environment = env.manager.get(env.id) + if inst_name: + ip = self.get_ip_by_instance_name(environment, inst_name) + else: + ip = environment.services[0]['instance']['floatingIpAddress'] + resp = requests.get('http://{0}/{1}'.format(ip, path)) + if resp.status_code == 200: + return resp + else: + self.fail("Service path unavailable") + + def deploy_environment(self, environment, session): + self.murano_client().sessions.deploy(environment.id, session.id) + return self.wait_for_environment_deploy(environment) + + def _get_telnet_app(self): + return { + "instance": { + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": str(uuid.uuid4()) + }, + "flavor": self.flavor, + "image": self.linux, + "name": "instance{0}".format(uuid.uuid4().hex[:5]), + }, + "name": "app{0}".format(uuid.uuid4().hex[:5]), + "?": { + "type": "io.murano.apps.linux.Telnet", + "id": str(uuid.uuid4()) + } + } + + def get_test_app(self): + return { + "instance": { + "flavor": self.flavor, + "image": self.linux, + "assignFloatingIp": True, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": str(uuid.uuid4()) + }, + "name": self.rand_name('mrntest') + }, + "name": self.rand_name('dummy'), + "?": { + "type": "io.murano.apps.test.UpdateExecutor", + "id": str(uuid.uuid4()) + } + } + + @classmethod + def upload_app(cls, app_dir, name, tags): + """Zip and upload application to Murano + + :param app_dir: Unzipped dir with an application + :param name: Application name + :param tags: Application tags + :return: Uploaded package + """ + zip_file_path = cls.zip_dir(os.path.dirname(__file__), app_dir) + cls.init_list("_package_files") + cls._package_files.append(zip_file_path) + return cls.upload_package( + name, tags, zip_file_path) diff --git a/murano_tempest_tests/tests/functional/engine/test_conf_language_support.py b/murano_tempest_tests/tests/functional/engine/test_conf_language_support.py new file mode 100644 index 0000000..742ec8e --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/test_conf_language_support.py @@ -0,0 +1,87 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 uuid + +import murano_tempest_tests.tests.functional.common.utils as common_utils +import murano_tempest_tests.tests.functional.engine.manager as core +from nose.plugins.attrib import attr as tag + + +class LanguageSupportTest(core.MuranoTestsCore): + + @classmethod + def setUpClass(cls): + super(LanguageSupportTest, cls).setUpClass() + cls.linux = core.CONF.murano.linux_image + cls.flavor = core.CONF.murano.standard_flavor + cls.keyname = core.CONF.murano.keyname + cls.instance_type = core.CONF.murano.instance_type + + try: + # Upload the Murano test package. + cls.upload_app('io.murano.conflang.chef.ExampleChef', + 'ExampleChef', {"tags": ["tag"]}) + cls.upload_app('io.murano.conflang.puppet.ExamplePuppet', + 'ExamplePuppet', {"tags": ["tag"]}) + except Exception: + cls.tearDownClass() + raise + + @classmethod + def tearDownClass(cls): + with common_utils.ignored(Exception): + try: + cls.purge_uploaded_packages() + except Exception: + raise + + def _test_deploy(self, environment_name, package_name, port): + post_body = { + "instance": { + "flavor": self.flavor, + "image": self.linux, + "keyname": self.keyname, + "assignFloatingIp": True, + 'name': environment_name, + "?": { + "type": "io.murano.resources.ConfLangInstance", + "id": str(uuid.uuid4()) + }, + }, + "name": environment_name, + "port": port, + "?": { + "type": package_name, + "id": str(uuid.uuid4()) + } + } + + environment_name = environment_name + uuid.uuid4().hex[:5] + environment = self.create_environment(name=environment_name) + session = self.create_session(environment) + self.add_service(environment, post_body, session) + self.deploy_environment(environment, session) + self.wait_for_environment_deploy(environment) + self.deployment_success_check(environment, port) + + @tag('gate', 'all', 'coverage') + def test_deploy_example_chef_example(self): + self._test_deploy('chefExample', + 'io.murano.conflang.chef.ExampleChef', 22) + + @tag('gate', 'all', 'coverage') + def test_deploy_example_puppet_example(self): + self._test_deploy('puppetExample', + "io.murano.conflang.puppet.ExamplePuppet", 22) diff --git a/murano_tempest_tests/tests/functional/engine/test_deployment.py b/murano_tempest_tests/tests/functional/engine/test_deployment.py new file mode 100644 index 0000000..18112ed --- /dev/null +++ b/murano_tempest_tests/tests/functional/engine/test_deployment.py @@ -0,0 +1,138 @@ +# Copyright (c) 2015 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 uuid + +from nose.plugins.attrib import attr as tag + +import murano_tempest_tests.tests.functional.engine.manager as core + + +class MuranoDeploymentTest(core.MuranoTestsCore): + + @classmethod + def setUpClass(cls): + super(MuranoDeploymentTest, cls).setUpClass() + + cls.linux = core.CONF.murano.linux_image + cls.flavor = core.CONF.murano.standard_flavor + + cls.upload_app('io.murano.apps.test.UpdateExecutor', + 'UpdateExecutor', + {"categories": ["Web"], "tags": ["tag"]}) + + cls.upload_app('io.murano.apps.test.Lighttpd', + 'Lighttpd', + {"categories": ["Web"], "tags": ["tag"]}) + + cls.upload_app('io.murano.apps.test.ApacheHttpServerCustom', + 'Apache HTTP Server Custom', + {"categories": ["Web"], "tags": ["test"]}) + + @classmethod + def tearDownClass(cls): + super(MuranoDeploymentTest, cls).tearDownClass() + + cls.purge_environments() + cls.purge_uploaded_packages() + + @tag('gate', 'all', 'coverage') + def test_app_deployment(self): + post_body = self.get_test_app() + environment_name = self.rand_name('dummyMurano') + environment = self.create_environment(name=environment_name) + session = self.create_session(environment) + self.add_service(environment, post_body, session) + self.deploy_environment(environment, session) + + @tag('gate', 'all', 'coverage') + def test_resources_deallocation(self): + app_1 = self.get_test_app() + app_2 = self.get_test_app() + environment_name = self.rand_name('dummyMurano') + environment = self.create_environment(name=environment_name) + session = self.create_session(environment) + self.add_service(environment, app_1, session) + self.add_service(environment, app_2, session) + self.deploy_environment(environment, session) + + environment = self.get_environment(environment) + app_for_remove = self.get_service(environment, app_1['name'], + to_dict=False) + session = self.create_session(environment) + environment = self.delete_service(environment, session, app_for_remove) + self.deploy_environment(environment, session) + + instance_name = app_1['instance']['name'] + stack = self._get_stack(environment.id) + template = self.get_stack_template(stack) + ip_addresses = '{0}-assigned-ip'.format(instance_name) + floating_ip = '{0}-FloatingIPaddress'.format(instance_name) + + self.assertNotIn(ip_addresses, template['outputs']) + self.assertNotIn(floating_ip, template['outputs']) + self.assertNotIn(instance_name, template['resources']) + + @tag('gate', 'all', 'coverage') + def test_dependent_apps(self): + post_body = self.get_test_app() + environment_name = self.rand_name('dummyMurano') + environment = self.create_environment(name=environment_name) + session = self.create_session(environment) + updater = self.add_service(environment, post_body, session, + to_dict=True) + post_body = { + "name": self.rand_name("lighttest"), + "updater": updater, + "?": { + "type": "io.murano.apps.test.Lighttpd", + "id": str(uuid.uuid4()) + } + } + self.add_service(environment, post_body, session) + self.deploy_environment(environment, session) + self.status_check(environment, + [[updater['instance']['name'], 22, 80]]) + + @tag('gate', 'all', 'coverage') + def test_simple_software_configuration(self): + post_body = { + "instance": { + "flavor": self.flavor, + "image": self.linux, + "assignFloatingIp": True, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": str(uuid.uuid4()) + }, + "name": self.rand_name("mrn-test"), + }, + "name": self.rand_name("ssc-test"), + "userName": self.rand_name("user"), + "?": { + "type": "io.murano.apps.test.ApacheHttpServerCustom", + "id": str(uuid.uuid4()) + } + } + username = post_body["userName"] + environment_name = self.rand_name('SSC-murano') + environment = self.create_environment(name=environment_name) + session = self.create_session(environment) + self.add_service(environment, post_body, session, to_dict=True) + self.deploy_environment(environment, session) + self.status_check(environment, + [[post_body['instance']['name'], 22, 80]]) + resp = self.check_path(environment, '', post_body['instance']['name']) + self.assertIn(username, resp.text, "Required information not found in " + "response from server") diff --git a/murano_tempest_tests/tests/functional/integration/__init__.py b/murano_tempest_tests/tests/functional/integration/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/murano_tempest_tests/tests/functional/integration/integration_base.py b/murano_tempest_tests/tests/functional/integration/integration_base.py new file mode 100644 index 0000000..7f5a0df --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/integration_base.py @@ -0,0 +1,157 @@ +# Copyright (c) 2015 OpenStack Foundation, 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 uuid + +from keystoneclient import exceptions as keystone_exceptions +import mistralclient.api.client as mistralclient +import testresources +import testtools + +import murano_tempest_tests.tests.functional.common.tempest_utils \ + as tempest_utils +import murano_tempest_tests.tests.functional.common.utils as utils + + +class MistralIntegration(testtools.TestCase, testtools.testcase.WithAttributes, + testresources.ResourcedTestCase, + tempest_utils.TempestDeployTestMixin): + + @classmethod + @utils.memoize + def mistral_client(cls): + keystone_client = cls.keystone_client() + + endpoint_type = 'publicURL' + service_type = 'workflowv2' + + mistral_url = keystone_client.service_catalog.url_for( + service_type=service_type, + endpoint_type=endpoint_type) + + auth_token = keystone_client.auth_token + + return mistralclient.client(mistral_url=mistral_url, + auth_url=keystone_client.auth_url, + project_id=keystone_client.tenant_id, + endpoint_type=endpoint_type, + service_type=service_type, + auth_token=auth_token, + user_id=keystone_client.user_id) + + @classmethod + def upload_mistral_showcase_app(cls): + app_dir = 'io.murano.apps.test.MistralShowcaseApp' + zip_file_path = cls.zip_dir(os.path.dirname(__file__), app_dir) + cls.init_list("_package_files") + cls._package_files.append(zip_file_path) + return cls.upload_package( + 'MistralShowcaseApp', + {"categories": ["Web"], "tags": ["tag"]}, + zip_file_path) + + @staticmethod + def _create_env_body(): + return { + "name": "Mistral_environment", + "?": { + "type": "io.murano.apps.test.MistralShowcaseApp", + "id": str(uuid.uuid4()) + } + } + + +class CongressIntegration(testtools.TestCase, + testtools.testcase.WithAttributes, + testresources.ResourcedTestCase, + tempest_utils.TempestDeployTestMixin): + + @classmethod + def _create_policy_req(cls, policy_name): + return {'abbreviation': None, 'kind': None, + 'name': policy_name, + 'description': None} + + @classmethod + def _upload_policy_enf_app(cls): + app_dir = 'io.murano.apps.test.PolicyEnforcementTestApp' + zip_file_path = cls.zip_dir(os.path.dirname(__file__), app_dir) + cls.init_list("_package_files") + cls._package_files.append(zip_file_path) + return cls.upload_package( + 'PolicyEnforcementTestApp', + {"categories": ["Web"], "tags": ["tag"]}, + zip_file_path) + + @classmethod + def _create_policy(cls, policy_names, kind=None): + for name in policy_names: + policy_req = {"name": name} + if kind: + policy_req["kind"] = kind + with utils.ignored(keystone_exceptions.Conflict): + cls.congress_client().create_policy(policy_req) + + rules = [] + rules_file = os.path.join( + os.path.dirname(__file__), + "rules_" + name + ".txt") + + if os.path.isfile(rules_file): + with open(rules_file) as f: + rules = [rule.strip() for rule in f.readlines() + if rule.strip()] + for rule in rules: + with utils.ignored(keystone_exceptions.Conflict): + cls.congress_client().create_policy_rule(name, + {'rule': rule}) + + def _create_test_app(self, flavor, key): + """Application create request body + + Deployment is expected to fail earlier due to policy violation. + Not existing image prevents real deployment to happen + in case that test goes wrong way. + + :param flavor: instance image flavor + :param key: key name + """ + + return { + "instance": { + "flavor": flavor, + "keyname": key, + "image": "not_existing_image", + "assignFloatingIp": True, + "?": { + "type": "io.murano.resources.LinuxMuranoInstance", + "id": str(uuid.uuid4()) + }, + "name": "testMurano" + }, + "name": "teMurano", + "?": { + "type": "io.murano.apps.test.PolicyEnforcementTestApp", + "id": str(uuid.uuid4()) + } + } + + def _check_deploy_failure(self, post_body, expected_text): + environment_name = 'PolicyEnfTestEnv' + uuid.uuid4().hex[:5] + env = self.deploy_apps(environment_name, post_body) + status = self.wait_for_final_status(env) + self.assertIn("failure", status[0], "Unexpected status : " + status[0]) + self.assertIn(expected_text, status[1].lower(), + "Unexpected status : " + status[1]) diff --git a/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Classes/MistralShowcaseApp.yaml b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Classes/MistralShowcaseApp.yaml new file mode 100644 index 0000000..cfe4a07 --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Classes/MistralShowcaseApp.yaml @@ -0,0 +1,32 @@ +Namespaces: + =: io.murano.apps.test + std: io.murano + sys: io.murano.system + + +Name: MistralShowcaseApp + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + mistralClient: + Contract: $.class(sys:MistralClient) + Usage: Runtime + + +Methods: + initialize: + Body: + - $environment: $.find(std:Environment).require() + - $this.mistralClient: new(sys:MistralClient, $environment) + + deploy: + Body: + - $resources: new('io.murano.system.Resources') + - $workflow: $resources.string('TestEcho_MistralWorkflow.yaml') + - $.mistralClient.upload(definition => $workflow) + - $output: $.mistralClient.run(name => 'test_echo', inputs => dict(input_1 => input_1_value)) + - $this.find(std:Environment).reporter.report($this, $output.get('out_3')) \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Resources/TestEcho_MistralWorkflow.yaml b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Resources/TestEcho_MistralWorkflow.yaml new file mode 100644 index 0000000..b09959b --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/Resources/TestEcho_MistralWorkflow.yaml @@ -0,0 +1,24 @@ +version: '2.0' + +test_echo: + type: direct + input: + - input_1 + output: + out_1: <% $.task1_output_1 %> + out_2: <% $.task2_output_2 %> + out_3: <% $.input_1 %> + tasks: + my_echo_test: + action: std.echo output='just a string' + publish: + task1_output_1: 'task1_output_1_value' + task1_output_2: 'task1_output_2_value' + on-success: + - my_echo_test_2 + + my_echo_test_2: + action: std.echo output='just a string' + publish: + task2_output_1: 'task2_output_1_value' + task2_output_2: 'task2_output_2_value' \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/manifest.yaml b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/manifest.yaml new file mode 100644 index 0000000..3eb290a --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.MistralShowcaseApp/manifest.yaml @@ -0,0 +1,10 @@ +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.MistralShowcaseApp +Name: MistralShowcaseApp +Description: | + MistralShowcaseApp. +Author: 'Mirantis, Inc' +Tags: [Servlets, Server, Pages, Java] +Classes: + io.murano.apps.test.MistralShowcaseApp: MistralShowcaseApp.yaml diff --git a/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/Classes/PolicyEnforcementTestApp.yaml b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/Classes/PolicyEnforcementTestApp.yaml new file mode 100644 index 0000000..c08c38b --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/Classes/PolicyEnforcementTestApp.yaml @@ -0,0 +1,48 @@ +Namespaces: + =: io.murano.apps.test + std: io.murano + res: io.murano.resources + sys: io.murano.system + + +Name: PolicyEnforcementTestApp + +Extends: std:Application + +Properties: + name: + Contract: $.string().notNull() + + instance: + Contract: $.class(res:Instance).notNull() + + host: + Contract: $.string() + Usage: Out + + user: + Contract: $.string() + Usage: Out + +Methods: + initialize: + Body: + - $._environment: $.find(std:Environment).require() + + deploy: + Body: + - If: not $.getAttr(deployed, false) + Then: + - $._environment.reporter.report($this, 'Creating VM') + - $securityGroupIngress: + - ToPort: 22 + FromPort: 22 + IpProtocol: tcp + External: true + - $._environment.securityGroupManager.addGroupIngress($securityGroupIngress) + - $.instance.deploy() + - $resources: new(sys:Resources) + - $._environment.reporter.report($this, 'Test VM is installed') + - $.host: $.instance.ipAddresses[0] + - $.user: 'root' + - $.setAttr(deployed, true) diff --git a/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/manifest.yaml b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/manifest.yaml new file mode 100644 index 0000000..5eac9c6 --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/io.murano.apps.test.PolicyEnforcementTestApp/manifest.yaml @@ -0,0 +1,10 @@ +Format: 1.0 +Type: Application +FullName: io.murano.apps.test.PolicyEnforcementTestApp +Name: PolicyEnforcementTestApp +Description: | + This is a simple test app with a single VM for policy enforcement testing purposes. +Author: 'Hewlett-Packard' +Tags: [test] +Classes: + io.murano.apps.test.PolicyEnforcementTestApp: PolicyEnforcementTestApp.yaml diff --git a/murano_tempest_tests/tests/functional/integration/rules_murano_action.txt b/murano_tempest_tests/tests/functional/integration/rules_murano_action.txt new file mode 100644 index 0000000..442c277 --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/rules_murano_action.txt @@ -0,0 +1,18 @@ +action("deleteEnv") + +murano:states-(eid,st) :- deleteEnv(eid), murano:states( eid, st) + +murano:parent_types-(tid, type) :- deleteEnv(eid), murano:connected(eid, tid),murano:parent_types(tid,type) +murano:parent_types-(eid, type) :- deleteEnv(eid), murano:parent_types(eid,type) + +murano:properties-(oid, pn, pv) :- deleteEnv(eid), murano:connected( eid, oid),murano:properties(oid, pn, pv) +murano:properties-(eid, pn, pv) :- deleteEnv(eid), murano:properties(eid, pn, pv) + +murano:objects-(oid, pid, ot) :- deleteEnv(eid), murano:connected(eid, oid), murano:objects(oid, pid, ot) +murano:objects-(eid, tnid, ot) :- deleteEnv(eid), murano:objects(eid, tnid, ot) + +murano:relationships-(sid,tid, rt) :- deleteEnv(eid), murano:connected( eid, sid), murano:relationships( sid, tid, rt) +murano:relationships-(eid,tid, rt) :- deleteEnv(eid), murano:relationships( eid, tid, rt) + +murano:connected-(tid, tid2) :- deleteEnv(eid), murano:connected(eid, tid), murano:connected(tid,tid2) +murano:connected-(eid,tid) :- deleteEnv(eid), murano:connected(eid,tid) \ No newline at end of file diff --git a/murano_tempest_tests/tests/functional/integration/rules_murano_system.txt b/murano_tempest_tests/tests/functional/integration/rules_murano_system.txt new file mode 100644 index 0000000..640a62e --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/rules_murano_system.txt @@ -0,0 +1,7 @@ +missing_key("") +invalid_flavor_name("really.bad.flavor") +predeploy_errors(eid, obj_id, msg):-murano:objects(obj_id, pid, type), murano_env_of_object(obj_id, eid), murano:properties(obj_id, "flavor", flavor_name), invalid_flavor_name(flavor_name), murano:properties(obj_id, "name", obj_name), concat(obj_name, ": bad flavor", msg) +predeploy_errors(eid, obj_id, msg):-murano:objects(obj_id, pid, type), murano_env_of_object(obj_id, eid), murano:properties(obj_id, "keyname", key_name), missing_key(key_name), murano:properties(obj_id, "name", obj_name), concat(obj_name, ": missing key", msg) +murano_env_of_object(oid,eid):-murano:connected(eid,oid), murano:objects(eid,tid,"io.murano.Environment") +bad_flavor_synonyms("horrible.flavor") +predeploy_modify(eid, obj_id, action):-murano:objects(obj_id, pid, type), murano_env_of_object(obj_id, eid), murano:properties(obj_id, "flavor", flavor_name), bad_flavor_synonyms(flavor_name), concat("set-property: {object_id: ", obj_id, first_part ), concat(first_part, ", prop_name: flavor, value: really.bad.flavor}", action) diff --git a/murano_tempest_tests/tests/functional/integration/test_mistral.py b/murano_tempest_tests/tests/functional/integration/test_mistral.py new file mode 100644 index 0000000..b855a25 --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/test_mistral.py @@ -0,0 +1,62 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 uuid + +from nose.plugins.attrib import attr as tag + +import murano_tempest_tests.tests.functional.common.utils as common_utils +import murano_tempest_tests.tests.functional.integration.integration_base \ + as core + + +class MistralTest(core.MistralIntegration): + + @classmethod + def setUpClass(cls): + super(MistralTest, cls).setUpClass() + + try: + # Upload the Murano test package. + cls.upload_mistral_showcase_app() + + except Exception: + cls.tearDownClass() + raise + + @classmethod + def tearDownClass(cls): + with common_utils.ignored(Exception): + cls.purge_environments() + with common_utils.ignored(Exception): + cls.purge_uploaded_packages() + + @tag('all', 'coverage') + def test_deploy_package_success(self): + # Test expects successful deployment and one output: input_1_value. + + # Create env json string. + post_body = self._create_env_body() + + environment_name = 'Mistral_environment' + uuid.uuid4().hex[:5] + + # Deploy the environment. + env = self.deploy_apps(environment_name, post_body) + + status = self.wait_for_final_status(env) + + self.assertIn("ready", status[0], + "Unexpected status : " + status[0]) + self.assertIn("input_1_value", status[1], + "Unexpected output value: " + status[1]) diff --git a/murano_tempest_tests/tests/functional/integration/test_policy_enf.py b/murano_tempest_tests/tests/functional/integration/test_policy_enf.py new file mode 100644 index 0000000..dbcfde4 --- /dev/null +++ b/murano_tempest_tests/tests/functional/integration/test_policy_enf.py @@ -0,0 +1,89 @@ +# Copyright (c) 2015 OpenStack Foundation +# +# 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 muranoclient.common.exceptions as murano_exceptions +from nose.plugins.attrib import attr as tag + +import murano_tempest_tests.tests.functional.common.utils as common_utils +import murano_tempest_tests.tests.functional.integration.integration_base \ + as core + + +class PolicyEnforcementTest(core.CongressIntegration): + + @classmethod + def setUpClass(cls): + super(PolicyEnforcementTest, cls).setUpClass() + + cls._create_policy(["murano", "murano_system"]) + cls._create_policy(["murano_action"], kind="action") + + with common_utils.ignored(murano_exceptions.HTTPInternalServerError): + cls._upload_policy_enf_app() + + @classmethod + def tearDownClass(cls): + cls.purge_uploaded_packages() + + def tearDown(self): + super(PolicyEnforcementTest, self).tearDown() + self.purge_environments() + + @tag('all', 'coverage') + def test_deploy_policy_fail_key(self): + """Test expects failure due to empty key name. + + In rules_murano_system.txt file are defined congress + rules preventing deploy environment where instances + have empty keyname property. In other words admin + prevented spawn instance without assigned key pair. + """ + + self._check_deploy_failure( + self._create_test_app(key='', + flavor='m1.small'), + 'missing key') + + @tag('all', 'coverage') + def test_deploy_policy_fail_flavor(self): + """Test expects failure due to blacklisted flavor + + In rules_murano_system.txt file are defined congress + rules preventing deploy environment where instances + have flavor property set to 'really.bad.flavor'. + """ + + self._check_deploy_failure( + self._create_test_app(flavor='really.bad.flavor', + key='test-key'), + 'bad flavor') + + @tag('all', 'coverage') + def test_set_property_policy(self): + """Tests environment modification by policy + + In rules_murano_system.txt file are defined congress + rules changing flavor property. There are defined + synonyms for 'really.bad.flavor'. One of such synonyms + is 'horrible.flavor' Environment is modified prior deployment. + The synonym name 'horrible.flavor' is set to original + value 'really.bad.flavor' and then deployment is aborted + because instances of 'really.bad.flavor' are prevented + to be deployed like for the test above. + """ + + self._check_deploy_failure( + self._create_test_app(key="test-key", + flavor="horrible.flavor"), + "bad flavor") diff --git a/requirements.txt b/requirements.txt index 5595dd1..662777d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,3 +10,12 @@ oslo.utils>=3.33.0 # Apache-2.0 testtools>=2.2.0 # MIT tempest>=17.1.0 # Apache-2.0 requests>=2.14.2 # Apache-2.0 +nose>=1.3.7 # LGPL +testresources>=2.0.0 # Apache-2.0/BSD + +python-keystoneclient>=3.8.0 # Apache-2.0 +python-heatclient>=1.10.0 # Apache-2.0 +python-neutronclient>=6.7.0 # Apache-2.0 +python-muranoclient>=0.8.2 # Apache-2.0 +python-congressclient<2000,>=1.9.0 # Apache-2.0 +python-mistralclient!=3.2.0,>=3.1.0 # Apache-2.0