Files

Class/Module Index [+]

Quicksearch

Chef::Provider::Deploy

Attributes

previous_release_path[R]
release_path[R]
scm_provider[R]

Public Class Methods

new(new_resource, run_context) click to toggle source
# File lib/chef/provider/deploy.rb, line 36
def initialize(new_resource, run_context)
  super(new_resource, run_context)

  # will resolve to either git or svn based on resource attributes,
  # and will create a resource corresponding to that provider
  @scm_provider = new_resource.scm_provider.new(new_resource, run_context)

  # @configuration is not used by Deploy, it is only for backwards compat with
  # chef-deploy or capistrano hooks that might use it to get environment information
  @configuration = @new_resource.to_hash
  @configuration[:environment] = @configuration[:environment] && @configuration[:environment]["RAILS_ENV"]
end

Public Instance Methods

action_deploy() click to toggle source
# File lib/chef/provider/deploy.rb, line 98
def action_deploy
  save_release_state
  if deployed?(release_path )
    if current_release?(release_path )
      Chef::Log.debug("#{@new_resource} is the latest version")
    else
      rollback_to release_path
    end
  else

    with_rollback_on_error do
      deploy
    end
  end
end
action_force_deploy() click to toggle source
# File lib/chef/provider/deploy.rb, line 114
def action_force_deploy
  if deployed?(release_path)
    converge_by("delete deployed app at #{release_path} prior to force-deploy") do
      Chef::Log.info("Already deployed app at #{release_path}, forcing.")
      FileUtils.rm_rf(release_path)
      Chef::Log.info("#{@new_resource} forcing deploy of already deployed app at #{release_path}")
    end
  end

  # Alternatives:
  # * Move release_path directory before deploy and move it back when error occurs
  # * Rollback to previous commit
  # * Do nothing - because deploy is force, it will be retried in short time
  # Because last is simpliest, keep it
  deploy
end
action_rollback() click to toggle source
# File lib/chef/provider/deploy.rb, line 131
def action_rollback
  rollback_to all_releases[-2]
end
all_releases() click to toggle source
# File lib/chef/provider/deploy.rb, line 245
def all_releases
  Dir.glob(@new_resource.deploy_to + "/releases/*").sort
end
callback(what, callback_code=nil) click to toggle source
# File lib/chef/provider/deploy.rb, line 178
def callback(what, callback_code=nil)
  @collection = Chef::ResourceCollection.new
  case callback_code
  when Proc
    Chef::Log.info "#{@new_resource} running callback #{what}"
    recipe_eval(&callback_code)
  when String
    run_callback_from_file("#{release_path}/#{callback_code}")
  when nil
    run_callback_from_file("#{release_path}/deploy/#{what}.rb")
  end
end
cleanup!() click to toggle source
# File lib/chef/provider/deploy.rb, line 230
def cleanup!
  converge_by("update release history data") do
    release_created(release_path)
  end

  chop = -1 - @new_resource.keep_releases
  all_releases[0..chop].each do |old_release|
    converge_by("remove old release #{old_release}") do
      Chef::Log.info "#{@new_resource} removing old release #{old_release}"
      FileUtils.rm_rf(old_release)
    end
    release_deleted(old_release)
  end
end
copy_cached_repo() click to toggle source
# File lib/chef/provider/deploy.rb, line 267
def copy_cached_repo
  target_dir_path = @new_resource.deploy_to + "/releases"
  converge_by("deploy from repo to #{target_dir_path} ") do
    FileUtils.rm_rf(release_path) if ::File.exist?(release_path)
    FileUtils.mkdir_p(target_dir_path)
    FileUtils.cp_r(::File.join(@new_resource.destination, "."), release_path, :preserve => true)
    Chef::Log.info "#{@new_resource} copied the cached checkout to #{release_path}"
  end
end
define_resource_requirements() click to toggle source
# File lib/chef/provider/deploy.rb, line 73
def define_resource_requirements
  requirements.assert(:rollback) do |a|
    a.assertion { all_releases[-2] }
    a.failure_message(RuntimeError, "There is no release to rollback to!")
    #There is no reason to assume 2 deployments in a single chef run, hence fails in whyrun.
  end

  [ @new_resource.before_migrate, @new_resource.before_symlink,
    @new_resource.before_restart, @new_resource.after_restart ].each do |script|
    requirements.assert(:deploy, :force_deploy) do |a|
      callback_file = "#{release_path}/#{script}"
      a.assertion do
        if script && script.class == String
          ::File.exist?(callback_file)
        else
          true
        end
      end
      a.failure_message(RuntimeError, "Can't find your callback file #{callback_file}")
      a.whyrun("Would assume callback file #{callback_file} included in release")
    end
  end

end
deploy() click to toggle source
# File lib/chef/provider/deploy.rb, line 152
def deploy
  verify_directories_exist
  update_cached_repo # no converge-by - scm provider will dothis
  enforce_ownership
  copy_cached_repo
  install_gems
  enforce_ownership
  callback(:before_migrate, @new_resource.before_migrate)
  migrate
  callback(:before_symlink, @new_resource.before_symlink)
  symlink
  callback(:before_restart, @new_resource.before_restart)
  restart
  callback(:after_restart, @new_resource.after_restart)
  cleanup!
  Chef::Log.info "#{@new_resource} deployed to #{@new_resource.deploy_to}"
end
enforce_ownership() click to toggle source
# File lib/chef/provider/deploy.rb, line 277
def enforce_ownership
  converge_by("force ownership of #{@new_resource.deploy_to} to #{@new_resource.group}:#{@new_resource.user}") do
    FileUtils.chown_R(@new_resource.user, @new_resource.group, @new_resource.deploy_to)
    Chef::Log.info("#{@new_resource} set user to #{@new_resource.user}") if @new_resource.user
    Chef::Log.info("#{@new_resource} set group to #{@new_resource.group}") if @new_resource.group
  end
end
load_current_resource() click to toggle source
# File lib/chef/provider/deploy.rb, line 53
def load_current_resource
  @scm_provider.load_current_resource
  @release_path = @new_resource.deploy_to + "/releases/#{release_slug}"
end
migrate() click to toggle source
# File lib/chef/provider/deploy.rb, line 191
def migrate
  run_symlinks_before_migrate

  if @new_resource.migrate
    enforce_ownership

    environment = @new_resource.environment
    env_info = environment && environment.map do |key_and_val|
      "#{key_and_val.first}='#{key_and_val.last}'"
    end.join(" ")

    converge_by("execute migration command #{@new_resource.migration_command}") do
      Chef::Log.info "#{@new_resource} migrating #{@new_resource.user} with environment #{env_info}"
      run_command(run_options(:command => @new_resource.migration_command, :cwd=>release_path, :log_level => :info))
    end
  end
end
purge_tempfiles_from_current_release() click to toggle source
# File lib/chef/provider/deploy.rb, line 343
def purge_tempfiles_from_current_release
  log_info = @new_resource.purge_before_symlink.join(", ")
  converge_by("purge directories in checkout #{log_info}") do
    @new_resource.purge_before_symlink.each { |dir| FileUtils.rm_rf(release_path + "/#{dir}") }
    Chef::Log.info("#{@new_resource} purged directories in checkout #{log_info}")
  end
end
restart() click to toggle source
# File lib/chef/provider/deploy.rb, line 216
def restart
  if restart_cmd = @new_resource.restart_command
    if restart_cmd.kind_of?(Proc)
      Chef::Log.info("#{@new_resource} restarting app with embedded recipe")
      recipe_eval(&restart_cmd)
    else
      converge_by("restart app using command #{@new_resource.restart_command}") do
        Chef::Log.info("#{@new_resource} restarting app")
        run_command(run_options(:command => @new_resource.restart_command, :cwd => @new_resource.current_path))
      end
    end
  end
end
rollback() click to toggle source
# File lib/chef/provider/deploy.rb, line 170
def rollback
  Chef::Log.info "#{@new_resource} rolling back to previous release #{release_path}"
  symlink
  Chef::Log.info "#{@new_resource} restarting with previous release"
  restart
end
rollback_to(target_release_path) click to toggle source
# File lib/chef/provider/deploy.rb, line 135
def rollback_to(target_release_path)
  @release_path = target_release_path

  rp_index = all_releases.index(release_path)
  releases_to_nuke = all_releases[(rp_index + 1)..-1]

  rollback

  releases_to_nuke.each do |i|
    converge_by("roll back by removing release #{i}") do
      Chef::Log.info "#{@new_resource} removing release: #{i}"
      FileUtils.rm_rf i
    end
    release_deleted(i)
  end
end
run(command, &block) click to toggle source
# File lib/chef/provider/deploy.rb, line 62
def run(command, &block)
  exec = execute(command, &block)
  exec.user(@new_resource.user) if @new_resource.user
  exec.group(@new_resource.group) if @new_resource.group
  exec.cwd(release_path) unless exec.cwd
  exec.environment(@new_resource.environment) unless exec.environment
  converge_by("execute #{command}") do
    exec
  end
end
run_scm_sync() click to toggle source
# File lib/chef/provider/deploy.rb, line 258
def run_scm_sync
  @scm_provider.run_action(:sync)
end
sudo(command,&block) click to toggle source
# File lib/chef/provider/deploy.rb, line 58
def sudo(command,&block)
  execute(command, &block)
end
svn_force_export() click to toggle source
# File lib/chef/provider/deploy.rb, line 262
def svn_force_export
  Chef::Log.info "#{@new_resource} exporting source repository"
  @scm_provider.run_action(:force_export)
end
update_cached_repo() click to toggle source
# File lib/chef/provider/deploy.rb, line 249
def update_cached_repo
  if @new_resource.svn_force_export
  # TODO assertion, non-recoverable - @scm_provider must be svn if force_export?
    svn_force_export
  else
    run_scm_sync
  end
end
verify_directories_exist() click to toggle source
# File lib/chef/provider/deploy.rb, line 285
def verify_directories_exist
  create_dir_unless_exists(@new_resource.deploy_to)
  create_dir_unless_exists(@new_resource.shared_path)
end
whyrun_supported?() click to toggle source
# File lib/chef/provider/deploy.rb, line 49
def whyrun_supported?
  true
end

Protected Instance Methods

create_dir_unless_exists(dir) click to toggle source
# File lib/chef/provider/deploy.rb, line 419
def create_dir_unless_exists(dir)
  if ::File.directory?(dir)
    Chef::Log.debug "#{@new_resource} not creating #{dir} because it already exists"
    return false
  end
  converge_by("create new directory #{dir}") do
    begin
      FileUtils.mkdir_p(dir)
      Chef::Log.debug "#{@new_resource} created directory #{dir}"
      if @new_resource.user
        FileUtils.chown(@new_resource.user, nil, dir)
        Chef::Log.debug("#{@new_resource} set user to #{@new_resource.user} for #{dir}")
      end
      if @new_resource.group
        FileUtils.chown(nil, @new_resource.group, dir)
        Chef::Log.debug("#{@new_resource} set group to #{@new_resource.group} for #{dir}")
      end
    rescue => e
      raise Chef::Exceptions::FileNotFound.new("Cannot create directory #{dir}: #{e.message}")
    end
  end
end
current_release?(release) click to toggle source
# File lib/chef/provider/deploy.rb, line 474
def current_release?(release)
  @previous_release_path == release
end
deployed?(release) click to toggle source
# File lib/chef/provider/deploy.rb, line 470
def deployed?(release)
  all_releases.include?(release)
end
gem_packages() click to toggle source
# File lib/chef/provider/deploy.rb, line 383
def gem_packages
  return [] unless ::File.exist?("#{release_path}/gems.yml")
  gems = YAML.load(IO.read("#{release_path}/gems.yml"))

  gems.map do |g|
    r = Chef::Resource::GemPackage.new(g[:name], run_context)
    r.version g[:version]
    r.action :install
    r.source "http://gems.github.com"
    r
  end
end
gem_resource_collection_runner() click to toggle source
# File lib/chef/provider/deploy.rb, line 375
def gem_resource_collection_runner
  gems_collection = Chef::ResourceCollection.new
  gem_packages.each { |rbgem| gems_collection << rbgem }
  gems_run_context = run_context.dup
  gems_run_context.resource_collection = gems_collection
  Chef::Runner.new(gems_run_context)
end
install_gems() click to toggle source
# File lib/chef/provider/deploy.rb, line 371
def install_gems
  gem_resource_collection_runner.converge
end
release_created(release_path) click to toggle source

Internal callback, called after copy_cached_repo. Override if you need to keep state externally. Note that YOU are responsible for implementing whyrun-friendly behavior in any actions you take in this callback.

# File lib/chef/provider/deploy.rb, line 357
def release_created(release_path)
end
release_deleted(release_path) click to toggle source

Note that YOU are responsible for using appropriate whyrun nomenclature Override if you need to keep state externally. Note that YOU are responsible for implementing whyrun-friendly behavior in any actions you take in this callback.

# File lib/chef/provider/deploy.rb, line 364
def release_deleted(release_path)
end
release_slug() click to toggle source
# File lib/chef/provider/deploy.rb, line 367
def release_slug
  raise Chef::Exceptions::Override, "You must override release_slug in #{self.to_s}"
end
run_callback_from_file(callback_file) click to toggle source
# File lib/chef/provider/deploy.rb, line 410
def run_callback_from_file(callback_file)
  Chef::Log.info "#{@new_resource} queueing checkdeploy hook #{callback_file}"
  recipe_eval do
    Dir.chdir(release_path) do
      from_file(callback_file) if ::File.exist?(callback_file)
    end
  end
end
run_options(run_opts={}) click to toggle source
# File lib/chef/provider/deploy.rb, line 396
def run_options(run_opts={})
  run_opts[:user] = @new_resource.user if @new_resource.user
  run_opts[:group] = @new_resource.group if @new_resource.group
  run_opts[:environment] = @new_resource.environment if @new_resource.environment
  run_opts[:log_tag] = @new_resource.to_s
  run_opts[:log_level] ||= :debug
  if run_opts[:log_level] == :info
    if STDOUT.tty? && !Chef::Config[:daemon] && Chef::Log.info?
      run_opts[:live_stream] = STDOUT
    end
  end
  run_opts
end
save_release_state() click to toggle source
# File lib/chef/provider/deploy.rb, line 463
def save_release_state
  if ::File.exists?(@new_resource.current_path)
    release = ::File.readlink(@new_resource.current_path)
    @previous_release_path = release if ::File.exists?(release)
  end
end
with_rollback_on_error() click to toggle source
# File lib/chef/provider/deploy.rb, line 442
def with_rollback_on_error
  yield
rescue ::Exception => e
  if @new_resource.rollback_on_error
    Chef::Log.warn "Error on deploying #{release_path}: #{e.message}"
    failed_release = release_path

    if previous_release_path
      @release_path = previous_release_path
      rollback
    end
    converge_by("remove failed deploy #{failed_release}") do
      Chef::Log.info "Removing failed deploy #{failed_release}"
      FileUtils.rm_rf failed_release
    end
    release_deleted(failed_release)
  end

  raise
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.