Jenkinsfile multiline sh continue on error - bash

I want to do some Docker cleanup steps before a Jenkins build. This is the build step:
steps {
script {
try {
sh '''
docker container stop $(docker ps -q)
docker rm $(docker ps -aq)
docker rmi -f $(docker images -q)
docker build -f Dockerfile_build | tee buildlog.txt
'''
} catch(err) {
echo err.getMessage()
}
}
}
The first lines of the sh part may fail, causing the whole build to fail:
[Pipeline] sh
++ docker ps -q
+ docker container stop Error: you must provide at least one name or id
[Pipeline] echo
script returned exit code 125
However, that only means that there's no cleanup to do. I want to continue the build job, no matter how many of the first three lines fail. My question is whether I have to put each of them in its own try/catch block, or if there's a more concise way of saying "try these and ignore any errors".

If you want to continue your script even if you get errors for individual commands you can set +e in your script, but the sh step will never error out. You can also ignore first 3 commands and then error out the last command as well by setting -e and removing it. See the example below.
sh '''
#!/bin/bash
set +e
docker container stop $(docker ps -q)
docker rm $(docker ps -aq)
docker rmi -f $(docker images -q)
set -e
docker build -f Dockerfile_build | tee buildlog.txt
'''

Related

Stop and remove docker container with force ignore

How can I delete a docker container with force ignore that means if there is no running docker container it should do nothing and if there is a running docker container with the name then it should stop and remove that container.
I am executing the following code to stop and remove the container.
docker rm -f CONTAINER_NAME || true
If there is a running container then everything works fine, however, if there is no container then the following error is thrown:
Error: No such container: CONTAINER_NAME
Is there something like --force ignore? I need this behaviour in order to include it in an automated Makefile.
try this exit code will be 1:
docker rm -f CONTAINER_NAME 2> /dev/null
this with exit code 0:
docker rm -f CONTAINER_NAME 2> /dev/null || true
Makefiles have built-in support to ignore errors on a specific command by adding a dash before the command.
rmDocker:
-docker rm -f CONTAINER_NAME
#echo "Container removed!"
You'll still see the error message, but the makefile will ignore the error and proceed anyway.
Output:
docker rm -f CONTAINER_NAME
Error: No such container: CONTAINER_NAME
make: [rmDocker] Error 1 (ignored)
Container removed!
Reference: GNU Make Manual
You can add OR true value, as per below:
One Container
docker rm -f CONTAINER_1 || true
Multiple Containers
(docker rm -f CONTAINER_1 || true) && (docker rm -f CONTAINER_2 || true)
I prefer
docker container inspect CONTAINER_NAME &>/dev/null && docker rm -f CONTAINER_NAME
Solution based on this answer: docker container inspect sets return-code to 1 if container does not exist, else sets it to 0, so docker rm will be executed, too.
As an alternative you can run container with docker run --rm. Container will remove itself once stopped.

Jenkins shell dont interpret $ variables

I am trying to deploy a nodejs app inside docker container on a prod machine using jenkins.
I have this shell :
ssh -tt vagrant#10.2.3.129<<EOF
cd ~/app/backend
git pull
cat <<EOM >./Dockerfile
FROM node:8
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD [ "npm", "start" ]
EOM
docker build -t vagrant/node-web-app .
docker kill $(docker ps -q)
docker rm $(docker ps -a -q)
docker run -p 3000:3000 -d vagrant/node-web-app
exit
EOF
this will connect via ssh to prod machine and create a Dockerfile then build and run image. but it failed.
and this a part of the jenkins logs:
Successfully built 8e5796ea9846
vagrant#ubuntu-xenial:~$ docker kill
"docker kill" requires at least 1 argument.
See 'docker kill --help'.
Usage: docker kill [OPTIONS] CONTAINER [CONTAINER...]
Kill one or more running containers
vagrant#ubuntu-xenial:~$ docker rm
"docker rm" requires at least 1 argument.
See 'docker rm --help'.
Usage: docker rm [OPTIONS] CONTAINER [CONTAINER...]
Remove one or more containers
vagrant#ubuntu-xenial:~$ docker run -p 3000:3000 -d vagrant/node-web-app
0cc8b5b67f70065ace03e744500b5b66c79941b4cb36d53a3186845445435bb5
docker: Error response from daemon: driver failed programming external connectivity on endpoint stupefied_margulis (d0e4cdd5642c288a31537e1bb8feb7dde2d19c0f83fe5d8fdb003dcba13f53a0): Bind for 0.0.0.0:3000 failed: port is already allocated.
vagrant#ubuntu-xenial:~$ exit
logout
Connection to 10.2.1.129 closed.
Build step 'Execute shell' marked build as failure
Finished: FAILURE
It seems like jenkins dont execute the " $(docker ps -q) "
and " $(docker ps -a -q) "
so docker kill and docker rm got 0 arguments.
But why this happen ?
I found the issue,
Just I have to replace "$" with "\$" .
this solve the problem.

Is there a flag to silence docker when an empty list is provided

Such a feature is useful when running multiple docker commands in one that follow this pattern:
docker do_smth $(docker query_smth)
For example:
docker stop $(docker ps -q)
or
docker rm $(docker ps -a -q)
or
docker network rm $(docker inspect ... --format ...)
If the inner docker command returns an empty list, the outer command will fail because and will display the help.
"docker stop" requires at least 1 argument.
See 'docker stop --help'.
Usage: docker stop [OPTIONS] CONTAINER [CONTAINER...] [flags]
Stop one or more running containers
Is there a way to silence docker or make docker not complain on empty lists? Something like: "Kill everybody. If there is no one, job done."
This would be similar to mkdir -p exiting_directory vs mkdir exiting_directory where the former will not complain if the directories exist.
For scripting where the result may be empty, I prefer to use xargs --no-run-if-empty:
docker ps -aq | xargs --no-run-if-empty docker rm

How can I remove all containers by image name and do nothing if it already removed

I have bash script which stops and remove all docker container by image name.
I can stop and remove all docker container by image name by single command
docker rm $(docker stop $( docker ps -a -q --filter ancestor=image_name))
But if container not exist this expression docker ps -a -q --filter ancestor=image_name not returns nothing and docker stop command fails. How can I remove all containers by image name and do nothing if it already removed?
Try this...
for i in $(docker ps -a -q --filter ancestor=image_name); do docker rm $(docker stop $i); done
It should only call docker rm if docker ps returns results. Works in my environment.

Docker kill not working when executed in shell script

The following works fine when running the commands manually line by line in the terminal:
docker create -it --name test path
docker start test
docker exec test /bin/sh -c "go test ./..."
docker stop test
docker rm -test
But when I run it as a shell script, the Docker container is neither stopped nor removed.
#!/usr/bin/env bash
set -e
docker create -it --name test path
docker start test
docker exec test /bin/sh -c "go test ./..."
docker stop test
docker rm -test
How can I make it work from within a shell script?
If you use set -e the script will exit when any command fails. i.e. when a commands return code != 0. This means if your start, exec or stop fails, you will be left with a container still there.
You can remove the set -e but you probably still want to use the return code for the go test command as the overall return code.
#!/usr/bin/env bash
docker create -it --name test path
docker start test
docker exec test /bin/sh -c "go test ./..."
rc=$?
docker stop test
docker rm test
exit $rc
Trap
Using set -e is actually quite useful and catches a lot of issues that are silently ignored in most scripts. A slightly more complex solution is to use a trap to run your clean up steps on EXIT, which means set -e can be used.
#!/usr/bin/env bash
set -e
# Set a default return code
RC=2
# Cleanup
function cleanup {
echo "Removing container"
docker stop test || true
docker rm -f test || true
exit $RC
}
trap cleanup EXIT
# Test steps
docker create -it --name test path
docker start test
docker exec test /bin/sh -c "go test ./..."
RC=$?

Resources