Last active
March 14, 2019 21:17
-
-
Save andyneff/26830a64793ea18f6363e5578dc5664f to your computer and use it in GitHub Desktop.
Like a virtual env for docker
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env bash | |
function _de_cleanup() | |
{ | |
for f in ~/.ssh/docker_*; do | |
if ! \fuser "${f}" >& /dev/null; then | |
\rm "${f}" | |
fi | |
done | |
} | |
#** | |
# .. function:: de_activate | |
# | |
# Docker environment activate - connected to remote docker server using ssh tunneling. This does **not** employ the *insecure* method of exposing the docker daemon to an open TCP port. | |
# | |
# :Arguments: ``$1``... - Arguments to ``ssh`` command, machine name and options | |
# | |
# :Parameters: [``DE_OLDER``] - Set to 1 if using ssh older than 6.7. When you use this flag, the server needs to have ``socat`` installed. | |
# | |
# .. rubric:: Example | |
# | |
# .. code-block:: bash | |
# | |
# de_activate username@server -p 1234 | |
# | |
# .. seealso:: | |
# :func:`de_reverse_activate` | |
#** | |
function de_activate() | |
{ | |
local docker_host | |
local ssh_args | |
if [ "$#" == "0" ]; then | |
echo "usage: $0 <ssh flags>" | |
echo " Set DE_OLDER to 1 when using openssh version before 6.7" | |
return 1 | |
fi | |
_de_ssh_args=("${@}") | |
_OLD_DOCKER_HOST="${DOCKER_HOST-}" | |
ssh_args=(-o ControlPath=~/.ssh/%C -o ControlMaster=auto -o ControlPersist=yes) | |
_de_cleanup | |
if [ "${DE_OLDER-}" = "1" ]; then | |
ssh -n -L 2375:localhost:2375 "${@}" \ | |
'socat TCP-LISTEN:2375,fork,bind=localhost UNIX-CONNECT:/var/run/docker.sock& | |
pid=$! | |
trap "kill $pid" 0 | |
while printf \\0; do | |
sleep 5 | |
done' & | |
docker_host="tcp://localhost:2375" | |
else | |
# Requires opensshd 6.7 or newer... THANKS CentOS! :( | |
_de_socket="$(mktemp -u -d ~/.ssh/docker_XXXXXXXX)" | |
ssh_args+=(-fTN -L "${_de_socket}":/var/run/docker.sock "${@}") | |
docker_host="unix://${_de_socket}" | |
if ssh -O check "${ssh_args[@]}" >&/dev/null; then | |
# I can't tell when a command fails. Oh well | |
ssh -o ServerAliveInterval=60 "${ssh_args[@]}"& | |
else | |
ssh -o ServerAliveInterval=60 "${ssh_args[@]}" || return $? | |
fi | |
fi | |
export DOCKER_HOST="${docker_host}" | |
#This will not always be correct, but oh well | |
VIRTUAL_ENV="de $1" | |
echo "VIRTUAL_ENV=\"${VIRTUAL_ENV}\"" | |
echo "export DOCKER_HOST=\"${DOCKER_HOST}\"" | |
function de_deactivate() | |
{ | |
ssh -o ControlPath=~/.ssh/de_%C -O exit "${_de_ssh_args[@]}" # || return $? | |
if [ "${_OLD_DOCKER_HOST-}" == "" ]; then | |
unset DOCKER_HOST | |
else | |
export DOCKER_HOST="${_OLD_DOCKER_HOST}" | |
fi | |
# Newer way leaves a stray socket behind... which if you close without | |
# deactivating will result in a "socket" leak | |
if [ "${_de_socket+set}" == "set" ]; then | |
\rm "${_de_socket}" | |
unset _de_socket | |
fi | |
_de_cleanup | |
unset _de_ssh_args VIRTUAL_ENV | |
unset -f de_deactivate | |
} | |
} | |
#** | |
# .. function:: de_reverse_activate | |
# | |
# Not as good as :func:`de_activate`, but needed on systems where you can't directly ssh into a docker enabled user or for some reason on Synology this is needed too. | |
# | |
# :Arguments: [``$1``] - Name of computer connecting back on. Optional if 2... is only one argument, and defaults to $(hostname) | |
# ``$2``... - Arguments to ``ssh`` command, machine name and options | |
# | |
# :Parameters: [``CHANGE_USER``] - The ssh-ed user needs to ``sudo`` to another user, add that command using the ``CHANGE_USER`` environment variable. Typically something like ``sudo su - root -c``. The ``-c`` is needed as the return ``ssh`` command is passed in as one argument to the ``su`` command. | |
# .. rubric:: Example | |
# | |
# .. code-block:: bash | |
# | |
# de_reverse_activate my_username@my_hostname username@server -p 1234 | |
# # or | |
# CHANGE_USER="sudo su - root -c" de_reverse_activate my_username@my_hostname username@server -p 1234 | |
# | |
# .. seealso:: | |
# :func:`de_reverse_activate` | |
#** | |
# Old way won't handle spaces in home directory, etc... | |
# function de_reverse_activate() | |
# { | |
# if [ "$#" = "1" ]; then | |
# de_reverse_activate $(hostname) "$1" | |
# fi | |
# local phone_home="${1}" | |
# shift 1 | |
# local de_socket="$(mktemp -u -d ~/.ssh/docker_XXXXXXXX)" | |
# if [ -n "${CHANGE_USER+set}" ]; then | |
# ssh -t "${@}" ${CHANGE_USER} "'ssh -t -R \"${de_socket}:/var/run/docker.sock\" ${phone_home} env \"DOCKER_HOST=unix://${de_socket}\" bash'" | |
# else | |
# ssh -t "${@}" ssh -t -R "${de_socket}:/var/run/docker.sock" ${phone_home} env "DOCKER_HOST=unix://${de_socket}" bash | |
# fi | |
# } | |
function de_reverse_activate() | |
{ | |
if [ "$#" = "1" ]; then | |
de_reverse_activate $(hostname) "$1" | |
fi | |
local phone_home="${1}" | |
local args=() | |
shift 1 | |
local de_socket="$(mktemp -u -d ~/".ssh/docker_XXXXXXXX")" | |
args[0]="$(print_command ssh -t "${@}")" | |
args[1]="ssh -t -R '${de_socket}:/var/run/docker.sock' ${phone_home}" | |
args[2]="env 'DOCKER_HOST=unix://${de_socket}' 'DISPLAY=${DISPLAY}' bash -c" | |
args[3]="cd '$(pwd)'; exec bash" | |
if [ -n "${CHANGE_USER+set}" ]; then | |
if [ "${CHANGE_SINGLE-}" = "1" ]; then | |
args=("${args[0]}" "${CHANGE_USER}" "${args[@]:1}") | |
else | |
args[1]="${CHANGE_USER} ${args[1]}" | |
fi | |
fi | |
eval "$(quotemire "${args[@]}")" | |
} | |
#** | |
# .. function:: print_command | |
# | |
# :Arguments: ``$1``... - List of command + arguments to be echoed | |
# :Output: *stdout* - A quote escaped version of the command + arguments, ready to be ``eval``ed | |
# | |
# Accurately echoes out a properly escaped single string representation of a command + arguments. | |
# | |
# .. rubric:: Example | |
# | |
# .. code-block:: bash | |
# | |
# print_command this is a t\ e\ \ s\'\"t | |
# | |
# # Results in | |
# this is a 't e s'"'"'"t' | |
# | |
# # Typical usage | |
# eval "$(print_command "${stuff[@]}")" | |
# or | |
# bash -c "$(print_command "${stuff[@]}")" | |
#** | |
function print_command() | |
{ | |
while [ "$#" -gt 0 ]; do | |
# if [[ ${1} =~ ^[a-zA-Z0-9_.:/=-]*$ ]]; then | |
# https://unix.stackexchange.com/a/357932/123413 | |
if [[ ${1} =~ ^[a-zA-Z0-9_.:/=@%^,+-]*$ ]]; then | |
printf -- "${1}" | |
else | |
printf "'${1//\'/\'\"\'\"\'}'" | |
fi | |
shift 1 | |
[ "$#" -gt 0 ] && printf " " | |
done | |
printf "\n" | |
} | |
#** | |
# .. function:: quotemire | |
# | |
# :Arguments: ``$1``... - List of commands in the chain | |
# :Output: *stdout* - The final conglomeration of all the arguments together, ready to be eval'ed | |
# | |
# When you have to execute a command that calls another command, that calls another command, where to put the quotes can get out of hand after the 3rd or 4th iteration. | |
# | |
# .. rubric:: Problem Example | |
# | |
# .. code-block:: bash | |
# | |
# ssh server "su - ben -c 'bash -c \"ls -la '\''/tmp/foo bar'\''\"'" | |
# | |
# :func:`quotemire` allows you to instead handle this as this chain of commands as an array of strings, and then it programatically combines it into one long command | |
# | |
# .. rubric:: Example | |
# | |
# .. code-block:: bash | |
# args=() | |
# args[0]="ssh server" | |
# args[1]="su - ben -c" | |
# args[2]="bash -c" | |
# args[3]="ls -la" | |
# args[4]="/tmp/foo bar" | |
# | |
# # Or if you want to combine args[3] and args[4] | |
# # args[3]="ls -la '/tmp/foo bar'" | |
# | |
# eval "$(quotemire "${args[@]}")" | |
# # or | |
# bash -c "$(quotemire "${args[@]}")" | |
# | |
# This chain quoting is only an issue for commands like ``su -c``, ``bash -c`` and ``ssh`` (because ssh does not handle command arguments correctly). However other commands like ``env``, ``sudo``, etc... do not need this quotation, so should not be made separate arguments: | |
# | |
# .. rubric:: Mixed Example | |
# | |
# args=() | |
# args[0]="$(print_command ssh -t "${@}")" | |
# args[1]="ssh -t -R '${de_socket}:/var/run/docker.sock' ${phone_home}" | |
# args[2]="env 'DOCKER_HOST=unix://${de_socket}' 'DISPLAY=${DISPLAY}' bash -c" | |
# args[3]="cd '$(pwd)'; exec bash" | |
# | |
# In this example, the ``env`` and ``bash`` are on the same argument, because env does handle multiple arguments for a command correctly | |
#** | |
function quotemire() | |
{ | |
local args | |
if [ "$#" -gt 0 ]; then | |
args="${!#}" | |
fi | |
local x | |
for (( x=$#-2; x>=0; x-- )); do | |
args="${@:$x+1:1} $(print_command "${args}")" | |
done | |
echo "${args}" | |
} | |
#** | |
# .. function:: dcp2v | |
# | |
# Docker copy files to a docker volume | |
# | |
# :Arguments: ``$1`` - Volume name | |
# ``$2``... - Filenames | |
# .. seealso:: | |
# :func:`dcpfv` | |
#** | |
function dcp2v() | |
{ | |
local volume="$1" | |
shift 1 | |
tar zc "${@}" | docker run -i --rm -v "${volume}":/cp -w /cp alpine tar zx | |
} | |
#** | |
# .. function:: dcpfv | |
# | |
# Copy files from a docker volume | |
# | |
# :Arguments: ``$1`` - Volume name | |
# ``$2``... - Filenames | |
# | |
# .. note:: | |
# | |
# Does not handle spaces correctly. See :func:`dcpfv2` | |
# | |
# .. seealso:: | |
# :func:`dcpfv2` | |
#** | |
function dcpfv() | |
{ | |
local volume="$1" | |
shift 1 | |
local args=("${@}") | |
docker run --rm -e IFS=t \ | |
-v "${volume}":/cp:ro -w /cp \ | |
debian bash -c "$(declare -p args);"' eval tar zc "${args[@]}"' | tar zx | |
} | |
#** | |
# .. function:: dcpfv2 | |
# | |
# Version of :func:`dcpfv` that supports spaces | |
# | |
# :Arguments: ``$1`` - Volume name | |
# ``$2``... - Filenames | |
# .. seealso:: | |
# :func:`dcpfv` | |
#** | |
function dcpfv2() | |
{ | |
local volume="$1" | |
shift 1 | |
local args=("${@}") | |
docker run --rm -e IFS=$'\t' \ | |
-v "${volume}":/cp:ro -w /cp \ | |
debian bash -c "$(declare -p args);"' tar zc "${args[@]}"' | tar zx | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment