Tekton Pipelines — Interfaces#
Interface catalog#
Resolver (remoteresolution framework)#
- Package:
github.com/tektoncd/pipeline/pkg/remoteresolution/resolver/framework - File:
pkg/remoteresolution/resolver/framework/interface.go - Methods:
Initialize(ctx context.Context) error GetName(ctx context.Context) string GetSelector(ctx context.Context) map[string]string Validate(ctx context.Context, req *v1beta1.ResolutionRequestSpec) error Resolve(ctx context.Context, req *v1beta1.ResolutionRequestSpec) (ResolvedResource, error) - Purpose: Defines the contract every remote resolver must fulfil. Each resolver (git, OCI bundle, Tekton Hub, cluster, HTTP) implements this to fetch Task/Pipeline YAML from its respective backend. The framework routes a
ResolutionRequestCRD to the resolver whoseGetSelectorlabels match. - Implementations:
GitResolver,BundleResolver,HubResolver,ClusterResolver,HTTPResolver(all inpkg/remoteresolution/resolver/) - Design quality: Well-segregated. The interface has exactly as many methods as needed to drive the framework lifecycle (init, name, selector, validate, resolve). Optional behaviour (custom timeout, admin config) is split into separate optional interfaces (
TimedResolution,ConfigWatcher), correctly applying ISP. - Note: A legacy copy exists at
pkg/resolution/resolver/framework/interface.gowith a// Deprecatedcomment; the new version usesResolutionRequestSpecinstead of[]Paramfor the validate/resolve signatures.
Requester#
- Package:
github.com/tektoncd/pipeline/pkg/remoteresolution/resource - File:
pkg/remoteresolution/resource/request.go - Methods:
Submit(ctx context.Context, name ResolverName, req Request) (ResolvedResource, error) - Purpose: Abstracts how a reconciler submits a resource resolution request. The concrete implementation (
CRDRequester) creates aResolutionRequestCRD and polls for its completion; tests inject a fake. This single-method interface is the seam between reconcilers and the resolution subsystem. - Implementations:
CRDRequester(production), inline fakes in reconciler tests. - Design quality: Minimal and focused — one method is all callers need. Follows the consumer-defined interface pattern: the reconciler packages own this interface definition, not the resolution package.
ResolvedResource#
- Package:
github.com/tektoncd/pipeline/pkg/resolution/resolver/framework - File:
pkg/resolution/resolver/framework/interface.go - Methods:
Data() []byte Annotations() map[string]string RefSource() *pipelinev1.RefSource - Purpose: The output of a successful resolution.
Data()is the raw YAML bytes of the fetched resource;Annotations()carries resolver-specific metadata;RefSource()provides provenance for supply chain security (Sigstore verification). - Implementations: Concrete structs within each resolver implementation.
- Design quality: Clean value-object interface. The
RefSource()method was added specifically to support trusted resource verification without coupling the resolver framework to security logic.
RunObject#
- Package:
github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1 - File:
pkg/apis/pipeline/v1beta1/run_interface.go - Methods:
// Embedded: runtime.Object (GetObjectKind, DeepCopyObject) // Embedded: metav1.ObjectMetaAccessor (GetObjectMeta) GetStatusCondition() apis.ConditionAccessor IsSuccessful() bool IsCancelled() bool HasStarted() bool IsDone() bool - Purpose: Unified status-query interface across all run types:
TaskRun,PipelineRun,Run, andCustomRun. Reconcilers and helper functions that need to inspect execution state useRunObjectso they work across all four types without type-switching. - Implementations:
v1.TaskRun,v1.PipelineRun,v1alpha1.Run,v1beta1.CustomRun - Design quality: Good use of interface embedding — builds on
runtime.Objectandmetav1.ObjectMetaAccessorfrom the Kubernetes API machinery instead of re-declaring those methods. The extension interfaceRunObjectWithRetries(addsGetRetryCount()) handles the case where only two types support retries.
TaskObject / PipelineObject#
- Package:
github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1 - File:
pkg/apis/pipeline/v1beta1/task_interface.go,pipeline_interface.go - Methods (TaskObject):
apis.Defaultable // SetDefaults(ctx) TaskMetadata() metav1.ObjectMeta TaskSpec() TaskSpec Copy() TaskObject - Methods (PipelineObject):
apis.Defaultable PipelineMetadata() metav1.ObjectMeta PipelineSpec() PipelineSpec Copy() PipelineObject - Purpose: Abstract over CRD version variants (v1alpha1/v1beta1/v1) of
TaskandPipeline. Code that retrieves and processes task/pipeline definitions uses these interfaces rather than concrete version-specific types, decoupling the reconciler from API version changes. - Implementations:
v1.Task,v1beta1.Task,v1.Pipeline,v1beta1.Pipeline - Design quality: Follows the same pattern as
RunObject. Embeddingapis.Defaultableis a Knative convention that ensures webhooks can callSetDefaultsuniformly. TheCopy()method returns the interface type to support safe deep-copy within the reconcile loop.
dag.Task / dag.Tasks#
- Package:
github.com/tektoncd/pipeline/pkg/reconciler/pipeline/dag - File:
pkg/reconciler/pipeline/dag/dag.go - Methods:
// Task HashKey() string Deps() []string // Tasks Items() []Task - Purpose: The DAG package is intentionally decoupled from Kubernetes types. These interfaces let
dag.Buildwork with any pipeline task representation. Callers implementTaskon theirPipelineTaskwrapper types, andTaskson their list wrappers. - Implementations:
PipelineRunFacts(pipelinerun reconciler) adapts realPipelineTaskobjects to these interfaces. - Design quality: Excellent. The pure-logic DAG package has zero Kubernetes dependencies. The two-interface design (
Task+Tasks) mirrors the standard collection pattern. Because this is a private package the interfaces primarily exist for clarity and testability rather than extensibility.
Waiter / Runner / PostWriter#
- Package:
github.com/tektoncd/pipeline/pkg/entrypoint - File:
pkg/entrypoint/entrypointer.go - Methods:
// Waiter Wait(ctx context.Context, file string, expectContent bool, breakpointOnFailure bool) error // Runner Run(ctx context.Context, args ...string) error // PostWriter Write(file, content string) - Purpose: These three interfaces decompose the
Entrypointer’s responsibilities so each can be tested in isolation.Waiterblocks until a semaphore file appears (previous step done),Runnerexecutes the actual user command, andPostWriterwrites the completion file signalling the next step. - Implementations: Production implementations in the same package; fakes used in tests via table-driven test structs.
- Design quality: Textbook application of the single-responsibility principle. Each interface is one method. By injecting these through the
Entrypointerstruct, the entire sequential-execution logic can be unit tested without spawning processes or touching the filesystem.
ControllerAPIClient / EntrypointerAPIClient (SPIRE)#
- Package:
github.com/tektoncd/pipeline/pkg/spire - File:
pkg/spire/spire.go - Methods (ControllerAPIClient):
AppendStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun) error CheckSpireVerifiedFlag(tr *v1beta1.TaskRun) bool Close() error CreateEntries(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod, ttl time.Duration) error DeleteEntry(ctx context.Context, tr *v1beta1.TaskRun, pod *corev1.Pod) error VerifyStatusInternalAnnotation(ctx context.Context, tr *v1beta1.TaskRun, logger *zap.SugaredLogger) error VerifyTaskRunResults(ctx context.Context, prs []result.RunResult, tr *v1beta1.TaskRun) error SetConfig(c spireconfig.SpireConfig) - Methods (EntrypointerAPIClient):
Close() error Sign(ctx context.Context, results []result.RunResult) ([]result.RunResult, error) - Purpose: Decouple the reconciler and entrypoint from the concrete SPIFFE/SPIRE gRPC client. When SPIRE is disabled (build tag), a no-op implementation is substituted without changing any calling code. The controller client manages SPIRE workload entries for pods; the entrypointer client signs step results.
- Implementations: Real gRPC client (
SpireControllerAPIClient,SpireEntrypointerAPIClient), no-op stubs for disabled/test builds. - Design quality: The split into two interfaces (controller vs. entrypointer) respects the different trust boundaries and lifetimes.
ControllerAPIClientis broader (8 methods) reflecting the controller’s security-management role, but all methods are coherently related to a single responsibility (SPIRE workload identity management).
remote.Resolver (legacy object fetcher)#
- Package:
github.com/tektoncd/pipeline/pkg/remote - File:
pkg/remote/resolver.go - Methods:
List(ctx context.Context) ([]ResolvedObject, error) Get(ctx context.Context, kind, name string) (runtime.Object, *v1.RefSource, error) - Purpose: The older, pre-CRD mechanism for fetching remote Tekton objects (e.g., from OCI registries). Still used by the bundle resolver’s internal implementation. Returns deserialized
runtime.Objectrather than raw bytes, unlike the newerResolvedResourceinterface. - Implementations:
OCIResolverinpkg/remote/oci/ - Design quality: Simple two-method interface consistent with standard read-only repository patterns. Being superseded by the CRD-based resolution framework but retained for OCI bundle support.
Interface patterns#
- Size distribution: Predominantly small — the majority are 1–4 methods.
ControllerAPIClientat 8 methods is the outlier, but each method is cohesive. No god interfaces visible outside of generated clientset code. - Embedding: Moderate use of interface composition.
RunObjectembedsruntime.Object+metav1.ObjectMetaAccessor.RunObjectWithRetriesembedsRunObject.TaskObjectandPipelineObjectembedapis.Defaultable(Knative). This builds on Kubernetes API machinery conventions rather than inventing new hierarchies. - Implicit satisfaction: Mixed. Core domain interfaces (
Resolver,Requester,RunObject) are defined in the package that consumes them (or in a dedicated interface file), not in the implementing packages. This is idiomatic Go. Generated clientset interfaces are defined alongside their implementations. - Stdlib interfaces used:
context.Contextpervasive (not an interface Tekton defines but used as the primary cancellation/injection mechanism). Theruntime.Objectandmetav1.ObjectMetaAccessorinterfaces from Kubernetes API machinery function like stdlib here. No directio.Reader/io.Writerusage at the core abstraction layer.
Key abstractions#
Resolver(remoteresolution framework): The primary extension point of the entire system. Every remote source (git, OCI, HTTP, Tekton Hub) is an implementation. The separation ofValidateandResolvelets the framework reject bad requests early. The optionalTimedResolutionandConfigWatchersub-interfaces keep the core contract minimal while allowing optional capabilities.Requester: A single-method interface that is the boundary between reconcilers and the async CRD-based resolution protocol. Its minimalism makes it trivially fakeable, which enables thorough unit testing of both reconcilers.RunObject: The polymorphic handle on all run types. Code that reports on, monitors, or finalizes runs (metrics, events, cloud events) uses this interface to work uniformly acrossTaskRun,PipelineRun, and custom run types without version-specific type switches.dag.Task/dag.Tasks: A zero-dependency abstraction layer that keeps the DAG engine pure. The DAG package can be tested and reasoned about entirely independently of Kubernetes. This is the clearest example of thoughtful interface placement in the codebase.Waiter/Runner/PostWriter: Three single-method interfaces that together make the entrypoint’s sequential-execution logic fully unit-testable. Demonstrates the Go idiom of decomposing a struct’s collaborators into narrow interfaces that express exactly what the struct needs from each dependency.
Interface-driven extensibility#
Tekton’s main extensibility mechanism is the Resolver interface. Adding a new source type (e.g., a new VCS or artifact store) requires only implementing Resolver, registering it with a controller, and deploying it — no changes to the core reconcilers. The framework handles CRD routing, timeouts, and error reporting. Optional ConfigWatcher and TimedResolution sub-interfaces follow the “optional interface” pattern (runtime type assertion) to add capabilities without breaking existing implementations.
The RunObject + TaskObject + PipelineObject interfaces provide a softer form of extensibility: CustomRun (v1beta1) lets third-party controllers intercept pipeline task execution, implementing RunObject to integrate with the PipelineRun reconciler’s status tracking.
The SPIRE interfaces (ControllerAPIClient, EntrypointerAPIClient) enable the security subsystem to be compiled out via build tags (spire / no spire) without any conditional logic in calling code — a clean build-time extensibility seam.