summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2022-01-11 17:15:17 -0700
committerChad Smith <chad.smith@canonical.com>2022-01-12 16:08:49 -0700
commit88c5934381b990bd90569d8d74b6937e33d4cfae (patch)
tree6759f9125d02e7098ef46d41ff94c44e492ea637
parent9917d60c11384b32a6c1e2d2c1182da017cf6189 (diff)
downloadcloud-init-git-88c5934381b990bd90569d8d74b6937e33d4cfae.tar.gz
schema: warn when multiple schema files present, but use the first one
-rw-r--r--cloudinit/config/schema.py14
-rw-r--r--conftest.py1
-rw-r--r--tests/unittests/config/test_schema.py71
3 files changed, 79 insertions, 7 deletions
diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py
index 3fb5dbe8..7e445043 100644
--- a/cloudinit/config/schema.py
+++ b/cloudinit/config/schema.py
@@ -633,11 +633,19 @@ def get_schema() -> dict:
os.path.join(paths.schema_dir, "cloud-init-schema-*")
)
full_schema = None
- for schema_file in schema_files:
+ if len(schema_files) > 1:
+ LOG.warning(
+ "Found multiple cloud-init-schema files in %s. Ignoring %s",
+ paths.schema_dir,
+ ", ".join(schema_files[1:]),
+ )
+ elif len(schema_files) == 1:
try:
- full_schema = json.loads(load_file(schema_file))
+ full_schema = json.loads(load_file(schema_files[0]))
except Exception as e:
- LOG.warning("Cannot parse JSON schema file %s. %s", schema_file, e)
+ LOG.warning(
+ "Cannot parse JSON schema file %s. %s", schema_files[0], e
+ )
if not full_schema:
LOG.warning(
"No base JSON schema files found at %s/cloud-init-schema-*."
diff --git a/conftest.py b/conftest.py
index 3979eb0a..6ec78b0a 100644
--- a/conftest.py
+++ b/conftest.py
@@ -198,6 +198,7 @@ def paths(tmpdir):
(This uses the builtin tmpdir fixture.)
"""
dirs = {
+ "schema_dir": tmpdir.mkdir("schema").strpath,
"cloud_dir": tmpdir.mkdir("cloud_dir").strpath,
"run_dir": tmpdir.mkdir("run_dir").strpath,
}
diff --git a/tests/unittests/config/test_schema.py b/tests/unittests/config/test_schema.py
index 35631734..4b2e4113 100644
--- a/tests/unittests/config/test_schema.py
+++ b/tests/unittests/config/test_schema.py
@@ -29,6 +29,7 @@ from cloudinit.config.schema import (
validate_cloudconfig_metaschema,
validate_cloudconfig_schema,
)
+from cloudinit.util import copy as util_copy
from cloudinit.util import write_file
from tests.unittests.helpers import (
CiTestCase,
@@ -82,8 +83,40 @@ def get_module_variable(var_name) -> dict:
class TestGetSchema:
- def test_get_schema_coalesces_known_schema(self):
+ @mock.patch("cloudinit.config.schema.read_cfg_paths")
+ def test_get_schema_warn_on_multiple_schema_files(
+ self, read_cfg_paths, paths, caplog
+ ):
+ """Warn about ignored files when multiple schemas in schema_dir."""
+ read_cfg_paths.return_value = paths
+
+ for schema_file in Path(cloud_init_project_dir("config/")).glob(
+ "cloud-init-schema*.json"
+ ):
+ util_copy(schema_file, paths.schema_dir)
+ # Add a duplicate entry that'll be warned
+ duplicate_schema = (
+ f"{paths.schema_dir}/{schema_file.name}".replace(
+ ".json", ".2.json"
+ )
+ )
+ util_copy(schema_file, duplicate_schema)
+ get_schema()
+ assert (
+ f"Found multiple cloud-init-schema files in {paths.schema_dir}"
+ in caplog.text
+ )
+
+ @mock.patch("cloudinit.config.schema.read_cfg_paths")
+ def test_get_schema_coalesces_known_schema(self, read_cfg_paths, paths):
"""Every cloudconfig module with schema is listed in allOf keyword."""
+ read_cfg_paths.return_value = paths
+
+ # Copy existing packages base schema files into paths.schema_dir
+ for schema_file in Path(cloud_init_project_dir("config/")).glob(
+ "cloud-init-schema*.json"
+ ):
+ util_copy(schema_file, paths.schema_dir)
schema = get_schema()
assert sorted(
[
@@ -107,9 +140,39 @@ class TestGetSchema:
[meta["id"] for meta in get_metas().values() if meta is not None]
)
assert "http://json-schema.org/draft-04/schema#" == schema["$schema"]
- assert ["$defs", "$schema", "allOf"] == sorted(
- list(get_schema().keys())
- )
+ assert ["$defs", "$schema", "allOf"] == sorted(list(schema.keys()))
+ # New style schema should be defined in static schema file in $defs
+ expected_subschema_defs = [
+ {"$ref": "#/$defs/cc_apt_pipelining"},
+ ]
+ found_subschema_defs = []
+ legacy_schema_keys = []
+ for subschema in schema["allOf"]:
+ if "$ref" in subschema:
+ found_subschema_defs.append(subschema)
+ else: # Legacy subschema sourced from cc_* module 'schema' attr
+ legacy_schema_keys.extend(subschema["properties"].keys())
+
+ assert expected_subschema_defs == found_subschema_defs
+ # This list will dwindle as we move legacy schema to new $defs
+ assert [
+ "apk_repos",
+ "apt",
+ "bootcmd",
+ "chef",
+ "drivers",
+ "locale",
+ "locale_configfile",
+ "ntp",
+ "resize_rootfs",
+ "runcmd",
+ "snap",
+ "ubuntu_advantage",
+ "updates",
+ "write_files",
+ "write_files",
+ "zypper",
+ ] == sorted(legacy_schema_keys)
class TestLoadDoc: