Skip to content

Instantly share code, notes, and snippets.

@akatrevorjay
Created March 28, 2019 22:01
Show Gist options
  • Save akatrevorjay/14e6ab8802281abe39f5c019f3ce4646 to your computer and use it in GitHub Desktop.
Save akatrevorjay/14e6ab8802281abe39f5c019f3ce4646 to your computer and use it in GitHub Desktop.
#!/bin/bash
set -eo pipefail
usage() {
cat <<-'EOF'
# Container exec
A method for exporting commands for transparent docker-compose usage from your shell,
letting you forget that you're even using commands across different containers.
## Come again?
There are several scripts in the `bin/` folder that are part of `container-exec`, which as it's name may suggest can be
useful for running commands easily inside of a given container.
This includes bringing a command that's *inside* of a specific container *outside* to your local environment in a
mostly transparent fashion that may help your wrists from the pain of typing `docker-compose exec -u postgres postgres
psql` just to get to a postgresql prompt.
In other words, this is magic that allows you to:
```sh
# Run psql as the postgres user in the postgres container
# equivalent to: docker-compose exec -u postgres postgres psql
./bin/psql
```
As you normally would, but it will actually be running transparently inside of the `postgres` container.
Return values and arguments are preserved as you should expect as well.
Other examples:
```sh
# Start a shell in the web container
# equivalent to: docker-compose exec web bash
./bin/web bash
```
```sh
# These also run in the web container as if you were inside of it just fine:
./bin/manage.py shell
./bin/runtests.py -vv
```
## How does this work?
```sh
./bin
├── container-exec # ~30 lines of mostly voodoo.
├── postgres # shim that sources container-exec above, named the same as the container.
└── psql -> postgres # symlink to postgres above, specifying a command to run in the destination container.
```
Via a one line shim *file* named the same as the container (the line is just sourcing `container-exec`).
Commands are then made by just symlinking to said shim.
* Commands can be optionally prefixed by `{container_name}-`.
This should be the go to unless it's absolutely obvious what container a command will intuitively run in, such as
`psql` or `manage.py`. (Definitely not ambiguities like `bash` and such.)
Take a look at the comments below if you want to *go deeper*, it's not large.
## What's in a shim?
Just a line to `source` this script:
```sh
#!/bin/bash
source "${0%/*}/container-exec"
```
Optionally (and rarely) you'll want a set of commands to be executed as a specific user (looking at you, postgres):
```sh
#!/bin/bash
username=$(readlink "$0") # optional; just sets the user to run commands as in the container
source "${0%/*}/container-exec"
```
## Source
Part of boilerplate: https://github.com/akatrevorjay/boilerplate
EOF
}
main() {
# Get the basename of what was executed to get here.
name="${0##*/}"
# If we're running from a symlink, we're a command to run inside of that container, so we'll need that name.
# If not, we are the container shim, and our arguments contain the command to run.
realname=$(readlink "$0" || echo "$name")
container="$realname"
# If our name is prefixed with the container name and a dash, strip.
name="${name#$realname-}"
case "$realname" in
$name)
[ $# -gt 0 ] || set -- --help
case "$1" in
-h|--help|help)
usage
exit 0
;;
esac
;;
esac
cmd=(
docker-compose
exec
)
# Allow specifying the username to run as in the shim
[[ -z "$username" ]] || cmd+=(--user "$username")
cmd+=("$container")
# If we're running from a symlink, we're a command to run inside of that container.
[[ "$name" == "$realname" ]] || cmd+=("$name")
exec "${cmd[@]}" "$@"
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment