From 05d31a6911fd0a86373d0bc920f13f1ba6ccf556 Mon Sep 17 00:00:00 2001 From: Justin Gondron Date: Thu, 26 Jul 2018 12:53:07 -0400 Subject: [PATCH] - Changed everything to use project_root instead of /app, in order to disambiguate this from the project_root/app directory - Added sync excludes for common unneeded directories that may exist in dev workdirs - Changed docker-sync to sync the full project directory - Changed Dockerfile.rails to bundle in into a separate directory. This is to preserve any changes bundler will make to the Gemfile.lock, so that we can copy it back to the host once sync is started. I'm running an additional command at the end of the Dockerfile to copy of the lock file back to project_root in the case when someone is not using docker-sync - The healthy dependency condition is no longer available in v3 of docker compose. Copied the wait-for-it script referenced in https://github.com/peter-evans/docker-compose-healthcheck/issues/3 and added a rails entry script that will handle all the things associated with first starting the rails app, including waiting for mysql to start. --- docker-compose.yml | 6 +- docker-sync.yml | 5 +- docker/Dockerfile.rails | 16 +++- docker/rails_entry.sh | 7 ++ docker/wait-for-it.sh | 177 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 202 insertions(+), 9 deletions(-) create mode 100755 docker/rails_entry.sh create mode 100755 docker/wait-for-it.sh diff --git a/docker-compose.yml b/docker-compose.yml index c31e71404..b2316da09 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,11 +23,13 @@ services: build: context: . dockerfile: docker/Dockerfile.rails - command: bundle exec thin start -p 3000 --ssl --ssl-key-file dev_server_keys/server.key --ssl-cert-file dev_server_keys/server.crt + command: bash docker/rails_entry.sh + environment: + BUNDLE_PATH: "/bundle" ports: - "3000:3000" volumes: - - rails-app-sync:/app/app:nocopy + - rails-app-sync:/project_root:nocopy depends_on: - mysql - jetty diff --git a/docker-sync.yml b/docker-sync.yml index 1b546fbbe..197e4ef86 100644 --- a/docker-sync.yml +++ b/docker-sync.yml @@ -4,6 +4,5 @@ options: verbose: true syncs: rails-app-sync: # tip: add -sync and you keep consistent names as a convention - src: './app' - # sync_strategy: 'native_osx' # not needed, this is the default now - sync_excludes: ['ignored_folder', '.ignored_dot_folder'] + src: '.' + sync_excludes: ['.git', 'tmp', 'jetty', 'vendor/bundle'] diff --git a/docker/Dockerfile.rails b/docker/Dockerfile.rails index 621a33278..62d1e2e64 100644 --- a/docker/Dockerfile.rails +++ b/docker/Dockerfile.rails @@ -1,9 +1,17 @@ FROM ruby:2.1.10 RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs -RUN mkdir /app -WORKDIR /app -COPY . /app +# Put the installed gems outside of project_root so that the sync volume won't interfere +RUN mkdir /bundle +COPY Gemfile /bundle +COPY Gemfile.lock /bundle +WORKDIR /bundle +RUN bundle install --without headless --path /bundle -RUN bundle install --without headless +RUN mkdir /project_root +WORKDIR /project_root +COPY . /project_root + +# Gemfile.lock may have changed after bundling, copy it back into the project_root +RUN cp /bundle/Gemfile.lock /project_root/ diff --git a/docker/rails_entry.sh b/docker/rails_entry.sh new file mode 100755 index 000000000..039fa9b23 --- /dev/null +++ b/docker/rails_entry.sh @@ -0,0 +1,7 @@ +# All the things that will execute when starting the rails service +# Copy the generated lock file back into the project root. This is to sync this change back to the +# application after the container has mounted the sync volume +cp /bundle/Gemfile.lock ./ +bash docker/wait-for-it.sh mysql:3306 +bundle exec rake db:schema:load +bundle exec thin start -p 3000 --ssl --ssl-key-file dev_server_keys/server.key --ssl-cert-file dev_server_keys/server.crt diff --git a/docker/wait-for-it.sh b/docker/wait-for-it.sh new file mode 100755 index 000000000..bbe404324 --- /dev/null +++ b/docker/wait-for-it.sh @@ -0,0 +1,177 @@ +#!/usr/bin/env bash +# Use this script to test if a given TCP host/port are available + +cmdname=$(basename $0) + +echoerr() { if [[ $QUIET -ne 1 ]]; then echo "$@" 1>&2; fi } + +usage() +{ + cat << USAGE >&2 +Usage: + $cmdname host:port [-s] [-t timeout] [-- command args] + -h HOST | --host=HOST Host or IP under test + -p PORT | --port=PORT TCP port under test + Alternatively, you specify the host and port as host:port + -s | --strict Only execute subcommand if the test succeeds + -q | --quiet Don't output any status messages + -t TIMEOUT | --timeout=TIMEOUT + Timeout in seconds, zero for no timeout + -- COMMAND ARGS Execute command with args after the test finishes +USAGE + exit 1 +} + +wait_for() +{ + if [[ $TIMEOUT -gt 0 ]]; then + echoerr "$cmdname: waiting $TIMEOUT seconds for $HOST:$PORT" + else + echoerr "$cmdname: waiting for $HOST:$PORT without a timeout" + fi + start_ts=$(date +%s) + while : + do + if [[ $ISBUSY -eq 1 ]]; then + nc -z $HOST $PORT + result=$? + else + (echo > /dev/tcp/$HOST/$PORT) >/dev/null 2>&1 + result=$? + fi + if [[ $result -eq 0 ]]; then + end_ts=$(date +%s) + echoerr "$cmdname: $HOST:$PORT is available after $((end_ts - start_ts)) seconds" + break + fi + sleep 1 + done + return $result +} + +wait_for_wrapper() +{ + # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692 + if [[ $QUIET -eq 1 ]]; then + timeout $BUSYTIMEFLAG $TIMEOUT $0 --quiet --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + else + timeout $BUSYTIMEFLAG $TIMEOUT $0 --child --host=$HOST --port=$PORT --timeout=$TIMEOUT & + fi + PID=$! + trap "kill -INT -$PID" INT + wait $PID + RESULT=$? + if [[ $RESULT -ne 0 ]]; then + echoerr "$cmdname: timeout occurred after waiting $TIMEOUT seconds for $HOST:$PORT" + fi + return $RESULT +} + +# process arguments +while [[ $# -gt 0 ]] +do + case "$1" in + *:* ) + hostport=(${1//:/ }) + HOST=${hostport[0]} + PORT=${hostport[1]} + shift 1 + ;; + --child) + CHILD=1 + shift 1 + ;; + -q | --quiet) + QUIET=1 + shift 1 + ;; + -s | --strict) + STRICT=1 + shift 1 + ;; + -h) + HOST="$2" + if [[ $HOST == "" ]]; then break; fi + shift 2 + ;; + --host=*) + HOST="${1#*=}" + shift 1 + ;; + -p) + PORT="$2" + if [[ $PORT == "" ]]; then break; fi + shift 2 + ;; + --port=*) + PORT="${1#*=}" + shift 1 + ;; + -t) + TIMEOUT="$2" + if [[ $TIMEOUT == "" ]]; then break; fi + shift 2 + ;; + --timeout=*) + TIMEOUT="${1#*=}" + shift 1 + ;; + --) + shift + CLI=("$@") + break + ;; + --help) + usage + ;; + *) + echoerr "Unknown argument: $1" + usage + ;; + esac +done + +if [[ "$HOST" == "" || "$PORT" == "" ]]; then + echoerr "Error: you need to provide a host and port to test." + usage +fi + +TIMEOUT=${TIMEOUT:-15} +STRICT=${STRICT:-0} +CHILD=${CHILD:-0} +QUIET=${QUIET:-0} + +# check to see if timeout is from busybox? +# check to see if timeout is from busybox? +TIMEOUT_PATH=$(realpath $(which timeout)) +if [[ $TIMEOUT_PATH =~ "busybox" ]]; then + ISBUSY=1 + BUSYTIMEFLAG="-t" +else + ISBUSY=0 + BUSYTIMEFLAG="" +fi + +if [[ $CHILD -gt 0 ]]; then + wait_for + RESULT=$? + exit $RESULT +else + if [[ $TIMEOUT -gt 0 ]]; then + wait_for_wrapper + RESULT=$? + else + wait_for + RESULT=$? + fi +fi + +if [[ $CLI != "" ]]; then + if [[ $RESULT -ne 0 && $STRICT -eq 1 ]]; then + echoerr "$cmdname: strict mode, refusing to execute subprocess" + exit $RESULT + fi + exec "${CLI[@]}" +else + exit $RESULT +fi