Skip to content

Commit b178163

Browse files
committed
Validate config during upgrades if possible
1 parent 725daaf commit b178163

File tree

3 files changed

+54
-7
lines changed

3 files changed

+54
-7
lines changed

assigner/config/__init__.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from collections import UserDict
55

6-
from assigner.config.versions import upgrade, validate, ValidationError
6+
from assigner.config.versions import upgrade, validate, ValidationError, VersionError
77

88

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

3943
def __enter__(self):
4044
return self

assigner/config/versions.py

+43-5
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,22 @@ class ValidationError(jsonschema.ValidationError):
1111
pass
1212

1313

14-
def validate(config, version=(len(SCHEMAS) - 1)):
15-
assert version < len(SCHEMAS)
14+
class VersionError(Exception):
15+
pass
16+
17+
18+
class UpgradeError(Exception):
19+
pass
20+
21+
22+
def validate(config, version=None):
23+
if version is None:
24+
version = get_version(config)
25+
26+
if version >= len(SCHEMAS):
27+
raise VersionError(
28+
"Configuration version %d is newer than latest known configuration version %d" % (version, len(SCHEMAS) - 1)
29+
)
1630

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

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

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

57+
# Determine whether we should look for upgrade-caused
58+
# validation errors. If the initial config doesn't validate,
59+
# we can't tell whether upgrading has made things worse, but
60+
# we'll try anyway.
61+
try:
62+
validate(config, current)
63+
is_valid = True
64+
except ValidationError:
65+
is_valid = False
66+
4667
for version in range(current, latest):
4768
config = UPGRADES[version](config)
69+
70+
# Upgrade validation.
71+
# Upgrades should be rare, so we can afford to be very particular about them.
4872
assert get_version(config) == version + 1
73+
if is_valid:
74+
try:
75+
validate(config, version + 1)
76+
except ValidationError as e:
77+
# pylint: disable=bad-continuation
78+
raise UpgradeError(
79+
"""
80+
Upgrading configuration from version %d to %d resulted in an invalid configuration:
81+
%s
82+
83+
This is a bug. Please file an issue at https://github.com/redkyn/assigner/issues with your configuration.
84+
Your original configuration has been restored.
85+
""" % (version, version + 1, e.message)
86+
)
4987

5088
return config

assigner/tests/config_test.py

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from assigner.tests.utils import AssignerTestCase
22

3-
from assigner.config.versions import validate, get_version, upgrade, ValidationError
3+
from assigner.config.versions import validate, get_version, upgrade, ValidationError, VersionError
44
from assigner.config.upgrades import UPGRADES
55
from assigner.config.schemas import SCHEMAS
66

@@ -41,6 +41,7 @@
4141
},
4242
]
4343

44+
TOO_NEW_CONFIG = {"version": len(SCHEMAS)}
4445

4546
class UpgradeTester(AssignerTestCase):
4647
def test_that_we_are_testing_all_schemas_and_upgrades(self):
@@ -86,3 +87,7 @@ def test_empty_config_upgrade(self):
8687
for config in EMPTY_CONFIGS:
8788
config = upgrade(config)
8889
self.assertEqual(config, EMPTY_CONFIGS[-1])
90+
91+
def test_too_new_config(self):
92+
with self.assertRaises(VersionError):
93+
validate(TOO_NEW_CONFIG)

0 commit comments

Comments
 (0)