Clarify process CPU and improve Docker actions

This commit is contained in:
Maxim
2026-08-21 21:58:00 +03:00
parent f6498a2fc1
commit f764195ba5
6 changed files with 113 additions and 32 deletions

View File

@@ -41,18 +41,22 @@ type agentConfig struct {
}
type AgentContainer struct {
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
State string `json:"state"`
Status string `json:"status"`
Health string `json:"health,omitempty"`
Restarts int64 `json:"restarts"`
CPUPercent float64 `json:"cpuPercent"`
MemoryUsage uint64 `json:"memoryUsage"`
Ports []string `json:"ports,omitempty"`
Compose bool `json:"compose"`
UpdateAvailable bool `json:"updateAvailable"`
ID string `json:"id"`
Name string `json:"name"`
Image string `json:"image"`
State string `json:"state"`
Status string `json:"status"`
Health string `json:"health,omitempty"`
Restarts int64 `json:"restarts"`
CPUPercent float64 `json:"cpuPercent"`
MemoryUsage uint64 `json:"memoryUsage"`
Ports []string `json:"ports,omitempty"`
Compose bool `json:"compose"`
UpdateAvailable bool `json:"updateAvailable"`
CurrentVersion string `json:"currentVersion,omitempty"`
CurrentDigest string `json:"currentDigest,omitempty"`
AvailableVersion string `json:"availableVersion,omitempty"`
AvailableDigest string `json:"availableDigest,omitempty"`
}
type AgentInterface struct {
@@ -99,6 +103,7 @@ type AgentReport struct {
UptimeSeconds int64 `json:"uptimeSeconds"`
Load1 float64 `json:"load1"`
CPUPercent float64 `json:"cpuPercent"`
CPUCount int `json:"cpuCount"`
MemoryTotal uint64 `json:"memoryTotal"`
MemoryUsed uint64 `json:"memoryUsed"`
SwapTotal uint64 `json:"swapTotal"`
@@ -394,7 +399,9 @@ func runGuestAgent(opts agentOptions) error {
if requestErr != nil {
fmt.Fprintf(os.Stderr, "agent: %v\n", requestErr)
}
time.Sleep(10 * time.Second)
// Короткий интервал нужен для интерактивных команд из панели. Тяжёлый
// системный инвентарь отдельно кэшируется и не запускается каждые 3 секунды.
time.Sleep(3 * time.Second)
}
}
@@ -530,7 +537,7 @@ func saveAgentConfig(path string, c agentConfig) error {
func collectAgentReport() AgentReport {
hostname, _ := os.Hostname()
machineID, _ := os.ReadFile("/etc/machine-id")
report := AgentReport{Hostname: hostname, MachineID: strings.TrimSpace(string(machineID)), Version: version, OS: runtime.GOOS, Arch: runtime.GOARCH, CollectedAt: time.Now().Unix()}
report := AgentReport{Hostname: hostname, MachineID: strings.TrimSpace(string(machineID)), Version: version, OS: runtime.GOOS, Arch: runtime.GOARCH, CPUCount: runtime.NumCPU(), CollectedAt: time.Now().Unix()}
if data, err := os.ReadFile("/proc/uptime"); err == nil {
fields := strings.Fields(string(data))
if len(fields) > 0 {
@@ -929,14 +936,10 @@ func collectDockerContainers() ([]AgentContainer, string) {
item.Health = detail.State.Health.Status
}
item.Compose = detail.Config.Labels["com.docker.compose.service"] != ""
imageResponse, imageErr := client.Get("http://docker/images/" + url.PathEscape(v.Image) + "/json")
if imageErr == nil {
var imageDetail struct{ ID string }
if json.NewDecoder(imageResponse.Body).Decode(&imageDetail) == nil && detail.Image != "" && imageDetail.ID != "" {
item.UpdateAvailable = detail.Image != imageDetail.ID
}
imageResponse.Body.Close()
}
item.CurrentVersion, item.CurrentDigest, _ = inspectDockerImage(client, detail.Image, v.Image)
var availableID string
item.AvailableVersion, item.AvailableDigest, availableID = inspectDockerImage(client, v.Image, v.Image)
item.UpdateAvailable = detail.Image != "" && availableID != "" && detail.Image != availableID
}
inspect.Body.Close()
}
@@ -987,6 +990,46 @@ func collectDockerContainers() ([]AgentContainer, string) {
}
return result, ""
}
func inspectDockerImage(client *http.Client, reference, fallback string) (string, string, string) {
if reference == "" {
return "", "", ""
}
response, err := client.Get("http://docker/images/" + url.PathEscape(reference) + "/json")
if err != nil {
return "", "", ""
}
defer response.Body.Close()
var detail struct {
ID string
RepoTags []string
RepoDigests []string
Config struct{ Labels map[string]string }
}
if response.StatusCode != http.StatusOK || json.NewDecoder(response.Body).Decode(&detail) != nil {
return "", "", ""
}
version := firstNonEmpty(detail.Config.Labels["org.opencontainers.image.version"], detail.Config.Labels["version"], firstString(detail.RepoTags), fallback, shortDockerDigest(detail.ID))
digest := ""
if len(detail.RepoDigests) > 0 {
digest = detail.RepoDigests[0]
if _, value, found := strings.Cut(digest, "@"); found {
digest = value
}
}
if digest == "" {
digest = detail.ID
}
return version, shortDockerDigest(digest), detail.ID
}
func shortDockerDigest(value string) string {
value = strings.TrimPrefix(value, "sha256:")
if len(value) > 12 {
value = value[:12]
}
return value
}
func firstString(v []string) string {
if len(v) > 0 {
return v[0]