Skip to content

Commit 12f0481

Browse files
committed
test(RELEASE-2325): add idempotent retrigger e2e test for rh-advisories
Adds a new integration test suite rh-advisories-idempotent that validates safe re-release behavior when the same snapshot is released twice through the rh-advisories pipeline. Signed-off-by: Elena German <elgerman@redhat.com> Assisted-by: Claude
1 parent 24511fc commit 12f0481

23 files changed

Lines changed: 695 additions & 63 deletions

File tree

integration-tests/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ This directory contains end-to-end integration tests for the Release Service Cat
77
The following integration test suites are available:
88

99
- **[collectors](collectors/)** - Tests for advisory data collection and processing
10+
- **[rh-advisories-idempotent](rh-advisories-idempotent/)** - Tests idempotent re-release behavior for the rh-advisories pipeline: verifies that a second release with the same snapshot detects the existing advisory, skips all downstream tasks, and correctly populates `advisory.url` in the Release CR status
1011
- **[fbc-release](fbc-release/)** - Tests for File-Based Catalog (FBC) release pipeline
1112
- **[push-to-addons-registry](push-to-addons-registry/)** - Tests for pushing to addon registries
1213
- **[rh-push-helm-chart-to-registry-redhat-io](rh-push-helm-chart-to-registry-redhat-io/)** - Tests for Helm OCI chart release pipeline

integration-tests/lib/test-functions.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,60 @@ cleanup_old_resources() {
709709
set -e
710710
}
711711

