services/sync/tests/unit/test_node_reassignment.js

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 _("Test that node reassignment responses are respected on all kinds of " +
michael@0 5 "requests.");
michael@0 6
michael@0 7 Cu.import("resource://gre/modules/Log.jsm");
michael@0 8 Cu.import("resource://services-common/rest.js");
michael@0 9 Cu.import("resource://services-sync/constants.js");
michael@0 10 Cu.import("resource://services-sync/service.js");
michael@0 11 Cu.import("resource://services-sync/status.js");
michael@0 12 Cu.import("resource://services-sync/util.js");
michael@0 13 Cu.import("resource://testing-common/services/sync/rotaryengine.js");
michael@0 14 Cu.import("resource://testing-common/services/sync/utils.js");
michael@0 15
michael@0 16 Service.engineManager.clear();
michael@0 17
michael@0 18 function run_test() {
michael@0 19 Log.repository.getLogger("Sync.AsyncResource").level = Log.Level.Trace;
michael@0 20 Log.repository.getLogger("Sync.ErrorHandler").level = Log.Level.Trace;
michael@0 21 Log.repository.getLogger("Sync.Resource").level = Log.Level.Trace;
michael@0 22 Log.repository.getLogger("Sync.RESTRequest").level = Log.Level.Trace;
michael@0 23 Log.repository.getLogger("Sync.Service").level = Log.Level.Trace;
michael@0 24 Log.repository.getLogger("Sync.SyncScheduler").level = Log.Level.Trace;
michael@0 25 initTestLogging();
michael@0 26
michael@0 27 ensureLegacyIdentityManager();
michael@0 28
michael@0 29 Service.engineManager.register(RotaryEngine);
michael@0 30
michael@0 31 // None of the failures in this file should result in a UI error.
michael@0 32 function onUIError() {
michael@0 33 do_throw("Errors should not be presented in the UI.");
michael@0 34 }
michael@0 35 Svc.Obs.add("weave:ui:login:error", onUIError);
michael@0 36 Svc.Obs.add("weave:ui:sync:error", onUIError);
michael@0 37
michael@0 38 run_next_test();
michael@0 39 }
michael@0 40
michael@0 41 /**
michael@0 42 * Emulate the following Zeus config:
michael@0 43 * $draining = data.get($prefix . $host . " draining");
michael@0 44 * if ($draining == "drain.") {
michael@0 45 * log.warn($log_host_db_status . " migrating=1 (node-reassignment)" .
michael@0 46 * $log_suffix);
michael@0 47 * http.sendResponse("401 Node reassignment", $content_type,
michael@0 48 * '"server request: node reassignment"', "");
michael@0 49 * }
michael@0 50 */
michael@0 51 const reassignBody = "\"server request: node reassignment\"";
michael@0 52
michael@0 53 // API-compatible with SyncServer handler. Bind `handler` to something to use
michael@0 54 // as a ServerCollection handler.
michael@0 55 function handleReassign(handler, req, resp) {
michael@0 56 resp.setStatusLine(req.httpVersion, 401, "Node reassignment");
michael@0 57 resp.setHeader("Content-Type", "application/json");
michael@0 58 resp.bodyOutputStream.write(reassignBody, reassignBody.length);
michael@0 59 }
michael@0 60
michael@0 61 /**
michael@0 62 * A node assignment handler.
michael@0 63 */
michael@0 64 function installNodeHandler(server, next) {
michael@0 65 let newNodeBody = server.baseURI;
michael@0 66 function handleNodeRequest(req, resp) {
michael@0 67 _("Client made a request for a node reassignment.");
michael@0 68 resp.setStatusLine(req.httpVersion, 200, "OK");
michael@0 69 resp.setHeader("Content-Type", "text/plain");
michael@0 70 resp.bodyOutputStream.write(newNodeBody, newNodeBody.length);
michael@0 71 Utils.nextTick(next);
michael@0 72 }
michael@0 73 let nodePath = "/user/1.0/johndoe/node/weave";
michael@0 74 server.server.registerPathHandler(nodePath, handleNodeRequest);
michael@0 75 _("Registered node handler at " + nodePath);
michael@0 76 }
michael@0 77
michael@0 78 function prepareServer() {
michael@0 79 let deferred = Promise.defer();
michael@0 80 configureIdentity({username: "johndoe"}).then(() => {
michael@0 81 let server = new SyncServer();
michael@0 82 server.registerUser("johndoe");
michael@0 83 server.start();
michael@0 84 Service.serverURL = server.baseURI;
michael@0 85 Service.clusterURL = server.baseURI;
michael@0 86 do_check_eq(Service.userAPIURI, server.baseURI + "user/1.0/");
michael@0 87 deferred.resolve(server);
michael@0 88 });
michael@0 89 return deferred.promise;
michael@0 90 }
michael@0 91
michael@0 92 function getReassigned() {
michael@0 93 try {
michael@0 94 return Services.prefs.getBoolPref("services.sync.lastSyncReassigned");
michael@0 95 } catch (ex if (ex.result == Cr.NS_ERROR_UNEXPECTED)) {
michael@0 96 return false;
michael@0 97 } catch (ex) {
michael@0 98 do_throw("Got exception retrieving lastSyncReassigned: " +
michael@0 99 Utils.exceptionStr(ex));
michael@0 100 }
michael@0 101 }
michael@0 102
michael@0 103 /**
michael@0 104 * Make a test request to `url`, then watch the result of two syncs
michael@0 105 * to ensure that a node request was made.
michael@0 106 * Runs `between` between the two. This can be used to undo deliberate failure
michael@0 107 * setup, detach observers, etc.
michael@0 108 */
michael@0 109 function syncAndExpectNodeReassignment(server, firstNotification, between,
michael@0 110 secondNotification, url) {
michael@0 111 let deferred = Promise.defer();
michael@0 112 function onwards() {
michael@0 113 let nodeFetched = false;
michael@0 114 function onFirstSync() {
michael@0 115 _("First sync completed.");
michael@0 116 Svc.Obs.remove(firstNotification, onFirstSync);
michael@0 117 Svc.Obs.add(secondNotification, onSecondSync);
michael@0 118
michael@0 119 do_check_eq(Service.clusterURL, "");
michael@0 120
michael@0 121 // Track whether we fetched node/weave. We want to wait for the second
michael@0 122 // sync to finish so that we're cleaned up for the next test, so don't
michael@0 123 // run_next_test in the node handler.
michael@0 124 nodeFetched = false;
michael@0 125
michael@0 126 // Verify that the client requests a node reassignment.
michael@0 127 // Install a node handler to watch for these requests.
michael@0 128 installNodeHandler(server, function () {
michael@0 129 nodeFetched = true;
michael@0 130 });
michael@0 131
michael@0 132 // Allow for tests to clean up error conditions.
michael@0 133 between();
michael@0 134 }
michael@0 135 function onSecondSync() {
michael@0 136 _("Second sync completed.");
michael@0 137 Svc.Obs.remove(secondNotification, onSecondSync);
michael@0 138 Service.scheduler.clearSyncTriggers();
michael@0 139
michael@0 140 // Make absolutely sure that any event listeners are done with their work
michael@0 141 // before we proceed.
michael@0 142 waitForZeroTimer(function () {
michael@0 143 _("Second sync nextTick.");
michael@0 144 do_check_true(nodeFetched);
michael@0 145 Service.startOver();
michael@0 146 server.stop(deferred.resolve);
michael@0 147 });
michael@0 148 }
michael@0 149
michael@0 150 Svc.Obs.add(firstNotification, onFirstSync);
michael@0 151 Service.sync();
michael@0 152 }
michael@0 153
michael@0 154 // Make sure that it works!
michael@0 155 let request = new RESTRequest(url);
michael@0 156 request.get(function () {
michael@0 157 do_check_eq(request.response.status, 401);
michael@0 158 Utils.nextTick(onwards);
michael@0 159 });
michael@0 160 yield deferred.promise;
michael@0 161 }
michael@0 162
michael@0 163 add_task(function test_momentary_401_engine() {
michael@0 164 _("Test a failure for engine URLs that's resolved by reassignment.");
michael@0 165 let server = yield prepareServer();
michael@0 166 let john = server.user("johndoe");
michael@0 167
michael@0 168 _("Enabling the Rotary engine.");
michael@0 169 let engine = Service.engineManager.get("rotary");
michael@0 170 engine.enabled = true;
michael@0 171
michael@0 172 // We need the server to be correctly set up prior to experimenting. Do this
michael@0 173 // through a sync.
michael@0 174 let global = {syncID: Service.syncID,
michael@0 175 storageVersion: STORAGE_VERSION,
michael@0 176 rotary: {version: engine.version,
michael@0 177 syncID: engine.syncID}}
michael@0 178 john.createCollection("meta").insert("global", global);
michael@0 179
michael@0 180 _("First sync to prepare server contents.");
michael@0 181 Service.sync();
michael@0 182
michael@0 183 _("Setting up Rotary collection to 401.");
michael@0 184 let rotary = john.createCollection("rotary");
michael@0 185 let oldHandler = rotary.collectionHandler;
michael@0 186 rotary.collectionHandler = handleReassign.bind(this, undefined);
michael@0 187
michael@0 188 // We want to verify that the clusterURL pref has been cleared after a 401
michael@0 189 // inside a sync. Flag the Rotary engine to need syncing.
michael@0 190 john.collection("rotary").timestamp += 1000;
michael@0 191
michael@0 192 function between() {
michael@0 193 _("Undoing test changes.");
michael@0 194 rotary.collectionHandler = oldHandler;
michael@0 195
michael@0 196 function onLoginStart() {
michael@0 197 // lastSyncReassigned shouldn't be cleared until a sync has succeeded.
michael@0 198 _("Ensuring that lastSyncReassigned is still set at next sync start.");
michael@0 199 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 200 do_check_true(getReassigned());
michael@0 201 }
michael@0 202
michael@0 203 _("Adding observer that lastSyncReassigned is still set on login.");
michael@0 204 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 205 }
michael@0 206
michael@0 207 yield syncAndExpectNodeReassignment(server,
michael@0 208 "weave:service:sync:finish",
michael@0 209 between,
michael@0 210 "weave:service:sync:finish",
michael@0 211 Service.storageURL + "rotary");
michael@0 212 });
michael@0 213
michael@0 214 // This test ends up being a failing fetch *after we're already logged in*.
michael@0 215 add_task(function test_momentary_401_info_collections() {
michael@0 216 _("Test a failure for info/collections that's resolved by reassignment.");
michael@0 217 let server = yield prepareServer();
michael@0 218
michael@0 219 _("First sync to prepare server contents.");
michael@0 220 Service.sync();
michael@0 221
michael@0 222 // Return a 401 for info requests, particularly info/collections.
michael@0 223 let oldHandler = server.toplevelHandlers.info;
michael@0 224 server.toplevelHandlers.info = handleReassign;
michael@0 225
michael@0 226 function undo() {
michael@0 227 _("Undoing test changes.");
michael@0 228 server.toplevelHandlers.info = oldHandler;
michael@0 229 }
michael@0 230
michael@0 231 yield syncAndExpectNodeReassignment(server,
michael@0 232 "weave:service:sync:error",
michael@0 233 undo,
michael@0 234 "weave:service:sync:finish",
michael@0 235 Service.infoURL);
michael@0 236 });
michael@0 237
michael@0 238 add_task(function test_momentary_401_storage_loggedin() {
michael@0 239 _("Test a failure for any storage URL, not just engine parts. " +
michael@0 240 "Resolved by reassignment.");
michael@0 241 let server = yield prepareServer();
michael@0 242
michael@0 243 _("Performing initial sync to ensure we are logged in.")
michael@0 244 Service.sync();
michael@0 245
michael@0 246 // Return a 401 for all storage requests.
michael@0 247 let oldHandler = server.toplevelHandlers.storage;
michael@0 248 server.toplevelHandlers.storage = handleReassign;
michael@0 249
michael@0 250 function undo() {
michael@0 251 _("Undoing test changes.");
michael@0 252 server.toplevelHandlers.storage = oldHandler;
michael@0 253 }
michael@0 254
michael@0 255 do_check_true(Service.isLoggedIn, "already logged in");
michael@0 256 yield syncAndExpectNodeReassignment(server,
michael@0 257 "weave:service:sync:error",
michael@0 258 undo,
michael@0 259 "weave:service:sync:finish",
michael@0 260 Service.storageURL + "meta/global");
michael@0 261 });
michael@0 262
michael@0 263 add_task(function test_momentary_401_storage_loggedout() {
michael@0 264 _("Test a failure for any storage URL, not just engine parts. " +
michael@0 265 "Resolved by reassignment.");
michael@0 266 let server = yield prepareServer();
michael@0 267
michael@0 268 // Return a 401 for all storage requests.
michael@0 269 let oldHandler = server.toplevelHandlers.storage;
michael@0 270 server.toplevelHandlers.storage = handleReassign;
michael@0 271
michael@0 272 function undo() {
michael@0 273 _("Undoing test changes.");
michael@0 274 server.toplevelHandlers.storage = oldHandler;
michael@0 275 }
michael@0 276
michael@0 277 do_check_false(Service.isLoggedIn, "not already logged in");
michael@0 278 yield syncAndExpectNodeReassignment(server,
michael@0 279 "weave:service:login:error",
michael@0 280 undo,
michael@0 281 "weave:service:sync:finish",
michael@0 282 Service.storageURL + "meta/global");
michael@0 283 });
michael@0 284
michael@0 285 add_task(function test_loop_avoidance_storage() {
michael@0 286 _("Test that a repeated failure doesn't result in a sync loop " +
michael@0 287 "if node reassignment cannot resolve the failure.");
michael@0 288
michael@0 289 let server = yield prepareServer();
michael@0 290
michael@0 291 // Return a 401 for all storage requests.
michael@0 292 let oldHandler = server.toplevelHandlers.storage;
michael@0 293 server.toplevelHandlers.storage = handleReassign;
michael@0 294
michael@0 295 let firstNotification = "weave:service:login:error";
michael@0 296 let secondNotification = "weave:service:login:error";
michael@0 297 let thirdNotification = "weave:service:sync:finish";
michael@0 298
michael@0 299 let nodeFetched = false;
michael@0 300 let deferred = Promise.defer();
michael@0 301
michael@0 302 // Track the time. We want to make sure the duration between the first and
michael@0 303 // second sync is small, and then that the duration between second and third
michael@0 304 // is set to be large.
michael@0 305 let now;
michael@0 306
michael@0 307 function onFirstSync() {
michael@0 308 _("First sync completed.");
michael@0 309 Svc.Obs.remove(firstNotification, onFirstSync);
michael@0 310 Svc.Obs.add(secondNotification, onSecondSync);
michael@0 311
michael@0 312 do_check_eq(Service.clusterURL, "");
michael@0 313
michael@0 314 // We got a 401 mid-sync, and set the pref accordingly.
michael@0 315 do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
michael@0 316
michael@0 317 // Track whether we fetched node/weave. We want to wait for the second
michael@0 318 // sync to finish so that we're cleaned up for the next test, so don't
michael@0 319 // run_next_test in the node handler.
michael@0 320 nodeFetched = false;
michael@0 321
michael@0 322 // Verify that the client requests a node reassignment.
michael@0 323 // Install a node handler to watch for these requests.
michael@0 324 installNodeHandler(server, function () {
michael@0 325 nodeFetched = true;
michael@0 326 });
michael@0 327
michael@0 328 // Update the timestamp.
michael@0 329 now = Date.now();
michael@0 330 }
michael@0 331
michael@0 332 function onSecondSync() {
michael@0 333 _("Second sync completed.");
michael@0 334 Svc.Obs.remove(secondNotification, onSecondSync);
michael@0 335 Svc.Obs.add(thirdNotification, onThirdSync);
michael@0 336
michael@0 337 // This sync occurred within the backoff interval.
michael@0 338 let elapsedTime = Date.now() - now;
michael@0 339 do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
michael@0 340
michael@0 341 // This pref will be true until a sync completes successfully.
michael@0 342 do_check_true(getReassigned());
michael@0 343
michael@0 344 // The timer will be set for some distant time.
michael@0 345 // We store nextSync in prefs, which offers us only limited resolution.
michael@0 346 // Include that logic here.
michael@0 347 let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
michael@0 348 _("Next sync scheduled for " + Service.scheduler.nextSync);
michael@0 349 _("Expected to be slightly greater than " + expectedNextSync);
michael@0 350
michael@0 351 do_check_true(Service.scheduler.nextSync >= expectedNextSync);
michael@0 352 do_check_true(!!Service.scheduler.syncTimer);
michael@0 353
michael@0 354 // Undo our evil scheme.
michael@0 355 server.toplevelHandlers.storage = oldHandler;
michael@0 356
michael@0 357 // Bring the timer forward to kick off a successful sync, so we can watch
michael@0 358 // the pref get cleared.
michael@0 359 Service.scheduler.scheduleNextSync(0);
michael@0 360 }
michael@0 361 function onThirdSync() {
michael@0 362 Svc.Obs.remove(thirdNotification, onThirdSync);
michael@0 363
michael@0 364 // That'll do for now; no more syncs.
michael@0 365 Service.scheduler.clearSyncTriggers();
michael@0 366
michael@0 367 // Make absolutely sure that any event listeners are done with their work
michael@0 368 // before we proceed.
michael@0 369 waitForZeroTimer(function () {
michael@0 370 _("Third sync nextTick.");
michael@0 371 do_check_false(getReassigned());
michael@0 372 do_check_true(nodeFetched);
michael@0 373 Service.startOver();
michael@0 374 server.stop(deferred.resolve);
michael@0 375 });
michael@0 376 }
michael@0 377
michael@0 378 Svc.Obs.add(firstNotification, onFirstSync);
michael@0 379
michael@0 380 now = Date.now();
michael@0 381 Service.sync();
michael@0 382 yield deferred.promise;
michael@0 383 });
michael@0 384
michael@0 385 add_task(function test_loop_avoidance_engine() {
michael@0 386 _("Test that a repeated 401 in an engine doesn't result in a sync loop " +
michael@0 387 "if node reassignment cannot resolve the failure.");
michael@0 388 let server = yield prepareServer();
michael@0 389 let john = server.user("johndoe");
michael@0 390
michael@0 391 _("Enabling the Rotary engine.");
michael@0 392 let engine = Service.engineManager.get("rotary");
michael@0 393 engine.enabled = true;
michael@0 394 let deferred = Promise.defer();
michael@0 395
michael@0 396 // We need the server to be correctly set up prior to experimenting. Do this
michael@0 397 // through a sync.
michael@0 398 let global = {syncID: Service.syncID,
michael@0 399 storageVersion: STORAGE_VERSION,
michael@0 400 rotary: {version: engine.version,
michael@0 401 syncID: engine.syncID}}
michael@0 402 john.createCollection("meta").insert("global", global);
michael@0 403
michael@0 404 _("First sync to prepare server contents.");
michael@0 405 Service.sync();
michael@0 406
michael@0 407 _("Setting up Rotary collection to 401.");
michael@0 408 let rotary = john.createCollection("rotary");
michael@0 409 let oldHandler = rotary.collectionHandler;
michael@0 410 rotary.collectionHandler = handleReassign.bind(this, undefined);
michael@0 411
michael@0 412 // Flag the Rotary engine to need syncing.
michael@0 413 john.collection("rotary").timestamp += 1000;
michael@0 414
michael@0 415 function onLoginStart() {
michael@0 416 // lastSyncReassigned shouldn't be cleared until a sync has succeeded.
michael@0 417 _("Ensuring that lastSyncReassigned is still set at next sync start.");
michael@0 418 do_check_true(getReassigned());
michael@0 419 }
michael@0 420
michael@0 421 function beforeSuccessfulSync() {
michael@0 422 _("Undoing test changes.");
michael@0 423 rotary.collectionHandler = oldHandler;
michael@0 424 }
michael@0 425
michael@0 426 function afterSuccessfulSync() {
michael@0 427 Svc.Obs.remove("weave:service:login:start", onLoginStart);
michael@0 428 Service.startOver();
michael@0 429 server.stop(deferred.resolve);
michael@0 430 }
michael@0 431
michael@0 432 let firstNotification = "weave:service:sync:finish";
michael@0 433 let secondNotification = "weave:service:sync:finish";
michael@0 434 let thirdNotification = "weave:service:sync:finish";
michael@0 435
michael@0 436 let nodeFetched = false;
michael@0 437
michael@0 438 // Track the time. We want to make sure the duration between the first and
michael@0 439 // second sync is small, and then that the duration between second and third
michael@0 440 // is set to be large.
michael@0 441 let now;
michael@0 442
michael@0 443 function onFirstSync() {
michael@0 444 _("First sync completed.");
michael@0 445 Svc.Obs.remove(firstNotification, onFirstSync);
michael@0 446 Svc.Obs.add(secondNotification, onSecondSync);
michael@0 447
michael@0 448 do_check_eq(Service.clusterURL, "");
michael@0 449
michael@0 450 _("Adding observer that lastSyncReassigned is still set on login.");
michael@0 451 Svc.Obs.add("weave:service:login:start", onLoginStart);
michael@0 452
michael@0 453 // We got a 401 mid-sync, and set the pref accordingly.
michael@0 454 do_check_true(Services.prefs.getBoolPref("services.sync.lastSyncReassigned"));
michael@0 455
michael@0 456 // Track whether we fetched node/weave. We want to wait for the second
michael@0 457 // sync to finish so that we're cleaned up for the next test, so don't
michael@0 458 // run_next_test in the node handler.
michael@0 459 nodeFetched = false;
michael@0 460
michael@0 461 // Verify that the client requests a node reassignment.
michael@0 462 // Install a node handler to watch for these requests.
michael@0 463 installNodeHandler(server, function () {
michael@0 464 nodeFetched = true;
michael@0 465 });
michael@0 466
michael@0 467 // Update the timestamp.
michael@0 468 now = Date.now();
michael@0 469 }
michael@0 470
michael@0 471 function onSecondSync() {
michael@0 472 _("Second sync completed.");
michael@0 473 Svc.Obs.remove(secondNotification, onSecondSync);
michael@0 474 Svc.Obs.add(thirdNotification, onThirdSync);
michael@0 475
michael@0 476 // This sync occurred within the backoff interval.
michael@0 477 let elapsedTime = Date.now() - now;
michael@0 478 do_check_true(elapsedTime < MINIMUM_BACKOFF_INTERVAL);
michael@0 479
michael@0 480 // This pref will be true until a sync completes successfully.
michael@0 481 do_check_true(getReassigned());
michael@0 482
michael@0 483 // The timer will be set for some distant time.
michael@0 484 // We store nextSync in prefs, which offers us only limited resolution.
michael@0 485 // Include that logic here.
michael@0 486 let expectedNextSync = 1000 * Math.floor((now + MINIMUM_BACKOFF_INTERVAL) / 1000);
michael@0 487 _("Next sync scheduled for " + Service.scheduler.nextSync);
michael@0 488 _("Expected to be slightly greater than " + expectedNextSync);
michael@0 489
michael@0 490 do_check_true(Service.scheduler.nextSync >= expectedNextSync);
michael@0 491 do_check_true(!!Service.scheduler.syncTimer);
michael@0 492
michael@0 493 // Undo our evil scheme.
michael@0 494 beforeSuccessfulSync();
michael@0 495
michael@0 496 // Bring the timer forward to kick off a successful sync, so we can watch
michael@0 497 // the pref get cleared.
michael@0 498 Service.scheduler.scheduleNextSync(0);
michael@0 499 }
michael@0 500
michael@0 501 function onThirdSync() {
michael@0 502 Svc.Obs.remove(thirdNotification, onThirdSync);
michael@0 503
michael@0 504 // That'll do for now; no more syncs.
michael@0 505 Service.scheduler.clearSyncTriggers();
michael@0 506
michael@0 507 // Make absolutely sure that any event listeners are done with their work
michael@0 508 // before we proceed.
michael@0 509 waitForZeroTimer(function () {
michael@0 510 _("Third sync nextTick.");
michael@0 511 do_check_false(getReassigned());
michael@0 512 do_check_true(nodeFetched);
michael@0 513 afterSuccessfulSync();
michael@0 514 });
michael@0 515 }
michael@0 516
michael@0 517 Svc.Obs.add(firstNotification, onFirstSync);
michael@0 518
michael@0 519 now = Date.now();
michael@0 520 Service.sync();
michael@0 521 yield deferred.promise;
michael@0 522 });

mercurial