mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-20 06:00:06 +08:00
* feat(snap): run gateway as a user service by default Replace the existing system `gateway` service with separate `user-gateway` and `system-gateway` services. For new installs, only the `user-gateway` service is enabled. For existing installs, only the `system-gateway` service is enabled. Existing installs continue to have a one-time migration which removes legacy insecure configurations. It is up to `install.sh` or users to manually copy mTLS credentials from the root-owned `$SNAP_COMMON/tls` to the invoking user's OpenShell snap directory. This is explained in the snap description in `snapcraft.yaml`, visible in the Snap Store listing and via `snap info openshell`. Service enablement is now managed by a new snap configuration option named `gateway-mode`, so users can switch from one mode to another via e.g. `sudo snap set openshell gateway-mode=user`. The `install` and `post-refresh` hooks select which service to start by setting this mode. If the `gateway-mode` is already set, then we know the one-time migration has already taken place. Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * fix(snap): simplify the snap gateway wrapper Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * fix(snap): use `snap start --user` when running as non-root Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * fix(snap): ensure install.sh waits for the correct docker daemon Signed-off-by: Oliver Calder <oliver.calder@canonical.com> * feat(snap): document how to safely switch between gateway modes New installs run the gateway as a user service by default, while upgrades of existing installs from prior to the user gateway continue to use the existing system gateway. The user and system gateway are fundamentally different services and there is no way to migrate state between them. However, it may be desirable for a user to run the gateway as a system service after a fresh install. To support this, this commit adds documentation for how to safely transition from the user gateway mode to the system gateway mode, and vice versa. Also, clarifies when it is necessary to manually copy TLS credentials from the system gateway state directory to the user's home directory: when upgrading a pre-TLS snap installation, or when switching from user to system gateway mode. When upgrading an existing install with an HTTPS gateway running as a system service, copying credentials again is not necessary. Signed-off-by: Oliver Calder <oliver.calder@canonical.com> --------- Signed-off-by: Oliver Calder <oliver.calder@canonical.com>
476 lines
18 KiB
YAML
476 lines
18 KiB
YAML
name: Release Canary
|
|
|
|
on:
|
|
workflow_dispatch:
|
|
workflow_run:
|
|
workflows: ["Release Dev"]
|
|
types: [completed]
|
|
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
|
|
defaults:
|
|
run:
|
|
shell: bash
|
|
|
|
env:
|
|
OPENSHELL_VERSION: dev
|
|
OPENSHELL_TELEMETRY_ENABLED: "false"
|
|
|
|
jobs:
|
|
macos:
|
|
name: macOS Homebrew
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: macos-latest-xlarge
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Ensure VM driver
|
|
run: |
|
|
launchctl setenv OPENSHELL_COMPUTE_DRIVER vm
|
|
launchctl setenv OPENSHELL_TELEMETRY_ENABLED "$OPENSHELL_TELEMETRY_ENABLED"
|
|
|
|
- name: Install and check gateway status
|
|
run: |
|
|
set -euo pipefail
|
|
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh | sh
|
|
openshell --version
|
|
openshell status
|
|
|
|
# GitHub-hosted macOS runners do not expose the Hypervisor.framework
|
|
# support libkrun needs. Sandbox launch is covered by the VM E2E lane.
|
|
- name: Collect Homebrew diagnostics
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
brew services info openshell
|
|
for log in \
|
|
"$(brew --prefix)/var/log/openshell/openshell-gateway.out.log" \
|
|
"$(brew --prefix)/var/log/openshell/openshell-gateway.err.log"; do
|
|
if [ -f "$log" ]; then
|
|
echo "--- $log ---"
|
|
tail -n 300 "$log"
|
|
fi
|
|
done
|
|
|
|
ubuntu-deb:
|
|
name: Ubuntu DEB
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Remove snapd
|
|
run: |
|
|
set -euo pipefail
|
|
snaps=$(snap list 2>/dev/null | awk 'NR > 1 { print $1 }')
|
|
if [ -n "$snaps" ]; then
|
|
sudo snap remove $snaps
|
|
fi
|
|
sudo apt-get -y --purge remove snapd
|
|
! command -v snap >/dev/null 2>&1
|
|
|
|
- name: Ensure Docker
|
|
run: |
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker.io
|
|
fi
|
|
sudo systemctl start docker || sudo service docker start
|
|
mkdir -p "${HOME}/.config/openshell"
|
|
printf 'OPENSHELL_COMPUTE_DRIVER=docker\nOPENSHELL_TELEMETRY_ENABLED=%s\n' \
|
|
"$OPENSHELL_TELEMETRY_ENABLED" > "${HOME}/.config/openshell/gateway.env"
|
|
docker info
|
|
|
|
- name: Install and check status
|
|
run: |
|
|
set -euo pipefail
|
|
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh | sh
|
|
openshell status
|
|
sandbox="rc-${GITHUB_RUN_ID}"
|
|
openshell sandbox create --name "$sandbox" --detach
|
|
openshell sandbox exec --name "$sandbox" --no-tty -- true
|
|
openshell sandbox delete "$sandbox"
|
|
|
|
fedora:
|
|
name: Fedora RPM
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: linux-amd64-cpu8
|
|
timeout-minutes: 20
|
|
env:
|
|
FEDORA_CANARY_CONTAINER: openshell-fedora-canary-${{ github.run_id }}-${{ github.run_attempt }}
|
|
steps:
|
|
- name: Start Fedora systemd container and root user manager
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
docker run --detach \
|
|
--name "${FEDORA_CANARY_CONTAINER}" \
|
|
--privileged \
|
|
--cgroupns=host \
|
|
--tmpfs /run \
|
|
--tmpfs /tmp \
|
|
--volume /sys/fs/cgroup:/sys/fs/cgroup:rw \
|
|
fedora:latest \
|
|
bash -lc 'dnf install -y curl dbus-daemon podman systemd && exec /usr/sbin/init'
|
|
|
|
for _ in $(seq 1 120); do
|
|
if docker exec "${FEDORA_CANARY_CONTAINER}" systemctl list-units --no-pager >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
if [ "$(docker inspect -f '{{.State.Running}}' "${FEDORA_CANARY_CONTAINER}")" != "true" ]; then
|
|
echo "::error::Fedora systemd container exited before systemd became reachable"
|
|
docker logs "${FEDORA_CANARY_CONTAINER}" >&2 || true
|
|
exit 1
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
if ! docker exec "${FEDORA_CANARY_CONTAINER}" systemctl list-units --no-pager >/dev/null 2>&1; then
|
|
echo "::error::Fedora systemd container did not become reachable within 120s"
|
|
docker logs "${FEDORA_CANARY_CONTAINER}" >&2 || true
|
|
exit 1
|
|
fi
|
|
|
|
docker exec --interactive "${FEDORA_CANARY_CONTAINER}" env \
|
|
HOME=/root \
|
|
XDG_RUNTIME_DIR=/run/user/0 \
|
|
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus \
|
|
bash -s <<'EOF'
|
|
set -euo pipefail
|
|
# install.sh manages the RPM gateway as a systemd user unit. This
|
|
# container is booted with systemd as PID 1, but it still has no
|
|
# login session. Start root's user manager explicitly so the
|
|
# installer can test service restart and gateway registration
|
|
# instead of its "restart later" fallback.
|
|
mkdir -p "${XDG_RUNTIME_DIR}"
|
|
chmod 700 "${XDG_RUNTIME_DIR}"
|
|
systemctl start user-runtime-dir@0.service || true
|
|
systemctl start user@0.service
|
|
|
|
for _ in $(seq 1 30); do
|
|
if systemctl --user daemon-reload; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
if ! systemctl --user daemon-reload; then
|
|
systemctl status user@0.service --no-pager >&2 || true
|
|
journalctl -u user@0.service --no-pager -n 80 >&2 || true
|
|
systemctl --user status --no-pager >&2 || true
|
|
exit 1
|
|
fi
|
|
EOF
|
|
|
|
- name: Install and check status
|
|
run: |
|
|
set -euo pipefail
|
|
|
|
docker exec --interactive "${FEDORA_CANARY_CONTAINER}" env \
|
|
HOME=/root \
|
|
XDG_RUNTIME_DIR=/run/user/0 \
|
|
DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/0/bus \
|
|
OPENSHELL_VERSION="$OPENSHELL_VERSION" \
|
|
OPENSHELL_TELEMETRY_ENABLED="$OPENSHELL_TELEMETRY_ENABLED" \
|
|
CANARY_SANDBOX="rc-${GITHUB_RUN_ID}" \
|
|
INSTALL_SH_URL="https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh" \
|
|
bash -s <<'EOF'
|
|
set -euo pipefail
|
|
mkdir -p "${HOME}/.config/openshell"
|
|
printf 'OPENSHELL_COMPUTE_DRIVER=podman\nOPENSHELL_TELEMETRY_ENABLED=%s\n' \
|
|
"$OPENSHELL_TELEMETRY_ENABLED" > "${HOME}/.config/openshell/gateway.env"
|
|
podman info
|
|
curl -LsSf "${INSTALL_SH_URL}" | sh
|
|
openshell status
|
|
openshell sandbox create --name "$CANARY_SANDBOX" --detach
|
|
openshell sandbox exec --name "$CANARY_SANDBOX" --no-tty -- true
|
|
openshell sandbox delete "$CANARY_SANDBOX"
|
|
EOF
|
|
|
|
- name: Stop Fedora systemd container
|
|
if: always()
|
|
run: |
|
|
docker rm -f "${FEDORA_CANARY_CONTAINER}" >/dev/null 2>&1 || true
|
|
|
|
ubuntu-snap-system-docker:
|
|
name: Ubuntu Snap with system Docker
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
OPENSHELL_INSTALL_METHOD: snap
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Install snapd
|
|
run: |
|
|
set -euo pipefail
|
|
sudo apt-get update
|
|
sudo apt-get install -y snapd
|
|
sudo systemctl enable --now snapd.socket
|
|
sudo systemctl start snapd
|
|
sudo snap wait system seed.loaded
|
|
|
|
- name: Ensure Docker
|
|
run: |
|
|
set -euo pipefail
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
sudo apt-get update
|
|
sudo apt-get install -y docker.io
|
|
fi
|
|
sudo systemctl start docker || sudo service docker start
|
|
docker info
|
|
|
|
- name: Install and check status
|
|
run: |
|
|
set -euo pipefail
|
|
systemctl --user set-environment \
|
|
"OPENSHELL_TELEMETRY_ENABLED=${OPENSHELL_TELEMETRY_ENABLED}"
|
|
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh |
|
|
DOCKER_HOST=unix:///tmp/nonexistent-docker.sock DOCKER_CONTEXT=nonexistent sh
|
|
sudo snap list openshell
|
|
if sudo snap list docker >/dev/null 2>&1; then
|
|
echo "install.sh unexpectedly installed the Docker snap" >&2
|
|
exit 1
|
|
fi
|
|
snap info openshell | grep -E '^tracking: +latest/edge$'
|
|
docker info
|
|
sudo snap connections openshell | grep -E '^docker +openshell:docker +:docker +'
|
|
openshell --version
|
|
openshell.prover --version
|
|
snap services openshell
|
|
systemctl --user is-enabled --quiet snap.openshell.user-gateway.service
|
|
systemctl --user is-active snap.openshell.user-gateway.service
|
|
! systemctl is-enabled --quiet snap.openshell.system-gateway.service
|
|
! systemctl is-active --quiet snap.openshell.system-gateway.service
|
|
journalctl --user -u snap.openshell.user-gateway.service --no-pager |
|
|
grep -F "mTLS user authentication enabled"
|
|
openshell gateway list | grep -F "https://127.0.0.1:17670"
|
|
openshell status
|
|
|
|
- name: Check a policy boundary with the Snap prover
|
|
run: |
|
|
set -euo pipefail
|
|
prover_dir=$(mktemp -d "$HOME/openshell-prover-canary.XXXXXX")
|
|
trap 'rm -rf "$prover_dir"' EXIT
|
|
cat >"$prover_dir/boundary.yaml" <<'EOF'
|
|
version: 1
|
|
filesystem_policy:
|
|
read_only:
|
|
- /usr
|
|
- /etc
|
|
EOF
|
|
cat >"$prover_dir/candidate.yaml" <<'EOF'
|
|
version: 1
|
|
filesystem_policy:
|
|
read_only:
|
|
- /usr
|
|
EOF
|
|
result=$(openshell.prover check "$prover_dir/candidate.yaml" \
|
|
--boundary "$prover_dir/boundary.yaml")
|
|
grep -q '^result: within_boundary$' <<<"$result"
|
|
|
|
- name: Create and exercise a sandbox
|
|
run: |
|
|
set -euo pipefail
|
|
sandbox="rc-${GITHUB_RUN_ID}"
|
|
openshell sandbox create --name "$sandbox" --detach
|
|
openshell sandbox exec --name "$sandbox" --no-tty -- true
|
|
openshell sandbox delete "$sandbox"
|
|
|
|
- name: Collect Snap diagnostics
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
docker info
|
|
sudo systemctl status docker.service --no-pager
|
|
sudo journalctl -b -u docker.service --no-pager -n 300
|
|
sudo snap services openshell
|
|
sudo snap connections openshell
|
|
sudo snap changes
|
|
systemctl --user status snap.openshell.user-gateway.service --no-pager
|
|
journalctl --user -u snap.openshell.user-gateway.service --no-pager -n 300
|
|
sudo systemctl status snap.openshell.system-gateway.service --no-pager
|
|
sudo journalctl -b -u snap.openshell.system-gateway.service --no-pager -n 300
|
|
sudo journalctl -b -u snapd.service --no-pager -n 300
|
|
snap logs openshell.user-gateway -n=300
|
|
sudo ss -ltnp '( sport = :17670 )'
|
|
|
|
ubuntu-snap-docker-preflight:
|
|
name: Ubuntu Snap Docker preflight
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
OPENSHELL_INSTALL_METHOD: snap
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Install snapd
|
|
run: |
|
|
set -euo pipefail
|
|
sudo apt-get update
|
|
sudo apt-get install -y snapd
|
|
sudo systemctl enable --now snapd.socket
|
|
sudo systemctl start snapd
|
|
sudo snap wait system seed.loaded
|
|
|
|
- name: Remove system Docker
|
|
run: |
|
|
set -euo pipefail
|
|
sudo systemctl stop docker.service docker.socket 2>/dev/null || true
|
|
sudo apt-get -y --purge remove docker-ce docker-ce-cli containerd.io \
|
|
docker-buildx-plugin docker-compose-plugin docker.io 2>/dev/null || true
|
|
! command -v docker >/dev/null 2>&1
|
|
|
|
- name: Verify installer rejects missing Docker
|
|
run: |
|
|
set -euo pipefail
|
|
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh -o install.sh
|
|
if OPENSHELL_VERSION=dev sh install.sh >install.log 2>&1; then
|
|
echo "install.sh unexpectedly succeeded without Docker" >&2
|
|
cat install.log >&2
|
|
exit 1
|
|
fi
|
|
cat install.log
|
|
grep -F "Docker is required before installing the OpenShell snap" install.log
|
|
! sudo snap list docker >/dev/null 2>&1
|
|
! sudo snap list openshell >/dev/null 2>&1
|
|
|
|
- name: Verify installer rejects Docker snap
|
|
run: |
|
|
set -euo pipefail
|
|
sudo snap install docker
|
|
if OPENSHELL_VERSION=dev sh install.sh >docker-snap.log 2>&1; then
|
|
echo "install.sh unexpectedly succeeded with the Docker snap" >&2
|
|
cat docker-snap.log >&2
|
|
exit 1
|
|
fi
|
|
cat docker-snap.log
|
|
grep -F "the Docker snap is not currently compatible with OpenShell" docker-snap.log
|
|
sudo snap list docker >/dev/null
|
|
! sudo snap list openshell >/dev/null 2>&1
|
|
|
|
- name: Collect Snap diagnostics
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
cat install.log
|
|
cat docker-snap.log
|
|
sudo snap changes
|
|
sudo journalctl -b -u snapd.service --no-pager -n 300
|
|
|
|
kubernetes:
|
|
name: Kubernetes Helm (kind)
|
|
if: ${{ github.event_name == 'workflow_dispatch' || github.event.workflow_run.conclusion == 'success' }}
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
env:
|
|
KIND_CLUSTER_NAME: release-canary-${{ github.run_id }}
|
|
RELEASE_NAME: openshell
|
|
RELEASE_NAMESPACE: openshell
|
|
KIND_GATEWAY_NAME: kind
|
|
AGENT_SANDBOX_VERSION: v1.0.3
|
|
steps:
|
|
- name: Checkout Agent Sandbox helper
|
|
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
with:
|
|
sparse-checkout: |
|
|
e2e/support/install-agent-sandbox.sh
|
|
sparse-checkout-cone-mode: false
|
|
persist-credentials: false
|
|
|
|
- name: Install Helm
|
|
uses: azure/setup-helm@9bc31f4ebc9c6b171d7bfbaa5d006ae7abdb4310 # v5.0.1
|
|
|
|
- name: Create kind cluster
|
|
uses: helm/kind-action@ef37e7f390d99f746eb8b610417061a60e82a6cc # v1.14.0
|
|
with:
|
|
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
|
|
wait: 120s
|
|
|
|
- name: Install Agent Sandbox controller
|
|
run: bash e2e/support/install-agent-sandbox.sh
|
|
|
|
- name: Install OpenShell Helm chart from GHCR OCI
|
|
run: |
|
|
set -euo pipefail
|
|
helm install "$RELEASE_NAME" oci://ghcr.io/nvidia/openshell/helm-chart \
|
|
--version 0.0.0-dev \
|
|
--namespace "$RELEASE_NAMESPACE" --create-namespace \
|
|
--set server.disableTls=true \
|
|
--set server.auth.allowUnauthenticatedUsers=true \
|
|
--set "server.telemetryEnabled=${OPENSHELL_TELEMETRY_ENABLED}" \
|
|
--wait --timeout 5m
|
|
|
|
- name: Verify gateway pod is Ready
|
|
run: |
|
|
set -euo pipefail
|
|
kubectl wait --namespace "$RELEASE_NAMESPACE" \
|
|
--for=condition=Ready pod \
|
|
--selector="app.kubernetes.io/name=openshell,app.kubernetes.io/instance=${RELEASE_NAME}" \
|
|
--timeout=300s
|
|
|
|
- name: Port-forward gateway service
|
|
run: |
|
|
set -euo pipefail
|
|
nohup kubectl port-forward --namespace "$RELEASE_NAMESPACE" \
|
|
"svc/${RELEASE_NAME}" 8080:8080 \
|
|
> port-forward.log 2>&1 &
|
|
echo $! > port-forward.pid
|
|
for _ in $(seq 1 30); do
|
|
if (echo > /dev/tcp/127.0.0.1/8080) >/dev/null 2>&1; then
|
|
echo "port-forward is reachable"
|
|
exit 0
|
|
fi
|
|
sleep 1
|
|
done
|
|
echo "port-forward did not become reachable" >&2
|
|
cat port-forward.log >&2
|
|
exit 1
|
|
|
|
- name: Remove snapd
|
|
run: |
|
|
set -euo pipefail
|
|
snaps=$(snap list 2>/dev/null | awk 'NR > 1 { print $1 }')
|
|
if [ -n "$snaps" ]; then
|
|
sudo snap remove $snaps
|
|
fi
|
|
sudo apt-get -y --purge remove snapd
|
|
! command -v snap >/dev/null 2>&1
|
|
|
|
- name: Install OpenShell CLI
|
|
run: |
|
|
set -euo pipefail
|
|
mkdir -p "${HOME}/.config/openshell"
|
|
printf 'OPENSHELL_COMPUTE_DRIVER=docker\n' > "${HOME}/.config/openshell/gateway.env"
|
|
curl -LsSf https://raw.githubusercontent.com/NVIDIA/OpenShell/${{ github.event.workflow_run.head_sha || github.sha }}/install.sh | sh
|
|
|
|
- name: Register kind gateway and check status
|
|
run: |
|
|
set -euo pipefail
|
|
openshell gateway add http://127.0.0.1:8080 --local --name "$KIND_GATEWAY_NAME"
|
|
openshell status
|
|
sandbox="rc-${GITHUB_RUN_ID}"
|
|
openshell sandbox create --name "$sandbox" --detach
|
|
openshell sandbox exec --name "$sandbox" --no-tty -- true
|
|
openshell sandbox delete "$sandbox"
|
|
|
|
- name: Diagnostics on failure
|
|
if: failure()
|
|
run: |
|
|
set +e
|
|
echo "--- helm status ---"
|
|
helm status "$RELEASE_NAME" --namespace "$RELEASE_NAMESPACE"
|
|
echo "--- helm get manifest ---"
|
|
helm get manifest "$RELEASE_NAME" --namespace "$RELEASE_NAMESPACE"
|
|
echo "--- get all ---"
|
|
kubectl get all --namespace "$RELEASE_NAMESPACE"
|
|
echo "--- describe pods ---"
|
|
kubectl describe pods --namespace "$RELEASE_NAMESPACE"
|
|
echo "--- pod logs ---"
|
|
kubectl logs --namespace "$RELEASE_NAMESPACE" \
|
|
--selector="app.kubernetes.io/name=openshell,app.kubernetes.io/instance=${RELEASE_NAME}" \
|
|
--tail=200 --all-containers --prefix
|
|
echo "--- port-forward log ---"
|
|
cat port-forward.log 2>/dev/null
|
|
echo "--- openshell gateway list ---"
|
|
openshell gateway list 2>/dev/null
|
|
echo "--- openshell version ---"
|
|
openshell --version 2>/dev/null
|