Check for unknown config options

The list of valid sections + config-items came from
grep'ing the source for `.get_config`
This commit is contained in:
meejah 2016-09-05 16:34:17 -06:00
parent 00ea41ef2c
commit 2732c37941
5 changed files with 176 additions and 7 deletions

View File

@ -17,7 +17,7 @@ from allmydata.immutable.offloaded import Helper
from allmydata.control import ControlServer
from allmydata.introducer.client import IntroducerClient
from allmydata.util import (hashutil, base32, pollmixin, log, keyutil, idlib,
yamlutil)
yamlutil, configutil)
from allmydata.util.encodingutil import (get_filesystem_encoding,
from_utf8_or_none)
from allmydata.util.fileutil import abspath_expanduser_unicode
@ -28,7 +28,7 @@ from allmydata.history import History
from allmydata.interfaces import IStatsProducer, SDMF_VERSION, MDMF_VERSION
from allmydata.nodemaker import NodeMaker
from allmydata.blacklist import Blacklist
from allmydata.node import OldConfigOptionError
from allmydata.node import OldConfigOptionError, _common_config_sections
KiB=1024
@ -37,6 +37,62 @@ GiB=1024*MiB
TiB=1024*GiB
PiB=1024*TiB
def _valid_config_sections():
cfg = _common_config_sections()
cfg.update({
"client": (
"helper.furl",
"introducer.furl",
"key_generator.furl",
"mutable.format",
"peers.preferred",
"shares.happy",
"shares.needed",
"shares.total",
"stats_gatherer.furl",
),
"drop_upload": ( # deprecated already?
"enabled",
),
"ftpd": (
"accounts.file",
"accounts.url",
"enabled",
"port",
),
"storage": (
"debug_discard",
"enabled",
"expire.cutoff_date",
"expire.enabled",
"expire.immutable",
"expire.mode",
"expire.mode",
"expire.mutable",
"expire.override_lease_duration",
"readonly",
"reserved_space",
),
"sftpd": (
"accounts.file",
"accounts.url",
"enabled",
"host_privkey_file",
"host_pubkey_file",
"port",
),
"helper": (
"enabled",
),
"magic_folder": (
"download.umask",
"enabled",
"local.directory",
),
})
return cfg
def _make_secret():
return base32.b2a(os.urandom(hashutil.CRYPTO_VAL_SIZE)) + "\n"
@ -123,6 +179,7 @@ class Client(node.Node, pollmixin.PollMixin):
node.Node.__init__(self, basedir)
# All tub.registerReference must happen *after* we upcall, since
# that's what does tub.setLocation()
configutil.validate_config(self.config_fname, self.config, _valid_config_sections())
self._magic_folder = None
self.started_timestamp = time.time()
self.logSource="Client"

View File

@ -5,13 +5,17 @@ from twisted.application import service
from foolscap.api import Referenceable
import allmydata
from allmydata import node
from allmydata.util import log, rrefutil
from allmydata.util import log, rrefutil, configutil
from allmydata.util.fileutil import abspath_expanduser_unicode
from allmydata.introducer.interfaces import \
RIIntroducerPublisherAndSubscriberService_v2
from allmydata.introducer.common import unsign_from_foolscap, \
SubscriberDescriptor, AnnouncementDescriptor
def _valid_config_sections():
return node._common_config_sections()
class FurlFileConflictError(Exception):
pass
@ -22,7 +26,7 @@ class IntroducerNode(node.Node):
def __init__(self, basedir=u"."):
node.Node.__init__(self, basedir)
self.read_config()
configutil.validate_config(self.config_fname, self.config, _valid_config_sections())
self.init_introducer()
webport = self.get_config("node", "web.port", None)
if webport:

View File

