Skip to content

Commit 3fbb75d

Browse files
committed
feat: support PipelineRun running status reporting on Github with Kueue
this adds support running status of the PipelineRun when PipelineRuns are managed by Kueue. https://issues.redhat.com/browse/SRVKP-7738 Signed-off-by: Zaki Shaikh <[email protected]>
1 parent eacd4ac commit 3fbb75d

File tree

5 files changed

+112
-16
lines changed

5 files changed

+112
-16
lines changed

docs/content/docs/guide/repositorycrd.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ of the pipelineruns will be executed in alphabetical order, one after the
169169
other. At any given time, only one pipeline run will be in the running state,
170170
while the rest will be queued.
171171

172+
### Kueue - Kubernetes-native Job Queueing
173+
174+
Pipelines-as-Code now accommodates [Kueue](https://kueue.sigs.k8s.io/) as an alternative, Kubernetes-native solution for queuing PipelineRun.
175+
To get started, you can deploy the experimental integration provided by the [konflux-ci/tekton-kueue](https://github.com/konflux-ci/tekton-kueue) project. This allows you to schedule PipelineRuns through Kueue's queuing mechanism.
176+
177+
Note: The [konflux-ci/tekton-kueue](https://github.com/konflux-ci/tekton-kueue) project and the Pipelines-as-Code integration is only intended for testing
178+
It is only meant for experimentation and should not be used in production environments.
179+
172180
## Scoping GitHub token to a list of private and public repositories within and outside namespaces
173181

174182
By default, the GitHub token that Pipelines-as-Code generates is scoped only to the repository where the payload comes from.

pkg/pipelineascode/pipelineascode.go

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ import (
1414
"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
1515
"github.com/openshift-pipelines/pipelines-as-code/pkg/matcher"
1616
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
17-
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
1817
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
1918
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
2019
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
@@ -241,12 +240,23 @@ func (p *PacRun) startPR(ctx context.Context, match matcher.Match) (*tektonv1.Pi
241240
OriginalPipelineRunName: pr.GetAnnotations()[keys.OriginalPRName],
242241
}
243242

243+
patchAnnotations := map[string]string{}
244+
patchLabels := map[string]string{}
245+
whatPatching := ""
244246
// if pipelineRun is in pending state then report status as queued
245247
if pr.Spec.Status == tektonv1.PipelineRunSpecStatusPending {
246248
status.Status = queuedStatus
247249
if status.Text, err = mt.MakeTemplate(p.vcx.GetTemplate(provider.QueueingPipelineType)); err != nil {
248250
return nil, fmt.Errorf("cannot create message template: %w", err)
249251
}
252+
// If the PipelineRun is in the "queued" state, add the appropriate label and annotation.
253+
// These are later used by the watcher to determine whether the PipelineRun status
254+
// should be reported back to the Git provider. We do add the `state` annotations and label when
255+
// concurrency is enabled but this would happen when PipelineRun's status has been changed by
256+
// the other controller and PaC is not aware of that change.
257+
whatPatching = "annotations.state and labels.state"
258+
patchAnnotations[keys.State] = kubeinteraction.StateQueued
259+
patchLabels[keys.State] = kubeinteraction.StateQueued
250260
}
251261

252262
if err := p.vcx.CreateStatus(ctx, p.event, status); err != nil {
@@ -257,7 +267,12 @@ func (p *PacRun) startPR(ctx context.Context, match matcher.Match) (*tektonv1.Pi
257267

258268
// Patch pipelineRun with logURL annotation, skips for GitHub App as we patch logURL while patching CheckrunID
259269
if _, ok := pr.Annotations[keys.InstallationID]; !ok {
260-
pr, err = action.PatchPipelineRun(ctx, p.logger, "logURL", p.run.Clients.Tekton, pr, getLogURLMergePatch(p.run.Clients, pr))
270+
patchAnnotations[keys.LogURL] = p.run.Clients.ConsoleUI().DetailURL(pr)
271+
whatPatching = "annotations.logURL, " + whatPatching
272+
}
273+
274+
if len(patchAnnotations) > 0 || len(patchLabels) > 0 {
275+
pr, err = action.PatchPipelineRun(ctx, p.logger, whatPatching, p.run.Clients.Tekton, pr, getMergePatch(patchAnnotations, patchLabels))
261276
if err != nil {
262277
// we still return the created PR with error, and allow caller to decide what to do with the PR, and avoid
263278
// unneeded SIGSEGV's
@@ -268,12 +283,11 @@ func (p *PacRun) startPR(ctx context.Context, match matcher.Match) (*tektonv1.Pi
268283
return pr, nil
269284
}
270285

271-
func getLogURLMergePatch(clients clients.Clients, pr *tektonv1.PipelineRun) map[string]any {
286+
func getMergePatch(annotations, labels map[string]string) map[string]any {
272287
return map[string]any{
273288
"metadata": map[string]any{
274-
"annotations": map[string]string{
275-
keys.LogURL: clients.ConsoleUI().DetailURL(pr),
276-
},
289+
"annotations": annotations,
290+
"labels": labels,
277291
},
278292
}
279293
}

pkg/pipelineascode/pipelineascode_test.go

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,49 @@ func TestRun(t *testing.T) {
497497
finalStatusText: "User fantasio is not allowed to trigger CI by GitOps comment on push commit in this repo.",
498498
skipReplyingOrgPublicMembers: true,
499499
},
500+
{
501+
name: "pull request/pipelinerun created in pending state (state changed by other controller)",
502+
runevent: info.Event{
503+
Event: &github.PullRequestEvent{
504+
PullRequest: &github.PullRequest{
505+
Number: github.Ptr(666),
506+
},
507+
},
508+
SHA: "fromwebhook",
509+
Organization: "owner",
510+
Sender: "owner",
511+
Repository: "repo",
512+
URL: "https://service/documentation",
513+
HeadBranch: "press",
514+
BaseBranch: "main",
515+
EventType: "pull_request",
516+
TriggerTarget: "pull_request",
517+
PullRequestNumber: 666,
518+
InstallationID: 1234,
519+
},
520+
tektondir: "testdata/pending_pipelinerun",
521+
},
522+
{
523+
name: "pull request/pipelinerun created in pending state without installationID (state changed by other controller)",
524+
runevent: info.Event{
525+
Event: &github.PullRequestEvent{
526+
PullRequest: &github.PullRequest{
527+
Number: github.Ptr(666),
528+
},
529+
},
530+
SHA: "fromwebhook",
531+
Organization: "owner",
532+
Sender: "owner",
533+
Repository: "repo",
534+
URL: "https://service/documentation",
535+
HeadBranch: "press",
536+
BaseBranch: "main",
537+
EventType: "pull_request",
538+
TriggerTarget: "pull_request",
539+
PullRequestNumber: 666,
540+
},
541+
tektondir: "testdata/pending_pipelinerun",
542+
},
500543
}
501544
for _, tt := range tests {
502545
t.Run(tt.name, func(t *testing.T) {
@@ -675,10 +718,10 @@ func TestRun(t *testing.T) {
675718
secretName, ok := pr.GetAnnotations()[keys.GitAuthSecret]
676719
assert.Assert(t, ok, "Cannot find secret %s on annotations", secretName)
677720
}
678-
if tt.concurrencyLimit > 0 {
679-
concurrencyLimit, ok := pr.GetAnnotations()[keys.State]
680-
assert.Assert(t, ok, "State hasn't been set on PR", concurrencyLimit)
681-
assert.Equal(t, concurrencyLimit, kubeinteraction.StateQueued)
721+
if tt.concurrencyLimit > 0 || pr.Spec.Status == pipelinev1.PipelineRunSpecStatusPending {
722+
state, ok := pr.GetAnnotations()[keys.State]
723+
assert.Assert(t, ok, "State hasn't been set on PR", state)
724+
assert.Equal(t, state, kubeinteraction.StateQueued)
682725
}
683726
}
684727
}
@@ -690,12 +733,8 @@ func TestGetLogURLMergePatch(t *testing.T) {
690733
con := consoleui.FallBackConsole{}
691734
clients := clients.Clients{}
692735
clients.SetConsoleUI(con)
693-
pr := &pipelinev1.PipelineRun{
694-
ObjectMeta: metav1.ObjectMeta{
695-
Name: "test-pipeline-run",
696-
},
697-
}
698-
result := getLogURLMergePatch(clients, pr)
736+
ann := map[string]string{keys.LogURL: con.URL()}
737+
result := getMergePatch(ann, map[string]string{})
699738
m, ok := result["metadata"].(map[string]any)
700739
assert.Assert(t, ok)
701740
a, ok := m["annotations"].(map[string]string)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: tekton.dev/v1beta1
2+
kind: PipelineRun
3+
metadata:
4+
generateName: pull_request-test1
5+
annotations:
6+
pipelinesascode.tekton.dev/on-target-branch: "[main]"
7+
pipelinesascode.tekton.dev/on-event: "[pull_request]"
8+
spec:
9+
status: PipelineRunPending
10+
pipelineSpec:
11+
tasks:
12+
- name: max
13+
taskSpec:
14+
steps:
15+
- name: success
16+
image: registry.access.redhat.com/ubi9/ubi-minimal
17+
script: 'exit 0'

pkg/reconciler/reconciler.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,24 @@ func (r *Reconciler) ReconcileKind(ctx context.Context, pr *tektonv1.PipelineRun
6060
return nil
6161
}
6262

63+
reason := ""
64+
if len(pr.Status.GetConditions()) > 0 {
65+
reason = pr.Status.GetConditions()[0].GetReason()
66+
}
67+
// This condition handles cases where the PipelineRun has entered a "Running" state,
68+
// but its status in the Git provider remains "queued" (e.g., due to updates made by
69+
// another controller outside PaC). To maintain consistency between the PipelineRun
70+
// status and the Git provider status, we update both the PipelineRun resource and
71+
// the corresponding status on the Git provider here.
72+
if reason == string(tektonv1.PipelineRunReasonRunning) && state == kubeinteraction.StateQueued {
73+
repoName := pr.GetAnnotations()[keys.Repository]
74+
repo, err := r.repoLister.Repositories(pr.Namespace).Get(repoName)
75+
if err != nil {
76+
return fmt.Errorf("failed to get repository CR: %w", err)
77+
}
78+
return r.updatePipelineRunToInProgress(ctx, logger, repo, pr)
79+
}
80+
6381
// if its a GitHub App pipelineRun PR then process only if check run id is added otherwise wait
6482
if _, ok := pr.Annotations[keys.InstallationID]; ok {
6583
if _, ok := pr.Annotations[keys.CheckRunID]; !ok {

0 commit comments

Comments
 (0)