blob: 86a6748aee7ad61ee49b93279f2c92c97022072c [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import re
from pathlib import Path
import yaml
REPOSITORY_ROOT = Path(__file__).resolve().parents[2]
COMPOSE_PATH = REPOSITORY_ROOT / "docker-compose.yml"
DOCKERFILE_PATH = REPOSITORY_ROOT / "Dockerfile"
DOCKERIGNORE_PATH = REPOSITORY_ROOT / ".dockerignore"
START_SCRIPT_PATH = REPOSITORY_ROOT / "start_server.sh"
REQUIREMENTS_PATH = REPOSITORY_ROOT / "requirements.txt"
RUNTIME_SECURITY_PATH = REPOSITORY_ROOT / "doris_mcp_server" / "utils" / "security.py"
DEFAULT_VARIABLE = re.compile(r"^\$\{[^}:]+:-([^}]+)\}$")
PINNED_IMAGES = {
"doris-fe": (
"apache/doris:fe-3.0.8@"
"sha256:749afa2be75cd58d54a0bdb3edaa805d5974646f67500f094912f8dbc4a96b8f"
),
"doris-be": (
"apache/doris:be-3.0.8@"
"sha256:a337e2b17a65c86c22a1205467a825d46dc3380c94ba8024e53eea033641f12e"
),
"redis": (
"redis:7.4.10-alpine@"
"sha256:e7723ff73d963f5cc6d9c4643ea3d989527a402a319239054e9472a7fb9219a2"
),
"prometheus": (
"prom/prometheus:v3.13.0@"
"sha256:c6b27ea434f8389bfe233fbc7be381cf50587c286e871bc842008f5a1b1908a7"
),
"grafana": (
"grafana/grafana:13.1.0@"
"sha256:121a7a9ece6dc10b969f1f96eed64b4f07dfac0d0b8abc070f7cb83bbde86f63"
),
"nginx": (
"nginx:1.30.4-alpine@"
"sha256:97d490c12ba55b4946b01546d1c3ed324e8d41ab1c9fcb2a616aa470620e5b46"
),
}
def _compose() -> dict:
return yaml.safe_load(COMPOSE_PATH.read_text(encoding="utf-8"))
def _default_published_port(port: str | dict) -> int:
published = str(
port["published"] if isinstance(port, dict) else port.rsplit(":", 1)[0]
)
match = DEFAULT_VARIABLE.fullmatch(published)
return int(match.group(1) if match else published)
def test_default_compose_host_ports_are_unique() -> None:
compose = _compose()
published: list[int] = []
for service in compose["services"].values():
published.extend(
_default_published_port(port) for port in service.get("ports", [])
)
assert len(published) == len(set(published))
def test_compose_does_not_reserve_a_fixed_bridge_subnet() -> None:
network = _compose()["networks"]["doris-network"]
assert network == {"driver": "bridge"}
def test_mcp_service_uses_one_real_listener_and_readiness_probe() -> None:
service = _compose()["services"]["doris-mcp-server"]
assert service["ports"] == ["${MCP_HTTP_PORT:-3000}:3000"]
assert "SERVER_HOST=0.0.0.0" in service["environment"]
assert "SERVER_PORT=3000" in service["environment"]
assert (
"MCP_ALLOWED_HOSTS=${MCP_ALLOWED_HOSTS:-127.0.0.1:*,localhost:*}"
in service["environment"]
)
assert service["healthcheck"]["test"] == [
"CMD",
"curl",
"--fail",
"--silent",
"--show-error",
"--max-time",
"3",
"http://127.0.0.1:3000/ready",
]
assert service["depends_on"]["doris-fe"]["condition"] == "service_healthy"
assert service["depends_on"]["doris-be"]["condition"] == "service_healthy"
def test_grafana_default_port_does_not_conflict_with_mcp() -> None:
grafana = _compose()["services"]["grafana"]
assert grafana["ports"] == ["${GRAFANA_HTTP_PORT:-3003}:3000"]
def test_external_images_are_versioned_and_digest_pinned() -> None:
services = _compose()["services"]
assert {
service: services[service]["image"] for service in PINNED_IMAGES
} == PINNED_IMAGES
assert all(":latest" not in image for image in PINNED_IMAGES.values())
def test_compose_credentials_are_file_backed_secrets() -> None:
compose = _compose()
services = compose["services"]
secret_files = {
name: value["file"] for name, value in compose["secrets"].items()
}
assert secret_files == {
"doris_password": (
"${COMPOSE_DORIS_PASSWORD_FILE:-./.secrets/doris_password}"
),
"doris_fe_custom_config": (
"${COMPOSE_DORIS_FE_CUSTOM_CONFIG_FILE:"
"-./.secrets/doris_fe_custom.conf}"
),
"mcp_static_token": (
"${COMPOSE_MCP_STATIC_TOKEN_FILE:-./.secrets/mcp_static_token}"
),
"redis_password": (
"${COMPOSE_REDIS_PASSWORD_FILE:-./.secrets/redis_password}"
),
"grafana_admin_password": (
"${COMPOSE_GRAFANA_ADMIN_PASSWORD_FILE:"
"-./.secrets/grafana_admin_password}"
),
}
assert services["doris-mcp-server"]["secrets"] == [
"doris_password",
"mcp_static_token",
]
assert "DORIS_PASSWORD_FILE=/run/secrets/doris_password" in (
services["doris-mcp-server"]["environment"]
)
assert "TOKEN_ADMIN_FILE=/run/secrets/mcp_static_token" in (
services["doris-mcp-server"]["environment"]
)
assert services["redis"]["secrets"] == ["redis_password"]
assert services["grafana"]["secrets"] == ["grafana_admin_password"]
assert (
"GF_SECURITY_ADMIN_PASSWORD__FILE=/run/secrets/grafana_admin_password"
in services["grafana"]["environment"]
)
compose_text = COMPOSE_PATH.read_text(encoding="utf-8")
for weak_value in ("doris123", "redis123", "admin123", "analyst123"):
assert weak_value not in compose_text
def test_doris_nodes_mount_password_and_initial_root_config_secrets() -> None:
services = _compose()["services"]
assert services["doris-fe"]["secrets"] == [
{
"source": "doris_password",
"target": "/etc/basic_auth/password",
},
{
"source": "doris_fe_custom_config",
"target": "/opt/apache-doris/fe/conf/fe_custom.conf",
},
]
assert services["doris-be"]["secrets"] == [
{
"source": "doris_password",
"target": "/etc/basic_auth/password",
}
]
def test_redis_password_is_not_exposed_in_command_or_healthcheck() -> None:
redis = _compose()["services"]["redis"]
assert redis["command"][-1] == (
'exec redis-server --appendonly yes --requirepass '
'"$$(cat /run/secrets/redis_password)"'
)
assert redis["healthcheck"]["test"] == [
"CMD-SHELL",
(
'REDISCLI_AUTH="$$(cat /run/secrets/redis_password)" '
"redis-cli --no-auth-warning ping | grep -qx PONG"
),
]
def test_image_healthcheck_uses_liveness_on_the_real_listener() -> None:
dockerfile = DOCKERFILE_PATH.read_text(encoding="utf-8")
assert (
"FROM python:3.12.11-slim-bookworm@"
"sha256:519591d6871b7bc437060736b9f7456b8731f1499a57e22e6c285135ae657bf7"
in dockerfile
)
assert "http://127.0.0.1:3000/live" in dockerfile
assert "EXPOSE 3000\n" in dockerfile
assert "EXPOSE 3000 3001 3002" not in dockerfile
assert "COPY . ." not in dockerfile
assert "COPY doris_mcp_server ./doris_mcp_server" in dockerfile
assert "COPY start_server.sh ." in dockerfile
def test_container_dependency_manifest_uses_mcp_sdk_v2() -> None:
requirements = REQUIREMENTS_PATH.read_text(encoding="utf-8").splitlines()
assert "mcp>=2.0.0,<2.1.0" in requirements
assert "mcp>=1.8.0,<2.0.0" not in requirements
def test_docker_build_context_is_an_explicit_runtime_allowlist() -> None:
dockerignore = DOCKERIGNORE_PATH.read_text(encoding="utf-8").splitlines()
assert "**" in dockerignore
assert "!doris_mcp_server/**" in dockerignore
assert "!requirements.txt" in dockerignore
assert "!start_server.sh" in dockerignore
assert "!LICENSE.txt" in dockerignore
def test_start_script_uses_canonical_server_host_and_documents_both_probes() -> None:
script = START_SCRIPT_PATH.read_text(encoding="utf-8")
assert 'SERVER_HOST="${SERVER_HOST:-${MCP_HOST:-127.0.0.1}}"' in script
assert '--host "${SERVER_HOST}" --port "${SERVER_PORT}"' in script
assert "load_secret_file DORIS_PASSWORD" in script
assert "load_secret_file TOKEN_ADMIN" in script
assert "Both ${variable_name} and ${file_variable_name} are set" in script
assert 'unset "${file_variable_name}"' in script
assert "/live" in script
assert "/ready" in script
def test_runtime_has_no_fixed_legacy_credentials() -> None:
security = RUNTIME_SECURITY_PATH.read_text(encoding="utf-8")
for fixed_credential in (
"valid_token_123",
"admin_token_456",
'"password": "admin123"',
'"password": "analyst123"',
):
assert fixed_credential not in security
assert "_authenticate_legacy_token" not in security
assert "Basic authentication is not supported" in security