Skip to content

Commit 27f4190

Browse files
authored
Merge pull request #169 from AMDResearch/develop
chore: merge develop to main
2 parents 055b1cc + 2147fcb commit 27f4190

52 files changed

Lines changed: 2944 additions & 284 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ The simplest way to deploy AUP Learning Cloud on a single machine in a developme
4242

4343
```bash
4444
# Ryzen AI APU only: OEM kernel for ROCm on Ubuntu 24.04 (reboot required)
45-
sudo apt update && sudo apt install linux-image-6.14.0-1018-oem
45+
sudo apt update && sudo apt install linux-oem-6.14
4646

4747
# Install Docker
4848
curl -fsSL https://get.docker.com | sh

auplc_installer/rocm.py

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,8 @@ def _wait_daemonset_ready(name: str) -> None:
5858

5959

6060
def _patch_image_pull_policy(daemonset: str) -> None:
61-
"""For offline mode: avoid pulling the device plugin image from a registry."""
62-
patch = '[{"op":"replace","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}]'
61+
"""Avoid pulling ROCm DaemonSet images when they already exist locally."""
62+
patch = '[{"op":"add","path":"/spec/template/spec/containers/0/imagePullPolicy","value":"IfNotPresent"}]'
6363
run(
6464
[
6565
"kubectl",
@@ -90,7 +90,6 @@ def deploy_rocm_gpu_device_plugin(*, offline_mode: bool, bundle_dir: Path | None
9090
str(bundle_dir / "manifests/k8s-ds-amdgpu-dp.yaml"),
9191
]
9292
)
93-
_patch_image_pull_policy("amdgpu-device-plugin-daemonset")
9493
else:
9594
url = (
9695
"https://raw.githubusercontent.com/ROCm/k8s-device-plugin/"
@@ -101,9 +100,11 @@ def deploy_rocm_gpu_device_plugin(*, offline_mode: bool, bundle_dir: Path | None
101100
verify_sha256(tmp, ROCM_DEVICE_PLUGIN_SHA256)
102101
run(["kubectl", "create", "-f", tmp])
103102
os.remove(tmp)
104-
_wait_daemonset_ready("amdgpu-device-plugin-daemonset")
105103
log("Successfully deployed ROCm GPU device plugin.")
106104

105+
_patch_image_pull_policy("amdgpu-device-plugin-daemonset")
106+
_wait_daemonset_ready("amdgpu-device-plugin-daemonset")
107+
107108
deploy_rocm_gpu_node_labeller(offline_mode=offline_mode, bundle_dir=bundle_dir)
108109

109110

@@ -117,28 +118,27 @@ def deploy_rocm_gpu_node_labeller(*, offline_mode: bool, bundle_dir: Path | None
117118
log("Deploying ROCm GPU node labeller...")
118119
if _exists_daemonset("amdgpu-labeller-daemonset"):
119120
log("ROCm GPU node labeller already exists.")
120-
return
121-
122-
if offline_mode and bundle_dir is not None:
123-
run(
124-
[
125-
"kubectl",
126-
"create",
127-
"-f",
128-
str(bundle_dir / "manifests/k8s-ds-amdgpu-labeller.yaml"),
129-
]
130-
)
131-
_patch_image_pull_policy("amdgpu-labeller-daemonset")
132121
else:
133-
url = (
134-
"https://raw.githubusercontent.com/ROCm/k8s-device-plugin/"
135-
f"{ROCM_DEVICE_PLUGIN_COMMIT}/k8s-ds-amdgpu-labeller.yaml"
136-
)
137-
tmp = "/tmp/k8s-ds-amdgpu-labeller.yaml"
138-
run(["wget", url, "-O", tmp])
139-
verify_sha256(tmp, ROCM_LABELLER_SHA256)
140-
run(["kubectl", "create", "-f", tmp])
141-
os.remove(tmp)
122+
if offline_mode and bundle_dir is not None:
123+
run(
124+
[
125+
"kubectl",
126+
"create",
127+
"-f",
128+
str(bundle_dir / "manifests/k8s-ds-amdgpu-labeller.yaml"),
129+
]
130+
)
131+
else:
132+
url = (
133+
"https://raw.githubusercontent.com/ROCm/k8s-device-plugin/"
134+
f"{ROCM_DEVICE_PLUGIN_COMMIT}/k8s-ds-amdgpu-labeller.yaml"
135+
)
136+
tmp = "/tmp/k8s-ds-amdgpu-labeller.yaml"
137+
run(["wget", url, "-O", tmp])
138+
verify_sha256(tmp, ROCM_LABELLER_SHA256)
139+
run(["kubectl", "create", "-f", tmp])
140+
os.remove(tmp)
141+
log("Successfully deployed ROCm GPU node labeller.")
142142

143+
_patch_image_pull_policy("amdgpu-labeller-daemonset")
143144
_wait_daemonset_ready("amdgpu-labeller-daemonset")
144-
log("Successfully deployed ROCm GPU node labeller.")

deploy/ansible/playbooks/pb-pxe-controller.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,8 @@
7272
pxe_apt_mirror: "http://tw.archive.ubuntu.com/ubuntu"
7373

7474
pxe_rootfs_packages:
75-
- linux-image-6.14.0-1018-oem
76-
- linux-headers-6.14.0-1018-oem
75+
- linux-image-oem-6.14
76+
- linux-headers-oem-6.14
7777
- initramfs-tools
7878
- linux-firmware
7979
- nfs-common

deploy/ansible/roles/pxe_controller/defaults/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,8 @@ pxe_web_port: 8080
106106
pxe_ubuntu_codename: "noble"
107107

108108
pxe_rootfs_packages:
109-
- linux-image-6.14.0-1018-oem
110-
- linux-headers-6.14.0-1018-oem
109+
- linux-image-oem-6.14
110+
- linux-headers-oem-6.14
111111
- initramfs-tools
112112
- linux-firmware
113113
- nfs-common
2.33 MB
Loading

dockerfiles/Base/Dockerfile.cpu

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,3 +94,5 @@ COPY --from=runtime-status-builder --chown=1000:100 /build/runtime/notebook/jupy
9494

9595
RUN python3 -mpip install --no-cache-dir /tmp/auplc-jupyterlab-runtime-status && \
9696
rm -rf /tmp/auplc-jupyterlab-runtime-status
97+
98+
WORKDIR /home/jovyan

dockerfiles/Base/Dockerfile.rocm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
129129
run-one \
130130
libatomic1 \
131131
unzip \
132+
libavutil-dev \
132133
&& apt-get clean \
133134
&& rm -rf /var/lib/apt/lists/*
134135

@@ -266,6 +267,6 @@ RUN echo '#!/bin/bash' > /entrypoint.sh && \
266267
EXPOSE 8888
267268

268269
USER $NB_UID
269-
WORKDIR /opt/workspace
270+
WORKDIR /home/jovyan
270271

271272
CMD ["/bin/bash", "/entrypoint.sh"]

dockerfiles/Base/README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,32 @@ docker build \
8686
docker build -t ghcr.io/amdresearch/auplc-default:latest --file Dockerfile.cpu .
8787
```
8888

