def finish(self): """Clean-up the upgrade-tool files and report about package changes.""" shutil.rmtree(self._config.upgrade_tool_directory) if os.getuid() == 0: uid = pwd.getpwnam("landscape").pw_uid gid = grp.getgrnam("landscape").gr_gid else: uid = None gid = None reporter = find_reporter_command(self._config) # Force an apt-update run, because the sources.list has changed args = ["--force-apt-update"] if self._config.config is not None: args.append("--config=%s" % self._config.config) return spawn_process(reporter, args=args, uid=uid, gid=gid, path=os.getcwd(), env=os.environ)
def test_spawn_process_output(self): """ The process returns the expected standard output. """ def callback(args): out, err, code = args self.assertEqual(out, b"a b") self.assertEqual(err, b"") self.assertEqual(code, 0) result = spawn_process(self.command, args=("a", "b")) result.addCallback(callback) return result
def test_spawn_process_with_stdin(self): """ Optionally C{spawn_process} accepts a C{stdin} argument. """ create_text_file(self.command, "#!/bin/sh\n/bin/cat") def callback(args): out, err, code = args self.assertEqual(b"hello", out) result = spawn_process(self.command, stdin="hello") result.addCallback(callback) return result
def run_apt_update(self): """Run apt-update and log a warning in case of non-zero exit code. @return: a deferred returning (out, err, code) """ if (self._config.force_apt_update or self._apt_sources_have_changed() or self._apt_update_timeout_expired( self._config.apt_update_interval)): result = spawn_process(self.apt_update_filename) def callback((out, err, code)): accepted_apt_errors = ( "Problem renaming the file /var/cache/apt/srcpkgcache.bin", "Problem renaming the file /var/cache/apt/pkgcache.bin") touch_file(self._config.update_stamp_filename) logging.debug( "'%s' exited with status %d (out='%s', err='%s')" % ( self.apt_update_filename, code, out, err)) if code != 0: logging.warning("'%s' exited with status %d (%s)" % ( self.apt_update_filename, code, err)) # Errors caused by missing cache files are acceptable, as # they are not an issue for the lists update process. # These errors can happen if an 'apt-get clean' is run # while 'apt-get update' is running. for message in accepted_apt_errors: if message in err: out, err, code = "", "", 0 break elif not self._facade.get_channels(): code = 1 err = ("There are no APT sources configured in %s or %s." % (self.sources_list_filename, self.sources_list_directory)) deferred = self._broker.call_if_accepted( "package-reporter-result", self.send_result, code, err) deferred.addCallback(lambda ignore: (out, err, code)) return deferred return result.addCallback(callback) else: logging.debug("'%s' didn't run, update interval has not passed" % self.apt_update_filename) return succeed(("", "", 0))
def test_spawn_process_error(self): """ The process returns the expected standard error. """ create_text_file(self.command, "#!/bin/sh\necho -n $@ >&2") def callback(args): out, err, code = args self.assertEqual(out, b"") self.assertEqual(err, b"a b") self.assertEqual(code, 0) result = spawn_process(self.command, args=("a", "b")) result.addCallback(callback) return result
def test_spawn_process_return_value(self): """ The process is executed and returns the expected exit code. """ create_text_file(self.command, "#!/bin/sh\nexit 2") def callback(args): out, err, code = args self.assertEqual(out, b"") self.assertEqual(err, b"") self.assertEqual(code, 2) result = spawn_process(self.command) result.addCallback(callback) return result
def _apt_update(self, deferred): """ Run apt-update using the passed in deferred, which allows for callers to inspect the result code. """ env = {} if self._config.http_proxy: env["http_proxy"] = self._config.http_proxy if self._config.https_proxy: env["https_proxy"] = self._config.https_proxy result = spawn_process(self.apt_update_filename, env=env) def callback(args, deferred): return deferred.callback(args) return result.addCallback(callback, deferred)
def upgrade(self, code_name, operation_id, allow_third_party=False, debug=False): """Run the upgrade-tool command and send a report of the results. @param code_name: The code-name of the release to upgrade to. @param operation_id: The activity id for this task. @param allow_third_party: Whether to enable non-official APT repo. @param debug: Whether to turn on debug level logging. """ upgrade_tool_directory = self._config.upgrade_tool_directory upgrade_tool_filename = os.path.join(upgrade_tool_directory, code_name) args = ["--frontend", "DistUpgradeViewNonInteractive"] env = os.environ.copy() if allow_third_party: env["RELEASE_UPRADER_ALLOW_THIRD_PARTY"] = "True" if debug: env["DEBUG_UPDATE_MANAGER"] = "True" result = spawn_process(upgrade_tool_filename, args=args, env=env, path=upgrade_tool_directory, wait_pipes=False) def send_operation_result(args): out, err, code = args out = out.decode("utf-8") err = err.decode("utf-8") if code == 0: status = SUCCEEDED else: status = FAILED text = self.make_operation_result_text(out, err) message = self.make_operation_result_message( operation_id, status, text, code) logging.info("Queuing message with release upgrade results to " "exchange urgently.") return self._send_message(message) result.addCallback(send_operation_result) return result
def test_spawn_process_callback_multiple_newlines(self): """ If output ends with more than one newline, empty lines are preserved. """ create_text_file(self.command, "#!/bin/sh\n/bin/echo -ne $@") param = r"some text\nanother line\n\n\n" expected = [b"some text", b"another line", b"", b""] lines = [] def line_received(line): lines.append(line) def callback(args): out, err, code = args self.assertEqual(expected, lines) result = spawn_process(self.command, args=(param, ), line_received=line_received) result.addCallback(callback) return result
def test_spawn_process_callback(self): """ If a callback for process output is provieded, it is called for every line of output. """ create_text_file(self.command, "#!/bin/sh\n/bin/echo -ne $@") param = r"some text\nanother line\nok, last one\n" expected = [b"some text", b"another line", b"ok, last one"] lines = [] def line_received(line): lines.append(line) def callback(args): out, err, code = args self.assertEqual(expected, lines) result = spawn_process(self.command, args=(param, ), line_received=line_received) result.addCallback(callback) return result
def test_spawn_process_callback_no_newline(self): """ If output ends without a newline, the line is still passed to the callback. """ create_text_file(self.command, "#!/bin/sh\n/bin/echo -ne $@") param = r"some text\nanother line\nok, last one" expected = [b"some text", b"another line", b"ok, last one"] lines = [] def line_received(line): lines.append(line) def callback(args): out, err, code = args self.assertEqual(expected, lines) result = spawn_process(self.command, args=(param, ), line_received=line_received) result.addCallback(callback) return result
def test_spawn_process_with_signal(self): """ In case the process gets terminated by a signal, it raises a C{SignalError}. """ # This script will kill itself. create_text_file(self.command, """\ #!/bin/sh kill $$ """) def callback(args): raise RuntimeError("Errback was not called.") def errback(failure): out, err, code = failure.value.args self.assertEqual(out, b"") self.assertEqual(err, b"") self.assertEqual(code, 15) # 15 for SIGTERM result = spawn_process(self.command) result.addCallbacks(callback, errback) return result
def _run_process(self, command, args, uid=None, gid=None): """ Run the process in an asynchronous fashion, to be overriden in tests. """ return spawn_process(command, args, uid=uid, gid=gid)