# Argus Master 模块

Argus Master 是基于 Flask + SQLite 的节点管理服务，负责：

- 接收 agent 的注册与重注册请求，分配/校验节点 ID。
- 存储节点元数据、配置、健康状态，并根据上报时间计算在线状态。
- 输出仅包含在线节点的 `nodes.json`，供其他模块（如 metric）消费。
- 提供查询、配置更新、统计等 REST API。

## 构建与运行

```bash
cd src/master
./scripts/build_images.sh            # 生成 argus-master:latest 镜像
```

如需离线构建，先在有网环境运行准备脚本：

```bash
cd src/master
./scripts/prepare_offline_wheels.sh --pip-version 25.2  # 可选 --clean
```

脚本会把 `requirements.txt` 及 pip 指定版本全部下载到 `offline_wheels/`。随后将源码目录（含该子目录）与基础镜像一并拷贝到内网，执行：

```bash
cd src/master
./scripts/build_images.sh --offline --tag argus-master:latest
```

若内网缺少 `python:3.11-slim`，请提前在外网 `docker save` 后通过离线介质 `docker load`。

本仓库提供的端到端测试会使用 `src/master/tests/docker-compose.yml` 启动示例环境：

```bash
cd src/master/tests
./scripts/01_up_master.sh            # 构建镜像并启动容器，监听 http://localhost:31300
```

服务日志与数据默认写入 `tests/private/argus/master/`（或自定义的挂载目录）。

## 运行时环境变量

| 变量 | 默认值 | 说明 |
| --- | --- | --- |
| `DB_PATH` | `/private/argus/master/db.sqlite3` | SQLite 数据库存放路径。目录会在启动时自动创建。 |
| `METRIC_NODES_JSON_PATH` | `/private/argus/metric/prometheus/nodes.json` | `nodes.json` 输出位置，仅包含在线节点。采用原子写入避免部分文件。 |
| `OFFLINE_THRESHOLD_SECONDS` | `180` | 若距离最近一次上报时间超过该值，调度器会将节点标记为 `offline`。 |
| `ONLINE_THRESHOLD_SECONDS` | `120` | 若最新上报时间距当前不超过该值，则标记为 `online`。范围处于两个阈值之间时保持原状态。 |
| `SCHEDULER_INTERVAL_SECONDS` | `30` | 调度器检查节点状态与刷新 `nodes.json` 的周期。 |
| `NODE_ID_PREFIX` | `A` | 新节点 ID 的前缀，实际 ID 形如 `A1`、`A2`。 |
| `AUTH_MODE` | `disabled` | 预留的认证开关，当前固定为禁用。 |

## 进程与监控

镜像内通过 `supervisord` 管理进程：

- `master`：执行 `/usr/local/bin/start-master.sh`，默认以 4 个 Gunicorn worker 监听 `0.0.0.0:3000`；可通过环境变量 `GUNICORN_WORKERS`、`GUNICORN_BIND`、`GUNICORN_EXTRA_ARGS` 调整。
- `dns-monitor`：轮询 `/private/argus/etc/dns.conf`，若发现变更则调用 `/private/argus/etc/update-dns.sh`，日志输出在 `/var/log/supervisor/dns-monitor.log`。

镜像构建阶段会安装 `supervisor`/`net-tools`/`inetutils-ping`/`vim` 等基础工具，并在运行前把 apt 源切换到内网镜像，方便容器内进一步运维。

## 域名注册与 DNS 联动

- Master 容器启动时会主动执行 `/private/argus/etc/update-dns.sh`（若存在），把自身 `/etc/resolv.conf` 指向 bind 服务提供的 DNS；随后解析 `eth0` 的 IPv4 地址并写入 `/private/argus/etc/master.argus.com`。该文件会被 bind 模块的 `argus_dns_sync.sh` 监控，用于生成 `master.argus.com` → 当前容器 IP 的 A 记录。
- 测试与生产都需要将 bind 下发的 `update-dns.sh`、`dns.conf` 等文件挂载到 `/private/argus/etc/`。在 E2E 场景中，`tests/private/argus/etc` 会由脚本自动准备。
- 其他模块（如 agent）在启动脚本中只需执行同一份 `update-dns.sh`，即可使用域名访问 master；若域名注册异常，agent 将无法成功上报，可据此快速定位问题。

## REST API 详解

基础路径：`/api/v1/master`，全部返回 JSON。

### 1. `GET /nodes`
- **用途**：获取所有节点的简要信息。
- **响应示例**：
  ```json
  [
    {"id": "A1", "name": "dev-user-inst-pod-0", "status": "online", "type": "agent", "version": "1.1.0"}
  ]
  ```

### 2. `GET /nodes/{id}`
- **用途**：获取节点详情（包含配置、健康、持久化时间戳等）。
- **错误**：`404` 表示节点不存在。

### 3. `POST /nodes`
- **用途**：注册或重注册节点。
- **请求体**：
  ```json
  {
    "id": "A1",            // 可选，重注册时携带
    "name": "dev-user-inst-pod-0",
    "type": "agent",
    "version": "1.1.0",
    "meta_data": {
      "hostname": "dev-user-inst-pod-0",
      "ip": "10.0.0.10",
      "env": "dev",
      "user": "testuser",
      "instance": "testinst",
      "cpu_number": 4,
      "memory_in_bytes": 2147483648,
      "gpu_number": 0
    }
  }
  ```
- **成功返回**：
  - 新节点：`201 Created`，返回完整节点对象。
  - 重注册：`200 OK`，返回更新后的节点对象。
- **错误情况**：
  - `404 Not Found`：携带的 ID 在 Master 中不存在。
  - `500 Internal Server Error`：携带的 ID 与已有名称不匹配。
  - `400 Bad Request`：请求体缺字段或类型不正确。

### 4. `PUT /nodes/{id}/status`
- **用途**：Agent 上报状态。Master 记录 `last_report`（服务器时间）与 `agent_last_report`（上报内时间），并更新 `health` 字段。
- **请求体示例**：
  ```json
  {
    "timestamp": "2025-09-24T03:24:59Z",
    "health": {
      "log-fluentbit": {"status": "healthy"},
      "metric-node-exporter": {"status": "healthy"}
    }
  }
  ```
- **响应**：`200 OK`，返回最新节点对象。`404` 表示节点不存在。

### 5. `PUT /nodes/{id}/config`
- **用途**：局部更新节点配置与标签。
- **请求体示例**：
  ```json
  {
    "config": {"log_level": "debug"},
    "label": ["gpu", "exp001"]
  }
  ```
- **说明**：字段可任选其一；未提供的配置保持原值。更新标签会触发 `nodes.json` 重新生成。
- **错误**：`404` 表示节点不存在；`400` 表示请求体不合法。

### 6. `GET /nodes/statistics`
- **用途**：统计节点总数及按状态分布。
- **响应示例**：
  ```json
  {
    "total": 2,
    "status_statistics": [
      {"status": "online", "count": 1},
      {"status": "offline", "count": 1}
    ]
  }
  ```

### 7. 健康探针
- `GET /healthz`：进程存活检查。
- `GET /readyz`：数据库可用性检查（会尝试访问 `DB_PATH`）。


如需验证离线镜像，可使用自动化脚本：
```bash
cd src/master/tests
./scripts/00_e2e_test_offline.sh    # 构建离线镜像并执行完整 E2E
```

## 端到端测试场景

执行 `src/master/tests/scripts/00_e2e_test.sh` 会串联以下用例（脚本 01–10）：

1. **01_up_master**：构建镜像、启动容器、初始化目录与卷。 
2. **02_verify_ready_and_nodes_json**：轮询 `/readyz`，校验初始 `nodes.json` 为 `[]`。
3. **03_register_via_curl**：模拟 agent 注册，保存返回的节点 ID，并确认节点出现在列表接口中。
4. **04_reregister_and_error_cases**：覆盖重注册成功、携带未知 ID 的 `404`、ID/名称不匹配触发 `500` 等场景。
5. **05_status_report_via_curl**：上报健康信息并验证状态自动从 `initialized`→`online`→`offline`→`online` 的转换。
6. **06_config_update_and_nodes_json**：更新配置/标签，检查 `nodes.json` 中的标签同步，并确保离线节点不会出现在文件里。
7. **07_stats_single_node**：等待节点掉线，验证统计接口与 `nodes.json` 为空列表。
8. **08_multi_node_stats**：注册第二节点，使一在线一离线，校验统计聚合和 `nodes.json` 仅包含在线节点。
9. **09_restart_persistence**：重启 master 容器，确认节点数据、统计结果与 `nodes.json` 在持久化目录中保持不变。
10. **10_down**：停止并清理容器、网络与临时目录。

## 相关持久化文件

- SQLite：默认位于 `DB_PATH`，包含 `nodes` 与 `kv` 两张表。
- `nodes.json`：由调度器周期生成，仅保留状态为 `online` 的节点信息。
- 测试用例中的 `tests/private/`、`tests/tmp/` 会随脚本自动清理，避免污染后续运行。

如需在生产环境运行，可将镜像推送到私有仓库，或参考测试 Compose 配置自行部署；只需确保上述环境变量在容器内正确设置即可。
