This commit is contained in:
Samir Hanna Verza 2026-11-04 12:27:28 -03:00 • committed by GitHub
commit eafdfde1fb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 72 additions and 5 deletions

View file

@ -191,7 +191,11 @@ When remote vars are set, Strix dual-writes telemetry to both local JSONL and th
</ParamField>
<ParamField path="DOCKER_HOST" type="string">
Docker daemon address, for example `unix:///var/run/docker.sock` or `tcp://10.0.0.5:2375`. When not set, Strix connects to the same daemon as the `docker` CLI: the current docker context (`docker context use`), then the default socket. Docker Desktop, OrbStack and Colima register their sockets as contexts, so no extra configuration is needed for them.
Docker daemon address, for example `unix:///var/run/docker.sock` or `tcp://10.0.0.5:2375`. When not set, Strix uses `DOCKER_CONTEXT`, then the saved current context (`docker context use`), then the default socket. Docker Desktop, OrbStack and Colima register their sockets as contexts, so no extra configuration is needed for them.
</ParamField>
<ParamField path="DOCKER_CONTEXT" type="string">
Select a Docker context for this shell without changing the saved current context. Used when `DOCKER_HOST` is unset. A missing context is an error; Strix does not silently fall back to another daemon. Set to `default` to use the SDK's default connection settings.
</ParamField>
<ParamField path="STRIX_RUNTIME_BACKEND" default="docker" type="string">

View file

@ -44,13 +44,13 @@ class DockerConnectionError(RuntimeError):
def resolve_docker_endpoint(environ: dict[str, str] | None = None) -> DockerEndpoint:
"""``DOCKER_HOST``, else the current docker context, else the SDK default."""
"""``DOCKER_HOST``, shell/saved context, then the SDK default."""
env = os.environ if environ is None else environ
host = env.get("DOCKER_HOST", "").strip()
if host:
return DockerEndpoint(host, "DOCKER_HOST")
name = get_current_context_name()
name = env.get("DOCKER_CONTEXT", "").strip() or get_current_context_name()
if name == DEFAULT_CONTEXT:
return DockerEndpoint(None, "default socket")

View file

@ -2,9 +2,11 @@
from __future__ import annotations
import hashlib
import importlib
import json
from types import SimpleNamespace
from typing import Any, cast
from typing import TYPE_CHECKING, Any, cast
import pytest
from docker.errors import DockerException
@ -19,6 +21,10 @@ from strix.runtime.docker_connection import (
)
if TYPE_CHECKING:
from pathlib import Path
cli_utils: Any = importlib.import_module("strix.interface.utils")
@ -34,7 +40,9 @@ def _sdk_error(inner: BaseException) -> DockerException:
def test_docker_host_wins_over_context(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(docker_connection, "get_current_context_name", lambda: "desktop-linux")
endpoint = resolve_docker_endpoint({"DOCKER_HOST": "tcp://10.0.0.5:2375"})
endpoint = resolve_docker_endpoint(
{"DOCKER_HOST": "tcp://10.0.0.5:2375", "DOCKER_CONTEXT": "selected"}
)
assert endpoint == DockerEndpoint("tcp://10.0.0.5:2375", "DOCKER_HOST")
@ -56,6 +64,61 @@ def test_default_context_uses_the_sdk_default(monkeypatch: pytest.MonkeyPatch) -
assert resolve_docker_endpoint({}) == DockerEndpoint(None, "default socket")
@pytest.mark.parametrize(
("selected", "expected_host"),
[
("selected", "unix:///tmp/strix-selected.sock"),
("default", None),
("", "unix:///tmp/strix-saved.sock"),
],
)
def test_shell_context_overrides_the_saved_context(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
selected: str,
expected_host: str | None,
) -> None:
"""Read a real Docker context store, without contacting any daemon."""
(tmp_path / "config.json").write_text(json.dumps({"currentContext": "saved"}))
for name in ("saved", "selected"):
context_id = hashlib.sha256(name.encode()).hexdigest()
metadata = tmp_path / "contexts" / "meta" / context_id / "meta.json"
metadata.parent.mkdir(parents=True)
metadata.write_text(
json.dumps(
{
"Name": name,
"Metadata": {},
"Endpoints": {
"docker": {
"Host": f"unix:///tmp/strix-{name}.sock",
"SkipTLSVerify": True,
}
},
}
)
)
monkeypatch.setenv("DOCKER_CONFIG", str(tmp_path))
monkeypatch.setenv("DOCKER_CONTEXT", selected)
monkeypatch.delenv("DOCKER_HOST", raising=False)
assert resolve_docker_endpoint().host == expected_host
def test_missing_shell_context_does_not_fall_back_to_saved_context(
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
(tmp_path / "config.json").write_text(json.dumps({"currentContext": "default"}))
monkeypatch.setenv("DOCKER_CONFIG", str(tmp_path))
monkeypatch.setenv("DOCKER_CONTEXT", "missing")
monkeypatch.delenv("DOCKER_HOST", raising=False)
with pytest.raises(
DockerConnectionError, match="docker context 'missing' is selected but does not exist"
):
resolve_docker_endpoint()
def test_missing_or_broken_current_context_is_an_error(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(docker_connection, "get_current_context_name", lambda: "gone")
monkeypatch.setattr(