Skip to content
Open
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 .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tools/triage/triage binary -diff
91 changes: 85 additions & 6 deletions .tekton/pulp-deploy-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -797,31 +797,110 @@ spec:
cmd_prefix bash -c "HOME=/tmp/home pip3 install -r /tmp/unittest_requirements.txt -r /tmp/functest_requirements.txt"
# Because we pass the path to pytest -o cache_dir=/tmp/home/.cache/pytest_cache, pulpcore-manager must be in the same dir
cmd_prefix bash -c "ln -s /usr/local/lib/pulp/bin/pulpcore-manager /tmp/home/.local/bin/pulpcore-manager || /bin/true"

# === triage test impact analysis ===
TRIAGE_OK=true

# Check for [full-test] override in SNAPSHOT
if echo '$(params.SNAPSHOT)' | grep -qi 'full-test'; then
echo "triage: [full-test] marker found, running all tests"
TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
cmd_prefix bash -c "mkdir -p /tmp/home/.triage && curl -fsS -o /tmp/home/.triage/openapi.json http://localhost:8000/api/pulp/api/v3/docs/api.json" || TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
cmd_prefix bash -c "HOME=/tmp/home django-admin triage_urls --output /tmp/home/.triage/urls.json" || TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
cmd_prefix bash -c "HOME=/tmp/home /src/tools/triage/triage discover --project-root /src --python python3" || TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
cmd_prefix bash -c "HOME=/tmp/home /src/tools/triage/triage map --project-root /src --force" || TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
cmd_prefix bash -c "HOME=/tmp/home /src/tools/triage/triage select --diff /src/.triage/pr.diff --json --pyargs" > /tmp/select.json || TRIAGE_OK=false
fi

if [ "$TRIAGE_OK" = "true" ]; then
if ! python3 -c "import json; json.load(open('/tmp/select.json'))" 2>/dev/null; then
echo "triage: invalid select.json, falling back"
TRIAGE_OK=false
fi
fi

if [ "$TRIAGE_OK" = "false" ]; then
echo "triage: running full test suite"
else
echo "triage: $(python3 -c "import json; d=json.load(open('/tmp/select.json')); print(f'selected {d[\"tests_selected\"]}/{d[\"tests_total\"]} tests, plugins: {d[\"plugins_affected\"]}')")"
fi
# === end triage ===

echo "CURL OUTPUT"
curl https://env-${NS}.apps.crc-eph.r9lp.p1.openshiftapps.com/api/pulp-content/default/
echo "ROUTES"
oc_wrapper get route
set +e
# Only testing test_download_content because it is a very thorough test that tests that all the components of pulp can work
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --suppress-no-test-exit-code --pyargs pulp_rpm.tests.functional -m parallel -n 8 -k 'test_download_content' --junitxml=/tmp/home/junit-pulp-parallel.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulp_rpm' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --suppress-no-test-exit-code --pyargs pulp_rpm.tests.functional -m parallel -n 8 -k 'test_download_content' --junitxml=/tmp/home/junit-pulp-parallel.xml" || debug_and_fail
else
echo "triage: skipping pulp_rpm tests (plugin not affected)"
fi
# Never test test_package_manager_consume because they require sudo
# Do not test test_domain_create because it requires more than 2GB of RAM
# Only testing test_download_policies because they are very thorough tests that test that all the components of pulp can work
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_rpm.tests.functional -m 'not parallel' -k 'test_download_policies' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulp_rpm' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_rpm.tests.functional -m 'not parallel' -k 'test_download_policies' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
else
echo "triage: skipping pulp_rpm tests (plugin not affected)"
fi

# Run the jq header auth test
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulpcore.tests.functional -m 'parallel' -n 8 -k 'test_jq_header_remote_auth' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulpcore' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulpcore.tests.functional -m 'parallel' -n 8 -k 'test_jq_header_remote_auth' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
else
echo "triage: skipping pulpcore tests (plugin not affected)"
fi

### END Adapted from ./.github/workflows/scripts/script.sh

# Run pulp_maven functional tests
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_maven.tests.functional.api.test_download_content --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulp_maven' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_maven.tests.functional.api.test_download_content --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
else
echo "triage: skipping pulp_maven tests (plugin not affected)"
fi

# Run pulp_npm functional tests
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_npm.tests.functional -k 'test_pull_through_install' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulp_npm' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_npm.tests.functional -k 'test_pull_through_install' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
else
echo "triage: skipping pulp_npm tests (plugin not affected)"
fi

# Run pulp_service functional tests
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_service.tests.functional -m 'not parallel' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
if [ "$TRIAGE_OK" = "false" ] || python3 -c "import json,sys; sys.exit(0 if 'pulp_service' in json.load(open('/tmp/select.json')).get('plugins_affected',[]) else 1)" 2>/dev/null; then
if [ "$TRIAGE_OK" = "true" ]; then
NODEIDS=$(python3 -c "import json; d=json.load(open('/tmp/select.json')); [print(t) for t in d['tests_to_run'] if 'pulp_service' in t]" 2>/dev/null | tr '\n' ' ')
NEW_TESTS=$(python3 -c "import json; d=json.load(open('/tmp/select.json')); [print(t) for t in d.get('new_test_files',[])]" 2>/dev/null | tr '\n' ' ')
ALL_TESTS="$NODEIDS $NEW_TESTS"
if [ -n "$(echo $ALL_TESTS | tr -d ' ')" ]; then
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs $ALL_TESTS --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
else
echo "triage: no pulp_service tests to run"
fi
else
cmd_prefix bash -c "HOME=/tmp/home PYTHONPATH=/tmp/home/.local/lib/python3.11/site-packages/ XDG_CONFIG_HOME=/tmp/home/.config API_PROTOCOL=http API_HOST=pulp-api API_PORT=8000 ADMIN_USERNAME=admin ADMIN_PASSWORD=$PASSWORD /tmp/home/.local/bin/pytest -o cache_dir=/tmp/home/.cache/pytest_cache -v -r sx --color=yes --pyargs pulp_service.tests.functional -m 'not parallel' --junitxml=/tmp/home/junit-pulp-serial.xml" || debug_and_fail
fi
else
echo "triage: skipping pulp_service tests (plugin not affected)"
fi

- name: push-api-json-files-to-pulp
when:
Expand Down
22 changes: 22 additions & 0 deletions .tekton/pulp-pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,27 @@ spec:
workspace: workspace
- name: basic-auth
workspace: git-auth
- name: triage-diff
runAfter:
- clone-repository
taskSpec:
steps:
- name: generate-diff
image: registry.access.redhat.com/ubi9/ubi-minimal:latest
script: |
#!/bin/bash
set -ex
cd $(workspaces.source.path)/source
git fetch origin main --depth=1 || true
mkdir -p .triage
git diff origin/main...HEAD -- . > .triage/pr.diff 2>/dev/null || echo "" > .triage/pr.diff
echo "triage: diff is $(wc -l < .triage/pr.diff) lines"
workspaces:
- name: source
workspace: workspace
workspaces:
- name: source
workspace: workspace
- name: prefetch-dependencies
params:
- name: input
Expand Down Expand Up @@ -225,6 +246,7 @@ spec:
- name: NO_PROXY
value: $(tasks.init.results.no-proxy)
runAfter:
- triage-diff
- prefetch-dependencies
taskRef:
params:
Expand Down
Empty file.
Empty file.
87 changes: 87 additions & 0 deletions pulp_service/pulp_service/management/commands/triage_urls.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
"""
Django management command to generate .triage/urls.json.

Run from within a Pulp Django environment:

django-admin triage_urls --output .triage/urls.json
"""

import json
import sys

from django.core.management.base import BaseCommand
from django.urls import URLPattern, URLResolver, get_resolver


class Command(BaseCommand):
help = "Export URL routing map for triage test impact analysis"

def add_arguments(self, parser):
parser.add_argument(
"--output",
type=str,
default=None,
help="Output file path (default: stdout)",
)

def handle(self, *args, **options):
resolver = get_resolver()
routes = []

self._collect_routes(resolver, "", routes)
routes.sort(key=lambda route: route["pattern"])

output = json.dumps(routes, indent=2)
if options["output"]:
with open(options["output"], "w") as output_file:
output_file.write(output)
self.stderr.write(f"Wrote {len(routes)} routes to {options['output']}")
else:
sys.stdout.write(output)

def _collect_routes(self, resolver, prefix, routes):
for pattern in resolver.url_patterns:
if isinstance(pattern, URLResolver):
new_prefix = prefix + str(pattern.pattern)
self._collect_routes(pattern, new_prefix, routes)
elif isinstance(pattern, URLPattern):
route = self._extract_route(pattern, prefix)
if route:
routes.append(route)

def _extract_route(self, pattern, prefix):
full_pattern = prefix + str(pattern.pattern)
callback = pattern.callback

viewset_class = _resolve_viewset_class(callback)
if viewset_class is None:
return None

dotted_path = f"{viewset_class.__module__}.{viewset_class.__qualname__}"
actions = _resolve_actions(callback)

return {
"pattern": "/" + full_pattern.lstrip("/"),
"viewset": dotted_path,
"actions": sorted(set(actions)),
}


def _resolve_viewset_class(callback):
if hasattr(callback, "cls"):
return callback.cls
if hasattr(callback, "view_class"):
return callback.view_class
if hasattr(callback, "initkwargs") and "cls" in callback.initkwargs:
return callback.initkwargs["cls"]
return None


def _resolve_actions(callback):
if hasattr(callback, "actions"):
return list(callback.actions.values())
if hasattr(callback, "initkwargs"):
actions_map = callback.initkwargs.get("actions", {})
if isinstance(actions_map, dict):
return list(actions_map.values())
return []
18 changes: 18 additions & 0 deletions tools/triage/BUILD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# triage binary

Pre-built from: https://github.com/pulp/agent-project (triage/ directory)

## Rebuild

```bash
cd agent-project/triage
cargo build --release --target x86_64-unknown-linux-gnu
cp target/release/triage /path/to/pulp-service/tools/triage/triage
cd /path/to/pulp-service/tools/triage && sha256sum triage > triage.sha256
```

## Verify

```bash
cd tools/triage && sha256sum -c triage.sha256
```
Binary file added tools/triage/triage
Binary file not shown.
1 change: 1 addition & 0 deletions tools/triage/triage.sha256
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
26d884a4fbd20612e20f66bb17b8301aa20ce776b1ffaa8c7826533b3464cc9f triage
Loading