From 41b51471d112165d52631e63c62e7c762778fbef Mon Sep 17 00:00:00 2001 From: Preeti Wadhwani Date: Thu, 13 Aug 2026 16:16:27 +0530 Subject: [PATCH 1/3] docs(cost-management): add local dev setup guide Walks new contributors through Yarn workspace concepts, running the plugin locally (yarn start / start-app / start-backend), and what actually runs under the hood (Rspack dev server for frontend, plain Node process for backend). Co-authored-by: Cursor --- .../cost-management/docs/local-dev-setup.md | 238 ++++++++++++++++++ 1 file changed, 238 insertions(+) create mode 100644 workspaces/cost-management/docs/local-dev-setup.md diff --git a/workspaces/cost-management/docs/local-dev-setup.md b/workspaces/cost-management/docs/local-dev-setup.md new file mode 100644 index 00000000000..b68c3be649c --- /dev/null +++ b/workspaces/cost-management/docs/local-dev-setup.md @@ -0,0 +1,238 @@ +# Local Development Setup — Cost Management Plugin + +This guide walks a developer who is **new to Backstage and Red Hat Developer Hub (RHDH)** through setting up, running, and understanding the Cost Management plugin's local development environment. + +--- + +## Table of Contents + +- [Prerequisites](#prerequisites) +- [Understanding Yarn Workspaces](#understanding-yarn-workspaces) +- [Repository & Workspace Layout](#repository--workspace-layout) +- [Getting Started](#getting-started) +- [Understanding the Config Files](#understanding-the-config-files) +- [Running the Plugin Locally](#running-the-plugin-locally) +- [Verifying Your Setup](#verifying-your-setup) +- [RBAC in Local Development](#rbac-in-local-development) +- [Testing Against a Real RHDH Instance](#testing-against-a-real-rhdh-instance) +- [Common Commands Cheat Sheet](#common-commands-cheat-sheet) +- [Troubleshooting](#troubleshooting) +- [File Reference](#file-reference) + +--- + +## Prerequisites + +- **[Backstage](https://backstage.io/docs/overview/what-is-backstage)** — an open-source developer portal framework (originally by Spotify, now a CNCF Incubating project). You assemble an instance from a frontend app + backend app, each built from **plugins**. +- **[RHDH](https://docs.redhat.com/en/documentation/red_hat_developer_hub/1.9/html/about_red_hat_developer_hub/index)** — Red Hat's supported distribution of Backstage. In production, RHDH loads most plugins **dynamically** (pre-built OCI artifacts) instead of compiling them into the app — see [Testing Against a Real RHDH Instance](#testing-against-a-real-rhdh-instance). +- **[Yarn](https://yarnpkg.com/) 4.17.1** — the package manager used across this repo, pinned at the **root** `rhdh-plugins/package.json` + `.yarnrc.yml` (not in this workspace's own `package.json`, but inherited automatically since Yarn merges `.yarnrc.yml` from parent directories — as long as you clone the full monorepo per [Getting Started](#getting-started)). The feature that matters most is **[Workspaces](https://yarnpkg.com/features/workspaces)**: `plugins/cost-management-backend` is symlinked straight into `node_modules` for anything that depends on it — no publish step needed to test local changes. + + If `yarn` isn't already on your machine, enable [Corepack](https://nodejs.org/api/corepack.html) (ships with Node.js): `corepack enable`. To verify `yarn` is resolving through Corepack (rather than a separate global install): + + ```bash + cat "$(which yarn)" | grep -i corepack # should print a match + ``` + +- **Node.js 22 or 24** (`engines.node` in `package.json`). +- **Red Hat service account credentials** — the plugin talks to the real [Cost Management API](https://console.redhat.com/openshift/cost-management). You'll need a service account with the `Cost OpenShift Viewer` role ([create one here](https://console.redhat.com/iam/service-accounts/)) to see real data. You can still install/run without it — you just won't get data back. + +--- + +## Understanding Yarn Workspaces + +A **Yarn workspace** lets multiple packages in one repo be installed together, where packages that depend on each other locally are **symlinked** into `node_modules` instead of being fetched from npm. No publish step, no version bump — edit the source, and every dependent package sees the change immediately. + +This workspace declares its members in `package.json`: + +```json +"workspaces": { + "packages": ["packages/*", "plugins/*"] +} +``` + +Each plugin then depends on another using the `workspace:^` protocol instead of a normal semver range, e.g. `plugins/cost-management` depends on the shared common package: + +```json +"@red-hat-developer-hub/plugin-cost-management-common": "workspace:^" +``` + +After `yarn install`, that line becomes a real symlink on disk: + +``` +node_modules/@red-hat-developer-hub/plugin-cost-management-common -> ../../plugins/cost-management-common +``` + +The same thing happens for the throwaway dev shell: `packages/app` depends on `@red-hat-developer-hub/plugin-cost-management` via `workspace:^`, so `yarn start` always runs against your current, unpublished plugin source. + +**Why it's built this way:** Backstage plugins are meant to be independently published npm packages that any Backstage app (including real RHDH) can install. But to develop and test them, you need something to run them in — that's what `packages/app` + `packages/backend` are for. Yarn workspaces are the bridge: they let that disposable test harness consume your live plugin code directly, instead of forcing a publish just to test a change. + +One side effect worth knowing: dependencies are **hoisted** into a single shared `node_modules` at the workspace root (`workspaces/cost-management/node_modules`) rather than duplicated inside each package folder. So it's normal for `plugins/cost-management/`, `plugins/cost-management-backend/`, and `plugins/cost-management-common/` to have **no `node_modules` of their own** — Node resolves imports by walking up to the shared one. + +--- + +## Repository & Workspace Layout + +`rhdh-plugins` is a **monorepo of monorepos**. This is the one mental model worth internalizing: + +``` +rhdh-plugins/ ← outer monorepo (yarn@4.17.1, root package.json) +└── workspaces/ + ├── cost-management/ ← an independent Yarn workspace root (its own yarn.lock!) + │ ├── app-config.yaml ← base Backstage config (committed) + │ ├── app-config.local.yaml ← personal local overrides (git-ignored) + │ ├── packages/ + │ │ ├── app/ ← throwaway Backstage frontend, dev only + │ │ └── backend/ ← throwaway Backstage backend, dev only + │ ├── plugins/ + │ │ ├── cost-management/ ← published frontend plugin + │ │ ├── cost-management-backend/ ← published backend plugin + │ │ └── cost-management-common/ ← shared types, API clients, permissions + │ └── docs/ ← this file, plus rbac.md, dynamic-plugin.md + ├── orchestrator/ ← a different plugin, own workspace root + └── ... +``` + +**Critical distinction:** + +| Directory | Published to npm? | Purpose | +| ---------------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------- | +| `plugins/cost-management*` | **Yes** | The actual product code shipped to customers | +| `packages/app`, `packages/backend` | **No** (`private: true`) | Throwaway Backstage instance used _only_ to run the plugin locally. RHDH never uses this code. | + +If you're adding a feature, the answer is almost always `plugins/cost-management*`, never `packages/`. + +--- + +## Getting Started + +```bash +git clone https://github.com/redhat-developer/rhdh-plugins.git +cd rhdh-plugins + +# 1. Root install (repo-wide tooling) +yarn install + +# 2. Workspace install (the actual plugin dependencies) +cd workspaces/cost-management +yarn install +``` + +See [CONTRIBUTING.md](../../../CONTRIBUTING.md) for the full fork/branch/PR workflow. + +--- + +## Understanding the Config Files + +Backstage merges multiple YAML config files at startup (later files win on conflicts) — see [Backstage Configuration docs](https://backstage.io/docs/conf/). All paths below are relative to `workspaces/cost-management/`. + +- **`app-config.yaml`** — base config, committed to git. Safe defaults, no secrets. +- **`app-config.local.yaml`** — **git-ignored**, loaded automatically on top of `app-config.yaml` when present. This is where your `costManagement.clientId` / `clientSecret` and other personal secrets/overrides live. Every developer keeps their own copy; nobody's secrets get pushed. + + ```yaml + costManagement: + clientId: + clientSecret: + optimizationWorkflowId: 'patch-k8s-resource' + ``` + + > This file only affects the local dev shell (`packages/app` + `packages/backend`). It has **no effect on RHDH**. + +### The same filename means two different things + +| | `workspaces/cost-management/app-config.local.yaml` (this repo) | `rhdh-local/configs/app-config/app-config.local.yaml` (the `rhdh-local` repo) | +| -------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------------------------- | +| Scope | This plugin's throwaway dev shell | A full RHDH instance via Podman/Docker Compose | +| How the plugin loads | Compiled in as a source-level workspace dependency | Loaded as a **dynamic plugin** from an OCI image (like production) | +| When to use | Fast local iteration on plugin code | Verifying the packaged artifact behaves correctly inside real RHDH before it ships | + +Both need the same `costManagement.clientId` / `clientSecret` keys, but they're different files in different repos feeding different runtimes. + +**Want to explore more?** + +- [`rhdh-local`](https://github.com/redhat-developer/rhdh-local) — the repository referenced above, for reference +- [Demo recording: Testing Cost Management plugin with rhdh-local](https://drive.google.com/file/d/1d2Es7n8sAm8dRY0dIfVkFJFBW6asKaJk/view?usp=drive_web) — if interested + +--- + +## Running the Plugin Locally + +Run from `workspaces/cost-management/`: + +| Command | What it does | Use when... | +| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `yarn start` **(recommended, day-to-day default)** | Full dev app: frontend (`:3000`) + backend (`:7007`), with catalog, auth, RBAC, search | Default choice for local dev — you get the real sidebar/catalog/RBAC, so what you see matches how the plugin behaves inside an actual Backstage instance | +| `yarn start-app` / `yarn start-backend` | Just one half of the full dev app (`packages/app` or `packages/backend` alone) | You only need to restart/watch one side (e.g. backend logs are noisy, or you're only touching frontend code) while the other keeps running | +| `yarn start:dev` / `yarn start:fe-plugin` / `yarn start:be-plugin` | Isolated plugin dev servers via `createDevApp()` — no real sidebar, catalog, or RBAC | Rarely needed; only for very fast, isolated hot-reload iteration on a single component/route when you don't care about the surrounding app context (see [Troubleshooting](#troubleshooting) for the "missing sidebar" gotcha this causes) | + +> **In practice:** most day-to-day development in this plugin just uses `yarn start` (or `yarn start-app` / `yarn start-backend` individually) — that's what's documented and verified throughout the rest of this guide. The `start:dev`/`start:fe-plugin`/`start:be-plugin` trio is documented for completeness but isn't the typical workflow. + +**What's actually running under the hood:** `backstage-cli package start` launches a different server depending on the package's `backstage.role`. For `packages/app` (`role: frontend`), it starts an **[Rspack](https://rspack.dev/)** dev server (`RspackDevServer`) — a Rust-based, webpack-compatible bundler with hot module reload — serving the UI on `:3000`. For `packages/backend` (`role: backend`), it just runs `src/index.ts` directly as a **plain Node.js process** (TypeScript transpiled on the fly, no bundler), auto-restarting on file changes, listening on `:7007`. Curious to dig deeper? See the [Backstage CLI docs](https://backstage.io/docs/tooling/cli/build-system) and the [Rspack documentation](https://rspack.dev/). + +--- + +## Verifying Your Setup + +With `yarn start` running, open **http://localhost:3000**, sign in (guest or GitHub), and confirm: + +- **Cost Management** appears in the sidebar +- **Optimizations** and **OpenShift** tabs load +- If your `costManagement` credentials are valid, real data from console.redhat.com populates the pages (otherwise expect 401/403 — see [Troubleshooting](#troubleshooting)) + +--- + +## RBAC in Local Development + +The plugin doesn't ship an RBAC engine — it only consumes Backstage's abstract `PermissionsService`. Locally, `packages/backend/src/index.ts` explicitly adds `@backstage-community/plugin-rbac-backend`, reading policies from `policy.local.csv`. In RHDH production, RHDH provides its own RBAC backend instead — the plugin code is identical either way. + +Full permission/policy reference: [docs/rbac.md](./rbac.md). + +--- + +## Testing Against a Real RHDH Instance + +Once a change works in the local dev shell, validate it inside an actual RHDH instance, loading the plugin as a **dynamic plugin** (OCI image) the way a customer would: + +- **[`rhdh-local`](https://github.com/redhat-developer/rhdh-local)** — Podman/Docker Compose-based local RHDH stack +- **A real RHDH cluster** (Operator/Helm) — see [docs/dynamic-plugin.md](./dynamic-plugin.md) for the ConfigMap/Secret setup + +See it in action: [Demo recording: Testing Cost Management plugin with rhdh-local](https://drive.google.com/file/d/1d2Es7n8sAm8dRY0dIfVkFJFBW6asKaJk/view?usp=drive_web). + +--- + +## Common Commands Cheat Sheet + +Run from `workspaces/cost-management/`: + +| Command | What it does | +| --------------------------------------- | ----------------------------------------------------------------------------------------------------------- | +| `yarn install` | Install dependencies | +| `yarn start` | Run locally, full dev app **(recommended)** — see [Running the Plugin Locally](#running-the-plugin-locally) | +| `yarn start-app` / `yarn start-backend` | Run just the frontend or backend half of `yarn start` | +| `yarn test` / `yarn test:all` | Unit tests / with coverage | +| `yarn lint` / `yarn lint:all` | Lint changed files / everything | +| `yarn tsc` | Type-check | +| `yarn build:all` | Build everything in this workspace | +| `yarn changeset` | Create a changeset for your PR ([CONTRIBUTING.md](../../../CONTRIBUTING.md#creating-changesets)) | + +--- + +## Troubleshooting + +- **Node version error on `yarn start`** — check `node -v` against `engines.node` (`22 || 24`); use `nvm use`. +- **Sidebar doesn't show "Cost Management"** — you're probably on `yarn start:dev`, which doesn't render the real sidebar. Use `yarn start`. +- **401/403 or no data on OpenShift/Optimizations pages** — `costManagement.clientId`/`clientSecret` in `app-config.local.yaml` are missing/invalid, or the service account lacks `Cost OpenShift Viewer`. +- **Permission always `DENY` despite a correct-looking CSV rule** — check `policy.local.csv` for a duplicate row; a single duplicate anywhere silently rejects the whole file's reload. Run `sort policy.local.csv | uniq -d` to check. + +--- + +## File Reference + +| File | Purpose | +| ------------------------------------ | ------------------------------------------------- | +| `app-config.yaml` | Base Backstage config (committed) | +| `app-config.local.yaml` | Personal local overrides + secrets (git-ignored) | +| `policy.local.csv` | Local RBAC policy rules | +| `packages/app/`, `packages/backend/` | Throwaway dev shell (not shipped) | +| `plugins/cost-management*/` | Published plugin code (frontend, backend, common) | +| `docs/rbac.md` | Full RBAC permission and policy reference | +| `docs/dynamic-plugin.md` | Installing the plugin as an RHDH dynamic plugin | From 84b6fc83a1d38358e37d1c2fc598fc1adb720843 Mon Sep 17 00:00:00 2001 From: Preeti Wadhwani Date: Fri, 14 Aug 2026 11:50:39 +0530 Subject: [PATCH 2/3] docs(cost-management): add guide for testing plugin on rhdh-local Documents both the pre-install and Extensions Catalog installation flows for verifying the cost-management 2.2.0 plugin locally before deploying to a real RHDH cluster. Co-authored-by: Cursor --- .../testing-cost-management-on-rhdh-local.md | 332 ++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 workspaces/cost-management/docs/testing-cost-management-on-rhdh-local.md diff --git a/workspaces/cost-management/docs/testing-cost-management-on-rhdh-local.md b/workspaces/cost-management/docs/testing-cost-management-on-rhdh-local.md new file mode 100644 index 00000000000..6156cd84a94 --- /dev/null +++ b/workspaces/cost-management/docs/testing-cost-management-on-rhdh-local.md @@ -0,0 +1,332 @@ +# Testing Cost Management 2.2.0 on RHDH Local + +## Overview + +This guide covers how to run and test the cost-management dynamic plugin (v2.2.0) locally using the `rhdh-local` Compose-based setup. This is useful for verifying plugin behavior before deploying to a real RHDH cluster. + +There are two ways to install the plugin locally: + +1. **Pre-install via override file** — plugin is installed at startup (recommended for development) +2. **Install via Extensions Catalog UI** — plugin is browsed and installed from the marketplace UI (simulates the user experience) + +## Prerequisites + +- **Podman** or **Docker** with Compose support +- The `rhdh-local` repository checked out at `rhdh-local/` +- The `rhdh-plugin-export-overlays` repository checked out at `rhdh-plugin-export-overlays/` (sibling directory) +- Cost Management API credentials (`CM_CLIENT_ID`, `CM_CLIENT_SECRET`) if testing backend functionality + +## RHDH Image Version + +The cost-management 2.2.0 plugin is built against **Backstage 1.49.4**. The default RHDH image (`quay.io/rhdh-community/rhdh:1.9`) ships with Backstage ~1.36.x, which is significantly older. To ensure compatibility, use the `next` tag. + +Add to your `.env` file: + +```bash +RHDH_IMAGE=quay.io/rhdh-community/rhdh:next +``` + +Available image tags: + +| Tag | Description | Backstage Version | Use Case | +| ---------- | -------------------------- | ----------------- | ------------------------------------------------------------- | +| `1.9` | Current GA (stable) | ~1.36.x | Customer-facing compatibility testing | +| `next` | Nightly from `main` branch | ~1.49.x | Development/testing of plugins built against latest Backstage | +| `next-1.9` | Nightly patches for 1.9 | ~1.36.x | Testing 1.9 patches | +| `1.10` | **Not available yet** | N/A | Will be created when 1.10 goes GA | + +For more details on available images, see `rhdh-local/docs/rhdh-local-guide/container-image-guide.md`. + +## Setup + +### 1. Dynamic Plugins Configuration (Pre-install Method) + +The override file has been created at: + +``` +rhdh-local/configs/dynamic-plugins/dynamic-plugins.override.yaml +``` + +It configures both the frontend and backend cost-management plugins from the Quay registry: + +- **Frontend**: `oci://quay.io/redhat-resource-optimization/dynamic-plugins:2.2.0!red-hat-developer-hub-plugin-cost-management` +- **Backend**: `oci://quay.io/redhat-resource-optimization/dynamic-plugins:2.2.0!red-hat-developer-hub-plugin-cost-management-backend` + +The frontend plugin configuration includes: + +- Sidebar menu with "Cost management" parent and nested "Optimizations" and "OpenShift" items (using dot notation for `menuItems` keys) +- Custom icon (`CostManagementIconOutlined`) +- Two dynamic routes (`/cost-management/optimizations` and `/cost-management/openshift`) + +### 2. Environment Variables + +Create a `.env` file in the `rhdh-local/` root (or add to the existing one): + +```bash +# Use the next image to match plugin's Backstage version +RHDH_IMAGE=quay.io/rhdh-community/rhdh:next + +# Cost Management API credentials +CM_CLIENT_ID=your-client-id-here +CM_CLIENT_SECRET=your-client-secret-here +``` + +These are referenced by the backend plugin's `pluginConfig` via `${CM_CLIENT_ID}` and `${CM_CLIENT_SECRET}`. + +### 3. App Config + +The `configs/app-config/app-config.local.yaml` contains two important sections: + +**Cost Management backend config** — required for the backend plugin to authenticate with the Red Hat API: + +```yaml +costManagement: + clientId: ${CM_CLIENT_ID} + clientSecret: ${CM_CLIENT_SECRET} + optimizationWorkflowId: 'patch-k8s-resource' +``` + +This config block is required regardless of how you install the plugin (pre-install or Extensions Catalog). The `${CM_CLIENT_ID}` and `${CM_CLIENT_SECRET}` are substituted from your `.env` file at RHDH startup. + +**Catalog entities** — registers cost-management Plugin and Package entities for the Extensions Catalog: + +```yaml +# Cost Management entities (for local testing) +- type: file + target: /marketplace/catalog-entities/plugins/cost-management.yaml + rules: + - allow: [Plugin] +- type: file + target: /marketplace/catalog-entities/packages/cost-management/cost-management.yaml + rules: + - allow: [Package] +- type: file + target: /marketplace/catalog-entities/packages/cost-management/cost-management-backend.yaml + rules: + - allow: [Package] +``` + +These are bind-mounted from `rhdh-plugin-export-overlays` via `compose.override.yaml`. + +## Running + +### Start RHDH Local + +```bash +cd rhdh-local + +# Start the stack (install-dynamic-plugins runs first, then RHDH starts) +podman compose up -d + +# Or with Docker +docker compose up -d +``` + +The startup sequence: + +1. `install-dynamic-plugins` service runs `prepare-and-install-dynamic-plugins.sh` — detects the override file and uses it +2. Plugin OCI images are pulled from Quay and installed into the `dynamic-plugins-root` volume +3. `rhdh` service starts once plugins are installed, loading the generated `app-config.dynamic-plugins.yaml` + +### Verify Plugin Installation + +```bash +# Check logs for plugin loading +podman compose logs rhdh | grep -i "cost-management" + +# Check install logs +podman compose logs install-dynamic-plugins | grep -i "cost-management" +``` + +Look for messages like: + +``` +loaded dynamic frontend plugin 'red-hat-developer-hub-plugin-cost-management' +loaded dynamic backend plugin 'red-hat-developer-hub-plugin-cost-management-backend' +``` + +### Access RHDH + +Open **http://localhost:7008** in your browser. + +Verify: + +- "Cost management" appears in the sidebar with the custom icon +- Clicking it expands to show "Optimizations" and "OpenShift" nested items +- Navigating to `/cost-management/optimizations` loads the Optimizations page +- Navigating to `/cost-management/openshift` loads the OpenShift page + +### Restart After Config Changes + +If you modify the override file or app config: + +```bash +# Re-install plugins and restart +podman compose run install-dynamic-plugins +podman compose restart rhdh +``` + +### Stop + +```bash +podman compose down +``` + +## Testing Different Image Sources + +### Using GHCR Images (PR-built) + +To test with a PR-built image from the overlay repo (e.g., PR #2398): + +```yaml +# In dynamic-plugins.override.yaml, replace the package lines: +- package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-plugin-cost-management:pr_2398__2.2.0!red-hat-developer-hub-plugin-cost-management +``` + +### Using GHCR Images (production) + +After merging to main in the overlay repo, production images are tagged `bs___`: + +```yaml +- package: oci://ghcr.io/redhat-developer/rhdh-plugin-export-overlays/red-hat-developer-hub-plugin-cost-management:bs_1.49.4__2.2.0!red-hat-developer-hub-plugin-cost-management +``` + +### Using Local Build + +If you've built the plugin locally with `dynamic-plugins.sh`: + +```yaml +- package: ./local-plugins/red-hat-developer-hub-plugin-cost-management +``` + +Mount the local directory via `compose-dynamic-plugins-root.yaml`: + +```bash +podman compose -f compose.yaml -f compose-dynamic-plugins-root.yaml up -d +``` + +## Troubleshooting + +### Plugin not loading + +1. Check install logs: `podman compose logs install-dynamic-plugins` +2. Verify the override file is detected: look for "Using dynamic-plugins.override.yaml" in logs +3. Check RHDH logs for errors: `podman compose logs rhdh | grep -i error` + +### Sidebar menu not showing nested items + +Ensure `menuItems` keys use **dot notation** (not slashes): + +- Correct: `cost-management.optimizations` +- Incorrect: `cost-management/optimizations` + +### OCI image pull failures + +If pulling from Quay fails, ensure you're authenticated: + +```bash +podman login quay.io +# or for GHCR: +podman login ghcr.io +``` + +For GHCR, set `REGISTRY_AUTH_FILE` in `.env` if needed (see `docs/rhdh-local-guide/container-image-guide.md`). + +### Backend plugin errors (401/403) + +Verify `CM_CLIENT_ID` and `CM_CLIENT_SECRET` are set correctly in your `.env` file and that the credentials have access to the cost.redhat.com API. + +## Testing via Extensions Catalog (Marketplace UI) + +Instead of pre-installing the plugin via the override file, you can test the full user experience of discovering and installing the plugin through the Extensions Catalog UI. This simulates how customers would install plugins on a real RHDH cluster. + +### Prerequisites + +The following must already be in place (these are already configured in this workspace): + +1. **`compose.override.yaml`** — bind-mounts Plugin and Package catalog entities from `rhdh-plugin-export-overlays` into the container +2. **`app-config.local.yaml`** — registers the cost-management Plugin and Package entities as catalog locations, and includes the `costManagement` config block with `clientId` and `clientSecret` +3. **Extensions plugins enabled** — the `dynamic-plugins.override.yaml` (or default `dynamic-plugins.yaml`) must include the Extensions frontend and backend plugins + +**Important:** The `costManagement` config block must be in `app-config.local.yaml` (not just in the dynamic plugins config). When the Extensions Catalog installs a plugin, it saves the `pluginConfig` to `dynamic-plugins.extensions.yaml`, but top-level app-config keys like `costManagement` must exist in the actual app-config files. Without this, the backend plugin will fail with: + +``` +Missing required config value at 'costManagement.clientId' in 'app-config.local.yaml' +``` + +### Steps + +1. **Start RHDH without cost-management pre-installed.** + + If you want to test the Extensions Catalog flow exclusively, remove the cost-management entries from `dynamic-plugins.override.yaml` (keep only the `includes` and Extensions plugins). Or use the default `dynamic-plugins.yaml` which already has Extensions enabled — just rename/delete the override file. + +2. **Start the stack:** + + ```bash + cd rhdh-local + podman compose up -d + ``` + +3. **Open the Extensions Catalog:** + + Navigate to **http://localhost:7008/extensions** in your browser. + +4. **Find the Cost Management plugin:** + + Search for "cost-management" or browse the catalog. You should see both: + + - **Cost Management Frontend** — with version 2.2.0 + - **Cost Management Backend** — with version 2.2.0 + + These are loaded from the Package entities mounted from `rhdh-plugin-export-overlays/workspaces/cost-management/metadata/`. + +5. **Install the plugin via the UI:** + + Click on the plugin and follow the install flow. The Extensions backend saves the installation config to `dynamic-plugins.extensions.yaml` inside the container volume. + +6. **Restart to apply:** + + After installing via the UI, restart to load the newly installed plugins: + + ```bash + podman compose run install-dynamic-plugins + podman compose restart rhdh + ``` + +7. **Verify** the plugin appears in the sidebar and pages load correctly. + +### How It Works + +The Extensions Catalog flow relies on three pieces: + +- **Plugin entities** (`cost-management.yaml` in `catalog-entities/extensions/plugins/`) — define the plugin in the marketplace catalog +- **Package entities** (`metadata/cost-management.yaml`, `metadata/cost-management-backend.yaml`) — contain the `dynamicArtifact` OCI reference and `appConfigExamples` that the Extensions UI uses to generate install config +- **Extensions backend plugin** — writes the install config to `dynamic-plugins.extensions.yaml`, which is included via the `includes` directive in the dynamic plugins config + +### Pre-install vs. Extensions Catalog + +| Aspect | Pre-install (Override File) | Extensions Catalog (UI) | +| ------------------ | -------------------------------- | ---------------------------------------------------------- | +| **Setup** | Edit YAML config manually | Browse and click in UI | +| **Config control** | Full control over `pluginConfig` | Generated from `appConfigExamples` in metadata | +| **Testing focus** | Plugin functionality | End-user install experience + metadata correctness | +| **When to use** | Day-to-day development | Validating metadata, `appConfigExamples`, and install flow | +| **Restart needed** | At initial startup | After each UI install | + +Testing via the Extensions Catalog is particularly useful for validating that: + +- The `appConfigExamples` in metadata produce a working configuration +- The `dynamicArtifact` OCI reference is correct and pullable +- The plugin appears correctly in the marketplace with proper metadata + +For automated E2E testing on OpenShift, see [adding-e2e-tests-to-overlay-repo.md](adding-e2e-tests-to-overlay-repo.md). + +## File Reference + +| File | Purpose | +| ------------------------------------------------------------------ | ---------------------------------------- | +| `rhdh-local/configs/dynamic-plugins/dynamic-plugins.override.yaml` | Plugin config with cost-management 2.2.0 | +| `rhdh-local/configs/app-config/app-config.local.yaml` | Catalog entities for cost-management | +| `rhdh-local/compose.override.yaml` | Bind mounts for overlay repo metadata | +| `rhdh-local/default.env` | Default env vars (BASE_URL, DB, etc.) | +| `rhdh-local/.env` | Your local overrides (CM credentials) | From a7595aaa8843e04cda9563be4e7a817b308090c4 Mon Sep 17 00:00:00 2001 From: Preeti Wadhwani Date: Fri, 14 Aug 2026 11:54:23 +0530 Subject: [PATCH 3/3] docs(cost-management): link testing-cost-management-on-rhdh-local guide Cross-references the new rhdh-local testing guide from local-dev-setup.md so developers can find the pre-install / Extensions Catalog instructions. Co-authored-by: Cursor --- .../cost-management/docs/local-dev-setup.md | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/workspaces/cost-management/docs/local-dev-setup.md b/workspaces/cost-management/docs/local-dev-setup.md index b68c3be649c..aee6a753d53 100644 --- a/workspaces/cost-management/docs/local-dev-setup.md +++ b/workspaces/cost-management/docs/local-dev-setup.md @@ -87,7 +87,7 @@ rhdh-plugins/ ← outer monorepo (yarn@4.17.1, root pac │ │ ├── cost-management/ ← published frontend plugin │ │ ├── cost-management-backend/ ← published backend plugin │ │ └── cost-management-common/ ← shared types, API clients, permissions - │ └── docs/ ← this file, plus rbac.md, dynamic-plugin.md + │ └── docs/ ← this file, plus rbac.md, dynamic-plugin.md, testing-cost-management-on-rhdh-local.md ├── orchestrator/ ← a different plugin, own workspace root └── ... ``` @@ -150,6 +150,7 @@ Both need the same `costManagement.clientId` / `clientSecret` keys, but they're **Want to explore more?** - [`rhdh-local`](https://github.com/redhat-developer/rhdh-local) — the repository referenced above, for reference +- [docs/testing-cost-management-on-rhdh-local.md](./testing-cost-management-on-rhdh-local.md) — step-by-step guide to installing and verifying the plugin inside `rhdh-local` - [Demo recording: Testing Cost Management plugin with rhdh-local](https://drive.google.com/file/d/1d2Es7n8sAm8dRY0dIfVkFJFBW6asKaJk/view?usp=drive_web) — if interested --- @@ -192,7 +193,7 @@ Full permission/policy reference: [docs/rbac.md](./rbac.md). Once a change works in the local dev shell, validate it inside an actual RHDH instance, loading the plugin as a **dynamic plugin** (OCI image) the way a customer would: -- **[`rhdh-local`](https://github.com/redhat-developer/rhdh-local)** — Podman/Docker Compose-based local RHDH stack +- **[`rhdh-local`](https://github.com/redhat-developer/rhdh-local)** — Podman/Docker Compose-based local RHDH stack. For step-by-step setup (pre-install via override file, or the Extensions Catalog install flow), see [docs/testing-cost-management-on-rhdh-local.md](./testing-cost-management-on-rhdh-local.md). - **A real RHDH cluster** (Operator/Helm) — see [docs/dynamic-plugin.md](./dynamic-plugin.md) for the ConfigMap/Secret setup See it in action: [Demo recording: Testing Cost Management plugin with rhdh-local](https://drive.google.com/file/d/1d2Es7n8sAm8dRY0dIfVkFJFBW6asKaJk/view?usp=drive_web). @@ -227,12 +228,13 @@ Run from `workspaces/cost-management/`: ## File Reference -| File | Purpose | -| ------------------------------------ | ------------------------------------------------- | -| `app-config.yaml` | Base Backstage config (committed) | -| `app-config.local.yaml` | Personal local overrides + secrets (git-ignored) | -| `policy.local.csv` | Local RBAC policy rules | -| `packages/app/`, `packages/backend/` | Throwaway dev shell (not shipped) | -| `plugins/cost-management*/` | Published plugin code (frontend, backend, common) | -| `docs/rbac.md` | Full RBAC permission and policy reference | -| `docs/dynamic-plugin.md` | Installing the plugin as an RHDH dynamic plugin | +| File | Purpose | +| ----------------------------------------------- | ------------------------------------------------------------------------------- | +| `app-config.yaml` | Base Backstage config (committed) | +| `app-config.local.yaml` | Personal local overrides + secrets (git-ignored) | +| `policy.local.csv` | Local RBAC policy rules | +| `packages/app/`, `packages/backend/` | Throwaway dev shell (not shipped) | +| `plugins/cost-management*/` | Published plugin code (frontend, backend, common) | +| `docs/rbac.md` | Full RBAC permission and policy reference | +| `docs/dynamic-plugin.md` | Installing the plugin as an RHDH dynamic plugin | +| `docs/testing-cost-management-on-rhdh-local.md` | Testing the plugin locally via `rhdh-local` (pre-install or Extensions Catalog) |