diff --git a/doc/source/specification/murano-env-temp.rst b/doc/source/specification/murano-env-temp.rst
index c3fe4959..0277e6e5 100644
--- a/doc/source/specification/murano-env-temp.rst
+++ b/doc/source/specification/murano-env-temp.rst
@@ -65,6 +65,13 @@ List Environments Templates
| | | environment templates |
+----------+----------------------------------+----------------------------------+
+*Parameters:*
+
+* `is_public` - boolean, indicates whether public environment templates are listed or not.
+ *True* public environments templates from all tenants are listed.
+ *False* private environments templates from current tenant are listed
+ *empty* all tenant templates plus public templates from all tenants are listed
+
*Response*
This call returns a list of environment templates. Only the basic properties are
@@ -81,6 +88,7 @@ returned.
"created": "2014-05-14T13:02:46",
"tenant_id": "726ed856965f43cc8e565bc991fa76c3",
"version": 0,
+ "is_public": false,
"id": "2fa5ab704749444bbeafe7991b412c33"
},
{
@@ -88,8 +96,9 @@ returned.
"networking": {},
"name": "test2",
"created": "2014-05-14T13:02:51",
- "tenant_id": "726ed856965f43cc8e565bc991fa76c3",
+ "tenant_id": "123452452345346345634563456345346",
"version": 0,
+ "is_public": true,
"id": "744e44812da84e858946f5d817de4f72"
}
]
@@ -414,3 +423,58 @@ Create an environment from an environment template
+----------------+-----------------------------------------------------------+
| 409 | The environment already exists |
+----------------+-----------------------------------------------------------+
+
+
+**POST /templates/{env-temp-id}/clone**
+
+*Request*
+
++----------+--------------------------------+-------------------------------------------------+
+| Method | URI | Description |
++==========+================================+=================================================+
+| POST | /templates/{env-temp-id}/clone | It clones a public template from one tenant |
+| | | to another |
++----------+--------------------------------+-------------------------------------------------+
+
+*Parameters:*
+
+* `env-temp-id` - environment template ID, required
+
+*Example Payload*
+::
+
+ {
+ 'name': 'cloned_env_template_name'
+ }
+
+*Content-Type*
+ application/json
+
+*Response*
+
+::
+
+ {
+ "updated": "2015-01-26T09:12:51",
+ "name": "cloned_env_template_name",
+ "created": "2015-01-26T09:12:51",
+ "tenant_id": "00000000000000000000000000000001",
+ "version": 0,
+ "is_public": False,
+ "id": "aa9033ca7ce245fca10e38e1c8c4bbf7",
+ }
+
++----------------+-----------------------------------------------------------+
+| Code | Description |
++================+===========================================================+
+| 200 | OK. Environment Template cloned successfully |
++----------------+-----------------------------------------------------------+
+| 401 | User is not authorized to access this session |
++----------------+-----------------------------------------------------------+
+| 403 | User has no access to these resources |
++----------------+-----------------------------------------------------------+
+| 404 | The environment template does not exist |
++----------------+-----------------------------------------------------------+
+| 409 | Conflict. The environment template name already exists |
++----------------+-----------------------------------------------------------+
+
diff --git a/murano/api/v1/router.py b/murano/api/v1/router.py
index 5479b6fd..cce2315d 100644
--- a/murano/api/v1/router.py
+++ b/murano/api/v1/router.py
@@ -121,6 +121,10 @@ class API(wsgi.Router):
controller=templates_resource,
action='create_environment',
conditions={'method': ['POST']})
+ mapper.connect('/templates/{env_template_id}/clone',
+ controller=templates_resource,
+ action='clone',
+ conditions={'method': ['POST']})
applications_resource = template_applications.create_resource()
mapper.connect('/templates/{env_template_id}/services',
diff --git a/murano/api/v1/templates.py b/murano/api/v1/templates.py
index 99087c3e..4fc1c7a1 100644
--- a/murano/api/v1/templates.py
+++ b/murano/api/v1/templates.py
@@ -17,10 +17,11 @@ from oslo_log import log as logging
from webob import exc
from murano.api.v1 import request_statistics
-from murano.common.i18n import _
+from murano.common.i18n import _, _LE
from murano.common import policy
from murano.common import utils
from murano.common import wsgi
+from murano.db.models import EnvironmentTemplate
from murano.db.services import core_services
from murano.db.services import environment_templates as env_temps
from murano.db.services import environments as envs
@@ -40,12 +41,27 @@ class Controller(object):
"""
LOG.debug('EnvTemplates:List')
policy.check('list_env_templates', request.context)
+ tenant_id = request.context.tenant
+ filters = {}
+ if request.GET.get('is_public'):
+ is_public = request.GET.get('is_public', 'false').lower() == 'true'
+ if not is_public:
+
+ filters['is_public'] = False
+ filters = {'tenant_id': tenant_id}
+ elif is_public:
+ filters['is_public'] = True
+
+ list_templates = env_temps.EnvTemplateServices.\
+ get_env_templates_by(filters)
+
+ else:
+ filters = (EnvironmentTemplate.is_public is True,
+ EnvironmentTemplate.tenant_id == tenant_id)
+ list_templates = env_temps.EnvTemplateServices.\
+ get_env_templates_or_by(filters)
- filters = {'tenant_id': request.context.tenant}
- list_templates = env_temps.EnvTemplateServices.\
- get_env_templates_by(filters)
list_templates = [temp.to_dict() for temp in list_templates]
-
return {"templates": list_templates}
@request_statistics.stats_count(API_NAME, 'Create')
@@ -59,24 +75,11 @@ class Controller(object):
"""
LOG.debug('EnvTemplates:Create
'.format(body=body))
policy.check('create_env_template', request.context)
+
+ self._validate_body_name(body)
try:
- LOG.debug('ENV TEMP NAME: {templ_name}>'.format(
- templ_name=body['name']))
- if not str(body['name']).strip():
- msg = _('Environment Template must contain at least one '
- 'non-white space symbol')
- LOG.error(msg)
- raise exc.HTTPBadRequest(msg)
- except Exception:
- msg = _('Env template body is incorrect')
- LOG.exception(msg)
- raise exc.HTTPClientError(msg)
- if len(body['name']) > 255:
- msg = _('Environment Template name should be 255 characters '
- 'maximum')
- LOG.exception(msg)
- raise exc.HTTPBadRequest(explanation=msg)
- try:
+ LOG.debug('ENV TEMP NAME: {templ_name}>'.
+ format(templ_name=body['name']))
template = env_temps.EnvTemplateServices.create(
body.copy(), request.context.tenant)
return template.to_dict()
@@ -172,23 +175,15 @@ class Controller(object):
:param body: the environment name
:return: session_id and environment_id
"""
- LOG.debug('Templates:Create environment '.
- format(templ_id=env_template_id))
target = {"env_template_id": env_template_id}
policy.check('create_environment', request.context, target)
self._validate_request(request, env_template_id)
+ LOG.debug('Templates:Create environment '.
+ format(templ_id=env_template_id))
template = env_temps.EnvTemplateServices.\
get_env_template(env_template_id)
-
- if ('name' not in body or not str(body['name']).strip()):
- msg = _('Environment Template must contain at least one '
- 'non-white space symbol')
- LOG.error(msg)
- raise exc.HTTPBadRequest(explanation=msg)
- LOG.debug('ENVIRONMENT NAME: {env_name}>'.format(
- env_name=body['name']))
-
+ self._validate_body_name(body)
try:
environment = envs.EnvironmentServices.create(
body.copy(), request.context)
@@ -214,19 +209,75 @@ class Controller(object):
)
return {"session_id": session.id, "environment_id": environment.id}
+ @request_statistics.stats_count(API_NAME, 'Clone')
+ def clone(self, request, env_template_id, body):
+ """It clones the env template from another env template
+ from other tenant.
+ :param request: the operation request.
+ :param env_template_id: the env template ID.
+ :param body: the request body.
+ :return: the description of the created template.
+ """
+
+ LOG.debug('EnvTemplates:Clone '.
+ format(body, env_template_id))
+ policy.check('clone_env_template', request.context)
+
+ old_env_template = self._validate_exists(env_template_id)
+
+ if not old_env_template.get('is_public'):
+ msg = _LE('User has no access to these resources.')
+ LOG.error(msg)
+ raise exc.HTTPForbidden(explanation=msg)
+ self._validate_body_name(body)
+ LOG.debug('ENV TEMP NAME: {0}'.format(body['name']))
+
+ try:
+ is_public = body.get('is_public', False)
+ template = env_temps.EnvTemplateServices.clone(
+ env_template_id, request.context.tenant, body['name'],
+ is_public)
+ except db_exc.DBDuplicateEntry:
+ msg = _('Environment with specified name already exists')
+ LOG.error(msg)
+ raise exc.HTTPConflict(explanation=msg)
+
+ return template.to_dict()
+
def _validate_request(self, request, env_template_id):
+ self._validate_exists(env_template_id)
+ get_env_template = env_temps.EnvTemplateServices.get_env_template
+ env_template = get_env_template(env_template_id)
+ if env_template.is_public or request.context.is_admin:
+ return
+ if env_template.tenant_id != request.context.tenant:
+ msg = _LE('User has no access to these resources.')
+ LOG.error(msg)
+ raise exc.HTTPForbidden(explanation=msg)
+
+ def _validate_exists(self, env_template_id):
env_template_exists = env_temps.EnvTemplateServices.env_template_exist
if not env_template_exists(env_template_id):
msg = _('EnvTemplate is not found').format(
temp_id=env_template_id)
- LOG.exception(msg)
+ LOG.error(msg)
raise exc.HTTPNotFound(explanation=msg)
get_env_template = env_temps.EnvTemplateServices.get_env_template
- env_template = get_env_template(env_template_id)
- if env_template.tenant_id != request.context.tenant:
- msg = _('User is not authorized to access this tenant resources')
- LOG.error(msg)
- raise exc.HTTPForbidden(explanation=msg)
+ return get_env_template(env_template_id)
+
+ def _validate_body_name(self, body):
+
+ if not('name' in body and body['name'].strip()):
+ msg = _('Please, specify a name of the environment template.')
+ LOG.exception(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
+
+ name = unicode(body['name'])
+ if len(name) > 255:
+ msg = _('Environment template name should be 255 characters '
+ 'maximum')
+ LOG.exception(msg)
+ raise exc.HTTPBadRequest(explanation=msg)
def create_resource():
diff --git a/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py b/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py
new file mode 100644
index 00000000..d473c6e6
--- /dev/null
+++ b/murano/db/migration/alembic_migrations/versions/011_add_is_public_to_template.py
@@ -0,0 +1,44 @@
+# 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.
+
+"""
+Add the is_public column to the environment-template for public
+environment template functionality.
+
+Revision ID: 011
+Revises: table template
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = '011'
+down_revision = '010'
+
+from alembic import op
+import sqlalchemy as sa
+
+
+MYSQL_ENGINE = 'InnoDB'
+MYSQL_CHARSET = 'utf8'
+
+
+def upgrade():
+ op.add_column('environment-template',
+ sa.Column('is_public', sa.Boolean(),
+ default=False, nullable=True))
+ # end Alembic commands #
+
+
+def downgrade():
+ op.drop_column('environment-template', 'is_public')
+ # end Alembic commands #
diff --git a/murano/db/models.py b/murano/db/models.py
index efbdda73..7013e421 100644
--- a/murano/db/models.py
+++ b/murano/db/models.py
@@ -97,6 +97,7 @@ class EnvironmentTemplate(Base, TimestampMixin):
tenant_id = sa.Column(sa.String(36), nullable=False)
version = sa.Column(sa.BigInteger, nullable=False, default=0)
description = sa.Column(st.JsonBlob(), nullable=False, default={})
+ is_public = sa.Column(sa.Boolean, default=False)
def to_dict(self):
dictionary = super(EnvironmentTemplate, self).to_dict()
diff --git a/murano/db/services/environment_templates.py b/murano/db/services/environment_templates.py
index 79928283..f6fc2732 100644
--- a/murano/db/services/environment_templates.py
+++ b/murano/db/services/environment_templates.py
@@ -19,6 +19,7 @@ from murano.db import session as db_session
from oslo_db import exception as db_exc
from oslo_log import log as logging
+from sqlalchemy.sql import or_
LOG = logging.getLogger(__name__)
@@ -38,6 +39,19 @@ class EnvTemplateServices(object):
return templates
+ @staticmethod
+ def get_env_templates_or_by(filters):
+ """Returns list of environment-templates.
+
+ :param filters: property filters
+ :return: Returns list of environment-templates
+ """
+ unit = db_session.get_session()
+ templates = unit.query(models.EnvironmentTemplate). \
+ filter(or_(*filters)).all()
+
+ return templates
+
@staticmethod
def create(env_template_params, tenant_id):
"""Creates environment-template with specified params, in particular - name.
@@ -166,3 +180,32 @@ class EnvTemplateServices(object):
"""
session = db_session.get_session()
return session.query(models.EnvironmentTemplate).get(env_template_id)
+
+ @staticmethod
+ def clone(env_template_id, tenant_id, env_template_name, is_public):
+ """Clones environment-template with specified params, in particular - name.
+
+ :param env_template_params: Dict, e.g. {'name': 'temp-name'}
+ :param tenant_id: Tenant Id
+ :return: Created Template
+ """
+
+ template = EnvTemplateServices.get_env_template(env_template_id)
+ env_template_params = template.to_dict()
+ env_template_params['id'] = uuidutils.generate_uuid()
+ env_template_params['tenant_id'] = tenant_id
+ env_template_params['name'] = env_template_name
+ env_template_params['is_public'] = is_public
+ env_temp_desc = EnvTemplateServices.get_description(env_template_id)
+ if "services" in env_temp_desc:
+ env_template_params['services'] = env_temp_desc['services']
+
+ env_template = models.EnvironmentTemplate()
+ env_template.update(env_template_params)
+
+ unit = db_session.get_session()
+ with unit.begin():
+ unit.add(env_template)
+ env_template.update({'description': env_template_params})
+ env_template.save(unit)
+ return env_template
diff --git a/murano/tests/functional/api/base.py b/murano/tests/functional/api/base.py
index 19b5b16e..fe30d1c5 100644
--- a/murano/tests/functional/api/base.py
+++ b/murano/tests/functional/api/base.py
@@ -245,15 +245,40 @@ class MuranoClient(rest_client.RestClient):
"""Check the environment templates deployed by the user."""
resp, body = self.get('v1/templates')
+ return resp, json.loads(body)['templates']
+
+ def get_public_env_templates_list(self):
+ """Check the public environment templates deployed by the user."""
+ resp, body = self.get('v1/templates?is_public=true')
+ return resp, json.loads(body)
+
+ def get_private_env_templates_list(self):
+ """Check the public environment templates deployed by the user."""
+ resp, body = self.get('v1/templates?is_public=false')
return resp, json.loads(body)
def create_env_template(self, env_template_name):
"""Check the creation of an environment template."""
- body = {'name': env_template_name}
+ body = {'name': env_template_name, "is_public": False}
resp, body = self.post('v1/templates', json.dumps(body))
return resp, json.loads(body)
+ def create_clone_env_template(self, env_template_id,
+ cloned_env_template_name):
+ """Clone an environment template."""
+ body = {'name': cloned_env_template_name}
+ resp, body = self.post('v1/templates/{0}/clone'.
+ format(env_template_id), json.dumps(body))
+
+ return resp, json.loads(body)
+
+ def create_public_env_template(self, env_template_name):
+ """Check the creation of an environment template."""
+ body = {'name': env_template_name, "is_public": True}
+ resp, body = self.post('v1/templates', json.dumps(body))
+ return resp, json.loads(body)
+
def create_env_template_with_apps(self, env_template_name):
"""Check the creation of an environment template."""
body = {'name': env_template_name}
@@ -427,10 +452,34 @@ class TestCase(TestAuth):
return environment
def create_env_template(self, name):
- env_template = self.client.create_env_template(name)[1]
+ resp, env_template = self.client.create_env_template(name)
self.env_templates.append(env_template)
+ return resp, env_template
- return env_template
+ def create_public_env_template(self, name):
+ resp, env_template = self.client.create_public_env_template(name)
+ self.env_templates.append(env_template)
+ return resp, env_template
+
+ def create_env_template_with_apps(self, name):
+ resp, env_template = self.client.create_env_template_with_apps(name)
+ self.env_templates.append(env_template)
+ return resp, env_template
+
+ def clone_env_template(self, env_template_id, cloned_env_template_name):
+ create_clone_env_temp = self.client.create_clone_env_template
+ resp, env_template = create_clone_env_temp(env_template_id,
+ cloned_env_template_name)
+ self.env_templates.append(env_template)
+ return resp, env_template
+
+ def create_env_from_template(self, env_template_id, env_name):
+ resp, env_id = self.client.create_env_from_template(env_template_id,
+ env_name)
+ resp, env = self.client.get_environment(env_id['environment_id'])
+
+ self.environments.append(env)
+ return resp, env
def create_demo_service(self, environment_id, session_id, client=None):
if not client:
diff --git a/murano/tests/functional/api/v1/test_env_templates.py b/murano/tests/functional/api/v1/test_env_templates.py
index 76497ec6..5f66eaad 100644
--- a/murano/tests/functional/api/v1/test_env_templates.py
+++ b/murano/tests/functional/api/v1/test_env_templates.py
@@ -23,11 +23,11 @@ class TestEnvTemplate(base.TestCase):
@tag('all', 'coverage')
@attr(type='smoke')
- def test_list_env_templates(self):
+ def test_list_empty_env_templates(self):
"""Check getting the list of environment templates."""
resp, body = self.client.get_env_templates_list()
- self.assertIn('templates', body)
+ self.assertEqual(0, len(body))
self.assertEqual(resp.status, 200)
@tag('all', 'coverage')
@@ -36,23 +36,24 @@ class TestEnvTemplate(base.TestCase):
"""It checks the creation and deletion of an enviroment template."""
env_templates_list_start = self.client.get_env_templates_list()[1]
- resp, env_template = self.client.create_env_template('test_env_temp')
+ resp, env_template = self.create_env_template('test_env_temp')
self.env_templates.append(env_template)
self.assertEqual(resp.status, 200)
+ self.assertFalse(env_template['is_public'])
self.assertEqual('test_env_temp', env_template['name'])
env_templates_list = self.client.get_env_templates_list()[1]
- self.assertEqual(len(env_templates_list_start['templates']) + 1,
- len(env_templates_list['templates']))
+ self.assertEqual(len(env_templates_list_start) + 1,
+ len(env_templates_list))
self.client.delete_env_template(env_template['id'])
env_templates_list = self.client.get_env_templates_list()[1]
- self.assertEqual(len(env_templates_list_start['templates']),
- len(env_templates_list['templates']))
+ self.assertEqual(len(env_templates_list_start),
+ len(env_templates_list))
self.env_templates.pop(self.env_templates.index(env_template))
@@ -60,78 +61,151 @@ class TestEnvTemplate(base.TestCase):
@attr(type='smoke')
def test_get_env_template(self):
"""Check getting information about an environment template."""
- resp, env_template = self.client.create_env_template('test_env_temp')
+ resp, env_template = self.create_env_template('test_env_temp')
resp, env_obtained_template =\
self.client.get_env_template(env_template['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(env_obtained_template['name'], 'test_env_temp')
- self.client.delete_env_template(env_template['id'])
@tag('all', 'coverage')
@attr(type='smoke')
def test_create_env_template_with_apps(self):
"""Check the creation of an environment template with applications."""
resp, env_template = \
- self.client.create_env_template_with_apps('test_env_temp')
+ self.create_env_template_with_apps('test_env_temp')
self.assertEqual(resp.status, 200)
resp, apps_template = \
self.client.get_apps_in_env_template(env_template['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(len(apps_template), 1)
- self.client.delete_env_template(env_template['id'])
@tag('all', 'coverage')
@attr(type='smoke')
def test_create_app_in_env_template(self):
"""Check the creationg of applications in an environment template."""
- resp, env_template = self.client.create_env_template('test_env_temp')
- resp, apps = self.client.get_apps_in_env_template(env_template['id'])
+ resp, env_temp = self.create_env_template('test_env_temp')
+ self.assertEqual(resp.status, 200)
+
+ resp, apps = self.client.get_apps_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(len(apps), 0)
- resp, apps = self.client.create_app_in_env_template(env_template['id'])
+ resp, apps = self.client.create_app_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
- resp, apps = self.client.get_apps_in_env_template(env_template['id'])
+ resp, apps = self.client.get_apps_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(len(apps), 1)
- self.client.delete_env_template(env_template['id'])
-
@tag('all', 'coverage')
@attr(type='smoke')
def test_delete_app_in_env_template(self):
"""Check the deletion of applications in an environmente template."""
- resp, env_template = self.client.create_env_template('test_env_temp')
-
- resp, apps = self.client.create_app_in_env_template(env_template['id'])
+ resp, env_temp = self.create_env_template_with_apps('test_env_temp')
self.assertEqual(resp.status, 200)
- resp, apps = self.client.get_apps_in_env_template(env_template['id'])
+ resp, apps = self.client.get_apps_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(len(apps), 1)
- resp = self.client.delete_app_in_env_template(env_template['id'])
+ resp = self.client.delete_app_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
- resp, apps = self.client.get_apps_in_env_template(env_template['id'])
+ resp, apps = self.client.get_apps_in_env_template(env_temp['id'])
self.assertEqual(resp.status, 200)
self.assertEqual(len(apps), 0)
- self.client.delete_env_template(env_template['id'])
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_create_public_env_template(self):
+ """Check the creation of a public environment template."""
+ resp, env_temp = self.create_public_env_template('test_env_temp')
+ self.assertEqual(resp.status, 200)
+ resp, env = self.client.get_env_template(env_temp['id'])
+ self.assertEqual(resp.status, 200)
+ self.assertTrue(env['is_public'], 200)
+
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_clone_env_template(self):
+ """Check the creation of a public environment template."""
+ resp, env_template = self.\
+ create_public_env_template('test_env_temp')
+ self.assertEqual(resp.status, 200)
+ resp, cloned_templ = self.clone_env_template(env_template['id'],
+ 'cloned_template')
+ self.assertEqual(resp.status, 200)
+ self.assertTrue(cloned_templ['name'], 'cloned_template')
+
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_clone_env_template_private(self):
+ """Check the creation of a public environment template."""
+ resp, env_template = self.\
+ create_env_template('test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertRaises(exceptions.Forbidden,
+ self.clone_env_template,
+ env_template['id'], 'cloned_template')
+
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_get_public_env_templates(self):
+ """Check the deletion of applications in an environmente template."""
+ resp, public_env_template = \
+ self.create_public_env_template('public_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(public_env_template['is_public'], True)
+ resp, private_env_template = \
+ self.create_env_template('private_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(private_env_template['is_public'], False)
+ resp, public_envs = self.client.get_public_env_templates_list()
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(len(public_envs), 1)
+
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_get_private_env_templates(self):
+ """Check the deletion of applications in an environmente template."""
+ resp, public_env_template = \
+ self.create_public_env_template('public_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(public_env_template['is_public'], True)
+ resp, private_env_template = \
+ self.create_env_template('private_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(private_env_template['is_public'], False)
+ resp, private_envs = self.client.get_private_env_templates_list()
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(len(private_envs), 1)
+
+ @tag('all', 'coverage')
+ @attr(type='smoke')
+ def test_get_env_templates(self):
+ """Check the deletion of applications in an environmente template."""
+ resp, public_env_template = \
+ self.create_public_env_template('public_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(public_env_template['is_public'], True)
+ resp, private_env_template = \
+ self.create_env_template('private_test_env_temp')
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(private_env_template['is_public'], False)
+ resp, envs_templates = self.client.get_env_templates_list()
+ self.assertEqual(resp.status, 200)
+ self.assertEqual(len(envs_templates), 2)
@tag('all', 'coverage')
@attr(type='smoke')
def test_create_env_from_template(self):
"""Check the creation of an environment from a template."""
resp, env_template = \
- self.client.create_env_template_with_apps('test_env_temp')
+ self.create_env_template_with_apps('test_env_temp')
self.assertEqual(resp.status, 200)
- resp, env = self.client.create_env_from_template(env_template['id'],
- "env")
+ resp, env = self.create_env_from_template(env_template['id'],
+ "env")
self.assertEqual(resp.status, 200)
-
- self.client.delete_env_template(env_template['id'])
- self.client.delete_environment(env['environment_id'])
+ self.assertIsNotNone(env)
@tag('all', 'coverage')
@attr(type='negative')
@@ -153,7 +227,7 @@ class TestEnvTemplate(base.TestCase):
@attr(type='negative')
def test_double_delete_env_template(self):
"""Check the deletion of an wrong environment template request."""
- _, env_template = self.client.create_env_template('test_env_temp')
+ _, env_template = self.create_env_template('test_env_temp')
self.client.delete_env_template(env_template['id'])
@@ -165,7 +239,7 @@ class TestEnvTemplate(base.TestCase):
@attr(type='negative')
def test_get_deleted_env_template(self):
"""Check the deletion of an wrong environment template request."""
- _, env_template = self.client.create_env_template('test_env_temp')
+ _, env_template = self.create_env_template('test_env_temp')
self.client.delete_env_template(env_template['id'])
@@ -182,22 +256,19 @@ class TestEnvTemplatesTenantIsolation(base.NegativeTestCase):
"""It tests getting information from an environment
template from another user.
"""
- env_template = self.create_env_template('test_env_temp')
+ _, env_template = self.create_env_template('test_env_temp')
self.assertRaises(exceptions.Forbidden,
self.alt_client.get_env_template, env_template['id'])
- self.client.delete_env_template(env_template['id'])
-
@tag('all', 'coverage')
@attr(type='negative')
def test_delete_env_template_from_another_tenant(self):
"""It tests deleting information from an environment
template from another user.
"""
- env_template = self.create_env_template('test_env_temp')
+ _, env_template = self.create_env_template('test_env_temp')
self.assertRaises(exceptions.Forbidden,
self.alt_client.delete_env_template,
env_template['id'])
- self.client.delete_env_template(env_template['id'])
diff --git a/murano/tests/unit/api/v1/test_env_templates.py b/murano/tests/unit/api/v1/test_env_templates.py
index 115fec7b..9e5c187e 100644
--- a/murano/tests/unit/api/v1/test_env_templates.py
+++ b/murano/tests/unit/api/v1/test_env_templates.py
@@ -59,6 +59,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase):
expected = {'tenant_id': self.tenant,
'id': 'env_template_id',
+ 'is_public': False,
'name': 'mytemp',
'version': 0,
'created': timeutils.isotime(fake_now)[:-1],
@@ -86,6 +87,138 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase):
result = req.get_response(self.api)
self.assertEqual(expected, json.loads(result.body))
+ def test_list_public_env_templates(self):
+ """Create an template, test templates.public()."""
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'list_env_templates': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+
+ body = {'name': 'mytemp2', 'is_public': True}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertTrue(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('list_env_templates')
+ req = self._get('/templates', {'is_public': True})
+
+ result = req.get_response(self.api)
+ self.assertEqual(1, len(json.loads(result.body)))
+ self.assertTrue(json.loads(result.body)['templates'][0]['is_public'])
+
+ def test_clone_env_templates(self):
+ """Create an template, test templates.public()."""
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'clone_env_template': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp2', 'is_public': True}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ env_template_id = json.loads(result.body)['id']
+ self.assertTrue(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('clone_env_template')
+ body = {'name': 'clone', 'is_public': False}
+ req = self._post('/templates/%s/clone' % env_template_id,
+ json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertFalse(json.loads(result.body)['is_public'])
+ self.assertEqual('clone', json.loads(result.body)['name'])
+
+ def test_clone_env_templates_private(self):
+ """Create an template, test templates.public()."""
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'clone_env_template': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp2', 'is_public': False}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ env_template_id = json.loads(result.body)['id']
+ self.assertFalse(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('clone_env_template')
+ body = {'name': 'clone', 'is_public': False}
+ req = self._post('/templates/%s/clone' % env_template_id,
+ json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertEqual(result.status_code, 403)
+
+ def test_list_public_env_templates_default(self):
+ """Create an template, test list public with no
+ public templates.
+ """
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'list_env_templates': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp'}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertFalse(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('list_env_templates')
+ req = self._get('/templates', {'is_public': True})
+ result = req.get_response(self.api)
+
+ self.assertFalse(0, len(json.loads(result.body)))
+
+ def test_list_private_env_templates(self):
+ """Create an template, test list public with no
+ public templates.
+ """
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'list_env_templates': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp', 'is_public': False}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertFalse(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('list_env_templates')
+ req = self._get('/templates', {'is_public': False})
+ result = req.get_response(self.api)
+ self.assertEqual(1, len(json.loads(result.body)['templates']))
+
+ def test_list_env_templates(self):
+ """Create an template, test list public with no
+ public templates.
+ """
+ self._set_policy_rules(
+ {'create_env_template': '@',
+ 'list_env_templates': '@'}
+ )
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp', 'is_public': False}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertFalse(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('create_env_template')
+ body = {'name': 'mytemp1', 'is_public': True}
+ req = self._post('/templates', json.dumps(body))
+ result = req.get_response(self.api)
+ self.assertTrue(json.loads(result.body)['is_public'])
+
+ self.expect_policy_check('list_env_templates')
+ req = self._get('/templates')
+ result = req.get_response(self.api)
+
+ self.assertEqual(2, len(json.loads(result.body)['templates']))
+
def test_illegal_template_name_create(self):
"""Check that an illegal temp name results in an HTTPClientError."""
self._set_policy_rules(
@@ -114,7 +247,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase):
result = req.get_response(self.api)
self.assertEqual(400, result.status_code)
result_msg = result.text.replace('\n', '')
- self.assertIn('Environment Template name should be 255 characters '
+ self.assertIn('Environment template name should be 255 characters '
'maximum',
result_msg)
@@ -158,6 +291,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase):
expected = dict(
id='12345',
+ is_public=False,
name='my-temp',
version=0,
created=fake_now,
@@ -242,6 +376,7 @@ class TestEnvTemplateApi(tb.ControllerTest, tb.MuranoApiTestCase):
timeutils.utcnow.override_time = fake_now
expected = {'tenant_id': self.tenant,
'id': self.uuids[0],
+ 'is_public': False,
'name': 'env_template_name',
'version': 0,
'created': timeutils.isotime(fake_now)[:-1],
diff --git a/murano/tests/unit/db/services/test_templates_service.py b/murano/tests/unit/db/services/test_templates_service.py
index 4cae83d2..6861d930 100644
--- a/murano/tests/unit/db/services/test_templates_service.py
+++ b/murano/tests/unit/db/services/test_templates_service.py
@@ -27,7 +27,7 @@ class TestTemplateServices(base.MuranoWithDBTestCase,
def setUp(self):
super(TestTemplateServices, self).setUp()
self.template_services = env_temp.EnvTemplateServices
- self.uuids = ['template_id']
+ self.uuids = ['template_id', 'template_id2']
self.mock_uuid = self._stub_uuid(self.uuids)
self.addCleanup(mock.patch.stopall)
@@ -42,6 +42,18 @@ class TestTemplateServices(base.MuranoWithDBTestCase,
self.assertEqual(fixture.environment_template_desc,
template_des.description)
+ def test_clone_template(self):
+ """Check the clonation of a template."""
+ body = {
+ "name": "my_template"
+ }
+ template = self.template_services.create(body, 'tenant_id')
+ cloned_template = self.template_services.clone(template['id'],
+ 'id2',
+ "my_template2", False)
+ self.assertEqual(cloned_template.description['name'], 'my_template2')
+ self.assertEqual(cloned_template.description['tenant_id'], 'id2')
+
def test_get_empty_template(self):
"""Check obtaining information about a template without services."""
fixture = self.useFixture(et.EmptyEnvironmentTemplateFixture())
diff --git a/releasenotes/notes/public-template-a8853ac02dcf9396.yaml b/releasenotes/notes/public-template-a8853ac02dcf9396.yaml
new file mode 100644
index 00000000..b8d83e58
--- /dev/null
+++ b/releasenotes/notes/public-template-a8853ac02dcf9396.yaml
@@ -0,0 +1,6 @@
+---
+features:
+ - Added public field to environment templates. GET method
+ for api now displays public templates from other projects(tenants).
+ - Added public filter to environment templates api.
+ - Added clone action to environment templates.