Skip to content

Instantly share code, notes, and snippets.

@jdrago999
Created January 30, 2015 21:05
Show Gist options
  • Save jdrago999/6f93c3b95423fa9bf8c7 to your computer and use it in GitHub Desktop.
Save jdrago999/6f93c3b95423fa9bf8c7 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby2.0
class Proxy
instance_methods.each do |m|
undef_method m unless m =~ /(^__|^send$|^object_id$)/
end
def initialize(*targets)
@targets = targets
end
protected
def method_missing(name, *args, &block)
@targets.map do |target|
target.public_send(name, *args, &block)
end
end
end
log_file_path = "/tmp/codedeploy-agent.update.log"
require 'logger'
if($stdout.isatty)
# if we are being run in a terminal, log to stdout and the log file.
@log = Logger.new(Proxy.new(File.open(log_file_path, 'a+'), $stdout))
else
# keep at most 2MB of old logs rotating out 1MB at a time
@log = Logger.new(log_file_path, 2, 1048576)
# make sure anything coming out of ruby ends up in the log file
$stdout.reopen(log_file_path, 'a+')
$stderr.reopen(log_file_path, 'a+')
end
@log.level = Logger::INFO
begin
# use the gems bundled with the agent if present
Gem.use_paths(nil, Gem.path << "/opt/codedeploy-agent/vendor")
require 'fileutils'
require 'openssl'
require 'open-uri'
require 'uri'
require 'json'
def usage
print <<EOF
install <package-type>
package-type: 'rpm', 'deb', or 'auto'
Installs fetches the latest package version of the specified type and
installs it. rpms are installed with yum; debs are installed using gdebi.
This program is invoked automatically to update the agent once per day using
the same package manager the codedeploy-agent is initially installed with.
To use this script for a hands free install on any system specify a package
type of 'auto'. This will detect if yum or gdebi is present on the system
and select the one present if possible. If both rpm and deb package managers
are detected the automatic detection will abort
When using the automatic setup, if the system has apt-get but not gdebi,
the gdebi will be installed using apt-get first.
EOF
end
def run_command(*args)
exit_ok = system(*args)
$stdout.flush
$stderr.flush
@log.debug("Exit code: #{$?.exitstatus}")
return exit_ok
end
def get_ec2_metadata_region
begin
uri = URI.parse('http://169.254.169.254/latest/meta-data/placement/availability-zone')
az = uri.read(:read_timeout => 120)
az.strip
rescue OpenURI::HTTPError => e
@log.warn("Could not get region from EC2 metadata service at '#{uri.to_s}'")
end
if (az !~ /[a-z]{2}-[a-z]+-\d+[a-z]/)
@log.warn("Invalid availability zone name: '#{az}'.")
nil
else
az.chop
end
end
def get_region
@log.info('Checking AWS_REGION environment variable for region information...')
region = ENV['AWS_REGION']
return region if region
@log.info('Checking EC2 metadata service for region information...')
region = get_ec2_metadata_region
return region if region
@log.info('Using fail-safe default region: us-east-1')
return 'us-east-1'
end
def get_s3_uri(region, bucket, key)
if (region == 'us-east-1')
URI.parse("https://s3.amazonaws.com/#{bucket}/#{key}")
else
URI.parse("https://s3-#{region}.amazonaws.com/#{bucket}/#{key}")
end
end
def get_package_from_s3(region, bucket, key, package_file)
@log.info("Downloading package from bucket #{bucket} and key #{key}...")
uri = get_s3_uri(region, bucket, key)
# stream package file to disk
begin
File.open(package_file, 'w+b') do |file|
uri.open(:ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER, :redirect => true, :read_timeout => 120) do |s3|
file.write(s3.read)
end
end
rescue OpenURI::HTTPError => e
@log.error("Could not find package to download at '#{uri.to_s}'")
exit(1)
end
end
def get_version_file_from_s3(region, bucket, key)
@log.info("Downloading version file from bucket #{bucket} and key #{key}...")
uri = get_s3_uri(region, bucket, key)
begin
version_string = uri.read(:ssl_verify_mode => OpenSSL::SSL::VERIFY_PEER, :redirect => true, :read_timeout => 120)
JSON.parse(version_string)
rescue OpenURI::HTTPError => e
@log.error("Could not find version file to download at '#{uri.to_s}'")
exit(1)
end
end
def install_from_s3(region, bucket, version_file_key, type, install_cmd)
version_data = get_version_file_from_s3(region, bucket, version_file_key)
package_key = version_data[type]
package_base_name = package_key.split('/')[-1] # base name for the key in S3
package_file = "/tmp/#{package_base_name}"
get_package_from_s3(region, bucket, package_key, package_file)
install_cmd << package_file
@log.info("Executing `#{install_cmd.join(" ")}`...")
if (!run_command(*install_cmd))
@log.error("Error installing #{package_file}.")
FileUtils.rm(package_file)
exit(1)
end
FileUtils.rm(package_file)
end
def do_sanity_check(cmd)
@log.info("Waiting for a while before I check for a running agent")
sleep(3 * 60)
res = run_command(cmd, 'codedeploy-agent', 'status')
if (res.nil? || res == false)
@log.info("No codedeploy agent seems to be running. Starting the agent.")
run_command(cmd, 'codedeploy-agent', 'start-no-update')
end
end
if (Process.uid != 0)
@log.error('Must run as root to install packages')
exit(1)
end
@log.info("Starting update check.")
if (ARGV.length > 1)
usage
@log.error('Too many arguments.')
exit(1)
elsif (ARGV.length < 1)
usage
@log.error('Expected package type as argument.')
exit(1)
end
@type = ARGV[0].downcase;
if (@type == 'auto')
@log.info('Attempting to automatically detect supported package manager type for system...')
has_yum = run_command('which yum >/dev/null 2>/dev/null')
has_apt_get = run_command('which apt-get >/dev/null 2>/dev/null')
has_gdebi = run_command('which gdebi >/dev/null 2>/dev/null')
has_zypper = run_command('which zypper >/dev/null 2>/dev/null')
if (has_yum && (has_apt_get || has_gdebi))
@log.error('Detected both supported rpm and deb package managers. Please specify which package type to use manually.')
exit(1)
end
if(has_yum)
@type = 'rpm'
elsif(has_zypper)
@type = 'zypper'
elsif(has_gdebi)
@type = 'deb'
elsif(has_apt_get)
@type = 'deb'
@log.warn('apt-get found but no gdebi. Installing gdebi with `apt-get install gdebi -y`...')
#use -y to answer yes to confirmation prompts
if(!run_command('/usr/bin/apt-get', 'install', 'gdebi', '-y'))
@log.error('Could not install gdebi.')
exit(1)
end
else
@log.error('Could not detect any supported package managers.')
exit(1)
end
end
region = get_region
bucket = "aws-codedeploy-#{region}"
version_file_key = 'latest/VERSION'
case @type
when 'help'
usage
when 'rpm'
#use -y to answer yes to confirmation prompts
install_cmd = ['/usr/bin/yum', '-y', 'localinstall']
install_from_s3(region, bucket, version_file_key, @type, install_cmd)
do_sanity_check('/sbin/service')
when 'deb'
#use -n for non-interactive mode
#use -o to not overwrite config files unless they have not been changed
install_cmd = ['/usr/bin/gdebi', '-n', '-o', 'Dpkg::Options::="--force-confdef"', '-o', 'Dpg::Options::="--force-conffold"']
install_from_s3(region, bucket, version_file_key, @type, install_cmd)
do_sanity_check('/usr/sbin/service')
when 'zypper'
#use -n for non-interactive mode
install_cmd = ['/usr/bin/zypper', 'install', '-n']
install_from_s3(region, bucket, version_file_key, 'rpm', install_cmd)
else
@log.error("Unsupported package type '#{@type}'")
exit(1)
end
@log.info("Update check complete.")
@log.info("Stopping updater.")
rescue SystemExit => e
# don't log exit() as an error
raise e
rescue Exception => e
# make sure all unhandled exceptions are logged to the log
@log.error("Unhandled exception: #{e.inspect}")
e.backtrace.each do |line|
@log.error(" at " + line)
end
exit(1)
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment