Merge forward 3007.x into 3008.x - #69908
Open
dwoz wants to merge 133 commits into
Open
Conversation
When a prereq chain is set up such that one state prereq's another state that itself prereq's a third state (e.g. state1 --prereq--> state2 --prereq--> state3), the intermediate state's own prereq check node was created without its own prereq requirements. This allowed the intermediate state to be evaluated in test mode independently of the tail of the chain, so any state that always proposes changes in test mode (e.g. test.succeed_with_changes, module.run) caused the head of the chain to run even when the tail produced no changes. When adding a new prereq requisite (_add_prereq) from a chunk that already has its own prereq check node (because another state prereq's it), also register a PREREQ edge from the requisite's prereq check node into the chunk's own prereq check node. This ensures the intermediate prereq check waits for the tail's prereq check outcome before deciding whether to run in test mode. Also confirms the pre-existing regression test saltstack#68438 now passes on Windows and Linux. Fixes saltstack#68438
Restore the ``Test Salt / Rocky Linux 9 unit zeromq 4`` CI job to green after the 3006.x→3007.x→3008.x merge-forward chain pulled 3006.x-only regression tests into 3007.x whose expectations don't match the 3007.x runtime API surface. - ``test_verify_master_accepts_cached_key_with_whitespace_drift``, ``test_verify_master_caches_clean_key_on_first_contact``, ``test_authenticate_caps_retry_loop_with_auth_retries_69442``, ``test_authenticate_default_does_not_cap_retry_loop_69442``: switch from the removed 3006.x ``crypt.gen_keys(pki_dir, name, keysize)`` signature to the 3007.x ``crypt.write_keys(...)`` equivalent, and add ``keys.cache_driver`` to opts so ``AsyncAuth.__singleton_init__`` can construct the keystore cache. - ``test_gen_signature_signs_clean_key`` and ``test_gen_signature_signs_clean_key_trailing_newline``: skip on 3007.x. The module-level ``salt.crypt.gen_signature`` was removed by the master-pki cache refactor; the replacement ``MasterKeys.gen_signature`` signs ``pub.public_bytes()`` from a key object rather than the raw file content, so the saltstack#68930 whitespace- drift bug the tests were written against is not reachable on 3007.x. - ``test_maintenance_duration``: add ``eauth_tokens.cache_driver``, ``eauth_tokens.cluster_id``, and ``cluster_id`` to the test's opts dict. ``Maintenance._post_fork_init`` now constructs a long-lived ``LoadAuth`` (as part of the memory-leak fix that caches it across loop iterations); ``LoadAuth.__init__`` reads those keys. - ``test_minion_manager_stop_unblocks_resolve_dns_69466``: assert on either ``io_loop.create_task`` or ``io_loop.add_callback`` being called once. 3007.x refactored ``MinionManager.stop()`` to use ``create_task`` instead of ``add_callback``; the 3006.x-origin test hard-coded the older form. - ``test_event_unpack_with_SaltDeserializationError``: assert on the new debug-level "skipping malformed event (deserialization error)" message that the memory-leak hardening emits from ``SaltEvent._get_event`` instead of the pre-hardening ``log.error("Unable to deserialize received event")`` call the test originally targeted. The hardening intentionally demotes the log level so a single bad IPC frame cannot spam the operator log.
The test used a fixed time.sleep(2) after spawning the salt CLI before sending SIGINT. On slow CI hosts (observed on Photon OS 5 Arm64, both tcp(fips) and zeromq(fips) integration lanes) the CLI had not yet published its job when the signal arrived. Its scripts._handle_signals path then took the AttributeError/KeyError branch (no pub_data), emitted only "Exiting gracefully on Ctrl-c", and skipped the "This job's jid is" message the test asserts on. Wait on the master's salt/job/*/new event via event_listener instead. That guarantees pub_data is populated in the CLI process before we interrupt it, so the jid-bearing signal-handler branch always runs. Fixes flakiness in tests/pytests/integration/cli/test_salt.py::test_interrupt_on_long_running_job seen at: https://github.com/saltstack/salt/actions/runs/28505613358/job/84502049475 https://github.com/saltstack/salt/actions/runs/28505613358/job/84502049337
…prereq-chain-dag Fix prereq chain regression in nested prereq DAG setup (saltstack#68438)
Two bugs kept the Sync .lock files job from running for 3007.x PRs, leaving lock files stale: - on.pull_request.branches omitted 3007.x (it only listed master and 3006.x), so the workflow never triggered for PRs targeting 3007.x. Add all four release branches. - The actor guard only matched 'dependabot', so it skipped whenever the salt-pr-bot rebase bot re-pushed a branch. Also fire for salt-pr-bot.
Two bugs kept the Sync .lock files job from running for 3006.x PRs, leaving lock files stale: - on.pull_request.branches omitted 3006.x (it only listed master and 3006.x), so the workflow never triggered for PRs targeting 3006.x. Add all four release branches. - The actor guard only matched 'dependabot', so it skipped whenever the salt-pr-bot rebase bot re-pushed a branch. Also fire for salt-pr-bot.
…c-fix-3006.x Fix dependabot lock-sync workflow triggering on 3006.x
…c-fix-3007.x Fix dependabot lock-sync workflow triggering on 3007.x
state.orchestrate overwrites __opts__["user"] with __user__ (the publishing user, salt.utils.user.get_specific_user(), which returns "sudo_<login>" when salt-run was launched under sudo). The post-saltstack#67716 privilege-drop path in saltutil.runner/saltutil.wheel reads that value as the runas target and asks chugid to switch to it, which then raises KeyError from pwd.getpwnam wrapped in CommandExecutionError: Failed to run 'cache.grains' as user 'sudo_alice': KeyError: "getpwnam(): name not found: 'sudo_alice'" Validate the candidate against the passwd database in _master_user_runas and skip the privilege drop when it does not resolve to a real account, falling back to historical in-process behavior. Fixes saltstack#69600
SerializerExtension.load_yaml only guarded against a YAMLError whose problem_mark is absent. PyYAML's libyaml (C) loader populates problem_mark but leaves its buffer as None, so load_yaml took the else branch and passed buffer=None into salt.utils.stringutils.get_context(), which crashed with "AttributeError: 'NoneType' object has no attribute 'splitlines'" instead of raising the intended TemplateRuntimeError. Fall back to the stringified exception when the buffer is missing, and add a regression test. Fixes saltstack#69533
…ltstack#69455) The unmaintained `linode-python` 1.1.1 package targets the retired Linode API v3 and uses `is not 0` / `is 1` against literals, which Python 3.12+ emits as `SyntaxWarning` from `linode/api.py` lines 293, 348, and 356. On RHEL/Rocky/Oracle Linux 9.x, the salt-common onedir's post-install scriptlet imports the onedir's Python which in turn imports `linode-python` and the warnings surface during every package install/upgrade. Drop `linode-python` from `requirements/static/pkg/linux.txt` and its dependent CI/pkg linux lockfiles so it is no longer installed into the salt-common onedir. `salt.cloud.clouds.linode` already uses the Linode APIv4 over HTTP/JSON (no `linode-python` import), so the package is purely vestigial. This backports saltstack#69339 (3006.x) and mirrors saltstack#68871 (master/3008.x) to 3007.x. Fixes saltstack#69455
The Fedora 40 skips on test_peer_communication and test_grains_remove_add were added in 5a85699 (2024-05-17) as a temporary workaround referencing saltstack#66539 and saltstack#66540 with no root cause captured. Fedora 40 reached end-of-life on 2025-05-13 and the tests now pass without the workaround on 3007.x and master, so the skips can be dropped. Fixes saltstack#66540 Fixes saltstack#66539
* fix timeout for salt-api * Add changelog and regression test for salt-api timeout fix Addresses @twangboy's CHANGES_REQUESTED on PR saltstack#62188. - changelog/62187.fixed.md describes the salt-api hang fix. - test_mk_token_missing_password_returns_empty pins the missing-password /username path: mk_token must return {} instead of raising SaltInvocationError, which previously escaped through the master clear-payload handler and hung salt-api workers for ~3 minutes per bad request. Co-authored-by: carrysauce <carrysauce@users.noreply.github.com> * Apply reviewer suggestions: separate try/except for format_call, rename changelog to PR number - Give format_call its own try/except block catching SaltInvocationError with a descriptive debug message, as requested by @twangboy - Rename changelog/62187.fixed.md to changelog/62188.fixed.md (PR number, not issue number) --------- Co-authored-by: Alex Donec <alex.donec@pinely.com> Co-authored-by: Daniel A. Wozniak <dwozniak@broadcom.com> Co-authored-by: carrysauce <carrysauce@users.noreply.github.com> Co-authored-by: Daniel A. Wozniak <daniel.wozniak@broadcom.com>
* Fix onchanges requisites hard-failing when their target fails
A failed onchanges/onchanges_any target was classified the same as a
failed require/watch target, causing the dependent state to hard-fail
instead of being skipped with result=True per the documented truth
table. Also relocates test_slots_documented.py so its `state` fixture
can resolve, and adds a regression test locking in prereq's already
correct skip-on-failed-target behavior.
* Fix inconsistent heading levels in highstate.rst breaking docs build
The "Highstate Output" section's eight subsections (state_output,
state_verbose, state_output_diff, state_output_pct, state_output_profile,
state_tabular, state_compress_ids, Choosing a mode) used "~" underlines,
which docutils resolves to heading level 4 since "~" had not appeared
earlier in the document. Their parent section is level 2 ("="), so this
skipped level 3 entirely, and Sphinx's man-page build (which runs with
-W, treating warnings as errors) failed with eight "Inconsistent title
style: skip from level 2 to 4" errors, breaking the "Generate MAN Pages"
CI job.
Change the eight subsection underlines from "~" to "-", matching the
level-3 convention already used by every other subsection in this file
(e.g. "Top file", "Include declaration"), restoring a consistent
level 1/2/3 hierarchy with no skipped level.
* Fix undefined :ref: label 'beacon' in standalone_minion.rst
The tutorial referenced :ref:`beacons <beacon>`, but no label named
"beacon" exists; the beacons index page defines the label "beacons"
(doc/topics/beacons/index.rst:1), matching how every other doc page
links to it (e.g. doc/topics/development/modules/index.rst:181). The
typo tripped Sphinx's -W (warnings-as-errors) html build with
"WARNING: undefined label: 'beacon' [ref.ref]".
* Fix IndexError in State.__eval_slot for non-dotted accessor
When a slot expression has no dotted post-`)` accessor but has trailing
whitespace, `return_get` was a truthy non-empty string that entered the
dict-traversal branch and crashed on `split(".", 1)[1]`. Guard the
branch on `.` being present instead.
Also strip surrounding matching quotes from `~`-appended text so that
documented examples like `~ "/suffix"` produce `/suffix`, not
`"/suffix"`.
Fixes saltstack#69661
* Replace macos-14 arm64 runner with macos-15
macos-14 begins deprecation on 2026-07-06 and is unsupported after
2026-11-02 per actions/runner-images#13518
Ref: actions/runner-images#13518
* Fix Windows slot documented tests dropping path backslashes
The two tests in tests/pytests/functional/states/test_slots_documented.py
embed a tmp_path directly into the SLS source, e.g.::
- name: __slot__:salt:test.echo(D:\a\_temp\...\slots_marker_arg)
State.__eval_slot dispatches the ``test.echo(...)`` call through
salt.utils.args.parse_function, which is built on top of
``shlex.shlex(posix=True)``. In POSIX mode shlex treats a backslash as a
generic escape character, so on Windows every backslash in the path was
consumed and the slot resolved to
``D:a_temppytest-of-...slots_marker_arg`` -- a relative path -- so
file.managed rejected it with "not an absolute path" and the assertion
that the marker file was created failed.
Format the paths through ``PurePath.as_posix()`` before splicing them
into the SLS. Windows accepts forward-slash separators for filesystem
paths, and shlex leaves ``/`` untouched, so the slot now resolves to the
same path the assertion checks. Behavior on Linux is unchanged (already
forward slashes).
---------
Co-authored-by: Daniel A. Wozniak <daniel.wozniak@broadcom.com>
* gitfs walkthrough: drop EOL Ubuntu 14/Debian Wheezy/CentOS 7.3 notes and pin pygit2>=1.13.1, GitPython>=3.1.50 (matches base.txt + CI lockfiles). Adds a GitLab subsection covering deploy tokens, project access tokens, PATs, and SSH deploy keys. * git_pillar docstring: enumerate supported remote URL forms (https://, ssh://, scp-style user@host:path, file://). The scp-style colon is the most common source of 'Failed to resolve address' errors. * s3fs docstring: document s3.location / s3.service_url / s3.https_enable / s3.path_style / s3.verify_ssl with a Regional endpoints section, citing the SigV4 redirect failure mode. * file_roots.rst: new section explaining why /srv/salt is the default. Update netconfig/napalm_network examples to use /srv/salt instead of /etc/salt/states so the example matches the recommendation. * New tests: tests/pytests/functional/fileserver/gitfs/ test_documented_providers.py and tests/pytests/unit/fileserver/test_s3fs_documented_options.py pin the docs to the loader. Closes saltstack#62260 Closes saltstack#56127 Closes saltstack#60809 Closes saltstack#60408 Closes saltstack#53746
…th tomllib) (saltstack#69607) (saltstack#69608) * Fix pkg.list_holds returning empty on dnf5 (onedir lacks 'toml') _list_holds_dnf5 parsed /etc/dnf/versionlock.toml via salt.serializers.tomlmod, which depends on the third-party 'toml' library. 'toml' is a CI-only dependency and is not bundled in the onedir packages, and the onedir ships Python 3.10 (no stdlib tomllib), so the parse failed silently and pkg.list_holds always returned []. With hold: True this made pkg.installed re-hold packages on every run. Parse with the standard-library tomllib (available once the onedir ships Python 3.11 in 3006.27, see saltstack#69526), falling back to the toml serializer on older interpreters where it is installed. Fixes saltstack#69607 * Add unit tests for yumpkg.version_cmp, group_diff, and list_repos Expand test coverage of salt/modules/yumpkg.py beyond the saltstack#69607 change, per the contributor policy of improving coverage in modules a PR touches: - version_cmp: assert it delegates to lowpkg.version_cmp with ignore_epoch - group_diff: assert group members are split into installed/not-installed - list_repos: assert .repo files are parsed, non-repo files skipped, and each repo records its source file
…saltstack#69841) Salt never removed installer/uninstaller files downloaded by pkg.install and pkg.remove, causing the minion cache to grow unbounded (saltstack#69817). Add an opt-in winrepo_installer_cache_expire minion option that removes tracked installer cache files older than the configured age each time pkg.refresh_db runs. Disabled by default.
… cross-origin redirect) (saltstack#69846) SimpleAsyncHTTPClient forwarded the Authorization/Cookie headers and auth_username/auth_password to a different origin when following a redirect, because the redirected request's headers were only ever a shallow-copy alias of the original request's headers. Explicitly copy the headers before mutating them, and strip credentials when the redirect target's scheme/host/port differs from the original request. Fixing the aliasing bug also exposed a latent issue: the existing Content-Length/Content-Type/etc. stripping for 302/303-to-GET redirects deleted from the original request's headers instead of the new request's, which only worked by accident because of the aliasing. Both now correctly target the new request.
…tack#69854) pip 25.2's vendored urllib3 (1.26.20) carries two CVEs (CVE-2025-66418, CVE-2026-21441), so tools/pkg/build.py worked around this by downloading pip 25.2, hand-patching its vendored urllib3/_version.py and urllib3/response.py with unified diffs stored in pkg/patches/pip-urllib3/, and force-installing that patched wheel into every onedir build (macOS standalone, onedir_dependencies, and salt_onedir, which also covers the debian/rpm/windows packages that consume its output). pip 26.1.2 already vendors a genuine urllib3 2.6.3 containing the real upstream fixes for both CVEs, making the hand-patch unnecessary and, since it's a unified diff against pip 25.2's exact 1.26.20 source, unable to apply cleanly to 26.1.2 anyway. Replace _build_patched_pip_wheel/_patch_pip_wheel_urllib3/_apply_unified_diff with a plain _download_pip_wheel() that pulls pip==26.1.2, update the three call sites, and remove pkg/patches/pip-urllib3/. tests/pytests/pkg/integration/test_pip_urllib3_patch.py only existed to verify the hand-patch was applied; delete it rather than keep assertions pinned to pip's internal vendoring choices. requirements/constraints.txt's pip == 26.0.1 dev/lint tooling pin is untouched here -- it's already past 25.2 and unrelated to the packaged pip this change bumps. Fixes saltstack#69852
…altstack#69753) (saltstack#69805) Scheduled highstate on 3006.27 minions intermittently failed with ``SaltClientError: Nonce verification error`` raised from ``Crypticle.loads``. Two independent races contribute: (a) ``salt/channel/client.py`` -- ``_do_transfer`` looked up ``self.auth.session_crypticle`` twice, once for ``dumps`` on the send path and once for ``loads`` on the receive path. A concurrent re-auth could swap the reference between the two, causing the reply to be decrypted with a different key or session than the request was encrypted with. Pin the ``session_crypticle`` reference at send time and pass it through ``_package_load`` so both halves of the transfer use the same object. Also wrap ``_crypted_transfer`` and ``crypted_transfer_decode_dictentry`` in a per-channel ``tornado.locks.Lock`` so concurrent coroutines driving the same channel cannot interleave their (send, decrypt-reply) windows. (b) ``salt/transport/zeromq.py`` -- the minion/syndic daemon branch of ``_init_socket`` built the ZMQ IDENTITY as ``salt-req/{role}/{minion_id}/{slot}`` where ``slot`` came from a process-lifetime ``itertools.count()``. That counter's state is inherited across ``fork()``, so two concurrent scheduled-job forks each draw the same slot value on their first ``next()`` call. Combined with the master's ``ROUTER_HANDOVER=1``, siblings claimed the same routing-id and in-flight replies queued for one child were re-routed to the sibling, decrypting cleanly (same session key) but failing the nonce check. Add ``os.getpid()`` to the identity so forked children are disambiguated by pid. Separately, widen the CLI branch's identity slot from 8-bit ``pid % 256`` to a per-process 24-bit ``secrets.randbits(24)`` cached at import time so bursty ``salt-call`` invocations from the same shell do not birthday-collide on the master's ROUTER. (c) ``salt/crypt.py`` -- backport the diagnostic message from master that includes the received and expected nonces in the ``SaltClientError`` string, so future field reports of this class can distinguish a true crossed reply (two uuid4 hex strings) from a replay or a zero-nonce default. Live end-to-end validation on v3006.27 master + v3006.27 minion under the same driven load (three 3-second schedules + 8-way concurrent ``salt-call state.highstate`` + 6-way ``saltutil.refresh_pillar`` / ``mine.update`` / ``cp.list_master`` / ``state.show_top`` waves): baseline 9 ``Nonce verification error`` events in 240 s dropped to 0 events in 434 s across 7 master AES rotations and 128 scheduled highstate jobs. Fixes saltstack#69753
In batch mode the salt CLI computed its exit code by iterating batch.run(),
which yields nothing when the target matches no minions (Batch.run returns
early on an empty minion list). retcode stayed 0 and the CLI exited 0, while
the non-batch path exits 2 ("No return received") for the same case
(saltstack#57357). Mirror the non-batch behavior: when no minions matched, print
"No return received" and exit 2.
Fixes saltstack#57357
test_dockercompose.py and test_keystone_role_grant.py came forward from 3006.x, but their source modules were purged from 3007.x by dc526dc 'Initial purge of community extensions'. pylint E0611 fails on the now-broken imports.
test_linux_shadow: skip the 'crypto' variant of test_gen_password when
the stdlib crypt module is unavailable (removed in Python 3.11+). The
tests were previously masked by a module-level importorskip('spwd')
which the merge removed; that guard is no longer valid because
salt.modules.linux_shadow was decoupled from spwd in saltstack#69651.
conftest (rest_tornado): add 'keys.cache_driver' to the app_mock opts so
saltnado tests that construct CkMinions (which now instantiates
salt.key.get_key on 3007.x+) don't hit KeyError.
- test_master.py: convert async publish tests to async def + await (ClearFuncs.publish is async on 3007.x+, was sync on 3006.x) - test_crypt.py::test_authenticate_missing_creds_attribute_67947: adopt the 3007.x gen_keys(keysize) signature + write PEMs to disk, and add keys.cache_driver to opts - test_jinja_custom_filters.py::test_regex_search/match_no_group: update expected value to match 3007.x's intentional filter change (ff28cd0, ungrouped matches return (match_str,) not ()) - test_client.py::test_do_transfer_serialized_by_lock: construct the Future() inside the coroutine (unbundled tornado requires a running loop) - test_cloud.py::test_vm_config_merger_with_overrides: wrap the overrides payload as {name: {...}} to match Cloud.vm_config's per-VM keying on 3007.x - salt/cluster/consensus/raft/scheduler.py: nudge colliding timeouts by 1e-9 s to prevent dict-key collision when two nodes' random follower_timeouts happen to land on the same millisecond (fixes pre-existing flake exposed by test_cluster_log_entry)
- rest_tornado/conftest.py app_mock: add __role=master (salt.key.get_key now instantiated on 3007.x+ via CkMinions rejects empty __role with ValueError). - test_documented_providers.py base_opts: add gitfs_proxy and gitfs_depth to satisfy PER_REMOTE_OVERRIDES on 3007.x (init_remotes hits failhard when either is missing from global opts).
Removes changelog entries whose fixes target files that no longer exist after the initial purge of community extensions (dc526dc) and the removal of the vendored tornado tree: Vendored tornado (salt/ext/tornado/ purged): - 69845: CVE-2026-49853 SimpleAsyncHTTPClient redirect header stripping - 69848: CVE-2026-49855 _GzipMessageDelegate gzip bomb protection Extension purge (files no longer in tree): - 52220: keystone_role_grant state - 54122: grafana4_datasource state - 55143: DigitalOcean cloud driver - 60408: s3fs fileserver docs - 61082: poudriere module - 63051: dockercompose module (python_on_whales backend) - 65233: metadata grain - 68961: vault functional test container - 69529: s3fs fileserver race
…7-25 Merge forward 3006.x into 3007.x
twangboy
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.