89+
## Resource Path Contract
90+
91+
Resource metadata in `runtime/values.yaml` can set `defaultPath` for the
92+
initial landing path inside the container. It controls where JupyterLab or
93+
code-server opens first. It is not a security boundary, an access boundary, or a
94+
runtime guarantee that the directory exists.
95+
96+
The Hub chooses the target path in this order:
97+
98+
1. Custom Repo clone path, when the user supplies a repository.
99+
2. Resource `defaultPath`, when configured.
100+
3. The image or single-user application default, normally the image `WORKDIR`.
101+
102+
For official images, keep `custom.resources.metadata.<resource>.defaultPath` in
103+
sync with the image `WORKDIR`. Check the local image contracts with:
104+
105+
```bash
106+
make -C dockerfiles verify-resource-contracts
107+
```
108+
109+
That verifier checks the official image contract. Runtime spawning still does
110+
not check path existence for arbitrary or custom images. If an environment
111+
points at a custom image, make sure the configured `defaultPath` exists in that
112+
image, or omit `defaultPath` to let the image `WORKDIR` control the initial
113+
folder.
114+
89115
## Generic Code Images
90116

91117
The base images remain the foundation for notebook and coding environments. Generic code-server images are built separately from `dockerfiles/Code/`:

dockerfiles/Code/README.md

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ those tools to Base notebook images or Course images. CPU and GPU Code images
3838
are built from the same Dockerfile and differ only by `BASE_IMAGE`, so the Code
3939
layer stays consistent across hardware targets.
4040

