Skip to content

fix etcdv3 user/password auth - #69108

Closed
Dkhil wants to merge 10000 commits into
saltstack:3005.xfrom
Dkhil:fix/etcdv3_auth
Closed

Dkhil wants to merge 10000 commits into
saltstack:3005.xfrom
Dkhil:fix/etcdv3_auth

Conversation

@Dkhil

@Dkhil Dkhil commented May 10, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

fix authification for etcd module when using user/password based auth

Revolution1/etcd3-py#41

What issues does this PR fix or reference?

Fixes
auth when using user/passsword based authentification with etcdv3

Daniel A. Wozniak and others added 30 commits April 14, 2026 18:16
…tion regressions by relying on loader packs
…es, fixed integration regressions, and improved matcher fallback logic
If reap_stray_processes cannot kill a process,
it logs which process it gived up on. However,
if the process exits after the alive check and
before the log, this causes psutil.NoSuchProcess
exception
Batch mode previously generated a separate JID per batch iteration,
making it impossible to track a batch operation as a single job via
salt-run jobs.lookup_jid. Generate one JID at the start of batch.run()
and pass it to every cmd_iter_no_block call so all iterations share it.

In local_cache.save_minions(), change from overwrite to read-merge-write
so that each batch iteration accumulates its minion list under the shared
JID rather than replacing the previous iteration's list.

In client.cmd_iter_no_block(), filter the jid kwarg before passing to
get_iter_returns to avoid conflicting with its positional jid argument.

Fixes saltstack#25362, saltstack#58502
Add a high-performance GitFS provider that interacts directly with the
system's Git binary. This provider supports shallow clones (depth=1)
and optimized tree traversal via ls-tree, significantly reducing
latency and memory footprint at scale.

Includes:
- Minimum Git version check (2.3.0+) for GIT_SSH_COMMAND support.
- Comprehensive unit tests for the new provider.
- Integration into the existing GitFS parametrized test suite.
Ensure that mocked or third-party providers used in tests (like 'gitfoo')
are not skipped if they haven't initialized their .repo attribute yet.
This fixes test_update in tests/pytests/unit/runners/test_git_pillar.py.
Ensures that attributes like 'privkey' are available when verify_auth()
is called during initialization via verify_provider(). This fixes
the AttributeError in GitCLI unit tests.
- Consolidated duplicated GitCLI class implementations into a single cohesive class.
- Fixed environment filtering in envs() to correctly re-initialize config attributes (ref_types, disable_saltenv_mapping) from the instance, ensuring global and per-remote overrides are respected.
- Added missing threading import in salt/utils/gitfs.py.
- Cleaned up unnecessary pass statement in setup_callbacks to satisfy pylint.
Monkey-patch Container.terminate() to poll Docker until the container
name is fully released before returning.  The upstream implementation
calls container.remove(force=True) + container.wait(), but wait()
returns as soon as the process stops — Docker may not have released
the name yet.  When parameterized vault fixtures reuse the same name
across versions, the next containers.run() gets a 409 "name already
in use" error.

Also add SKIP_SETCAP=1 to the vault container environment so that
vault v2.0+ does not attempt setcap (which requires CAP_SETFCAP).

Workaround for saltstack/pytest-salt-factories#198
Daniel A. Wozniak and others added 20 commits May 6, 2026 01:39
Root cause for the recurring ``test_state_show_top`` failures on
Photon FIPS shard 7: ``tests/pytests/integration/states/test_ssh_pki.py``
defines its own ``sshpkica_salt_minion`` and ``sshpki_salt_minion``
fixtures (separate from the modules-level test_ssh_pki I fixed
previously) without FIPS overrides. On a FIPS-enabled platform those
minions cannot sign in to their master, so each ``factory.started()``
burns ~127s × 3 attempts = 382 seconds before raising
``FactoryNotStarted``. The CI log shows::

    FactoryNotStarted: SaltMinion(id='sshpkica-minion3clih0', ...)
    factory has failed to confirm running status after 3 attempts,
    which took 382.07 seconds

