Skip to content

Commit 96afa8d

Browse files
committed
Print connection strings after creating the cluster.
1 parent c22b161 commit 96afa8d

File tree

3 files changed

+131
-0
lines changed

3 files changed

+131
-0
lines changed

Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,6 @@ ENV PATH=$PATH:/root/.local/bin
2121

2222
COPY entrypoint.sh .
2323

24+
COPY print_connstrs.py .
25+
2426
ENTRYPOINT ["./entrypoint.sh"]

entrypoint.sh

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ shift
1414
echo "Starting MongoDB cluster …"
1515
mlaunch "$@" --bind_ip_all
1616

17+
echo
18+
./print_connstrs.py < data/.mlaunch_startup
19+
1720
# Hang forever:
1821
tail -f /dev/null

print_connstrs.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
#!/usr/bin/env python3
2+
import json
3+
import sys
4+
from typing import List, Optional, Tuple
5+
6+
7+
def build_hosts(hostname: str, start_port: int, count: int) -> str:
8+
"""
9+
Build host list like: hostname:start_port,hostname:start_port+1,...
10+
Returns just the host list (no mongodb:// prefix).
11+
"""
12+
return ",".join(f"{hostname}:{start_port + i}" for i in range(count))
13+
14+
15+
def is_numeric_string(s: str) -> bool:
16+
"""
17+
Return True if s is a non-empty string of digits (e.g. "3", "10").
18+
"""
19+
return isinstance(s, str) and s.isdigit()
20+
21+
22+
def parse_sharded(sharded_field) -> Tuple[bool, Optional[List[str]], Optional[int]]:
23+
"""
24+
Parse the 'sharded' field.
25+
26+
Returns:
27+
(is_sharded, shard_names, shard_count)
28+
29+
- Non-sharded (replset):
30+
(False, None, None)
31+
32+
- Sharded, numeric-string single-element array (e.g. ["3"]):
33+
(True, None, 3) # shard_count = 3
34+
35+
- Sharded, named shards (e.g. ["shardA", "shardB"]):
36+
(True, ["shardA", "shardB"], 2)
37+
38+
Raises:
39+
ValueError if the 'sharded' field has an invalid shape.
40+
"""
41+
if sharded_field is None:
42+
return False, None, None
43+
44+
if not isinstance(sharded_field, list):
45+
raise ValueError(f"Invalid 'sharded' value (not null or list): {sharded_field!r}")
46+
47+
if len(sharded_field) == 0:
48+
raise ValueError("Invalid 'sharded' value: empty array")
49+
50+
# All members must be strings
51+
if not all(isinstance(x, str) for x in sharded_field):
52+
raise ValueError(
53+
"Invalid 'sharded' value: list must contain only strings "
54+
f"(got: {sharded_field!r})"
55+
)
56+
57+
# Case 1: single numeric-string element → shard count
58+
if len(sharded_field) == 1 and is_numeric_string(sharded_field[0]):
59+
shard_count = int(sharded_field[0])
60+
if shard_count <= 0:
61+
raise ValueError(f"Invalid shard count in 'sharded': {sharded_field[0]!r}")
62+
return True, None, shard_count
63+
64+
# Case 2: array of shard names (strings)
65+
shard_names = sharded_field
66+
shard_count = len(shard_names)
67+
return True, shard_names, shard_count
68+
69+
70+
def main():
71+
data = json.load(sys.stdin)
72+
parsed_args = data.get("parsed_args", {})
73+
74+
hostname = parsed_args.get("hostname", "localhost")
75+
base_port = int(parsed_args.get("port", 27017))
76+
nodes = int(parsed_args.get("nodes", 1))
77+
sharded_field = parsed_args.get("sharded")
78+
mongos_count = int(parsed_args.get("mongos", 0))
79+
80+
try:
81+
is_sharded, shard_names, shard_count = parse_sharded(sharded_field)
82+
except ValueError as e:
83+
print(f"Error parsing 'sharded': {e}", file=sys.stderr)
84+
sys.exit(1)
85+
86+
# Non-sharded: replica set
87+
if not is_sharded:
88+
hosts = build_hosts(hostname, base_port, nodes)
89+
conn_str = f"mongodb://{hosts}"
90+
print("Connection string:")
91+
print(conn_str)
92+
return
93+
94+
# Sharded deployment
95+
96+
# 1. Mongos connection string
97+
mongos_hosts = build_hosts(hostname, base_port, mongos_count)
98+
mongos_conn_str = f"mongodb://{mongos_hosts}"
99+
print("Main connection string:")
100+
print(f"{mongos_conn_str}")
101+
102+
print()
103+
104+
# 2. Shards
105+
# Shard ports start immediately after the last mongos port.
106+
shard_base_port = base_port + mongos_count
107+
108+
# If shard_names is None, then shard_count came from numeric string like ["3"]
109+
if shard_names is None:
110+
width = max(2, len(str(shard_count)))
111+
shard_names = [f"shard{(i + 1):0{width}d}" for i in range(shard_count)]
112+
113+
# Sanity: shard_count should match len(shard_names)
114+
if shard_count is None:
115+
shard_count = len(shard_names)
116+
117+
print("Per-shard connection strings:")
118+
for i, shard_name in enumerate(shard_names):
119+
first_node_port = shard_base_port + i * nodes
120+
shard_hosts = build_hosts(hostname, first_node_port, nodes)
121+
shard_conn_str = f"mongodb://{shard_hosts}"
122+
print(f"{shard_name}: {shard_conn_str}")
123+
124+
125+
if __name__ == "__main__":
126+
main()

0 commit comments

Comments
 (0)