summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2023-03-29 08:14:51 -0600
committerGitHub <noreply@github.com>2023-03-29 08:14:51 -0600
commitd7bdba6f941ed04dc39e97af215e46e2baf07507 (patch)
treeef83b16755f9ca86b36e01926783b75737607b9f
parented3ebfc4f6c6c4f70a3be855165933c697469399 (diff)
downloadcloud-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.py9
-rw-r--r--tests/unittests/config/test_schema.py5
-rw-r--r--tests/unittests/helpers.py14
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():