Teach tub to start synchronously even with ip autodetect

* remove when_tub_ready() from all code
* synchronous-ify all node/client/introducer startup code

refs ticket:2491
This commit is contained in:
Brian Warner
2016-04-26 21:54:45 -07:00
parent ffc2f48cfe
commit 96c625920c
9 changed files with 84 additions and 160 deletions

View File

@ -131,6 +131,8 @@ class Client(node.Node, pollmixin.PollMixin):
def __init__(self, basedir="."): def __init__(self, basedir="."):
node.Node.__init__(self, basedir) node.Node.__init__(self, basedir)
# All tub.registerReference must happen *after* we upcall, since
# that's what does tub.setLocation()
self.started_timestamp = time.time() self.started_timestamp = time.time()
self.logSource="Client" self.logSource="Client"
self.encoding_params = self.DEFAULT_ENCODING_PARAMETERS.copy() self.encoding_params = self.DEFAULT_ENCODING_PARAMETERS.copy()
@ -188,15 +190,7 @@ class Client(node.Node, pollmixin.PollMixin):
self.get_app_versions(), self.get_app_versions(),
self._sequencer) self._sequencer)
self.introducer_client = ic self.introducer_client = ic
# hold off on starting the IntroducerClient until our tub has been
# started, so we'll have a useful address on our RemoteReference, so
# that the introducer's status page will show us.
d = self.when_tub_ready()
def _start_introducer_client(res):
ic.setServiceParent(self) ic.setServiceParent(self)
d.addCallback(_start_introducer_client)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="URyI5w")
def init_stats_provider(self): def init_stats_provider(self):
gatherer_furl = self.get_config("client", "stats_gatherer.furl", None) gatherer_furl = self.get_config("client", "stats_gatherer.furl", None)
@ -309,18 +303,12 @@ class Client(node.Node, pollmixin.PollMixin):
expiration_sharetypes=expiration_sharetypes) expiration_sharetypes=expiration_sharetypes)
self.add_service(ss) self.add_service(ss)
d = self.when_tub_ready()
# we can't do registerReference until the Tub is ready
def _publish(res):
furl_file = os.path.join(self.basedir, "private", "storage.furl").encode(get_filesystem_encoding()) furl_file = os.path.join(self.basedir, "private", "storage.furl").encode(get_filesystem_encoding())
furl = self.tub.registerReference(ss, furlFile=furl_file) furl = self.tub.registerReference(ss, furlFile=furl_file)
ann = {"anonymous-storage-FURL": furl, ann = {"anonymous-storage-FURL": furl,
"permutation-seed-base32": self._init_permutation_seed(ss), "permutation-seed-base32": self._init_permutation_seed(ss),
} }
self.introducer_client.publish("storage", ann, self._node_key) self.introducer_client.publish("storage", ann, self._node_key)
d.addCallback(_publish)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="aLGBKw")
def init_client(self): def init_client(self):
helper_furl = self.get_config("client", "helper.furl", None) helper_furl = self.get_config("client", "helper.furl", None)
@ -433,41 +421,26 @@ class Client(node.Node, pollmixin.PollMixin):
return self.history return self.history
def init_control(self): def init_control(self):
d = self.when_tub_ready()
def _publish(res):
c = ControlServer() c = ControlServer()
c.setServiceParent(self) c.setServiceParent(self)
control_url = self.tub.registerReference(c) control_url = self.tub.registerReference(c)
self.write_private_config("control.furl", control_url + "\n") self.write_private_config("control.furl", control_url + "\n")
d.addCallback(_publish)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="d3tNXA")
def init_helper(self): def init_helper(self):
d = self.when_tub_ready()
def _publish(self):
self.helper = Helper(os.path.join(self.basedir, "helper"), self.helper = Helper(os.path.join(self.basedir, "helper"),
self.storage_broker, self._secret_holder, self.storage_broker, self._secret_holder,
self.stats_provider, self.history) self.stats_provider, self.history)
# TODO: this is confusing. BASEDIR/private/helper.furl is created # TODO: this is confusing. BASEDIR/private/helper.furl is created by
# by the helper. BASEDIR/helper.furl is consumed by the client # the helper. BASEDIR/helper.furl is consumed by the client who wants
# who wants to use the helper. I like having the filename be the # to use the helper. I like having the filename be the same, since
# same, since that makes 'cp' work smoothly, but the difference # that makes 'cp' work smoothly, but the difference between config
# between config inputs and generated outputs is hard to see. # inputs and generated outputs is hard to see.
helper_furlfile = os.path.join(self.basedir, helper_furlfile = os.path.join(self.basedir,
"private", "helper.furl").encode(get_filesystem_encoding()) "private", "helper.furl").encode(get_filesystem_encoding())
self.tub.registerReference(self.helper, furlFile=helper_furlfile) self.tub.registerReference(self.helper, furlFile=helper_furlfile)
d.addCallback(_publish)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="K0mW5w")
def init_key_gen(self, key_gen_furl): def init_key_gen(self, key_gen_furl):
d = self.when_tub_ready()
def _subscribe(self):
self.tub.connectTo(key_gen_furl, self._got_key_generator) self.tub.connectTo(key_gen_furl, self._got_key_generator)
d.addCallback(_subscribe)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="z9DMzw")
def _got_key_generator(self, key_generator): def _got_key_generator(self, key_generator):
self._key_generator.set_remote_generator(key_generator) self._key_generator.set_remote_generator(key_generator)

