[Bug]: Deltaproxy sub-proxies share one module object, so module-level state in proxy/execution modules leaks across all controlled minions
Maintainer thường phản hồi trong vòng 3 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 35/100
Hướng nghiên cứu
Start by reading salt/loader/lazy.py, especially LazyLoader._load_module() and subproxy_post_master_init(), to see how loaded_base_name builds the module namespace and how sys.modules reuses module objects. Reproduce with the fakedev proxy/module from the report, comparing fakedev.host, fakedev.context_host and grains.item device_host across minion1-3. Done means each sub-proxy has isolated module globals and every controlled minion reports its own host.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
What happened?
Description
This is a follow-up to #68248. That issue was closed by #69426, which re-packs __grains__ into each sub-proxy's loaders. The underlying problem is broader and that change does not fix it: with deltaproxy, every sub-proxy's LazyLoader resolves a given module to the same Python module object. Any module-level state (connection handles, DETAILS dicts, caches) is therefore shared by all controlled minions, and the last sub-proxy to run init() wins. Every execution module that reaches the device through __proxy__ then returns the data of that one device, which is why grains, and in fact any function, return identical data for all controlled minions.
I applied #69426 to a 3006.9 install and the behaviour remains, as noted in #68248.
Root cause
In salt/loader/lazy.py, LazyLoader._load_module() loads modules with the deprecated spec.loader.load_module(), under a namespace built from loaded_base_name and the tag, e.g. salt.loaded.ext.proxy.fakedev. Every sub-proxy loader created in subproxy_post_master_init() uses the same loaded_base_name (salt.loaded), so they all compute the same namespace. When that name is already in sys.modules, load_module() re-executes the code into the existing module object instead of creating a new one.
The packed dunders (__context__, __grains__, __pillar__, __proxy__, ...) are injected as NamedLoaderContext objects that resolve through the active loader, so those stay per sub-proxy. Plain module globals do not.
Setup
Deltaproxy config (/etc/salt/proxy):
master: 127.0.0.1
metaproxy: deltaproxy
enable_fqdns_grains: False
multiprocessing: False # also reproduces with True
skip_connect_on_init: True
Pillar top.sls:
base:
dp_control:
- control
minion1:
- p_minion1
minion2:
- p_minion2
minion3:
- p_minion3
control.sls:
proxy:
proxytype: deltaproxy
ids: [minion1, minion2, minion3]
p_minionN.sls (N = 1, 2, 3):
proxy:
proxytype: fakedev
host: 10.0.0.1N
salt://_proxy/fakedev.py. It follows the common pattern of keeping connection state in a module global, and also stores the host in __context__ for comparison:
__proxyenabled__ = ["fakedev"]
DETAILS = {}
def __virtual__():
return "fakedev"
def init(opts):
DETAILS["host"] = opts["proxy"]["host"]
DETAILS["initialized"] = True
__context__["fakedev"] = {"host": opts["proxy"]["host"]}
def initialized():
return DETAILS.get("initialized", False)
def shutdown(opts):
return True
def grains():
return {"device_host": DETAILS.get("host")}
def host():
return DETAILS.get("host")
def context_host():
return __context__["fakedev"]["host"]
salt://_modules/fakedev.py:
__proxyenabled__ = ["fakedev"]
def __virtual__():
return "fakedev"
def host():
return __proxy__["fakedev.host"]()
def context_host():
return __proxy__["fakedev.context_host"]()
Steps to Reproduce the behavior
salt-proxy --proxyid=dp_control -d
salt-key -A
salt 'minion*' fakedev.host --out=txt
salt 'minion*' fakedev.context_host --out=txt
salt 'minion*' grains.item device_host --out=txt
salt 'minion*' pillar.get proxy:host --out=txt
Output:
# module global: every minion reports the last-initialised device
minion1: 10.0.0.13
minion2: 10.0.0.13
minion3: 10.0.0.13
# __context__: correct per minion
minion1: 10.0.0.11
minion2: 10.0.0.12
minion3: 10.0.0.13
# grains derived from the proxy module: same wrong value everywhere
minion1: {'device_host': '10.0.0.13'}
minion2: {'device_host': '10.0.0.13'}
minion3: {'device_host': '10.0.0.13'}
# pillar: correct per minion
minion1: 10.0.0.11
minion2: 10.0.0.12
minion3: 10.0.0.13
The same result occurs with multiprocessing: True, because forked job processes inherit the already-shared module.
Expected behavior
Each sub-proxy should have isolated module state, so each controlled minion talks to and reports on its own device, as it would under a regular per-device salt-proxy.
Additional context
Related: #68248 (closed by #69426, which only addresses __grains__ re-packing).
Type of salt install
Official deb
Major version
3006.x
What supported OS are you seeing the problem on? Can select multiple. (If bug appears on an unsupported OS, please open a GitHub Discussion instead)
ubuntu-24.04, ubuntu-22.04
salt --versions-report output
Salt Version:
Salt: 3006.9
Python Version:
Python: 3.10.14 (main, Jun 26 2024, 11:44:37) [GCC 11.2.0]
Dependency Versions:
cffi: 1.14.6
cherrypy: unknown
cryptography: 42.0.5
dateutil: 2.8.1
docker-py: Not Installed
gitdb: Not Installed
gitpython: Not Installed
Jinja2: 3.1.4
libgit2: Not Installed
looseversion: 1.0.2
M2Crypto: Not Installed
Mako: Not Installed
msgpack: 1.0.2
msgpack-pure: Not Installed
mysql-python: Not Installed
packaging: 22.0
pycparser: 2.21
pycrypto: Not Installed
pycryptodome: 3.19.1
pygit2: Not Installed
python-gnupg: 0.4.8
PyYAML: 6.0.1
PyZMQ: 23.2.0
relenv: 0.17.0
smmap: Not Installed
timelib: 0.2.4
Tornado: 4.5.3
ZMQ: 4.3.4
System Versions:
dist: ubuntu 22.04.4 jammy
locale: utf-8
machine: x86_64
release: 5.15.0-185-generic
system: Linux
version: Ubuntu 22.04.4 jammy
- Ngôn ngữ chính
- Python
- Star
- 15.8k
- Fork
- 5.6k
- Merge trung bình
- 5 ngày 9 giờ
- Pull request đã merge (30 ngày)
- 99
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của saltstack/salt
-
[Bug]: Scheduled returns scan all accepted minion keys causing extreme loadCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mởbug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 76/100
saltstack/salt#70789 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 3 ngày
-
[Bug]: pip.installed with upgrade=True upgrades a package but returns an empty changes dictionaryCó thể đã có người làm @ITJamie đã nhận 1 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 3 ngày
-
bug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 3 ngày
-
[Bug]: Minion with file_client: local crashes on connect: 'AsyncPillar' object has no attribute 'aclose'Có thể đã có người làm @shawnrruss đã nhận 9 ngày trước. Đang mởbug needs-triage
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 3 ngày
-
[Bug]: mine.update clear=True raises UnboundLocalError on the master and stores nothingCó thể đã có người làm @cestercian đã nhận 11 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 74/100
saltstack/salt#70622 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 3 ngày
Tất cả issue của saltstack/salt
Issue tương tự
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
btclib-org/btclib-wallet#872 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 87/100
btclib-org/btclib#3093 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
good first issue
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 85/100
patrickballard/wheelgap#269 ·
-
priority: low python
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 88/100
LearningCircuit/local-deep-research#8635 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
README says the land/sea mask must match the XS grid; the demo mask does not, and Step 4 worksĐang mởdocumentation
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
buckai-observatory/Automated-Cloud-Removal-and-Seamless-Satellite-Image-Mosaicking-Pipeline#52 ·
Maintainer thường phản hồi trong vòng 1 ngày