diff --git a/bigchaindb/__init__.py b/bigchaindb/__init__.py index c0e4fd56..4c555e47 100644 --- a/bigchaindb/__init__.py +++ b/bigchaindb/__init__.py @@ -5,24 +5,46 @@ import os # PORT_NUMBER = reduce(lambda x, y: x * y, map(ord, 'BigchainDB')) % 2**16 # basically, the port number is 9984 -_database_rethinkdb = { - 'backend': os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'rethinkdb'), + +_base_database_rethinkdb = { 'host': os.environ.get('BIGCHAINDB_DATABASE_HOST', 'localhost'), 'port': int(os.environ.get('BIGCHAINDB_DATABASE_PORT', 28015)), 'name': os.environ.get('BIGCHAINDB_DATABASE_NAME', 'bigchain'), - 'connection_timeout': 5000, - 'max_tries': 3, } -_database_mongodb = { - 'backend': os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'mongodb'), +# The following variable is used by `bigchaindb configure` to +# prompt the user for database values. We cannot rely on +# _base_database_rethinkdb.keys() or _base_database_mongodb.keys() +# because dicts are unordered. I tried to configure + +_database_keys_map = { + 'mongodb': ('host', 'port', 'name', 'replicaset'), + 'rethinkdb': ('host', 'port', 'name') +} + +_base_database_mongodb = { 'host': os.environ.get('BIGCHAINDB_DATABASE_HOST', 'localhost'), 'port': int(os.environ.get('BIGCHAINDB_DATABASE_PORT', 27017)), 'name': os.environ.get('BIGCHAINDB_DATABASE_NAME', 'bigchain'), 'replicaset': os.environ.get('BIGCHAINDB_DATABASE_REPLICASET', 'bigchain-rs'), + 'ssl': bool(os.environ.get('BIGCHAINDB_DATABASE_SSL', False)), + 'login': os.environ.get('BIGCHAINDB_DATABASE_LOGIN'), + 'password': os.environ.get('BIGCHAINDB_DATABASE_PASSWORD') +} + +_database_rethinkdb = { + 'backend': os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'rethinkdb'), 'connection_timeout': 5000, 'max_tries': 3, } +_database_rethinkdb.update(_base_database_rethinkdb) + +_database_mongodb = { + 'backend': os.environ.get('BIGCHAINDB_DATABASE_BACKEND', 'mongodb'), + 'connection_timeout': 5000, + 'max_tries': 3, +} +_database_mongodb.update(_base_database_mongodb) _database_map = { 'mongodb': _database_mongodb, diff --git a/bigchaindb/commands/bigchaindb.py b/bigchaindb/commands/bigchaindb.py index ce0cbfa0..d4e37daa 100644 --- a/bigchaindb/commands/bigchaindb.py +++ b/bigchaindb/commands/bigchaindb.py @@ -88,26 +88,21 @@ def run_configure(args, skip_if_exists=False): # select the correct config defaults based on the backend print('Generating default configuration for backend {}' .format(args.backend), file=sys.stderr) + database_keys = bigchaindb._database_keys_map[args.backend] conf['database'] = bigchaindb._database_map[args.backend] if not args.yes: for key in ('bind', ): val = conf['server'][key] - conf['server'][key] = \ - input_on_stderr('API Server {}? (default `{}`): '.format(key, val)) \ - or val + conf['server'][key] = input_on_stderr('API Server {}? (default `{}`): '.format(key, val), val) - for key in ('host', 'port', 'name'): + for key in database_keys: val = conf['database'][key] - conf['database'][key] = \ - input_on_stderr('Database {}? (default `{}`): '.format(key, val)) \ - or val + conf['database'][key] = input_on_stderr('Database {}? (default `{}`): '.format(key, val), val) val = conf['backlog_reassign_delay'] - conf['backlog_reassign_delay'] = \ - input_on_stderr(('Stale transaction reassignment delay (in ' - 'seconds)? (default `{}`): '.format(val))) \ - or val + conf['backlog_reassign_delay'] = input_on_stderr( + 'Stale transaction reassignment delay (in seconds)? (default `{}`): '.format(val), val) if config_path != '-': bigchaindb.config_utils.write_config(conf, config_path) diff --git a/bigchaindb/commands/utils.py b/bigchaindb/commands/utils.py index f163a825..cd59856c 100644 --- a/bigchaindb/commands/utils.py +++ b/bigchaindb/commands/utils.py @@ -74,12 +74,50 @@ def start_logging_process(command): return start_logging +def _convert(value, default=None, convert=None): + def convert_bool(value): + if value.lower() in ('true', 't', 'yes', 'y'): + return True + if value.lower() in ('false', 'f', 'no', 'n'): + return False + raise ValueError('{} cannot be converted to bool'.format(value)) + + if value == '': + value = None + + if convert is None: + if default is not None: + convert = type(default) + else: + convert = str + + if convert == bool: + convert = convert_bool + + if value is None: + return default + else: + return convert(value) + + # We need this because `input` always prints on stdout, while it should print # to stderr. It's a very old bug, check it out here: # - https://bugs.python.org/issue1927 -def input_on_stderr(prompt=''): +def input_on_stderr(prompt='', default=None, convert=None): + """Output a string to stderr and wait for input. + + Args: + prompt (str): the message to display. + default: the default value to return if the user + leaves the field empty + convert (callable): a callable to be used to convert + the value the user inserted. If None, the type of + ``default`` will be used. + """ + print(prompt, end='', file=sys.stderr) - return builtins.input() + value = builtins.input() + return _convert(value, default, convert) def start_rethinkdb(): diff --git a/tests/commands/test_utils.py b/tests/commands/test_utils.py index f3b64c18..85aa8de4 100644 --- a/tests/commands/test_utils.py +++ b/tests/commands/test_utils.py @@ -13,6 +13,33 @@ def reset_bigchaindb_config(monkeypatch): monkeypatch.setattr('bigchaindb.config', bigchaindb._config) +def test_input_on_stderr(): + from bigchaindb.commands.utils import input_on_stderr, _convert + + with patch('builtins.input', return_value='I love cats'): + assert input_on_stderr() == 'I love cats' + + # input_on_stderr uses `_convert` internally, from now on we will + # just use that function + + assert _convert('hack the planet') == 'hack the planet' + assert _convert('42') == '42' + assert _convert('42', default=10) == 42 + assert _convert('', default=10) == 10 + assert _convert('42', convert=int) == 42 + assert _convert('True', convert=bool) is True + assert _convert('False', convert=bool) is False + assert _convert('t', convert=bool) is True + assert _convert('3.14', default=1.0) == 3.14 + assert _convert('TrUe', default=False) is True + + with pytest.raises(ValueError): + assert _convert('TRVE', default=False) + + with pytest.raises(ValueError): + assert _convert('ಠ_ಠ', convert=int) + + @pytest.mark.usefixtures('ignore_local_config_file', 'reset_bigchaindb_config') def test_configure_bigchaindb_configures_bigchaindb(): from bigchaindb.commands.utils import configure_bigchaindb diff --git a/tests/test_config_utils.py b/tests/test_config_utils.py index 4234e242..04c70325 100644 --- a/tests/test_config_utils.py +++ b/tests/test_config_utils.py @@ -11,7 +11,6 @@ ORIGINAL_CONFIG = copy.deepcopy(bigchaindb._config) @pytest.fixture(scope='function', autouse=True) def clean_config(monkeypatch, request): - import bigchaindb original_config = copy.deepcopy(ORIGINAL_CONFIG) backend = request.config.getoption('--database-backend') @@ -181,6 +180,9 @@ def test_autoconfigure_read_both_from_file_and_env(monkeypatch, request): 'connection_timeout': 5000, 'max_tries': 3, 'replicaset': 'bigchain-rs', + 'ssl': False, + 'login': None, + 'password': None } database = {}