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

Add state_events option for state module functions #63316

Merged
merged 7 commits into from
Feb 3, 2023
Merged
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
3 changes: 3 additions & 0 deletions changelog/63316.added
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Adds a state_events option to state.highstate, state.apply, state.sls, state.sls_id.
This allows users to enable state_events on a per use basis rather than having to
enable them globally for all state runs.
3 changes: 2 additions & 1 deletion doc/ref/states/requisites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -795,7 +795,6 @@ In this example, `cmd.run` would be run only if either of the `file.managed`
states generated changes and at least one of the watched state's "result" is
``True``.

.. _requisites-fire-event:

Altering States
---------------
Expand Down Expand Up @@ -1125,6 +1124,8 @@ salt/states/ file.
``mod_run_check_cmd`` is used to check for the check_cmd options. To override
this one, include a ``mod_run_check_cmd`` in the states file for the state.

.. _requisites-fire-event:

Fire Event Notifications
========================

Expand Down
11 changes: 9 additions & 2 deletions doc/topics/event/master_events.rst
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,15 @@ Job events

.. salt:event:: salt/job/<JID>/prog/<MID>/<RUN NUM>

Fired each time a each function in a state run completes execution. Must be
enabled using the :conf_master:`state_events` option.
Fired each time a each function in a state run completes execution. Can
also be fired on individual state if the :ref:`fire_event <requisites-fire-event>`
option is set on that state.

Can be enabled for all state runs in the Salt master config with the
:conf_master:`state_events` option. To enable for an individual state
run, pass ``state_events=True`` to the :py:mod:`state <salt.modules.state>`
function being used.


:var data: The data returned from the state module function.
:var id: The minion ID.
Expand Down
47 changes: 44 additions & 3 deletions salt/modules/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,12 @@ def apply_(mods=None, **kwargs):

salt '*' state.apply localconfig=/path/to/minion.yml

state_events
The state_events option sends progress events as each function in
a state run completes execution.

.. versionadded:: 3006.0
barneysowood marked this conversation as resolved.
Show resolved Hide resolved


.. rubric:: APPLYING INDIVIDUAL SLS FILES (A.K.A. :py:func:`STATE.SLS <salt.modules.state.sls>`)

Expand Down Expand Up @@ -816,6 +822,12 @@ def apply_(mods=None, **kwargs):
module types.

.. versionadded:: 2017.7.8,2018.3.3,2019.2.0

state_events
The state_events option sends progress events as each function in
a state run completes execution.

.. versionadded:: 3006.0
"""
if mods:
return sls(mods, **kwargs)
Expand Down Expand Up @@ -974,7 +986,7 @@ def run_request(name="default", **kwargs):
return {}


def highstate(test=None, queue=None, **kwargs):
def highstate(test=None, queue=None, state_events=None, **kwargs):
"""
Retrieve the state data from the salt master for this minion and execute it

Expand Down Expand Up @@ -1072,6 +1084,12 @@ def highstate(test=None, queue=None, **kwargs):

.. versionadded:: 2015.8.4

state_events
The state_events option sends progress events as each function in
a state run completes execution.

.. versionadded:: 3006.0

CLI Examples:

.. code-block:: bash
Expand Down Expand Up @@ -1128,6 +1146,9 @@ def highstate(test=None, queue=None, **kwargs):
"is specified."
)

if state_events is not None:
opts["state_events"] = state_events

