errparse

package
v0.19.1065 Latest Latest
Warning

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

Go to latest
Published: Jul 16, 2026 License: AGPL-3.0 Imports: 5 Imported by: 0

Documentation

Overview

Package errparse turns the raw error output of a failed runner job into a typed compositeerrors.CompositeError.

Parsing is hierarchical: a single failure blob is a stack of nested errors (cloud-provider inside tool inside orchestration), and we want to surface the most specific layer, "Missing IAM permission s3:CreateBucket" rather than "terraform apply failed". Parsers therefore declare a Layer, and the registry tries the deepest (provider) layer first, then tool, then a generic emit-the-body fallback.

The deepest layer holds the most parsers and grows without bound, so gating must be cheap: a parser advertises the Tools it applies to (a free facet index) and the Signals that must be physically present in the text (a single compiled multi-pattern scan). A parser's expensive Parse only runs when its facet gate passes and one of its signals is present, so per-error cost is a function of the text length and the handful of matched candidates, not the total number of registered parsers.

Index

Constants

View Source
const (
	ErrorMetadataOutput  = "error_output"
	ErrorMetadataMessage = "message"
)

Variables

This section is empty.

Functions

func Parse

Parse dispatches ctx against the default registry.

func ParseRunnerJobResult added in v0.19.1058

func ParseRunnerJobResult(success bool, errorMetadata map[string]string, runnerJob *app.RunnerJob) (*compositeerrors.CompositeErrorData, error)

func Register

func Register(p Parser)

Register adds a parser to the default registry.

func RunnerJobErrorText added in v0.19.1058

func RunnerJobErrorText(errorMetadata map[string]string) string

Types

type Layer

type Layer int

Layer orders parsers from most-specific to generic. Lower layers are tried first, so a provider-level cause wins over the tool-level fallback.

const (
	// LayerProvider is the cloud-provider layer (AWS/Azure/GCP error codes).
	LayerProvider Layer = 0
	// LayerToolSpecific is for a specific, recognised cause within a tool (e.g.
	// terraform's state-lock failure). It is tried before LayerTool so a
	// specific classifier wins over the tool's catch-all, while a provider
	// cause still wins over both.
	LayerToolSpecific Layer = 9
	// LayerTool is the execution-tool layer catch-all (terraform/helm/pulumi/
	// docker): a recognised-but-unclassified diagnostic for that tool.
	LayerTool Layer = 10
	// LayerGeneric is the always-matches fallback that emits the cleaned error
	// body when no specific parser recognises the failure.
	LayerGeneric Layer = 100
)

type Option added in v0.19.1059

type Option func(*builtParser)

Option customizes a Parser built by NewParser.

func AlwaysCandidate added in v0.19.1059

func AlwaysCandidate() Option

AlwaysCandidate opts the parser out of signal gating so it is a candidate for every job. It exists for the generic fallback; a normal parser should gate on WithSignals so it is not run against unrelated failures.

func WithProviders added in v0.19.1059

func WithProviders(providers ...Provider) Option

WithProviders gates the parser on the cloud provider the job targeted. The gate is applied after a signal match (provider resolution is a lazy lookup) and fails open: a job whose provider cannot be resolved still passes, so provider gating is only ever a false-positive guard. Passing no providers or ProviderUnknown is a configuration mistake and panics.

func WithSignals added in v0.19.1059

func WithSignals(signals ...string) Option

WithSignals gates the parser on substrings that must be present in the raw text; the parser's Parse only runs once one is found. At least one signal is required (use AlwaysCandidate for a deliberately signal-less parser).

func WithTools added in v0.19.1059

func WithTools(tools ...Tool) Option

WithTools restricts the parser to the given execution tools, so it is never considered for a job run by another tool. With no WithTools the parser is tool-agnostic (considered for every job).

type Owner

type Owner struct {
	Type string
	ID   string
}

Owner identifies the row a runner job belongs to (polymorphic: table name + id), e.g. ("install_deploys", "<id>").

type ParseContext

type ParseContext struct {
	// Raw is the untruncated error output captured from the job.
	Raw string

	// Tool is the execution tool, or ToolUnknown when it can't be determined.
	Tool Tool

	// Operation is the job operation (e.g. "apply-plan"), free-form.
	Operation string

	// Group is the runner job group (e.g. "deploy", "build", "sandbox").
	Group string

	// Owner is the row the job belongs to.
	Owner Owner

	// Meta carries the runner-sent error metadata (step, handler, job_type, ...).
	Meta map[string]string

	// ResolveProvider lazily resolves the cloud provider. It is optional: when
	// nil, Provider() reports ProviderUnknown.
	ResolveProvider func() Provider
	// contains filtered or unexported fields
}

ParseContext carries the facets a parser gates and parses on. The free facets (Raw, Tool, Operation, Group, Owner, Meta) are populated eagerly by the caller from the already-loaded runner job; Provider is resolved lazily and memoized so an unmatched job never pays for the DB lookup.

func (*ParseContext) Provider

func (c *ParseContext) Provider() Provider

Provider returns the cloud provider, resolving it at most once. It fails open to ProviderUnknown so an unresolvable owner never suppresses provider parsing (provider gating is a false-positive guard, never the sole gate).

type Parser

