Support for multi-class yamls in client
MuranoClient needs to analyse package classes (names and inheritance
chains) to properly add packages to Glare. This requires it to
properly resolve class names in the appropriate namespaces.
Since Mitaka Murano supports multi-class yamls, i.e. yaml files
containing multiple MuranoPL classes in a single yaml file using it
multi-document format. Optionally this allows to reuse a single
namespace definition for multiple classes defined in the same file.
Client was not aware of this feature, always loading classes as
single-document yamls. It was also always using the current-class
namespace definitions, thus likely to fail if the shared namespace
definition is in use.
This has been addressed.
Closes-bug: #1608440
Change-Id: Ibd08454f71e0266c76a2ac517b441df97311d8b9
(cherry picked from commit 98adda6a46
)
This commit is contained in:
parent
3234f25bee
commit
56605d2740
|
@ -353,18 +353,38 @@ class Package(FileWrapperMixin):
|
|||
except Exception:
|
||||
return []
|
||||
|
||||
@property
|
||||
def resolvers(self):
|
||||
if not hasattr(self, '_resolvers'):
|
||||
self.classes
|
||||
return self._resolvers
|
||||
|
||||
@property
|
||||
def classes(self):
|
||||
if not hasattr(self, '_classes'):
|
||||
self._classes = {}
|
||||
self._resolvers = {}
|
||||
for class_name, class_file in six.iteritems(
|
||||
self.manifest.get('Classes', {})):
|
||||
filename = "Classes/%s" % class_file
|
||||
if filename not in self.contents.namelist():
|
||||
continue
|
||||
klass = yaml.load(self.contents.open(filename),
|
||||
DummyYaqlYamlLoader)
|
||||
self._classes[class_name] = klass
|
||||
klass_list = yaml.load_all(self.contents.open(filename),
|
||||
DummyYaqlYamlLoader)
|
||||
if not klass_list:
|
||||
raise ValueError('No classes defined in file')
|
||||
resolver = None
|
||||
for klass in klass_list:
|
||||
ns = klass.get('Namespaces')
|
||||
if ns:
|
||||
resolver = NamespaceResolver(ns)
|
||||
name = klass.get('Name')
|
||||
if name and resolver:
|
||||
name = resolver.resolve_name(name)
|
||||
if name == class_name:
|
||||
self._classes[class_name] = klass
|
||||
self._resolvers[class_name] = resolver
|
||||
break
|
||||
return self._classes
|
||||
|
||||
@property
|
||||
|
@ -669,3 +689,32 @@ def traverse_and_replace(obj,
|
|||
_maybe_replace(obj, key, value)
|
||||
else:
|
||||
_maybe_replace(obj, key, value)
|
||||
|
||||
|
||||
class NamespaceResolver(object):
|
||||
"""Copied from main murano repo
|
||||
|
||||
original at murano/dsl/namespace_resolver.py
|
||||
"""
|
||||
|
||||
def __init__(self, namespaces):
|
||||
self._namespaces = namespaces
|
||||
self._namespaces[''] = ''
|
||||
|
||||
def resolve_name(self, name, relative=None):
|
||||
if name is None:
|
||||
raise ValueError()
|
||||
if name and name.startswith(':'):
|
||||
return name[1:]
|
||||
if ':' in name:
|
||||
parts = name.split(':')
|
||||
if len(parts) != 2 or not parts[1]:
|
||||
raise NameError('Incorrectly formatted name ' + name)
|
||||
if parts[0] not in self._namespaces:
|
||||
raise KeyError('Unknown namespace prefix ' + parts[0])
|
||||
return '.'.join((self._namespaces[parts[0]], parts[1]))
|
||||
if not relative and '=' in self._namespaces and '.' not in name:
|
||||
return '.'.join((self._namespaces['='], name))
|
||||
if relative and '.' not in name:
|
||||
return '.'.join((relative, name))
|
||||
return name
|
||||
|
|
|
@ -52,7 +52,8 @@ class ArtifactRepo(object):
|
|||
for k, v in six.iteritems(kwargs):
|
||||
package_draft[k] = v
|
||||
|
||||
inherits = self._get_local_inheritance(package.classes)
|
||||
inherits = self._get_local_inheritance(package.classes,
|
||||
package.resolvers)
|
||||
|
||||
package_draft['inherits'] = inherits
|
||||
|
||||
|
@ -90,16 +91,16 @@ class ArtifactRepo(object):
|
|||
return self.client.artifacts.get(app_id)
|
||||
|
||||
@staticmethod
|
||||
def _get_local_inheritance(classes):
|
||||
def _get_local_inheritance(classes, resolvers):
|
||||
result = {}
|
||||
for class_name, klass in six.iteritems(classes):
|
||||
if 'Extends' not in klass:
|
||||
continue
|
||||
ns = klass.get('Namespaces')
|
||||
if ns:
|
||||
resolver = NamespaceResolver(ns)
|
||||
resolver = utils.NamespaceResolver(ns)
|
||||
else:
|
||||
resolver = None
|
||||
resolver = resolvers.get(class_name)
|
||||
|
||||
if isinstance(klass['Extends'], list):
|
||||
bases = klass['Extends']
|
||||
|
@ -337,32 +338,3 @@ class PackageWrapper(object):
|
|||
{'pkg_name': self.name,
|
||||
'attrs': ", ".join(missing_keys)})
|
||||
return {key: getattr(self, key) for key in keys}
|
||||
|
||||
|
||||
class NamespaceResolver(object):
|
||||
"""Copied from main murano repo
|
||||
|
||||
original at murano/dsl/namespace_resolver.py
|
||||
"""
|
||||
|
||||
def __init__(self, namespaces):
|
||||
self._namespaces = namespaces
|
||||
self._namespaces[''] = ''
|
||||
|
||||
def resolve_name(self, name, relative=None):
|
||||
if name is None:
|
||||
raise ValueError()
|
||||
if name and name.startswith(':'):
|
||||
return name[1:]
|
||||
if ':' in name:
|
||||
parts = name.split(':')
|
||||
if len(parts) != 2 or not parts[1]:
|
||||
raise NameError('Incorrectly formatted name ' + name)
|
||||
if parts[0] not in self._namespaces:
|
||||
raise KeyError('Unknown namespace prefix ' + parts[0])
|
||||
return '.'.join((self._namespaces[parts[0]], parts[1]))
|
||||
if not relative and '=' in self._namespaces and '.' not in name:
|
||||
return '.'.join((self._namespaces['='], name))
|
||||
if relative and '.' not in name:
|
||||
return '.'.join((relative, name))
|
||||
return name
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
fixes:
|
||||
- Fixed a bug when a package containing multi-class yamls could not be added
|
||||
to glare-based catalog.
|
Loading…
Reference in New Issue