model

package
v0.1.8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 17, 2026 License: MIT Imports: 2 Imported by: 0

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

Constants

This section is empty.

Variables

This section is empty.

Functions

func ParseCPU

func ParseCPU(q string) int64

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 ParseMem

func ParseMem(q string) int64

ParseMem returns a memory quantity in bytes, or 0 if unset/unparseable.

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.

func (Finding) Less

func (f Finding) Less(g Finding) int

Less reports whether f should sort before g: severity desc, then confidence desc, then ID for a stable order. Used with slices.SortStableFunc, so equal elements keep registry order.

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

func (p *ProbeView) Deadline() int32

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.

const (
	Critical Severity = "critical"
	Warning  Severity = "warning"
	Info     Severity = "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.

func (*Snapshot) Missing

func (s *Snapshot) Missing(step string) bool

Missing reports whether a named gather step failed, so a detector can lower its confidence rather than silently reason from absent data.

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 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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL