fix(macos-container): start builder with dns
lint / lint (push) Successful in 1m45s
test / unit (pull_request) Successful in 37s
test / integration (pull_request) Successful in 19s

This commit is contained in:
2026-06-10 20:12:45 -04:00
parent f7f9892b53
commit d123b99347
3 changed files with 94 additions and 4 deletions
@@ -45,6 +45,7 @@ def build_image(ref: str, context: str, *, dockerfile: str = "") -> None:
f"building image {ref} from {context} with Apple Container "
"(layer cache keeps repeat builds fast)"
)
_ensure_builder_dns()
args = [_CONTAINER, "build", "-t", ref, "--dns", dns_server()]
if dockerfile:
args.extend(["-f", dockerfile])
@@ -52,6 +53,54 @@ def build_image(ref: str, context: str, *, dockerfile: str = "") -> None:
subprocess.run(args, check=True)
def _ensure_builder_dns() -> None:
dns = dns_server()
if _builder_has_dns(dns):
return
subprocess.run(
[_CONTAINER, "builder", "stop"],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
check=False,
)
subprocess.run(
[_CONTAINER, "builder", "start", "--dns", dns],
check=True,
)
def _builder_has_dns(dns: str) -> bool:
result = subprocess.run(
[_CONTAINER, "builder", "status", "--format", "json"],
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
return False
try:
data = json.loads(result.stdout or "[]")
except json.JSONDecodeError:
return False
entries = data if isinstance(data, list) else [data]
for entry in entries:
if not isinstance(entry, dict):
continue
status = entry.get("status")
if isinstance(status, dict) and status.get("state") != "running":
continue
config = entry.get("configuration")
config_dns = config.get("dns") if isinstance(config, dict) else None
nameservers = (
config_dns.get("nameservers")
if isinstance(config_dns, dict)
else None
)
if isinstance(nameservers, list) and dns in nameservers:
return True
return False
def image_exists(ref: str) -> bool:
return _silent_run([_CONTAINER, "image", "inspect", ref]) == 0
+4 -1
View File
@@ -170,7 +170,10 @@ delivery design lands.
- Integration tests run on macOS hosts with Apple Container installed
and verify that egress cannot bypass the sidecar. They also preflight
Apple Container BuildKit DNS because image builds must resolve
package mirrors before a launch smoke can be meaningful.
package mirrors before a launch smoke can be meaningful. The backend
starts/restarts the Apple Container builder with the configured DNS
server before image builds so BuildKit `RUN` steps inherit a working
resolver.
## References
+41 -3
View File
@@ -29,7 +29,16 @@ class TestMacosContainerAvailability(unittest.TestCase):
class TestMacosContainerCommands(unittest.TestCase):
def test_build_image(self):
with patch.object(util.subprocess, "run") as run, \
status = util.subprocess.CompletedProcess(
args=[],
returncode=0,
stdout=(
'[{"status":{"state":"running"},'
'"configuration":{"dns":{"nameservers":["9.9.9.9"]}}}]'
),
stderr="",
)
with patch.object(util.subprocess, "run", return_value=status) as run, \
patch.object(util.os, "environ", {
"BOT_BOTTLE_MACOS_CONTAINER_DNS": "9.9.9.9",
}):
@@ -39,9 +48,38 @@ class TestMacosContainerCommands(unittest.TestCase):
"container", "build", "-t", "bot-bottle-agent:latest",
"--dns", "9.9.9.9", "-f", "/repo/Dockerfile", "/repo",
],
run.call_args.args[0],
run.call_args_list[-1].args[0],
)
self.assertTrue(run.call_args_list[-1].kwargs["check"])
def test_build_image_restarts_builder_when_dns_mismatches(self):
status = util.subprocess.CompletedProcess(
args=[],
returncode=0,
stdout=(
'[{"status":{"state":"running"},'
'"configuration":{"dns":{"nameservers":[]}}}]'
),
stderr="",
)
with patch.object(util.subprocess, "run", return_value=status) as run, \
patch.object(util.os, "environ", {
"BOT_BOTTLE_MACOS_CONTAINER_DNS": "9.9.9.9",
}):
util.build_image("bot-bottle-agent:latest", "/repo")
calls = [c.args[0] for c in run.call_args_list]
self.assertIn(["container", "builder", "stop"], calls)
self.assertIn(
["container", "builder", "start", "--dns", "9.9.9.9"],
calls,
)
self.assertEqual(
[
"container", "build", "-t", "bot-bottle-agent:latest",
"--dns", "9.9.9.9", "/repo",
],
calls[-1],
)
self.assertTrue(run.call_args.kwargs["check"])
def test_container_exists_parses_quiet_list(self):
completed = util.subprocess.CompletedProcess(