That timeout thrashing was the "FIPS slowdown" we were seeing in shard
7. ``test_state_show_top`` ran in the degraded shard environment, the
60s retry exhausted, and the test failed. The retry-budget bump and
per-test timeout decorator I added before were addressing the symptom,
not the cause.

- tests/pytests/integration/states/test_ssh_pki.py: spread fips_mode +
  publish_signing_algorithm into ``sshpki_master_config``, and add
  fips_mode + encryption_algorithm + signing_algorithm to
  ``ca_minion_config`` and the ``sshpki_salt_minion`` defaults. Mirrors
  the fix already applied to the modules-level test_ssh_pki.

- tests/pytests/integration/ssh/test_state.py: revert the
  ``@pytest.mark.timeout(300, func_only=True)`` decorator on
  ``test_state_show_top``. The retry-budget bump from
  ``ed529cace14`` (cherry-picked earlier) is sufficient now that the
  upstream sshpki minion no longer hangs the shard for 6+ minutes.

Reproduced in a Photon-4 + openssl-fips-provider container per
``CONTAINERS.txt`` with the run-25415854545 onedir + nox artifacts:

* states/test_ssh_pki.py::test_certificate_managed_remote — passes in
  40s with the fix, sshpkica minion starts cleanly.
* Combined run of states/test_ssh_pki.py + test_state_show_top — 10
  passed in 139s, ``test_state_show_top`` no longer fails.
The previous floor of 80.10.2 was conservative beyond the CVE fix line
and caused PEP 517 build-env installs to fail with ``ResolutionImpossible``
on Python 3.14: pip 25.2 bootstraps the isolated build env with
``setuptools == 78.1.1``, which the floor rejected. Symptom in CI was
the macOS arm64 onedir build dying when source-building yarl 1.23.0
(salt's ``--no-binary=:all:`` policy forces sdist install; no cp314
wheel was used; pip's build env subprocess inherited PIP_CONSTRAINT
from the parent and the conflict surfaced).

setuptools 78.1.1 is the fix for GHSA-5rjg-fvgr-3xxf / PYSEC-2025-49
(path traversal in ``PackageIndex.download``), so this floor stays
above all known vulnerabilities. Static pin files do not need to be
regenerated because uv still resolves the latest available setuptools
(82.0.1) under the new floor.
…est 9, etc

This bundles the surgical fixes that surfaced when pyversion102 moved to
Python 3.14, pyOpenSSL 26, bcrypt 5, virtualenv with our patched pip,
and pytest 9 as the onedir baseline.

Multiprocessing / forkserver
----------------------------
* ``salt/scripts.py`` pins ``multiprocessing.set_start_method("fork")`` at
  master entry on non-Windows. Python 3.14 changed the Linux default to
  ``forkserver``, which spawns ``multiprocessing.resource_tracker`` before
  the master drops privileges in ``check_user()`` — the tracker stays
  root and ``test_salt_user_master`` fails with a non-salt child. Forking
  lazily after the privilege drop keeps Py3.13 semantics.

* ``tests/pytests/unit/utils/test_gitfs_locks.py``: hoist
  ``MockedProvider`` from a closure inside ``MyMockedGitProvider.__init__``
  to module level and pass ``tmp_name`` via ``self.opts["_test_tmp_name"]``
  so instances survive pickle round-trip into a forkserver child.

* ``tests/pytests/functional/utils/test_process.py``: replace local
  ``def target(): pass`` inside ``test_subprocess_list_fds`` with a
  module-level ``_noop_target`` for the same reason.

Onedir / virtualenv embed
-------------------------
* ``tools/pkg/build.py``: when copying the patched ``pip-25.2`` wheel
  into virtualenv's embed dir we already rewrote ``BUNDLE_SUPPORT`` but
  never updated ``BUNDLE_SHA256``. virtualenv's ``_verify_bundled_wheel``
  raises ``RuntimeError`` for any wheel listed in BUNDLE_SUPPORT without
  a recorded sha. Compute and write SHA256 entries for every wheel we
  drop into ``embed_dir``.

