fix(macos-container): start builder with dns

This commit is contained in:
2026-06-10 20:12:45 -04:00
parent 890a146413
commit 5e927bcd13
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 " f"building image {ref} from {context} with Apple Container "
"(layer cache keeps repeat builds fast)" "(layer cache keeps repeat builds fast)"
) )
_ensure_builder_dns()
args = [_CONTAINER, "build", "-t", ref, "--dns", dns_server()] args = [_CONTAINER, "build", "-t", ref, "--dns", dns_server()]
if dockerfile: if dockerfile:
args.extend(["-f", dockerfile]) args.extend(["-f", dockerfile])
@@ -52,6 +53,54 @@ def build_image(ref: str, context: str, *, dockerfile: str = "") -> None:
subprocess.run(args, check=True) 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: def image_exists(ref: str) -> bool:
return _silent_run([_CONTAINER, "image", "inspect", ref]) == 0 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 - Integration tests run on macOS hosts with Apple Container installed
and verify that egress cannot bypass the sidecar. They also preflight and verify that egress cannot bypass the sidecar. They also preflight
Apple Container BuildKit DNS because image builds must resolve 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 ## References
+41 -3
View File
@@ -29,7 +29,16 @@ class TestMacosContainerAvailability(unittest.TestCase):
class TestMacosContainerCommands(unittest.TestCase): class TestMacosContainerCommands(unittest.TestCase):
def test_build_image(self): 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", { patch.object(util.os, "environ", {
"BOT_BOTTLE_MACOS_CONTAINER_DNS": "9.9.9.9", "BOT_BOTTLE_MACOS_CONTAINER_DNS": "9.9.9.9",
}): }):
@@ -39,9 +48,38 @@ class TestMacosContainerCommands(unittest.TestCase):
"container", "build", "-t", "bot-bottle-agent:latest", "container", "build", "-t", "bot-bottle-agent:latest",
"--dns", "9.9.9.9", "-f", "/repo/Dockerfile", "/repo", "--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): def test_container_exists_parses_quiet_list(self):
completed = util.subprocess.CompletedProcess( completed = util.subprocess.CompletedProcess(