Skip to content

Commit 10f808d

Browse files
Use toolforge envvars if available
If both of the relevant envvars are set (TOOL_REPLICA_USER + TOOL_REPLICA_PASSWORD for the replicas, TOOL_TOOLSDB_USER + TOOL_TOOLSDB_PASSWORD for toolsdb – they’re currently the same but might diverge later), use them and skip reading the replica.my.cnf, which may not be mounted. If only one envvar is set, don’t use it – this is mostly “should never happen” territory but I think that makes slightly more sense than trying to combine an incomplete set of envvars with the replica.my.cnf file. Bug: T339940
1 parent 0d764c9 commit 10f808d

3 files changed

Lines changed: 154 additions & 1 deletion

File tree

.local-dictionary.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ MERCHANTABILITY
44
Mehta
55
PrivateFileWorldReadableError
66
dbname
7+
delenv
78
fileno
89
func
910
intersphinx
@@ -15,6 +16,7 @@ pygments
1516
pymysql
1617
qualname
1718
rtd
19+
setenv
1820
sitematrix
1921
tmp
2022
toolforge

src/toolforge/__init__.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,15 @@ def connect(
7474
host = f"{extension}.{host}"
7575
host = kwargs.pop("host", host)
7676

77+
try:
78+
user = os.environ["TOOL_REPLICA_USER"]
79+
password = os.environ["TOOL_REPLICA_PASSWORD"]
80+
except KeyError:
81+
kwargs.setdefault("read_default_file", os.path.expanduser("~/replica.my.cnf"))
82+
else:
83+
kwargs.setdefault("user", user)
84+
kwargs.setdefault("password", password)
85+
7786
return _connect(
7887
database=dbname + "_p",
7988
host=host,
@@ -84,7 +93,6 @@ def connect(
8493
def _connect(*args: str, **kwargs: str) -> _Connection: # pragma: no cover
8594
"""Wraper for pymysql.connect to make testing easier."""
8695
kw = {
87-
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
8896
"charset": "utf8mb4",
8997
}
9098
kw.update(kwargs)
@@ -98,6 +106,15 @@ def toolsdb(dbname: str, **kwargs: str) -> _Connection:
98106
:param `**kwargs`: For :meth:`pymysql.connect <pymysql.connections.Connection.__init__>`
99107
:return: :class:`pymysql.connections.Connection`
100108
"""
109+
try:
110+
user = os.environ["TOOL_TOOLSDB_USER"]
111+
password = os.environ["TOOL_TOOLSDB_PASSWORD"]
112+
except KeyError:
113+
kwargs.setdefault("read_default_file", os.path.expanduser("~/replica.my.cnf"))
114+
else:
115+
kwargs.setdefault("user", user)
116+
kwargs.setdefault("password", password)
117+
101118
return _connect(
102119
database=dbname,
103120
host="tools.db.svc.wikimedia.cloud",

tests/main_test.py

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#!/usr/bin/env python
22
import io
33
import json
4+
import os
45

56
import pytest
67
import requests
@@ -63,6 +64,7 @@ def test_set_user_agent(self, tool, url, email, expect):
6364
{
6465
"database": "enwiki_p",
6566
"host": "enwiki.web.db.svc.wikimedia.cloud",
67+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
6668
},
6769
),
6870
(
@@ -71,6 +73,7 @@ def test_set_user_agent(self, tool, url, email, expect):
7173
{
7274
"database": "enwiki_p",
7375
"host": "enwiki.web.db.svc.wikimedia.cloud",
76+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
7477
},
7578
),
7679
(
@@ -79,6 +82,7 @@ def test_set_user_agent(self, tool, url, email, expect):
7982
{
8083
"database": "enwiki_p",
8184
"host": "enwiki.analytics.db.svc.wikimedia.cloud",
85+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
8286
},
8387
),
8488
(
@@ -87,6 +91,7 @@ def test_set_user_agent(self, tool, url, email, expect):
8791
{
8892
"database": "meta_p",
8993
"host": "s7.analytics.db.svc.wikimedia.cloud",
94+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
9095
},
9196
),
9297
(
@@ -95,13 +100,77 @@ def test_set_user_agent(self, tool, url, email, expect):
95100
{
96101
"database": "wikidatawiki_p",
97102
"host": "termstore.wikidatawiki.web.db.svc.wikimedia.cloud",
103+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
98104
},
99105
),
100106
],
101107
)
102108
def test_connect(self, mocker, args, kwargs, expects):
103109
self._assert_connect(mocker, toolforge.connect, args, kwargs, expects)
104110

111+
@pytest.mark.parametrize(
112+
("env", "expects"),
113+
[
114+
(
115+
{
116+
"TOOL_REPLICA_USER": "user name",
117+
"TOOL_REPLICA_PASSWORD": "password",
118+
},
119+
{
120+
"user": "user name",
121+
"password": "password",
122+
},
123+
),
124+
(
125+
{
126+
"TOOL_REPLICA_USER": None,
127+
"TOOL_REPLICA_PASSWORD": None,
128+
},
129+
{
130+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
131+
},
132+
),
133+
(
134+
{
135+
"TOOL_REPLICA_USER": "unused user name",
136+
"TOOL_REPLICA_PASSWORD": None,
137+
},
138+
{
139+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
140+
},
141+
),
142+
(
143+
{
144+
"TOOL_REPLICA_USER": None,
145+
"TOOL_REPLICA_PASSWORD": "unused password",
146+
},
147+
{
148+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
149+
},
150+
),
151+
(
152+
{
153+
"TOOL_TOOLSDB_USER": "unused",
154+
"TOOL_TOOLSDB_PASSWORD": "(toolsdb != replica)",
155+
},
156+
{
157+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
158+
},
159+
),
160+
],
161+
)
162+
def test_connect_env(self, mocker, monkeypatch, env, expects):
163+
for name, value in env.items():
164+
if value is None:
165+
monkeypatch.delenv(name, raising=False)
166+
else:
167+
monkeypatch.setenv(name, value)
168+
args = ["enwiki_p"]
169+
kwargs = {}
170+
expects["database"] = "enwiki_p"
171+
expects["host"] = "enwiki.web.db.svc.wikimedia.cloud"
172+
self._assert_connect(mocker, toolforge.connect, args, kwargs, expects)
173+
105174
def _assert_connect(self, mocker, func, args, kwargs, expect):
106175
"""Mock toolforge._connect and assert it is called as expected.
107176
@@ -134,13 +203,15 @@ def test_connect_rejects_unknown_cluster(self, mocker):
134203
{
135204
"database": "s12345__foo",
136205
"host": "tools.db.svc.wikimedia.cloud",
206+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
137207
},
138208
),
139209
(
140210
["s12345__foo_p"],
141211
{
142212
"database": "s12345__foo_p",
143213
"host": "tools.db.svc.wikimedia.cloud",
214+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
144215
},
145216
),
146217
],
@@ -149,6 +220,69 @@ def test_toolsdb(self, mocker, args, expects):
149220
kwargs = {}
150221
self._assert_connect(mocker, toolforge.toolsdb, args, kwargs, expects)
151222

223+
@pytest.mark.parametrize(
224+
("env", "expects"),
225+
[
226+
(
227+
{
228+
"TOOL_TOOLSDB_USER": "user name",
229+
"TOOL_TOOLSDB_PASSWORD": "password",
230+
},
231+
{
232+
"user": "user name",
233+
"password": "password",
234+
},
235+
),
236+
(
237+
{
238+
"TOOL_TOOLSDB_USER": None,
239+
"TOOL_TOOLSDB_PASSWORD": None,
240+
},
241+
{
242+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
243+
},
244+
),
245+
(
246+
{
247+
"TOOL_TOOLSDB_USER": "unused user name",
248+
"TOOL_TOOLSDB_PASSWORD": None,
249+
},
250+
{
251+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
252+
},
253+
),
254+
(
255+
{
256+
"TOOL_TOOLSDB_USER": None,
257+
"TOOL_TOOLSDB_PASSWORD": "unused password",
258+
},
259+
{
260+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
261+
},
262+
),
263+
(
264+
{
265+
"TOOL_REPLICA_USER": "unused",
266+
"TOOL_REPLICA_PASSWORD": "(toolsdb != replica)",
267+
},
268+
{
269+
"read_default_file": os.path.expanduser("~/replica.my.cnf"),
270+
},
271+
),
272+
],
273+
)
274+
def test_toolsdb_env(self, mocker, monkeypatch, env, expects):
275+
for name, value in env.items():
276+
if value is None:
277+
monkeypatch.delenv(name, raising=False)
278+
else:
279+
monkeypatch.setenv(name, value)
280+
args = ["s12345__foo"]
281+
kwargs = {}
282+
expects["database"] = "s12345__foo"
283+
expects["host"] = "tools.db.svc.wikimedia.cloud"
284+
self._assert_connect(mocker, toolforge.toolsdb, args, kwargs, expects)
285+
152286
def test_assert_private_file_no_args(self, mocker):
153287
load = mocker.Mock(
154288
return_value="data",

0 commit comments

Comments
 (0)