Ejemplo n.º 1
0
    def execute(self):
        binary = self.require_single_root_target()
        if isinstance(binary, PythonBinary):
            # We can't throw if binary isn't a PythonBinary, because perhaps we were called on a
            # jvm_binary, in which case we have to no-op and let jvm_run do its thing.
            # TODO(benjy): Use MutexTask to coordinate this.

            pex = self.create_pex(binary.pexinfo)
            args = [*self.get_passthru_args(), *self.get_options().args]

            env = self.prepare_pex_env()

            self.context.release_lock()
            cmdline = " ".join(pex.cmdline(args))
            with self.context.new_workunit(
                name="run", cmd=cmdline, labels=[WorkUnitLabel.TOOL, WorkUnitLabel.RUN]
            ):
                po = pex.run(blocking=False, args=args, env=env)
                try:
                    result = po.wait()
                    if result != 0:
                        msg = f"{cmdline} ... exited non-zero ({result})"
                        raise TaskError(msg, exit_code=result)
                except KeyboardInterrupt:
                    # The process may still have exited, even if we were interrupted.
                    safe_kill(po.pid, signal.SIGINT)
                    raise
Ejemplo n.º 2
0
    def execute(self):
        binary = self.require_single_root_target()
        if isinstance(binary, PythonBinary):
            # We can't throw if binary isn't a PythonBinary, because perhaps we were called on a
            # jvm_binary, in which case we have to no-op and let jvm_run do its thing.
            # TODO(benjy): Use MutexTask to coordinate this.

            pex = self.create_pex(binary.pexinfo)
            args = []
            for arg in self.get_options().args:
                args.extend(safe_shlex_split(arg))
            args += self.get_passthru_args()

            env = os.environ.copy()
            env.update(self.ensure_interpreter_search_path_env())

            self.context.release_lock()
            cmdline = ' '.join(pex.cmdline(args))
            with self.context.new_workunit(
                    name='run',
                    cmd=cmdline,
                    labels=[WorkUnitLabel.TOOL, WorkUnitLabel.RUN]):
                po = pex.run(blocking=False, args=args, env=env)
                try:
                    result = po.wait()
                    if result != 0:
                        msg = '{cmdline} ... exited non-zero ({code})'.format(
                            cmdline=cmdline, code=result)
                        raise TaskError(msg, exit_code=result)
                except KeyboardInterrupt:
                    # The process may still have exited, even if we were interrupted.
                    safe_kill(po.pid, signal.SIGINT)
                    raise
Ejemplo n.º 3
0
    def maybe_send_signal(self, signum):
        """Send the signal `signum`. No error is raised if the pid is None.

        :param signum: The signal number to send to the remote process.
        """
        if self.remote_pid is not None:
            safe_kill(self.remote_pid, signum)
