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
96 changes: 34 additions & 62 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -104,81 +104,53 @@ jobs:
token: ${{ secrets.CLONE_REPO_TOKEN }}
path: './pdp-tester'

# Start k3d cluster for Kubernetes-based pdp-tester
- name: Start k3d cluster
uses: AbsaOSS/k3d-action@v2.4.0
- name: Set up Python
uses: actions/setup-python@v5
with:
cluster-name: pdp-tester
args: --k3s-arg "--disable=traefik@server:0"

# Import PDP image into k3d with 'next' tag (locally built)
- name: Import PDP image into k3d
run: k3d image import permitio/pdp-v2:next -c pdp-tester
python-version: "3.12"

# Build pdp-tester image and import into k3d
- name: Build and import pdp-tester image
# Docker runtime backend: launch the PDP as a local container via aiodocker

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[MEDIUM] Unrelated pdp-tester CI migration bundled into a CVE-patch PR (duplicates open PR #319)

Problem: This PR's stated purpose is patching CVEs in the pdp-v2 image (the requirements.txt cryptography bump + the aioresponses test shim). The entire k3d -> Docker-runtime migration of the pdp-tester job (commit 75d974b) is unrelated to that goal and is the whole subject of a separate open PR, #319 (PER-15361). Both PRs edit .github/workflows/tests.yml, and the two copies have already diverged: #319 additionally adds timeout-minutes: 15 on the job, cache: "pip" on setup-python, and explicit non-zero-exit handling (... || status=$? then a later exit 1) so a tester crash that does not print test cases failed still fails the step. This copy has none of those. Whichever PR merges first forces a conflict on the other; if this (older) copy lands first, #319's hardening is at risk of being dropped or re-litigated during conflict resolution.

Suggestion: Drop the tests.yml change from this PR and let #319 own the pdp-tester migration, keeping this PR scoped to requirements.txt + horizon/tests/conftest.py. Alternatively, if this PR should carry the migration, close #319 and port its timeout-minutes / pip-cache / exit-status handling here so the better version is the one that lands.

# (no k3d/Helm/kubectl). This is the runtime the pdp-tester maintainers
# recommend for CI and sidesteps the AbsaOSS/k3d-action download breakage
# (its default k3d v5.4.6 has no checksums.txt, so install.sh now 404s).
- name: Install pdp-tester (docker extra)
working-directory: ./pdp-tester
run: |
docker build -t pdp-tester:ci .
k3d image import pdp-tester:ci -c pdp-tester
run: pip install -e ".[docker]"

# Create namespace and secrets
- name: Create secrets
env:
PERMIT_TOKEN: ${{ secrets.PDP_TESTER_API_KEY }}
run: |
kubectl create namespace pdp-tester || true
kubectl create secret generic pdp-tester-credentials \
-n pdp-tester \
--from-literal=token="${PERMIT_TOKEN}" \
--dry-run=client -o yaml | kubectl apply -f -

# Deploy pdp-tester via Helm with the "next" PDP image
- name: Deploy pdp-tester via Helm
- name: Run PDP tester against the local Docker PDP image
working-directory: ./pdp-tester
env:
TOKEN: ${{ secrets.PDP_TESTER_API_KEY }}
API_URL: https://permitio.api.stg.permit.io
# Test the locally built image (loaded above) instead of pulling from
# Docker Hub. LOCAL_IMAGE forces the runtime's local-only tag path, so
# only permitio/pdp-v2:next is launched.
LOCAL_IMAGE: permitio/pdp-v2
# Preserve the previous Helm startTimeout, and keep crashed containers
# around so the post-mortem step below can read their logs.
START_TIMEOUT: "180"
AUTO_REMOVE: "false"
run: |
helm install pdp-tester ./deploy/helm/pdp-tester \
--set mode=job \
--set permit.existingSecret=pdp-tester-credentials \
--set permit.apiUrl=https://permitio.api.stg.permit.io \
--set image.repository=pdp-tester \
--set image.tag=ci \
--set image.pullPolicy=Never \
--set pdp.image=permitio/pdp-v2 \
--set 'pdp.localTags[0]=next' \
--set 'pdp.includeTags=' \
--set tests.skipGenerate=true \
--set tests.startTimeout=180 \
--set namespace.create=false \
--set logJson=false

- name: Wait for Job completion
run: |
kubectl wait --for=condition=complete job/pdp-tester \
-n pdp-tester --timeout=600s

- name: Check test results
run: |
LOGS=$(kubectl logs job/pdp-tester -n pdp-tester)
echo "$LOGS" | tail -30
if echo "$LOGS" | grep -q "test cases failed"; then
set -o pipefail
python -m pdp_tester --docker --local --tag next --skip-generate 2>&1 | tee /tmp/tester.log
if grep -q "test cases failed" /tmp/tester.log; then
echo "::error::Some test cases failed!"
exit 1
fi

- name: Print tester logs
- name: Capture PDP container state and logs
if: always()
run: |
echo "=== PDP Tester logs ==="
kubectl logs job/pdp-tester -n pdp-tester --tail=200 || true
echo "=== PDP containers (all states) ==="
docker ps -a --filter "label=pdp-tester.permit.io/managed-by=pdp-tester" \
--format '{{.Names}}\t{{.Status}}' || true
echo ""
echo "=== PDP Pod logs ==="
kubectl logs -l pdp-tester.permit.io/managed-by=pdp-tester \
-n pdp-tester --tail=50 || true

- name: Teardown k3d cluster
if: always()
run: k3d cluster delete pdp-tester || true
echo "=== PDP container logs ==="
for c in $(docker ps -aq --filter "label=pdp-tester.permit.io/managed-by=pdp-tester"); do
name=$(docker inspect --format '{{.Name}}' "$c" | sed 's#^/##')
echo "--- ${name} ---"
docker logs "$c" 2>&1 | tail -200 || true
done

docker-scout:
runs-on: ubuntu-latest
Expand Down
40 changes: 40 additions & 0 deletions horizon/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Pytest configuration and compatibility shims for the horizon test-suite.

aioresponses (our HTTP mocking library) builds ``aiohttp.ClientResponse``
instances directly. aiohttp 3.14 made ``stream_writer`` a *required*
keyword-only argument of ``ClientResponse.__init__`` (it is only read for its
``output_size``), which the latest released aioresponses (0.7.9) does not pass,
so every mocked response raises::

TypeError: ClientResponse.__init__() missing 1 required keyword-only
argument: 'stream_writer'

We intentionally stay on the aiohttp 3.14.x line: the June 2026 security fixes
(e.g. GHSA-63hw-fmq6-xxg2 and the other advisories in that batch) landed in
3.14 and were not backported to 3.13.x, so pinning aiohttp down just to satisfy
aioresponses would reintroduce those CVEs into the shipped image. Instead we
Comment on lines +12 to +15
mirror the upstream aioresponses fix (PR #288, not yet released) by injecting a
dummy ``stream_writer``. This is a no-op on aiohttp < 3.14 and can be removed
once aioresponses publishes a release that includes PR #288.
"""

import inspect
from unittest.mock import Mock

import aioresponses.core as _aioresponses_core
from aiohttp.client_reqrep import ClientResponse as _ClientResponse

if "stream_writer" in inspect.signature(_ClientResponse.__init__).parameters:

class _StreamWriterCompatClientResponse(_ClientResponse):
"""ClientResponse that supplies aiohttp 3.14's required stream_writer."""

def __init__(self, *args, **kwargs):
if "stream_writer" not in kwargs:
kwargs["stream_writer"] = Mock(output_size=0)
super().__init__(*args, **kwargs)

# aioresponses._build_response falls back to this module-level name when the
# matcher has no explicit response_class, which is the case for all of our
# mocks. Patching it here routes every mocked response through the subclass.
_aioresponses_core.ClientResponse = _StreamWriterCompatClientResponse
5 changes: 4 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ httpx>=0.27.0,<1
# TODO: change to use re2 in the future, currently not supported in alpine due to c++ library issues
# google-re2 # use re2 instead of re for regex matching because it's simiplier and safer for user inputted regexes
protobuf>=6.33.5 # pinned to avoid CVE-2026-0994
cryptography>=46.0.5,<47 # pinned to avoid CVE-2026-26007
cryptography>=48.0.1,<49 # pinned to avoid GHSA-537c-gmf6-5ccf (and CVE-2026-26007)
# NOTE: starlette is capped <1 by opal-common/opal-client 0.9.6 (latest), so the
# starlette High CVEs (CVE-2026-54283, CVE-2026-48818; fixed in 1.1.0/1.3.1) cannot
# be resolved until OPAL relaxes its `starlette<1` bound. See PER-15358.
opal-common==0.9.6
opal-client==0.9.6
Loading