Python 3.14 deprecations / removals
-----------------------------------
* ``salt/template.py`` and ``salt/utils/templates.py``: replace
  ``codecs.open(...)`` with ``salt.utils.files.fopen(...)``. Python 3.14
  emits ``DeprecationWarning: codecs.open() is deprecated`` to stderr,
  which broke ``test_check_no_import_error``.

* ``salt/states/network.py``: ``difflib.unified_diff(old, new, ...)``
  with ``old``/``new`` as plain strings now raises ``TypeError: input
  must be a sequence of strings, not str`` (was always wrong, 3.14
  enforces). Splitlines first at all 8 call sites.

* ``salt/modules/baredoc.py``: ``ast.NameConstant`` / ``ast.Str`` /
  ``ast.Num`` were removed in Python 3.14. Collapse to a single
  ``isinstance(arg_default, ast.Constant)`` branch using ``.value``.

* ``tests/pytests/unit/auth/test_pam.py``: ``pathlib.Path.exists()``
  now calls ``os.path.exists()`` internally, so the broad
  ``patch("os.path.exists", return_value=False)`` in
  ``test_if_sys_executable_is_used_to_call_pam_auth`` accidentally
  killed the ``pyexe.exists()`` guard in
  ``salt.auth.pam.authenticate``. Narrow to a ``side_effect`` that only
  short-circuits the ``/usr/bin/python3`` lookup.

pyOpenSSL 25 removals
---------------------
* ``salt/modules/tls.py``: probe for ``OpenSSL.crypto.X509Extension``
  at import time; if missing, ``__virtual__()`` refuses to load with a
  pointer to the ``x509_v2`` modules. The legacy ``tls`` module relies
  on ``X509.add_extensions`` / ``X509Req.add_extensions`` /
  ``X509Extension``, all removed in pyOpenSSL 25. Test classes pick up
  the same probe via skipif markers.

* ``salt/beacons/cert_info.py``: same treatment for
  ``X509.get_extension`` / ``X509.get_extension_count``. The beacon
  refuses to load and the test gets a module-level ``skipif``.

pytest 9 collection
-------------------
* ``tests/pytests/scenarios/compat/test_with_versions.py``: pytest 9
  promoted ``Marks applied to fixtures have no effect`` from a warning
  to a collection error. Drop the marks decorating ``salt_minion`` and
  ``cp_file_source`` fixtures (the module-level ``pytestmark`` already
  carries equivalent gating, and the file is already
  ``@pytest.mark.skip("GREAT MODULE MIGRATION")``).

bcrypt 5 / passlib 1.7.4
------------------------
* ``tests/pytests/unit/utils/test_pycrypto.py``:
  ``test_gen_hash_passlib[blowfish-expected2]`` blows up because
  passlib's bcrypt backend init probes the wrap bug with a >72-byte
  password, and bcrypt 5.0 turned the silent truncation into a hard
  ``ValueError``. Skip the blowfish parametrize when bcrypt >= 5.0
  until passlib upstream learns about the new limit.
…tant

Continuing from 2914189, this addresses CI fallout that surfaced
once the previous batch landed.

salt/utils/process.py
---------------------
``Process.__getstate__`` / ``__setstate__`` now round-trip
``_INTERNAL_PROCESS_FINALIZE_FUNCTION_LIST``. Under fork the child
inherited it via memory copy, so SIGTERM-triggered cleanup hooks
(e.g. ``gitfs_finalize_cleanup``) ran fine; under forkserver/spawn
the child got an empty module-level list and the hooks silently
no-op'd. ``test_git_provider_sigterm_cleanup`` was the first symptom
-- it asserted the gitfs lock file got removed on SIGTERM and the
file kept hanging around.

salt/modules/baredoc.py
-----------------------
``_get_module_name`` was still using the legacy ``assign.value.s``
accessor (the ``ast.Str.s`` shim, removed in 3.14). The bare
``except AttributeError: pass`` swallowed it, so ``__virtualname__``
was never extracted and modules like ``cmdmod`` got recorded under
their filename (``cmdmod``) instead of their virtual name (``cmd``).
Switch to ``ast.Constant.value``. Caught by
``test_baredoc_list_modules`` failing with ``KeyError: 'cmd'``.

