diff --git a/docs/snapshot-controller.md b/docs/snapshot-controller.md
index 5a7e0b411..55bd077b0 100644
--- a/docs/snapshot-controller.md
+++ b/docs/snapshot-controller.md
@@ -17,7 +17,7 @@ flowchart TD
are_there_any_ITS{"Are there any
IntegrationTestScenario
present for the given
Application?"}
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")
+ fetch_all_required_ITS("Fetch all the required
(non-optional) IntegrationTestScenario
for the given Application
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?}
diff --git a/gitops/snapshot.go b/gitops/snapshot.go
index 68da3dfda..50f95a479 100644
--- a/gitops/snapshot.go
+++ b/gitops/snapshot.go
@@ -20,8 +20,10 @@ import (
"context"
"errors"
"fmt"
+ "github.com/konflux-ci/integration-service/api/v1beta2"
"reflect"
"strconv"
+ "strings"
"time"
"github.com/google/go-containerregistry/pkg/name"
@@ -92,6 +94,9 @@ const (
// SnapshotOverrideType is the type of Snapshot which was created for override Global Candidate List.
SnapshotOverrideType = "override"
+ // SnapshotGroupType is the type of Snapshot which was created for pull request groups.
+ SnapshotGroupType = "group"
+
// PipelineAsCodeEventTypeLabel is the type of event which triggered the pipelinerun in build service
PipelineAsCodeEventTypeLabel = PipelinesAsCodePrefix + "/event-type"
@@ -869,6 +874,10 @@ func IsComponentSnapshot(snapshot *applicationapiv1alpha1.Snapshot) bool {
return metadata.HasLabelWithValue(snapshot, SnapshotTypeLabel, SnapshotComponentType)
}
+func IsGroupSnapshot(snapshot *applicationapiv1alpha1.Snapshot) bool {
+ return metadata.HasLabelWithValue(snapshot, SnapshotTypeLabel, SnapshotGroupType)
+}
+
func IsComponentSnapshotCreatedByPACPushEvent(snapshot *applicationapiv1alpha1.Snapshot) bool {
return IsComponentSnapshot(snapshot) && IsSnapshotCreatedByPACPushEvent(snapshot)
}
@@ -885,3 +894,56 @@ func SetOwnerReference(ctx context.Context, adapterClient client.Client, snapsho
}
return snapshot, nil
}
+
+// IsContextValidForSnapshot checks the context and compares it against the Snapshot to determine if it applies
+func IsContextValidForSnapshot(scenarioContextName string, snapshot *applicationapiv1alpha1.Snapshot) bool {
+ // `application` context is supported for backwards-compatibility and considered the same as `all`
+ if scenarioContextName == "application" || scenarioContextName == "all" {
+ return true
+ } else if scenarioContextName == "component" && IsComponentSnapshot(snapshot) {
+ return true
+ } else if strings.HasPrefix(scenarioContextName, "component_") {
+ componentName := strings.TrimPrefix(scenarioContextName, "component_")
+ if metadata.HasLabelWithValue(snapshot, SnapshotComponentLabel, componentName) {
+ return true
+ }
+ } else if scenarioContextName == "group" && IsGroupSnapshot(snapshot) {
+ return true
+ } else if scenarioContextName == "override" && IsOverrideSnapshot(snapshot) {
+ return true
+ } else if scenarioContextName == "push" && IsSnapshotCreatedByPACPushEvent(snapshot) {
+ return true
+ } else if scenarioContextName == "pull_request" && !IsSnapshotCreatedByPACPushEvent(snapshot) {
+ return true
+ }
+ return false
+}
+
+// IsScenarioApplicableToSnapshotsContext checks the contexts list for a given IntegrationTestScenario and
+// compares it against the Snapshot to determine if the scenario applies to it
+func IsScenarioApplicableToSnapshotsContext(scenario *v1beta2.IntegrationTestScenario, snapshot *applicationapiv1alpha1.Snapshot) bool {
+ // If the contexts list is empty, we assume that the scenario applies to all contexts by default
+ if len(scenario.Spec.Contexts) == 0 {
+ return true
+ }
+ for _, scenarioContext := range scenario.Spec.Contexts {
+ scenarioContext := scenarioContext //G601
+ if IsContextValidForSnapshot(scenarioContext.Name, snapshot) {
+ return true
+ }
+ }
+ return false
+}
+
+// FilterIntegrationTestScenariosWithContext returns a filtered list of IntegrationTestScenario from the given list
+// of IntegrationTestScenarios compared against the given Snapshot based on individual IntegrationTestScenario contexts
+func FilterIntegrationTestScenariosWithContext(scenarios *[]v1beta2.IntegrationTestScenario, snapshot *applicationapiv1alpha1.Snapshot) *[]v1beta2.IntegrationTestScenario {
+ var filteredScenarioList []v1beta2.IntegrationTestScenario
+ for _, scenario := range *scenarios {
+ scenario := scenario //G601
+ if IsScenarioApplicableToSnapshotsContext(&scenario, snapshot) {
+ filteredScenarioList = append(filteredScenarioList, scenario)
+ }
+ }
+ return &filteredScenarioList
+}
diff --git a/gitops/snapshot_test.go b/gitops/snapshot_test.go
index e20253611..1f6c699ad 100644
--- a/gitops/snapshot_test.go
+++ b/gitops/snapshot_test.go
@@ -17,6 +17,7 @@ limitations under the License.
package gitops_test
import (
+ "github.com/konflux-ci/integration-service/api/v1beta2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
. "github.com/onsi/gomega/gstruct"
@@ -623,6 +624,92 @@ var _ = Describe("Gitops functions for managing Snapshots", Ordered, func() {
Expect(err).ToNot(HaveOccurred())
})
})
+ })
+
+ Context("Filter integration tests for a given Snapshot based on their context", func() {
+ When("There are a number of integration test scenarios with different contexts", func() {
+ integrationTestScenario := &v1beta2.IntegrationTestScenario{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "example-pass",
+ Namespace: "default",
+ Labels: map[string]string{
+ "test.appstudio.openshift.io/optional": "false",
+ },
+ },
+ Spec: v1beta2.IntegrationTestScenarioSpec{
+ Application: "application-sample",
+ ResolverRef: v1beta2.ResolverRef{
+ Resolver: "git",
+ Params: []v1beta2.ResolverParameter{
+ {
+ Name: "url",
+ Value: "https://github.com/redhat-appstudio/integration-examples.git",
+ },
+ {
+ Name: "revision",
+ Value: "main",
+ },
+ {
+ Name: "pathInRepo",
+ Value: "pipelineruns/integration_pipelinerun_pass.yaml",
+ },
+ },
+ },
+ },
+ }
+ applicationScenario := integrationTestScenario.DeepCopy()
+ applicationScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "application", Description: "Application Testing"}}
+ componentScenario := integrationTestScenario.DeepCopy()
+ componentScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "component", Description: "Component"}}
+ componentSampleScenario := integrationTestScenario.DeepCopy()
+ componentSampleScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "component_component-sample", Description: "Component component-sample"}}
+ componentSample2Scenario := integrationTestScenario.DeepCopy()
+ componentSample2Scenario.Spec.Contexts = []v1beta2.TestContext{{Name: "component_component-sample-2", Description: "Component component-sample-2"}}
+ pullRequestScenario := integrationTestScenario.DeepCopy()
+ pullRequestScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "pull_request", Description: "Pull Request"}}
+ pushScenario := integrationTestScenario.DeepCopy()
+ pushScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "push", Description: "Component"}}
+ groupScenario := integrationTestScenario.DeepCopy()
+ groupScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "group", Description: "PR Group Testing"}}
+ overrideScenario := integrationTestScenario.DeepCopy()
+ overrideScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "override", Description: "Override Snapshot testing"}}
+ componentAndGroupScenario := integrationTestScenario.DeepCopy()
+ componentAndGroupScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "group"}, {Name: "component"}}
+ unsupportedScenario := integrationTestScenario.DeepCopy()
+ unsupportedScenario.Spec.Contexts = []v1beta2.TestContext{{Name: "n/a"}}
+
+ allScenarios := []v1beta2.IntegrationTestScenario{*integrationTestScenario, *applicationScenario,
+ *componentScenario, *componentSampleScenario, *componentSample2Scenario, *pullRequestScenario,
+ *pushScenario, *groupScenario, *componentAndGroupScenario, *unsupportedScenario}
+
+ It("Returns only the scenarios matching the context for a given kind of Snapshot", func() {
+ // A component Snapshot for a push event referencing the component-sample
+ filteredScenarios := gitops.FilterIntegrationTestScenariosWithContext(&allScenarios, hasSnapshot)
+ Expect(*filteredScenarios).To(HaveLen(6))
+
+ // A component Snapshot for pull request event referencing the component-sample
+ hasSnapshot.Labels[gitops.PipelineAsCodeEventTypeLabel] = gitops.PipelineAsCodePullRequestType
+ filteredScenarios = gitops.FilterIntegrationTestScenariosWithContext(&allScenarios, hasSnapshot)
+ Expect(*filteredScenarios).To(HaveLen(6))
+
+ // A group Snapshot for pull request event referencing component-sample-2
+ hasSnapshot.Labels[gitops.SnapshotComponentLabel] = "component-sample-2"
+ filteredScenarios = gitops.FilterIntegrationTestScenariosWithContext(&allScenarios, hasSnapshot)
+ Expect(*filteredScenarios).To(HaveLen(6))
+
+ // A group Snapshot for pull request event for a PR group
+ hasSnapshot.Labels[gitops.SnapshotTypeLabel] = "group"
+ hasSnapshot.Labels[gitops.SnapshotComponentLabel] = ""
+ filteredScenarios = gitops.FilterIntegrationTestScenariosWithContext(&allScenarios, hasSnapshot)
+ Expect(*filteredScenarios).To(HaveLen(5))
+
+ // An override Snapshot
+ hasSnapshot.Labels[gitops.SnapshotTypeLabel] = "override"
+ filteredScenarios = gitops.FilterIntegrationTestScenariosWithContext(&allScenarios, hasSnapshot)
+ Expect(*filteredScenarios).To(HaveLen(3))
+ })
+ })
})
+
})
diff --git a/internal/controller/snapshot/snapshot_adapter.go b/internal/controller/snapshot/snapshot_adapter.go
index 332ba7c4d..beb5b87bf 100644
--- a/internal/controller/snapshot/snapshot_adapter.go
+++ b/internal/controller/snapshot/snapshot_adapter.go
@@ -163,13 +163,14 @@ func (a *Adapter) EnsureIntegrationPipelineRunsExist() (controller.OperationResu
return controller.ContinueProcessing()
}
- integrationTestScenarios, err := a.loader.GetAllIntegrationTestScenariosForApplication(a.context, a.client, a.application)
+ 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)
}
- if integrationTestScenarios != nil {
+ if allIntegrationTestScenarios != nil {
+ integrationTestScenarios := gitops.FilterIntegrationTestScenariosWithContext(allIntegrationTestScenarios, a.snapshot)
a.logger.Info(
fmt.Sprintf("Found %d IntegrationTestScenarios for application", len(*integrationTestScenarios)),
"Application.Name", a.application.Name,
@@ -254,7 +255,7 @@ func (a *Adapter) EnsureIntegrationPipelineRunsExist() (controller.OperationResu
}
}
- requiredIntegrationTestScenarios, err := a.loader.GetRequiredIntegrationTestScenariosForApplication(a.context, a.client, a.application)
+ allRequiredIntegrationTestScenarios, err := a.loader.GetRequiredIntegrationTestScenariosForApplication(a.context, a.client, a.application)
if err != nil {
a.logger.Error(err, "Failed to get all required IntegrationTestScenarios")
patch := client.MergeFrom(a.snapshot.DeepCopy())
@@ -263,6 +264,7 @@ func (a *Adapter) EnsureIntegrationPipelineRunsExist() (controller.OperationResu
a.snapshot, h.LogActionUpdate)
return controller.RequeueOnErrorOrStop(a.client.Status().Patch(a.context, a.snapshot, patch))
}
+ requiredIntegrationTestScenarios := gitops.FilterIntegrationTestScenariosWithContext(allRequiredIntegrationTestScenarios, a.snapshot)
if len(*requiredIntegrationTestScenarios) == 0 && !gitops.IsSnapshotMarkedAsPassed(a.snapshot) {
err := gitops.MarkSnapshotAsPassed(a.context, a.client, a.snapshot, "No required IntegrationTestScenarios found, skipped testing")
if err != nil {
diff --git a/internal/controller/statusreport/statusreport_adapter.go b/internal/controller/statusreport/statusreport_adapter.go
index 33e0061da..65e524471 100644
--- a/internal/controller/statusreport/statusreport_adapter.go
+++ b/internal/controller/statusreport/statusreport_adapter.go
@@ -121,10 +121,11 @@ func (a *Adapter) EnsureSnapshotTestStatusReportedToGitProvider() (controller.Op
func (a *Adapter) EnsureSnapshotFinishedAllTests() (controller.OperationResult, error) {
// Get all required integrationTestScenarios for the Application and then use the Snapshot status annotation
// to check if all Integration tests were finished for that Snapshot
- integrationTestScenarios, err := a.loader.GetRequiredIntegrationTestScenariosForApplication(a.context, a.client, a.application)
+ allRequiredIntegrationTestScenarios, err := a.loader.GetRequiredIntegrationTestScenariosForApplication(a.context, a.client, a.application)
if err != nil {
return controller.RequeueWithError(err)
}
+ integrationTestScenarios := gitops.FilterIntegrationTestScenariosWithContext(allRequiredIntegrationTestScenarios, a.snapshot)
a.logger.Info(fmt.Sprintf("Found %d required integration test scenarios", len(*integrationTestScenarios)))
testStatuses, err := gitops.NewSnapshotIntegrationTestStatusesFromSnapshot(a.snapshot)