Pulse/internal/api/types.go
2026-04-10 17:32:30 +01:00

371 lines
13 KiB
Go

package api
// Common response types for API endpoints
// HealthResponse represents the health check response
type HealthResponse struct {
Status string `json:"status"`
Timestamp int64 `json:"timestamp"`
Uptime float64 `json:"uptime"`
ProxyInstallScriptAvailable bool `json:"proxyInstallScriptAvailable,omitempty"`
DevModeSSH bool `json:"devModeSSH,omitempty"` // DEV/TEST ONLY: SSH keys allowed in containers
Dependencies map[string]bool `json:"dependencies"`
}
func EmptyHealthResponse() HealthResponse {
return HealthResponse{}.NormalizeCollections()
}
func (r HealthResponse) NormalizeCollections() HealthResponse {
if r.Dependencies == nil {
r.Dependencies = map[string]bool{}
}
return r
}
// VersionResponse represents version information
type VersionResponse struct {
Version string `json:"version"`
BuildTime string `json:"buildTime,omitempty"`
Build string `json:"build,omitempty"`
GoVersion string `json:"goVersion,omitempty"`
Runtime string `json:"runtime,omitempty"`
Channel string `json:"channel,omitempty"`
IsDocker bool `json:"isDocker"`
IsSourceBuild bool `json:"isSourceBuild"`
IsDevelopment bool `json:"isDevelopment"`
DeploymentType string `json:"deploymentType,omitempty"`
UpdateAvailable bool `json:"updateAvailable"`
LatestVersion string `json:"latestVersion,omitempty"`
Containerized bool `json:"containerized"`
ContainerID string `json:"containerId,omitempty"`
}
// ChartResponse represents chart data
type ChartResponse struct {
ChartData map[string]VMChartData `json:"data"`
NodeData map[string]NodeChartData `json:"nodeData"`
StorageData map[string]StorageChartData `json:"storageData"`
DockerData map[string]VMChartData `json:"dockerData"` // Docker container metrics (keyed by container ID)
DockerHostData map[string]VMChartData `json:"dockerHostData"` // Docker host metrics (keyed by host ID)
AgentData map[string]VMChartData `json:"agentData"` // Unified agent metrics (keyed by agent ID)
GuestTypes map[string]string `json:"guestTypes"` // Maps guest ID to type ("vm", "system-container", "k8s")
Timestamp int64 `json:"timestamp"`
Stats ChartStats `json:"stats"`
}
func EmptyChartResponse() ChartResponse {
return ChartResponse{}.NormalizeCollections()
}
func (r ChartResponse) NormalizeCollections() ChartResponse {
if r.ChartData == nil {
r.ChartData = map[string]VMChartData{}
}
if r.NodeData == nil {
r.NodeData = map[string]NodeChartData{}
}
if r.StorageData == nil {
r.StorageData = map[string]StorageChartData{}
}
if r.DockerData == nil {
r.DockerData = map[string]VMChartData{}
}
if r.DockerHostData == nil {
r.DockerHostData = map[string]VMChartData{}
}
if r.AgentData == nil {
r.AgentData = map[string]VMChartData{}
}
if r.GuestTypes == nil {
r.GuestTypes = map[string]string{}
}
r.Stats = r.Stats.NormalizeCollections()
return r
}
// InfrastructureChartsResponse is a lightweight variant of ChartResponse used by
// infra-only clients (Infrastructure summary sparklines, prewarm caches).
// It avoids the heavy guest/storage chart payload and associated compute.
type InfrastructureChartsResponse struct {
NodeData map[string]NodeChartData `json:"nodeData"`
DockerHostData map[string]VMChartData `json:"dockerHostData"` // Docker host metrics (keyed by host ID)
AgentData map[string]VMChartData `json:"agentData"` // Unified agent metrics (keyed by agent ID)
Timestamp int64 `json:"timestamp"`
Stats ChartStats `json:"stats"`
}
func EmptyInfrastructureChartsResponse() InfrastructureChartsResponse {
return InfrastructureChartsResponse{}.NormalizeCollections()
}
func (r InfrastructureChartsResponse) NormalizeCollections() InfrastructureChartsResponse {
if r.NodeData == nil {
r.NodeData = map[string]NodeChartData{}
}
if r.DockerHostData == nil {
r.DockerHostData = map[string]VMChartData{}
}
if r.AgentData == nil {
r.AgentData = map[string]VMChartData{}
}
r.Stats = r.Stats.NormalizeCollections()
return r
}
// WorkloadChartsResponse is a lightweight chart payload used by Workloads
// summary sparklines. It intentionally excludes infrastructure/storage series.
type WorkloadChartsResponse struct {
ChartData map[string]VMChartData `json:"data"` // Workload metrics keyed by workload ID
DockerData map[string]VMChartData `json:"dockerData"` // Docker container metrics keyed by container ID
GuestTypes map[string]string `json:"guestTypes"` // Maps guest ID to type ("vm", "system-container", "k8s")
Timestamp int64 `json:"timestamp"` // Unix timestamp in milliseconds
Stats ChartStats `json:"stats"` // Includes pointCounts + source hints
}
func EmptyWorkloadChartsResponse() WorkloadChartsResponse {
return WorkloadChartsResponse{}.NormalizeCollections()
}
func (r WorkloadChartsResponse) NormalizeCollections() WorkloadChartsResponse {
if r.ChartData == nil {
r.ChartData = map[string]VMChartData{}
}
if r.DockerData == nil {
r.DockerData = map[string]VMChartData{}
}
if r.GuestTypes == nil {
r.GuestTypes = map[string]string{}
}
r.Stats = r.Stats.NormalizeCollections()
return r
}
// WorkloadsSummaryMetricData captures aggregate workload trend lines for a
// single metric (median and p95 across workloads).
type WorkloadsSummaryMetricData struct {
P50 []MetricPoint `json:"p50"`
P95 []MetricPoint `json:"p95"`
}
func (m WorkloadsSummaryMetricData) NormalizeCollections() WorkloadsSummaryMetricData {
if m.P50 == nil {
m.P50 = []MetricPoint{}
}
if m.P95 == nil {
m.P95 = []MetricPoint{}
}
return m
}
// WorkloadsGuestCounts captures workload counts used by the workloads summary
// cards for quick context.
type WorkloadsGuestCounts struct {
Total int `json:"total"`
Running int `json:"running"`
Stopped int `json:"stopped"`
}
// WorkloadsSummaryContributor identifies a high-impact workload for a metric.
type WorkloadsSummaryContributor struct {
ID string `json:"id"`
Name string `json:"name"`
Value float64 `json:"value"`
}
// WorkloadsSummaryContributors groups top contributors by metric.
type WorkloadsSummaryContributors struct {
CPU []WorkloadsSummaryContributor `json:"cpu"`
Memory []WorkloadsSummaryContributor `json:"memory"`
Disk []WorkloadsSummaryContributor `json:"disk"`
Network []WorkloadsSummaryContributor `json:"network"`
}
func (c WorkloadsSummaryContributors) NormalizeCollections() WorkloadsSummaryContributors {
if c.CPU == nil {
c.CPU = []WorkloadsSummaryContributor{}
}
if c.Memory == nil {
c.Memory = []WorkloadsSummaryContributor{}
}
if c.Disk == nil {
c.Disk = []WorkloadsSummaryContributor{}
}
if c.Network == nil {
c.Network = []WorkloadsSummaryContributor{}
}
return c
}
// WorkloadsSummaryBlastRadius describes how concentrated each metric pressure is.
type WorkloadsSummaryBlastRadius struct {
Scope string `json:"scope"` // idle, concentrated, mixed, distributed
Top3Share float64 `json:"top3Share"`
ActiveWorkloads int `json:"activeWorkloads"`
}
// WorkloadsSummaryBlastRadiusGroup groups blast-radius insights by metric.
type WorkloadsSummaryBlastRadiusGroup struct {
CPU WorkloadsSummaryBlastRadius `json:"cpu"`
Memory WorkloadsSummaryBlastRadius `json:"memory"`
Disk WorkloadsSummaryBlastRadius `json:"disk"`
Network WorkloadsSummaryBlastRadius `json:"network"`
}
// WorkloadsSummaryChartsResponse is a compact response for workloads top-card
// sparklines. It avoids returning per-workload time series.
type WorkloadsSummaryChartsResponse struct {
CPU WorkloadsSummaryMetricData `json:"cpu"`
Memory WorkloadsSummaryMetricData `json:"memory"`
Disk WorkloadsSummaryMetricData `json:"disk"`
Network WorkloadsSummaryMetricData `json:"network"`
GuestCounts WorkloadsGuestCounts `json:"guestCounts"`
TopContributors WorkloadsSummaryContributors `json:"topContributors"`
BlastRadius WorkloadsSummaryBlastRadiusGroup `json:"blastRadius"`
Timestamp int64 `json:"timestamp"`
Stats ChartStats `json:"stats"`
}
func EmptyWorkloadsSummaryChartsResponse() WorkloadsSummaryChartsResponse {
return WorkloadsSummaryChartsResponse{}.NormalizeCollections()
}
func (r WorkloadsSummaryChartsResponse) NormalizeCollections() WorkloadsSummaryChartsResponse {
r.CPU = r.CPU.NormalizeCollections()
r.Memory = r.Memory.NormalizeCollections()
r.Disk = r.Disk.NormalizeCollections()
r.Network = r.Network.NormalizeCollections()
r.TopContributors = r.TopContributors.NormalizeCollections()
r.Stats = r.Stats.NormalizeCollections()
return r
}
// StorageSummaryTrendResponse is a compact response for the dashboard storage
// card. It intentionally avoids returning per-pool and per-disk series.
type StorageSummaryTrendResponse struct {
Capacity []MetricPoint `json:"capacity"`
Timestamp int64 `json:"timestamp"`
Stats ChartStats `json:"stats"`
}
func EmptyStorageSummaryTrendResponse() StorageSummaryTrendResponse {
return StorageSummaryTrendResponse{}.NormalizeCollections()
}
func (r StorageSummaryTrendResponse) NormalizeCollections() StorageSummaryTrendResponse {
if r.Capacity == nil {
r.Capacity = []MetricPoint{}
}
r.Stats = r.Stats.NormalizeCollections()
return r
}
// ChartStats represents chart statistics
type ChartStats struct {
OldestDataTimestamp int64 `json:"oldestDataTimestamp"`
Range string `json:"range,omitempty"`
RangeSeconds int64 `json:"rangeSeconds,omitempty"`
MetricsStoreEnabled bool `json:"metricsStoreEnabled"`
PrimarySourceHint string `json:"primarySourceHint,omitempty"`
InMemoryThresholdSecs int64 `json:"inMemoryThresholdSecs,omitempty"`
PointCounts ChartPointCounts `json:"pointCounts,omitempty"`
}
func (s ChartStats) NormalizeCollections() ChartStats {
return s
}
// ChartPointCounts summarizes how many points were returned in /api/charts.
type ChartPointCounts struct {
Total int `json:"total,omitempty"`
Guests int `json:"guests,omitempty"`
Nodes int `json:"nodes,omitempty"`
Storage int `json:"storage,omitempty"`
DockerContainers int `json:"dockerContainers,omitempty"`
DockerHosts int `json:"dockerHosts,omitempty"`
Agents int `json:"agents,omitempty"`
}
// VMChartData represents chart data for a VM
type VMChartData map[string][]MetricPoint
// NodeChartData represents chart data for a node
type NodeChartData map[string][]MetricPoint
// StorageChartData represents chart data for storage
type StorageChartData map[string][]MetricPoint
// StorageChartsResponse represents storage charts API response.
// It includes both pool-level capacity metrics and physical disk metrics.
type StorageChartsResponse struct {
Pools map[string]StoragePoolChartData `json:"pools"`
Disks map[string]StorageDiskChartData `json:"disks"`
Stats ChartStats `json:"stats"`
}
func EmptyStorageChartsResponse() StorageChartsResponse {
return StorageChartsResponse{}.NormalizeCollections()
}
func (r StorageChartsResponse) NormalizeCollections() StorageChartsResponse {
if r.Pools == nil {
r.Pools = map[string]StoragePoolChartData{}
}
if r.Disks == nil {
r.Disks = map[string]StorageDiskChartData{}
}
for key, pool := range r.Pools {
r.Pools[key] = pool.NormalizeCollections()
}
for key, disk := range r.Disks {
r.Disks[key] = disk.NormalizeCollections()
}
r.Stats = r.Stats.NormalizeCollections()
return r
}
// StoragePoolChartData holds per-pool capacity time-series.
type StoragePoolChartData struct {
Name string `json:"name"`
Usage []MetricPoint `json:"usage"`
Used []MetricPoint `json:"used"`
Avail []MetricPoint `json:"avail"`
}
func (d StoragePoolChartData) NormalizeCollections() StoragePoolChartData {
if d.Usage == nil {
d.Usage = []MetricPoint{}
}
if d.Used == nil {
d.Used = []MetricPoint{}
}
if d.Avail == nil {
d.Avail = []MetricPoint{}
}
return d
}
// StorageDiskChartData holds per-disk temperature time-series.
type StorageDiskChartData struct {
Name string `json:"name"`
Node string `json:"node"`
Temperature []MetricPoint `json:"temperature"`
}
func (d StorageDiskChartData) NormalizeCollections() StorageDiskChartData {
if d.Temperature == nil {
d.Temperature = []MetricPoint{}
}
return d
}
// MetricPoint represents a single metric data point
type MetricPoint struct {
Timestamp int64 `json:"timestamp"`
Value float64 `json:"value"`
}
// AgentVersionResponse represents Docker agent version information
type AgentVersionResponse struct {
Version string `json:"version"`
}