Skip to content

Commit aaa8619

Browse files
committed
Add Support for managedBy field in TaskRun and PipelineRun
Added a "managedBy" field to delegate responsibility of controlling the lifecycle of PipelineRuns/TaskRuns. The semantics of the field: Whenever the value is set, and it does not point to the built-in controller, then we skip the reconciliation. * The field is immutable * The field is not defaulted Assisted By: Gemini
1 parent d97e81a commit aaa8619

29 files changed

+858
-14
lines changed

config/300-crds/300-pipelinerun.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,13 @@ spec:
6161
description: PipelineRunSpec defines the desired state of PipelineRun
6262
type: object
6363
properties:
64+
managedBy:
65+
description: |-
66+
ManagedBy indicates which controller is responsible for reconciling
67+
this resource. If unset or set to "tekton.dev/pipeline", the default
68+
Tekton controller will manage this resource.
69+
This field is immutable.
70+
type: string
6471
params:
6572
description: Params is a list of parameter names and values.
6673
type: array
@@ -3135,6 +3142,13 @@ spec:
31353142
description: PipelineRunSpec defines the desired state of PipelineRun
31363143
type: object
31373144
properties:
3145+
managedBy:
3146+
description: |-
3147+
ManagedBy indicates which controller is responsible for reconciling
3148+
this resource. If unset or set to "tekton.dev/pipeline", the default
3149+
Tekton controller will manage this resource.
3150+
This field is immutable.
3151+
type: string
31383152
params:
31393153
description: Params is a list of parameter names and values.
31403154
type: array

config/300-crds/300-taskrun.yaml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,13 @@ spec:
136136
if enabled, pause TaskRun on failure of a step
137137
failed step will not exit
138138
type: string
139+
managedBy:
140+
description: |-
141+
ManagedBy indicates which controller is responsible for reconciling
142+
this resource. If unset or set to "tekton.dev/pipeline", the default
143+
Tekton controller will manage this resource.
144+
This field is immutable.
145+
type: string
139146
params:
140147
description: Params is a list of Param
141148
type: array
@@ -2336,6 +2343,13 @@ spec:
23362343
if enabled, pause TaskRun on failure of a step
23372344
failed step will not exit
23382345
type: string
2346+
managedBy:
2347+
description: |-
2348+
ManagedBy indicates which controller is responsible for reconciling
2349+
this resource. If unset or set to "tekton.dev/pipeline", the default
2350+
Tekton controller will manage this resource.
2351+
This field is immutable.
2352+
type: string
23392353
params:
23402354
description: Params is a list of Param
23412355
type: array