Ejemplo n.º 4
0
  def _process_session(self):
    """Process the outputs of the nailgun session.

    :raises: :class:`NailgunProtocol.ProcessStreamTimeout` if a timeout set from a signal handler
                                                           with .set_exit_timeout() completes.
    :raises: :class:`Exception` if the session completes before the timeout, the `reason` argument
                                to .set_exit_timeout() will be raised."""
    try:
      for chunk_type, payload in self.iter_chunks(
        MaybeShutdownSocket(self._sock),
        return_bytes=True,
        timeout_object=self,
      ):
        # TODO(#6579): assert that we have at this point received all the chunk types in
        # ChunkType.REQUEST_TYPES, then require PID and PGRP (exactly once?), and then allow any of
        # ChunkType.EXECUTION_TYPES.
        if chunk_type == ChunkType.STDOUT:
          self._write_flush(self._stdout, payload)
        elif chunk_type == ChunkType.STDERR:
          self._write_flush(self._stderr, payload)
        elif chunk_type == ChunkType.EXIT:
          self._write_flush(self._stdout)
          self._write_flush(self._stderr)
          return int(payload)
        elif chunk_type == ChunkType.PID:
          self.remote_pid = int(payload)
          self.remote_process_cmdline = psutil.Process(self.remote_pid).cmdline()
          if self._remote_pid_callback:
            self._remote_pid_callback(self.remote_pid)
        elif chunk_type == ChunkType.PGRP:
          self.remote_pgrp = int(payload)
          if self._remote_pgrp_callback:
            self._remote_pgrp_callback(self.remote_pgrp)
        elif chunk_type == ChunkType.START_READING_INPUT:
          self._maybe_start_input_writer()
        else:
          raise self.ProtocolError('received unexpected chunk {} -> {}'.format(chunk_type, payload))
    except NailgunProtocol.ProcessStreamTimeout as e:
      assert(self.remote_pid is not None)
      # NB: We overwrite the process title in the pantsd process, which causes it to have an
      # argv with lots of empty spaces for some reason. We filter those out and pretty-print the
      # rest here.
      filtered_remote_cmdline = safe_shlex_join(
        arg for arg in self.remote_process_cmdline if arg != '')
      logger.warning(
        "timed out when attempting to gracefully shut down the remote client executing \"{}\". "
        "sending SIGKILL to the remote client at pid: {}. message: {}"
        .format(filtered_remote_cmdline, self.remote_pid, e))
    finally:
      # Bad chunk types received from the server can throw NailgunProtocol.ProtocolError in
      # NailgunProtocol.iter_chunks(). This ensures the NailgunStreamWriter is always stopped.
      self._maybe_stop_input_writer()
      # If an asynchronous error was set at any point (such as in a signal handler), we want to make
      # sure we clean up the remote process before exiting with error.
      if self._exit_reason:
        if self.remote_pgrp:
          safe_kill(self.remote_pgrp, signal.SIGKILL)
        if self.remote_pid:
          safe_kill(self.remote_pid, signal.SIGKILL)
        raise self._exit_reason
Ejemplo n.º 5
0
  def _process_session(self):
    """Process the outputs of the nailgun session.

    :raises: :class:`NailgunProtocol.ProcessStreamTimeout` if a timeout set from a signal handler
                                                           with .set_exit_timeout() completes.
    :raises: :class:`Exception` if the session completes before the timeout, the `reason` argument
                                to .set_exit_timeout() will be raised."""
    try:
      for chunk_type, payload in self.iter_chunks(
        MaybeShutdownSocket(self._sock),
        return_bytes=True,
        timeout_object=self,
      ):
        # TODO(#6579): assert that we have at this point received all the chunk types in
        # ChunkType.REQUEST_TYPES, then require PID and PGRP (exactly once?), and then allow any of
        # ChunkType.EXECUTION_TYPES.
        if chunk_type == ChunkType.STDOUT:
          self._write_flush(self._stdout, payload)
        elif chunk_type == ChunkType.STDERR:
          self._write_flush(self._stderr, payload)
        elif chunk_type == ChunkType.EXIT:
          self._write_flush(self._stdout)
          self._write_flush(self._stderr)
          return int(payload)
        elif chunk_type == ChunkType.PID:
          self.remote_pid = int(payload)
          self.remote_process_cmdline = psutil.Process(self.remote_pid).cmdline()
          if self._remote_pid_callback:
            self._remote_pid_callback(self.remote_pid)
        elif chunk_type == ChunkType.PGRP:
          self.remote_pgrp = int(payload)
          if self._remote_pgrp_callback:
            self._remote_pgrp_callback(self.remote_pgrp)
        elif chunk_type == ChunkType.START_READING_INPUT:
          self._maybe_start_input_writer()
        else:
          raise self.ProtocolError('received unexpected chunk {} -> {}'.format(chunk_type, payload))
    except NailgunProtocol.ProcessStreamTimeout as e:
      assert(self.remote_pid is not None)
      # NB: We overwrite the process title in the pantsd process, which causes it to have an
      # argv with lots of empty spaces for some reason. We filter those out and pretty-print the
      # rest here.
      filtered_remote_cmdline = safe_shlex_join(
        arg for arg in self.remote_process_cmdline if arg != '')
      logger.warning(
        "timed out when attempting to gracefully shut down the remote client executing \"{}\". "
        "sending SIGKILL to the remote client at pid: {}. message: {}"
        .format(filtered_remote_cmdline, self.remote_pid, e))
    finally:
      # Bad chunk types received from the server can throw NailgunProtocol.ProtocolError in
      # NailgunProtocol.iter_chunks(). This ensures the NailgunStreamWriter is always stopped.
      self._maybe_stop_input_writer()
      # If an asynchronous error was set at any point (such as in a signal handler), we want to make
      # sure we clean up the remote process before exiting with error.
      if self._exit_reason:
        if self.remote_pgrp:
          safe_kill(self.remote_pgrp, signal.SIGKILL)
        if self.remote_pid:
          safe_kill(self.remote_pid, signal.SIGKILL)
        raise self._exit_reason
Ejemplo n.º 6
0
  def execute(self):
    binary = self.require_single_root_target()
    if isinstance(binary, PythonBinary):
      # We can't throw if binary isn't a PythonBinary, because perhaps we were called on a
      # jvm_binary, in which case we have to no-op and let jvm_run do its thing.
      # TODO(benjy): Use MutexTask to coordinate this.

      pex = self.create_pex(binary.pexinfo)
      args = []
      for arg in self.get_options().args:
        args.extend(safe_shlex_split(arg))
      args += self.get_passthru_args()

      self.context.release_lock()
      cmdline = ' '.join(pex.cmdline(args))
      with self.context.new_workunit(name='run',
                                     cmd=cmdline,
                                     labels=[WorkUnitLabel.TOOL, WorkUnitLabel.RUN]):
        po = pex.run(blocking=False, args=args, env=os.environ.copy())
        try:
          result = po.wait()
          if result != 0:
            msg = '{cmdline} ... exited non-zero ({code})'.format(cmdline=cmdline, code=result)
            raise TaskError(msg, exit_code=result)
        except KeyboardInterrupt:
          # The process may still have exited, even if we were interrupted.
          safe_kill(po.pid, signal.SIGINT)
          raise
Ejemplo n.º 7
0
    def maybe_send_signal(self, signum, include_pgrp=True):
        """Send the signal `signum` send if the PID and/or PGRP chunks have been received.

    No error is raised if the pid or pgrp are None or point to an already-dead process.
    """
        remote_pid = self._maybe_last_pid()
        if remote_pid is not None:
            safe_kill(remote_pid, signum)
        if include_pgrp:
            remote_pgrp = self._maybe_last_pgrp()
            if remote_pgrp:
                safe_kill(remote_pgrp, signum)
Ejemplo n.º 8
0
  def maybe_send_signal(self, signum, include_pgrp=True):
    """Send the signal `signum` send if the PID and/or PGRP chunks have been received.

    No error is raised if the pid or pgrp are None or point to an already-dead process.
    """
    remote_pid = self._maybe_last_pid()
    if remote_pid is not None:
      safe_kill(remote_pid, signum)
    if include_pgrp:
      remote_pgrp = self._maybe_last_pgrp()
      if remote_pgrp:
        safe_kill(remote_pgrp, signum)
Ejemplo n.º 9
0
    def maybe_send_signal(self, signum, include_pgrp=True):
        """Send the signal `signum` send if the PID and/or PGRP chunks have been received.

        No error is raised if the pid or pgrp are None or point to an already-dead process.

        :param signum: The signal number to send to the remote process.
        :param include_pgrp: If True, it will try to kill the pgrp as well
        """
        remote_pid = self._maybe_last_pid()
        if remote_pid is not None:
            safe_kill(remote_pid, signum)
        if include_pgrp:
            remote_pgrp = self._maybe_last_pgrp()
            if remote_pgrp:
                safe_kill(remote_pgrp, signum)