1+ FROM ubuntu:20.04
2+
3+ # Prevent interactive prompts during package installation
4+ ENV DEBIAN_FRONTEND=noninteractive
5+
6+ WORKDIR /app
7+
8+ # Install Python and pip
9+ RUN apt-get update && apt-get install -y \
10+ python3 \
11+ python3-pip \
12+ && rm -rf /var/lib/apt/lists/*
13+
14+ # Install dependencies
15+ RUN pip3 install requests pyyaml
16+
17+ # Create a simple xDS server script
18+ RUN echo '#!/usr/bin/env python3\n\
19+ import json\n\
20+ import time\n\
21+ import threading\n\
22+ import http.server\n\
23+ import socketserver\n\
24+ import logging\n\
25+ \n\
26+ logging.basicConfig(level=logging.INFO)\n\
27+ logger = logging.getLogger(__name__)\n\
28+ \n\
29+ class XDSServer:\n\
30+ def __init__(self):\n\
31+ self.listeners = {}\n\
32+ self.version = 1\n\
33+ self._lock = threading.Lock()\n\
34+ self.server = None\n\
35+ \n\
36+ def start(self, port):\n\
37+ class XDSHandler(http.server.BaseHTTPRequestHandler):\n\
38+ def do_POST(self):\n\
39+ if self.path == "/v3/discovery:listeners":\n\
40+ content_length = int(self.headers["Content-Length"])\n\
41+ post_data = self.rfile.read(content_length)\n\
42+ response_data = self.server.xds_server.handle_lds_request(post_data)\n\
43+ self.send_response(200)\n\
44+ self.send_header("Content-type", "application/json")\n\
45+ self.end_headers()\n\
46+ self.wfile.write(response_data.encode())\n\
47+ elif self.path == "/add_listener":\n\
48+ content_length = int(self.headers["Content-Length"])\n\
49+ post_data = self.rfile.read(content_length)\n\
50+ data = json.loads(post_data.decode())\n\
51+ self.server.xds_server.add_listener(data["name"], data["config"])\n\
52+ self.send_response(200)\n\
53+ self.send_header("Content-type", "application/json")\n\
54+ self.end_headers()\n\
55+ self.wfile.write(json.dumps({"status": "success"}).encode())\n\
56+ elif self.path == "/remove_listener":\n\
57+ content_length = int(self.headers["Content-Length"])\n\
58+ post_data = self.rfile.read(content_length)\n\
59+ data = json.loads(post_data.decode())\n\
60+ success = self.server.xds_server.remove_listener(data["name"])\n\
61+ if success:\n\
62+ self.send_response(200)\n\
63+ self.send_header("Content-type", "application/json")\n\
64+ self.end_headers()\n\
65+ self.wfile.write(json.dumps({"status": "success"}).encode())\n\
66+ else:\n\
67+ self.send_response(404)\n\
68+ self.send_header("Content-type", "application/json")\n\
69+ self.end_headers()\n\
70+ self.wfile.write(json.dumps({"status": "not_found"}).encode())\n\
71+ elif self.path == "/state":\n\
72+ state = self.server.xds_server.get_state()\n\
73+ self.send_response(200)\n\
74+ self.send_header("Content-type", "application/json")\n\
75+ self.end_headers()\n\
76+ self.wfile.write(json.dumps(state).encode())\n\
77+ else:\n\
78+ self.send_response(404)\n\
79+ self.end_headers()\n\
80+ \n\
81+ def log_message(self, format, *args):\n\
82+ pass\n\
83+ \n\
84+ class XDSServer(socketserver.TCPServer):\n\
85+ def __init__(self, server_address, RequestHandlerClass, xds_server):\n\
86+ self.xds_server = xds_server\n\
87+ super().__init__(server_address, RequestHandlerClass)\n\
88+ \n\
89+ self.server = XDSServer(("0.0.0.0", port), XDSHandler, self)\n\
90+ self.server_thread = threading.Thread(target=self.server.serve_forever)\n\
91+ self.server_thread.daemon = True\n\
92+ self.server_thread.start()\n\
93+ logger.info(f"xDS server started on port {port}")\n\
94+ \n\
95+ def handle_lds_request(self, request_data):\n\
96+ with self._lock:\n\
97+ response = {\n\
98+ "version_info": str(self.version),\n\
99+ "resources": [],\n\
100+ "type_url": "type.googleapis.com/envoy.config.listener.v3.Listener"\n\
101+ }\n\
102+ for listener_name, listener_config in self.listeners.items():\n\
103+ # Wrap the listener config in a proper Any message\n\
104+ wrapped_config = {\n\
105+ "@type": "type.googleapis.com/envoy.config.listener.v3.Listener",\n\
106+ **listener_config\n\
107+ }\n\
108+ response["resources"].append(wrapped_config)\n\
109+ return json.dumps(response)\n\
110+ \n\
111+ def add_listener(self, listener_name, listener_config):\n\
112+ with self._lock:\n\
113+ self.listeners[listener_name] = listener_config\n\
114+ self.version += 1\n\
115+ logger.info(f"Added listener {listener_name}, version {self.version}")\n\
116+ \n\
117+ def remove_listener(self, listener_name):\n\
118+ with self._lock:\n\
119+ if listener_name in self.listeners:\n\
120+ del self.listeners[listener_name]\n\
121+ self.version += 1\n\
122+ logger.info(f"Removed listener {listener_name}, version {self.version}")\n\
123+ return True\n\
124+ return False\n\
125+ \n\
126+ def get_state(self):\n\
127+ with self._lock:\n\
128+ return {\n\
129+ "version": self.version,\n\
130+ "listeners": list(self.listeners.keys())\n\
131+ }\n\
132+ \n\
133+ if __name__ == "__main__":\n\
134+ xds_server = XDSServer()\n\
135+ xds_server.start(18000)\n\
136+ try:\n\
137+ while True:\n\
138+ time.sleep(1)\n\
139+ except KeyboardInterrupt:\n\
140+ print("Shutting down xDS server...")\n\
141+ ' > /app/xds_server.py
142+
143+ # Make the script executable
144+ RUN chmod +x /app/xds_server.py
145+
146+ # Expose the xDS server port
147+ EXPOSE 18000
148+
149+ # Run the xDS server
150+ CMD ["python3", "/app/xds_server.py"]
0 commit comments