From c8617bdd742df5a73690419a3e71fb3a61d2127a Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 21 Aug 2026 11:42:08 +0200 Subject: [PATCH] fix(snap): recover gateway after Docker connection Signed-off-by: Evan Lezar --- .agents/skills/test-release-canary/SKILL.md | 11 +++++++- .github/workflows/release-canary.yml | 31 +++++++++++++++++++-- docs/about/installation.mdx | 3 ++ python/openshell/release_formula_test.py | 12 ++++++++ snap/hooks/connect-plug-docker | 12 ++++++++ snapcraft.yaml | 6 ++-- 6 files changed, 69 insertions(+), 6 deletions(-) create mode 100755 snap/hooks/connect-plug-docker diff --git a/.agents/skills/test-release-canary/SKILL.md b/.agents/skills/test-release-canary/SKILL.md index 5e8bbf394c..43a86c849d 100644 --- a/.agents/skills/test-release-canary/SKILL.md +++ b/.agents/skills/test-release-canary/SKILL.md @@ -14,6 +14,7 @@ The Release Canary (`.github/workflows/release-canary.yml`) smoke-tests the arti | `macos` | `macos-latest-xlarge` | `install.sh` resolves the Homebrew formula, brew installs the cask, and `openshell status` reaches the brew-services–backed local gateway with the VM driver. | | `ubuntu` | `ubuntu-latest` | `install.sh` installs the Debian package, the post-install systemd user service starts, and `openshell status` reaches the local gateway with the Docker driver. | | `fedora` | `fedora:latest` container | `install.sh` installs the RPM packages, the local gateway starts under Podman, and `openshell status` succeeds. | +| `ubuntu-snap` | `ubuntu-latest` | Downloads the Snap artifact from Release Dev, installs it with `--dangerous`, connects the required interfaces, and waits up to 30 seconds for the recovered local gateway. | | `kubernetes` | `ubuntu-latest` + kind | `helm install oci://ghcr.io/nvidia/openshell/helm-chart --version 0.0.0-dev` succeeds in a kind cluster, the gateway pod becomes Ready, port-forward exposes 8080, and the released CLI registers the in-cluster gateway and runs `openshell status` against it. | All canary jobs disable anonymous OpenShell telemetry. Host package jobs inject @@ -41,7 +42,7 @@ on: ``` - **Automatic.** Every successful `Release Dev` run (on `main` or a manual dispatch of Release Dev) fires the canary. Each job gates on `github.event.workflow_run.conclusion == 'success'` so a failed Release Dev does not run the canary. -- **Manual.** `workflow_dispatch` lets you run the canary on demand against any branch's workflow definition. +- **Manual.** `workflow_dispatch` lets you run the canary on demand against any branch's workflow definition. To include `ubuntu-snap`, supply `release-dev-run-id` for a successful Release Dev run whose Snap artifact should be tested; without it, that job is skipped because no artifact is available. When dispatched manually, `github.event.workflow_run.head_sha` is empty and the workflow falls back to `github.sha` (the branch tip) for the `install.sh` URL. @@ -53,6 +54,13 @@ Run the canary as-is on the current branch: gh workflow run release-canary.yml --ref "$(git branch --show-current)" ``` +To exercise the Ubuntu Snap job, pass the successful Release Dev run ID: + +```shell +gh workflow run release-canary.yml --ref "$(git branch --show-current)" \ + -f release-dev-run-id= +``` + Watch the run that starts: ```shell @@ -122,6 +130,7 @@ Loopback registration auto-derives the gateway name to `openshell` if `--name` i |---|---|---| | `macos`/`ubuntu`/`fedora` job fails on `install.sh` | Latest tagged release missing an asset, checksum mismatch, or `install.sh` regression on this branch. | Job log around the `curl … install.sh \| sh` step. | | `macos`/`ubuntu`/`fedora` job fails on `openshell status` | Local gateway service did not start (systemd/brew/podman). Often a driver issue. | Service logs in the job log; `OPENSHELL_DRIVERS` env in the "Ensure …" step. | +| `ubuntu-snap` fails after interface connection | The gateway did not recover after Docker became available, or did not become reachable within the 30-second bound. | Failure diagnostics dump Snap service/connection/change state, gateway and snapd journals, Snap logs, and port 17670 listeners. | | `kubernetes` job fails on `helm install --wait` | Chart did not deploy in 5 min — usually image pull failure or readiness probe failing. | "Diagnostics on failure" step dumps `helm status`, manifest, pod describe, pod logs. | | `kubernetes` job fails on `kubectl wait` | Gateway pod stuck `CrashLoopBackOff` or `ImagePullBackOff`. | Diagnostics dump; check `:dev` image existence at `ghcr.io/nvidia/openshell/gateway`. | | `kubernetes` job fails on `openshell gateway add` or `status` | Port-forward not reachable, or CLI/gateway proto mismatch. | `port-forward.log` and `openshell gateway list` in the diagnostics dump. | diff --git a/.github/workflows/release-canary.yml b/.github/workflows/release-canary.yml index 937e774db7..ee4074142e 100644 --- a/.github/workflows/release-canary.yml +++ b/.github/workflows/release-canary.yml @@ -2,6 +2,11 @@ name: Release Canary on: workflow_dispatch: + inputs: + release-dev-run-id: + description: "Successful Release Dev run ID whose Snap artifact to test" + required: false + type: string workflow_run: workflows: ["Release Dev"] types: [completed] @@ -154,7 +159,7 @@ jobs: ubuntu-snap: name: Ubuntu Snap - if: ${{ github.event.workflow_run.conclusion == 'success' }} + if: ${{ github.event.workflow_run.conclusion == 'success' || inputs.release-dev-run-id != '' }} runs-on: ubuntu-latest timeout-minutes: 20 steps: @@ -176,7 +181,7 @@ jobs: uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: github-token: ${{ github.token }} - run-id: ${{ github.event.workflow_run.id }} + run-id: ${{ inputs.release-dev-run-id || github.event.workflow_run.id }} pattern: snap-linux-amd64 path: release/ merge-multiple: true @@ -202,7 +207,27 @@ jobs: sudo snap services openshell openshell gateway add http://127.0.0.1:17670 --local --name snap-docker openshell gateway select snap-docker - openshell status + for _ in $(seq 1 30); do + if openshell status; then + exit 0 + fi + sleep 1 + done + echo "Gateway did not become ready within 30 seconds" >&2 + exit 1 + + - name: Collect Snap diagnostics + if: failure() + run: | + set +e + sudo snap services openshell + sudo snap connections openshell + sudo snap changes + sudo systemctl status snap.openshell.gateway.service --no-pager + sudo journalctl -b -u snap.openshell.gateway.service --no-pager -n 300 + sudo journalctl -b -u snapd.service --no-pager -n 300 + sudo snap logs openshell.gateway -n=300 + sudo ss -ltnp '( sport = :17670 )' kubernetes: name: Kubernetes Helm (kind) diff --git a/docs/about/installation.mdx b/docs/about/installation.mdx index e464d3da42..159bf40aa0 100644 --- a/docs/about/installation.mdx +++ b/docs/about/installation.mdx @@ -104,6 +104,9 @@ requires manual connection: sudo snap connect openshell:docker docker:docker-daemon ``` +Connecting Docker automatically restarts the gateway so it discovers the newly +available Docker socket. This restart interrupts active sandbox sessions. + The snap declares `default-provider: docker` on the Docker plug so snapd will offer to install the Docker snap, but the connection itself must be made manually. diff --git a/python/openshell/release_formula_test.py b/python/openshell/release_formula_test.py index f6a1f7d13a..62c27115b5 100644 --- a/python/openshell/release_formula_test.py +++ b/python/openshell/release_formula_test.py @@ -4,6 +4,7 @@ from __future__ import annotations import re +import stat import subprocess import sys from pathlib import Path @@ -130,6 +131,17 @@ def test_snap_wrapper_uses_optional_gateway_config_without_generating_toml() -> assert 'exec "${SNAP}/bin/openshell-gateway" "$@"' in wrapper +def test_snap_docker_connect_hook_restarts_gateway() -> None: + repo_root = Path(__file__).resolve().parents[2] + hook = repo_root / "snap/hooks/connect-plug-docker" + + assert hook.is_file() + assert hook.stat().st_mode & stat.S_IXUSR + assert 'snapctl restart "${SNAP_INSTANCE_NAME}.gateway"' in hook.read_text( + encoding="utf-8" + ) + + def test_rpm_spec_uses_gateway_defaults_without_config_helper() -> None: repo_root = Path(__file__).resolve().parents[2] spec = (repo_root / "openshell.spec").read_text(encoding="utf-8") diff --git a/snap/hooks/connect-plug-docker b/snap/hooks/connect-plug-docker new file mode 100755 index 0000000000..cfd334b74c --- /dev/null +++ b/snap/hooks/connect-plug-docker @@ -0,0 +1,12 @@ +#!/bin/sh +# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +# The gateway daemon can start when this plug is still disconnected. Restart it +# after Docker access becomes available so driver auto-detection runs with the +# socket exposed by docker:docker-daemon. This hook does not make normal gateway +# startup conditional on Docker; it runs only after an operator connects Docker. + +set -eu + +snapctl restart "${SNAP_INSTANCE_NAME}.gateway" diff --git a/snapcraft.yaml b/snapcraft.yaml index d221c51e7e..cfa2fd81cc 100644 --- a/snapcraft.yaml +++ b/snapcraft.yaml @@ -22,12 +22,14 @@ description: | Support for system-installed Docker is coming in snapd 2.76. - 2. Connect the required interfaces and start the gateway: + 2. Connect the required interfaces: sudo snap connect openshell:docker docker:docker-daemon sudo snap connect openshell:log-observe sudo snap connect openshell:system-observe - sudo snap start openshell.gateway + + Connecting Docker automatically restarts the gateway so it discovers + the newly available Docker socket. 3. Verify the gateway and register it locally: