diff options
author | Chad Smith <chad.smith@canonical.com> | 2023-03-29 08:14:51 -0600 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-29 08:14:51 -0600 |
commit | d7bdba6f941ed04dc39e97af215e46e2baf07507 (patch) | |
tree | ef83b16755f9ca86b36e01926783b75737607b9f | |
parent | ed3ebfc4f6c6c4f70a3be855165933c697469399 (diff) | |
download | cloud-init-git-d7bdba6f941ed04dc39e97af215e46e2baf07507.tar.gz |
schema: do not manipulate draft4 metaschema for jsonschema 2.6.0 (#2098)
Only set additionalProperties = False on draft4 schema
definition in jsonschema 3.0 or greater
because cloud-init globally registers its draft4 extensions
as the primary validator for any draft4-based schemas in
the same python process.
This affects solutions such as subiquity and
ubuntu-desktop-installer which invoke jsonschema.validate
in the same process at runtime just after calling
cloudinit.schema.get_jsonschema_validator.
The resulting Tracebacks are seen as something like:
jsonschema.exceptions.SchemaError:
{'$ref': '#/definitions/ref_id'} is not valid under any of the
given schema
Background:
cloud-init needs to extend draft4 schema to better
validate and warn 'deprecated' properties in draft4-based
cloud-init schema definitions. Our unittests also attempt
to strictly validate any meta schema definitions for the
cc_* config modules.
To accomplish strict meta schema validation cloud-init makes
a copy of the draft4 meta schema and adds an
'additionalProperties' = True to that schema to raise specific
errors and catch typos in cc_ module schema definitions.
Given that cloud-init at runtime extends and registers
a draft4 schema validator, any external consumers
of jsonschema.validate with draft4-base schemas are
exposed to cloud-init's validator so let's limit our risk
exposure.
For python 2.6.0, we cannot specify make draft4 schema
strict because any "$ref" keys are not yet resolved
to their actual #/defintions/<id> values so the traceback above
will always be generated in 'strict' mode for complex schemas.
This does not affect jsonschema 3.0+ which appears to resolve
schema $refs values before schema validation.
-rw-r--r-- | cloudinit/config/schema.py | 9 | ||||
-rw-r--r-- | tests/unittests/config/test_schema.py | 5 | ||||
-rw-r--r-- | tests/unittests/helpers.py | 14 |
3 files changed, 22 insertions, 6 deletions
diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py index 0669defc..9886bde6 100644 --- a/cloudinit/config/schema.py +++ b/cloudinit/config/schema.py @@ -319,8 +319,7 @@ def get_jsonschema_validator(): # type jsonschema attributes in cloud-init's schema. # This allows #cloud-config to provide valid yaml "content: !!binary | ..." - strict_metaschema = deepcopy(Draft4Validator.META_SCHEMA) - strict_metaschema["additionalProperties"] = False + meta_schema = deepcopy(Draft4Validator.META_SCHEMA) # This additional label allows us to specify a different name # than the property key when generating docs. @@ -328,10 +327,11 @@ def get_jsonschema_validator(): # otherwise the property label in the generated docs will be a # regular expression. # http://json-schema.org/understanding-json-schema/reference/object.html#pattern-properties - strict_metaschema["properties"]["label"] = {"type": "string"} + meta_schema["properties"]["label"] = {"type": "string"} validator_kwargs = {} if hasattr(Draft4Validator, "TYPE_CHECKER"): # jsonschema 3.0+ + meta_schema["additionalProperties"] = False # Unsupported in 2.6.0 type_checker = Draft4Validator.TYPE_CHECKER.redefine( "string", is_schema_byte_string ) @@ -339,6 +339,7 @@ def get_jsonschema_validator(): "type_checker": type_checker, } else: # jsonschema 2.6 workaround + # pylint:disable-next=no-member types = Draft4Validator.DEFAULT_TYPES # pylint: disable=E1101 # Allow bytes as well as string (and disable a spurious unsupported # assignment-operation pylint warning which appears because this @@ -354,7 +355,7 @@ def get_jsonschema_validator(): validators["anyOf"] = _anyOf cloudinitValidator = create( - meta_schema=strict_metaschema, + meta_schema=meta_schema, validators=validators, version="draft4", **validator_kwargs, diff --git a/tests/unittests/config/test_schema.py b/tests/unittests/config/test_schema.py index 8276511d..0e49dbf3 100644 --- a/tests/unittests/config/test_schema.py +++ b/tests/unittests/config/test_schema.py @@ -51,6 +51,7 @@ from tests.unittests.helpers import ( mock, skipUnlessHypothesisJsonSchema, skipUnlessJsonSchema, + skipUnlessJsonSchemaVersionGreaterThan, ) from tests.unittests.util import FakeDataSource @@ -422,7 +423,7 @@ class TestValidateCloudConfigSchema: context_mgr.value ) - @skipUnlessJsonSchema() + @skipUnlessJsonSchemaVersionGreaterThan(version=(3, 0, 0)) def test_validateconfig_strict_metaschema_do_not_raise_exception( self, caplog ): @@ -1770,7 +1771,7 @@ class TestStrictMetaschema: else: logging.warning("module %s has no schema definition", name) - @skipUnlessJsonSchema() + @skipUnlessJsonSchemaVersionGreaterThan(version=(3, 0, 0)) def test_validate_bad_module(self): """Throw exception by default, don't throw if throw=False diff --git a/tests/unittests/helpers.py b/tests/unittests/helpers.py index 32503fb8..0656da33 100644 --- a/tests/unittests/helpers.py +++ b/tests/unittests/helpers.py @@ -491,9 +491,23 @@ try: import jsonschema assert jsonschema # avoid pyflakes error F401: import unused + _jsonschema_version = tuple( + int(part) for part in jsonschema.__version__.split(".") # type: ignore + ) _missing_jsonschema_dep = False except ImportError: _missing_jsonschema_dep = True + _jsonschema_version = (0, 0, 0) + + +def skipUnlessJsonSchemaVersionGreaterThan(version=(0, 0, 0)): + return skipIf( + _jsonschema_version <= version, + reason=( + f"python3-jsonschema {_jsonschema_version} not greater than" + f" {version}" + ), + ) def skipUnlessJsonSchema(): |