type Parser interface {
	// Layer places the parser in the specificity hierarchy.
	Layer() Layer

	// Tools are the execution tools this parser applies to. An empty slice
	// means tool-agnostic (considered for every job). Used to bucket parsers
	// for cheap facet-based narrowing.
	Tools() []Tool

	// Signals are substrings that must be present in the raw text for this
	// parser to be a candidate. They are compiled into a single multi-pattern
	// matcher per bucket. An empty slice means "always a candidate" (used by
	// the generic fallback), which never runs the matcher.
	Signals() []string

	// Applicable confirms the parser should run after a signal match, using the
	// finer facets (notably Provider). It must fail open on unknown facets.
	Applicable(ctx *ParseContext) bool

	// Parse attempts to produce a typed error, or returns nil to defer to the
	// next candidate. It must be selective: a miss returns nil rather than a
	// low-confidence match.
	Parse(ctx *ParseContext) compositeerrors.CompositeError
}

Parser recognises one class of failure. Implementations must be pure and local: no DB or network access (the one lazy DB facet, Provider, is resolved by the context, not the parser).

func NewParser added in v0.19.1059

func NewParser(layer Layer, parse ParserFunc, opts ...Option) Parser

NewParser builds a Parser from its layer and parse behaviour plus optional facets. It covers the common case where a parser's gating is static, so a parser package registers a free function instead of a bespoke type with five one-line methods.

The registration models a three-stage candidate pipeline (cheapest first), with layer as the tiebreak once several parsers match:

WithTools      structural bucket, considered before any text scan
WithSignals /  substrings that must be present (one cheap scan), or
AlwaysCandidate opt out of signal gating (the generic fallback)
WithProviders  lazy provider gate applied after a signal match
layer          precedence when several parsers still match

layer and parse are required: there is no safe zero value for layer (the zero Layer is LayerProvider) and a nil parse panics. Exactly one of WithSignals or AlwaysCandidate must be given, so a parser that simply forgot its signals is rejected rather than silently running against every job.

The Parser interface remains the underlying contract; implement it directly when a parser must hold state or gate on facets these options do not cover (Operation, Group, Owner).

type ParserFunc added in v0.19.1059

ParserFunc is the core recognise-and-classify behaviour of a parser: it attempts to produce a typed error from ctx, or returns nil to defer to the next candidate.

type Provider

type Provider string

Provider is the cloud provider a job targeted. It is resolved lazily because it costs a DB lookup, and only matters once a provider-specific signal has already been found in the text.

const (
	ProviderUnknown Provider = ""
	ProviderAWS     Provider = "aws"
	ProviderAzure   Provider = "azure"
	ProviderGCP     Provider = "gcp"
)

type Registry

type Registry struct {
	// contains filtered or unexported fields
}

Registry holds the registered parsers and dispatches a ParseContext to them. Parsers are grouped into facet buckets by Tool; within a bucket the signaled parsers share one compiled matcher. The bucket layout is built once, lazily, on the first Parse so that init-time registration order does not matter.

Registration is expected to happen entirely at init time (parser packages blank-imported at the chokepoint). Register panics if called after the first Parse, because a late parser would be silently dropped from the already-built buckets. That bug is far easier to catch as a panic than as a parser that mysteriously never fires.

Parsers are referenced by their index into r.parsers everywhere (buckets, dedup, candidates) rather than by interface value: a Parser may be any implementation, including a non-comparable struct, so it must never be used as a map key.

func (*Registry) Parse

Parse runs the applicable parsers against ctx and returns the first confident match in layer order (provider, then tool, then generic), or nil when nothing matches. Within a layer, ties are broken by registration order so dispatch is deterministic regardless of bucket collection order.

func (*Registry) Register

func (r *Registry) Register(p Parser)

Register adds a parser. It is intended to be called from init functions and panics on misuse (nil parser, empty signal string, or registration after the registry has been built).

type Tool

type Tool string

Tool is the underlying execution tool a runner job used. It is derived from the runner job type and used as a free facet to bucket parsers, so terraform parsers are never even considered for a helm job.

const (
	ToolUnknown    Tool = ""
	ToolTerraform  Tool = "terraform"
	ToolHelm       Tool = "helm"
	ToolPulumi     Tool = "pulumi"
	ToolDocker     Tool = "docker"
	ToolKubernetes Tool = "kubernetes"
	ToolOCI        Tool = "oci"
)

func ToolForRunnerJob added in v0.19.1058

func ToolForRunnerJob(runnerJob *app.RunnerJob) Tool

Directories

Path Synopsis
Package all blank-imports every errparse parser package so their init() registrations land in the default registry.
Package all blank-imports every errparse parser package so their init() registrations land in the default registry.
Package aws holds the AWS provider-layer CompositeError parsers: cloud error codes surfaced from a runner job's raw output.
Package aws holds the AWS provider-layer CompositeError parsers: cloud error codes surfaced from a runner job's raw output.
Package errparsetest provides a shared, table-driven contract runner for errparse parsers.
Package errparsetest provides a shared, table-driven contract runner for errparse parsers.
Package generic holds the tool-agnostic, always-matching fallback parser.
Package generic holds the tool-agnostic, always-matching fallback parser.
Package helm holds tool-layer CompositeError parsers for helm jobs.
Package helm holds tool-layer CompositeError parsers for helm jobs.
Package terraform holds tool-layer CompositeError parsers for terraform jobs.
Package terraform holds tool-layer CompositeError parsers for terraform jobs.

Jump to

Keyboard shortcuts

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