View File

@ -48,15 +48,10 @@ class IntroducerNode(node.Node):
""" """
raise FurlFileConflictError(textwrap.dedent(msg)) raise FurlFileConflictError(textwrap.dedent(msg))
os.rename(old_public_fn, private_fn) os.rename(old_public_fn, private_fn)
d = self.when_tub_ready()
def _publish(res):
furl = self.tub.registerReference(introducerservice, furl = self.tub.registerReference(introducerservice,
furlFile=private_fn) furlFile=private_fn)
self.log(" introducer is at %s" % furl, umid="qF2L9A") self.log(" introducer is at %s" % furl, umid="qF2L9A")
self.introducer_url = furl # for tests self.introducer_url = furl # for tests
d.addCallback(_publish)
d.addErrback(log.err, facility="tahoe.init",
level=log.BAD, umid="UaNs9A")
def init_web(self, webport): def init_web(self, webport):
self.log("init_web(webport=%s)", args=(webport,), umid="2bUygA") self.log("init_web(webport=%s)", args=(webport,), umid="2bUygA")

View File

@ -3,13 +3,12 @@ from base64 import b32decode, b32encode
from twisted.python import log as twlog from twisted.python import log as twlog
from twisted.application import service from twisted.application import service
from twisted.internet import reactor from foolscap.api import Tub, app_versions
from foolscap.api import Tub, eventually, app_versions
import foolscap.logging.log import foolscap.logging.log
from allmydata import get_package_versions, get_package_versions_string from allmydata import get_package_versions, get_package_versions_string
from allmydata.util import log from allmydata.util import log
from allmydata.util import fileutil, iputil, observer from allmydata.util import fileutil, iputil
from allmydata.util.assertutil import precondition, _assert from allmydata.util.assertutil import _assert
from allmydata.util.fileutil import abspath_expanduser_unicode from allmydata.util.fileutil import abspath_expanduser_unicode
from allmydata.util.encodingutil import get_filesystem_encoding, quote_output from allmydata.util.encodingutil import get_filesystem_encoding, quote_output
from allmydata.util import configutil from allmydata.util import configutil
@ -74,7 +73,6 @@ class Node(service.MultiService):
service.MultiService.__init__(self) service.MultiService.__init__(self)
self.basedir = abspath_expanduser_unicode(unicode(basedir)) self.basedir = abspath_expanduser_unicode(unicode(basedir))
self._portnumfile = os.path.join(self.basedir, self.PORTNUMFILE) self._portnumfile = os.path.join(self.basedir, self.PORTNUMFILE)
self._tub_ready_observerlist = observer.OneShotObserverList()
fileutil.make_dirs(os.path.join(self.basedir, "private"), 0700) fileutil.make_dirs(os.path.join(self.basedir, "private"), 0700)
open(os.path.join(self.basedir, "private", "README"), "w").write(PRIV_README) open(os.path.join(self.basedir, "private", "README"), "w").write(PRIV_README)
@ -228,6 +226,7 @@ class Node(service.MultiService):
self.tub.setLocation(location) self.tub.setLocation(location)
self.log("Tub location set to %s" % (location,)) self.log("Tub location set to %s" % (location,))
# the Tub is now ready for tub.registerReference()
self.tub.setServiceParent(self) self.tub.setServiceParent(self)
def get_app_versions(self): def get_app_versions(self):
@ -321,25 +320,13 @@ class Node(service.MultiService):
os.chmod("twistd.pid", 0644) os.chmod("twistd.pid", 0644)
except EnvironmentError: except EnvironmentError:
pass pass
# Delay until the reactor is running.
eventually(self._startService)
def _startService(self):
precondition(reactor.running)
self.log("Node._startService")
service.MultiService.startService(self) service.MultiService.startService(self)
self.log("%s running" % self.NODETYPE) self.log("%s running" % self.NODETYPE)
self._tub_ready_observerlist.fire(self)
def stopService(self): def stopService(self):
self.log("Node.stopService") self.log("Node.stopService")
d = self._tub_ready_observerlist.when_fired()
def _really_stopService(ignored):
self.log("Node._really_stopService")
return service.MultiService.stopService(self) return service.MultiService.stopService(self)
d.addCallback(_really_stopService)
return d
def shutdown(self): def shutdown(self):
"""Shut down the node. Returns a Deferred that fires (with None) when """Shut down the node. Returns a Deferred that fires (with None) when
@ -375,9 +362,6 @@ class Node(service.MultiService):
def log(self, *args, **kwargs): def log(self, *args, **kwargs):
return log.msg(*args, **kwargs) return log.msg(*args, **kwargs)
def when_tub_ready(self):
return self._tub_ready_observerlist.when_fired()
def add_service(self, s): def add_service(self, s):
s.setServiceParent(self) s.setServiceParent(self)
return s return s

