Move global.push config to salt.auto_apply

The active-push tunables (enabled, highstate_interval_hours, debounce_seconds,
drain_interval, batch, batch_wait) described how Salt auto-applies changes, not
general grid config, so relocate them from the global namespace to a new
salt.auto_apply settings module.

- Add salt/salt/{defaults.yaml,auto_apply.map.jinja,soc_salt.yaml,adv_salt.yaml}.
  auto_apply.map.jinja is a dedicated, side-effect-free merge map (the existing
  salt/salt/map.jinja dereferences pillar.host.mainint at import time).
- Remove the push blocks from salt/global/{defaults,soc_global}.yaml.
- Register salt.soc_salt/salt.adv_salt in pillar/top.sls; seed the local pillar
  stubs for fresh installs (make_some_dirs) and upgrades (ensure_salt_local_pillar
  in soup, wired into up_to_3.2.0).
- Repoint all consumers: GLOBALMERGED.push.* -> AUTOAPPLY.* (schedule, salt
  master, manager beacons, beacons_pushstate, orch.push_batch) and
  pillar.get('global:push...') -> 'salt:auto_apply...' (push reactors,
  so-push-drainer).
- Add a salt: fleetwide-highstate entry to pillar_push_map.yaml so edits keep
  applying immediately, matching the prior global-namespace behavior.
