Skip to content

Commit 2fa7a00

Browse files
radofuchsRadovan Fuchs
authored andcommitted
LCORE-948: Restart prow e2e pod (lightspeed-core#1181)
* update workflow to restart the pod --------- Co-authored-by: Radovan Fuchs <rfuchs@rfuchs-thinkpadp1gen7.tpb.csb>
1 parent 93c0085 commit 2fa7a00

24 files changed

Lines changed: 1224 additions & 228 deletions

dev-tools/mcp-mock-server/server.py

Lines changed: 47 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,19 @@
66
useful for validating that Lightspeed Core Stack correctly sends auth headers
77
to MCP servers.
88
9-
The server runs both HTTP and HTTPS simultaneously on consecutive ports.
9+
The server runs HTTP and optionally HTTPS on consecutive ports.
10+
Set MCP_HTTP_ONLY=true to disable HTTPS (useful when openssl is unavailable).
1011
1112
Usage:
1213
python server.py [http_port]
1314
1415
Example:
1516
python server.py 3000 # HTTP on 3000, HTTPS on 3001
17+
MCP_HTTP_ONLY=true python server.py 3000 # HTTP only on 3000
1618
"""
1719

1820
import json
21+
import os
1922
import ssl
2023
import subprocess
2124
import sys
@@ -268,61 +271,79 @@ def run_https_server(port: int, httpd: HTTPServer) -> None:
268271

269272

270273
def main() -> None:
271-
"""Start the mock MCP server with both HTTP and HTTPS."""
274+
"""Start the mock MCP server with HTTP and optionally HTTPS."""
272275
http_port = int(sys.argv[1]) if len(sys.argv) > 1 else 3000
273-
https_port = http_port + 1
276+
http_only = os.environ.get("MCP_HTTP_ONLY", "").lower() in ("true", "1", "yes")
274277

275278
# Create HTTP server
276279
http_server = HTTPServer(("", http_port), MCPMockHandler)
277280

278-
# Create HTTPS server with self-signed certificate
279-
https_server = HTTPServer(("", https_port), MCPMockHandler)
280-
281-
# Generate or load self-signed certificate
282-
script_dir = Path(__file__).parent
283-
cert_dir = script_dir / ".certs"
284-
cert_file, key_file = generate_self_signed_cert(cert_dir)
285-
286-
# Wrap socket with SSL
287-
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
288-
context.load_cert_chain(cert_file, key_file)
289-
https_server.socket = context.wrap_socket(https_server.socket, server_side=True)
281+
https_server = None
282+
if not http_only:
283+
try:
284+
https_port = http_port + 1
285+
https_server = HTTPServer(("", https_port), MCPMockHandler)
286+
287+
# Generate or load self-signed certificate
288+
script_dir = Path(__file__).parent
289+
cert_dir = script_dir / ".certs"
290+
cert_file, key_file = generate_self_signed_cert(cert_dir)
291+
292+
# Wrap socket with SSL
293+
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
294+
context.load_cert_chain(cert_file, key_file)
295+
https_server.socket = context.wrap_socket(
296+
https_server.socket, server_side=True
297+
)
298+
except (subprocess.CalledProcessError, FileNotFoundError, OSError) as e:
299+
print(f"HTTPS setup failed ({e}), running HTTP only")
300+
https_server = None
290301

291302
print("=" * 70)
292-
print("MCP Mock Server starting with HTTP and HTTPS")
303+
if https_server:
304+
print("MCP Mock Server starting with HTTP and HTTPS")
305+
else:
306+
print("MCP Mock Server starting (HTTP only)")
293307
print("=" * 70)
294308
print(f"HTTP: http://localhost:{http_port}")
295-
print(f"HTTPS: https://localhost:{https_port}")
309+
if https_server:
310+
print(f"HTTPS: https://localhost:{https_port}")
296311
print("=" * 70)
297312
print("Debug endpoints:")
298313
print(" • /debug/headers - View captured headers")
299314
print(" • /debug/requests - View request log")
300315
print("MCP endpoint:")
301316
print(" • POST to any path (e.g., / or /mcp/v1/list_tools)")
302317
print("=" * 70)
303-
print("Note: HTTPS uses a self-signed certificate (for testing only)")
318+
if https_server:
319+
print("Note: HTTPS uses a self-signed certificate (for testing only)")
304320
print("Press Ctrl+C to stop")
305321
print()
306322

307-
# Start servers in separate threads
323+
# Start HTTP server in a thread
308324
http_thread = threading.Thread(
309325
target=run_http_server, args=(http_port, http_server), daemon=True
310326
)
311-
https_thread = threading.Thread(
312-
target=run_https_server, args=(https_port, https_server), daemon=True
313-
)
314-
315327
http_thread.start()
316-
https_thread.start()
328+
329+
# Start HTTPS server if available
330+
https_thread = None
331+
if https_server:
332+
https_thread = threading.Thread(
333+
target=run_https_server, args=(https_port, https_server), daemon=True
334+
)
335+
https_thread.start()
317336

318337
try:
319338
# Keep main thread alive
320339
http_thread.join()
321-
https_thread.join()
340+
if https_thread:
341+
https_thread.join()
322342
except KeyboardInterrupt:
323343
print("\nShutting down mock servers...")
324344
http_server.shutdown()
325-
https_server.shutdown()
345+
if https_server:
346+
https_server.shutdown()
326347

327348

328349
if __name__ == "__main__":
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Lightspeed Core Service (LCS)
2+
service:
3+
host: 0.0.0.0
4+
port: 8080
5+
auth_enabled: false
6+
workers: 1
7+
color_log: true
8+
access_log: true
9+
llama_stack:
10+
# Uses a remote llama-stack service
11+
# The instance would have already been started with a llama-stack-run.yaml file
12+
use_as_library_client: false
13+
# Alternative for "as library use"
14+
# use_as_library_client: true
15+
# library_client_config_path: <path-to-llama-stack-run.yaml-file>
16+
url: http://${env.E2E_LLAMA_HOSTNAME}:8321
17+
api_key: xyzzy
18+
user_data_collection:
19+
feedback_enabled: true
20+
feedback_storage: "/tmp/data/feedback"
21+
transcripts_enabled: true
22+
transcripts_storage: "/tmp/data/transcripts"
23+
24+
# Conversation cache for storing Q&A history
25+
conversation_cache:
26+
type: "sqlite"
27+
sqlite:
28+
db_path: "/tmp/data/conversation-cache.db"
29+
30+
authentication:
31+
module: "noop-with-token"
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Lightspeed Core Service (LCS) - RH Identity Auth
2+
service:
3+
host: 0.0.0.0
4+
port: 8080
5+
auth_enabled: true
6+
workers: 1
7+
color_log: true
8+
access_log: true
9+
llama_stack:
10+
use_as_library_client: false
11+
url: http://${env.E2E_LLAMA_HOSTNAME}:8321
12+
api_key: xyzzy
13+
user_data_collection:
14+
feedback_enabled: true
15+
feedback_storage: "/tmp/data/feedback"
16+
transcripts_enabled: true
17+
transcripts_storage: "/tmp/data/transcripts"
18+
conversation_cache:
19+
type: "sqlite"
20+
sqlite:
21+
db_path: "/tmp/data/conversation-cache.db"
22+
authentication:
23+
module: "rh-identity"
24+
rh_identity_config:
25+
required_entitlements: ["rhel"]
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
name: Lightspeed Core Service (LCS)
2+
service:
3+
host: 0.0.0.0
4+
port: 8080
5+
auth_enabled: false
6+
workers: 1
7+
color_log: true
8+
access_log: true
9+
llama_stack:
10+
# Uses a remote llama-stack service
11+
# The instance would have already been started with a llama-stack-run.yaml file
12+
use_as_library_client: false
13+
# Alternative for "as library use"
14+
# use_as_library_client: true
15+
# library_client_config_path: <path-to-llama-stack-run.yaml-file>
16+
url: http://${env.E2E_LLAMA_HOSTNAME}:8321
17+
api_key: xyzzy
18+
user_data_collection:
19+
feedback_enabled: true
20+
feedback_storage: "/invalid"
21+
transcripts_enabled: true
22+
transcripts_storage: "/tmp/data/transcripts"
23+
24+
authentication:
25+
module: "noop-with-token"
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Lightspeed Core Service (LCS)
2+
service:
3+
host: 0.0.0.0
4+
port: 8080
5+
auth_enabled: false
6+
workers: 1
7+
color_log: true
8+
access_log: true
9+
llama_stack:
10+
# Uses a remote llama-stack service
11+
# The instance would have already been started with a llama-stack-run.yaml file
12+
use_as_library_client: false
13+
# Alternative for "as library use"
14+
# use_as_library_client: true
15+
# library_client_config_path: <path-to-llama-stack-run.yaml-file>
16+
url: http://${env.E2E_LLAMA_HOSTNAME}:8321
17+
api_key: xyzzy
18+
user_data_collection:
19+
feedback_enabled: true
20+
feedback_storage: "/tmp/data/feedback"
21+
transcripts_enabled: true
22+
transcripts_storage: "/tmp/data/transcripts"
23+
24+
# NO conversation_cache configured - for testing error handling
25+
26+
authentication:
27+
module: "noop-with-token"
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
name: Lightspeed Core Service (RBAC E2E Tests)
2+
service:
3+
host: 0.0.0.0
4+
port: 8080
5+
auth_enabled: true
6+
workers: 1
7+
color_log: true
8+
access_log: true
9+
10+
llama_stack:
11+
use_as_library_client: false
12+
url: http://${env.E2E_LLAMA_HOSTNAME}:8321
13+
api_key: xyzzy
14+
15+
user_data_collection:
16+
feedback_enabled: true
17+
feedback_storage: "/tmp/data/feedback"
18+
transcripts_enabled: true
19+
transcripts_storage: "/tmp/data/transcripts"
20+
21+
# Conversation cache for storing Q&A history
22+
conversation_cache:
23+
type: "sqlite"
24+
sqlite:
25+
db_path: "/tmp/data/conversation-cache.db"
26+
27+
# JWK token authentication with role extraction
28+
authentication:
29+
module: "jwk-token"
30+
jwk_config:
31+
url: "http://mock-jwks:8000/.well-known/jwks.json"
32+
jwt_configuration:
33+
user_id_claim: "sub"
34+
username_claim: "name"
35+
# Role rules: extract roles from JWT claims
36+
role_rules:
37+
# Grant 'admin' role to users with admin=true in JWT
38+
- jsonpath: "$.admin"
39+
operator: "equals"
40+
value: [true]
41+
roles: ["admin"]
42+
# Grant 'user' role to users with role=user in JWT
43+
- jsonpath: "$.role"
44+
operator: "equals"
45+
value: ["user"]
46+
roles: ["user"]
47+
# Grant 'viewer' role to users with role=viewer in JWT
48+
- jsonpath: "$.role"
49+
operator: "equals"
50+
value: ["viewer"]
51+
roles: ["viewer"]
52+
# Grant 'query_only' role based on permissions array containing 'query'
53+
- jsonpath: "$.permissions[*]"
54+
operator: "contains"
55+
value: "query"
56+
roles: ["query_only"]
57+
58+
# Authorization: map roles to actions
59+
authorization:
60+
access_rules:
61+
# Admin role gets full access
62+
- role: "admin"
63+
actions: ["admin"]
64+
# User role can query, access conversations, and provide feedback
65+
- role: "user"
66+
actions:
67+
- "query"
68+
- "streaming_query"
69+
- "get_conversation"
70+
- "list_conversations"
71+
- "delete_conversation"
72+
- "update_conversation"
73+
- "feedback"
74+
- "get_models"
75+
- "get_tools"
76+
- "info"
77+
- "model_override"
78+
# Viewer role can only read (no mutations)
79+
- role: "viewer"
80+
actions:
81+
- "get_conversation"
82+
- "list_conversations"
83+
- "get_models"
84+
- "get_tools"
85+
- "info"
86+
# Query-only role can only query (no model_override - must use defaults)
87+
- role: "query_only"
88+
actions:
89+
- "query"
90+
- "streaming_query"
91+
# Everyone (*) role gets basic info access
92+
- role: "*"
93+
actions:
94+
- "info"

tests/e2e-prow/rhoai/configs/lightspeed-stack.yaml

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,21 @@ user_data_collection:
2323

2424
authentication:
2525
module: "noop"
26+
27+
mcp_servers:
28+
# Mock server with client-provided auth - should appear in mcp-auth/client-options response
29+
- name: "github-api"
30+
provider_id: "model-context-protocol"
31+
url: "http://mcp-mock-server:3000"
32+
authorization_headers:
33+
Authorization: "client"
34+
# Mock server with client-provided auth (different header) - should appear in response
35+
- name: "gitlab-api"
36+
provider_id: "model-context-protocol"
37+
url: "http://mcp-mock-server:3000"
38+
authorization_headers:
39+
X-API-Token: "client"
40+
# Mock server with no auth - should NOT appear in response
41+
- name: "public-api"
42+
provider_id: "model-context-protocol"
43+
url: "http://mcp-mock-server:3000"

0 commit comments

Comments
 (0)