Skip to content
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
4 changes: 2 additions & 2 deletions docs/build_pipeline_controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ get_pipeline_run{Pipeline updated?}
failed_pipeline_run{Pipeline failed?}
finalizer_exists{Does the finalizer already exist?}
need_to_set_integration_test{Build pipelineRun is newly triggered?<br>Or Build pipelineRun failed?<br> Or Failing to create snapshot?}
retrieve_associated_entity(Retrieve the entity <br> component/application)
retrieve_associated_entity(Retrieve the entity <br> component/application or componentGroup)
determine_snapshot{Does a snapshot exist?}
prep_snapshot(Gather Application components<br> Add new component)
prep_snapshot(Gather Application or ComponentGroup components<br> Add new component)
check_chains{Chains annotation present?}
annotate_pipelineRun(Annotate pipeline with <br> name of Snapshot)
add_finalizer(Add finalizer to build PLR)
Expand Down
8 changes: 4 additions & 4 deletions docs/snapshot-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ flowchart TD

%% Node definitions
ensure1(Process further if: Snapshot testing <br>is not finished yet)
are_there_any_ITS{"Are there any <br>IntegrationTestScenario <br>present for the given <br>Application?"}
are_there_any_ITS{"Are there any <br>IntegrationTestScenario <br>present for the given <br>Application/ComponentGroup?"}
create_new_test_PLR(<b>Create a new Test PipelineRun</b> for each <br>of the above ITS, if it doesn't exists already)
mark_snapshot_InProgress(<b>Mark</b> Snapshot's Integration-testing <br>status as 'InProgress')
fetch_all_required_ITS("Fetch all the required <br>(non-optional) IntegrationTestScenario <br>for the given Application <br> filtered by ITS context(s)")
fetch_all_required_ITS("Fetch all the required <br>(non-optional) IntegrationTestScenario <br>for the given Application/ComponentGroup <br> filtered by ITS context(s)")
encountered_error1{Encountered error?}
mark_snapshot_Invalid1(<b>Mark</b> the Snapshot as Invalid)
is_atleast_1_required_ITS{Is there atleast <br>1 required ITS?}
Expand Down Expand Up @@ -60,7 +60,7 @@ flowchart TD

%% Node definitions
ensure3(Process further if: Snapshot is valid & <br>Snapshot testing succeeded & <br>Snapshot was not created by <br>PAC Pull Request Event & <br> Snapshot wasn't auto-released)
fetch_all_ReleasePlans("Fetch ALL the ReleasePlan CRs <br>for the given Application, that have the <br>'release.appstudio.openshift.io/auto-release' <br>label set to 'True'")
fetch_all_ReleasePlans("Fetch ALL the ReleasePlan CRs <br>for the given Application/ComponentGroup, that have the <br>'release.appstudio.openshift.io/auto-release' <br>label set to 'True'")
encountered_error31{Encountered error?}
create_Release(<b>Create a Release</b> for each of the above <br>ReleasePlan if it doesn't exists already)
encountered_error32{Encountered error?}
Expand Down Expand Up @@ -120,7 +120,7 @@ flowchart TD
ensure5(Process further if: Snapshot has <b>neither</b> push event type label <br><b>nor</b> PRGroupCreation annotation)
validate_build_pipelinerun{Did all gotten build pipelineRun <br>under the same group <br>succeed <b>and</b> <br>component snapshot are already created?}
annotate_component_snapshot(<b>Annotate</b> component snapshot)
get_component_snapshots_and_sort(<b>Iterate</b> all application components and <br><b>get<b/> all component snapshots <br>for each component under the same pr group sha <br>then <b>sort</b> snapshots)
get_component_snapshots_and_sort(<b>Iterate</b> all application or componentGroup components and <br><b>get<b/> all component snapshots <br>for each component under the same pr group sha <br>then <b>sort</b> snapshots)
can_find_snapshotComponent_from_latest_snapshot(<b>Can</b> find the latest snapshot with open pull/merge request?)
add_snapshot_to_group_snapshot_candidate(<b>Add</b> snapshotComponent of component <br>to group snapshot components candidate)
get_snapshotComponent_from_gcl(<b>Get</b> snapshotComponent from <br>Global Candidate List)
Expand Down
28 changes: 22 additions & 6 deletions gitops/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,8 @@ type ComponentSnapshotInfo struct {
RepoUrl string `json:"repoUrl"`
// Pull/Merge request number for updated component
PullRequestNumber string `json:"pullRequestNumber"`
// Version of the component
Version string `json:"version"`
}

// AddedToGlobalCandidateListStatus contains the information which will be added to build PLR or override snapshot about updating GCL
Expand Down Expand Up @@ -394,6 +396,9 @@ const componentSnapshotInfosSchema = `{
},
"pullRequestNumber": {
"type": "string"
},
"version": {
"type": "string"
}
},
"required": ["namespace", "component", "buildPipelineRun", "snapshot"]
Expand Down Expand Up @@ -783,6 +788,7 @@ func CanSnapshotBePromoted(snapshot *applicationapiv1alpha1.Snapshot) (bool, []s
}

// NewSnapshot creates a new snapshot based on the supplied application and components
// TODO: Remove this function once application model is fully deprecated [APPLICATION] - a matching function which supports ComponentGroups is located in the snapshot package
func NewSnapshot(application *applicationapiv1alpha1.Application, snapshotComponents *[]applicationapiv1alpha1.SnapshotComponent) *applicationapiv1alpha1.Snapshot {
// Use fallback timestamp (current time) - will be overridden in prepareSnapshotForPipelineRun
// if BuildPipelineRunStartTime is available
Expand Down Expand Up @@ -1045,7 +1051,7 @@ func PrepareSnapshot(ctx context.Context, adapterClient client.Client, applicati
return snapshot, nil
}

// FindMatchingSnapshot tries to finds the expected Snapshot with the same set of images.
// FindMatchingSnapshot tries to find the expected Snapshot with the same set of images.
func FindMatchingSnapshot(application *applicationapiv1alpha1.Application, allSnapshots *[]applicationapiv1alpha1.Snapshot, expectedSnapshot *applicationapiv1alpha1.Snapshot) *applicationapiv1alpha1.Snapshot {
for _, foundSnapshot := range *allSnapshots {
foundSnapshot := foundSnapshot
Expand Down Expand Up @@ -1228,7 +1234,7 @@ func CopyBuildPipelineRunResultsToSnapshot(pipelineRun *tektonv1.PipelineRun, sn

// CopyTempGroupSnapshotLabelsAndAnnotations coppies labels and annotations from build pipelineRun or tested snapshot
// into regular snapshot
func CopyTempGroupSnapshotLabelsAndAnnotations(application *applicationapiv1alpha1.Application, snapshot *applicationapiv1alpha1.Snapshot, componentName string, source *metav1.ObjectMeta, prefixes []string) {
func CopyTempGroupSnapshotLabelsAndAnnotations(object *metav1.ObjectMeta, snapshot *applicationapiv1alpha1.Snapshot, componentName string, source *metav1.ObjectMeta, prefixes []string, objectIsApplication bool) {
if snapshot.Labels == nil {
snapshot.Labels = map[string]string{}
}
Expand All @@ -1238,7 +1244,12 @@ func CopyTempGroupSnapshotLabelsAndAnnotations(application *applicationapiv1alph
}
snapshot.Labels[SnapshotTypeLabel] = SnapshotGroupType

snapshot.Labels[ApplicationNameLabel] = application.Name
if objectIsApplication {
snapshot.Labels[ApplicationNameLabel] = object.Name
} else {
// the object is a ComponentGroup
snapshot.Labels[ComponentGroupNameLabel] = object.Name
}

// Copy PAC annotations/labels from source(tested snapshot or pipelinerun) to snapshot.
_ = metadata.CopyLabelsWithPrefixReplacement(source, &snapshot.ObjectMeta, "pipelinesascode.tekton.dev", PipelinesAsCodePrefix)
Expand Down Expand Up @@ -1366,9 +1377,9 @@ func GetPRGroup(object client.Object) (string, string) {
}

// FindMatchingSnapshotComponent find the snapshot component from the given snapshot according to the name of the given component name
func FindMatchingSnapshotComponent(snapshot *applicationapiv1alpha1.Snapshot, component *applicationapiv1alpha1.Component) applicationapiv1alpha1.SnapshotComponent {
func FindMatchingSnapshotComponent(snapshot *applicationapiv1alpha1.Snapshot, componentName string) applicationapiv1alpha1.SnapshotComponent {
for _, snapshotComponent := range snapshot.Spec.Components {
if snapshotComponent.Name == component.Name {
if snapshotComponent.Name == componentName {
return snapshotComponent
}
}
Expand Down Expand Up @@ -1460,7 +1471,11 @@ func SetAnnotationAndLabelForGroupSnapshot(groupSnapshot *applicationapiv1alpha1
}
groupSnapshot.Labels[SnapshotTypeLabel] = SnapshotGroupType
groupSnapshot.Labels[PRGroupHashLabel] = componentSnapshot.Labels[PRGroupHashLabel]
groupSnapshot.Labels[ApplicationNameLabel] = componentSnapshot.Spec.Application
if componentSnapshot.Spec.Application != "" {
groupSnapshot.Labels[ApplicationNameLabel] = componentSnapshot.Spec.Application
} else {
groupSnapshot.Labels[ComponentGroupNameLabel] = componentSnapshot.Spec.ComponentGroup
}

return groupSnapshot, nil
}
Expand Down Expand Up @@ -1522,6 +1537,7 @@ func GetShaFromSnapshot(ctx context.Context, snapshot *applicationapiv1alpha1.Sn
}

// PrepareTempGroupSnapshot will prepare a temp group snapshot used to check the integration test scenario that should be applied to the group snapshot under that application
// TODO: Remove this function once application model is fully deprecated [APPLICATION] - a matching function which supports ComponentGroups is located in the snapshot package
func PrepareTempGroupSnapshot(application *applicationapiv1alpha1.Application, snapshot *applicationapiv1alpha1.Snapshot) *applicationapiv1alpha1.Snapshot {
tempGroupSnapshot := NewSnapshot(application, &[]applicationapiv1alpha1.SnapshotComponent{})
tempGroupSnapshot, _ = SetAnnotationAndLabelForGroupSnapshot(tempGroupSnapshot, snapshot, []ComponentSnapshotInfo{})
Expand Down
8 changes: 4 additions & 4 deletions gitops/snapshot_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,7 +548,7 @@ var _ = Describe("Gitops functions for managing Snapshots", Ordered, func() {
Expect(createdSnapshot.Name).To(MatchRegexp(`^application-sample-\d{8}-\d{6}-\d{3}$`))
})

It("ensures NewSnapshot truncates application name if longer than 43 characters", func() {
It("ensures NewApplicationSnapshot truncates application name if longer than 43 characters [APPLICATION]", func() {
longAppName := "this-is-a-very-long-application-name-that-exceeds-43-chars"
longApp := &applicationapiv1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Expand All @@ -572,7 +572,7 @@ var _ = Describe("Gitops functions for managing Snapshots", Ordered, func() {
Expect(snapshot.Spec.Application).To(Equal(longAppName))
})

It("ensures NewSnapshot does not truncate application name at 43 characters", func() {
It("ensures NewApplicationSnapshot does not truncate application name at 43 characters [APPLICATION]", func() {
exactAppName := "this-is-application-name-exactly-43-chars" // 43 chars
exactApp := &applicationapiv1alpha1.Application{
ObjectMeta: metav1.ObjectMeta{
Expand Down Expand Up @@ -1243,7 +1243,7 @@ var _ = Describe("Gitops functions for managing Snapshots", Ordered, func() {
})

It("Can find the correct snapshotComponent for the given component name", func() {
FoundSnapshotComponent := gitops.FindMatchingSnapshotComponent(hasComSnapshot1, hasComp)
FoundSnapshotComponent := gitops.FindMatchingSnapshotComponent(hasComSnapshot1, hasComp.Name)
Expect(FoundSnapshotComponent.Name).To(Equal(hasComp.Name))
})

Expand Down Expand Up @@ -1353,7 +1353,7 @@ var _ = Describe("Gitops functions for managing Snapshots", Ordered, func() {
},
}
prefixes := []string{gitops.BuildPipelineRunPrefix}
gitops.CopyTempGroupSnapshotLabelsAndAnnotations(hasApp, tempGroupSnapshot, hasComp.Name, &buildPipelineRun.ObjectMeta, prefixes)
gitops.CopyTempGroupSnapshotLabelsAndAnnotations(&hasApp.ObjectMeta, tempGroupSnapshot, hasComp.Name, &buildPipelineRun.ObjectMeta, prefixes, true)
Expect(metadata.HasLabel(tempGroupSnapshot, "pac.test.appstudio.openshift.io/event-type")).To(BeTrue())
Expect(metadata.HasLabel(tempGroupSnapshot, "appstudio.openshift.io/component")).To(BeFalse())
})
Expand Down
16 changes: 14 additions & 2 deletions helpers/component_group.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,20 @@ import (

func GetComponentGroupNames(componentGroups *[]v1beta2.ComponentGroup) []string {
Comment thread
dirgim marked this conversation as resolved.
names := []string{}
for _, componentGroup := range *componentGroups {
names = append(names, componentGroup.Name)
if componentGroups != nil {
for _, componentGroup := range *componentGroups {
names = append(names, componentGroup.Name)
}
}
return names
}

func GetComponentNamesFromComponentGroup(componentGroup *v1beta2.ComponentGroup) []string {
names := []string{}
if componentGroup != nil && componentGroup.Spec.Components != nil {
for _, component := range componentGroup.Spec.Components {
names = append(names, component.Name)
}
}
return names
}
Expand Down
77 changes: 71 additions & 6 deletions helpers/component_group_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
/*
Copyright 2022 Red Hat Inc.

Copyright 2026 Red Hat Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Expand All @@ -14,11 +13,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package helpers
package helpers_test
Comment thread
14rcole marked this conversation as resolved.

import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

"github.com/konflux-ci/integration-service/api/v1beta2"
"github.com/konflux-ci/integration-service/helpers"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
Expand All @@ -28,18 +30,81 @@ const (
version = "v1"
)

var _ = Describe("Utility functions", Ordered, func() {
var _ = Describe("Helpers for component groups", func() {

Context("Validating component version strings", func() {
It("Can generate a valid component version string", func() {
expectedResult := fmt.Sprintf("%s/%s", name, version)
result := GetComponentVersionString(name, version)
result := helpers.GetComponentVersionString(name, version)
Expect(result).To(Equal(expectedResult))
})

It("Can generate a valid component version log string", func() {
expectedResult := fmt.Sprintf("%s (version %s)", name, version)
result := GetComponentVersionLogString(name, version)
result := helpers.GetComponentVersionLogString(name, version)
Expect(result).To(Equal(expectedResult))
})
})

Context("GetComponentGroupNames", func() {

It("returns an empty slice when componentGroups is nil", func() {
Expect(helpers.GetComponentGroupNames(nil)).To(BeEmpty())
})

It("returns an empty slice when there are no component groups", func() {
groups := []v1beta2.ComponentGroup{}
Expect(helpers.GetComponentGroupNames(&groups)).To(BeEmpty())
})

It("returns the name of each component group in order", func() {
groups := []v1beta2.ComponentGroup{
{
ObjectMeta: metav1.ObjectMeta{Name: "frontend"},
Spec: v1beta2.ComponentGroupSpec{},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "backend"},
Spec: v1beta2.ComponentGroupSpec{},
},
}
Expect(helpers.GetComponentGroupNames(&groups)).To(Equal([]string{"frontend", "backend"}))
})
})

Context("GetComponentNamesFromComponentGroup", func() {

It("returns an empty slice when the component group is nil", func() {
Expect(helpers.GetComponentNamesFromComponentGroup(nil)).To(BeEmpty())
})

It("returns an empty slice when Spec.Components is nil", func() {
group := &v1beta2.ComponentGroup{
ObjectMeta: metav1.ObjectMeta{Name: "my-group"},
Spec: v1beta2.ComponentGroupSpec{},
}
Expect(helpers.GetComponentNamesFromComponentGroup(group)).To(BeEmpty())
})

It("returns an empty slice when Spec.Components is empty", func() {
group := &v1beta2.ComponentGroup{
ObjectMeta: metav1.ObjectMeta{Name: "my-group"},
Spec: v1beta2.ComponentGroupSpec{Components: []v1beta2.ComponentReference{}},
}
Expect(helpers.GetComponentNamesFromComponentGroup(group)).To(BeEmpty())
})

It("returns each component Name from Spec.Components in order", func() {
group := &v1beta2.ComponentGroup{
ObjectMeta: metav1.ObjectMeta{Name: "my-group"},
Spec: v1beta2.ComponentGroupSpec{
Components: []v1beta2.ComponentReference{
{Name: "api-service", ComponentVersion: v1beta2.ComponentVersionReference{Name: "main"}},
{Name: "worker", ComponentVersion: v1beta2.ComponentVersionReference{Name: "release-1"}},
},
},
}
Expect(helpers.GetComponentNamesFromComponentGroup(group)).To(Equal([]string{"api-service", "worker"}))
})
})
})
Loading
Loading