package docker import ( "context" "fmt" "time" "github.com/moby/moby/client" ) // SystemStats is a host-level snapshot combining daemon capacity // (NCPU, memory total) with container counts and disk usage broken down // by category. Workload CPU/memory utilization is aggregated from // per-container samples by the stats collector, not here. type SystemStats struct { Timestamp time.Time `json:"timestamp"` // Capacity from Docker daemon. NCPU int `json:"ncpu"` MemoryTotal int64 `json:"memory_total"` // Container/image counts. Containers int `json:"containers"` Running int `json:"running"` Paused int `json:"paused"` Stopped int `json:"stopped"` Images int `json:"images"` // Disk usage by category (bytes). DiskImagesBytes int64 `json:"disk_images_bytes"` DiskContainersBytes int64 `json:"disk_containers_bytes"` DiskVolumesBytes int64 `json:"disk_volumes_bytes"` DiskBuildCacheBytes int64 `json:"disk_build_cache_bytes"` // Reclaimable disk space by category (bytes). DiskImagesReclaimable int64 `json:"disk_images_reclaimable"` DiskContainersReclaimable int64 `json:"disk_containers_reclaimable"` DiskVolumesReclaimable int64 `json:"disk_volumes_reclaimable"` DiskBuildCacheReclaimable int64 `json:"disk_build_cache_reclaimable"` // DiskTotalBytes is the sum of the category totals. DiskTotalBytes int64 `json:"disk_total_bytes"` } // GetSystemStats returns a one-shot host-level snapshot. The Info() call // and disk usage call are made in sequence. Disk usage failures do not // fail the whole call — the result degrades gracefully with zero disk fields. func (c *Client) GetSystemStats(ctx context.Context) (SystemStats, error) { info, err := c.Info(ctx) if err != nil { return SystemStats{}, fmt.Errorf("system stats: %w", err) } stats := SystemStats{ Timestamp: time.Now().UTC(), NCPU: info.NCPU, MemoryTotal: info.MemoryTotal, Containers: info.Containers, Running: info.Running, Paused: info.Paused, Stopped: info.Stopped, Images: info.Images, } du, derr := c.api.DiskUsage(ctx, client.DiskUsageOptions{ Containers: true, Images: true, Volumes: true, BuildCache: true, }) if derr == nil { stats.DiskImagesBytes = du.Images.TotalSize stats.DiskContainersBytes = du.Containers.TotalSize stats.DiskVolumesBytes = du.Volumes.TotalSize stats.DiskBuildCacheBytes = du.BuildCache.TotalSize stats.DiskImagesReclaimable = du.Images.Reclaimable stats.DiskContainersReclaimable = du.Containers.Reclaimable stats.DiskVolumesReclaimable = du.Volumes.Reclaimable stats.DiskBuildCacheReclaimable = du.BuildCache.Reclaimable stats.DiskTotalBytes = stats.DiskImagesBytes + stats.DiskContainersBytes + stats.DiskVolumesBytes + stats.DiskBuildCacheBytes } return stats, nil }