Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Annotation to integrate with Results #1245

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pkg/apis/pipelinesascode/keys/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,10 @@ const (
// PublicGithubAPIURL default is "https://api.github.com" but it can be overridden by X-GitHub-Enterprise-Host header.
PublicGithubAPIURL = "https://api.github.com"
// InstallationURL gives us the Installation ID for the GitHub Application.
InstallationURL = "/app/installations"
GithubApplicationID = "github-application-id"
GithubPrivateKey = "github-private-key"
InstallationURL = "/app/installations"
GithubApplicationID = "github-application-id"
GithubPrivateKey = "github-private-key"
ResultsRecordSummary = "results.tekton.dev/recordSummaryAnnotations"
)

var ParamsRe = regexp.MustCompile(`{{([^}]{2,})}}`)
15 changes: 14 additions & 1 deletion pkg/kubeinteraction/labels.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package kubeinteraction

import (
"fmt"
"strconv"

"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode"
Expand All @@ -19,7 +20,11 @@ const (
StateFailed = "failed"
)

func AddLabelsAndAnnotations(event *info.Event, pipelineRun *tektonv1.PipelineRun, repo *apipac.Repository, providerinfo *info.ProviderConfig) {
func AddLabelsAndAnnotations(event *info.Event, pipelineRun *tektonv1.PipelineRun, repo *apipac.Repository, providerinfo *info.ProviderConfig) error {
if event == nil {
return fmt.Errorf("nil event")
}

// Add labels on the soon to be created pipelinerun so UI/CLI can easily
// query them.
labels := map[string]string{
Expand Down Expand Up @@ -85,4 +90,12 @@ func AddLabelsAndAnnotations(event *info.Event, pipelineRun *tektonv1.PipelineRu
for k, v := range annotations {
pipelineRun.Annotations[k] = v
}

// Add annotations to PipelineRuns to integrate with Tekton Results
err := AddResultsAnnotation(event, pipelineRun)
if err != nil {
return fmt.Errorf("failed to add results annotations with error: %w", err)
}

return nil
}
3 changes: 2 additions & 1 deletion pkg/kubeinteraction/labels_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ func TestAddLabelsAndAnnotations(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
AddLabelsAndAnnotations(tt.args.event, tt.args.pipelineRun, tt.args.repo, &info.ProviderConfig{})
err := AddLabelsAndAnnotations(tt.args.event, tt.args.pipelineRun, tt.args.repo, &info.ProviderConfig{})
assert.NilError(t, err)
assert.Assert(t, tt.args.pipelineRun.Labels[keys.URLOrg] == tt.args.event.Organization, "'%s' != %s",
tt.args.pipelineRun.Labels[keys.URLOrg], tt.args.event.Organization)
assert.Assert(t, tt.args.pipelineRun.Annotations[keys.URLOrg] == tt.args.event.Organization, "'%s' != %s",
Expand Down
37 changes: 37 additions & 0 deletions pkg/kubeinteraction/resultsannotation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package kubeinteraction

import (
"encoding/json"

"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
)

type ResultAnnotation struct {
Repo string `json:"repo"`
Commit string `json:"commit"`
EventType string `json:"eventType"`
PullRequestID int `json:"pull_request-id,omitempty"`
}

// Add annotation to PipelineRuns produced by PaC for capturing additional
// data specific for TektonResults.
func AddResultsAnnotation(event *info.Event, pipelineRun *tektonv1.PipelineRun) error {
resultAnnotation := ResultAnnotation{
Repo: event.Repository,
Commit: event.SHA,
EventType: event.EventType,
PullRequestID: event.PullRequestNumber,
}

resAnnotationJSON, err := json.Marshal(resultAnnotation)
if err != nil {
return err
}

// append the result annotation
pipelineRun.Annotations[keys.ResultsRecordSummary] = string(resAnnotationJSON)

return nil
}
68 changes: 68 additions & 0 deletions pkg/kubeinteraction/resultsannotation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package kubeinteraction

import (
"encoding/json"
"testing"

"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
v1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"gotest.tools/v3/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestAddResultsAnnotation(t *testing.T) {
testCases := []struct {
name string
event *info.Event
expectedError error
}{
{
name: "Valid Event",
event: &info.Event{
Repository: "tektoncd/results",
SHA: "8789abb6",
EventType: "PR",
PullRequestNumber: 123,
},
expectedError: nil,
},
{
name: "Empty Event",
event: &info.Event{},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if event is empty then accessing attributes of event such as event.SHA will fail right 🤔

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fields of the event that is being used for creating ResultsAnnotation should have default values in case it's an empty struct, right? I think that's why the unit tests pass.

expectedError: nil,
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
// Prepare test data
pipelineRun := &v1.PipelineRun{
ObjectMeta: metav1.ObjectMeta{
Annotations: map[string]string{},
},
}
err := AddResultsAnnotation(tt.event, pipelineRun)
assert.NilError(t, err)

// If no error, check annotations
if err == nil {
// Expected result annotation
resultAnnotation := ResultAnnotation{
Repo: tt.event.Repository,
Commit: tt.event.SHA,
EventType: tt.event.EventType,
PullRequestID: tt.event.PullRequestNumber,
}
expectedJSON, err := json.Marshal(resultAnnotation)
if err != nil {
t.Fatalf("Failed to marshal expected result annotation: %v", err)
}
expectedAnnotation := string(expectedJSON)

// Check if annotation is added correctly
assert.Assert(t, pipelineRun.Annotations[keys.ResultsRecordSummary] == expectedAnnotation, "Unexpected record summary annotation. Expected: %s, Got: %s", expectedAnnotation, pipelineRun.Annotations[keys.ResultsRecordSummary])
}
})
}
}
9 changes: 6 additions & 3 deletions pkg/pipelineascode/pipelineascode.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,10 @@ func (p *PacRun) startPR(ctx context.Context, match matcher.Match) (*tektonv1.Pi
}

// Add labels and annotations to pipelinerun
kubeinteraction.AddLabelsAndAnnotations(p.event, match.PipelineRun, match.Repo, p.vcx.GetConfig())
err := kubeinteraction.AddLabelsAndAnnotations(p.event, match.PipelineRun, match.Repo, p.vcx.GetConfig())
if err != nil {
p.logger.Errorf("Error adding labels/annotations to PipelineRun '%s' in namespace '%s': %v", match.PipelineRun.GetName(), match.Repo.GetNamespace(), err)
}

// if concurrency is defined then start the pipelineRun in pending state and
// state as queued
Expand All @@ -162,8 +165,8 @@ func (p *PacRun) startPR(ctx context.Context, match matcher.Match) (*tektonv1.Pi
match.PipelineRun, metav1.CreateOptions{})
if err != nil {
// we need to make difference between markdown error and normal error that goes to namespace/controller stream
return nil, fmt.Errorf("creating pipelinerun %s in namespace %s has failed.\n\nTekton Controller has reported this error: ```%s``` ", match.PipelineRun.GetGenerateName(),
match.Repo.GetNamespace(), err.Error())
return nil, fmt.Errorf("creating pipelinerun %s in namespace %s has failed.\n\nTekton Controller has reported this error: ```%w``` ", match.PipelineRun.GetGenerateName(),
match.Repo.GetNamespace(), err)
}

// Create status with the log url
Expand Down
61 changes: 61 additions & 0 deletions test/gitea_results_annotation_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
//go:build e2e
// +build e2e

package test

import (
"context"
"encoding/json"
"strconv"
"testing"

"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/keys"
"github.com/openshift-pipelines/pipelines-as-code/pkg/kubeinteraction"
tgitea "github.com/openshift-pipelines/pipelines-as-code/test/pkg/gitea"
"github.com/openshift-pipelines/pipelines-as-code/test/pkg/options"
"gotest.tools/v3/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func TestGiteaResultsAnnotations(t *testing.T) {
topts := &tgitea.TestOpts{
Regexp: successRegexp,
TargetEvent: options.PullRequestEvent,
YAMLFiles: map[string]string{
".tekton/pipeline.yaml": "testdata/pipelinerun.yaml",
},
CheckForStatus: "success",
}
defer tgitea.TestPR(t, topts)()

// assertions for checking results specific annotation in the PipelineRuns manifest here
prs, err := topts.ParamsRun.Clients.Tekton.TektonV1().PipelineRuns(topts.TargetNS).List(context.Background(), metav1.ListOptions{})
assert.NilError(t, err)
for _, pr := range prs.Items {
annotations := pr.GetAnnotations()
assert.Assert(t, annotations != nil, "Annotations should not be nil")

val, exists := annotations[keys.PullRequest]
if !exists {
t.Fatalf("Annotation %s does not exist", keys.PullRequest)
}

pullRequestNumber, err := strconv.Atoi(val)
assert.NilError(t, err)

// Assert specific annotation
resultAnnotation := kubeinteraction.ResultAnnotation{
Repo: topts.TargetNS,
Commit: topts.PullRequest.Head.Sha,
EventType: topts.TargetEvent,
PullRequestID: pullRequestNumber,
}
expectedJSON, err := json.Marshal(resultAnnotation)
assert.NilError(t, err)
expectedResultAnnotation := string(expectedJSON)

// an example of results annotation format
// results.tekton.dev/recordSummaryAnnotations:{"repo":"pac-demo","commit":"62f8c8b7e4c3fc38cfbe7fcce2660e5b95de2d9a","eventType":"pull_request","pull_request-id":7}
assert.Equal(t, annotations[keys.ResultsRecordSummary], expectedResultAnnotation, "Unexpected annotation value")
}
}
Loading