Level-triggered Work Queue (inherited from Knative)#
Usage: Core reconciliation model for all three controllers (TaskRun, PipelineRun, ResolutionRequest)
Example:pkg/client/injection/reconciler/pipeline/v1/taskrun/controller.go — controller.NewContext(ctx, rec, controller.ControllerOptions{...}) creates a work queue backed reconciler; Kubernetes informer events enqueue items; configurable thread count (THREADS_PER_CONTROLLER)
Assessment: Highly idiomatic for Kubernetes operators. The queue absorbs bursts and ensures at-least-once reconciliation. Tekton inherits this wholesale from Knative — no custom implementation.
Usage: Result file reading in internal/sidecarlogresults — reads N result files concurrently
Example:internal/sidecarlogresults/sidecarlogresults.go:158-205 — launches a goroutine per result file via errgroup.Group, sends SidecarLogResult values to an unbuffered results channel; a second channelGroup goroutine closes the channel after all workers finish; the main goroutine range-drains the channel
Assessment: Canonical fan-out/fan-in with errgroup. Uses a two-group pattern (workers + closer) to coordinate channel closure cleanly. Effective and idiomatic.
Usage: All binary entry points (cmd/controller, cmd/resolvers, cmd/webhook, cmd/sidecarlogresults)
Example:cmd/controller/main.go:86 — ctx := injection.WithNamespaceScope(signals.NewContext(), *namespace) — Knative’s signals.NewContext() returns a context cancelled on SIGTERM/SIGINT
Assessment: Clean pattern. Binary-level shutdown is fully delegated to Knative’s signal handling; no manual os.Signal channels in controller code (though cmd/sidecarlogresults does it manually due to its simpler structure).
Raw usage: 61 go func occurrences (excluding vendor) — moderate for an operator-style project. Most goroutines are managed by the Knative work queue, not spawned ad-hoc.
Count: 34 non-vendor occurrences of sync.Mutex, sync.RWMutex, sync.Once, sync.WaitGroup, sync.Map, or atomic.* — low, reflecting that shared state is mostly in Kubernetes API objects rather than in-process memory.
Style: Mixed — sentinel errors for well-known conditions, fmt.Errorf with %w for context wrapping, custom typed errors for domain-specific semantics
fmt.Errorf %w: 348 occurrences — dominant wrapping mechanism throughout the codebase
Sentinel errors: Defined in dedicated errors.go files per package (e.g., pkg/trustedresources/errors.go, pkg/trustedresources/verifier/errors.go). Pattern: var ErrXxx = errors.New("...") exported at package level. Callers use errors.Is(err, pkg.ErrXxx).
Custom typed errors: A distinctive Go idiom used in pkg/entrypoint:
type ContextError string — wraps context error messages as a named string type; ErrContextCanceled = ContextError(context.Canceled.Error()) allows typed comparison via errors.Is
type SkipError string — signals that a step was skipped (predecessor failed); caught by the reconciler to update status rather than fail the TaskRun
type DebugBeforeStepError string — signals breakpoint-induced skipping
type MessageLengthError string (in pkg/termination) — signals termination message overflow
All implement Error() string by converting to string. This allows errors.Is matching on the typed value directly.
Wrapping approach:fmt.Errorf("doing X: %w", err) throughout — no pkg/errors, no custom wrap functions
Mechanism: Kubernetes ConfigMaps are watched live; on each change the config.Store updates an in-memory struct attached to context.Context. Callers use config.FromContextOrDefaults(ctx) — never blocking, always returns a valid struct.
Example:pkg/pod/pod.go:158-165 — 6 consecutive config.FromContextOrDefaults(ctx).FeatureFlags.* calls at the top of Build() — no argument passing needed; config flows through context
Binary flags: Image references and thread counts set via flag package at startup; no Viper. Environment variable THREADS_PER_CONTROLLER supplemented by flag.
Assessment: Elegant for hot-reload of ConfigMaps without restart. The downside is that context.Context becomes a grab-bag — it carries logger, config, clients, and cancellation simultaneously.
Approach: Service locator via context.Context (Knative injection pattern)
Evidence:cmd/controller/main.go — Kubernetes clients and informers are registered into context before controller factories run. Controllers extract dependencies with typed Get(ctx) functions: kubeclient.Get(ctx), taskruninformer.Get(ctx), logging.FromContext(ctx). No wire, dig, or fx.
Assessment: Functional but non-standard. It trades explicit constructor parameters for implicit context threading. This makes dependency graphs invisible to static analysis and increases test setup complexity. It is idiomatic for Knative-based operators but unusual outside that ecosystem.
Where:pkg/pod/pod.go:145 — type Transformer func(*corev1.Pod) (*corev1.Pod, error)
How:Builder.Build(ctx, taskRun, taskSpec, transformers ...Transformer) applies each transformer in sequence after building the base Pod. Separate packages contribute transformers: pkg/internal/computeresources.NewTransformer, pkg/internal/affinityassistant.NewTransformer, pkg/internal/defaultresourcerequirements.NewTransformer
Assessment: Clean extensibility without subclassing. New pod mutations are added by writing a new Transformer function and passing it at the call site. Avoids a growing switch/case in Build. This is a well-executed functional decorator pattern.
Where:pkg/pod.Builder (struct with Build method) and pkg/reconciler/pipeline/dag.Build (package-level function)
How:pod.Builder holds shared configuration (image refs, KubeClient, entrypoint cache); Build is called per TaskRun. dag.Build(tasks Tasks, deps map[string][]string) (*Graph, error) is a pure-function builder with no state.
Assessment: Both are clear and testable. The dag package’s stateless Build function (no constructor needed) is particularly clean.
Where:pkg/entrypoint — Waiter and Runner interfaces abstract file polling and process execution; pkg/remoteresolution — Requester interface abstracts CRD-based resolution
How: Production code depends only on the interface; tests inject fakes (fakeErrorWaiter, fakeErrorRunner)
Assessment: Classic Go interface pattern. Interfaces are defined at the consumer side, are small (1-3 methods), and enable full unit testing without Kubernetes dependencies.
How:type ContextError string — a named type over string that implements error. Sentinel values like ErrContextCanceled = ContextError(context.Canceled.Error()) are compared with errors.Is. Because ContextError is a distinct type, an errors.Is check only matches the typed sentinel, not a plain string with the same value.
Assessment: Unusual but valid. It achieves typed error discrimination without a struct. The string representation is human-readable. The tradeoff is that error messages are hardcoded; changing the string breaks equality.
How: The entire DAG package — Graph, Node, Task interface, Build, GetCandidateTasks, cycle detection — has zero Kubernetes imports. It operates on abstract Task and Tasks interfaces.
Assessment: Excellent separation of concerns. The scheduling algorithm is independently testable and understandable without Kubernetes knowledge. A textbook example of isolating domain logic from infrastructure.
pkg/apis/pipeline/pod/template.go:294 — func mergeByName[T any](base, overrides []T) []T — merges container slices by name field (uses reflection internally or similar)
pkg/reconciler/pipelinerun/resources/apply.go:194 — func paramExists[T paramValue](paramName string, bucket map[string]T) bool — parameterized over concrete param value types
pkg/entrypoint/entrypointer_test.go:2313 — func ptr[T any](value T) *T — test helper
Assessment: Conservative, appropriate use. Generics are applied only where they eliminate repetition in type-safe ways; the codebase does not over-generalize.
Table-driven tests (inferred from testing patterns)#
Prevalent across the codebase — the standard Go approach; reconciler tests follow Knative’s reconcilertesting.TableTest pattern with named test cases.
How: Structured events are emitted to the Kubernetes event log on state transitions (TaskRun started, failed, succeeded). CloudEvents are emitted separately via pkg/cloudevent for external consumers.
Assessment: Two-track notification: Kubernetes events for cluster-internal observability; CloudEvents for external integration (e.g., Tekton Triggers). Clean separation.