| 581 | def test_multiple_clients(self): |
| 582 | self.skip_if_cannot_daemonize() |
| 583 | basedir = self.workdir("test_client") |
| 584 | # this furl must be syntactically correct, else 'tahoe start' will |
| 585 | # correctly report an error. It doesn't need to point at a real |
| 586 | # introducer. |
| 587 | introducer_furl = "pb://xrndsskn2zuuian5ltnxrte7lnuqdrkz@127.0.0.1:55617/introducer" |
| 588 | c1 = os.path.join(basedir, "c1") |
| 589 | HOTLINE_FILE_1 = os.path.join(c1, "suicide_prevention_hotline") |
| 590 | TWISTD_PID_FILE_1 = os.path.join(c1, "twistd.pid") |
| 591 | PORTNUMFILE_1 = os.path.join(c1, "client.port") |
| 592 | c1_args = ["--quiet", "create-node", "--basedir", c1, "--webport", "0", |
| 593 | "--introducer", introducer_furl] |
| 594 | |
| 595 | c2 = os.path.join(basedir, "c2") |
| 596 | HOTLINE_FILE_2 = os.path.join(c2, "suicide_prevention_hotline") |
| 597 | TWISTD_PID_FILE_2 = os.path.join(c2, "twistd.pid") |
| 598 | PORTNUMFILE_2 = os.path.join(c2, "client.port") |
| 599 | c2_args = ["--quiet", "create-node", "--basedir", c2, "--webport", "0", |
| 600 | "--introducer", introducer_furl] |
| 601 | |
| 602 | d = utils.getProcessOutputAndValue(bintahoe, args=c1_args, env=os.environ) |
| 603 | def _cb1(res): |
| 604 | out, err, rc_or_sig = res |
| 605 | self.failUnlessEqual(rc_or_sig, 0) |
| 606 | return utils.getProcessOutputAndValue(bintahoe, args=c2_args, |
| 607 | env=os.environ) |
| 608 | d.addCallback(_cb1) |
| 609 | def _cb2(res): |
| 610 | out, err, rc_or_sig = res |
| 611 | self.failUnlessEqual(rc_or_sig, 0) |
| 612 | # both nodes have been constructed, but they are not yet running |
| 613 | |
| 614 | # By writing this file, we get sixty seconds before the client |
| 615 | # will exit. This insures that even if the 'stop' command doesn't |
| 616 | # work (and the test fails), the client should still terminate. |
| 617 | open(HOTLINE_FILE_1, "w").write("") |
| 618 | open(HOTLINE_FILE_2, "w").write("") |
| 619 | # now it's safe to start the nodes |
| 620 | start_args = ["--quiet", "start", "-m", c1, c2] |
| 621 | return utils.getProcessOutputAndValue(bintahoe, args=start_args, |
| 622 | env=os.environ) |
| 623 | d.addCallback(_cb2) |
| 624 | |
| 625 | def _node_has_started(): |
| 626 | open(HOTLINE_FILE_1, "w").write("") |
| 627 | open(HOTLINE_FILE_2, "w").write("") |
| 628 | return (os.path.exists(PORTNUMFILE_1) |
| 629 | and os.path.exists(PORTNUMFILE_2)) |
| 630 | |
| 631 | def _cb3(res): |
| 632 | out, err, rc_or_sig = res |
| 633 | open(HOTLINE_FILE_1, "w").write("") |
| 634 | open(HOTLINE_FILE_2, "w").write("") |
| 635 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
| 636 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
| 637 | self.failUnlessEqual(out, "", errstr) |
| 638 | # See test_client_no_noise -- for now we ignore noise. |
| 639 | # self.failUnlessEqual(err, "", errstr) |
| 640 | |
| 641 | # if 'tahoe start' exited without error, we're guaranteed that it |
| 642 | # successfully loaded the code and tahoe.cfg for the last node |
| 643 | # (argv[-1]), since these happen before twistd.run() calls |
| 644 | # fork(). There made have been a runtime error after fork(). |
| 645 | # Also, there may have been an import or config error while |
| 646 | # loading any of the earlier nodes (argv[:-1]), since 'tahoe |
| 647 | # start' must do an additional fork before calling twistd.run() |
| 648 | # on those. |
| 649 | |
| 650 | # so watch the filesystem to determine when the nodes have |
| 651 | # started running |
| 652 | |
| 653 | return self.poll(_node_has_started) |
| 654 | d.addCallback(_cb3) |
| 655 | |
| 656 | def _restart(ign): |
| 657 | # delete the port-number files, then restart the nodes. The new |
| 658 | # instances will re-write those files. |
| 659 | os.unlink(PORTNUMFILE_1) |
| 660 | os.unlink(PORTNUMFILE_2) |
| 661 | restart_args = ["--quiet", "restart", "-m", c1, c2] |
| 662 | return utils.getProcessOutputAndValue(bintahoe, args=restart_args, |
| 663 | env=os.environ) |
| 664 | d.addCallback(_restart) |
| 665 | def _did_restart(res): |
| 666 | out, err, rc_or_sig = res |
| 667 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
| 668 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
| 669 | self.failUnlessEqual(out, "", errstr) |
| 670 | # See test_client_no_noise -- for now we ignore noise. |
| 671 | # self.failUnlessEqual(err, "", errstr) |
| 672 | |
| 673 | # we must still poll for the startup process to get far enough |
| 674 | return self.poll(_node_has_started) |
| 675 | d.addCallback(_did_restart) |
| 676 | # when that finishes, we know the nodes have restarted. If 'tahoe |
| 677 | # restart -m' is broken, that will time out. |
| 678 | |
| 679 | # now we can kill it. TODO: On a slow machine, the node might kill |
| 680 | # itself before we get a chance too, especially if spawning the |
| 681 | # 'tahoe stop' command takes a while. |
| 682 | def _stop(res): |
| 683 | open(HOTLINE_FILE_1, "w").write("") |
| 684 | open(HOTLINE_FILE_2, "w").write("") |
| 685 | self.failUnless(os.path.exists(TWISTD_PID_FILE_1), |
| 686 | (TWISTD_PID_FILE_1, os.listdir(c1))) |
| 687 | self.failUnless(os.path.exists(TWISTD_PID_FILE_2), |
| 688 | (TWISTD_PID_FILE_2, os.listdir(c2))) |
| 689 | stop_args = ["--quiet", "stop", "-m", c1, c2] |
| 690 | return utils.getProcessOutputAndValue(bintahoe, args=stop_args, |
| 691 | env=os.environ) |
| 692 | d.addCallback(_stop) |
| 693 | |
| 694 | def _cb4(res): |
| 695 | out, err, rc_or_sig = res |
| 696 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
| 697 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
| 698 | self.failUnlessEqual(out, "", errstr) |
| 699 | # See test_client_no_noise -- for now we ignore noise. |
| 700 | # self.failUnlessEqual(err, "", errstr) |
| 701 | |
| 702 | # the parent was supposed to poll and wait until it sees |
| 703 | # twistd.pid go away before it exits, so twistd.pid should be |
| 704 | # gone by now. |
| 705 | self.failIf(os.path.exists(TWISTD_PID_FILE_1)) |
| 706 | self.failIf(os.path.exists(TWISTD_PID_FILE_2)) |
| 707 | d.addCallback(_cb4) |
| 708 | def _remove_hotline(res): |
| 709 | # always remove these, so that if something went wrong, the nodes |
| 710 | # will quit eventually |
| 711 | os.unlink(HOTLINE_FILE_1) |
| 712 | os.unlink(HOTLINE_FILE_2) |
| 713 | return res |
| 714 | d.addBoth(_remove_hotline) |
| 715 | return d |
| 716 | |
| 717 | |