1+ """
2+ VIDEX container entrypoint.
3+
4+ This entrypoint provides two modes:
5+ - `server`: start the long-running VIDEX stats server (default)
6+ - `sync`: run the one-shot sync/env build script and exit
7+
8+ Design notes:
9+ - Keep argument handling minimal.
10+ - Do not rewrite user arguments.
11+ - Best-effort warnings are emitted for common container networking pitfalls
12+ (e.g. using localhost/127.0.0.1 in --target inside a container).
13+ """
14+
15+ from __future__ import annotations
16+
17+ import os
18+ import sys
19+ import subprocess
20+ from typing import List , Optional , Tuple
21+
22+ LOCALHOST_NAMES = {"127.0.0.1" , "localhost" , "::1" }
23+
24+
25+ def _in_container_best_effort () -> bool :
26+ """
27+ Best-effort heuristics to detect container environment.
28+ Used only for warnings (never for rewriting args or failing).
29+ """
30+ explicit = os .environ .get ("VIDEX_CONTAINER" )
31+ if explicit and explicit .strip ().lower () not in {"0" , "false" , "no" }:
32+ return True
33+
34+ if os .path .exists ("/.dockerenv" ):
35+ return True
36+
37+ if os .environ .get ("container" ):
38+ return True
39+
40+ try :
41+ with open ("/proc/1/cgroup" , "rt" , encoding = "utf-8" ) as f :
42+ c = f .read ()
43+ hints = ("docker" , "containerd" , "kubepods" , "podman" )
44+ return any (h in c for h in hints )
45+ except OSError :
46+ return False
47+
48+
49+ def _usage () -> str :
50+ return (
51+ "Usage:\n "
52+ " <image> [server]\n "
53+ " <image> sync --target HOST:PORT:DB:USER:PASS [--videx ...] [other args]\n "
54+ "\n "
55+ "Commands:\n "
56+ " server Start VIDEX server (default).\n "
57+ " sync Run one-shot scripts to collect metadata from --target, then add metadata into videx-server, and create virtual tables in --videx.\n "
58+ "\n "
59+ "Notes:\n "
60+ " In a container, 127.0.0.1/localhost refers to the container itself.\n "
61+ " See doc/VIDEX_SERVER_DOCKER.md for Docker networking tips.\n "
62+ )
63+
64+
65+ def _extract_flag_value (argv : List [str ], name : str ) -> Tuple [Optional [str ], bool ]:
66+ """
67+ Extract the value of a CLI flag from argv, supporting:
68+ --name value
69+ --name=value
70+
71+ Returns (value, present):
72+ - present=False => flag not present
73+ - present=True and value=None => flag present but missing value
74+ """
75+ for i , tok in enumerate (argv ):
76+ if tok == name :
77+ if i + 1 >= len (argv ) or argv [i + 1 ].startswith ("--" ):
78+ return None , True
79+ return argv [i + 1 ], True
80+ if tok .startswith (name + "=" ):
81+ return tok .split ("=" , 1 )[1 ], True
82+ return None , False
83+
84+
85+ def _parse_target_host (target : str ) -> Optional [str ]:
86+ """
87+ Parse host from a connection string of form:
88+ host:port:db:user:password
89+
90+ We only need host for warnings, so do not over-validate.
91+ If format is unexpected, return None.
92+ """
93+ if not target or ":" not in target :
94+ return None
95+ host = target .split (":" , 1 )[0 ].strip ()
96+ return host or None
97+
98+
99+ def _maybe_warn_localhost_target (argv : List [str ]) -> None :
100+ """
101+ Print best-effort warnings about using localhost/127.0.0.1 inside containers.
102+ No rewriting; no hard failure.
103+ """
104+ target , present = _extract_flag_value (argv , "--target" )
105+
106+ if not present :
107+ sys .stderr .write (
108+ "Warning: 'sync' usually needs --target HOST:PORT:DB:USER:PASS.\n "
109+ " The sync script will likely fail without it.\n \n "
110+ )
111+ return
112+
113+ if target is None :
114+ sys .stderr .write (
115+ "Warning: '--target' flag is present but has no value.\n "
116+ " The sync script will likely fail. Usage:\n \n "
117+ f"{ _usage ()} \n "
118+ )
119+ return
120+
121+ host = _parse_target_host (target )
122+ if not host or host not in LOCALHOST_NAMES :
123+ return
124+
125+ if not _in_container_best_effort ():
126+ return
127+
128+ sys .stderr .write (
129+ "Warning: You may be running in a container, but the `--target` parameter is configured with 127.0.0.1/localhost.\n "
130+ " In a container, localhost usually refers to the container itself.\n "
131+ " If your MariaDB/VIDEX runs on the host machine, this may fail.\n \n "
132+ "Suggestions:\n "
133+ " - Docker Desktop (Mac/Windows): try host.docker.internal in --target.\n "
134+ " - Linux Docker Engine: add this when running the container:\n "
135+ " --add-host=host.docker.internal:host-gateway\n "
136+ " then use host.docker.internal in --target.\n "
137+ " - If DB runs in the same container / same network namespace, localhost can be correct.\n \n "
138+ )
139+
140+
141+ def _run_module (module : str , argv : List [str ]) -> int :
142+ cmd = [sys .executable , "-m" , module ] + argv
143+ return subprocess .call (cmd )
144+
145+
146+ def _run_server (argv : List [str ]) -> int :
147+ # Runs: python -m sub_platforms.sql_opt.videx.scripts.start_videx_server ...
148+ return _run_module ("sub_platforms.sql_opt.videx.scripts.start_videx_server" , argv )
149+
150+
151+ def _run_sync (argv : List [str ]) -> int :
152+ # Runs: python -m sub_platforms.sql_opt.videx.scripts.videx_build_env ...
153+ return _run_module ("sub_platforms.sql_opt.videx.scripts.videx_build_env" , argv )
154+
155+
156+ def main () -> int :
157+ if len (sys .argv ) <= 1 :
158+ return _run_server ([])
159+
160+ subcmd = sys .argv [1 ]
161+ argv = sys .argv [2 :]
162+
163+ if subcmd in ("-h" , "--help" , "help" ):
164+ sys .stdout .write (_usage ())
165+ return 0
166+
167+ if subcmd == "server" :
168+ return _run_server (argv )
169+
170+ if subcmd == "sync" :
171+ _maybe_warn_localhost_target (argv )
172+ return _run_sync (argv )
173+
174+ # Convenience: if user passes flags without 'server', treat as server args.
175+ if subcmd .startswith ("-" ):
176+ return _run_server ([subcmd ] + argv )
177+
178+ sys .stderr .write (f"Error: unknown command '{ subcmd } '.\n \n { _usage ()} \n " )
179+ return 2
180+
181+
182+ if __name__ == "__main__" :
183+ raise SystemExit (main ())
0 commit comments