diff options
Diffstat (limited to 'morphlib')
-rw-r--r-- | morphlib/morphloader.py | 57 | ||||
-rw-r--r-- | morphlib/morphloader_tests.py | 80 | ||||
-rw-r--r-- | morphlib/morphologyfactory.py | 10 |
3 files changed, 142 insertions, 5 deletions
diff --git a/morphlib/morphloader.py b/morphlib/morphloader.py index 32b5b40b..e4367fa1 100644 --- a/morphlib/morphloader.py +++ b/morphlib/morphloader.py @@ -18,11 +18,27 @@ import collections import logging +import warnings import yaml import morphlib +class MorphologyObsoleteFieldWarning(UserWarning): + + def __init__(self, morphology, spec, field): + self.kind = morphology['kind'] + self.morphology_name = morphology.get('name', '<unknown>') + self.stratum_name = spec.get('alias', spec['morph']) + self.field = field + + def __str__(self): + format_string = ('%(kind)s morphology %(morphology_name)s refers to ' + 'stratum %(stratum_name)s with the %(field)s field. ' + 'Defaulting to null.') + return format_string % self.__dict__ + + class MorphologySyntaxError(morphlib.Error): def __init__(self, morphology): @@ -366,6 +382,9 @@ class MorphologyLoader(object): raise DuplicateStratumError(morph['name'], name) names.add(name) + # Validate stratum spec fields + self._validate_stratum_specs_fields(morph, 'strata') + # We allow the ARMv7 little-endian architecture to be specified # as armv7 and armv7l. Normalise. if morph['arch'] == 'armv7': @@ -409,6 +428,9 @@ class MorphologyLoader(object): raise NoStratumBuildDependenciesError( morph['name'], morph.filename) + # Validate build-dependencies if specified + self._validate_stratum_specs_fields(morph, 'build-depends') + # Require build-dependencies for each chunk. for spec in morph['chunks']: if 'build-depends' not in spec: @@ -496,6 +518,18 @@ class MorphologyLoader(object): type(pattern), morphology_name) errors.append(e) + @classmethod + def _warn_obsolete_field(cls, morphology, spec, field): + warnings.warn(MorphologyObsoleteFieldWarning(morphology, spec, field), + stacklevel=2) + + @classmethod + def _validate_stratum_specs_fields(cls, morphology, specs_field): + for spec in morphology.get(specs_field, None) or []: + for obsolete_field in ('repo', 'ref'): + if obsolete_field in spec: + cls._warn_obsolete_field(morphology, spec, obsolete_field) + def _require_field(self, field, morphology): if field not in morphology: raise MissingFieldError(field, morphology.filename) @@ -542,9 +576,23 @@ class MorphologyLoader(object): if key in morphology and morphology[key] == defaults[key]: del morphology[key] - if kind in ('stratum', 'cluster'): + if kind in ('system', 'stratum', 'cluster'): getattr(self, '_unset_%s_defaults' % kind)(morphology) + @classmethod + def _set_stratum_specs_defaults(cls, morphology, specs_field): + for spec in morphology.get(specs_field, None) or []: + for obsolete_field in ('repo', 'ref'): + if obsolete_field in spec: + del spec[obsolete_field] + + @classmethod + def _unset_stratum_specs_defaults(cls, morphology, specs_field): + for spec in morphology.get(specs_field, []): + for obsolete_field in ('repo', 'ref'): + if obsolete_field in spec: + del spec[obsolete_field] + def _set_cluster_defaults(self, morph): for system in morph.get('systems', []): if 'deploy-defaults' not in system: @@ -560,7 +608,10 @@ class MorphologyLoader(object): del system['deploy'] def _set_system_defaults(self, morph): - pass + self._set_stratum_specs_defaults(morph, 'strata') + + def _unset_system_defaults(self, morph): + self._unset_stratum_specs_defaults(morph, 'strata') def _set_stratum_defaults(self, morph): for spec in morph['chunks']: @@ -568,6 +619,7 @@ class MorphologyLoader(object): spec['repo'] = spec['name'] if 'morph' not in spec: spec['morph'] = spec['name'] + self._set_stratum_specs_defaults(morph, 'build-depends') def _unset_stratum_defaults(self, morph): for spec in morph['chunks']: @@ -575,6 +627,7 @@ class MorphologyLoader(object): del spec['repo'] if 'morph' in spec and spec['morph'] == spec['name']: del spec['morph'] + self._unset_stratum_specs_defaults(morph, 'strata') def _set_chunk_defaults(self, morph): if morph['max-jobs'] is not None: diff --git a/morphlib/morphloader_tests.py b/morphlib/morphloader_tests.py index bd3e77e3..b8738804 100644 --- a/morphlib/morphloader_tests.py +++ b/morphlib/morphloader_tests.py @@ -16,12 +16,15 @@ # =*= License: GPL-2 =*= +import contextlib import os import shutil import tempfile import unittest +import warnings import morphlib +from morphlib.morphloader import MorphologyObsoleteFieldWarning class MorphologyLoaderTests(unittest.TestCase): @@ -648,10 +651,13 @@ name: foo name='foo', arch='testarch', strata=[ - {'morph': 'bar'}, + { + 'morph': 'bar', + 'repo': 'obsolete', + 'ref': 'obsolete', + }, ]) self.loader.set_defaults(m) - self.loader.validate(m) self.assertEqual( { 'kind': 'system', @@ -675,7 +681,11 @@ name: foo 'name': 'foo', 'arch': 'testarch', 'strata': [ - {'morph': 'bar'}, + { + 'morph': 'bar', + 'repo': None, + 'ref': None, + }, ], 'configuration-extensions': [], }) @@ -788,3 +798,67 @@ name: foo self.assertEqual(m['name'], 'foo') self.assertEqual(m['kind'], 'cluster') self.assertEqual(m['systems'][0]['morph'], 'bar') + + @contextlib.contextmanager + def catch_warnings(*warning_classes): + with warnings.catch_warnings(record=True) as caught_warnings: + warnings.resetwarnings() + for warning_class in warning_classes: + warnings.simplefilter("always", warning_class) + yield caught_warnings + + def test_warns_when_systems_refer_to_strata_with_repo_or_ref(self): + for obsolete_field in ('repo', 'ref'): + m = morphlib.morph3.Morphology( + name="foo", + kind="system", + arch="testarch", + strata=[ + { + 'morph': 'bar', + obsolete_field: 'obsolete', + }]) + + with self.catch_warnings(MorphologyObsoleteFieldWarning) \ + as caught_warnings: + + self.loader.validate(m) + self.assertEqual(len(caught_warnings), 1) + warning = caught_warnings[0].message + self.assertEqual(warning.kind, 'system') + self.assertEqual(warning.morphology_name, 'foo') + self.assertEqual(warning.stratum_name, 'bar') + self.assertEqual(warning.field, obsolete_field) + + def test_warns_when_strata_refer_to_build_depends_with_repo_or_ref(self): + for obsolete_field in ('repo', 'ref'): + m = morphlib.morph3.Morphology( + { + 'name': 'foo', + 'kind': 'stratum', + 'build-depends': [ + { + 'morph': 'bar', + obsolete_field: 'obsolete' + }, + ], + 'chunks': [ + { + 'morph': 'chunk', + 'name': 'chunk', + 'build-mode': 'test', + 'build-depends': [], + }, + ], + }) + + with self.catch_warnings(MorphologyObsoleteFieldWarning) \ + as caught_warnings: + + self.loader.validate(m) + self.assertEqual(len(caught_warnings), 1) + warning = caught_warnings[0].message + self.assertEqual(warning.kind, 'stratum') + self.assertEqual(warning.morphology_name, 'foo') + self.assertEqual(warning.stratum_name, 'bar') + self.assertEqual(warning.field, obsolete_field) diff --git a/morphlib/morphologyfactory.py b/morphlib/morphologyfactory.py index 3462dd36..8a0b047a 100644 --- a/morphlib/morphologyfactory.py +++ b/morphlib/morphologyfactory.py @@ -145,6 +145,11 @@ class MorphologyFactory(object): morphology.needs_artifact_metadata_cached = False + morphlib.morphloader.MorphologyLoader._validate_stratum_specs_fields( + morphology, 'strata') + morphlib.morphloader.MorphologyLoader._set_stratum_specs_defaults( + morphology, 'strata') + def _check_and_tweak_stratum(self, morphology, reponame, sha1, filename): '''Check and tweak a stratum morphology.''' @@ -164,6 +169,11 @@ class MorphologyFactory(object): morphology.builds_artifacts = [morphology['name']] morphology.needs_artifact_metadata_cached = True + morphlib.morphloader.MorphologyLoader._validate_stratum_specs_fields( + morphology, 'build-depends') + morphlib.morphloader.MorphologyLoader._set_stratum_specs_defaults( + morphology, 'build-depends') + def _check_and_tweak_chunk(self, morphology, reponame, sha1, filename): '''Check and tweak a chunk morphology.''' |