report

package
v0.9.0 Latest Latest
Warning

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

Go to latest
Published: Jul 8, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// PersistenceChronic marks a finding present in most scans in the window.
	PersistenceChronic = "chronic"
	// PersistenceIntermittent marks a finding that comes and goes.
	PersistenceIntermittent = "intermittent"
	// PersistenceTransient marks a finding seen in only a few scans.
	PersistenceTransient = "transient"
)

Persistence classes describe how consistently a finding recurs across a window of scans. The data carries no operator resolution label, so recurrence across scans is the strongest learned signal available for whether a finding is a standing problem worth attention or a transient blip.

View Source
const (
	SeverityInfo     = "info"
	SeverityWarning  = "warning"
	SeverityCritical = "critical"
)

Severity levels for divergences.

Variables

View Source
var ClusterColors = []string{
	"#58a6ff", "#f85149", "#3fb950", "#d29922",
	"#bc8cff", "#39d353", "#f78166", "#8b949e",
}

ClusterColors assigns a consistent color to each cluster for visual tracking.

View Source
var ErrBuild = errors.New("build report")

ErrBuild indicates a failure building the comparison report.

View Source
var ScannerLabels = map[string]string{
	"version":           "Kubernetes Version",
	"namespaces":        "Namespaces",
	"crds":              "Custom Resource Definitions",
	"services":          "Services",
	"ingresses":         "Ingress Resources",
	"resources":         "Node Resources",
	"rbac":              "RBAC Configuration",
	"security":          "Pod Security Standards",
	"network-policies":  "Network Policies",
	"resource-quotas":   "Resource Quotas and Limits",
	"node-health":       "Node Health",
	"metrics":           "Resource Utilization",
	"events":            "Cluster Events",
	"workload-security": "Workload Security",
	"rbac-audit":        "RBAC Audit",
	"image-audit":       "Image Hygiene",
	"certs":             "Certificate Expiry",
	"deprecated-apis":   "Deprecated APIs",
	"workload-coverage": "PDB/HPA Coverage",
	"cluster-info":      "Node OS/Kernel Drift",
	"admission":         "Admission Webhooks",
	"geo":               "Geographic Location",
}

ScannerLabels maps scanner keys to human-readable names.

View Source
var SystemNamespaceExact = map[string]bool{
	"default":         true,
	"kube-system":     true,
	"kube-public":     true,
	"kube-node-lease": true,
}

SystemNamespaceExact are the namespace names treated as system regardless of prefix matching.

View Source
var SystemNamespacePrefixes = []string{
	"kube-",
	"gke-",
	"eks-",
	"aks-",
	"cattle-",
	"istio-",
	"linkerd-",
	"cert-manager",
	"monitoring",
	"logging",
	"velero",
	"prometheus",
}

SystemNamespacePrefixes are the namespace name prefixes excluded from fleet-divergence findings. Cloud and add-on namespaces legitimately differ across providers; flagging them as drift drowns real signal.

Functions

func IsSystemNamespace

func IsSystemNamespace(name string) bool

IsSystemNamespace reports whether a namespace name should be treated as a system namespace and therefore excluded from divergence findings.

func RenderHTML

func RenderHTML(r *Report) ([]byte, error)

RenderHTML produces a self-contained HTML dashboard from a Report. The report data is embedded as a JSON blob and all rendering happens client-side in JS.

func VersionSkewSeverity

func VersionSkewSeverity(versions []string) string

VersionSkewSeverity returns the severity for a set of Kubernetes version strings present across a fleet. Patch differences are info, single-minor skew is warning, and skew of more than one minor (outside Kubernetes' own supported skew policy) is critical. Strings that fail to parse are ignored, so a single garbled response cannot escalate severity.

Types

type Brief added in v0.4.0

type Brief struct {
	// Headline is the one-line status.
	Headline string `json:"headline"`
	// Lines are the supporting summary points, most important first.
	Lines []string `json:"lines,omitempty"`
}

Brief is a short, deterministic executive summary of a fleet report, suitable for the top of a dashboard or a status email. It is templated from the report with no randomness, so the same scan always yields the same words.

func GenerateBrief added in v0.4.0

func GenerateBrief(r *Report) Brief

GenerateBrief synthesizes an executive summary from a fully built report: the score, the worst incident, cohort drift, degraded coverage, and the fixes that matter most.

type BuildOptions

type BuildOptions struct {
	// OutlierThreshold controls outlier sensitivity (standard deviations).
	// Lower values flag more outliers. Default is 3.5.
	OutlierThreshold float64
	// Groups maps group name to cluster names for group-aware analysis.
	Groups map[string][]string
	// ClusterTags maps cluster name to a cohort tag value. When non-empty,
	// tagged clusters land in tagged cohorts instead of being auto-grouped.
	// Untagged clusters still get auto-cohorted.
	ClusterTags map[string]string
}

BuildOptions controls report generation behavior.

type CapacityAnalysis

type CapacityAnalysis struct {
	// Cluster is the cluster name.
	Cluster string `json:"cluster"`
	// CPUUtilization is the average CPU percentage.
	CPUUtilization float64 `json:"cpu_utilization"`
	// MemoryUtilization is the average memory percentage.
	MemoryUtilization float64 `json:"memory_utilization"`
	// HasMemoryPressure is true when any node reports MemoryPressure=True.
	HasMemoryPressure bool `json:"has_memory_pressure"`
	// HasDiskPressure is true when any node reports DiskPressure=True.
	HasDiskPressure bool `json:"has_disk_pressure"`
	// HasOOMEvents is true when OOMKilling events were detected.
	HasOOMEvents bool `json:"has_oom_events"`
	// HasSchedulingFailures is true when FailedScheduling events were detected.
	HasSchedulingFailures bool `json:"has_scheduling_failures"`
	// NodeCount is the total number of nodes.
	NodeCount int `json:"node_count"`
	// HealthyNodes is the number of healthy nodes.
	HealthyNodes int `json:"healthy_nodes"`
	// HeadroomCPU is the percentage of CPU capacity still available.
	HeadroomCPU float64 `json:"headroom_cpu"`
	// HeadroomMemory is the percentage of memory capacity still available.
	HeadroomMemory float64 `json:"headroom_memory"`
	// Status is the assessed state: healthy, busy, strained, critical.
	Status string `json:"status"`
	// Recommendation is a plain English capacity recommendation.
	Recommendation string `json:"recommendation,omitempty"`
	// NodesNeededForTarget is how many additional nodes would bring memory below 70%.
	NodesNeededForTarget int `json:"nodes_needed_for_target,omitempty"`
	// GroupDeviation describes how this cluster compares to its group peers.
	GroupDeviation string `json:"group_deviation,omitempty"`
}

CapacityAnalysis holds smart analysis for a single cluster, correlating multiple signals rather than applying naive thresholds.

func AnalyzeCapacity

func AnalyzeCapacity(r *Report, groups map[string][]string) []CapacityAnalysis

AnalyzeCapacity runs smart capacity analysis across all clusters in a report. Groups can be nil when no grouping is available.

type Category

type Category struct {
	// Name is the category display name.
	Name string `json:"name"`
	// Scanners lists the scanner names in this category.
	Scanners []string `json:"scanners"`
}

Category groups related scanners for report organization.

func Categories

func Categories() []Category

Categories returns the scanner groupings used in reports.

type CategoryReport

type CategoryReport struct {
	// Name is the category display name.
	Name string `json:"name"`
	// Scanners lists scanner names in this category.
	Scanners []string `json:"scanners"`
}

CategoryReport holds a category and its scanner names for the report.

type ClusterHealth

type ClusterHealth struct {
	// Name is the cluster context name.
	Name string `json:"name"`
	// Status is "healthy", "busy", "degraded", or "critical".
	Status string `json:"status"`
	// FindingCounts maps severity to count for this cluster.
	FindingCounts map[string]int `json:"finding_counts"`
	// KubernetesVersion is the cluster's k8s version.
	KubernetesVersion string `json:"kubernetes_version"`
	// NodeCount is the number of nodes.
	NodeCount int `json:"node_count"`
	// HealthyNodes is the number of healthy nodes.
	HealthyNodes int `json:"healthy_nodes"`
	// AvgCPU is the average CPU utilization percentage.
	AvgCPU float64 `json:"avg_cpu"`
	// AvgMemory is the average memory utilization percentage.
	AvgMemory float64 `json:"avg_memory"`
	// WarningEvents is the number of warning events.
	WarningEvents int `json:"warning_events"`
	// NamespaceCount is the number of namespaces.
	NamespaceCount int `json:"namespace_count"`
	// DegradedScanners is how many scanner runs did not return complete,
	// trustworthy data for this cluster (errored, degraded, or unavailable).
	// Nonzero means this cluster's report rests on partial coverage.
	DegradedScanners int `json:"degraded_scanners,omitempty"`
}

ClusterHealth summarizes a single cluster's overall state.

func GenerateClusterHealth

func GenerateClusterHealth(r *Report, findings []Finding) []ClusterHealth

GenerateClusterHealth builds per-cluster health summaries.

type ClusterScore

type ClusterScore struct {
	// Cluster is the kubeconfig context name the score applies to.
	Cluster string `json:"cluster"`
	// Score is the 0-100 health score.
	Score int `json:"score"`
	// Grade is the letter grade rollup.
	Grade string `json:"grade"`
	// Headline is a one-line plain-English summary.
	Headline string `json:"headline"`
}

ClusterScore is the per-cluster analogue of FleetScore. Same 0-100 scale, computed from one cluster's findings and health rather than the whole fleet. Used for per-cluster forecasts and as a ranking key on the dashboard.

func ComputeClusterScore

func ComputeClusterScore(h ClusterHealth, findings []Finding) ClusterScore

ComputeClusterScore evaluates one cluster in isolation. It does not consult fleet-wide context (use ComputeFleetScore for that); a cluster's own findings, status, and node/utilization metrics fully determine the score.

func ComputeClusterScores

func ComputeClusterScores(r *Report) []ClusterScore

ComputeClusterScores returns one ClusterScore per cluster in the report.

type ClusterTrend

type ClusterTrend struct {
	// Cluster is the cluster name.
	Cluster string `json:"cluster"`
	// Scanner is the scanner that produces the metric.
	Scanner string `json:"scanner"`
	// Field is the metric field name.
	Field string `json:"field"`
	// Points are the historical data points, oldest first.
	Points []TrendPoint `json:"points"`
	// Direction indicates the overall trend.
	Direction TrendDirection `json:"direction"`
	// Confidence describes how trustworthy this direction is: "high", "low",
	// or empty when no direction was computed.
	Confidence string `json:"confidence,omitempty"`
	// RSquared is the coefficient of determination for the fitted slope.
	RSquared float64 `json:"r_squared,omitempty"`
}

ClusterTrend tracks how a specific metric changes over time for one cluster.

func ComputeClusterTrends

func ComputeClusterTrends(cluster string, scans []ScanMeta, resultsByScan map[string]map[string]map[string]any) []ClusterTrend

ComputeClusterTrends builds trends for a single cluster from historical scan results. resultsByScan is keyed by scanID then scanner name then field name.

type CohortSummary

type CohortSummary struct {
	// Name is the cohort label (tag value for tagged cohorts, "auto-N" for
	// auto-detected cohorts, or "fleet" for too-small fleets).
	Name string `json:"name"`
	// Source identifies how the cohort was produced.
	Source string `json:"source"`
	// Clusters lists the member cluster names, sorted.
	Clusters []string `json:"clusters"`
	// Outliers are clusters that deviate from this cohort's baseline.
	Outliers []OutlierResult `json:"outliers,omitempty"`
}

CohortSummary describes one cohort in the report: its name, how it was derived, the clusters it contains, and the outliers that show up against the cohort's own baseline rather than the fleet's. The cohort view picks up drift the fleet-level view drowns out, because edge and prod clusters have different "normal" without being wrong.

type Divergence

type Divergence struct {
	// Field identifies what differs (e.g. "git_version").
	Field string `json:"field"`
	// Severity indicates how important this divergence is.
	Severity string `json:"severity"`
	// Values maps cluster name to the value it reported for this field.
	Values map[string]string `json:"values"`
}

Divergence describes a single point of difference between clusters.

type Finding

type Finding struct {
	// Title is a short description of the finding.
	Title string `json:"title"`
	// Description is a detailed explanation.
	Description string `json:"description"`
	// Severity is critical, warning, or info.
	Severity string `json:"severity"`
	// Cluster is the cluster this finding applies to, or "fleet" for cross-cluster.
	Cluster string `json:"cluster"`
	// Scanner is the scanner that produced this finding.
	Scanner string `json:"scanner"`
	// Affected lists the offending resources (nodes, pods, bindings, images)
	// scoped to this finding. Empty when the finding has no specific noun.
	Affected []string `json:"affected,omitempty"`
	// Remediation is the suggested fix. Empty when no automated suggestion fits.
	Remediation *Remediation `json:"remediation,omitempty"`
}

Finding is a human-readable issue discovered across the fleet. Findings name the affected resources (pods, nodes, bindings) inline so operators do not have to drill into the per-cluster JSON to identify what to fix.

func GenerateFindings

func GenerateFindings(r *Report) []Finding

GenerateFindings analyzes a report and produces human-readable findings. At scale (more than 20 clusters), outlier-based findings replace pairwise comparison for version and namespace drift.

func GenerateTrendFindings

func GenerateTrendFindings(clusterTrends []ClusterTrend, fleetTrends []FleetTrend) []Finding

GenerateTrendFindings produces findings from historical trends.

func SelfDriftFindings added in v0.3.0

func SelfDriftFindings(drifts []SelfDrift) []Finding

SelfDriftFindings turns self-drift detections into findings so a cluster that broke from its own history reaches the same surfaces as every other finding.

type FindingPersistence added in v0.8.0

type FindingPersistence struct {
	// Fingerprint is the stable finding identifier, matching its ack key.
	Fingerprint string `json:"fingerprint"`
	// Cluster is the finding's cluster scope, or "fleet".
	Cluster string `json:"cluster"`
	// Scanner is the scanner that produced the finding.
	Scanner string `json:"scanner"`
	// Title is the finding title as last observed.
	Title string `json:"title"`
	// Severity is the finding's severity as last observed.
	Severity string `json:"severity"`
	// Present is the number of scans in the window that contained the finding.
	Present int `json:"present"`
	// Total is the number of scans in the window.
	Total int `json:"total"`
	// Fraction is Present divided by Total, in the range zero to one.
	Fraction float64 `json:"fraction"`
	// Streak is the number of consecutive most-recent scans containing it.
	Streak int `json:"streak"`
	// Class is chronic, intermittent, or transient.
	Class string `json:"class"`
	// Acked reports whether the finding has an active acknowledgement.
	Acked bool `json:"acked"`
}

FindingPersistence summarizes how often one finding recurred across a window of scans. It is an implicit, unsupervised severity signal: a critical that recurs in every scan is a standing problem, while one seen once is likely noise or an already-resolved blip.

func ComputePersistence added in v0.8.0

func ComputePersistence(series [][]Finding, acked map[string]bool) []FindingPersistence

ComputePersistence classifies each distinct finding by how often it recurred across series, a slice of per-scan finding sets ordered oldest to newest. acked holds the fingerprints with an active acknowledgement. Results are sorted most-persistent first, then by severity, cluster, and title, so the standing problems lead. An empty series yields no results.

type FleetScore

type FleetScore struct {
	// Score is the rounded 0-100 health score.
	Score int `json:"score"`
	// Grade is a single-letter rollup of Score: A 90+, B 80+, C 70+, D 60+, F under 60.
	Grade string `json:"grade"`
	// Headline is a one-line plain-English summary suitable for a hero card.
	Headline string `json:"headline"`
	// Drivers names the top three factors pulling the score below 100, in
	// descending order of impact. Empty when the fleet is at 100.
	Drivers []FleetScoreDriver `json:"drivers,omitempty"`
}

FleetScore is a single 0-100 indicator of overall fleet health, suitable for a status-TV hero number on the dashboard. The score is intentionally asymmetric: clusters in trouble pull it down faster than warnings do, and individual findings are capped so a noisy fleet does not collapse to zero on volume alone. The Drivers list names the top reasons the score sits where it does so operators can read the number and the explanation in one glance.

func ComputeFleetScore

func ComputeFleetScore(r *Report) FleetScore

ComputeFleetScore returns a FleetScore derived from a built Report. The function is pure: callers can compare scores across scans simply by calling it on two different reports. Safe to call on nil; returns a perfect score with a placeholder headline.

type FleetScoreDriver

type FleetScoreDriver struct {
	// Reason is a short human-readable label.
	Reason string `json:"reason"`
	// Impact is the number of points (whole or fractional, rounded) this
	// driver removed from the score.
	Impact int `json:"impact"`
}

FleetScoreDriver describes a single factor reducing the fleet score.

type FleetScoreForecast

type FleetScoreForecast struct {
	// Predicted is the projected Fleet Score at PredictedFor.
	Predicted int `json:"predicted"`
	// Lower is the lower bound of a 95% prediction interval, clamped to 0-100.
	Lower int `json:"lower"`
	// Upper is the upper bound of the 95% prediction interval, clamped to 0-100.
	Upper int `json:"upper"`
	// PredictedFor is the wall-clock time the forecast targets. The handler
	// chooses this (typically "now + median inter-scan gap"); the math is
	// time-aware so a quoted ETA reflects the actual extrapolation.
	PredictedFor time.Time `json:"predicted_for"`
	// Slope is the slope of the fitted line in points per hour. Negative means
	// the fleet is degrading.
	Slope float64 `json:"slope_per_hour"`
	// RSquared is the coefficient of determination for the fit.
	RSquared float64 `json:"r_squared"`
	// Basis is the number of historical points used in the fit.
	Basis int `json:"basis"`
	// Sufficient is true when the fit cleared the minimum sample and noise
	// thresholds and the forecast is worth showing.
	Sufficient bool `json:"sufficient"`
	// Headline is a one-line plain-English summary suitable for the dashboard.
	Headline string `json:"headline"`
}

FleetScoreForecast is the predicted next Fleet Score with an uncertainty band derived from the regression's standard error. When the input is too sparse or too noisy to be meaningful, Sufficient is false and callers should hide the forecast in the UI.

func ForecastFleetScore

func ForecastFleetScore(history []FleetScoreHistoryPoint, predictedFor time.Time) FleetScoreForecast

ForecastFleetScore fits an OLS line to the given history (oldest first or any order; the function sorts by timestamp) and returns a forecast for `predictedFor`. If predictedFor is the zero time the function picks a sensible target one median scan interval into the future.

type FleetScoreHistoryPoint

type FleetScoreHistoryPoint struct {
	// ScanID is the scan that produced the score.
	ScanID string `json:"scan_id"`
	// Timestamp is when the scan executed.
	Timestamp time.Time `json:"timestamp"`
	// Score is the Fleet Score for that scan.
	Score int `json:"score"`
}

FleetScoreHistoryPoint is one observation in a Fleet Score time series.

type FleetTrend

type FleetTrend struct {
	// Scanner is the scanner name.
	Scanner string `json:"scanner"`
	// Field is the metric field name.
	Field string `json:"field"`
	// Direction indicates the overall trend.
	Direction TrendDirection `json:"direction"`
	// Points are fleet-aggregated values over time (e.g. count of unique versions).
	Points []TrendPoint `json:"points"`
	// Confidence describes how trustworthy this direction is.
	Confidence string `json:"confidence,omitempty"`
	// RSquared is the coefficient of determination for the fitted slope.
	RSquared float64 `json:"r_squared,omitempty"`
}

FleetTrend tracks a fleet-wide metric over time.

func ComputeFleetTrends

func ComputeFleetTrends(scans []ScanMeta, allResults map[string]map[string]map[string]any) []FleetTrend

ComputeFleetTrends analyzes how fleet-wide uniformity changes over time for string fields like version. It counts unique values per scan.

type Incident added in v0.4.0

type Incident struct {
	// Cluster is the cluster the incident is on.
	Cluster string `json:"cluster"`
	// Title is a synthesized one-line summary.
	Title string `json:"title"`
	// Theme is the root-cause family the fused findings belong to.
	Theme string `json:"theme"`
	// Severity is the highest severity among the member findings.
	Severity string `json:"severity"`
	// Summary is a deterministic, templated root-cause hypothesis.
	Summary string `json:"summary"`
	// Findings lists the titles of the fused member findings, sorted.
	Findings []string `json:"findings"`
}

Incident groups findings that share a likely root cause on one cluster, so an operator reads one incident instead of a flat list of symptoms. A failing admission webhook, an expiring cert, and a deprecated-API rejection are one incident, not three unrelated warnings.

func FuseIncidents added in v0.4.0

func FuseIncidents(findings []Finding) []Incident

FuseIncidents groups findings by cluster and root-cause theme, emitting an incident wherever two or more findings on a cluster share a theme. Findings with no theme, or that stand alone, are left as ordinary findings.

type MisCohortFinding

type MisCohortFinding struct {
	// Cluster is the cluster whose tag disagrees with its profile.
	Cluster string `json:"cluster"`
	// TaggedAs is the user-applied cohort tag the cluster carries.
	TaggedAs string `json:"tagged_as"`
	// ProfileMatches is the cohort tag a majority of similar clusters carry.
	ProfileMatches string `json:"profile_matches"`
}

MisCohortFinding describes a cluster whose user-applied cohort tag does not match where its scanner profile places it. This catches mislabeled clusters that survived because every existing tool only looks at one cluster at a time.

type OutlierResult

type OutlierResult struct {
	// Cluster is the cluster that deviates.
	Cluster string `json:"cluster"`
	// Field is the data field that deviates.
	Field string `json:"field"`
	// Value is the cluster's value for the field.
	Value string `json:"value"`
	// FleetNorm is the typical fleet value (median for numeric, mode for string).
	FleetNorm string `json:"fleet_norm"`
	// Deviation is the modified z-score for numeric fields.
	Deviation float64 `json:"deviation,omitempty"`
	// Scanner is the scanner that produced this data.
	Scanner string `json:"scanner"`
	// Severity is critical, warning, or info.
	Severity string `json:"severity"`
}

OutlierResult describes a cluster that deviates from the fleet norm.

func DetectOutliers

func DetectOutliers(r *Report, threshold float64) []OutlierResult

DetectOutliers analyzes a report and returns clusters that deviate from fleet norms. The threshold controls sensitivity for numeric fields: lower values flag more outliers. The function only emits findings when the sample is large enough to be statistically meaningful (see minMADSample).

type Remediation

type Remediation struct {
	// Command is a kubectl or related command that addresses the finding.
	Command string `json:"command,omitempty"`
	// YAML is a manifest snippet the operator can apply directly.
	YAML string `json:"yaml,omitempty"`
	// RunbookURL is an optional runbook link.
	RunbookURL string `json:"runbook_url,omitempty"`
}

Remediation describes the concrete action an operator can take to resolve a finding. Command is a kubectl invocation parameterized with the actual offending resource names so the user does not have to re-discover them. YAML, when present, is a multi-line baseline manifest that satisfies the requirement (for example a default-deny NetworkPolicy or a ResourceQuota). RunbookURL points at an internal runbook the operator has wired up; it is optional and may be empty.

type Report

type Report struct {
	// Timestamp is when the scan was executed.
	Timestamp string `json:"timestamp"`
	// Clusters lists the kubeconfig contexts that were scanned.
	Clusters []string `json:"clusters"`
	// Categories groups sections by functional area.
	Categories []CategoryReport `json:"categories"`
	// Sections holds per-scanner comparison results keyed by scanner name.
	Sections map[string]*SectionReport `json:"sections"`
	// Summary holds fleet-wide summary statistics.
	Summary Summary `json:"summary"`
	// Findings lists human-readable issues discovered across the fleet.
	Findings []Finding `json:"findings"`
	// ClusterHealths holds per-cluster health summaries.
	ClusterHealths []ClusterHealth `json:"cluster_healths"`
	// Outliers lists clusters that deviate from fleet norms. Populated when
	// the fleet has more than 20 clusters.
	Outliers []OutlierResult `json:"outliers,omitempty"`
	// Capacity holds smart capacity analysis per cluster, correlating
	// utilization, pressure, events, and headroom.
	Capacity []CapacityAnalysis `json:"capacity,omitempty"`
	// FleetScore is the single 0-100 indicator of overall fleet health,
	// suitable as a hero number on a status TV. Populated by Build.
	FleetScore FleetScore `json:"fleet_score"`
	// Cohorts partitions the fleet into groups of similar clusters and
	// reports within-cohort outliers. Cohorts come from user-supplied
	// cluster tags when present, otherwise from agglomerative clustering
	// on scanner-derived features.
	Cohorts []CohortSummary `json:"cohorts,omitempty"`
	// Degraded lists scanner runs that did not return complete, trustworthy
	// data for a cluster, so the report surfaces reduced coverage instead of
	// reading a failed or forbidden scan as a clean, zero-resource result.
	Degraded []ScannerStatus `json:"degraded,omitempty"`
	// Incidents groups related findings by shared root cause so operators read
	// one incident instead of a flat list of correlated symptoms.
	Incidents []Incident `json:"incidents,omitempty"`
	// Brief is a short, deterministic executive summary of this report.
	Brief Brief `json:"brief"`
}

Report is the top-level output structure.

func Build

func Build(clusters []string, results map[string]map[string]scanner.Result, opts ...BuildOptions) *Report

Build creates a Report from per-cluster scanner results. The results map is keyed by cluster name, then by scanner name. Options are optional; when omitted, defaults are used.

func (*Report) DegradedByCluster added in v0.2.0

func (r *Report) DegradedByCluster() map[string]int

DegradedByCluster counts, per cluster, how many scanner runs did not return complete, trustworthy data. Clusters with full coverage are omitted. Returns nil when the whole fleet scanned cleanly.

type ScanMeta

type ScanMeta struct {
	// ID is the scan identifier.
	ID string
	// Timestamp is when the scan ran.
	Timestamp time.Time
}

ScanMeta identifies a scan for trend analysis without depending on the store package.

type ScannerStatus added in v0.2.0

type ScannerStatus struct {
	// Cluster is the cluster the scanner ran against.
	Cluster string `json:"cluster"`
	// Scanner is the scanner name.
	Scanner string `json:"scanner"`
	// State is "degraded", "errored", or "unavailable".
	State string `json:"state"`
	// Reason is a short explanation of why the data is not fully trustworthy.
	Reason string `json:"reason,omitempty"`
}

ScannerStatus records a scanner run that did not return complete, trustworthy data for one cluster. It lets the report show degraded coverage ("3 of 24 scanners degraded on cluster X") instead of silently treating a failed or forbidden scan as a clean result.

type SectionReport

type SectionReport struct {
	// Uniform is true when all clusters produced identical data for this scanner.
	Uniform bool `json:"uniform"`
	// PerCluster holds the raw scanner data from each cluster.
	PerCluster map[string]any `json:"per_cluster"`
	// Divergences describes specific differences found between clusters.
	Divergences []Divergence `json:"divergences,omitempty"`
}

SectionReport holds comparison data for one scanner across all clusters.

type SelfDrift added in v0.3.0

type SelfDrift struct {
	// Cluster is the cluster that drifted from its own past.
	Cluster string `json:"cluster"`
	// Scanner is the scanner that produces the metric.
	Scanner string `json:"scanner"`
	// Field is the metric field name.
	Field string `json:"field"`
	// Baseline is the median of the cluster's history before the change point.
	Baseline float64 `json:"baseline"`
	// Latest is the mean of the cluster's values after the change point.
	Latest float64 `json:"latest"`
	// Deviation is the modified z-score of Latest against the baseline, or the
	// fractional step when the baseline was flat.
	Deviation float64 `json:"deviation"`
	// ChangedAt is when the shift began, taken from the change-point scan.
	ChangedAt time.Time `json:"changed_at"`
	// Direction is worsening or improving, calibrated per metric.
	Direction TrendDirection `json:"direction"`
	// Severity is warning for a worsening shift, info for an improving one.
	Severity string `json:"severity"`
}

SelfDrift describes a cluster whose own metric shifted away from its history. The cross-cluster view cannot see this because the cluster may still look normal next to its peers while having moved sharply from its own past.

func DetectSelfDrift added in v0.3.0

func DetectSelfDrift(cluster string, scans []ScanMeta, resultsByScan map[string]map[string]map[string]any) []SelfDrift

DetectSelfDrift finds metrics where one cluster diverged from its own history. It mirrors ComputeClusterTrends' inputs: resultsByScan is keyed by scan ID, then scanner name, then field. For each tracked metric it locates the single most likely change point in the cluster's time series and flags a shift whose recent segment is far from the cluster's own pre-change median and MAD.

type Summary

type Summary struct {
	// ClusterCount is the number of clusters scanned.
	ClusterCount int `json:"cluster_count"`
	// ScannerCount is the number of scanners executed.
	ScannerCount int `json:"scanner_count"`
	// UniformCount is how many scanners found identical data across all clusters.
	UniformCount int `json:"uniform_count"`
	// DivergentCount is how many scanners found differences.
	DivergentCount int `json:"divergent_count"`
	// TotalDivergences is the total number of individual divergence points.
	TotalDivergences int `json:"total_divergences"`
	// CriticalCount is the number of critical-severity divergences.
	CriticalCount int `json:"critical_count"`
	// WarningCount is the number of warning-severity divergences.
	WarningCount int `json:"warning_count"`
}

Summary holds fleet-wide statistics.

type TrendDirection

type TrendDirection string

TrendDirection indicates whether a metric is improving, worsening, or stable.

const (
	// TrendStable indicates no significant change.
	TrendStable TrendDirection = "stable"
	// TrendImproving indicates the metric is getting better.
	TrendImproving TrendDirection = "improving"
	// TrendWorsening indicates the metric is getting worse.
	TrendWorsening TrendDirection = "worsening"
)

type TrendPoint

type TrendPoint struct {
	// Timestamp is when this value was observed.
	Timestamp time.Time `json:"timestamp"`
	// ScanID is the scan that produced this value.
	ScanID string `json:"scan_id"`
	// Value is the metric value.
	Value float64 `json:"value"`
}

TrendPoint represents a metric value at a point in time.

Jump to

Keyboard shortcuts

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