Search code examples
dockernyc

How do I hold stopping docker container to get NYC report. Sleep doesn't work


I run NYC in my nodejs program that uses cluster to start child processes. I've used dumb-init to propagate the SIGINT to my program where I gracefully handle the signal. When I kill the program within the docker container, I get the coverage but when I do docker stop it doesn't wait for all the clusters to die, because of this NYC coverage isn't computed.

Is there any way to delay docker from exiting so that the coverage data created is saved?

The dockerfile executes a script which calls yarn start and in the start script, I've added NYC. I tried adding sleep in script after the yarn start but to no avail.

My dockerfile looks like this:-

FROM node:14
ARG ENVIRONMENT_NAME
ARG BUILD_NAME
ARG APP_PATH
ENV APP_PATH=${APP_PATH:-/default/path} 
RUN mkdir -p ${APP_PATH}
ADD . ${APP_PATH}
WORKDIR ${APP_PATH}
RUN --mount=type=cache,target=/root/.yarn YARN_CACHE_FOLDER=/root/.yarn yarn
RUN yarn
RUN yarn build:$BUILD_NAME


FROM node:14-alpine
ARG ENVIRONMENT_NAME
ARG BUILD_NAME
ARG APP_PATH
ENV APP_PATH=${APP_PATH:-/default/path} 
RUN yarn global add sequelize-cli@6.2.0 nyc
RUN yarn add shelljs bull dotenv pg sequelize@6.6.5
RUN apk add --no-cache dumb-init
ADD scripts/migrate-and-run.sh ${APP_PATH}/
ADD package.json ${APP_PATH}/
ADD . ${APP_PATH}/
COPY --from=0 ${APP_PATH}/dist ${APP_PATH}/dist
ADD https://keploy-enterprise.s3.us-west-2.amazonaws.com/releases/latest/assets/freeze_time_arm64.so /lib/keploy/freeze_time_arm64.so
RUN chmod +x /lib/keploy/freeze_time_arm64.so
ENV LD_PRELOAD=/lib/keploy/freeze_time_arm64.so


# Set working directory
WORKDIR ${APP_PATH}
ENTRYPOINT ["dumb-init"]

# RUN echo "hi"

# Set entrypoint and command
CMD [ "sh","./scripts/migrate-and-run.sh"]

# Expose port 9000
EXPOSE 9000

The script that is executed in the dockerfile

set -a . ".env$ENVIRONMENT_NAME" set +a
sleep 10
echo $BUILD_NAME
if [ "$BUILD_NAME" == "local" ]
then
    npx sequelize-cli db:drop
    npx sequelize-cli db:create
fi

npx sequelize-cli db:migrate

# seed data for local builds
if [ "$BUILD_NAME" == "local" ]
then
    for file in seeders/*
    do
        :
        npx sequelize-cli db:seed --seed $file
    done
fi

yarn start

Solution

  • Maybe there is a way to use the script to help resolve this problem.

    You can trap the signal in the script and send it to the running child processes and make the child wait till it is completed.

    #!/bin/sh
    
    set -a
    . ".env.$ENVIRONMENT_NAME"
    set +a
    
    # Function to handle SIGINT signal
    handle_int() {
        echo "SIGINT received, forwarding to child process..."
        kill -INT "$child" 2>/dev/null
        echo "Waiting for child process to exit..."
        wait "$child"
        echo "Child process exited. Waiting for NYC coverage data to be fully written..."
        sleep 10  # Wait for 50 seconds
        echo "Exiting after delay..."
        exit 0
    }
    
    # Trap SIGINT signal
    trap 'handle_int' INT TERM
    
    sleep 10  # Ensure services like DB are up
    
    echo $BUILD_NAME
    if [ "$BUILD_NAME" = "local" ]; then
        npx sequelize-cli db:drop
        npx sequelize-cli db:create
    fi
    
    echo $LD_PRELOAD
    npx sequelize-cli db:migrate
    
    # Seed data for local builds
    if [ "$BUILD_NAME" = "local" ]; then
        for file in seeders/*; do
            npx sequelize-cli db:seed --seed $file
        done
    fi
    
    # Start yarn and get its PID
    yarn start &
    
    child=$!
    echo "Started yarn with PID $child"
    
    wait "$child"
    

    Here the wait "$child" command in a shell script is used to pause the execution of the script until the process identified by the variable $child terminates.

    Why This Process Is Beneficial Synchronisation:

    1. You may make sure that any cleanup or follow-up actions (such as copying NYC coverage data) only take place after the program has ceased executing by waiting for the child process to die.
    2. It enables appropriate signal handling in the script. For instance, the script can wait for the child process to end before performing any necessary cleanup if it receives a SIGINT.
    3. It makes sure that commands that come after wait "$child" are only run when the child process has completed, preserving the proper sequence of events.