From 92ee1fa499fabdb6d0c6099da145769445a76877 Mon Sep 17 00:00:00 2001 From: Abhijeet Kasurde Date: Mon, 28 Oct 2024 09:54:16 -0700 Subject: [PATCH] [stable-2.17] debconf: set empty password value (#84034) Fixes: #83214 Signed-off-by: Abhijeet Kasurde (cherry picked from commit 79e8c4c26c85f39101dad71ae4ecf468f939583b) --- .../fragments/debconf_empty_password.yml | 3 + lib/ansible/modules/debconf.py | 29 +++--- .../targets/debconf/tasks/main.yml | 99 +++++++++++++------ test/units/modules/test_debconf.py | 63 ++++++++++++ 4 files changed, 153 insertions(+), 41 deletions(-) create mode 100644 changelogs/fragments/debconf_empty_password.yml create mode 100644 test/units/modules/test_debconf.py diff --git a/changelogs/fragments/debconf_empty_password.yml b/changelogs/fragments/debconf_empty_password.yml new file mode 100644 index 00000000000..473dc53e0d5 --- /dev/null +++ b/changelogs/fragments/debconf_empty_password.yml @@ -0,0 +1,3 @@ +--- +bugfixes: + - debconf - set empty password values (https://github.com/ansible/ansible/issues/83214). diff --git a/lib/ansible/modules/debconf.py b/lib/ansible/modules/debconf.py index 259d92b132b..2d89a2be134 100644 --- a/lib/ansible/modules/debconf.py +++ b/lib/ansible/modules/debconf.py @@ -134,21 +134,24 @@ def get_password_value(module, pkg, question, vtype): cmd = [getsel] rc, out, err = module.run_command(cmd) if rc != 0: - module.fail_json(msg="Failed to get the value '%s' from '%s'" % (question, pkg)) + module.fail_json(msg=f"Failed to get the value '{question}' from '{pkg}': {err}") - desired_line = None for line in out.split("\n"): - if line.startswith(pkg): - desired_line = line - break - - if not desired_line: - module.fail_json(msg="Failed to find the value '%s' from '%s'" % (question, pkg)) - - (dpkg, dquestion, dvtype, dvalue) = desired_line.split() - if dquestion == question and dvtype == vtype: - return dvalue - return '' + if not line.startswith(pkg): + continue + + # line is a collection of tab separated values + fields = line.split('\t') + if len(fields) <= 3: + # No password found, return a blank password + return '' + try: + if fields[1] == question and fields[2] == vtype: + # If correct question and question type found, return password value + return fields[3] + except IndexError: + # Fail safe + return '' def get_selections(module, pkg): diff --git a/test/integration/targets/debconf/tasks/main.yml b/test/integration/targets/debconf/tasks/main.yml index 1b5877e034e..f650d3e7019 100644 --- a/test/integration/targets/debconf/tasks/main.yml +++ b/test/integration/targets/debconf/tasks/main.yml @@ -1,21 +1,7 @@ # Test code for the debconf module. -# (c) 2017, James Tanner - -# This file is part of Ansible -# -# Ansible is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# Ansible is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with Ansible. If not, see . - +# Copyright: (c) 2017, James Tanner +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) ## ## debconf query ## @@ -23,16 +9,16 @@ - block: - name: query the tzdata package debconf: - name: tzdata + name: tzdata register: debconf_test0 - name: validate results for test 0 assert: - that: - - 'debconf_test0.changed is defined' - - 'debconf_test0.current is defined' - - '"tzdata/Zones/Etc" in debconf_test0.current' - - 'debconf_test0.current["tzdata/Zones/Etc"] == "UTC"' + that: + - 'debconf_test0.changed is defined' + - 'debconf_test0.current is defined' + - '"tzdata/Zones/Etc" in debconf_test0.current' + - 'debconf_test0.current["tzdata/Zones/Etc"] == "UTC"' - name: install debconf-utils apt: @@ -46,12 +32,13 @@ question: ddclient/password value: "MySecretValue" vtype: password + no_log: false register: debconf_test1 - name: validate results for test 1 assert: - that: - - debconf_test1.changed + that: + - debconf_test1.changed - name: Change password again debconf: @@ -59,13 +46,69 @@ question: ddclient/password value: "MySecretValue" vtype: password - no_log: yes + no_log: false register: debconf_test2 - name: validate results for test 1 assert: - that: - - not debconf_test2.changed + that: + - not debconf_test2.changed + + - name: Check if empty password is set + debconf: + name: ddclient2 + question: ddclient/password + value: "" + vtype: password + no_log: false + register: debconf_test1 + + - name: validate if password is set to empty value + assert: + that: + - debconf_test1.changed + + - name: Change empty password again (idempotency) + debconf: + name: ddclient2 + question: ddclient/password + value: "MySecretValue" + vtype: password + no_log: false + register: debconf_test2 + + - name: validate if the empty password is changed to the given value + assert: + that: + - debconf_test2.changed + + - name: Set different question for same package name + debconf: + name: ddclient2 + question: ddclient/password1 + value: "Sample" + vtype: password + no_log: false + register: debconf_test1 + + - name: validate if different question for same package name is set + assert: + that: + - debconf_test1.changed + + - name: Set different question for same package name again (idempotency) + debconf: + name: ddclient2 + question: ddclient/password1 + value: "Sample" + vtype: password + no_log: false + register: debconf_test2 + + - name: validate if different question for same package name is set (idempotency) + assert: + that: + - not debconf_test2.changed - name: Multiselect value debconf: diff --git a/test/units/modules/test_debconf.py b/test/units/modules/test_debconf.py new file mode 100644 index 00000000000..2ec1d6c29b2 --- /dev/null +++ b/test/units/modules/test_debconf.py @@ -0,0 +1,63 @@ +# Copyright: Contributors to the Ansible project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) +from __future__ import annotations + +import pytest + +from ansible.modules.debconf import get_password_value + + +password_testdata = [ + pytest.param( + ( + "ddclient2 ddclient/password1 password Sample", + "ddclient2", + "ddclient/password1", + "password", + ), + "Sample", + id="valid_password", + ), + pytest.param( + ( + "ddclient2 ddclient/password1 password", + "ddclient2", + "ddclient/password1", + "password", + ), + '', + id="invalid_password", + ), + pytest.param( + ( + "ddclient2 ddclient/password password", + "ddclient2", + "ddclient/password1", + "password", + ), + '', + id="invalid_password_none", + ), + pytest.param( + ( + "ddclient2 ddclient/password", + "ddclient2", + "ddclient/password", + "password", + ), + '', + id="invalid_line", + ), +] + + +@pytest.mark.parametrize("test_input,expected", password_testdata) +def test_get_password_value(mocker, test_input, expected): + module = mocker.MagicMock() + mocker.patch.object( + module, "get_bin_path", return_value="/usr/bin/debconf-get-selections" + ) + mocker.patch.object(module, "run_command", return_value=(0, test_input[0], "")) + + res = get_password_value(module, *test_input[1:]) + assert res == expected