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
This commit is contained in:
parent
aa02b816d3
commit
98adda6a46
|
@ -357,18 +357,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
|
||||
|
@ -749,3 +769,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
|
||||
|
|
|
@ -54,7 +54,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)
|
||||
|
||||
# check for global inheritance
|
||||
ancestor_queue = collections.deque(inherits.keys())
|
||||
|
@ -109,16 +110,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']
|
||||
|
@ -356,32 +357,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