This project is a minimal OpenShift Console Plugin for managing resources associated with secrets management. This includes the CRDs for:
- cert-manager
- external secrets operator
- secrets store csi driver
This project is based off of the OpenShift dynamic console plugin template seen here
Dynamic plugins
allow you to extend the
OpenShift UI
at runtime, adding custom pages and other extensions. They are based on
webpack module federation.
Plugins are registered with console using the ConsolePlugin custom resource
and enabled in the console operator config by a cluster administrator.
Using the latest v1 API version of ConsolePlugin CRD, requires OpenShift 4.12
and higher. For using old v1alpha1 API version us OpenShift version 4.10 or 4.11.
For an example of a plugin that works with OpenShift 4.11, see the release-4.11 branch.
For a plugin that works with OpenShift 4.10, see the release-4.10 branch.
Node.js and yarn are required to build and run the example. To run OpenShift console in a container, either Docker or podman 3.2.0+ and oc are required.
Note: This plugin was primarily generated using cursor and AI prompts.
Before you begin, ensure you have the following installed on your system:
- Node.js (v16 or higher) - Download here
- Yarn package manager - Installation guide
- Docker Desktop - Download here
- OpenShift CLI (oc) - Download here
- Git - For cloning the repository
-
Clone the repository
git clone <repository-url> cd ocp-secrets-management
-
Install dependencies
yarn install
-
Start Docker Desktop
- Make sure Docker Desktop is running on your system
- You should see the Docker icon in your system tray/menu bar
-
Login to your OpenShift cluster
oc login <your-openshift-cluster-url>
-
Start the plugin development server
yarn start
This will start the webpack development server on
http://localhost:9001 -
In a new terminal, start the OpenShift Console
yarn start-console
This will start the OpenShift Console on
http://localhost:9000 -
Access the plugin
- Open your browser and navigate to
http://localhost:9000 - Login with your OpenShift credentials
- Look for "Secrets Management" in the navigation menu
- Open your browser and navigate to
make update– Regenerate CRD types and other generated artifacts. Run this after you change code so thatsrc/generated/crdsand the build stay in sync.make sync-crd-types– If you added or changed imports from./crdsor./components/crds, run this to have the Cursor agent or Claude CLI sync types and shims (or it will print a prompt to paste into Cursor/Claude if no CLI is installed).make verify– Run typecheck, lint, and tests. Use before opening a PR.
yarn start- Start the plugin development serveryarn start-console- Start the OpenShift Console with plugin integrationyarn build- Build the plugin for productionyarn build-dev- Build the plugin for developmentyarn lint- Run ESLint for code quality checksyarn test- Run Jest unit tests
The project includes a comprehensive E2E test suite built with Playwright. Tests are split into two categories:
These tests use intercepted API responses (mocks) so they can run anywhere -- locally, in CI, without a live cluster.
make test-e2e-premerge # headless (CI mode)
make test-e2e-premerge-headed # headed (visible browser, for debugging)What they cover:
- Plugin hidden state when no operators are installed
- Dashboard rendering with mock certificates, external secrets, and secret provider classes
- Empty state messages for all resource types
- Filter dropdowns and status badges
These tests run against a real cluster with operators and resources installed. They validate actual API interactions and UI behavior.
Required environment variables:
| Variable | Description | Example |
|---|---|---|
BRIDGE_BASE_ADDRESS |
OpenShift Console URL | https://console-openshift-console.apps.<cluster>.devcluster.openshift.com |
BRIDGE_KUBEADMIN_PASSWORD |
kubeadmin password | wPNqQ-eQJnk-oBC8x-47B7p |
OC_SERVER |
API server URL (used by delete test for oc commands) |
https://api.<cluster>.devcluster.openshift.com:6443 |
export BRIDGE_BASE_ADDRESS='https://console-openshift-console.apps.<cluster>.devcluster.openshift.com'
export BRIDGE_KUBEADMIN_PASSWORD='<password>'
export OC_SERVER='https://api.<cluster>.devcluster.openshift.com:6443'
make test-e2e # headed (watch tests run)
make test-e2e-headless # headless (CI mode)What they cover:
- Certificate expiry warning badges (success/warning/danger colors)
- Delete resource via UI (creates test cert, deletes via modal, verifies removal)
- Inspect pane with metadata, specification, status, and sensitive data toggle
- Secrets Store CSI Driver: SecretProviderClass listing, provider icons, inspect pane
make test-e2e-all # pre-merge + post-merge, headless (requires cluster env vars)The cluster must have the following installed:
- cert-manager Operator for Red Hat OpenShift (
openshift-cert-manager-operator, channelstable-v1) - External Secrets Operator for Red Hat OpenShift (
openshift-external-secrets-operator, channelstable-v1) - Secrets Store CSI Driver Operator (
secrets-store-csi-driver-operator, channelstable) - SMC console plugin deployed and enabled
- Test resources: ClusterIssuer, Certificates (varied expiry), ExternalSecret, SecretStore, SecretProviderClasses
Note on ESO: Use the Red Hat operator (
openshift-external-secrets-operatorfromredhat-operators), not the community one. The Red Hat version serves thev1API that the plugin expects. The community operator only servesv1beta1, which causes "Model does not exist" errors.
- Headed (
--headed) -- Opens a visible browser window so you can watch tests interact with the UI. Useful for debugging. - Headless (default) -- Runs the browser invisibly in the background. Used in CI environments where there is no display.
The plugin is deployed by the Secrets Management Operator in the operator/ directory.
Prerequisites: podman or docker, oc logged into your OpenShift cluster, and push access to your container registry (e.g. quay.io).
We provide two deployment methods:
Method 1: Direct Deployment (Development - Fast)
# Build and deploy directly to cluster (no OLM)
export SM_QUAY_USER=myusername
./scripts/deploy-to-cluster.shMethod 2: Bundle Deployment (Production - Via OLM)
# Build and deploy via OLM bundle
export SM_QUAY_USER=myusername
export SM_IMAGE_TAG=v1.0.0
./scripts/deploy-via-bundle.shSee DEPLOYMENT_METHODS.md for detailed comparison.
Alternatively, build each image individually:
-
Build the plugin image (from repo root). Set
PLUGIN_IMGto your image (defaults in Makefile useopenshift.io/ocp-secrets-management:latest):make plugin-image PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:latest
Note: The plugin image build can take several minutes (often 5–15 minutes). The Dockerfile has a multi-stage build that runs
yarn installandyarn build(production webpack) inside the image with no cache ofnode_modulesordist/. So each build does a full npm install and a full production webpack bundle; that’s why it’s slow. Using the same tag (e.g.:latest) withimagePullPolicy: Alwaysensures the cluster pulls the new image after you push. -
Build the operator image (from
operator/). SetIMGto your image (defaults in operator Makefile useopenshift.io/ocp-secrets-management-operator:latest):cd operator make build make podman-build IMG=quay.io/<my-org>/ocp-secrets-management-operator:latest # or: make docker-build IMG=quay.io/<my-org>/ocp-secrets-management-operator:latest
-
Push both images to your registry (pass the same
PLUGIN_IMG/IMGyou used to build):make plugin-push PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:latest cd operator && make podman-push IMG=quay.io/<my-org>/ocp-secrets-management-operator:latest
-
Deploy the operator and apply the config. The deploy targets substitute
IMGandPLUGIN_IMGinto the manifests before applying, so the cluster uses the images you built (no need to edit YAML):cd operator make deploy IMG=quay.io/<my-org>/ocp-secrets-management-operator:latest make deploy-sample PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:latest
Committed manifests use the official
openshift.io/images; the Makefile replaces those withIMGandPLUGIN_IMGat deploy time (same idea as external-secrets-operator deploy). -
Restart deployments so they use the new images (if you use
:latest):oc rollout restart deployment/secrets-management-operator -n openshift-secrets-management oc rollout restart deployment/ocp-secrets-management-plugin -n openshift-secrets-management
With
imagePullPolicy: Alwayson the plugin (as in the sample), the plugin pods will pull the new image on restart.
Official image references in this repo (samples, manager manifest, CSV, charts) use openshift.io/. When you build and push to deploy yourself, set PLUGIN_IMG and IMG (e.g. make plugin-image PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:latest and make podman-build IMG=quay.io/<my-org>/ocp-secrets-management-operator:latest); the Makefiles default those vars to openshift.io/.
-
Plugin image (what runs in the cluster)
make deploy-sample PLUGIN_IMG=...substitutesPLUGIN_IMGinto the sampleSecretsManagementConfigbefore applying, so use e.g.make deploy-sample PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:latest. The committed sample uses the officialopenshift.io/default. -
Plugin image (when building)
Rootmake plugin-imageusesPLUGIN_IMG(defaultquay.io/<my-org>/ocp-secrets-management:latest). Override with e.g.make plugin-image PLUGIN_IMG=quay.io/<my-org>/ocp-secrets-management:v1.0.0. -
Operator image (what runs in the cluster)
make deploy IMG=...substitutesIMGinto the manager manifest before applying, so use e.g.make deploy IMG=quay.io/<my-org>/ocp-secrets-management-operator:latestto deploy your image. The committed file keeps the officialopenshift.io/default. -
Operator image (when building)
Inoperator/,make podman-buildandmake podman-pushuseIMG(defaultquay.io/<my-org>/ocp-secrets-management-operator:latest). Override with e.g.make podman-build IMG=quay.io/<my-org>/ocp-secrets-management-operator:v1.0.0.
This plugin provides a comprehensive interface for managing secrets-related Kubernetes resources:
- Certificates (cert-manager.io/v1)
- Issuers & ClusterIssuers (cert-manager.io/v1)
- ExternalSecrets & ClusterExternalSecrets (external-secrets.io/v1)
- SecretStores & ClusterSecretStores (external-secrets.io/v1)
- PushSecrets & ClusterPushSecrets (external-secrets.io/v1alpha1)
- SecretProviderClasses (secrets-store.csi.x-k8s.io/v1)
- Resource Filtering - Filter by operator (cert-manager, external-secrets, secrets-store-csi) and resource kind
- Resource Inspection - View detailed metadata, labels, annotations, specifications, and status
- Resource Deletion - Delete resources with confirmation dialogs
- Sensitive Data Toggle - Show/hide sensitive information in resource details
- Real-time Updates - Live resource monitoring with Kubernetes watch API
If you encounter "EADDRINUSE" errors:
# Kill existing Node.js processes
killall -9 node
# Restart the services
yarn start
# In new terminal:
yarn start-consoleIf the console fails to start:
- Ensure Docker Desktop is running
- Try restarting Docker Desktop
- Check if port 9000 is available
If the plugin doesn't appear in the console:
- Verify both
yarn startandyarn start-consoleare running - Check the browser console for errors
- Ensure you're logged into the correct OpenShift cluster
If resources don't load:
- Verify your OpenShift user has appropriate RBAC permissions
- Check that cert-manager and external-secrets-operator are installed in your cluster
- Ensure the "demo" namespace exists (or modify the code to use your desired namespace)
- The plugin uses the OpenShift Console Dynamic Plugin SDK
- Hot reloading is enabled for development efficiency
- All console debugging has been removed for production readiness
- CSRF tokens are handled automatically for API requests
This project uses Codecov to track and report code coverage metrics.
To generate a coverage report on your local machine:
# Run tests with coverage
yarn coverage
# View the HTML coverage report in your browser
open coverage/lcov-report/index.html # macOS
xdg-open coverage/lcov-report/index.html # Linux- Dashboard: View detailed coverage reports at codecov.io/gh/openshift/ocp-secrets-management-console
- Pull Requests: Coverage reports are automatically posted as comments on pull requests
- Coverage Threshold: Minimum coverage threshold is set to 50% for all metrics (branches, functions, lines, statements)
Coverage is automatically collected and uploaded to Codecov during CI/CD runs:
- Presubmit Tests: Coverage is collected on every PR
- Postsubmit Tests: Coverage baseline is updated on merges to
main - E2E Coverage: End-to-end test coverage is tracked separately with the
e2eflag
Coverage settings are configured in:
jest.config.ts- Jest coverage collection and thresholds.codecov.yml- Codecov behavior and reporting preferencesimages/ci/Dockerfile.coverage- Coverage-enabled container image for CI