Documentation
¶
Overview ¶
Package model holds the plain-data types shared by the gather, detect and tool layers.
Nothing here may reference a Kubernetes client, a context, or a live API object: Snapshot must round-trip through YAML so that `argus capture` writes fixtures, tests read them back, and production runs the identical detect path. That constraint is load-bearing — see the plan.
Index ¶
- func ParseCPU(q string) int64
- func ParseMem(q string) int64
- func Wrap(s string, indent int) string
- type ConditionView
- type ContainerSpecView
- type ContainerStateView
- type ContainerView
- type EventGroup
- type Evidence
- type Finding
- type HPAView
- type LogBundle
- type LogGroup
- type NodeView
- type PDBView
- type PodView
- type ProbeView
- type ReplicaSetView
- type ServiceView
- type Severity
- type Snapshot
- type ToolHint
- type TriageGroup
- type TriageResult
- type WorkloadView
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ParseCPU ¶
ParseCPU returns a CPU quantity in millicores, or 0 if unset/unparseable. Detectors do arithmetic on these, so a bad value must degrade to "unknown", never panic.
func Wrap ¶ added in v0.1.9
Wrap reflows text to ~92 columns with the given indent.
Lives here because both the diagnosis renderer and the log renderer need it, and a copy in each is how two renderers start disagreeing about their own output. Long unwrapped lines are not cosmetic: an evidence line ran to 181 characters, which wraps unreadably in a terminal and forced horizontal scrolling everywhere else it was shown.
Types ¶
type ConditionView ¶
type ConditionView struct {
Type string `json:"type"`
Status string `json:"status"`
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
LastChangeSecsAgo int64 `json:"last_change_seconds_ago,omitempty"`
}
ConditionView is a k8s condition reduced to what a detector reads.
type ContainerSpecView ¶
type ContainerSpecView struct {
Name string `json:"name"`
Image string `json:"image"`
RequestCPU string `json:"request_cpu,omitempty"`
RequestMem string `json:"request_mem,omitempty"`
LimitCPU string `json:"limit_cpu,omitempty"`
LimitMem string `json:"limit_mem,omitempty"`
// Command is the entrypoint override. It is what exit 126/127 is usually about, and citing it
// turns "exit 127" into "your command is X and the image does not contain it".
Command []string `json:"command,omitempty"`
Args []string `json:"args,omitempty"`
EnvKeys []string `json:"env_keys,omitempty"` // keys only; values are redacted at the projection boundary
EnvFrom []string `json:"env_from,omitempty"`
Mounts []string `json:"mounts,omitempty"`
Readiness *ProbeView `json:"readiness,omitempty"`
Liveness *ProbeView `json:"liveness,omitempty"`
Startup *ProbeView `json:"startup,omitempty"`
}
ContainerSpecView is the desired shape of a container — the half of ContainerView that also appears in a ReplicaSet template, which is why it is factored out and diffable on its own.
type ContainerStateView ¶
type ContainerStateView struct {
Status string `json:"status"` // running | waiting | terminated
Reason string `json:"reason,omitempty"`
Message string `json:"message,omitempty"`
ExitCode int32 `json:"exit_code,omitempty"`
Signal int32 `json:"signal,omitempty"`
SecondsAgo int64 `json:"seconds_ago,omitempty"` // since started or finished
}
ContainerStateView flattens running/waiting/terminated into one shape.
type ContainerView ¶
type ContainerView struct {
ContainerSpecView `json:",inline"`
Ready bool `json:"ready"`
Started bool `json:"started"`
RestartCount int32 `json:"restart_count"`
State ContainerStateView `json:"state"`
LastState *ContainerStateView `json:"last_state,omitempty"`
// UsageCPU/UsageMem come from metrics.k8s.io; empty when the metrics API is unavailable.
UsageCPU string `json:"usage_cpu,omitempty"`
UsageMem string `json:"usage_mem,omitempty"`
}
ContainerView merges the container spec, its live status, and its current metrics — the three things every detector needs to correlate and which kubectl makes you fetch separately.
type EventGroup ¶
type EventGroup struct {
Type string `json:"type"` // Normal | Warning
Reason string `json:"reason"`
Message string `json:"message"` // normalized: UIDs, IPs, digests stripped
Count int32 `json:"count"` // total occurrences, respecting the apiserver's own aggregation
ObjectKind string `json:"object_kind,omitempty"`
ObjectName string `json:"object_name,omitempty"` // one example object, not the only one
// ObjectCount is how many distinct objects reported this. Forty pods reporting the same
// BackOff is one group with ObjectCount 40, which is the fact an SRE actually wants.
ObjectCount int `json:"object_count,omitempty"`
FirstSeenSecondsAgo int64 `json:"first_seen_seconds_ago"`
LastSeenSecondsAgo int64 `json:"last_seen_seconds_ago"`
}
EventGroup is a deduplicated bucket of events. A hundred BackOff events collapse to one line.
type Evidence ¶
type Evidence struct {
Source string `json:"source"` // "pod.lastState", "event", "replicaset.diff", "metrics"
Ref string `json:"ref"` // "pod/checkout-api-7d9f", "rs/checkout-api-7d9f"
Excerpt string `json:"excerpt"` // the actual value that triggered the detector
}
Evidence is a citation. Every Finding must carry at least one — a high-confidence diagnosis with nothing to check is worse than no diagnosis during an incident.
type Finding ¶
type Finding struct {
ID string `json:"id"` // "oomkill.limit-too-low"
Severity Severity `json:"severity"` // critical | warning | info
// Confidence is how sure we are the finding is real, independent of severity. Be honest: a
// detector working from partial data (see Snapshot.Degraded) must dock its own confidence.
Confidence float64 `json:"confidence"` // 0.0 - 1.0
Scope string `json:"scope"` // "workload/checkout-api", "node/gke-abc"
Title string `json:"title"` // one line
Detail string `json:"detail"` // 2-4 sentences: cause, not symptom
Evidence []Evidence `json:"evidence"` // never empty; enforced by test
NextTool *ToolHint `json:"next_tool,omitempty"`
// Suppresses lists finding IDs this one subsumes. The node detector uses it to widen scope:
// three Deployments failing on one node is one node finding, not three workload diagnoses.
Suppresses []string `json:"-"`
}
Finding is one ranked diagnosis. Flat and JSON-friendly, matching the gospect-mcp finding model.
type HPAView ¶
type HPAView struct {
Name string `json:"name"`
MinReplicas int32 `json:"min_replicas"`
MaxReplicas int32 `json:"max_replicas"`
Current int32 `json:"current_replicas"`
Desired int32 `json:"desired_replicas"`
Conditions []ConditionView `json:"conditions,omitempty"`
}
HPAView explains scale decisions that look like workload failures.
type LogBundle ¶ added in v0.1.2
type LogBundle struct {
Pod string `json:"pod"`
Container string `json:"container"`
// Previous reports whether these are the *previous* container instance's logs. On a
// crashlooping pod that is almost always what you want: the current instance is in backoff
// and has produced nothing.
Previous bool `json:"previous"`
// Reason explains why this pod, container and instance were chosen, so the selection is
// auditable rather than magic.
Reason string `json:"reason"`
Groups []LogGroup `json:"groups"`
// DroppedGroups counts distinct lines elided by the token budget. Never silent: a truncated
// log that does not say it was truncated reads as a complete one.
DroppedGroups int `json:"dropped_groups,omitempty"`
Note string `json:"note,omitempty"`
}
LogBundle is projected container output: redacted, grouped and budgeted.
Raw logs are the least structured and most dangerous thing argus emits — unbounded in size, and the one place an application can print a credential. Everything here exists to bound one of those two risks.
type LogGroup ¶ added in v0.1.2
type LogGroup struct {
Text string `json:"text"`
Count int `json:"count"`
// FirstSecondsAgo/LastSecondsAgo are zero when the log carried no usable timestamps.
FirstSecondsAgo int64 `json:"first_seconds_ago,omitempty"`
LastSecondsAgo int64 `json:"last_seconds_ago,omitempty"`
}
LogGroup is a set of log lines identical after normalization. Ten thousand identical panics collapse to one entry with a count, which is the difference between a readable diagnosis and a context window full of the same stack trace.
type NodeView ¶
type NodeView struct {
Name string `json:"name"`
Ready bool `json:"ready"`
Unschedulable bool `json:"unschedulable,omitempty"`
Conditions []ConditionView `json:"conditions,omitempty"`
Taints []string `json:"taints,omitempty"`
AllocCPU string `json:"alloc_cpu,omitempty"`
AllocMem string `json:"alloc_mem,omitempty"`
}
NodeView carries the conditions that explain workload failures the workload did not cause.
type PDBView ¶
type PDBView struct {
Name string `json:"name"`
DesiredHealthy int32 `json:"desired_healthy"`
CurrentHealthy int32 `json:"current_healthy"`
DisruptionsAllowed int32 `json:"disruptions_allowed"`
}
PDBView explains rollouts that are stuck rather than broken.
type PodView ¶
type PodView struct {
Name string `json:"name"`
Node string `json:"node,omitempty"`
Phase string `json:"phase"`
Ready bool `json:"ready"`
Labels map[string]string `json:"labels,omitempty"`
OwnerKind string `json:"owner_kind,omitempty"`
OwnerName string `json:"owner_name,omitempty"`
CreatedSecondsAgo int64 `json:"created_seconds_ago"`
// SchedulingReason/Message come from the PodScheduled condition when Phase is Pending.
SchedulingReason string `json:"scheduling_reason,omitempty"`
SchedulingMessage string `json:"scheduling_message,omitempty"`
Containers []ContainerView `json:"containers,omitempty"`
}
PodView is the projected pod. Budget: under 400 tokens serialized — enforced by test.
type ProbeView ¶
type ProbeView struct {
Kind string `json:"kind"` // http | tcp | exec | grpc
InitialDelay int32 `json:"initial_delay"`
Period int32 `json:"period"`
Timeout int32 `json:"timeout"`
FailureThreshold int32 `json:"failure_threshold"`
SuccessThreshold int32 `json:"success_threshold,omitempty"`
}
ProbeView is the probe timing a detector reasons about; the handler details do not matter to it.
func (*ProbeView) Deadline ¶
Deadline is how long the probe tolerates a slow start before the kubelet acts, in seconds. This is the number the readiness detector compares against observed startup time — computing it by hand from four separate fields is exactly the arithmetic humans get wrong at 3am.
type ReplicaSetView ¶
type ReplicaSetView struct {
Name string `json:"name"`
Revision string `json:"revision,omitempty"`
Desired int32 `json:"desired"`
Ready int32 `json:"ready"`
Available int32 `json:"available"`
Current bool `json:"current"` // matches the workload's current pod-template hash
CreatedSecondsAgo int64 `json:"created_seconds_ago"`
// Template is the projected pod template, which is what the rollout detector diffs.
Template []ContainerSpecView `json:"template,omitempty"`
}
ReplicaSetView carries enough of a ReplicaSet to diff two generations and tell which one is the current rollout target.
type ServiceView ¶
type ServiceView struct {
Name string `json:"name"`
Selector map[string]string `json:"selector,omitempty"`
Ports []string `json:"ports,omitempty"`
ReadyCount int `json:"ready_count"`
NotReadyCount int `json:"not_ready_count"`
// MatchedPods is how many pods in the namespace the selector matches at all, ready or not.
// Zero here means a label mismatch; non-zero with ReadyCount 0 means a readiness failure.
MatchedPods int `json:"matched_pods"`
}
ServiceView plus its endpoint readiness — the pair that exposes the classic silent failure where a Service selector matches nothing and `kubectl get` looks entirely healthy.
type Severity ¶
type Severity string
Severity is how bad the finding is if it is real. Ranked critical > warning > info.
type Snapshot ¶
type Snapshot struct {
Scope string `json:"scope"` // "workload/prod/checkout-api" or "cluster"
Namespace string `json:"namespace"` // empty for cluster scope
Workload *WorkloadView `json:"workload,omitempty"`
ReplicaSets []ReplicaSetView `json:"replicasets,omitempty"`
Pods []PodView `json:"pods,omitempty"`
Events []EventGroup `json:"events,omitempty"`
Services []ServiceView `json:"services,omitempty"`
Nodes []NodeView `json:"nodes,omitempty"`
HPA *HPAView `json:"hpa,omitempty"`
PDB *PDBView `json:"pdb,omitempty"`
// Degraded lists gather steps that failed or timed out. Detectors working from partial data
// must dock their confidence — see Snapshot.Missing.
Degraded []string `json:"degraded,omitempty"`
// Notes records deliberate elisions — data we chose not to keep, not data we failed to get.
// Kept separate from Degraded so that trimming rollout history never makes a detector think
// the apiserver was unreachable. Silent truncation reads as "we looked at everything".
Notes []string `json:"notes,omitempty"`
}
Snapshot is everything argus gathered about one question, projected down to an explicit allowlist of fields. Detectors are pure functions over this type.
Time is stored as *seconds ago* rather than as absolute timestamps. A fixture with absolute times silently rots: a detector asking "was this OOMKill recent?" stops firing the day after the fixture is captured, and the test still passes because no detector fires and none was expected. Relative time keeps committed fixtures meaningful forever.
type ToolHint ¶
type ToolHint struct {
Tool string `json:"tool"`
Args map[string]string `json:"args,omitempty"`
Reason string `json:"reason,omitempty"`
}
ToolHint points the model at the next useful call rather than making it guess.
type TriageGroup ¶ added in v0.1.10
type TriageGroup struct {
Kind string `json:"kind"`
Name string `json:"name"`
Namespace string `json:"namespace"`
Ready int32 `json:"ready"`
Desired int32 `json:"desired"`
Pods int `json:"pods"`
Findings []Finding `json:"findings"`
}
TriageGroup is one controller's findings. Triage reports controllers, never pods: forty crashlooping pods of one Deployment is one entry with a count, not forty entries.
type TriageResult ¶ added in v0.1.10
type TriageResult struct {
Scope string `json:"scope"`
Scanned int `json:"workloads_scanned"`
Unhealthy int `json:"workloads_with_findings"`
// Cluster carries findings that are about shared infrastructure rather than any one workload —
// an unhealthy node above all. Reported once here instead of repeated on every workload it
// happens to host, which is the same collapsing principle applied one level up.
Cluster []Finding `json:"cluster,omitempty"`
Groups []TriageGroup `json:"groups"`
Omitted int `json:"omitted_groups,omitempty"`
Degraded []string `json:"degraded,omitempty"`
Notes []string `json:"notes,omitempty"`
}
TriageResult is the whole-cluster answer to "what is broken right now".
type WorkloadView ¶
type WorkloadView struct {
Kind string `json:"kind"` // Deployment | StatefulSet | DaemonSet | Rollout
Name string `json:"name"`
Namespace string `json:"namespace"`
Labels map[string]string `json:"labels,omitempty"`
Selector map[string]string `json:"selector,omitempty"`
Desired int32 `json:"desired"`
Ready int32 `json:"ready"`
Updated int32 `json:"updated"`
Available int32 `json:"available"`
Generation int64 `json:"generation"`
ObservedGeneration int64 `json:"observed_generation"`
CreatedSecondsAgo int64 `json:"created_seconds_ago"`
Conditions []ConditionView `json:"conditions,omitempty"`
}
WorkloadView is the controller under diagnosis.