How to detect fully interactive shell in bash from docker? - bash

I'm wanting to detect in "docker run" whether -ti has been passed to the entrypoint script.
docker run --help for -t -i
-i, --interactive=false Keep STDIN open even if not attached
-t, --tty=false Allocate a pseudo-TTY
I have tried the following but even when tested locally (not inside docker) it didn't work and printed out "Not interactive" always.
#!/bin/bash
[[ $- == *i* ]] && echo 'Interactive' || echo 'Not interactive'

entrypoint.sh:
#!/bin/bash
set -e
if [ -t 0 ] ; then
echo "(interactive shell)"
else
echo "(not interactive shell)"
fi
/bin/bash -c "$#"
Dockerfile:
FROM debian:7.8
COPY entrypoint.sh /usr/bin/entrypoint.sh
RUN chmod 755 /usr/bin/entrypoint.sh
ENTRYPOINT ["/usr/bin/entrypoint.sh"]
CMD ["/bin/bash"]
build the image:
$ docker build -t is_interactive .
run the image interactively:
$ docker run -ti --rm is_interactive "/bin/bash"
(interactive shell)
root#dd7dd9bf3f4e:/$ echo something
something
root#dd7dd9bf3f4e:/$ echo $HOME
/root
root#dd7dd9bf3f4e:/$ exit
exit
run the image not interactively:
$ docker run --rm is_interactive "echo \$HOME"
(not interactive shell)
/root
$
This stackoverflow answer helped me find [ -t 0 ].

Related

How not to terminate after carried out commands in bash

After carrying out commands with "-c" option of bash, how can I make the terminal wait for input while preserving the environment?
Like CMD /K *** or pwsh -NoExit -Command ***.
From a comment by Cyrus:
You can achieve something similar by abusing the --rcfile option:
bash --rcfile <(echo "export PS1='> ' && ls")
From bash manpage:
--rcfile file
Execute commands from file instead of the system wide initialization file /etc/bash.bashrc and the standard personal initialization file ~/.bashrc if the shell is interactive
This is the answer I was looking for. Thank you!!
As an example of use, I use the following method to use the latest docker image with my preferred repository without building the image:
# Call bash in the container from bash
docker run --rm -it ubuntu:22.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(deb(-src|)\s+)https?://(archive|security)\.ubuntu\.com/ubuntu/%\1http://mirrors.xtom.com/ubuntu/%'\'' /etc/apt/sources.list && apt update && FooBar=`date -uIs`')"
# ... from pwsh
docker run --rm -it ubuntu:22.04 bash -c "bash --rcfile <(echo 'sed -i -E '\''s%^(deb(-src|)\s+)https?://(archive|security)\.ubuntu\.com/ubuntu/%\1http://mirrors.xtom.com/ubuntu/%'\'' /etc/apt/sources.list && apt update && FooBar=``date -uIs``')"
# Call dash (BusyBox ash) in the container from bash
docker run --rm -it alpine:latest ash -c "ash -c 'export ENV=\$1;ash' -s <(echo 'sed -i -E '\''s%^https?://dl-cdn\.alpinelinux\.org/alpine/%https://ftp.udx.icscoe.jp/Linux/alpine/%'\'' /etc/apk/repositories && apk update && FooBar=`date -uIs`')"
# ... from pwsh
docker run --rm -it alpine:latest ash -c "ash -c 'export ENV=`$1;ash' -s <(echo 'sed -i -E '\''s%^https?://dl-cdn\.alpinelinux\.org/alpine/%https://ftp.udx.icscoe.jp/Linux/alpine/%'\'' /etc/apk/repositories && apk update && FooBar=``date -uIs``')"

Impossible to start a script into my docker container

I am trying to create my own image based on Centos.
I don't understand why when I use CMD command in my docker file to execute a script at startup, it's impossible to start my image (Exited (0) immediatly).
If build without the CMD command and then I connect to the container and I execute "sh /opt/jbossEAP/Mock/scripts/mock_start.sh". I have no issue
I have tryied to use entrypoint command but same result :(
FROM centos:7
ENV container docker
RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i == \
systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]
RUN yum update -y
RUN mkdir -p /opt/jbossEAP/Mock/scripts/
ADD ./scripts /opt/jbossEAP/Mock/scripts/
RUN chmod +x /opt/jbossEAP/Mock/scripts/mock_start.sh
### START SCRIPT ###
CMD sh /opt/jbossEAP/Mock/scripts/mock_start.sh
mock_start.sh
#!/bin/sh
############################################
echo "hello"
I suspect your CMD or ENTRYPOINT does work, but that the container simply finishes after outputting hello
You can check your docker's output even after it has been stopped with:
docker logs <container-id>
Read https://stackoverflow.com/a/28214133/4486184 for more information on how it works and how to avoid that.
My guesses could be wrong, so please always add to your question:
How you start your docker image
docker ps -a's output
the relevant part of docker logs <container-id>'s output
You're right!!!
I just add and now it's ok.
CMD sh /opt/jbossEAP/Mock/scripts/mock_start.sh && tail -f /dev/null
Thank you very much

Command line shortcut to connect to a docker container

Is there any shortcut command to connect to a docker container without running docker exec -it 'container_id' bash every time?
Here is a shorter command line shortcut to:
Check if a container is running
If running, connect to a running container using docker exec -it <container> bash command:
Script docker-enter:
#!/bin/bash
name="${1?needs one argument}"
containerId=$(docker ps | awk -v app="$name:" '$2 ~ app{print $1}')
if [[ -n "$containerId" ]]; then
docker exec -it $containerId bash
else
echo "No docker container with name: $name is running"
fi
Then run it as:
docker-enter webapp
I'm using the following alias on OS X:
alias dex='function _dex(){ docker exec -i -t "$(basename $(pwd) | tr -d "[\-_]")_$1_1" /bin/bash -c "export TERM=xterm; exec bash" };_dex'
In the same directory as my docker-files, I run "dex php" to enter the PHP container.
If random id is complicated. Start container with name docker run --name test image and connect with its name docker exec -it test bash.

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=$?

docker run -i -t image /bin/bash - source files first

This works:
# echo 1 and exit:
$ docker run -i -t image /bin/bash -c "echo 1"
1
# exit
# echo 1 and return shell in docker container:
$ docker run -i -t image /bin/bash -c "echo 1; /bin/bash"
1
root#4c064f2554de:/#
Question: How could I source a file into the shell? (this does not work)
$ docker run -i -t image /bin/bash -c "source <(curl -Ls git.io/apeepg) && /bin/bash"
# content from http://git.io/apeepg is sourced and shell is returned
root#4c064f2554de:/#
In my case, I use RUN source command (which will run using /bin/bash) in a Dockerfile to install nvm for node.js
Here is an example.
FROM ubuntu:14.04
RUN rm /bin/sh && ln -s /bin/bash /bin/sh
...
...
RUN source ~/.nvm/nvm.sh && nvm install 0.11.14
I wanted something similar, and expanding a bit on your idea, came up with the following:
docker run -ti --rm ubuntu \
bash -c 'exec /bin/bash --rcfile /dev/fd/1001 \
1002<&0 \
<<<$(echo PS1=it_worked: ) \
1001<&0 \
0<&1002'
--rcfile /dev/fd/1001 will use that file descriptor's contents instead of .bashrc
1002<&0 saves stdin
<<<$(echo PS1=it_worked: ) puts PS1=it_worked: on stdin
1001<&0 moves this stdin to fd 1001, which we use as rcfile
0<&1002 restores the stdin that we saved initially
You can use .bashrc in interactive containers:
RUN curl -O git.io/apeepg.sh && \
echo 'source apeepg.sh' >> ~/.bashrc
Then just run as usual with docker run -it --rm some/image bash.
Note that this will only work with interactive containers.
I don't think you can do this, at least not right now. What you could do is modify your image, and add the file you want to source, like so:
FROM image
ADD my-file /my-file
RUN ["source", "/my-file", "&&", "/bin/bash"]

Resources