This commit is contained in:
Sloane Hertel 2026-02-03 19:16:22 -06:00 committed by GitHub
commit 3ca20ca734
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 119 additions and 9 deletions

View file

@ -0,0 +1,2 @@
bugfixes:
- hostname - Fixed use=generic and use=debian and automatic detection for minimal Debian install without dbus (https://github.com/ansible/ansible/issues/85069).

View file

@ -81,9 +81,9 @@ from ansible.module_utils.common.text.converters import to_native, to_text
STRATS = {
'alpine': 'Alpine',
'debian': 'Systemd',
'debian': 'File',
'freebsd': 'FreeBSD',
'generic': 'Base',
'generic': 'File',
'macos': 'Darwin',
'macosx': 'Darwin',
'darwin': 'Darwin',
@ -109,7 +109,7 @@ class BaseStrategy(object):
def update_current_hostname(self):
name = self.module.params['name']
current_name = self.get_current_hostname()
if current_name != name:
if current_name is not None and current_name != name:
if not self.module.check_mode:
self.set_current_hostname(name)
self.changed = True
@ -122,7 +122,7 @@ class BaseStrategy(object):
self.set_permanent_hostname(name)
self.changed = True
def get_current_hostname(self):
def get_current_hostname(self) -> None | str:
return self.get_permanent_hostname()
def set_current_hostname(self, name):
@ -171,9 +171,9 @@ class UnimplementedStrategy(BaseStrategy):
class CommandStrategy(BaseStrategy):
COMMAND = 'hostname'
def __init__(self, module):
def __init__(self, module: AnsibleModule, cmd_required: bool = True):
super(CommandStrategy, self).__init__(module)
self.hostname_cmd = self.module.get_bin_path(self.COMMAND, True)
self.hostname_cmd = self.module.get_bin_path(self.COMMAND, cmd_required)
def get_current_hostname(self):
cmd = [self.hostname_cmd]
@ -195,9 +195,32 @@ class CommandStrategy(BaseStrategy):
pass
class FileStrategy(BaseStrategy):
def requires_hostname_cmd(strategy_method):
def wrapper(self, *args, **kwargs):
if self.hostname_cmd is None:
self.module.warn(
f"The command '{self.COMMAND}' is not in the PATH, "
f"falling back to use the file {self.FILE} exclusively."
)
return
return strategy_method(self, *args, **kwargs)
return wrapper
class FileStrategy(CommandStrategy):
FILE = '/etc/hostname'
def __init__(self, module: AnsibleModule, cmd_required: bool = False):
super().__init__(module, cmd_required=cmd_required)
@requires_hostname_cmd
def set_current_hostname(self, name):
return super().set_current_hostname(name)
@requires_hostname_cmd
def get_current_hostname(self):
return super().get_current_hostname()
def get_permanent_hostname(self):
if not os.path.isfile(self.FILE):
return ''
@ -296,6 +319,11 @@ class SystemdStrategy(BaseStrategy):
super(SystemdStrategy, self).__init__(module)
self.hostnamectl_cmd = self.module.get_bin_path(self.COMMAND, True)
@property
def has_hostnamectl(self):
rc, out, err = self.module.run_command(self.hostnamectl_cmd)
return bool(rc == 0)
def get_current_hostname(self):
cmd = [self.hostnamectl_cmd, '--transient', 'status']
rc, out, err = self.module.run_command(cmd)
@ -612,7 +640,10 @@ class Hostname(object):
self.strategy = strategy(module)
elif platform.system() == 'Linux' and ServiceMgrFactCollector.is_systemd_managed(module):
# This is Linux and systemd is active
self.strategy = SystemdStrategy(module)
if (strategy := SystemdStrategy(module)) and strategy.has_hostnamectl:
self.strategy = strategy
else:
self.strategy = self.strategy_class(module)
else:
self.strategy = self.strategy_class(module)
@ -867,7 +898,7 @@ def main():
changed = hostname.update_current_and_permanent_hostname()
if name != current_hostname:
if current_hostname is not None and name != current_hostname:
name_before = current_hostname
else:
name_before = permanent_hostname

View file

@ -0,0 +1,20 @@
---
- name: Test AlpineStrategy by setting hostname
become: 'yes'
hostname:
use: alpine
name: "{{ ansible_distribution_release }}-bebop.ansible.example.com"
- name: Test AlpineStrategy by getting current hostname
command: hostname
register: get_hostname
- name: Test AlpineStrategy by verifying /etc/hostname content
command: grep -v '^#' /etc/hostname
register: grep_hostname
- name: Test AlpineStrategy using assertions
assert:
that:
- "ansible_distribution_release ~ '-bebop.ansible.example.com' in get_hostname.stdout"
- "ansible_distribution_release ~ '-bebop.ansible.example.com' in grep_hostname.stdout"

View file

@ -18,3 +18,50 @@
that:
- "ansible_distribution_release ~ '-bebop.ansible.example.com' in get_hostname.stdout"
- "ansible_distribution_release ~ '-bebop.ansible.example.com' in grep_hostname.stdout"
- name: Test DebianStrategy without hostname
block:
- name: Get hostname command
shell: which hostname
register: hostname
ignore_errors: True
- name: Move hostname command
shell: "mv {{ hostname.stdout }} {{ hostname.stdout }}.bak"
when: hostname.stdout != ""
become: True
register: moved_hostname
- name: Test no change is made without hostname in the PATH
hostname:
name: "{{ ansible_distribution_release }}-bebop.ansible.example.com"
use: debian
register: debian_no_change
- assert:
that: debian_no_change is not changed
- name: Test updating /etc/hostname
hostname:
name: "{{ ansible_distribution_release }}-bop.ansible.example.com"
use: debian
register: debian_changed
- name: Verify /etc/hostname content
command: grep -v '^#' /etc/hostname
register: grep_hostname
- name: Assert /etc/hostname has been modified
assert:
that:
- debian_changed is changed
- "ansible_distribution_release ~ '-bop.ansible.example.com' in grep_hostname.stdout"
always:
- command: "mv {{ hostname.stdout }}.bak {{ hostname.stdout }}"
when: moved_hostname is defined and moved_hostname is changed
become: True
- name: Verify the current hostname is unchanged
command: hostname
register: current_hostname
failed_when: "ansible_distribution_release ~ '-bop.ansible.example.com' in current_hostname.stdout"

View file

@ -24,3 +24,13 @@
- hn3 is not changed
- current_after_hn2.stdout == 'crocodile.ansible.test.doesthiswork.net.example.com'
- current_after_hn2.stdout == current_after_hn2.stdout
- name: Test use=generic
become: True
hostname:
name: crocodile.ansible.test.doesthiswork.net.example.com
use: generic
register: hn4
- assert:
that: hn4 is not changed