docs/pipelineruns.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ weight: 204
3535
- [The <code>status</code> field](#the-status-field)
3636
- [Monitoring execution status](#monitoring-execution-status)
3737
- [Marking off user errors](#marking-off-user-errors)
38+
- [Delegating reconciliation](#delegating-reconciliation)
3839
- [Cancelling a <code>PipelineRun</code>](#cancelling-a-pipelinerun)
3940
- [Gracefully cancelling a <code>PipelineRun</code>](#gracefully-cancelling-a-pipelinerun)
4041
- [Gracefully stopping a <code>PipelineRun</code>](#gracefully-stopping-a-pipelinerun)
@@ -80,6 +81,7 @@ A `PipelineRun` definition supports the following fields:
8081
- [`timeouts`](#configuring-a-failure-timeout) - Specifies the timeout before the `PipelineRun` fails. `timeouts` allows more granular timeout configuration, at the pipeline, tasks, and finally levels
8182
- [`podTemplate`](#specifying-a-pod-template) - Specifies a [`Pod` template](./podtemplates.md) to use as the basis for the configuration of the `Pod` that executes each `Task`.
8283
- [`workspaces`](#specifying-workspaces) - Specifies a set of workspace bindings which must match the names of workspaces declared in the pipeline being used.
84+
- [`managedBy`](#delegating-reconciliation) - Specifies the controller responsible for managing this PipelineRun's lifecycle.
8385

8486
[kubernetes-overview]:
8587
https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
@@ -1642,6 +1644,39 @@ NAME STARTED DURATION STATUS
16421644
pipelinerun-with-params 5 seconds ago 0s Failed(ParameterMissing)
16431645
```
16441646

1647+
## Delegating reconciliation
1648+
1649+
The `managedBy` field allows you to delegate the responsibility of managing a `PipelineRun`'s lifecycle to an external controller. When this field is set to a value other than `"tekton.dev/pipeline"`, the Tekton Pipeline controller will ignore the `PipelineRun`, allowing your external controller to take full control. This delegation enables several advanced use cases, such as implementing custom pipeline execution logic, integrating with external management tools, using advanced scheduling algorithms, or coordinating PipelineRuns across multiple clusters (like using [MultiKueue](https://kueue.sigs.k8s.io/docs/concepts/multikueue/)).
1650+
1651+
### Example
1652+
1653+
```yaml
1654+
apiVersion: tekton.dev/v1
1655+
kind: PipelineRun
1656+
metadata:
1657+
name: externally-managed-pipeline
1658+
spec:
1659+
pipelineRef:
1660+
name: my-pipeline
1661+
managedBy: "my-custom-controller"
1662+
```
1663+
1664+
### Behavior
1665+
1666+
- **When `managedBy` is empty**: The Tekton Pipeline controller manages the PipelineRun normally
1667+
- **When `managedBy` is set to `"tekton.dev/pipeline"`**: The Tekton Pipeline controller manages the PipelineRun normally
1668+
- **When `managedBy` is set to any other value**: The Tekton Pipeline controller ignores the PipelineRun completely
1669+
- **Immutability**: The `managedBy` field is immutable and cannot be changed after creation
1670+
1671+
### External controller responsibilities
1672+
1673+
When you set `managedBy` to a custom value, your external controller is responsible for:
1674+
1675+
- Creating and managing TaskRuns
1676+
- Updating PipelineRun status
1677+
- Handling timeouts and cancellations
1678+
- Managing retries and error handling
1679+
16451680
## Cancelling a `PipelineRun`
16461681

16471682
To cancel a `PipelineRun` that's currently executing, update its definition

docs/taskruns.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ weight: 202
4040
- [Debug Environment](#debug-environment)
4141
- [Events](events.md#taskruns)
4242
- [Running a TaskRun Hermetically](hermetic.md)
43+
- [Delegating reconciliation](#delegating-reconciliation)
4344
- [Code examples](#code-examples)
4445
- [Example `TaskRun` with a referenced `Task`](#example-taskrun-with-a-referenced-task)
4546
- [Example `TaskRun` with an embedded `Task`](#example-taskrun-with-an-embedded-task)
@@ -79,6 +80,7 @@ A `TaskRun` definition supports the following fields:
7980
- [`debug`](#debugging-a-taskrun)- Specifies any breakpoints and debugging configuration for the `Task` execution.
8081
- [`stepSpecs`](#configuring-task-steps-and-sidecars-in-a-taskrun) - Specifies configuration to use to override the `Task`'s `Step`s.
8182
- [`sidecarSpecs`](#configuring-task-steps-and-sidecars-in-a-taskrun) - Specifies configuration to use to override the `Task`'s `Sidecar`s.
83+
- [`managedBy`](#delegating-reconciliation) - Specifies the controller responsible for managing this TaskRun's lifecycle.
8284

8385
[kubernetes-overview]:
8486
https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields
@@ -1112,6 +1114,40 @@ this is available as a [TaskRun example](../examples/v1/taskruns/run-steps-as-no
11121114

11131115
More information about Pod and Container Security Contexts can be found via the [Kubernetes website](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/#set-the-security-context-for-a-pod).
11141116

1117+
## Delegating reconciliation
1118+
1119+
The `managedBy` field allows you to delegate the responsibility of managing a `TaskRun`'s lifecycle to an external controller. When this field is set to a value other than `"tekton.dev/pipeline"`, the Tekton Pipeline controller will ignore the `TaskRun`, allowing your external controller to take full control. This delegation enables several advanced use cases, such as implementing custom pipeline execution logic, integrating with external management tools, using advanced scheduling algorithms, or coordinating PipelineRuns across multiple clusters (like using [MultiKueue](https://kueue.sigs.k8s.io/docs/concepts/multikueue/)).
1120+
1121+
### Example
1122+
1123+
```yaml
1124+
apiVersion: tekton.dev/v1
1125+
kind: TaskRun
1126+
metadata:
1127+
name: externally-managed-task
1128+
spec:
1129+
taskRef:
1130+
name: my-task
1131+
managedBy: "my-custom-controller"
1132+
```
1133+
1134+
### Behavior
1135+
1136+
- **When `managedBy` is empty**: The Tekton Pipeline controller manages the TaskRun normally
1137+
- **When `managedBy` is set to `"tekton.dev/pipeline"`**: The Tekton Pipeline controller manages the TaskRun normally
1138+
- **When `managedBy` is set to any other value**: The Tekton Pipeline controller ignores the TaskRun completely
1139+
- **Immutability**: The `managedBy` field is immutable and cannot be changed after creation
1140+
1141+
### External controller responsibilities
1142+
1143+
When you set `managedBy` to a custom value, your external controller is responsible for:
1144+
1145+
- Creating and managing Pods
1146+
- Updating TaskRun status
1147+
- Handling timeouts and cancellations
1148+
- Managing retries and error handling
1149+
- Processing step results and artifacts
1150+
11151151
---
11161152

11171153
Except as otherwise noted, the content of this page is licensed under the

pkg/apis/pipeline/register.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ const (
5555
// MemberOfLabelKey is used as the label identifier for a PipelineTask
5656
// Set to Tasks/Finally depending on the position of the PipelineTask
5757
MemberOfLabelKey = GroupName + "/memberOf"
58+
59+
// ManagedBy is the value of the "managedBy" field for resources
60+
// managed by the Tekton Pipeline controller.
61+
ManagedBy = GroupName + "/pipeline"
5862
)
5963

6064
var (

pkg/apis/pipeline/v1/openapi_generated.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/pipeline/v1/pipelinerun_types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ type PipelineRunSpec struct {
283283
// +optional
284284
// +listType=atomic
285285
TaskRunSpecs []PipelineTaskRunSpec `json:"taskRunSpecs,omitempty"`
286+
// ManagedBy indicates which controller is responsible for reconciling
287+
// this resource. If unset or set to "tekton.dev/pipeline", the default
288+
// Tekton controller will manage this resource.
289+
// This field is immutable.
290+
// +optional
291+
ManagedBy *string `json:"managedBy,omitempty"`
286292
}
287293

288294
// TimeoutFields allows granular specification of pipeline, task, and finally timeouts

pkg/apis/pipeline/v1/pipelinerun_validation.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,11 @@ func (ps *PipelineRunSpec) ValidateUpdate(ctx context.Context) (errs *apis.Field
138138
if !ok || oldObj == nil {
139139
return
140140
}
141+
142+
if (oldObj.Spec.ManagedBy == nil) != (ps.ManagedBy == nil) || (oldObj.Spec.ManagedBy != nil && *oldObj.Spec.ManagedBy != *ps.ManagedBy) {
143+
errs = errs.Also(apis.ErrInvalidValue("managedBy is immutable", "spec.managedBy"))
144+
}
145+
141146
if oldObj.IsDone() {
142147
// try comparing without any copying first
143148
// this handles the common case where only finalizers changed
@@ -162,10 +167,10 @@ func (ps *PipelineRunSpec) ValidateUpdate(ctx context.Context) (errs *apis.Field
162167
// Handle started but not done case
163168
old := oldObj.Spec.DeepCopy()
164169
old.Status = ps.Status
170+
old.ManagedBy = ps.ManagedBy // Already tested before
165171
if !equality.Semantic.DeepEqual(old, ps) {
166172
errs = errs.Also(apis.ErrInvalidValue("Once the PipelineRun has started, only status updates are allowed", ""))
167173
}
168-
169174
return
170175
}
171176

pkg/apis/pipeline/v1/pipelinerun_validation_test.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
corev1 "k8s.io/api/core/v1"
3333
corev1resources "k8s.io/apimachinery/pkg/api/resource"
3434
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
35+
ptr "k8s.io/utils/pointer"
3536
"knative.dev/pkg/apis"
3637
duckv1 "knative.dev/pkg/apis/duck/v1"
3738
)
@@ -1751,6 +1752,56 @@ func TestPipelineRunSpec_ValidateUpdate(t *testing.T) {
17511752
Message: `invalid value: Once the PipelineRun is complete, no updates are allowed`,
17521753
Paths: []string{""},
17531754
},
1755+
}, {
1756+
name: "is update ctx, baseline is not done, managedBy changes",
1757+
baselinePipelineRun: &v1.PipelineRun{
1758+
Spec: v1.PipelineRunSpec{
1759+
ManagedBy: ptr.String("tekton.dev/pipeline"),
1760+
},
1761+
Status: v1.PipelineRunStatus{
1762+
Status: duckv1.Status{
1763+
Conditions: duckv1.Conditions{
1764+
{Type: apis.ConditionSucceeded, Status: corev1.ConditionUnknown},
1765+
},
1766+
},
1767+
},
1768+
},
1769+
pipelineRun: &v1.PipelineRun{
1770+
Spec: v1.PipelineRunSpec{
1771+
ManagedBy: ptr.String("some-other-controller"),
1772+
},
1773+
},
1774+
isCreate: false,
1775+
isUpdate: true,
1776+
expectedError: apis.FieldError{
1777+
Message: `invalid value: managedBy is immutable`,
1778+
Paths: []string{"spec.managedBy"},
1779+
},
1780+
}, {
1781+
name: "is update ctx, baseline is unknown, managedBy changes",
1782+
baselinePipelineRun: &v1.PipelineRun{
1783+
Spec: v1.PipelineRunSpec{
1784+
ManagedBy: ptr.String("tekton.dev/pipeline"),
1785+
},
1786+
Status: v1.PipelineRunStatus{
1787+
Status: duckv1.Status{
1788+
Conditions: duckv1.Conditions{
1789+
{Type: apis.ConditionSucceeded, Status: corev1.ConditionUnknown},
1790+
},
1791+
},
1792+
},
1793+
},
1794+
pipelineRun: &v1.PipelineRun{
1795+
Spec: v1.PipelineRunSpec{
1796+
ManagedBy: ptr.String("some-other-controller"),
1797+
},
1798+
},
1799+
isCreate: false,
1800+
isUpdate: true,
1801+
expectedError: apis.FieldError{
1802+
Message: `invalid value: managedBy is immutable`,
1803+
Paths: []string{"spec.managedBy"},
1804+
},
17541805
},
17551806
}
17561807

pkg/apis/pipeline/v1/swagger.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,10 @@
661661
"description": "PipelineRunSpec defines the desired state of PipelineRun",
662662
"type": "object",
663663
"properties": {
664+
"managedBy": {
665+
"description": "ManagedBy indicates which controller is responsible for reconciling this resource. If unset or set to \"tekton.dev/pipeline\", the default Tekton controller will manage this resource. This field is immutable.",
666+
"type": "string"
667+
},
664668
"params": {
665669
"description": "Params is a list of parameter names and values.",
666670
"type": "array",
@@ -2049,6 +2053,10 @@
20492053
"debug": {
20502054
"$ref": "#/definitions/v1.TaskRunDebug"
20512055
},
2056+
"managedBy": {
2057+
"description": "ManagedBy indicates which controller is responsible for reconciling this resource. If unset or set to \"tekton.dev/pipeline\", the default Tekton controller will manage this resource. This field is immutable.",
2058+
"type": "string"
2059+
},
20522060
"params": {
20532061
"type": "array",
20542062
"items": {

0 commit comments

Comments
 (0)