Moving service state tests to pytest.

This commit is contained in:
Gareth J. Greenaway 2021-03-01 17:16:12 -08:00 committed by Megan Wilhite
parent 2b81f5ba0f
commit 4bb4edde40
3 changed files with 556 additions and 599 deletions

View file

@ -23,7 +23,7 @@ salt/modules/(aix_group|groupadd|mac_group|pw_group|solaris_group|win_groupadd)\
- unit.states.test_group
salt/modules/(debian_service|freebsdservice|gentoo_service|launchctl_service|mac_service|netbsdservice|openbsdrcctl_service|openbsdservice|rh_service|runit|linux_service|smf_service|systemd_service|upstart_service|win_service)\.py:
- unit.states.test_service
- pytests.unit.states.test_service
- integration.modules.test_service
- integration.states.test_service

View file

@ -2,45 +2,522 @@
:codeauthor: Gareth J. Greenaway <ggreenaway@vmware.com>
"""
import logging
import os
import pytest
import salt.modules.beacons as beaconmod
import salt.states.beacon as beaconstate
import salt.states.service as servicestate
import salt.states.service as service
import salt.utils.platform
from salt.utils.event import SaltEvent
from tests.support.mock import MagicMock, patch
log = logging.getLogger(__name__)
def func(name):
"""
Mock func method
"""
return name
@pytest.fixture
def configure_loader_modules():
return {
servicestate: {
service: {
"__env__": "base",
"__salt__": {},
"__opts__": {"test": False, "cachedir": ""},
"__instance_id__": "",
"__low__": {},
"__utils__": {},
"__context__": {},
},
beaconstate: {"__salt__": {}, "__opts__": {}},
beaconmod: {"__salt__": {}, "__opts__": {}},
}
def test_get_systemd_only():
def test_func(cats, dogs, no_block):
pass
with patch.object(service._get_systemd_only, "HAS_SYSTEMD", True, create=True):
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
assert len(warnings) == 0
assert ret == {"no_block": 2}
ret, warnings = service._get_systemd_only(test_func, {"cats": 1, "unmask": 3})
assert len(warnings) == 0
assert ret == {}
def test_get_systemd_only_platform():
def test_func(cats, dogs, no_block):
pass
with patch.object(service._get_systemd_only, "HAS_SYSTEMD", False, create=True):
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
assert warnings == ["The 'no_block' argument is not supported by this platform"]
assert ret == {}
ret, warnings = service._get_systemd_only(test_func, {"cats": 1, "unmask": 3})
assert len(warnings) == 0
assert ret == {}
def test_get_systemd_only_no_mock():
def test_func(cats, dogs, no_block):
pass
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
assert isinstance(ret, dict)
assert isinstance(warnings, list)
def test_running():
"""
Test to verify that the service is running
"""
ret = [
{"comment": "", "changes": {}, "name": "salt", "result": True},
{
"changes": {},
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt is set to start",
"name": "salt",
"result": None,
},
{
"changes": "saltstack",
"comment": "Started service salt",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "Service salt failed to start",
"name": "salt",
"result": False,
},
{
"changes": "saltstack",
"comment": "Started service salt\nService masking not available on this minion",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "Started service salt\nService masking not available on this minion",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "The service salt is disabled but enable is not True. Set enable to True to successfully start the service.",
"name": "salt",
"result": False,
},
]
tmock = MagicMock(return_value=True)
fmock = MagicMock(return_value=False)
vmock = MagicMock(return_value="salt")
with patch.object(service, "_enabled_used_error", vmock):
assert service.running("salt", enabled=1) == "salt"
with patch.object(service, "_available", fmock):
assert service.running("salt") == ret[0]
with patch.object(service, "_available", tmock):
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__, {"service.enabled": tmock, "service.status": tmock},
):
assert service.running("salt") == ret[1]
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[False, True]),
"service.status": tmock,
},
):
with patch.object(service, "_enable", mock):
assert service.running("salt", True) == ret[2]
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[True, False]),
"service.status": tmock,
},
):
with patch.object(service, "_disable", mock):
assert service.running("salt", False) == ret[2]
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, True]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
assert service.running("salt", True) == ret[4]
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, True]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.unmask": MagicMock(side_effect=[False, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
assert service.running("salt", True, unmask=True) == ret[7]
with patch.dict(service.__opts__, {"test": True}):
with patch.dict(service.__salt__, {"service.status": tmock}):
assert service.running("salt") == ret[5]
with patch.dict(service.__salt__, {"service.status": fmock}):
assert service.running("salt") == ret[3]
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, False]),
"service.enabled": MagicMock(side_effect=[True, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
assert service.running("salt", True) == ret[6]
# test some unique cases simulating Windows
with patch.object(salt.utils.platform, "is_windows", tmock):
# We should fail if a service is disabled on Windows and enable
# isn't set.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": tmock,
},
):
assert service.running("salt", None) == ret[9]
assert service.__context__ == {"service.state": "running"}
# test some unique cases simulating macOS
with patch.object(salt.utils.platform, "is_darwin", tmock):
# We should fail if a service is disabled on macOS and enable
# isn't set.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": tmock,
},
):
assert service.running("salt", None) == ret[9]
assert service.__context__ == {"service.state": "running"}
# test enabling a service prior starting it on macOS
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, "loaded"]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.start": tmock,
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
assert service.running("salt", True) == ret[4]
assert service.__context__ == {"service.state": "running"}
# if an enable attempt fails on macOS or windows then a
# disabled service will always fail to start.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": fmock,
},
):
with patch.object(
service,
"_enable",
MagicMock(
return_value={"changes": "saltstack", "result": False}
),
):
assert service.running("salt", True) == ret[6]
assert service.__context__ == {"service.state": "running"}
def test_dead():
"""
Test to ensure that the named service is dead
"""
ret = [
{"changes": {}, "comment": "", "name": "salt", "result": True},
{
"changes": "saltstack",
"comment": "The service salt is already dead",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt is set to be killed",
"name": "salt",
"result": None,
},
{
"changes": "saltstack",
"comment": "Service salt was killed",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt failed to die",
"name": "salt",
"result": False,
},
{
"changes": "saltstack",
"comment": "The service salt is already dead",
"name": "salt",
"result": True,
},
]
info_mock = MagicMock(return_value={"StartType": ""})
mock = MagicMock(return_value="salt")
with patch.object(service, "_enabled_used_error", mock):
assert service.dead("salt", enabled=1) == "salt"
tmock = MagicMock(return_value=True)
fmock = MagicMock(return_value=False)
with patch.object(service, "_available", fmock):
assert service.dead("salt") == ret[0]
with patch.object(service, "_available", tmock):
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.dict(service.__opts__, {"test": True}):
with patch.dict(
service.__salt__,
{
"service.enabled": fmock,
"service.stop": tmock,
"service.status": fmock,
"service.info": info_mock,
},
):
with patch.object(service, "_enable", mock):
assert service.dead("salt", True) == ret[5]
with patch.dict(
service.__salt__,
{
"service.enabled": tmock,
"service.status": tmock,
"service.info": info_mock,
},
):
assert service.dead("salt") == ret[2]
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__,
{
"service.enabled": fmock,
"service.stop": tmock,
"service.status": fmock,
"service.info": info_mock,
},
):
with patch.object(service, "_enable", mock):
assert service.dead("salt", True) == ret[1]
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[True, True, False]),
"service.status": MagicMock(side_effect=[True, False, False]),
"service.stop": MagicMock(return_value="stack"),
"service.info": info_mock,
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
assert service.dead("salt", True) == ret[3]
# test an initd which a wrong status (True even if dead)
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[False, False, False]),
"service.status": MagicMock(side_effect=[True, True, True]),
"service.stop": MagicMock(return_value="stack"),
"service.info": info_mock,
},
):
with patch.object(service, "_disable", MagicMock(return_value={})):
assert service.dead("salt", False) == ret[4]
assert service.__context__ == {"service.state": "dead"}
def test_dead_with_missing_service():
"""
Tests the case in which a service.dead state is executed on a state
which does not exist.
See https://github.com/saltstack/salt/issues/37511
"""
name = "thisisnotarealservice"
with patch.dict(
service.__salt__, {"service.available": MagicMock(return_value=False)}
):
ret = service.dead(name=name)
assert ret == {
"changes": {},
"comment": "The named service {} is not available".format(name),
"result": True,
"name": name,
}
def test_enabled():
"""
Test to verify that the service is enabled
"""
ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True}
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.object(service, "_enable", mock):
assert service.enabled("salt") == ret
assert service.__context__ == {"service.state": "enabled"}
def test_disabled():
"""
Test to verify that the service is disabled
"""
ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True}
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.object(service, "_disable", mock):
assert service.disabled("salt") == ret
assert service.__context__ == {"service.state": "disabled"}
def test_mod_watch():
"""
Test to the service watcher, called to invoke the watch command.
"""
ret = [
{
"changes": {},
"comment": "Service is already stopped",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Unable to trigger watch for service.stack",
"name": "salt",
"result": False,
},
{
"changes": {},
"comment": "Service is set to be started",
"name": "salt",
"result": None,
},
{
"changes": {"salt": "salt"},
"comment": "Service started",
"name": "salt",
"result": "salt",
},
]
mock = MagicMock(return_value=False)
with patch.dict(service.__salt__, {"service.status": mock}):
assert service.mod_watch("salt", "dead") == ret[0]
with patch.dict(service.__salt__, {"service.start": func}):
with patch.dict(service.__opts__, {"test": True}):
assert service.mod_watch("salt", "running") == ret[2]
with patch.dict(service.__opts__, {"test": False}):
assert service.mod_watch("salt", "running") == ret[3]
assert service.mod_watch("salt", "stack") == ret[1]
def test_mod_beacon():
"""
Test to create a beacon based on a service
"""
name = "sshd"
with patch.dict(
servicestate.__salt__, {"beacons.list": MagicMock(return_value={})}
):
with patch.dict(
servicestate.__states__, {"beacon.present": beaconstate.present}
):
ret = servicestate.mod_beacon(name, sfun="copy")
with patch.dict(service.__salt__, {"beacons.list": MagicMock(return_value={})}):
with patch.dict(service.__states__, {"beacon.present": beaconstate.present}):
ret = service.mod_beacon(name, sfun="copy")
expected = {
"name": name,
"changes": {},
@ -104,9 +581,7 @@ def test_mod_beacon():
with pytest.helpers.temp_directory() as tempdir:
sock_dir = os.path.join(tempdir, "test-socks")
with patch.dict(
servicestate.__states__, {"beacon.present": beaconstate.present}
):
with patch.dict(service.__states__, {"beacon.present": beaconstate.present}):
with patch.dict(beaconstate.__salt__, beacon_state_mocks):
with patch.dict(beaconmod.__salt__, beacon_mod_mocks):
with patch.dict(
@ -115,7 +590,7 @@ def test_mod_beacon():
with patch.object(
SaltEvent, "get_event", side_effect=event_returns
):
ret = servicestate.mod_beacon(
ret = service.mod_beacon(
name, sfun="running", beacon="True"
)
expected = {
@ -126,3 +601,70 @@ def test_mod_beacon():
}
assert ret == expected
@pytest.mark.skipif(
salt.utils.platform.is_darwin(),
reason="service.running is currently failing on OSX",
)
@pytest.mark.destructive_test
@pytest.mark.slow_test
def test_running_with_reload():
opts = salt.config.DEFAULT_MINION_OPTS.copy()
opts["grains"] = salt.loader.grains(opts)
utils = salt.loader.utils(opts)
modules = salt.loader.minion_mods(opts, utils=utils)
service_name = "cron"
cmd_name = "crontab"
os_family = opts["grains"]["os_family"]
os_release = opts["grains"]["osrelease"]
if os_family == "RedHat":
service_name = "crond"
elif os_family == "Arch":
service_name = "sshd"
cmd_name = "systemctl"
elif os_family == "MacOS":
service_name = "org.ntp.ntpd"
if int(os_release.split(".")[1]) >= 13:
service_name = "com.openssh.sshd"
elif os_family == "Windows":
service_name = "Spooler"
if os_family != "Windows" and salt.utils.path.which(cmd_name) is None:
pytest.skip("{} is not installed".format(cmd_name))
pre_srv_enabled = (
True if service_name in modules["service.get_enabled"]() else False
)
post_srv_disable = False
if not pre_srv_enabled:
modules["service.enable"](service_name)
post_srv_disable = True
try:
with patch.dict(service.__grains__, opts["grains"]), patch.dict(
service.__opts__, opts
), patch.dict(service.__salt__, modules), patch.dict(
service.__utils__, utils
), patch.dict(
service.__opts__, {"test": False}
):
service.dead(service_name, enable=False)
result = service.running(name=service_name, enable=True, reload=False)
if salt.utils.platform.is_windows():
comment = "Started service {}".format(service_name)
else:
comment = "Service {} has been enabled, and is running".format(service_name)
expected = {
"changes": {service_name: True},
"comment": comment,
"name": service_name,
"result": True,
}
assert result == expected
finally:
if post_srv_disable:
modules["service.disable"](service_name)

View file

@ -1,585 +0,0 @@
"""
:codeauthor: Rahul Handay <rahulha@saltstack.com>
"""
import pytest
import salt.config
import salt.loader
import salt.states.service as service
import salt.utils.platform
from tests.support.mixins import LoaderModuleMockMixin
from tests.support.mock import MagicMock, patch
from tests.support.unit import TestCase, skipIf
def func(name):
"""
Mock func method
"""
return name
class ServiceTestCase(TestCase, LoaderModuleMockMixin):
"""
Validate the service state
"""
def setup_loader_modules(self):
return {service: {"__context__": {}}}
def test_get_systemd_only(self):
def test_func(cats, dogs, no_block):
pass
with patch.object(service._get_systemd_only, "HAS_SYSTEMD", True):
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
self.assertEqual(len(warnings), 0)
self.assertEqual(ret, {"no_block": 2})
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "unmask": 3}
)
self.assertEqual(len(warnings), 0)
self.assertEqual(ret, {})
def test_get_systemd_only_platform(self):
def test_func(cats, dogs, no_block):
pass
with patch.object(service._get_systemd_only, "HAS_SYSTEMD", False):
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
self.assertEqual(
warnings, ["The 'no_block' argument is not supported by this platform"]
)
self.assertEqual(ret, {})
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "unmask": 3}
)
self.assertEqual(len(warnings), 0)
self.assertEqual(ret, {})
def test_get_systemd_only_no_mock(self):
def test_func(cats, dogs, no_block):
pass
ret, warnings = service._get_systemd_only(
test_func, {"cats": 1, "no_block": 2, "unmask": 3}
)
self.assertIsInstance(ret, dict)
self.assertIsInstance(warnings, list)
def test_running(self):
"""
Test to verify that the service is running
"""
ret = [
{"comment": "", "changes": {}, "name": "salt", "result": True},
{
"changes": {},
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt is set to start",
"name": "salt",
"result": None,
},
{
"changes": "saltstack",
"comment": "Started service salt",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "The service salt is already running",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "Service salt failed to start",
"name": "salt",
"result": False,
},
{
"changes": "saltstack",
"comment": "Started service salt\nService masking not available on this minion",
"name": "salt",
"result": True,
},
{
"changes": "saltstack",
"comment": "Started service salt\nService masking not available on this minion",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "The service salt is disabled but enable is not True. Set enable to True to successfully start the service.",
"name": "salt",
"result": False,
},
]
tmock = MagicMock(return_value=True)
fmock = MagicMock(return_value=False)
vmock = MagicMock(return_value="salt")
with patch.object(service, "_enabled_used_error", vmock):
self.assertEqual(service.running("salt", enabled=1), "salt")
with patch.object(service, "_available", fmock):
self.assertDictEqual(service.running("salt"), ret[0])
with patch.object(service, "_available", tmock):
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__,
{"service.enabled": tmock, "service.status": tmock},
):
self.assertDictEqual(service.running("salt"), ret[1])
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[False, True]),
"service.status": tmock,
},
):
with patch.object(service, "_enable", mock):
self.assertDictEqual(service.running("salt", True), ret[2])
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[True, False]),
"service.status": tmock,
},
):
with patch.object(service, "_disable", mock):
self.assertDictEqual(service.running("salt", False), ret[2])
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, True]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
self.assertDictEqual(service.running("salt", True), ret[4])
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, True]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.unmask": MagicMock(side_effect=[False, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
self.assertDictEqual(
service.running("salt", True, unmask=True), ret[7]
)
with patch.dict(service.__opts__, {"test": True}):
with patch.dict(service.__salt__, {"service.status": tmock}):
self.assertDictEqual(service.running("salt"), ret[5])
with patch.dict(service.__salt__, {"service.status": fmock}):
self.assertDictEqual(service.running("salt"), ret[3])
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, False]),
"service.enabled": MagicMock(side_effect=[True, True]),
"service.start": MagicMock(return_value="stack"),
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
self.assertDictEqual(service.running("salt", True), ret[6])
# test some unique cases simulating Windows
with patch.object(salt.utils.platform, "is_windows", tmock):
# We should fail if a service is disabled on Windows and enable
# isn't set.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": tmock,
},
):
self.assertDictEqual(service.running("salt", None), ret[9])
self.assertEqual(
service.__context__, {"service.state": "running"}
)
# test some unique cases simulating macOS
with patch.object(salt.utils.platform, "is_darwin", tmock):
# We should fail if a service is disabled on macOS and enable
# isn't set.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": tmock,
},
):
self.assertDictEqual(service.running("salt", None), ret[9])
self.assertEqual(
service.__context__, {"service.state": "running"}
)
# test enabling a service prior starting it on macOS
with patch.dict(
service.__salt__,
{
"service.status": MagicMock(side_effect=[False, "loaded"]),
"service.enabled": MagicMock(side_effect=[False, True]),
"service.start": tmock,
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
self.assertDictEqual(service.running("salt", True), ret[4])
self.assertEqual(
service.__context__, {"service.state": "running"}
)
# if an enable attempt fails on macOS or windows then a
# disabled service will always fail to start.
with patch.dict(
service.__salt__,
{
"service.status": fmock,
"service.enabled": fmock,
"service.start": fmock,
},
):
with patch.object(
service,
"_enable",
MagicMock(
return_value={"changes": "saltstack", "result": False}
),
):
self.assertDictEqual(service.running("salt", True), ret[6])
self.assertEqual(
service.__context__, {"service.state": "running"}
)
def test_dead(self):
"""
Test to ensure that the named service is dead
"""
ret = [
{"changes": {}, "comment": "", "name": "salt", "result": True},
{
"changes": "saltstack",
"comment": "The service salt is already dead",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt is set to be killed",
"name": "salt",
"result": None,
},
{
"changes": "saltstack",
"comment": "Service salt was killed",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Service salt failed to die",
"name": "salt",
"result": False,
},
{
"changes": "saltstack",
"comment": "The service salt is already dead",
"name": "salt",
"result": True,
},
]
info_mock = MagicMock(return_value={"StartType": ""})
mock = MagicMock(return_value="salt")
with patch.object(service, "_enabled_used_error", mock):
self.assertEqual(service.dead("salt", enabled=1), "salt")
tmock = MagicMock(return_value=True)
fmock = MagicMock(return_value=False)
with patch.object(service, "_available", fmock):
self.assertDictEqual(service.dead("salt"), ret[0])
with patch.object(service, "_available", tmock):
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.dict(service.__opts__, {"test": True}):
with patch.dict(
service.__salt__,
{
"service.enabled": fmock,
"service.stop": tmock,
"service.status": fmock,
"service.info": info_mock,
},
):
with patch.object(service, "_enable", mock):
self.assertDictEqual(service.dead("salt", True), ret[5])
with patch.dict(
service.__salt__,
{
"service.enabled": tmock,
"service.status": tmock,
"service.info": info_mock,
},
):
self.assertDictEqual(service.dead("salt"), ret[2])
with patch.dict(service.__opts__, {"test": False}):
with patch.dict(
service.__salt__,
{
"service.enabled": fmock,
"service.stop": tmock,
"service.status": fmock,
"service.info": info_mock,
},
):
with patch.object(service, "_enable", mock):
self.assertDictEqual(service.dead("salt", True), ret[1])
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[True, True, False]),
"service.status": MagicMock(side_effect=[True, False, False]),
"service.stop": MagicMock(return_value="stack"),
"service.info": info_mock,
},
):
with patch.object(
service,
"_enable",
MagicMock(return_value={"changes": "saltstack"}),
):
self.assertDictEqual(service.dead("salt", True), ret[3])
# test an initd which a wrong status (True even if dead)
with patch.dict(
service.__salt__,
{
"service.enabled": MagicMock(side_effect=[False, False, False]),
"service.status": MagicMock(side_effect=[True, True, True]),
"service.stop": MagicMock(return_value="stack"),
"service.info": info_mock,
},
):
with patch.object(service, "_disable", MagicMock(return_value={})):
self.assertDictEqual(service.dead("salt", False), ret[4])
self.assertEqual(service.__context__, {"service.state": "dead"})
def test_dead_with_missing_service(self):
"""
Tests the case in which a service.dead state is executed on a state
which does not exist.
See https://github.com/saltstack/salt/issues/37511
"""
name = "thisisnotarealservice"
with patch.dict(
service.__salt__, {"service.available": MagicMock(return_value=False)}
):
ret = service.dead(name=name)
self.assertDictEqual(
ret,
{
"changes": {},
"comment": "The named service {} is not available".format(name),
"result": True,
"name": name,
},
)
def test_enabled(self):
"""
Test to verify that the service is enabled
"""
ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True}
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.object(service, "_enable", mock):
self.assertDictEqual(service.enabled("salt"), ret)
self.assertEqual(service.__context__, {"service.state": "enabled"})
def test_disabled(self):
"""
Test to verify that the service is disabled
"""
ret = {"changes": "saltstack", "comment": "", "name": "salt", "result": True}
mock = MagicMock(return_value={"changes": "saltstack"})
with patch.object(service, "_disable", mock):
self.assertDictEqual(service.disabled("salt"), ret)
self.assertEqual(service.__context__, {"service.state": "disabled"})
def test_mod_watch(self):
"""
Test to the service watcher, called to invoke the watch command.
"""
ret = [
{
"changes": {},
"comment": "Service is already stopped",
"name": "salt",
"result": True,
},
{
"changes": {},
"comment": "Unable to trigger watch for service.stack",
"name": "salt",
"result": False,
},
{
"changes": {},
"comment": "Service is set to be started",
"name": "salt",
"result": None,
},
{
"changes": {"salt": "salt"},
"comment": "Service started",
"name": "salt",
"result": "salt",
},
]
mock = MagicMock(return_value=False)
with patch.dict(service.__salt__, {"service.status": mock}):
self.assertDictEqual(service.mod_watch("salt", "dead"), ret[0])
with patch.dict(service.__salt__, {"service.start": func}):
with patch.dict(service.__opts__, {"test": True}):
self.assertDictEqual(service.mod_watch("salt", "running"), ret[2])
with patch.dict(service.__opts__, {"test": False}):
self.assertDictEqual(service.mod_watch("salt", "running"), ret[3])
self.assertDictEqual(service.mod_watch("salt", "stack"), ret[1])
@skipIf(salt.utils.platform.is_darwin(), "service.running is currently failing on OSX")
@pytest.mark.destructive_test
class ServiceTestCaseFunctional(TestCase, LoaderModuleMockMixin):
"""
Validate the service state
"""
def setup_loader_modules(self):
self.opts = salt.config.DEFAULT_MINION_OPTS.copy()
self.opts["grains"] = salt.loader.grains(self.opts)
self.utils = salt.loader.utils(self.opts)
self.modules = salt.loader.minion_mods(self.opts, utils=self.utils)
self.service_name = "cron"
cmd_name = "crontab"
os_family = self.opts["grains"]["os_family"]
os_release = self.opts["grains"]["osrelease"]
if os_family == "RedHat":
self.service_name = "crond"
elif os_family == "Arch":
self.service_name = "sshd"
cmd_name = "systemctl"
elif os_family == "MacOS":
self.service_name = "org.ntp.ntpd"
if int(os_release.split(".")[1]) >= 13:
self.service_name = "com.openssh.sshd"
elif os_family == "Windows":
self.service_name = "Spooler"
if os_family != "Windows" and salt.utils.path.which(cmd_name) is None:
self.skipTest("{} is not installed".format(cmd_name))
return {
service: {
"__grains__": self.opts["grains"],
"__opts__": self.opts,
"__salt__": self.modules,
"__utils__": self.utils,
},
}
def setUp(self):
self.pre_srv_enabled = (
True
if self.service_name in self.modules["service.get_enabled"]()
else False
)
self.post_srv_disable = False
if not self.pre_srv_enabled:
self.modules["service.enable"](self.service_name)
self.post_srv_disable = True
def tearDown(self):
if self.post_srv_disable:
self.modules["service.disable"](self.service_name)
@pytest.mark.slow_test
def test_running_with_reload(self):
with patch.dict(service.__opts__, {"test": False}):
service.dead(self.service_name, enable=False)
result = service.running(name=self.service_name, enable=True, reload=False)
if salt.utils.platform.is_windows():
comment = "Started service {}".format(self.service_name)
else:
comment = "Service {} has been enabled, and is running".format(
self.service_name
)
expected = {
"changes": {self.service_name: True},
"comment": comment,
"name": self.service_name,
"result": True,
}
self.assertDictEqual(result, expected)