diff --git a/cloudinit/sources/DataSourceWSL.py b/cloudinit/sources/DataSourceWSL.py index ddb31411681..5e146ecc177 100644 --- a/cloudinit/sources/DataSourceWSL.py +++ b/cloudinit/sources/DataSourceWSL.py @@ -405,7 +405,8 @@ def _get_data(self) -> bool: user_data = ConfigData(self.find_user_data_file(seed_dir)) except (ValueError, IOError) as err: - LOG.error( + log = LOG.info if agent_data else LOG.error + log( "Unable to load any user-data file in %s: %s", seed_dir, str(err), diff --git a/doc/rtd/reference/datasources/wsl.rst b/doc/rtd/reference/datasources/wsl.rst index c6970448b5c..8e1644e52f6 100644 --- a/doc/rtd/reference/datasources/wsl.rst +++ b/doc/rtd/reference/datasources/wsl.rst @@ -52,6 +52,8 @@ User data can be supplied in any cloud-config files or shell scripts. At runtime, the WSL datasource looks for user data in the following locations inside the Windows host filesystem, in the order specified below. +The WSL datasource will be enabled if cloud-init discovers at least one of the +applicable config files described below. First, configurations from Ubuntu Pro/Landscape are checked for in the following paths: @@ -71,8 +73,8 @@ following paths: used instead of the one provided by the ``agent.yaml``, which is treated as a default. -Then, if a file from (1) is not found, a user-provided configuration will be -looked for instead in the following order: +Then, if a file from (1) is not found, optional user-provided configuration +will be looked for in the following order: 1. ``%USERPROFILE%\.cloud-init\.user-data`` holds user data for a specific instance configuration. The datasource resolves the name attributed diff --git a/tests/unittests/sources/test_wsl.py b/tests/unittests/sources/test_wsl.py index 1ba374e468f..2012cd90a3c 100644 --- a/tests/unittests/sources/test_wsl.py +++ b/tests/unittests/sources/test_wsl.py @@ -5,6 +5,7 @@ # This file is part of cloud-init. See LICENSE file for license information. import logging import os +import re from copy import deepcopy from email.mime.multipart import MIMEMultipart from pathlib import PurePath @@ -460,6 +461,52 @@ def test_get_data_jinja(self, m_lsb_release, paths, tmpdir): cast(MIMEMultipart, ud), "text/cloud-config" ), "No cloud-config part should exist" + @pytest.mark.parametrize("with_agent_data", [True, False]) + @mock.patch("cloudinit.util.lsb_release") + def test_get_data_x( + self, m_lsb_release, with_agent_data, caplog, paths, tmpdir + ): + """ + Assert behavior of empty .cloud-config dir with and without agent data + """ + m_lsb_release.return_value = SAMPLE_LINUX_DISTRO + data_path = tmpdir.join(".cloud-init", f"{INSTANCE_NAME}.user-data") + data_path.dirpath().mkdir() + + if with_agent_data: + ubuntu_pro_tmp = tmpdir.join(".ubuntupro", ".cloud-init") + os.makedirs(ubuntu_pro_tmp, exist_ok=True) + agent_path = ubuntu_pro_tmp.join("agent.yaml") + agent_path.write(AGENT_SAMPLE) + + ds = wsl.DataSourceWSL( + sys_cfg=SAMPLE_CFG, + distro=_get_distro("ubuntu"), + paths=paths, + ) + + assert ds.get_data() is with_agent_data + if with_agent_data: + assert ds.userdata_raw == AGENT_SAMPLE + else: + assert ds.userdata_raw is None + + expected_log_level = logging.INFO if with_agent_data else logging.ERROR + regex = ( + "Unable to load any user-data file in /[^:]*/.cloud-init:" + " /.*/.cloud-init directory is empty" + ) + messages = [ + x.message + for x in caplog.records + if x.levelno == expected_log_level and re.match(regex, x.message) + ] + assert ( + len(messages) > 0 + ), "Expected log message matching '{}' with log level '{}'".format( + regex, expected_log_level + ) + @mock.patch("cloudinit.util.get_linux_distro") def test_data_precedence(self, m_get_linux_dist, tmpdir, paths): """Validates the precedence of user-data files."""