Skip to content

Fix deltaproxy sub-proxies returning identical grains data (#68248)#69426

Open
dwoz wants to merge 2 commits into
saltstack:3006.xfrom
dwoz:fix/issue-68248
Open

Fix deltaproxy sub-proxies returning identical grains data (#68248)#69426
dwoz wants to merge 2 commits into
saltstack:3006.xfrom
dwoz:fix/issue-68248

Conversation

@dwoz

@dwoz dwoz commented Jun 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

In a deltaproxy controller, each sub-proxy is meant to talk to a distinct
device. With this PR, the grains visible inside the modules loaded for a
sub-proxy (e.g. grains.item serial_number) are the ones produced by that
sub-proxy's own proxymodule, not whatever was loaded on the first pass
through the shared control proxy.

What issues does this PR fix or reference?

Fixes #68248

Previous Behavior

subproxy_post_master_init computed each sub-proxy's grains twice:

  1. First pass, before the sub-proxy's own proxymodule had been initialised,
    using the parent control proxy's LazyLoader (main_proxy). This dict
    was packed into the sub-proxy's execution-module, returner, executor and
    proxy LazyLoaders via pack["__grains__"].
  2. Second pass, after proxytype.init(proxyopts), using the sub-proxy's
    own freshly-created proxymodule. The result was assigned to
    proxyopts["grains"] but not re-packed into any LazyLoader, so
    __grains__ inside loaded modules continued to point at the first-pass
    dict.

Because the first-pass grains all flow through the same main_proxy
LazyLoader, every sub-proxy ends up serving its modules identical
__grains__ data — so running grains.item serial_number against four
controlled minions returns the same value from each.

New Behavior

After the post-init grains refresh, the fresh per-sub-proxy grains dict is
re-packed into _proxy_minion.functions.pack["__grains__"],
_proxy_minion.returners.pack["__grains__"],
_proxy_minion.executors.pack["__grains__"] and
_proxy_minion.proxy.pack["__grains__"]. Each sub-proxy's modules now
see grain values that come from that sub-proxy's own device.

A regression test in tests/pytests/unit/metaproxy/test_deltaproxy.py
drives subproxy_post_master_init for two distinct minion IDs with a
mocked loader stack and asserts that each sub-proxy's
functions.pack["__grains__"] and proxy.pack["__grains__"] reflect the
post-init per-id grains rather than the shared placeholder values.

Merge requirements satisfied?

  • Docs
  • Changelog
  • Tests written/updated

Commits signed with GPG?

Yes

@dwoz dwoz requested a review from a team as a code owner June 12, 2026 00:15
@dwoz dwoz added this to the Sulphur v3006.26 milestone Jun 12, 2026
@dwoz dwoz added the test:full Run the full test suite label Jun 12, 2026
Daniel A. Wozniak and others added 2 commits June 14, 2026 22:57
subproxy_post_master_init computes the sub-proxy grains twice: first
through the parent control proxy's LazyLoader (because the sub-proxy's
own proxymodule has not been initialised yet), then again after the
sub-proxy's init() has been run so the grains can talk to the actual
device. Only the first grains dict was packed into the sub-proxy's
execution-module/returner/executor/proxy LazyLoaders; the post-init
refresh updated proxyopts["grains"] but left the loaders' pack pointing
at the original placeholder dict.

The result is that every sub-proxy under a deltaproxy ends up serving
its modules a __grains__ that was populated by the same shared control
proxy connection, so e.g. grains.item serial_number returns the same
value for every controlled minion.

Re-pack the post-init grains into all four loaders so __grains__ inside
loaded modules reflects each sub-proxy's own device.

Fixes saltstack#68248
Drive ``subproxy_post_master_init`` end-to-end through real
``salt.loader.proxy`` / ``salt.loader.grains`` loaders against an
on-disk extension_modules tree containing a purpose-built proxy
module that returns ``serial_number = <sub-proxy id>``.

Two distinct sub-proxy ids are exercised; the test asserts each
sub-proxy's execution-module / returner / executor / proxy loaders
expose an ``__grains__`` dict whose ``serial_number`` reflects that
sub-proxy's own device (not the placeholder shared with its siblings)
and that ``grains.items()`` invoked via the loader sees the per-device
values. Before the fix, both sub-proxies' loaders point at the same
first-pass grains dict and the test fails with the same serial_number
for every controlled minion.

Complements the unit test in
tests/pytests/unit/metaproxy/test_deltaproxy.py which mocks the
loader stack; this functional test uses real LazyLoaders.

Refs saltstack#68248
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

test:full Run the full test suite

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant