Skip to content

Commit

Permalink
Merge pull request konflux-ci#827 from hongweiliu17/STONEINTG-997
Browse files Browse the repository at this point in the history
feat(STONEINTG-997): save build PLR info to build PLR and snapshot metadata
  • Loading branch information
hongweiliu17 authored Aug 6, 2024
2 parents 64a5f50 + faecbfe commit f913d46
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 0 deletions.
6 changes: 6 additions & 0 deletions docs/build_pipeline_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ flowchart TD
%% Node definitions
predicate((PREDICATE: <br> Filter events related to <br> PipelineRuns))
new_pipeline_run{Pipeline created?}
new_pipeline_run_without_prgroup{PR group is added to pipelineRun metadata?}
get_pipeline_run{Pipeline updated?}
failed_pipeline_run{Pipeline failed?}
finalizer_exists{Does the finalizer already exist?}
Expand All @@ -24,15 +25,20 @@ add_finalizer(Add finalizer to build PLR)
remove_finalizer(Remove finalizer from build PLR)
error[Return error]
continue[Continue processing]
update_metadata(add PR group info to build pipelineRun metadata)
%% Node connections
predicate --> get_pipeline_run
predicate --> new_pipeline_run
predicate --> new_pipeline_run_without_prgroup
predicate --> failed_pipeline_run
new_pipeline_run --Yes--> finalizer_exists
finalizer_exists --No--> add_finalizer
add_finalizer --> continue
failed_pipeline_run --Yes --> remove_finalizer
new_pipeline_run_without_prgroup --No --> update_metadata
new_pipeline_run_without_prgroup --Yes --> continue
update_metadata --> continue
get_pipeline_run --Yes --> retrieve_associated_entity
get_pipeline_run --No --> error
retrieve_associated_entity --No --> error
Expand Down
9 changes: 9 additions & 0 deletions gitops/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,15 @@ const (
// SnapshotStatusReportAnnotation contains metadata of tests related to status reporting to git provider
SnapshotStatusReportAnnotation = "test.appstudio.openshift.io/git-reporter-status"

// PRGroupAnnotation contains the pr group name
PRGroupAnnotation = "test.appstudio.openshift.io/pr-group"

// PRGroupHashLabel contains the pr group name in sha format
PRGroupHashLabel = "test.appstudio.openshift.io/pr-group-sha"

// BuildPipelineRunStartTime contains the start time of build pipelineRun
BuildPipelineRunStartTime = "test.appstudio.openshift.io/pipelinerunstarttime"

// BuildPipelineRunPrefix contains the build pipeline run related labels and annotations
BuildPipelineRunPrefix = "build.appstudio"

Expand Down
58 changes: 58 additions & 0 deletions internal/controller/buildpipeline/buildpipeline_adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (
"github.com/konflux-ci/integration-service/loader"
"github.com/konflux-ci/integration-service/tekton"
"github.com/konflux-ci/operator-toolkit/controller"
"github.com/konflux-ci/operator-toolkit/metadata"
applicationapiv1alpha1 "github.com/redhat-appstudio/application-api/api/v1alpha1"
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"k8s.io/apimachinery/pkg/api/errors"
Expand Down Expand Up @@ -149,6 +150,33 @@ func (a *Adapter) EnsurePipelineIsFinalized() (controller.OperationResult, error
return controller.ContinueProcessing()
}

// EnsurePRGroupAnnotated is an operation that will ensure that the pr group info
// is added to build pipelineRun metadata label and annotation once it is created,
// then these label and annotation will be copied to component snapshot when it is created
func (a *Adapter) EnsurePRGroupAnnotated() (controller.OperationResult, error) {
if metadata.HasLabel(a.pipelineRun, gitops.PRGroupHashLabel) && metadata.HasAnnotation(a.pipelineRun, gitops.PRGroupAnnotation) {
a.logger.Info("build pipelineRun has had pr group info in metadata, no need to update")
return controller.ContinueProcessing()
}

err := a.addPRGroupToBuildPLRMetadata(a.pipelineRun)
if err != nil {
if errors.IsNotFound(err) {
a.logger.Error(err, "failed to add pr group info to build pipelineRun metadata due to notfound pipelineRun")
return controller.StopProcessing()
} else {
a.logger.Error(err, "failed to add pr group info to build pipelineRun metadata")
return controller.RequeueWithError(err)
}

}

a.logger.LogAuditEvent("pr group info is updated to build pipelineRun metadata", a.pipelineRun, h.LogActionUpdate,
"pipelineRun.Name", a.pipelineRun.Name)

return controller.ContinueProcessing()
}

// getImagePullSpecFromPipelineRun gets the full image pullspec from the given build PipelineRun,
// In case the Image pullspec can't be composed, an error will be returned.
func (a *Adapter) getImagePullSpecFromPipelineRun(pipelineRun *tektonv1.PipelineRun) (string, error) {
Expand Down Expand Up @@ -218,6 +246,10 @@ func (a *Adapter) prepareSnapshotForPipelineRun(pipelineRun *tektonv1.PipelineRu
snapshot.Labels[gitops.BuildPipelineRunFinishTimeLabel] = strconv.FormatInt(time.Now().Unix(), 10)
}

if pipelineRun.Status.StartTime != nil {
snapshot.Annotations[gitops.BuildPipelineRunStartTime] = strconv.FormatInt(pipelineRun.Status.StartTime.Time.Unix(), 10)
}

return snapshot, nil
}

Expand Down Expand Up @@ -329,3 +361,29 @@ func (a *Adapter) updatePipelineRunWithCustomizedError(canRemoveFinalizer *bool,
return controller.RequeueOnErrorOrContinue(cerr)

}

// addPRGroupToBuildPLRMetadata will add pr-group info gotten from souce-branch to annotation
// and also the string in sha format to metadata label
func (a *Adapter) addPRGroupToBuildPLRMetadata(pipelineRun *tektonv1.PipelineRun) error {
prGroupName := tekton.GetPRGroupNameFromBuildPLR(pipelineRun)
if prGroupName != "" {
prGroupHashName := tekton.GenerateSHA(prGroupName)

return retry.RetryOnConflict(retry.DefaultRetry, func() error {
var err error
pipelineRun, err = a.loader.GetPipelineRun(a.context, a.client, pipelineRun.Name, pipelineRun.Namespace)
if err != nil {
return err
}

patch := client.MergeFrom(pipelineRun.DeepCopy())

_ = metadata.SetAnnotation(&pipelineRun.ObjectMeta, gitops.PRGroupAnnotation, prGroupName)
_ = metadata.SetLabel(&pipelineRun.ObjectMeta, gitops.PRGroupHashLabel, prGroupHashName)

return a.client.Patch(a.context, pipelineRun, patch)
})
}
a.logger.Info("can't find source branch info in build PLR, not need to update build pipelineRun metadata")
return nil
}
38 changes: 38 additions & 0 deletions internal/controller/buildpipeline/buildpipeline_adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"github.com/konflux-ci/integration-service/helpers"
"github.com/konflux-ci/integration-service/loader"
"github.com/konflux-ci/integration-service/tekton"
"github.com/konflux-ci/operator-toolkit/metadata"
"knative.dev/pkg/apis"
v1 "knative.dev/pkg/apis/duck/v1"

Expand Down Expand Up @@ -267,6 +268,8 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
"build.appstudio.openshift.io/repo": "https://github.com/devfile-samples/devfile-sample-go-basic?rev=c713067b0e65fb3de50d1f7c457eb51c2ab0dbb0",
"foo": "bar",
"chains.tekton.dev/signed": "true",
"pipelinesascode.tekton.dev/source-branch": "sourceBranch",
"pipelinesascode.tekton.dev/url-org": "redhat",
},
},
Spec: tektonv1.PipelineRunSpec{
Expand Down Expand Up @@ -317,6 +320,7 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
Value: *tektonv1.NewStructuredValues(SampleCommit),
},
},
StartTime: &metav1.Time{Time: time.Now()},
},
Status: v1.Status{
Conditions: v1.Conditions{
Expand Down Expand Up @@ -410,6 +414,8 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
Expect(expectedSnapshot.Labels).NotTo(BeNil())
Expect(expectedSnapshot.Labels).Should(HaveKeyWithValue(Equal(gitops.BuildPipelineRunNameLabel), Equal(buildPipelineRun.Name)))
Expect(expectedSnapshot.Labels).Should(HaveKeyWithValue(Equal(gitops.ApplicationNameLabel), Equal(hasApp.Name)))
Expect(metadata.HasAnnotation(expectedSnapshot, gitops.BuildPipelineRunStartTime)).To(BeTrue())
Expect(expectedSnapshot.Annotations[gitops.BuildPipelineRunStartTime]).NotTo(BeNil())
})

It("ensures that Labels and Annotations were copied to snapshot from pipelinerun", func() {
Expand Down Expand Up @@ -1014,6 +1020,38 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {

})

When("add pr group to the build pipelineRun annotations and labels", func() {
BeforeEach(func() {
adapter = NewAdapter(ctx, buildPipelineRun, hasComp, hasApp, logger, loader.NewMockLoader(), k8sClient)
})
It("add pr group to the build pipelineRun annotations and labels", func() {
existingBuildPLR := new(tektonv1.PipelineRun)
err := k8sClient.Get(ctx, types.NamespacedName{
Namespace: buildPipelineRun.Namespace,
Name: buildPipelineRun.Name,
}, existingBuildPLR)
Expect(err).NotTo(HaveOccurred())
Expect(metadata.HasAnnotation(existingBuildPLR, gitops.PRGroupAnnotation)).To(BeFalse())
Expect(metadata.HasLabel(existingBuildPLR, gitops.PRGroupHashLabel)).To(BeFalse())

// Add label and annotation to PLR
result, err := adapter.EnsurePRGroupAnnotated()
Expect(err).NotTo(HaveOccurred())
Expect(result.CancelRequest).To(BeFalse())
Expect(result.RequeueRequest).To(BeFalse())

err = adapter.client.Get(adapter.context, types.NamespacedName{
Namespace: buildPipelineRun.Namespace,
Name: buildPipelineRun.Name,
}, existingBuildPLR)
Expect(err).NotTo(HaveOccurred())
Expect(metadata.HasAnnotation(existingBuildPLR, gitops.PRGroupAnnotation)).To(BeTrue())
Expect(existingBuildPLR.Annotations).Should(HaveKeyWithValue(Equal(gitops.PRGroupAnnotation), Equal("sourceBranch")))
Expect(metadata.HasLabel(existingBuildPLR, gitops.PRGroupHashLabel)).To(BeTrue())
Expect(existingBuildPLR.Labels[gitops.PRGroupHashLabel]).NotTo(BeNil())
})
})

When("running pipeline with deletion timestamp is processed", func() {

var runningDeletingBuildPipeline *tektonv1.PipelineRun
Expand Down
2 changes: 2 additions & 0 deletions internal/controller/buildpipeline/buildpipeline_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,15 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu

return controller.ReconcileHandler([]controller.Operation{
adapter.EnsurePipelineIsFinalized,
adapter.EnsurePRGroupAnnotated,
adapter.EnsureSnapshotExists,
})
}

// AdapterInterface is an interface defining all the operations that should be defined in an Integration adapter.
type AdapterInterface interface {
EnsurePipelineIsFinalized() (controller.OperationResult, error)
EnsurePRGroupAnnotated() (controller.OperationResult, error)
EnsureSnapshotExists() (controller.OperationResult, error)
}

Expand Down
47 changes: 47 additions & 0 deletions tekton/build_pipeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,31 @@ package tekton

import (
"context"
"crypto/sha256"
"encoding/json"
"fmt"
"strings"

h "github.com/konflux-ci/integration-service/helpers"
"github.com/konflux-ci/operator-toolkit/metadata"
tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
)

const (
// master branch in github/gitlab
MasterBranch = "master"

// main branch in github/gitlab
MainBranch = "main"

// PipelineAsCodeSourceBranchAnnotation is the branch name of the the pull request is created from
PipelineAsCodeSourceBranchAnnotation = "pipelinesascode.tekton.dev/source-branch"

// PipelineAsCodeSourceRepoOrg is the repo org build PLR is triggered by
PipelineAsCodeSourceRepoOrg = "pipelinesascode.tekton.dev/url-org"
)

// AnnotateBuildPipelineRun sets annotation for a build pipelineRun in defined context and returns that pipeline
func AnnotateBuildPipelineRun(ctx context.Context, pipelineRun *tektonv1.PipelineRun, key, value string, cl client.Client) error {
patch := client.MergeFrom(pipelineRun.DeepCopy())
Expand All @@ -40,6 +56,19 @@ func AnnotateBuildPipelineRun(ctx context.Context, pipelineRun *tektonv1.Pipelin
return nil
}

// LabelBuildPipelineRun sets annotation for a build pipelineRun in defined context and returns that pipeline
func LabelBuildPipelineRun(ctx context.Context, pipelineRun *tektonv1.PipelineRun, key, value string, cl client.Client) error {
patch := client.MergeFrom(pipelineRun.DeepCopy())

_ = metadata.SetLabel(&pipelineRun.ObjectMeta, key, value)

err := cl.Patch(ctx, pipelineRun, patch)
if err != nil {
return err
}
return nil
}

// AnnotateBuildPipelineRunWithCreateSnapshotAnnotation sets annotation test.appstudio.openshift.io/create-snapshot-status to build pipelineRun with
// a message that reflects either success or failure for creating a snapshot
func AnnotateBuildPipelineRunWithCreateSnapshotAnnotation(ctx context.Context, pipelineRun *tektonv1.PipelineRun, cl client.Client, ensureSnapshotExistsErr error) error {
Expand Down Expand Up @@ -67,3 +96,21 @@ func AnnotateBuildPipelineRunWithCreateSnapshotAnnotation(ctx context.Context, p
}
return AnnotateBuildPipelineRun(ctx, pipelineRun, h.CreateSnapshotAnnotationName, string(jsonResult), cl)
}

// GetPRGroupNameFromBuildPLR gets the PR group from the substring before @ from
// the source-branch pac annotation, for main, it generate PR group with {source-branch}-{url-org}
func GetPRGroupNameFromBuildPLR(pipelineRun *tektonv1.PipelineRun) string {
if prGroup, found := pipelineRun.ObjectMeta.Annotations[PipelineAsCodeSourceBranchAnnotation]; found {
if prGroup == MainBranch || prGroup == MasterBranch && metadata.HasAnnotation(pipelineRun, PipelineAsCodeSourceRepoOrg) {
prGroup = prGroup + "-" + pipelineRun.ObjectMeta.Annotations[PipelineAsCodeSourceRepoOrg]
}
return strings.Split(prGroup, "@")[0]
}
return ""
}

// GenerateSHA generate a 63 charactors sha string used by pipelineRun and snapshot label
func GenerateSHA(str string) string {
hash := sha256.Sum256([]byte(str))
return fmt.Sprintf("%x", hash)[0:62]
}
Loading

0 comments on commit f913d46

Please sign in to comment.