diff options
author | Chad Smith <chad.smith@canonical.com> | 2022-01-11 17:15:17 -0700 |
---|---|---|
committer | Chad Smith <chad.smith@canonical.com> | 2022-01-12 16:08:49 -0700 |
commit | 88c5934381b990bd90569d8d74b6937e33d4cfae (patch) | |
tree | 6759f9125d02e7098ef46d41ff94c44e492ea637 | |
parent | 9917d60c11384b32a6c1e2d2c1182da017cf6189 (diff) | |
download | cloud-init-git-88c5934381b990bd90569d8d74b6937e33d4cfae.tar.gz |
schema: warn when multiple schema files present, but use the first one
-rw-r--r-- | cloudinit/config/schema.py | 14 | ||||
-rw-r--r-- | conftest.py | 1 | ||||
-rw-r--r-- | tests/unittests/config/test_schema.py | 71 |
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: |