try:
st_ = salt.state.HighState(
opts,
Expand Down Expand Up @@ -1186,7 +1207,15 @@ def highstate(test=None, queue=None, **kwargs):
return ret


def sls(mods, test=None, exclude=None, queue=None, sync_mods=None, **kwargs):
def sls(
mods,
test=None,
exclude=None,
queue=None,
sync_mods=None,
state_events=None,
**kwargs
):
"""
Execute the states in one or more SLS files

Expand Down Expand Up @@ -1296,6 +1325,12 @@ def sls(mods, test=None, exclude=None, queue=None, sync_mods=None, **kwargs):

.. versionadded:: 2017.7.8,2018.3.3,2019.2.0

state_events
The state_events option sends progress events as each function in
a state run completes execution.

.. versionadded:: 3006.0

CLI Example:

.. code-block:: bash
Expand Down Expand Up @@ -1382,6 +1417,9 @@ def sls(mods, test=None, exclude=None, queue=None, sync_mods=None, **kwargs):
except KeyError:
log.warning("Invalid custom module type '%s', ignoring", module_type)

if state_events is not None:
opts["state_events"] = state_events

try:
st_ = salt.state.HighState(
opts,
Expand Down Expand Up @@ -1765,7 +1803,7 @@ def show_states(queue=None, **kwargs):
return list(states.keys())


def sls_id(id_, mods, test=None, queue=None, **kwargs):
def sls_id(id_, mods, test=None, queue=None, state_events=None, **kwargs):
"""
Call a single ID from the named module(s) and handle all requisites

Expand Down Expand Up @@ -1835,6 +1873,9 @@ def sls_id(id_, mods, test=None, queue=None, **kwargs):
"is specified."
)

if state_events is not None:
opts["state_events"] = state_events

try:
st_ = salt.state.HighState(
opts,
Expand Down
4 changes: 3 additions & 1 deletion salt/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -3742,7 +3742,9 @@ def __gen_opts(self, opts):
)
opts["env_order"] = mopts.get("env_order", opts.get("env_order", []))
opts["default_top"] = mopts.get("default_top", opts.get("default_top"))
opts["state_events"] = mopts.get("state_events")
opts["state_events"] = (
opts.get("state_events") or mopts.get("state_events") or False
)
opts["state_aggregate"] = (
opts.get("state_aggregate") or mopts.get("state_aggregate") or False
)
Expand Down
115 changes: 115 additions & 0 deletions tests/pytests/integration/modules/state/test_state_state_events.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
"""
tests.pytests.integration.modules.state.test_state_state_events
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"""

import logging
import time

import pytest

log = logging.getLogger(__name__)


@pytest.fixture(scope="module")
def configure_state_tree(salt_master, salt_minion):
top_file = """
base:
'{}':
- state-event
""".format(
salt_minion.id
)

state_event_sls = """
show_notification:
test.show_notification:
- text: Notification
"""

with salt_master.state_tree.base.temp_file(
"top.sls", top_file
), salt_master.state_tree.base.temp_file("state-event.sls", state_event_sls):
yield


@pytest.fixture(scope="module")
def state_event_tag():
"""
State event tag to match
"""
return "salt/job/*/prog/{}/0"


def test_highstate_state_events(
event_listener,
salt_master,
salt_minion,
salt_call_cli,
configure_state_tree,
state_event_tag,
):
"""
Test state.highstate with state_events=True
"""

start_time = time.time()
ret = salt_call_cli.run("state.highstate", state_events=True)
assert ret.returncode == 0
assert ret.data

event_pattern = (salt_master.id, state_event_tag.format(salt_minion.id))
matched_events = event_listener.wait_for_events(
[event_pattern], after_time=start_time, timeout=30
)
assert matched_events.found_all_events


def test_sls_state_events(
event_listener,
salt_master,
salt_minion,
salt_call_cli,
configure_state_tree,
state_event_tag,
):
"""
Test state.sls with state_events=True
"""

start_time = time.time()
ret = salt_call_cli.run("state.sls", "state-event", state_events=True)
assert ret.returncode == 0
assert ret.data

event_pattern = (salt_master.id, state_event_tag.format(salt_minion.id))
matched_events = event_listener.wait_for_events(
[event_pattern], after_time=start_time, timeout=30
)
assert matched_events.found_all_events


def test_sls_id_state_events(
event_listener,
salt_master,
salt_minion,
salt_call_cli,
configure_state_tree,
state_event_tag,
):
"""
Test state.sls_id with state_events=True
"""

start_time = time.time()
ret = salt_call_cli.run(
"state.sls_id", "show_notification", "state-event", state_events=True
)
assert ret.returncode == 0
assert ret.data

event_pattern = (salt_master.id, state_event_tag.format(salt_minion.id))
matched_events = event_listener.wait_for_events(
[event_pattern], after_time=start_time, timeout=30
)
assert matched_events.found_all_events