@ -29,6 +29,39 @@ def _import_i2p():
except ImportError: # pragma: no cover
return None
def _common_config_sections():
return {
"connections": (
"tcp",
),
"node": (
"log_gatherer.furl",
"nickname",
"reveal-ip-address",
"tempdir",
"timeout.disconnect",
"timeout.keepalive",
"tub.location",
"tub.port",
"web.port",
"web.static",
),
"i2p": (
"enabled",
"i2p.configdir",
"i2p.executable",
"launch",
"sam.port",
),
"tor": (
"control.port",
"enabled",
"launch",
"socks.port",
"tor.executable",
),
}
# Add our application versions to the data that Foolscap's LogPublisher
# reports.
for thing, things_version in get_package_versions().iteritems():
@ -91,6 +124,7 @@ class Node(service.MultiService):
def __init__(self, basedir=u"."):
service.MultiService.__init__(self)
self.basedir = abspath_expanduser_unicode(unicode(basedir))
self.config_fname = os.path.join(self.basedir, "tahoe.cfg")
self._portnumfile = os.path.join(self.basedir, self.PORTNUMFILE)
fileutil.make_dirs(os.path.join(self.basedir, "private"), 0700)
open(os.path.join(self.basedir, "private", "README"), "w").write(PRIV_README)
@ -159,11 +193,10 @@ class Node(service.MultiService):
self.error_about_old_config_files()
self.config = ConfigParser.SafeConfigParser()
tahoe_cfg = os.path.join(self.basedir, "tahoe.cfg")
try:
self.config = configutil.get_config(tahoe_cfg)
self.config = configutil.get_config(self.config_fname)
except EnvironmentError:
if os.path.exists(tahoe_cfg):
if os.path.exists(self.config_fname):
raise
def error_about_old_config_files(self):

View File

@ -32,3 +32,45 @@ class ConfigUtilTests(CLITestMixin, GridTestMixin, unittest.TestCase):
config = configutil.get_config(tahoe_cfg)
self.failUnlessEqual(config.get("node", "descriptor"), descriptor)
def test_config_validation_success(self):
d = self.mktemp()
os.mkdir(d)
fname = os.path.join(d, 'tahoe.cfg')
with open(fname, 'w') as f:
f.write('[node]\nvalid = foo\n')
config = configutil.get_config(fname)
# should succeed, no exceptions
configutil.validate_config(fname, config, dict(node=['valid']))
def test_config_validation_invalid_item(self):
d = self.mktemp()
os.mkdir(d)
fname = os.path.join(d, 'tahoe.cfg')
with open(fname, 'w') as f:
f.write('[node]\nvalid = foo\ninvalid = foo\n')
config = configutil.get_config(fname)
self.assertRaises(
configutil.UnknownConfigError,
configutil.validate_config,
fname, config, dict(node=['valid']),
)
def test_config_validation_invalid_section(self):
d = self.mktemp()
os.mkdir(d)
fname = os.path.join(d, 'tahoe.cfg')
with open(fname, 'w') as f:
f.write('[node]\nvalid = foo\n[invalid]\n')
config = configutil.get_config(fname)
self.assertRaises(
configutil.UnknownConfigError,
configutil.validate_config,
fname, config, dict(node=['valid']),
)

View File

@ -2,6 +2,14 @@
from ConfigParser import SafeConfigParser
class UnknownConfigError(Exception):
"""
An unknown config item was found.
This is possibly raised by validate_config()
"""
def get_config(tahoe_cfg):
config = SafeConfigParser()
f = open(tahoe_cfg, "rb")
@ -27,3 +35,28 @@ def write_config(tahoe_cfg, config):
config.write(f)
finally:
f.close()
def validate_config(fname, cfg, valid_sections):
"""
raises UnknownConfigError if there are any unknown sections or config
values.
"""
for section in cfg.sections():
try:
valid_in_section = valid_sections[section]
except KeyError:
raise UnknownConfigError(
"'{fname}' contains unknown section [{section}]".format(
fname=fname,
section=section,
)
)
for option in cfg.options(section):
if option not in valid_in_section:
raise UnknownConfigError(
"'{fname}' section [{section}] contains unknown option '{option}'".format(
fname=fname,
section=section,
option=option,
)
)