This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch 
gh-readonly-queue/dev/pr-12722-74905675806fb615559f84252b5f75cbd8797c77
in repository https://gitbox.apache.org/repos/asf/seatunnel.git

commit a9db0e65428bd95dfd00ee21484f10dc6908db27
Author: SEZ <[email protected]>
AuthorDate: Wed Oct 14 12:52:22 2026 +0000

    [Fix][seatunnel-cli] Default the engine REST base URL to the Jetty HTTP 
port (#12722)
---
 seatunnel-cli/README.md                     |  4 +-
 seatunnel-cli/README.zh-CN.md               |  4 +-
 seatunnel-cli/env.example.sh                |  2 +-
 seatunnel-cli/seatunnel_cli/connectors.py   | 18 ++++++--
 seatunnel-cli/tests/test_engine_api_base.py | 68 +++++++++++++++++++++++++++++
 5 files changed, 87 insertions(+), 9 deletions(-)

diff --git a/seatunnel-cli/README.md b/seatunnel-cli/README.md
index 677402e626..58f6888e0c 100644
--- a/seatunnel-cli/README.md
+++ b/seatunnel-cli/README.md
@@ -224,7 +224,7 @@ For source installs (`setup.sh` / `pip install`), the 
directory structure does n
 For live connector metadata and REST API job submission, start the SeaTunnel 
server:
 
 ```bash
-export SEATUNNEL_API_BASE=http://localhost:5801  # Default
+export SEATUNNEL_API_BASE=http://localhost:8080  # Default: the engine's Jetty 
HTTP port
 ```
 
 When the engine is running, the CLI operates in **cluster mode** with live 
connector metadata, engine-level validation, and direct job submission via REST 
API. When unavailable, it falls back to **offline mode** using the bundled 
connector metadata.
@@ -248,7 +248,7 @@ When the engine is running, the CLI operates in **cluster 
mode** with live conne
 | `ORCAROUTER_SMALL_FAST_MODEL` | No | `orcarouter/auto` | Fast model for 
OrcaRouter provider |
 | `ORCAROUTER_ECHO_REASONING_CONTENT` | No | `true` | Preserve and replay 
`reasoning_content` for OpenAI-compatible reasoning models (parity with 
`OPENAI_ECHO_REASONING_CONTENT`) |
 | `SEATUNNEL_HOME` | No | Auto-detect | SeaTunnel installation directory. 
Auto-detected in distribution tarball; set manually for source install |
-| `SEATUNNEL_API_BASE` | No | `http://localhost:5801` | SeaTunnel REST API 
endpoint |
+| `SEATUNNEL_API_BASE` | No | `http://localhost:8080` | SeaTunnel REST API 
endpoint (the engine's Jetty HTTP port, not the 5801 member port) |
 | `SEATUNNEL_CLI_DATA` | No | `<cli-package>/.data/` | Override CLI data 
directory (sessions, memory, config) |
 
 ## Usage
diff --git a/seatunnel-cli/README.zh-CN.md b/seatunnel-cli/README.zh-CN.md
index 42945f8121..f57c82fc23 100644
--- a/seatunnel-cli/README.zh-CN.md
+++ b/seatunnel-cli/README.zh-CN.md
@@ -192,7 +192,7 @@ apache-seatunnel-3.0.1/          <-- 自动检测为 SEATUNNEL_HOME
 如需实时连接器元数据和 REST API 作业提交,请启动 SeaTunnel 服务:
 
 ```bash
-export SEATUNNEL_API_BASE=http://localhost:5801  # 默认值
+export SEATUNNEL_API_BASE=http://localhost:8080  # 默认值:引擎的 Jetty HTTP 端口
 ```
 
 引擎运行时,CLI 以**集群模式**运行,支持实时连接器元数据、引擎级校验和通过 REST API 
直接提交作业。引擎不可用时,回退到**离线模式**,使用内置的连接器元数据。
@@ -216,7 +216,7 @@ export SEATUNNEL_API_BASE=http://localhost:5801  # 默认值
 | `ORCAROUTER_SMALL_FAST_MODEL` | 否 | `orcarouter/auto` | OrcaRouter 提供商的快速模型 |
 | `ORCAROUTER_ECHO_REASONING_CONTENT` | 否 | `true` | 保留并回传 
`reasoning_content`(与 `OPENAI_ECHO_REASONING_CONTENT` 对齐) |
 | `SEATUNNEL_HOME` | 否 | 自动检测 | SeaTunnel 安装目录。发行版压缩包中自动检测;源码安装需手动设置 |
-| `SEATUNNEL_API_BASE` | 否 | `http://localhost:5801` | SeaTunnel REST API 端点 |
+| `SEATUNNEL_API_BASE` | 否 | `http://localhost:8080` | SeaTunnel REST API 
端点(引擎的 Jetty HTTP 端口,不是 5801 成员端口) |
 | `SEATUNNEL_CLI_DATA` | 否 | `<cli-package>/.data/` | 覆盖 CLI 数据目录(会话、记忆、配置) |
 
 ## 使用方法
diff --git a/seatunnel-cli/env.example.sh b/seatunnel-cli/env.example.sh
index c53c4e5f57..8556126476 100755
--- a/seatunnel-cli/env.example.sh
+++ b/seatunnel-cli/env.example.sh
@@ -62,4 +62,4 @@
 
 # ─── SeaTunnel Engine (optional) ───
 # export SEATUNNEL_HOME=/path/to/seatunnel
-# export SEATUNNEL_API_BASE=http://localhost:5801
+# export SEATUNNEL_API_BASE=http://localhost:8080
diff --git a/seatunnel-cli/seatunnel_cli/connectors.py 
b/seatunnel-cli/seatunnel_cli/connectors.py
index ecce50c197..ade13972ef 100644
--- a/seatunnel-cli/seatunnel_cli/connectors.py
+++ b/seatunnel-cli/seatunnel_cli/connectors.py
@@ -48,11 +48,21 @@ logger = logging.getLogger(__name__)
 
 # ─── Runtime API client ───
 
-# Default REST API base — SeaTunnel engine HTTP port.
-# In cluster mode (seatunnel-server.sh): REST API on port 5801 by default.
+# Default REST API base — the engine's Jetty HTTP port, 8080.
+#
+# Not 5801. Every endpoint this CLI calls (/running-jobs, /option-rules,
+# /submit-job, /job-info) is registered as a Jetty servlet in JettyService and
+# served on `seatunnel.engine.http.port`, which is 8080 both as the code
+# default and in the packaged config/seatunnel.yaml. Port 5801 is the
+# Hazelcast member port: there the same handlers answer only under
+# RestConstant.CONTEXT_PATH ("/hazelcast/rest/maps") and only when Hazelcast's
+# own REST API is enabled, which the packaged config does not do. A base URL
+# of localhost:5801 therefore returns 404 for every call the CLI makes.
+#
 # In local mode (-m local): REST API is NOT available.
-# Users can override with SEATUNNEL_API_BASE env var.
-_ENGINE_API_BASE = os.environ.get("SEATUNNEL_API_BASE", 
"http://localhost:5801";)
+# Users can override with SEATUNNEL_API_BASE env var — needed when
+# enable-dynamic-port moves the listener off 8080, or for a remote cluster.
+_ENGINE_API_BASE = os.environ.get("SEATUNNEL_API_BASE", 
"http://localhost:8080";)
 _API_CACHE: dict[str, dict] = {}  # memory cache: "source:Jdbc" → parsed 
response
 _ENGINE_AVAILABLE: bool | None = None  # None = not checked yet
 _ENGINE_CHECK_TS: float = 0  # timestamp of last check — recheck after 60s
diff --git a/seatunnel-cli/tests/test_engine_api_base.py 
b/seatunnel-cli/tests/test_engine_api_base.py
new file mode 100644
index 0000000000..af16d3817a
--- /dev/null
+++ b/seatunnel-cli/tests/test_engine_api_base.py
@@ -0,0 +1,68 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Tests for the engine REST base URL the CLI talks to by default.
+
+The default is load-bearing and easy to get wrong, because SeaTunnel exposes
+two HTTP ports that answer on different path prefixes: the Jetty HTTP port
+(`seatunnel.engine.http.port`, 8080 in the packaged config) serves the v2 REST
+API at `/running-jobs`, `/option-rules`, `/submit-job` and `/job-info`, while
+the Hazelcast member port 5801 serves the same handlers only under
+`/hazelcast/rest/maps` and only when Hazelcast's own REST API is enabled.
+
+A wrong default is silent: every call 404s, the engine reads as unreachable,
+and the CLI quietly falls back to offline metadata. These tests pin the port
+and the override so that cannot regress unnoticed.
+"""
+
+import importlib
+
+from seatunnel_cli import connectors
+
+
+def _reload_with_env(monkeypatch, value):
+    """Reload `connectors` so it re-resolves the base URL from the environment.
+
+    The module reads SEATUNNEL_API_BASE once at import time, which is why the
+    production code caches it in a module attribute and why a test has to
+    reload rather than just set the variable.
+    """
+    if value is None:
+        monkeypatch.delenv("SEATUNNEL_API_BASE", raising=False)
+    else:
+        monkeypatch.setenv("SEATUNNEL_API_BASE", value)
+    return importlib.reload(connectors)
+
+
+def test_the_default_is_the_jetty_http_port(monkeypatch):
+    try:
+        assert _reload_with_env(monkeypatch, None)._ENGINE_API_BASE == 
"http://localhost:8080";
+    finally:
+        # Leave the module resolved the way the rest of the session expects.
+        monkeypatch.undo()
+        importlib.reload(connectors)
+
+
+def test_the_environment_override_still_wins(monkeypatch):
+    # The override is the documented escape hatch for a remote cluster, and for
+    # a cluster whose enable-dynamic-port moved the listener off 8080.
+    try:
+        reloaded = _reload_with_env(monkeypatch, "http://zeta-master:18080";)
+        assert reloaded._ENGINE_API_BASE == "http://zeta-master:18080";
+    finally:
+        monkeypatch.undo()
+        importlib.reload(connectors)

Reply via email to