mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-10-25 13:10:03 +00:00
3a4a3fd62b
Expose confined, identity-bound filesystem observations through the shared resource pipeline so investigations can distinguish an exhausted container mount from unrelated host capacity. Keep unavailable measurements explicit. Isolate alert-history reads from durable writes and reuse one chronological fold across polling. Catch up through bounded durable event IDs so simultaneous readers do not replay every retained snapshot. Retain expired actions when investigation outcomes move back to needs attention, and keep attached Assistant context focused. Record live storage diagnosis, healthy and dependency controls, approved and rejected Docker outcomes, source-bound browser proof and exact test limits. Missing-access continuity, VM dispatch completion and remaining Assistant orchestration defects stay open in the redesign plan.
373 lines
17 KiB
Go
373 lines
17 KiB
Go
package dockeragent
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/rcourtman/pulse-go-rewrite/pkg/agents/filesystem"
|
|
hostagent "github.com/rcourtman/pulse-go-rewrite/pkg/agents/host"
|
|
)
|
|
|
|
type MemoryMetric = hostagent.MemoryMetric
|
|
type Disk = hostagent.Disk
|
|
type NetworkInterface = hostagent.NetworkInterface
|
|
|
|
// Report represents a single heartbeat from the Docker agent to Pulse.
|
|
type Report struct {
|
|
Agent AgentInfo `json:"agent"`
|
|
Host HostInfo `json:"host"`
|
|
// InventoryComplete is nil for legacy full reports, true for an explicit
|
|
// complete inventory, and false for a liveness/status-only helper report.
|
|
// Servers must preserve the last complete inventory when it is false.
|
|
InventoryComplete *bool `json:"inventoryComplete,omitempty"`
|
|
Containers []Container `json:"containers"`
|
|
Images []Image `json:"images,omitempty"`
|
|
Volumes []Volume `json:"volumes,omitempty"`
|
|
Networks []Network `json:"networks,omitempty"`
|
|
Services []Service `json:"services,omitempty"`
|
|
Tasks []Task `json:"tasks,omitempty"`
|
|
Nodes []Node `json:"nodes,omitempty"`
|
|
Secrets []Secret `json:"secrets,omitempty"`
|
|
Configs []Config `json:"configs,omitempty"`
|
|
StorageUsage *StorageUsage `json:"storageUsage,omitempty"`
|
|
Timestamp time.Time `json:"timestamp"`
|
|
SequenceID string `json:"sequenceId,omitempty"`
|
|
}
|
|
|
|
// AgentInfo describes the reporting agent instance.
|
|
type AgentInfo struct {
|
|
ID string `json:"id"`
|
|
Version string `json:"version"`
|
|
Type string `json:"type,omitempty"` // "unified", "host", or "docker" - empty means legacy
|
|
IntervalSeconds int `json:"intervalSeconds"`
|
|
Modules []hostagent.ModuleStatus `json:"modules,omitempty"`
|
|
}
|
|
|
|
// HostInfo contains metadata about the Docker host where the agent runs.
|
|
type HostInfo struct {
|
|
Hostname string `json:"hostname"`
|
|
Name string `json:"name,omitempty"`
|
|
MachineID string `json:"machineId,omitempty"`
|
|
OS string `json:"os,omitempty"`
|
|
Runtime string `json:"runtime,omitempty"`
|
|
RuntimeVersion string `json:"runtimeVersion,omitempty"`
|
|
CollectionMode string `json:"collectionMode,omitempty"`
|
|
KernelVersion string `json:"kernelVersion,omitempty"`
|
|
Architecture string `json:"architecture,omitempty"`
|
|
DockerVersion string `json:"dockerVersion,omitempty"`
|
|
TotalCPU int `json:"totalCpu,omitempty"`
|
|
TotalMemoryBytes int64 `json:"totalMemoryBytes,omitempty"`
|
|
UptimeSeconds int64 `json:"uptimeSeconds,omitempty"`
|
|
Swarm *SwarmInfo `json:"swarm,omitempty"`
|
|
CPUUsagePercent float64 `json:"cpuUsagePercent,omitempty"`
|
|
LoadAverage []float64 `json:"loadAverage,omitempty"`
|
|
Memory MemoryMetric `json:"memory,omitempty"`
|
|
Disks []Disk `json:"disks,omitempty"`
|
|
Network []NetworkInterface `json:"network,omitempty"`
|
|
Security *HostSecurityInfo `json:"security,omitempty"`
|
|
}
|
|
|
|
const CollectionModeTypedHelperSummary = "typed-helper-summary"
|
|
|
|
// HostSecurityInfo captures container-runtime security posture data reported by the agent.
|
|
type HostSecurityInfo struct {
|
|
AuthorizationPlugins []string `json:"authorizationPlugins,omitempty"`
|
|
}
|
|
|
|
// Container captures the runtime state for a Docker container at report time.
|
|
type Container struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Image string `json:"image"`
|
|
ImageDigest string `json:"imageDigest,omitempty"` // Current image digest for update detection
|
|
CreatedAt time.Time `json:"createdAt"`
|
|
State string `json:"state"`
|
|
Status string `json:"status"`
|
|
Health string `json:"health,omitempty"`
|
|
HealthcheckTargets []string `json:"healthcheckTargets,omitempty"`
|
|
CPUPercent float64 `json:"cpuPercent"`
|
|
MemoryUsageBytes int64 `json:"memoryUsageBytes"`
|
|
MemoryLimitBytes int64 `json:"memoryLimitBytes"`
|
|
MemoryPercent float64 `json:"memoryPercent"`
|
|
UptimeSeconds int64 `json:"uptimeSeconds"`
|
|
RestartCount int `json:"restartCount"`
|
|
ExitCode int `json:"exitCode"`
|
|
OOMKilled *bool `json:"oomKilled,omitempty"`
|
|
StartedAt *time.Time `json:"startedAt,omitempty"`
|
|
FinishedAt *time.Time `json:"finishedAt,omitempty"`
|
|
Ports []ContainerPort `json:"ports,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
Env []string `json:"env,omitempty"`
|
|
Networks []ContainerNetwork `json:"networks,omitempty"`
|
|
NetworkRXBytes uint64 `json:"networkRxBytes,omitempty"`
|
|
NetworkTXBytes uint64 `json:"networkTxBytes,omitempty"`
|
|
WritableLayerBytes int64 `json:"writableLayerBytes,omitempty"`
|
|
RootFilesystemBytes int64 `json:"rootFilesystemBytes,omitempty"`
|
|
BlockIO *ContainerBlockIO `json:"blockIo,omitempty"`
|
|
Mounts []ContainerMount `json:"mounts,omitempty"`
|
|
Filesystems []filesystem.Observation `json:"filesystems,omitempty"`
|
|
Podman *PodmanContainer `json:"podman,omitempty"`
|
|
UpdateStatus *UpdateStatus `json:"updateStatus,omitempty"` // Image update detection status
|
|
}
|
|
|
|
// ContainerPort tracks an exposed container port mapping.
|
|
type ContainerPort struct {
|
|
PrivatePort int `json:"privatePort"`
|
|
PublicPort int `json:"publicPort,omitempty"`
|
|
Protocol string `json:"protocol"`
|
|
IP string `json:"ip,omitempty"`
|
|
}
|
|
|
|
// ContainerNetwork summarises container network addresses by network name.
|
|
type ContainerNetwork struct {
|
|
Name string `json:"name"`
|
|
IPv4 string `json:"ipv4,omitempty"`
|
|
IPv6 string `json:"ipv6,omitempty"`
|
|
}
|
|
|
|
// ContainerBlockIO summarises high-level block I/O metrics for a container.
|
|
type ContainerBlockIO struct {
|
|
ReadBytes uint64 `json:"readBytes,omitempty"`
|
|
WriteBytes uint64 `json:"writeBytes,omitempty"`
|
|
ReadBytesPresent *bool `json:"readBytesPresent,omitempty"`
|
|
WriteBytesPresent *bool `json:"writeBytesPresent,omitempty"`
|
|
}
|
|
|
|
// CounterPresence distinguishes observed zero from an omitted direction. Older
|
|
// agents omitted zero values and presence, so only their positive counters are
|
|
// unambiguous. New agents carry explicit presence independently of counter value.
|
|
func (io *ContainerBlockIO) CounterPresence() (read, write bool) {
|
|
if io == nil {
|
|
return false, false
|
|
}
|
|
read, write = io.ReadBytes > 0, io.WriteBytes > 0
|
|
if io.ReadBytesPresent != nil {
|
|
read = *io.ReadBytesPresent
|
|
}
|
|
if io.WriteBytesPresent != nil {
|
|
write = *io.WriteBytesPresent
|
|
}
|
|
return
|
|
}
|
|
|
|
// PodmanContainer carries metadata extracted from Podman-specific annotations.
|
|
type PodmanContainer struct {
|
|
PodName string `json:"podName,omitempty"`
|
|
PodID string `json:"podId,omitempty"`
|
|
Infra bool `json:"infra,omitempty"`
|
|
ComposeProject string `json:"composeProject,omitempty"`
|
|
ComposeService string `json:"composeService,omitempty"`
|
|
ComposeWorkdir string `json:"composeWorkdir,omitempty"`
|
|
ComposeConfig string `json:"composeConfigHash,omitempty"`
|
|
AutoUpdatePolicy string `json:"autoUpdatePolicy,omitempty"`
|
|
AutoUpdateRestart string `json:"autoUpdateRestart,omitempty"`
|
|
UserNS string `json:"userNamespace,omitempty"`
|
|
}
|
|
|
|
// ContainerMount describes a mount point exposed inside a container.
|
|
type ContainerMount struct {
|
|
Type string `json:"type,omitempty"`
|
|
Source string `json:"source,omitempty"`
|
|
Destination string `json:"destination,omitempty"`
|
|
Mode string `json:"mode,omitempty"`
|
|
RW bool `json:"rw"`
|
|
Propagation string `json:"propagation,omitempty"`
|
|
Name string `json:"name,omitempty"`
|
|
Driver string `json:"driver,omitempty"`
|
|
}
|
|
|
|
// UpdateStatus tracks the image update status for a container.
|
|
type UpdateStatus struct {
|
|
UpdateAvailable bool `json:"updateAvailable"`
|
|
CurrentDigest string `json:"currentDigest,omitempty"`
|
|
LatestDigest string `json:"latestDigest,omitempty"`
|
|
LastChecked time.Time `json:"lastChecked"`
|
|
Error string `json:"error,omitempty"` // e.g., "rate limited", "auth required"
|
|
}
|
|
|
|
// Image summarises a local image available through the Docker-compatible API.
|
|
type Image struct {
|
|
ID string `json:"id"`
|
|
RepoTags []string `json:"repoTags,omitempty"`
|
|
RepoDigests []string `json:"repoDigests,omitempty"`
|
|
SizeBytes int64 `json:"sizeBytes,omitempty"`
|
|
SharedSizeBytes int64 `json:"sharedSizeBytes,omitempty"`
|
|
Containers int64 `json:"containers,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
}
|
|
|
|
// Volume summarises a local volume available through the Docker-compatible API.
|
|
type Volume struct {
|
|
Name string `json:"name"`
|
|
Driver string `json:"driver,omitempty"`
|
|
Mountpoint string `json:"mountpoint,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
CreatedAt string `json:"createdAt,omitempty"`
|
|
SizeBytes int64 `json:"sizeBytes,omitempty"`
|
|
RefCount int64 `json:"refCount,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
Options map[string]string `json:"options,omitempty"`
|
|
}
|
|
|
|
// Network summarises a local network available through the Docker-compatible API.
|
|
type Network struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Driver string `json:"driver,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
EnableIPv4 bool `json:"enableIpv4,omitempty"`
|
|
EnableIPv6 bool `json:"enableIpv6,omitempty"`
|
|
Internal bool `json:"internal,omitempty"`
|
|
Attachable bool `json:"attachable,omitempty"`
|
|
Ingress bool `json:"ingress,omitempty"`
|
|
ConfigOnly bool `json:"configOnly,omitempty"`
|
|
Subnets []NetworkSubnet `json:"subnets,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
Options map[string]string `json:"options,omitempty"`
|
|
}
|
|
|
|
// NetworkSubnet summarises a network IPAM subnet.
|
|
type NetworkSubnet struct {
|
|
Subnet string `json:"subnet,omitempty"`
|
|
Gateway string `json:"gateway,omitempty"`
|
|
}
|
|
|
|
// StorageUsage summarises the Docker-compatible /system/df API buckets.
|
|
type StorageUsage struct {
|
|
Images StorageUsageBucket `json:"images,omitempty"`
|
|
Containers StorageUsageBucket `json:"containers,omitempty"`
|
|
Volumes StorageUsageBucket `json:"volumes,omitempty"`
|
|
BuildCache StorageUsageBucket `json:"buildCache,omitempty"`
|
|
}
|
|
|
|
// StorageUsageBucket captures one /system/df resource family.
|
|
type StorageUsageBucket struct {
|
|
TotalCount int64 `json:"totalCount,omitempty"`
|
|
ActiveCount int64 `json:"activeCount,omitempty"`
|
|
TotalSizeBytes int64 `json:"totalSizeBytes,omitempty"`
|
|
ReclaimableBytes int64 `json:"reclaimableBytes,omitempty"`
|
|
}
|
|
|
|
// AgentKey returns the stable identifier for a reporting agent.
|
|
func (r Report) AgentKey() string {
|
|
if r.Agent.ID != "" {
|
|
return r.Agent.ID
|
|
}
|
|
if r.Host.MachineID != "" {
|
|
return r.Host.MachineID
|
|
}
|
|
return r.Host.Hostname
|
|
}
|
|
|
|
// SwarmInfo captures metadata about the Docker Swarm state for the reporting node.
|
|
type SwarmInfo struct {
|
|
NodeID string `json:"nodeId,omitempty"`
|
|
NodeRole string `json:"nodeRole,omitempty"`
|
|
LocalState string `json:"localState,omitempty"`
|
|
ControlAvailable bool `json:"controlAvailable,omitempty"`
|
|
ClusterID string `json:"clusterId,omitempty"`
|
|
ClusterName string `json:"clusterName,omitempty"`
|
|
Scope string `json:"scope,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// Service summarises a Docker Swarm service and its aggregate status.
|
|
type Service struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Stack string `json:"stack,omitempty"`
|
|
Image string `json:"image,omitempty"`
|
|
Mode string `json:"mode,omitempty"`
|
|
DesiredTasks int `json:"desiredTasks,omitempty"`
|
|
RunningTasks int `json:"runningTasks,omitempty"`
|
|
CompletedTasks int `json:"completedTasks,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
EndpointPorts []ServicePort `json:"endpointPorts,omitempty"`
|
|
UpdateStatus *ServiceUpdate `json:"updateStatus,omitempty"`
|
|
CreatedAt *time.Time `json:"createdAt,omitempty"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
}
|
|
|
|
// ServicePort describes an exposed service endpoint.
|
|
type ServicePort struct {
|
|
Name string `json:"name,omitempty"`
|
|
Protocol string `json:"protocol,omitempty"`
|
|
TargetPort uint32 `json:"targetPort,omitempty"`
|
|
PublishedPort uint32 `json:"publishedPort,omitempty"`
|
|
PublishMode string `json:"publishMode,omitempty"`
|
|
}
|
|
|
|
// ServiceUpdate captures the current rolling update status for a service.
|
|
type ServiceUpdate struct {
|
|
State string `json:"state,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
|
}
|
|
|
|
// Task summarises an individual Docker Swarm task (replica).
|
|
type Task struct {
|
|
ID string `json:"id"`
|
|
ServiceID string `json:"serviceId,omitempty"`
|
|
ServiceName string `json:"serviceName,omitempty"`
|
|
Slot int `json:"slot,omitempty"`
|
|
NodeID string `json:"nodeId,omitempty"`
|
|
NodeName string `json:"nodeName,omitempty"`
|
|
DesiredState string `json:"desiredState,omitempty"`
|
|
CurrentState string `json:"currentState,omitempty"`
|
|
Error string `json:"error,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
ContainerID string `json:"containerId,omitempty"`
|
|
ContainerName string `json:"containerName,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
StartedAt *time.Time `json:"startedAt,omitempty"`
|
|
CompletedAt *time.Time `json:"completedAt,omitempty"`
|
|
}
|
|
|
|
// Node summarises a Docker Swarm node returned by the manager-side nodes API.
|
|
type Node struct {
|
|
ID string `json:"id"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
Role string `json:"role,omitempty"`
|
|
Availability string `json:"availability,omitempty"`
|
|
State string `json:"state,omitempty"`
|
|
Message string `json:"message,omitempty"`
|
|
Address string `json:"address,omitempty"`
|
|
ManagerReachability string `json:"managerReachability,omitempty"`
|
|
ManagerAddress string `json:"managerAddress,omitempty"`
|
|
Leader bool `json:"leader,omitempty"`
|
|
EngineVersion string `json:"engineVersion,omitempty"`
|
|
OS string `json:"os,omitempty"`
|
|
Architecture string `json:"architecture,omitempty"`
|
|
NanoCPUs int64 `json:"nanoCpus,omitempty"`
|
|
MemoryBytes int64 `json:"memoryBytes,omitempty"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
EngineLabels map[string]string `json:"engineLabels,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
}
|
|
|
|
// Secret summarises Docker Swarm secret metadata. Secret payload data is never
|
|
// collected or serialized in Pulse reports.
|
|
type Secret struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
DriverName string `json:"driverName,omitempty"`
|
|
TemplatingDriver string `json:"templatingDriver,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
}
|
|
|
|
// Config summarises Docker Swarm config metadata. Config payload data is never
|
|
// collected or serialized in Pulse reports.
|
|
type Config struct {
|
|
ID string `json:"id"`
|
|
Name string `json:"name"`
|
|
Labels map[string]string `json:"labels,omitempty"`
|
|
TemplatingDriver string `json:"templatingDriver,omitempty"`
|
|
CreatedAt time.Time `json:"createdAt,omitempty"`
|
|
UpdatedAt *time.Time `json:"updatedAt,omitempty"`
|
|
}
|