summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChad Smith <chad.smith@canonical.com>2023-05-12 11:29:15 -0600
committerGitHub <noreply@github.com>2023-05-12 11:29:15 -0600
commit73f34575da4f99a376998516a13c3a79cc640ae3 (patch)
tree06f396d4fcda64ad8cb4476538b50d524c59a2b9
parent9f8450368c2ae713de0a2308f5d3cb73de5b39f2 (diff)
downloadcloud-init-git-73f34575da4f99a376998516a13c3a79cc640ae3.tar.gz
schema: read_cfg_paths call init.fetch to lookup /v/l/c/instance
Fix cloud-init schema --system being unable to find merged userdata stored at /var/lib/cloud/instance/cloud_config.txt. Init.paths.get_ipath only has visibility to merged cloud config in /var/lib/cloud/<instance_id>/cloud-config.txt after fetching the existing cached datasource which provides instance-id from metadata in order to determine the unique instance-id which represents the path to the cloud-config.txt. To support reuse of read_cfg_paths helper function, add an optional parameter fetch_existing_datasource which indicates whether reading the existing datasource is necessary for this helper function. cloud-init schema --system calls read_cfg_paths providing fetch_existing_datasource="trust" prior to calls to paths.get_ipath().
-rw-r--r--[-rwxr-xr-x]cloudinit/cmd/devel/__init__.py12
-rw-r--r--cloudinit/config/schema.py2
-rw-r--r--tests/integration_tests/modules/test_cli.py5
-rw-r--r--tests/unittests/cmd/devel/test_init.py29
-rw-r--r--tests/unittests/config/test_schema.py3
5 files changed, 47 insertions, 4 deletions
diff --git a/cloudinit/cmd/devel/__init__.py b/cloudinit/cmd/devel/__init__.py
index 9a8f2ebd..357c4ae7 100755..100644
--- a/cloudinit/cmd/devel/__init__.py
+++ b/cloudinit/cmd/devel/__init__.py
@@ -17,9 +17,17 @@ def addLogHandlerCLI(logger, log_level):
return logger
-def read_cfg_paths() -> Paths:
- """Return a Paths object based on the system configuration on disk."""
+def read_cfg_paths(fetch_existing_datasource: str = "") -> Paths:
+ """Return a Paths object based on the system configuration on disk.
+
+ :param fetch_existing_datasource: String one of check or trust. Whether to
+ load the pickled datasource before returning Paths. This is necessary
+ when using instance paths via Paths.get_ipath method which are only
+ known from the instance-id metadata in the detected datasource.
+ """
init = Init(ds_deps=[])
+ if fetch_existing_datasource:
+ init.fetch(existing=fetch_existing_datasource)
init.read_cfg()
return init.paths
diff --git a/cloudinit/config/schema.py b/cloudinit/config/schema.py
index 9005e924..4b7e8016 100644
--- a/cloudinit/config/schema.py
+++ b/cloudinit/config/schema.py
@@ -1264,7 +1264,7 @@ def handle_schema_args(name, args):
if args.docs:
print(load_doc(args.docs))
return
- paths = read_cfg_paths()
+ paths = read_cfg_paths(fetch_existing_datasource="trust")
if args.instance_data:
instance_data_path = args.instance_data
elif os.getuid() != 0:
diff --git a/tests/integration_tests/modules/test_cli.py b/tests/integration_tests/modules/test_cli.py
index 30f56ad7..e1bcd7c7 100644
--- a/tests/integration_tests/modules/test_cli.py
+++ b/tests/integration_tests/modules/test_cli.py
@@ -58,7 +58,10 @@ def test_invalid_userdata(client: IntegrationInstance):
result = client.execute("cloud-init schema --system")
assert not result.ok
assert "Cloud config schema errors" in result.stderr
- assert 'needs to begin with "#cloud-config"' in result.stderr
+ assert (
+ "Expected first line to be one of: #!, ## template: jinja,"
+ " #cloud-boothook, #cloud-config" in result.stderr
+ )
result = client.execute("cloud-init status --long")
if not result.ok:
raise AssertionError(
diff --git a/tests/unittests/cmd/devel/test_init.py b/tests/unittests/cmd/devel/test_init.py
new file mode 100644
index 00000000..503bc08f
--- /dev/null
+++ b/tests/unittests/cmd/devel/test_init.py
@@ -0,0 +1,29 @@
+from unittest import mock
+
+from cloudinit import stages
+from cloudinit.cmd.devel import read_cfg_paths
+from tests.unittests.util import TEST_INSTANCE_ID, FakeDataSource
+
+
+class TestReadCfgPaths:
+ def test_read_cfg_paths_fetches_cached_datasource(self, tmpdir):
+ init = stages.Init()
+ init._cfg = {
+ "system_info": {
+ "distro": "ubuntu",
+ "paths": {"cloud_dir": tmpdir, "run_dir": tmpdir},
+ }
+ }
+ with mock.patch("cloudinit.cmd.devel.Init") as m_init:
+ with mock.patch.object(init, "_restore_from_cache") as restore:
+ restore.return_value = FakeDataSource(paths=init.paths)
+ with mock.patch(
+ "cloudinit.util.read_conf_from_cmdline", return_value={}
+ ):
+ m_init.return_value = init
+ paths = read_cfg_paths()
+ assert paths.get_ipath() is None
+ paths = read_cfg_paths(fetch_existing_datasource="trust")
+ assert (
+ paths.get_ipath() == f"/var/lib/cloud/instances/{TEST_INSTANCE_ID}"
+ )
diff --git a/tests/unittests/config/test_schema.py b/tests/unittests/config/test_schema.py
index ceb689a4..b5314b69 100644
--- a/tests/unittests/config/test_schema.py
+++ b/tests/unittests/config/test_schema.py
@@ -2060,3 +2060,6 @@ apt_reboot_if_required: Default: ``false``. Deprecated in version 22.2.\
expected_err.format(cfg_file=user_data_fn, id_path=id_path) == err
)
assert "deprec" not in caplog.text
+ assert read_cfg_paths.call_args_list == [
+ mock.call(fetch_existing_datasource="trust")
+ ]