Skip to content
Draft
Show file tree
Hide file tree
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
211 changes: 211 additions & 0 deletions docs/installation/openshift-installation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
---
title: Deploy HAMi on OpenShift
sidebar_label: HAMi on OpenShift
translated: true
---

This guide covers deploying HAMi on OpenShift, including SCC, random UIDs, non-privileged ports, and SELinux.

## Prerequisites

- OpenShift cluster with the NVIDIA GPU Operator installed
- NVIDIA drivers and Container Toolkit ready
- NVIDIA RuntimeClass configured by the GPU Operator
- Permission to create cluster-scoped `SecurityContextConstraints` (SCC)

Verify the cluster:

```bash
oc get runtimeclass
oc get nodes -L nvidia.com/gpu.present
oc describe node <gpu-node> | grep -A5 Taints
```

Example values in this guide assume the following GPU Operator layout. Adjust names and paths to match your cluster:

```text
RuntimeClass: nvidia
node label: nvidia.com/gpu.present=true
taint: nvidia.com/gpu=true:NoSchedule
driver root: /run/nvidia/driver
toolkit validation: /run/nvidia/validations
```

## Recommended configuration

Create a dedicated project for HAMi:

```bash
oc new-project hami
```

Create `values-openshift.yaml`:

```yaml
platform:
openshift: true

openshift:
securityContextConstraints:
create: true
name: hami-device-plugin

selinux:
enabled: true
type: container_file_t
level: s0
Comment on lines +45 to +56

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- relevant files ---'
git ls-files | rg '(^|/)(openshift-installation\.md|values\.yaml|templates/.*scc|templates/.*selinux|Chart\.yaml)$' | head -200

printf '%s\n' '--- English guide ---'
sed -n '35,105p' docs/installation/openshift-installation.md
sed -n '145,220p' docs/installation/openshift-installation.md

printf '%s\n' '--- Chinese guide ---'
sed -n '35,105p' i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md
sed -n '145,220p' i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md

printf '%s\n' '--- chart references ---'
rg -n --glob 'values.yaml' --glob 'Chart.yaml' --glob '*.tpl' --glob '*.yaml' \
  'securityContextConstraints|selinux|driverRoot|openshift|hami-device-plugin|container_file_t' .

Repository: Project-HAMi/website

Length of output: 5684


🏁 Script executed:

#!/bin/bash
set -eu

base='https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami'
printf '%s\n' '--- upstream values.yaml ---'
curl -fsSL "$base/values.yaml" |
  rg -n -C 5 'platform:|openshift:|securityContextConstraints|selinux:|nvidiaDriverRoot|runtimeClassName|httpPort|httpTargetPort'

printf '%s\n' '--- upstream chart metadata ---'
curl -fsSL "$base/Chart.yaml" |
  rg -n '^(apiVersion|name|version|appVersion):'

printf '%s\n' '--- upstream template paths ---'
curl -fsSL 'https://api.github.com/repos/Project-HAMi/HAMi/contents/charts/hami/templates' |
  jq -r '.[].name' |
  rg -i 'scc|selinux|security|scheduler|device'

printf '%s\n' '--- published chart index ---'
curl -fsSL 'https://project-hami.github.io/HAMi/index.yaml' |
  sed -n '1,100p'

Repository: Project-HAMi/website

Length of output: 2453


🏁 Script executed:

#!/bin/bash
set -u

index_url='https://project-hami.github.io/HAMi/index.yaml'
index="$(curl -fsSL "$index_url")" || {
  echo 'index fetch failed'
  exit 1
}
printf '%s\n' '--- chart index entries ---'
printf '%s\n' "$index" | rg -n -m 20 '^(    - version:|      urls:|        - )'

url="$(printf '%s\n' "$index" | awk '
  /^    - version:/ { if (!seen++) keep=1; else keep=0 }
  keep && /^        - / { sub(/^        - /, ""); print; exit }
')"
case "$url" in
  http*) ;;
  *) url="https://project-hami.github.io/HAMi/$url" ;;
esac
printf '%s\n' "--- selected package: $url ---"

printf '%s\n' '--- package file list ---'
curl -fsSL "$url" | tar -tzf - | rg '(^|/)(values.yaml|Chart.yaml|templates/)' | head -200

printf '%s\n' '--- packaged values ---'
curl -fsSL "$url" |
  tar -xzO --wildcards '*/values.yaml' |
  rg -n -C 4 'platform:|openshift:|securityContextConstraints|selinux:|nvidiaDriverRoot|runtimeClassName|httpPort|httpTargetPort'

Repository: Project-HAMi/website

Length of output: 509


🏁 Script executed:

#!/bin/bash
set -u

printf '%s\n' '--- chart index header ---'
curl -fsSL 'https://project-hami.github.io/HAMi/index.yaml' | sed -n '1,80p'

archive='/tmp/hami-master.tar.gz'
curl -fsSL 'https://codeload.github.com/Project-HAMi/HAMi/tar.gz/refs/heads/master' -o "$archive"

printf '%s\n' '--- upstream chart files ---'
tar -tzf "$archive" |
  rg '/charts/hami/(Chart.yaml|values.yaml|templates/)' |
  head -250

printf '%s\n' '--- upstream chart metadata ---'
tar -xOzf "$archive" --wildcards '*/charts/hami/Chart.yaml' |
  rg -n '^(apiVersion|name|version|appVersion):'

printf '%s\n' '--- upstream chart values ---'
tar -xOzf "$archive" --wildcards '*/charts/hami/values.yaml' |
  rg -n -C 5 'platform:|openshift:|securityContextConstraints|selinux:|nvidiaDriverRoot|runtimeClassName|httpPort|httpTargetPort'

Repository: Project-HAMi/website

Length of output: 7060


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import io
import re
import tarfile
import urllib.request

index_url = "https://project-hami.github.io/HAMi/index.yaml"
index = urllib.request.urlopen(index_url).read().decode()
urls = re.findall(r'^\s+- (https?://\S+\.tgz)$', index, re.MULTILINE)

keys = (
    "platform",
    "openshift",
    "securityContextConstraints",
    "selinux",
    "container_file_t",
    "SecurityContextConstraints",
)
print(f"published chart archives: {len(urls)}")

for url in urls:
    version = re.search(r"hami-([^/]+)\.tgz$", url).group(1)
    try:
        data = urllib.request.urlopen(url).read()
        with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as archive:
            files = {}
            for member in archive.getmembers():
                if member.isfile() and (
                    member.name.endswith("/values.yaml")
                    or "/templates/" in member.name
                ):
                    files[member.name] = archive.extractfile(member).read().decode(
                        errors="replace"
                    )
        matches = {
            key: [
                name for name, text in files.items() if key in text
            ]
            for key in keys
        }
        supported = {key: names for key, names in matches.items() if names}
        print(f"{version}: {supported or 'no documented OpenShift/S​​ELinux keys/resources'}")
    except Exception as exc:
        print(f"{version}: ERROR {exc}")
PY

Repository: Project-HAMi/website

Length of output: 2444


🏁 Script executed:

#!/bin/bash
set -eu

python3 - <<'PY'
import io
import re
import tarfile
import urllib.request

index_url = "https://project-hami.github.io/HAMi/index.yaml"
index = urllib.request.urlopen(index_url).read().decode()
urls = re.findall(r'^\s+- (https?://\S+\.tgz)$', index, re.MULTILINE)

keys = (
    "platform",
    "openshift",
    "securityContextConstraints",
    "selinux",
    "container_file_t",
    "SecurityContextConstraints",
)
print(f"published chart archives: {len(urls)}")

for url in urls:
    version = re.search(r"hami-([^/]+)\.tgz$", url).group(1)
    try:
        data = urllib.request.urlopen(url).read()
        with tarfile.open(fileobj=io.BytesIO(data), mode="r:gz") as archive:
            files = {}
            for member in archive.getmembers():
                if member.isfile() and (
                    member.name.endswith("/values.yaml")
                    or "/templates/" in member.name
                ):
                    files[member.name] = archive.extractfile(member).read().decode(
                        errors="replace"
                    )
        matches = {
            key: [
                name for name, text in files.items() if key in text
            ]
            for key in keys
        }
        supported = {key: names for key, names in matches.items() if names}
        print(f"{version}: {supported or 'no documented OpenShift/SELinux keys/resources'}")
    except Exception as exc:
        print(f"{version}: ERROR {exc}")
PY

Repository: Project-HAMi/website

Length of output: 2444


🏁 Script executed:

#!/bin/bash
set -u

tmp_index=/tmp/hami-index.yaml
curl -k -fsSL 'https://project-hami.github.io/HAMi/index.yaml' -o "$tmp_index"

urls="$(
  rg -o 'https?://[^[:space:]]+/hami-[^[:space:]]+\.tgz' "$tmp_index" |
  sort -u
)"
printf '%s\n' "--- published archives: $(printf '%s\n' "$urls" | sed '/^$/d' | wc -l) ---"

while IFS= read -r url; do
  [ -n "$url" ] || continue
  version="${url##*/hami-}"
  version="${version%.tgz}"
  archive="/tmp/hami-${version}.tgz"
  curl -k -fsSL "$url" -o "$archive"

  matches="$(
    tar -xOzf "$archive" --wildcards '*/values.yaml' '*/templates/*' 2>/dev/null |
      rg -n 'platform:|openshift:|securityContextConstraints|selinux:|container_file_t|SecurityContextConstraints' ||
      true
  )"

  if [ -n "$matches" ]; then
    printf '%s: MATCH\n%s\n' "$version" "$matches"
  else
    printf '%s: no OpenShift/SELinux keys or resources\n' "$version"
  fi
done <<EOF
$urls
EOF

Repository: Project-HAMi/website

Length of output: 833


Do not document unsupported Helm values

Chart versions 2.4.0 through 2.9.0 do not define these values or render SCC and SELinux resources. Helm therefore ignores this configuration.

Update both guides at #L47-L58. Add chart support and pin that release, or document the required OpenShift resources separately.

📍 Affects 2 files
  • docs/installation/openshift-installation.md#L47-L58 (this comment)
  • i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md#L47-L58
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/installation/openshift-installation.md` around lines 47 - 58, Update the
OpenShift configuration sections at
docs/installation/openshift-installation.md:47-58 and
i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md:47-58
to remove unsupported securityContextConstraints and SELinux Helm values; either
document a chart version that supports and renders these resources with the
release pinned, or describe the required OpenShift resources separately in both
guides.

Source: MCP tools


scheduler:
service:
httpPort: 443
httpTargetPort: 9443
Comment on lines +59 to +61

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- candidate files ---'
git ls-files | rg '(^|/)(docs/installation/openshift-installation\.md|i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation\.md|charts/hami/.*scheduler.*|charts/hami/.*values.*)$' || true

printf '%s\n' '--- English guide relevant sections ---'
sed -n '40,75p;150,175p;195,220p' docs/installation/openshift-installation.md

printf '%s\n' '--- Chinese guide relevant sections ---'
sed -n '40,75p;150,175p;195,220p' i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md

printf '%s\n' '--- chart scheduler references ---'
rg -n -S 'bind|targetPort|containerPort|scheduler.*port|httpPort|httpTargetPort|9443|scheduler.*url|scheduler.*URL' charts/hami charts 2>/dev/null | head -250

printf '%s\n' '--- chart tree ---'
git ls-files charts/hami | head -200

Repository: Project-HAMi/website

Length of output: 3611


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- repository references to the chart and scheduler ports ---'
rg -n -S 'hami-charts/hami|scheduler extender|httpTargetPort|httpPort|9443|scheduler.*bind|bind.*port|scheduler.*url|scheduler.*URL' . \
  -g '!node_modules' -g '!build' -g '!dist' | head -300

