Skip to content
Merged
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
245 changes: 238 additions & 7 deletions ui/apps/platform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,20 +60,251 @@ If **stackrox/ui** is your workspace root folder, you can create or edit stackro

### Running as an OpenShift Console plugin

A subset of the code can also be embedded in the OpenShift Console UI using webpack federated modules. The build tooling for
this is completely separate from the build tooling for the standalone version, but both versions share a large amount of application code.
A subset of the code can also be embedded in the OpenShift Console UI using
[webpack federated modules](https://webpack.js.org/concepts/module-federation/).
The build tooling for this is completely separate from the build tooling for the
standalone version, but both versions share a large amount of application code.

For additional reference, see the
[OpenShift Console Plugin SDK docs](https://github.com/openshift/console/tree/main/frontend/packages/console-dynamic-plugin-sdk)
and the [console-plugin-template](https://github.com/openshift/console-plugin-template?tab=readme-ov-file#development)
repository.

#### How the plugin works

OpenShift Console uses webpack Module Federation to load plugins at runtime. The
key concepts are:

- **Host / Remote**: The console is the "host" application. Our plugin is a
"remote" that exposes named modules (React components) via a manifest.
- **Shared singletons**: Certain dependencies (React, Redux, PatternFly
Topology, react-router, etc.) are provided by the console as singletons.
Plugins use the console's copy at runtime -- they cannot bundle their own.
See [Compatibility](#compatibility) for the full list and implications.
- **ConsolePlugin CRD**: In production, the console discovers plugins via a
`ConsolePlugin` custom resource that points to the Service and base path
serving the plugin manifest and bundles.

The plugin webpack config (`webpack.ocp-plugin.config.js`) uses the
`ConsoleRemotePlugin` from `@openshift-console/dynamic-plugin-sdk-webpack`,
which wraps Module Federation with console-specific conventions. It generates
the manifest, declares shared modules, and registers console
[extensions](https://github.com/openshift/console/tree/main/frontend/packages/console-dynamic-plugin-sdk/docs)
(routes, nav items, resource tabs, context providers).

#### Authentication and request flow

The plugin never talks to Central directly. All API requests flow through the
console's proxy and `sensor-proxy`, which handles authentication and
authorization using the user's existing OpenShift session.

```txt
Browser (OpenShift Console)
|
| Plugin component calls axios.get('/v1/...')
|
v
consoleFetchAxiosAdapter (src/ConsolePlugin/consoleFetchAxiosAdapter.ts)
| Overrides axios default adapter
| Injects ACS-AUTH-NAMESPACE-SCOPE header (active namespace)
| Calls consoleFetch() from SDK (adds user's OCP bearer token + CSRF)
|
v
Console Proxy
| Route: /api/proxy/plugin/advanced-cluster-security/api-service/...
| ConsolePlugin CRD proxy config: authorization: UserToken
| Console injects the user's bearer token into the upstream request
|
v
sensor-proxy (in-cluster Service, port 443)
| Validates OCP token against Kubernetes RBAC
| Applies ACS RBAC based on namespace scope header
| Forwards authenticated request to Central
|
v
Central
| Generates dynamic access scope
| Processes request with full auth context
| Returns data filtered by user permissions
```

#### Code structure

The plugin-specific code lives in `src/ConsolePlugin/`. Everything else under
`src/` (providers, services, hooks, components in `Containers/`) is shared
between the standalone UI and the plugin.

```txt
src/
├── index.tsx # Standalone UI entry point
├── ConsolePlugin/ # Plugin-specific code and wrappers
│ ├── PluginProvider.tsx # Context provider: sets up axios adapter,
│ │ # wraps shared providers (auth, flags, etc.)
│ ├── consoleFetchAxiosAdapter.ts # Bridges axios -> consoleFetch (SDK)
│ ├── ScopeContext.tsx # Tracks active namespace from console
│ ├── PluginContent.tsx # Permission gate wrapper
│ ├── hooks/ # Plugin-specific hooks
│ │ ├── useAnalyticsPageView.ts
│ │ ├── useDefaultWorkloadCveViewContext.ts
│ │ └── useWorkloadId.ts
│ ├── Components/ # Plugin-specific general UI components
│ │
│ │ # Exposed modules (entry points registered as console extensions):
│ ├── SecurityVulnerabilitiesPage/ # Top-level /acs/security/vulnerabilities route
│ ├── CveDetailPage/ # CVE detail route
│ ├── ImageDetailPage/ # Image detail route
│ ├── WorkloadSecurityTab/ # "Security" tab on Deployment, StatefulSet, etc.
│ ├── AdministrationNamespaceSecurityTab/ # "Security" tab on Namespace
│ └── ProjectSecurityTab/ # "Security" tab on Project
├── Containers/Vulnerabilities/ # Vuln Management page components - shared
├── providers/ # Shared context providers
├── services/ # Shared API service functions
└── hooks/ # Shared hooks
```

Each exposed module is a thin wrapper that imports shared
components from `Containers/` and adds plugin-specific concerns like namespace
scoping and analytics tracking.

#### Adding a new plugin extension

To add a new UI surface to the console plugin (e.g. a new tab on a Kubernetes
resource, or a new route), follow these steps. For the full list of available
extension types, see the
[Console SDK extension docs](https://github.com/openshift/console/tree/main/frontend/packages/console-dynamic-plugin-sdk/docs).

1. **Create the entry point component** in `src/ConsolePlugin/YourExtension/YourExtension.tsx`.

Keep it minimal -- import shared components and add only what's plugin-specific.
Use existing entry points as templates. For example, a resource tab:

```tsx
// src/ConsolePlugin/MyResourceSecurityTab/MyResourceSecurityTab.tsx
import { useParams } from 'react-router-dom-v5-compat';

import SomeSharedComponent from 'Containers/SomeArea/SomeSharedComponent';
import { useAnalyticsPageView } from '../hooks/useAnalyticsPageView';

export function MyResourceSecurityTab() {
useAnalyticsPageView();
const { ns, name } = useParams();

return <SomeSharedComponent namespace={ns} name={name} />;
}
```

2. **Register the exposed module** in `webpack.ocp-plugin.config.js` under
`pluginMetadata.exposedModules`:

**Prerequisites**
```js
exposedModules: {
// ...existing modules
MyResourceSecurityTab: './ConsolePlugin/MyResourceSecurityTab/MyResourceSecurityTab',
},
```

3. **Add the console extension** in the `extensions` array in the same file.

For a horizontal nav tab on a Kubernetes resource:

```js
{
type: 'console.tab/horizontalNav',
properties: {
model: {
group: 'apps',
kind: 'MyResource',
version: 'v1',
},
page: {
name: 'Security',
href: 'security',
},
component: { $codeRef: 'MyResourceSecurityTab.MyResourceSecurityTab' },
},
},
```

For additional reference, see the [console-plugin-template](https://github.com/openshift/console-plugin-template?tab=readme-ov-file#development) repository.
For a new route:

```js
{
type: 'console.page/route',
properties: {
exact: true,
path: '/acs/my-area/my-page',
component: { $codeRef: 'MyPage.MyPage' },
},
},
```

You need
4. **Test it** by running the plugin dev environment (see [Running the plugin](#running-the-plugin)
below) and navigating to the resource or route in the console.

#### Compatibility

The plugin's runtime environment is controlled by the OpenShift Console, not by
us. The console provides a set of
[shared singleton modules](https://github.com/openshift/console/blob/release-4.19/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules/shared-modules-meta.ts)
that plugins **must** use -- you cannot bundle your own copy of these libraries.
At runtime, the console's version is what executes, regardless of what version
is in our `package.json`.

The shared modules ([shared-modules-meta.ts](https://github.com/openshift/console/blob/main/frontend/packages/console-dynamic-plugin-sdk/src/shared-modules/shared-modules-meta.ts)) (as of console 4.19) are:

- `react` / `react-dom`
- `react-redux`
- `react-router`
- `react-router-dom`
- `react-router-dom-v5-compat`
- `react-i18next`
- `redux`
- `redux-thunk`
- `@openshift-console/dynamic-plugin-sdk`
- `@openshift-console/dynamic-plugin-sdk-internal`
- `@patternfly/react-topology`

All are singletons with no fallback allowed.

Libraries **not** in this list (e.g. `@patternfly/react-core`,
`@patternfly/react-table`, `@patternfly/react-icons`, `axios`, `@apollo/client`)
are bundled in our plugin and can be versioned independently.

**Note that although _we_ provide `@patterfly/react-core`, the console plugin build strips out PatternFlyCSS.
This means that although we do ship the PatternFly runtime code, we are still limited to the styles provided
by the console.**

**What this means in practice:**

- **React version**: Console 4.19 ships React 17. Our `package.json` declares
React 18, but the plugin runs on React 17 at runtime. Avoid React 18-only
APIs (`useId`, `useDeferredValue`, `useTransition`, `createRoot`, automatic
batching) in any code path reachable from the plugin.
- **react-router**: Console 4.19 ships react-router v5. We use
`react-router-dom-v5-compat` for v6-style APIs (`useParams`, `useNavigate`).
Note that both `react-router-dom` and `react-router-dom-v5-compat` are
deprecated in newer console versions in favor of `react-router` (v7+).
- **PatternFly**: Non-shared PF packages (react-core, react-table, etc.) are
bundled by us, so minor version differences are fine. However, large version
gaps between our bundled PF and the console's PF can cause visual
inconsistencies (spacing, colors, component behavior).
major version bumps that will require migration work when we target newer
console releases.

Our webpack config declares `dependencies: { '@console/pluginAPI': '>=4.19.0' }`,
which means the console will only load our plugin if its API version satisfies
that constraint.

#### Prerequisites

You need:

1. A running OpenShift cluster and kubeconfig available in order to run the plugin.
2. `podman` or `docker`
3. `oc`

**Architecture**
#### Architecture

A plugin development environment has the following network components:

Expand All @@ -84,7 +315,7 @@ A plugin development environment has the following network components:

The plugin uses OpenShift user authentication and proxies all API requests through the `sensor-proxy` service, which handles authentication/authorization and forwards requests to Central. This matches the production flow where the console plugin communicates through sensor-proxy rather than directly to Central.

**Running the plugin**
#### Running the plugin

First, start the webpack dev server to make the plugin configuration files and js bundles available:

Expand Down
Loading