712+
# --- Release / PipelineRun Query Helpers ---
713+
# These helpers are available to all test suites via this shared library.
714+
715+
# Return the short PipelineRun name (no namespace prefix) for the managed
716+
# pipeline associated with a Release CR.
717+
get_pipelinerun_name_from_release() {
718+
local release_name=$1
719+
720+
local pipelinerun_full
721+
pipelinerun_full=$(kubectl get release "${release_name}" -n "${tenant_namespace}" \
722+
-o jsonpath='{.status.managedProcessing.pipelineRun}' 2>/dev/null)
723+
724+
if [ -z "${pipelinerun_full}" ]; then
725+
return 1
726+
fi
727+
728+
basename "${pipelinerun_full}"
729+
}
730+
731+
# Return the full Release CR as JSON.
732+
get_release_json() {
733+
local release_name=$1
734+
kubectl get release "${release_name}" -n "${tenant_namespace}" -o json 2>/dev/null
735+
}
736+
737+
# Return 0 (true) if the named task appears in the PipelineRun's skippedTasks
738+
# list, 1 (false) otherwise.
739+
is_task_skipped() {
740+
local release_name=$1
741+
local task_name=$2
742+
743+
local pipelinerun_name
744+
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1
745+
746+
local skipped_task
747+
skipped_task=$(kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
748+
-o jsonpath="{.status.skippedTasks[?(@.name=='${task_name}')].name}" 2>/dev/null)
749+
750+
[[ -n "${skipped_task}" ]]
751+
}
752+
753+
# Return the value of a named pipeline-level result from the managed PipelineRun
754+
# associated with a Release CR.
755+
get_pipelinerun_result() {
756+
local release_name=$1
757+
local result_name=$2
758+
759+
local pipelinerun_name
760+
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1
761+
762+
kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
763+
-o jsonpath="{.status.results[?(@.name=='${result_name}')].value}" 2>/dev/null
764+
}
765+
712766
# Function to verify Release contents
713767
verify_release_contents() {
714768
echo "📝 Note: Test Suite may implement ${FUNCNAME[0]}" \

integration-tests/pipelines/e2e-tests-periodic-pipeline.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,9 @@ spec:
9999
failed_test_cases=()
100100
success_test_cases=()
101101
102-
ALL_TESTCASES=("e2e" "collectors" "fbc-release" "release-to-github" "push-to-external-registry" \
103-
"rhtap-service-push" "rh-push-to-registry-redhat-io" "rh-push-to-external-registry" \
104-
"push-to-addons-registry")
102+
ALL_TESTCASES=("e2e" "collectors" "rh-advisories-idempotent" "fbc-release" "release-to-github" \
103+
"push-to-external-registry" "rhtap-service-push" "rh-push-to-registry-redhat-io" \
104+
"rh-push-to-external-registry" "push-to-addons-registry")
105105
# push-rpms-to-pulp fails, will be fixed in RELEASE-2049
106106
# "push-to-addons-registry" "push-rpms-to-pulp")
107107
overall_test_count=${#ALL_TESTCASES[@]}

integration-tests/push-to-external-registry-idempotent/test.sh

Lines changed: 1 addition & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -13,55 +13,13 @@
1313
# --- Global Script Variables (Defaults) ---
1414
CLEANUP="true"
1515

16-
# Helper: Get PipelineRun name from Release CR
17-
# Returns just the PipelineRun name (without namespace prefix)
18-
get_pipelinerun_name_from_release() {
19-
local release_name=$1
20-
21-
# Get the actual PipelineRun name from the Release CR
22-
# Format is: namespace/name, we need just the name part
23-
local pipelinerun_full
24-
pipelinerun_full=$(kubectl get release "${release_name}" -n "${tenant_namespace}" \
25-
-o jsonpath='{.status.managedProcessing.pipelineRun}' 2>/dev/null)
26-
27-
if [ -z "${pipelinerun_full}" ]; then
28-
return 1
29-
fi
30-
31-
# Extract and return just the name part after the /
32-
basename "${pipelinerun_full}"
33-
}
34-
35-
# Helper: Get release as JSON
36-
get_release_json() {
37-
local release_name=$1
38-
kubectl get release "${release_name}" -n "${tenant_namespace}" -o json
39-
}
40-
4116
# Check if all components were filtered (idempotency validation)
4217
# Returns 0 (true) if push-snapshot task was skipped, 1 (false) otherwise
4318
were_all_components_filtered() {
4419
local release_name=$1
4520

4621
# Check if all components were filtered by seeing if push-snapshot was skipped
47-
is_taskrun_skipped "${release_name}" "push-snapshot"
48-
}
49-
50-
# Check if a specific task was skipped in the pipeline
51-
# Returns 0 (true) if task was skipped, 1 (false) otherwise
52-
is_taskrun_skipped() {
53-
local release_name=$1
54-
local task_name=$2
55-
56-
local pipelinerun_name
57-
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1
58-
59-
# Check if task appears in PipelineRun's skippedTasks list
60-
local skipped_task
61-
skipped_task=$(kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
62-
-o jsonpath="{.status.skippedTasks[?(@.name=='${task_name}')].name}" 2>/dev/null)
63-
64-
[[ -n "${skipped_task}" ]]
22+
is_task_skipped "${release_name}" "push-snapshot"
6523
}
6624

6725
# Verify a single release has valid artifacts and image can be pulled
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# rh-advisories-idempotent test
2+
3+
## Overview
4+
5+
This test validates idempotent re-release behavior for the `rh-advisories` pipeline:
6+
7+
1. **First release**: all tasks run, advisory created (`skip_release=false`)
8+
2. **Second release** (same snapshot): `filter-already-released-advisory-images` detects the
9+
existing advisory in Pyxis → `skip_release=true`
10+
3. All downstream tasks are skipped; release CR still reaches `Released=True`
11+
4. `advisory.url` is present in second release status (populated from filter result,
12+
not from `create-advisory`)
13+
14+
## Setup
15+
16+
### Dependencies
17+
* GitHub repo: https://github.com/hacbs-release-tests/e2e-base
18+
* GitHub personal access token (classic) for above repo with **admin:repo_hook**,
19+
**delete_repo**, **repo** scopes.
20+
* The password to the vault files. (Contact a member of the Release team should you
21+
want to run this test suite.)
22+
* Access to the target cluster and tenant and managed namespaces
23+
* **Cluster:** stg-rh01 (staging cluster)
24+
* **Tenant Namespace:** `dev-release-team-tenant` (local) or `rhtap-release-2-tenant` (PaC)
25+
* **Managed Namespace:** `managed-release-team-tenant`
26+
27+
### Required Environment Variables
28+
- `GITHUB_TOKEN` - GitHub personal access token
29+
- `VAULT_PASSWORD_FILE` - Path to file containing ansible vault password
30+
- `RELEASE_CATALOG_GIT_URL` - Release service catalog URL for the RPA
31+
- `RELEASE_CATALOG_GIT_REVISION` - Release service catalog revision for the RPA
32+
33+
### Optional Environment Variables
34+
- `KUBECONFIG` - Kubeconfig file for cluster access
35+
- `LARGE_SNAPSHOT_COMPONENT_COUNT` - Number of components in snapshot (default: 1)
36+
- `LARGE_SNAPSHOT_TIMEOUT` - Pipeline timeout (default: 2h0m0s)
37+
38+
### Test Properties
39+
#### [test.env](test.env)
40+
- Contains resource names and configuration values for testing.
41+
- Uses a single-component pre-built snapshot to minimize first release duration.
42+
43+
#### [test.sh](test.sh)
44+
- Overrides standard build functions to skip builds and use pre-built images.
45+
- Implements the two-phase idempotent test: first release then second release.
46+
- Verifies all acceptance criteria post second release.
47+
48+
### Test Functions
49+
#### [lib/test-functions.sh](../lib/test-functions.sh)
50+
- Reusable functions for tests.
51+
52+
### Secrets
53+
- Secrets are stored in ansible vault files (symlinked from `rh-advisories-large-snapshot`):
54+
- [vault/managed-secrets.yaml](vault/managed-secrets.yaml)
55+
- [vault/tenant-secrets.yaml](vault/tenant-secrets.yaml)
56+
57+
## Acceptance Criteria
58+
59+
- Second release: all major tasks (`create-advisory`, `push-snapshot`, `verify-conforma`,
60+
`rh-sign-image`, etc.) appear in `skippedTasks`
61+
- `advisory.url` present in second release `status.artifacts` (written by
62+
`update-cr-status-skipped` from filter result, not `create-advisory`)
63+
64+
## Running the test
65+
66+
For local testing:
67+
68+
```shell
69+
../run-test.sh rh-advisories-idempotent
70+
```
71+
72+
**Note:** This test runs two full releases sequentially. The first release takes
73+
approximately 1-2 hours in staging; the second release completes quickly once the
74+
filter detects the existing advisory.
75+
76+
### Debugging
77+
78+
Use `--skip-cleanup` to preserve resources after the test:
79+
80+
```shell
81+
../run-test.sh rh-advisories-idempotent --skip-cleanup
82+
```
83+
84+
### Maintenance
85+
86+
To update secrets:
87+
88+
```shell
89+
ansible-vault decrypt vault/tenant-secrets.yaml --output "/tmp/tenant-secrets.yaml" \
90+
--vault-password-file <vault password file>
91+
vi /tmp/tenant-secrets.yaml
92+
ansible-vault encrypt /tmp/tenant-secrets.yaml --output "vault/tenant-secrets.yaml" \
93+
--vault-password-file <vault password file>
94+
rm /tmp/tenant-secrets.yaml
95+
```
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../collectors/resources/managed/ec-policy.yaml
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
kind: Kustomization
3+
apiVersion: kustomize.config.k8s.io/v1beta1
4+
5+
namespace: ${managed_namespace}
6+
resources:
7+
- sa.yaml
8+
- sa-rolebinding.yaml
9+
- rpa.yaml
10+
- ec-policy.yaml
11+
- secrets/managed-secrets.yaml
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
---
2+
apiVersion: appstudio.redhat.com/v1alpha1
3+
kind: ReleasePlanAdmission
4+
metadata:
5+
name: ${release_plan_admission_name}
6+
labels:
7+
originating-tool: "${originating_tool}"
8+
spec:
9+
applications:
10+
- ${application_name}
11+
data:
12+
jira:
13+
jiraAdvisorySecret: advisory-jira-secret-${component_name}
14+
cgw:
15+
cgwSecret: publish-to-cgw-secret-${component_name}
16+
atlas:
17+
server: stage
18+
atlas-sso-secret-name: atlas-staging-sso-secret-${component_name}
19+
atlas-retry-aws-secret-name: atlas-retry-s3-staging-secret-${component_name}
20+
pyxis:
21+
server: stage
22+
secret: pyxis-${component_name}
23+
sign:
24+
configMapName: "hacbs-signing-pipeline-config-staging-e2e-pq"
25+
cosignSecretName: "konflux-cosign-signing-stage-${component_name}"
26+
mapping:
27+
defaults:
28+
tags:
29+
- latest
30+
pushSourceContainer: true
31+
components:
32+
- name: ${component_name}
33+
repositories:
34+
- url: quay.io/redhat-pending/rhtap----rh-advisories-component
35+
releaseNotes:
36+
product_id:
37+
- 999
38+
product_name: Red Hat Comp2
39+
product_version: '1.0.1'
40+
cpe: 'cpe:/a:redhat:comp2:1::appstream'
41+
product_stream: comp2-1.0
42+
origin: ${tenant_namespace}
43+
pipeline:
44+
pipelineRef:
45+
params:
46+
- name: url
47+
value: "${RELEASE_CATALOG_GIT_URL}"
48+
- name: revision
49+
value: "${RELEASE_CATALOG_GIT_REVISION}"
50+
- name: pathInRepo
51+
value: pipelines/managed/rh-advisories/rh-advisories.yaml
52+
resolver: git
53+
serviceAccountName: ${managed_sa_name}
54+
timeouts:
55+
pipeline: 4h0m0s
56+
tasks: 4h0m0s
57+
policy: standard-${component_name}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../collectors/resources/managed/sa-rolebinding.yaml
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../collectors/resources/managed/sa.yaml

0 commit comments

Comments
 (0)