Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d99dba037c | |||
| 9a878bd885 | |||
| 0f72843150 |
@@ -68,6 +68,11 @@ def build_image(ref: str, context: str, *, dockerfile: str = "") -> None:
|
|||||||
_ensure_builder_dns()
|
_ensure_builder_dns()
|
||||||
args = [_CONTAINER, "build", "-t", ref, "--dns", dns_server()]
|
args = [_CONTAINER, "build", "-t", ref, "--dns", dns_server()]
|
||||||
if dockerfile:
|
if dockerfile:
|
||||||
|
# `container build` resolves -f relative to the current working
|
||||||
|
# directory, not the build context. Anchor a relative Dockerfile to
|
||||||
|
# the context so builds work from any cwd.
|
||||||
|
if not os.path.isabs(dockerfile):
|
||||||
|
dockerfile = os.path.join(context, dockerfile)
|
||||||
args.extend(["-f", dockerfile])
|
args.extend(["-f", dockerfile])
|
||||||
args.append(context)
|
args.append(context)
|
||||||
subprocess.run(args, check=True)
|
subprocess.run(args, check=True)
|
||||||
|
|||||||
@@ -148,7 +148,13 @@ class GitHttpHandler(BaseHTTPRequestHandler):
|
|||||||
key, _, value = line.decode("latin1").partition(":")
|
key, _, value = line.decode("latin1").partition(":")
|
||||||
value = value.strip()
|
value = value.strip()
|
||||||
if key.lower() == "status":
|
if key.lower() == "status":
|
||||||
status = int(value.split()[0])
|
try:
|
||||||
|
status = int(value.split()[0])
|
||||||
|
except (ValueError, IndexError):
|
||||||
|
self.log_message(
|
||||||
|
"malformed CGI Status header %r; using 500", value,
|
||||||
|
)
|
||||||
|
status = 500
|
||||||
else:
|
else:
|
||||||
headers.append((key, value))
|
headers.append((key, value))
|
||||||
self.send_response(status)
|
self.send_response(status)
|
||||||
|
|||||||
@@ -256,6 +256,57 @@ class TestGitHttpBackend(unittest.TestCase):
|
|||||||
os.environ["GIT_GATE_ACCESS_HOOK"] = value
|
os.environ["GIT_GATE_ACCESS_HOOK"] = value
|
||||||
|
|
||||||
|
|
||||||
|
class TestMalformedStatusHeader(unittest.TestCase):
|
||||||
|
"""Malformed CGI Status: headers must not propagate as unhandled exceptions;
|
||||||
|
the handler should fall back to HTTP 500."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
from http.server import ThreadingHTTPServer
|
||||||
|
import tempfile
|
||||||
|
self._tmp = tempfile.mkdtemp()
|
||||||
|
os.environ["GIT_PROJECT_ROOT"] = self._tmp
|
||||||
|
self._server = ThreadingHTTPServer(("127.0.0.1", 0), GitHttpHandler)
|
||||||
|
self._thread = threading.Thread(
|
||||||
|
target=self._server.serve_forever, daemon=True,
|
||||||
|
)
|
||||||
|
self._thread.start()
|
||||||
|
self._port = self._server.server_port
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
self._server.shutdown()
|
||||||
|
self._server.server_close()
|
||||||
|
os.environ.pop("GIT_PROJECT_ROOT", None)
|
||||||
|
import shutil
|
||||||
|
shutil.rmtree(self._tmp, ignore_errors=True)
|
||||||
|
|
||||||
|
def _get_with_backend_response(self, cgi_response: bytes) -> int:
|
||||||
|
with mock.patch(
|
||||||
|
"bot_bottle.git_http_backend.subprocess.run",
|
||||||
|
return_value=mock.Mock(returncode=0, stdout=cgi_response),
|
||||||
|
):
|
||||||
|
req = urllib.request.Request(
|
||||||
|
f"http://127.0.0.1:{self._port}/repo.git/info/refs",
|
||||||
|
method="GET",
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
with urllib.request.urlopen(req, timeout=3) as resp:
|
||||||
|
return resp.status
|
||||||
|
except urllib.error.HTTPError as e: # type: ignore
|
||||||
|
return e.code
|
||||||
|
|
||||||
|
def test_empty_status_value_returns_500(self):
|
||||||
|
status = self._get_with_backend_response(
|
||||||
|
b"Status: \r\nContent-Type: text/plain\r\n\r\n"
|
||||||
|
)
|
||||||
|
self.assertEqual(500, status)
|
||||||
|
|
||||||
|
def test_non_numeric_status_returns_500(self):
|
||||||
|
status = self._get_with_backend_response(
|
||||||
|
b"Status: bad\r\nContent-Type: text/plain\r\n\r\n"
|
||||||
|
)
|
||||||
|
self.assertEqual(500, status)
|
||||||
|
|
||||||
|
|
||||||
class TestContentLengthBounds(unittest.TestCase):
|
class TestContentLengthBounds(unittest.TestCase):
|
||||||
"""PRD 0041: malformed or oversized Content-Length is rejected before
|
"""PRD 0041: malformed or oversized Content-Length is rejected before
|
||||||
git http-backend is invoked."""
|
git http-backend is invoked."""
|
||||||
|
|||||||
@@ -73,6 +73,33 @@ resolver #2
|
|||||||
)
|
)
|
||||||
self.assertTrue(run.call_args_list[-1].kwargs["check"])
|
self.assertTrue(run.call_args_list[-1].kwargs["check"])
|
||||||
|
|
||||||
|
def test_build_image_anchors_relative_dockerfile_to_context(self):
|
||||||
|
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",
|
||||||
|
}):
|
||||||
|
util.build_image(
|
||||||
|
"bot-bottle-sidecars:latest",
|
||||||
|
"/repo",
|
||||||
|
dockerfile="Dockerfile.sidecars",
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
[
|
||||||
|
"container", "build", "-t", "bot-bottle-sidecars:latest",
|
||||||
|
"--dns", "9.9.9.9", "-f", "/repo/Dockerfile.sidecars", "/repo",
|
||||||
|
],
|
||||||
|
run.call_args_list[-1].args[0],
|
||||||
|
)
|
||||||
|
|
||||||
def test_commit_container_execs_tar_and_builds_image(self):
|
def test_commit_container_execs_tar_and_builds_image(self):
|
||||||
# stderr is bytes because subprocess.run uses stderr=PIPE without text=True
|
# stderr is bytes because subprocess.run uses stderr=PIPE without text=True
|
||||||
completed = util.subprocess.CompletedProcess(
|
completed = util.subprocess.CompletedProcess(
|
||||||
|
|||||||
Reference in New Issue
Block a user