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
2 changes: 2 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
plugins
!plugins/auth-backend-module-oidc-provider
!plugins/dynamic-plugins-info-backend
!plugins/kuadrant
!plugins/kuadrant-backend
!plugins/licensed-users-info-backend
!plugins/scalprum-backend
*.local.yaml
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ jobs:
- name: Setup RBAC and Kuadrant on kind cluster
run: |
cd kuadrant-dev-setup
kubectl apply -f rbac/rhdh-rbac.yaml
kubectl apply -f rbac/rhdh-cluster-role.yaml -f rbac/rhdh-rbac.yaml
./scripts/kube-env-setup.sh
make kuadrant-install CLUSTER_NAME=kuadrant-test
make demo-install
Expand Down
99 changes: 99 additions & 0 deletions .github/workflows/e2e-dynamic.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
name: E2E (dynamic plugins)

on:
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
OINC_VERSION: v0.3.1
OCP_VERSION: "4.21"
KUADRANT_VERSION: "1.5.1"
RHDH_BASE_IMAGE: quay.io/rhdh-community/rhdh:1.10
RHDH_IMAGE_REPOSITORY: localhost/kuadrant-rhdh-e2e
RHDH_IMAGE_TAG: ci
RHDH_CHART_VERSION: "6.2.2"
RHDH_URL: http://rhdh.localhost:9080
DEX_URL: http://dex.localhost:9080

jobs:
e2e-rhdh-dynamic:
runs-on: ubuntu-latest
timeout-minutes: 90

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Free disk space
run: |
sudo rm -rf /usr/share/dotnet /opt/ghc /usr/local/lib/android \
/opt/hostedtoolcache/CodeQL /usr/local/share/boost || true
df -h /

- name: Configure local routes
run: |
echo "127.0.0.1 rhdh.localhost" | sudo tee -a /etc/hosts
echo "127.0.0.1 dex.localhost" | sudo tee -a /etc/hosts

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version-file: .nvmrc

- name: Enable Corepack
run: corepack enable

- name: Install oinc
run: |
curl -fsSL -o "$RUNNER_TEMP/oinc" \
"https://github.com/jasonmadigan/oinc/releases/download/${OINC_VERSION}/oinc-linux-amd64"
chmod +x "$RUNNER_TEMP/oinc"
sudo mv "$RUNNER_TEMP/oinc" /usr/local/bin/oinc

- name: Install Helm
uses: azure/setup-helm@v4

- name: Install e2e dependencies
run: make e2e-deps PLAYWRIGHT_INSTALL_ARGS=--with-deps

- name: Build and start dynamic RHDH
run: make dynamic-up

- name: Run e2e tests
run: make e2e-specs

- name: RHDH diagnostics on failure
if: failure()
run: |
kubectl -n rhdh get pods,route,svc || true
kubectl -n rhdh logs -l app.kubernetes.io/component=backstage --all-containers --tail=300 || true
kubectl -n kuadrant-system get pods -o wide || true
kubectl -n localhost get pods || true
kubectl -n localhost logs deployment/dex --tail=100 || true
curl -fsS "$DEX_URL/.well-known/openid-configuration" || true

- name: Upload test report
if: always()
uses: actions/upload-artifact@v4
with:
name: playwright-report-dynamic
path: e2e-tests/playwright-report/
retention-days: 7

- name: Upload test artifacts
if: failure()
uses: actions/upload-artifact@v4
with:
name: playwright-artifacts-dynamic
path: e2e-tests/test-results/
retention-days: 7

