Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3007.x] Fix minion config option startup_states #66605

Open
wants to merge 4 commits into
base: 3007.x
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/66592.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix minion config option startup_states
10 changes: 5 additions & 5 deletions salt/minion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1146,7 +1146,7 @@ async def _connect_minion(self, minion):
minion.setup_scheduler(before_connect=True)
if minion.opts.get("master_type", "str") != "disable":
await minion.connect_master(failed=failed)
minion.tune_in(start=False)
await minion.tune_in(start=False)
self.minions.append(minion)
break
except SaltClientError as exc:
Expand Down Expand Up @@ -2407,7 +2407,7 @@ def timeout_handler(*_):
log.trace("ret_val = %s", ret_val) # pylint: disable=no-member
return ret_val

def _state_run(self):
async def _state_run(self):
"""
Execute a state run based on information set in the minion config file
"""
Expand All @@ -2432,7 +2432,7 @@ def _state_run(self):
else:
data["fun"] = "state.highstate"
data["arg"] = []
self._handle_decoded_payload(data)
await self._handle_decoded_payload(data)

def _refresh_grains_watcher(self, refresh_interval_in_minutes):
"""
Expand Down Expand Up @@ -3111,7 +3111,7 @@ def remove_periodic_callback(self, name):
return True

# Main Minion Tune In
def tune_in(self, start=True):
async def tune_in(self, start=True):
"""
Lock onto the publisher. This is the main event loop for the minion
:rtype : None
Expand All @@ -3138,7 +3138,7 @@ def tune_in(self, start=True):
salt.utils.win_functions.enable_ctrl_logoff_handler()

# On first startup execute a state run if configured to do so
self._state_run()
await self._state_run()

self.setup_beacons()
self.setup_scheduler()
Expand Down
114 changes: 114 additions & 0 deletions tests/pytests/integration/minion/test_startup_states.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
"""Test minion configuration option startup_states.

There are four valid values for this option, which are validated by checking the jobs
executed after minion start.
"""

import pytest


@pytest.fixture
def salt_minion_startup_states_empty_string(salt_master, salt_minion_id):
config_overrides = {
"startup_states": "",
}
factory = salt_master.salt_minion_daemon(
f"{salt_minion_id}-empty-string",
overrides=config_overrides,
)
with factory.started():
yield factory


@pytest.fixture
def salt_minion_startup_states_highstate(salt_master, salt_minion_id):
config_overrides = {
"startup_states": "highstate",
}
factory = salt_master.salt_minion_daemon(
f"{salt_minion_id}-highstate",
overrides=config_overrides,
)
with factory.started():
yield factory


@pytest.fixture
def salt_minion_startup_states_sls(salt_master, salt_minion_id):
config_overrides = {"startup_states": "sls", "sls_list": ["example-sls"]}
factory = salt_master.salt_minion_daemon(
f"{salt_minion_id}-sls",
overrides=config_overrides,
)
with factory.started():
yield factory


@pytest.fixture
def salt_minion_startup_states_top(salt_master, salt_minion_id):
config_overrides = {"startup_states": "top", "top_file": "example-top.sls"}
factory = salt_master.salt_minion_daemon(
f"{salt_minion_id}-top",
overrides=config_overrides,
)
with factory.started():
yield factory


def test_startup_states_empty_string(
salt_run_cli, salt_minion_startup_states_empty_string
):
# Get jobs for this minion
ret = salt_run_cli.run(
"jobs.list_jobs", f"search_target={salt_minion_startup_states_empty_string.id}"
)
# Check no job was run
assert len(ret.data.keys()) == 0


def test_startup_states_highstate(salt_run_cli, salt_minion_startup_states_highstate):
with salt_minion_startup_states_highstate:
# Get jobs for this minion
ret = salt_run_cli.run(
"jobs.list_jobs", f"search_target={salt_minion_startup_states_highstate.id}"
)
# Check there is exactly one job
assert len(ret.data.keys()) == 1
# Check that job executes state.highstate
job_ret = next(iter(ret.data.values()))
assert "Function" in job_ret
assert job_ret["Function"] == "state.highstate"
assert "Arguments" in job_ret
assert job_ret["Arguments"] == []


def test_startup_states_sls(salt_run_cli, salt_minion_startup_states_sls):
with salt_minion_startup_states_sls:
# Get jobs for this minion
ret = salt_run_cli.run(
"jobs.list_jobs", f"search_target={salt_minion_startup_states_sls.id}"
)
# Check there is exactly one job
assert len(ret.data.keys()) == 1
# Check that job executes state.sls
job_ret = next(iter(ret.data.values()))
assert "Function" in job_ret
assert job_ret["Function"] == "state.sls"
assert "Arguments" in job_ret
assert job_ret["Arguments"] == [["example-sls"]]


def test_startup_states_top(salt_run_cli, salt_minion_startup_states_top):
with salt_minion_startup_states_top:
# Get jobs for this minion
ret = salt_run_cli.run(
"jobs.list_jobs", f"search_target={salt_minion_startup_states_top.id}"
)
# Check there is exactly one job
assert len(ret.data.keys()) == 1
# Check that job executes state.top
job_ret = next(iter(ret.data.values()))
assert "Function" in job_ret
assert job_ret["Function"] == "state.top"
assert "Arguments" in job_ret
assert job_ret["Arguments"] == ["example-top.sls"]
12 changes: 6 additions & 6 deletions tests/pytests/unit/test_minion.py
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,7 @@ class SleepCalledException(Exception):


@pytest.mark.slow_test
def test_beacons_before_connect(minion_opts):
async def test_beacons_before_connect(minion_opts):
"""
Tests that the 'beacons_before_connect' option causes the beacons to be initialized before connect.
"""
Expand All @@ -515,7 +515,7 @@ def test_beacons_before_connect(minion_opts):
try:

try:
minion.tune_in(start=True)
await minion.tune_in(start=True)
except RuntimeError:
pass

Expand All @@ -527,7 +527,7 @@ def test_beacons_before_connect(minion_opts):


@pytest.mark.slow_test
def test_scheduler_before_connect(minion_opts):
async def test_scheduler_before_connect(minion_opts):
"""
Tests that the 'scheduler_before_connect' option causes the scheduler to be initialized before connect.
"""
Expand All @@ -546,7 +546,7 @@ def test_scheduler_before_connect(minion_opts):
minion = salt.minion.Minion(minion_opts, io_loop=io_loop)
try:
try:
minion.tune_in(start=True)
await minion.tune_in(start=True)
except RuntimeError:
pass

Expand Down Expand Up @@ -616,7 +616,7 @@ def test_minion_module_refresh_beacons_refresh(minion_opts):


@pytest.mark.slow_test
def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks(
async def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_callbacks(
minion_opts,
):
with patch("salt.minion.Minion.ctx", MagicMock(return_value={})), patch(
Expand All @@ -636,7 +636,7 @@ def test_when_ping_interval_is_set_the_callback_should_be_added_to_periodic_call
try:
minion.connected = MagicMock(side_effect=(False, True))
minion._fire_master_minion_start = MagicMock()
minion.tune_in(start=False)
await minion.tune_in(start=False)
except RuntimeError:
pass

Expand Down
Loading