This commit is contained in:
Josh Patterson
2026-06-24 15:17:48 -04:00
parent 61aa963a2d
commit dfdb1fbaeb
19 changed files with 101 additions and 67 deletions
-7
View File
@@ -1,10 +1,3 @@
global:
pcapengine: SURICATA
pipeline: REDIS
push:
enabled: true
highstate_interval_hours: 2
debounce_seconds: 30
drain_interval: 15
batch: '25%'
batch_wait: 15
-37
View File
@@ -59,41 +59,4 @@ global:
description: Allows use of Endgame with Security Onion. This feature requires a license from Endgame.
global: True
advanced: True
push:
enabled:
description: Master kill-switch for the active push feature. When disabled, rule and pillar changes are picked up at the next scheduled highstate instead of being pushed immediately.
forcedType: bool
helpLink: push
global: True
highstate_interval_hours:
description: How often every minion in the grid runs a scheduled state.highstate, in hours. Lower values keep minions closer in sync at the cost of more load; higher values reduce load but increase worst-case latency for non-pushed changes. The salt-minion health check restarts a minion if its last highstate is older than this value plus one hour.
forcedType: int
helpLink: push
global: True
advanced: True
debounce_seconds:
description: Trailing-edge debounce window in seconds. A push intent must be quiet for this long before the drainer dispatches. Rapid bursts of edits within this window coalesce into one dispatch.
forcedType: int
helpLink: push
global: True
advanced: True
drain_interval:
description: How often the push drainer checks for ready intents, in seconds. Small values lower dispatch latency at the cost of more background work on the manager.
forcedType: int
helpLink: push
global: True
advanced: True
batch:
description: "Host batch size for push orchestrations. A number (e.g. '10') or a percentage (e.g. '25%'). Limits how many minions run the push state at once so large fleets don't thundering-herd."
helpLink: push
global: True
advanced: True
regex: '^([0-9]+%?)$'
regexFailureMessage: Enter a whole number or a whole-number percentage (e.g. 10 or 25%).
batch_wait:
description: Seconds to wait between host batches in a push orchestration. Gives the fleet time to breathe between waves.
forcedType: int
helpLink: push
global: True
advanced: True
+2 -2
View File
@@ -1,10 +1,10 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% from 'global/map.jinja' import GLOBALMERGED %}
{% from 'salt/auto_apply.map.jinja' import AUTOAPPLY %}
include:
- salt.minion
{% if GLOBALS.is_manager and GLOBALMERGED.push.enabled %}
{% if GLOBALS.is_manager and AUTOAPPLY.enabled %}
salt_beacons_pushstate:
file.managed:
- name: /etc/salt/minion.d/beacons_pushstate.conf
@@ -1,7 +1,7 @@
{% from 'global/map.jinja' import GLOBALMERGED %}
{% from 'salt/auto_apply.map.jinja' import AUTOAPPLY %}
beacons:
pillar_db:
- interval: {{ GLOBALMERGED.push.drain_interval }}
- interval: {{ AUTOAPPLY.drain_interval }}
- disable_during_state_run: True
inotify:
- disable_during_state_run: True
+3 -3
View File
@@ -60,9 +60,9 @@ def _make_logger():
def _load_push_cfg():
"""Read the global:push pillar subtree via salt-call. Returns a dict."""
"""Read the salt:auto_apply pillar subtree via salt-call. Returns a dict."""
caller = salt.client.Caller()
cfg = caller.cmd('pillar.get', 'global:push', {})
cfg = caller.cmd('pillar.get', 'salt:auto_apply', {})
return cfg if isinstance(cfg, dict) else {}
@@ -135,7 +135,7 @@ def main():
try:
push = _load_push_cfg()
except Exception:
log.exception('failed to read global:push pillar; aborting drain pass')
log.exception('failed to read salt:auto_apply pillar; aborting drain pass')
return 1
if not push.get('enabled', True):
+17
View File
@@ -690,6 +690,21 @@ ensure_postgres_local_pillar() {
chown -R socore:socore "$dir"
}
ensure_salt_local_pillar() {
# The salt.auto_apply settings (moved from global.push) are a new SOC settings
# module, so the new pillar/top.sls references salt.soc_salt / salt.adv_salt
# unconditionally. Managers upgrading from before this change have no
# /opt/so/saltstack/local/pillar/salt/ (make_some_dirs only runs at install
# time), so the stubs must be created here before salt-master restarts against
# the new top.sls.
echo "Ensuring salt local pillar stubs exist."
local dir=/opt/so/saltstack/local/pillar/salt
mkdir -p "$dir"
[[ -f "$dir/soc_salt.sls" ]] || touch "$dir/soc_salt.sls"
[[ -f "$dir/adv_salt.sls" ]] || touch "$dir/adv_salt.sls"
chown -R socore:socore "$dir"
}
ensure_postgres_secret() {
# On a fresh install, generate_passwords + secrets_pillar seed
# secrets:postgres_pass in /opt/so/saltstack/local/pillar/secrets.sls. That
@@ -851,6 +866,8 @@ kibana_backport_streams_index_template() {
}
up_to_3.2.0() {
ensure_salt_local_pillar
fix_logstash_0013_lumberjack_pipeline_name
pin_elasticsearch_data_retention_method
+3 -3
View File
@@ -1,7 +1,7 @@
{% from 'global/map.jinja' import GLOBALMERGED %}
{% from 'salt/auto_apply.map.jinja' import AUTOAPPLY %}
{% set actions = salt['pillar.get']('actions', []) %}
{% set BATCH = GLOBALMERGED.push.batch %}
{% set BATCH_WAIT = GLOBALMERGED.push.batch_wait %}
{% set BATCH = AUTOAPPLY.batch %}
{% set BATCH_WAIT = AUTOAPPLY.batch_wait %}
{% for action in actions %}
{% if action.get('highstate') %}
+11
View File
@@ -185,6 +185,17 @@ registry:
- state: registry
tgt: 'G@role:so-eval or G@role:so-import or G@role:so-manager or G@role:so-managerhype or G@role:so-managersearch or G@role:so-standalone'
# salt: fanout to a fleetwide highstate. The salt.auto_apply settings tune the
# push pipeline itself (enabled, debounce/drain intervals, batch sizing) and the
# per-minion highstate schedule; they are consumed by the manager's schedule,
# beacons, and master reactor config as well as every minion's highstate
# schedule, so a targeted re-apply isn't meaningful. A salt audit row only fires
# for SOC-driven salt.auto_apply edits -- salt version bumps go through soup, not
# SOC, so they never reach this map.
salt:
- highstate: True
tgt: '*'
# sensoroni: universal.
sensoroni:
- state: sensoroni
+2 -2
View File
@@ -59,9 +59,9 @@ def _load_push_map():
def _push_enabled():
try:
caller = Caller()
return bool(caller.cmd('pillar.get', 'global:push:enabled', True))
return bool(caller.cmd('pillar.get', 'salt:auto_apply:enabled', True))
except Exception:
LOG.exception('push_pillar: pillar.get global:push:enabled failed, assuming enabled')
LOG.exception('push_pillar: pillar.get salt:auto_apply:enabled failed, assuming enabled')
return True
+2 -2
View File
@@ -35,9 +35,9 @@ def _sensor_compound():
def _push_enabled():
try:
caller = Caller()
return bool(caller.cmd('pillar.get', 'global:push:enabled', True))
return bool(caller.cmd('pillar.get', 'salt:auto_apply:enabled', True))
except Exception:
LOG.exception('push_strelka: pillar.get global:push:enabled failed, assuming enabled')
LOG.exception('push_strelka: pillar.get salt:auto_apply:enabled failed, assuming enabled')
return True
+2 -2
View File
@@ -34,9 +34,9 @@ def _sensor_compound_plus_import():
def _push_enabled():
try:
caller = Caller()
return bool(caller.cmd('pillar.get', 'global:push:enabled', True))
return bool(caller.cmd('pillar.get', 'salt:auto_apply:enabled', True))
except Exception:
LOG.exception('push_suricata: pillar.get global:push:enabled failed, assuming enabled')
LOG.exception('push_suricata: pillar.get salt:auto_apply:enabled failed, assuming enabled')
return True
View File
+2
View File
@@ -0,0 +1,2 @@
{% import_yaml 'salt/defaults.yaml' as SALT_DEFAULTS %}
{% set AUTOAPPLY = salt['pillar.get']('salt:auto_apply', SALT_DEFAULTS.salt.auto_apply, merge=True) %}
+8
View File
@@ -0,0 +1,8 @@
salt:
auto_apply:
enabled: true
highstate_interval_hours: 2
debounce_seconds: 30
drain_interval: 15
batch: '25%'
batch_wait: 15
+2 -2
View File
@@ -10,7 +10,7 @@
# software that is protected by the license key."
{% from 'allowed_states.map.jinja' import allowed_states %}
{% from 'global/map.jinja' import GLOBALMERGED %}
{% from 'salt/auto_apply.map.jinja' import AUTOAPPLY %}
{% if sls in allowed_states %}
include:
@@ -65,7 +65,7 @@ engines_config:
- name: /etc/salt/master.d/engines.conf
- source: salt://salt/files/engines.conf
{% if GLOBALMERGED.push.enabled %}
{% if AUTOAPPLY.enabled %}
reactor_pushstate_config:
file.managed:
- name: /etc/salt/master.d/reactor_pushstate.conf
+38
View File
@@ -0,0 +1,38 @@
salt:
auto_apply:
enabled:
description: Master kill-switch for the active push feature. When disabled, rule and pillar changes are picked up at the next scheduled highstate instead of being pushed immediately.
forcedType: bool
helpLink: push
global: True
highstate_interval_hours:
description: How often every minion in the grid runs a scheduled state.highstate, in hours. Lower values keep minions closer in sync at the cost of more load; higher values reduce load but increase worst-case latency for non-pushed changes. The salt-minion health check restarts a minion if its last highstate is older than this value plus one hour.
forcedType: int
helpLink: push
global: True
advanced: True
debounce_seconds:
description: Trailing-edge debounce window in seconds. A push intent must be quiet for this long before the drainer dispatches. Rapid bursts of edits within this window coalesce into one dispatch.
forcedType: int
helpLink: push
global: True
advanced: True
drain_interval:
description: How often the push drainer checks for ready intents, in seconds. Small values lower dispatch latency at the cost of more background work on the manager.
forcedType: int
helpLink: push
global: True
advanced: True
batch:
description: "Host batch size for push orchestrations. A number (e.g. '10') or a percentage (e.g. '25%'). Limits how many minions run the push state at once so large fleets don't thundering-herd."
helpLink: push
global: True
advanced: True
regex: '^([0-9]+%?)$'
regexFailureMessage: Enter a whole number or a whole-number percentage (e.g. 10 or 25%).
batch_wait:
description: Seconds to wait between host batches in a push orchestration. Gives the fleet time to breathe between waves.
forcedType: int
helpLink: push
global: True
advanced: True
+4 -4
View File
@@ -1,22 +1,22 @@
{% from 'vars/globals.map.jinja' import GLOBALS %}
{% from 'global/map.jinja' import GLOBALMERGED %}
{% from 'salt/auto_apply.map.jinja' import AUTOAPPLY %}
highstate_schedule:
schedule.present:
- function: state.highstate
- hours: {{ GLOBALMERGED.push.highstate_interval_hours }}
- hours: {{ AUTOAPPLY.highstate_interval_hours }}
- maxrunning: 1
{% if not GLOBALS.is_manager %}
- splay: 1800
{% endif %}
{% if GLOBALS.is_manager and GLOBALMERGED.push.enabled %}
{% if GLOBALS.is_manager and AUTOAPPLY.enabled %}
push_drain_schedule:
schedule.present:
- function: cmd.run
- job_args:
- /usr/sbin/so-push-drainer
- seconds: {{ GLOBALMERGED.push.drain_interval }}
- seconds: {{ AUTOAPPLY.drain_interval }}
- maxrunning: 1
- return_job: False
{% elif GLOBALS.is_manager %}