tests/conftest.py
-----------------
psutil 7.x calls ``os.pidfd_open(pid, 0)`` to wait for a process,
and only falls back to the legacy ``waitpid`` path for
``ESRCH``/``EMFILE``/``ENFILE``/``ENODEV``. On systemd-managed
daemons whose pid was already reaped (and on at least Linux arm64
6.x kernels) ``pidfd_open`` returns ``EINVAL`` instead of
``ESRCH`` for a non-existent pid, which propagates out of
saltfactories teardown and shows up as ``ERROR at teardown of
<test>`` even when the test itself was skipped. ``test_compare_
pkg_versions_redhat_rc`` is skipped on Debian/Ubuntu/Rocky-arm64
images (no ``rpmdev-vercmp``) but the salt-minion fixture
teardown still tripped this in five different pkg-test slugs.

Test-only monkey-patch in ``tests/conftest.py`` so EINVAL also
falls back to ``wait_pid_posix``. Idempotent and guarded.

tests/pytests/unit/states/test_network.py
-----------------------------------------
``test_routes`` and ``test_system`` were asserting against the
*old broken* per-character ``unified_diff`` output (a 4-byte
"True" became ``-T\n-r\n-u\n-e``). The Py3.14 fix in 2914189
made these calls correct (line-based: ``-True``), and the
expected ``comment`` strings now reflect the corrected diff.

tests/pytests/unit/utils/test_reactor2.py
-----------------------------------------
``test_reactor_reactions`` was patching ``codecs.open`` to feed
mock SLS content into ``salt.template.compile_template``. After
2914189 switched the production read to
``salt.utils.files.fopen``, the old patch was a no-op and
reactions came back empty. Patch the new target instead and
drop the now-unused ``import codecs``.
The Py3.14 fix for ``difflib.unified_diff`` in 2914189 assumed the
``old``/``new`` values were always strings (or None) and called
``.splitlines()`` on them. That matched the unit-test mocks but not
production: the ``ip.*`` execution modules return a *list of lines*
(``debian_ip._read_temp`` does ``readlines()``), so calling
``.splitlines()`` on the live data raised ``AttributeError: 'list'
object has no attribute 'splitlines'``, which the state caught into
the comment and broke ``test_system`` in
``tests/integration/states/test_network.py`` on Ubuntu Arm64
integration zeromq 2.

Introduce a small ``_diff_lines`` helper that:
* lets lists pass through (production path),
* splits strings on newlines (unit-test path), and
* maps None / False to ``[]``.

All 8 ``unified_diff`` call sites now feed ``_diff_lines(old),
_diff_lines(new)``.
tests/conftest.py
-----------------
Python 3.14 changed the Linux default ``multiprocessing`` start
method from ``fork`` to ``forkserver``, and forkserver pickles the
target callable across to a fresh interpreter. That is fine for
production daemons (``salt/scripts.py:salt_master`` already pins
``fork``), but breaks tests that hand a ``TestCase`` ``@staticmethod``
or other not-importable callable to ``multiprocessing.Process``: the
child fails with ``ModuleNotFoundError: No module named 'tests'``
because pytest's dynamic ``tests/`` import path is not propagated
to the fresh interpreter when running under ``ONEDIR_TESTRUN``.
Symptom in CI was
``test_signal_processing_handle_signals_called`` -- the SIGTERM
handler never ran because the worker died at unpickle time and the
test asserted on a never-set ``Event``.

Pin the test session to ``fork`` (Py3.13 semantics) at conftest
import. Production gets the same pinning via ``salt/scripts.py``,
so test and prod stay aligned.

changelog/69014.fixed.md
------------------------
Replace the unrelated 3008.x backport note with the actual
high-level intent of saltstack#69014: "Upgrade packaged python to 3.14".
…509 race

Tests for 3008/9.14 on Python 3.14 hit several distinct regressions in CI.
This commit bundles fixes for the symptoms that surfaced in pyversion102:

- macOS pkg-test daemons: when the .pkg installer runs on the GHA macOS
  runner, ``/Library/LaunchDaemons/com.saltstack.salt.{master,minion,api,
  syndic}.plist`` are bootstrapped with ``master: salt`` (unresolvable on
  the runner). saltfactories then sees the launchctl-managed minion under
  the same plist label and skips its own ``salt_minion`` daemon, so the
  local-client fan-out gets back ``{'return': [{}]}`` and ``test_salt_api``
  fails with ``StopIteration``. Bootout the four pkg-installed launchctl
  daemons after install and re-enable their labels (undoing any prior
  ``launchctl disable`` from teardown). Also add ``salt_minion`` to the
  ``test_salt_api`` fixture chain so the fan-out has a connected minion
  even on platforms where the auto-install path fails.

- Linux scenarios session-teardown hang: ``test_issue_regression_65265``
  lazily creates a ``zmq.asyncio.Context()`` inside ``server.publish()``
  without ever calling ``server.close()``. On Python 3.14 the resulting
  ``ZMQbg/Reaper`` and ``ZMQbg/IO/0`` libzmq threads keep the pytest
  interpreter alive forever after ``pytest_sessionfinish`` -- every Linux
  scenarios job sat idle for 1h41m until the workflow timeout fired.
  Wrap the body in try/finally and call ``server.close()`` to term the
  context.

- ``test_certificate_managed_works_with_queued_state_application`` was a
  3-second sleep race; under the slower Py3.14 onedir salt-call startup
  the queued state could finish before the parallel state began. Replace
  the ``test.sleep 3`` + jobwait polling with deterministic file-marker
  synchronization (``start_marker``/``go_marker``).

- ``_pin_multiprocessing_fork()`` in salt/scripts.py was platform-gating
  on win32 only, which left macOS forced into ``fork`` and silently
  corrupted workers via libdispatch's "fork after thread init" rule.
  Restrict the pin to Linux. Mirror the same Linux-only guard in
  tests/conftest.py and add ``_patch_psutil_pidfd_open_einval`` so
  psutil 7.x EINVAL on already-reaped pids stops surfacing as fixture
  teardown errors.
…ckaging, pymssql

Bump the directly-pinned source declarations in requirements/base.txt for
Python 3.14 only, leaving older Python lockfiles on their existing
versions. Re-resolved transitives (gitpython 3.1.49->3.1.50, markdown-it-py
4.0.0->4.1.0) come along on the same regen.

- MarkupSafe: 2.1.5 -> 3.0.3 (cap raised to <4.0.0 for py3.14)
- packaging: 24.0 -> 26.2 (the older "salt-ssh older python" pin still
  applies to python_version >= '3.11' and python_version < '3.14')
- pymssql: 2.3.11 -> 2.3.13 (win32, py3.14 branch only)

clr-loader stays at 0.2.10 because pythonnet 3.1.0rc0 (the only release
that supports py3.14) pins clr_loader<0.3.0,>=0.2.7. Bumping clr-loader
would require a pythonnet release that both supports py3.14 and relaxes
that pin, which doesn't exist yet.

The freebsd .txt files for py3.9-3.13 picked up the new env-marker
branches but their resolved versions are unchanged for python_version <
'3.14' -- the older Python lockfiles still pin packaging==24.0,
pymssql==2.3.11, and the prior MarkupSafe / gitpython values.
@Dkhil
Dkhil requested a review from a team as a code owner May 10, 2026 13:57
@Dkhil
Dkhil requested review from Akm0d and removed request for a team May 10, 2026 13:57
@dwoz

ghost commented May 12, 2026

Copy link
Copy Markdown
Contributor

@Dkhil This should go into 3006.x not 3005.x

@Dkhil

ghost commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

@Dkhil This should go into 3006.x not 3005.x

Ok will fix

@Dkhil Dkhil closed this May 12, 2026
@Dkhil
Dkhil deleted the fix/etcdv3_auth branch May 12, 2026 17:35
@Dkhil

ghost commented May 12, 2026

Copy link
Copy Markdown
Contributor Author

#69136

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.

6 participants