- name: Teardown oinc cluster
if: always()
run: make teardown
40 changes: 31 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,9 +120,27 @@ yarn test # run kuadrant e2e tests
yarn test:smoke # run smoke tests only
```

Tests available:
- `kuadrant-plugin.spec.ts` - basic navigation and rendering tests
- `kuadrant-rbac.spec.ts` - comprehensive RBAC permission tests covering all personas
**Dynamic-plugin E2E (manually dispatched in CI):**
```bash
make e2e-dynamic # build, bake, boot oinc, run the specs, tear down
make preflight # check required tooling, change nothing
```
Needs oinc (pinned version), docker, helm, kubectl, curl, python3 and node/yarn, and
installs none of them; it does run `yarn install` and `playwright install chromium`, as
CI does. Cluster is left up on failure for inspection.

For manual testing, run the same phases without the one-shot teardown:
```bash
make dynamic-up # build and leave the RHDH environment running
make e2e-deps # install Playwright locally (once)
make e2e-specs # run the specs against it (repeatable, no rebuild)
make teardown # delete the cluster (no-op if there is none)
```
`dynamic-up` leaves RHDH at `http://rhdh.localhost:9080` and always rebuilds the image.
It signs in through dex with the same five personas as `yarn dev`, so the whole spec set
runs there. It deliberately skips Playwright installation for browser-only manual
testing. After `e2e-deps`, `e2e-specs` forwards `PLAYWRIGHT_ARGS` and needs no rebuild.
See [docs/e2e-testing.md](docs/e2e-testing.md) and [docs/oinc.md](docs/oinc.md).

### Linting and Formatting
```bash
Expand All @@ -138,12 +156,16 @@ yarn export-dynamic -- -- --dev # export all dynamic plugins for local dev
```

### Testing Different Roles
```bash
yarn user:consumer # switch to API Consumer
yarn user:owner # switch to API Owner
yarn user:default # restore default permissions
```
After switching roles, restart with `yarn dev`.

`yarn dev` starts a local dex container on `:5556`; `make dynamic-up` deploys dex on the
oinc cluster from the same files. Either way, sign in through the dex quick-login picker
as one of five personas: `admin@kuadrant.local`, `owner1@`, `owner2@`, `consumer1@`,
`consumer2@` (passwords match usernames). Sign out and back in to switch.

Personas live in [`kuadrant-dev-setup/dex/config.yaml`](kuadrant-dev-setup/dex/config.yaml)
and [`catalog-entities/kuadrant-users.yaml`](catalog-entities/kuadrant-users.yaml); their
group membership maps to roles in [`rbac-policy.csv`](rbac-policy.csv). Adding a persona
is a one-file edit that both environments pick up.

## Testing Infrastructure

Expand Down
89 changes: 89 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
.PHONY: help preflight dynamic-build e2e-deps dynamic-cluster dynamic-up \
e2e-specs e2e-dynamic teardown

MAKEFLAGS += --no-print-directory

OINC_VERSION ?= v0.3.1
OCP_VERSION ?= 4.21
KUADRANT_VERSION ?= 1.5.1
RHDH_BASE_IMAGE ?= quay.io/rhdh-community/rhdh:1.10
RHDH_IMAGE_REPOSITORY ?= localhost/kuadrant-rhdh-e2e
RHDH_IMAGE_TAG ?= ci
RHDH_CHART_VERSION ?= 6.2.2
RHDH_URL ?= http://rhdh.localhost:9080
DEX_URL ?= http://dex.localhost:9080
PLAYWRIGHT_INSTALL_ARGS ?=
PLAYWRIGHT_ARGS ?=

RHDH_IMAGE = $(RHDH_IMAGE_REPOSITORY):$(RHDH_IMAGE_TAG)

export OCP_VERSION KUADRANT_VERSION RHDH_BASE_IMAGE RHDH_IMAGE_REPOSITORY
export RHDH_IMAGE_TAG RHDH_CHART_VERSION RHDH_URL DEX_URL

help:
@echo "Dynamic-plugin RHDH testing:"
@echo " make dynamic-up build and start RHDH for manual testing"
@echo " make e2e-deps install the Playwright test dependencies"
@echo " make e2e-specs run the full e2e suite against a running RHDH"
@echo " make e2e-dynamic run the full build, test and teardown path"
@echo " make teardown delete the oinc cluster"
@echo ""
@echo "Override versions and image settings on the make command line."

preflight:
@for tool in node yarn helm docker kubectl curl python3 oinc; do \
command -v $$tool >/dev/null 2>&1 || { echo "error: '$$tool' not found on PATH"; exit 1; }; \
done
@docker info >/dev/null 2>&1 || { echo "error: docker is not running"; exit 1; }
@version=$$(oinc version 2>/dev/null | grep -o 'v[0-9][^[:space:]]*' | head -1); \
case "$$version" in \
$(OINC_VERSION)|$(OINC_VERSION)-*) ;; \
*) echo "error: oinc $(OINC_VERSION) required, found $${version:-unknown}"; exit 1 ;; \
esac

