Skip to content

Commit fee4509

Browse files
committed
Move tools to mcp server
1 parent c6e0008 commit fee4509

File tree

11 files changed

+274
-223
lines changed

11 files changed

+274
-223
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
__pycache__/
2+
**/__pycache__

_prow_mcp_server/Containerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,6 @@ COPY mcp_server.py ./
99
COPY drain.py ./
1010
COPY drain3.ini ./
1111

12+
EXPOSE 9000
13+
1214
CMD ["python", "mcp_server.py"]

_prow_mcp_server/mcp.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"localhost/mcp-server:latest"
1111
],
1212
"env": {
13-
"MCP_TRANSPORT": "stdio"
13+
"MCP_TRANSPORT": "streamable-http"
1414
}
1515
}
1616
}

_prow_mcp_server/mcp_server.py

Lines changed: 127 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,139 @@
22
import asyncio
33
from typing import Any, Optional, Dict
44
from dateutil.parser import parse as parse_date
5+
import re
56

67
from drain import DrainExtractor
78

89
import httpx
9-
from mcp.server.fastmcp import FastMCP
10+
from fastmcp import FastMCP
1011

11-
mcp = FastMCP("prow-mcp-server")
12+
mcp = FastMCP(name="prow-mcp-server", stateless_http=True, host="0.0.0.0", port=9000)
1213

1314
GCS_URL = "https://gcsweb-ci.apps.ci.l2s4.p1.openshiftapps.com/gcs/test-platform-results/logs"
1415

1516
_drain_extractor: Optional['DrainExtractor'] = None
1617

18+
def extract_installation_info(log_content: str) -> Dict[str, Any]:
19+
"""Extract installation information from build-log.txt."""
20+
install_info = {
21+
"installer_version": None,
22+
"installer_commit": None,
23+
"release_image": None,
24+
"instance_types": {},
25+
"install_duration": None,
26+
"architecture": None,
27+
"cluster_config": {},
28+
"install_success": False
29+
}
30+
31+
# Extract openshift-install version and commit (can be on separate lines)
32+
version_patterns = [
33+
r'openshift-install v([^\s"]+)',
34+
r'"openshift-install v([^\s"]+)"'
35+
]
36+
37+
for pattern in version_patterns:
38+
version_match = re.search(pattern, log_content)
39+
if version_match:
40+
install_info["installer_version"] = version_match.group(1)
41+
break
42+
43+
# Extract commit (separate pattern)
44+
commit_patterns = [
45+
r'built from commit ([a-f0-9]+)',
46+
r'"built from commit ([a-f0-9]+)"'
47+
]
48+
49+
for pattern in commit_patterns:
50+
commit_match = re.search(pattern, log_content)
51+
if commit_match:
52+
install_info["installer_commit"] = commit_match.group(1)
53+
break
54+
55+
# Extract release image
56+
release_patterns = [
57+
r'Installing from release ([^\s]+)',
58+
r'release image "([^"]+)"',
59+
r'RELEASE_IMAGE_LATEST for release image "([^"]+)"'
60+
]
61+
for pattern in release_patterns:
62+
release_match = re.search(pattern, log_content)
63+
if release_match:
64+
install_info["release_image"] = release_match.group(1)
65+
break
66+
67+
# Extract instance types from install-config.yaml section
68+
# Look for compute and controlPlane sections
69+
compute_type_pattern = r'compute:.*?type:\s*([^\s\n]+)'
70+
control_type_pattern = r'controlPlane:.*?type:\s*([^\s\n]+)'
71+
72+
compute_match = re.search(compute_type_pattern, log_content, re.DOTALL)
73+
if compute_match:
74+
install_info["instance_types"]["compute"] = compute_match.group(1)
75+
76+
control_match = re.search(control_type_pattern, log_content, re.DOTALL)
77+
if control_match:
78+
install_info["instance_types"]["control_plane"] = control_match.group(1)
79+
80+
# Extract architecture
81+
arch_pattern = r'architecture:\s*([^\s\n]+)'
82+
arch_match = re.search(arch_pattern, log_content)
83+
if arch_match:
84+
install_info["architecture"] = arch_match.group(1)
85+
86+
# Extract cluster configuration details
87+
# Replicas
88+
compute_replicas_pattern = r'compute:.*?replicas:\s*(\d+)'
89+
control_replicas_pattern = r'controlPlane:.*?replicas:\s*(\d+)'
90+
91+
compute_replicas_match = re.search(compute_replicas_pattern, log_content, re.DOTALL)
92+
if compute_replicas_match:
93+
install_info["cluster_config"]["compute_replicas"] = int(compute_replicas_match.group(1))
94+
95+
control_replicas_match = re.search(control_replicas_pattern, log_content, re.DOTALL)
96+
if control_replicas_match:
97+
install_info["cluster_config"]["control_replicas"] = int(control_replicas_match.group(1))
98+
99+
# Network type
100+
network_pattern = r'networkType:\s*([^\s\n]+)'
101+
network_match = re.search(network_pattern, log_content)
102+
if network_match:
103+
install_info["cluster_config"]["network_type"] = network_match.group(1)
104+
105+
# Platform and region
106+
platform_pattern = r'platform:\s*([^\s\n]+):'
107+
region_pattern = r'region:\s*([^\s\n]+)'
108+
109+
platform_match = re.search(platform_pattern, log_content)
110+
if platform_match:
111+
install_info["cluster_config"]["platform"] = platform_match.group(1)
112+
113+
region_match = re.search(region_pattern, log_content)
114+
if region_match:
115+
install_info["cluster_config"]["region"] = region_match.group(1)
116+
117+
# Extract install duration (clean up quotes)
118+
duration_patterns = [
119+
r'Time elapsed:\s*([^\n"]+)',
120+
r'Install complete!.*?Time elapsed:\s*([^\n"]+)'
121+
]
122+
123+
for pattern in duration_patterns:
124+
duration_match = re.search(pattern, log_content, re.DOTALL)
125+
if duration_match:
126+
duration = duration_match.group(1).strip().strip('"')
127+
install_info["install_duration"] = duration
128+
break
129+
130+
# Check if installation was successful
131+
if "Install complete!" in log_content:
132+
install_info["install_success"] = True
133+
elif "level=error" in log_content or "FATAL" in log_content:
134+
install_info["install_success"] = False
135+
136+
return install_info
137+
17138
async def make_request(
18139
url: str, method: str = "GET", data: dict[str, Any] = None
19140
) -> dict[str, Any] | None:
@@ -64,10 +185,7 @@ async def get_job_metadata(job_name: str, build_id: str) -> dict:
64185
for arg in args:
65186
if arg.startswith("--target="):
66187
test_name=arg.replace("--target=","")
67-
68-
69-
70-
188+
71189
return {"status": status, "build_id": build_id, "job_name": job_name, "test_name": test_name}
72190

73191
except Exception as e:
@@ -247,5 +365,6 @@ async def get_install_logs(job_name: str, build_id: str, test_name: str):
247365
# print(result)
248366

249367
if __name__ == "__main__":
250-
# asyncio.run(main())
251-
mcp.run(transport=os.environ.get("MCP_TRANSPORT", "stdio"))
368+
# mcp.streamable_http_app()
369+
mcp.run(transport="streamable-http", host="0.0.0.0", port=9000)
370+

_prow_mcp_server/requirements.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
httpx
2-
fastmcp
2+
fastmcp>=2.10.6
3+
mcp>=1.8.0
34
python-dateutil
45
drain3

0 commit comments

Comments
 (0)