diff --git a/src/pyinfra/facts/bsdinit.py b/src/pyinfra/facts/bsdinit.py index f4c5a1286..a0f2e7e17 100644 --- a/src/pyinfra/facts/bsdinit.py +++ b/src/pyinfra/facts/bsdinit.py @@ -2,6 +2,8 @@ from typing_extensions import override +from pyinfra.api import FactBase + from .sysvinit import InitdStatus @@ -21,3 +23,23 @@ def command(self) -> str: """ default = dict + + +class RcdEnabled(FactBase): + """ + Returns a dict of OpenBSD rc.d services enabled at boot. + """ + + default = dict + + @override + def requires_command(self) -> str: + return "rcctl" + + @override + def command(self) -> str: + return "rcctl ls on" + + @override + def process(self, output: list[str]) -> dict[str, bool]: + return {line: True for line in output} diff --git a/src/pyinfra/operations/bsdinit.py b/src/pyinfra/operations/bsdinit.py index 8165f0922..62479db1c 100644 --- a/src/pyinfra/operations/bsdinit.py +++ b/src/pyinfra/operations/bsdinit.py @@ -5,14 +5,33 @@ from __future__ import annotations from pyinfra import host -from pyinfra.api import operation -from pyinfra.facts.bsdinit import RcdStatus +from pyinfra.api import QuoteString, StringCommand, operation +from pyinfra.facts.bsdinit import RcdEnabled, RcdStatus from pyinfra.facts.server import Os from . import files from .util.service import handle_service_control +def _handle_openbsd_enabled(service: str, enabled: bool): + enabled_services = host.get_fact(RcdEnabled) + is_enabled = enabled_services.get(service, False) + + if enabled is True: + if not is_enabled: + yield StringCommand("rcctl enable", QuoteString(service)) + enabled_services[service] = True + else: + host.noop(f"service {service} is enabled") + + if enabled is False: + if is_enabled: + yield StringCommand("rcctl disable", QuoteString(service)) + enabled_services[service] = False + else: + host.noop(f"service {service} is disabled") + + @operation() def service( service: str, @@ -33,15 +52,21 @@ def service( + enabled: whether this service should be enabled/disabled on boot """ + os = host.get_fact(Os) status_argument = "status" - if host.get_fact(Os) == "OpenBSD": + command_formatter = "test -e /etc/rc.d/{0} && /etc/rc.d/{0} {1} || /usr/local/etc/rc.d/{0} {1}" + if os == "OpenBSD": status_argument = "check" + command_formatter = "rcctl {1} {0}" + + if enabled is True: + yield from _handle_openbsd_enabled(service, enabled) yield from handle_service_control( host, service, host.get_fact(RcdStatus), - "test -e /etc/rc.d/{0} && /etc/rc.d/{0} {1} || /usr/local/etc/rc.d/{0} {1}", + command_formatter, running, restarted, reloaded, @@ -49,6 +74,11 @@ def service( status_argument=status_argument, ) + if os == "OpenBSD": + if enabled is False: + yield from _handle_openbsd_enabled(service, enabled) + return + # BSD init is simple, just add/remove _enabled="YES" if isinstance(enabled, bool): yield from files.line._inner( diff --git a/tests/facts/bsdinit.RcdEnabled/services.yaml b/tests/facts/bsdinit.RcdEnabled/services.yaml new file mode 100644 index 000000000..dcf3c4152 --- /dev/null +++ b/tests/facts/bsdinit.RcdEnabled/services.yaml @@ -0,0 +1,8 @@ +command: rcctl ls on +requires_command: rcctl +output: | + cron + rad +fact: + cron: true + rad: true diff --git a/tests/operations/bsdinit.service/openbsd_disabled.yaml b/tests/operations/bsdinit.service/openbsd_disabled.yaml new file mode 100644 index 000000000..c8d512574 --- /dev/null +++ b/tests/operations/bsdinit.service/openbsd_disabled.yaml @@ -0,0 +1,12 @@ +args: + - rad +kwargs: + enabled: false +facts: + server.Os: OpenBSD + bsdinit.RcdStatus: + rad: true + bsdinit.RcdEnabled: + rad: true +commands: + - rcctl disable rad diff --git a/tests/operations/bsdinit.service/openbsd_enabled_start.yaml b/tests/operations/bsdinit.service/openbsd_enabled_start.yaml new file mode 100644 index 000000000..373be89b6 --- /dev/null +++ b/tests/operations/bsdinit.service/openbsd_enabled_start.yaml @@ -0,0 +1,12 @@ +args: + - rad +kwargs: + enabled: true +facts: + server.Os: OpenBSD + bsdinit.RcdStatus: + rad: false + bsdinit.RcdEnabled: {} +commands: + - rcctl enable rad + - rcctl start rad