From 78d19c271c94c8cdfec3fb8807fee4c14a9d9bcd Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Thu, 8 Mar 2007 15:10:36 -0700
Subject: [PATCH] rearrange service startup a bit, now Node.startService()
 returns a Deferred that fires when the tub is actually ready, and there is
 also a Node.when_tub_ready() hook. This allows get_local_addresses() to be
 slow and not break everything. Changed all necessary test cases to accomodate
 this slow startup.

---
 src/allmydata/client.py            |  1 +
 src/allmydata/node.py              | 50 +++++++++++++++++++++++-------
 src/allmydata/test/test_client.py  |  5 +--
 src/allmydata/test/test_queen.py   |  6 ++--
 src/allmydata/test/test_storage.py |  3 +-
 src/allmydata/test/test_system.py  | 10 ++++--
 6 files changed, 57 insertions(+), 18 deletions(-)

diff --git a/src/allmydata/client.py b/src/allmydata/client.py
index 53956915..f0b3927b 100644
--- a/src/allmydata/client.py
+++ b/src/allmydata/client.py
@@ -29,6 +29,7 @@ class Client(node.Node, Referenceable):
     def __init__(self, basedir="."):
         node.Node.__init__(self, basedir)
         self.queen = None # self.queen is either None or a RemoteReference
+        self.my_pburl = None
         self.all_peers = set()
         self.peer_pburls = {}
         self.connections = {}
diff --git a/src/allmydata/node.py b/src/allmydata/node.py
index db45ac93..da656a19 100644
--- a/src/allmydata/node.py
+++ b/src/allmydata/node.py
@@ -1,10 +1,11 @@
 
-from twisted.application import service
 import os.path
+from twisted.python import log
+from twisted.application import service
+from twisted.internet import defer
 from foolscap import Tub
 from allmydata.util.iputil import get_local_addresses
-from allmydata.util import idlib
-from twisted.python import log
+from allmydata.util import idlib, observer
 
 class Node(service.MultiService):
     # this implements common functionality of both Client nodes and the Queen
@@ -18,6 +19,7 @@ class Node(service.MultiService):
     def __init__(self, basedir="."):
         service.MultiService.__init__(self)
         self.basedir = os.path.abspath(basedir)
+        self._tub_ready_observerlist = observer.OneShotObserverList()
         assert self.CERTFILE, "Your node.Node subclass must provide CERTFILE"
         certfile = os.path.join(self.basedir, self.CERTFILE)
         if os.path.exists(certfile):
@@ -55,6 +57,37 @@ class Node(service.MultiService):
                 m.setServiceParent(self)
                 self.log("AuthorizedKeysManhole listening on %d" % portnum)
 
+    def startService(self):
+        """Start the node. Returns a Deferred that fires (with self) when it
+        is ready to go.
+
+        Many callers don't pay attention to the return value from
+        startService, since they aren't going to do anything special when it
+        finishes. If they are (for example unit tests which need to wait for
+        the node to fully start up before it gets shut down), they can wait
+        for the Deferred I return to fire. In particular, you should wait for
+        my startService() Deferred to fire before you call my stopService()
+        method.
+        """
+
+        # note: this class can only be started and stopped once.
+        service.MultiService.startService(self)
+        d = defer.succeed(None)
+        d.addCallback(lambda res: get_local_addresses())
+        d.addCallback(self._setup_tub)
+        d.addCallback(lambda res: self.tub_ready())
+        def _ready(res):
+            self.log("%s running" % self.NODETYPE)
+            self._tub_ready_observerlist.fire(self)
+            return self
+        d.addCallback(_ready)
+        return d
+
+    def shutdown(self):
+        """Shut down the node. Returns a Deferred that fires (with None) when
+        it finally stops kicking."""
+        return self.stopService()
+
     def log(self, msg):
         log.msg(self.short_nodeid + ": " + msg)
 
@@ -86,15 +119,10 @@ class Node(service.MultiService):
         # called when the Tub is available for registerReference
         pass
 
+    def when_tub_ready(self):
+        return self._tub_ready_observerlist.when_fired()
+
     def add_service(self, s):
         s.setServiceParent(self)
         return s
 
-    def startService(self):
-        # note: this class can only be started and stopped once.
-        service.MultiService.startService(self)
-        local_addresses = get_local_addresses()
-        self._setup_tub(local_addresses)
-        self.tub_ready()
-        self.log("%s running" % self.NODETYPE)
-
diff --git a/src/allmydata/test/test_client.py b/src/allmydata/test/test_client.py
index 5cd01e06..eee9dbc9 100644
--- a/src/allmydata/test/test_client.py
+++ b/src/allmydata/test/test_client.py
@@ -6,8 +6,9 @@ from allmydata import client
 class Basic(unittest.TestCase):
     def test_loadable(self):
         c = client.Client("")
-        c.startService()
-        return c.stopService()
+        d = c.startService()
+        d.addCallback(lambda res: c.stopService())
+        return d
 
     def test_permute(self):
         c = client.Client("")
diff --git a/src/allmydata/test/test_queen.py b/src/allmydata/test/test_queen.py
index 7e12917a..0a242c4c 100644
--- a/src/allmydata/test/test_queen.py
+++ b/src/allmydata/test/test_queen.py
@@ -6,5 +6,7 @@ from allmydata import queen
 class Basic(unittest.TestCase):
     def test_loadable(self):
         q = queen.Queen()
-        q.startService()
-        return q.stopService()
+        d = q.startService()
+        d.addCallback(lambda res: q.stopService())
+        return d
+
diff --git a/src/allmydata/test/test_storage.py b/src/allmydata/test/test_storage.py
index dcc4035e..31da06ab 100644
--- a/src/allmydata/test/test_storage.py
+++ b/src/allmydata/test/test_storage.py
@@ -21,7 +21,8 @@ class StorageTest(unittest.TestCase):
         self.node.setServiceParent(self.svc)
         self.tub = Tub()
         self.tub.setServiceParent(self.svc)
-        return self.svc.startService()
+        self.svc.startService()
+        return self.node.when_tub_ready()
 
     def test_create_bucket(self):
         """
diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py
index 3ebc49f1..0ec50774 100644
--- a/src/allmydata/test/test_system.py
+++ b/src/allmydata/test/test_system.py
@@ -40,10 +40,16 @@ class SystemTest(unittest.TestCase):
         self.numclients = NUMCLIENTS
         if not os.path.isdir("queen"):
             os.mkdir("queen")
-        q = self.queen = self.add_service(queen.Queen(basedir="queen"))
+        self.queen = self.add_service(queen.Queen(basedir="queen"))
+        d = self.queen.when_tub_ready()
+        d.addCallback(self._set_up_nodes_2)
+        return d
+
+    def _set_up_nodes_2(self, res):
+        q = self.queen
         self.queen_pburl = q.urls["roster"]
         self.clients = []
-        for i in range(NUMCLIENTS):
+        for i in range(self.numclients):
             basedir = "client%d" % i
             if not os.path.isdir(basedir):
                 os.mkdir(basedir)
-- 
2.45.2