Skip to content

feat(e2e): make e2e:kubernetes work transparently on OpenShift #2956

Description

@jgarciao

User Story

As a contributor running the Kubernetes e2e test suite on an OpenShift cluster,
I want mise run e2e:kubernetes to detect OpenShift automatically and handle
Security Context Constraints (SCC) setup, gateway transport, and teardown,
so that I can run the full Rust e2e suite — including the connect-based
suites — on OpenShift without a separate task or manual steps.

Problem Statement

Running mise run e2e:kubernetes on OpenShift requires manual preparation, and
even with that preparation the connect-based suites do not pass:

  1. SCC setup: the contributor must pre-create the namespace, grant the
    privileged SCC to the openshell-sandbox service account, and override the
    chart's hardcoded runAsUser: 1000 / fsGroup: 1000, which OpenShift's
    restricted-v2 SCC rejects because it requires UIDs from the
    namespace-assigned range.
  2. Transport: the harness reaches the gateway via kubectl port-forward. On
    OpenShift this stalls the SSH-relay sandbox connect path — that command
    opens an SSH session needing many small round trips, each tunneled through
    port-forward, so the connect suites (live_policy_update, port_forward,
    sync, connect-based sandbox_lifecycle, settings_management) run to the
    ready timeout instead of passing.
  3. Image freshness: on an existing cluster the harness pulls the published
    gateway/supervisor image (default latest), and nodes may serve a stale
    cached copy, so a run does not actually exercise the intended image.

A separate e2e:openshift task exists but only verifies the gateway pod
starts — it runs no Rust e2e coverage.

Impact / Why This Matters

Without this, contributors validating OpenShell on OpenShift must manually
create the namespace, grant/clean up SCCs, and pass Helm overrides — error-prone
and easy to forget, especially cleanup. Worse, even after all that, the
connect-based suites time out over port-forward, so the most important
sandbox-connectivity coverage silently fails and OpenShift appears broken when
it is not. The existing e2e:openshift task creates a false impression of
coverage. The harness should make the same command that works on Kind/k3d work
end-to-end on OpenShift.

Proposed Design

mise run e2e:kubernetes (and every variant like e2e:kubernetes:db) should
behave identically on vanilla Kubernetes and OpenShift:

  1. Auto-detection: detect OpenShift by the presence of the
    route.openshift.io API group. No flags or env vars.
  2. SCC handling (OpenShift only): apply an SCC-compatible Helm overlay that
    removes hardcoded runAsUser/fsGroup; grant privileged to
    openshell-sandbox; for DB scenarios grant anyuid to the PostgreSQL
    fixture service account.
  3. Gateway transport (OpenShift only): reach the gateway through a
    passthrough OpenShift Route secured with mandatory mTLS instead of
    port-forward, so the connect suites pass. The harness derives the Route host
    from the cluster ingress domain (<release>-<namespace>.<apps-domain>), bakes
    it into the server cert SANs before the Route exists, extracts client mTLS
    material from the openshell-client-tls secret, waits for the Route to serve
    mTLS, asserts a certless caller is rejected at the TLS handshake, and
    registers an mTLS CLI gateway. The health endpoint continues to use
    port-forward (not on the SSH path).
  4. Image pull policy (OpenShift overlay): force image.pullPolicy /
    supervisor.image.pullPolicy to Always so a run against latest uses the
    fresh image rather than a stale node-cached copy.
  5. Existing/remote-cluster ergonomics (documentation): document that on an
    existing cluster the CLI is built from the branch while the image is the
    published one, so IMAGE_TAG should be pinned when versions diverge; and that
    e2e-host-gateway must be dropped on remote clusters because
    host.openshell.internal is unreachable from remote pods.
  6. Cleanup: remove SCC bindings and extracted client-key material in the
    cleanup function, even on failure/interrupt.
  7. Remove e2e:openshift: delete the task and e2e-openshift.sh;
    e2e:kubernetes now covers OpenShift.
  8. No changes on vanilla Kubernetes: every OpenShift branch is gated on
    OPENSHIFT_DETECTED; the port-forward/plaintext path is unchanged.

Acceptance Criteria

  • mise run e2e:kubernetes auto-detects OpenShift and prints "OpenShift detected" on an OpenShift cluster
  • It does not falsely detect OpenShift on Kind/k3d/vanilla clusters
  • The chart deploys without runAsUser/fsGroup on OpenShift (SCC overlay applied automatically)
  • privileged SCC is granted to openshell-sandbox before Helm install and removed during cleanup
  • anyuid SCC is granted to the PostgreSQL fixture service account in DB scenarios and removed during cleanup
  • On OpenShift the gateway is reached over a passthrough Route with mandatory mTLS; a certless caller is rejected at the TLS handshake and the run fails loudly if it is not
  • The connect-based suites (live_policy_update, port_forward, sync, connect-based sandbox_lifecycle, settings_management) pass on OpenShift
  • The OpenShift overlay forces pullPolicy: Always for gateway and supervisor images
  • Extracted client mTLS key material is removed during cleanup
  • The e2e:openshift task and e2e-openshift.sh script are removed
  • TESTING.md documents Kubernetes e2e tasks, OpenShift auto-detection, IMAGE_TAG pinning for CLI/image version skew, dropping e2e-host-gateway on remote clusters, and relevant environment variables
  • All oc commands use --context "${KUBE_CONTEXT}" to target the correct cluster

Alternatives Considered

  1. Keep e2e:openshift as a separate task: This is the status quo. It
    requires maintaining two scripts with diverging logic. The separate script
    only checks pod readiness and provides no actual test coverage. Rejected
    because it creates a false sense of OpenShift coverage.

  2. Require explicit --openshift flag or environment variable: This would
    work but adds friction and is easy to forget. Auto-detection via the
    route.openshift.io API group is reliable and requires no user action.
    The API group check uses --no-headers output piped to grep -q . to
    avoid false positives from successful-but-empty kubectl api-resources
    responses.

  3. Separate Helm values file passed manually: Contributors could pass
    OPENSHELL_E2E_KUBE_EXTRA_VALUES pointing to an SCC overlay. This works
    but pushes OpenShift knowledge onto each contributor and doesn't handle
    SCC grant/cleanup. Rejected in favor of full automation.

  4. Keep port-forward on OpenShift: rejected — the SSH-relay connect suites
    stall to the ready timeout over port-forward's tunneled round trips, so the
    most valuable coverage never passes. A real network path (passthrough Route)
    is required.

  5. Server-only TLS on the Route (no mTLS): rejected — that would expose the
    gateway on a routable hostname with no access gate. Mandatory mTLS (the client
    cert as the access gate, require_client_auth implied by clientCaSecretName

    • no OIDC) keeps the Route closed to certless callers.

Agent Investigation

The implementation was developed and tested on both Kind and OpenShift clusters.
Key findings:

  • PR history: e2e-openshift.sh was created in PR feat(helm): add optional PostgreSQL backing store #1579 as a shortcut
    during PostgreSQL backing store work. The author deferred OpenShift integration
    into the main harness. PRs refactor(helm): require external postgres for ha #1844, fix(e2e): make postgres fixture compatible with OpenShift #2002, and chore(ci): disable telemetry in internal test runs #2648 iterated on the OpenShift
    e2e path without merging it into the main harness. No CI workflow references
    e2e:openshift.

  • Helm template fix needed: The podSecurityContext block in
    _gateway-workload.tpl renders invalid YAML when values are null. Wrapping
    with {{- with }} makes it conditional. This is a production chart bug
    independent of the e2e harness and is being tracked separately in fix(helm): omit podSecurityContext block when value is null #3033.

  • OpenShift detection pitfall: kubectl api-resources --api-group=route.openshift.io
    returns exit code 0 even on vanilla Kubernetes (empty result set). Detection
    must check for actual output, not just exit code.

  • oc context targeting: oc adm policy commands default to oc's own
    current context, which may differ from KUBE_CONTEXT. All oc commands must
    use --context "${KUBE_CONTEXT}".

  • Port-forward vs SSH relay: kubectl port-forward carries SSH's many small
    setup frames poorly, so the connect suites time out. The passthrough Route +
    mTLS path makes them pass.

  • Deterministic Route host: OpenShift serves any name under the cluster
    ingress (apps) domain via the router wildcard, so
    <release>-<namespace>.<apps-domain> can be baked into the server cert SANs
    before the Route is created.

  • Stale node image cache: latest can be served stale from a node's cache;
    pullPolicy: Always is required for a meaningful run.

  • CLI/image version skew and remote clusters: on an existing cluster the CLI
    is built from the branch while the gateway/supervisor image is the published
    one, so IMAGE_TAG must be pinned when versions diverge; and e2e-host-gateway
    must be dropped on remote clusters because host.openshell.internal is
    unreachable from remote pods. Both are documented in TESTING.md.

  • Test results: Both Kind and OpenShift test runs were completed. On Kind,
    all tests pass or fail with pre-existing issues (host.openshell.internal
    unreachable, Docker is Podman shim). On OpenShift, the same pre-existing
    failures occur. Tests that don't depend on sandbox SSH readiness (smoke,
    bypass_detection, community_image, landlock, no_proxy, etc.) pass normally.

Related issues:


I have an implementation ready here and can open a PR once this is accepted.

Activity

  1. added
    state:acceptedA maintainer decided OpenShell should pursue this issue
    on Sep 3, 2026
  2. krishicks commented on Sep 3, 2026

    @krishicks
    Collaborator

    This may break again until we have e2e:kubernetes running on OpenShift regularly, but getting it working now is a great idea.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:acceptedA maintainer decided OpenShell should pursue this issue

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions