diff --git a/atsf_agent/cmd/agent/main.go b/atsf_agent/cmd/agent/main.go index 482704b2..d7e95a4e 100644 --- a/atsf_agent/cmd/agent/main.go +++ b/atsf_agent/cmd/agent/main.go @@ -24,6 +24,7 @@ func main() { if err != nil { log.Fatal(err) } + 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) client := httpclient.New(cfg.ServerURL, cfg.AgentToken, cfg.RequestTimeout) stateStore := state.NewStore(cfg.StatePath) @@ -49,8 +50,10 @@ func main() { ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM) defer stop() + log.Printf("agent process started") if err = runner.Run(ctx); err != nil && err != context.Canceled { log.Fatal(err) } + log.Printf("agent process stopped") } diff --git a/atsf_agent/internal/agent/runner.go b/atsf_agent/internal/agent/runner.go index bc08413c..ddbdddf6 100644 --- a/atsf_agent/internal/agent/runner.go +++ b/atsf_agent/internal/agent/runner.go @@ -32,15 +32,22 @@ func (r *Runner) Run(ctx context.Context) error { if err != nil { return err } + log.Printf("agent runner started: node_id=%s node=%s ip=%s", nodeID, r.Config.NodeName, r.Config.NodeIP) if err = r.HeartbeatService.Register(ctx, r.nodePayload(nodeID)); err != nil { log.Printf("agent register failed: %v", err) + } else { + log.Printf("agent register succeeded: node_id=%s", nodeID) } if err = r.SyncService.SyncOnStartup(ctx); err != nil { r.recordSyncError(err) log.Printf("agent startup sync failed: %v", err) + } else { + log.Printf("agent startup sync completed") } if err = r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID)); err != nil { log.Printf("agent startup heartbeat failed: %v", err) + } else { + log.Printf("agent startup heartbeat succeeded: node_id=%s", nodeID) } heartbeatTicker := time.NewTicker(r.Config.HeartbeatInterval) @@ -51,15 +58,19 @@ func (r *Runner) Run(ctx context.Context) error { for { select { case <-ctx.Done(): + log.Printf("agent runner shutting down: %v", ctx.Err()) return ctx.Err() case <-heartbeatTicker.C: if err = r.HeartbeatService.Heartbeat(ctx, r.nodePayload(nodeID)); err != nil { log.Printf("agent heartbeat failed: %v", err) } case <-syncTicker.C: + log.Printf("agent sync tick: node_id=%s", nodeID) if err = r.SyncService.SyncOnce(ctx); err != nil { r.recordSyncError(err) log.Printf("agent sync failed: %v", err) + } else { + log.Printf("agent sync completed") } } } @@ -75,6 +86,7 @@ func (r *Runner) recordSyncError(err error) { return } snapshot.LastError = err.Error() + log.Printf("recording sync error into state: %s", snapshot.LastError) if saveErr := r.StateStore.Save(snapshot); saveErr != nil { log.Printf("save state after sync error failed: %v", saveErr) } diff --git a/atsf_agent/internal/httpclient/client.go b/atsf_agent/internal/httpclient/client.go index 59e6927c..99a183dd 100644 --- a/atsf_agent/internal/httpclient/client.go +++ b/atsf_agent/internal/httpclient/client.go @@ -5,6 +5,7 @@ import ( "context" "encoding/json" "errors" + "log" "net/http" "strings" "time" @@ -29,6 +30,7 @@ func New(baseURL string, token string, timeout time.Duration) *Client { } func (c *Client) RegisterNode(ctx context.Context, payload protocol.NodePayload) error { + log.Printf("http register node request: node_id=%s current_version=%s", payload.NodeID, payload.CurrentVersion) return c.postJSON(ctx, "/api/agent/nodes/register", payload, nil) } @@ -37,6 +39,7 @@ func (c *Client) Heartbeat(ctx context.Context, payload protocol.NodePayload) er } func (c *Client) GetActiveConfig(ctx context.Context) (*protocol.ActiveConfigResponse, error) { + log.Printf("http get active config request") resp := protocol.APIResponse[protocol.ActiveConfigResponse]{} if err := c.getJSON(ctx, "/api/agent/config-versions/active", &resp); err != nil { return nil, err @@ -44,10 +47,12 @@ func (c *Client) GetActiveConfig(ctx context.Context) (*protocol.ActiveConfigRes if !resp.Success { return nil, errors.New(resp.Message) } + log.Printf("http get active config response: version=%s checksum=%s support_files=%d", resp.Data.Version, resp.Data.Checksum, len(resp.Data.SupportFiles)) return &resp.Data, nil } func (c *Client) ReportApplyLog(ctx context.Context, payload protocol.ApplyLogPayload) error { + log.Printf("http report apply log request: node_id=%s version=%s result=%s", payload.NodeID, payload.Version, payload.Result) return c.postJSON(ctx, "/api/agent/apply-logs", payload, nil) } @@ -75,23 +80,47 @@ func (c *Client) postJSON(ctx context.Context, path string, body any, target any } func (c *Client) do(req *http.Request, target any) error { + if !isHeartbeatRequest(req) { + log.Printf("http request start: method=%s path=%s", req.Method, req.URL.Path) + } res, err := c.httpClient.Do(req) if err != nil { + log.Printf("http request failed: method=%s path=%s error=%v", req.Method, req.URL.Path, err) return err } defer res.Body.Close() if res.StatusCode != http.StatusOK { + log.Printf("http request returned non-200: method=%s path=%s status=%s", req.Method, req.URL.Path, res.Status) return errors.New(res.Status) } if target == nil { var wrapper protocol.APIResponse[json.RawMessage] if err = json.NewDecoder(res.Body).Decode(&wrapper); err != nil { + log.Printf("http response decode failed: method=%s path=%s error=%v", req.Method, req.URL.Path, err) return err } if !wrapper.Success { + log.Printf("http api response failed: method=%s path=%s message=%s", req.Method, req.URL.Path, wrapper.Message) return errors.New(wrapper.Message) } + if !isHeartbeatRequest(req) { + log.Printf("http request succeeded: method=%s path=%s", req.Method, req.URL.Path) + } return nil } - return json.NewDecoder(res.Body).Decode(target) + if err = json.NewDecoder(res.Body).Decode(target); err != nil { + log.Printf("http response decode failed: method=%s path=%s error=%v", req.Method, req.URL.Path, err) + return err + } + if !isHeartbeatRequest(req) { + log.Printf("http request succeeded: method=%s path=%s", req.Method, req.URL.Path) + } + return nil +} + +func isHeartbeatRequest(req *http.Request) bool { + if req == nil || req.URL == nil { + return false + } + return req.Method == http.MethodPost && req.URL.Path == "/api/agent/nodes/heartbeat" } diff --git a/atsf_agent/internal/nginx/manager.go b/atsf_agent/internal/nginx/manager.go index 11d02f6a..2678270e 100644 --- a/atsf_agent/internal/nginx/manager.go +++ b/atsf_agent/internal/nginx/manager.go @@ -6,6 +6,7 @@ import ( "encoding/hex" "errors" "fmt" + "log" "os" "os/exec" "path/filepath" @@ -41,18 +42,22 @@ type PathExecutor struct { } func (e *PathExecutor) Test(ctx context.Context) error { + log.Printf("running nginx 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)) } + log.Printf("nginx 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) output, err := e.Runner.Run(ctx, e.Path, "-s", "reload") if err != nil { return fmt.Errorf("nginx reload failed: %w: %s", err, string(output)) } + log.Printf("nginx reload succeeded with binary: %s", e.Path) return nil } @@ -71,6 +76,7 @@ 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, @@ -87,6 +93,7 @@ func (e *DockerExecutor) Test(ctx context.Context) error { if err != nil { return fmt.Errorf("docker nginx -t failed: %w: %s", err, string(output)) } + log.Printf("docker nginx test succeeded: container=%s", e.ContainerName) return nil } @@ -95,6 +102,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) output, err := e.Runner.Run(ctx, e.DockerBinary, "inspect", "-f", "{{.State.Running}}", e.ContainerName) if err == nil { if recreate { @@ -104,6 +112,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) return nil } if err := e.removeContainer(ctx); err != nil { @@ -115,6 +124,7 @@ 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) output, err := e.Runner.Run(ctx, e.DockerBinary, "rm", "-f", e.ContainerName) if err != nil { text := string(output) @@ -123,10 +133,12 @@ func (e *DockerExecutor) removeContainer(ctx context.Context) error { } return fmt.Errorf("docker rm nginx failed: %w: %s", err, text) } + log.Printf("docker nginx 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) runArgs := []string{ "run", "-d", "--name", e.ContainerName, @@ -140,6 +152,7 @@ func (e *DockerExecutor) runContainer(ctx context.Context) error { if runErr != nil { return fmt.Errorf("docker run nginx failed: %w: %s", runErr, string(runOutput)) } + log.Printf("docker nginx container started: container=%s", e.ContainerName) return nil } @@ -151,27 +164,33 @@ 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)) backup, err := m.backup() if err != nil { return err } if err = m.writeSupportFiles(supportFiles); err != nil { + log.Printf("writing support files failed, restoring backup: error=%v", err) _ = m.restore(backup) return err } 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) _ = 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) _ = 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) _ = m.restore(backup) return err } + log.Printf("nginx apply completed successfully: route_config=%s", m.RouteConfigPath) return nil } @@ -179,6 +198,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) return m.Executor.EnsureRuntime(ctx, recreate) } @@ -201,7 +221,9 @@ func (m *Manager) CurrentChecksum() (string, error) { if err != nil { return "", err } - return bundleChecksum(normalized, files), nil + result := bundleChecksum(normalized, files) + log.Printf("nginx current checksum calculated: route_config=%s checksum=%s support_files=%d", m.RouteConfigPath, result, len(files)) + return result, nil } type ExecutorOptions struct { @@ -272,6 +294,7 @@ func (m *Manager) backup() (*backupState, error) { return nil, err } state.Files = files + log.Printf("nginx backup captured: route_exists=%t support_files=%d", state.RouteExisted, len(state.Files)) return state, nil } @@ -279,6 +302,7 @@ func (m *Manager) restore(state *backupState) error { if state == nil { return nil } + log.Printf("restoring nginx backup: route_existed=%t support_files=%d", state.RouteExisted, len(state.Files)) if state.RouteExisted { if err := os.WriteFile(m.RouteConfigPath, state.RouteData, 0o644); err != nil { return err @@ -304,6 +328,7 @@ func (m *Manager) restore(state *backupState) error { return err } } + log.Printf("nginx backup restored") return nil } @@ -311,6 +336,7 @@ func (m *Manager) writeSupportFiles(supportFiles []protocol.SupportFile) error { if m.CertDir == "" { return nil } + log.Printf("writing nginx support files: cert_dir=%s count=%d", m.CertDir, len(supportFiles)) if err := os.RemoveAll(m.CertDir); err != nil && !os.IsNotExist(err) { return err } @@ -326,6 +352,7 @@ func (m *Manager) writeSupportFiles(supportFiles []protocol.SupportFile) error { return err } } + log.Printf("nginx support files written: cert_dir=%s count=%d", m.CertDir, len(supportFiles)) return nil } diff --git a/atsf_agent/internal/sync/service.go b/atsf_agent/internal/sync/service.go index 9d08f1cc..556d8a5d 100644 --- a/atsf_agent/internal/sync/service.go +++ b/atsf_agent/internal/sync/service.go @@ -2,6 +2,7 @@ package sync import ( "context" + "log" "atsflare-agent/internal/protocol" "atsflare-agent/internal/state" @@ -46,33 +47,48 @@ func (s *Service) SyncOnStartup(ctx context.Context) error { } func (s *Service) sync(ctx context.Context, startup bool) error { + mode := "periodic" + if startup { + mode = "startup" + } + log.Printf("sync started: mode=%s", mode) snapshot, err := s.stateStore.Load() if err != nil { return err } config, err := s.client.GetActiveConfig(ctx) if err != nil { + log.Printf("fetch active config failed: mode=%s error=%v", mode, err) return err } + log.Printf("active config fetched: mode=%s version=%s checksum=%s support_files=%d", mode, config.Version, config.Checksum, len(config.SupportFiles)) currentChecksum, err := s.nginxManager.CurrentChecksum() if err != nil { return err } + 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) if startup { + log.Printf("ensuring nginx 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) } snapshot.CurrentVersion = config.Version snapshot.CurrentChecksum = config.Checksum snapshot.LastError = "" + log.Printf("sync finished without changes: mode=%s version=%s", mode, config.Version) return s.stateStore.Save(snapshot) } if snapshot.CurrentVersion == config.Version && snapshot.CurrentChecksum == config.Checksum && !startup { + 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) 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) snapshot.LastError = err.Error() _ = s.stateStore.Save(snapshot) reportErr := s.client.ReportApplyLog(ctx, protocol.ApplyLogPayload{ @@ -82,20 +98,28 @@ func (s *Service) sync(ctx context.Context, startup bool) error { Message: err.Error(), }) if reportErr != nil { + log.Printf("report failed apply log failed: version=%s error=%v", config.Version, reportErr) return reportErr } + 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) snapshot.CurrentVersion = config.Version snapshot.CurrentChecksum = config.Checksum snapshot.LastError = "" if err = s.stateStore.Save(snapshot); err != nil { return err } - return s.client.ReportApplyLog(ctx, protocol.ApplyLogPayload{ + if err = s.client.ReportApplyLog(ctx, protocol.ApplyLogPayload{ NodeID: snapshot.NodeID, Version: config.Version, Result: ApplyResultSuccess, Message: "apply success", - }) + }); err != nil { + log.Printf("report successful apply log failed: version=%s error=%v", config.Version, err) + return err + } + log.Printf("successful apply log reported: version=%s", config.Version) + return nil } diff --git a/atsf_server/main.go b/atsf_server/main.go index ad9a2e9a..056faeff 100644 --- a/atsf_server/main.go +++ b/atsf_server/main.go @@ -1,75 +1,85 @@ -package main - -import ( - "embed" - "gin-template/common" - "gin-template/middleware" - "gin-template/model" - "gin-template/router" - "github.com/gin-contrib/sessions" - "github.com/gin-contrib/sessions/cookie" - "github.com/gin-contrib/sessions/redis" - "github.com/gin-gonic/gin" - "log" - "os" - "strconv" -) - -//go:embed web/build -var buildFS embed.FS - -//go:embed web/build/index.html -var indexPage []byte - -func main() { - common.SetupGinLog() - common.SysLog("Gin Template " + common.Version + " started") - if os.Getenv("GIN_MODE") != "debug" { - gin.SetMode(gin.ReleaseMode) - } - // Initialize SQL Database - err := model.InitDB() - if err != nil { - common.FatalLog(err) - } - defer func() { - err := model.CloseDB() - if err != nil { - common.FatalLog(err) - } - }() - - // Initialize Redis - err = common.InitRedisClient() - if err != nil { - common.FatalLog(err) - } - - // Initialize options - model.InitOptionMap() - - // Initialize HTTP server - server := gin.Default() - //server.Use(gzip.Gzip(gzip.DefaultCompression)) - server.Use(middleware.CORS()) - - // Initialize session store - if common.RedisEnabled { - opt := common.ParseRedisOption() - store, _ := redis.NewStore(opt.MinIdleConns, opt.Network, opt.Addr, opt.Password, []byte(common.SessionSecret)) - server.Use(sessions.Sessions("session", store)) - } else { - store := cookie.NewStore([]byte(common.SessionSecret)) - server.Use(sessions.Sessions("session", store)) - } - - router.SetRouter(server, buildFS, indexPage) - var port = os.Getenv("PORT") - if port == "" { - port = strconv.Itoa(*common.Port) - } - err = server.Run(":" + port) - if err != nil { - log.Println(err) - } -} +package main + +import ( + "embed" + "fmt" + "gin-template/common" + "gin-template/middleware" + "gin-template/model" + "gin-template/router" + "github.com/gin-contrib/sessions" + "github.com/gin-contrib/sessions/cookie" + "github.com/gin-contrib/sessions/redis" + "github.com/gin-gonic/gin" + "log" + "os" + "strconv" +) + +//go:embed web/build +var buildFS embed.FS + +//go:embed web/build/index.html +var indexPage []byte + +func main() { + common.SetupGinLog() + common.SysLog("Gin Template " + common.Version + " started") + if os.Getenv("GIN_MODE") != "debug" { + gin.SetMode(gin.ReleaseMode) + } + // Initialize SQL Database + err := model.InitDB() + if err != nil { + common.FatalLog(err) + } + defer func() { + err := model.CloseDB() + if err != nil { + common.FatalLog(err) + } + }() + + // Initialize Redis + err = common.InitRedisClient() + if err != nil { + common.FatalLog(err) + } + + // Initialize options + model.InitOptionMap() + + // Initialize HTTP server + server := gin.Default() + //server.Use(gzip.Gzip(gzip.DefaultCompression)) + server.Use(middleware.CORS()) + + // Initialize session store + if common.RedisEnabled { + opt := common.ParseRedisOption() + store, _ := redis.NewStore(opt.MinIdleConns, opt.Network, opt.Addr, opt.Password, []byte(common.SessionSecret)) + server.Use(sessions.Sessions("session", store)) + } else { + store := cookie.NewStore([]byte(common.SessionSecret)) + server.Use(sessions.Sessions("session", store)) + } + + router.SetRouter(server, buildFS, indexPage) + var port = os.Getenv("PORT") + if port == "" { + port = strconv.Itoa(*common.Port) + } + common.SysLog(fmt.Sprintf("server config: port=%s gin_mode=%s sqlite_path=%s redis_enabled=%t upload_path=%s log_dir=%s agent_token_configured=%t node_offline_threshold=%s", port, gin.Mode(), common.SQLitePath, common.RedisEnabled, common.UploadPath, valueOrDefault(*common.LogDir, "stdout"), common.AgentToken != "", common.NodeOfflineThreshold)) + common.SysLog(fmt.Sprintf("server listening on :%s", port)) + err = server.Run(":" + port) + if err != nil { + log.Println(err) + } +} + +func valueOrDefault(value string, fallback string) string { + if value == "" { + return fallback + } + return value +} diff --git a/atsf_server/service/agent.go b/atsf_server/service/agent.go index 1e3442fa..9aaf76e5 100644 --- a/atsf_server/service/agent.go +++ b/atsf_server/service/agent.go @@ -62,16 +62,19 @@ type NodeView struct { } func RegisterNode(payload AgentNodePayload) (*model.Node, error) { + common.SysLog("agent register request received: node_id=" + strings.TrimSpace(payload.NodeID) + " name=" + strings.TrimSpace(payload.Name) + " ip=" + strings.TrimSpace(payload.IP)) return upsertNode(payload) } func HeartbeatNode(payload AgentNodePayload) (*model.Node, error) { + common.SysLog("agent heartbeat received: node_id=" + strings.TrimSpace(payload.NodeID) + " current_version=" + strings.TrimSpace(payload.CurrentVersion)) return upsertNode(payload) } func GetActiveConfigForAgent() (*AgentConfigResponse, error) { version, err := model.GetActiveConfigVersion() if err != nil { + common.SysError("agent requested active config but no active version is available") return nil, err } var supportFiles []SupportFile @@ -80,6 +83,7 @@ func GetActiveConfigForAgent() (*AgentConfigResponse, error) { return nil, err } } + common.SysLog("agent fetched active config: version=" + version.Version + " checksum=" + version.Checksum) return &AgentConfigResponse{ Version: version.Version, Checksum: version.Checksum, @@ -104,6 +108,7 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) { if payload.Result != ApplyResultOK && payload.Result != ApplyResultFailed { return nil, errors.New("result 仅支持 success 或 failed") } + common.SysLog("agent apply log received: node_id=" + payload.NodeID + " version=" + payload.Version + " result=" + payload.Result) log := &model.ApplyLog{ NodeID: payload.NodeID, @@ -133,6 +138,11 @@ func ReportApplyLog(payload ApplyLogPayload) (*model.ApplyLog, error) { if err != nil { return nil, err } + if payload.Result == ApplyResultOK { + common.SysLog("agent apply reported success: node_id=" + payload.NodeID + " version=" + payload.Version) + } else { + common.SysError("agent apply reported failure: node_id=" + payload.NodeID + " version=" + payload.Version + " message=" + payload.Message) + } return log, nil } @@ -143,6 +153,16 @@ func ListNodeViews() ([]*NodeView, error) { } views := make([]*NodeView, 0, len(nodes)) for _, node := range nodes { + computedStatus := computeNodeStatus(node.LastSeenAt) + if node.Status != computedStatus { + if computedStatus == NodeStatusOffline { + common.SysError("node offline: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP + " last_seen_at=" + node.LastSeenAt.Format(time.RFC3339)) + } else { + common.SysLog("node online: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP) + } + _ = model.DB.Model(node).Update("status", computedStatus).Error + node.Status = computedStatus + } view := &NodeView{ ID: node.ID, NodeID: node.NodeID, @@ -150,7 +170,7 @@ func ListNodeViews() ([]*NodeView, error) { IP: node.IP, AgentVersion: node.AgentVersion, NginxVersion: node.NginxVersion, - Status: computeNodeStatus(node.LastSeenAt), + Status: computedStatus, CurrentVersion: node.CurrentVersion, LastSeenAt: node.LastSeenAt, LastError: node.LastError, @@ -203,6 +223,10 @@ func upsertNode(payload AgentNodePayload) (*model.Node, error) { NodeID: payload.NodeID, } } + previousStatus := node.Status + previousIP := node.IP + previousVersion := node.CurrentVersion + previousAgentVersion := node.AgentVersion node.Name = payload.Name node.IP = payload.IP node.AgentVersion = payload.AgentVersion @@ -215,11 +239,17 @@ func upsertNode(payload AgentNodePayload) (*model.Node, error) { if err = model.DB.Create(node).Error; err != nil { return nil, err } + common.SysLog("node online: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP + " agent_version=" + node.AgentVersion) return node, nil } if err = model.DB.Model(node).Select("name", "ip", "agent_version", "nginx_version", "status", "current_version", "last_seen_at", "last_error").Updates(node).Error; err != nil { return nil, err } + if previousStatus != NodeStatusOnline { + common.SysLog("node online: node_id=" + node.NodeID + " name=" + node.Name + " ip=" + node.IP + " agent_version=" + node.AgentVersion) + } else if previousIP != node.IP || previousVersion != node.CurrentVersion || previousAgentVersion != node.AgentVersion { + common.SysLog("node metadata updated: node_id=" + node.NodeID + " ip=" + previousIP + "->" + node.IP + " agent_version=" + previousAgentVersion + "->" + node.AgentVersion + " current_version=" + previousVersion + "->" + node.CurrentVersion) + } return node, nil }