8000 Use closure to pass args to handlers (refs #60). · pythonAI/client_python@44df258 · GitHub
[go: up one dir, main page]

Skip to content

Commit 44df258

Browse files
committed
Use closure to pass args to handlers (refs prometheus#60).
1 parent 8adfe8a commit 44df258

File tree

5 files changed

+65
-33
lines changed

5 files changed

+65
-33
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,24 @@ for more information.
321321
`instance_ip_grouping_key` returns a grouping key with the instance label set
322322
to the host's IP address.
323323

324+
### Handlers for authentication
325+
326+
If the push gateway you are connecting to is protected with HTTP Basic Auth,
327+
you can use a special handler to set the Authorization header.
328+
329+
```python
330+
from prometheus_client import CollectorRegistry, Gauge, push_to_gateway
331+
import os
332+
333+
def my_auth_handler(url, method, timeout, headers, data):
334+
username = os.environ['PUSHGW_USERNAME']
335+
password = os.environ['PUSHGW_PASSWORD']
336+
return basic_auth_handler(url, method, timeout, headers, data, username, password)
337+
registry = CollectorRegistry()
338+
g = Gauge('job_last_success_unixtime', 'Last time a batch job successfully finished', registry=registry)
339+
g.set_to_current_time()
340+
push_to_gateway('localhost:9091', job='batchA', registry=registry, handler=my_auth_handler)
341+
```
324342

325343
## Bridges
326344

prometheus_client/exposition.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ def write_to_textfile(path, registry):
119119
os.rename(tmppath, path)
120120

121121

122-
def push_to_gateway(gateway, job, registry, grouping_key=None, timeout=None, handler=None, handler_args=None):
122+
def push_to_gateway(gateway, job, registry, grouping_key=None, timeout=None, handler=None):
123123
'''Push metrics to the given pushgateway.
124124
125125
`gateway` the url for your push gateway. Either of the form
@@ -155,14 +155,13 @@ def push_to_gateway(gateway, job, registry, grouping_key=None, timeout=None, han
155155
failure.
156156
'content' is the data which should be used to form the HTTP
157157
Message Body.
158-
`handler_args` is an optional dict of extra arguments to provide
159158
160159
This overwrites all metrics with the same job and grouping_key.
161160
This uses the PUT HTTP method.'''
162-
_use_gateway('PUT', gateway, job, registry, grouping_key, timeout, handler, handler_args)
161+
_use_gateway('PUT', gateway, job, registry, grouping_key, timeout, handler)
163162

164163

165-
def pushadd_to_gateway(gateway, job, registry, grouping_key=None, timeout=None, handler=None, handler_args=None):
164+
def pushadd_to_gateway(gateway, job, registry, grouping_key=None, timeout=None, handler=None):
166165
'''PushAdd metrics to the given pushgateway.
167166
168167
`gateway` the url for your push gateway. Either of the form
@@ -183,10 +182,10 @@ def pushadd_to_gateway(gateway, job, registry, grouping_key=None, timeout=None,
183182
184183
This replaces metrics with the same name, job and grouping_key.
185184
This uses the POST HTTP method.'''
186-
_use_gateway('POST', gateway, job, registry, grouping_key, timeout, handler, handler_args)
185+
_use_gateway('POST', gateway, job, registry, grouping_key, timeout, handler)
187186

188187

189-
def delete_from_gateway(gateway, job, grouping_key=None, timeout=None, handler=None, handler_args=None):
188+
def delete_from_gateway(gateway, job, grouping_key=None, timeout=None, handler=None):
190189
'''Delete metrics from the given pushgateway.
191190
192191
`gateway` the url for your push gateway. Either of the form
@@ -206,10 +205,10 @@ def delete_from_gateway(gateway, job, grouping_key=None, timeout=None, handler=N
206205
207206
This deletes metrics with the given job and grouping_key.
208207
This uses the DELETE HTTP method.'''
209-
_use_gateway('DELETE', gateway, job, None, grouping_key, timeout, handler, handler_args)
208+
_use_gateway('DELETE', gateway, job, None, grouping_key, timeout, handler)
210209

211210

212-
def _use_gateway(method, gateway, job, registry, grouping_key, timeout, handler, handler_args):
211+
def _use_gateway(method, gateway, job, registry, grouping_key, timeout, handler):
213212
gateway_url = urlparse(gateway)
214213
if not gateway_url.scheme:
215214
gateway = 'http://{0}'.format(gateway)
@@ -227,10 +226,8 @@ def _use_gateway(method, gateway, job, registry, grouping_key, timeout, handler,
227226
headers=[('Content-Type', CONTENT_TYPE_LATEST)]
228227
if handler is None:
229228
handler = default_handler
230-
if handler_args is None:
231-
handler_args = dict()
232229
handler(url=url, method=method, timeout=timeout,
233-
headers=headers, data=data, **handler_args)
230+
headers=headers, data=data)()
234231

235232
def instance_ip_grouping_key():
236233
'''Grouping key with instance set to the IP Address of this host.'''

prometheus_client/handlers/base.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,16 @@
66
# Python 3
77
from urllib.request import build_opener, Request, HTTPHandler
88

9-
def handler(url, method, timeout, headers, data, **kwargs):
10-
'''Default handler that implements HTTP/HTTPS connections.'''
11-
request = Request(url, data=data)
12-
request.get_method = lambda: method
13-
for k, v in headers:
14-
request.add_header(k, v)
15-
resp = build_opener(HTTPHandler).open(request, timeout=timeout)
16-
if resp.code >= 400:
17-
raise IOError("error talking to pushgateway: {0} {1}".format(
18-
resp.code, resp.msg))
9+
def handler(url, method, timeout, headers, data):
10+
def handle():
11+
'''Default handler that implements HTTP/HTTPS connections.'''
12+
request = Request(url, data=data)
13+
request.get_method = lambda: method
14+
for k, v in headers:
15+
request.add_header(k, v)
16+
resp = build_opener(HTTPHandler).open(request, timeout=timeout)
17+
if resp.code >= 400:
18+
raise IOError("error talking to pushgateway: {0} {1}".format(
19+
resp.code, resp.msg))
20+
21+
return handle

prometheus_client/handlers/basic_auth.py

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44

55
from prometheus_client.handlers.base import handler as default_handler
66

7-
def handler(url, method, timeout, headers, data, **kwargs):
8-
'''Handler that implements HTTP Basic Auth by setting auth headers if
9-
'username' passed as keyword argument.'''
10-
if 'username' in kwargs:
11-
username = kwargs['username']
12-
password = kwargs['password']
13-
auth_value = "{0}:{1}".format(username, password)
14-
auth_header = "Basic {0}".format(base64.b64encode(bytes(auth_value)))
15-
headers.append(['Authorization', auth_header])
16-
default_handler(url, method, timeout, headers, data)
7+
def handler(url, method, timeout, headers, data, username = None, password = None):
8+
def handle():
9+
'''Handler that implements HTTP Basic Auth by setting auth headers if
10+
'username' and 'password' arguments are supplied and not None.'''
11+
if username is not None and password is not None:
12+
auth_value = "{0}:{1}".format(username, password)
13+
auth_header = "Basic {0}".format(base64.b64encode(bytes(auth_value)))
14+
headers.append(['Authorization', auth_header])
15+
default_handler(url, method, timeout, headers, data)()
16+
17+
return handle

tests/test_exposition.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from prometheus_client import push_to_gateway, pushadd_to_gateway, delete_from_gateway
1515
from prometheus_client import CONTENT_TYPE_LATEST, instance_ip_grouping_key
1616
from prometheus_client.handlers.base import handler as default_handler
17+
from prometheus_client.handlers.basic_auth import handler as basic_auth_handler
1718

1819
try:
1920
from BaseHTTPServer import BaseHTTPRequestHandler
@@ -99,7 +100,10 @@ def setUp(self):
99100
self.requests = requests = []
100101
class TestHandler(BaseHTTPRequestHandler):
101102
def do_PUT(self):
102-
self.send_response(201)
103+
if 'with_basic_auth' in self.requestline and self.headers['authorization'] != 'Basic Zm9vOmJhcg==':
104+
self.send_response(401)
105+
else:
106+
self.send_response(201)
103107
length = int(self.headers['content-length'])
104108
requests.append((self, self.rfile.read(length)))
105109
self.end_headers()
@@ -168,14 +172,23 @@ def test_delete_with_groupingkey(self):
168172
def test_push_with_handler(self):
169173
def my_test_handler(url, method, timeout, headers, data):
170174
headers.append(['X-Test-Header', 'foobar'])
171-
default_handler(url, method, timeout, headers, data)
175+
return default_handler(url, method, timeout, headers, data)
172176
push_to_gateway(self.address, "my_job", self.registry, handler=my_test_handler)
173177
self.assertEqual(self.requests[0][0].command, 'PUT')
174178
self.assertEqual(self.requests[0][0].path, '/metrics/job/my_job')
175179
self.assertEqual(self.requests[0][0].headers.get('content-type'), CONTENT_TYPE_LATEST)
176180
self.assertEqual(self.requests[0][0].headers.get('x-test-header'), 'foobar')
177181
self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g gauge\ng 0.0\n')
178182

183+
def test_push_with_basic_auth_handler(self):
184+
def my_auth_handler(url, method, timeout, headers, data):
185+
return basic_auth_handler(url, method, timeout, headers, data, "foo", "bar")
186+
push_to_gateway(self.address, "my_job_with_basic_auth", self.registry, handler=my_auth_handler)
187+
self.assertEqual(self.requests[0][0].command, 'PUT')
188+
self.assertEqual(self.requests[0][0].path, '/metrics/job/my_job_with_basic_auth')
189+
self.assertEqual(self.requests[0][0].headers.get('content-type'), CONTENT_TYPE_LATEST)
190+
self.assertEqual(self.requests[0][1], b'# HELP g help\n# TYPE g gauge\ng 0.0\n')
191+
179192
@unittest.skipIf(
180193
sys.platform == "darwin",
181194
"instance_ip_grouping_key() does not work on macOS."

0 commit comments

Comments
 (0)
0