class InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor
Constants
- InvalidCommandNameFailure
Attributes
command_methods[R]
deployment_system[R]
Public Class Methods
command(name, &blk)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 43 def self.command(name, &blk) @command_methods ||= Hash.new method = Seahorse::Util.underscore(name).to_sym @command_methods[name] = method define_method(method, &blk) end
new(options = {})
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 24 def initialize(options = {}) @deployment_system = "CodeDeploy" @hook_mapping = options[:hook_mapping] if(!@hook_mapping.nil?) map end begin max_revisions = ProcessManager::Config.config[:max_revisions] @archives_to_retain = max_revisions.nil?? ARCHIVES_TO_RETAIN : Integer(max_revisions) if @archives_to_retain < 0 raise ArgumentError end rescue ArgumentError log(:error, "Invalid configuration :max_revision=#{max_revisions}") Platform.util.quit() end log(:info, "Archives to retain is: #{@archives_to_retain}}") end
Public Instance Methods
command_method(command_name)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 66 def command_method(command_name) raise InvalidCommandNameFailure.new("Unsupported command type: #{command_name}.") unless self.class.command_methods.has_key?(command_name) self.class.command_methods[command_name] end
execute_command(command, deployment_specification)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 52 def execute_command(command, deployment_specification) method_name = command_method(command.command_name) log(:debug, "Command #{command.command_name} maps to method #{method_name}") deployment_specification = DeploymentSpecification.parse(deployment_specification) log(:debug, "Successfully parsed the deployment spec") log(:debug, "Creating deployment root directory #{deployment_root_dir(deployment_specification)}") FileUtils.mkdir_p(deployment_root_dir(deployment_specification)) raise "Error creating deployment root directory #{deployment_root_dir(deployment_specification)}" if !File.directory?(deployment_root_dir(deployment_specification)) send(method_name, command, deployment_specification) end
map()
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 120 def map @hook_mapping.each_pair do |command, lifecycle_events| InstanceAgent::Plugins::CodeDeployPlugin::CommandExecutor.command command do |cmd, deployment_spec| #run the scripts script_log = ScriptLog.new lifecycle_events.each do |lifecycle_event| hook_command = HookExecutor.new(:lifecycle_event => lifecycle_event, :application_name => deployment_spec.application_name, :deployment_id => deployment_spec.deployment_id, :deployment_group_name => deployment_spec.deployment_group_name, :deployment_group_id => deployment_spec.deployment_group_id, :deployment_root_dir => deployment_root_dir(deployment_spec), :last_successful_deployment_dir => last_successful_deployment_dir(deployment_spec.deployment_group_id), :app_spec_path => app_spec_path) script_log.concat_log(hook_command.execute) end script_log.log end end end
Private Instance Methods
app_spec_path()
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 370 def app_spec_path 'appspec.yml' end
archive_root_dir(deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 152 def archive_root_dir(deployment_spec) File.join(deployment_root_dir(deployment_spec), 'deployment-archive') end
artifact_bundle(deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 365 def artifact_bundle(deployment_spec) File.join(deployment_root_dir(deployment_spec), 'bundle.tar') end
cleanup_old_archives(deployment_group)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 340 def cleanup_old_archives(deployment_group) deployment_archives = Dir.entries(File.join(ProcessManager::Config.config[:root_dir], deployment_group)) # remove . and .. deployment_archives.delete(".") deployment_archives.delete("..") full_path_deployment_archives = deployment_archives.map{ |f| File.join(ProcessManager::Config.config[:root_dir], deployment_group, f)} extra = full_path_deployment_archives.size - @archives_to_retain return unless extra > 0 # Never remove the last successful deployment last_success = last_successful_deployment_dir(deployment_group) full_path_deployment_archives.delete(last_success) # Sort oldest -> newest, take first `extra` elements oldest_extra = full_path_deployment_archives.sort_by{ |f| File.mtime(f) }.take(extra) # Absolute path takes care of relative root directories directories = oldest_extra.map{ |f| File.absolute_path(f) } FileUtils.rm_rf(directories) end
default_app_spec(deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 166 def default_app_spec(deployment_spec) default_app_spec_location = File.join(archive_root_dir(deployment_spec), app_spec_path) log(:debug, "Checking for app spec in #{default_app_spec_location}") app_spec = ApplicationSpecification::ApplicationSpecification.parse(File.read(default_app_spec_location)) end
deployment_instructions_dir()
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 147 def deployment_instructions_dir() File.join(ProcessManager::Config.config[:root_dir], 'deployment-instructions') end
deployment_root_dir(deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 142 def deployment_root_dir(deployment_spec) File.join(ProcessManager::Config.config[:root_dir], deployment_spec.deployment_group_id, deployment_spec.deployment_id) end
description()
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 375 def description self.class.to_s end
download_from_github(deployment_spec, account, repo, commit, anonymous, token)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 226 def download_from_github(deployment_spec, account, repo, commit, anonymous, token) retries = 0 errors = [] if InstanceAgent::Platform.util.supported_oses.include? 'windows' deployment_spec.bundle_type = 'zip' format = 'zipball' else deployment_spec.bundle_type = 'tar' format = 'tarball' end uri = URI.parse("https://api.github.com/repos/#{account}/#{repo}/#{format}/#{commit}") options = {:ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER, :redirect => true, :ssl_ca_cert => ENV['AWS_SSL_CA_DIRECTORY']} if anonymous log(:debug, "Anonymous GitHub repository download requested.") else log(:debug, "Authenticated GitHub repository download requested.") options.update({'Authorization' => "token #{token}"}) end begin # stream bundle file to disk log(:info, "Requesting URL: '#{uri.to_s}'") File.open(artifact_bundle(deployment_spec), 'w+b') do |file| uri.open(options) do |github| log(:debug, "GitHub response: '#{github.meta.to_s}'") while (buffer = github.read(8 * 1024 * 1024)) file.write buffer end end end rescue OpenURI::HTTPError => e log(:error, "Could not download bundle at '#{uri.to_s}'. Server returned code #{e.io.status[0]} '#{e.io.status[1]}'") log(:debug, "Server returned error response body #{e.io.string}") errors << "#{e.io.status[0]} '#{e.io.status[1]}'" if retries < 3 time_to_sleep = (10 * (3 ** retries)) # 10 sec, 30 sec, 90 sec log(:debug, "Retrying download in #{time_to_sleep} seconds.") sleep(time_to_sleep) retries += 1 retry else raise "Could not download bundle at '#{uri.to_s}' after #{retries} retries. Server returned codes: #{errors.join("; ")}." end end end
download_from_s3(deployment_spec, bucket, key, version, etag)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 178 def download_from_s3(deployment_spec, bucket, key, version, etag) log(:debug, "Downloading artifact bundle from bucket '#{bucket}' and key '#{key}', version '#{version}', etag '#{etag}'") region = ENV['AWS_REGION'] || InstanceMetadata.region proxy_uri = nil if InstanceAgent::Config.config[:proxy_uri] proxy_uri = URI(InstanceAgent::Config.config[:proxy_uri]) end if InstanceAgent::Config.config[:log_aws_wire] s3 = Aws::S3::Client.new( :region => region, :ssl_ca_directory => ENV['AWS_SSL_CA_DIRECTORY'], # wire logs might be huge; customers should be careful about turning them on # allow 1GB of old wire logs in 64MB chunks :logger => Logger.new( File.join(InstanceAgent::Config.config[:log_dir], "#{InstanceAgent::Config.config[:program_name]}.aws_wire.log"), 16, 64 * 1024 * 1024), :http_wire_trace => true, :signature_version => 'v4', :http_proxy => proxy_uri) else s3 = Aws::S3::Client.new( :region => region, :ssl_ca_directory => ENV['AWS_SSL_CA_DIRECTORY'], :signature_version => 'v4', :http_proxy => proxy_uri) end File.open(artifact_bundle(deployment_spec), 'wb') do |file| if !version.nil? object = s3.get_object({:bucket => bucket, :key => key, :version_id => version}, :target => file) else object = s3.get_object({:bucket => bucket, :key => key}, :target => file) end if(!etag.nil? && !(etag.gsub(/"/,'').eql? object.etag.gsub(/"/,''))) msg = "Expected deployment artifact bundle etag #{etag} but was actually #{object.etag}" log(:error, msg) raise RuntimeError, msg end end log(:debug, "Download complete from bucket #{bucket} and key #{key}") end
last_install_file_path(deployment_group)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 173 def last_install_file_path(deployment_group) File.join(deployment_instructions_dir, "#{deployment_group}_last_successful_install") end
last_successful_deployment_dir(deployment_group)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 157 def last_successful_deployment_dir(deployment_group) last_install_file_location = last_install_file_path(deployment_group) return unless File.exist? last_install_file_location File.open last_install_file_location do |f| return f.read.chomp end end
log(severity, message)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 380 def log(severity, message) raise ArgumentError, "Unknown severity #{severity.inspect}" unless InstanceAgent::Log::SEVERITIES.include?(severity.to_s) InstanceAgent::Log.send(severity.to_sym, "#{description}: #{message}") end
unpack_bundle(cmd, bundle_file, deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 279 def unpack_bundle(cmd, bundle_file, deployment_spec) strip_leading_directory = deployment_spec.revision_source == 'GitHub' if strip_leading_directory # Extract to a temporary directory first so we can move the files around dst = File.join(deployment_root_dir(deployment_spec), 'deployment-archive-temp') actual_dst = File.join(deployment_root_dir(deployment_spec), 'deployment-archive') FileUtils.rm_rf(dst) else dst = File.join(deployment_root_dir(deployment_spec), 'deployment-archive') end if "tar".eql? deployment_spec.bundle_type InstanceAgent::Platform.util.extract_tar(bundle_file, dst) elsif "tgz".eql? deployment_spec.bundle_type InstanceAgent::Platform.util.extract_tgz(bundle_file, dst) elsif "zip".eql? deployment_spec.bundle_type Zip::File.open(bundle_file) do |zipfile| zipfile.each do |f| file_dst = File.join(dst, f.name) FileUtils.mkdir_p(File.dirname(file_dst)) zipfile.extract(f, file_dst) end end else # If the bundle was a generated through a Sabini Repository # it will be in tar format, and it won't have a bundle type InstanceAgent::Platform.util.extract_tar(bundle_file, dst) end if strip_leading_directory log(:info, "Stripping leading directory from archive bundle contents.") # Find leading directory to remove archive_root_files = Dir.entries(dst) archive_root_files.delete_if { |name| name == '.' || name == '..' } if (archive_root_files.size != 1) log(:warn, "Expected archive to have a single root directory containing the actual bundle root, but it had #{archive_root_files.size} entries instead. Skipping leading directory removal and using archive as is.") FileUtils.mv(dst, actual_dst) return end nested_archive_root = File.join(dst, archive_root_files[0]) log(:debug, "Actual archive root at #{nested_archive_root}. Moving to #{actual_dst}") FileUtils.mv(nested_archive_root, actual_dst) FileUtils.rmdir(dst) log(:debug, Dir.entries(actual_dst).join("; ")) end end
update_last_successful_install(deployment_spec)
click to toggle source
# File lib/instance_agent/plugins/codedeploy/command_executor.rb, line 333 def update_last_successful_install(deployment_spec) File.open(last_install_file_path(deployment_spec.deployment_group_id), 'w+') do |f| f.write deployment_root_dir(deployment_spec) end end