Scripting Docker, Not Connected After Running Script? - bash

So I have a script that looks like this:
#!/bin/bash
if [ $1 ]; then
docker-machine start $1
docker-machine env $1
eval $(docker-machine env $1)
docker ps -a
fi
Once it has run though, the scope of these commands seem to be over. For instance I don't have a connection to the docker-machine once the script has run, but I'd like to script this part out so I can have access to it.
For instance, after running this script ("./script.sh") I still can't run "docker ps -a".
What's the reason this happens and how could I get it to effectively be connected to after executing this script?

A script (or any other process) cannot modify the environment of its parent process. That is precisely why docker-machine env emits shell code that needs to be evaluated with eval.
If you want these variables accessible outside of your script, you would need to arrange to run eval $(docker-machine env <whatever>) in your current shell.

Related

Access a bash script variable outside the docker container in which the script is running

I have a bash script running inside a docker container. In this script, I set the value of some variable.
Can I somehow access the value of this variable outside the container?
I tried to make the variable "global" but could not figure out how to do it. Is it a good idea to make the required variable an environment variable inside the container?
How to reproduce
Create a bash script called temp.sh with the following contents:
a=$RANDOM
Now, run this file in a docker container as follows:
docker run -it --rm -v $(pwd):/opt alpine sh -c "sh /opt/temp.sh"
Desired behaviour: To be able to access the variable a outside the docker container
Credit: This comment by Mark
I mounted a directory on the docker filesystem using
docker run -v <host-file-system-directory>:<docker-file-system-directory>
In the bash script, I added
echo "$variable" >docker-file-system-directory/variable.txt
As I had mounted a host filesystem directory on the docker filesystem, I can still access variable.txt simply using cat <host-file-system-directory>/variable.txt
Note that docker-file-system-directory must be an absolute path, and not a relative path.
One way of achieving that is using docker exec, if your container is running and has access to bash.
#!/usr/bin/env bash
set -x
yourContainerName="testContainerName"
test=$(docker exec -i "${yourContainerName}" bash <<EOF
# do some work here e.g. execute your script
testVar="thisIsTest" # the value we want to access outside of container
echo \$testVar
EOF
)
echo $test
We pass a multiline script to docker container, which in the end echo's the value we need. This value is then accessible from shell that executed docker exec.
Output looks like this:
++ docker exec -i testContainerName bash
+ test=thisIsTest
+ echo thisIsTest
thisIsTest

Entering text into a docker container via ssh from bash file

What I am trying to do is setup a local development database and to prevent everyone having to go through all the steps I thought it would be useful to create a script.
What I have below stop once it is in the terminal, which looks like:
output
./dbSetup.sh
hash of container 0d1b182aa6f1
/ #
At which point I have to manually enter exit.
script
#!/bin/bash
command=$(docker ps | grep personal)
set $command
echo "hash of container ${1}"
docker exec -it ${1} sh
Is there a way I can inject a command via a script into a dockers container terminal?
In order to execute command inside a container, you can use something like this:
docker exec -ti my_container sh -c "echo a && echo b"
More information available at: https://docs.docker.com/engine/reference/commandline/exec/
Your script finds a running Docker container and opens a shell to it. The "-it" makes it interactive and allocates a tty which is why it continues to wait for input, e.g. "exit". If the plan is to execute some commands to initialize a local development database, I'd recommend looking at building an image with a Dockerfile instead. i.e. Once you figure out the commands to run, they would become RUN commands and the container after docker run would expose a local development database.
If you really want some commands to run within the shell after it is started and maintain the session, depending on the base image, you might be able to mount a bash profile that has the required commands, e.g. -v db_profile:/etc/profile.d where db_profile is a folder with the shell scripts you want to run. To get them to run you'd exec sh -l to have the login startup scripts to run.

eval $(docker-machine env default) not working in iterm startup script

I have a startup script for iterm that starts a docker vm if missing, and connects to it if it already exists.
iterm_startup.sh
#!/bin/bash
typeset cmnd="docker-machine ls --filter name='default' --filter state='stopped' | grep default"
typeset ret_code
echo "running startup script ===> ${cmnd}"
eval $cmnd
ret_code=$?
# If not 0, means docker vm has already started
if [ $ret_code != 0 ]; then
eval $(docker-machine env default)
# If 0, means defai;t docker vm not yet started
elif [ $ret_code == 0 ]; then
docker-machine start default
fi
When a docker machine exists, it goes into the block that runs eval $(docker-machine env default). However, when I go to the terminal, it still hasn't connected to the docker vm. I have to manually run eval $(docker-machine env default) in the shell again.
Can someone help me understand why eval $(docker-machine env default) doesn't work as expected in a script? :)
From the settings you showed, it looks like you're asking iTerm to "type in" the path to your script and run it as the first thing when a new terminal window opens. When that happens the script runs as a subprocess. If you look at what docker-machine env prints out it's just a set of export shell commands, so now you stumble on the general rule that subprocesses can't affect their parents' environments.
The simple answer is to change the command to "source" your script so that it runs in the context of the terminal's shell. Try changing your "send text at start" to
. ~/scripts/iterm_startup.sh
Looking at your script, I suspect you could make it much simpler using docker-machine status, and I suspect you need to do the "eval" step even if the machine isn't running yet.
MACHINE=default
if [ $(docker-machine status "$MACHINE" 2>&1) != "Running" ]; then
docker-machine start "$MACHINE"
fi
eval $(docker-machine env "$MACHINE")

How to Set docker container ip to environment variable dynamically on startup?

I want to export docker container hostname as an environment variable which I can later use in my app. In my docker file I call my script "run" as last command
CMD run
The run file is executable and works fine with rest of commands I perform but before them I want to export container hostname to an env. variable as follows
"run" File Try 1
#!/bin/bash
export DOCKER_MACHINE_IP=`hostname -i`
my_other_commands
exec tail -f /dev/null
But when I enter docker container and check, the variable is not set. If I use
echo $DOCKER_MACHINE_IP
in run file after exporting, it shows ip on console when I try
docker logs
I also tried sourcing another script from "run" file as follows
"run" File Try 2
#!/bin/bash
source ./bin/script
my_other_commands
exec tail -f /dev/null
and the script again contains the export command. But this also does not set the environment variable. What I am doing wrong?
When you execute a script, any environment variable set by that script will be lost when the script exits.
But for both the cases you've posted above the environment variable should be accessible for the commands in your scripts, but when you enter the docker container via docker run you will get a new shell, which does not contain your variable.
tl;dr Your exported environment variable will only be available to sub shells of the shell which set the variable. And if you need it when logging in you should source the ./bin/script file.

Eval in docker-machine: terminal vs shell script

I'm trying to run a simple shell script to automate changing docker-machine environments. The problem is this, when I run the following command directly in the Mac terminal the following is outputted:
eval $(docker-machine env default)
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default * digitalocean Running tcp://***.**.***.***:**** v1.12.0
So basically what you would expect, however when I run the following .sh script:
#!/usr/bin/env bash
eval $(docker-machine env default)
The output is:
./run.sh
docker-machine ls
NAME ACTIVE DRIVER STATE URL SWARM DOCKER ERRORS
default digitalocean Running tcp://***.**.***.***:**** v1.12.0
So basically, it is not setting it as active and I cannot access it.
Has anyone run into this issue before and knows how to solve it? Seems really strange to me, have got pretty much everything else running and automated apart from this facet.
Cheers, Aaron
I think you need to source your shell script
source ./myscript.sh
as the exports in the eval are being returned to the process you started to run the shell in and then being disposed of. These need to go to the parent e.g. login shell
Consider a.sh
#!/bin/bash
eval $(echo 'export a=123')
export b=234
when run in two ways
$ ./a.sh
$ echo $a
$ echo $b
$ source a.sh
$ echo $a
123
$ echo $b
234
$

Resources