diff --git a/README.rst b/README.rst index 6ae8da63..a5b61355 100644 --- a/README.rst +++ b/README.rst @@ -29,6 +29,19 @@ Example # in another process c.kv.put('foo', 'bar') + +Alternatively you can create a client from the same `environment variables`_ that +the consul command line client uses. e.g. CONSUL_HTTP_ADDR, CONSUL_HTTP_TOKEN. + +.. code:: python + + import consul + + c = consul.Consul.from_env() + + c.agent.self() + + Installation ------------ @@ -45,6 +58,7 @@ Installation image:: https://img.shields.io/coveralls/cablehead/python-consul.svg?style=flat-square :target: https://coveralls.io/r/cablehead/python-consul?branch=master .. _Read the Docs: https://python-consul.readthedocs.io/ +.. _environment variables: https://www.consul.io/docs/commands/index.html#environment-variables Status ------ diff --git a/consul/aio.py b/consul/aio.py index 5e347f53..54554ebb 100644 --- a/consul/aio.py +++ b/consul/aio.py @@ -3,6 +3,8 @@ import asyncio import warnings +from six.moves import urllib + import aiohttp from consul import base @@ -17,8 +19,14 @@ class HTTPClient(base.HTTPClient): def __init__(self, *args, loop=None, **kwargs): super(HTTPClient, self).__init__(*args, **kwargs) self._loop = loop or asyncio.get_event_loop() - connector = aiohttp.TCPConnector(loop=self._loop, - verify_ssl=self.verify) + if 'unix://' in self.base_uri: + pr = urllib.parse.urlparse(self.base_uri) + self.base_uri = 'http://consul' + connector = aiohttp.UnixConnector(loop=self._loop, + path=pr.path) + else: + connector = aiohttp.TCPConnector(loop=self._loop, + verify_ssl=self.verify) self._session = aiohttp.ClientSession(connector=connector) @asyncio.coroutine @@ -64,9 +72,9 @@ def __init__(self, *args, loop=None, **kwargs): self._loop = loop or asyncio.get_event_loop() super().__init__(*args, **kwargs) - def connect(self, host, port, scheme, verify=True, cert=None): - return HTTPClient(host, port, scheme, loop=self._loop, - verify=verify, cert=None) + def connect(self, base_uri, verify=True, cert=None, auth=None): + return HTTPClient(base_uri, loop=self._loop, + verify=verify, cert=cert, auth=auth) def close(self): """Close all opened http connections""" diff --git a/consul/base.py b/consul/base.py index ee6ab254..87bfc759 100755 --- a/consul/base.py +++ b/consul/base.py @@ -246,14 +246,11 @@ def cb(response): class HTTPClient(six.with_metaclass(abc.ABCMeta, object)): - def __init__(self, host='127.0.0.1', port=8500, scheme='http', - verify=True, cert=None): - self.host = host - self.port = port - self.scheme = scheme + def __init__(self, base_uri, verify=True, cert=None, auth=None): + self.base_uri = base_uri self.verify = verify - self.base_uri = '%s://%s:%s' % (self.scheme, self.host, self.port) self.cert = cert + self.auth = auth def uri(self, path, params=None): uri = self.base_uri + urllib.parse.quote(path, safe='/:') @@ -288,7 +285,9 @@ def __init__( consistency='default', dc=None, verify=True, - cert=None): + cert=None, + auth=None, + addr=None): """ *token* is an optional `ACL token`_. If supplied it will be used by default for all requests made with this client session. It's still @@ -305,27 +304,19 @@ def __init__( *verify* is whether to verify the SSL certificate for HTTPS requests - *cert* client side certificates for HTTPS requests + *cert* tuple containing client certificate and key for HTTPS requests + + *addr* url to use instead of host, port and scheme. + e.g. unix:///var/run/consul/http.sock, http://localhost:8500/ """ # TODO: Status - if os.getenv('CONSUL_HTTP_ADDR'): - try: - host, port = os.getenv('CONSUL_HTTP_ADDR').split(':') - except ValueError: - raise ConsulException('CONSUL_HTTP_ADDR (%s) invalid, ' - 'does not match :' - % os.getenv('CONSUL_HTTP_ADDR')) - use_ssl = os.getenv('CONSUL_HTTP_SSL') - if use_ssl is not None: - scheme = 'https' if use_ssl == 'true' else 'http' - if os.getenv('CONSUL_HTTP_SSL_VERIFY') is not None: - verify = os.getenv('CONSUL_HTTP_SSL_VERIFY') == 'true' - - self.http = self.connect(host, port, scheme, verify, cert) - self.token = os.getenv('CONSUL_HTTP_TOKEN', token) - self.scheme = scheme + if not addr: + addr = '{0}://{1}:{2}'.format(scheme, host, port) + + self.http = self.connect(addr, verify=verify, cert=cert, auth=auth) + self.token = token self.dc = dc assert consistency in ('default', 'consistent', 'stale'), \ 'consistency must be either default, consistent or state' @@ -344,6 +335,62 @@ def __init__( self.coordinate = Consul.Coordinate(self) self.operator = Consul.Operator(self) + @classmethod + def from_env(cls, consistency='default', dc=None): + """ + Return a client configured from environment variables. + The environment variables used are the same as those used by the + consul command-line client. Refer to the consul documentation [1] for + a list of existing environment variables. + + Additionally all supported given keyword arguments are passed on the + client constructor. + + [1] https://www.consul.io/docs/commands/index.html#environment-variables # noqa: E501 + """ + oe = os.environ + kwargs = { + 'consistency': consistency, + 'dc': dc, + 'token': oe.get('CONSUL_HTTP_TOKEN', None), + } + + addr = oe.get('CONSUL_HTTP_ADDR', None) + if addr: + if not addr.startswith('http'): + # Ensure addr starts with a scheme. + ssl = oe.get('CONSUL_HTTP_SSL', False) + if ssl == 'false': + ssl = False + elif ssl == 'true': + ssl = True + + if ssl: + scheme = 'https' + else: + scheme = 'http' + addr = '%s://%s' % (scheme, addr) + kwargs['addr'] = addr + + verify = oe.get('CONSUL_CACERT', + oe.get('CONSUL_HTTP_SSL_VERIFY', None)) + if verify: + if verify == 'false': + verify = False + elif verify == 'true': + verify = True + kwargs['verify'] = verify + + if 'CONSUL_CLIENT_CERT' in oe \ + and 'CONSUL_CLIENT_KEY' in oe: + kwargs['cert'] = (oe['CONSUL_CLIENT_CERT'], + oe['CONSUL_CLIENT_KEY']) + + if 'CONSUL_HTTP_AUTH' in oe: + kwargs['auth'] = oe['CONSUL_HTTP_AUTH'].split(':') + + return cls(**kwargs) + class Event(object): """ The event command provides a mechanism to fire a custom user event to diff --git a/consul/std.py b/consul/std.py index 96a5b9dc..a333c5be 100644 --- a/consul/std.py +++ b/consul/std.py @@ -1,5 +1,7 @@ import requests +from six.moves import urllib + from consul import base @@ -9,7 +11,19 @@ class HTTPClient(base.HTTPClient): def __init__(self, *args, **kwargs): super(HTTPClient, self).__init__(*args, **kwargs) - self.session = requests.session() + if 'unix://' in self.base_uri: + pr = urllib.parse.urlparse(self.base_uri) + netloc = urllib.parse.quote_plus(pr.path) + self.base_uri = 'http+unix://{0}'.format(netloc) + try: + import requests_unixsocket + self.session = requests_unixsocket.Session() + except ImportError: + raise base.ConsulException('To use a unix socket to connect to' + ' Consul you need to install the' + ' "requests_unixsocket" package.') + else: + self.session = requests.session() def response(self, response): response.encoding = 'utf-8' @@ -40,5 +54,5 @@ def post(self, callback, path, params=None, data=''): class Consul(base.Consul): - def connect(self, host, port, scheme, verify=True, cert=None): - return HTTPClient(host, port, scheme, verify, cert) + def connect(self, base_uri, verify=True, cert=None, auth=None): + return HTTPClient(base_uri, verify=verify, cert=cert, auth=auth) diff --git a/consul/tornado.py b/consul/tornado.py index 50507748..bb4d30f5 100644 --- a/consul/tornado.py +++ b/consul/tornado.py @@ -12,6 +12,7 @@ class HTTPClient(base.HTTPClient): def __init__(self, *args, **kwargs): super(HTTPClient, self).__init__(*args, **kwargs) + # TODO: support for unix:// uris self.client = httpclient.AsyncHTTPClient() def response(self, response): @@ -53,5 +54,5 @@ def post(self, callback, path, params=None, data=''): class Consul(base.Consul): - def connect(self, host, port, scheme, verify=True, cert=None): - return HTTPClient(host, port, scheme, verify=verify, cert=cert) + def connect(self, base_uri, verify=True, cert=None, auth=None): + return HTTPClient(base_uri, verify=verify, cert=cert, auth=auth) diff --git a/consul/twisted.py b/consul/twisted.py index 206105cb..6f50c4c0 100644 --- a/consul/twisted.py +++ b/consul/twisted.py @@ -34,6 +34,7 @@ def getContext(self, hostname, port): class HTTPClient(base.HTTPClient): def __init__(self, contextFactory, *args, **kwargs): super(HTTPClient, self).__init__(*args, **kwargs) + # TODO: support for unix:// uris agent_kwargs = dict( reactor=reactor, pool=HTTPConnectionPool(reactor)) if contextFactory is not None: @@ -125,13 +126,12 @@ def delete(self, callback, path, params=None): class Consul(base.Consul): @staticmethod - def connect(host, - port, - scheme, + def connect(base_uri, verify=True, cert=None, + auth=None, contextFactory=None, **kwargs): return HTTPClient( - contextFactory, host, port, scheme, verify=verify, cert=cert, + contextFactory, base_uri, verify=verify, cert=cert, auth=auth, **kwargs) diff --git a/requirements.txt b/requirements.txt index 2846886d..ce3023dc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ requests>=2.0 +requests-unixsocket>=0.1.5 six>=1.4 diff --git a/tests/test_aio.py b/tests/test_aio.py index b251231f..374ccc5b 100644 --- a/tests/test_aio.py +++ b/tests/test_aio.py @@ -24,13 +24,17 @@ def fin(): return loop +@pytest.fixture +def c(consul_port, loop): + return consul.aio.Consul(port=consul_port, loop=loop) + + class TestAsyncioConsul(object): - def test_kv(self, loop, consul_port): + def test_kv(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) print(c) index, data = yield from c.kv.get('foo') @@ -44,11 +48,10 @@ def main(): loop.run_until_complete(main()) - def test_consul_ctor(self, loop, consul_port): + def test_consul_ctor(self, loop, c): # same as previous but with global event loop @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port) assert c._loop is loop yield from c.kv.put('foo', struct.pack('i', 1000)) index, data = yield from c.kv.get('foo') @@ -58,10 +61,9 @@ def main(): asyncio.set_event_loop(loop) loop.run_until_complete(main()) - def test_kv_binary(self, loop, consul_port): + def test_kv_binary(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) yield from c.kv.put('foo', struct.pack('i', 1000)) index, data = yield from c.kv.get('foo') assert struct.unpack('i', data['Value']) == (1000,) @@ -69,8 +71,7 @@ def main(): loop.run_until_complete(main()) - def test_kv_missing(self, loop, consul_port): - c = consul.aio.Consul(port=consul_port, loop=loop) + def test_kv_missing(self, loop, c): @asyncio.coroutine def main(): @@ -90,10 +91,9 @@ def put(): loop.run_until_complete(main()) - def test_kv_put_flags(self, loop, consul_port): + def test_kv_put_flags(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) yield from c.kv.put('foo', 'bar') index, data = yield from c.kv.get('foo') assert data['Flags'] == 0 @@ -106,10 +106,9 @@ def main(): loop.run_until_complete(main()) - def test_kv_delete(self, loop, consul_port): + def test_kv_delete(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) yield from c.kv.put('foo1', '1') yield from c.kv.put('foo2', '2') yield from c.kv.put('foo3', '3') @@ -128,8 +127,7 @@ def main(): loop.run_until_complete(main()) - def test_kv_subscribe(self, loop, consul_port): - c = consul.aio.Consul(port=consul_port, loop=loop) + def test_kv_subscribe(self, loop, c): @asyncio.coroutine def get(): @@ -149,10 +147,9 @@ def put(): loop.run_until_complete(get()) - def test_transaction(self, loop, consul_port): + def test_transaction(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) value = base64.b64encode(b"1").decode("utf8") d = {"KV": {"Verb": "set", "Key": "asdf", "Value": value}} r = yield from c.txn.put([d]) @@ -164,10 +161,9 @@ def main(): c.close() loop.run_until_complete(main()) - def test_agent_services(self, loop, consul_port): + def test_agent_services(self, loop, c): @asyncio.coroutine def main(): - c = consul.aio.Consul(port=consul_port, loop=loop) services = yield from c.agent.services() assert services == {} response = yield from c.agent.service.register('foo') @@ -192,8 +188,7 @@ def main(): loop.run_until_complete(main()) - def test_catalog(self, loop, consul_port): - c = consul.aio.Consul(port=consul_port, loop=loop) + def test_catalog(self, loop, c): @asyncio.coroutine def nodes(): @@ -223,8 +218,7 @@ def register(): loop.run_until_complete(nodes()) - def test_session(self, loop, consul_port): - c = consul.aio.Consul(port=consul_port, loop=loop) + def test_session(self, loop, c): @asyncio.coroutine def monitor(): diff --git a/tests/test_base.py b/tests/test_base.py index ef76a9fa..00ae1a83 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,5 +1,7 @@ import collections +from contextlib import contextmanager import json +import os import pytest @@ -14,9 +16,11 @@ class HTTPClient(object): - def __init__(self, host=None, port=None, scheme=None, - verify=True, cert=None): - pass + def __init__(self, base_uri, verify=True, cert=None, auth=None): + self.base_uri = base_uri + self.verify = verify + self.cert = cert + self.auth = auth def get(self, callback, path, params=None): return Request('get', path, params, None) @@ -29,8 +33,83 @@ def delete(self, callback, path, params=None): class Consul(consul.base.Consul): - def connect(self, host, port, scheme, verify=True, cert=None): - return HTTPClient(host, port, scheme, verify=verify, cert=None) + def connect(self, base_uri, verify=True, cert=None, auth=None): + return HTTPClient(base_uri, verify=verify, cert=cert, auth=auth) + + +class TestEnvironment(object): + + @contextmanager + def environ(self, **env): + original_env = {} + for key in env: + original_env[key] = os.getenv(key) + os.environ.update(env) + try: + yield + finally: + for key, value in original_env.items(): + if value is None: + del os.environ[key] + else: + os.environ[key] = value + + def test_CONSUL_HTTP_ADDR(self): + CONSUL_HTTP_ADDR = 'http://127.0.0.23:4242' + with self.environ(CONSUL_HTTP_ADDR=CONSUL_HTTP_ADDR): + c = Consul.from_env() + assert c.http.base_uri == CONSUL_HTTP_ADDR + + def test_CONSUL_HTTP_ADDR_scheme_http(self): + CONSUL_HTTP_ADDR = '127.0.0.23:4242' + with self.environ(CONSUL_HTTP_ADDR=CONSUL_HTTP_ADDR): + c = Consul.from_env() + assert c.http.base_uri == 'http://' + CONSUL_HTTP_ADDR + + def test_CONSUL_HTTP_ADDR_with_CONSUL_HTTP_SSL(self): + CONSUL_HTTP_ADDR = '127.0.0.23:4242' + with self.environ(CONSUL_HTTP_ADDR=CONSUL_HTTP_ADDR, + CONSUL_HTTP_SSL='true'): + c = Consul.from_env() + assert c.http.base_uri == 'https://' + CONSUL_HTTP_ADDR + + def test_CONSUL_HTTP_TOKEN(self): + CONSUL_HTTP_TOKEN = '1bdc2cb4-9b02-4b3c-9df5-eb86214e1a6c' + with self.environ(CONSUL_HTTP_TOKEN=CONSUL_HTTP_TOKEN): + c = Consul.from_env() + assert c.token == CONSUL_HTTP_TOKEN + + def test_cert(self): + CONSUL_CLIENT_CERT = '/path/to/client.crt' + CONSUL_CLIENT_KEY = '/path/to/client.key' + with self.environ(CONSUL_CLIENT_CERT=CONSUL_CLIENT_CERT, + CONSUL_CLIENT_KEY=CONSUL_CLIENT_KEY): + c = Consul.from_env() + assert c.http.cert == (CONSUL_CLIENT_CERT, CONSUL_CLIENT_KEY) + + def test_CONSUL_HTTP_AUTH(self): + CONSUL_HTTP_AUTH = 'username:s3cr3t' + with self.environ(CONSUL_HTTP_AUTH=CONSUL_HTTP_AUTH): + c = Consul.from_env() + assert c.http.auth == CONSUL_HTTP_AUTH.split(':') + + def test_CONSUL_HTTP_SSL_VERIFY_True(self): + CONSUL_HTTP_SSL_VERIFY = 'true' + with self.environ(CONSUL_HTTP_SSL_VERIFY=CONSUL_HTTP_SSL_VERIFY): + c = Consul.from_env() + assert c.http.verify is True + + def test_CONSUL_HTTP_SSL_VERIFY_False(self): + CONSUL_HTTP_SSL_VERIFY = 'false' + with self.environ(CONSUL_HTTP_SSL_VERIFY=CONSUL_HTTP_SSL_VERIFY): + c = Consul.from_env() + assert c.http.verify is False + + def test_CONSUL_CACERT(self): + CONSUL_CACERT = '/path/to/ca.crt' + with self.environ(CONSUL_CACERT=CONSUL_CACERT): + c = Consul.from_env() + assert c.http.verify == CONSUL_CACERT def _should_support(c): diff --git a/tests/test_std.py b/tests/test_std.py index 6aef1e72..c7383fd7 100644 --- a/tests/test_std.py +++ b/tests/test_std.py @@ -13,33 +13,34 @@ Check = consul.Check +@pytest.fixture +def c(consul_port): + return consul.Consul(port=consul_port) + + class TestHTTPClient(object): def test_uri(self): - http = consul.std.HTTPClient() + http = consul.std.HTTPClient('http://127.0.0.1:8500') assert http.uri('/v1/kv') == 'http://127.0.0.1:8500/v1/kv' assert http.uri('/v1/kv', params={'index': 1}) == \ 'http://127.0.0.1:8500/v1/kv?index=1' class TestConsul(object): - def test_kv(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv(self, c): index, data = c.kv.get('foo') assert data is None assert c.kv.put('foo', 'bar') is True index, data = c.kv.get('foo') assert data['Value'] == six.b('bar') - def test_kv_wait(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv_wait(self, c): assert c.kv.put('foo', 'bar') is True index, data = c.kv.get('foo') check, data = c.kv.get('foo', index=index, wait='20ms') assert index == check - def test_kv_encoding(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_kv_encoding(self, c): # test binary c.kv.put('foo', struct.pack('i', 1000)) index, data = c.kv.get('foo') @@ -63,8 +64,7 @@ def test_kv_encoding(self, consul_port): # check unencoded values raises assert pytest.raises(AssertionError, c.kv.put, 'foo', {1: 2}) - def test_kv_put_cas(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv_put_cas(self, c): assert c.kv.put('foo', 'bar', cas=50) is False assert c.kv.put('foo', 'bar', cas=0) is True index, data = c.kv.get('foo') @@ -74,8 +74,7 @@ def test_kv_put_cas(self, consul_port): index, data = c.kv.get('foo') assert data['Value'] == six.b('bar2') - def test_kv_put_flags(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv_put_flags(self, c): c.kv.put('foo', 'bar') index, data = c.kv.get('foo') assert data['Flags'] == 0 @@ -84,8 +83,7 @@ def test_kv_put_flags(self, consul_port): index, data = c.kv.get('foo') assert data['Flags'] == 50 - def test_kv_recurse(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv_recurse(self, c): index, data = c.kv.get('foo/', recurse=True) assert data is None @@ -102,8 +100,7 @@ def test_kv_recurse(self, consul_port): assert [x['Value'] for x in data] == [ None, six.b('1'), six.b('2'), six.b('3')] - def test_kv_delete(self, consul_port): - c = consul.Consul(port=consul_port) + def test_kv_delete(self, c): c.kv.put('foo1', '1') c.kv.put('foo2', '2') c.kv.put('foo3', '3') @@ -117,9 +114,7 @@ def test_kv_delete(self, consul_port): index, data = c.kv.get('foo', recurse=True) assert data is None - def test_kv_delete_cas(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_kv_delete_cas(self, c): c.kv.put('foo', 'bar') index, data = c.kv.get('foo') @@ -130,9 +125,7 @@ def test_kv_delete_cas(self, consul_port): index, data = c.kv.get('foo') assert data is None - def test_kv_acquire_release(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_kv_acquire_release(self, c): pytest.raises( consul.ConsulException, c.kv.put, 'foo', 'bar', acquire='foo') @@ -149,9 +142,7 @@ def test_kv_acquire_release(self, consul_port): c.session.destroy(s1) c.session.destroy(s2) - def test_kv_keys_only(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_kv_keys_only(self, c): assert c.kv.put('bar', '4') is True assert c.kv.put('base/foo', '1') is True assert c.kv.put('base/base/foo', '5') is True @@ -159,8 +150,7 @@ def test_kv_keys_only(self, consul_port): index, data = c.kv.get('base/', keys=True, separator='/') assert data == ['base/base/', 'base/foo'] - def test_transaction(self, consul_port): - c = consul.Consul(port=consul_port) + def test_transaction(self, c): value = base64.b64encode(b"1").decode("utf8") d = {"KV": {"Verb": "set", "Key": "asdf", "Value": value}} r = c.txn.put([d]) @@ -170,17 +160,13 @@ def test_transaction(self, consul_port): r = c.txn.put([d]) assert r["Results"][0]["KV"]["Value"] == value - def test_event(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_event(self, c): assert c.event.fire("fooname", "foobody") index, events = c.event.list() assert [x['Name'] == 'fooname' for x in events] assert [x['Payload'] == 'foobody' for x in events] - def test_event_targeted(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_event_targeted(self, c): assert c.event.fire("fooname", "foobody") index, events = c.event.list(name="othername") assert events == [] @@ -189,8 +175,7 @@ def test_event_targeted(self, consul_port): assert [x['Name'] == 'fooname' for x in events] assert [x['Payload'] == 'foobody' for x in events] - def test_agent_checks(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_checks(self, c): def verify_and_dereg_check(check_id): assert set(c.agent.checks().keys()) == set([check_id]) @@ -219,7 +204,7 @@ def verify_check_status(check_id, status, notes=None): check_id='check_id') is True verify_and_dereg_check('check_id') - http_addr = "http://127.0.0.1:{0}".format(consul_port) + http_addr = c.http.base_uri assert c.agent.check.register( 'http_check', Check.http(http_addr, '10ms')) is True time.sleep(1) @@ -256,10 +241,9 @@ def verify_check_status(check_id, status, notes=None): verify_check_status('ttl_check', 'critical') verify_and_dereg_check('ttl_check') - def test_service_dereg_issue_156(self, consul_port): + def test_service_dereg_issue_156(self, c): # https://github.com/cablehead/python-consul/issues/156 service_name = 'app#127.0.0.1#3000' - c = consul.Consul(port=consul_port) c.agent.service.register(service_name) time.sleep(80/1000.0) @@ -275,8 +259,7 @@ def test_service_dereg_issue_156(self, consul_port): index, nodes = c.health.service(service_name) assert [node['Service']['ID'] for node in nodes] == [] - def test_agent_checks_service_id(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_checks_service_id(self, c): c.agent.service.register('foo1') time.sleep(40/1000.0) @@ -305,8 +288,7 @@ def test_agent_checks_service_id(self, consul_port): time.sleep(40/1000.0) - def test_agent_register_check_no_service_id(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_register_check_no_service_id(self, c): index, nodes = c.health.service("foo1") assert nodes == [] @@ -324,8 +306,7 @@ def test_agent_register_check_no_service_id(self, consul_port): time.sleep(40/1000.0) - def test_agent_register_enable_tag_override(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_register_enable_tag_override(self, c): index, nodes = c.health.service("foo1") assert nodes == [] @@ -335,9 +316,7 @@ def test_agent_register_enable_tag_override(self, consul_port): # Cleanup tasks c.agent.check.deregister('foo') - def test_agent_service_maintenance(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_agent_service_maintenance(self, c): c.agent.service.register('foo', check=Check.ttl('100ms')) time.sleep(40/1000.0) @@ -362,9 +341,7 @@ def test_agent_service_maintenance(self, consul_port): time.sleep(40/1000.0) - def test_agent_node_maintenance(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_agent_node_maintenance(self, c): c.agent.maintenance('true', "test") time.sleep(40/1000.0) @@ -380,8 +357,7 @@ def test_agent_node_maintenance(self, consul_port): checks_post = c.agent.checks() assert '_node_maintenance' not in checks_post.keys() - def test_agent_members(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_members(self, c): members = c.agent.members() for x in members: assert x['Status'] == 1 @@ -393,14 +369,12 @@ def test_agent_members(self, consul_port): for x in wan_members: assert 'dc1' in x['Name'] - def test_agent_self(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_self(self, c): assert set(c.agent.self().keys()) == set(['Member', 'Stats', 'Config', 'Coord', 'DebugConfig', 'Meta']) - def test_agent_services(self, consul_port): - c = consul.Consul(port=consul_port) + def test_agent_services(self, c): assert c.agent.service.register('foo') is True assert set(c.agent.services().keys()) == set(['foo']) assert c.agent.service.deregister('foo') is True @@ -413,9 +387,7 @@ def test_agent_services(self, consul_port): if k == 'foo'][0] == '10.10.10.1' assert c.agent.service.deregister('foo') is True - def test_catalog(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_catalog(self, c): # grab the node our server created, so we can ignore it _, nodes = c.catalog.nodes() assert len(nodes) == 1 @@ -486,9 +458,7 @@ def test_catalog(self, consul_port): nodes.remove(current) assert [x['Node'] for x in nodes] == [] - def test_health_service(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_health_service(self, c): # check there are no nodes for the service 'foo' index, nodes = c.health.service('foo') assert nodes == [] @@ -551,9 +521,7 @@ def test_health_service(self, consul_port): index, nodes = c.health.service('foo') assert nodes == [] - def test_health_state(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_health_state(self, c): # The empty string is for the Serf Health Status check, which has an # empty ServiceID index, nodes = c.health.state('any') @@ -614,16 +582,13 @@ def test_health_state(self, consul_port): index, nodes = c.health.state('any') assert [node['ServiceID'] for node in nodes] == [''] - def test_health_node(self, consul_port): - c = consul.Consul(port=consul_port) + def test_health_node(self, c): # grab local node name node = c.agent.self()['Config']['NodeName'] index, checks = c.health.node(node) assert node in [check["Node"] for check in checks] - def test_health_checks(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_health_checks(self, c): c.agent.service.register( 'foobar', service_id='foobar', check=Check.ttl('10s')) @@ -641,9 +606,7 @@ def test_health_checks(self, consul_port): index, checks = c.health.checks('foobar') assert len(checks) == 0 - def test_session(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_session(self, c): # session.create pytest.raises(consul.ConsulException, c.session.create, node='n2') pytest.raises(consul.ConsulException, c.session.create, dc='dc2') @@ -676,9 +639,7 @@ def test_session(self, consul_port): _, sessions = c.session.list() assert sessions == [] - def test_session_delete_ttl_renew(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_session_delete_ttl_renew(self, c): s = c.session.create(behavior='delete', ttl=20) # attempt to renew an unknown session @@ -697,8 +658,7 @@ def test_session_delete_ttl_renew(self, consul_port): index, data = c.kv.get('foo') assert data is None - def test_acl_disabled(self, consul_port): - c = consul.Consul(port=consul_port) + def test_acl_disabled(self, c): pytest.raises(consul.ACLDisabled, c.acl.list) pytest.raises(consul.ACLDisabled, c.acl.info, '1'*36) pytest.raises(consul.ACLDisabled, c.acl.create) @@ -866,9 +826,7 @@ def test_acl_implicit_token_use(self, acl_consul): assert set([x['ID'] for x in acls]) == \ set(['anonymous', master_token]) - def test_status_leader(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_status_leader(self, c): agent_self = c.agent.self() leader = c.status.leader() addr_port = agent_self['Stats']['consul']['leader_addr'] @@ -877,10 +835,7 @@ def test_status_leader(self, consul_port): "Leader value was {0}, expected value " \ "was {1}".format(leader, addr_port) - def test_status_peers(self, consul_port): - - c = consul.Consul(port=consul_port) - + def test_status_peers(self, c): agent_self = c.agent.self() addr_port = agent_self['Stats']['consul']['leader_addr'] @@ -890,9 +845,7 @@ def test_status_peers(self, consul_port): "Expected value '{0}' " \ "in peer list but it was not present".format(addr_port) - def test_query(self, consul_port): - c = consul.Consul(port=consul_port) - + def test_query(self, c): # check that query list is empty queries = c.query.list() assert queries == [] @@ -920,15 +873,13 @@ def test_query(self, consul_port): # delete query assert c.query.delete(query['ID']) - def test_coordinate(self, consul_port): - c = consul.Consul(port=consul_port) + def test_coordinate(self, c): c.coordinate.nodes() c.coordinate.datacenters() assert set(c.coordinate.datacenters()[0].keys()) == \ set(['Datacenter', 'Coordinates', 'AreaID']) - def test_operator(self, consul_port): - c = consul.Consul(port=consul_port) + def test_operator(self, c): config = c.operator.raft_config() assert config["Index"] == 1 leader = False diff --git a/tests/test_tornado.py b/tests/test_tornado.py index f69e5ead..75845ec1 100644 --- a/tests/test_tornado.py +++ b/tests/test_tornado.py @@ -22,6 +22,11 @@ def loop(): return loop +@pytest.fixture +def c(consul_port): + return consul.tornado.Consul(port=consul_port) + + def sleep(loop, s): result = gen.Future() loop.add_timeout( @@ -30,10 +35,9 @@ def sleep(loop, s): class TestConsul(object): - def test_kv(self, loop, consul_port): + def test_kv(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) index, data = yield c.kv.get('foo') assert data is None response = yield c.kv.put('foo', 'bar') @@ -43,18 +47,16 @@ def main(): loop.stop() loop.run_sync(main) - def test_kv_binary(self, loop, consul_port): + def test_kv_binary(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) yield c.kv.put('foo', struct.pack('i', 1000)) index, data = yield c.kv.get('foo') assert struct.unpack('i', data['Value']) == (1000,) loop.stop() loop.run_sync(main) - def test_kv_missing(self, loop, consul_port): - c = consul.tornado.Consul(port=consul_port) + def test_kv_missing(self, loop, c): @gen.coroutine def main(): @@ -72,10 +74,9 @@ def put(): loop.add_timeout(time.time()+(2.0/100), put) loop.run_sync(main) - def test_kv_put_flags(self, loop, consul_port): + def test_kv_put_flags(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) yield c.kv.put('foo', 'bar') index, data = yield c.kv.get('foo') assert data['Flags'] == 0 @@ -87,10 +88,9 @@ def main(): loop.stop() loop.run_sync(main) - def test_kv_delete(self, loop, consul_port): + def test_kv_delete(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) yield c.kv.put('foo1', '1') yield c.kv.put('foo2', '2') yield c.kv.put('foo3', '3') @@ -108,8 +108,7 @@ def main(): loop.stop() loop.run_sync(main) - def test_kv_subscribe(self, loop, consul_port): - c = consul.tornado.Consul(port=consul_port) + def test_kv_subscribe(self, loop, c): @gen.coroutine def get(): @@ -127,10 +126,9 @@ def put(): loop.add_timeout(time.time()+(1.0/100), put) loop.run_sync(get) - def test_kv_encoding(self, loop, consul_port): + def test_kv_encoding(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) # test binary response = yield c.kv.put('foo', struct.pack('i', 1000)) @@ -166,10 +164,9 @@ def main(): loop.stop() loop.run_sync(main) - def test_transaction(self, loop, consul_port): + def test_transaction(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) value = base64.b64encode(b"1").decode("utf8") d = {"KV": {"Verb": "set", "Key": "asdf", "Value": value}} r = yield c.txn.put([d]) @@ -181,10 +178,9 @@ def main(): loop.stop() loop.run_sync(main) - def test_agent_services(self, loop, consul_port): + def test_agent_services(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) services = yield c.agent.services() assert services == {} response = yield c.agent.service.register('foo') @@ -208,8 +204,7 @@ def main(): loop.stop() loop.run_sync(main) - def test_catalog(self, loop, consul_port): - c = consul.tornado.Consul(port=consul_port) + def test_catalog(self, loop, c): @gen.coroutine def nodes(): @@ -237,10 +232,9 @@ def register(): loop.add_timeout(time.time()+(1.0/100), register) loop.run_sync(nodes) - def test_health_service(self, loop, consul_port): + def test_health_service(self, loop, c): @gen.coroutine def main(): - c = consul.tornado.Consul(port=consul_port) # check there are no nodes for the service 'foo' index, nodes = yield c.health.service('foo') @@ -302,8 +296,7 @@ def main(): loop.run_sync(main) - def test_health_service_subscribe(self, loop, consul_port): - c = consul.tornado.Consul(port=consul_port) + def test_health_service_subscribe(self, loop, c): class Config(object): pass @@ -341,8 +334,7 @@ def keepalive(): loop.add_callback(monitor) loop.run_sync(keepalive) - def test_session(self, loop, consul_port): - c = consul.tornado.Consul(port=consul_port) + def test_session(self, loop, c): @gen.coroutine def monitor(): diff --git a/tests/test_twisted.py b/tests/test_twisted.py index 097674c5..36db890a 100644 --- a/tests/test_twisted.py +++ b/tests/test_twisted.py @@ -11,6 +11,11 @@ Check = consul.Check +@pytest.fixture +def c(consul_port): + return consul.twisted.Consul(port=consul_port) + + def sleep(seconds): """ An asynchronous sleep function using twsited. Source: @@ -25,8 +30,7 @@ def sleep(seconds): class TestConsul(object): @pytest.inlineCallbacks - def test_kv(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv(self, c): index, data = yield c.kv.get('foo') assert data is None response = yield c.kv.put('foo', 'bar') @@ -35,15 +39,13 @@ def test_kv(self, consul_port): assert data['Value'] == six.b('bar') @pytest.inlineCallbacks - def test_kv_binary(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv_binary(self, c): yield c.kv.put('foo', struct.pack('i', 1000)) index, data = yield c.kv.get('foo') assert struct.unpack('i', data['Value']) == (1000,) @pytest.inlineCallbacks - def test_kv_missing(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv_missing(self, c): reactor.callLater(2.0 / 100, c.kv.put, 'foo', 'bar') yield c.kv.put('index', 'bump') index, data = yield c.kv.get('foo') @@ -52,8 +54,7 @@ def test_kv_missing(self, consul_port): assert data['Value'] == six.b('bar') @pytest.inlineCallbacks - def test_kv_put_flags(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv_put_flags(self, c): yield c.kv.put('foo', 'bar') index, data = yield c.kv.get('foo') assert data['Flags'] == 0 @@ -64,8 +65,7 @@ def test_kv_put_flags(self, consul_port): assert data['Flags'] == 50 @pytest.inlineCallbacks - def test_kv_delete(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv_delete(self, c): yield c.kv.put('foo1', '1') yield c.kv.put('foo2', '2') yield c.kv.put('foo3', '3') @@ -82,8 +82,7 @@ def test_kv_delete(self, consul_port): assert data is None @pytest.inlineCallbacks - def test_kv_subscribe(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_kv_subscribe(self, c): @defer.inlineCallbacks def put(): @@ -97,8 +96,7 @@ def put(): assert data['Value'] == six.b('bar') @pytest.inlineCallbacks - def test_transaction(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_transaction(self, c): value = base64.b64encode(b"1").decode("utf8") d = {"KV": {"Verb": "set", "Key": "asdf", "Value": value}} r = yield c.txn.put([d]) @@ -109,8 +107,7 @@ def test_transaction(self, consul_port): assert r["Results"][0]["KV"]["Value"] == value @pytest.inlineCallbacks - def test_agent_services(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_agent_services(self, c): services = yield c.agent.services() assert services == {} response = yield c.agent.service.register('foo') @@ -134,8 +131,7 @@ def test_agent_services(self, consul_port): assert services == {} @pytest.inlineCallbacks - def test_catalog(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_catalog(self, c): @defer.inlineCallbacks def register(): @@ -160,8 +156,7 @@ def register(): assert [x['Node'] for x in nodes] == [] @pytest.inlineCallbacks - def test_health_service(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_health_service(self, c): # check there are no nodes for the service 'foo' index, nodes = yield c.health.service('foo') @@ -221,8 +216,7 @@ def test_health_service(self, consul_port): assert nodes == [] @pytest.inlineCallbacks - def test_health_service_subscribe(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_health_service_subscribe(self, c): class Config(object): def __init__(self): @@ -254,8 +248,7 @@ def update(self): yield c.agent.service.deregister('foo:1') @pytest.inlineCallbacks - def test_session(self, consul_port): - c = consul.twisted.Consul(port=consul_port) + def test_session(self, c): index, services = yield c.session.list() assert services == []