class Fulmar::Infrastructure::Model::Transfer::RsyncWithVersions

Provides syncing with versioning on the server

Every deployment results in a new directory in the releases directory with the deployment time as the folder name. A symlink 'current' points to this new directory after publish() is called.

Constants

DEFAULT_CONFIG
TIME_FOLDER
TIME_READABLE

Public Class Methods

new(config) click to toggle source

@param [Fulmar::Domain::Service::ConfigurationService] config

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 36
def initialize(config)
  @config = config
  @config.merge(DEFAULT_CONFIG)
  super(@config)

  if @config[:rsync][:exclude_file].blank? && File.exist?(@config[:local_path] + '/.rsyncignore')
    @config[:rsync][:exclude_file] = @config[:local_path] + '/.rsyncignore'
  end
end

Public Instance Methods

cleanup() click to toggle source

Cleans up old releases limited by :limit_releases @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 110
def cleanup
  limit = @config[:limit_releases].to_i
  return true unless limit > 0
  releases = list_releases
  return true if releases.length <= limit
  obsolete_dirs = releases[0, releases.length - limit].collect { |dir| "\"#{@config[:releases_dir]}/#{dir}\"" }
  @remote_shell.run "rm -fr #{obsolete_dirs.join(' ')}"
end
current_release() click to toggle source

Return the release at which the “current” symlinks points at

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 120
def current_release
  prepare unless @prepared
  @remote_shell.run 'readlink -f current'
  @remote_shell.last_output.first.split('/').last
end
list_releases(plain = true) click to toggle source

Lists the existing releases on the remote machine @param plain [boolean] if the list should be plain directory names or more readable time strings with a star for the current release @return [Array] list of dirs or dates/times

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 92
def list_releases(plain = true)
  prepare unless @prepared
  @remote_shell.run "ls -1tr '#{@config[:releases_dir]}'"
  list = @remote_shell.last_output
  return list if plain

  current = current_release
  list.collect do |item|
    if item =~ /^\d{4}-\d{2}-\d{2}_\d{6}/
      Time.strptime(item, TIME_FOLDER).strftime(TIME_READABLE) + (item == current ? ' *' : '')
    else
      item + (item == current ? ' *' : '')
    end
  end
end
prepare() click to toggle source

Ensures all needed services are set up

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 47
def prepare
  super
  @remote_shell = Fulmar::Shell.new @config[:remote_path], @config.ssh_user_and_host
  @remote_shell.debug = @config[:debug]

  if @config[:version_name] == 'time'
    @config[:version_name] = Time.now.strftime(TIME_FOLDER)
  elsif /^[A-Z][A-Z0-9\-_]+$/ =~ @config[:version_name]
    @config[:version_name] = ENV[@config[:version_name]].split('/').last
  end
end
publish() click to toggle source

Publishes the current release (i.e. sets the 'current' symlink) @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 72
def publish
  prepare unless @prepared
  create_symlink
end
release_dir() click to toggle source

Gets the currently generated release directory @return [String] the release directory

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 85
def release_dir
  @config[:releases_dir] + '/' + @config[:version_name]
end
release_path() click to toggle source

Gets the currently generated absolute release path @return [String] the release directory

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 79
def release_path
  @config[:remote_path] + '/' + release_dir
end
revert(release = last_release) click to toggle source

Reverts to a given release

@params release [String] the release folder or time string (which is found in the output list) @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 130
def revert(release = last_release)
  prepare unless @prepared

  return false if release.nil?

  # Convenience: Allow more readable version string from output
  if release =~ /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/
    release = Time.strptime(item, TIME_READABLE).strftime(TIME_FOLDER)
  end

  create_symlink release
end
transfer() click to toggle source

Copy the files via rsync to the release_path on the remote machine @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 61
def transfer
  prepare unless @prepared

  raise 'Deployment failed when trying to prepare remote directories for sync.' unless create_paths
  raise 'Deployment failed. Cannot sync files.' unless @local_shell.run(rsync_command)
  raise 'Deployment failed when trying to move file from temporary upload dir.' unless copy_temp_to_release
  raise 'Deployment failed when creating symlinks for shared folders' unless add_shared
end

Protected Instance Methods

add_shared() click to toggle source

Creates symlinks into the shared folder

An entry in :shared might be “data/resources/images”, so we first need to create the directory “data/resources” in the release dir and afterwards create the link @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 204
def add_shared
  commands = [] # Collect all remote commands first, then execute them in one step to avoid reconnecting very often
  return true if @config[:shared].nil? || @config[:shared].empty?

  @config[:shared].each do |path|
    commands << "mkdir -p \"#{release_dir}/#{File.dirname(path)}\""

    unless remote_path_exists?("#{@config[:shared_dir]}/#{path}")
      commands << "mkdir -p \"#{@config[:shared_dir]}/#{path}\""
    end

    commands << "rm -fr \"#{release_dir}/#{path}\""
    commands << "mkdir -p \"#{release_dir}/#{File.dirname(path)}\""
    commands << "ln -s \"#{@config[:remote_path]}/#{@config[:shared_dir]}/#{path}\" \"#{release_dir}/#{path}\""
  end

  @remote_shell.run commands if commands.length > 0
end
copy_temp_to_release() click to toggle source

Copies the data from the sync temp to the actual release directory @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 180
def copy_temp_to_release
  @remote_shell.run [
    "mkdir -p #{release_dir}",
    "rsync -a --delete #{@config[:temp_dir]}/ #{release_dir}/",
    "touch #{release_dir}"
  ]
end
create_paths() click to toggle source

Creates all necessary paths on the remote machine @return [true, false] success

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 154
def create_paths
  paths = [@config[:releases_dir], @config[:temp_dir], @config[:shared_dir]]
  @remote_shell.run(paths.collect { |path| "mkdir -p '#{@config[:remote_path]}/#{path}'" })
end
last_release() click to toggle source
# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 145
def last_release
  list = list_releases
  current = current_release
  current_index = list.index(current).to_i
  current_index.zero? ? nil : list[current_index - 1]
end
remote_path_exists?(dir) click to toggle source
# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 223
def remote_path_exists?(dir)
  @remote_shell.run "test -e \"#{dir}\""
end
rsync_command() click to toggle source

Builds the rsync command @return [String] the command

# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 167
def rsync_command
  options = %w[-rl --delete-excluded]
  options << rsync_excludes if rsync_excludes
  options << "--exclude-from='#{@config[:rsync][:exclude_file]}'" if @config[:rsync][:exclude_file]
  options << "--chown='#{@config[:rsync][:chown]}'" if @config[:rsync][:chown]
  options << "--chmod='#{@config[:rsync][:chmod]}'" if @config[:rsync][:chmod]
  options << '--delete' if @config[:rsync][:delete]

  "rsync #{options.join(' ')} '#{@config[:local_path]}/' '#{@config.ssh_user_and_host}:#{@config[:remote_path]}/#{@config[:temp_dir]}'"
end
rsync_excludes() click to toggle source
# File lib/fulmar/infrastructure/model/transfer/rsync_with_versions.rb, line 159
def rsync_excludes
  return nil unless @config[:rsync][:exclude]
  excludes = [*@config[:rsync][:exclude]]
  excludes.map { |exclude| "--exclude='#{exclude}'" }.join(' ')
end