printf '%s\n' '--- guide headings and links ---'
rg -n -S 'helm|chart|scheduler|Service|service|9443|443|GitHub|github' \
  docs/installation/openshift-installation.md \
  i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md

printf '%s\n' '--- upstream chart repository metadata (read-only remote queries) ---'
for url in \
  'https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/values.yaml' \
  'https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/templates/scheduler/deployment.yaml' \
  'https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/templates/scheduler/service.yaml' \
  'https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/templates/scheduler/configmap.yaml'
do
  printf '\n--- %s ---\n' "$url"
  curl -L --fail --silent --show-error "$url" | \
    rg -n -C 4 '9443|httpPort|httpTargetPort|containerPort|bind|url|URL|scheduler' | head -160 || true
done

Repository: Project-HAMi/website

Length of output: 50376


🏁 Script executed:

#!/bin/bash
set -eu
log=/tmp/coderabbit-shell-logs/shell-output-WySvM9

printf '%s\n' '--- upstream query sections from the prior result ---'
rg -n -C 2 '^--- https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami' "$log" || true

printf '%s\n' '--- exact guide context around the port contract ---'
sed -n '108,130p' docs/installation/openshift-installation.md
sed -n '108,130p' i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md

printf '%s\n' '--- current upstream chart files, reduced to relevant keys ---'
for file in values.yaml templates/scheduler/deployment.yaml templates/scheduler/service.yaml templates/scheduler/configmap.yaml; do
  url="https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/$file"
  printf '\n--- %s ---\n' "$file"
  curl -L --fail --silent --show-error "$url" |
    awk 'BEGIN{p=0} /9443|httpPort|httpTargetPort|containerPort|bind|url|URL|scheduler/{p=1} p{print} p && NR>0{c++} c>=80{exit}' |
    head -100
done

Repository: Project-HAMi/website

Length of output: 13215


🏁 Script executed:

#!/bin/bash
set -eu

for file in values.yaml templates/scheduler/deployment.yaml templates/scheduler/service.yaml templates/scheduler/configmap.yaml; do
  url="https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/$file"
  tmp=$(mktemp)
  curl -L --fail --silent --show-error "$url" > "$tmp"
  printf '\n--- %s: matching lines ---\n' "$file"
  rg -n -C 8 '9443|httpTargetPort|httpPort|containerPort|targetPort|urlPrefix|--port|port:|bind|listen|LISTEN|admissionWebhook' "$tmp" | head -240
  rm -f "$tmp"
done

Repository: Project-HAMi/website

Length of output: 10955


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- installation command and chart version context ---'
sed -n '82,100p' docs/installation/openshift-installation.md
sed -n '82,100p' i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md

printf '%s\n' '--- read-only behavioral verifier for the documented values ---'
python3 - <<'PY'
import re
import urllib.request

base = "https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/"
files = {}
for name in (
    "templates/scheduler/deployment.yaml",
    "templates/scheduler/service.yaml",
    "templates/scheduler/configmap.yaml",
):
    with urllib.request.urlopen(base + name) as response:
        files[name] = response.read().decode()

deployment = files["templates/scheduler/deployment.yaml"]
service = files["templates/scheduler/service.yaml"]
configmap = files["templates/scheduler/configmap.yaml"]

# The documented values leave admissionWebhook.enabled at its chart default: true.
assert "admissionWebhook:\n    enabled: true" in urllib.request.urlopen(
    base + "values.yaml"
).read().decode()

bind = re.search(r"--http_bind=0\.0\.0\.0:(\d+)", deployment)
container = re.search(r"containerPort: (\d+)", deployment)
target = re.search(r"targetPort: \{\{ \.Values\.scheduler\.service\.httpTargetPort \| default 443 \}\}", service)
url = re.search(r'urlPrefix: "https://127\.0\.0\.1:(\d+)"', configmap)

print({
    "scheduler_bind_port": bind.group(1) if bind else None,
    "container_port": container.group(1) if container else None,
    "service_target_port": "documented override: 9443",
    "configmap_url_port": url.group(1) if url else None,
})
assert bind and bind.group(1) == "443"
assert container and container.group(1) == "443"
assert target
assert url and url.group(1) == "443"
print("Documented targetPort 9443 does not match the chart listener or extender URL.")
PY

Repository: Project-HAMi/website

Length of output: 3297


🏁 Script executed:

#!/bin/bash
set -eu

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

for file in values.yaml templates/scheduler/deployment.yaml templates/scheduler/service.yaml templates/scheduler/configmap.yaml; do
  mkdir -p "$tmpdir/$(dirname "$file")"
  curl -L --fail --silent --show-error \
    "https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/$file" \
    -o "$tmpdir/$file"
done

python3 - "$tmpdir" <<'PY'
import pathlib
import re
import sys

root = pathlib.Path(sys.argv[1])
values = (root / "values.yaml").read_text()
deployment = (root / "templates/scheduler/deployment.yaml").read_text()
service = (root / "templates/scheduler/service.yaml").read_text()
configmap = (root / "templates/scheduler/configmap.yaml").read_text()

assert re.search(r"admissionWebhook:\s*\n\s*enabled:\s*true", values)
bind = re.search(r"--http_bind=0\.0\.0\.0:(\d+)", deployment)
container = re.search(r"containerPort:\s*(\d+)", deployment)
target = re.search(r"targetPort:\s*\{\{\s*\.Values\.scheduler\.service\.httpTargetPort", service)
url = re.search(r'urlPrefix:\s*"https://127\.0\.0\.1:(\d+)"', configmap)

result = {
    "documented_service_port": 443,
    "documented_service_target_port": 9443,
    "chart_scheduler_bind_port": bind.group(1) if bind else None,
    "chart_container_port": container.group(1) if container else None,
    "chart_configmap_url_port": url.group(1) if url else None,
    "service_target_is_configurable": bool(target),
}
print(result)

assert result["chart_scheduler_bind_port"] == "443"
assert result["chart_container_port"] == "443"
assert result["chart_configmap_url_port"] == "443"
assert result["service_target_is_configurable"]
assert result["documented_service_target_port"] != int(result["chart_scheduler_bind_port"])
print("The documented 443 -> 9443 mapping is inconsistent with the chart's 443 listener and extender URL.")
PY

Repository: Project-HAMi/website

Length of output: 247


🏁 Script executed:

#!/bin/bash
set -eu

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

for file in values.yaml templates/scheduler/deployment.yaml templates/scheduler/service.yaml templates/scheduler/configmap.yaml; do
  mkdir -p "$tmpdir/$(dirname "$file")"
  curl -L --fail --silent --show-error \
    "https://raw.githubusercontent.com/Project-HAMi/HAMi/master/charts/hami/$file" \
    -o "$tmpdir/$file"
done

python3 - "$tmpdir" <<'PY'
import pathlib
import re
import sys

root = pathlib.Path(sys.argv[1])
values = (root / "values.yaml").read_text()
deployment = (root / "templates/scheduler/deployment.yaml").read_text()
service = (root / "templates/scheduler/service.yaml").read_text()
configmap = (root / "templates/scheduler/configmap.yaml").read_text()

# The chart default enables the TLS scheduler endpoint; comments may occur
# between the key and its value.
admission_enabled = bool(re.search(
    r"admissionWebhook:\s*(?:\n\s*#.*)*\n\s*enabled:\s*true", values
))
bind = re.search(r"--http_bind=0\.0\.0\.0:(\d+)", deployment)
container = re.search(r"containerPort:\s*(\d+)", deployment)
target = re.search(
    r"targetPort:\s*\{\{\s*\.Values\.scheduler\.service\.httpTargetPort", service
)
url = re.search(r'urlPrefix:\s*"https://127\.0\.0\.1:(\d+)"', configmap)

result = {
    "admission_webhook_default_enabled": admission_enabled,
    "documented_service_port": 443,
    "documented_service_target_port": 9443,
    "chart_scheduler_bind_port": bind.group(1) if bind else None,
    "chart_container_port": container.group(1) if container else None,
    "chart_configmap_url_port": url.group(1) if url else None,
    "service_target_is_configurable": bool(target),
}
print(result)

assert admission_enabled
assert result["chart_scheduler_bind_port"] == "443"
assert result["chart_container_port"] == "443"
assert result["chart_configmap_url_port"] == "443"
assert result["service_target_is_configurable"]
assert result["documented_service_target_port"] != int(result["chart_scheduler_bind_port"])
print("The documented 443 -> 9443 mapping is inconsistent with the chart's 443 listener and extender URL.")
PY

Repository: Project-HAMi/website

Length of output: 515


Align the OpenShift scheduler port contract

The chart binds the scheduler extender and container to 443, and the scheduler ConfigMap uses https://127.0.0.1:443. A Service target port of 9443 has no listener. Update the chart and both guides, or document httpTargetPort: 443 until the chart supports the complete port change.

📍 Affects 2 files
  • docs/installation/openshift-installation.md#L63-L65 (this comment)
  • i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md#L63-L65
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/installation/openshift-installation.md` around lines 63 - 65, Align the
OpenShift Service port documentation in
docs/installation/openshift-installation.md:63-65 and
i18n/zh/docusaurus-plugin-content-docs/current/installation/openshift-installation.md:63-65
with the chart’s scheduler listener and ConfigMap URL by documenting
httpTargetPort as 443, unless the chart is simultaneously updated to support
9443 end to end. Apply the same correction consistently in both guides.

Source: MCP tools


devicePlugin:
runtimeClassName: nvidia
nvidiaDriverRoot: /run/nvidia/driver

gpuOperatorToolkitReady:
enabled: true
hostPath: /run/nvidia/validations

nvidiaNodeSelector:
nvidia.com/gpu.present: "true"
```

To use the built-in OpenShift `privileged` SCC instead of creating a custom SCC:

```yaml
openshift:
securityContextConstraints:
create: false
name: privileged
```

Install HAMi:

```bash
helm repo add hami-charts https://project-hami.github.io/HAMi/
helm repo update

helm upgrade --install hami hami-charts/hami \
--namespace hami \
--create-namespace \
-f values-openshift.yaml
```

## Security model

### Scheduler and admission

Scheduler and admission run under OpenShift `restricted-v2` or an equivalent restricted SCC.

Security context applied when OpenShift is enabled:

```yaml
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefault
```

OpenShift assigns the container UID from the project UID range. On non-OpenShift clusters, the chart default remains `scheduler.patch.runAsUser: 2000`.

Port mapping:

```text
Service port 443 -> targetPort 9443 -> containerPort 9443
```

The Deployment, Service, and kube-scheduler extender ConfigMap use the same target port.

### Device plugin

The chart creates the `hami-device-plugin` SCC and grants it to the device-plugin ServiceAccount. The SCC allows:

- privileged container
- host PID
- hostPath
- `SYS_ADMIN` capability
- `RunAsAny` for UID and SELinux context

Allowed volume types: `configMap`, `downwardAPI`, `emptyDir`, `hostPath`, `projected`, `secret`. Host IPC, host network, and host ports remain off.

Scheduler, admission, and workload ServiceAccounts continue to use the platform restricted SCC.

## SELinux

Enable SELinux relabeling:

```yaml
selinux:
enabled: true
type: container_file_t
level: s0
```

The relabel initContainer applies `container_file_t` to HAMi shared directories:

```text
/usr/local/vgpu
/usr/local/vgpu/containers
/tmp/vgpulock
```

Restricted workload containers can then access these paths under the standard SELinux container domain.

The NVIDIA driver root is managed by the GPU Operator and mounted read-only into the device plugin and monitor:

```text
/run/nvidia/driver
```

SELinux labels on the driver root remain under GPU Operator ownership. After uninstall, restore host SELinux labels and directory permissions manually if the node requires it.

## Verification

Render manifests:

```bash
helm template hami hami-charts/hami \
--namespace hami \
-f values-openshift.yaml > /tmp/hami-openshift.yaml

grep -nE 'SecurityContextConstraints|system:openshift:scc|http_bind|targetPort|urlPrefix|runtimeClassName' \
/tmp/hami-openshift.yaml
```

Check rollout status:

```bash
oc rollout status deployment/hami-scheduler -n hami
oc rollout status daemonset/hami-device-plugin -n hami
```

Check assigned SCC:

```bash
oc get pods -n hami \
-o 'custom-columns=NAME:.metadata.name,SCC:.metadata.annotations.openshift\.io/scc'
```

Expected results:

- scheduler and admission: restricted SCC
- device-plugin: `hami-device-plugin` SCC
- scheduler extender listens on `9443`
- scheduler Service maps `443` to `9443`
- device-plugin uses the configured NVIDIA RuntimeClass
- SELinux relabel scope: HAMi shared directories

Check SELinux labels on the node:

```bash
oc debug node/<gpu-node> -- chroot /host \
ls -Zd /usr/local/vgpu /usr/local/vgpu/containers /tmp/vgpulock
```

HAMi shared directories should show the configured `container_file_t`.
Loading