Packages

Help you bottle, ship and serve your Elixir apps. Bottler is a collection of tools that aims to help you generate releases, ship them to your servers, install them there, and get them live on production.

Current section

Files

Jump to
bottler lib bottler install.ex
Raw

lib/bottler/install.ex

require Logger, as: L
require Bottler.Helpers, as: H
alias SSHEx, as: S
defmodule Bottler.Install do
@moduledoc """
Functions to install an already shipped release on remote servers.
Actually running release is not touched. Next restart will run
the new release.
"""
@doc """
Install previously shipped release on remote servers, making it _current_
release.
Returns `{:ok, details}` when done, `{:error, details}` if anything fails.
"""
def install(config) do
:ssh.start # sometimes it's not already started at this point...
config[:servers] |> Keyword.values # each ip
|> Enum.map(fn(s) -> s ++ [ user: config[:remote_user] ] end) # add user
|> H.in_tasks( fn(args) -> on_server(args) end )
end
defp on_server(args) do
ip = args[:ip] |> to_char_list
user = args[:user] |> to_char_list
L.info "Installing #{Mix.Project.get!.project[:version]} on #{ip}..."
{:ok, conn} = :ssh.connect(ip, 22,
[{:user,user},{:silently_accept_hosts,true}], 5000)
place_files conn, user, ip
make_current conn, user, ip
cleanup_old_releases conn, user, ip
:ok
end
# Decompress release file, put it in place, and make needed movements
#
defp place_files(conn, user, ip) do
L.info "Settling files on #{ip}..."
vsn = Mix.Project.get!.project[:version]
app = Mix.Project.get!.project[:app]
path = '/home/#{user}/#{app}/'
S.cmd! conn, 'mkdir -p #{path}releases/#{vsn}'
S.cmd! conn, 'mkdir -p #{path}log'
S.cmd! conn, 'mkdir -p #{path}tmp'
{:ok, _, 0} = S.run conn,
'tar --directory #{path}releases/#{vsn}/ ' ++
'-xf /tmp/#{app}.tar.gz'
S.cmd! conn, 'ln -sfn #{path}tmp ' ++
'#{path}releases/#{vsn}/tmp'
S.cmd! conn, 'ln -sfn #{path}log ' ++
'#{path}releases/#{vsn}/log'
S.cmd! conn,
'ln -sfn #{path}releases/#{vsn}/releases/#{vsn} ' ++
'#{path}releases/#{vsn}/boot'
S.cmd! conn,
'ln -sfn #{path}releases/#{vsn}/lib/#{app}-#{vsn}/scripts ' ++
'#{path}releases/#{vsn}/scripts'
end
defp make_current(conn, user, ip) do
L.info "Marking release as current on #{ip}..."
app = Mix.Project.get!.project[:app]
vsn = Mix.Project.get!.project[:version]
{:ok, _, 0} = S.run conn,
'ln -sfn /home/#{user}/#{app}/releases/#{vsn} ' ++
' /home/#{user}/#{app}/current'
end
defp cleanup_old_releases(conn, user, ip) do
L.info "Cleaning up old releases on #{ip}..."
app = Mix.Project.get!.project[:app]
{:ok, res, 0} = S.run conn, 'ls -t /home/#{user}/#{app}/releases'
excess_releases = res |> String.split("\n") |> Enum.slice(5..-2)
for r <- excess_releases do
L.info "Cleaning up old #{r}..."
{:ok, _, 0} = S.run conn, 'rm -fr /home/#{user}/#{app}/releases/#{r}'
end
end
end