View File

@ -148,12 +148,9 @@ class StatsProvider(Referenceable, service.MultiService):
def startService(self): def startService(self):
if self.node and self.gatherer_furl: if self.node and self.gatherer_furl:
d = self.node.when_tub_ready()
def connect(junk):
nickname_utf8 = self.node.nickname.encode("utf-8") nickname_utf8 = self.node.nickname.encode("utf-8")
self.node.tub.connectTo(self.gatherer_furl, self.node.tub.connectTo(self.gatherer_furl,
self._connected, nickname_utf8) self._connected, nickname_utf8)
d.addCallback(connect)
service.MultiService.startService(self) service.MultiService.startService(self)
def count(self, name, delta=1): def count(self, name, delta=1):

View File

@ -116,10 +116,8 @@ class SystemFramework(pollmixin.PollMixin):
#print "STARTING" #print "STARTING"
self.stats = {} self.stats = {}
self.statsfile = open(os.path.join(self.basedir, "stats.out"), "a") self.statsfile = open(os.path.join(self.basedir, "stats.out"), "a")
d = self.make_introducer() self.make_introducer()
def _more(res): d = self.start_client()
return self.start_client()
d.addCallback(_more)
def _record_control_furl(control_furl): def _record_control_furl(control_furl):
self.control_furl = control_furl self.control_furl = control_furl
#print "OBTAINING '%s'" % (control_furl,) #print "OBTAINING '%s'" % (control_furl,)
@ -174,12 +172,7 @@ class SystemFramework(pollmixin.PollMixin):
os.mkdir(iv_basedir) os.mkdir(iv_basedir)
iv = introducer.IntroducerNode(basedir=iv_basedir) iv = introducer.IntroducerNode(basedir=iv_basedir)
self.introducer = self.add_service(iv) self.introducer = self.add_service(iv)
d = self.introducer.when_tub_ready() self.introducer_furl = self.introducer.introducer_url
def _introducer_ready(res):
q = self.introducer
self.introducer_furl = q.introducer_url
d.addCallback(_introducer_ready)
return d
def make_nodes(self): def make_nodes(self):
self.nodes = [] self.nodes = []

View File

@ -481,8 +481,8 @@ class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
f.close() f.close()
iv = IntroducerNode(basedir=iv_dir) iv = IntroducerNode(basedir=iv_dir)
self.introducer = self.add_service(iv) self.introducer = self.add_service(iv)
d = self.introducer.when_tub_ready() self._get_introducer_web()
d.addCallback(self._get_introducer_web) d = defer.succeed(None)
if use_stats_gatherer: if use_stats_gatherer:
d.addCallback(self._set_up_stats_gatherer) d.addCallback(self._set_up_stats_gatherer)
if use_key_generator: if use_key_generator:
@ -492,7 +492,7 @@ class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
d.addCallback(self._grab_stats) d.addCallback(self._grab_stats)
return d return d
def _get_introducer_web(self, res): def _get_introducer_web(self):
f = open(os.path.join(self.getdir("introducer"), "node.url"), "r") f = open(os.path.join(self.getdir("introducer"), "node.url"), "r")
self.introweb_url = f.read().strip() self.introweb_url = f.read().strip()
f.close() f.close()
@ -591,8 +591,7 @@ class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
c = self.add_service(client.Client(basedir=basedirs[0])) c = self.add_service(client.Client(basedir=basedirs[0]))
self.clients.append(c) self.clients.append(c)
c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE) c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
d = c.when_tub_ready()
def _ready(res):
f = open(os.path.join(basedirs[0],"private","helper.furl"), "r") f = open(os.path.join(basedirs[0],"private","helper.furl"), "r")
helper_furl = f.read() helper_furl = f.read()
f.close() f.close()
@ -610,8 +609,7 @@ class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
self.clients.append(c) self.clients.append(c)
c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE) c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
log.msg("STARTING") log.msg("STARTING")
return self.wait_for_connections() d = self.wait_for_connections()
d.addCallback(_ready)
def _connected(res): def _connected(res):
log.msg("CONNECTED") log.msg("CONNECTED")
# now find out where the web port was # now find out where the web port was
@ -643,7 +641,6 @@ class SystemTestMixin(pollmixin.PollMixin, testutil.StallMixin):
self.clients[num] = new_c self.clients[num] = new_c
new_c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE) new_c.set_default_mutable_keysize(TEST_RSA_KEY_SIZE)
self.add_service(new_c) self.add_service(new_c)
return new_c.when_tub_ready()
d.addCallback(_stopped) d.addCallback(_stopped)
d.addCallback(lambda res: self.wait_for_connections()) d.addCallback(lambda res: self.wait_for_connections())
def _maybe_get_webport(res): def _maybe_get_webport(res):

View File

@ -184,8 +184,6 @@ class NoNetworkClient(Client):
service.MultiService.startService(self) service.MultiService.startService(self)
def stopService(self): def stopService(self):
service.MultiService.stopService(self) service.MultiService.stopService(self)
def when_tub_ready(self):
raise NotImplementedError("NoNetworkClient has no Tub")
def init_control(self): def init_control(self):
pass pass
def init_helper(self): def init_helper(self):

View File

@ -34,13 +34,9 @@ class Node(testutil.SignalMixin, testutil.ReallyEqualMixin, unittest.TestCase):
os.mkdir(basedir) os.mkdir(basedir)
public_fn = os.path.join(basedir, "introducer.furl") public_fn = os.path.join(basedir, "introducer.furl")
private_fn = os.path.join(basedir, "private", "introducer.furl") private_fn = os.path.join(basedir, "private", "introducer.furl")
q1 = IntroducerNode(basedir) q1 = IntroducerNode(basedir)
d = fireEventually(None) del q1
d.addCallback(lambda res: q1.startService())
d.addCallback(lambda res: q1.when_tub_ready())
d.addCallback(lambda res: q1.stopService())
d.addCallback(flushEventualQueue)
def _check_furl(res):
# new nodes create unguessable furls in private/introducer.furl # new nodes create unguessable furls in private/introducer.furl
ifurl = fileutil.read(private_fn) ifurl = fileutil.read(private_fn)
self.failUnless(ifurl) self.failUnless(ifurl)
@ -58,21 +54,13 @@ class Node(testutil.SignalMixin, testutil.ReallyEqualMixin, unittest.TestCase):
# when we see only the public one, move it to private/ and use # when we see only the public one, move it to private/ and use
# the existing furl instead of creating a new one # the existing furl instead of creating a new one
os.unlink(private_fn) os.unlink(private_fn)
q2 = IntroducerNode(basedir) q2 = IntroducerNode(basedir)
d2 = fireEventually(None) del q2
d2.addCallback(lambda res: q2.startService())
d2.addCallback(lambda res: q2.when_tub_ready())
d2.addCallback(lambda res: q2.stopService())
d2.addCallback(flushEventualQueue)
def _check_furl2(res):
self.failIf(os.path.exists(public_fn)) self.failIf(os.path.exists(public_fn))
ifurl2 = fileutil.read(private_fn) ifurl2 = fileutil.read(private_fn)
self.failUnless(ifurl2) self.failUnless(ifurl2)
self.failUnlessEqual(ifurl2.strip(), guessable) self.failUnlessEqual(ifurl2.strip(), guessable)
d2.addCallback(_check_furl2)
return d2
d.addCallback(_check_furl)
return d
def test_web_static(self): def test_web_static(self):
basedir = u"introducer.Node.test_web_static" basedir = u"introducer.Node.test_web_static"

View File

@ -4465,7 +4465,6 @@ class IntroducerWeb(unittest.TestCase):
d = fireEventually(None) d = fireEventually(None)
d.addCallback(lambda ign: self.node.startService()) d.addCallback(lambda ign: self.node.startService())
d.addCallback(lambda ign: self.node.when_tub_ready())
d.addCallback(lambda ign: self.GET("/")) d.addCallback(lambda ign: self.GET("/"))
def _check(res): def _check(res):