Skip to content

Instantly share code, notes, and snippets.

@tily
Created June 7, 2013 02:27
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tily/5726678 to your computer and use it in GitHub Desktop.
Save tily/5726678 to your computer and use it in GitHub Desktop.
#!/bin/bash
OPSWORKS_USERDATA_LOG_FILE='/var/log/aws/opsworks/user-data.log'
export OPSWORKS_USERDATA_LOG_FILE
mkdir -p $(dirname ${OPSWORKS_USERDATA_LOG_FILE})
touch ${OPSWORKS_USERDATA_LOG_FILE} && chmod 0600 ${OPSWORKS_USERDATA_LOG_FILE}
exec &> >(tee -a ${OPSWORKS_USERDATA_LOG_FILE}) 2>&1
conslog () {
echo "[$(date --rfc-2822)] opsworks-init: $1" | tee -a ${OPSWORKS_USERDATA_LOG_FILE} > /dev/console
}
execute () {
conslog "Executing: $1"
eval "$1"
if (($? > 0))
then
conslog "Bailing out, failed to execute: $1"
exit
fi
}
set -e
conslog 'Initiallizing AWS OpsWorks environment'
conslog 'Installing system packages'
execute 'yum -y update'
execute 'yum -y install ruby ruby-devel rubygems libicu-devel openssl-devel libxml2-devel libxslt-devel'
conslog 'Setup motd'
release_name=`head -1 /etc/issue |sed -e 's/ \\\\.*//'`
cat <<EOM > /etc/motd.opsworks-static
This instance is managed with AWS OpsWorks.
###### OpsWorks Summary ######
Operating System: $release_name
OpsWorks Instance: pantherinae
OpsWorks Instance ID: bbad6b41-6787-4276-9c15-7ca11ad81db7
OpsWorks Layers: rails-app
OpsWorks Stack: tily001
EC2 Region: us-east-1
EC2 Availability Zone: us-east-1d
Visit http://aws.amazon.com/opsworks for more information.
EOM
execute 'rm /etc/motd && ln -s /etc/motd.opsworks-static /etc/motd'
conslog 'Creating agent pre-install script'
umask 0077
WORKDIR='/var/lib/aws/opsworks'
mkdir -p ${WORKDIR}
chmod 0750 ${WORKDIR}
# download the agent and initiate installation
DOWNLOAD_AND_INITIATE="${WORKDIR}/spark.rb"
cat <<"EOM" > $DOWNLOAD_AND_INITIATE
#!/usr/bin/env ruby
#
# This downloads the opsworks instance agent and initiates its installation
require 'fileutils'
require 'yaml'
LOG_FILE = ENV['OPSWORKS_USERDATA_LOG_FILE'] || '/dev/null'
OPSWORKS_AGENT_INSTALLER_URL='https://opsworks-instance-agent.s3.amazonaws.com:443/103/opsworks-agent-installer.tgz'
OPSWORKS_AGENT_INSTALLER_TGZ='opsworks-agent-installer.tgz'
OPSWORKS_AGENT_INSTALLER_DIR='opsworks-agent-installer'
# encoding: UTF-8
require 'digest/sha1'
module OpsWorks
module Instance
class Downloader
attr_accessor :url, :log_file, :checksum_url
def initialize(url, log_file = nil, checksum_url = nil)
raise ArgumentError, 'need URL' if url.nil?
@url = url
@log_file = log_file
@checksum_url = checksum_url || url + '-checksum'
end
def download
ensure_wget_installed
fetch_with_checksum
end
protected
def log(msg)
`echo "\n#{msg}\n" >> #{log_file}` if log_file
end
def ensure_wget_installed
unless system('which wget > /dev/null')
log "wget not installed - aborting download"
raise "wget is not installed or not in $PATH"
end
end
def fetch(path, retried_count = 0)
raise "failed to download #{path} after #{retried_count} attempts" if retried_count > 4
if log_file
downloaded = system("wget -nv -T 30 -O /tmp/#{File.basename(path)} #{path} >> #{log_file} 2>&1")
else
downloaded = system("wget -nv -T 30 -O /tmp/#{File.basename(path)} #{path}")
end
unless downloaded
retried_count += 1
log "download of #{path} failed - sleeping #{retried_count * 15}"
sleep retried_count * 15
fetch(path, retried_count)
end
end
def fetch_with_checksum(retried_count = 0)
raise "failed to match checkum of #{url} after #{retried_count} attempts" if retried_count > 4
fetch(url)
fetch(checksum_url)
content_path = "/tmp/#{File.basename(url)}"
checksum_path = "/tmp/#{File.basename(checksum_url)}"
unless content_verified?(content_path, checksum_path)
retried_count += 1
log "checksum mismatch - re-downloading after #{retried_count * 15} seconds"
FileUtils.rm(content_path) if File.exists?(content_path)
FileUtils.rm(checksum_path) if File.exists?(checksum_path)
sleep retried_count * 15
fetch_with_checksum(retried_count)
end
log "Download of #{url} finished and checksum verified"
end
def content_verified?(content_path, checksum_path)
size_ok?(content_path, checksum_path) && checksum_ok?(content_path, checksum_path)
end
def size_ok?(content_path, checksum_path)
log "verifying size of #{content_path}"
original_size = nil
File.readlines( checksum_path ).each do |line|
next unless line.match(/\ASIZE/)
original_size = line.chomp.sub(/\ASIZE\ /,'').to_i
end
return original_size == File.size(content_path)
end
def checksum_ok?(content_path, checksum_path)
log "verifying checksum of #{content_path}"
original_checksum = nil
File.readlines( checksum_path ).each do |line|
next unless line.match(/\ASHA-1/)
original_checksum = line.chomp.sub(/\ASHA-1\ /,'')
end
return original_checksum == checksum(content_path)
end
def checksum(file)
now = Time.now.to_i
case checksum_cmd
when :shasum
check = `shasum #{file}`.match(/([a-zA-Z0-9]+)\s+/)[1]
when :ruby
check = Digest::SHA1.hexdigest(File.read(file))
else
raise "No known checksum CMD? #{checksum_cmd.inspect}"
end
log "Took #{Time.now.to_i - now} seconds to compute checksum of #{file}"
check
end
def checksum_cmd
@_checksum_cmd ||= if system('which shasum > /dev/null')
log "using shasum as the checksum method"
:shasum
else
log "using Ruby's Digest::SHA1 as the checksum method"
:ruby
end
end
end
end
end
def generate_agent_pre_config
File.umask 0077
pre_config_file = '/var/lib/aws/opsworks/pre_config.yml'
FileUtils.mkdir_p File.dirname(pre_config_file)
File.open(pre_config_file, 'w') do |f|
f.print YAML.dump(
:hostname => 'pantherinae',
:identity => 'bbad6b41-6787-4276-9c15-7ca11ad81db7',
:agent_installer_base_url => 'opsworks-instance-agent.s3.amazonaws.com',
:agent_installer_tgz => 'opsworks-agent-installer.tgz',
:verbose => false,
:instance_service_region => 'us-east-1',
:instance_service_endpoint => 'opsworks-instance-service.us-east-1.amazonaws.com',
:instance_service_port => '443',
:instance_public_key => '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuTv2vHNXd2Fk6vfNq6s2
ChAVVcshrHWYnnGBPuaA3/lS7uyPN/o2R57q9b3k8RWprKDQMErinazBSqwq/aAa
B94ZkCFlCtjcapV7M6a9/uPt42TW8jx2ysWz3zAttUKQ6hWV9qCwxEtusM9vaRlJ
DAJ9MINeegm6yf64b7aj7Wu7T8JBMOvpwvjgWZFU2bVS2UuS6AvVR4gfOxDupKpJ
LDWghADR37RNrcJND2ei5x6lHMDPV2ZaDJ9OG22JoeQltDR47SkBOHgnlvYVVOCB
uMyQoeyeuygkmPp+JFBJxz0K3xepgLLlSuTh6hkK7NpO6lJjEnbf6GvyGgYOBd23
9wIDAQAB
-----END PUBLIC KEY-----
',
:instance_private_key => '-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuTv2vHNXd2Fk6vfNq6s2ChAVVcshrHWYnnGBPuaA3/lS7uyP
N/o2R57q9b3k8RWprKDQMErinazBSqwq/aAaB94ZkCFlCtjcapV7M6a9/uPt42TW
8jx2ysWz3zAttUKQ6hWV9qCwxEtusM9vaRlJDAJ9MINeegm6yf64b7aj7Wu7T8JB
MOvpwvjgWZFU2bVS2UuS6AvVR4gfOxDupKpJLDWghADR37RNrcJND2ei5x6lHMDP
V2ZaDJ9OG22JoeQltDR47SkBOHgnlvYVVOCBuMyQoeyeuygkmPp+JFBJxz0K3xep
gLLlSuTh6hkK7NpO6lJjEnbf6GvyGgYOBd239wIDAQABAoIBADdUagcwjNfkB7kH
/C9jHOk0lKrj2lMhbU0mqmyXfbdpShSEJOObocsS9SwiZNh+mAgwoP9L3xUqHTKo
6s6HnD7tYMVktEHhNTXBIOP00pvoiY56+Jmy5ej71Ra91WlnUNIbUIgyx5pazd2S
mLzUCLXFqI9tLjNV5K2hoIX+EElCelSXV4BrmPk4K75PyfZ08D++jouopyVCf6DV
5MA81iYqradx4amaqXY8EU5FN8iEuBiiyWlx4kfYCPSL1rORJHVCjAsYoWKN6qPD
QDWvfwwJ3hajJJ3qLjp4Ttf2tYBI9ghMqT5+JYRgulVvW0utCpnz1+BiDASCvhir
elRutEECgYEA7BEvCZ72zZNvN8UW9ipmpATc1qhIx4B1VcWmkMxB8wJ31j0DoH1/
VGYcqNGluGLlWLYDctmgXQ/KWiWEvlJ4P2+j/8tvJ0zBCKinOTrqj91ijginZyw4
cbf4k1a48CLm0gxlkC0lGUWydicAYVyrL4QnWC+or/LHuu70tsXRQYMCgYEAyN/6
hqoVaza9zEZIQRnSn36Oj8HqxZ5bA5UlqggXsoBaqsJxuQcHNzOyg/0EsBCP6f2P
ndJ4hbNcG91dturivr4/e1wKqYuvanFr/wgTLQZLpKUuQ13IGVf6KIYrUCKGrps2
hmbPwic4gXEojgJRWvCZE55cM25IOObw3rtGaX0CgYEAzh6m6oihSJiOCK0PQnt9
SYNxbABeI8v3J6A/sriWcN/b0PVPYGeTsRbDuWfsghXf2Vh7pGF/EyLBdXrC8AdE
NWd+U8/GSxNKjQvtjxvxSRJthkTqHjzPQAP5iHS1X/+peQgX+g5hETL5Aw9E1r46
Pm/uhqJ+cLrUSFVy2M/ayY8CgYEAhI4A9UJhlflur9ElNwiCS8uo6caOVVAVQTz3
uMdUU++NZSehAfdYVJ4hzCV4JXMYdNFlJ0FPVPMx8hVXFTDU5ggHPjFTu+Q4omYs
XQQIG2sPGZwC9mN5evfqVTmMdXoDJE+6YGCR8s4M5KY2m9d9tiQU77RNpYa91AbZ
pjEw2sUCgYBWieKBYwWwjRiEMas0eU4HjykwqDG48TE2A2UGNyHQgc44pATj0Fs1
Orbb0d3DZt1uV97bOxWtmh26ewGp7t1mC3qZTsUbtGqQDzTPbpuSULGCyeBSjEgS
ve8DvwA73DAm21Qn3BKsHczOKAR8KwmKpYDkH5fQ5epXYyEuBUhaTg==
-----END RSA PRIVATE KEY-----
',
:charlie_public_key => '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAni7eKdm34oaCvGiw96Fk
lyLX+aPfInYzilkk+AY3pXF6nijpQ2cm3ZeM2EoqZFTv3a/meosNBAs3Q3Sy1e4G
7Ibn/xwMof+iSBvimx3PGKFzNP0BhY9yS6AMEMxtmqksHb0glwmFeJcomdhxZV1F
ziWTtL6ZEyvCg0I7rxGm1ceQmD25eK90VcZVh4LJtNfnwcZRM4eC+KK9Qllxw5hW
vB4Z52JMMZbEG9MYCLydWSY9rnVkAyQ0ngJUaJ3q7JsbkBV/J5BcrGgcbioR1k+h
INRoHwBQU9WnT/x8W+N6vwJb4o6v2hBR1H2GSDLwyZ7wC8EVH+XafWYpU1g/nSEe
aQIDAQAB
-----END PUBLIC KEY-----
',
:wait_between_runs => '60'
)
end
end
def log(msg)
puts msg
`echo "#{msg}" >> #{LOG_FILE}`
end
def measure(description)
now = Time.now.to_i
log("#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - Starting: #{description}")
yield
log("#{Time.now.strftime("%Y-%m-%d %H:%M:%S")} - Finished: #{description} in #{Time.now.to_i - now} sec")
end
def download_installer
`rm -rf /tmp/#{File.basename(OPSWORKS_AGENT_INSTALLER_URL)}`
`rm -rf /tmp/#{File.basename(OPSWORKS_AGENT_INSTALLER_URL, '.tgz')}*`
OpsWorks::Instance::Downloader.new(OPSWORKS_AGENT_INSTALLER_URL, LOG_FILE).download
end
def unpack_agent
`cd /tmp && tar -xvzpof #{OPSWORKS_AGENT_INSTALLER_TGZ}`
end
def run_installer
`cd /tmp && ruby #{OPSWORKS_AGENT_INSTALLER_DIR}/boot.rb && cd /tmp && rm -rf #{OPSWORKS_AGENT_INSTALLER_DIR}*`
end
measure 'Running all Userdata' do
measure('Download Installer') { download_installer }
measure('Unpack Agent Package') { unpack_agent }
measure('Create pre configuration file') { generate_agent_pre_config }
measure('Run Installer') { run_installer }
end
EOM
conslog 'Running agent pre-install script'
execute "/usr/bin/ruby ${DOWNLOAD_AND_INITIATE}"
if (($? == 0))
then
execute "rm ${DOWNLOAD_AND_INITIATE}"
conslog 'Succesfully installed the AWS OpsWorks agent'
conslog 'Rebooting instance'
execute 'reboot'
else
conslog 'Agent Installation failed.'
conslog 'Please verify the log files found under /var/log/aws/opsworks and submmit findings to AWS Support.'
conslog 'You can also verify the instace system console on the AWS EC2 Console'
conslog 'This instance will be shutdown 30 minutes after this failure'
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment