mirror of
https://github.com/bigchaindb/bigchaindb.git
synced 2024-10-13 13:34:05 +00:00

* Adjust imports to bigchaindb_common * Adjust get_spent function signature * Adjust block serialization * Fix BigchainApi Test * Fix TestTransactionValidation tests * Fix TestBlockValidation tests * WIP: TestMultipleInputs * Adjust tests to tx-model interface changes - Fix old tests - Fix tests in TestMultipleInputs class * Remove fulfillment message tests * Fix TransactionMalleability tests * Remove Cryptoconditions tests * Remove create_transaction * Remove signing logic * Remove consensus plugin * Fix block_creation pipeline * Fix election pipeline * Replace some util functions with bdb_common ones - timestamp ==> gen_timestamp - serialize. * Implement Block model * Simplify function signatures for vote functions Change parameter interface for the following functions: - has_previous_vote - verify_vote_signature - block_election_status so that they take a block's id and voters instead of a fake block. * Integrate Block and Transaction model * Fix leftover tests and cleanup conftest * Add bigchaindb-common to install_requires * Delete transactions after block is written (#609) * delete transactions after block is written * cleanup transaction_exists * check for duplicate transactions * delete invalid tx from backlog * test duplicate transaction * Remove dead code * Test processes.py * Test invalid tx in on server * Fix tests for core.py * Fix models tests * Test commands main fn * Add final coverage to vote pipeline * Add more tests to voting pipeline * Remove consensus plugin docs and misc * Post rebase fixes * Fix rebase mess * Remove extra blank line * Improve docstring * Remove comment handled in bigchaindb/cryptoconditions#27; see https://github.com/bigchaindb/cryptoconditions/issues/27 * Fix block serialization in block creation * Add signed_ prefix to transfer_tx * Improve docs * Add library documentation page on pipelines * PR feedback for models.py * Impr. readability of get_last_voted_block * Use dict comprehension * Add docker-compose file to build and serve docs locally for development purposes * Change private_key for signing_key * Improve docstrings * Remove consensus docs * Document new consensus module * Create different transactions for the block * Cleanup variable names in block.py * Create different transactions for the block * Cleanup variable names in block.py
243 lines
7.5 KiB
Python
243 lines
7.5 KiB
Python
"""Utils to configure BigchainDB.
|
|
|
|
By calling `file_config`, the global configuration (stored in
|
|
`$HOME/.bigchaindb`) will be updated with the values contained
|
|
in the configuration file.
|
|
|
|
Note that there is a precedence in reading configuration values:
|
|
- local config file;
|
|
- environment vars;
|
|
- default config file (contained in ``bigchaindb.__init__``).
|
|
"""
|
|
|
|
|
|
import os
|
|
import copy
|
|
import json
|
|
import logging
|
|
import collections
|
|
|
|
from bigchaindb_common import exceptions
|
|
|
|
import bigchaindb
|
|
|
|
|
|
# TODO: move this to a proper configuration file for logging
|
|
logging.getLogger('requests').setLevel(logging.WARNING)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
CONFIG_DEFAULT_PATH = os.environ.setdefault(
|
|
'BIGCHAINDB_CONFIG_PATH',
|
|
os.path.join(os.path.expanduser('~'), '.bigchaindb'),
|
|
)
|
|
|
|
CONFIG_PREFIX = 'BIGCHAINDB'
|
|
CONFIG_SEP = '_'
|
|
|
|
|
|
def map_leafs(func, mapping):
|
|
"""Map a function to the leafs of a mapping."""
|
|
|
|
def _inner(mapping, path=None):
|
|
if path is None:
|
|
path = []
|
|
|
|
for key, val in mapping.items():
|
|
if isinstance(val, collections.Mapping):
|
|
_inner(val, path + [key])
|
|
else:
|
|
mapping[key] = func(val, path=path+[key])
|
|
|
|
return mapping
|
|
|
|
return _inner(copy.deepcopy(mapping))
|
|
|
|
|
|
# Thanks Alex <3
|
|
# http://stackoverflow.com/a/3233356/597097
|
|
def update(d, u):
|
|
"""Recursively update a mapping (i.e. a dict, list, set, or tuple).
|
|
|
|
Conceptually, d and u are two sets trees (with nodes and edges).
|
|
This function goes through all the nodes of u. For each node in u,
|
|
if d doesn't have that node yet, then this function adds the node from u,
|
|
otherwise this function overwrites the node already in d with u's node.
|
|
|
|
Args:
|
|
d (mapping): The mapping to overwrite and add to.
|
|
u (mapping): The mapping to read for changes.
|
|
|
|
Returns:
|
|
mapping: An updated version of d (updated by u).
|
|
"""
|
|
for k, v in u.items():
|
|
if isinstance(v, collections.Mapping):
|
|
r = update(d.get(k, {}), v)
|
|
d[k] = r
|
|
else:
|
|
d[k] = u[k]
|
|
return d
|
|
|
|
|
|
def file_config(filename=None):
|
|
"""Returns the config values found in a configuration file.
|
|
|
|
Args:
|
|
filename (str): the JSON file with the configuration values.
|
|
If ``None``, CONFIG_DEFAULT_PATH will be used.
|
|
|
|
Returns:
|
|
dict: The config values in the specified config file (or the
|
|
file at CONFIG_DEFAULT_PATH, if filename == None)
|
|
"""
|
|
logger.debug('On entry into file_config(), filename = {}'.format(filename))
|
|
|
|
if filename is None:
|
|
filename = CONFIG_DEFAULT_PATH
|
|
|
|
logger.debug('file_config() will try to open `{}`'.format(filename))
|
|
with open(filename) as f:
|
|
try:
|
|
config = json.load(f)
|
|
except ValueError as err:
|
|
raise exceptions.ConfigurationError(
|
|
'Failed to parse the JSON configuration from `{}`, {}'.format(filename, err)
|
|
)
|
|
|
|
logger.info('Configuration loaded from `{}`'.format(filename))
|
|
|
|
return config
|
|
|
|
|
|
def env_config(config):
|
|
"""Return a new configuration with the values found in the environment.
|
|
|
|
The function recursively iterates over the config, checking if there is
|
|
a matching env variable. If an env variable is found, the func updates
|
|
the configuration with that value.
|
|
|
|
The name of the env variable is built combining a prefix (``BIGCHAINDB``)
|
|
with the path to the value. If the ``config`` in input is:
|
|
``{'database': {'host': 'localhost'}}``
|
|
this function will try to read the env variable ``BIGCHAINDB_DATABASE_HOST``.
|
|
"""
|
|
|
|
def load_from_env(value, path):
|
|
var_name = CONFIG_SEP.join([CONFIG_PREFIX] + list(map(lambda s: s.upper(), path)))
|
|
return os.environ.get(var_name, value)
|
|
|
|
return map_leafs(load_from_env, config)
|
|
|
|
|
|
def update_types(config, reference, list_sep=':'):
|
|
"""Return a new configuration where all the values types
|
|
are aligned with the ones in the default configuration"""
|
|
|
|
def _coerce(current, value):
|
|
# Coerce a value to the `current` type.
|
|
try:
|
|
# First we try to apply current to the value, since it
|
|
# might be a function
|
|
return current(value)
|
|
except TypeError:
|
|
# Then we check if current is a list AND if the value
|
|
# is a string.
|
|
if isinstance(current, list) and isinstance(value, str):
|
|
# If so, we use the colon as the separator
|
|
return value.split(list_sep)
|
|
|
|
try:
|
|
# If we are here, we should try to apply the type
|
|
# of `current` to the value
|
|
return type(current)(value)
|
|
except TypeError:
|
|
# Worst case scenario we return the value itself.
|
|
return value
|
|
|
|
def _update_type(value, path):
|
|
current = reference
|
|
|
|
for elem in path:
|
|
try:
|
|
current = current[elem]
|
|
except KeyError:
|
|
return value
|
|
|
|
return _coerce(current, value)
|
|
|
|
return map_leafs(_update_type, config)
|
|
|
|
|
|
def set_config(config):
|
|
"""Set bigchaindb.config equal to the default config dict,
|
|
then update that with whatever is in the provided config dict,
|
|
and then set bigchaindb.config['CONFIGURED'] = True
|
|
|
|
Args:
|
|
config (dict): the config dict to read for changes
|
|
to the default config
|
|
|
|
Note:
|
|
Any previous changes made to ``bigchaindb.config`` will be lost.
|
|
"""
|
|
# Deep copy the default config into bigchaindb.config
|
|
bigchaindb.config = copy.deepcopy(bigchaindb._config)
|
|
# Update the default config with whatever is in the passed config
|
|
update(bigchaindb.config, update_types(config, bigchaindb.config))
|
|
bigchaindb.config['CONFIGURED'] = True
|
|
|
|
|
|
def update_config(config):
|
|
"""Update bigchaindb.config with whatever is in the provided config dict,
|
|
and then set bigchaindb.config['CONFIGURED'] = True
|
|
|
|
Args:
|
|
config (dict): the config dict to read for changes
|
|
to the default config
|
|
"""
|
|
|
|
# Update the default config with whatever is in the passed config
|
|
update(bigchaindb.config, update_types(config, bigchaindb.config))
|
|
bigchaindb.config['CONFIGURED'] = True
|
|
|
|
|
|
def write_config(config, filename=None):
|
|
"""Write the provided configuration to a specific location.
|
|
|
|
Args:
|
|
config (dict): a dictionary with the configuration to load.
|
|
filename (str): the name of the file that will store the new configuration. Defaults to ``None``.
|
|
If ``None``, the HOME of the current user and the string ``.bigchaindb`` will be used.
|
|
"""
|
|
if not filename:
|
|
filename = CONFIG_DEFAULT_PATH
|
|
|
|
with open(filename, 'w') as f:
|
|
json.dump(config, f, indent=4)
|
|
|
|
|
|
def autoconfigure(filename=None, config=None, force=False):
|
|
"""Run ``file_config`` and ``env_config`` if the module has not
|
|
been initialized."""
|
|
|
|
if not force and bigchaindb.config.get('CONFIGURED'):
|
|
logger.debug('System already configured, skipping autoconfiguration')
|
|
return
|
|
|
|
# start with the current configuration
|
|
newconfig = bigchaindb.config
|
|
|
|
# update configuration from file
|
|
try:
|
|
newconfig = update(newconfig, file_config(filename=filename))
|
|
except FileNotFoundError as e:
|
|
logger.warning('Cannot find config file `%s`.' % e.filename)
|
|
|
|
# override configuration with env variables
|
|
newconfig = env_config(newconfig)
|
|
|
|
if config:
|
|
newconfig = update(newconfig, config)
|
|
|
|
set_config(newconfig) # sets bigchaindb.config
|