dynamic-build:
yarn install --immutable
TURBO_FORCE=1 yarn build
cd plugins/kuadrant && yarn export-dynamic
cd plugins/kuadrant-backend && yarn export-dynamic
docker build -f e2e-tests/rhdh/Dockerfile \
--build-arg RHDH_BASE_IMAGE="$(RHDH_BASE_IMAGE)" \
-t "$(RHDH_IMAGE)" .

# Kept separate so manual browser testing does not install Playwright.
e2e-deps:
cd e2e-tests && PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 yarn install --immutable
cd e2e-tests && yarn playwright install $(PLAYWRIGHT_INSTALL_ARGS) chromium

dynamic-cluster:
./oinc/setup-cluster.sh
oinc load-image "$(RHDH_IMAGE)"
PLUGIN_SOURCE=baked ./oinc/setup-rhdh.sh
BASE_URL="$(RHDH_URL)" ./e2e-tests/rhdh/wait-for-catalog.sh

dynamic-up: preflight dynamic-build dynamic-cluster
@echo ""
@echo "RHDH is ready at $(RHDH_URL) (Dex: $(DEX_URL))."
@echo "Run 'make e2e-deps e2e-specs' for the tests, or 'make teardown' when done."

e2e-specs:
@curl -fsS -o /dev/null --max-time 10 "$(RHDH_URL)" || { \
echo "error: no RHDH responding at $(RHDH_URL); run 'make dynamic-up' first"; \
exit 1; \
}
cd e2e-tests && BASE_URL="$(RHDH_URL)" yarn test $(PLAYWRIGHT_ARGS)

teardown:
./oinc/teardown.sh

# Leave a failed local environment running for inspection. CI always invokes the
# teardown target in its own `if: always()` step.
e2e-dynamic: preflight dynamic-build e2e-deps
@if ! ( $(MAKE) dynamic-cluster && CI=1 $(MAKE) e2e-specs ); then \
echo "dynamic e2e failed; the cluster is available for inspection"; \
echo " RHDH: $(RHDH_URL)"; \
echo " logs: kubectl -n rhdh logs -l app.kubernetes.io/component=backstage --all-containers --tail=300"; \
echo " stop: make teardown"; \
exit 1; \
fi
@$(MAKE) teardown
17 changes: 10 additions & 7 deletions docs/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

## Workflows

Three GitHub Actions workflows live in `.github/workflows/`:
GitHub Actions workflows live in `.github/workflows/`:

| Workflow | Trigger | Purpose |
| ---------------------------- | ------------------------------------------- | ---------------------------------------------- |
| `ci.yml` | PRs, merge queue, pushes to main, or manual | Build, lint, type-check, unit tests, e2e tests |
| `publish.yml` | GitHub Release published, or manual | Publish packages to npm |
| `contributor-governance.yml` | Issues and PRs | Run the shared contributor-governance checks |
| Workflow | Trigger | Purpose |
| ---------------------------- | ------------------------------------------- | ------------------------------------------------------------ |
| `ci.yml` | PRs, merge queue, pushes to main, or manual | Build, lint, type-check, unit tests, static-plugin e2e tests |
| `e2e-dynamic.yml` | Manual (`workflow_dispatch`) | Full e2e suite against dynamic plugins in RHDH |
| `publish.yml` | GitHub Release published, or manual | Publish packages to npm |
| `contributor-governance.yml` | Issues and PRs | Run the shared contributor-governance checks |

## Release Flow

Expand Down Expand Up @@ -85,10 +86,12 @@ before publication. Without this step, the frontend would be missing
- **build-validate**: Lint, prettier, type-check, build, verify output bundles exist
- **export-plugins**: Build and verify the frontend and backend dynamic exports
- **unittests**: Backend and frontend unit tests
- **e2e-tests**: Spins up a kind cluster with Kuadrant, starts Backstage, runs Playwright tests
- **e2e-tests**: Spins up a kind cluster with Kuadrant, starts Backstage with the plugins statically linked, and runs Playwright

E2e test artifacts (Playwright report, videos on failure) are uploaded and retained for 7 days.

The static path remains the default required PR check. The dynamic-plugin workflow is manual-only because it also builds an RHDH image and boots an oinc cluster. It runs the same full Playwright suite against the current branch's exported dynamic plugins. Its build and cluster commands are shared with the local Make targets; see [E2E Testing](e2e-testing.md#running-against-rhdh-dynamic-plugins).

## npm Trusted Publishing

`publish.yml` uses npm's trusted publishing (`--provenance` flag) with GitHub's OIDC tokens (`id-token: write` permission). No npm tokens are stored as secrets.
48 changes: 47 additions & 1 deletion docs/e2e-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@ yarn test --grep "Happy Path" # specific test suite
yarn test --grep "permissions matrix" # RBAC tests only
```

## Running against RHDH (dynamic plugins)

The required CI job above remains the static-plugin path. The separate `E2E (dynamic plugins)` workflow is manually dispatched and runs the same full suite against the current branch's `export-dynamic` output in RHDH on oinc.

The root Makefile is the shared driver for CI and local use. For a one-shot local run:

```bash
make e2e-dynamic
```

This builds and exports both plugins, bakes them into an RHDH image, creates the oinc cluster, runs the suite, and tears down on success. A failed run leaves the cluster up for inspection.

For manual UI testing or repeated spec runs:

```bash
make dynamic-up # build and leave RHDH running
make e2e-deps # needed once before running Playwright locally
make e2e-specs # repeat without rebuilding RHDH
make teardown
```

`dynamic-up` deliberately does not install Playwright. RHDH is available at `http://rhdh.localhost:9080`; that `.localhost` origin keeps Web Crypto and clipboard APIs available over HTTP. Both RHDH and `yarn dev` authenticate through Dex with the same personas. See [oinc Development Environment](oinc.md) for the cluster, image, authentication, and version details.

The Make targets require oinc v0.3.1, Docker, Helm, kubectl, curl, Python 3, Node, and Yarn. They install project dependencies but do not install those tools. Version and image defaults can be overridden on the command line, for example:

```bash
make dynamic-up KUADRANT_VERSION=1.5.1 RHDH_IMAGE_TAG=my-test
make e2e-specs PLAYWRIGHT_ARGS="--grep 'permissions matrix'"
```

## Key Principles

### 1. Tests verify real behaviour
Expand Down Expand Up @@ -137,11 +167,27 @@ npx playwright show-trace test-results/.../trace.zip

## Test Users

Tests use Dex authentication with these users:
Tests use Dex authentication with these users in both environments:

- `admin@kuadrant.local` - full permissions
- `owner1@kuadrant.local` - API owner (can manage own APIs)
- `owner2@kuadrant.local` - API owner (for ownership isolation tests)
- `consumer1@kuadrant.local` - API consumer (can request access)
- `consumer2@kuadrant.local` - API consumer (for isolation tests)

Passwords match the username local part. Personas are defined in `kuadrant-dev-setup/dex/config.yaml`; their catalog users and group membership are in `catalog-entities/kuadrant-users.yaml`, with roles mapped in `rbac-policy.csv`.

## Runtime guards

Kuadrant specs import `test` and `expect` from `playwright/fixtures/test.ts`. The fixture fails a test that sees a Kuadrant backend 5xx, an uncaught page exception, or an unexpected console error. This prevents an empty-state assertion from passing over a failed fetch.

Tests that intentionally stub an error response opt out only for that scope:

```typescript
test.describe("simulated backend failures", () => {
test.use({ allowExpectedErrors: true });
});
```

## Adding testids

Expand Down
4 changes: 4 additions & 0 deletions docs/kuadrant-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ When OIDC is detected, the UI renders an `OidcProviderCard` component that shows

See [`plugins/kuadrant/src/components/OidcProviderCard/OidcProviderCard.tsx`](../plugins/kuadrant/src/components/OidcProviderCard/OidcProviderCard.tsx) for the card implementation.

### API Keys Tab and Card Gating

The entity provider emits `kuadrant.io/auth-apikey: "true"` only when an API-key scheme is discovered. OIDC-only products omit it. This is intentionally presence-based: RHDH dynamic-plugin `hasAnnotation` conditions do not inspect the value, so emitting `"false"` would still show the API Keys tab and overview card. Static mode's `=== "true"` check remains compatible with an absent annotation.

## PublishStatus for APIProducts

APIProducts have a Draft/Published workflow:
Expand Down
Loading
Loading