Skip to content
This repository has been archived by the owner on Jan 9, 2023. It is now read-only.

Commit

Permalink
Merge pull request #17 from maruno/amqp_ssl_url_user_pass
Browse files Browse the repository at this point in the history
Add support for amqp+ssl URL's and configured username and password
  • Loading branch information
matyaskuti authored Aug 2, 2018
2 parents ef7b012 + 2486a92 commit 8f2d5a7
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 24 deletions.
28 changes: 24 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,31 @@ Available tools
* ``qb session outgoing`` - List outgoing sessions from the server.


Environment variables
---------------------
Configuration & Environment variables
-------------------------------------
Several options exist to configure Qpid Bow. In order of preference:

``AMQP_SERVERS`` - comma-separated list of main and failover servers to connect to
**Pass in arguments**
One can always override the used server URL using arguments:

``AMQP_TEST_SERVERS`` - Same as ``AMQP_SERVERS``, used solely for unittests
* For the CLI tools, use the ``--broker-url`` command line argument.
* For the library pass in the keyword argument ``server_url``.

**Configure using a dict**
When using Qpid Bow as a library, one can pass in config using a dict to:
``qpid_bow.config.configure``

The dict can contain the following entries:

* ``amqp_url`` - Comma-separated list of main and failover servers to connect to.
* ``username`` - Username to use when no username is provided in the URL.
* ``password`` - Password to use when no password is provided in the URL.

**Environment variables**
The easiest way to configure Qpid Bow's tools and library is to use environment variables.
These variables can be added to your shell's profile and will automatically get picked up.

* ``AMQP_SERVERS`` - Comma-separated list of main and failover servers to connect to.
* ``AMQP_TEST_SERVERS`` - Same as ``AMQP_SERVERS``, used solely for unittests.

example: ``AMQP_SERVERS=amqp://user:[email protected]:5672,amqp://user:[email protected]:5672``
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.0.2
1.1.0
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
copyright = '2018, Bynder B.V.'
author = 'Bynder B.V.'

version = '1.0' # The short X.Y version.
release = '1.0.2' # The full version, including alpha/beta/rc tags.
version = '1.1' # The short X.Y version.
release = '1.1.0' # The full version, including alpha/beta/rc tags.

exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']

Expand Down
56 changes: 39 additions & 17 deletions qpid_bow/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
Optional,
)

from urllib.parse import urlsplit, urlunsplit

config: dict = {}


Expand All @@ -19,30 +21,50 @@ def configure(new_config: Mapping):
config.update(new_config)


def get_urls(urls: Optional[str] = None) -> List[str]:
"""Retrieves server urls from one of the sources.
def process_url(url: str) -> str:
"""Processes a URL for usage with Qpid Proton.
The sources priority comes in the following order: passed arguments,
global config, AMQP_SERVERS environment variable.
- ActiveMQ amqp+ssl scheme is replaced with amqps.
- Adds username and password from config.
Args:
urls: Comma-separated urls.
url: Input URL.
Returns:
List[str]: Returns list of urls to connect to.
str: Processed URL.
"""
if urls:
return [url.strip() for url in urls.split(',')]
split_url = urlsplit(url.strip())
if split_url.scheme == 'amqp+ssl':
split_url = split_url._replace(scheme='amqps')

if ((not split_url.username or not split_url.password) and
'username' in config and 'password' in config):
user_pass = f"{config['username']}:{config['password']}@"
new_netloc = user_pass + split_url.netloc
split_url = split_url._replace(netloc=new_netloc)

return urlunsplit(split_url)


if config.get('amqp_url'):
return [url.strip() for url in config['amqp_url'].split(',')]
def get_urls(argument_urls: Optional[str] = None) -> List[str]:
"""Retrieves server argument_urls from one of the sources.
amqp_servers = environ.get('AMQP_SERVERS')
The sources priority comes in the following order: passed arguments,
global config, AMQP_SERVERS environment variable.
Args:
argument_urls: Comma-separated argument_urls.
if amqp_servers:
environ_urls = []
for server in amqp_servers.split(','):
environ_urls.append(server.strip())
return environ_urls
Returns:
List[str]: Returns list of argument_urls to connect to.
"""
if argument_urls:
raw_urls = argument_urls
elif 'amqp_url' in config:
raw_urls = config['amqp_url']
elif 'AMQP_SERVERS' in environ:
raw_urls = environ['AMQP_SERVERS']
else:
raise ValueError('AMQP server url is not configured')

raise ValueError('AMQP server url is not configured')
return [process_url(url) for url in raw_urls.split(',')]
85 changes: 85 additions & 0 deletions test/test_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from contextlib import suppress
from os import environ
from unittest import TestCase

from qpid_bow.config import configure, config, get_urls, process_url

class TestGetURLs(TestCase):
def setUp(self):
self.old_config = config
self.old_env = environ.get('AMQP_SERVERS')

def tearDown(self):
config.clear()
config.update(self.old_config)
if self.old_env:
environ['AMQP_SERVERS'] = self.old_env
else:
with suppress(KeyError):
del environ['AMQP_SERVERS']

def test_no_config(self):
with suppress(KeyError):
del environ['AMQP_SERVERS']

with self.assertRaises(ValueError):
get_urls()

def test_get_urls_priority_from_environ(self):
environ['AMQP_SERVERS'] = 'amqps://environ.example'
self.assertEqual(get_urls(), ['amqps://environ.example'])

def test_get_urls_priority_from_config(self):
environ['AMQP_SERVERS'] = 'amqps://environ.example'
configure({'amqp_url': 'amqps://config.example'})

self.assertEqual(get_urls(), ['amqps://config.example'])

def test_get_urls_priority_from_args(self):
environ['AMQP_SERVERS'] = 'amqps://environ.example'
configure({'amqp_url': 'amqps://config.example'})

self.assertEqual(get_urls('amqps://args.example'),
['amqps://args.example'])

def test_get_urls_comma_seperated(self):
self.assertEqual(
get_urls('amqps://args1.example, amqps://args2.example'),
['amqps://args1.example', 'amqps://args2.example'])

def test_get_urls_activemq_format(self):
self.assertEqual(get_urls('amqp+ssl://args.example'),
['amqps://args.example'])

def test_get_urls_activemq_format_comma_seperated(self):
self.assertEqual(
get_urls('amqp+ssl://args1.example, amqp+ssl://args2.example'),
['amqps://args1.example', 'amqps://args2.example'])

def test_get_urls_user_passwd_config_mixed(self):
config['username'] = 'otheruser'
config['password'] = 'otherpass'
self.assertEqual(
get_urls('amqps://args1.example,amqps://user:[email protected]'),
['amqps://otheruser:[email protected]',
'amqps://user:[email protected]'])

def test_process_url_noop(self):
valid_url = 'amqp://some.example'
self.assertEqual(process_url(valid_url), valid_url)

def test_process_url_activemq(self):
self.assertEqual(process_url('amqp+ssl://some.example'),
'amqps://some.example')

def test_process_url_user_passwd_config(self):
config['username'] = 'user'
config['password'] = 'pass'
self.assertEqual(process_url('amqps://some.example'),
'amqps://user:[email protected]')

def test_process_url_user_passwd_no_override(self):
config['username'] = 'otheruser'
config['password'] = 'otherpass'
valid_url = 'amqps://user:[email protected]'
self.assertEqual(process_url(valid_url), valid_url)

0 comments on commit 8f2d5a7

Please sign in to comment.