diff --git a/docs/build_pipeline_controller.md b/docs/build_pipeline_controller.md
index fa1d3f899d..5cb8fc55cc 100644
--- a/docs/build_pipeline_controller.md
+++ b/docs/build_pipeline_controller.md
@@ -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?
Or Build pipelineRun failed?
Or Failing to create snapshot?}
-retrieve_associated_entity(Retrieve the entity
component/application)
+retrieve_associated_entity(Retrieve the entity
component/application or componentGroup)
determine_snapshot{Does a snapshot exist?}
-prep_snapshot(Gather Application components
Add new component)
+prep_snapshot(Gather Application or ComponentGroup components
Add new component)
check_chains{Chains annotation present?}
annotate_pipelineRun(Annotate pipeline with
name of Snapshot)
add_finalizer(Add finalizer to build PLR)
diff --git a/docs/snapshot-controller.md b/docs/snapshot-controller.md
index 5b75b70aaa..ab2fa34439 100644
--- a/docs/snapshot-controller.md
+++ b/docs/snapshot-controller.md
@@ -14,10 +14,10 @@ flowchart TD
%% Node definitions
ensure1(Process further if: Snapshot testing
is not finished yet)
- are_there_any_ITS{"Are there any
IntegrationTestScenario
present for the given
Application?"}
+ are_there_any_ITS{"Are there any
IntegrationTestScenario
present for the given
Application/ComponentGroup?"}
create_new_test_PLR(Create a new Test PipelineRun for each
of the above ITS, if it doesn't exists already)
mark_snapshot_InProgress(Mark Snapshot's Integration-testing
status as 'InProgress')
- fetch_all_required_ITS("Fetch all the required
(non-optional) IntegrationTestScenario
for the given Application
filtered by ITS context(s)")
+ fetch_all_required_ITS("Fetch all the required
(non-optional) IntegrationTestScenario
for the given Application/ComponentGroup
filtered by ITS context(s)")
encountered_error1{Encountered error?}
mark_snapshot_Invalid1(Mark the Snapshot as Invalid)
is_atleast_1_required_ITS{Is there atleast
1 required ITS?}
@@ -60,7 +60,7 @@ flowchart TD
%% Node definitions
ensure3(Process further if: Snapshot is valid &
Snapshot testing succeeded &
Snapshot was not created by
PAC Pull Request Event &
Snapshot wasn't auto-released)
- fetch_all_ReleasePlans("Fetch ALL the ReleasePlan CRs
for the given Application, that have the
'release.appstudio.openshift.io/auto-release'
label set to 'True'")
+ fetch_all_ReleasePlans("Fetch ALL the ReleasePlan CRs
for the given Application/ComponentGroup, that have the
'release.appstudio.openshift.io/auto-release'
label set to 'True'")
encountered_error31{Encountered error?}
create_Release(Create a Release for each of the above
ReleasePlan if it doesn't exists already)
encountered_error32{Encountered error?}
@@ -120,7 +120,7 @@ flowchart TD
ensure5(Process further if: Snapshot has neither push event type label
nor PRGroupCreation annotation)
validate_build_pipelinerun{Did all gotten build pipelineRun
under the same group
succeed and
component snapshot are already created?}
annotate_component_snapshot(Annotate component snapshot)
- get_component_snapshots_and_sort(Iterate all application components and
get all component snapshots
for each component under the same pr group sha
then sort snapshots)
+ get_component_snapshots_and_sort(Iterate all application or componentGroup components and
get all component snapshots
for each component under the same pr group sha
then sort snapshots)
can_find_snapshotComponent_from_latest_snapshot(Can find the latest snapshot with open pull/merge request?)
add_snapshot_to_group_snapshot_candidate(Add snapshotComponent of component
to group snapshot components candidate)
get_snapshotComponent_from_gcl(Get snapshotComponent from
Global Candidate List)
diff --git a/gitops/snapshot.go b/gitops/snapshot.go
index c67a662731..67a800a529 100644
--- a/gitops/snapshot.go
+++ b/gitops/snapshot.go
@@ -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
@@ -394,6 +396,9 @@ const componentSnapshotInfosSchema = `{
},
"pullRequestNumber": {
"type": "string"
+ },
+ "version": {
+ "type": "string"
}
},
"required": ["namespace", "component", "buildPipelineRun", "snapshot"]
@@ -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
@@ -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
@@ -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{}
}
@@ -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)
@@ -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
}
}
@@ -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
}
@@ -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{})
diff --git a/gitops/snapshot_test.go b/gitops/snapshot_test.go
index 21e7226d52..3ae85a08f9 100644
--- a/gitops/snapshot_test.go
+++ b/gitops/snapshot_test.go
@@ -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{
@@ -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{
@@ -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))
})
@@ -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())
})
diff --git a/helpers/component_group.go b/helpers/component_group.go
index 1899c33819..d1eb403b50 100644
--- a/helpers/component_group.go
+++ b/helpers/component_group.go
@@ -22,8 +22,20 @@ import (
func GetComponentGroupNames(componentGroups *[]v1beta2.ComponentGroup) []string {
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
}
diff --git a/helpers/component_group_test.go b/helpers/component_group_test.go
index e4d0a04708..43af22ec3d 100644
--- a/helpers/component_group_test.go
+++ b/helpers/component_group_test.go
@@ -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
@@ -14,11 +13,14 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package helpers
+package helpers_test
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"
)
@@ -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"}))
+ })
+ })
})
diff --git a/internal/controller/buildpipeline/buildpipeline_adapter.go b/internal/controller/buildpipeline/buildpipeline_adapter.go
index 0e9030359b..fd290d6c8e 100644
--- a/internal/controller/buildpipeline/buildpipeline_adapter.go
+++ b/internal/controller/buildpipeline/buildpipeline_adapter.go
@@ -487,66 +487,109 @@ func (a *Adapter) EnsureIntegrationTestReportedToGitProvider() (controller.Opera
return controller.ContinueProcessing()
}
- a.logger.Info(fmt.Sprintf("try to set integration test status according to the build PLR status %s", integrationTestStatus.String()))
-
+ var numComponentSnapshotScenarios, numGroupSnapshotScenarios int
// TODO: remove application section after migration
- var allIntegrationTestScenarios *[]v1beta2.IntegrationTestScenario
if a.application != nil {
- allIntegrationTestScenarios, err = a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application)
+ numComponentSnapshotScenarios, numGroupSnapshotScenarios, err = a.reportIntegrationStatusAndHandleGroupsForApplication(&integrationTestStatus)
if err != nil {
- a.logger.Error(err, "Failed to get integration test scenarios for the following application",
- "Application.Namespace", a.application.Namespace, "Application.Name", a.application.Name)
+ a.logger.Error(err, "failed to report status and handle groups")
return controller.RequeueWithError(err)
}
} else {
- allIntegrationTestScenarios, err = a.loader.GetAllIntegrationTestScenariosForComponentGroups(a.context, a.client, a.componentGroups)
+ numComponentSnapshotScenarios, numGroupSnapshotScenarios, err = a.reportIntegrationStatusAndHandleGroups(&integrationTestStatus)
if err != nil {
- // PipelineRun, ComponentGroups, and IntegrationTestScenarios must be in same namespace
- ns := a.pipelineRun.Namespace
- componentGroupNames := h.GetComponentGroupNames(a.componentGroups)
- a.logger.Error(err, "Failed to get integration test scenarios for the following componentGroups",
- "ComponentGroup.Namespace", ns, "ComponentGroups", componentGroupNames)
+ a.logger.Error(err, "failed to report status and handle groups")
return controller.RequeueWithError(err)
}
}
- if allIntegrationTestScenarios == nil {
- return controller.ContinueProcessing()
+ if numComponentSnapshotScenarios > 0 || numGroupSnapshotScenarios > 0 {
+ if err = tekton.AnnotateBuildPipelineRun(a.context, a.pipelineRun, h.SnapshotCreationReportAnnotation, integrationTestStatus.String(), a.client); err != nil {
+ a.logger.Error(err, fmt.Sprintf("failed to write build plr annotation %s", h.SnapshotCreationReportAnnotation))
+ return controller.RequeueWithError(fmt.Errorf("failed to write snapshot report status metadata for annotation %s: %w", h.SnapshotCreationReportAnnotation, err))
+ }
}
+ return controller.ContinueProcessing()
- var numComponentSnapshotScenarios, numGroupSnapshotScenarios int
- tempComponentSnapshot := a.prepareTempComponentSnapshot(a.pipelineRun)
- numComponentSnapshotScenarios, err = a.reportStatusForExpectedSnapshot(a.pipelineRun, tempComponentSnapshot, allIntegrationTestScenarios, integrationTestStatus, a.component.Name)
+}
+
+func (a *Adapter) reportIntegrationStatusAndHandleGroupsForApplication(integrationTestStatus *intgteststat.IntegrationTestStatus) (int, int, error) {
+ numComponentSnapshotScenarios := 0
+ numGroupSnapshotScenarios := 0
+ var tempComponentSnapshot *applicationapiv1alpha1.Snapshot
+
+ allIntegrationTestScenarios, err := a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application)
if err != nil {
- a.logger.Error(err, "failed to report status for expected group Snapshot")
- return controller.RequeueWithError(err)
+ a.logger.Error(err, "Failed to get integration test scenarios for the following application",
+ "Application.Namespace", a.application.Namespace, "Application.Name", a.application.Name)
+ return 0, 0, err
+ }
+
+ if allIntegrationTestScenarios == nil {
+ return 0, 0, nil
+ }
+ a.logger.Info(fmt.Sprintf("try to set integration test status according to the build PLR status %s", integrationTestStatus.String()))
+ tempComponentSnapshot = a.prepareTempComponentSnapshot(a.pipelineRun, &a.application.ObjectMeta, true)
+ numComponentSnapshotScenarios, err = a.reportStatusForExpectedSnapshot(a.pipelineRun, tempComponentSnapshot, allIntegrationTestScenarios, *integrationTestStatus, a.component.Name)
+ if err != nil {
+ return 0, 0, fmt.Errorf("failed to report status for expected group Snapshot: %w", err)
}
a.logger.Info("try to check if group snapshot is expected for build PLR")
- isGroupSnapshotExpected, err := a.isGroupSnapshotExpectedForBuildPLR(a.pipelineRun, tempComponentSnapshot)
+ isGroupSnapshotExpected, err := a.isGroupSnapshotExpectedForBuildPLR(a.pipelineRun, tempComponentSnapshot, a.application.Name, gitops.ApplicationNameLabel)
if err != nil {
a.logger.Error(err, "failed to check if group snapshot is expected")
- return controller.RequeueWithError(err)
+ return 0, 0, fmt.Errorf("failed to check if group snapshot is expected: %w", err)
}
-
if isGroupSnapshotExpected {
a.logger.Info("group snapshot is expected to be created for build pipelinerun, group integration test should be set for found context scenario", "pipelineRun.Name", a.pipelineRun.Name)
- tempGroupSnapshot := a.prepareTempGroupSnapshot(a.pipelineRun)
- numGroupSnapshotScenarios, err = a.reportStatusForExpectedSnapshot(a.pipelineRun, tempGroupSnapshot, allIntegrationTestScenarios, integrationTestStatus, gitops.ComponentNameForGroupSnapshot)
+ tempGroupSnapshot := a.prepareTempGroupSnapshot(a.pipelineRun, &a.application.ObjectMeta, true)
+ numGroupSnapshotScenarios, err = a.reportStatusForExpectedSnapshot(a.pipelineRun, tempGroupSnapshot, allIntegrationTestScenarios, *integrationTestStatus, gitops.ComponentNameForGroupSnapshot)
if err != nil {
- a.logger.Error(err, "failed to report status for expected group Snapshot")
- return controller.RequeueWithError(err)
+ return 0, 0, fmt.Errorf("failed to report status for expected group Snapshot: %w", err)
}
}
+ return numComponentSnapshotScenarios, numGroupSnapshotScenarios, nil
+}
- if numComponentSnapshotScenarios > 0 || numGroupSnapshotScenarios > 0 {
- if err = tekton.AnnotateBuildPipelineRun(a.context, a.pipelineRun, h.SnapshotCreationReportAnnotation, integrationTestStatus.String(), a.client); err != nil {
- a.logger.Error(err, fmt.Sprintf("failed to write build plr annotation %s", h.SnapshotCreationReportAnnotation))
- return controller.RequeueWithError(fmt.Errorf("failed to write snapshot report status metadata for annotation %s: %w", h.SnapshotCreationReportAnnotation, err))
+func (a *Adapter) reportIntegrationStatusAndHandleGroups(integrationTestStatus *intgteststat.IntegrationTestStatus) (int, int, error) {
+ numComponentSnapshotScenarios := 0
+ numGroupSnapshotScenarios := 0
+
+ for _, componentGroup := range *a.componentGroups {
+ integrationTestScenariosForGroup, err := a.loader.GetAllIntegrationTestScenariosForComponentGroup(a.context, a.client, &componentGroup)
+ if err != nil {
+ return 0, 0, fmt.Errorf("failed to get integration test scenarios for the componentGroup %s: %w", componentGroup.Name, err)
}
- }
- return controller.ContinueProcessing()
+ if integrationTestScenariosForGroup == nil || len(*integrationTestScenariosForGroup) == 0 {
+ continue
+ }
+ a.logger.Info(fmt.Sprintf("try to set integration test status according to the build PLR status %s", integrationTestStatus.String()))
+ tempComponentSnapshot := a.prepareTempComponentSnapshot(a.pipelineRun, &componentGroup.ObjectMeta, false)
+ num, err := a.reportStatusForExpectedSnapshot(a.pipelineRun, tempComponentSnapshot, integrationTestScenariosForGroup, *integrationTestStatus, a.component.Name)
+ if err != nil {
+ return 0, 0, fmt.Errorf("failed to report status for expected group Snapshot: %w", err)
+ }
+ numComponentSnapshotScenarios += num
+ a.logger.Info("try to check if group snapshot is expected for build PLR")
+ isGroupSnapshotExpected, err := a.isGroupSnapshotExpectedForBuildPLR(a.pipelineRun, tempComponentSnapshot, componentGroup.Name, gitops.ComponentGroupNameLabel)
+ if err != nil {
+ return 0, 0, fmt.Errorf("failed to check if group snapshot is expected: %w", err)
+ }
+ if isGroupSnapshotExpected {
+ a.logger.Info("group snapshot is expected to be created for build pipelinerun, group integration test should be set for found context scenario", "pipelineRun.Name", a.pipelineRun.Name)
+ tempGroupSnapshot := a.prepareTempGroupSnapshot(a.pipelineRun, &componentGroup.ObjectMeta, false)
+ groupName := fmt.Sprintf("%s %s", gitops.ComponentNameForGroupSnapshot, componentGroup.Name)
+ numGroup, err := a.reportStatusForExpectedSnapshot(a.pipelineRun, tempGroupSnapshot, integrationTestScenariosForGroup, *integrationTestStatus, groupName)
+ if err != nil {
+ a.logger.Error(err, "failed to report status for expected group Snapshot")
+ return 0, 0, fmt.Errorf("failed to report status for expected group Snapshot: %w", err)
+ }
+ numGroupSnapshotScenarios += numGroup
+ }
+ }
+ return numComponentSnapshotScenarios, numGroupSnapshotScenarios, nil
}
// EnsureComponentSnapshotAnnotatedForMergedPR annotates all component snapshots when push build PLR is triggered for the same PR since the PR/MR are merged
@@ -669,38 +712,73 @@ func (a *Adapter) EnsureSupercededSnapshotsCanceled() (result controller.Operati
func (a *Adapter) notifySnapshotsInGroupAboutBuild(pipelineRun *tektonv1.PipelineRun, message string) error {
prGroupHash := pipelineRun.Labels[gitops.PRGroupHashLabel]
- buildPipelineRuns, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.pipelineRun.Namespace, prGroupHash, a.application.Name)
- if err != nil {
- a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
- return err
- }
-
- // Don't do anything if the build pipelineRun isn't the latest for its component
- if !tekton.IsLatestBuildPipelineRunInComponent(pipelineRun, buildPipelineRuns) {
- a.logger.Info("not the latest pipelineRun, skipping notifying the group about the failure")
- return nil
- }
+ var allComponentSnapshotsInGroup *[]applicationapiv1alpha1.Snapshot
+ var buildPipelineRuns *[]tektonv1.PipelineRun
+ if a.application != nil {
+ var err error
+ buildPipelineRuns, err = a.loader.GetPipelineRunsWithPRGroupHashForApplication(a.context, a.client, a.pipelineRun.Namespace, prGroupHash, a.application.Name)
+ if err != nil {
+ return fmt.Errorf("failed to get build pipelineRuns for given pr group hash %s: %w", prGroupHash, err)
+ }
- applicationComponents, err := a.loader.GetAllApplicationComponents(a.context, a.client, a.application)
- if err != nil {
- return err
- }
+ // Don't do anything if the build pipelineRun isn't the latest for its component
+ if !tekton.IsLatestBuildPipelineRunInComponent(pipelineRun, buildPipelineRuns) {
+ a.logger.Info("not the latest pipelineRun, skipping notifying the group about the failure")
+ return nil
+ }
- // Annotate all latest component Snapshots that are part of the PR group
- for _, applicationComponent := range *applicationComponents {
- applicationComponent := applicationComponent // G601
- allComponentSnapshotsInGroup, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, a.pipelineRun.Namespace, applicationComponent.Name, prGroupHash, a.application.Name)
+ applicationComponents, err := a.loader.GetAllApplicationComponents(a.context, a.client, a.application)
if err != nil {
- a.logger.Error(err, "Failed to fetch Snapshots for component", "component.Name", applicationComponent.Name)
- return err
+ return fmt.Errorf("failed to get all application components for application %s: %w", a.application.Name, err)
}
- if len(*allComponentSnapshotsInGroup) > 0 {
- latestSnapshot := gitops.SortSnapshots(*allComponentSnapshotsInGroup)[0]
- err = gitops.AnnotateSnapshot(a.context, &latestSnapshot, gitops.PRGroupCreationAnnotation,
- message, a.client)
+ // Annotate all latest component Snapshots that are part of the PR group
+ for _, applicationComponent := range *applicationComponents {
+ applicationComponent := applicationComponent // G601
+ allComponentSnapshotsInGroup, err = a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client,
+ a.pipelineRun.Namespace, applicationComponent.Name, prGroupHash, a.application.Name, gitops.ApplicationNameLabel)
if err != nil {
- return err
+ return fmt.Errorf("failed to fetch Snapshots for component %s: %w", applicationComponent.Name, err)
+ }
+ if allComponentSnapshotsInGroup != nil && len(*allComponentSnapshotsInGroup) > 0 {
+ latestSnapshot := gitops.SortSnapshots(*allComponentSnapshotsInGroup)[0]
+ err = gitops.AnnotateSnapshot(a.context, &latestSnapshot, gitops.PRGroupCreationAnnotation,
+ message, a.client)
+ if err != nil {
+ return fmt.Errorf("failed to annotate latest snapshot %s of component %s: %w", latestSnapshot.Name, applicationComponent.Name, err)
+ }
+ }
+ }
+ } else {
+ allBuildPipelineRunsWithPRGroupHash, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.pipelineRun.Namespace, prGroupHash)
+ if err != nil {
+ return fmt.Errorf("failed to get build pipelineRuns for given pr group hash %s: %w", prGroupHash, err)
+ }
+ buildPipelineRuns = a.filterPipelineRunsForComponentGroups(allBuildPipelineRunsWithPRGroupHash, a.componentGroups)
+
+ // Don't do anything if the build pipelineRun isn't the latest for its component
+ if !tekton.IsLatestBuildPipelineRunInComponent(pipelineRun, buildPipelineRuns) {
+ a.logger.Info("not the latest pipelineRun, skipping notifying the group about the failure")
+ return nil
+ }
+ for _, componentGroup := range *a.componentGroups {
+ componentGroup := componentGroup
+ for _, groupComponent := range componentGroup.Spec.Components {
+ groupComponent := groupComponent
+ allComponentSnapshotsInGroup, err = a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client,
+ a.pipelineRun.Namespace, groupComponent.Name, prGroupHash, componentGroup.Name, gitops.ComponentGroupNameLabel)
+ if err != nil {
+ return fmt.Errorf("failed to fetch Snapshots for component %s and pr group hash %s: %w", groupComponent.Name, prGroupHash, err)
+ }
+ if allComponentSnapshotsInGroup != nil && len(*allComponentSnapshotsInGroup) > 0 {
+ latestSnapshot := gitops.SortSnapshots(*allComponentSnapshotsInGroup)[0]
+ err = gitops.AnnotateSnapshot(a.context, &latestSnapshot, gitops.PRGroupCreationAnnotation,
+ message, a.client)
+ if err != nil {
+ return fmt.Errorf("failed to annotate latest snapshot %s of component %s with PR group creation annotation: %w",
+ latestSnapshot.Name, groupComponent.Name, err)
+ }
+ }
}
}
}
@@ -713,7 +791,7 @@ func (a *Adapter) notifySnapshotsInGroupAboutBuild(pipelineRun *tektonv1.Pipelin
if !h.HasPipelineRunFinished(&buildPipelineRun) && buildPipelineRun.Labels[tektonconsts.ComponentNameLabel] != a.component.Name {
err := tekton.AnnotateBuildPipelineRun(a.context, &buildPipelineRun, gitops.PRGroupCreationAnnotation, message, a.client)
if err != nil {
- return err
+ return fmt.Errorf("failed to annotate build pipelineRun %s with PR group creation annotation: %w", buildPipelineRun.Name, err)
}
}
}
@@ -723,6 +801,22 @@ func (a *Adapter) notifySnapshotsInGroupAboutBuild(pipelineRun *tektonv1.Pipelin
return nil
}
+func (a *Adapter) filterPipelineRunsForComponentGroups(allBuildPipelineRuns *[]tektonv1.PipelineRun, componentGroups *[]v1beta2.ComponentGroup) *[]tektonv1.PipelineRun {
+ var filteredBuildPipelineRuns []tektonv1.PipelineRun
+ for _, buildPipelineRun := range *allBuildPipelineRuns {
+ if builtComponentName, found := buildPipelineRun.Labels[tektonconsts.PipelineRunComponentLabel]; found {
+ for _, componentGroup := range *componentGroups {
+ componentNames := h.GetComponentNamesFromComponentGroup(&componentGroup)
+ if slices.Contains(componentNames, builtComponentName) {
+ filteredBuildPipelineRuns = append(filteredBuildPipelineRuns, buildPipelineRun)
+ break
+ }
+ }
+ }
+ }
+ return &filteredBuildPipelineRuns
+}
+
// prepareSnapshotForPipelineRun prepares the Snapshot for a given PipelineRun,
// component and application. In case the Snapshot can't be created, an error will be returned.
func (a *Adapter) prepareSnapshotForPipelineRun(pipelineRun *tektonv1.PipelineRun, component *applicationapiv1alpha1.Component, application *applicationapiv1alpha1.Application) (*applicationapiv1alpha1.Snapshot, error) {
@@ -1042,7 +1136,7 @@ func (a *Adapter) addPRGroupToBuildPLRMetadata(pipelineRun *tektonv1.PipelineRun
// prepareTempComponentSnapshot will create a temporary component snapshot object to copy the labels/annotations from build pipelinerun
// and be used to communicate with git provider
-func (a *Adapter) prepareTempComponentSnapshot(pipelineRun *tektonv1.PipelineRun) *applicationapiv1alpha1.Snapshot {
+func (a *Adapter) prepareTempComponentSnapshot(pipelineRun *tektonv1.PipelineRun, object *metav1.ObjectMeta, isApplication bool) *applicationapiv1alpha1.Snapshot {
tempComponentSnapshot := &applicationapiv1alpha1.Snapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "tempComponentSnapshot",
@@ -1050,13 +1144,13 @@ func (a *Adapter) prepareTempComponentSnapshot(pipelineRun *tektonv1.PipelineRun
},
}
prefixes := []string{gitops.BuildPipelineRunPrefix, gitops.TestLabelPrefix, gitops.CustomLabelPrefix, tektonconsts.ResourceLabelSuffix}
- gitops.CopySnapshotLabelsAndAnnotations(&a.application.ObjectMeta, tempComponentSnapshot, a.component.Name, &pipelineRun.ObjectMeta, prefixes, true)
+ gitops.CopySnapshotLabelsAndAnnotations(object, tempComponentSnapshot, a.component.Name, &pipelineRun.ObjectMeta, prefixes, isApplication)
return tempComponentSnapshot
}
// prepareTempGroupSnapshot will create a temporary group snapshot object to copy the labels/annotations from build pipelinerun
// and be used to communicate with git provider
-func (a *Adapter) prepareTempGroupSnapshot(pipelineRun *tektonv1.PipelineRun) *applicationapiv1alpha1.Snapshot {
+func (a *Adapter) prepareTempGroupSnapshot(pipelineRun *tektonv1.PipelineRun, object *metav1.ObjectMeta, isApplication bool) *applicationapiv1alpha1.Snapshot {
tempGroupSnapshot := &applicationapiv1alpha1.Snapshot{
ObjectMeta: metav1.ObjectMeta{
Name: "tempGroupSnapshot",
@@ -1064,7 +1158,7 @@ func (a *Adapter) prepareTempGroupSnapshot(pipelineRun *tektonv1.PipelineRun) *a
},
}
prefixes := []string{gitops.BuildPipelineRunPrefix}
- gitops.CopyTempGroupSnapshotLabelsAndAnnotations(a.application, tempGroupSnapshot, a.component.Name, &pipelineRun.ObjectMeta, prefixes)
+ gitops.CopyTempGroupSnapshotLabelsAndAnnotations(object, tempGroupSnapshot, a.component.Name, &pipelineRun.ObjectMeta, prefixes, isApplication)
return tempGroupSnapshot
}
@@ -1190,10 +1284,20 @@ func generateIntgTestStatusDetails(buildPLR *tektonv1.PipelineRun, integrationTe
// getComponentFromLatestFlightBuildPLR get the components from the build pipelineruns which have not snapshot created for the given pr group
// according to the given pr group
func (a *Adapter) getComponentsFromLatestFlightBuildPLR(prGroup, prGroupHash string) ([]string, error) {
- pipelineRuns, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.pipelineRun.Namespace, prGroupHash, a.application.Name)
- if err != nil {
- a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
- return nil, err
+ var pipelineRuns *[]tektonv1.PipelineRun
+ var err error
+ if a.application != nil {
+ pipelineRuns, err = a.loader.GetPipelineRunsWithPRGroupHashForApplication(a.context, a.client, a.pipelineRun.Namespace, prGroupHash, a.application.Name)
+ if err != nil {
+ a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s and application %s", prGroupHash, a.application.Name))
+ return nil, err
+ }
+ } else {
+ pipelineRuns, err = a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.pipelineRun.Namespace, prGroupHash)
+ if err != nil {
+ a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
+ return nil, err
+ }
}
var componentsFromPipelineRun []string
@@ -1216,7 +1320,7 @@ func (a *Adapter) getComponentsFromLatestFlightBuildPLR(prGroup, prGroupHash str
}
// isGroupSnapshotExpectedForBuildPLR return group context ITS if group snapshot is expected for the same pr group with the given build PLR
-func (a *Adapter) isGroupSnapshotExpectedForBuildPLR(pipelineRun *tektonv1.PipelineRun, tempComponentSnapshot *applicationapiv1alpha1.Snapshot) (bool, error) {
+func (a *Adapter) isGroupSnapshotExpectedForBuildPLR(pipelineRun *tektonv1.PipelineRun, tempComponentSnapshot *applicationapiv1alpha1.Snapshot, ownerName, ownerLabel string) (bool, error) {
prGroupHash, prGroup := gitops.GetPRGroup(pipelineRun)
if prGroupHash == "" || prGroup == "" {
a.logger.Error(fmt.Errorf("NotFound"), fmt.Sprintf("Failed to get PR group label/annotation from pipelineRun %s/%s", pipelineRun.Namespace, pipelineRun.Name))
@@ -1229,11 +1333,11 @@ func (a *Adapter) isGroupSnapshotExpectedForBuildPLR(pipelineRun *tektonv1.Pipel
return false, err
}
if len(componentsWithOpenPRMR) > 1 {
- a.logger.Info(fmt.Sprintf("there is more than 1 component with open pr or mr found, so group snapshot is expected: %s", componentsWithOpenPRMR))
+ a.logger.Info(fmt.Sprintf("there is more than 1 component with open pr or mr found, so group snapshot is expected for %s: %s", ownerName, componentsWithOpenPRMR))
return true, nil
}
- componentsFromSnapshot, err := a.loader.GetComponentsFromSnapshotForPRGroup(a.context, a.client, pipelineRun.Namespace, prGroup, prGroupHash, a.application.Name)
+ componentsFromSnapshot, err := a.loader.GetComponentsFromSnapshotForPRGroup(a.context, a.client, pipelineRun.Namespace, prGroupHash, ownerName, ownerLabel)
if err != nil {
a.logger.Error(err, fmt.Sprintf("failed to get component from component snapshot for pr group %s", prGroup))
return false, err
@@ -1245,7 +1349,7 @@ func (a *Adapter) isGroupSnapshotExpectedForBuildPLR(pipelineRun *tektonv1.Pipel
continue
}
- snapshots, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, pipelineRun.Namespace, componentName, prGroupHash, a.application.Name)
+ snapshots, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, pipelineRun.Namespace, componentName, prGroupHash, ownerName, ownerLabel)
if err != nil {
a.logger.Error(err, "Failed to fetch Snapshots for component", "component.Name", componentName)
return false, err
@@ -1263,10 +1367,10 @@ func (a *Adapter) isGroupSnapshotExpectedForBuildPLR(pipelineRun *tektonv1.Pipel
}
if len(componentsWithOpenPRMR) < 2 {
- a.logger.Info(fmt.Sprintf("The number %d of components affected by this PR group %s is less than 2, skipping group snapshot status report", len(componentsWithOpenPRMR), prGroup))
+ a.logger.Info(fmt.Sprintf("The number %d of components affected by this PR group %s is less than 2, skipping group snapshot status report for %s", len(componentsWithOpenPRMR), prGroup, ownerName))
return false, nil
}
- a.logger.Info(fmt.Sprintf("there is more than 1 component with open pr or mr found, so group snapshot is expected: %s", componentsWithOpenPRMR))
+ a.logger.Info(fmt.Sprintf("there is more than 1 component with open pr or mr found, so group snapshot is expected for %s: %s", ownerName, componentsWithOpenPRMR))
return true, nil
}
@@ -1349,7 +1453,7 @@ func isBuildPLROlderThanLastBuild(pipelineRun *tektonv1.PipelineRun, component *
func (a *Adapter) IsLatestBuildPipelineRunInComponentWithPRGroupHash(buildPlr *tektonv1.PipelineRun) (bool, error) {
prGroupHash := buildPlr.Labels[gitops.PRGroupHashLabel]
prGroupName := buildPlr.Annotations[gitops.PRGroupAnnotation]
- pipelineRuns, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, buildPlr.Namespace, prGroupHash, a.application.Name)
+ pipelineRuns, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, buildPlr.Namespace, prGroupHash)
if err != nil {
a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
return false, err
diff --git a/internal/controller/buildpipeline/buildpipeline_adapter_test.go b/internal/controller/buildpipeline/buildpipeline_adapter_test.go
index 9a913f2722..96b14dfa5b 100644
--- a/internal/controller/buildpipeline/buildpipeline_adapter_test.go
+++ b/internal/controller/buildpipeline/buildpipeline_adapter_test.go
@@ -539,6 +539,87 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
+ When("filterPipelineRunsForComponentGroups is called", func() {
+
+ BeforeEach(func() {
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &[]v1beta2.ComponentGroup{*hasCompGroup}, logger, loader.NewMockLoader(), k8sClient)
+ })
+
+ It("returns an empty filtered list when there are no build PipelineRuns", func() {
+ empty := []tektonv1.PipelineRun{}
+ groups := &[]v1beta2.ComponentGroup{*hasCompGroup}
+
+ result := adapter.filterPipelineRunsForComponentGroups(&empty, groups)
+ Expect(result).NotTo(BeNil())
+ Expect(*result).To(BeEmpty())
+ })
+
+ It("keeps PipelineRuns whose component label matches a component listed in some ComponentGroup, and skips others", func() {
+ plrMatching := tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "build-plr-matching",
+ Labels: map[string]string{
+ tektonconsts.PipelineRunComponentLabel: "component-sample",
+ },
+ },
+ }
+ plrUnknownComponent := tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "build-plr-unknown",
+ Labels: map[string]string{
+ tektonconsts.PipelineRunComponentLabel: "not-in-any-group",
+ },
+ },
+ }
+ plrNoComponentLabel := tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{Name: "build-plr-no-label"},
+ }
+
+ stack := []tektonv1.PipelineRun{plrMatching, plrUnknownComponent, plrNoComponentLabel}
+ groups := &[]v1beta2.ComponentGroup{*hasCompGroup}
+
+ result := adapter.filterPipelineRunsForComponentGroups(&stack, groups)
+ Expect(result).NotTo(BeNil())
+ Expect(*result).To(HaveLen(1))
+ Expect((*result)[0].Name).To(Equal("build-plr-matching"))
+ Expect((*result)[0].Labels[tektonconsts.PipelineRunComponentLabel]).To(Equal("component-sample"))
+ })
+
+ It("includes a PipelineRun when the component matches any ComponentGroup in order", func() {
+ groupAlpha := v1beta2.ComponentGroup{
+ ObjectMeta: metav1.ObjectMeta{Name: "group-alpha"},
+ Spec: v1beta2.ComponentGroupSpec{
+ Components: []v1beta2.ComponentReference{
+ {Name: "only-alpha", ComponentVersion: v1beta2.ComponentVersionReference{Name: "v1"}},
+ },
+ },
+ }
+ groupBeta := v1beta2.ComponentGroup{
+ ObjectMeta: metav1.ObjectMeta{Name: "group-beta"},
+ Spec: v1beta2.ComponentGroupSpec{
+ Components: []v1beta2.ComponentReference{
+ {Name: "only-beta", ComponentVersion: v1beta2.ComponentVersionReference{Name: "v1"}},
+ },
+ },
+ }
+ plr := tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "build-plr-only-beta",
+ Labels: map[string]string{
+ tektonconsts.PipelineRunComponentLabel: "only-beta",
+ },
+ },
+ }
+ stack := []tektonv1.PipelineRun{plr}
+ groups := &[]v1beta2.ComponentGroup{groupAlpha, groupBeta}
+
+ result := adapter.filterPipelineRunsForComponentGroups(&stack, groups)
+ Expect(result).NotTo(BeNil())
+ Expect(*result).To(HaveLen(1))
+ Expect((*result)[0].Name).To(Equal("build-plr-only-beta"))
+ })
+ })
+
When("NewAdapter is created", func() {
BeforeEach(func() {
adapter = createAdapter()
@@ -678,7 +759,7 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
Eventually(func() bool {
result, err := adapter.EnsureSnapshotExists()
return !result.CancelRequest && err != nil
- }, time.Second*10).Should(BeTrue())
+ }, time.Second*15).Should(BeTrue())
expectedLogEntry := "Exceeded timeout waiting for Chains signing"
Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
@@ -1814,8 +1895,8 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
- // NOTE: PR group support will be addressed in STONEINTG-1519
- When("add pr group to the build pipelineRun annotations and labels", func() {
+ // TODO: Remove after the application model is deprecated
+ When("add pr group to the build pipelineRun annotations and labels [APPLICATION]", func() {
BeforeEach(func() {
adapter = NewAdapterWithApplication(ctx, buildPipelineRun, hasComp, hasApp, logger, loader.NewMockLoader(), k8sClient)
})
@@ -1851,6 +1932,43 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
+ When("add pr group to the build pipelineRun annotations and labels", func() {
+ BeforeEach(func() {
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, logger, loader.NewMockLoader(), k8sClient)
+ })
+ It("add pr group to the build pipelineRun annotations and labels", func() {
+ existingBuildPLR := new(tektonv1.PipelineRun)
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, existingBuildPLR)
+ return err == nil
+ }, time.Second*10).Should(BeTrue())
+
+ 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())
+
+ Eventually(func() bool {
+ _ = adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, existingBuildPLR)
+ return metadata.HasAnnotation(existingBuildPLR, gitops.PRGroupAnnotation) && metadata.HasLabel(existingBuildPLR, gitops.PRGroupHashLabel)
+ }, time.Second*10).Should(BeTrue())
+
+ Expect(existingBuildPLR.Annotations).Should(HaveKeyWithValue(Equal(gitops.PRGroupAnnotation), Equal("sourceBranch")))
+ Expect(existingBuildPLR.Labels[gitops.PRGroupHashLabel]).NotTo(BeNil())
+ })
+ })
+
When("running pipeline with deletion timestamp is processed", func() {
var runningDeletingBuildPipeline *tektonv1.PipelineRun
@@ -2209,8 +2327,8 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
}, time.Second*20).Should(BeTrue())
})
- // NOTE: PR group support will be addressed in STONEINTG-1519
- When("add pr group to the build pipelineRun annotations and labels", func() {
+ // TODO: Remove after application model is deprecated
+ When("add pr group to the build pipelineRun annotations and labels [APPLICATION]", func() {
BeforeEach(func() {
// Mock an in-flight component build PLR that belongs to the same PR group
otherComp := hasComp.DeepCopy()
@@ -2286,6 +2404,92 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
return err == nil && metadata.HasAnnotation(hasSnapshot, gitops.PRGroupCreationAnnotation)
}, time.Second*10).Should(BeTrue())
+ Eventually(func() bool {
+ err = adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, buildPipelineRun)
+ return err == nil && metadata.HasAnnotation(buildPipelineRun, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+ })
+ })
+ When("add pr group to the build pipelineRun annotations and labels", func() {
+ BeforeEach(func() {
+ // Mock an in-flight component build PLR that belongs to the same PR group
+ otherComp := hasComp.DeepCopy()
+ otherComp.Name = "other-component"
+
+ buildPipelineRun2 = buildPipelineRun.DeepCopy()
+ buildPipelineRun2.Name = "incoming-build-pipeline-run"
+ buildPipelineRun2.Labels[tektonconsts.ComponentNameLabel] = otherComp.Name
+ delete(buildPipelineRun2.Annotations, gitops.PRGroupAnnotation)
+ delete(buildPipelineRun2.Labels, gitops.PRGroupHashLabel)
+ buildPipelineRun2.ResourceVersion = ""
+
+ Expect(k8sClient.Create(ctx, buildPipelineRun2)).Should(Succeed())
+
+ buildPipelineRun2.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Running",
+ Status: "Unknown",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun2)).Should(Succeed())
+
+ // Set the timestamp in the future, so it's newer than the original buildPipelineRun
+ buildPipelineRun2.CreationTimestamp = metav1.NewTime(time.Now().Add(time.Hour * 12))
+
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := &[]v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun2, otherComp, componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasComp},
+ },
+ {
+ ContextKey: loader.GetComponentSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot},
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*buildPipelineRun, *buildPipelineRun2},
+ },
+ })
+ })
+ AfterEach(func() {
+ Expect(k8sClient.Delete(ctx, buildPipelineRun2)).Should(Succeed())
+ })
+ It("notifies the latest Snapshots and in-flight builds in the PR group about the incoming new build pipelineRun", func() {
+ result, err := adapter.EnsurePRGroupAnnotated()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+
+ expectedLogEntry := "pr group info is updated to build pipelineRun metadata"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ expectedLogEntry = "notified all component snapshots and build pipelines in the pr group about the build pipeline status"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ expectedLogEntry = "build pipelineRun has had pr group info in metadata, no need to update"
+ Expect(buf.String()).ShouldNot(ContainSubstring(expectedLogEntry))
+
+ Eventually(func() bool {
+ err := adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: hasSnapshot.Namespace,
+ Name: hasSnapshot.Name,
+ }, hasSnapshot)
+ return err == nil && metadata.HasAnnotation(hasSnapshot, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+
Eventually(func() bool {
err = adapter.client.Get(adapter.context, types.NamespacedName{
Namespace: buildPipelineRun.Namespace,
@@ -2345,8 +2549,8 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
}, time.Second*20).Should(BeTrue())
})
- // NOTE: PR group support will be addressed in STONEINTG-1519
- When("add pr group to the build pipelineRun annotations and labels", func() {
+ // TODO: Remove after application model is deprecated
+ When("add pr group to the build pipelineRun annotations and labels [APPLICATION]", func() {
BeforeEach(func() {
// Add label and annotation to PLR
err := metadata.AddLabels(buildPipelineRun, map[string]string{gitops.PRGroupHashLabel: prGroupSha})
@@ -2431,9 +2635,95 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
}, time.Second*10).Should(BeTrue())
})
})
-
- // NOTE: PR group support will be addressed in STONEINTG-1519
When("add pr group to the build pipelineRun annotations and labels", func() {
+ BeforeEach(func() {
+ // Add label and annotation to PLR
+ err := metadata.AddLabels(buildPipelineRun, map[string]string{gitops.PRGroupHashLabel: prGroupSha})
+ Expect(err).NotTo(HaveOccurred())
+ err = metadata.AddAnnotations(buildPipelineRun, map[string]string{gitops.PRGroupAnnotation: prGroup})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient.Update(ctx, buildPipelineRun)).Should(Succeed())
+
+ Eventually(func() bool {
+ _ = k8sClient.Get(ctx, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, buildPipelineRun)
+ return metadata.HasAnnotation(buildPipelineRun, gitops.PRGroupAnnotation) && metadata.HasLabel(buildPipelineRun, gitops.PRGroupHashLabel)
+ }, time.Second*10).Should(BeTrue())
+
+ Expect(helpers.HasPipelineRunFinished(buildPipelineRun)).Should(BeTrue())
+ Expect(helpers.HasPipelineRunSucceeded(buildPipelineRun)).Should(BeFalse())
+
+ // Mock an in-flight component build PLR that belongs to the same PR group and component and is newer
+ inFlightBuildPLR := buildPipelineRun.DeepCopy()
+ inFlightBuildPLR.Name = "in-flight-build-plr"
+ inFlightBuildPLR.CreationTimestamp = metav1.NewTime(time.Now().Add(time.Hour * 12))
+ inFlightBuildPLR.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Running",
+ Status: "Unknown",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := &[]v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasComp},
+ },
+ {
+ ContextKey: loader.GetComponentSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot},
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*inFlightBuildPLR, *buildPipelineRun},
+ },
+ })
+ })
+ It("doesn't notify latest Snapshots and in-flight builds in the PR group about the build pipeline failure because it's not the latest build", func() {
+ result, err := adapter.EnsurePRGroupAnnotated()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+
+ expectedLogEntry := "not the latest pipelineRun, skipping notifying the group about the failure"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ expectedLogEntry = "notified all component snapshots and build pipelines in the pr group about the build pipeline status"
+ Expect(buf.String()).ShouldNot(ContainSubstring(expectedLogEntry))
+
+ Eventually(func() bool {
+ err := adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: hasSnapshot.Namespace,
+ Name: hasSnapshot.Name,
+ }, hasSnapshot)
+ return err == nil && !metadata.HasAnnotation(hasSnapshot, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+
+ Eventually(func() bool {
+ err = adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, buildPipelineRun)
+ return err == nil && !metadata.HasAnnotation(buildPipelineRun, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+ })
+ })
+
+ // TODO: Remove after the application model is deprecated
+ When("add pr group to the build pipelineRun annotations and labels [APPLICATION]", func() {
BeforeEach(func() {
// Remove the PR group creation Annotation from the group Snapshot
delete(hasSnapshot.Annotations, gitops.PRGroupCreationAnnotation)
@@ -2518,11 +2808,97 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
Expect(buildPipelineRun.Annotations[gitops.PRGroupCreationAnnotation]).Should(ContainSubstring(expectedBuildFailureMsg))
})
})
+ When("add pr group to the build pipelineRun annotations and labels", func() {
+ BeforeEach(func() {
+ // Remove the PR group creation Annotation from the group Snapshot
+ delete(hasSnapshot.Annotations, gitops.PRGroupCreationAnnotation)
+ Expect(k8sClient.Update(ctx, hasSnapshot)).Should(Succeed())
+ // Add label and annotation to PLR
+ err := metadata.AddLabels(buildPipelineRun, map[string]string{gitops.PRGroupHashLabel: prGroupSha})
+ Expect(err).NotTo(HaveOccurred())
+ err = metadata.AddAnnotations(buildPipelineRun, map[string]string{gitops.PRGroupAnnotation: prGroup})
+ Expect(err).NotTo(HaveOccurred())
+ Expect(k8sClient.Update(ctx, buildPipelineRun)).Should(Succeed())
+
+ Eventually(func() bool {
+ _ = k8sClient.Get(ctx, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, buildPipelineRun)
+ return metadata.HasAnnotation(buildPipelineRun, gitops.PRGroupAnnotation) && metadata.HasLabel(buildPipelineRun, gitops.PRGroupHashLabel)
+ }, time.Second*10).Should(BeTrue())
+
+ Expect(helpers.HasPipelineRunFinished(buildPipelineRun)).Should(BeTrue())
+ Expect(helpers.HasPipelineRunSucceeded(buildPipelineRun)).Should(BeFalse())
+
+ // Mock an in-flight component build PLR that belongs to the same PR group
+ inFlightBuildPLR := buildPipelineRun.DeepCopy()
+ inFlightBuildPLR.Labels[tektonconsts.ComponentNameLabel] = "another-component-sample"
+ inFlightBuildPLR.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Running",
+ Status: "Unknown",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, logger, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasComp},
+ },
+ {
+ ContextKey: loader.GetComponentSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot},
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*inFlightBuildPLR},
+ },
+ })
+ })
+ It("notifies all latest Snapshots and in-flight builds in the PR group about the build pipeline failure", func() {
+ result, err := adapter.EnsurePRGroupAnnotated()
+ Expect(err).NotTo(HaveOccurred())
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+
+ Eventually(func() bool {
+ err = adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: hasSnapshot.Namespace,
+ Name: hasSnapshot.Name,
+ }, hasSnapshot)
+ return err == nil && metadata.HasAnnotation(hasSnapshot, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+
+ expectedBuildFailureMsg := fmt.Sprintf("build PLR %s failed for component %s so it can't be added to the group Snapshot for PR group %s", buildPipelineRun.Name, hasComp.Name, prGroup)
+ Expect(hasSnapshot.Annotations[gitops.PRGroupCreationAnnotation]).Should(ContainSubstring(expectedBuildFailureMsg))
+
+ Eventually(func() bool {
+ err = adapter.client.Get(adapter.context, types.NamespacedName{
+ Namespace: buildPipelineRun.Namespace,
+ Name: buildPipelineRun.Name,
+ }, buildPipelineRun)
+ return err == nil && metadata.HasAnnotation(buildPipelineRun, gitops.PRGroupCreationAnnotation)
+ }, time.Second*10).Should(BeTrue())
+
+ Expect(buildPipelineRun.Annotations[gitops.PRGroupCreationAnnotation]).Should(ContainSubstring(expectedBuildFailureMsg))
+ })
+ })
})
- // NOTE: PR group support will be addressed in STONEINTG-1519
- When("a build PLR is triggered or retirggered, succeeded or failed", func() {
+ // TODO: Remove after the application model is deprecated
+ When("a build PLR is triggered or retriggered, succeeded or failed [APPLICATION]", func() {
BeforeEach(func() {
patch := client.MergeFrom(buildPipelineRun.DeepCopy())
_ = metadata.SetAnnotation(&buildPipelineRun.ObjectMeta, gitops.PRGroupAnnotation, prGroup)
@@ -2800,7 +3176,7 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
expectedLogEntry = "group snapshot is expected to be created for build pipelinerun"
Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
- expectedLogEntry = "there is more than 1 component with open pr or mr found, so group snapshot is expected: [component-sample another-component-sample]"
+ expectedLogEntry = "there is more than 1 component with open pr or mr found, so group snapshot is expected for application-sample: [component-sample another-component-sample]"
Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
Expect(result.CancelRequest).To(BeFalse())
Expect(result.RequeueRequest).To(BeFalse())
@@ -2808,8 +3184,352 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
- // NOTE: PR group support will be addressed in STONEINTG-1519
- When("integration status should not be set from build PLR", func() {
+ When("a build PLR is triggered or retriggered, succeeded or failed", func() {
+ BeforeEach(func() {
+ patch := client.MergeFrom(buildPipelineRun.DeepCopy())
+ _ = metadata.SetAnnotation(&buildPipelineRun.ObjectMeta, gitops.PRGroupAnnotation, prGroup)
+ _ = metadata.SetLabel(&buildPipelineRun.ObjectMeta, gitops.PRGroupHashLabel, prGroupSha)
+ Expect(k8sClient.Patch(ctx, buildPipelineRun, patch)).Should(Succeed())
+ ctrl := gomock.NewController(GinkgoT())
+ mockReporter = status.NewMockReporterInterface(ctrl)
+ mockStatus = status.NewMockStatusInterface(ctrl)
+ mockReporter.EXPECT().GetReporterName().Return("mocked-reporter").AnyTimes()
+ mockStatus.EXPECT().GetReporter(gomock.Any()).Return(mockReporter).AnyTimes()
+ mockStatus.EXPECT().FindSnapshotWithOpenedPR(gomock.Any(), gomock.Any(), gomock.Any()).Return(hasSnapshot, 0, nil).AnyTimes()
+ mockReporter.EXPECT().GetReporterName().AnyTimes()
+ mockReporter.EXPECT().Initialize(gomock.Any(), gomock.Any()).AnyTimes()
+ mockReporter.EXPECT().ReportStatus(gomock.Any(), gomock.Any()).AnyTimes()
+ })
+ It("ensure integration test is initialized from build PLR", func() {
+ buildPipelineRun.Status = tektonv1.PipelineRunStatus{
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Running",
+ Status: "Unknown",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun)).Should(Succeed())
+
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupsContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ Expect(metadata.HasAnnotationWithValue(buildPipelineRun, helpers.SnapshotCreationReportAnnotation, intgteststat.BuildPLRInProgress.String())).To(BeTrue())
+
+ result, err = adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ expectedLogEntry := "integration test has been set correctly or is being processed, no need to set integration test status from build pipelinerun"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ })
+
+ It("ensure integration test is set from build PLR when build PLR fails", func() {
+ buildPipelineRun.Status = tektonv1.PipelineRunStatus{
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Failed",
+ Status: "False",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun)).Should(Succeed())
+
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupsContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ Expect(metadata.HasAnnotationWithValue(buildPipelineRun, helpers.SnapshotCreationReportAnnotation, intgteststat.BuildPLRFailed.String())).To(BeTrue())
+
+ result, err = adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ expectedLogEntry := "integration test has been set correctly or is being processed, no need to set integration test status from build pipelinerun"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ })
+
+ It("ensure integration test is set from build PLR when build PLR succeeded but snapshot is not created", func() {
+ Expect(metadata.SetAnnotation(buildPipelineRun, helpers.CreateSnapshotAnnotationName, "failed to create snapshot due to error")).ShouldNot(HaveOccurred())
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupsContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ Expect(metadata.HasAnnotationWithValue(buildPipelineRun, helpers.SnapshotCreationReportAnnotation, intgteststat.SnapshotCreationFailed.String())).To(BeTrue())
+
+ result, err = adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ expectedLogEntry := "integration test has been set correctly or is being processed, no need to set integration test status from build pipelinerun"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ })
+
+ It("add annotation to snapshot when no git provider is found", func() {
+ // Create a snapshot WITHOUT git provider info but WITH proper component labels
+ snapshotWithoutGitProvider := &applicationapiv1alpha1.Snapshot{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test-snapshot-no-git",
+ Namespace: "test-namespace",
+ Labels: map[string]string{
+ gitops.SnapshotTypeLabel: gitops.SnapshotComponentType,
+ gitops.SnapshotComponentLabel: "test-component",
+ },
+ Annotations: map[string]string{
+ "test.appstudio.openshift.io/status": "[{\"scenario\":\"scenario1\",\"status\":\"InProgress\",\"startTime\":\"2023-07-26T16:57:49+02:00\",\"lastUpdateTime\":\"2023-08-26T17:57:50+02:00\",\"details\":\"Test in progress\"}]",
+ },
+ // NO git provider labels!
+ },
+ }
+
+ // Create a buffer for logging
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+
+ // Set up mocks
+ ctrl := gomock.NewController(GinkgoT())
+ mockReporter = status.NewMockReporterInterface(ctrl)
+ mockStatus := status.NewMockStatusInterface(ctrl)
+
+ // Mock GetReporter to return nil (no git provider found)
+ mockStatus.EXPECT().GetReporter(gomock.Any()).Return(nil)
+
+ // Create adapter
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+
+ // Create the required test data
+ integrationTestScenarios := []v1beta2.IntegrationTestScenario{*integrationTestScenario}
+ testStatus := intgteststat.IntegrationTestStatusInProgress
+ componentName := "test-component"
+
+ // Call the method
+ statusCode, err := adapter.ReportIntegrationTestStatusAccordingToBuildPLR(
+ buildPipelineRun,
+ snapshotWithoutGitProvider,
+ &integrationTestScenarios,
+ testStatus,
+ componentName)
+
+ // Check results
+ Expect(err).ToNot(HaveOccurred())
+ Expect(statusCode).To(BeTrue())
+ Expect(snapshotWithoutGitProvider.Annotations).To(HaveKey(gitops.GitReportingFailureAnnotation))
+
+ // Check that the error was logged
+ Expect(buf.String()).Should(ContainSubstring("Failed to get git reporter for snapshot - missing required labels/annotations"))
+ })
+
+ It("Ensure group context integration test can be initialized", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ buildPipelineRun = &tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "pipelinerun-build-sample",
+ Namespace: "default",
+ Labels: map[string]string{
+ "pipelines.appstudio.openshift.io/type": "build",
+ "pipelines.openshift.io/used-by": "build-cloud",
+ "pipelines.openshift.io/runtime": "nodejs",
+ "pipelines.openshift.io/strategy": "s2i",
+ "appstudio.openshift.io/component": "component-sample",
+ "build.appstudio.redhat.com/target_branch": "main",
+ "pipelinesascode.tekton.dev/event-type": "pull_request",
+ "pipelinesascode.tekton.dev/pull-request": "1",
+ "pipelinesascode.tekton.dev/git-provider": "github",
+ customLabel: "custom-label",
+ gitops.PRGroupHashLabel: prGroupSha,
+ },
+ Annotations: map[string]string{
+ "appstudio.redhat.com/updateComponentOnSuccess": "false",
+ "pipelinesascode.tekton.dev/on-target-branch": "[main,master]",
+ "build.appstudio.openshift.io/repo": "https://github.com/devfile-samples/devfile-sample-go-basic?rev=c713067b0e65fb3de50d1f7c457eb51c2ab0dbb0",
+ "chains.tekton.dev/signed": "true",
+ "pipelinesascode.tekton.dev/source-branch": "sourceBranch",
+ "pipelinesascode.tekton.dev/url-org": "redhat",
+ "pipelinesascode.tekton.dev/git-provider": "github",
+ gitops.PRGroupAnnotation: prGroup,
+ },
+ CreationTimestamp: metav1.NewTime(time.Now().Add(time.Hour * 1)),
+ },
+ Spec: tektonv1.PipelineRunSpec{},
+ }
+
+ buildPipelineRun2 = &tektonv1.PipelineRun{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "pipelinerun-build-sample-2",
+ Namespace: "default",
+ Labels: map[string]string{
+ "pipelines.appstudio.openshift.io/type": "build",
+ "pipelines.openshift.io/used-by": "build-cloud",
+ "pipelines.openshift.io/runtime": "nodejs",
+ "pipelines.openshift.io/strategy": "s2i",
+ "appstudio.openshift.io/component": "component-sample",
+ "pipelinesascode.tekton.dev/event-type": "pull_request",
+ "pipelinesascode.tekton.dev/git-provider": "github",
+ gitops.PRGroupHashLabel: prGroupSha,
+ },
+ Annotations: map[string]string{
+ "appstudio.redhat.com/updateComponentOnSuccess": "false",
+ "pipelinesascode.tekton.dev/on-target-branch": "[main,master]",
+ gitops.PRGroupAnnotation: prGroup,
+ "pipelinesascode.tekton.dev/git-provider": "github",
+ },
+ CreationTimestamp: metav1.NewTime(time.Now()),
+ },
+ Spec: tektonv1.PipelineRunSpec{},
+ }
+
+ //mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), hasComSnapshot2).Return(true, nil)
+ mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), gomock.Any()).Return(true, 0, nil).AnyTimes()
+
+ // Add another componentGroup with only one component
+ hasCompGroup2 := hasCompGroup.DeepCopy()
+ hasCompGroup2.Name = "component-group-sample2"
+ hasCompGroup2.Spec.Components = []v1beta2.ComponentReference{
+ {
+ Name: "component-sample",
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ }
+ // Add another componentGroup with two components
+ hasCompGroup3 := hasCompGroup.DeepCopy()
+ hasCompGroup3.Name = "component-group-sample3"
+ hasCompGroup3.Spec.Components = []v1beta2.ComponentReference{
+ {
+ Name: "component-sample",
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ {
+ Name: "another-component-sample",
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ }
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup, *hasCompGroup2, *hasCompGroup3}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: *hasCompGroup,
+ },
+ {
+ ContextKey: loader.GetPRSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot, *hasComSnapshot2},
+ },
+ {
+ ContextKey: loader.GetComponentSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasSnapshot, *hasComSnapshot2},
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*buildPipelineRun, *buildPipelineRun2},
+ },
+ {
+ ContextKey: loader.ComponentGroupComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasComp, *hasComp2},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.GetComponentsFromSnapshotForPRGroupKey,
+ Resource: []string{hasComp.Name, hasComp2.Name},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupsContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario, *groupIntegrationTestScenario},
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ expectedLogEntry := "Opened PR/MR in snapshot is found"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ expectedLogEntry = "group snapshot is expected to be created for build pipelinerun"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ expectedLogEntry = "there is more than 1 component with open pr or mr found, so group snapshot is expected for component-group-sample: [component-sample another-component-sample]"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ // Since the second componentGroup contains only one component, it should not have an expected group Snapshot
+ expectedLogEntry = "there is more than 1 component with open pr or mr found, so group snapshot is expected for component-group-sample2: [component-sample]"
+ Expect(buf.String()).ShouldNot(ContainSubstring(expectedLogEntry))
+ // Since the thirt componentGroup contains both components, it should have an expected group Snapshot
+ expectedLogEntry = "there is more than 1 component with open pr or mr found, so group snapshot is expected for component-group-sample3: [component-sample another-component-sample]"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(err).NotTo(HaveOccurred())
+ })
+ })
+
+ // TODO: Remove after the application model is deprecated
+ When("integration status should not be set from build PLR [APPLICATION]", func() {
BeforeAll(func() {
patch := client.MergeFrom(buildPipelineRun.DeepCopy())
_ = metadata.SetAnnotation(&buildPipelineRun.ObjectMeta, gitops.PRGroupAnnotation, prGroup)
@@ -2875,6 +3595,75 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
+ When("integration status should not be set from build PLR", func() {
+ BeforeAll(func() {
+ patch := client.MergeFrom(buildPipelineRun.DeepCopy())
+ _ = metadata.SetAnnotation(&buildPipelineRun.ObjectMeta, gitops.PRGroupAnnotation, prGroup)
+ _ = metadata.SetLabel(&buildPipelineRun.ObjectMeta, gitops.PRGroupHashLabel, prGroupSha)
+ Expect(k8sClient.Patch(ctx, buildPipelineRun, patch)).Should(Succeed())
+ })
+ It("integration test will not be set from build PLR when build PLR succeeded and snapshot is created", func() {
+ Expect(metadata.SetAnnotation(buildPipelineRun, tektonconsts.SnapshotNamesLabel, "snashot-sample")).ShouldNot(HaveOccurred())
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupContextKey,
+ Resource: hasCompGroup,
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ Expect(metadata.HasAnnotationWithValue(buildPipelineRun, helpers.SnapshotCreationReportAnnotation, "SnapshotCreated")).To(BeTrue())
+ expectedLogEntry := "snapshot has been created for build pipelineRun, no need to report integration status from build pipelinerun status"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ })
+
+ It("integration test will not be set from build PLR when build PLR is not from pac pull request event", func() {
+ Expect(metadata.DeleteLabel(buildPipelineRun, tektonconsts.PipelineAsCodePullRequestLabel)).ShouldNot(HaveOccurred())
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupContextKey,
+ Resource: hasCompGroup,
+ },
+ })
+
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ Expect(!result.CancelRequest && err == nil).To(BeTrue())
+ expectedLogEntry := "build pipelineRun is not created by pull/merge request, no need to set integration test status in git provider"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ })
+
+ It("integration test will not be set from build PLR when pr group is not annotated to build PLR", func() {
+ Expect(metadata.DeleteLabel(buildPipelineRun, gitops.PRGroupHashLabel)).ShouldNot(HaveOccurred())
+ Expect(metadata.DeleteAnnotation(buildPipelineRun, gitops.PRGroupAnnotation)).ShouldNot(HaveOccurred())
+ buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ componentGroups := []v1beta2.ComponentGroup{*hasCompGroup}
+ adapter = NewAdapter(ctx, buildPipelineRun, hasComp, &componentGroups, log, loader.NewMockLoader(), k8sClient)
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupContextKey,
+ Resource: hasCompGroup,
+ },
+ })
+ result, err := adapter.EnsureIntegrationTestReportedToGitProvider()
+ expectedLogEntry := "pr group info has not been added to build pipelineRun metadata, skipping reporting tests for the build pipelineRun"
+ Expect(buf.String()).Should(ContainSubstring(expectedLogEntry))
+ Expect(!result.RequeueRequest && err == nil).To(BeTrue())
+ })
+ })
+
// TODO: Duplicate for ComponentGroup (maybe)
When("build pipelineRun succdeds and is signed", func() {
BeforeEach(func() {
@@ -2929,7 +3718,7 @@ var _ = Describe("Pipeline Adapter", Ordered, func() {
})
})
- When("build pipelineRun succdeds and is signed [APPLICATION]", func() {
+ When("build pipelineRun succeeds and is signed [APPLICATION]", func() {
BeforeEach(func() {
Expect(metadata.DeleteLabel(buildPipelineRun, tektonconsts.PipelineAsCodePullRequestLabel)).ShouldNot(HaveOccurred())
adapter = createAdapterApplication()
diff --git a/internal/controller/snapshot/snapshot_adapter.go b/internal/controller/snapshot/snapshot_adapter.go
index e587722d61..3f55a3597a 100644
--- a/internal/controller/snapshot/snapshot_adapter.go
+++ b/internal/controller/snapshot/snapshot_adapter.go
@@ -21,6 +21,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "slices"
"strconv"
"strings"
"time"
@@ -734,7 +735,7 @@ func (a *Adapter) validateOverrideSnapshotComponents() error {
}
// EnsureGroupSnapshotExist is an operation that ensure the group snapshot is created for component snapshots
-// once a new component snapshot is created for an pull request and there are multiple existing PRs belonging to the same PR group
+// once a new component snapshot is created for a pull request and there are multiple existing PRs belonging to the same PR group
func (a *Adapter) EnsureGroupSnapshotExist() (controller.OperationResult, error) {
if gitops.IsSnapshotCreatedByPACPushEvent(a.snapshot) {
a.logger.Info("The snapshot is not created by PAC pull request, no need to create group snapshot")
@@ -775,8 +776,14 @@ func (a *Adapter) EnsureGroupSnapshotExist() (controller.OperationResult, error)
return controller.ContinueProcessing()
}
- // TODO: handle group snapshots in STONEINTG-1519
- groupSnapshot, componentSnapshotInfos, err := a.prepareGroupSnapshot(a.application, prGroup, prGroupHash)
+ var groupSnapshot *applicationapiv1alpha1.Snapshot
+ var componentSnapshotInfos []gitops.ComponentSnapshotInfo
+ // TODO: Remove application branch once the application model is deprecated
+ if a.application != nil {
+ groupSnapshot, componentSnapshotInfos, err = a.prepareGroupSnapshotApplication(prGroup, prGroupHash)
+ } else {
+ groupSnapshot, componentSnapshotInfos, err = a.prepareGroupSnapshot(prGroup, prGroupHash)
+ }
if err != nil {
a.logger.Error(err, "failed to prepare group snapshot")
if h.IsUnrecoverableMetadataError(err) || clienterrors.IsNotFound(err) {
@@ -994,90 +1001,160 @@ func (a *Adapter) HandlePipelineCreationError(err error, integrationTestScenario
return controller.RequeueWithError(err)
}
-// TODO: update in STONEINTG-1519
-func (a *Adapter) prepareGroupSnapshot(application *applicationapiv1alpha1.Application, prGroup, prGroupHash string) (*applicationapiv1alpha1.Snapshot, []gitops.ComponentSnapshotInfo, error) {
- componentsToCheck, err := a.loader.GetComponentsFromSnapshotForPRGroup(a.context, a.client, application.Namespace, prGroup, prGroupHash, application.Name)
+// prepareGroupSnapshot prepares a Group Snapshot based on the existing component Snapshots which belong to the same PR group
+// It contains all of the updated components from each of the component Snapshots, while the rest of the components are taken from the Global Candidate List
+func (a *Adapter) prepareGroupSnapshot(prGroup, prGroupHash string) (*applicationapiv1alpha1.Snapshot, []gitops.ComponentSnapshotInfo, error) {
+ snapshotComponents := make([]applicationapiv1alpha1.SnapshotComponent, 0)
+ componentSnapshotInfos := make([]gitops.ComponentSnapshotInfo, 0)
+ ownerName := a.componentGroup.Name
+ ownerLabel := gitops.ComponentGroupNameLabel
+ namespace := a.componentGroup.Namespace
+ snapshotComponentsFromGCL, invalidComponents := snapshot.GetSnapshotComponentsFromGCL(a.componentGroup, a.logger.Logger)
+
+ componentsToCheck, err := a.loader.GetComponentsFromSnapshotForPRGroup(a.context, a.client, namespace, prGroupHash, ownerName, ownerLabel)
if err != nil {
return nil, nil, err
}
if len(componentsToCheck) < 2 {
a.logger.Info(fmt.Sprintf("The number %d of components affected by this PR group %s is less than 2, skipping group snapshot creation", len(componentsToCheck), prGroup))
- return nil, nil, err
+ return nil, nil, nil
}
- applicationComponents, err := a.loader.GetAllApplicationComponents(a.context, a.client, application)
- if err != nil {
- return nil, nil, err
- }
-
- snapshotComponents := make([]applicationapiv1alpha1.SnapshotComponent, 0)
- componentSnapshotInfos := make([]gitops.ComponentSnapshotInfo, 0)
- for _, applicationComponent := range *applicationComponents {
- applicationComponent := applicationComponent // G601
+ for _, groupComponent := range a.componentGroup.Spec.Components {
+ groupComponent := groupComponent // G601
- var foundSnapshotWithOpenedPR *applicationapiv1alpha1.Snapshot
- var statusCode int
componentInCheck := false
for _, c := range componentsToCheck {
- if c == applicationComponent.Name {
+ if c == groupComponent.Name {
componentInCheck = true
break
}
}
if componentInCheck {
- snapshots, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, application.Namespace, applicationComponent.Name, prGroupHash, application.Name)
+ snapshots, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, namespace, groupComponent.Name, prGroupHash, ownerName, ownerLabel)
if err != nil {
- a.logger.Error(err, "Failed to fetch Snapshots for component", "component.Name", applicationComponent.Name)
+ a.logger.Error(err, "Failed to fetch Snapshots for component", "component.Name", groupComponent.Name)
return nil, nil, err
}
- foundSnapshotWithOpenedPR, statusCode, err = a.status.FindSnapshotWithOpenedPR(a.context, snapshots, a.snapshot)
+ foundSnapshotWithOpenedPR, statusCode, err := a.status.FindSnapshotWithOpenedPR(a.context, snapshots, a.snapshot)
if err != nil {
a.logger.Error(err, "failed to find snapshot with open PR or MR", "statusCode", statusCode)
return nil, nil, err
}
if foundSnapshotWithOpenedPR != nil {
a.logger.Info("PR/MR in snapshot is opened, will find snapshotComponent and add to groupSnapshot")
- snapshotComponent := gitops.FindMatchingSnapshotComponent(foundSnapshotWithOpenedPR, &applicationComponent)
+ snapshotComponent := gitops.FindMatchingSnapshotComponent(foundSnapshotWithOpenedPR, groupComponent.Name)
componentSnapshotInfos = append(componentSnapshotInfos, gitops.ComponentSnapshotInfo{
- Component: applicationComponent.Name,
+ Component: groupComponent.Name,
BuildPipelineRun: foundSnapshotWithOpenedPR.Labels[gitops.BuildPipelineRunNameLabel],
Snapshot: foundSnapshotWithOpenedPR.Name,
Namespace: a.snapshot.Namespace,
RepoUrl: foundSnapshotWithOpenedPR.Annotations[gitops.PipelineAsCodeRepoUrlAnnotation],
PullRequestNumber: foundSnapshotWithOpenedPR.Annotations[gitops.PipelineAsCodePullRequestAnnotation],
+ Version: groupComponent.ComponentVersion.Name,
})
snapshotComponents = append(snapshotComponents, snapshotComponent)
continue
}
}
- a.logger.Info("can't find snapshot with open pull/merge request for component, try to find snapshotComponent from Global Candidate List", "component", applicationComponent.Name)
+ a.logger.Info("can't find snapshot with open pull/merge request for component, try to find snapshotComponent from Global Candidate List", "component", groupComponent.Name)
// if there is no component snapshot found for open PR/MR, we get snapshotComponent from gcl
- componentSource, err := gitops.GetComponentSourceFromComponent(&applicationComponent)
+ snapshotComponent, err := snapshot.FetchSnapshotComponentFromGCL(groupComponent.Name, snapshotComponentsFromGCL, invalidComponents)
if err != nil {
- a.logger.Error(err, "component cannot be added to snapshot for application due to missing git source", "component.Name", applicationComponent.Name)
+ a.logger.Error(err, "component cannot be added to snapshot", "component.Name", groupComponent.Name)
continue
}
- containerImage := applicationComponent.Status.LastPromotedImage
- if containerImage == "" {
- a.logger.Info("component cannot be added to snapshot for application due to missing containerImage", "component.Name", applicationComponent.Name)
- continue
+ if snapshotComponent != nil {
+ a.logger.Info("component with containerImage from Global Candidate List will be added to group snapshot", "component.Name", snapshotComponent.Name)
+ snapshotComponents = append(snapshotComponents, *snapshotComponent)
} else {
- // if the containerImage doesn't have a valid digest, the component
- // will not be added to snapshot
- err := gitops.ValidateImageDigest(containerImage)
+ a.logger.Info("component cannot be added to snapshot, no GCL entries found for it", "component.Name", groupComponent.Name)
+ }
+ }
+
+ // if the valid component snapshot from open MR/PR is less than 2, won't create group snapshot
+ if len(componentSnapshotInfos) < 2 {
+ return nil, componentSnapshotInfos, nil
+ }
+
+ groupSnapshot := snapshot.NewSnapshot(a.componentGroup, &snapshotComponents)
+ err = ctrl.SetControllerReference(a.componentGroup, groupSnapshot, a.client.Scheme())
+ if err != nil {
+ a.logger.Error(err, "failed to set owner reference to group snapshot")
+ return nil, nil, err
+ }
+
+ groupSnapshot, err = gitops.SetAnnotationAndLabelForGroupSnapshot(groupSnapshot, a.snapshot, componentSnapshotInfos)
+ if err != nil {
+ a.logger.Error(err, "failed to annotate group snapshot")
+ return nil, nil, err
+ }
+
+ return groupSnapshot, componentSnapshotInfos, nil
+}
+
+// prepareGroupSnapshotApplication prepares a Group Snapshot based on the existing component Snapshots which belong to the same PR group
+// It contains all of the updated components from each of the component Snapshots, while the rest of the components are taken from the Global Candidate List
+func (a *Adapter) prepareGroupSnapshotApplication(prGroup, prGroupHash string) (*applicationapiv1alpha1.Snapshot, []gitops.ComponentSnapshotInfo, error) {
+ snapshotComponents := make([]applicationapiv1alpha1.SnapshotComponent, 0)
+ componentSnapshotInfos := make([]gitops.ComponentSnapshotInfo, 0)
+ ownerName := a.application.Name
+ ownerLabel := gitops.ApplicationNameLabel
+ namespace := a.application.Namespace
+ components, err := a.loader.GetAllApplicationComponents(a.context, a.client, a.application)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ componentsToCheck, err := a.loader.GetComponentsFromSnapshotForPRGroup(a.context, a.client, namespace, prGroupHash, ownerName, ownerLabel)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(componentsToCheck) < 2 {
+ a.logger.Info(fmt.Sprintf("The number %d of components affected by this PR group %s is less than 2, skipping group snapshot creation", len(componentsToCheck), prGroup))
+ return nil, nil, nil
+ }
+ for _, applicationComponent := range *components {
+ applicationComponent := applicationComponent // G601
+
+ //nolint:govet // Allow parameter inference
+ if slices.Contains(componentsToCheck, applicationComponent.Name) {
+ snapshots, err := a.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(a.context, a.client, namespace, applicationComponent.Name, prGroupHash, ownerName, ownerLabel)
if err != nil {
- a.logger.Error(err, "component cannot be added to snapshot for application due to invalid digest in containerImage", "component.Name", applicationComponent.Name)
- continue
+ a.logger.Error(err, "Failed to fetch Snapshots for component", "component.Name", applicationComponent.Name)
+ return nil, nil, err
}
- snapshotComponent := applicationapiv1alpha1.SnapshotComponent{
- Name: applicationComponent.Name,
- ContainerImage: containerImage,
- Source: *componentSource,
+ foundSnapshotWithOpenedPR, statusCode, err := a.status.FindSnapshotWithOpenedPR(a.context, snapshots, a.snapshot)
+ if err != nil {
+ a.logger.Error(err, "failed to find snapshot with open PR or MR", "statusCode", statusCode)
+ return nil, nil, err
}
- a.logger.Info("component with containerImage from Global Candidate List will be added to group snapshot", "component.Name", snapshotComponent.Name)
- snapshotComponents = append(snapshotComponents, snapshotComponent)
+ if foundSnapshotWithOpenedPR != nil {
+ a.logger.Info("PR/MR in snapshot is opened, will find snapshotComponent and add to groupSnapshot")
+ snapshotComponent := gitops.FindMatchingSnapshotComponent(foundSnapshotWithOpenedPR, applicationComponent.Name)
+ componentSnapshotInfos = append(componentSnapshotInfos, gitops.ComponentSnapshotInfo{
+ Component: applicationComponent.Name,
+ BuildPipelineRun: foundSnapshotWithOpenedPR.Labels[gitops.BuildPipelineRunNameLabel],
+ Snapshot: foundSnapshotWithOpenedPR.Name,
+ Namespace: a.snapshot.Namespace,
+ RepoUrl: foundSnapshotWithOpenedPR.Annotations[gitops.PipelineAsCodeRepoUrlAnnotation],
+ PullRequestNumber: foundSnapshotWithOpenedPR.Annotations[gitops.PipelineAsCodePullRequestAnnotation],
+ })
+ snapshotComponents = append(snapshotComponents, snapshotComponent)
+ continue
+ }
+ }
+
+ a.logger.Info("can't find snapshot with open pull/merge request for component, try to find snapshotComponent from Global Candidate List", "component", applicationComponent.Name)
+ // if there is no component snapshot found for open PR/MR, we get snapshotComponent from gcl
+ snapshotComponent, err := a.fetchSnapshotComponentFromApplicationGCL(&applicationComponent)
+ if err != nil {
+ a.logger.Error(err, "component cannot be added to snapshot", "component.Name", applicationComponent.Name)
+ continue
}
+ a.logger.Info("component with containerImage from Global Candidate List will be added to group snapshot", "component.Name", snapshotComponent.Name)
+ snapshotComponents = append(snapshotComponents, *snapshotComponent)
}
// if the valid component snapshot from open MR/PR is less than 2, won't create group snapshot
@@ -1085,8 +1162,10 @@ func (a *Adapter) prepareGroupSnapshot(application *applicationapiv1alpha1.Appli
return nil, componentSnapshotInfos, nil
}
- groupSnapshot := gitops.NewSnapshot(application, &snapshotComponents)
- err = ctrl.SetControllerReference(application, groupSnapshot, a.client.Scheme())
+ var groupSnapshot *applicationapiv1alpha1.Snapshot
+
+ groupSnapshot = gitops.NewSnapshot(a.application, &snapshotComponents)
+ err = ctrl.SetControllerReference(a.application, groupSnapshot, a.client.Scheme())
if err != nil {
a.logger.Error(err, "failed to set owner reference to group snapshot")
return nil, nil, err
@@ -1101,13 +1180,52 @@ func (a *Adapter) prepareGroupSnapshot(application *applicationapiv1alpha1.Appli
return groupSnapshot, componentSnapshotInfos, nil
}
+// fetchSnapshotComponentFromApplicationGCL fetches a Snapshot component from the Application's GCL
+// TODO: Remove once the application model is fully deprecated [APPLICATION]
+func (a *Adapter) fetchSnapshotComponentFromApplicationGCL(component *applicationapiv1alpha1.Component) (*applicationapiv1alpha1.SnapshotComponent, error) {
+ var snapshotComponent applicationapiv1alpha1.SnapshotComponent
+ componentSource, err := gitops.GetComponentSourceFromComponent(component)
+ if err != nil {
+ return nil, fmt.Errorf("component cannot be added to snapshot for application due to missing git source")
+ }
+ containerImage := component.Status.LastPromotedImage
+
+ if containerImage == "" {
+ return nil, fmt.Errorf("component cannot be added to snapshot for application due to missing containerImage")
+ }
+
+ // if the containerImage doesn't have a valid digest, the component
+ // will not be added to snapshot
+ err = gitops.ValidateImageDigest(containerImage)
+ if err != nil {
+ return nil, fmt.Errorf("component cannot be added to snapshot for application due to invalid digest in containerImage")
+ }
+ snapshotComponent = applicationapiv1alpha1.SnapshotComponent{
+ Name: component.Name,
+ ContainerImage: containerImage,
+ Source: *componentSource,
+ }
+ return &snapshotComponent, nil
+}
+
// haveAllPipelineRunProcessedForPrGroup checks if all build plr has been processed for the given pr group
-// TODO: update in STONEINTG-1519
func (a *Adapter) haveAllPipelineRunProcessedForPrGroup(prGroup, prGroupHash string) (bool, error) {
- pipelineRuns, err := a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.snapshot.Namespace, prGroupHash, a.application.Name)
- if err != nil {
- a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
- return false, err
+ var pipelineRuns *[]tektonv1.PipelineRun
+ var err error
+ if a.application != nil {
+ pipelineRuns, err = a.loader.GetPipelineRunsWithPRGroupHashForApplication(a.context, a.client, a.snapshot.Namespace, prGroupHash, a.application.Name)
+ if err != nil {
+ a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
+ return false, err
+ }
+
+ } else {
+ pipelineRuns, err = a.loader.GetPipelineRunsWithPRGroupHash(a.context, a.client, a.snapshot.Namespace, prGroupHash)
+ if err != nil {
+ a.logger.Error(err, fmt.Sprintf("Failed to get build pipelineRuns for given pr group hash %s", prGroupHash))
+ return false, err
+ }
+ pipelineRuns = a.filterPipelineRunsForComponentGroup(pipelineRuns, a.componentGroup)
}
for _, pipelineRun := range *pipelineRuns {
@@ -1148,6 +1266,21 @@ func (a *Adapter) haveAllPipelineRunProcessedForPrGroup(prGroup, prGroupHash str
return true, nil
}
+// filterPipelineRunsForComponentGroup filters pipelineRuns to only ones that have components that belong to the componentGroup
+func (a *Adapter) filterPipelineRunsForComponentGroup(allPipelineRuns *[]tektonv1.PipelineRun, componentGroup *v1beta2.ComponentGroup) *[]tektonv1.PipelineRun {
+ var filteredPipelineRuns []tektonv1.PipelineRun
+ componentNames := h.GetComponentNamesFromComponentGroup(componentGroup)
+ for _, pipelineRun := range *allPipelineRuns {
+ if builtComponentName, found := pipelineRun.Labels[tektonconsts.PipelineRunComponentLabel]; found {
+ if slices.Contains(componentNames, builtComponentName) {
+ filteredPipelineRuns = append(filteredPipelineRuns, pipelineRun)
+ continue
+ }
+ }
+ }
+ return &filteredPipelineRuns
+}
+
// checkAndCancelOldSnapshotsPipelineRun sorts all snapshots for component group and cancels all running integrationTest pipelineruns within component group
// removes finalizer before the pipelinerun is set as CancelledRunFinally to be gracefully cancelled
func (a *Adapter) checkAndCancelOldSnapshotsPipelineRun() error {
@@ -1179,9 +1312,9 @@ func (a *Adapter) checkAndCancelOldSnapshotsPipelineRun() error {
}
// TODO: remove if statement when we deprecated old application model
if a.application != nil {
- snapshots, err = a.loader.GetMatchingGroupSnapshotsForPRGroupHash(a.context, a.client, a.application.Namespace, prGroupHash, a.application.Name)
+ snapshots, err = a.loader.GetMatchingGroupSnapshotsForPRGroupHash(a.context, a.client, a.application.Namespace, prGroupHash, a.application.Name, gitops.ApplicationNameLabel)
} else {
- snapshots, err = a.loader.GetMatchingGroupSnapshotsForPRGroupHash(a.context, a.client, a.componentGroup.Namespace, prGroupHash, a.componentGroup.Name)
+ snapshots, err = a.loader.GetMatchingGroupSnapshotsForPRGroupHash(a.context, a.client, a.componentGroup.Namespace, prGroupHash, a.componentGroup.Name, gitops.ComponentGroupNameLabel)
}
if err != nil {
a.logger.Error(fmt.Errorf("failed to get group snapshot for pr group from group snapshot"), "snapshot.Namespace", a.snapshot.Namespace, "snapshot.Name", a.snapshot.Name)
diff --git a/internal/controller/snapshot/snapshot_adapter_test.go b/internal/controller/snapshot/snapshot_adapter_test.go
index 4c5635c1ad..91b03ebb85 100644
--- a/internal/controller/snapshot/snapshot_adapter_test.go
+++ b/internal/controller/snapshot/snapshot_adapter_test.go
@@ -770,6 +770,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
gitops.PipelineAsCodeSHALabel: "sha",
gitops.PipelineAsCodePullRequestAnnotation: "1",
gitops.ApplicationNameLabel: hasApp.Name,
+ gitops.ComponentGroupNameLabel: hasCompGroup.Name,
},
Annotations: map[string]string{
"test.appstudio.openshift.io/pr-last-update": "2023-08-26T17:57:50+02:00",
@@ -781,7 +782,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
},
},
Spec: applicationapiv1alpha1.SnapshotSpec{
- Application: hasApp.Name,
+ ComponentGroup: hasCompGroup.Name,
Components: []applicationapiv1alpha1.SnapshotComponent{
{
Name: "component1",
@@ -817,7 +818,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
"pac.test.appstudio.openshift.io/url-repository": "testrepo",
gitops.PipelineAsCodeSHALabel: "sha",
gitops.PipelineAsCodePullRequestAnnotation: "1",
- gitops.ApplicationNameLabel: hasApp.Name,
+ gitops.ComponentGroupNameLabel: hasCompGroup.Name,
},
Annotations: map[string]string{
"test.appstudio.openshift.io/pr-last-update": "2023-08-26T17:57:50+02:00",
@@ -828,7 +829,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
},
},
Spec: applicationapiv1alpha1.SnapshotSpec{
- Application: hasApp.Name,
+ ComponentGroup: hasCompGroup.Name,
Components: []applicationapiv1alpha1.SnapshotComponent{
{
Name: hasCom1.Name,
@@ -855,7 +856,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
"pac.test.appstudio.openshift.io/url-repository": "testrepo",
gitops.PipelineAsCodeSHALabel: "sha",
gitops.PipelineAsCodePullRequestAnnotation: "1",
- gitops.ApplicationNameLabel: hasApp.Name,
+ gitops.ComponentGroupNameLabel: hasCompGroup.Name,
},
Annotations: map[string]string{
"test.appstudio.openshift.io/pr-last-update": "2023-08-26T17:57:50+02:00",
@@ -867,7 +868,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
},
},
Spec: applicationapiv1alpha1.SnapshotSpec{
- Application: hasApp.Name,
+ ComponentGroup: hasCompGroup.Name,
Components: []applicationapiv1alpha1.SnapshotComponent{
{
Name: hasCom3.Name,
@@ -890,7 +891,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
"pipelines.openshift.io/used-by": "build-cloud",
"pipelines.openshift.io/runtime": "nodejs",
"pipelines.openshift.io/strategy": "s2i",
- "appstudio.openshift.io/component": "component-sample",
+ "appstudio.openshift.io/component": "component1-sample",
"pipelinesascode.tekton.dev/event-type": "pull_request",
"build.appstudio.redhat.com/target_branch": "main",
"test.appstudio.openshift.io/pr-group-sha": prGroupSha,
@@ -3366,12 +3367,38 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
})
})
- // NOTE: update in group snapshot ticket
- When("Adapter is created for component snapshot with pr group", func() {
- It("ensures component snapshot will not be processed if it is not from pull/merge request", func() {
+ // TODO: Remove after the Application model is deprecated
+ When("Adapter is created for component snapshot with pr group [APPLICATION]", func() {
+ BeforeEach(func() {
+ hasComSnapshot1.Spec.Application = hasApp.Name
+ hasComSnapshot1.Spec.ComponentGroup = ""
+ hasComSnapshot1.Labels[gitops.ApplicationNameLabel] = hasApp.Name
+ hasComSnapshot2.Spec.Application = hasApp.Name
+ hasComSnapshot2.Spec.ComponentGroup = ""
+ hasComSnapshot2.Labels[gitops.ApplicationNameLabel] = hasApp.Name
+ hasComSnapshot3.Spec.Application = hasApp.Name
+ hasComSnapshot3.Spec.ComponentGroup = ""
+ hasComSnapshot3.Labels[gitops.ApplicationNameLabel] = hasApp.Name
+ })
+ It("Calling EnsureGroupSnapshotExist when there is running build PLR belonging to the same pr group sha", func() {
+ buildPipelineRun1.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "Running",
+ Status: "Unknown",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun1)).Should(Succeed())
+
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- hasComSnapshot1.Labels[gitops.PipelineAsCodeEventTypeLabel] = gitops.PipelineAsCodePushType
adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
@@ -3382,19 +3409,39 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
ContextKey: loader.SnapshotContextKey,
Resource: hasComSnapshot1,
},
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*buildPipelineRun1},
+ },
})
result, err := adapter.EnsureGroupSnapshotExist()
Expect(result.CancelRequest).To(BeFalse())
Expect(result.RequeueRequest).To(BeFalse())
- Expect(buf.String()).Should(ContainSubstring("The snapshot is not created by PAC pull request"))
+ Expect(buf.String()).Should(ContainSubstring("is still running, won't create group snapshot"))
Expect(err).ToNot(HaveOccurred())
+ Expect(metadata.HasAnnotation(hasComSnapshot1, gitops.PRGroupCreationAnnotation)).To(BeTrue())
})
- It("ensures component snapshot will not be processed if it has been processed", func() {
+ It("Calling EnsureGroupSnapshotExist when there is failed build PLR belonging to the same pr group sha", func() {
+ buildPipelineRun1.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "failed",
+ Status: "False",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun1)).Should(Succeed())
+
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- hasComSnapshot1.Annotations[gitops.PRGroupCreationAnnotation] = "processed"
adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
@@ -3405,6 +3452,323 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
ContextKey: loader.SnapshotContextKey,
Resource: hasComSnapshot1,
},
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*buildPipelineRun1},
+ },
+ })
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("failed, won't create group snapshot"))
+ Expect(err).ToNot(HaveOccurred())
+ Expect(metadata.HasAnnotation(hasComSnapshot1, gitops.PRGroupCreationAnnotation)).To(BeTrue())
+ })
+
+ It("Calling en when there is successful build PLR belonging to the same pr group sha but component snapshot is not created", func() {
+ buildPipelineRun1.Status = tektonv1.PipelineRunStatus{
+ PipelineRunStatusFields: tektonv1.PipelineRunStatusFields{
+ Results: []tektonv1.PipelineRunResult{},
+ },
+ Status: v1.Status{
+ Conditions: v1.Conditions{
+ apis.Condition{
+ Reason: "succeeded",
+ Status: "True",
+ Type: apis.ConditionSucceeded,
+ },
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, buildPipelineRun1)).Should(Succeed())
+
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ApplicationContextKey,
+ Resource: hasApp,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{*buildPipelineRun1},
+ },
+ })
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("has succeeded but component snapshot has not been created now"))
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("Stop processing when there is no annotationID in snapshot", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ApplicationContextKey,
+ Resource: hasApp,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{},
+ },
+ {
+ ContextKey: loader.GetPRSnapshotsKey,
+ Resource: []applicationapiv1alpha1.Snapshot{*hasComSnapshot1, *hasComSnapshot3},
+ },
+ {
+ ContextKey: loader.ApplicationComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasCom1, *hasCom3},
+ },
+ })
+
+ // create 3 snapshots here because we need to get snapshot twice so that we can't use the mocked snapshot
+ Expect(k8sClient.Create(adapter.context, hasComSnapshot2)).Should(Succeed())
+ Eventually(func() error {
+ err := k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot2.Name,
+ Namespace: "default",
+ }, hasComSnapshot2)
+ return err
+ }, time.Second*10).ShouldNot(HaveOccurred())
+ Expect(k8sClient.Create(adapter.context, hasComSnapshot3)).Should(Succeed())
+ Eventually(func() error {
+ err := k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot3.Name,
+ Namespace: "default",
+ }, hasComSnapshot3)
+ return err
+ }, time.Second*10).ShouldNot(HaveOccurred())
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("failed to get app credentials from Snapshot"))
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("Stop processing when there is only one component affected for pr group", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ApplicationContextKey,
+ Resource: hasApp,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{},
+ },
+ {
+ ContextKey: loader.ApplicationComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasCom1, *hasCom3},
+ },
+ })
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("The number 1 of components affected by this PR group feature1 is less than 2, skipping group snapshot creation"))
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("Ensure group snapshot can be created", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+
+ ctrl := gomock.NewController(GinkgoT())
+ mockStatus := status.NewMockStatusInterface(ctrl)
+ mockStatus.EXPECT().FindSnapshotWithOpenedPR(gomock.Any(), gomock.Any(), gomock.Any()).Return(hasComSnapshot2, 0, nil)
+ mockStatus.EXPECT().FindSnapshotWithOpenedPR(gomock.Any(), gomock.Any(), gomock.Any()).Return(hasComSnapshot3, 0, nil)
+ // mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), gomock.Any()).Return(true, nil)
+ mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), gomock.Any()).AnyTimes()
+
+ adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+
+ adapter.status = mockStatus
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ApplicationContextKey,
+ Resource: hasApp,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
+ {
+ ContextKey: loader.GetBuildPLRContextKey,
+ Resource: []tektonv1.PipelineRun{},
+ },
+ {
+ ContextKey: loader.ApplicationComponentsContextKey,
+ Resource: []applicationapiv1alpha1.Component{*hasComp, *hasCom1, *hasCom3, *hasCompMissingImageDigest, *hasCompWithValidImage},
+ },
+ })
+
+ // create 3 snapshots here because we need to get snapshot twice so that we can't use the mocked snapshot
+ Expect(k8sClient.Create(adapter.context, hasComSnapshot2)).Should(Succeed())
+ Eventually(func() error {
+ err := k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot2.Name,
+ Namespace: "default",
+ }, hasComSnapshot2)
+ return err
+ }, time.Second*10).ShouldNot(HaveOccurred())
+ Expect(k8sClient.Create(adapter.context, hasComSnapshot3)).Should(Succeed())
+ Eventually(func() error {
+ err := k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot3.Name,
+ Namespace: "default",
+ }, hasComSnapshot3)
+ return err
+ }, time.Second*10).ShouldNot(HaveOccurred())
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("component cannot be added to snapshot for application due to missing containerImage"))
+ Expect(err).NotTo(HaveOccurred())
+ Eventually(func() bool {
+ _ = k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot2.Name,
+ Namespace: "default",
+ }, hasComSnapshot2)
+ return metadata.HasAnnotation(hasComSnapshot2, gitops.PRGroupCreationAnnotation) &&
+ Expect(hasComSnapshot2.Annotations[gitops.PRGroupCreationAnnotation]).Should(ContainSubstring("is created for pr group"))
+ }, time.Second*10).Should(BeTrue())
+ Eventually(func() bool {
+ _ = k8sClient.Get(adapter.context, types.NamespacedName{
+ Name: hasComSnapshot3.Name,
+ Namespace: "default",
+ }, hasComSnapshot3)
+ return metadata.HasAnnotation(hasComSnapshot3, gitops.PRGroupCreationAnnotation) &&
+ Expect(hasComSnapshot3.Annotations[gitops.PRGroupCreationAnnotation]).Should(ContainSubstring("is created for pr group"))
+ }, time.Second*10).Should(BeTrue())
+ })
+ })
+
+ When("Adapter is created for component snapshot with pr group", func() {
+ BeforeAll(func() {
+ hasCompGroup.Spec.Components = []v1beta2.ComponentReference{
+ v1beta2.ComponentReference{
+ Name: hasComp.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ v1beta2.ComponentReference{
+ Name: hasCom1.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ v1beta2.ComponentReference{
+ Name: hasCom3.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ }
+ Expect(k8sClient.Update(ctx, hasCompGroup)).Should(Succeed())
+
+ hasCompGroup.Status = v1beta2.ComponentGroupStatus{
+ Conditions: []metav1.Condition{
+ metav1.Condition{
+ Type: "Succeeded",
+ Status: metav1.ConditionTrue,
+ Reason: "testing",
+ Message: "test condition",
+ LastTransitionTime: metav1.Time{Time: time.Now()},
+ },
+ },
+ GlobalCandidateList: []v1beta2.ComponentState{
+ v1beta2.ComponentState{
+ Name: hasComp.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: "",
+ LastPromotedCommit: "",
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ v1beta2.ComponentState{
+ Name: hasCom1.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: fmt.Sprintf("%s@%s", sample_image, sampleDigest),
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ v1beta2.ComponentState{
+ Name: hasCom3.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: fmt.Sprintf("%s@%s", sample_image, sampleDigest),
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ },
+ }
+ Expect(k8sClient.Status().Update(ctx, hasCompGroup)).Should(Succeed())
+ })
+
+ It("ensures component snapshot will not be processed if it is not from pull/merge request", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ hasComSnapshot1.Labels[gitops.PipelineAsCodeEventTypeLabel] = gitops.PipelineAsCodePushType
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupContextKey,
+ Resource: hasCompGroup,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
+ })
+
+ result, err := adapter.EnsureGroupSnapshotExist()
+ Expect(result.CancelRequest).To(BeFalse())
+ Expect(result.RequeueRequest).To(BeFalse())
+ Expect(buf.String()).Should(ContainSubstring("The snapshot is not created by PAC pull request"))
+ Expect(err).ToNot(HaveOccurred())
+ })
+
+ It("ensures component snapshot will not be processed if it has been processed", func() {
+ var buf bytes.Buffer
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+ hasComSnapshot1.Annotations[gitops.PRGroupCreationAnnotation] = "processed"
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
+ },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasComSnapshot1,
+ },
})
result, err := adapter.EnsureGroupSnapshotExist()
@@ -3419,11 +3783,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
Expect(metadata.DeleteLabel(hasComSnapshot1, gitops.PRGroupHashLabel)).ShouldNot(HaveOccurred())
Expect(metadata.HasLabel(hasComSnapshot1, gitops.PRGroupHashLabel)).To(BeFalse())
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3458,11 +3822,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3501,11 +3865,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3544,11 +3908,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3570,11 +3934,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
It("Stop processing when there is no annotationID in snapshot", func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3589,12 +3953,12 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
Resource: []applicationapiv1alpha1.Snapshot{*hasComSnapshot1, *hasComSnapshot3},
},
{
- ContextKey: loader.ApplicationComponentsContextKey,
+ ContextKey: loader.ComponentGroupComponentsContextKey,
Resource: []applicationapiv1alpha1.Component{*hasCom1, *hasCom3},
},
})
- // create 3 snasphots here because we need to get snapshot twice so that we can't use the mocked snapshot
+ // create 3 snapshots here because we need to get snapshot twice so that we can't use the mocked snapshot
Expect(k8sClient.Create(adapter.context, hasComSnapshot2)).Should(Succeed())
Eventually(func() error {
err := k8sClient.Get(adapter.context, types.NamespacedName{
@@ -3622,11 +3986,11 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
It("Stop processing when there is only one component affected for pr group", func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ adapter = NewAdapter(ctx, hasComSnapshot1, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3637,7 +4001,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
Resource: []tektonv1.PipelineRun{},
},
{
- ContextKey: loader.ApplicationComponentsContextKey,
+ ContextKey: loader.ComponentGroupComponentsContextKey,
Resource: []applicationapiv1alpha1.Component{*hasCom1, *hasCom3},
},
})
@@ -3649,7 +4013,7 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
Expect(err).ToNot(HaveOccurred())
})
- It("Ensure group snasphot can be created", func() {
+ It("Ensure group snapshot can be created", func() {
var buf bytes.Buffer
log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
@@ -3660,13 +4024,106 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
// mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), gomock.Any()).Return(true, nil)
mockStatus.EXPECT().IsPRMRInSnapshotOpened(gomock.Any(), gomock.Any()).AnyTimes()
- adapter = NewAdapterWithApplication(ctx, hasComSnapshot1, hasApp, log, loader.NewMockLoader(), k8sClient)
+ tmpHasCompGroup := hasCompGroup.DeepCopy()
+ tmpHasCompGroup.Spec = v1beta2.ComponentGroupSpec{
+ Components: []v1beta2.ComponentReference{
+ {
+ Name: hasComp.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ {
+ Name: hasCom1.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ {
+ Name: hasCom3.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ {
+ Name: hasCompMissingImageDigest.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ {
+ Name: hasCompWithValidImage.Name,
+ ComponentVersion: v1beta2.ComponentVersionReference{
+ Name: "v1",
+ Revision: "main",
+ },
+ },
+ },
+ }
+ tmpHasCompGroup.Status = v1beta2.ComponentGroupStatus{
+ Conditions: []metav1.Condition{
+ metav1.Condition{
+ Type: "Succeeded",
+ Status: metav1.ConditionTrue,
+ Reason: "testing",
+ Message: "test condition",
+ LastTransitionTime: metav1.Time{Time: time.Now()},
+ },
+ },
+ GlobalCandidateList: []v1beta2.ComponentState{
+ {
+ Name: hasComp.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: sample_image,
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ {
+ Name: hasCom1.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: sample_image,
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ {
+ Name: hasCom3.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: sample_image,
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ {
+ Name: hasCompMissingImageDigest.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ {
+ Name: hasCompWithValidImage.Name,
+ Version: "v1",
+ URL: SampleRepoLink,
+ LastPromotedImage: sample_image,
+ LastPromotedCommit: sample_commit,
+ LastPromotedBuildTime: &metav1.Time{Time: time.Now()},
+ },
+ },
+ }
+
+ adapter = NewAdapter(ctx, hasComSnapshot1, tmpHasCompGroup, log, loader.NewMockLoader(), k8sClient)
adapter.status = mockStatus
adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
{
- ContextKey: loader.ApplicationContextKey,
- Resource: hasApp,
+ ContextKey: loader.ComponentGroupsContextKey,
+ Resource: hasCompGroup,
},
{
ContextKey: loader.SnapshotContextKey,
@@ -3677,12 +4134,12 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
Resource: []tektonv1.PipelineRun{},
},
{
- ContextKey: loader.ApplicationComponentsContextKey,
+ ContextKey: loader.ComponentGroupComponentsContextKey,
Resource: []applicationapiv1alpha1.Component{*hasComp, *hasCom1, *hasCom3, *hasCompMissingImageDigest, *hasCompWithValidImage},
},
})
- // create 3 snasphots here because we need to get snapshot twice so that we can't use the mocked snapshot
+ // create 3 snapshots here because we need to get snapshot twice so that we can't use the mocked snapshot
Expect(k8sClient.Create(adapter.context, hasComSnapshot2)).Should(Succeed())
Eventually(func() error {
err := k8sClient.Get(adapter.context, types.NamespacedName{
diff --git a/internal/controller/statusreport/statusreport_adapter.go b/internal/controller/statusreport/statusreport_adapter.go
index 2add5c27d2..301d2c67a7 100644
--- a/internal/controller/statusreport/statusreport_adapter.go
+++ b/internal/controller/statusreport/statusreport_adapter.go
@@ -24,6 +24,7 @@ import (
"time"
applicationapiv1alpha1 "github.com/konflux-ci/application-api/api/v1alpha1"
+ "github.com/konflux-ci/integration-service/snapshot"
"github.com/konflux-ci/operator-toolkit/controller"
"k8s.io/client-go/util/retry"
"sigs.k8s.io/controller-runtime/pkg/client"
@@ -231,43 +232,60 @@ func (a *Adapter) EnsureGroupSnapshotCreationStatusReportedToGitProvider() (cont
return controller.ContinueProcessing()
}
- // TODO: remove when old application-specific code is removed (see STONEINTG-1519 for ComponentGroup path)
- if a.application != nil {
- integrationTestStatus := intgteststat.GroupSnapshotCreationFailed
+ var allIntegrationTestScenarios *[]v1beta2.IntegrationTestScenario
+ var tempGroupSnapshot *applicationapiv1alpha1.Snapshot
+ var resourceBlurb string
+ var err error
+ integrationTestStatus := intgteststat.GroupSnapshotCreationFailed
+ var groupName string
- allIntegrationTestScenarios, err := a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application)
+ // TODO: remove when old application-specific code is removed
+ if a.application != nil {
+ resourceBlurb = fmt.Sprintf("application %s in namespace %s", a.application.Name, a.application.Namespace)
+ groupName = gitops.ComponentNameForGroupSnapshot
+ tempGroupSnapshot = gitops.PrepareTempGroupSnapshot(a.application, a.snapshot)
+ allIntegrationTestScenarios, err = a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application)
if err != nil {
- a.logger.Error(err, "Failed to get integration test scenarios for the following application",
- "Application.Namespace", a.application.Namespace, "Application.Name", a.application.Name)
+ a.logger.Error(err, fmt.Sprintf("Failed to get integration test scenarios for %s", resourceBlurb))
+ return controller.RequeueWithError(err)
+ }
+ } else {
+ resourceBlurb = fmt.Sprintf("component group %s in namespace %s", a.componentGroup.Name, a.componentGroup.Namespace)
+ groupName = fmt.Sprintf("%s %s", gitops.ComponentNameForGroupSnapshot, a.componentGroup.Name)
+ tempGroupSnapshot = snapshot.PrepareTempGroupSnapshot(a.componentGroup, a.snapshot)
+ allIntegrationTestScenarios, err = a.loader.GetAllIntegrationTestScenariosForComponentGroup(a.context, a.client, a.componentGroup)
+ if err != nil {
+ a.logger.Error(err, fmt.Sprintf("Failed to get integration test scenarios for %s", resourceBlurb))
return controller.RequeueWithError(err)
}
- if allIntegrationTestScenarios != nil {
- tempGroupSnapshot := gitops.PrepareTempGroupSnapshot(a.application, a.snapshot)
- filterIntegrationTestScenarios := gitops.FilterIntegrationTestScenariosWithContext(allIntegrationTestScenarios, tempGroupSnapshot)
-
- a.logger.Info(
- fmt.Sprintf("Found %d IntegrationTestScenarios for application", len(*filterIntegrationTestScenarios)),
- "Application.Name", a.application.Name,
- "IntegrationTestScenarios", len(*filterIntegrationTestScenarios))
- if len(*filterIntegrationTestScenarios) > 0 {
- isErrorRecoverable, err := a.ReportGroupSnapshotCreationStatus(a.snapshot, filterIntegrationTestScenarios, integrationTestStatus, gitops.ComponentNameForGroupSnapshot)
-
- if err != nil {
- a.logger.Error(err, "failed to report group snapshot createion status to git provider from component snapshot",
- "snapshot.Namespace", a.snapshot.Namespace, "snapshot.Name", a.snapshot.Name, "isErrorRecoverable", isErrorRecoverable)
- if helpers.IsObjectYoungerThanThreshold(a.snapshot, SnapshotRetryTimeout) && isErrorRecoverable {
- return controller.RequeueWithError(err)
- } else {
- return controller.ContinueProcessing()
- }
- }
- if err = gitops.AnnotateSnapshot(a.context, a.snapshot, gitops.PRGroupCreationAnnotation, gitops.GroupSnapshotCreationFailureReported, a.client); err != nil {
- a.logger.Error(err, fmt.Sprintf("failed to write group snapshot creation status to annotation %s", gitops.PRGroupCreationAnnotation))
- return controller.RequeueWithError(fmt.Errorf("failed to write group snapshot creation status to annotation %s: %w", gitops.PRGroupCreationAnnotation, err))
- }
+ }
+
+ if allIntegrationTestScenarios == nil {
+ return controller.ContinueProcessing()
+ }
+
+ filterIntegrationTestScenarios := gitops.FilterIntegrationTestScenariosWithContext(allIntegrationTestScenarios, tempGroupSnapshot)
+
+ a.logger.Info(
+ fmt.Sprintf("Found %d IntegrationTestScenarios for %s", len(*filterIntegrationTestScenarios), resourceBlurb),
+ "IntegrationTestScenarios", len(*filterIntegrationTestScenarios))
+ if len(*filterIntegrationTestScenarios) > 0 {
+ isErrorRecoverable, err := a.ReportGroupSnapshotCreationStatus(a.snapshot, filterIntegrationTestScenarios, integrationTestStatus, groupName)
+
+ if err != nil {
+ a.logger.Error(err, "failed to report group snapshot creation status to git provider from component snapshot",
+ "snapshot.Namespace", a.snapshot.Namespace, "snapshot.Name", a.snapshot.Name, "isErrorRecoverable", isErrorRecoverable)
+ if helpers.IsObjectYoungerThanThreshold(a.snapshot, SnapshotRetryTimeout) && isErrorRecoverable {
+ return controller.RequeueWithError(err)
+ } else {
+ return controller.ContinueProcessing()
}
}
+ if err = gitops.AnnotateSnapshot(a.context, a.snapshot, gitops.PRGroupCreationAnnotation, gitops.GroupSnapshotCreationFailureReported, a.client); err != nil {
+ a.logger.Error(err, fmt.Sprintf("failed to write group snapshot creation status to annotation %s", gitops.PRGroupCreationAnnotation))
+ return controller.RequeueWithError(fmt.Errorf("failed to write group snapshot creation status to annotation %s: %w", gitops.PRGroupCreationAnnotation, err))
+ }
}
return controller.ContinueProcessing()
}
@@ -476,7 +494,11 @@ func (a *Adapter) iterateIntegrationTestStatusDetailsInStatusReport(reporter sta
// set componentName to component name of component snapshot or pr group name of group snapshot when reporting status to git provider
componentNameOrPrGroup := ""
if gitops.IsGroupSnapshot(testedSnapshot) {
- componentNameOrPrGroup = gitops.ComponentNameForGroupSnapshot
+ if a.application != nil {
+ componentNameOrPrGroup = gitops.ComponentNameForGroupSnapshot
+ } else {
+ componentNameOrPrGroup = fmt.Sprintf("%s %s", gitops.ComponentNameForGroupSnapshot, a.componentGroup.Name)
+ }
} else if gitops.IsComponentSnapshot(testedSnapshot) {
componentNameOrPrGroup = testedSnapshot.Labels[gitops.SnapshotComponentLabel]
} else {
diff --git a/internal/controller/statusreport/statusreport_adapter_test.go b/internal/controller/statusreport/statusreport_adapter_test.go
index 387d6f5e2b..ecc69cc2d3 100644
--- a/internal/controller/statusreport/statusreport_adapter_test.go
+++ b/internal/controller/statusreport/statusreport_adapter_test.go
@@ -1134,11 +1134,21 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
})
When("EnsureGroupSnapshotCreationStatusReportedToGitProvider is called with ComponentGroup adapter", func() {
- var cgSnapshot *applicationapiv1alpha1.Snapshot
var hasCompGroup *v1beta2.ComponentGroup
BeforeEach(func() {
buf = bytes.Buffer{}
+ log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
+
+ ctrl := gomock.NewController(GinkgoT())
+ mockReporter = status.NewMockReporterInterface(ctrl)
+ mockStatus := status.NewMockStatusInterface(ctrl)
+ mockReporter.EXPECT().GetReporterName().Return("mocked-reporter").AnyTimes()
+ mockStatus.EXPECT().GetReporter(gomock.Any()).Return(mockReporter)
+ mockStatus.EXPECT().GetReporter(gomock.Any()).AnyTimes()
+ mockReporter.EXPECT().GetReporterName().AnyTimes()
+ mockReporter.EXPECT().Initialize(gomock.Any(), gomock.Any()).Times(1)
+ mockReporter.EXPECT().ReportStatus(gomock.Any(), gomock.Any()).Times(1)
hasCompGroup = &v1beta2.ComponentGroup{
ObjectMeta: metav1.ObjectMeta{
@@ -1147,38 +1157,45 @@ var _ = Describe("Snapshot Adapter", Ordered, func() {
},
}
- cgSnapshot = &applicationapiv1alpha1.Snapshot{
- ObjectMeta: metav1.ObjectMeta{
- Name: "cg-snapshot-noop",
- Namespace: "default",
- Labels: map[string]string{
- gitops.SnapshotTypeLabel: gitops.SnapshotComponentType,
- gitops.SnapshotComponentLabel: "component-sample",
- },
- Annotations: map[string]string{
- gitops.PRGroupCreationAnnotation: gitops.FailedToCreateGroupSnapshotMsg + " some error",
- },
+ hasPRSnapshot.Spec.Application = ""
+ hasPRSnapshot.Spec.ComponentGroup = hasCompGroup.Name
+ hasPRSnapshot.Labels[gitops.ComponentGroupNameLabel] = hasCompGroup.Name
+ Expect(k8sClient.Update(ctx, hasPRSnapshot)).Should(Succeed())
+
+ adapter = NewAdapter(ctx, hasPRSnapshot, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
+ adapter.context = toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: loader.ComponentGroupContextKey,
+ Resource: hasCompGroup,
},
- Spec: applicationapiv1alpha1.SnapshotSpec{
- ComponentGroup: hasCompGroup.Name,
- Components: []applicationapiv1alpha1.SnapshotComponent{
- {
- Name: "component-sample",
- ContainerImage: SampleImage,
- },
- },
+ {
+ ContextKey: loader.SnapshotContextKey,
+ Resource: hasPRSnapshot,
},
- }
-
- log := helpers.IntegrationLogger{Logger: buflogr.NewWithBuffer(&buf)}
- adapter = NewAdapter(ctx, cgSnapshot, hasCompGroup, log, loader.NewMockLoader(), k8sClient)
+ {
+ ContextKey: loader.RequiredIntegrationTestScenariosForSnapshotContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ {
+ ContextKey: loader.AllIntegrationTestScenariosForComponentGroupsContextKey,
+ Resource: []v1beta2.IntegrationTestScenario{*integrationTestScenario},
+ },
+ })
+ adapter.status = mockStatus
+ Expect(reflect.TypeOf(adapter)).To(Equal(reflect.TypeOf(&Adapter{})))
})
- It("should be a no-op for ComponentGroup path (deferred to STONEINTG-1519)", func() {
+ It("ensure group snapshot create failure is reported to git provider", func() {
result, err := adapter.EnsureGroupSnapshotCreationStatusReportedToGitProvider()
Expect(result.CancelRequest).To(BeFalse())
Expect(result.RequeueRequest).To(BeFalse())
- Expect(err).ToNot(HaveOccurred())
+ Expect(buf.String()).Should(ContainSubstring("Successfully report group snapshot creation failure"))
+ Expect(buf.String()).Should(ContainSubstring("Successfully updated the test.appstudio.openshift.io/create-groupsnapshot-status"))
+ Expect(err).Should(Succeed())
})
})
})
diff --git a/loader/loader.go b/loader/loader.go
index fd786e983c..839ff4d726 100644
--- a/loader/loader.go
+++ b/loader/loader.go
@@ -43,6 +43,7 @@ import (
type ObjectLoader interface {
GetReleasesWithSnapshot(ctx context.Context, c client.Client, snapshot *applicationapiv1alpha1.Snapshot) (*[]releasev1alpha1.Release, error)
GetAllApplicationComponents(ctx context.Context, c client.Client, application *applicationapiv1alpha1.Application) (*[]applicationapiv1alpha1.Component, error)
+ GetAllComponentGroupComponents(ctx context.Context, c client.Client, componentGroup *v1beta2.ComponentGroup) (*[]applicationapiv1alpha1.Component, error)
GetApplicationFromSnapshot(ctx context.Context, c client.Client, snapshot *applicationapiv1alpha1.Snapshot) (*applicationapiv1alpha1.Application, error)
GetComponentGroupFromSnapshot(ctx context.Context, c client.Client, snapshot *applicationapiv1alpha1.Snapshot) (*v1beta2.ComponentGroup, error)
GetComponentFromSnapshot(ctx context.Context, c client.Client, snapshot *applicationapiv1alpha1.Snapshot) (*applicationapiv1alpha1.Component, error)
@@ -71,12 +72,13 @@ type ObjectLoader interface {
GetAllTaskRunsWithMatchingPipelineRunLabel(ctx context.Context, c client.Client, pipelineRun *tektonv1.PipelineRun) (*[]tektonv1.TaskRun, error)
GetPipelineRun(ctx context.Context, c client.Client, name, namespace string) (*tektonv1.PipelineRun, error)
GetComponent(ctx context.Context, c client.Client, name, namespace string) (*applicationapiv1alpha1.Component, error)
- GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error)
- GetPipelineRunsWithPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error)
- GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, snapshot, componentName, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error)
+ GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, ownerName string, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error)
+ GetPipelineRunsWithPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash string) (*[]tektonv1.PipelineRun, error)
+ GetPipelineRunsWithPRGroupHashForApplication(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error)
+ GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, snapshot, componentName, prGroupHash, ownerName string, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error)
GetAllIntegrationPipelineRunsForSnapshot(ctx context.Context, adapterClient client.Client, snapshot *applicationapiv1alpha1.Snapshot) ([]tektonv1.PipelineRun, error)
- GetComponentsFromSnapshotForPRGroup(ctx context.Context, c client.Client, namespace, prGroup, prGroupHash, applicationName string) ([]string, error)
- GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error)
+ GetComponentsFromSnapshotForPRGroup(ctx context.Context, c client.Client, namespace, prGroupHash, ownerName string, ownerLabel string) ([]string, error)
+ GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, ownerName string, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error)
GetResolutionRequest(ctx context.Context, c client.Client, namespace, name string) (resolutionv1beta1.ResolutionRequest, error)
GetPRComponentSnapshotsForComponentApplication(ctx context.Context, c client.Client, namespace, applicationName, componentName, prNumber string) (*[]applicationapiv1alpha1.Snapshot, error)
GetPRComponentSnapshotsForComponent(ctx context.Context, c client.Client, componentGroupNames []string, namespace, componentName, prNumber string) (*[]applicationapiv1alpha1.Snapshot, error)
@@ -123,6 +125,26 @@ func (l *loader) GetAllApplicationComponents(ctx context.Context, c client.Clien
return &applicationComponents.Items, nil
}
+func (l *loader) GetAllComponentGroupComponents(ctx context.Context, c client.Client, componentGroup *v1beta2.ComponentGroup) (*[]applicationapiv1alpha1.Component, error) {
+ componentGroupComponents := []applicationapiv1alpha1.Component{}
+
+ for _, groupComponent := range componentGroup.Spec.Components {
+ component := &applicationapiv1alpha1.Component{}
+ err := c.Get(ctx, types.NamespacedName{
+ Namespace: componentGroup.Namespace,
+ Name: groupComponent.Name,
+ }, component)
+
+ if err != nil {
+ return nil, err
+ }
+
+ componentGroupComponents = append(componentGroupComponents, *component)
+ }
+
+ return &componentGroupComponents, nil
+}
+
// GetApplicationFromSnapshot loads from the cluster the Application referenced in the given Snapshot.
// If the Snapshot doesn't specify an Component or this is not found in the cluster, an error will be returned.
// TODO: delete when we get rid of application model
@@ -608,7 +630,6 @@ func (l *loader) GetAllSnapshotsForPR(ctx context.Context, c client.Client, obje
// PipelineAsCodePullRequestAnnotation is also a label
// Only snapshots with IntegrationWorkflowAnnotation set to "pull-request" are returned,
// so on-push snapshots that carry the same PR label are excluded.
-// TODO: make this function take ObjectMeta rather than application
func (l *loader) GetAllPullSnapshotsForPR(ctx context.Context, c client.Client, object metav1.ObjectMeta, componentName, pullRequest string) (*[]applicationapiv1alpha1.Snapshot, error) {
snapshots := &applicationapiv1alpha1.SnapshotList{}
opts := []client.ListOption{
@@ -665,7 +686,36 @@ func (l *loader) GetComponent(ctx context.Context, c client.Client, name, namesp
}
// GetPipelineRunsWithPRGroupHash gets the build pipelineRun with the given pr group hash string and the same namespace with the given snapshot
-func (l *loader) GetPipelineRunsWithPRGroupHash(ctx context.Context, adapterClient client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error) {
+func (l *loader) GetPipelineRunsWithPRGroupHash(ctx context.Context, adapterClient client.Client, namespace, prGroupHash string) (*[]tektonv1.PipelineRun, error) {
+ buildPipelineRuns := &tektonv1.PipelineRunList{}
+
+ prGroupLabelRequirement, err := labels.NewRequirement("test.appstudio.openshift.io/pr-group-sha", selection.In, []string{prGroupHash})
+ if err != nil {
+ return nil, err
+ }
+ plrTypeLabelRequirement, err := labels.NewRequirement("pipelines.appstudio.openshift.io/type", selection.In, []string{"build"})
+ if err != nil {
+ return nil, err
+ }
+
+ labelSelector := labels.NewSelector().
+ Add(*prGroupLabelRequirement).
+ Add(*plrTypeLabelRequirement)
+
+ opts := &client.ListOptions{
+ Namespace: namespace,
+ LabelSelector: labelSelector,
+ }
+
+ err = adapterClient.List(ctx, buildPipelineRuns, opts)
+ if err != nil {
+ return nil, err
+ }
+ return &buildPipelineRuns.Items, nil
+}
+
+// GetPipelineRunsWithPRGroupHashForApplication gets the build pipelineRun with the given pr group hash string, application name and the same namespace with the given snapshot
+func (l *loader) GetPipelineRunsWithPRGroupHashForApplication(ctx context.Context, adapterClient client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error) {
buildPipelineRuns := &tektonv1.PipelineRunList{}
applicationLabelRequirement, err := labels.NewRequirement("appstudio.openshift.io/application", selection.In, []string{applicationName})
@@ -699,10 +749,10 @@ func (l *loader) GetPipelineRunsWithPRGroupHash(ctx context.Context, adapterClie
}
// GetMatchingComponentSnapshotsForComponentAndPRGroupHash gets the component snapshot with the given pr group hash string and the the same namespace with the given snapshot
-func (l *loader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, namespace, componentName, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+func (l *loader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, namespace, componentName, prGroupHash, ownerName, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
snapshots := &applicationapiv1alpha1.SnapshotList{}
- applicationLabelRequirement, err := labels.NewRequirement("appstudio.openshift.io/application", selection.In, []string{applicationName})
+ ownerLabelRequirement, err := labels.NewRequirement(ownerLabel, selection.In, []string{ownerName})
if err != nil {
return nil, err
}
@@ -720,7 +770,7 @@ func (l *loader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx con
}
labelSelector := labels.NewSelector().
- Add(*applicationLabelRequirement).
+ Add(*ownerLabelRequirement).
Add(*componentLabelRequirement).
Add(*prGroupLabelRequirement).
Add(*snapshotTypeLabelRequirement)
@@ -737,14 +787,15 @@ func (l *loader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx con
return &snapshots.Items, nil
}
-// GetMatchingGroupSnapshotsForPRGroupHash gets the group snapshots with the given pr group hash string and the the same namespace
-func (l *loader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+// GetMatchingGroupSnapshotsForPRGroupHash gets the group snapshots with the given pr group hash string and the same namespace
+func (l *loader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, ownerName string, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
snapshots := &applicationapiv1alpha1.SnapshotList{}
- applicationLabelRequirement, err := labels.NewRequirement("appstudio.openshift.io/application", selection.In, []string{applicationName})
+ ownerLabelRequirement, err := labels.NewRequirement(ownerLabel, selection.In, []string{ownerName})
if err != nil {
return nil, err
}
+
prGroupLabelRequirement, err := labels.NewRequirement("test.appstudio.openshift.io/pr-group-sha", selection.In, []string{prGroupHash})
if err != nil {
return nil, err
@@ -755,7 +806,7 @@ func (l *loader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c
}
labelSelector := labels.NewSelector().
- Add(*applicationLabelRequirement).
+ Add(*ownerLabelRequirement).
Add(*prGroupLabelRequirement).
Add(*snapshotTypeLabelRequirement)
@@ -771,11 +822,11 @@ func (l *loader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c
return &snapshots.Items, nil
}
-// GetMatchingComponentSnapshotsForPRGroupHash gets the component snapshot with the given pr group hash string and the the same namespace with the given snapshot
-func (l *loader) GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+// GetMatchingComponentSnapshotsForPRGroupHash gets the component snapshot with the given pr group hash string and the same namespace with the given snapshot
+func (l *loader) GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, ownerName string, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
snapshots := &applicationapiv1alpha1.SnapshotList{}
- applicationLabelRequirement, err := labels.NewRequirement("appstudio.openshift.io/application", selection.In, []string{applicationName})
+ ownerLabelRequirement, err := labels.NewRequirement(ownerLabel, selection.In, []string{ownerName})
if err != nil {
return nil, err
}
@@ -789,7 +840,7 @@ func (l *loader) GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context
}
labelSelector := labels.NewSelector().
- Add(*applicationLabelRequirement).
+ Add(*ownerLabelRequirement).
Add(*prGroupLabelRequirement).
Add(*snapshotTypeLabelRequirement)
@@ -824,8 +875,8 @@ func (l *loader) GetAllIntegrationPipelineRunsForSnapshot(ctx context.Context, a
}
// GetComponentsFromSnapshotForPRGroup returns the component names affected by the given pr group hash
-func (l *loader) GetComponentsFromSnapshotForPRGroup(ctx context.Context, client client.Client, namespace, prGroup, prGroupHash, applicationName string) ([]string, error) {
- snapshots, err := l.GetMatchingComponentSnapshotsForPRGroupHash(ctx, client, namespace, prGroupHash, applicationName)
+func (l *loader) GetComponentsFromSnapshotForPRGroup(ctx context.Context, client client.Client, namespace, prGroupHash, ownerName string, ownerLabel string) ([]string, error) {
+ snapshots, err := l.GetMatchingComponentSnapshotsForPRGroupHash(ctx, client, namespace, prGroupHash, ownerName, ownerLabel)
if err != nil {
return nil, err
}
diff --git a/loader/loader_mock.go b/loader/loader_mock.go
index 7414d87a96..77aadf45bc 100644
--- a/loader/loader_mock.go
+++ b/loader/loader_mock.go
@@ -74,6 +74,7 @@ const (
ComponentGroupsContextKey
RequiredIntegrationTestScenariosForSnapshotContextKey
GetPushComponentSnapshotsForComponentContextKey
+ ComponentGroupComponentsContextKey
)
func NewMockLoader() ObjectLoader {
@@ -100,6 +101,14 @@ func (l *mockLoader) GetAllApplicationComponents(ctx context.Context, c client.C
return &components, err
}
+func (l *mockLoader) GetAllComponentGroupComponents(ctx context.Context, c client.Client, componentGroup *v1beta2.ComponentGroup) (*[]applicationapiv1alpha1.Component, error) {
+ if ctx.Value(ComponentGroupComponentsContextKey) == nil {
+ return l.loader.GetAllComponentGroupComponents(ctx, c, componentGroup)
+ }
+ components, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, ComponentGroupComponentsContextKey, []applicationapiv1alpha1.Component{})
+ return &components, err
+}
+
// GetApplicationFromSnapshot returns the resource and error passed as values of the context.
func (l *mockLoader) GetApplicationFromSnapshot(ctx context.Context, c client.Client, snapshot *applicationapiv1alpha1.Snapshot) (*applicationapiv1alpha1.Application, error) {
if ctx.Value(ApplicationContextKey) == nil {
@@ -322,35 +331,44 @@ func (l *mockLoader) GetComponent(ctx context.Context, c client.Client, name, na
}
// GetPipelineRunsWithPRGroupHash returns the resource and error passed as values of the context.
-func (l *mockLoader) GetPipelineRunsWithPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error) {
+func (l *mockLoader) GetPipelineRunsWithPRGroupHash(ctx context.Context, c client.Client, namespace, prGroupHash string) (*[]tektonv1.PipelineRun, error) {
+ if ctx.Value(GetBuildPLRContextKey) == nil {
+ return l.loader.GetPipelineRunsWithPRGroupHash(ctx, c, namespace, prGroupHash)
+ }
+ pipelineRuns, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetBuildPLRContextKey, []tektonv1.PipelineRun{})
+ return &pipelineRuns, err
+}
+
+// GetPipelineRunsWithPRGroupHashForApplication returns the resource and error passed as values of the context.
+func (l *mockLoader) GetPipelineRunsWithPRGroupHashForApplication(ctx context.Context, c client.Client, namespace, prGroupHash, applicationName string) (*[]tektonv1.PipelineRun, error) {
if ctx.Value(GetBuildPLRContextKey) == nil {
- return l.loader.GetPipelineRunsWithPRGroupHash(ctx, c, namespace, prGroupHash, applicationName)
+ return l.loader.GetPipelineRunsWithPRGroupHashForApplication(ctx, c, namespace, prGroupHash, applicationName)
}
pipelineRuns, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetBuildPLRContextKey, []tektonv1.PipelineRun{})
return &pipelineRuns, err
}
// GetMatchingComponentSnapshotsForComponentAndPRGroupHash returns the resource and error passed as values of the context
-func (l *mockLoader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, namespace, componentName, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+func (l *mockLoader) GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx context.Context, c client.Client, namespace, componentName, prGroupHash, ownerName, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
if ctx.Value(GetComponentSnapshotsKey) == nil {
- return l.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, c, namespace, componentName, prGroupHash, applicationName)
+ return l.loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, c, namespace, componentName, prGroupHash, ownerName, ownerLabel)
}
snapshots, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetComponentSnapshotsKey, []applicationapiv1alpha1.Snapshot{})
return &snapshots, err
}
// GetMatchingComponentSnapshotsForPRGroupHash returns the resource and error passed as values of the context
-func (l *mockLoader) GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+func (l *mockLoader) GetMatchingComponentSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, ownerName, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
if ctx.Value(GetPRSnapshotsKey) == nil {
- return l.loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, c, nameSpace, prGroupHash, applicationName)
+ return l.loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, c, nameSpace, prGroupHash, ownerName, ownerLabel)
}
snapshots, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetPRSnapshotsKey, []applicationapiv1alpha1.Snapshot{})
return &snapshots, err
}
-func (l *mockLoader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, applicationName string) (*[]applicationapiv1alpha1.Snapshot, error) {
+func (l *mockLoader) GetMatchingGroupSnapshotsForPRGroupHash(ctx context.Context, c client.Client, nameSpace, prGroupHash, ownerName, ownerLabel string) (*[]applicationapiv1alpha1.Snapshot, error) {
if ctx.Value(GetGroupSnapshotsKey) == nil {
- return l.loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, c, nameSpace, prGroupHash, applicationName)
+ return l.loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, c, nameSpace, prGroupHash, ownerName, ownerLabel)
}
snapshots, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetGroupSnapshotsKey, []applicationapiv1alpha1.Snapshot{})
return &snapshots, err
@@ -365,9 +383,9 @@ func (l *mockLoader) GetAllIntegrationPipelineRunsForSnapshot(ctx context.Contex
return pipelineRuns, err
}
-func (l *mockLoader) GetComponentsFromSnapshotForPRGroup(ctx context.Context, c client.Client, namespace, prGroup, prGroupHash, applicationName string) ([]string, error) {
+func (l *mockLoader) GetComponentsFromSnapshotForPRGroup(ctx context.Context, c client.Client, namespace, prGroupHash, ownerName, ownerLabel string) ([]string, error) {
if ctx.Value(GetComponentsFromSnapshotForPRGroupKey) == nil {
- return l.loader.GetComponentsFromSnapshotForPRGroup(ctx, c, namespace, prGroup, prGroupHash, applicationName)
+ return l.loader.GetComponentsFromSnapshotForPRGroup(ctx, c, namespace, prGroupHash, ownerName, ownerLabel)
}
components, err := toolkit.GetMockedResourceAndErrorFromContext(ctx, GetComponentsFromSnapshotForPRGroupKey, []string{})
return components, err
diff --git a/loader/loader_mock_test.go b/loader/loader_mock_test.go
index 1cd40d68d0..03480c122d 100644
--- a/loader/loader_mock_test.go
+++ b/loader/loader_mock_test.go
@@ -52,7 +52,7 @@ var _ = Describe("Release Adapter", Ordered, func() {
})
})
- Context("When calling GetAllApplicationComponents", func() {
+ Context("When calling GetAllApplicationComponents [APPLICATION]", func() {
It("returns resource and error from the context", func() {
applicationComponents := []applicationapiv1alpha1.Component{}
mockContext := toolkit.GetMockedContext(ctx, []toolkit.MockData{
@@ -67,6 +67,21 @@ var _ = Describe("Release Adapter", Ordered, func() {
})
})
+ Context("When calling GetAllComponentGroupComponents", func() {
+ It("returns resource and error from the context", func() {
+ groupComponents := []applicationapiv1alpha1.Component{}
+ mockContext := toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: ComponentGroupComponentsContextKey,
+ Resource: groupComponents,
+ },
+ })
+ resource, err := loader.GetAllComponentGroupComponents(mockContext, nil, nil)
+ Expect(resource).To(Equal(&groupComponents))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
Context("When calling GetApplicationFromSnapshot [APPLICATION]", func() {
It("returns resource and error from the context", func() {
application := &applicationapiv1alpha1.Application{}
@@ -361,7 +376,22 @@ var _ = Describe("Release Adapter", Ordered, func() {
Resource: plrs,
},
})
- resource, err := loader.GetPipelineRunsWithPRGroupHash(mockContext, nil, "", "", "")
+ resource, err := loader.GetPipelineRunsWithPRGroupHash(mockContext, nil, "", "")
+ Expect(resource).To(Equal(&plrs))
+ Expect(err).ToNot(HaveOccurred())
+ })
+ })
+
+ Context("When calling GetPipelineRunsWithPRGroupHashForApplication", func() {
+ It("returns resource and error from the context", func() {
+ plrs := []tektonv1.PipelineRun{}
+ mockContext := toolkit.GetMockedContext(ctx, []toolkit.MockData{
+ {
+ ContextKey: GetBuildPLRContextKey,
+ Resource: plrs,
+ },
+ })
+ resource, err := loader.GetPipelineRunsWithPRGroupHashForApplication(mockContext, nil, "", "", "")
Expect(resource).To(Equal(&plrs))
Expect(err).ToNot(HaveOccurred())
})
@@ -376,7 +406,7 @@ var _ = Describe("Release Adapter", Ordered, func() {
Resource: snapshots,
},
})
- resource, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(mockContext, nil, "", "", "", "")
+ resource, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(mockContext, nil, "", "", "", "", "")
Expect(resource).To(Equal(&snapshots))
Expect(err).ToNot(HaveOccurred())
})
@@ -391,7 +421,7 @@ var _ = Describe("Release Adapter", Ordered, func() {
Resource: snapshots,
},
})
- resource, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(mockContext, nil, "", "", "")
+ resource, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(mockContext, nil, "", "", "", "")
Expect(resource).To(Equal(&snapshots))
Expect(err).ToNot(HaveOccurred())
})
@@ -421,7 +451,7 @@ var _ = Describe("Release Adapter", Ordered, func() {
Resource: snapshots,
},
})
- resource, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(mockContext, nil, "", "", "")
+ resource, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(mockContext, nil, "", "", "", "")
Expect(resource).To(Equal(&snapshots))
Expect(err).ToNot(HaveOccurred())
})
diff --git a/loader/loader_test.go b/loader/loader_test.go
index 0b68a8a69c..0b9f606566 100644
--- a/loader/loader_test.go
+++ b/loader/loader_test.go
@@ -61,6 +61,7 @@ var _ = Describe("Loader", Ordered, func() {
const (
SampleRepoLink = "https://github.com/devfile-samples/devfile-sample-java-springboot-basic"
applicationName = "application-sample"
+ componentGroupName = "component-group-sample"
snapshotName = "snapshot-sample"
cgSnapshotName = "componentgroup-snapshot-sample"
groupSnapshotName = "group-snapshot-sample"
@@ -670,6 +671,12 @@ var _ = Describe("Loader", Ordered, func() {
Expect(applicationComponents).NotTo(BeNil())
})
+ It("ensures the ComponentGroup Components can be found ", func() {
+ componentGroupComponents, err := loader.GetAllComponentGroupComponents(ctx, k8sClient, hasComponentGroup1)
+ Expect(err).ToNot(HaveOccurred())
+ Expect(componentGroupComponents).NotTo(BeNil())
+ })
+
It("ensures we can get an Application from a Snapshot [APPLICATION]", func() {
app, err := loader.GetApplicationFromSnapshot(ctx, k8sClient, hasSnapshot)
Expect(err).ToNot(HaveOccurred())
@@ -943,7 +950,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("ensures the merge queue build pipeline runs can be found", func() {
- groupShaPipelineRuns, err := loader.GetPipelineRunsWithPRGroupHash(ctx, k8sClient, mergeQueueBuildPipelineRun.Namespace, mergeQueueHash, applicationName)
+ groupShaPipelineRuns, err := loader.GetPipelineRunsWithPRGroupHash(ctx, k8sClient, mergeQueueBuildPipelineRun.Namespace, mergeQueueHash)
Expect(err).ToNot(HaveOccurred())
Expect(groupShaPipelineRuns).ToNot(BeNil())
Expect(*groupShaPipelineRuns).To(HaveLen(1))
@@ -1004,7 +1011,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("ensures all merge queue snapshots can be found for a given PR group hash", func() {
- groupShaComponentSnapshots, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, mergeQueueHash, applicationName)
+ groupShaComponentSnapshots, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, mergeQueueHash, applicationName, gitops.ApplicationNameLabel)
Expect(err).ToNot(HaveOccurred())
Expect(groupShaComponentSnapshots).ToNot(BeNil())
Expect(*groupShaComponentSnapshots).To(HaveLen(3))
@@ -1014,7 +1021,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("ensures all merge queue component snapshots can be found for a given component and PR group hash", func() {
- groupShaComponentSnapshots, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, hasComp.Name, mergeQueueHash, applicationName)
+ groupShaComponentSnapshots, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, hasComp.Name, mergeQueueHash, applicationName, gitops.ApplicationNameLabel)
Expect(err).ToNot(HaveOccurred())
Expect(groupShaComponentSnapshots).ToNot(BeNil())
Expect(*groupShaComponentSnapshots).To(HaveLen(2))
@@ -1025,7 +1032,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("ensures all merge queue group snapshots can be found for a given component and PR group hash", func() {
- groupShaComponentSnapshots, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, mergeQueueHash, applicationName)
+ groupShaComponentSnapshots, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, k8sClient, mergeQueueSnapshot.Namespace, mergeQueueHash, applicationName, gitops.ApplicationNameLabel)
Expect(err).ToNot(HaveOccurred())
Expect(groupShaComponentSnapshots).ToNot(BeNil())
Expect(*groupShaComponentSnapshots).To(HaveLen(1))
@@ -1219,7 +1226,15 @@ var _ = Describe("Loader", Ordered, func() {
})
It("Can get build plr with pr group hash", func() {
- fetchedBuildPLRs, err := loader.GetPipelineRunsWithPRGroupHash(ctx, k8sClient, hasSnapshot.Namespace, prGroupSha, hasApp.Name)
+ fetchedBuildPLRs, err := loader.GetPipelineRunsWithPRGroupHash(ctx, k8sClient, hasSnapshot.Namespace, prGroupSha)
+ Expect(err).To(Succeed())
+ Expect((*fetchedBuildPLRs)[0].Name).To(Equal(buildPipelineRun.Name))
+ Expect((*fetchedBuildPLRs)[0].Namespace).To(Equal(buildPipelineRun.Namespace))
+ Expect((*fetchedBuildPLRs)[0].Spec).To(Equal(buildPipelineRun.Spec))
+ })
+
+ It("Can get build plr with pr group hash [APPLICATION]", func() {
+ fetchedBuildPLRs, err := loader.GetPipelineRunsWithPRGroupHashForApplication(ctx, k8sClient, hasSnapshot.Namespace, prGroupSha, hasApp.Name)
Expect(err).To(Succeed())
Expect((*fetchedBuildPLRs)[0].Name).To(Equal(buildPipelineRun.Name))
Expect((*fetchedBuildPLRs)[0].Namespace).To(Equal(buildPipelineRun.Namespace))
@@ -1227,7 +1242,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("Can get matching snapshot for component and pr group hash", func() {
- fetchedSnapshots, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, k8sClient, hasSnapshot.Namespace, hasComp.Name, prGroupSha, hasApp.Name)
+ fetchedSnapshots, err := loader.GetMatchingComponentSnapshotsForComponentAndPRGroupHash(ctx, k8sClient, hasSnapshot.Namespace, hasComp.Name, prGroupSha, hasApp.Name, gitops.ApplicationNameLabel)
Expect(err).To(Succeed())
Expect((*fetchedSnapshots)[0].Name).To(Equal(hasSnapshot.Name))
Expect((*fetchedSnapshots)[0].Namespace).To(Equal(hasSnapshot.Namespace))
@@ -1235,7 +1250,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("Can get matching snapshot for pr group hash", func() {
- fetchedSnapshots, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, k8sClient, "", prGroupSha, hasApp.Name)
+ fetchedSnapshots, err := loader.GetMatchingComponentSnapshotsForPRGroupHash(ctx, k8sClient, "", prGroupSha, hasApp.Name, gitops.ApplicationNameLabel)
Expect(err).To(Succeed())
Expect((*fetchedSnapshots)[0].Name).To(Equal(hasSnapshot.Name))
Expect((*fetchedSnapshots)[0].Namespace).To(Equal(hasSnapshot.Namespace))
@@ -1243,7 +1258,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("Can get matching group snapshot for pr group hash", func() {
- fetchedSnapshots, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, k8sClient, "", prGroupSha, hasApp.Name)
+ fetchedSnapshots, err := loader.GetMatchingGroupSnapshotsForPRGroupHash(ctx, k8sClient, "", prGroupSha, hasApp.Name, gitops.ApplicationNameLabel)
Expect(err).To(Succeed())
Expect((*fetchedSnapshots)[0].Name).To(Equal(hasGroupSnapshot.Name))
Expect((*fetchedSnapshots)[0].Namespace).To(Equal(hasGroupSnapshot.Namespace))
@@ -1251,7 +1266,7 @@ var _ = Describe("Loader", Ordered, func() {
})
It("Can get matching components from snapshots for pr group hash", func() {
- components, err := loader.GetComponentsFromSnapshotForPRGroup(ctx, k8sClient, "", "", prGroupSha, hasApp.Name)
+ components, err := loader.GetComponentsFromSnapshotForPRGroup(ctx, k8sClient, "", prGroupSha, hasApp.Name, gitops.ApplicationNameLabel)
Expect(err).To(Succeed())
Expect((components)[0]).To(Equal(hasComp.Name))
diff --git a/snapshot/create.go b/snapshot/create.go
index 82db381530..51cf9c8241 100644
--- a/snapshot/create.go
+++ b/snapshot/create.go
@@ -154,7 +154,7 @@ func CreateSnapshotWithCollisionHandling(ctx context.Context, client client.Clie
// In case the Snapshot can't be created, an error will be returned.
func PrepareSnapshot(ctx context.Context, adapterClient client.Client, componentGroup *v1beta2.ComponentGroup, newSnapshotComponent applicationapiv1alpha1.SnapshotComponent, log logr.Logger) (*applicationapiv1alpha1.Snapshot, error) {
- snapshotComponents, invalidComponents := getSnapshotComponentsFromGCL(componentGroup, log)
+ snapshotComponents, invalidComponents := GetSnapshotComponentsFromGCL(componentGroup, log)
upsertNewComponentImage(&snapshotComponents, &invalidComponents, newSnapshotComponent, log)
if len(snapshotComponents) == 0 {
@@ -185,7 +185,7 @@ func PrepareSnapshot(ctx context.Context, adapterClient client.Client, component
}
// This prevents race conditions if EnsureGCLAlignedWithSpecComponents runs late
-func getSnapshotComponentsFromGCL(componentGroup *v1beta2.ComponentGroup, log logr.Logger) ([]applicationapiv1alpha1.SnapshotComponent, []v1beta2.ComponentState) {
+func GetSnapshotComponentsFromGCL(componentGroup *v1beta2.ComponentGroup, log logr.Logger) ([]applicationapiv1alpha1.SnapshotComponent, []v1beta2.ComponentState) {
var snapshotComponents []applicationapiv1alpha1.SnapshotComponent
var invalidComponents []v1beta2.ComponentState
@@ -221,7 +221,7 @@ func getSnapshotComponentsFromGCL(componentGroup *v1beta2.ComponentGroup, log lo
}
// Get ComponentSource for the component which is not built in this pipeline
- componentSource := getComponentSourceFromGCLComponent(gclComponent)
+ componentSource := GetComponentSourceFromGCLComponent(gclComponent)
snapshotComponents = append(snapshotComponents, applicationapiv1alpha1.SnapshotComponent{
Name: name,
@@ -307,7 +307,7 @@ func NewSnapshot(componentGroup *v1beta2.ComponentGroup, snapshotComponents *[]a
return snapshot
}
-func getComponentSourceFromGCLComponent(gclComponent v1beta2.ComponentState) applicationapiv1alpha1.ComponentSource {
+func GetComponentSourceFromGCLComponent(gclComponent v1beta2.ComponentState) applicationapiv1alpha1.ComponentSource {
// NOTE: if we need to fall back on data from component CR we can do it in here
componentSource := applicationapiv1alpha1.ComponentSource{
ComponentSourceUnion: applicationapiv1alpha1.ComponentSourceUnion{
@@ -346,3 +346,10 @@ func getSnapshotComponentFromBuildPLR(pipelineRun *tektonv1.PipelineRun, compone
Source: *componentSource,
}, nil
}
+
+// PrepareTempGroupSnapshot will prepare a temp group snapshot used to check the integration test scenario that should be applied to the group snapshot under that componentGroup
+func PrepareTempGroupSnapshot(componentGroup *v1beta2.ComponentGroup, snapshot *applicationapiv1alpha1.Snapshot) *applicationapiv1alpha1.Snapshot {
+ tempGroupSnapshot := NewSnapshot(componentGroup, &[]applicationapiv1alpha1.SnapshotComponent{})
+ tempGroupSnapshot, _ = gitops.SetAnnotationAndLabelForGroupSnapshot(tempGroupSnapshot, snapshot, []gitops.ComponentSnapshotInfo{})
+ return tempGroupSnapshot
+}
diff --git a/snapshot/create_test.go b/snapshot/create_test.go
index 91c98b836d..4578b79619 100644
--- a/snapshot/create_test.go
+++ b/snapshot/create_test.go
@@ -537,7 +537,7 @@ var _ = Describe("Snapshot creation functions", Ordered, func() {
It("Ensures valid and invalid snapshotComponents can be gathered from the GCL", func() {
var buf bytes.Buffer
readableLog := buflogr.NewWithBuffer(&buf)
- snapshotComponents, invalidComponents := getSnapshotComponentsFromGCL(hasCompGroup, readableLog)
+ snapshotComponents, invalidComponents := GetSnapshotComponentsFromGCL(hasCompGroup, readableLog)
Expect(snapshotComponents).To(HaveLen(1))
Expect(snapshotComponents[0].Name).To(Equal(componentName))
@@ -553,7 +553,7 @@ var _ = Describe("Snapshot creation functions", Ordered, func() {
newSnapshotComponent, err := getSnapshotComponentFromBuildPLR(buildPipelineRun, componentName, logger)
Expect(err).NotTo(HaveOccurred())
- snapshotComponents, invalidComponents := getSnapshotComponentsFromGCL(hasCompGroup, logger)
+ snapshotComponents, invalidComponents := GetSnapshotComponentsFromGCL(hasCompGroup, logger)
Expect(snapshotComponents).To(HaveLen(1))
Expect(invalidComponents).To(HaveLen(1))
@@ -570,7 +570,7 @@ var _ = Describe("Snapshot creation functions", Ordered, func() {
newSnapshotComponent, err := getSnapshotComponentFromBuildPLR(buildPipelineRun, componentName, logger)
Expect(err).NotTo(HaveOccurred())
- snapshotComponents, invalidComponents := getSnapshotComponentsFromGCL(hasCompGroup, logger)
+ snapshotComponents, invalidComponents := GetSnapshotComponentsFromGCL(hasCompGroup, logger)
Expect(snapshotComponents).To(HaveLen(1))
Expect(invalidComponents).To(HaveLen(1))
@@ -589,7 +589,7 @@ var _ = Describe("Snapshot creation functions", Ordered, func() {
Expect(err).NotTo(HaveOccurred())
newSnapshotComponent.Name = componentName2
- snapshotComponents, invalidComponents := getSnapshotComponentsFromGCL(hasCompGroup, logger)
+ snapshotComponents, invalidComponents := GetSnapshotComponentsFromGCL(hasCompGroup, logger)
Expect(snapshotComponents).To(HaveLen(1))
Expect(invalidComponents).To(HaveLen(1))
@@ -622,4 +622,51 @@ var _ = Describe("Snapshot creation functions", Ordered, func() {
Expect(snapshot.Annotations[helpers.CreateSnapshotAnnotationName]).To(Equal("Component(s) 'another-component-sample (version v1)' is(are) not included in snapshot due to missing valid containerImage or git source"))
})
})
+
+ It("ensures NewSnapshot truncates application name if longer than 43 characters", func() {
+ longGroupName := "this-is-a-very-long-application-name-that-exceeds-43-chars"
+ longGroup := &v1beta2.ComponentGroup{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: longGroupName,
+ Namespace: "namespace",
+ },
+ }
+ snapshotComponents := []applicationapiv1alpha1.SnapshotComponent{
+ {
+ Name: "component-1",
+ ContainerImage: "registry.io/image1:v1.0.0",
+ },
+ }
+
+ snapshot := NewSnapshot(longGroup, &snapshotComponents)
+
+ // Name should be truncated to 44 characters + "-" + 19 character timestamp
+ expectedPrefix := longGroupName[:43]
+ Expect(snapshot.Name).To(HavePrefix(expectedPrefix + "-"))
+ Expect(snapshot.Name).To(MatchRegexp(`^` + expectedPrefix + `-\d{8}-\d{6}-\d{3}$`))
+ Expect(snapshot.Spec.ComponentGroup).To(Equal(longGroupName))
+ })
+
+ It("ensures NewSnapshot does not truncate application name at 43 characters", func() {
+ exactGroupName := "this-is-application-name-exactly-43-chars" // 43 chars
+ exactGroup := &v1beta2.ComponentGroup{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: exactGroupName,
+ Namespace: "namespace",
+ },
+ }
+ snapshotComponents := []applicationapiv1alpha1.SnapshotComponent{
+ {
+ Name: "component-1",
+ ContainerImage: "registry.io/image1:v1.0.0",
+ },
+ }
+
+ snapshot := NewSnapshot(exactGroup, &snapshotComponents)
+
+ // Name should be exactAppName + "-" + 19 character timestamp
+ Expect(snapshot.Name).To(HavePrefix(exactGroupName + "-"))
+ Expect(snapshot.Name).To(MatchRegexp(`^` + exactGroupName + `-\d{8}-\d{6}-\d{3}$`))
+ Expect(snapshot.Spec.ComponentGroup).To(Equal(exactGroupName))
+ })
})
diff --git a/snapshot/gcl.go b/snapshot/gcl.go
index bb2e3bf753..844fd389b6 100644
--- a/snapshot/gcl.go
+++ b/snapshot/gcl.go
@@ -163,3 +163,17 @@ func UpdateGCLForOverrideSnapshot(ctx context.Context, adapterClient client.Clie
return err
}
+
+func FetchSnapshotComponentFromGCL(componentName string, snapshotComponentsFromGCL []applicationapiv1alpha1.SnapshotComponent, invalidComponents []v1beta2.ComponentState) (*applicationapiv1alpha1.SnapshotComponent, error) {
+ for _, snapshotComponentFromGCL := range snapshotComponentsFromGCL {
+ if snapshotComponentFromGCL.Name == componentName {
+ return &snapshotComponentFromGCL, nil
+ }
+ }
+ for _, invalidComponent := range invalidComponents {
+ if invalidComponent.Name == componentName {
+ return nil, fmt.Errorf("component cannot be added to snapshot due to invalid digest in containerImage")
+ }
+ }
+ return nil, nil
+}
diff --git a/status/status.go b/status/status.go
index 1a2c275a90..0ef9b58117 100644
--- a/status/status.go
+++ b/status/status.go
@@ -430,23 +430,21 @@ func GenerateSummaryForAllScenarios(state intgteststat.IntegrationTestStatus, co
return summary, nil
}
-// function GenerateComponentNameWithPrefix to generate component name "pr group" or "component component-sample"
+// GenerateComponentNameWithPrefix to generate component name "pr group" or "component component-sample"
// to help search it in comment correctly to avoid component name is searched in pr group report
func GenerateComponentNameWithPrefix(componentName string) string {
- if componentName == gitops.ComponentNameForGroupSnapshot {
- componentName = gitops.ComponentNameForGroupSnapshot
- } else {
+ if !strings.HasPrefix(componentName, gitops.ComponentNameForGroupSnapshot) {
componentName = "component " + componentName
}
return componentName
}
-// function GenerateTestSummaryPrefixForComponent to generate test summary prefix for component name "pr group" or "component component-sample"
+// GenerateTestSummaryPrefixForComponent to generate test summary prefix for component name "pr group" or "component component-sample"
// to help search it in comment correctly since all comments are posted together
func GenerateTestSummaryPrefixForComponent(componentName string) string {
var commentTitle string
- if componentName == gitops.ComponentNameForGroupSnapshot {
- commentTitle = "Integration test for " + gitops.ComponentNameForGroupSnapshot
+ if strings.HasPrefix(componentName, gitops.ComponentNameForGroupSnapshot) {
+ commentTitle = "Integration test for " + componentName
} else {
commentTitle = "Integration test for component " + componentName
}