How to quote entrypoint argument when updating a docker service - bash

I've a small script to update some services in my docker stack.
The problem is that I'm not able to update the entrypoint of a service.
For example, my script generates the following command :
docker service update --entrypoint 'go run main.go web' myservice:latest
If a run this command in my terminal it works like a charm but when the command is launched by my script I get the following error:
invalid argument "'go" for "--entrypoint" flag: EOF found when expecting closing quote
Here are the interesting lines in my script :
CMD="go run main web"
...
if [ -n "$CMD" ]; then
update="--entrypoint '${CMD}'"
fi
if [ -n "$update" ]; then
docker service update $update $fullname
echo "docker service update $update $fullname" // output => docker service update --entrypoint 'go run main.go web' myservice:latest
fi
Any help is welcome.

The problem is with your script. Using update as you are, you are running the following
docker service update --entrypoint \'go run main web\'
not
docker service update --entrypoint 'go run main web'
Use an array to store the arguments instead
CMD="go run main web"
if [ -n "$CMD" ]; then
update=(--entrypoint "$CMD")
fi
if [ -n "$update" ]; then # Really just checks ${update[0]}, but that's sufficient
docker service update "${update[#]}" "$fullname"
echo "docker service update ${update[*]} $fullname"
fi
Alternatively, you can test CMD directly to decide if you want to run the command with the --entrypoint option at all.
CMD="go run main web"
if [ -n "$CMD" ]; then
docker service update --entrypoint "$CMD" "$fullname"
echo "docker service --entrypoint $CMD $fullname"
fi

Related

How to run command conditionally in docker compose

I want to run command conditionally in docker-compose
because when someone run this application at first time,
They would have to run migrate command so that they can run django application properly
But If their docker have run migrate, there is no need to run migrate again
So this is the command to check that their docker have run migrate.
if [[ -z $(python3 zeus/manage.py showmigrations | grep '\[ \]')]]; then
echo 'no need to migrate'
else
echo 'need to migate'
fi
This is my docker-compose.
version: '3'
services:
db:
image: postgres
web:
command: >
bash -c "if [[ -z $(python3 zeus/manage.py showmigrations | grep '\[ \]')]]; then
echo 'no need to migrate'
else
echo 'need to migate'
fi && python3 zeus/manage.py runserver 0.0.0.0:8000
"
But Error occurs like this
ERROR: Invalid interpolation format for "build" option in service
"web": "bash -c "if [[ -z $(python3 zeus/manage.py showmigrations | grep '\[ \]')]]; then
echo 'no need to migrate' else echo 'need to migate' fi
&& python3 zeus/manage.py runserver 0.0.0.0:8000""
Any idea?
Edit
This is fine when I run the script of checking migration in normal bash
I think docker-compose can't parse $(python3 manage.py .....) part.
try this :
version: '3'
services:
db:
image: postgres
web:
command: bash -c "if [[ -z $$(python3 zeus/manage.py showmigrations | grep '\\[ \\]') ]]; then
echo 'no need to migrate';
else
echo 'need to migate';
fi && python3 zeus/manage.py runserver 0.0.0.0:8000"
three problems were there , you need to escap the escape charachter \ and add more $ to escape the replacment in compose and one more space before the last ]]
Try to avoid writing complicated scripts in docker-compose.yml, especially if they're for normal parts of your application setup.
A typical pattern is to put this sort of setup in an entrypoint script. That script ends with the shell command exec "$#". In a Docker context, that tells it to replace itself with the command (from a Dockerfile CMD statement or Docker Compose command:). For your example this could look like
#!/bin/sh
if [ -z $(python3 zeus/manage.py showmigrations | grep '\[ \]')]; then
echo 'no need to migrate'
else
echo 'need to migate'
fi
exec "$#"
Then in your Dockerfile, copy this file in and specify it as the ENTRYPOINT; leave your CMD that runs your application unmodified.
COPY entrypoint.sh /app
RUN chmod +x entrypoint.sh
ENTRYPOINT ["/app/entrypoint.sh"]
CMD python3 zeus/manage.py run --host=0.0.0.0:8000
The ENTRYPOINT statement must be the JSON-array form and must not have an explicit sh -c wrapper in it.
If you want to verify that things have gotten set up correctly, you can run
docker-compose run web sh
and you will get a shell at the point that exec "$#" is: after your migrations and other setup have run, but instead of your main server process.

Starting multiple services using shell script in Dockerfile

I am creating a Dockerfile to install and start the WebLogic 12c services using startup scripts at "docker run" command. I am passing the shell script in the CMD instruction which executes the startWeblogic.sh and startNodeManager.sh script. But when I logged in to the container, it has started only the first script startWeblogic.sh and not even started the second script which is obvious from the docker logs.
The same script executed inside the container manually and it is starting both the services. What is the right instruction for running the script to start multiple processes in a container and not to exit the container?
What am I missing in this script and in the dockerfile? I know that container can run only one process, but in a dirty way, how to start multiple services for an application like WebLogic which has a nameserver, node manager, managed server and creating managed domains and machines. The managed server can only be started when WebLogic nameserver is running.
Script: startscript.sh
#!/bin/bash
# Start the first process
/u01/app/oracle/product/wls122100/domains/verdomain/bin/startWebLogic.sh -D
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_first_process: $status"
exit $status
fi
# Start the second process
/u01/app/oracle/product/wls122100/domains/verdomain/bin/startNodeManager.sh -D
status=$?
if [ $status -ne 0 ]; then
echo "Failed to start my_second_process: $status"
exit $status
fi
while sleep 60; do
ps aux |grep "Name=adminserver" |grep -q -v grep
PROCESS_1_STATUS=$?
ps aux |grep node |grep -q -v grep
PROCESS_2_STATUS=$?
# If the greps above find anything, they exit with 0 status
# If they are not both 0, then something is wrong
if [ $PROCESS_1_STATUS -ne 0 -o $PROCESS_2_STATUS -ne 0 ]; then
echo "One of the processes has already exited."
exit 1
fi
done
Truncated the dockerfile.
RUN unzip $WLS_PKG
RUN $JAVA_HOME/bin/java -Xmx1024m -jar /u01/app/oracle/$WLS_JAR -silent -responseFile /u01/app/oracle/wls.rsp -invPtrLoc /u01/app/oracle/oraInst.loc > install.log
RUN rm -f $WLS_PKG
RUN . $WLS_HOME/server/bin/setWLSEnv.sh && java weblogic.version
RUN java weblogic.WLST -skipWLSModuleScanning create_basedomain.py
WORKDIR /u01/app/oracle
CMD ./startscript.sh
docker build and run commands:
docker build -f Dockerfile-weblogic --tag="weblogic12c:startweb" /var/dprojects
docker rund -d -it weblogic12c:startweb
docker exec -it 6313c4caccd3 bash
Please use supervisord for running multiple services in a docker container. It will make the whole process more robust and reliable.
Run supervisord -n as your CMD command and configure all your services in /etc/supervisord.conf.
Sample conf would look like:
[program:WebLogic]
command=/u01/app/oracle/product/wls122100/domains/verdomain/bin/startWebLogic.sh -D
stderr_logfile = /var/log/supervisord/WebLogic-stderr.log
stdout_logfile = /var/log/supervisord/WebLogic-stdout.log
autorestart=unexpected
[program:NodeManager]
command=/u01/app/oracle/product/wls122100/domains/verdomain/bin/startNodeManager.sh -D
stderr_logfile = /var/log/supervisord/NodeManager-stderr.log
stdout_logfile = /var/log/supervisord/NodeManager-stdout.log
autorestart=unexpected
It will handle all the things you are trying to do with a shell script.
Hope it helps!

Docker run with if statement within Docker Bamboo Task

I would like to run a docker openjdk:8-jdk with the following command:
if [ "$GIT_BRANCH" = "master" ]; then ./gradlew publish; else echo Skipped because it is not master branch; fi
I tried to do the following:
docker run --rm openjdk:8-jdk "if [ \"$GIT_BRANCH\" = \"master\" ]; then echo hi; else echo bla; fi"
But I get the following error: executable file not found in $PATH": unknown.
Furthermore it is not possible for me that I use the if statement like that:
if ...
docker run ...
else
echo Skipped
Because I have to run it as a bamboo docker task.
Since the command above is not executed within bash, bash has to be started first like that:
docker run --rm openjdk:8-jdk /bin/bash -c "if [ \"$GIT_BRANCH\" = \"master\" ]; then ./gradlew publish; else echo Skipped because it is not master branch; fi"

Docker Check if DB is Running

entrypoint.sh contains various cqlsh commands that require Cassandra. Without something like script.sh, cqlsh commands fail because Cassandra doesn't have enough time to start. When I execute the following locally, everything appears to work properly. However, when I run via Docker, script.sh never finishes. In other words, $status never changes from 1 to 0.
Dockerfile
FROM cassandra
RUN apt-get update && apt-get install -y netcat
RUN mkdir /dir
ADD ./scripts /dir/scripts
RUN /bin/bash -c 'service cassandra start'
RUN /bin/bash -c '/dir/scripts/script.sh'
RUN /bin/bash -c '/dir/scripts/entrypoint.sh'
script.sh
#!/bin/bash
set -e
cmd="$#"
status=$(nc -z localhost 9042; echo $?)
echo $status
while [ $status != 0 ]
do
sleep 3s
status=$(nc -z localhost 9042; echo $?)
echo $status
done
exec $cmd
Alternatively, I could do something like until cqlsh -e 'some code'; do .., as noted here for psql, but that doesn't appear to work for me. Wondering how best to approach the problem.
You're misusing the RUN command in your Dockerfile. It's not for starting services, it's for making filesystem changes in your image. The reason $status doesn't update is because you can't start Cassandra via a RUN command.
You should add service cassandra start and /dir/scripts/entrypoint.sh to your script.sh file, and make that the CMD that's executed by default:
Dockerfile
CMD ['/bin/bash', '-c', '/dir/scripts/script.sh']
script.sh
#!/bin/bash
set -e
# NOTE: I removed your `cmd` processing in favor of invoking entrypoint.sh
# directly.
# Start Cassandra before waiting for it to boot.
service cassandra start
status=$(nc -z localhost 9042; echo $?)
echo $status
while [ $status != 0 ]
do
sleep 3s
status=$(nc -z localhost 9042; echo $?)
echo $status
done
exec /bin/bash -c /dir/scripts/entrypoint.sh

TravisCI build succeeds even when tests fail

This is where I am running my tests in travis.yml:
# Run tests
script:
# Test application in Docker container
- ./tools/docker-test.sh
The shell script docker-test.sh looks like this:
#!/usr/bin/env bash
githash="$(git rev-parse --short HEAD)"
echo "-------------------------------------------------"
echo "| Running unit tests |"
echo "-------------------------------------------------"
docker create -it --name test eu.gcr.io/test/test:$githash
docker start test
docker exec test /bin/sh -c "go test ./..."
docker stop test
docker rm -fv test
The TravisCI build is a success even if the tests fail.
How can I get TravisCI to know if test have failed or not? I don't know if this is a problem with errors not being propagated from Docker, errors not being propagated from the shell script or TravisCI not knowing when go tests succeed or fail.
Your script is exiting with the status code of the last command docker rm -fv test.
You need to capture the status code of the test's, then clean up docker, then exit.
This code example is from a slightly different question over here but it's the same solution.
#!/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