Skip to content

Begin versioning Assigner configs #111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Feb 11, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Validate config during upgrades if possible
  • Loading branch information
LinuxMercedes committed Feb 9, 2018
commit 1bf7aeb6541d13f1422ec217da5d60a55e940bfe
6 changes: 5 additions & 1 deletion assigner/config/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from collections import UserDict

from assigner.config.versions import upgrade, validate, ValidationError
from assigner.config.versions import upgrade, validate, ValidationError, VersionError


class DuplicateUserError(Exception):
Expand Down Expand Up @@ -35,6 +35,10 @@ def __init__(self, filename):
validate(self.data)
except ValidationError as e:
logging.warning("Your configuration is not valid: %s", e.message)
except VersionError as e:
logging.warning(e)
logging.warning("Is your installation of Assigner up to date?")
logging.warning("Attempting to continue anyway...")

def __enter__(self):
return self
Expand Down
48 changes: 43 additions & 5 deletions assigner/config/versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,22 @@ class ValidationError(jsonschema.ValidationError):
pass


def validate(config, version=(len(SCHEMAS) - 1)):
assert version < len(SCHEMAS)
class VersionError(Exception):
pass


class UpgradeError(Exception):
pass


def validate(config, version=None):
if version is None:
version = get_version(config)

if version >= len(SCHEMAS):
raise VersionError(
"Configuration version %d is newer than latest known configuration version %d" % (version, len(SCHEMAS) - 1)
)

try:
jsonschema.validate(config, SCHEMAS[version])
Expand All @@ -35,16 +49,40 @@ def upgrade(config):
latest = len(SCHEMAS) - 1

if current > latest:
logger.warning("Configuration version %d is newer than latest known configuration version %d", current, latest)
logger.warning("Is your installation of Assigner up to date?")
logger.warning("Attempting to continue anyway...")
return config

if current != latest:
logger.info("Migrating configuration from version %d to version %d.", current, latest)

# Determine whether we should look for upgrade-caused
# validation errors. If the initial config doesn't validate,
# we can't tell whether upgrading has made things worse, but
# we'll try anyway.
try:
validate(config, current)
is_valid = True
except ValidationError:
is_valid = False

for version in range(current, latest):
config = UPGRADES[version](config)

# Upgrade validation.
# Upgrades should be rare, so we can afford to be very particular about them.
assert get_version(config) == version + 1
if is_valid:
try:
validate(config, version + 1)
except ValidationError as e:
# pylint: disable=bad-continuation
raise UpgradeError(
"""
Upgrading configuration from version %d to %d resulted in an invalid configuration:
%s

This is a bug. Please file an issue at https://github.com/redkyn/assigner/issues with your configuration.
Your original configuration has been restored.
""" % (version, version + 1, e.message)
)

return config
7 changes: 6 additions & 1 deletion assigner/tests/config_test.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from assigner.tests.utils import AssignerTestCase

from assigner.config.versions import validate, get_version, upgrade, ValidationError
from assigner.config.versions import validate, get_version, upgrade, ValidationError, VersionError
from assigner.config.upgrades import UPGRADES
from assigner.config.schemas import SCHEMAS

Expand Down Expand Up @@ -41,6 +41,7 @@
},
]

TOO_NEW_CONFIG = {"version": len(SCHEMAS)}

class UpgradeTester(AssignerTestCase):
def test_that_we_are_testing_all_schemas_and_upgrades(self):
Expand Down Expand Up @@ -86,3 +87,7 @@ def test_empty_config_upgrade(self):
for config in EMPTY_CONFIGS:
config = upgrade(config)
self.assertEqual(config, EMPTY_CONFIGS[-1])

def test_too_new_config(self):
with self.assertRaises(VersionError):
validate(TOO_NEW_CONFIG)