Restore YAQL validators support
It was broken after YAQl release since YAQL expression object contains recursive reference and deepcopy operation failed. So need to copy such objects manually in a separate function. To prevent such kind of failures, test of hot-based package was added. Change-Id: If3fde7552d9657055aded73c4967fb6832c17536 Closes-Bug: #1498253
This commit is contained in:
parent
6ce981df78
commit
b3bdf5f0aa
|
@ -33,7 +33,7 @@ from openstack_dashboard.api import glance
|
|||
from openstack_dashboard.api import nova
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
import yaql
|
||||
from yaql import legacy
|
||||
|
||||
from muranoclient.common import exceptions as muranoclient_exc
|
||||
from muranodashboard.api import packages as pkg_api
|
||||
|
@ -77,7 +77,7 @@ def make_yaql_validator(validator_property):
|
|||
message = validator_property.get('message', '')
|
||||
|
||||
def validator_func(value):
|
||||
context = yaql.create_context()
|
||||
context = legacy.create_context()
|
||||
context['$'] = value
|
||||
if not expr.evaluate(context=context):
|
||||
raise forms.ValidationError(message)
|
||||
|
|
|
@ -62,6 +62,19 @@ TYPES_KWARGS = {
|
|||
|
||||
|
||||
def _collect_fields(field_specs, form_name, service):
|
||||
def careful_deepcopy(x):
|
||||
"""Careful handling of deepcopy object with recursive.
|
||||
|
||||
There is a recursive reference in YAQL expression
|
||||
(since 1.0 version) and standard deepcopy can't handle this.
|
||||
"""
|
||||
original_validators = x.pop('validators', None)
|
||||
|
||||
result = copy.deepcopy(x)
|
||||
if original_validators:
|
||||
result['validators'] = original_validators
|
||||
return result
|
||||
|
||||
def process_widget(cls, kwargs):
|
||||
if isinstance(cls, tuple):
|
||||
cls, _w = cls
|
||||
|
@ -127,7 +140,7 @@ def _collect_fields(field_specs, form_name, service):
|
|||
|
||||
return name, cls(**kwargs)
|
||||
|
||||
return [make_field(copy.deepcopy(_spec)) for _spec in field_specs]
|
||||
return [make_field(careful_deepcopy(_spec)) for _spec in field_specs]
|
||||
|
||||
|
||||
class DynamicFormMetaclass(forms.forms.DeclarativeFieldsMetaclass):
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
heat_template_version: 2013-05-23
|
||||
|
||||
description: >
|
||||
Hello world HOT template that just defines a single server.
|
||||
Contains just base features to verify base HOT support.
|
||||
|
||||
parameters:
|
||||
flavor:
|
||||
type: string
|
||||
description: Flavor for the server to be created
|
||||
default: m1.small
|
||||
constraints:
|
||||
- allowed_values: [ m1.small, m1.medium, m1.large ]
|
||||
|
||||
resources:
|
||||
server:
|
||||
type: OS::Nova::Server
|
||||
properties:
|
||||
image: cirros-0.3.4-x86_64-uec
|
||||
flavor: { get_param: flavor }
|
||||
|
|
@ -258,12 +258,17 @@ class PackageBase(UITestCase):
|
|||
cls.murano_client,
|
||||
"PostgreSQL",
|
||||
{"categories": ["Databases"], "tags": ["tag"]})
|
||||
cls.hot_app_id = utils.upload_app_package(
|
||||
cls.murano_client,
|
||||
"HotExample",
|
||||
{"tags": ["hot"]}, hot=True)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(PackageBase, cls).tearDownClass()
|
||||
cls.murano_client.packages.delete(cls.mockapp_id)
|
||||
cls.murano_client.packages.delete(cls.postgre_id)
|
||||
cls.murano_client.packages.delete(cls.hot_app_id)
|
||||
|
||||
|
||||
class ImageTestCase(PackageBase):
|
||||
|
@ -399,8 +404,8 @@ class PackageTestCase(ApplicationTestCase):
|
|||
super(ApplicationTestCase, cls).setUpClass()
|
||||
cls.archive_name = "ToUpload"
|
||||
cls.alt_archive_name = "ModifiedAfterUpload"
|
||||
cls.archive = utils.compose_package(cls.archive_name,
|
||||
consts.Manifest,
|
||||
cls.manifest = os.path.join(consts.PackageDir, 'manifest.yaml')
|
||||
cls.archive = utils.compose_package(cls.archive_name, cls.manifest,
|
||||
consts.PackageDir)
|
||||
|
||||
def tearDown(self):
|
||||
|
@ -413,7 +418,7 @@ class PackageTestCase(ApplicationTestCase):
|
|||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
super(ApplicationTestCase, cls).tearDownClass()
|
||||
if os.path.exists(consts.Manifest):
|
||||
os.remove(consts.Manifest)
|
||||
if os.path.exists(cls.manifest):
|
||||
os.remove(cls.manifest)
|
||||
if os.path.exists(cls.archive):
|
||||
os.remove(cls.archive)
|
||||
|
|
|
@ -2,7 +2,8 @@ import os
|
|||
|
||||
PackageDir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'MockApp')
|
||||
Manifest = os.path.join(PackageDir, 'manifest.yaml')
|
||||
HotPackageDir = os.path.join(os.path.dirname(os.path.realpath(__file__)),
|
||||
'HotApp')
|
||||
|
||||
CategorySelector = "//a[contains(text(), '{0}')][contains(@class, 'dropdown-toggle')]" # noqa
|
||||
App = "//div[contains(@class, 'app-list')]//h4[contains(text(), '{0}')]"
|
||||
|
@ -18,6 +19,7 @@ CellStatus = "//td[contains(@class, 'status_{0}')]"
|
|||
ErrorMessage = '//span[contains(@class, "help-block") and contains(text(), "{0}")]' # noqa
|
||||
DatabaseCategory = "select[name='add_category-categories'] > option[value='Databases']" # noqa
|
||||
Action = '//a[contains(@class, "murano_action") and contains(text(), "testAction")]' # noqa
|
||||
HotFlavorField = '//div[contains(@class, "has-error")]//input'
|
||||
|
||||
# Buttons
|
||||
ButtonSubmit = ".//*[@name='wizard_goto_step'][2]"
|
||||
|
|
|
@ -610,6 +610,30 @@ class TestSuiteApplications(base.ApplicationTestCase):
|
|||
self.assertIn('Follow the white rabbit',
|
||||
self.driver.find_element_by_class_name('logs').text)
|
||||
|
||||
def test_hot_application(self):
|
||||
"""Checks that UI got the hot app is rendered correctly
|
||||
|
||||
Scenario:
|
||||
1. Navigate Applications and click Hot app 'Quick Deploy'
|
||||
2. Check for YAQL validator
|
||||
3. Check that app is added to the environment
|
||||
"""
|
||||
self.go_to_submenu('Applications')
|
||||
self.select_and_click_action_for_app('quick-add', self.hot_app_id)
|
||||
field_id = "{0}_0-name".format(self.hot_app_id)
|
||||
self.fill_field(by.By.ID, field_id, value='TestHotApp')
|
||||
self.driver.find_element_by_xpath(c.ButtonSubmit).click()
|
||||
self.fill_field(by.By.CSS_SELECTOR,
|
||||
'input[id$="flavor"]',
|
||||
value='testFlavor')
|
||||
self.driver.find_element_by_xpath(c.InputSubmit).click()
|
||||
self.check_element_on_page(by.By.XPATH, c.HotFlavorField)
|
||||
self.fill_field(by.By.CSS_SELECTOR,
|
||||
'input[id$="flavor"]',
|
||||
value='m1.small')
|
||||
self.driver.find_element_by_xpath(c.InputSubmit).click()
|
||||
self.check_element_on_page(by.By.LINK_TEXT, 'TestHotApp')
|
||||
|
||||
|
||||
class TestSuitePackages(base.PackageTestCase):
|
||||
def test_modify_package_name(self):
|
||||
|
|
|
@ -30,19 +30,24 @@ class ImageException(Exception):
|
|||
return self._error_string
|
||||
|
||||
|
||||
def upload_app_package(client, app_name, data):
|
||||
def upload_app_package(client, app_name, data, hot=False):
|
||||
try:
|
||||
archive = compose_package(app_name, consts.Manifest,
|
||||
consts.PackageDir)
|
||||
if not hot:
|
||||
manifest = os.path.join(consts.PackageDir, 'manifest.yaml')
|
||||
archive = compose_package(app_name, manifest, consts.PackageDir)
|
||||
else:
|
||||
manifest = os.path.join(consts.HotPackageDir, 'manifest.yaml')
|
||||
archive = compose_package(app_name, manifest,
|
||||
consts.HotPackageDir, hot=True)
|
||||
package = client.packages.create(data, {app_name: open(archive, 'rb')})
|
||||
return package.id
|
||||
finally:
|
||||
os.remove(archive)
|
||||
os.remove(consts.Manifest)
|
||||
os.remove(manifest)
|
||||
|
||||
|
||||
def compose_package(app_name, manifest, package_dir,
|
||||
require=None, archive_dir=None):
|
||||
require=None, archive_dir=None, hot=False):
|
||||
"""Composes a murano package
|
||||
|
||||
Composes package `app_name` with `manifest` file as a template for the
|
||||
|
@ -56,7 +61,11 @@ def compose_package(app_name, manifest, package_dir,
|
|||
mfest_copy = MANIFEST.copy()
|
||||
mfest_copy['FullName'] = fqn
|
||||
mfest_copy['Name'] = app_name
|
||||
mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'}
|
||||
if hot:
|
||||
mfest_copy['Format'] = 'Heat.HOT/1.0'
|
||||
else:
|
||||
mfest_copy['Format'] = '1.0'
|
||||
mfest_copy['Classes'] = {fqn: 'mock_muranopl.yaml'}
|
||||
if require:
|
||||
mfest_copy['Require'] = require
|
||||
f.write(yaml.dump(mfest_copy, default_flow_style=False))
|
||||
|
|
Loading…
Reference in New Issue