diff --git a/atsf_agent/agent.json b/atsf_agent/agent.json index 20f72bef..039bc812 100644 --- a/atsf_agent/agent.json +++ b/atsf_agent/agent.json @@ -5,10 +5,10 @@ "node_ip": "10.0.0.8", "agent_version": "0.1.0", "nginx_version": "1.25.5", - "route_config_path": "./etc/nginx/conf.d/atsflare_routes.conf", - "state_path": "./var/lib/atsflare/agent-state.json", - "nginx_binary": "nginx", + "data_dir": "./data", + "nginx_container_name": "atsflare-nginx", + "nginx_docker_image": "nginx:stable-alpine", "heartbeat_interval": 30000000000, "sync_interval": 30000000000, "request_timeout": 10000000000 -} \ No newline at end of file +} diff --git a/atsf_agent/internal/config/config.go b/atsf_agent/internal/config/config.go index 94a7b782..16653b6e 100644 --- a/atsf_agent/internal/config/config.go +++ b/atsf_agent/internal/config/config.go @@ -8,6 +8,11 @@ import ( "time" ) +const ( + defaultDockerRouteConfigRelativePath = "etc/nginx/conf.d/atsflare_routes.conf" + defaultDockerStateRelativePath = "var/lib/atsflare/agent-state.json" +) + type Config struct { ServerURL string `json:"server_url"` AgentToken string `json:"agent_token"` @@ -19,6 +24,7 @@ type Config struct { NginxContainerName string `json:"nginx_container_name"` NginxDockerImage string `json:"nginx_docker_image"` DockerBinary string `json:"docker_binary"` + DataDir string `json:"data_dir"` RouteConfigPath string `json:"route_config_path"` StatePath string `json:"state_path"` HeartbeatInterval time.Duration `json:"heartbeat_interval"` @@ -35,23 +41,18 @@ func Load(path string) (*Config, error) { if err = json.Unmarshal(data, cfg); err != nil { return nil, err } - applyDefaults(cfg) + applyDefaults(cfg, filepath.Dir(path)) if err = validate(cfg); err != nil { return nil, err } return cfg, nil } -func applyDefaults(cfg *Config) { +func applyDefaults(cfg *Config, baseDir string) { + baseDir = filepath.Clean(baseDir) if cfg.AgentVersion == "" { cfg.AgentVersion = "dev" } - if cfg.RouteConfigPath == "" { - cfg.RouteConfigPath = filepath.Clean("./atsflare_routes.conf") - } - if cfg.StatePath == "" { - cfg.StatePath = filepath.Clean("./atsf_agent_state.json") - } if cfg.NginxContainerName == "" { cfg.NginxContainerName = "atsflare-nginx" } @@ -61,6 +62,20 @@ func applyDefaults(cfg *Config) { if cfg.DockerBinary == "" { cfg.DockerBinary = "docker" } + if cfg.DataDir == "" { + cfg.DataDir = filepath.Join(baseDir, "data") + } + if cfg.NginxPath == "" { + cfg.RouteConfigPath = filepath.Join(cfg.DataDir, defaultDockerRouteConfigRelativePath) + cfg.StatePath = filepath.Join(cfg.DataDir, defaultDockerStateRelativePath) + } else { + if cfg.RouteConfigPath == "" { + cfg.RouteConfigPath = filepath.Join(cfg.DataDir, defaultDockerRouteConfigRelativePath) + } + if cfg.StatePath == "" { + cfg.StatePath = filepath.Join(cfg.DataDir, defaultDockerStateRelativePath) + } + } if cfg.HeartbeatInterval <= 0 { cfg.HeartbeatInterval = 30 * time.Second } diff --git a/atsf_agent/internal/config/config_test.go b/atsf_agent/internal/config/config_test.go new file mode 100644 index 00000000..b06d2359 --- /dev/null +++ b/atsf_agent/internal/config/config_test.go @@ -0,0 +1,107 @@ +package config + +import ( + "encoding/json" + "os" + "path/filepath" + "testing" +) + +func TestLoadDockerModeUsesManagedPaths(t *testing.T) { + dir := t.TempDir() + configPath := filepath.Join(dir, "agent.json") + payload := map[string]any{ + "server_url": "http://127.0.0.1:3000", + "agent_token": "token", + "node_name": "edge-01", + "node_ip": "10.0.0.8", + "agent_version": "0.1.0", + } + data, err := json.Marshal(payload) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + if err = os.WriteFile(configPath, data, 0o644); err != nil { + t.Fatalf("failed to write config: %v", err) + } + + cfg, err := Load(configPath) + if err != nil { + t.Fatalf("Load failed: %v", err) + } + + if cfg.DataDir != filepath.Join(dir, "data") { + t.Fatalf("unexpected data dir: %s", cfg.DataDir) + } + if cfg.RouteConfigPath != filepath.Join(dir, "data", defaultDockerRouteConfigRelativePath) { + t.Fatalf("unexpected route config path: %s", cfg.RouteConfigPath) + } + if cfg.StatePath != filepath.Join(dir, "data", defaultDockerStateRelativePath) { + t.Fatalf("unexpected state path: %s", cfg.StatePath) + } +} + +func TestLoadPathModeKeepsExplicitPaths(t *testing.T) { + dir := t.TempDir() + configPath := filepath.Join(dir, "agent.json") + payload := map[string]any{ + "server_url": "http://127.0.0.1:3000", + "agent_token": "token", + "node_name": "edge-01", + "node_ip": "10.0.0.8", + "nginx_path": "/opt/nginx/sbin/nginx", + "route_config_path": "/tmp/routes.conf", + "state_path": "/tmp/agent-state.json", + } + data, err := json.Marshal(payload) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + if err = os.WriteFile(configPath, data, 0o644); err != nil { + t.Fatalf("failed to write config: %v", err) + } + + cfg, err := Load(configPath) + if err != nil { + t.Fatalf("Load failed: %v", err) + } + + if cfg.RouteConfigPath != "/tmp/routes.conf" { + t.Fatalf("unexpected route config path: %s", cfg.RouteConfigPath) + } + if cfg.StatePath != "/tmp/agent-state.json" { + t.Fatalf("unexpected state path: %s", cfg.StatePath) + } +} + +func TestLoadUsesCustomDataDirForGeneratedFiles(t *testing.T) { + dir := t.TempDir() + configPath := filepath.Join(dir, "agent.json") + payload := map[string]any{ + "server_url": "http://127.0.0.1:3000", + "agent_token": "token", + "node_name": "edge-01", + "node_ip": "10.0.0.8", + "data_dir": "/srv/atsflare", + "agent_version": "0.1.0", + } + data, err := json.Marshal(payload) + if err != nil { + t.Fatalf("failed to marshal config: %v", err) + } + if err = os.WriteFile(configPath, data, 0o644); err != nil { + t.Fatalf("failed to write config: %v", err) + } + + cfg, err := Load(configPath) + if err != nil { + t.Fatalf("Load failed: %v", err) + } + + if cfg.RouteConfigPath != "/srv/atsflare/"+defaultDockerRouteConfigRelativePath { + t.Fatalf("unexpected route config path: %s", cfg.RouteConfigPath) + } + if cfg.StatePath != "/srv/atsflare/"+defaultDockerStateRelativePath { + t.Fatalf("unexpected state path: %s", cfg.StatePath) + } +} diff --git a/atsf_agent/var/lib/atsflare/agent-state.json b/atsf_agent/var/lib/atsflare/agent-state.json deleted file mode 100644 index 777190e7..00000000 --- a/atsf_agent/var/lib/atsflare/agent-state.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "node_id": "node-272a017338e3b01c", - "current_version": "", - "current_checksum": "", - "last_error": "nginx -t failed: exec: \"nginx\": executable file not found in $PATH: " -} \ No newline at end of file diff --git a/docs/deployment.md b/docs/deployment.md index 99a16ab8..6bed1cff 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -70,11 +70,9 @@ go run . "node_ip": "10.0.0.8", "agent_version": "0.1.0", "nginx_version": "1.25.5", - "nginx_path": "/opt/nginx/sbin/nginx", + "data_dir": "./data", "nginx_container_name": "atsflare-nginx", "nginx_docker_image": "nginx:stable-alpine", - "route_config_path": "/etc/nginx/conf.d/atsflare_routes.conf", - "state_path": "/var/lib/atsflare/agent-state.json", "heartbeat_interval": 30000000000, "sync_interval": 30000000000, "request_timeout": 10000000000 @@ -85,10 +83,13 @@ go run . - 时间字段单位是纳秒,因为当前实现直接使用 Go 的 `time.Duration` JSON 反序列化 - `agent_token` 必须与 Server 侧 `AGENT_TOKEN` 完全一致 -- `route_config_path` 只应指向 ATSFlare 独立管理的路由文件 -- `nginx_path` 用于显式指定独立 Nginx 可执行文件路径 -- 如果未指定 `nginx_path`,Agent 会尝试通过 Docker 启动独立 Nginx 容器 +- 生成资源默认统一落在 `./data` +- 如果未指定 `nginx_path`,Agent 会自动使用以下固定路径: + - `./data/etc/nginx/conf.d/atsflare_routes.conf` + - `./data/var/lib/atsflare/agent-state.json` +- 如果希望修改保存位置,可通过 `data_dir` 统一覆盖生成资源目录 - Docker 模式默认使用 `nginx_container_name` 和 `nginx_docker_image` +- `nginx_path` 仅在独立 Nginx 路径模式下使用;此时如有需要,仍可单独覆盖 `route_config_path` 和 `state_path` ### 3.2 启动 Agent diff --git a/docs/design.md b/docs/design.md index cd814db2..31fe13de 100644 --- a/docs/design.md +++ b/docs/design.md @@ -66,6 +66,7 @@ Agent 使用 Go 单体程序: * 显式配置 `nginx_path` 时,直接调用该路径下的 Nginx * 未配置 `nginx_path` 时,默认通过 Docker 运行独立 Nginx 容器 * 管理本机 Nginx 路由配置文件和 reload +* Agent 生成资源默认统一落在 `./data`,也允许通过单个基路径配置覆盖 ### Nginx 管理边界 diff --git a/docs/development-guidelines.md b/docs/development-guidelines.md index dd0acb99..c81e2181 100644 --- a/docs/development-guidelines.md +++ b/docs/development-guidelines.md @@ -50,6 +50,7 @@ Agent 放在 `atsf_agent`,使用 Go 单体程序开发。 * 优先调用独立 Nginx,不依赖系统全局 Nginx * 支持通过 `nginx_path` 显式指定独立 Nginx 可执行文件 * 未指定 `nginx_path` 时,默认通过 Docker 启动独立 Nginx 容器 +* Agent 生成资源默认统一放在 `./data`,可通过 `data_dir` 统一覆盖 * 负责本机 Nginx 路由配置写入、校验、reload、状态上报 ### 2.3 Nginx 配置边界