Module: Hamal::Stages

Includes:
Helpers
Included in:
Commands
Defined in:
lib/hamal.rb

Instance Method Summary collapse

Methods included from Helpers

#log, #on_server

Methods included from Config

#app_local_ports, #app_name, #app_repo, #config_file, #deploy_config, #deploy_env, #deployed_image, #deployed_revision, #project_root, #server

Instance Method Details

#build_new_imageObject



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/hamal.rb', line 79

def build_new_image
  image_exists = on_server { sh "docker image inspect #{deployed_image}" }.success?
  if image_exists
    log "Using existing image #{deployed_image} for deploy"
    return
  end

  log "Building new image #{deployed_image} for deploy"

  source_dir = "#{project_root}/src/#{deployed_revision}"

  on_server do
    log "  Checking out source at revision #{deployed_revision}..."
    sh! "rm -rf #{source_dir}"
    sh! "git clone [email protected]:#{app_repo}.git #{source_dir}"
  end
  on_server dir: source_dir do
    sh! "git checkout #{deployed_revision}"

    log "  Building image..."
    sh! "docker build -t #{deployed_image} ."

    log "  Cleaning up source dir..."
    sh! "rm -rf #{source_dir}"
  end
end

#clean_upObject



187
188
189
190
191
192
193
194
# File 'lib/hamal.rb', line 187

def clean_up
  log "Cleaning up"

  log "  Removing unused docker objects"
  on_server do
    sh! 'docker system prune --all --force --filter "until=24h"'
  end
end

#run_deploy_tasksObject



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# File 'lib/hamal.rb', line 106

def run_deploy_tasks
  log "Running migrations"

  on_server do
    sh! "docker run --rm " \
        "--label app=#{app_name} " \
        "--env-file #{project_root}/env_file " \
        "-e GIT_REVISION=#{deployed_revision} " \
        "-v #{project_root}/db:/rails/db/#{deploy_env} " \
        "-v #{project_root}/storage:/rails/storage " \
        "--entrypoint '/rails/bin/rails' " \
        "#{deployed_image} " \
        "-- db:migrate"
  end
end

#start_new_containerObject



122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/hamal.rb', line 122

def start_new_container
  log "Starting container for new version"

  # Determine which ports are currently bound and which are free for the new container
  running_containers = on_server { sh! "docker ps -q --filter label=app=#{app_name}" }.output.split
  bound_ports =
    running_containers.map do |container|
      port_settings = on_server { sh! "docker inspect --format '{{json .NetworkSettings.Ports}}' #{container}" }.output
      port_settings = JSON.parse port_settings
      (port_settings["3000/tcp"] || []).map { _1["HostPort"] }.compact
    end.flatten

  available_port = (app_local_ports - bound_ports).first
  abort "No TCP port available" unless available_port

  log "  Using port #{available_port} for new container"
  on_server do
    sh! "docker run -d --rm " \
        "--label app=#{app_name} " \
        "--env-file #{project_root}/env_file " \
        "-e GIT_REVISION=#{deployed_revision} " \
        "-v #{project_root}/db:/rails/db/#{deploy_env} " \
        "-v #{project_root}/storage:/rails/storage " \
        "-p 127.0.0.1:#{available_port}:3000 " \
        "#{deployed_image}"
  end

  [available_port, running_containers]
end

#stop_old_container(old_containers) ⇒ Object



174
175
176
177
178
179
180
181
182
183
184
185
# File 'lib/hamal.rb', line 174

def stop_old_container(old_containers)
  log "Stopping old container"

  if old_containers.empty?
    log "  (none found)"
    return
  end

  on_server do
    sh! "docker kill -s SIGTERM #{old_containers.join ' '}"
  end
end

#switch_traffic(new_container_port) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/hamal.rb', line 152

def switch_traffic(new_container_port)
  log "Switching traffic to new version"

  log "  Waiting for new version to become ready"
  health_checks = 1
  loop do
    new_container_ready = on_server { sh "curl -fs http://localhost:#{new_container_port}/healthz" }.success?
    break if new_container_ready

    abort "New container failed to start within 30 seconds, investigate!" if health_checks > 30

    health_checks += 1
    sleep 1
  end

  log "  Redirecting nginx to new version"
  on_server do
    sh! "ACTIVE_RAILS_PORT=#{new_container_port} envsubst < /etc/nginx/#{app_name}.conf.template > /etc/nginx/#{app_name}.conf"
    sh! "nginx -s reload"
  end
end