Skip to content

Commit 66da810

Browse files
Merge branch 'main' into 1451_change_config_overwriteability
2 parents ce196e5 + d880320 commit 66da810

6 files changed

Lines changed: 87 additions & 25 deletions

File tree

.github/workflows/_container.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ jobs:
8888
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
8989

9090
- name: install helm
91-
uses: Azure/setup-helm@1a275c3b69536ee54be43f2070a358922e12c8d4 # v4
91+
uses: Azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5
9292
with:
9393
token: ${{ secrets.GITHUB_TOKEN }}
9494
id: install

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
3434
uv sync --locked --no-editable --no-dev --managed-python
3535

3636
# The runtime stage copies the built venv into a runtime container
37-
FROM ubuntu:noble AS runtime
37+
FROM ubuntu:resolute AS runtime
3838

3939
# Add apt-get system dependecies for runtime here if needed
4040
RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \

src/blueapi/cli/cli.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,19 @@ def is_str_dict(val: Any) -> TypeGuard[TaskParameters]:
8181
invoke_without_command=True, context_settings={"auto_envvar_prefix": "BLUEAPI"}
8282
)
8383
@click.version_option(version=__version__, prog_name="blueapi")
84+
@click.option(
85+
"-h",
86+
"--host",
87+
type=str,
88+
help=textwrap.dedent(
89+
"""
90+
Hostname for the blueapi instance to use
91+
92+
Value should be the full URL including scheme (and port if non-default),
93+
eg `--host http://localhost:8000`
94+
"""
95+
),
96+
)
8497
@click.option(
8598
"-c", "--config", type=Path, help="Path to configuration YAML file", multiple=True
8699
)
@@ -100,7 +113,10 @@ def is_str_dict(val: Any) -> TypeGuard[TaskParameters]:
100113
)
101114
@click.pass_context
102115
def main(
103-
ctx: click.Context, config: tuple[Path, ...], log_level: str | None = None
116+
ctx: click.Context,
117+
config: tuple[Path, ...],
118+
host: str | None = None,
119+
log_level: str | None = None,
104120
) -> None:
105121
# if no command is supplied, run with the options passed
106122

@@ -112,7 +128,8 @@ def main(
112128
config_loader.use_values_from_yaml(*config)
113129
except FileNotFoundError as fnfe:
114130
raise ClickException(f"Config file not found: {fnfe.filename}") from fnfe
115-
131+
if host:
132+
config_loader.use_values({"api": {"url": host}})
116133
if log_level:
117134
config_loader.use_values({"logging": {"level": log_level}})
118135

src/blueapi/service/main.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -621,20 +621,18 @@ async def log_request_details(
621621
) -> Response:
622622
"""Middleware to log all request's host, method, path, status and request and
623623
body"""
624+
log = LOGGER.debug if request.url.path == "/healthz" else LOGGER.info
624625
request_body = await request.body()
625626
client = request.client or Address("Unknown", -1)
626627
log_message = f"{client.host}:{client.port} {request.method} {request.url.path}"
627628
extra = {
628629
"request_body": request_body,
629630
}
630-
LOGGER.debug(log_message, extra=extra)
631+
log(log_message, extra=extra)
631632

632633
response = await call_next(request)
633634
log_message += f" {response.status_code}"
634-
if request.url.path == "/healthz":
635-
LOGGER.debug(log_message, extra=extra)
636-
else:
637-
LOGGER.info(log_message, extra=extra)
635+
log(log_message, extra=extra)
638636

639637
return response
640638

tests/unit_tests/cli/test_cli.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,3 +1430,45 @@ def test_log_level_override(flag: str, level: str, runner: CliRunner):
14301430
runner.invoke(main, [flag])
14311431
mock_log.getLogger().setLevel.assert_called_once_with(level)
14321432
mock_log.StreamHandler().setLevel.assert_called_once_with(level)
1433+
1434+
1435+
@responses.activate
1436+
def test_host_option(runner: CliRunner):
1437+
response = responses.add(
1438+
responses.GET,
1439+
"http://override.example.com:5678/plans",
1440+
json={"plans": []},
1441+
status=200,
1442+
)
1443+
1444+
res = runner.invoke(
1445+
main,
1446+
["--host", "http://override.example.com:5678", "controller", "plans"],
1447+
)
1448+
assert response.call_count == 1
1449+
assert res.exit_code == 0
1450+
1451+
1452+
@responses.activate
1453+
def test_host_overrides_config(runner: CliRunner):
1454+
config_path = "tests/unit_tests/example_yaml/rest_config.yaml"
1455+
response = responses.add(
1456+
responses.GET,
1457+
"http://override.example.com:5678/plans",
1458+
json={"plans": []},
1459+
status=200,
1460+
)
1461+
1462+
res = runner.invoke(
1463+
main,
1464+
[
1465+
"--host",
1466+
"http://override.example.com:5678",
1467+
"--config",
1468+
config_path,
1469+
"controller",
1470+
"plans",
1471+
],
1472+
)
1473+
assert response.call_count == 1
1474+
assert res.exit_code == 0

tests/unit_tests/service/test_main.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from unittest import mock
2-
from unittest.mock import Mock
2+
from unittest.mock import Mock, call
33

44
import pytest
55
from fastapi import FastAPI, Request
@@ -29,31 +29,36 @@ async def root():
2929
assert response.headers["X-BlueAPI-VERSION"] == __version__
3030

3131

32-
async def test_log_request_details():
32+
@pytest.mark.parametrize("path,level", [("/", "info"), ("/healthz", "debug")])
33+
async def test_log_request_details(path: str, level: str):
3334
with mock.patch("blueapi.service.main.LOGGER") as logger:
3435
app = FastAPI()
3536
app.middleware("http")(log_request_details)
3637

37-
@app.post("/")
38+
@app.post(path)
3839
async def root():
3940
return {"message": "Hello World"}
4041

4142
client = TestClient(app)
42-
response = client.post("/", content="foo")
43+
response = client.post(path, content="foo")
4344

4445
assert response.status_code == 200
45-
logger.debug.assert_called_once_with(
46-
"testclient:50000 POST /",
47-
extra={
48-
"request_body": b"foo",
49-
},
50-
)
51-
52-
logger.info.assert_called_once_with(
53-
"testclient:50000 POST / 200",
54-
extra={
55-
"request_body": b"foo",
56-
},
46+
log_level = getattr(logger, level)
47+
log_level.assert_has_calls(
48+
[
49+
call(
50+
f"testclient:50000 POST {path}",
51+
extra={
52+
"request_body": b"foo",
53+
},
54+
),
55+
call(
56+
f"testclient:50000 POST {path} 200",
57+
extra={
58+
"request_body": b"foo",
59+
},
60+
),
61+
]
5762
)
5863

5964

0 commit comments

Comments
 (0)