Skip to content

Latest commit

 

History

History
326 lines (239 loc) · 8.68 KB

File metadata and controls

326 lines (239 loc) · 8.68 KB

RHDH Deployment

The RHDHDeployment class is the core class for managing RHDH deployments in OpenShift.

Basic Usage

import { RHDHDeployment } from "@red-hat-developer-hub/e2e-test-utils/rhdh";

// Create deployment with namespace
const deployment = new RHDHDeployment("my-test-namespace");

// Configure options
await deployment.configure({
  version: "1.5",
  method: "helm",
  auth: "keycloak",
});

// Deploy RHDH
await deployment.deploy();

// Access the deployed instance
console.log(`RHDH URL: ${deployment.rhdhUrl}`);

Using with Test Fixtures

When using the test fixtures, RHDHDeployment is automatically created:

import { test } from "@red-hat-developer-hub/e2e-test-utils/test";

test.beforeAll(async ({ rhdh }) => {
  // rhdh is already instantiated with namespace from project name
  await rhdh.configure({ auth: "keycloak" });
  await rhdh.deploy(); // automatically skips if already deployed
});

test("example", async ({ rhdh }) => {
  console.log(`URL: ${rhdh.rhdhUrl}`);
});

Configuration Options

DeploymentOptions

Option Type Description
version string RHDH version (e.g., "1.5"). Defaults to RHDH_VERSION or "next"
namespace string Kubernetes namespace. Set via constructor
method "helm" | "operator" Installation method. Defaults to INSTALLATION_METHOD or "helm"
auth "guest" | "keycloak" Authentication provider. Defaults to "keycloak"
appConfig string Path to app-config YAML
secrets string Path to secrets YAML
dynamicPlugins string Path to dynamic-plugins YAML
valueFile string Helm values file (Helm only)
subscription string Backstage CR file (Operator only)

Example: Full Configuration

await deployment.configure({
  version: "1.5",
  method: "helm",
  auth: "keycloak",
  appConfig: "tests/config/app-config-rhdh.yaml",
  secrets: "tests/config/rhdh-secrets.yaml",
  dynamicPlugins: "tests/config/dynamic-plugins.yaml",
  valueFile: "tests/config/value_file.yaml",
});

Methods

configure(options?)

Prepare for deployment by creating the namespace and setting options:

await deployment.configure({
  auth: "keycloak",
  appConfig: "tests/config/app-config.yaml",
});

deploy(options?)

Deploy RHDH to the cluster:

await deployment.deploy();

The deploy() method accepts optional parameters:

Option Type Default Description
timeout number | null 600_000 Playwright test timeout (ms) during deployment
force boolean false Force redeployment even if already deployed
// Default (600s)
await rhdh.deploy();

// Custom timeout (15 minutes)
await rhdh.deploy({ timeout: 900_000 });

// No timeout (infinite)
await rhdh.deploy({ timeout: 0 });

// Skip — let the consumer control the timeout
test.setTimeout(900_000);
await rhdh.deploy({ timeout: null });

deploy() automatically skips if the deployment already succeeded in the current test run (e.g., after a worker restart due to test failure). This prevents expensive re-deployments.

Force Redeploy

Use the force option to bypass the built-in runOnce protection and force a fresh deployment. This is useful for complex test scenarios where multiple describe sections need different RHDH configurations (different app configs or dynamic plugin sets) within the same test file:

test.describe("Plugin Set A", () => {
  test.beforeAll(async ({ rhdh }) => {
    await rhdh.configure({
      auth: "keycloak",
      dynamicPlugins: "tests/config/plugins-set-a.yaml",
    });
    await rhdh.deploy(); // First deployment
  });

  test("test with plugin set A", async ({ page }) => {
    // Tests using plugin set A
  });
});

test.describe("Plugin Set B", () => {
  test.beforeAll(async ({ rhdh }) => {
    await rhdh.configure({
      auth: "keycloak",
      dynamicPlugins: "tests/config/plugins-set-b.yaml",
    });
    await rhdh.deploy({ force: true }); // Force redeploy with new config
  });

  test("test with plugin set B", async ({ page }) => {
    // Tests using plugin set B
  });
});

This method:

  1. Merges configuration files (common → auth → project)
  2. Injects plugin metadata into dynamic plugins config
  3. Applies ConfigMaps (app-config, dynamic-plugins)
  4. Applies Secrets (with environment variable substitution)
  5. Installs RHDH via Helm or Operator
  6. Waits for the deployment to be ready
  7. Sets RHDH_BASE_URL environment variable

Base URL format

The base URL prefix depends on the installation method:

  • Helm: https://redhat-developer-hub-<namespace>.<cluster>
  • Operator: https://backstage-developer-hub-<namespace>.<cluster>

Helm behavior

Helm deployments perform a scale-down and restart after applying configs to avoid migration locks.

Operator version constraints

Operator deployments accept only:

  • Semantic versions like 1.5
  • "next"

Any other value will throw an error during deployment.

waitUntilReady(timeout?)

Wait for the RHDH deployment to be ready. Performs two-phase readiness check:

  1. Pod readiness — Waits for all pods to have Ready=True with early failure detection (CrashLoopBackOff, ImagePullBackOff, etc.)
  2. Route readiness — HTTP health check against the RHDH route, closing the gap between pod readiness and the OpenShift Router actually serving traffic
// Default timeout: 500 seconds (~8 minutes)
await deployment.waitUntilReady();

// Custom timeout
await deployment.waitUntilReady(600); // 10 minutes

rolloutRestart()

Restart the RHDH deployment (useful after config changes):

// Update configuration
await deployment.k8sClient.applyConfigMapFromObject(
  "app-config-rhdh",
  { newConfig: "value" },
  deployment.deploymentConfig.namespace
);

// Restart to apply changes
await deployment.rolloutRestart();

teardown()

Delete the namespace and all resources:

await deployment.teardown();

::: warning You typically don't need to call this manually. In CI, the built-in teardown reporter automatically deletes namespaces after all tests complete. See Namespace Cleanup. :::

Properties

rhdhUrl

The URL of the deployed RHDH instance:

const url = deployment.rhdhUrl;
// e.g., "https://backstage-my-namespace.apps.cluster.example.com"

deploymentConfig

The current deployment configuration:

const config = deployment.deploymentConfig;
console.log(config.namespace);  // "my-namespace"
console.log(config.version);    // "1.5"
console.log(config.method);     // "helm"
console.log(config.auth);       // "keycloak"

k8sClient

The Kubernetes client instance for direct cluster operations:

const k8s = deployment.k8sClient;

// Get route URL
const url = await k8s.getRouteLocation(
  deployment.deploymentConfig.namespace,
  "my-route"
);

// Apply custom ConfigMap
await k8s.applyConfigMapFromObject(
  "my-config",
  { key: "value" },
  deployment.deploymentConfig.namespace
);

Configuration File Paths

Configuration files are looked for in:

tests/config/
├── app-config-rhdh.yaml
├── dynamic-plugins.yaml
└── rhdh-secrets.yaml

Or specify custom paths:

await deployment.configure({
  appConfig: "custom/path/app-config.yaml",
  secrets: "custom/path/secrets.yaml",
  dynamicPlugins: "custom/path/plugins.yaml",
});

Configuration Merging Order

  1. Common configs (package/config/common/)
  2. Auth configs (package/config/auth/{guest|keycloak}/)
  3. Project configs (your tests/config/ files)

Later files override earlier ones, allowing you to customize only what you need.

Example: Pre-Deployment Setup

import { test } from "@red-hat-developer-hub/e2e-test-utils/test";
import { $ } from "@red-hat-developer-hub/e2e-test-utils/utils";

test.beforeAll(async ({ rhdh }) => {
  // Wrap in test.runOnce because the setup script is also expensive
  await test.runOnce("my-plugin-setup", async () => {
    const namespace = rhdh.deploymentConfig.namespace;

    // Configure RHDH
    await rhdh.configure({ auth: "keycloak" });

    // Run custom setup before deployment
    await $`bash scripts/setup.sh ${namespace}`;

    // Set runtime environment variables
    process.env.MY_CUSTOM_URL = await rhdh.k8sClient.getRouteLocation(
      namespace,
      "my-service"
    );

    // Deploy RHDH (has built-in protection, safe to nest inside runOnce)
    await rhdh.deploy();
  });
});