diff --git a/.bumpversion.cfg b/.bumpversion.cfg index 23b70e6b..2c9f5838 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,7 +1,7 @@ [bumpversion] commit = True tag = True -current_version = 0.7.3-dev +current_version = 1.0.1 parse = (?P\d+)\.(?P\d+)\.(?P\d+)(\-(?P[a-z]+))? serialize = {major}.{minor}.{patch}-{release} diff --git a/.gitignore b/.gitignore index 43534652..4e0af0fb 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ pip-log.txt # IntelliJ project files *.iml .idea/ +consul/consul_version.py diff --git a/.travis.yml b/.travis.yml index aaf5340b..2ab11056 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - 2.6 - 2.7 - pypy-5.3.1 - - 3.4 - 3.5 - 3.6 diff --git a/CHANGELOG.rst b/CHANGELOG.rst index e8606171..a14807fb 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,10 +1,12 @@ Change log ========== -0.7.3-dev ---------- +1.0.1 +----- -* TBD +* Support for Python 3.4 dropped (sorry) +* Add support for Consul 1.0.0 (thanks @matusvalo!) +* Expose all 400 errors and add tests for common callback handler (thanks @bagerard) 0.7.2 ----- diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..483408fc --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +VERSION = $(shell git describe --tags --match '[0-9]*.[0-9]' --abbrev=0) +REV = $(shell git rev-list $(VERSION)..HEAD | wc -l) + +update_version: + echo "Version $(VERSION) and Rev $(REV)"; echo '__version__ = '\''$(VERSION).$(REV)'\''' > consul/consul_version.py + +build: update_version + python setup.py sdist --formats=zip + +init: + pip install -r requirements.txt && pip install -r requirements-devel.txt + +test: + nosetests tests diff --git a/README.rst b/README.rst index 64474126..245267f6 100644 --- a/README.rst +++ b/README.rst @@ -9,7 +9,7 @@ Documentation Status ------ -|Build Status|\ |Coverage Status| +|Build Status| Example ------- @@ -51,6 +51,11 @@ There's a few API endpoints still to go to expose all features available in Consul v0.6.0. If you need an endpoint that's not in the documentation, just open an issue and I'll try and add it straight away. +Mailing List +------------ + +- https://groups.google.com/forum/#!forum/python-consul + Contributing ------------ diff --git a/consul/__init__.py b/consul/__init__.py index 006fc16f..8c2ae72d 100644 --- a/consul/__init__.py +++ b/consul/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.7.3-dev' +from consul.consul_version import __version__ from consul.std import Consul diff --git a/consul/base.py b/consul/base.py index 09203b6d..1f2156a3 100755 --- a/consul/base.py +++ b/consul/base.py @@ -4,6 +4,10 @@ import base64 import json import os +try: + from urlparse import urlparse +except ImportError: + from urllib.parse import urlparse import six from six.moves import urllib @@ -36,6 +40,11 @@ class BadRequest(ConsulException): pass +class ClientError(ConsulException): + """Encapsulates 4xx Http error code""" + pass + + # # Convenience to define checks @@ -158,24 +167,28 @@ def _compat( class CB(object): @classmethod - def __status(klass, response, allow_404=True): + def _status(klass, response, allow_404=True): # status checking - if response.code >= 500 and response.code < 600: + if 400 <= response.code < 500: + if response.code == 400: + raise BadRequest('%d %s' % (response.code, response.body)) + elif response.code == 401: + raise ACLDisabled(response.body) + elif response.code == 403: + raise ACLPermissionDenied(response.body) + elif response.code == 404: + if not allow_404: + raise NotFound(response.body) + else: + raise ClientError("%d %s" % (response.code, response.body)) + elif 500 <= response.code < 600: raise ConsulException("%d %s" % (response.code, response.body)) - if response.code == 400: - raise BadRequest('%d %s' % (response.code, response.body)) - if response.code == 401: - raise ACLDisabled(response.body) - if response.code == 403: - raise ACLPermissionDenied(response.body) - if response.code == 404 and not allow_404: - raise NotFound(response.body) @classmethod def bool(klass): # returns True on successful response def cb(response): - CB.__status(response) + CB._status(response) return response.code == 200 return cb @@ -204,40 +217,39 @@ def json( *is_id* only the 'ID' field of the json object will be returned. """ def cb(response): - CB.__status(response, allow_404=allow_404) - if response.code == 404: - return response.headers['X-Consul-Index'], None - - data = json.loads(response.body) - - if decode: - for item in data: - if item.get(decode) is not None: - item[decode] = base64.b64decode(item[decode]) - if is_id: - data = data['ID'] - if one: - if data == []: - data = None - if data is not None: - data = data[0] - if map: - data = map(data) + CB._status(response, allow_404=allow_404) + data = None + if response.code in [200]: + data = json.loads(response.body) + + if decode: + for item in data: + if item.get(decode) is not None: + item[decode] = base64.b64decode(item[decode]) + if is_id: + data = data['ID'] + if one: + if data == []: + data = None + if data is not None: + data = data[0] + if map: + data = map(data) if index: - return response.headers['X-Consul-Index'], data + return response.headers['X-Nomad-Index'], data return data return cb - class HTTPClient(six.with_metaclass(abc.ABCMeta, object)): def __init__(self, host='127.0.0.1', port=8500, scheme='http', - verify=True, cert=None): + verify=True, cert=None, token=None): self.host = host self.port = port self.scheme = scheme self.verify = verify self.base_uri = '%s://%s:%s' % (self.scheme, self.host, self.port) self.cert = cert + self.token = token def uri(self, path, params=None): uri = self.base_uri + urllib.parse.quote(path, safe='/:') @@ -265,14 +277,17 @@ def post(self, callback, path, params=None, data=''): class Consul(object): def __init__( self, - host='127.0.0.1', - port=8500, + host=None, + port=None, token=None, - scheme='http', + scheme=None, consistency='default', dc=None, verify=True, - cert=None): + cert=None, + ssl_key=None, + ssl_ca=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 @@ -289,26 +304,45 @@ def __init__( *verify* is whether to verify the SSL certificate for HTTPS requests - *cert* client side certificates for HTTPS requests + *cert* is the client side certificates for HTTPS requests + *ssl_cert* is the client side certificates for HTTPS requests + *ssl_key* is the unencrypted PEM encoded private key matching the client certificate + *ssl_ca* is the path to a PEM encoded CA cert file to use to verify the Nomad server SSL certificate """ # 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) + for consul_host in [host, os.getenv('CONSUL_HTTP_ADDR'), '127.0.0.1']: + if consul_host is not None: + url_details = urlparse(consul_host) + address = "%s%s" % (url_details.netloc, url_details.path) + addr_list = address.split(':') + host = addr_list[0] + if port is None and len(addr_list) > 1: + port = int(addr_list[1]) + if scheme is None and url_details.scheme != "": + scheme = url_details.scheme + break + + port = port if port is not None else 8500 + + if scheme is None: + scheme = 'https' if os.getenv('CONSUL_HTTP_SSL', '').lower() in ['true', 'on'] else 'http' + + verify = verify if verify is not None else os.getenv('CONSUL_HTTP_SSL_VERIFY', '').lower() in ['true', 'on'] + + self.token = token if token is not None else os.getenv('CONSUL_HTTP_TOKEN') + + # PEM encoded client certificate for TLS authentication to the Consul server (Must also specify key) + ssl_cert = cert if cert is not None else os.getenv('CONSUL_CLIENT_CERT') + + # unencrypted PEM encoded private key matching the client certificate + ssl_key = ssl_key if ssl_key is not None else os.getenv('CONSUL_CLIENT_KEY') + + # Path to a PEM encoded CA cert file to use to verify the Consul server SSL certificate + ssl_ca = ssl_ca if ssl_ca is not None else os.getenv('CONSUL_CACERT') + + self.http = self.connect(host, port, scheme, verify, cert, token=self.token) self.scheme = scheme self.dc = dc assert consistency in ('default', 'consistent', 'stale'), \ @@ -328,6 +362,9 @@ def __init__( self.coordinate = Consul.Coordinate(self) self.operator = Consul.Operator(self) + def connect(self, host, port, scheme, verify, cert, token): + pass + class Event(object): """ The event command provides a mechanism to fire a custom user event to @@ -886,7 +923,7 @@ def deregister(self, service_id): take care of deregistering the service with the Catalog. If there is an associated check, that is also deregistered. """ - return self.agent.http.get( + return self.agent.http.put( CB.bool(), '/v1/agent/service/deregister/%s' % service_id) def maintenance(self, service_id, enable, reason=None): @@ -999,7 +1036,7 @@ def deregister(self, check_id): """ Remove a check from the local agent. """ - return self.agent.http.get( + return self.agent.http.put( CB.bool(), '/v1/agent/check/deregister/%s' % check_id) @@ -1012,7 +1049,7 @@ def ttl_pass(self, check_id, notes=None): if notes: params['note'] = notes - return self.agent.http.get( + return self.agent.http.put( CB.bool(), '/v1/agent/check/pass/%s' % check_id, params=params) @@ -1027,7 +1064,7 @@ def ttl_fail(self, check_id, notes=None): if notes: params['note'] = notes - return self.agent.http.get( + return self.agent.http.put( CB.bool(), '/v1/agent/check/fail/%s' % check_id, params=params) @@ -1042,7 +1079,7 @@ def ttl_warn(self, check_id, notes=None): if notes: params['note'] = notes - return self.agent.http.get( + return self.agent.http.put( CB.bool(), '/v1/agent/check/warn/%s' % check_id, params=params) diff --git a/consul/std.py b/consul/std.py index 96a5b9dc..1f5bbf80 100644 --- a/consul/std.py +++ b/consul/std.py @@ -19,26 +19,42 @@ def response(self, response): def get(self, callback, path, params=None): uri = self.uri(path, params) return callback(self.response( - self.session.get(uri, verify=self.verify, cert=self.cert))) + self.session.get(uri, + headers={"X-Consul-Token": self.token}, + verify=self.verify, + cert=self.cert + ))) def put(self, callback, path, params=None, data=''): uri = self.uri(path, params) return callback(self.response( - self.session.put(uri, data=data, verify=self.verify, - cert=self.cert))) + self.session.put(uri, + data=data, + headers={"X-Consul-Token": self.token}, + verify=self.verify, + cert=self.cert + ))) def delete(self, callback, path, params=None): uri = self.uri(path, params) return callback(self.response( - self.session.delete(uri, verify=self.verify, cert=self.cert))) + self.session.delete(uri, + headers={"X-Consul-Token": self.token}, + verify=self.verify, + cert=self.cert + ))) def post(self, callback, path, params=None, data=''): uri = self.uri(path, params) return callback(self.response( - self.session.post(uri, data=data, verify=self.verify, - cert=self.cert))) + self.session.post(uri, + data=data, + headers={"X-Consul-Token": self.token}, + verify=self.verify, + cert=self.cert + ))) class Consul(base.Consul): - def connect(self, host, port, scheme, verify=True, cert=None): - return HTTPClient(host, port, scheme, verify, cert) + def connect(self, host, port, scheme, verify=True, cert=None, token=None): + return HTTPClient(host, port, scheme, verify, cert, token=token) diff --git a/consul/tornado.py b/consul/tornado.py index 50507748..9a098f3a 100644 --- a/consul/tornado.py +++ b/consul/tornado.py @@ -26,15 +26,25 @@ def _request(self, callback, request): if e.code == 599: raise base.Timeout response = e.response + except: + raise base.BadRequest("Unable to %s %s {headers: %s}", + request.method, + request.url, + [(key, value) for key, value in request.headers.get_all()], + ) raise gen.Return(callback(self.response(response))) def get(self, callback, path, params=None): uri = self.uri(path, params) - return self._request(callback, uri) + request = httpclient.HTTPRequest(uri, method='GET', + headers={"X-Consul-Token": self.token}, + validate_cert=self.verify) + return self._request(callback, request) def put(self, callback, path, params=None, data=''): uri = self.uri(path, params) request = httpclient.HTTPRequest(uri, method='PUT', + headers={"X-Consul-Token": self.token}, body='' if data is None else data, validate_cert=self.verify) return self._request(callback, request) @@ -42,16 +52,18 @@ def put(self, callback, path, params=None, data=''): def delete(self, callback, path, params=None): uri = self.uri(path, params) request = httpclient.HTTPRequest(uri, method='DELETE', + headers={"X-Consul-Token": self.token}, validate_cert=self.verify) return self._request(callback, request) def post(self, callback, path, params=None, data=''): uri = self.uri(path, params) request = httpclient.HTTPRequest(uri, method='POST', body=data, + headers={"X-Consul-Token": self.token}, validate_cert=self.verify) return self._request(callback, request) 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, host, port, scheme, verify=True, cert=None, token=None): + return HTTPClient(host, port, scheme, verify=verify, cert=cert, token=token) diff --git a/setup.py b/setup.py index e8ed5133..4d331759 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ metadata = dict( - re.findall("__([a-z]+)__ = '([^']+)'", open('consul/__init__.py').read())) + re.findall("__([a-z]+)__ = '([^']+)'", open('consul/consul_version.py').read())) requirements = [ @@ -72,6 +72,7 @@ def run_tests(self): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', ], ) diff --git a/sonar-project.properties b/sonar-project.properties index 04f093e8..7ae4d8fa 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,7 +1,7 @@ # Required metadata sonar.projectKey=com.github:cablehead:python-consul sonar.projectName=Python Consul -sonar.projectVersion=0.7.3-dev +sonar.projectVersion=1.0.1 # Comma-separated paths to directories with sources (required) sonar.sources=consul diff --git a/tests/conftest.py b/tests/conftest.py index 66f70ca5..8c279632 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -26,8 +26,6 @@ collect_ignore.append(p) p = os.path.join(os.path.dirname(__file__), 'test_tornado.py') collect_ignore.append(p) -else: - pytest_plugins = "pytest_twisted" def get_free_ports(num, host=None): @@ -55,10 +53,11 @@ def start_consul_instance(acl_master_token=None): instance is listening on """ ports = dict(zip( - ['http', 'rpc', 'serf_lan', 'serf_wan', 'server', 'dns'], - get_free_ports(5) + [-1])) + ['http', 'serf_lan', 'serf_wan', 'server', 'dns'], + get_free_ports(4) + [-1])) - config = {'ports': ports, 'performance': {'raft_multiplier': 1}} + config = {'ports': ports, 'performance': {'raft_multiplier': 1}, + 'enable_script_checks': True} if acl_master_token: config['acl_datacenter'] = 'dc1' config['acl_master_token'] = acl_master_token @@ -73,9 +72,9 @@ def start_consul_instance(acl_master_token=None): else: postfix = 'linux64' bin = os.path.join(os.path.dirname(__file__), 'consul.'+postfix) - command = '{bin} agent -server -bootstrap' \ + command = '{bin} agent -dev' \ ' -bind=127.0.0.1' \ - ' -config-dir=. -data-dir=./data' + ' -config-dir=.' command = command.format(bin=bin).strip() command = shlex.split(command) @@ -91,6 +90,7 @@ def start_consul_instance(acl_master_token=None): response = requests.get(base_uri + 'status/leader') except requests.ConnectionError: continue + print(response.text) if response.text.strip() != '""': break @@ -102,13 +102,13 @@ def start_consul_instance(acl_master_token=None): break time.sleep(0.1) - requests.get(base_uri + 'agent/service/deregister/foo') + requests.put(base_uri + 'agent/service/deregister/foo') # phew time.sleep(2) return p, ports['http'] -@pytest.yield_fixture(scope="session") +@pytest.yield_fixture(scope="module") def consul_instance(): p, port = start_consul_instance() yield port @@ -123,7 +123,7 @@ def consul_port(consul_instance): requests.delete(base_uri + 'kv/?recurse=1') -@pytest.yield_fixture(scope="session") +@pytest.yield_fixture(scope="module") def acl_consul_instance(): acl_master_token = uuid.uuid4().hex p, port = start_consul_instance(acl_master_token=acl_master_token) diff --git a/tests/consul.linux64 b/tests/consul.linux64 index c92f9421..2d4e23a0 100755 Binary files a/tests/consul.linux64 and b/tests/consul.linux64 differ diff --git a/tests/consul.osx b/tests/consul.osx index 0c79f438..c04427b6 100755 Binary files a/tests/consul.osx and b/tests/consul.osx differ diff --git a/tests/test_aio.py b/tests/test_aio.py index b6e0c943..cca60b73 100644 --- a/tests/test_aio.py +++ b/tests/test_aio.py @@ -169,12 +169,10 @@ def test_agent_services(self, loop, consul_port): def main(): c = consul.aio.Consul(port=consul_port, loop=loop) services = yield from c.agent.services() - del services['consul'] assert services == {} response = yield from c.agent.service.register('foo') assert response is True services = yield from c.agent.services() - del services['consul'] assert services == { 'foo': { 'Port': 0, @@ -183,12 +181,11 @@ def main(): 'ModifyIndex': 0, 'EnableTagOverride': False, 'Service': 'foo', - 'Tags': None, + 'Tags': [], 'Address': ''}, } response = yield from c.agent.service.deregister('foo') assert response is True services = yield from c.agent.services() - del services['consul'] assert services == {} c.close() diff --git a/tests/test_base.py b/tests/test_base.py index 7ea79fbd..44e0bc79 100755 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -5,6 +5,9 @@ import consul.base +CB = consul.base.CB +Response = consul.base.Response + Request = collections.namedtuple( 'Request', ['method', 'path', 'params', 'data']) @@ -77,6 +80,48 @@ def test_implicit(self): assert r(consistency='stale').params == {'stale': '1'} +class TestCB(object): + + def test_status_200_passes(self): + response = consul.base.Response(200, None, None) + CB._status(response) + + @pytest.mark.parametrize( + 'response, expected_exception', + [ + (Response(400, None, None), consul.base.BadRequest), + (Response(401, None, None), consul.base.ACLDisabled), + (Response(403, None, None), consul.base.ACLPermissionDenied), + ]) + def test_status_4xx_raises_error(self, response, expected_exception): + with pytest.raises(expected_exception): + CB._status(response) + + def test_status_404_allow_404(self): + response = Response(404, None, None) + CB._status(response, allow_404=True) + + def test_status_404_dont_allow_404(self): + response = Response(404, None, None) + with pytest.raises(consul.base.NotFound): + CB._status(response, allow_404=False) + + def test_status_405_raises_generic_ClientError(self): + response = Response(405, None, None) + with pytest.raises(consul.base.ClientError): + CB._status(response) + + @pytest.mark.parametrize( + 'response', + [ + Response(500, None, None), + Response(599, None, None), + ]) + def test_status_5xx_raises_error(self, response): + with pytest.raises(consul.base.ConsulException): + CB._status(response) + + class TestChecks(object): """ Check constructor helpers return valid check configurations. diff --git a/tests/test_std.py b/tests/test_std.py index ad6e45be..6aef1e72 100644 --- a/tests/test_std.py +++ b/tests/test_std.py @@ -289,12 +289,12 @@ def test_agent_checks_service_id(self, consul_port): time.sleep(40/1000.0) index, nodes = c.health.service('foo1') - assert [ + assert set([ check['ServiceID'] for node in nodes - for check in node['Checks']] == ['foo1', ''] - assert [ + for check in node['Checks']]) == set(['foo1', '']) + assert set([ check['CheckID'] for node in nodes - for check in node['Checks']] == ['foo', 'serfHealth'] + for check in node['Checks']]) == set(['foo', 'serfHealth']) # Clean up tasks assert c.agent.check.deregister('foo') is True @@ -395,16 +395,16 @@ def test_agent_members(self, consul_port): def test_agent_self(self, consul_port): c = consul.Consul(port=consul_port) - assert set(c.agent.self().keys()) == set(['Member', 'Coord', 'Config', - 'Stats']) + 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) - assert set(c.agent.services().keys()) == set(['consul']) assert c.agent.service.register('foo') is True - assert set(c.agent.services().keys()) == set(['consul', 'foo']) + assert set(c.agent.services().keys()) == set(['foo']) assert c.agent.service.deregister('foo') is True - assert set(c.agent.services().keys()) == set(['consul']) + assert set(c.agent.services().keys()) == set() # test address param assert c.agent.service.register('foo', address='10.10.10.1') is True @@ -764,7 +764,9 @@ def test_acl_explict_token_use(self, acl_consul): consul.ACLPermissionDenied, c.kv.delete, 'foo', token=token) assert c.kv.get('private/foo')[1]['Value'] == six.b('bar') - assert c.kv.get('private/foo', token=token)[1] is None + pytest.raises( + consul.ACLPermissionDenied, + c.kv.get, 'private/foo', token=token) pytest.raises( consul.ACLPermissionDenied, c.kv.put, 'private/foo', 'bar2', token=token) @@ -773,7 +775,9 @@ def test_acl_explict_token_use(self, acl_consul): c.kv.delete, 'private/foo', token=token) # test token pass through for service registration - c.agent.service.register("bar-1", token=token) + pytest.raises( + consul.ACLPermissionDenied, + c.agent.service.register, "bar-1", token=token) c.agent.service.register("foo-1", token=token) index, data = c.health.service('foo-1', token=token) assert data[0]['Service']['ID'] == "foo-1" @@ -784,7 +788,6 @@ def test_acl_explict_token_use(self, acl_consul): # clean up assert c.agent.service.deregister('foo-1') is True - assert c.agent.service.deregister('bar-1') is True c.acl.destroy(token, token=master_token) acls = c.acl.list(token=master_token) assert set([x['ID'] for x in acls]) == \ @@ -835,7 +838,9 @@ def test_acl_implicit_token_use(self, acl_consul): consul.ACLPermissionDenied, c_limited.kv.delete, 'foo') assert c.kv.get('private/foo')[1]['Value'] == six.b('bar') - assert c_limited.kv.get('private/foo')[1] is None + pytest.raises( + consul.ACLPermissionDenied, + c_limited.kv.get, 'private/foo') pytest.raises( consul.ACLPermissionDenied, c_limited.kv.put, 'private/foo', 'bar2') @@ -844,7 +849,10 @@ def test_acl_implicit_token_use(self, acl_consul): c_limited.kv.delete, 'private/foo') # check we can override the client's default token - assert c.kv.get('private/foo', token=token)[1] is None + pytest.raises( + consul.ACLPermissionDenied, + c.kv.get, 'private/foo', token=token + ) pytest.raises( consul.ACLPermissionDenied, c.kv.put, 'private/foo', 'bar2', token=token) @@ -862,11 +870,8 @@ def test_status_leader(self, consul_port): c = consul.Consul(port=consul_port) agent_self = c.agent.self() - port = agent_self['Config']['Ports']['Server'] - addr = agent_self['Config']['AdvertiseAddr'] - - addr_port = "{0}:{1}".format(addr, port) leader = c.status.leader() + addr_port = agent_self['Stats']['consul']['leader_addr'] assert leader == addr_port, \ "Leader value was {0}, expected value " \ @@ -877,10 +882,8 @@ def test_status_peers(self, consul_port): c = consul.Consul(port=consul_port) agent_self = c.agent.self() - port = agent_self['Config']['Ports']['Server'] - addr = agent_self['Config']['AdvertiseAddr'] - addr_port = "{0}:{1}".format(addr, port) + addr_port = agent_self['Stats']['consul']['leader_addr'] peers = c.status.peers() assert addr_port in peers, \ @@ -922,7 +925,7 @@ def test_coordinate(self, consul_port): c.coordinate.nodes() c.coordinate.datacenters() assert set(c.coordinate.datacenters()[0].keys()) == \ - set(['Datacenter', 'Coordinates']) + set(['Datacenter', 'Coordinates', 'AreaID']) def test_operator(self, consul_port): c = consul.Consul(port=consul_port) diff --git a/tests/test_tornado.py b/tests/test_tornado.py index dbe859be..fd7aba7b 100644 --- a/tests/test_tornado.py +++ b/tests/test_tornado.py @@ -186,12 +186,10 @@ def test_agent_services(self, loop, consul_port): def main(): c = consul.tornado.Consul(port=consul_port) services = yield c.agent.services() - del services['consul'] assert services == {} response = yield c.agent.service.register('foo') assert response is True services = yield c.agent.services() - del services['consul'] assert services == { 'foo': { 'Port': 0, @@ -200,12 +198,11 @@ def main(): 'ModifyIndex': 0, 'EnableTagOverride': False, 'Service': 'foo', - 'Tags': None, + 'Tags': [], 'Address': ''}, } response = yield c.agent.service.deregister('foo') assert response is True services = yield c.agent.services() - del services['consul'] assert services == {} loop.stop() loop.run_sync(main) diff --git a/tests/test_twisted.py b/tests/test_twisted.py index ce380005..e07c7837 100644 --- a/tests/test_twisted.py +++ b/tests/test_twisted.py @@ -112,12 +112,10 @@ def test_transaction(self, consul_port): def test_agent_services(self, consul_port): c = consul.twisted.Consul(port=consul_port) services = yield c.agent.services() - del services['consul'] assert services == {} response = yield c.agent.service.register('foo') assert response is True services = yield c.agent.services() - del services['consul'] assert services == { 'foo': { 'Port': 0, @@ -126,13 +124,12 @@ def test_agent_services(self, consul_port): 'ModifyIndex': 0, 'EnableTagOverride': False, 'Service': 'foo', - 'Tags': None, + 'Tags': [], 'Address': ''} } response = yield c.agent.service.deregister('foo') assert response is True services = yield c.agent.services() - del services['consul'] assert services == {} @pytest.inlineCallbacks diff --git a/tox.ini b/tox.ini index 1a97df92..4df9a5fc 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = flake8, py26, py27, pypy, py34, py35, py36 +envlist = flake8, py26, py27, pypy, py35, py36 [flake8] ignore = F811,E226 @@ -24,21 +24,6 @@ deps = commands = py.test --reruns=3 {posargs:consul tests} -[testenv:py34] -deps = - pytest - pytest-rerunfailures - pytest-twisted - twisted - treq - pyOpenSSL - tornado - aiohttp - flake8 -commands = - py.test --reruns=3 {posargs:consul tests} - flake8 --exclude=".tox/*,xx/*,__*,docs/*" - [testenv:py35] deps = pytest @@ -72,12 +57,3 @@ commands = [testenv:flake8] deps = flake8 commands = flake8 - -[testenv:coverage] -deps = - pytest-cov - tornado - coveralls -commands = - py.test --cov {envsitepackagesdir}/consul tests - coveralls