package app import ( "strings" "time" ) func transportRuntimeSnapshot(it TransportClient, now time.Time) TransportClientRuntime { rt := it.Runtime if strings.TrimSpace(rt.Backend) == "" { rt.Backend = selectTransportBackend(it).ID() } if len(rt.AllowedActions) == 0 { rt.AllowedActions = []string{"provision", "start", "stop", "restart"} } if rt.Metrics.Restarts < 0 { rt.Metrics.Restarts = 0 } if rt.Metrics.StateChanges < 0 { rt.Metrics.StateChanges = 0 } if strings.TrimSpace(rt.LastError.Message) == "" && strings.TrimSpace(it.Health.LastError) != "" { rt.LastError = TransportClientError{ Code: "BACKEND_RUNTIME_ERROR", Message: strings.TrimSpace(it.Health.LastError), Retryable: true, At: strings.TrimSpace(it.Health.LastCheck), } } rt.Metrics.UptimeSec = transportUptimeSec(it.Status, rt.StartedAt, now) return rt } func normalizeTransportRuntimeStored(rt TransportClientRuntime, kind TransportClientKind, cfg map[string]any) (TransportClientRuntime, bool) { changed := false if strings.TrimSpace(rt.Backend) == "" { rt.Backend = selectTransportBackend(TransportClient{Kind: kind, Config: cfg}).ID() changed = true } wantActions := []string{"provision", "start", "stop", "restart"} if !equalStringSlices(rt.AllowedActions, wantActions) { rt.AllowedActions = append([]string(nil), wantActions...) changed = true } if rt.Metrics.Restarts < 0 { rt.Metrics.Restarts = 0 changed = true } if rt.Metrics.StateChanges < 0 { rt.Metrics.StateChanges = 0 changed = true } if rt.Metrics.UptimeSec < 0 { rt.Metrics.UptimeSec = 0 changed = true } if rt.LastExitCode < 0 { rt.LastExitCode = 0 changed = true } if strings.TrimSpace(rt.LastError.Message) == "" && strings.TrimSpace(rt.LastError.Code) != "" { rt.LastError.Code = "" changed = true } return rt, changed } func transportUptimeSec(status TransportClientStatus, startedAt string, now time.Time) int64 { if status != TransportClientUp { return 0 } ts := strings.TrimSpace(startedAt) if ts == "" { return 0 } started, err := time.Parse(time.RFC3339, ts) if err != nil { return 0 } if started.After(now) { return 0 } return int64(now.Sub(started).Seconds()) }