From 090e0282fbf4bb0c295339576e4f1331d576135f Mon Sep 17 00:00:00 2001 From: Mohith1612 Date: Thu, 27 Aug 2026 21:29:08 +0530 Subject: [PATCH] fix(ssh_import_id): honor ssh_import_id on the default user handle() read ssh_import_id from the top-level config when the user was the default one, and from the user definition for everyone else. An ssh_import_id set inside a default user definition was therefore parsed, matched by the module's own activation check, and then silently dropped. Merge both sources for the default user. A top-level ssh_import_id keeps working on its own, and non-default users are unaffected. Fixes GH-4306 --- cloudinit/config/cc_ssh_import_id.py | 5 ++- .../unittests/config/test_cc_ssh_import_id.py | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/cloudinit/config/cc_ssh_import_id.py b/cloudinit/config/cc_ssh_import_id.py index 3bd1790d3ae..b3a1f067e6d 100644 --- a/cloudinit/config/cc_ssh_import_id.py +++ b/cloudinit/config/cc_ssh_import_id.py @@ -64,7 +64,10 @@ def handle(name: str, cfg: Config, cloud: Cloud, args: list) -> None: for user, user_cfg in users.items(): import_ids = [] if user_cfg["default"]: - import_ids = util.get_cfg_option_list(cfg, "ssh_import_id", []) + import_ids = util.uniq_merge( + util.get_cfg_option_list(cfg, "ssh_import_id", []), + user_cfg.get("ssh_import_id", []), + ) else: try: import_ids = user_cfg["ssh_import_id"] diff --git a/tests/unittests/config/test_cc_ssh_import_id.py b/tests/unittests/config/test_cc_ssh_import_id.py index 2daffdd9997..fcf13c935bf 100644 --- a/tests/unittests/config/test_cc_ssh_import_id.py +++ b/tests/unittests/config/test_cc_ssh_import_id.py @@ -78,6 +78,47 @@ def test_skip_inapplicable_configs(self, m_which, cfg, log, caplog): cc_ssh_import_id.handle("name", cfg, cloud, []) assert log in caplog.text + @pytest.mark.parametrize( + "cfg,expected", + ( + pytest.param( + {"user": {"name": "dave", "ssh_import_id": ["lp:user"]}}, + [mock.call(["lp:user"], "dave")], + id="default_user_config_is_honored", + ), + pytest.param( + {"user": {"name": "dave"}, "ssh_import_id": ["lp:top"]}, + [mock.call(["lp:top"], "dave")], + id="top_level_config_is_honored", + ), + pytest.param( + { + "user": {"name": "dave", "ssh_import_id": ["lp:user"]}, + "ssh_import_id": ["lp:top"], + }, + [mock.call(["lp:top", "lp:user"], "dave")], + id="both_are_merged", + ), + pytest.param( + { + "users": [ + "default", + {"name": "bob", "ssh_import_id": ["lp:bob"]}, + ] + }, + [mock.call(["lp:bob"], "bob")], + id="non_default_user_is_unaffected", + ), + ), + ) + @mock.patch(MODPATH + "import_ssh_ids") + @mock.patch(MODPATH + "subp.which") + def test_ssh_import_id_sources(self, m_which, m_import, cfg, expected): + """ssh_import_id on the default user is not ignored.""" + m_which.return_value = "/usr/bin/ssh-import-id" + cc_ssh_import_id.handle("name", cfg, get_cloud("ubuntu"), []) + assert expected == m_import.call_args_list + @mock.patch(MODPATH + "pwd.getpwnam") @mock.patch(MODPATH + "subp.subp") @mock.patch(MODPATH + "subp.which")