41-
The default Hub resource keys are `code-cpu` and `code-gpu`. Code-server launch behavior is configured through `custom.resources.metadata.<resource>.launchMode: code-server`, alongside the same `custom.resources.images`, `custom.resources.requirements`, and `custom.teams.mapping` model as notebook resources in `runtime/values.yaml`.
41+
The default Hub resource keys are `code-cpu` and `code-gpu`. Code-server launch behavior is configured through `custom.resources.metadata.<resource>.launchMode: code-server`, alongside the same `custom.resources.images`, `custom.resources.requirements`, `custom.resources.metadata.<resource>.defaultPath`, and `custom.teams.mapping` model as notebook resources in `runtime/values.yaml`.
4242

4343
## Build Commands
4444

@@ -86,7 +86,7 @@ images continue to share one package set and the Dockerfile remains small.
8686
The start script launches:
8787

8888
```bash
89-
code-server --auth none --bind-addr 127.0.0.1:8889 --ignore-last-opened "${AUPLC_CODE_WORKDIR:-/home/jovyan}"
89+
code-server --auth none --bind-addr 127.0.0.1:8889 --ignore-last-opened "${AUPLC_CODE_WORKDIR:-$(pwd)}"
9090
nginx -c /tmp/auplc-code-server-nginx.conf -g 'daemon off;'
9191
```
9292

@@ -96,6 +96,14 @@ code-server on loopback. The proxy must preserve the full browser `Host` value
9696
with `X-Forwarded-Host` so code-server's WebSocket origin check succeeds behind
9797
JupyterHub and NodePort-style local URLs.
9898

99+
`defaultPath` is the initial landing path for a resource. The target path is
100+
chosen in this order: Custom Repo clone path, resource `defaultPath`, then the
101+
image or single-user application default, normally the image `WORKDIR`. Omitted
102+
and `null` `defaultPath` values do not force a Hub landing override; empty
103+
strings are invalid; `/` means land at the container root. This setting doesn't
104+
limit what users can access. It only selects the first workspace shown by the
105+
application.
106+
99107
Git, Node.js LTS, `npm`, `npx`, `corepack`, pinned `pnpm`, TypeScript/frontend
100108
helpers, Pixi, and native build tools are installed in the image so cloned
101109
projects can use source control, frontend workflows, sudo-free user package
@@ -108,6 +116,13 @@ home directory instead of `/usr/local`: `NPM_CONFIG_PREFIX` defaults to
108116
This lets users install small project CLIs with commands such as
109117
`npm install -g cowsay` without sudo or write access to system directories.
110118

119+
For code-server's outgoing link protection, the Hub automatically injects the
120+
current public Hub host into `AUPLC_CODE_TRUSTED_DOMAINS` so the built-in
121+
Back-to-Hub action can open `/hub/home` without an external-site confirmation.
122+
Deployments that intentionally open additional trusted sites can set
123+
`custom.codeServer.extraTrustedDomains` to host/domain entries such as
124+
`docs.example.edu`; do not include URL schemes or paths.
125+
111126
Pixi is provided as the sudo-free, apt-like package manager for user-space
112127
native tools and project environments. The image writes `/etc/pixi/config.toml`
113128
so requests for `https://conda.anaconda.org/conda-forge` are redirected to the
@@ -138,7 +153,28 @@ downgrading a user-installed newer copy.
138153

139154
`--auth none` is acceptable only because JupyterHub and the JupyterHub proxy remain the authentication boundary. The user pod's port `8888` must stay private to the Hub/proxy path and must not be exposed directly through an unauthenticated service, ingress, or port-forward shared with untrusted users.
140155

141-
When users provide a Git repository on the spawn form, the existing init-container clone flow is reused. For resources with `launchMode: code-server`, the spawner points `AUPLC_CODE_WORKDIR` and the code-server `folder` URL parameter at the cloned directory so code-server opens the repository workspace. The launcher also passes `--ignore-last-opened` so a persisted previous workspace cannot override the requested folder.
156+
When users provide a Git repository on the spawn form, the existing init-container clone flow is reused. For resources with `launchMode: code-server`, the spawner points `AUPLC_CODE_WORKDIR` at the cloned directory or explicit resource target path, and the launcher starts code-server with that folder so it opens the requested workspace. If neither Custom Repo nor `defaultPath` is set, the launcher opens the image `WORKDIR`. The launcher also passes `--ignore-last-opened` so a persisted previous workspace cannot override the requested folder.
157+
158+
A direct code-server URL with `?folder=<path>` works when the browser reaches
159+
the proxied code-server root route. Hub spawn completion, however, redirects the
160+
browser to the server base URL, and code-server doesn't consume
161+
`JUPYTERHUB_DEFAULT_URL` by itself. AUPLC therefore keeps `AUPLC_CODE_WORKDIR` as
162+
the reliable adapter between Hub resource selection and the code-server process.
163+
The local proof is recorded in
164+
`.sisyphus/evidence/task-1-codeserver-default-url-proof.md`.
165+
166+
Official Code images are checked by the resource contract verifier:
167+
168+
```bash
169+
make -C dockerfiles verify-resource-contracts
170+
```
171+
172+
The verifier checks official image metadata, image `WORKDIR`, path existence,
173+
and the code-server launcher contract. Runtime spawning doesn't check path
174+
existence for arbitrary or custom images. If a custom code image sets a
175+
`defaultPath`, create that path in the image or code-server may show its own
176+
landing error. If the image already declares the desired `WORKDIR`, omit
177+
`defaultPath` to preserve it.
142178

143179
## Extensions
144180

dockerfiles/Code/start-code-server.sh

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ export NPM_CONFIG_PREFIX="${NPM_CONFIG_PREFIX:-${HOME:-/home/jovyan}/.local}"
1010
public_port="${PORT:-8888}"
1111
code_server_port="${AUPLC_CODE_SERVER_PORT:-8889}"
1212
service_prefix="${JUPYTERHUB_SERVICE_PREFIX:-/}"
13-
workdir="${AUPLC_CODE_WORKDIR:-/home/jovyan}"
13+
# Without a Hub-provided launch override, open code-server in the image WORKDIR.
14+
workdir="${AUPLC_CODE_WORKDIR:-$(pwd)}"
1415
extensions_list="${AUPLC_CODE_EXTENSIONS_LIST:-/opt/auplc/extensions/extensions.txt}"
1516
local_extensions_dir="${AUPLC_CODE_LOCAL_EXTENSIONS_DIR:-/opt/auplc/extensions/local}"
1617
extensions_dir="${AUPLC_CODE_EXTENSIONS_DIR:-/home/jovyan/.local/share/code-server/extensions}"
18+
trusted_domains="${AUPLC_CODE_TRUSTED_DOMAINS:-}"
1719

1820
mkdir -p "${NPM_CONFIG_PREFIX}/bin"
1921
mkdir -p "${PIXI_HOME}/bin"
@@ -49,6 +51,28 @@ seed_builtin_extensions() {
4951
fi
5052
}
5153

54+
trim() {
55+
local value="$1"
56+
value="${value#"${value%%[![:space:]]*}"}"
57+
value="${value%"${value##*[![:space:]]}"}"
58+
printf '%s' "${value}"
59+
}
60+
61+
build_trusted_domain_args() {
62+
local domains_csv="$1"
63+
local -n output_args="$2"
64+
local -a domains=()
65+
local domain
66+
67+
IFS=',' read -ra domains <<<"${domains_csv}"
68+
for domain in "${domains[@]}"; do
69+
domain="$(trim "${domain}")"
70+
if [ -n "${domain}" ]; then
71+
output_args+=(--link-protection-trusted-domains "${domain}")
72+
fi
73+
done
74+
}
75+
5276
case "${service_prefix}" in
5377
/*) ;;
5478
*) service_prefix="/${service_prefix}" ;;
@@ -65,6 +89,8 @@ nginx_conf="/tmp/auplc-code-server-nginx.conf"
6589
redirect_block=""
6690

6791
seed_builtin_extensions
92+
trusted_domain_args=()
93+
build_trusted_domain_args "${trusted_domains}" trusted_domain_args
6894

6995
if [ "${service_prefix}" != "/" ]; then
7096
redirect_block="
@@ -120,6 +146,7 @@ code-server \
120146
--auth none \
121147
--bind-addr "127.0.0.1:${code_server_port}" \
122148
--extensions-dir "${extensions_dir}" \
149+
"${trusted_domain_args[@]}" \
123150
--ignore-last-opened \
124151
"${workdir}" &
125152
code_server_pid="$!"

0 commit comments

Comments
 (0)