From ae03ea6cb60d6216752aff7453406178afa03f5a Mon Sep 17 00:00:00 2001 From: Neil Booth Date: Thu, 21 Sep 2017 13:12:55 +0800 Subject: [PATCH] Add env object for wallet engine Split out common Env parts to a base class. --- lib/env.py | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++ server/env.py | 68 +++--------------------------------------- wallet/env.py | 31 +++++++++++++++++++ 3 files changed, 117 insertions(+), 64 deletions(-) create mode 100644 lib/env.py create mode 100644 wallet/env.py diff --git a/lib/env.py b/lib/env.py new file mode 100644 index 0000000..cf3cb9b --- /dev/null +++ b/lib/env.py @@ -0,0 +1,82 @@ +# Copyright (c) 2017, Neil Booth +# +# All rights reserved. +# +# See the file "LICENCE" for information about the copyright +# and warranty status of this software. + +'''Class for server environment configuration and defaults.''' + + +from os import environ + +import lib.util as lib_util + + +class EnvBase(lib_util.LoggedClass): + '''Wraps environment configuration.''' + + class Error(Exception): + pass + + def __init__(self): + super().__init__() + self.allow_root = self.boolean('ALLOW_ROOT', False) + self.host = self.default('HOST', 'localhost') + self.rpc_host = self.default('RPC_HOST', 'localhost') + self.loop_policy = self.event_loop_policy() + + def default(self, envvar, default): + return environ.get(envvar, default) + + def boolean(self, envvar, default): + return bool(self.default(envvar, default)) + + def required(self, envvar): + value = environ.get(envvar) + if value is None: + raise self.Error('required envvar {} not set'.format(envvar)) + return value + + def integer(self, envvar, default): + value = environ.get(envvar) + if value is None: + return default + try: + return int(value) + except Exception: + raise self.Error('cannot convert envvar {} value {} to an integer' + .format(envvar, value)) + + def obsolete(self, envvars): + bad = [envvar for envvar in envvars if environ.get(envvar)] + if bad: + raise self.Error('remove obsolete environment variables {}' + .format(bad)) + + def event_loop_policy(self): + policy = self.default('EVENT_LOOP_POLICY', None) + if policy is None: + return None + if policy == 'uvloop': + import uvloop + return uvloop.EventLoopPolicy() + raise self.Error('unknown event loop policy "{}"'.format(policy)) + + def cs_host(self, *, for_rpc): + '''Returns the 'host' argument to pass to asyncio's create_server + call. The result can be a single host name string, a list of + host name strings, or an empty string to bind to all interfaces. + + If rpc is True the host to use for the RPC server is returned. + Otherwise the host to use for SSL/TCP servers is returned. + ''' + host = self.rpc_host if for_rpc else self.host + result = [part.strip() for part in host.split(',')] + if len(result) == 1: + result = result[0] + # An empty result indicates all interfaces, which we do not + # permitted for an RPC server. + if for_rpc and not result: + result = 'localhost' + return result diff --git a/server/env.py b/server/env.py index 4210da9..2f055f1 100644 --- a/server/env.py +++ b/server/env.py @@ -11,26 +11,23 @@ import resource from collections import namedtuple from ipaddress import ip_address -from os import environ from lib.coins import Coin +from lib.env import EnvBase import lib.util as lib_util NetIdentity = namedtuple('NetIdentity', 'host tcp_port ssl_port nick_suffix') -class Env(lib_util.LoggedClass): +class Env(EnvBase): '''Wraps environment configuration.''' - class Error(Exception): - pass - def __init__(self): super().__init__() self.obsolete(['UTXO_MB', 'HIST_MB', 'NETWORK']) - self.allow_root = self.boolean('ALLOW_ROOT', False) self.db_dir = self.required('DB_DIRECTORY') + self.db_engine = self.default('DB_ENGINE', 'leveldb') self.daemon_url = self.required('DAEMON_URL') coin_name = self.required('COIN').strip() network = self.default('NET', 'mainnet').strip() @@ -44,7 +41,6 @@ class Env(lib_util.LoggedClass): if self.ssl_port: self.ssl_certfile = self.required('SSL_CERTFILE') self.ssl_keyfile = self.required('SSL_KEYFILE') - self.rpc_host = self.default('RPC_HOST', 'localhost') self.rpc_port = self.integer('RPC_PORT', 8000) self.max_subscriptions = self.integer('MAX_SUBSCRIPTIONS', 10000) self.banner_file = self.default('BANNER_FILE', None) @@ -60,7 +56,6 @@ class Env(lib_util.LoggedClass): self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None) # The electrum client takes the empty string as unspecified self.donation_address = self.default('DONATION_ADDRESS', '') - self.db_engine = self.default('DB_ENGINE', 'leveldb') # Server limits to help prevent DoS self.max_send = self.integer('MAX_SEND', 1000000) self.max_subs = self.integer('MAX_SUBS', 250000) @@ -68,7 +63,7 @@ class Env(lib_util.LoggedClass): self.max_session_subs = self.integer('MAX_SESSION_SUBS', 50000) self.bandwidth_limit = self.integer('BANDWIDTH_LIMIT', 2000000) self.session_timeout = self.integer('SESSION_TIMEOUT', 600) - self.loop_policy = self.event_loop_policy() + # IRC self.irc = self.boolean('IRC', False) self.irc_nick = self.default('IRC_NICK', None) @@ -80,28 +75,6 @@ class Env(lib_util.LoggedClass): for identity in (clearnet_identity, tor_identity) if identity is not None] - def default(self, envvar, default): - return environ.get(envvar, default) - - def boolean(self, envvar, default): - return bool(self.default(envvar, default)) - - def required(self, envvar): - value = environ.get(envvar) - if value is None: - raise self.Error('required envvar {} not set'.format(envvar)) - return value - - def integer(self, envvar, default): - value = environ.get(envvar) - if value is None: - return default - try: - return int(value) - except Exception: - raise self.Error('cannot convert envvar {} value {} to an integer' - .format(envvar, value)) - def sane_max_sessions(self): '''Return the maximum number of sessions to permit. Normally this is MAX_SESSIONS. However, to prevent open file exhaustion, ajdust @@ -116,12 +89,6 @@ class Env(lib_util.LoggedClass): .format(env_value, value, nofile_limit)) return value - def obsolete(self, envvars): - bad = [envvar for envvar in envvars if environ.get(envvar)] - if bad: - raise self.Error('remove obsolete environment variables {}' - .format(bad)) - def clearnet_identity(self): host = self.default('REPORT_HOST', None) if host is None: @@ -176,30 +143,3 @@ class Env(lib_util.LoggedClass): ssl_port, '_tor', ) - - def event_loop_policy(self): - policy = self.default('EVENT_LOOP_POLICY', None) - if policy is None: - return None - if policy == 'uvloop': - import uvloop - return uvloop.EventLoopPolicy() - raise self.Error('unknown event loop policy "{}"'.format(policy)) - - def cs_host(self, *, for_rpc): - '''Returns the 'host' argument to pass to asyncio's create_server - call. The result can be a single host name string, a list of - host name strings, or an empty string to bind to all interfaces. - - If rpc is True the host to use for the RPC server is returned. - Otherwise the host to use for SSL/TCP servers is returned. - ''' - host = self.rpc_host if for_rpc else self.host - result = [part.strip() for part in host.split(',')] - if len(result) == 1: - result = result[0] - # An empty result indicates all interfaces, which is not - # permitted for the RPC server. - if for_rpc and not result: - result = 'localhost' - return result diff --git a/wallet/env.py b/wallet/env.py new file mode 100644 index 0000000..825b13f --- /dev/null +++ b/wallet/env.py @@ -0,0 +1,31 @@ +# Copyright (c) 2017, Neil Booth +# +# All rights reserved. +# +# See the file "LICENCE" for information about the copyright +# and warranty status of this software. + +'''Class for handling environment configuration and defaults.''' + + +from lib.env import EnvBase + + +class Env(EnvBase): + '''Wraps environment configuration.''' + + def __init__(self): + super().__init__() + self.db_dir = self.required('DB_DIRECTORY') + self.db_engine = self.default('DB_ENGINE', 'leveldb') + + self.ssl_port = self.integer('SSL_PORT', 20214) + self.ssl_certfile = self.required('SSL_CERTFILE') + self.ssl_keyfile = self.required('SSL_KEYFILE') + + self.rpc_port = self.integer('RPC_PORT', 8001) + + self.tor_proxy_host = self.default('TOR_PROXY_HOST', 'localhost') + self.tor_proxy_port = self.integer('TOR_PROXY_PORT', None) + + self.session_timeout = self.integer('SESSION_TIMEOUT', 600)