go-task / task

A task runner / simpler Make alternative written in Go
https://taskfile.dev
MIT License
11.36k stars 613 forks source link

Inadequate Propagation of SIGTERM and SIGINT to Task Subprocesses #1408

Open cake-monotone opened 11 months ago

cake-monotone commented 11 months ago

Script: signal-test.sh

#!/bin/sh

# Set up signal traps
trap 'echo "Received SIGINT"' SIGINT
trap 'echo "Received SIGTERM"' SIGTERM

# Infinite loop to keep the script running
while true; do
    sleep 1
done

Task Configuration: Taskfile.yml

version: "3"

tasks:
  signal-test:
    cmds:
      - sh ./signal-test.sh

Given the process structure:

# ps output for reference
PID   PPID  CMD
2       1    task signal-test
3       2    sh ./signal-test.sh

When executing kill -INT 2, the following output is observed:

kill -INT 2
# task: Signal received: "interrupt"
# However, the signal is not propagated to `signal-test.sh`

It is critical to note that the SIGINT signal fails to reach the subprocess. This issue is particularly problematic when using Taskfile within a Docker container. Both docker stop and CTRL-C interruption during docker run attempt to send SIGINT or SIGTERM to the Taskfile process directly. The described problem prevents Docker from achieving a 'graceful exit'.

cake-monotone commented 11 months ago

How about this flows?

  1. Propagate SIGINT or SIGTERM signals directly to the currently running command.
  2. Allow the command to complete its process.
  3. If the command finishes, the taskfile should stop without triggering any further commands.

What do you think of this idea?

Elijas commented 9 months ago

+1 Same here - the described problem prevents Docker from achieving a 'graceful exit'. Any solutions?

lookitsatravis commented 5 months ago

I'm also experiencing this issue with Docker. My work around is to start the container with -d and then I have a stop task which I run at the beginning of the stop task to try and kill the container. It's fine, but it would be better if signals propagated.

Here's what I do for anyone who is dealing with this for Docker:

# https://taskfile.dev

version: "3"

vars:
  IMAGE_NAME: my-app
  IMAGE_TAG: latest
  IMAGE: "{{.IMAGE_NAME}}:{{.IMAGE_TAG}}"

tasks:
  default:
    desc: Shows this help message
    cmds:
      - task --list-all

  build:
    desc: Build the app
    cmds:
      - docker build . -t {{.IMAGE}}

  start:
    desc: Start the app
    deps:
      - build
    cmds:
      - cmd: task stop
        ignore_error: true
      - docker run -d --rm -p 8080:8080 --name my-app {{.IMAGE}}
      - cmd: docker logs --follow my-app
        ignore_error: true

  stop:
    desc: Stop the app
    cmds:
      - docker stop my-app
trulede commented 5 months ago

The signal handling is consuming these signals. You could try an experiment and comment out this line and see if it works like you expect.

Or, an alternative might be to send a different signal and see if that gets propagated. For instance, HUP as follows:

docker kill --signal=SIGHUP  task_container

and then you need a program that catches that ... basic idea here https://gobyexample.com/signals.

The actual process that Task runs seems to come into existence here and AFAICS nothing would prevent a signal propagating through (other than they are currently consumed by Task implementation).