add enterprise lighthouse registry and preflight foundation
This commit is contained in:
parent
af75503a66
commit
9043f4fa60
@ -78,6 +78,8 @@ GLSV-0001=gls/GLSV-0001-SECURITY-VERIFICATION-ARCHITECTURE.hdlp
|
|||||||
GLS-NODE-0001=gls/GLS-NODE-0001-LIGHTHOUSE-NODE-ARCHITECTURE.hdlp
|
GLS-NODE-0001=gls/GLS-NODE-0001-LIGHTHOUSE-NODE-ARCHITECTURE.hdlp
|
||||||
LIGHTHOUSE-NODE-ARCHITECTURE=gls/GLS-NODE-0001-LIGHTHOUSE-NODE-ARCHITECTURE.hdlp
|
LIGHTHOUSE-NODE-ARCHITECTURE=gls/GLS-NODE-0001-LIGHTHOUSE-NODE-ARCHITECTURE.hdlp
|
||||||
NODE-LIGHTHOUSE-WORKORDER=tcs-core/workorders/GLW-WO-20260714-NODE-LIGHTHOUSE-001.hdlp
|
NODE-LIGHTHOUSE-WORKORDER=tcs-core/workorders/GLW-WO-20260714-NODE-LIGHTHOUSE-001.hdlp
|
||||||
|
HLP-LIGHTHOUSE-001=server-tools/enterprise-lighthouse/README.md
|
||||||
|
ENTERPRISE-LIGHTHOUSE-SERVICE=server-tools/enterprise-lighthouse/lighthouse.py
|
||||||
LL-NODE-LIGHTHOUSE=eternal-lake-heart/heartbeat-core/LL-NODE-LIGHTHOUSE-ANCHOR-20260714.hdlp
|
LL-NODE-LIGHTHOUSE=eternal-lake-heart/heartbeat-core/LL-NODE-LIGHTHOUSE-ANCHOR-20260714.hdlp
|
||||||
LL-SG-BRAIN-STATUS=eternal-lake-heart/heartbeat-core/LL-SG-BRAIN-STATUS-20260714.hdlp
|
LL-SG-BRAIN-STATUS=eternal-lake-heart/heartbeat-core/LL-SG-BRAIN-STATUS-20260714.hdlp
|
||||||
SG-BRAIN-STATUS=eternal-lake-heart/heartbeat-core/LL-SG-BRAIN-STATUS-20260714.hdlp
|
SG-BRAIN-STATUS=eternal-lake-heart/heartbeat-core/LL-SG-BRAIN-STATUS-20260714.hdlp
|
||||||
|
|||||||
@ -101,6 +101,7 @@ Stage 3 · RELEASE · 正式部署
|
|||||||
| GLSV-0001 | 光湖系统安全验证架构说明 | ICE-GL∞ × 当前协作实例 | REGISTERED_REFERENCE_ARCHITECTURE | GLS 图书域 · 2026-07-14 |
|
| GLSV-0001 | 光湖系统安全验证架构说明 | ICE-GL∞ × 当前协作实例 | REGISTERED_REFERENCE_ARCHITECTURE | GLS 图书域 · 2026-07-14 |
|
||||||
| GLS-NODE-0001 | 光湖总灯塔与分布式服务器节点接入架构 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED_ARCHITECTURE · IMPLEMENTATION_PENDING | 五域 / 域入口 / 个人服务器 / 人格体 / 当前实例 · 2026-07-14 |
|
| GLS-NODE-0001 | 光湖总灯塔与分布式服务器节点接入架构 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED_ARCHITECTURE · IMPLEMENTATION_PENDING | 五域 / 域入口 / 个人服务器 / 人格体 / 当前实例 · 2026-07-14 |
|
||||||
| LL-SG-BRAIN-STATUS-20260714 | 新加坡大脑服务器 · 铸渊现实运维状态锚 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED_OPERATIONAL_STATUS | BS-SG-001 已在线;新总灯塔节点接入待实施 · 2026-07-14 |
|
| LL-SG-BRAIN-STATUS-20260714 | 新加坡大脑服务器 · 铸渊现实运维状态锚 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED_OPERATIONAL_STATUS | BS-SG-001 已在线;新总灯塔节点接入待实施 · 2026-07-14 |
|
||||||
|
| HLP-LIGHTHOUSE-001 | 企业灯塔基础服务 · 节点登记 / 预检 / 审计 | ICE-GL∞ × 当前协作实例 | REGISTERED_TESTED_SOURCE · DEPLOY_PENDING | 仅固定动作预检;拒绝裸命令;2026-07-14 |
|
||||||
| HLP-OPS-0001 | HoloLake Era 分布式服务器控制与自然语言运维 · 第五域桥接规范 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED | Notion GLS 架构 v1.0 · 2026-07-12 |
|
| HLP-OPS-0001 | HoloLake Era 分布式服务器控制与自然语言运维 · 第五域桥接规范 | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED | Notion GLS 架构 v1.0 · 2026-07-12 |
|
||||||
| GLW-RD-002 | HLDP 研发语言与服务器转译链 · HoloLake 产品研发硬准入 | ICE-GL∞ × ICE-GL-ZL-001 | REGISTERED(规范)· RUNTIME_NOT_DEPLOYED | GLW-RD-002 · 2026-07-14 |
|
| GLW-RD-002 | HLDP 研发语言与服务器转译链 · HoloLake 产品研发硬准入 | ICE-GL∞ × ICE-GL-ZL-001 | REGISTERED(规范)· RUNTIME_NOT_DEPLOYED | GLW-RD-002 · 2026-07-14 |
|
||||||
| SYS-GLW-POS-0001 / GLW-OS-000 / GLW-OS-001 | 冰朔通感语言核操作系统 · Tolaria / 光湖 App | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED + DEVELOPMENT_ACTIVE | HLP-BRD-0002 · GLW-OS-001 · 2026-07-13 |
|
| SYS-GLW-POS-0001 / GLW-OS-000 / GLW-OS-001 | 冰朔通感语言核操作系统 · Tolaria / 光湖 App | ICE-GL∞ × ICE-GL-ZY001 | REGISTERED + DEVELOPMENT_ACTIVE | HLP-BRD-0002 · GLW-OS-001 · 2026-07-13 |
|
||||||
|
|||||||
@ -172,6 +172,7 @@ receipt_path: ""
|
|||||||
| GLSV 架构说明 | `REGISTERED_REFERENCE_ARCHITECTURE` | 当前已有 Gatekeeper v3.2 兼容实现;企业灯塔迁移未完成 |
|
| GLSV 架构说明 | `REGISTERED_REFERENCE_ARCHITECTURE` | 当前已有 Gatekeeper v3.2 兼容实现;企业灯塔迁移未完成 |
|
||||||
| 广州企业灯塔公众站 | `ONLINE` | `guanghu.chat` 现有公众主页可访问 |
|
| 广州企业灯塔公众站 | `ONLINE` | `guanghu.chat` 现有公众主页可访问 |
|
||||||
| 广州企业灯塔服务 | `REPAIR_REQUIRED` | 2026-07-14 只读检查发现服务账户缺少运行目录访问权限,处于重启循环;不得在此基础上直接叠加新功能 |
|
| 广州企业灯塔服务 | `REPAIR_REQUIRED` | 2026-07-14 只读检查发现服务账户缺少运行目录访问权限,处于重启循环;不得在此基础上直接叠加新功能 |
|
||||||
|
| 企业灯塔基础服务源码 | `REGISTERED_TESTED_SOURCE · DEPLOY_PENDING` | `HLP-LIGHTHOUSE-001` 已实现节点接入申请、固定动作预检与审计;不接受裸命令 |
|
||||||
| 节点注册库 | `NOT_DEPLOYED` | 尚未有此架构下的正式 `domain / node / human / persona / instance` 关系库 |
|
| 节点注册库 | `NOT_DEPLOYED` | 尚未有此架构下的正式 `domain / node / human / persona / instance` 关系库 |
|
||||||
| GLSV 授权中心(企业灯塔) | `NOT_DEPLOYED` | 需在修复灯塔生命周期后部署 |
|
| GLSV 授权中心(企业灯塔) | `NOT_DEPLOYED` | 需在修复灯塔生命周期后部署 |
|
||||||
| GLSV 节点连接器 | `NOT_DEPLOYED` | 需按域入口、再按个人服务器逐台接入 |
|
| GLSV 节点连接器 | `NOT_DEPLOYED` | 需按域入口、再按个人服务器逐台接入 |
|
||||||
|
|||||||
7
server-tools/enterprise-lighthouse/README.md
Normal file
7
server-tools/enterprise-lighthouse/README.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# 企业灯塔基础服务
|
||||||
|
|
||||||
|
这是总灯塔的第一阶段:五域登记、节点接入申请、固定动作预检和审计。它**不是**远程 Shell,拒绝 `cmd`、`shell`、`command` 字段,也不执行操作。
|
||||||
|
|
||||||
|
部署后只监听本机 `127.0.0.1:8031`。公众网站与后续 GLSV 页面通过 Nginx 以单独的受控路由接入;在邮件确认与节点连接器完成前,不开放节点激活或执行。
|
||||||
|
|
||||||
|
固定动作只有:`health_check`、`backup`、`deploy_release`、`restart_service`、`rollback`。部署、重启和回滚必须在预检中具备备份引用与回滚计划;预检通过也只是“可申请人类授权”,不会执行。
|
||||||
Binary file not shown.
Binary file not shown.
@ -0,0 +1,21 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Guanghu Enterprise Lighthouse Registry and Preflight
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=lighthouse
|
||||||
|
Group=lighthouse
|
||||||
|
WorkingDirectory=/opt/guanghu-enterprise-lighthouse
|
||||||
|
EnvironmentFile=/etc/guanghu-enterprise-lighthouse.env
|
||||||
|
ExecStart=/usr/bin/python3 /opt/guanghu-enterprise-lighthouse/lighthouse.py
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=3
|
||||||
|
NoNewPrivileges=yes
|
||||||
|
PrivateTmp=yes
|
||||||
|
ProtectSystem=strict
|
||||||
|
ProtectHome=yes
|
||||||
|
ReadWritePaths=/var/lib/guanghu-enterprise-lighthouse
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
162
server-tools/enterprise-lighthouse/lighthouse.py
Normal file
162
server-tools/enterprise-lighthouse/lighthouse.py
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Enterprise Lighthouse foundation: registry, preflight, and audit only.
|
||||||
|
|
||||||
|
This service is deliberately not a remote shell. It accepts node admission
|
||||||
|
requests and validates fixed operation plans; execution remains with a future
|
||||||
|
GLSV node connector after human authorization.
|
||||||
|
"""
|
||||||
|
import hmac
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import time
|
||||||
|
import uuid
|
||||||
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||||
|
from ipaddress import ip_address
|
||||||
|
|
||||||
|
DB = os.environ.get("LIGHTHOUSE_DB", "/var/lib/guanghu-enterprise-lighthouse/lighthouse.db")
|
||||||
|
TOKEN = os.environ.get("LIGHTHOUSE_ADMIN_TOKEN", "")
|
||||||
|
HOST = os.environ.get("LIGHTHOUSE_BIND", "127.0.0.1")
|
||||||
|
PORT = int(os.environ.get("LIGHTHOUSE_PORT", "8031"))
|
||||||
|
FIXED_ACTIONS = {"health_check", "backup", "deploy_release", "restart_service", "rollback"}
|
||||||
|
DOMAINS = {
|
||||||
|
"DOMAIN-ZS": "零感域", "DOMAIN-MAIN": "光湖主域", "DOMAIN-SUB": "光湖分域",
|
||||||
|
"DOMAIN-ZERO": "光湖零域", "DOMAIN-FIFTH": "第五域",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def now():
|
||||||
|
return int(time.time())
|
||||||
|
|
||||||
|
|
||||||
|
def connection():
|
||||||
|
os.makedirs(os.path.dirname(DB), exist_ok=True)
|
||||||
|
db = sqlite3.connect(DB)
|
||||||
|
db.row_factory = sqlite3.Row
|
||||||
|
db.executescript("""
|
||||||
|
CREATE TABLE IF NOT EXISTS domains (
|
||||||
|
id TEXT PRIMARY KEY, name TEXT NOT NULL, state TEXT NOT NULL, created_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS intakes (
|
||||||
|
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, state TEXT NOT NULL,
|
||||||
|
human_name TEXT NOT NULL, email TEXT NOT NULL, server_ip TEXT NOT NULL,
|
||||||
|
domain_id TEXT NOT NULL, persona_ids TEXT NOT NULL, repository_urls TEXT NOT NULL,
|
||||||
|
hosting_mode TEXT NOT NULL, notes TEXT NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS nodes (
|
||||||
|
id TEXT PRIMARY KEY, domain_id TEXT NOT NULL, intake_id TEXT, state TEXT NOT NULL,
|
||||||
|
display_name TEXT NOT NULL, server_ip TEXT NOT NULL, allowed_actions TEXT NOT NULL,
|
||||||
|
public_key_fingerprint TEXT, last_heartbeat INTEGER, created_at INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
CREATE TABLE IF NOT EXISTS audit (
|
||||||
|
id TEXT PRIMARY KEY, created_at INTEGER NOT NULL, kind TEXT NOT NULL, payload TEXT NOT NULL
|
||||||
|
);
|
||||||
|
""")
|
||||||
|
for domain_id, name in DOMAINS.items():
|
||||||
|
db.execute("INSERT OR IGNORE INTO domains VALUES (?, ?, 'PENDING_ENTRY_NODE', ?)", (domain_id, name, now()))
|
||||||
|
db.commit()
|
||||||
|
return db
|
||||||
|
|
||||||
|
|
||||||
|
def audit(db, kind, payload):
|
||||||
|
db.execute("INSERT INTO audit VALUES (?, ?, ?, ?)", (str(uuid.uuid4()), now(), kind, json.dumps(payload, ensure_ascii=False)))
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
|
||||||
|
def parse_json(handler):
|
||||||
|
length = int(handler.headers.get("Content-Length", "0"))
|
||||||
|
if not 0 < length <= 50_000:
|
||||||
|
raise ValueError("request body must be between 1 and 50000 bytes")
|
||||||
|
return json.loads(handler.rfile.read(length).decode("utf-8"))
|
||||||
|
|
||||||
|
|
||||||
|
def valid_email(value):
|
||||||
|
return isinstance(value, str) and len(value) <= 254 and value.count("@") == 1
|
||||||
|
|
||||||
|
|
||||||
|
def require_admin(handler):
|
||||||
|
received = handler.headers.get("X-Lighthouse-Admin-Token", "")
|
||||||
|
return bool(TOKEN) and hmac.compare_digest(received, TOKEN)
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
server_version = "GuanghuEnterpriseLighthouse/1.0"
|
||||||
|
|
||||||
|
def log_message(self, fmt, *args):
|
||||||
|
print("[lighthouse] " + fmt % args)
|
||||||
|
|
||||||
|
def respond(self, status, body):
|
||||||
|
encoded = json.dumps(body, ensure_ascii=False).encode("utf-8")
|
||||||
|
self.send_response(status)
|
||||||
|
self.send_header("Content-Type", "application/json; charset=utf-8")
|
||||||
|
self.send_header("Content-Length", str(len(encoded)))
|
||||||
|
self.send_header("Cache-Control", "no-store")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(encoded)
|
||||||
|
|
||||||
|
def do_GET(self):
|
||||||
|
db = connection()
|
||||||
|
try:
|
||||||
|
if self.path == "/health":
|
||||||
|
return self.respond(200, {"ok": True, "service": "guanghu-enterprise-lighthouse", "mode": "registry-and-preflight-only", "execution": "disabled"})
|
||||||
|
if self.path == "/v1/status":
|
||||||
|
counts = {row["state"]: row["count"] for row in db.execute("SELECT state, COUNT(*) AS count FROM nodes GROUP BY state")}
|
||||||
|
return self.respond(200, {"ok": True, "domains": [dict(row) for row in db.execute("SELECT id,name,state FROM domains ORDER BY id")], "node_counts": counts, "fixed_actions": sorted(FIXED_ACTIONS), "raw_shell": "rejected"})
|
||||||
|
if self.path == "/v1/nodes":
|
||||||
|
return self.respond(200, {"ok": True, "nodes": [dict(row) for row in db.execute("SELECT id,domain_id,state,display_name,allowed_actions,last_heartbeat,created_at FROM nodes ORDER BY created_at DESC")]})
|
||||||
|
return self.respond(404, {"ok": False, "error": "not found"})
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
def do_POST(self):
|
||||||
|
if not require_admin(self):
|
||||||
|
return self.respond(401, {"ok": False, "error": "admin authorization required"})
|
||||||
|
try:
|
||||||
|
payload = parse_json(self)
|
||||||
|
except (ValueError, json.JSONDecodeError) as error:
|
||||||
|
return self.respond(400, {"ok": False, "error": str(error)})
|
||||||
|
if "cmd" in payload or "shell" in payload or "command" in payload:
|
||||||
|
return self.respond(400, {"ok": False, "error": "raw commands are never accepted by the lighthouse"})
|
||||||
|
db = connection()
|
||||||
|
try:
|
||||||
|
if self.path == "/v1/intakes":
|
||||||
|
required = ("human_name", "email", "server_ip", "domain_id")
|
||||||
|
if any(not payload.get(field) for field in required) or payload["domain_id"] not in DOMAINS or not valid_email(payload["email"]):
|
||||||
|
return self.respond(400, {"ok": False, "error": "human_name, valid email, server_ip, and known domain_id are required"})
|
||||||
|
try:
|
||||||
|
ip_address(payload["server_ip"])
|
||||||
|
except ValueError:
|
||||||
|
return self.respond(400, {"ok": False, "error": "server_ip must be a valid IP address"})
|
||||||
|
intake_id = "INTAKE-" + uuid.uuid4().hex[:12].upper()
|
||||||
|
db.execute("INSERT INTO intakes VALUES (?, ?, 'PENDING_REVIEW', ?, ?, ?, ?, ?, ?, ?, ?)", (
|
||||||
|
intake_id, now(), payload["human_name"].strip(), payload["email"].strip(), payload["server_ip"], payload["domain_id"],
|
||||||
|
json.dumps(payload.get("persona_ids", [])), json.dumps(payload.get("repository_urls", [])),
|
||||||
|
payload.get("hosting_mode", "own"), payload.get("notes", ""),
|
||||||
|
))
|
||||||
|
audit(db, "intake_created", {"intake_id": intake_id, "domain_id": payload["domain_id"]})
|
||||||
|
return self.respond(201, {"ok": True, "intake_id": intake_id, "state": "PENDING_REVIEW", "next": "sovereign approval, human email confirmation, then node connector enrollment"})
|
||||||
|
if self.path == "/v1/preflight":
|
||||||
|
action, node_id = payload.get("action"), payload.get("target_node_id")
|
||||||
|
if action not in FIXED_ACTIONS or not node_id:
|
||||||
|
return self.respond(400, {"ok": False, "error": "fixed action and target_node_id are required", "allowed_actions": sorted(FIXED_ACTIONS)})
|
||||||
|
node = db.execute("SELECT * FROM nodes WHERE id=?", (node_id,)).fetchone()
|
||||||
|
if not node:
|
||||||
|
return self.respond(404, {"ok": False, "decision": "REJECT", "reason": "target node is not registered"})
|
||||||
|
allowed = json.loads(node["allowed_actions"])
|
||||||
|
reasons = []
|
||||||
|
if node["state"] != "ACTIVE": reasons.append("target node is not ACTIVE")
|
||||||
|
if action not in allowed: reasons.append("action is not in the node allowlist")
|
||||||
|
if action in {"deploy_release", "restart_service", "rollback"} and not payload.get("rollback_plan"): reasons.append("rollback_plan is required")
|
||||||
|
if action in {"deploy_release", "restart_service"} and not payload.get("backup_reference"): reasons.append("backup_reference is required")
|
||||||
|
decision = "ALLOW_FOR_AUTHORIZATION" if not reasons else "REJECT"
|
||||||
|
result = {"ok": not reasons, "decision": decision, "reasons": reasons, "execution": "not performed; GLSV human authorization still required"}
|
||||||
|
audit(db, "preflight", {"target_node_id": node_id, "action": action, "decision": decision, "reasons": reasons})
|
||||||
|
return self.respond(200, result)
|
||||||
|
return self.respond(404, {"ok": False, "error": "not found"})
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print(f"Enterprise Lighthouse listening on {HOST}:{PORT}")
|
||||||
|
ThreadingHTTPServer((HOST, PORT), Handler).serve_forever()
|
||||||
31
server-tools/enterprise-lighthouse/test_lighthouse.py
Normal file
31
server-tools/enterprise-lighthouse/test_lighthouse.py
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import urllib.request
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
ROOT = Path(__file__).parent
|
||||||
|
with tempfile.TemporaryDirectory() as temp:
|
||||||
|
env = {**os.environ, "LIGHTHOUSE_DB": f"{temp}/lighthouse.db", "LIGHTHOUSE_ADMIN_TOKEN": "test-token", "LIGHTHOUSE_PORT": "48031"}
|
||||||
|
process = subprocess.Popen([sys.executable, "lighthouse.py"], cwd=ROOT, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
||||||
|
try:
|
||||||
|
for _ in range(30):
|
||||||
|
try:
|
||||||
|
assert json.load(urllib.request.urlopen("http://127.0.0.1:48031/health", timeout=1))["execution"] == "disabled"
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
time.sleep(.1)
|
||||||
|
else: raise AssertionError("server did not start")
|
||||||
|
request = urllib.request.Request("http://127.0.0.1:48031/v1/intakes", data=json.dumps({"human_name":"Test","email":"test@example.invalid","server_ip":"203.0.113.8","domain_id":"DOMAIN-FIFTH"}).encode(), method="POST", headers={"X-Lighthouse-Admin-Token":"test-token"})
|
||||||
|
assert json.load(urllib.request.urlopen(request))["state"] == "PENDING_REVIEW"
|
||||||
|
bad = urllib.request.Request("http://127.0.0.1:48031/v1/intakes", data=b'{"cmd":"rm -rf /"}', method="POST", headers={"X-Lighthouse-Admin-Token":"test-token"})
|
||||||
|
try: urllib.request.urlopen(bad)
|
||||||
|
except urllib.error.HTTPError as error: assert error.code == 400
|
||||||
|
else: raise AssertionError("raw command was not rejected")
|
||||||
|
finally:
|
||||||
|
process.terminate(); process.wait(timeout=5)
|
||||||
|
print("enterprise lighthouse tests passed")
|
||||||
@ -20,6 +20,7 @@
|
|||||||
- [x] 登记桌面接入表为 `PENDING_REVIEW` 的输入窗口
|
- [x] 登记桌面接入表为 `PENDING_REVIEW` 的输入窗口
|
||||||
- [x] 登记 GLSV 中心 / 节点连接器双层模型
|
- [x] 登记 GLSV 中心 / 节点连接器双层模型
|
||||||
- [x] 对广州企业灯塔完成只读基础盘点
|
- [x] 对广州企业灯塔完成只读基础盘点
|
||||||
|
- [x] 完成企业灯塔基础服务源码与本地测试:登记申请、固定动作预检、审计;拒绝裸命令
|
||||||
|
|
||||||
## 当前阻塞
|
## 当前阻塞
|
||||||
|
|
||||||
@ -31,7 +32,7 @@
|
|||||||
## 后续固定顺序
|
## 后续固定顺序
|
||||||
|
|
||||||
1. 修复企业灯塔服务生命周期并保存当前配置备份。
|
1. 修复企业灯塔服务生命周期并保存当前配置备份。
|
||||||
2. 将灯塔源码与部署单元纳入正式仓库;建立测试与回滚点。
|
2. 部署已入第五域的 `HLP-LIGHTHOUSE-001`,建立测试与回滚点。
|
||||||
3. 部署 GLSV Authorization Center(先只读 / 申请状态)。
|
3. 部署 GLSV Authorization Center(先只读 / 申请状态)。
|
||||||
4. 部署一个 GLSV Node Connector 测试节点;验证邮箱确认、心跳和回执。
|
4. 部署一个 GLSV Node Connector 测试节点;验证邮箱确认、心跳和回执。
|
||||||
5. 登记每个域入口节点,再登记个人服务器节点。
|
5. 登记每个域入口节点,再登记个人服务器节点。
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user