Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .tekton/release-service-catalog-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ spec:
- default: ""
description: Image tag expiration time, time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively.
name: image-expires-after
type: string
- default: "false"
description: Build a source image.
name: build-source-image
Expand Down
1 change: 1 addition & 0 deletions .tekton/release-service-catalog-push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ spec:
- default: ""
description: Image tag expiration time, time values could be something like 1h, 2d, 3w for hours, days, and weeks, respectively.
name: image-expires-after
type: string
- default: "false"
description: Build a source image.
name: build-source-image
Expand Down
2 changes: 2 additions & 0 deletions integration-tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ This directory contains end-to-end integration tests for the Release Service Cat
The following integration test suites are available:

- **[collectors](collectors/)** - Tests for advisory data collection and processing
- **[collectors-no-cve](collectors-no-cve/)** - Tests for the no-CVE path of advisory data collection
- **[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
- **[fbc-release](fbc-release/)** - Tests for File-Based Catalog (FBC) release pipeline
- **[push-to-addons-registry](push-to-addons-registry/)** - Tests for pushing to addon registries
- **[rh-push-helm-chart-to-registry-redhat-io](rh-push-helm-chart-to-registry-redhat-io/)** - Tests for Helm OCI chart release pipeline
Expand Down
54 changes: 54 additions & 0 deletions integration-tests/lib/test-functions.sh
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,60 @@ cleanup_old_resources() {
set -e
}

# --- Release / PipelineRun Query Helpers ---
# These helpers are available to all test suites via this shared library.

# Return the short PipelineRun name (no namespace prefix) for the managed
# pipeline associated with a Release CR.
get_pipelinerun_name_from_release() {
local release_name=$1

local pipelinerun_full
pipelinerun_full=$(kubectl get release "${release_name}" -n "${tenant_namespace}" \
-o jsonpath='{.status.managedProcessing.pipelineRun}')

if [ -z "${pipelinerun_full}" ]; then
return 1
fi

basename "${pipelinerun_full}"
}
Comment thread
elenagerman marked this conversation as resolved.

# Return the full Release CR as JSON.
get_release_json() {
local release_name=$1
kubectl get release "${release_name}" -n "${tenant_namespace}" -o json
}
Comment thread
elenagerman marked this conversation as resolved.

# Return 0 (true) if the named task appears in the PipelineRun's skippedTasks
# list, 1 (false) otherwise.
is_task_skipped() {
local release_name=$1
local task_name=$2

local pipelinerun_name
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1

local skipped_task
skipped_task=$(kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
-o jsonpath="{.status.skippedTasks[?(@.name=='${task_name}')].name}")
Comment thread
elenagerman marked this conversation as resolved.

[[ -n "${skipped_task}" ]]
}

# Return the value of a named pipeline-level result from the managed PipelineRun
# associated with a Release CR.
get_pipelinerun_result() {
local release_name=$1
local result_name=$2

local pipelinerun_name
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1

kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
-o jsonpath="{.status.results[?(@.name=='${result_name}')].value}"
}

# Function to verify Release contents
verify_release_contents() {
echo "📝 Note: Test Suite may implement ${FUNCNAME[0]}" \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ spec:
success_test_cases=()

ALL_TESTCASES=("collectors" "collectors-no-cve" "e2e" "fbc-release" "push-rpms-to-pulp" \
"push-to-addons-registry" "push-to-external-registry-idempotent" \
"push-to-addons-registry" "push-to-external-registry-idempotent" "rh-advisories-idempotent" \
"push-to-external-registry" "release-to-github" "rh-push-helm-chart-to-registry-redhat-io" \
"rh-push-to-external-registry" "rh-push-to-external-registry-multi-component" \
"rh-push-to-registry-redhat-io" "rhtap-service-push")
Expand Down
44 changes: 1 addition & 43 deletions integration-tests/push-to-external-registry-idempotent/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,55 +13,13 @@
# --- Global Script Variables (Defaults) ---
CLEANUP="true"

# Helper: Get PipelineRun name from Release CR
# Returns just the PipelineRun name (without namespace prefix)
get_pipelinerun_name_from_release() {
local release_name=$1

# Get the actual PipelineRun name from the Release CR
# Format is: namespace/name, we need just the name part
local pipelinerun_full
pipelinerun_full=$(kubectl get release "${release_name}" -n "${tenant_namespace}" \
-o jsonpath='{.status.managedProcessing.pipelineRun}' 2>/dev/null)

if [ -z "${pipelinerun_full}" ]; then
return 1
fi

# Extract and return just the name part after the /
basename "${pipelinerun_full}"
}

# Helper: Get release as JSON
get_release_json() {
local release_name=$1
kubectl get release "${release_name}" -n "${tenant_namespace}" -o json
}

# Check if all components were filtered (idempotency validation)
# Returns 0 (true) if push-snapshot task was skipped, 1 (false) otherwise
were_all_components_filtered() {
local release_name=$1

# Check if all components were filtered by seeing if push-snapshot was skipped
is_taskrun_skipped "${release_name}" "push-snapshot"
}

# Check if a specific task was skipped in the pipeline
# Returns 0 (true) if task was skipped, 1 (false) otherwise
is_taskrun_skipped() {
local release_name=$1
local task_name=$2

local pipelinerun_name
pipelinerun_name=$(get_pipelinerun_name_from_release "${release_name}") || return 1

# Check if task appears in PipelineRun's skippedTasks list
local skipped_task
skipped_task=$(kubectl get pipelinerun "${pipelinerun_name}" -n "${managed_namespace}" \
-o jsonpath="{.status.skippedTasks[?(@.name=='${task_name}')].name}" 2>/dev/null)

[[ -n "${skipped_task}" ]]
is_task_skipped "${release_name}" "push-snapshot"
}

# Verify a single release has valid artifacts and image can be pulled
Expand Down
95 changes: 95 additions & 0 deletions integration-tests/rh-advisories-idempotent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# rh-advisories-idempotent test

## Overview

This test validates idempotent re-release behavior for the `rh-advisories` pipeline:

1. **First release**: all tasks run, advisory created (`skip_release=false`)
2. **Second release** (same snapshot): `filter-already-released-advisory-images` detects the
existing advisory in Pyxis → `skip_release=true`
3. All downstream tasks are skipped; release CR still reaches `Released=True`
4. `advisory.url` is present in second release status (populated from filter result,
not from `create-advisory`)

## Setup

### Dependencies
* GitHub repo: https://github.com/hacbs-release-tests/e2e-base
* GitHub personal access token (classic) for above repo with **admin:repo_hook**,
**delete_repo**, **repo** scopes.
* The password to the vault files. (Contact a member of the Release team should you
want to run this test suite.)
* Access to the target cluster and tenant and managed namespaces
* **Cluster:** stg-rh01 (staging cluster)
* **Tenant Namespace:** `dev-release-team-tenant` (local) or `rhtap-release-2-tenant` (PaC)
* **Managed Namespace:** `managed-release-team-tenant`

### Required Environment Variables
- `GITHUB_TOKEN` - GitHub personal access token
- `VAULT_PASSWORD_FILE` - Path to file containing ansible vault password
- `RELEASE_CATALOG_GIT_URL` - Release service catalog URL for the RPA
- `RELEASE_CATALOG_GIT_REVISION` - Release service catalog revision for the RPA

### Optional Environment Variables
- `KUBECONFIG` - Kubeconfig file for cluster access
- `LARGE_SNAPSHOT_COMPONENT_COUNT` - Number of components in snapshot (default: 1)
- `LARGE_SNAPSHOT_TIMEOUT` - Pipeline timeout (default: 2h0m0s)

### Test Properties
#### [test.env](test.env)
- Contains resource names and configuration values for testing.
- Uses a single-component pre-built snapshot to minimize first release duration.

#### [test.sh](test.sh)
- Overrides standard build functions to skip builds and use pre-built images.
- Implements the two-phase idempotent test: first release then second release.
- Verifies all acceptance criteria post second release.

### Test Functions
#### [lib/test-functions.sh](../lib/test-functions.sh)
- Reusable functions for tests.

### Secrets
- Secrets are stored in ansible vault files (symlinked from `rh-advisories-large-snapshot`):
- [vault/managed-secrets.yaml](vault/managed-secrets.yaml)
- [vault/tenant-secrets.yaml](vault/tenant-secrets.yaml)

## Acceptance Criteria

- Second release: all major tasks (`create-advisory`, `push-snapshot`, `verify-conforma`,
`rh-sign-image`, etc.) appear in `skippedTasks`
- `advisory.url` present in second release `status.artifacts` (written by
`update-cr-status-skipped` from filter result, not `create-advisory`)

## Running the test

For local testing:

```shell
../run-test.sh rh-advisories-idempotent
```

**Note:** This test runs two full releases sequentially. The first release takes
approximately 1-2 hours in staging; the second release completes quickly once the
filter detects the existing advisory.

### Debugging

Use `--skip-cleanup` to preserve resources after the test:

```shell
../run-test.sh rh-advisories-idempotent --skip-cleanup
```

### Maintenance

To update secrets:

```shell
ansible-vault decrypt vault/tenant-secrets.yaml --output "/tmp/tenant-secrets.yaml" \
--vault-password-file <vault password file>
vi /tmp/tenant-secrets.yaml
ansible-vault encrypt /tmp/tenant-secrets.yaml --output "vault/tenant-secrets.yaml" \
--vault-password-file <vault password file>
rm /tmp/tenant-secrets.yaml
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
kind: Kustomization
apiVersion: kustomize.config.k8s.io/v1beta1

namespace: ${managed_namespace}
resources:
- sa.yaml
- sa-rolebinding.yaml
- rpa.yaml
- ec-policy.yaml
- secrets/managed-secrets.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
---
apiVersion: appstudio.redhat.com/v1alpha1
kind: ReleasePlanAdmission
metadata:
name: ${release_plan_admission_name}
labels:
originating-tool: "${originating_tool}"
spec:
applications:
- ${application_name}
data:
jira:
jiraAdvisorySecret: advisory-jira-secret-${component_name}
cgw:
cgwSecret: publish-to-cgw-secret-${component_name}
atlas:
server: stage
atlas-sso-secret-name: atlas-staging-sso-secret-${component_name}
atlas-retry-aws-secret-name: atlas-retry-s3-staging-secret-${component_name}
pyxis:
server: stage
secret: pyxis-${component_name}
sign:
configMapName: "hacbs-signing-pipeline-config-staging-e2e-pq"
cosignSecretName: "konflux-cosign-signing-stage-${component_name}"
mapping:
defaults:
tags:
- latest
pushSourceContainer: true
components:
- name: ${component_name}
repositories:
- url: quay.io/redhat-pending/rhtap----rh-advisories-component
releaseNotes:
product_id:
- 999
product_name: Red Hat Comp2
product_version: '1.0.1'
cpe: 'cpe:/a:redhat:comp2:1::appstream'
product_stream: comp2-1.0
origin: ${tenant_namespace}
pipeline:
pipelineRef:
params:
- name: url
value: "${RELEASE_CATALOG_GIT_URL}"
- name: revision
value: "${RELEASE_CATALOG_GIT_REVISION}"
- name: pathInRepo
value: pipelines/managed/rh-advisories/rh-advisories.yaml
resolver: git
serviceAccountName: ${managed_sa_name}
timeouts:
pipeline: 4h0m0s
tasks: 4h0m0s
policy: standard-${component_name}
Comment thread
mmalina marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: ${tenant_namespace}
resources:
- application.yaml
- component.yaml
- sa.yaml
- sa-rolebinding.yaml
- rp.yaml
- secrets/tenant-secrets.yaml
33 changes: 33 additions & 0 deletions integration-tests/rh-advisories-idempotent/test.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
uuid=${uuid:-"$(openssl rand -hex 4)"}
# make sure uuid is 8 chars long
uuid="${uuid:0:8}"

export originating_tool="rh-advisories-idempotent-test"

# Namespaces
export tenant_namespace=dev-release-team-tenant
#
## Since this is a test that requires internal services,
## this name should not change.
#
export managed_namespace=managed-release-team-tenant

export application_name=rh-adv-idem-app-${uuid}
export component_type=rh-adv-idem
export component_name=${component_type}-${uuid}
export component_branch=${component_name}
## do not change this. it is a known branch created by Konflux
export appstudio_component_branch="appstudio-${component_name}"

export component_github_org=hacbs-release-tests
export component_base_branch=collectors-base
export component_repo_name=${component_github_org}/${component_name}
export component_base_repo_name=${component_github_org}/e2e-base
export component_git_url=https://github.com/$component_repo_name

export tenant_collector_sa_name=${component_type}-collector-sa-${uuid}
export tenant_sa_name=${component_type}-tenant-sa-${uuid}
export release_plan_name=${component_type}-rp-${uuid}

export managed_sa_name=${component_type}-sa-${uuid}
export release_plan_admission_name=${component_type}-rpa-${uuid}
Loading
Loading