Migrate CLI for tendermint integration

- Remove commands that were not required.
  - export-my-pubkey
  - set-shards
  - set-replicas
  - add-replicas
  - remove-replicas
- Update bigchaindb --help, usage description
- Re-enable tests
- Update docker-compose.travis.yml and Dockerfile for travis
  because some env variables are not needed
This commit is contained in:
Ahmed Muawia Khan 2018-02-22 21:34:31 +01:00
parent e6c77d5fcd
commit 5cea9c28bf
10 changed files with 48 additions and 577 deletions

View File

@ -19,8 +19,6 @@ from bigchaindb.tendermint.core import BigchainDB
from bigchaindb import backend
from bigchaindb.backend import schema
from bigchaindb.backend import query
from bigchaindb.backend.admin import (set_replicas, set_shards, add_replicas,
remove_replicas)
from bigchaindb.backend.exceptions import OperationError
from bigchaindb.commands import utils
from bigchaindb.commands.messages import (
@ -80,8 +78,8 @@ def run_configure(args, skip_if_exists=False):
# Patch the default configuration with the new values
conf = bigchaindb.config_utils.update(
conf,
bigchaindb.config_utils.env_config(bigchaindb.config))
conf,
bigchaindb.config_utils.env_config(bigchaindb.config))
print('Generating keypair', file=sys.stderr)
conf['keypair']['private'], conf['keypair']['public'] = \
@ -96,15 +94,18 @@ def run_configure(args, skip_if_exists=False):
if not args.yes:
for key in ('bind', ):
val = conf['server'][key]
conf['server'][key] = input_on_stderr('API Server {}? (default `{}`): '.format(key, val), val)
conf['server'][key] = input_on_stderr(
'API Server {}? (default `{}`): '.format(key, val), val)
for key in ('scheme', 'host', 'port'):
val = conf['wsserver'][key]
conf['wsserver'][key] = input_on_stderr('WebSocket Server {}? (default `{}`): '.format(key, val), val)
conf['wsserver'][key] = input_on_stderr(
'WebSocket Server {}? (default `{}`): '.format(key, val), val)
for key in database_keys:
val = conf['database'][key]
conf['database'][key] = input_on_stderr('Database {}? (default `{}`): '.format(key, val), 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(
@ -118,21 +119,6 @@ def run_configure(args, skip_if_exists=False):
print('Ready to go!', file=sys.stderr)
@configure_bigchaindb
def run_export_my_pubkey(args):
"""Export this node's public key to standard output
"""
pubkey = bigchaindb.config['keypair']['public']
if pubkey is not None:
print(pubkey)
else:
sys.exit("This node's public key wasn't set anywhere "
"so it can't be exported")
# raises SystemExit exception
# message is sent to stderr
# exits with exit code 1 (signals tha an error happened)
def _run_init():
# Try to access the keypair, throws an exception if it does not exist
b = bigchaindb.Bigchain()
@ -164,7 +150,8 @@ def run_recover(b):
if block:
while block['height'] > tendermint_height:
logger.info('BigchainDB is ahead of tendermint, removing block %s', block['height'])
logger.info(
'BigchainDB is ahead of tendermint, removing block %s', block['height'])
query.delete_latest_block(b.connection)
block = b.get_latest_block()
@ -175,7 +162,8 @@ def run_drop(args):
dbname = bigchaindb.config['database']['name']
if not args.yes:
response = input_on_stderr('Do you want to drop `{}` database? [y/n]: '.format(dbname))
response = input_on_stderr(
'Do you want to drop `{}` database? [y/n]: '.format(dbname))
if response != 'y':
return
@ -184,7 +172,8 @@ def run_drop(args):
try:
schema.drop_database(conn, dbname)
except DatabaseDoesNotExist:
print("Cannot drop '{name}'. The database does not exist.".format(name=dbname), file=sys.stderr)
print("Cannot drop '{name}'. The database does not exist.".format(
name=dbname), file=sys.stderr)
@configure_bigchaindb
@ -195,22 +184,7 @@ def run_start(args):
run_recover(BigchainDB())
if args.allow_temp_keypair:
if not (bigchaindb.config['keypair']['private'] or
bigchaindb.config['keypair']['public']):
private_key, public_key = crypto.generate_key_pair()
bigchaindb.config['keypair']['private'] = private_key
bigchaindb.config['keypair']['public'] = public_key
else:
logger.warning('Keypair found, no need to create one on the fly.')
if args.start_rethinkdb:
try:
proc = utils.start_rethinkdb()
except StartupError as e:
sys.exit(RETHINKDB_STARTUP_ERROR.format(e))
logger.info('RethinkDB started with PID %s' % proc.pid)
logger.info('Keypair found, no need to create one on the fly.')
try:
if not args.skip_initialize_database:
@ -227,50 +201,6 @@ def run_start(args):
start()
@configure_bigchaindb
def run_set_shards(args):
conn = backend.connect()
try:
set_shards(conn, shards=args.num_shards)
except OperationError as e:
sys.exit(str(e))
@configure_bigchaindb
def run_set_replicas(args):
conn = backend.connect()
try:
set_replicas(conn, replicas=args.num_replicas)
except OperationError as e:
sys.exit(str(e))
@configure_bigchaindb
def run_add_replicas(args):
# Note: This command is specific to MongoDB
conn = backend.connect()
try:
add_replicas(conn, args.replicas)
except (OperationError, NotImplementedError) as e:
sys.exit(str(e))
else:
print('Added {} to the replicaset.'.format(args.replicas))
@configure_bigchaindb
def run_remove_replicas(args):
# Note: This command is specific to MongoDB
conn = backend.connect()
try:
remove_replicas(conn, args.replicas)
except (OperationError, NotImplementedError) as e:
sys.exit(str(e))
else:
print('Removed {} from the replicaset.'.format(args.replicas))
def create_parser():
parser = argparse.ArgumentParser(
description='Control your BigchainDB node.',
@ -292,16 +222,13 @@ def create_parser():
default='localmongodb',
const='localmongodb',
nargs='?',
help='The backend to use. It can be either '
'rethinkdb or mongodb.')
help='The backend to use. It can only be '
'"localmongodb", currently.')
# parsers for showing/exporting config values
subparsers.add_parser('show-config',
help='Show the current configuration')
subparsers.add_parser('export-my-pubkey',
help="Export this node's public key")
# parser for database-level commands
subparsers.add_parser('init',
help='Init the database')
@ -313,63 +240,12 @@ def create_parser():
start_parser = subparsers.add_parser('start',
help='Start BigchainDB')
start_parser.add_argument('--dev-allow-temp-keypair',
dest='allow_temp_keypair',
action='store_true',
help='Generate a random keypair on start')
start_parser.add_argument('--dev-start-rethinkdb',
dest='start_rethinkdb',
action='store_true',
help='Run RethinkDB on start')
start_parser.add_argument('--no-init',
dest='skip_initialize_database',
default=False,
action='store_true',
help='Skip database initialization')
# parser for configuring the number of shards
sharding_parser = subparsers.add_parser('set-shards',
help='Configure number of shards')
sharding_parser.add_argument('num_shards', metavar='num_shards',
type=int, default=1,
help='Number of shards')
# parser for configuring the number of replicas
replicas_parser = subparsers.add_parser('set-replicas',
help='Configure number of replicas')
replicas_parser.add_argument('num_replicas', metavar='num_replicas',
type=int, default=1,
help='Number of replicas (i.e. the replication factor)')
# parser for adding nodes to the replica set
add_replicas_parser = subparsers.add_parser('add-replicas',
help='Add a set of nodes to the '
'replica set. This command '
'is specific to the MongoDB'
' backend.')
add_replicas_parser.add_argument('replicas', nargs='+',
type=utils.mongodb_host,
help='A list of space separated hosts to '
'add to the replicaset. Each host '
'should be in the form `host:port`.')
# parser for removing nodes from the replica set
rm_replicas_parser = subparsers.add_parser('remove-replicas',
help='Remove a set of nodes from the '
'replica set. This command '
'is specific to the MongoDB'
' backend.')
rm_replicas_parser.add_argument('replicas', nargs='+',
type=utils.mongodb_host,
help='A list of space separated hosts to '
'remove from the replicaset. Each host '
'should be in the form `host:port`.')
return parser

View File

@ -11,7 +11,6 @@ import sys
import rethinkdb as r
from pymongo import uri_parser
import bigchaindb
import bigchaindb.config_utils
from bigchaindb import backend
@ -125,46 +124,6 @@ def input_on_stderr(prompt='', default=None, convert=None):
return _convert(value, default, convert)
def start_rethinkdb():
"""Start RethinkDB as a child process and wait for it to be
available.
Raises:
:class:`~bigchaindb.common.exceptions.StartupError` if
RethinkDB cannot be started.
"""
proc = subprocess.Popen(['rethinkdb', '--bind', 'all'],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
universal_newlines=True)
dbname = bigchaindb.config['database']['name']
line = ''
for line in proc.stdout:
if line.startswith('Server ready'):
# FIXME: seems like tables are not ready when the server is ready,
# that's why we need to query RethinkDB to know the state
# of the database. This code assumes the tables are ready
# when the database is ready. This seems a valid assumption.
try:
conn = backend.connect()
# Before checking if the db is ready, we need to query
# the server to check if it contains that db
if conn.run(r.db_list().contains(dbname)):
conn.run(r.db(dbname).wait())
except (r.ReqlOpFailedError, r.ReqlDriverError) as exc:
raise StartupError('Error waiting for the database `{}` '
'to be ready'.format(dbname)) from exc
return proc
# We are here when we exhaust the stdout of the process.
# The last `line` contains info about the error.
raise StartupError(line)
def start(parser, argv, scope):
"""Utility function to execute a subcommand.
@ -206,34 +165,6 @@ def start(parser, argv, scope):
return func(args)
def mongodb_host(host):
"""Utility function that works as a type for mongodb ``host`` args.
This function validates the ``host`` args provided by to the
``add-replicas`` and ``remove-replicas`` commands and checks if each arg
is in the form "host:port"
Args:
host (str): A string containing hostname and port (e.g. "host:port")
Raises:
ArgumentTypeError: if it fails to parse the argument
"""
# check if mongodb can parse the host
try:
hostname, port = uri_parser.parse_host(host, default_port=None)
except ValueError as exc:
raise argparse.ArgumentTypeError(exc.args[0])
# we do require the port to be provided.
if port is None or hostname == '':
raise argparse.ArgumentTypeError('expected host in the form '
'`host:port`. Got `{}` instead.'
.format(host))
return host
base_parser = argparse.ArgumentParser(add_help=False, prog='bigchaindb')
base_parser.add_argument('-c', '--config',
@ -244,7 +175,8 @@ base_parser.add_argument('-c', '--config',
# the environment variables provided to configure the logger.
base_parser.add_argument('-l', '--log-level',
type=str.upper, # convert to uppercase for comparison to choices
choices=['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL'],
choices=['DEBUG', 'INFO',
'WARNING', 'ERROR', 'CRITICAL'],
help='Log level')
base_parser.add_argument('-y', '--yes', '--yes-please',

View File

@ -13,7 +13,7 @@ from bigchaindb.tendermint.utils import decode_transaction_base64
HOST = getenv('BIGCHAINDB_TENDERMINT_HOST', 'localhost')
PORT = int(getenv('BIGCHAINDB_TENDERMINT_PORT', 46657))
URL = f'ws://{HOST}:{PORT}/websocket'
URL = 'ws://{HOST}:{PORT}/websocket'
logger = logging.getLogger(__name__)

View File

@ -12,11 +12,7 @@ ENV PYTHONUNBUFFERED 0
ENV BIGCHAINDB_DATABASE_PORT 27017
ENV BIGCHAINDB_DATABASE_BACKEND $backend
ENV BIGCHAINDB_SERVER_BIND 0.0.0.0:9984
ENV BIGCHAINDB_WSSERVER_HOST 0.0.0.0
ENV BIGCHAINDB_WSSERVER_SCHEME ws
ENV BIGCHAINDB_WSSERVER_ADVERTISED_HOST 0.0.0.0
ENV BIGCHAINDB_WSSERVER_ADVERTISED_SCHEME ws
ENV BIGCHAINDB_START_TENDERMINT 0

View File

@ -19,8 +19,6 @@ services:
BIGCHAINDB_DATABASE_BACKEND: localmongodb
BIGCHAINDB_DATABASE_HOST: mdb
BIGCHAINDB_DATABASE_PORT: 27017
BIGCHAINDB_SERVER_BIND: 0.0.0.0:9984
BIGCHAINDB_WSSERVER_HOST: 0.0.0.0
BIGCHAINDB_START_TENDERMINT: 0
BIGCHAINDB_TENDERMINT_HOST: tendermint
BIGCHAINDB_TENDERMINT_PORT: 46657

View File

@ -47,8 +47,6 @@ def run_start_args(request):
param = getattr(request, 'param', {})
return Namespace(
config=param.get('config'),
start_rethinkdb=param.get('start_rethinkdb', False),
allow_temp_keypair=param.get('allow_temp_keypair', False),
skip_initialize_database=param.get('skip_initialize_database', False),
)

View File

@ -1,125 +0,0 @@
import pytest
import rethinkdb
from unittest.mock import Mock, patch
from argparse import Namespace
@patch('bigchaindb.commands.utils.start_rethinkdb', return_value=Mock())
def test_bigchain_run_start_with_rethinkdb(mock_start_rethinkdb,
mock_run_configure,
mock_processes_start,
mock_db_init_with_existing_db,
mocked_setup_logging):
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_start
args = Namespace(start_rethinkdb=True, allow_temp_keypair=False, config=None, yes=True,
skip_initialize_database=False)
run_start(args)
mock_start_rethinkdb.assert_called_with()
mocked_setup_logging.assert_called_once_with(user_log_config=config['log'])
@patch('subprocess.Popen')
def test_start_rethinkdb_returns_a_process_when_successful(mock_popen):
from bigchaindb.commands import utils
mock_popen.return_value = Mock(stdout=[
'Listening for client driver 1234',
'Server ready'])
assert utils.start_rethinkdb() is mock_popen.return_value
@patch('subprocess.Popen')
def test_start_rethinkdb_exits_when_cannot_start(mock_popen):
from bigchaindb.common import exceptions
from bigchaindb.commands import utils
mock_popen.return_value = Mock(stdout=['Nopety nope'])
with pytest.raises(exceptions.StartupError):
utils.start_rethinkdb()
@patch('rethinkdb.ast.Table.reconfigure')
def test_set_shards(mock_reconfigure, monkeypatch, b):
from bigchaindb.commands.bigchaindb import run_set_shards
# this will mock the call to retrieve the database config
# we will set it to return one replica
def mockreturn_one_replica(self, conn):
return {'shards': [{'replicas': [1]}]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_one_replica)
args = Namespace(num_shards=3, config=None)
run_set_shards(args)
mock_reconfigure.assert_called_with(replicas=1, shards=3, dry_run=False)
# this will mock the call to retrieve the database config
# we will set it to return three replica
def mockreturn_three_replicas(self, conn):
return {'shards': [{'replicas': [1, 2, 3]}]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_three_replicas)
run_set_shards(args)
mock_reconfigure.assert_called_with(replicas=3, shards=3, dry_run=False)
def test_set_shards_raises_exception(monkeypatch, b):
from bigchaindb.commands.bigchaindb import run_set_shards
# test that we are correctly catching the exception
def mock_raise(*args, **kwargs):
raise rethinkdb.ReqlOpFailedError('')
def mockreturn_one_replica(self, conn):
return {'shards': [{'replicas': [1]}]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_one_replica)
monkeypatch.setattr(rethinkdb.ast.Table, 'reconfigure', mock_raise)
args = Namespace(num_shards=3, config=None)
with pytest.raises(SystemExit) as exc:
run_set_shards(args)
assert exc.value.args == ('Failed to reconfigure tables.',)
@patch('rethinkdb.ast.Table.reconfigure')
def test_set_replicas(mock_reconfigure, monkeypatch, b):
from bigchaindb.commands.bigchaindb import run_set_replicas
# this will mock the call to retrieve the database config
# we will set it to return two shards
def mockreturn_two_shards(self, conn):
return {'shards': [1, 2]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_two_shards)
args = Namespace(num_replicas=2, config=None)
run_set_replicas(args)
mock_reconfigure.assert_called_with(replicas=2, shards=2, dry_run=False)
# this will mock the call to retrieve the database config
# we will set it to return three shards
def mockreturn_three_shards(self, conn):
return {'shards': [1, 2, 3]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_three_shards)
run_set_replicas(args)
mock_reconfigure.assert_called_with(replicas=2, shards=3, dry_run=False)
def test_set_replicas_raises_exception(monkeypatch, b):
from bigchaindb.commands.bigchaindb import run_set_replicas
# test that we are correctly catching the exception
def mock_raise(*args, **kwargs):
raise rethinkdb.ReqlOpFailedError('')
def mockreturn_two_shards(self, conn):
return {'shards': [1, 2]}
monkeypatch.setattr(rethinkdb.RqlQuery, 'run', mockreturn_two_shards)
monkeypatch.setattr(rethinkdb.ast.Table, 'reconfigure', mock_raise)
args = Namespace(num_replicas=2, config=None)
with pytest.raises(SystemExit) as exc:
run_set_replicas(args)
assert exc.value.args == ('Failed to reconfigure tables.',)

View File

@ -16,16 +16,12 @@ def test_make_sure_we_dont_remove_any_command():
assert parser.parse_args(['configure', 'localmongodb']).command
assert parser.parse_args(['configure', 'localmongodb']).command
assert parser.parse_args(['show-config']).command
assert parser.parse_args(['export-my-pubkey']).command
assert parser.parse_args(['init']).command
assert parser.parse_args(['drop']).command
assert parser.parse_args(['start']).command
assert parser.parse_args(['set-shards', '1']).command
assert parser.parse_args(['set-replicas', '1']).command
assert parser.parse_args(['add-replicas', 'localhost:27017']).command
assert parser.parse_args(['remove-replicas', 'localhost:27017']).command
@pytest.mark.tendermint
@patch('bigchaindb.commands.utils.start')
def test_main_entrypoint(mock_start):
from bigchaindb.commands.bigchaindb import main
@ -40,12 +36,13 @@ def test_bigchain_run_start(mock_run_configure,
mocked_setup_logging):
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_start
args = Namespace(start_rethinkdb=False, allow_temp_keypair=False, config=None, yes=True,
args = Namespace(config=None, yes=True,
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(user_log_config=config['log'])
@pytest.mark.tendermint
@pytest.mark.skipif(reason="BigchainDB doesn't support the automatic creation of a config file anymore")
def test_bigchain_run_start_assume_yes_create_default_config(
monkeypatch, mock_processes_start, mock_generate_key_pair,
@ -63,7 +60,8 @@ def test_bigchain_run_start_assume_yes_create_default_config(
value['return'] = newconfig
monkeypatch.setattr(config_utils, 'write_config', mock_write_config)
monkeypatch.setattr(config_utils, 'file_config', lambda x: config_utils.set_config(expected_config))
monkeypatch.setattr(config_utils, 'file_config',
lambda x: config_utils.set_config(expected_config))
monkeypatch.setattr('os.path.exists', lambda path: False)
args = Namespace(config=None, yes=True)
@ -76,6 +74,7 @@ def test_bigchain_run_start_assume_yes_create_default_config(
# TODO Please beware, that if debugging, the "-s" switch for pytest will
# interfere with capsys.
# See related issue: https://github.com/pytest-dev/pytest/issues/128
@pytest.mark.tendermint
@pytest.mark.usefixtures('ignore_local_config_file')
def test_bigchain_show_config(capsys):
from bigchaindb import config
@ -90,45 +89,7 @@ def test_bigchain_show_config(capsys):
assert output_config == config
@pytest.mark.usefixtures('ignore_local_config_file')
def test_bigchain_export_my_pubkey_when_pubkey_set(capsys, monkeypatch):
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_export_my_pubkey
args = Namespace(config=None)
# so in run_export_my_pubkey(args) below,
# filename=args.config='dummy' is passed to autoconfigure().
# We just assume autoconfigure() works and sets
# config['keypair']['public'] correctly (tested elsewhere).
# We force-set config['keypair']['public'] using monkeypatch.
monkeypatch.setitem(config['keypair'], 'public', 'Charlie_Bucket')
_, _ = capsys.readouterr() # has the effect of clearing capsys
run_export_my_pubkey(args)
out, _ = capsys.readouterr()
lines = out.splitlines()
assert config['keypair']['public'] in lines
assert 'Charlie_Bucket' in lines
@pytest.mark.usefixtures('ignore_local_config_file')
def test_bigchain_export_my_pubkey_when_pubkey_not_set(monkeypatch):
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_export_my_pubkey
args = Namespace(config=None)
monkeypatch.setitem(config['keypair'], 'public', None)
# assert that run_export_my_pubkey(args) raises SystemExit:
with pytest.raises(SystemExit) as exc_info:
run_export_my_pubkey(args)
# exc_info is an object of class ExceptionInfo
# https://pytest.org/latest/builtin.html#_pytest._code.ExceptionInfo
assert exc_info.type == SystemExit
# exc_info.value is an object of class SystemExit
# https://docs.python.org/3/library/exceptions.html#SystemExit
assert exc_info.value.code == \
"This node's public key wasn't set anywhere so it can't be exported"
@pytest.mark.tendermint
def test_bigchain_run_init_when_db_exists(mocker, capsys):
from bigchaindb.commands.bigchaindb import run_init
from bigchaindb.common.exceptions import DatabaseAlreadyExists
@ -148,6 +109,7 @@ def test_bigchain_run_init_when_db_exists(mocker, capsys):
)
@pytest.mark.tendermint
def test__run_init(mocker):
from bigchaindb.commands.bigchaindb import _run_init
bigchain_mock = mocker.patch(
@ -164,6 +126,7 @@ def test__run_init(mocker):
bigchain_mock.return_value.create_genesis_block.assert_called_once_with()
@pytest.mark.tendermint
@patch('bigchaindb.backend.schema.drop_database')
def test_drop_db_when_assumed_yes(mock_db_drop):
from bigchaindb.commands.bigchaindb import run_drop
@ -173,16 +136,19 @@ def test_drop_db_when_assumed_yes(mock_db_drop):
assert mock_db_drop.called
@pytest.mark.tendermint
@patch('bigchaindb.backend.schema.drop_database')
def test_drop_db_when_interactive_yes(mock_db_drop, monkeypatch):
from bigchaindb.commands.bigchaindb import run_drop
args = Namespace(config=None, yes=False)
monkeypatch.setattr('bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'y')
monkeypatch.setattr(
'bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'y')
run_drop(args)
assert mock_db_drop.called
@pytest.mark.tendermint
@patch('bigchaindb.backend.schema.drop_database')
def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys):
from bigchaindb import config
@ -197,16 +163,19 @@ def test_drop_db_when_db_does_not_exist(mock_db_drop, capsys):
name=config['database']['name'])
@pytest.mark.tendermint
@patch('bigchaindb.backend.schema.drop_database')
def test_drop_db_does_not_drop_when_interactive_no(mock_db_drop, monkeypatch):
from bigchaindb.commands.bigchaindb import run_drop
args = Namespace(config=None, yes=False)
monkeypatch.setattr('bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'n')
monkeypatch.setattr(
'bigchaindb.commands.bigchaindb.input_on_stderr', lambda x: 'n')
run_drop(args)
assert not mock_db_drop.called
@pytest.mark.tendermint
def test_run_configure_when_config_exists_and_skipping(monkeypatch):
from bigchaindb.commands.bigchaindb import run_configure
monkeypatch.setattr('os.path.exists', lambda path: True)
@ -218,6 +187,7 @@ def test_run_configure_when_config_exists_and_skipping(monkeypatch):
# TODO Beware if you are putting breakpoints in there, and using the '-s'
# switch with pytest. It will just hang. Seems related to the monkeypatching of
# input_on_stderr.
@pytest.mark.tendermint
def test_run_configure_when_config_does_not_exist(monkeypatch,
mock_write_config,
mock_generate_key_pair,
@ -230,6 +200,7 @@ def test_run_configure_when_config_does_not_exist(monkeypatch,
assert return_value is None
@pytest.mark.tendermint
def test_run_configure_when_config_does_exist(monkeypatch,
mock_write_config,
mock_generate_key_pair,
@ -242,16 +213,17 @@ def test_run_configure_when_config_does_exist(monkeypatch,
from bigchaindb.commands.bigchaindb import run_configure
monkeypatch.setattr('os.path.exists', lambda path: True)
monkeypatch.setattr('builtins.input', lambda: '\n')
monkeypatch.setattr('bigchaindb.config_utils.write_config', mock_write_config)
monkeypatch.setattr(
'bigchaindb.config_utils.write_config', mock_write_config)
args = Namespace(config='foo', yes=None)
run_configure(args)
assert value == {}
@pytest.mark.tendermint
@pytest.mark.parametrize('backend', (
'rethinkdb',
'mongodb',
'localmongodb',
))
def test_run_configure_with_backend(backend, monkeypatch, mock_write_config):
import bigchaindb
@ -279,54 +251,6 @@ def test_run_configure_with_backend(backend, monkeypatch, mock_write_config):
assert value['return'] == expected_config
@patch('bigchaindb.common.crypto.generate_key_pair',
return_value=('private_key', 'public_key'))
@pytest.mark.usefixtures('ignore_local_config_file')
def test_allow_temp_keypair_generates_one_on_the_fly(
mock_gen_keypair, mock_processes_start,
mock_db_init_with_existing_db, mocked_setup_logging):
import bigchaindb
from bigchaindb.commands.bigchaindb import run_start
bigchaindb.config['keypair'] = {'private': None, 'public': None}
args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True,
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(
user_log_config=bigchaindb.config['log'])
assert bigchaindb.config['keypair']['private'] == 'private_key'
assert bigchaindb.config['keypair']['public'] == 'public_key'
@patch('bigchaindb.common.crypto.generate_key_pair',
return_value=('private_key', 'public_key'))
@pytest.mark.usefixtures('ignore_local_config_file')
def test_allow_temp_keypair_doesnt_override_if_keypair_found(mock_gen_keypair,
mock_processes_start,
mock_db_init_with_existing_db,
mocked_setup_logging):
import bigchaindb
from bigchaindb.commands.bigchaindb import run_start
# Preconditions for the test
original_private_key = bigchaindb.config['keypair']['private']
original_public_key = bigchaindb.config['keypair']['public']
assert isinstance(original_public_key, str)
assert isinstance(original_private_key, str)
args = Namespace(allow_temp_keypair=True, start_rethinkdb=False, config=None, yes=True,
skip_initialize_database=False)
run_start(args)
mocked_setup_logging.assert_called_once_with(
user_log_config=bigchaindb.config['log'])
assert bigchaindb.config['keypair']['private'] == original_private_key
assert bigchaindb.config['keypair']['public'] == original_public_key
def test_run_start_when_db_already_exists(mocker,
monkeypatch,
run_start_args,
@ -346,6 +270,7 @@ def test_run_start_when_db_already_exists(mocker,
assert mocked_start.called
@pytest.mark.tendermint
def test_run_start_when_keypair_not_found(mocker,
monkeypatch,
run_start_args,
@ -371,33 +296,7 @@ def test_run_start_when_keypair_not_found(mocker,
assert not mocked_start.called
def test_run_start_when_start_rethinkdb_fails(mocker,
monkeypatch,
run_start_args,
mocked_setup_logging):
from bigchaindb import config
from bigchaindb.commands.bigchaindb import run_start
from bigchaindb.commands.messages import RETHINKDB_STARTUP_ERROR
from bigchaindb.common.exceptions import StartupError
run_start_args.start_rethinkdb = True
mocked_start = mocker.patch('bigchaindb.processes.start')
err_msg = 'Error starting rethinkdb.'
def mock_start_rethinkdb():
raise StartupError(err_msg)
monkeypatch.setattr(
'bigchaindb.commands.utils.start_rethinkdb', mock_start_rethinkdb)
with pytest.raises(SystemExit) as exc:
run_start(run_start_args)
mocked_setup_logging.assert_called_once_with(user_log_config=config['log'])
assert len(exc.value.args) == 1
assert exc.value.args[0] == RETHINKDB_STARTUP_ERROR.format(err_msg)
assert not mocked_start.called
@pytest.mark.tendermint
@patch('argparse.ArgumentParser.parse_args')
@patch('bigchaindb.commands.utils.base_parser')
@patch('bigchaindb.commands.utils.start')
@ -424,102 +323,14 @@ def test_calling_main(start_mock, base_parser_mock, parse_args_mock,
subparsers.add_parser.assert_any_call('show-config',
help='Show the current '
'configuration')
subparsers.add_parserassert_any_call('export-my-pubkey',
help="Export this node's public "
'key')
subparsers.add_parser.assert_any_call('init', help='Init the database')
subparsers.add_parser.assert_any_call('drop', help='Drop the database')
subparsers.add_parser.assert_any_call('start', help='Start BigchainDB')
subsubparsers.add_argument.assert_any_call('--dev-start-rethinkdb',
dest='start_rethinkdb',
action='store_true',
help='Run RethinkDB on start')
subsubparsers.add_argument.assert_any_call('--dev-allow-temp-keypair',
dest='allow_temp_keypair',
action='store_true',
help='Generate a random keypair on start')
subparsers.add_parser.assert_any_call('set-shards',
help='Configure number of shards')
subsubparsers.add_argument.assert_any_call('num_shards',
metavar='num_shards',
type=int, default=1,
help='Number of shards')
subparsers.add_parser.assert_any_call('set-replicas',
help='Configure number of replicas')
subsubparsers.add_argument.assert_any_call('num_replicas',
metavar='num_replicas',
type=int, default=1,
help='Number of replicas (i.e. '
'the replication factor)')
assert start_mock.called is True
@pytest.mark.usefixtures('ignore_local_config_file')
@patch('bigchaindb.commands.bigchaindb.add_replicas')
def test_run_add_replicas(mock_add_replicas):
from bigchaindb.commands.bigchaindb import run_add_replicas
from bigchaindb.backend.exceptions import OperationError
args = Namespace(config=None, replicas=['localhost:27017'])
# test add_replicas no raises
mock_add_replicas.return_value = None
assert run_add_replicas(args) is None
assert mock_add_replicas.call_count == 1
mock_add_replicas.reset_mock()
# test add_replicas with `OperationError`
mock_add_replicas.side_effect = OperationError('err')
with pytest.raises(SystemExit) as exc:
run_add_replicas(args)
assert exc.value.args == ('err',)
assert mock_add_replicas.call_count == 1
mock_add_replicas.reset_mock()
# test add_replicas with `NotImplementedError`
mock_add_replicas.side_effect = NotImplementedError('err')
with pytest.raises(SystemExit) as exc:
run_add_replicas(args)
assert exc.value.args == ('err',)
assert mock_add_replicas.call_count == 1
mock_add_replicas.reset_mock()
@pytest.mark.usefixtures('ignore_local_config_file')
@patch('bigchaindb.commands.bigchaindb.remove_replicas')
def test_run_remove_replicas(mock_remove_replicas):
from bigchaindb.commands.bigchaindb import run_remove_replicas
from bigchaindb.backend.exceptions import OperationError
args = Namespace(config=None, replicas=['localhost:27017'])
# test add_replicas no raises
mock_remove_replicas.return_value = None
assert run_remove_replicas(args) is None
assert mock_remove_replicas.call_count == 1
mock_remove_replicas.reset_mock()
# test add_replicas with `OperationError`
mock_remove_replicas.side_effect = OperationError('err')
with pytest.raises(SystemExit) as exc:
run_remove_replicas(args)
assert exc.value.args == ('err',)
assert mock_remove_replicas.call_count == 1
mock_remove_replicas.reset_mock()
# test add_replicas with `NotImplementedError`
mock_remove_replicas.side_effect = NotImplementedError('err')
with pytest.raises(SystemExit) as exc:
run_remove_replicas(args)
assert exc.value.args == ('err',)
assert mock_remove_replicas.call_count == 1
mock_remove_replicas.reset_mock()
@pytest.mark.tendermint
@pytest.mark.bdb
def test_recover_db_from_zombie_txn(b, monkeypatch):
@ -598,7 +409,7 @@ def test_recover_db_on_start(mock_autoconfigure,
mock_start,
mocked_setup_logging):
from bigchaindb.commands.bigchaindb import run_start
args = Namespace(start_rethinkdb=False, allow_temp_keypair=False, config=None, yes=True,
args = Namespace(config=None, yes=True,
skip_initialize_database=False)
run_start(args)

View File

@ -119,20 +119,5 @@ def test_start_sets_multiprocess_var_based_on_cli_args(mock_cpu_count):
scope = {'run_mp_arg_test': run_mp_arg_test}
assert utils.start(parser, ['mp_arg_test'], scope).multiprocess == 1
assert utils.start(parser, ['mp_arg_test', '--multiprocess'], scope).multiprocess == 42
def test_mongodb_host_type():
from bigchaindb.commands.utils import mongodb_host
# bad port provided
with pytest.raises(ArgumentTypeError):
mongodb_host('localhost:11111111111')
# no port information provided
with pytest.raises(ArgumentTypeError):
mongodb_host('localhost')
# bad host provided
with pytest.raises(ArgumentTypeError):
mongodb_host(':27017')
assert utils.start(
parser, ['mp_arg_test', '--multiprocess'], scope).multiprocess == 42