fix: pass search env into Docker task containers (SEARXNG_URL was lost)

Docker tasks do not inherit the worker process environment, they only
get extra_env (-e). SEARXNG_URL in the worker unit EnvironmentFile
therefore never reached codex -> MCP inside the container, so the
auto chain skipped SearXNG and only DuckDuckGo was tried (live e2e:
'all search providers failed: duckduckgo (DNS)').

_docker_task_context now merges any set SEARXNG_URL /
PERPLEXITY_API_KEY / EXA_API_KEY / TAVILY_API_KEY / BRAVE_API_KEY
from the worker environment into extra_env (values never logged;
paid keys follow the same extra_env path as gateway keys).

Tests: 2 new docker-env tests; websearch + tenant isolation = 25
passing; full cloud suite 1038 passing (4 skipped).
This commit is contained in:
host committed 2026-10-28 22:17:19 +08:00
1 parent ffeed25f50
commit d7f0f1be96
3 files changed
+66

No files matched your search

+16
View File
@@ -3,6 +3,22 @@
语义化版本见 `VERSION`(当前 0.1.0)。以下按日期倒序,只列已交付
的大项,每项附实测证据;未建成的东西在“诚实边界”里明说,不夸大。
## 2026-10-28:网页搜索 Docker env 透传(e2e 失败后的修复)
- 根因:Docker 任务容器不继承 worker 进程环境,只拿
extra_env(-e);`SEARXNG_URL` 只写进 worker unit 的
EnvironmentFile 时,容器内 codex -> MCP 根本看不到它,
auto 链跳过 SearXNG、只剩 DuckDuckGo,实弹 e2e 报错
“all search providers failed: duckduckgo (DNS)”。
- 修复:`cloud/worker.py _docker_task_context` 把 worker 环境
里已设置的 `SEARXNG_URL` / `PERPLEXITY_API_KEY` /
`EXA_API_KEY` / `TAVILY_API_KEY` / `BRAVE_API_KEY` 合并进
extra_env(值不进日志;付费 key 与网关 key 同款走法)。
- 单测:新增 2 个(有/无搜索 env 时的容器 env),连同
websearch 旧测、租户隔离旧测共 25 个通过;cloud 全量
1038 个通过(skip 4)。只本地提交,未推、未建 PR、
未合并、未部署;重跑 e2e 须等本修复部署后。
## 2026-10-28:网页搜索接 worker + egress(公开 SearXNG 路)
- `cloud/worker.py --websearch-mcp`(默认关):开启后
+36
View File
@@ -99,6 +99,42 @@ class WorkerWebsearchTestCase(unittest.TestCase):
self.assertEqual(config.count("[mcp_servers.websearch]"), 1)
class DockerEnvTestCase(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.TemporaryDirectory()
self.db_path = os.path.join(self.tmp.name, "control.db")
init_db(self.db_path)
def tearDown(self):
self.tmp.cleanup()
def _docker_context(self):
worker = Worker(self.db_path, "/fake/host.py",
env={"XCM_API_KEY": "test-placeholder"},
tenant_isolation=False, egress_proxy_port=None)
return worker._docker_task_context({"project_id": "projA"})
def test_searxng_url_passes_into_container_env(self):
from unittest import mock
with mock.patch.dict(os.environ,
{"SEARXNG_URL": "http://searx.diante.me"}):
ok, _, _, env, err = self._docker_context()
self.assertTrue(ok)
self.assertIsNone(err)
self.assertEqual(env.get("SEARXNG_URL"), "http://searx.diante.me")
def test_no_search_env_means_no_extra_env(self):
from unittest import mock
with mock.patch.dict(os.environ, {}, clear=False):
for k in ("SEARXNG_URL", "PERPLEXITY_API_KEY", "EXA_API_KEY",
"TAVILY_API_KEY", "BRAVE_API_KEY"):
os.environ.pop(k, None)
ok, _, _, env, err = self._docker_context()
self.assertTrue(ok)
self.assertIsNone(err)
self.assertIsNone(env)
class EgressFilterTestCase(unittest.TestCase):
def test_search_domains_present_in_basic_form(self):
lines = open(REPO_ROOT / "deploy" / "egress" /
+14
View File
@@ -2200,6 +2200,20 @@ class Worker:
"NO_PROXY": "localhost,127.0.0.1",
"no_proxy": "localhost,127.0.0.1",
}
# 搜索 env 透传:Docker 容器不继承 worker 进程环境,只拿
# extra_env(-e),所以 worker 环境里的搜索配置若不显式
# 放进这里,容器内 codex -> MCP 永远看不到 SEARXNG_URL,
# auto 链会跳过 SearXNG、只剩 DuckDuckGo(实弹 e2e 已证实)。
# 只透传已设置的项,值绝不进日志;付费 key 与网关 key 一样
# 走 extra_env(容器内进程本就需要它调 API)。
search_env = {
k: os.environ[k]
for k in ("SEARXNG_URL", "PERPLEXITY_API_KEY", "EXA_API_KEY",
"TAVILY_API_KEY", "BRAVE_API_KEY")
if os.environ.get(k)
}
if search_env:
proxy_env = _merge_env(proxy_env, search_env)
return (True, tenant_mounts, tenant_network, proxy_env, None)
def _run_fork(self, task, quota_check=None):