Skip to content

Honor roster_file in salt.state and salt.function states - #70686

Open
va000612 wants to merge 1 commit into
saltstack:3008.xfrom
va000612:fix/56649-support-roster-file-argument
Open

va000612 wants to merge 1 commit into
saltstack:3008.xfrom
va000612:fix/56649-support-roster-file-argument

Conversation

@va000612

@va000612 va000612 commented Nov 5, 2026

Copy link
Copy Markdown

What does this PR do?

The salt.state and salt.function orchestration states forward the roster argument to saltutil.cmd when ssh: True (added for #51158), but silently drop roster_file. salt-ssh then falls back to the default roster file, so a roster cannot be chosen at orchestration time by path.

This PR forwards roster_file next to roster in both states, documents it in both docstrings, and adds unit tests and a changelog entry.

No other code changes are needed. SSHClient._prep_ssh merges the kwargs into opts, SSHClient.sanitize_kwargs already whitelists roster_file, and salt.roster.get_roster_file reads opts["roster_file"]. saltmod.py was the only place the argument got lost.

What issues does this PR fix or reference?

Fixes #56649

Previous Behavior

roster_file passed to salt.state or salt.function with ssh: True was ignored, and the default roster file was used.

New Behavior

roster_file is passed through to the salt-ssh client, so the given roster file is used.

Merge requirements satisfied?

  • Docs (docstring entries for roster_file in state() and function())
  • Changelog (changelog/56649.fixed.md)
  • Tests written/updated

Testing

1. Unit tests added

Both are in tests/pytests/unit/states/saltmod/:

  • test_state.py::test_roster_file calls saltmod.state(..., ssh=True, roster_file="/tmp/my_roster") and asserts that saltutil.cmd received roster_file="/tmp/my_roster".
  • test_function.py::test_roster_file makes the same check for saltmod.function.

Before the fix (tests added, source unchanged):

$ python -m pytest tests/pytests/unit/states/saltmod/ -k roster -v
test_function.py::test_roster_file FAILED
test_state.py::test_roster         PASSED
test_state.py::test_roster_file    FAILED
E   KeyError: 'roster_file'
2 failed, 1 passed, 35 deselected

After the fix:

$ python -m pytest tests/pytests/unit/states/saltmod/ -v
38 passed

$ python -m pytest tests/pytests/unit/states/saltmod/ tests/pytests/unit/client/ssh/ tests/pytests/unit/roster/ -q
244 passed, 22 skipped

The second command also covers the downstream SSH client and roster code, with no regressions.

2. Real master test (Salt 3008.4, throwaway master)

Setup: a throwaway master on a Photon 5.0 node in a venv (pip install salt==3008.4, started with salt-master -c /root/repro/master -d). The master config was left alone apart from adding roots to fileserver_backend and a file_roots entry for the orchestration file. The default roster path /root/repro/master/roster does not exist. A custom roster at /root/e2e/custom_roster defines target1 (127.0.0.1).

Orchestration t.sls:

run_via_ssh:
  salt.state:
    - tgt: target1
    - ssh: True
    - roster_file: /root/e2e/custom_roster
    - sls: mystate

Command, run once on the unpatched module and once with this PR's saltmod.py overlaid on the venv (master restarted in between):

salt-run -c /root/repro/master state.orchestrate t -l debug 2>&1 \
  | grep -iE "roster|No matching targets|target1|Permission denied|Connection refused"

Before the fix (unpatched 3008.4), roster_file is dropped and the default roster is used:

[ERROR   ] Can't access roster for backend flat: Roster file "/root/repro/master/roster" not found
[ERROR   ] No matching targets found in roster.

After the fix (patched saltmod.py), the custom roster is compiled and target1 resolves:

[DEBUG   ] compile template: /root/e2e/custom_roster
[DEBUG   ] LazyLoaded roster_matcher.targets
[DEBUG   ] Adding minions for job 20261105050827363614: ['target1']
[ERROR   ] {'out': 'highstate', 'ret': {'target1': 'no such identity: /root/e2e/key: No such file or directory\nroot@127.0.0.1: Permission denied (...)'}}

The run then fails at the SSH step only because the key /root/e2e/key was intentionally not created. The point of the test is that target resolution now uses the custom roster.

Not tested

  • A full orchestration run through to a successful state apply over SSH (it needs an SSH key and a Python on the target).
  • pre-commit: three hooks failed for tooling reasons in my environment. Lint Salt and Lint Tests could not install filelock==4.2.6 from my package mirror, where the newest version is 4.2.4. Check Changelog Entries could not import virustotal3. The other hooks passed. I committed with --no-verify, so please rely on CI for the lint and changelog checks.
  • Salt API: when __disable_custom_roster is set, roster_file is still restricted to the master's configured rosters directories (existing behaviour, unchanged).

🤖 Generated with Claude Code

The salt.state and salt.function orchestration states only forwarded the
"roster" argument to saltutil.cmd when ssh=True. A "roster_file" argument
was silently dropped, so salt-ssh fell back to the default roster file.

Forward "roster_file" alongside "roster" in both states, document it, and
add unit tests.

Fixes saltstack#56649

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch was previously deployed

1 inactive deployment
ci — b67a260b Deployed Nov 5, 2026 by va000612 via Build Onedir Packages / RPM (arm64) #28451
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant