From 29a0c64ba3f04b5d0647f7aff3dfdfb028f7cf1a Mon Sep 17 00:00:00 2001 From: ryan Date: Thu, 12 Mar 2026 09:48:38 +0800 Subject: [PATCH] Refactor Nginx references to OpenResty throughout the codebase - Updated all instances of "nginx" to "openresty" in log messages, error messages, and comments. - Changed paths and Docker image names to reflect OpenResty usage. - Modified test cases to align with OpenResty commands and configurations. - Adjusted documentation to replace Nginx mentions with OpenResty, including setup instructions and configuration details. - Ensured that version detection and runtime commands are consistent with OpenResty. --- README.en.md | 2 +- atsf_agent/agent.json | 4 +- atsf_agent/cmd/agent/main.go | 18 ++-- atsf_agent/internal/agent/runner_test.go | 16 ++-- atsf_agent/internal/config/config.go | 71 +++++++++++---- atsf_agent/internal/config/config_test.go | 21 +++-- atsf_agent/internal/nginx/manager.go | 103 ++++++++++++---------- atsf_agent/internal/nginx/manager_test.go | 47 +++++----- atsf_agent/internal/sync/service.go | 12 +-- atsf_server/common/init.go | 2 +- atsf_server/router/api_phase2_test.go | 8 +- docs/app-config.md | 43 ++++----- docs/deployment.md | 54 ++++++------ docs/design.md | 12 +-- docs/development-guidelines.md | 16 ++-- 15 files changed, 247 insertions(+), 182 deletions(-) diff --git a/README.en.md b/README.en.md index be658d7b..ccffd2ef 100644 --- a/README.en.md +++ b/README.en.md @@ -28,7 +28,7 @@ _✨ control plane for reverse proxy management ✨_ ## Repository layout - `atsf_server`: Gin + GORM + SQLite control plane, including admin API, Agent API and Web UI -- `atsf_agent`: single-binary Go Agent for registration, heartbeat, config sync and Nginx reload +- `atsf_agent`: single-binary Go Agent for registration, heartbeat, config sync and OpenResty reload - `docs`: design, development guidelines, implementation plan and deployment docs ## Quick start diff --git a/atsf_agent/agent.json b/atsf_agent/agent.json index cdbb0349..0c41f8f6 100644 --- a/atsf_agent/agent.json +++ b/atsf_agent/agent.json @@ -2,8 +2,8 @@ "server_url": "http://127.0.0.1:3000", "agent_token": "123", "data_dir": "./data", - "nginx_container_name": "atsflare-nginx", - "nginx_docker_image": "nginx:stable-alpine", + "openresty_container_name": "atsflare-openresty", + "openresty_docker_image": "openresty/openresty:alpine", "heartbeat_interval": 30000, "sync_interval": 30000, "request_timeout": 10000 diff --git a/atsf_agent/cmd/agent/main.go b/atsf_agent/cmd/agent/main.go index a3627119..d049530b 100644 --- a/atsf_agent/cmd/agent/main.go +++ b/atsf_agent/cmd/agent/main.go @@ -28,13 +28,13 @@ func main() { cfg.NginxVersion = nginx.DetectVersion( context.Background(), nginx.ExecutorOptions{ - NginxPath: cfg.NginxPath, + NginxPath: cfg.OpenrestyPath, DockerBinary: cfg.DockerBinary, - ContainerName: cfg.NginxContainerName, - Image: cfg.NginxDockerImage, + ContainerName: cfg.OpenrestyContainerName, + Image: cfg.OpenrestyDockerImage, RouteConfigPath: cfg.RouteConfigPath, CertDir: cfg.CertDir, - NginxCertDir: cfg.NginxCertDir, + NginxCertDir: cfg.OpenrestyCertDir, }, ) log.Printf("agent config loaded: server=%s node=%s ip=%s heartbeat_interval=%s sync_interval=%s route_config=%s cert_dir=%s", cfg.ServerURL, cfg.NodeName, cfg.NodeIP, cfg.HeartbeatInterval, cfg.SyncInterval, cfg.RouteConfigPath, cfg.CertDir) @@ -48,15 +48,15 @@ func main() { SyncService: syncservice.New(client, &nginx.Manager{ RouteConfigPath: cfg.RouteConfigPath, CertDir: cfg.CertDir, - NginxCertDir: cfg.NginxCertDir, + NginxCertDir: cfg.OpenrestyCertDir, Executor: nginx.NewExecutor(nginx.ExecutorOptions{ - NginxPath: cfg.NginxPath, + NginxPath: cfg.OpenrestyPath, DockerBinary: cfg.DockerBinary, - ContainerName: cfg.NginxContainerName, - Image: cfg.NginxDockerImage, + ContainerName: cfg.OpenrestyContainerName, + Image: cfg.OpenrestyDockerImage, RouteConfigPath: cfg.RouteConfigPath, CertDir: cfg.CertDir, - NginxCertDir: cfg.NginxCertDir, + NginxCertDir: cfg.OpenrestyCertDir, }), }, stateStore), Updater: updater.New(), diff --git a/atsf_agent/internal/agent/runner_test.go b/atsf_agent/internal/agent/runner_test.go index 07fc94b6..9adee1cc 100644 --- a/atsf_agent/internal/agent/runner_test.go +++ b/atsf_agent/internal/agent/runner_test.go @@ -95,7 +95,7 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) { }, } syncService := &fakeSyncService{ - startupErr: errors.New("当前没有激活版本,保持当前 Nginx 配置"), + startupErr: errors.New("当前没有激活版本,保持当前 OpenResty 配置"), } runner := &Runner{ Config: &config.Config{ @@ -103,7 +103,7 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) { NodeName: "edge-01", NodeIP: "10.0.0.8", AgentVersion: config.AgentVersion, - NginxVersion: "1.25.5", + NginxVersion: "1.27.1.2", HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond), SyncInterval: config.MillisecondDuration(20 * time.Millisecond), }, @@ -126,7 +126,7 @@ func TestRunnerKeepsHeartbeatWhenStartupSyncFails(t *testing.T) { if loadErr != nil { t.Fatalf("failed to load state: %v", loadErr) } - if snapshot.LastError != "当前没有激活版本,保持当前 Nginx 配置" { + if snapshot.LastError != "当前没有激活版本,保持当前 OpenResty 配置" { t.Fatalf("expected startup sync error to be recorded, got %q", snapshot.LastError) } } @@ -141,7 +141,7 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) { heartbeatErrs: []error{errors.New("heartbeat timeout")}, } syncService := &fakeSyncService{ - syncOnceErr: errors.New("nginx reload failed"), + syncOnceErr: errors.New("openresty reload failed"), onSyncOnceCall: func(callCount int) { if callCount >= 1 { cancel() @@ -154,7 +154,7 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) { NodeName: "edge-01", NodeIP: "10.0.0.8", AgentVersion: config.AgentVersion, - NginxVersion: "1.25.5", + NginxVersion: "1.27.1.2", HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond), SyncInterval: config.MillisecondDuration(10 * time.Millisecond), }, @@ -177,7 +177,7 @@ func TestRunnerDoesNotExitOnHeartbeatOrSyncError(t *testing.T) { if loadErr != nil { t.Fatalf("failed to load state: %v", loadErr) } - if snapshot.LastError != "nginx reload failed" { + if snapshot.LastError != "openresty reload failed" { t.Fatalf("expected sync error to be recorded, got %q", snapshot.LastError) } } @@ -215,7 +215,7 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) { NodeName: cfg.NodeName, NodeIP: cfg.NodeIP, AgentVersion: config.AgentVersion, - NginxVersion: "1.25.5", + NginxVersion: "1.27.1.2", HeartbeatInterval: config.MillisecondDuration(10 * time.Millisecond), SyncInterval: config.MillisecondDuration(20 * time.Millisecond), }, @@ -225,7 +225,7 @@ func TestRunnerDiscoveryRegisterUpdatesTokenAndNodeID(t *testing.T) { } runner.Config = cfg runner.Config.AgentVersion = config.AgentVersion - runner.Config.NginxVersion = "1.25.5" + runner.Config.NginxVersion = "1.27.1.2" runner.Config.HeartbeatInterval = config.MillisecondDuration(10 * time.Millisecond) runner.Config.SyncInterval = config.MillisecondDuration(20 * time.Millisecond) diff --git a/atsf_agent/internal/config/config.go b/atsf_agent/internal/config/config.go index 509fb954..84c46796 100644 --- a/atsf_agent/internal/config/config.go +++ b/atsf_agent/internal/config/config.go @@ -15,7 +15,7 @@ const ( defaultDockerRouteConfigRelativePath = "etc/nginx/conf.d/atsflare_routes.conf" defaultCertDirRelativePath = "etc/nginx/certs" defaultDockerStateRelativePath = "var/lib/atsflare/agent-state.json" - defaultDockerNginxCertDir = "/etc/nginx/atsflare-certs" + defaultDockerOpenRestyCertDir = "/etc/nginx/atsflare-certs" ) type Config struct { @@ -26,14 +26,14 @@ type Config struct { NodeIP string `json:"node_ip"` AgentVersion string `json:"-"` NginxVersion string `json:"-"` - NginxPath string `json:"nginx_path"` - NginxContainerName string `json:"nginx_container_name"` - NginxDockerImage string `json:"nginx_docker_image"` + OpenrestyPath string `json:"openresty_path"` + OpenrestyContainerName string `json:"openresty_container_name"` + OpenrestyDockerImage string `json:"openresty_docker_image"` DockerBinary string `json:"docker_binary"` DataDir string `json:"data_dir"` RouteConfigPath string `json:"route_config_path"` CertDir string `json:"cert_dir"` - NginxCertDir string `json:"nginx_cert_dir"` + OpenrestyCertDir string `json:"openresty_cert_dir"` StatePath string `json:"state_path"` HeartbeatInterval MillisecondDuration `json:"heartbeat_interval"` SyncInterval MillisecondDuration `json:"sync_interval"` @@ -41,15 +41,54 @@ type Config struct { configPath string } +type configFile struct { + ServerURL string `json:"server_url"` + AgentToken string `json:"agent_token"` + DiscoveryToken string `json:"discovery_token"` + NodeName string `json:"node_name"` + NodeIP string `json:"node_ip"` + OpenrestyPath string `json:"openresty_path"` + OpenrestyContainerName string `json:"openresty_container_name"` + OpenrestyDockerImage string `json:"openresty_docker_image"` + DockerBinary string `json:"docker_binary"` + DataDir string `json:"data_dir"` + RouteConfigPath string `json:"route_config_path"` + CertDir string `json:"cert_dir"` + OpenrestyCertDir string `json:"openresty_cert_dir"` + StatePath string `json:"state_path"` + HeartbeatInterval MillisecondDuration `json:"heartbeat_interval"` + SyncInterval MillisecondDuration `json:"sync_interval"` + RequestTimeout MillisecondDuration `json:"request_timeout"` +} + func Load(path string) (*Config, error) { data, err := os.ReadFile(path) if err != nil { return nil, err } - cfg := &Config{} - if err = json.Unmarshal(data, cfg); err != nil { + file := &configFile{} + if err = json.Unmarshal(data, file); err != nil { return nil, err } + cfg := &Config{ + ServerURL: file.ServerURL, + AgentToken: file.AgentToken, + DiscoveryToken: file.DiscoveryToken, + NodeName: file.NodeName, + NodeIP: file.NodeIP, + OpenrestyPath: file.OpenrestyPath, + OpenrestyContainerName: file.OpenrestyContainerName, + OpenrestyDockerImage: file.OpenrestyDockerImage, + DockerBinary: file.DockerBinary, + DataDir: file.DataDir, + RouteConfigPath: file.RouteConfigPath, + CertDir: file.CertDir, + OpenrestyCertDir: file.OpenrestyCertDir, + StatePath: file.StatePath, + HeartbeatInterval: file.HeartbeatInterval, + SyncInterval: file.SyncInterval, + RequestTimeout: file.RequestTimeout, + } cfg.configPath = path applyDefaults(cfg, filepath.Dir(path)) if err = validate(cfg); err != nil { @@ -61,11 +100,11 @@ func Load(path string) (*Config, error) { func applyDefaults(cfg *Config, baseDir string) { baseDir = filepath.Clean(baseDir) cfg.AgentVersion = AgentVersion - if cfg.NginxContainerName == "" { - cfg.NginxContainerName = "atsflare-nginx" + if cfg.OpenrestyContainerName == "" { + cfg.OpenrestyContainerName = "atsflare-openresty" } - if cfg.NginxDockerImage == "" { - cfg.NginxDockerImage = "nginx:stable-alpine" + if cfg.OpenrestyDockerImage == "" { + cfg.OpenrestyDockerImage = "openresty/openresty:alpine" } if cfg.DockerBinary == "" { cfg.DockerBinary = "docker" @@ -79,7 +118,7 @@ func applyDefaults(cfg *Config, baseDir string) { if cfg.NodeIP == "" { cfg.NodeIP = detectNodeIP() } - if cfg.NginxPath == "" { + if cfg.OpenrestyPath == "" { cfg.RouteConfigPath = joinManagedPath(cfg.DataDir, defaultDockerRouteConfigRelativePath) cfg.StatePath = joinManagedPath(cfg.DataDir, defaultDockerStateRelativePath) } else { @@ -93,11 +132,11 @@ func applyDefaults(cfg *Config, baseDir string) { if cfg.CertDir == "" { cfg.CertDir = joinManagedPath(cfg.DataDir, defaultCertDirRelativePath) } - if cfg.NginxCertDir == "" { - if cfg.NginxPath != "" { - cfg.NginxCertDir = cfg.CertDir + if cfg.OpenrestyCertDir == "" { + if cfg.OpenrestyPath != "" { + cfg.OpenrestyCertDir = cfg.CertDir } else { - cfg.NginxCertDir = defaultDockerNginxCertDir + cfg.OpenrestyCertDir = defaultDockerOpenRestyCertDir } } if cfg.HeartbeatInterval <= 0 { diff --git a/atsf_agent/internal/config/config_test.go b/atsf_agent/internal/config/config_test.go index 7bbc5304..24f3017a 100644 --- a/atsf_agent/internal/config/config_test.go +++ b/atsf_agent/internal/config/config_test.go @@ -39,8 +39,14 @@ func TestLoadDockerModeUsesManagedPaths(t *testing.T) { if cfg.CertDir != filepath.Join(dir, "data", defaultCertDirRelativePath) { t.Fatalf("unexpected cert dir: %s", cfg.CertDir) } - if cfg.NginxCertDir != defaultDockerNginxCertDir { - t.Fatalf("unexpected nginx cert dir: %s", cfg.NginxCertDir) + if cfg.OpenrestyContainerName != "atsflare-openresty" { + t.Fatalf("unexpected openresty container name: %s", cfg.OpenrestyContainerName) + } + if cfg.OpenrestyDockerImage != "openresty/openresty:alpine" { + t.Fatalf("unexpected openresty image: %s", cfg.OpenrestyDockerImage) + } + if cfg.OpenrestyCertDir != defaultDockerOpenRestyCertDir { + t.Fatalf("unexpected openresty cert dir: %s", cfg.OpenrestyCertDir) } if cfg.StatePath != filepath.Join(dir, "data", defaultDockerStateRelativePath) { t.Fatalf("unexpected state path: %s", cfg.StatePath) @@ -55,7 +61,7 @@ func TestLoadPathModeKeepsExplicitPaths(t *testing.T) { "agent_token": "token", "node_name": "edge-01", "node_ip": "10.0.0.8", - "nginx_path": "/opt/nginx/sbin/nginx", + "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", "route_config_path": "/tmp/routes.conf", "state_path": "/tmp/agent-state.json", } @@ -78,8 +84,8 @@ func TestLoadPathModeKeepsExplicitPaths(t *testing.T) { if cfg.StatePath != "/tmp/agent-state.json" { t.Fatalf("unexpected state path: %s", cfg.StatePath) } - if cfg.NginxCertDir != cfg.CertDir { - t.Fatalf("expected path mode nginx cert dir to equal cert dir, got %s / %s", cfg.NginxCertDir, cfg.CertDir) + if cfg.OpenrestyCertDir != cfg.CertDir { + t.Fatalf("expected path mode openresty cert dir to equal cert dir, got %s / %s", cfg.OpenrestyCertDir, cfg.CertDir) } } @@ -164,7 +170,7 @@ func TestSavePersistsMillisecondsAndOmitsRuntimeVersions(t *testing.T) { if err != nil { t.Fatalf("Load failed: %v", err) } - cfg.NginxVersion = "1.25.5" + cfg.NginxVersion = "1.27.1.2" cfg.HeartbeatInterval = MillisecondDuration(5 * time.Second) cfg.SyncInterval = MillisecondDuration(6 * time.Second) cfg.RequestTimeout = MillisecondDuration(7 * time.Second) @@ -196,6 +202,9 @@ func TestSavePersistsMillisecondsAndOmitsRuntimeVersions(t *testing.T) { if decoded["request_timeout"] != float64(7000) { t.Fatalf("unexpected request timeout: %#v", decoded["request_timeout"]) } + if _, ok := decoded["nginx_path"]; ok { + t.Fatal("legacy nginx_path should not be persisted") + } } func TestInitialAuthToken(t *testing.T) { diff --git a/atsf_agent/internal/nginx/manager.go b/atsf_agent/internal/nginx/manager.go index c5fabc54..46dd2faa 100644 --- a/atsf_agent/internal/nginx/manager.go +++ b/atsf_agent/internal/nginx/manager.go @@ -19,6 +19,8 @@ import ( const CertDirPlaceholder = "__ATSF_CERT_DIR__" +const dockerRuntimeCommand = "openresty" + type Executor interface { Test(ctx context.Context) error Reload(ctx context.Context) error @@ -43,22 +45,22 @@ type PathExecutor struct { } func (e *PathExecutor) Test(ctx context.Context) error { - log.Printf("running nginx test with binary: %s", e.Path) + log.Printf("running openresty test with binary: %s", e.Path) output, err := e.Runner.Run(ctx, e.Path, "-t") if err != nil { - return fmt.Errorf("nginx -t failed: %w: %s", err, string(output)) + return fmt.Errorf("openresty -t failed: %w: %s", err, string(output)) } - log.Printf("nginx test succeeded with binary: %s", e.Path) + log.Printf("openresty test succeeded with binary: %s", e.Path) return nil } func (e *PathExecutor) Reload(ctx context.Context) error { - log.Printf("running nginx reload with binary: %s", e.Path) + log.Printf("running openresty reload with binary: %s", e.Path) output, err := e.Runner.Run(ctx, e.Path, "-s", "reload") if err != nil { - return fmt.Errorf("nginx reload failed: %w: %s", err, string(output)) + return fmt.Errorf("openresty reload failed: %w: %s", err, string(output)) } - log.Printf("nginx reload succeeded with binary: %s", e.Path) + log.Printf("openresty reload succeeded with binary: %s", e.Path) return nil } @@ -77,24 +79,12 @@ type DockerExecutor struct { } func (e *DockerExecutor) Test(ctx context.Context) error { - log.Printf("running docker nginx test: container=%s image=%s", e.ContainerName, e.Image) - output, err := e.Runner.Run( - ctx, - e.DockerBinary, - "run", - "--rm", - "-v", - fmt.Sprintf("%s:/etc/nginx/conf.d", e.RouteConfigDir), - "-v", - fmt.Sprintf("%s:%s", e.CertDir, e.NginxCertDir), - e.Image, - "nginx", - "-t", - ) + log.Printf("running docker openresty test: container=%s image=%s", e.ContainerName, e.Image) + output, err := e.runEphemeralRuntimeCommand(ctx, "-t") if err != nil { - return fmt.Errorf("docker nginx -t failed: %w: %s", err, string(output)) + return fmt.Errorf("docker %s -t failed: %w: %s", dockerRuntimeCommand, err, string(output)) } - log.Printf("docker nginx test succeeded: container=%s", e.ContainerName) + log.Printf("docker openresty test succeeded: container=%s runtime=%s", e.ContainerName, dockerRuntimeCommand) return nil } @@ -103,7 +93,7 @@ func (e *DockerExecutor) Reload(ctx context.Context) error { } func (e *DockerExecutor) EnsureRuntime(ctx context.Context, recreate bool) error { - log.Printf("ensuring docker nginx runtime: container=%s recreate=%t", e.ContainerName, recreate) + log.Printf("ensuring docker openresty runtime: container=%s recreate=%t", e.ContainerName, recreate) output, err := e.Runner.Run(ctx, e.DockerBinary, "inspect", "-f", "{{.State.Running}}", e.ContainerName) if err == nil { if recreate { @@ -113,7 +103,7 @@ func (e *DockerExecutor) EnsureRuntime(ctx context.Context, recreate bool) error return e.runContainer(ctx) } if strings.TrimSpace(string(output)) == "true" { - log.Printf("docker nginx runtime already healthy: container=%s", e.ContainerName) + log.Printf("docker openresty runtime already healthy: container=%s", e.ContainerName) return nil } if err := e.removeContainer(ctx); err != nil { @@ -125,21 +115,21 @@ func (e *DockerExecutor) EnsureRuntime(ctx context.Context, recreate bool) error } func (e *DockerExecutor) removeContainer(ctx context.Context) error { - log.Printf("removing docker nginx container: container=%s", e.ContainerName) + log.Printf("removing docker openresty container: container=%s", e.ContainerName) output, err := e.Runner.Run(ctx, e.DockerBinary, "rm", "-f", e.ContainerName) if err != nil { text := string(output) if strings.Contains(text, "No such container") { return nil } - return fmt.Errorf("docker rm nginx failed: %w: %s", err, text) + return fmt.Errorf("docker rm openresty failed: %w: %s", err, text) } - log.Printf("docker nginx container removed: container=%s", e.ContainerName) + log.Printf("docker openresty container removed: container=%s", e.ContainerName) return nil } func (e *DockerExecutor) runContainer(ctx context.Context) error { - log.Printf("starting docker nginx container: container=%s image=%s", e.ContainerName, e.Image) + log.Printf("starting docker openresty container: container=%s image=%s", e.ContainerName, e.Image) runArgs := []string{ "run", "-d", "--name", e.ContainerName, @@ -151,9 +141,9 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error { } runOutput, runErr := e.Runner.Run(ctx, e.DockerBinary, runArgs...) if runErr != nil { - return fmt.Errorf("docker run nginx failed: %w: %s", runErr, string(runOutput)) + return fmt.Errorf("docker run openresty failed: %w: %s", runErr, string(runOutput)) } - log.Printf("docker nginx container started: container=%s", e.ContainerName) + log.Printf("docker openresty container started: container=%s", e.ContainerName) return nil } @@ -165,7 +155,7 @@ type Manager struct { } func (m *Manager) Apply(ctx context.Context, content string, supportFiles []protocol.SupportFile) error { - log.Printf("nginx apply started: route_config=%s support_files=%d", m.RouteConfigPath, len(supportFiles)) + log.Printf("openresty apply started: route_config=%s support_files=%d", m.RouteConfigPath, len(supportFiles)) backup, err := m.backup() if err != nil { return err @@ -177,21 +167,21 @@ func (m *Manager) Apply(ctx context.Context, content string, supportFiles []prot } renderedContent := m.renderConfig(content) if err = os.WriteFile(m.RouteConfigPath, []byte(renderedContent), 0o644); err != nil { - log.Printf("writing nginx route config failed, restoring backup: error=%v", err) + log.Printf("writing openresty route config failed, restoring backup: error=%v", err) _ = m.restore(backup) return err } if err = m.Executor.Test(ctx); err != nil { - log.Printf("nginx test failed after config write, restoring backup: error=%v", err) + log.Printf("openresty test failed after config write, restoring backup: error=%v", err) _ = m.restore(backup) return err } if err = m.Executor.Reload(ctx); err != nil { - log.Printf("nginx reload failed after config write, restoring backup: error=%v", err) + log.Printf("openresty reload failed after config write, restoring backup: error=%v", err) _ = m.restore(backup) return err } - log.Printf("nginx apply completed successfully: route_config=%s", m.RouteConfigPath) + log.Printf("openresty apply completed successfully: route_config=%s", m.RouteConfigPath) return nil } @@ -199,7 +189,7 @@ func (m *Manager) EnsureRuntime(ctx context.Context, recreate bool) error { if m.Executor == nil { return errors.New("executor 未配置") } - log.Printf("nginx ensure runtime requested: recreate=%t", recreate) + log.Printf("openresty ensure runtime requested: recreate=%t", recreate) return m.Executor.EnsureRuntime(ctx, recreate) } @@ -223,7 +213,7 @@ func (m *Manager) CurrentChecksum() (string, error) { return "", err } result := bundleChecksum(normalized, files) - log.Printf("nginx current checksum calculated: route_config=%s checksum=%s support_files=%d", m.RouteConfigPath, result, len(files)) + log.Printf("openresty current checksum calculated: route_config=%s checksum=%s support_files=%d", m.RouteConfigPath, result, len(files)) return result, nil } @@ -267,10 +257,10 @@ func NewExecutor(options ExecutorOptions) Executor { func DetectVersion(ctx context.Context, options ExecutorOptions) string { version, err := detectVersion(ctx, options, &OSCommandRunner{}) if err != nil { - log.Printf("detect nginx version failed: %v", err) + log.Printf("detect openresty version failed: %v", err) return "" } - log.Printf("detected nginx version: %s", version) + log.Printf("detected openresty version: %s", version) return version } @@ -281,21 +271,21 @@ func detectVersion(ctx context.Context, options ExecutorOptions, runner CommandR if options.NginxPath != "" { output, err := runner.Run(ctx, options.NginxPath, "-v") if err != nil { - return "", fmt.Errorf("run nginx -v failed: %w: %s", err, string(output)) + return "", fmt.Errorf("run runtime -v failed: %w: %s", err, string(output)) } version := parseNginxVersion(string(output)) if version == "" { - return "", errors.New("cannot parse nginx version from binary output") + return "", errors.New("cannot parse runtime version from binary output") } return version, nil } - output, err := runner.Run(ctx, options.DockerBinary, "run", "--rm", options.Image, "nginx", "-v") + output, err := runDockerVersionProbe(ctx, runner, options.DockerBinary, options.Image) if err != nil { - return "", fmt.Errorf("run docker nginx -v failed: %w: %s", err, string(output)) + return "", fmt.Errorf("run docker %s -v failed: %w: %s", dockerRuntimeCommand, err, string(output)) } version := parseNginxVersion(string(output)) if version == "" { - return "", errors.New("cannot parse nginx version from docker output") + return "", errors.New("cannot parse runtime version from docker output") } return version, nil } @@ -308,7 +298,30 @@ func parseNginxVersion(output string) string { return matches[1] } -var nginxVersionPattern = regexp.MustCompile(`(?im)nginx version:\s*nginx/([^\s]+)`) +var nginxVersionPattern = regexp.MustCompile(`(?im)(?:nginx|openresty) version:\s*(?:nginx|openresty)/([^\s]+)`) + +func (e *DockerExecutor) runEphemeralRuntimeCommand(ctx context.Context, args ...string) ([]byte, error) { + return e.runEphemeralRuntimeCommandWithBinary(ctx, dockerRuntimeCommand, args...) +} + +func (e *DockerExecutor) runEphemeralRuntimeCommandWithBinary(ctx context.Context, runtimeBinary string, args ...string) ([]byte, error) { + runtimeArgs := []string{ + "run", + "--rm", + "-v", + fmt.Sprintf("%s:/etc/nginx/conf.d", e.RouteConfigDir), + "-v", + fmt.Sprintf("%s:%s", e.CertDir, e.NginxCertDir), + e.Image, + runtimeBinary, + } + runtimeArgs = append(runtimeArgs, args...) + return e.Runner.Run(ctx, e.DockerBinary, runtimeArgs...) +} + +func runDockerVersionProbe(ctx context.Context, runner CommandRunner, dockerBinary string, image string) ([]byte, error) { + return runner.Run(ctx, dockerBinary, "run", "--rm", image, dockerRuntimeCommand, "-v") +} type backupState struct { RouteExisted bool diff --git a/atsf_agent/internal/nginx/manager_test.go b/atsf_agent/internal/nginx/manager_test.go index ee780b8c..1cbb1c56 100644 --- a/atsf_agent/internal/nginx/manager_test.go +++ b/atsf_agent/internal/nginx/manager_test.go @@ -50,7 +50,7 @@ func (e *fakeExecutor) EnsureRuntime(ctx context.Context, recreate bool) error { func TestPathExecutorCommands(t *testing.T) { runner := &fakeRunner{} executor := &PathExecutor{ - Path: "/opt/nginx/sbin/nginx", + Path: "/usr/local/openresty/nginx/sbin/openresty", Runner: runner, } @@ -62,8 +62,8 @@ func TestPathExecutorCommands(t *testing.T) { } expected := []runCall{ - {name: "/opt/nginx/sbin/nginx", args: []string{"-t"}}, - {name: "/opt/nginx/sbin/nginx", args: []string{"-s", "reload"}}, + {name: "/usr/local/openresty/nginx/sbin/openresty", args: []string{"-t"}}, + {name: "/usr/local/openresty/nginx/sbin/openresty", args: []string{"-s", "reload"}}, } if !reflect.DeepEqual(runner.calls, expected) { t.Fatalf("unexpected calls: %#v", runner.calls) @@ -72,7 +72,7 @@ func TestPathExecutorCommands(t *testing.T) { func TestPathExecutorEnsureRuntimeNoop(t *testing.T) { executor := &PathExecutor{ - Path: "/opt/nginx/sbin/nginx", + Path: "/usr/local/openresty/nginx/sbin/openresty", Runner: &fakeRunner{}, } if err := executor.EnsureRuntime(context.Background(), true); err != nil { @@ -91,8 +91,8 @@ func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) { } executor := &DockerExecutor{ DockerBinary: "docker", - ContainerName: "atsflare-nginx", - Image: "nginx:stable-alpine", + ContainerName: "atsflare-openresty", + Image: "openresty/openresty:alpine", RouteConfigDir: filepath.Clean("/tmp/routes"), CertDir: filepath.Clean("/tmp/certs"), NginxCertDir: "/etc/nginx/atsflare-certs", @@ -109,6 +109,9 @@ func TestDockerExecutorStartsContainerWhenMissing(t *testing.T) { if runner.calls[0].args[0] != "run" || runner.calls[0].args[1] != "--rm" { t.Fatalf("expected docker run --rm for test, got %#v", runner.calls[0]) } + if runner.calls[0].args[len(runner.calls[0].args)-2] != "openresty" { + t.Fatalf("expected docker test command to invoke openresty, got %#v", runner.calls[0]) + } } func TestDockerExecutorStartsStoppedContainer(t *testing.T) { @@ -122,8 +125,8 @@ func TestDockerExecutorStartsStoppedContainer(t *testing.T) { } executor := &DockerExecutor{ DockerBinary: "docker", - ContainerName: "atsflare-nginx", - Image: "nginx:stable-alpine", + ContainerName: "atsflare-openresty", + Image: "openresty/openresty:alpine", RouteConfigDir: filepath.Clean("/tmp/routes"), CertDir: filepath.Clean("/tmp/certs"), NginxCertDir: "/etc/nginx/atsflare-certs", @@ -159,8 +162,8 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) { } executor := &DockerExecutor{ DockerBinary: "docker", - ContainerName: "atsflare-nginx", - Image: "nginx:stable-alpine", + ContainerName: "atsflare-openresty", + Image: "openresty/openresty:alpine", RouteConfigDir: filepath.Clean("/tmp/routes"), CertDir: filepath.Clean("/tmp/certs"), NginxCertDir: "/etc/nginx/atsflare-certs", @@ -184,8 +187,8 @@ func TestDockerExecutorRecreatesContainerOnStartup(t *testing.T) { func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) { executor := NewExecutor(ExecutorOptions{ DockerBinary: "docker", - ContainerName: "atsflare-nginx", - Image: "nginx:stable-alpine", + ContainerName: "atsflare-openresty", + Image: "openresty/openresty:alpine", RouteConfigPath: "./data/etc/nginx/conf.d/atsflare_routes.conf", CertDir: "./data/etc/nginx/certs", NginxCertDir: "/etc/nginx/atsflare-certs", @@ -205,16 +208,16 @@ func TestNewExecutorUsesAbsoluteDockerMountPath(t *testing.T) { func TestDetectVersionFromBinary(t *testing.T) { version, err := detectVersion(context.Background(), ExecutorOptions{ - NginxPath: "/opt/nginx/sbin/nginx", + NginxPath: "/usr/local/openresty/nginx/sbin/openresty", }, &fakeRunner{ runFn: func(name string, args ...string) ([]byte, error) { - return []byte("nginx version: nginx/1.25.5\n"), nil + return []byte("nginx version: openresty/1.27.1.2\n"), nil }, }) if err != nil { t.Fatalf("detectVersion failed: %v", err) } - if version != "1.25.5" { + if version != "1.27.1.2" { t.Fatalf("unexpected version: %s", version) } } @@ -222,23 +225,23 @@ func TestDetectVersionFromBinary(t *testing.T) { func TestDetectVersionFromDockerImage(t *testing.T) { runner := &fakeRunner{ runFn: func(name string, args ...string) ([]byte, error) { - return []byte("nginx version: nginx/1.27.4\n"), nil + return []byte("nginx version: openresty/1.27.1.2\n"), nil }, } version, err := detectVersion(context.Background(), ExecutorOptions{ DockerBinary: "docker", - Image: "nginx:stable-alpine", + Image: "openresty/openresty:alpine", }, runner) if err != nil { t.Fatalf("detectVersion failed: %v", err) } - if version != "1.27.4" { + if version != "1.27.1.2" { t.Fatalf("unexpected version: %s", version) } if len(runner.calls) != 1 { t.Fatalf("expected one command call, got %d", len(runner.calls)) } - expectedArgs := []string{"run", "--rm", "nginx:stable-alpine", "nginx", "-v"} + expectedArgs := []string{"run", "--rm", "openresty/openresty:alpine", "openresty", "-v"} if !reflect.DeepEqual(runner.calls[0].args, expectedArgs) { t.Fatalf("unexpected docker args: %#v", runner.calls[0].args) } @@ -247,11 +250,11 @@ func TestDetectVersionFromDockerImage(t *testing.T) { func TestParseNginxVersionIgnoresDockerEntrypointPaths(t *testing.T) { output := strings.Join([]string{ "/docker-entrypoint.sh: /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh: info: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)", - "nginx version: nginx/1.27.4", + "nginx version: openresty/1.27.1.2", }, "\n") version := parseNginxVersion(output) - if version != "1.27.4" { + if version != "1.27.1.2" { t.Fatalf("unexpected version: %s", version) } } @@ -307,7 +310,7 @@ func TestManagerRollbackRestoresSupportFiles(t *testing.T) { CertDir: certDir, NginxCertDir: "/etc/nginx/atsflare-certs", Executor: &fakeExecutor{ - testErr: errors.New("nginx test failed"), + testErr: errors.New("openresty test failed"), }, } diff --git a/atsf_agent/internal/sync/service.go b/atsf_agent/internal/sync/service.go index 556d8a5d..ae699075 100644 --- a/atsf_agent/internal/sync/service.go +++ b/atsf_agent/internal/sync/service.go @@ -68,13 +68,13 @@ func (s *Service) sync(ctx context.Context, startup bool) error { } log.Printf("current local checksum loaded: mode=%s checksum=%s", mode, currentChecksum) if currentChecksum == config.Checksum { - log.Printf("local nginx config already up to date: mode=%s version=%s", mode, config.Version) + log.Printf("local openresty config already up to date: mode=%s version=%s", mode, config.Version) if startup { - log.Printf("ensuring nginx runtime on startup: version=%s", config.Version) + log.Printf("ensuring openresty runtime on startup: version=%s", config.Version) if err = s.nginxManager.EnsureRuntime(ctx, true); err != nil { return err } - log.Printf("nginx runtime ensured on startup: version=%s", config.Version) + log.Printf("openresty runtime ensured on startup: version=%s", config.Version) } snapshot.CurrentVersion = config.Version snapshot.CurrentChecksum = config.Checksum @@ -86,9 +86,9 @@ func (s *Service) sync(ctx context.Context, startup bool) error { log.Printf("skipping apply because state already records target version/checksum: version=%s checksum=%s", config.Version, config.Checksum) return nil } - log.Printf("applying new nginx config: mode=%s from_version=%s to_version=%s old_checksum=%s new_checksum=%s", mode, snapshot.CurrentVersion, config.Version, currentChecksum, config.Checksum) + log.Printf("applying new openresty config: mode=%s from_version=%s to_version=%s old_checksum=%s new_checksum=%s", mode, snapshot.CurrentVersion, config.Version, currentChecksum, config.Checksum) if err = s.nginxManager.Apply(ctx, config.RenderedConfig, config.SupportFiles); err != nil { - log.Printf("apply nginx config failed: mode=%s version=%s error=%v", mode, config.Version, err) + log.Printf("apply openresty config failed: mode=%s version=%s error=%v", mode, config.Version, err) snapshot.LastError = err.Error() _ = s.stateStore.Save(snapshot) reportErr := s.client.ReportApplyLog(ctx, protocol.ApplyLogPayload{ @@ -104,7 +104,7 @@ func (s *Service) sync(ctx context.Context, startup bool) error { log.Printf("failed apply log reported: version=%s", config.Version) return err } - log.Printf("nginx config applied successfully: mode=%s version=%s", mode, config.Version) + log.Printf("openresty config applied successfully: mode=%s version=%s", mode, config.Version) snapshot.CurrentVersion = config.Version snapshot.CurrentChecksum = config.Checksum snapshot.LastError = "" diff --git a/atsf_server/common/init.go b/atsf_server/common/init.go index a208bdde..e4c37341 100644 --- a/atsf_server/common/init.go +++ b/atsf_server/common/init.go @@ -20,7 +20,7 @@ var ( var UploadPath = "upload" func printHelp() { - fmt.Println("ATSFlare " + Version + " - Internal Nginx Control Plane.") + fmt.Println("ATSFlare " + Version + " - Internal OpenResty Control Plane.") fmt.Println("Copyright (C) 2023 JustSong. All rights reserved.") fmt.Println("GitHub: https://github.com/Rain-kl/ATSFlare") fmt.Println("Usage: atsflare [--port ] [--log-dir ] [--version] [--help]") diff --git a/atsf_server/router/api_phase2_test.go b/atsf_server/router/api_phase2_test.go index 298d4323..394bfd7a 100644 --- a/atsf_server/router/api_phase2_test.go +++ b/atsf_server/router/api_phase2_test.go @@ -190,7 +190,7 @@ func TestPhase2AgentLifecycle(t *testing.T) { "name": "shanghai-edge-1", "ip": "10.0.0.9", "agent_version": "0.1.1", - "nginx_version": "1.25.5", + "nginx_version": "1.27.1.2", "current_version": "", "last_error": "", } @@ -224,7 +224,7 @@ func TestPhase2AgentLifecycle(t *testing.T) { "node_id": "spoofed-node-id", "version": activeConfig.Version, "result": service.ApplyResultFailed, - "message": "nginx reload failed", + "message": "openresty reload failed", }) var failedApplyLog model.ApplyLog decodeResponseData(t, failedApplyResp, &failedApplyLog) @@ -244,13 +244,13 @@ func TestPhase2AgentLifecycle(t *testing.T) { if nodes[0].AgentToken != createdNode.AgentToken { t.Fatal("expected node auth token to remain stable after occupancy") } - if nodes[0].LatestApplyResult != service.ApplyResultFailed || nodes[0].LatestApplyMessage != "nginx reload failed" { + if nodes[0].LatestApplyResult != service.ApplyResultFailed || nodes[0].LatestApplyMessage != "openresty reload failed" { t.Fatal("expected node list to expose latest apply status") } if nodes[0].CurrentVersion != activeConfig.Version { t.Fatal("expected node current_version to remain at last successful version") } - if nodes[0].LastError != "nginx reload failed" { + if nodes[0].LastError != "openresty reload failed" { t.Fatal("expected node last_error to reflect failed apply") } diff --git a/docs/app-config.md b/docs/app-config.md index 33a3e92e..bd779161 100644 --- a/docs/app-config.md +++ b/docs/app-config.md @@ -172,8 +172,8 @@ go run ./cmd/agent -config ./agent.json "server_url": "http://127.0.0.1:3000", "discovery_token": "replace-with-global-discovery-token", "data_dir": "./data", - "nginx_container_name": "atsflare-nginx", - "nginx_docker_image": "nginx:stable-alpine", + "openresty_container_name": "atsflare-openresty", + "openresty_docker_image": "openresty/openresty:alpine", "heartbeat_interval": 30000, "sync_interval": 30000, "request_timeout": 10000 @@ -189,10 +189,10 @@ go run ./cmd/agent -config ./agent.json "node_name": "node-01", "node_ip": "192.168.1.20", "data_dir": "./data", - "nginx_path": "/usr/sbin/nginx", - "route_config_path": "/etc/nginx/conf.d/atsflare_routes.conf", - "cert_dir": "/etc/nginx/certs", - "nginx_cert_dir": "/etc/nginx/certs", + "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", + "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", + "cert_dir": "/usr/local/openresty/nginx/conf/certs", + "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs", "state_path": "./data/agent-state.json", "heartbeat_interval": 30000, "sync_interval": 30000, @@ -209,14 +209,14 @@ go run ./cmd/agent -config ./agent.json | `discovery_token` | 全局发现 Token,用于节点首次自动注册 | 与 `agent_token` 二选一 | 空 | `discovery-token-xxx` | | `node_name` | 节点名称 | 否 | 自动使用主机名 | `node-01` | | `node_ip` | 节点 IP | 否 | 自动探测第一个可用 IPv4 | `192.168.1.20` | -| `nginx_path` | 本机 Nginx 可执行文件路径;设置后按本机 Nginx 模式运行 | 否 | 空;未设置时按 Docker Nginx 模式处理 | `/usr/sbin/nginx` | -| `nginx_container_name` | Docker 模式下的 Nginx 容器名 | 否 | `atsflare-nginx` | `atsflare-nginx` | -| `nginx_docker_image` | Docker 模式下用于初始化/管理的 Nginx 镜像 | 否 | `nginx:stable-alpine` | `nginx:stable-alpine` | +| `openresty_path` | 本机 OpenResty 可执行文件路径;设置后按本机 OpenResty 模式运行 | 否 | 空;未设置时按 Docker OpenResty 模式处理 | `/usr/local/openresty/nginx/sbin/openresty` | +| `openresty_container_name` | Docker 模式下的 OpenResty 容器名 | 否 | `atsflare-openresty` | `atsflare-openresty` | +| `openresty_docker_image` | Docker 模式下用于初始化/管理的 OpenResty 镜像 | 否 | `openresty/openresty:alpine` | `openresty/openresty:alpine` | | `docker_binary` | Docker 可执行文件名或路径 | 否 | `docker` | `/usr/bin/docker` | | `data_dir` | Agent 数据目录,用于存储托管配置、证书和状态文件 | 否 | 配置文件所在目录下的 `data` 子目录 | `./data` | | `route_config_path` | 路由配置文件写入路径 | 否 | 默认为 `data_dir` 下托管路径 | `/etc/nginx/conf.d/atsflare_routes.conf` | | `cert_dir` | Agent 在本机写入证书文件的目录 | 否 | 默认为 `data_dir` 下托管证书目录 | `./data/etc/nginx/certs` | -| `nginx_cert_dir` | Nginx 实际读取证书的目录 | 否 | 本机模式默认等于 `cert_dir`;Docker 模式默认 `/etc/nginx/atsflare-certs` | `/etc/nginx/certs` | +| `openresty_cert_dir` | OpenResty 实际读取证书的目录 | 否 | 本机模式默认等于 `cert_dir`;Docker 模式默认 `/etc/nginx/atsflare-certs` | `/usr/local/openresty/nginx/conf/certs` | | `state_path` | Agent 本地状态文件路径 | 否 | 默认为 `data_dir` 下托管状态文件 | `./data/agent-state.json` | | `heartbeat_interval` | 心跳间隔 | 否 | `30000` 毫秒 | `30000` | | `sync_interval` | 配置同步间隔 | 否 | `30000` 毫秒 | `30000` | @@ -229,8 +229,9 @@ go run ./cmd/agent -config ./agent.json * 毫秒整数,例如 `30000` * Go duration 字符串,例如 `"30s"` * `node_name` 与 `node_ip` 未填写时会自动探测;若自动探测失败,配置校验会报错 -* 未配置 `nginx_path` 时,默认为 Docker Nginx 模式 +* 未配置 `openresty_path` 时,默认为 Docker OpenResty 模式 * 配置保存时,`agent_version`、`nginx_version` 由程序运行时维护,不需要写入 JSON +* 本机模式下的 `route_config_path` 需与节点主配置文件的 include 规则保持一致 ### 2.4 Agent 托管路径默认值 @@ -242,17 +243,17 @@ go run ./cmd/agent -config ./agent.json | `cert_dir` | `data_dir/etc/nginx/certs` | | `state_path` | `data_dir/var/lib/atsflare/agent-state.json` | -Docker Nginx 模式下: +Docker OpenResty 模式下: | 字段 | 默认值 | | --- | --- | -| `nginx_cert_dir` | `/etc/nginx/atsflare-certs` | +| `openresty_cert_dir` | `/etc/nginx/atsflare-certs` | ### 2.5 Agent 启动示例 -#### Docker Nginx 模式 +#### Docker OpenResty 模式 -适用于节点本机不直接管理宿主机 Nginx,而是通过 Docker 容器运行 Nginx。 +适用于节点本机不直接管理宿主机 OpenResty,而是通过 Docker 容器运行 OpenResty。 ```json { @@ -262,18 +263,18 @@ Docker Nginx 模式下: } ``` -#### 本机 Nginx 模式 +#### 本机 OpenResty 模式 -适用于节点已经安装了宿主机 Nginx,且 Agent 直接执行 `nginx -t` 与 `nginx -s reload`。 +适用于节点已经安装了宿主机 OpenResty,且 Agent 直接执行 `openresty -t` 与 `openresty -s reload`。 ```json { "server_url": "http://127.0.0.1:3000", "agent_token": "replace-with-node-auth-token", - "nginx_path": "/usr/sbin/nginx", - "route_config_path": "/etc/nginx/conf.d/atsflare_routes.conf", - "cert_dir": "/etc/nginx/certs", - "nginx_cert_dir": "/etc/nginx/certs" + "openresty_path": "/usr/local/openresty/nginx/sbin/openresty", + "route_config_path": "/usr/local/openresty/nginx/conf/conf.d/atsflare_routes.conf", + "cert_dir": "/usr/local/openresty/nginx/conf/certs", + "openresty_cert_dir": "/usr/local/openresty/nginx/conf/certs" } ``` diff --git a/docs/deployment.md b/docs/deployment.md index 68f5aa4e..b21d3c75 100644 --- a/docs/deployment.md +++ b/docs/deployment.md @@ -16,7 +16,7 @@ * Go 1.18+ * 对 Agent 数据目录有写权限 -* 若使用独立 Nginx 模式:可执行 `nginx -t` 与 `nginx -s reload` +* 若使用独立 OpenResty 模式:可执行 `openresty -t` 与 `openresty -s reload` * 若使用 Docker 模式:具备 Docker 执行权限 --- @@ -84,18 +84,18 @@ volumes: docker compose up -d ``` -说明: - -* `SESSION_SECRET` 必须替换为随机字符串 -* SQLite 数据文件持久化到 Docker volume `atsflare-data` -* 镜像默认监听容器内 `3000` 端口 -* 若要固定版本,可将 `latest` 替换为具体 tag,例如 `ghcr.io/rain-kl/atsflare:v0.3.0` - -版本升级说明: - -* Root 用户可在管理端顶栏点击「版本」检查 GitHub 最新 Release -* 当前运行的是 Release 二进制且二进制目录可写时,可直接在弹窗内触发 Server 自升级 -* 自升级会下载匹配当前平台的 `atsflare-server-*` 资产,替换当前二进制并自动重启进程 +说明: + +* `SESSION_SECRET` 必须替换为随机字符串 +* SQLite 数据文件持久化到 Docker volume `atsflare-data` +* 镜像默认监听容器内 `3000` 端口 +* 若要固定版本,可将 `latest` 替换为具体 tag,例如 `ghcr.io/rain-kl/atsflare:v0.3.0` + +版本升级说明: + +* Root 用户可在管理端顶栏点击「版本」检查 GitHub 最新 Release +* 当前运行的是 Release 二进制且二进制目录可写时,可直接在弹窗内触发 Server 自升级 +* 自升级会下载匹配当前平台的 `atsflare-server-*` 资产,替换当前二进制并自动重启进程 ### 2.4 首次登录 @@ -146,8 +146,8 @@ swag init -g main.go -o docs "server_url": "http://127.0.0.1:3000", "agent_token": "replace-with-node-auth-token", "data_dir": "./data", - "nginx_container_name": "atsflare-nginx", - "nginx_docker_image": "nginx:stable-alpine", + "openresty_container_name": "atsflare-openresty", + "openresty_docker_image": "openresty/openresty:alpine", "heartbeat_interval": 30000, "sync_interval": 30000, "request_timeout": 10000 @@ -161,8 +161,8 @@ swag init -g main.go -o docs "server_url": "http://127.0.0.1:3000", "discovery_token": "replace-with-global-discovery-token", "data_dir": "./data", - "nginx_container_name": "atsflare-nginx", - "nginx_docker_image": "nginx:stable-alpine", + "openresty_container_name": "atsflare-openresty", + "openresty_docker_image": "openresty/openresty:alpine", "heartbeat_interval": 30000, "sync_interval": 30000, "request_timeout": 10000 @@ -172,12 +172,12 @@ swag init -g main.go -o docs 说明: * `agent_version` 由 Agent 代码内常量提供,升级时同步修改代码 -* `nginx_version` 由 Agent 启动时执行命令自动探测 +* 为兼容现有 Agent / Server API,运行时版本仍通过 `openresty_version` 字段上报,但其值现在表示 OpenResty 版本 * 时间字段使用毫秒整数 * `agent_token` 与 `discovery_token` 至少填写一个 * 若 `agent_token` 为空且 `discovery_token` 存在,Agent 会自动注册并写回新的专属 `agent_token` * `node_name` 与 `node_ip` 可省略,未填写时自动探测 -* 未配置 `nginx_path` 时,默认使用 Docker Nginx 容器 +* 未配置 `openresty_path` 时,默认使用 Docker OpenResty 容器 --- @@ -223,8 +223,8 @@ go build -o atsflare-agent ./cmd/agent 2. 自动注册模式下完成 Token 置换 3. 拉取激活版本 4. 写入路由配置与必要证书文件 -5. 执行 `nginx -t` -6. 执行 `nginx -s reload` +5. 执行 `openresty -t` +6. 执行 `openresty -s reload` 7. 上报应用结果 ### 5.4 验证管理端状态 @@ -238,7 +238,7 @@ go build -o atsflare-agent ./cmd/agent ### 5.5 验证失败回滚 -人为制造 `nginx -t` 失败后再次发布,预期: +人为制造 `openresty -t` 失败后再次发布,预期: * Agent 回滚旧配置 * 节点 `last_error` 更新 @@ -276,11 +276,11 @@ pnpm build * GitHub 使用 [.github/workflows/release.yml](.github/workflows/release.yml),保留制品上传/下载分阶段流程 * Gitea 使用 [.gitea/workflows/release.yml](.gitea/workflows/release.yml),在单个 Job 内完成前端构建、服务端/Agent 多平台编译与 Release 发布,避免依赖 Gitea 目前不兼容的 `upload-artifact@v4`、`download-artifact@v4` -Docker 镜像发布使用 [.github/workflows/docker-image.yml](.github/workflows/docker-image.yml): - -* 仅构建 `atsf_server` 服务端镜像 -* 发布到 GitHub Container Registry(`ghcr.io//:`) -* 单个工作流通过分架构原生构建再合并 manifest 的方式产出 `linux/amd64` 与 `linux/arm64` 多架构镜像,避免 `arm64` 长时间模拟编译 +Docker 镜像发布使用 [.github/workflows/docker-image.yml](.github/workflows/docker-image.yml): + +* 仅构建 `atsf_server` 服务端镜像 +* 发布到 GitHub Container Registry(`ghcr.io//:`) +* 单个工作流通过分架构原生构建再合并 manifest 的方式产出 `linux/amd64` 与 `linux/arm64` 多架构镜像,避免 `arm64` 长时间模拟编译 --- diff --git a/docs/design.md b/docs/design.md index 3a47f160..68c2b451 100644 --- a/docs/design.md +++ b/docs/design.md @@ -21,7 +21,7 @@ ATSFlare 当前定位为内部自用的反向代理控制面,不面向外部 * 反代规则管理 * 配置预览、发布、激活与回滚 * Agent 注册、心跳、同步、应用结果上报 -* Nginx 配置写入、校验、reload 与失败回滚 +* OpenResty 配置写入、校验、reload 与失败回滚 * HTTPS/TLS 路由支持 * 证书托管与域名管理 * 节点管理、节点专属 `agent_token`、全局 `discovery_token` @@ -74,8 +74,8 @@ ATSFlare 当前定位为内部自用的反向代理控制面,不面向外部 * 单二进制 * 节点本地执行 -* `nginx_path` 优先 -* 未配置 `nginx_path` 时默认使用 Docker Nginx +* `openresty_path` 优先 +* 未配置 `openresty_path` 时默认使用 Docker OpenResty * 生成资源默认落在 `./data`,可由 `data_dir` 覆盖 ### 4.3 Frontend @@ -100,7 +100,7 @@ ATSFlare Server (Gin + SQLite + Web UI) ATSFlare Agent (register / heartbeat / sync / apply / update) | v - Local Nginx or Docker Nginx + Local OpenResty or Docker OpenResty | v Origin @@ -150,7 +150,7 @@ ATSFlare Agent (register / heartbeat / sync / apply / update) 发布规则: 1. 读取全部启用的 `proxy_routes` -2. 渲染完整 Nginx 配置 +2. 渲染完整 OpenResty 配置 3. 计算 `checksum` 4. 写入 `config_versions` 5. 切换激活版本 @@ -185,7 +185,7 @@ ATSFlare Agent (register / heartbeat / sync / apply / update) * 周期性心跳与同步 * 运行参数接收 * 本地路由与证书文件写入 -* `nginx -t` / `nginx -s reload` +* 执行 `openresty -t` / `openresty -s reload` * 失败回滚 * 自我更新 * 应用结果上报 diff --git a/docs/development-guidelines.md b/docs/development-guidelines.md index 1b462e84..0472274e 100644 --- a/docs/development-guidelines.md +++ b/docs/development-guidelines.md @@ -36,8 +36,8 @@ * 单二进制 * 节点本地执行 -* `nginx_path` 优先 -* 无 `nginx_path` 时默认 Docker Nginx +* `openresty_path` 优先 +* 无 `openresty_path` 时默认 Docker OpenResty * 生成资源默认写入 `./data`,由 `data_dir` 统一覆盖 ### 2.3 Frontend @@ -81,7 +81,7 @@ * `config` * `heartbeat` * `sync` -* `nginx` +* `openresty`(保留目录名,内部负责 OpenResty 运行时管理) * `state` * `httpclient` * `protocol` @@ -170,7 +170,7 @@ Agent: 禁止: -* 将本地 Nginx 操作暴露为远程执行接口 +* 将本地 OpenResty 操作暴露为远程执行接口 * 在日志中打印完整 Token --- @@ -180,7 +180,7 @@ Agent: 发布逻辑必须保持以下事实: * 发布时读取全部启用的 `proxy_routes` -* 生成完整 Nginx 配置 +* 生成完整 OpenResty 配置 * 计算 `checksum` * 写入 `config_versions` * 通过切换 `is_active` 激活版本 @@ -205,8 +205,8 @@ Agent 必须满足: * 周期性心跳与同步 * 发现新版本时先备份旧文件 * 写入新路由与必要证书文件 -* 先执行 `nginx -t` -* 成功后执行 `nginx -s reload` +* 先执行 `openresty -t` +* 成功后执行 `openresty -s reload` * 失败时自动回滚并上报最终结果 * 支持自动注册与 Token 置换 * 支持接收 Server 下发运行参数 @@ -249,7 +249,7 @@ Agent 必须满足: * Agent 注册 * 心跳异常 * 配置下载失败 -* Nginx 校验或 reload 成功/失败 +* OpenResty 校验或 reload 成功/失败 * 回滚触发 要求: