Fix used package versions for stable inheritance

When an object from an object model is deserialized
on subsequent deployment it's version is usually
fixed in the object model. However the version of
base classes for that object is not as long as it meets
to the class requirements. Thus slightly different version
might be used on each deployment that does satisfies the
requirements but may have different internal state representation.

To solve this a list of all package versions that are in use by the
object model is written to the hidden section of the object model.
On the subsequent load package loader will prefer versions in the
list over newer versions available in the catalog at that time

Change-Id: I17087c0045210d28b87aba4d50c329f688df3954
Closes-Bug: #1602839
This commit is contained in:
Stan Lagun 2016-07-13 22:47:27 -07:00
parent 4c492f292c
commit 904cb248e4
5 changed files with 107 additions and 2 deletions

View File

@ -180,7 +180,11 @@ class TaskExecutor(object):
return self.exception_result(e, None, '<system>')
with package_loader.CombinedPackageLoader(self._session) as pkg_loader:
pkg_loader.import_fixation_table(
self._session.system_attributes.get('Packages', {}))
result = self._execute(pkg_loader)
self._session.system_attributes[
'Packages'] = pkg_loader.export_fixation_table()
self._model['SystemData'] = self._session.system_attributes
result['model'] = self._model
@ -248,6 +252,7 @@ class TaskExecutor(object):
except Exception as e:
return self.exception_result(e, None, '<result>')
pkg_loader.compact_fixation_table()
return {
'action': {
'result': action_result,

View File

@ -30,3 +30,15 @@ class MuranoPackageLoader(object):
@abc.abstractmethod
def register_package(self, package):
pass
@abc.abstractmethod
def import_fixation_table(self, fixations):
pass
@abc.abstractmethod
def export_fixation_table(self):
pass
@abc.abstractmethod
def compact_fixation_table(self):
pass

View File

@ -66,6 +66,8 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
self._mem_locks = []
self._ipc_locks = []
self._downloaded = []
self._fixations = collections.defaultdict(set)
self._new_fixations = collections.defaultdict(set)
def _get_glare_client(self):
glare_settings = CONF.glare
@ -140,6 +142,11 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
self._get_package_by_definition(package_definition))
def load_package(self, package_name, version_spec):
fixed_versions = self._fixations[package_name]
version = version_spec.select(fixed_versions)
if version:
version_spec = helpers.parse_version_spec(version)
packages = self._package_cache.get(package_name)
if packages:
version = version_spec.select(six.iterkeys(packages))
@ -157,8 +164,10 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
six.reraise(exceptions.NoPackageFound(package_name),
None, exc_info[2])
else:
return self._to_dsl_package(
self._get_package_by_definition(package_definition))
package = self._get_package_by_definition(package_definition)
self._fixations[package_name].add(package.version)
self._new_fixations[package_name].add(package.version)
return self._to_dsl_package(package)
def register_package(self, package):
for name in package.classes:
@ -402,6 +411,15 @@ class ApiPackageLoader(package_loader.MuranoPackageLoader):
self._mem_locks.append(mem_lock)
self._ipc_locks.append(ipc_lock)
def import_fixation_table(self, fixations):
self._fixations = deserialize_package_fixations(fixations)
def export_fixation_table(self):
return serialize_package_fixations(self._fixations)
def compact_fixation_table(self):
self._fixations = self._new_fixations.copy()
def cleanup(self):
"""Cleans up any lock we had acquired and removes any stale packages"""
@ -430,6 +448,8 @@ class DirectoryPackageLoader(package_loader.MuranoPackageLoader):
self._packages_by_name = {}
self._loaded_packages = set()
self._root_loader = root_loader or self
self._fixations = collections.defaultdict(set)
self._new_fixations = collections.defaultdict(set)
self._build_index()
def _build_index(self):
@ -453,6 +473,15 @@ class DirectoryPackageLoader(package_loader.MuranoPackageLoader):
continue
LOG.info(_LI('Loaded package from path {0}').format(folder))
def import_fixation_table(self, fixations):
self._fixations = deserialize_package_fixations(fixations)
def export_fixation_table(self):
return serialize_package_fixations(self._fixations)
def compact_fixation_table(self):
self._fixations = self._new_fixations.copy()
def load_class_package(self, class_name, version_spec):
packages = self._packages_by_class.get(class_name)
if not packages:
@ -463,12 +492,18 @@ class DirectoryPackageLoader(package_loader.MuranoPackageLoader):
return packages[version]
def load_package(self, package_name, version_spec):
fixed_versions = self._fixations[package_name]
version = version_spec.select(fixed_versions)
if version:
version_spec = helpers.parse_version_spec(version)
packages = self._packages_by_name.get(package_name)
if not packages:
raise exceptions.NoPackageFound(package_name)
version = version_spec.select(six.iterkeys(packages))
if not version:
raise exceptions.NoPackageFound(package_name)
self._fixations[package_name].add(version)
self._new_fixations[package_name].add(version)
return packages[version]
def register_package(self, package):
@ -543,6 +578,26 @@ class CombinedPackageLoader(package_loader.MuranoPackageLoader):
def register_package(self, package):
self.api_loader.register_package(package)
def export_fixation_table(self):
result = deserialize_package_fixations(
self.api_loader.export_fixation_table())
for loader in self.directory_loaders:
fixations = deserialize_package_fixations(
loader.export_fixation_table())
for key, value in six.iteritems(fixations):
result[key].update(value)
return serialize_package_fixations(result)
def import_fixation_table(self, fixations):
self.api_loader.import_fixation_table(fixations)
for loader in self.directory_loaders:
loader.import_fixation_table(fixations)
def compact_fixation_table(self):
self.api_loader.compact_fixation_table()
for loader in self.directory_loaders:
loader.compact_fixation_table()
def __enter__(self):
return self
@ -568,3 +623,18 @@ def _with_to_generator(context_obj):
with context_obj as obj:
yield obj
yield
def deserialize_package_fixations(fixations):
result = collections.defaultdict(set)
for name, versions in six.iteritems(fixations):
for version in versions:
result[name].add(helpers.parse_version(version))
return result
def serialize_package_fixations(fixations):
return {
name: list(str(v) for v in versions)
for name, versions in six.iteritems(fixations)
}

View File

@ -77,6 +77,15 @@ class TestPackageLoader(package_loader.MuranoPackageLoader):
else:
raise KeyError(class_name)
def export_fixation_table(self):
return {}
def import_fixation_table(self, fixations):
pass
def compact_fixation_table(self):
pass
def _build_index(self, directory):
yamls = [
os.path.join(dirpath, f)

View File

@ -57,6 +57,15 @@ class MockPackageLoader(package_loader.MuranoPackageLoader):
def load_package(self, package_name, version_spec):
return self._package
def export_fixation_table(self):
pass
def import_fixation_table(self, fixations):
pass
def compact_fixation_table(self):
pass
class MockPackage(object):
def __init__(self, classes):