config: implement config upgrades. remove system config.
This commit is contained in:
parent
58d889e480
commit
04a1809969
|
@ -5,13 +5,11 @@ import os
|
|||
import stat
|
||||
|
||||
from copy import deepcopy
|
||||
from .util import (user_dir, print_error, print_stderr, PrintError,
|
||||
from .util import (user_dir, print_error, PrintError,
|
||||
NoDynamicFeeEstimates)
|
||||
|
||||
from .bitcoin import MAX_FEE_RATE, FEE_TARGETS
|
||||
|
||||
SYSTEM_CONFIG_PATH = "/etc/electrum.conf"
|
||||
|
||||
config = None
|
||||
|
||||
|
||||
|
@ -25,22 +23,26 @@ def set_config(c):
|
|||
config = c
|
||||
|
||||
|
||||
FINAL_CONFIG_VERSION = 2
|
||||
|
||||
|
||||
class SimpleConfig(PrintError):
|
||||
"""
|
||||
The SimpleConfig class is responsible for handling operations involving
|
||||
configuration files.
|
||||
|
||||
There are 3 different sources of possible configuration values:
|
||||
There are two different sources of possible configuration values:
|
||||
1. Command line options.
|
||||
2. User configuration (in the user's config directory)
|
||||
3. System configuration (in /etc/)
|
||||
They are taken in order (1. overrides config options set in 2., that
|
||||
override config set in 3.)
|
||||
They are taken in order (1. overrides config options set in 2.)
|
||||
"""
|
||||
fee_rates = [5000, 10000, 20000, 30000, 50000, 70000, 100000, 150000, 200000, 300000]
|
||||
|
||||
def __init__(self, options={}, read_system_config_function=None,
|
||||
read_user_config_function=None, read_user_dir_function=None):
|
||||
def __init__(self, options=None, read_user_config_function=None,
|
||||
read_user_dir_function=None):
|
||||
|
||||
if options is None:
|
||||
options = {}
|
||||
|
||||
# This lock needs to be acquired for updating and reading the config in
|
||||
# a thread-safe way.
|
||||
|
@ -52,8 +54,6 @@ class SimpleConfig(PrintError):
|
|||
|
||||
# The following two functions are there for dependency injection when
|
||||
# testing.
|
||||
if read_system_config_function is None:
|
||||
read_system_config_function = read_system_config
|
||||
if read_user_config_function is None:
|
||||
read_user_config_function = read_user_config
|
||||
if read_user_dir_function is None:
|
||||
|
@ -63,24 +63,30 @@ class SimpleConfig(PrintError):
|
|||
|
||||
# The command line options
|
||||
self.cmdline_options = deepcopy(options)
|
||||
|
||||
# Portable wallets don't use a system config
|
||||
if self.cmdline_options.get('portable', False):
|
||||
self.system_config = {}
|
||||
else:
|
||||
self.system_config = read_system_config_function()
|
||||
# don't allow to be set on CLI:
|
||||
self.cmdline_options.pop('config_version', None)
|
||||
|
||||
# Set self.path and read the user config
|
||||
self.user_config = {} # for self.get in electrum_path()
|
||||
self.path = self.electrum_path()
|
||||
self.user_config = read_user_config_function(self.path)
|
||||
# Upgrade obsolete keys
|
||||
self.fixup_keys({'auto_cycle': 'auto_connect'})
|
||||
if not self.user_config:
|
||||
# avoid new config getting upgraded
|
||||
self.user_config = {'config_version': FINAL_CONFIG_VERSION}
|
||||
|
||||
# config "upgrade" - CLI options
|
||||
self.rename_config_keys(
|
||||
self.cmdline_options, {'auto_cycle': 'auto_connect'}, True)
|
||||
|
||||
# config upgrade - user config
|
||||
if self.requires_upgrade():
|
||||
self.upgrade()
|
||||
|
||||
# Make a singleton instance of 'self'
|
||||
set_config(self)
|
||||
|
||||
def electrum_path(self):
|
||||
# Read electrum_path from command line / system configuration
|
||||
# Read electrum_path from command line
|
||||
# Otherwise use the user's default data directory.
|
||||
path = self.get('electrum_path')
|
||||
if path is None:
|
||||
|
@ -102,45 +108,92 @@ class SimpleConfig(PrintError):
|
|||
self.print_error("electrum directory", path)
|
||||
return path
|
||||
|
||||
def fixup_config_keys(self, config, keypairs):
|
||||
def rename_config_keys(self, config, keypairs, deprecation_warning=False):
|
||||
"""Migrate old key names to new ones"""
|
||||
updated = False
|
||||
for old_key, new_key in keypairs.items():
|
||||
if old_key in config:
|
||||
if not new_key in config:
|
||||
if new_key not in config:
|
||||
config[new_key] = config[old_key]
|
||||
if deprecation_warning:
|
||||
self.print_stderr('Note that the {} variable has been deprecated. '
|
||||
'You should use {} instead.'.format(old_key, new_key))
|
||||
del config[old_key]
|
||||
updated = True
|
||||
return updated
|
||||
|
||||
def fixup_keys(self, keypairs):
|
||||
'''Migrate old key names to new ones'''
|
||||
self.fixup_config_keys(self.cmdline_options, keypairs)
|
||||
self.fixup_config_keys(self.system_config, keypairs)
|
||||
if self.fixup_config_keys(self.user_config, keypairs):
|
||||
self.save_user_config()
|
||||
|
||||
def set_key(self, key, value, save = True):
|
||||
def set_key(self, key, value, save=True):
|
||||
if not self.is_modifiable(key):
|
||||
print_stderr("Warning: not changing config key '%s' set on the command line" % key)
|
||||
self.print_stderr("Warning: not changing config key '%s' set on the command line" % key)
|
||||
return
|
||||
self._set_key_in_user_config(key, value, save)
|
||||
|
||||
def _set_key_in_user_config(self, key, value, save=True):
|
||||
with self.lock:
|
||||
if value is not None:
|
||||
self.user_config[key] = value
|
||||
else:
|
||||
self.user_config.pop(key, None)
|
||||
if save:
|
||||
self.save_user_config()
|
||||
return
|
||||
|
||||
def get(self, key, default=None):
|
||||
with self.lock:
|
||||
out = self.cmdline_options.get(key)
|
||||
if out is None:
|
||||
out = self.user_config.get(key)
|
||||
if out is None:
|
||||
out = self.system_config.get(key, default)
|
||||
out = self.user_config.get(key, default)
|
||||
return out
|
||||
|
||||
def requires_upgrade(self):
|
||||
return self.get_config_version() < FINAL_CONFIG_VERSION
|
||||
|
||||
def upgrade(self):
|
||||
with self.lock:
|
||||
self.print_error('upgrading config')
|
||||
|
||||
self.convert_version_2()
|
||||
|
||||
self.set_key('config_version', FINAL_CONFIG_VERSION, save=True)
|
||||
|
||||
def convert_version_2(self):
|
||||
if not self._is_upgrade_method_needed(1, 1):
|
||||
return
|
||||
|
||||
self.rename_config_keys(self.user_config, {'auto_cycle': 'auto_connect'})
|
||||
|
||||
try:
|
||||
# migrate server string FROM host:port:proto TO host:port
|
||||
server_str = self.user_config.get('server')
|
||||
host, port, protocol = str(server_str).rsplit(':', 2)
|
||||
assert protocol in ('s', 't')
|
||||
int(port) # Throw if cannot be converted to int
|
||||
server_str = str('{}:{}'.format(host, port))
|
||||
self._set_key_in_user_config('server', server_str)
|
||||
except BaseException:
|
||||
self._set_key_in_user_config('server', None)
|
||||
|
||||
self.set_key('config_version', 2)
|
||||
|
||||
def _is_upgrade_method_needed(self, min_version, max_version):
|
||||
cur_version = self.get_config_version()
|
||||
if cur_version > max_version:
|
||||
return False
|
||||
elif cur_version < min_version:
|
||||
raise BaseException(
|
||||
('config upgrade: unexpected version %d (should be %d-%d)'
|
||||
% (cur_version, min_version, max_version)))
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_config_version(self):
|
||||
config_version = self.get('config_version', 1)
|
||||
if config_version > FINAL_CONFIG_VERSION:
|
||||
self.print_stderr('WARNING: config version ({}) is higher than ours ({})'
|
||||
.format(config_version, FINAL_CONFIG_VERSION))
|
||||
return config_version
|
||||
|
||||
def is_modifiable(self, key):
|
||||
return not key in self.cmdline_options
|
||||
return key not in self.cmdline_options
|
||||
|
||||
def save_user_config(self):
|
||||
if not self.path:
|
||||
|
@ -298,21 +351,6 @@ class SimpleConfig(PrintError):
|
|||
return device
|
||||
|
||||
|
||||
def read_system_config(path=SYSTEM_CONFIG_PATH):
|
||||
"""Parse and return the system config settings in /etc/electrum.conf."""
|
||||
result = {}
|
||||
if os.path.exists(path):
|
||||
import configparser
|
||||
p = configparser.ConfigParser()
|
||||
try:
|
||||
p.read(path)
|
||||
for k, v in p.items('client'):
|
||||
result[k] = v
|
||||
except (configparser.NoSectionError, configparser.MissingSectionHeaderError):
|
||||
pass
|
||||
|
||||
return result
|
||||
|
||||
def read_user_config(path):
|
||||
"""Parse and store the user config settings in electrum.conf into user_config[]."""
|
||||
if not path:
|
||||
|
|
Loading…
Reference in New Issue