def setUp(self): super(UnitMachineDeploymentTest, self).setUp() self.charm = get_charm_from_path(self.sample_dir1) self.bundle = self.charm.as_bundle() self.juju_directory = self.makeDir() self.units_directory = os.path.join(self.juju_directory, "units") os.mkdir(self.units_directory) self.unit_name = "wordpress/0" self.deployment = UnitMachineDeployment(self.unit_name, self.juju_directory) self.assertEqual(self.deployment.unit_agent_module, "juju.agents.unit") self.deployment.unit_agent_module = "juju.agents.dummy"
def setUp(self): super(UnitMachineDeploymentTest, self).setUp() self.charm = get_charm_from_path(self.sample_dir1) self.bundle = self.charm.as_bundle() self.juju_directory = self.makeDir() self.units_directory = os.path.join(self.juju_directory, "units") os.mkdir(self.units_directory) self.unit_name = "wordpress/0" self.deployment = UnitMachineDeployment( self.unit_name, self.juju_directory) self.assertEqual( self.deployment.unit_agent_module, "juju.agents.unit") self.deployment.unit_agent_module = "juju.agents.dummy"
class UnitMachineDeploymentTest(RepositoryTestBase): def setUp(self): super(UnitMachineDeploymentTest, self).setUp() self.charm = get_charm_from_path(self.sample_dir1) self.bundle = self.charm.as_bundle() self.juju_directory = self.makeDir() self.units_directory = os.path.join(self.juju_directory, "units") os.mkdir(self.units_directory) self.unit_name = "wordpress/0" self.deployment = UnitMachineDeployment(self.unit_name, self.juju_directory) self.assertEqual(self.deployment.unit_agent_module, "juju.agents.unit") self.deployment.unit_agent_module = "juju.agents.dummy" def process_kill(self, pid): try: os.kill(pid, 9) except OSError: pass def test_unit_name_with_path_manipulation_raises_assertion(self): self.assertRaises(AssertionError, UnitMachineDeployment, "../../etc/password/zebra/0", self.units_directory) def test_unit_directory(self): self.assertEqual( self.deployment.directory, os.path.join(self.units_directory, self.unit_name.replace("/", "-"))) def test_unit_pid_file(self): self.assertEqual( self.deployment.pid_file, os.path.join(self.units_directory, "%s.pid" % (self.unit_name.replace("/", "-")))) def test_service_unit_start(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ d = self.deployment.start("0", get_test_zookeeper_address(), self.bundle) @inlineCallbacks def validate_result(result): # give process time to write its pid yield self.sleep(0.1) self.addCleanup(self.process_kill, int(open(self.deployment.pid_file).read())) self.assertEqual(result, True) d.addCallback(validate_result) return d def test_deployment_get_environment(self): zk_address = get_test_zookeeper_address() environ = self.deployment.get_environment(21, zk_address) environ.pop("PYTHONPATH") self.assertEqual(environ["JUJU_HOME"], self.juju_directory) self.assertEqual(environ["JUJU_UNIT_NAME"], self.unit_name) self.assertEqual(environ["JUJU_ZOOKEEPER"], zk_address) self.assertEqual(environ["JUJU_MACHINE_ID"], "21") def test_service_unit_start_with_integer_machine_id(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ d = self.deployment.start(21, get_test_zookeeper_address(), self.bundle) @inlineCallbacks def validate_result(result): # give process time to write its pid yield self.sleep(0.1) self.addCleanup(self.process_kill, int(open(self.deployment.pid_file).read())) self.assertEqual(result, True) d.addCallback(validate_result) return d def test_service_unit_start_with_agent_startup_error(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ self.deployment.unit_agent_module = "magichat.xr1" d = self.deployment.start("0", get_test_zookeeper_address(), self.bundle) self.failUnlessFailure(d, UnitDeploymentError) def validate_result(error): self.assertIn("No module named magichat", str(error)) d.addCallback(validate_result) return d def test_service_unit_start_agent_arguments(self): """ Starting a service unit will start a service unit agent with arguments denoting the current machine id, zookeeper server location, and the unit name. Additionally it will configure the log and pid file locations. """ machine_id = "0" zookeeper_hosts = "menagerie.example.com:2181" from twisted.internet import reactor mock_reactor = self.mocker.patch(reactor) environ = dict(os.environ) environ["JUJU_UNIT_NAME"] = self.unit_name environ["JUJU_HOME"] = self.juju_directory environ["JUJU_MACHINE_ID"] = machine_id environ["JUJU_ZOOKEEPER"] = zookeeper_hosts environ["PYTHONPATH"] = ":".join( filter(None, [ os.path.dirname(get_module_directory(juju)), environ.get("PYTHONPATH") ])) pid_file = os.path.join(self.units_directory, "%s.pid" % self.unit_name.replace("/", "-")) log_file = os.path.join(self.deployment.directory, "charm.log") args = [ sys.executable, "-m", "juju.agents.dummy", "-n", "--pidfile", pid_file, "--logfile", log_file ] mock_reactor.spawnProcess(MATCH_PROTOCOL, sys.executable, args, environ) self.mocker.replay() self.deployment.start(machine_id, zookeeper_hosts, self.bundle) def xtest_service_unit_start_pre_unpack(self): """ Attempting to start a charm before the charm is unpacked results in an exception. """ error = yield self.assertFailure( self.deployment.start("0", get_test_zookeeper_address(), self.bundle), UnitDeploymentError) self.assertEquals(str(error), "Charm must be unpacked first.") @inlineCallbacks def test_service_unit_destroy(self): """ Forcibly stop a unit, and destroy any directories associated to it on the machine, and kills the unit agent process. """ yield self.deployment.start("0", get_test_zookeeper_address(), self.bundle) # give the process time to write its pid file yield self.sleep(0.1) pid = int(open(self.deployment.pid_file).read()) yield self.deployment.destroy() # give the process time to die. yield self.sleep(0.1) e = self.assertRaises(OSError, os.kill, pid, 0) self.assertEqual(e.errno, 3) self.assertFalse(os.path.exists(self.deployment.directory)) self.assertFalse(os.path.exists(self.deployment.pid_file)) def test_service_unit_destroy_stale_pid(self): """ A stale pid file does not cause any errors. We mock away is_running as otherwise it will check for this, but there exists a small window when the result may disagree. """ self.makeFile("8917238", path=self.deployment.pid_file) mock_deployment = self.mocker.patch(self.deployment) mock_deployment.is_running() self.mocker.result(succeed(True)) self.mocker.replay() return self.deployment.destroy() def test_service_unit_destroy_perm_error(self): """ A stale pid file does not cause any errors. We mock away is_running as otherwise it will check for this, but there exists a small window when the result may disagree. """ if os.geteuid() == 0: return self.makeFile("1", path=self.deployment.pid_file) mock_deployment = self.mocker.patch(self.deployment) mock_deployment.is_running() self.mocker.result(succeed(True)) self.mocker.replay() return self.assertFailure(self.deployment.destroy(), OSError) @inlineCallbacks def test_service_unit_destroy_undeployed(self): """ If the unit is has not been deployed, nothing happens. """ yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) @inlineCallbacks def test_service_unit_destroy_not_running(self): """ If the unit is not running, then destroy will just remove its directory. """ self.deployment.unpack_charm(self.bundle) self.assertTrue(os.path.exists(self.deployment.directory)) yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) def test_unpack_charm(self): """ The deployment unpacks a charm bundle into the unit workspace. """ self.deployment.unpack_charm(self.bundle) unit_path = os.path.join(self.units_directory, self.unit_name.replace("/", "-")) self.assertTrue(os.path.exists(unit_path)) charm_path = os.path.join(unit_path, "charm") self.assertTrue(os.path.exists(charm_path)) charm = get_charm_from_path(charm_path) self.assertEqual(charm.get_revision(), self.charm.get_revision()) def test_unpack_charm_exception_invalid_charm(self): """ If the charm bundle is corrupted or invalid a deployment specific error is raised. """ error = self.assertRaises(UnitDeploymentError, self.deployment.unpack_charm, self.charm) self.assertEquals(str(error), "Invalid charm for deployment: %s" % self.charm.path) def test_is_running_no_pid_file(self): """ If there is no pid file the service unit is not running. """ self.assertEqual((yield self.deployment.is_running()), False) def test_is_running(self): """ The service deployment will check the pid and validate that the pid found is a running process. """ self.makeFile(str(os.getpid()), path=self.deployment.pid_file) self.assertEqual((yield self.deployment.is_running()), True) def test_is_running_against_unknown_error(self): """ If we don't have permission to access the process, the original error should get passed along. """ if os.geteuid() == 0: return self.makeFile("1", path=self.deployment.pid_file) self.assertFailure(self.deployment.is_running(), OSError) def test_is_running_invalid_pid_file(self): """ If the pid file is corrupted on disk, and does not contain a valid integer, then the agent is not running. """ self.makeFile("abcdef", path=self.deployment.pid_file) self.assertEqual((yield self.deployment.is_running()), False) def test_is_running_invalid_pid(self): """ If the pid file refers to an invalid process then the agent is not running. """ self.makeFile("669966", path=self.deployment.pid_file) self.assertEqual((yield self.deployment.is_running()), False)
class UnitMachineDeploymentTest(RepositoryTestBase): def setUp(self): super(UnitMachineDeploymentTest, self).setUp() self.charm = get_charm_from_path(self.sample_dir1) self.bundle = self.charm.as_bundle() self.juju_directory = self.makeDir() self.units_directory = os.path.join(self.juju_directory, "units") os.mkdir(self.units_directory) self.unit_name = "wordpress/0" self.deployment = UnitMachineDeployment( self.unit_name, self.juju_directory) self.assertEqual( self.deployment.unit_agent_module, "juju.agents.unit") self.deployment.unit_agent_module = "juju.agents.dummy" def process_kill(self, pid): try: os.kill(pid, 9) except OSError: pass def test_unit_name_with_path_manipulation_raises_assertion(self): self.assertRaises( AssertionError, UnitMachineDeployment, "../../etc/password/zebra/0", self.units_directory) def test_unit_directory(self): self.assertEqual( self.deployment.directory, os.path.join(self.units_directory, self.unit_name.replace("/", "-"))) def test_unit_pid_file(self): self.assertEqual( self.deployment.pid_file, os.path.join(self.units_directory, "%s.pid" % (self.unit_name.replace("/", "-")))) def test_service_unit_start(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ d = self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) @inlineCallbacks def validate_result(result): # give process time to write its pid yield self.sleep(0.1) self.addCleanup( self.process_kill, int(open(self.deployment.pid_file).read())) self.assertEqual(result, True) d.addCallback(validate_result) return d def test_deployment_get_environment(self): zk_address = get_test_zookeeper_address() environ = self.deployment.get_environment(21, zk_address) environ.pop("PYTHONPATH") self.assertEqual(environ["JUJU_HOME"], self.juju_directory) self.assertEqual(environ["JUJU_UNIT_NAME"], self.unit_name) self.assertEqual(environ["JUJU_ZOOKEEPER"], zk_address) self.assertEqual(environ["JUJU_MACHINE_ID"], "21") def test_service_unit_start_with_integer_machine_id(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ d = self.deployment.start( 21, get_test_zookeeper_address(), self.bundle) @inlineCallbacks def validate_result(result): # give process time to write its pid yield self.sleep(0.1) self.addCleanup( self.process_kill, int(open(self.deployment.pid_file).read())) self.assertEqual(result, True) d.addCallback(validate_result) return d def test_service_unit_start_with_agent_startup_error(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ self.deployment.unit_agent_module = "magichat.xr1" d = self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) self.failUnlessFailure(d, UnitDeploymentError) def validate_result(error): self.assertIn("No module named magichat", str(error)) d.addCallback(validate_result) return d def test_service_unit_start_agent_arguments(self): """ Starting a service unit will start a service unit agent with arguments denoting the current machine id, zookeeper server location, and the unit name. Additionally it will configure the log and pid file locations. """ machine_id = "0" zookeeper_hosts = "menagerie.example.com:2181" from twisted.internet import reactor mock_reactor = self.mocker.patch(reactor) environ = dict(os.environ) environ["JUJU_UNIT_NAME"] = self.unit_name environ["JUJU_HOME"] = self.juju_directory environ["JUJU_MACHINE_ID"] = machine_id environ["JUJU_ZOOKEEPER"] = zookeeper_hosts environ["PYTHONPATH"] = ":".join( filter(None, [ os.path.dirname(get_module_directory(juju)), environ.get("PYTHONPATH")])) pid_file = os.path.join( self.units_directory, "%s.pid" % self.unit_name.replace("/", "-")) log_file = os.path.join( self.deployment.directory, "charm.log") args = [sys.executable, "-m", "juju.agents.dummy", "-n", "--pidfile", pid_file, "--logfile", log_file] mock_reactor.spawnProcess( MATCH_PROTOCOL, sys.executable, args, environ) self.mocker.replay() self.deployment.start( machine_id, zookeeper_hosts, self.bundle) def xtest_service_unit_start_pre_unpack(self): """ Attempting to start a charm before the charm is unpacked results in an exception. """ error = yield self.assertFailure( self.deployment.start( "0", get_test_zookeeper_address(), self.bundle), UnitDeploymentError) self.assertEquals(str(error), "Charm must be unpacked first.") @inlineCallbacks def test_service_unit_destroy(self): """ Forcibly stop a unit, and destroy any directories associated to it on the machine, and kills the unit agent process. """ yield self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) # give the process time to write its pid file yield self.sleep(0.1) pid = int(open(self.deployment.pid_file).read()) yield self.deployment.destroy() # give the process time to die. yield self.sleep(0.1) e = self.assertRaises(OSError, os.kill, pid, 0) self.assertEqual(e.errno, 3) self.assertFalse(os.path.exists(self.deployment.directory)) self.assertFalse(os.path.exists(self.deployment.pid_file)) def test_service_unit_destroy_stale_pid(self): """ A stale pid file does not cause any errors. We mock away is_running as otherwise it will check for this, but there exists a small window when the result may disagree. """ self.makeFile("8917238", path=self.deployment.pid_file) mock_deployment = self.mocker.patch(self.deployment) mock_deployment.is_running() self.mocker.result(succeed(True)) self.mocker.replay() return self.deployment.destroy() def test_service_unit_destroy_perm_error(self): """ A stale pid file does not cause any errors. We mock away is_running as otherwise it will check for this, but there exists a small window when the result may disagree. """ if os.geteuid() == 0: return self.makeFile("1", path=self.deployment.pid_file) mock_deployment = self.mocker.patch(self.deployment) mock_deployment.is_running() self.mocker.result(succeed(True)) self.mocker.replay() return self.assertFailure(self.deployment.destroy(), OSError) @inlineCallbacks def test_service_unit_destroy_undeployed(self): """ If the unit is has not been deployed, nothing happens. """ yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) @inlineCallbacks def test_service_unit_destroy_not_running(self): """ If the unit is not running, then destroy will just remove its directory. """ self.deployment.unpack_charm(self.bundle) self.assertTrue(os.path.exists(self.deployment.directory)) yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) def test_unpack_charm(self): """ The deployment unpacks a charm bundle into the unit workspace. """ self.deployment.unpack_charm(self.bundle) unit_path = os.path.join( self.units_directory, self.unit_name.replace("/", "-")) self.assertTrue(os.path.exists(unit_path)) charm_path = os.path.join(unit_path, "charm") self.assertTrue(os.path.exists(charm_path)) charm = get_charm_from_path(charm_path) self.assertEqual( charm.get_revision(), self.charm.get_revision()) def test_unpack_charm_exception_invalid_charm(self): """ If the charm bundle is corrupted or invalid a deployment specific error is raised. """ error = self.assertRaises( UnitDeploymentError, self.deployment.unpack_charm, self.charm) self.assertEquals( str(error), "Invalid charm for deployment: %s" % self.charm.path) def test_is_running_no_pid_file(self): """ If there is no pid file the service unit is not running. """ self.assertEqual((yield self.deployment.is_running()), False) def test_is_running(self): """ The service deployment will check the pid and validate that the pid found is a running process. """ self.makeFile( str(os.getpid()), path=self.deployment.pid_file) self.assertEqual((yield self.deployment.is_running()), True) def test_is_running_against_unknown_error(self): """ If we don't have permission to access the process, the original error should get passed along. """ if os.geteuid() == 0: return self.makeFile("1", path=self.deployment.pid_file) self.assertFailure(self.deployment.is_running(), OSError) def test_is_running_invalid_pid_file(self): """ If the pid file is corrupted on disk, and does not contain a valid integer, then the agent is not running. """ self.makeFile("abcdef", path=self.deployment.pid_file) self.assertEqual( (yield self.deployment.is_running()), False) def test_is_running_invalid_pid(self): """ If the pid file refers to an invalid process then the agent is not running. """ self.makeFile("669966", path=self.deployment.pid_file) self.assertEqual( (yield self.deployment.is_running()), False)
class UnitMachineDeploymentTest(RepositoryTestBase): def setUp(self): super(UnitMachineDeploymentTest, self).setUp() self.charm = get_charm_from_path(self.sample_dir1) self.bundle = self.charm.as_bundle() self.juju_directory = self.makeDir() self.units_directory = os.path.join(self.juju_directory, "units") os.mkdir(self.units_directory) self.unit_name = "wordpress/0" self.rootfs = self.makeDir() self.init_dir = os.path.join(self.rootfs, "etc", "init") os.makedirs(self.init_dir) self.real_init_dir = self.patch( UpstartService, "init_dir", self.init_dir) self.deployment = UnitMachineDeployment( self.unit_name, self.juju_directory) self.assertEqual( self.deployment.unit_agent_module, "juju.agents.unit") self.deployment.unit_agent_module = "juju.agents.dummy" def setup_mock(self): self.check_call = self.mocker.replace("subprocess.check_call") self.getProcessOutput = self.mocker.replace( "twisted.internet.utils.getProcessOutput") def mock_is_running(self, running): self.getProcessOutput("/sbin/status", ["juju-wordpress-0"]) if running: self.mocker.result(succeed( "juju-wordpress-0 start/running, process 12345")) else: self.mocker.result(succeed("juju-wordpress-0 stop/waiting")) def _without_sudo(self, args, **_): self.assertEquals(args[0], "sudo") return subprocess.call(args[1:]) def mock_install(self): self.check_call(ANY, KWARGS) # cp to init dir self.mocker.call(self._without_sudo) self.check_call(ANY, KWARGS) # chmod 644 self.mocker.call(self._without_sudo) def mock_start(self): self.check_call(("sudo", "/sbin/start", "juju-wordpress-0"), KWARGS) self.mocker.result(0) for _ in range(5): self.mock_is_running(True) def mock_destroy(self): self.check_call(("sudo", "/sbin/stop", "juju-wordpress-0"), KWARGS) self.mocker.result(0) self.check_call(ANY, KWARGS) # rm from init dir self.mocker.call(self._without_sudo) def assert_pid_running(self, pid, expect): self.assertEquals(os.path.exists("/proc/%s" % pid), expect) def test_unit_name_with_path_manipulation_raises_assertion(self): self.assertRaises( AssertionError, UnitMachineDeployment, "../../etc/password/zebra/0", self.units_directory) def test_unit_directory(self): self.assertEqual( self.deployment.directory, os.path.join(self.units_directory, self.unit_name.replace("/", "-"))) def test_service_unit_start(self): """ Starting a service unit will result in a unit workspace being created if it does not exist and a running service unit agent. """ self.setup_mock() self.mock_install() self.mock_is_running(False) self.mock_start() self.mocker.replay() d = self.deployment.start( "123", get_test_zookeeper_address(), self.bundle) def verify_upstart(_): conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf") with open(conf_path) as f: lines = f.readlines() env = [] for line in lines: if line.startswith("env "): env.append(line[4:-1].split("=", 1)) if line.startswith("exec "): exec_ = line[5:-1] env = dict((k, v.strip('"')) for (k, v) in env) env.pop("PYTHONPATH") self.assertEquals(env, { "JUJU_HOME": self.juju_directory, "JUJU_UNIT_NAME": self.unit_name, "JUJU_ZOOKEEPER": get_test_zookeeper_address(), "JUJU_MACHINE_ID": "123"}) log_file = os.path.join( self.deployment.directory, "charm.log") command = " ".join([ "/usr/bin/python", "-m", "juju.agents.dummy", "--nodaemon", "--logfile", log_file, "--session-file", "/var/run/juju/unit-wordpress-0-agent.zksession", ">> /tmp/juju-wordpress-0.output 2>&1"]) self.assertEquals(exec_, command) d.addCallback(verify_upstart) return d @inlineCallbacks def test_service_unit_destroy(self): """ Forcibly stop a unit, and destroy any directories associated to it on the machine, and kills the unit agent process. """ self.setup_mock() self.mock_install() self.mock_is_running(False) self.mock_start() self.mock_is_running(True) self.mock_destroy() self.mocker.replay() yield self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf") self.assertFalse(os.path.exists(conf_path)) @inlineCallbacks def test_service_unit_destroy_undeployed(self): """ If the unit is has not been deployed, nothing happens. """ yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) @inlineCallbacks def test_service_unit_destroy_not_running(self): """ If the unit is not running, then destroy will just remove its directory. """ self.setup_mock() self.mock_install() self.mock_is_running(False) self.mock_start() self.mock_is_running(False) self.check_call(ANY, KWARGS) # rm from init dir self.mocker.call(self._without_sudo) self.mocker.replay() yield self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) yield self.deployment.destroy() self.assertFalse(os.path.exists(self.deployment.directory)) conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf") self.assertFalse(os.path.exists(conf_path)) def test_unpack_charm(self): """ The deployment unpacks a charm bundle into the unit workspace. """ self.deployment.unpack_charm(self.bundle) unit_path = os.path.join( self.units_directory, self.unit_name.replace("/", "-")) self.assertTrue(os.path.exists(unit_path)) charm_path = os.path.join(unit_path, "charm") self.assertTrue(os.path.exists(charm_path)) charm = get_charm_from_path(charm_path) self.assertEqual( charm.get_revision(), self.charm.get_revision()) def test_unpack_charm_exception_invalid_charm(self): """ If the charm bundle is corrupted or invalid a deployment specific error is raised. """ error = self.assertRaises( UnitDeploymentError, self.deployment.unpack_charm, self.charm) self.assertEquals( str(error), "Invalid charm for deployment: %s" % self.charm.path) @inlineCallbacks def test_is_running_not_installed(self): """ If there is no conf file the service unit is not running. """ self.assertEqual((yield self.deployment.is_running()), False) @inlineCallbacks def test_is_running_not_running(self): """ If the conf file exists, but job not running, unit not running """ conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf") with open(conf_path, "w") as f: f.write("blah") self.setup_mock() self.mock_is_running(False) self.mocker.replay() self.assertEqual((yield self.deployment.is_running()), False) @inlineCallbacks def test_is_running_success(self): """ Check running job. """ conf_path = os.path.join(self.init_dir, "juju-wordpress-0.conf") with open(conf_path, "w") as f: f.write("blah") self.setup_mock() self.mock_is_running(True) self.mocker.replay() self.assertEqual((yield self.deployment.is_running()), True) @uses_sudo @inlineCallbacks def test_run_actual_process(self): # "unpatch" to use real /etc/init self.patch(UpstartService, "init_dir", self.real_init_dir) yield self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) old_pid = yield self.deployment.get_pid() self.assert_pid_running(old_pid, True) # Give the job a chance to fall over and be restarted (if the # pid doesn't change, that hasn't hapened) yield self.sleep(0.1) self.assertEquals((yield self.deployment.get_pid()), old_pid) self.assert_pid_running(old_pid, True) # Kick the job over ourselves; check it comes back os.system("sudo kill -9 %s" % old_pid) yield self.sleep(0.1) self.assert_pid_running(old_pid, False) new_pid = yield self.deployment.get_pid() self.assertNotEquals(new_pid, old_pid) self.assert_pid_running(new_pid, True) yield self.deployment.destroy() self.assertEquals((yield self.deployment.get_pid()), None) self.assert_pid_running(new_pid, False) @uses_sudo @inlineCallbacks def test_fail_to_run_actual_process(self): self.deployment.unit_agent_module = "haha.disregard.that" self.patch(UpstartService, "init_dir", self.real_init_dir) d = self.deployment.start( "0", get_test_zookeeper_address(), self.bundle) e = yield self.assertFailure(d, UnitDeploymentError) self.assertTrue(str(e).startswith( "Failed to start job juju-wordpress-0; got output:\n")) self.assertIn("No module named haha", str(e)) yield self.deployment.destroy()