Skip to content

Instantly share code, notes, and snippets.

@madAndroid
Created February 13, 2014 12:42
Show Gist options
  • Save madAndroid/8974434 to your computer and use it in GitHub Desktop.
Save madAndroid/8974434 to your computer and use it in GitHub Desktop.
#!/usr/bin/env ruby
require 'yaml'
require 'optparse'
require 'fileutils'
require 'pp'
require 'json'
$text_highlight = "\e[1;37;41m"
$text_reset = "\e[0m"
##
### Options Parsing:
##
class OptparseCreateAMI
#def self.parse(args, env, defaults)
def self.parse(args)
defaults = YAML.load_file("config/defaults.yml")
if ARGV[0].nil? or ARGV[0][0] == '-'
env = 'base'
yaml_config = "config/base.yml"
settings = YAML.load_file("config/defaults.yml")
unless ARGV[0] == '--help'
log_warning("No [AMI_TYPE]-[ENV] given as first argument")
end
else
env = ARGV[0]
yaml_config = "config/#{ARGV[0]}.yml"
unless File.exists?("#{yaml_config}")
log_fatal_and_exit(2,"Yaml for '#{ARGV[0]}' does not exist in config directory")
else
settings = YAML.load_file("#{yaml_config}")
end
end
options = {}
options = options.merge(defaults)
optparse = OptionParser.new do |opts|
options[:banner] = "Usage: create_ami.rb AMI_TYPE-ENV [options], where AMI_TYPE is one of [ #{defaults[:available_ami_types].join(', ')} ]"
options[:sf_roles] = settings[:sf_roles] ||= defaults[:sf_roles]
opts.on('-r', '--sf-roles x,y,z', Array, 'Comma seperated list of roles for nodeless config') do |r|
options[:sf_roles] = r
end
options[:sf_location] = settings[:sf_location] ||= defaults[:sf_location]
opts.on('-l', '--sf-location [location]', 'sf_location to set, for nodeless config') do |l|
options[:sf_location] = l
end
options[:sf_environment] = settings[:sf_environment] ||= defaults[:sf_environment]
opts.on('-e', '--sf-environment [environment]', 'sf_environment to set, for nodeless config') do |e|
options[:sf_environment] = e
end
options[:hostname] = settings[:hostname] ||= defaults[:hostname]
opts.on('-h', '--hostname [hostname]', 'Comma seperated list of roles for nodeless config') do |h|
options[:hostname] = h
end
options[:sshkey_path] = settings[:sshkey_path] ||= defaults[:sshkey_path]
opts.on('-k', '--sshkey-path [relative_path]', 'sshkey_path to set, for nodeless config') do |l|
options[:sshkey_path] = l
end
options[:src_ami] = settings[:src_ami] ||= defaults[:base_ami]
opts.on('-s', '--src-ami', "Apply VPC specific modifications") do |s|
options[:src_ami] = s
end
options[:ami_type] = settings[:ami_type] ||= 'base'
opts.on('-a', '--ami-type', "AMI type to create - defaults to 'base'") do |ami|
options[:ami_type] = ami
end
options[:yaml_config] = "#{yaml_config}"
opts.on('-c', '--config', "path to YAML config with settings for #{env} - defaults to config/#{env}.yml") do |conf|
options[:yaml_config] = conf
end
options[:aws_access_key] = ENV['AWS_ACCESS_KEY']
opts.on('--aws-ak', "Set AWS_ACCESS_KEY (can be set with env var)") do |ak|
options[:aws_access_key] = ak
end
options[:aws_secret_key] = ENV['AWS_SECRET_KEY']
opts.on('--aws-sk', "Set AWS_SECRET_KEY (can be set with env var)") do |sk|
options[:aws_secret_key] = sk
end
options[:masterless] = false
opts.on('-d', '--[no-]masterless', "Run masterless - mostly used for bootstrapping") do |d|
options[:masterless] = d
end
options[:purge_checkout] = false
opts.on('-p', '--[no-]purge-checkout', "Run purge-checkout - used to remove repo checkout during (at end of) AMI build") do |p|
options[:purge_checkout] = p
end
options[:debug] = false
opts.on('-d', '--[no-]debug', "Provide extra output about what's happening") do |d|
options[:debug] = d
end
opts.separator ""
opts.separator "Common options:"
# No argument, shows at tail. This will print an options summary.
opts.on_tail("-h", "--help", "Show this message") do
puts opts
exit
end
# Another typical switch to print the version.
opts.on_tail("--version", "Show version") do
puts OptionParser::Version.join('.')
exit
end
end
if ARGV.count == 0
puts "\n *** No options provided *** \n\t"
$stderr.puts optparse
exit 2
end
begin
optparse.parse!
rescue OptionParser::InvalidOption, OptionParser::MissingArgument
$stderr.puts $!.to_s
$stderr.puts optparse
exit 2
end
options
end
end
## Main class:
class CreateAMI
attr_accessor :settings
def initialize(options)
environment = YAML.load_file("#{options[:yaml_config]}")
settings = environment.merge(options)
## set instance vars
@timestamp = Time.now.strftime("%Y-%m-%d-%H%M-%S")
@debug = settings[:debug]
@sf_roles = settings[:sf_roles]
@sf_location = settings[:sf_location]
@sf_environment = settings[:sf_environment]
@hostname = settings[:hostname]
@sshkey_path = settings[:sshkey_path]
@masterless = settings[:masterless]
@purge_checkout = settings[:purge_checkout]
@aws_ak = settings[:aws_access_key]
@aws_sk = settings[:aws_secret_key]
@s3_ak = settings[:s3_access_key]
@s3_sk = settings[:s3_secret_key]
@s3_firstboot_bucket = settings[:s3_firstboot_bucket]
@vpc_id = settings[:vpc_id]
@subnet_id = settings[:subnet_id]
@security_group_id = settings[:security_group_id]
@src_ami = settings[:src_ami]
@ami_type = settings[:ami_type]
@available_types = settings[:available_ami_types]
if settings[:hostname] != 'AWS-PROVIDED'
@user_data_hash = {
"hostname" => "#{settings[:hostname]}"
}
else
@user_data_hash = {}
end
@user_data_hash.merge!({
"sf_roles" => "#{@sf_roles.join(',')}",
"sf_location" => "#{@sf_location}",
"sf_environment" => "#{@sf_environment}"
})
@user_data = @user_data_hash.to_json
@user_datafile = "packer_cache/ec2_#{@ami_type}.json"
File.open("#{@user_datafile}","w") do |f|
f.write(@user_data)
end
if @debug
puts "Settings are:"
settings.each_pair { |k,v| pp "#{k}: #{v}" }
puts "user_data is: #{@user_data}"
end
check_prereqs(settings)
end
#
### Check prerequisites
#
def check_prereqs(options)
failed_check = false
if options[:aws_access_key].nil? or options[:aws_secret_key].nil?
puts 'Must set AWS_ACCESS_KEY and AWS_SECRET_KEY: either via switch, or env vars'
failed_check = true
end
if ! @available_types.include? @ami_type
puts "#{@ami_type} is not one of the available AMI types: #{@available_types.join(', ')}"
failed_check = true
end
options.each_pair { |k,v|
if v.nil?
puts "#{k} is a mandatory option, make sure you set '#{k}', either in yaml, or as argument to script"
failed_check = true
end
}
if ! File.file?("#{options[:yaml_config]}")
puts "YAML config file for #{options[:ami_type]} does not exists!"
failed_check = true
end
if ! File.file?("aws/ec2_#{@ami_type}.json")
puts "JSON template for AMI type: #{@ami_type} does not exist!"
failed_check = true
end
if failed_check
log_fatal_and_exit(2,"One or more pre-requisites failed")
end
end
def create_ami
packer_debug = '-debug' if @debug
system "packer build \
-var 'sf_roles=#{@sf_roles.join(',')}' \
-var 'sf_location=#{@sf_location}' \
-var 'sf_environment=#{@sf_environment}' \
-var 'aws_env=#{@sf_environment.upcase}' \
-var 'aws_access_key=#{@aws_ak}' \
-var 'aws_secret_key=#{@aws_sk}' \
-var 'hostname=#{@hostname}' \
-var 'path_to_id_rsa=#{@sshkey_path}' \
-var 's3_firstboot_bucket=#{@s3_firstboot_bucket}' \
-var 's3_access_key=#{@s3_ak}' \
-var 's3_secret_key=#{@s3_sk}' \
-var 'vpc_id=#{@vpc_id}' \
-var 'subnet_id=#{@subnet_id}' \
-var 'security_group_id=#{@security_group_id}' \
-var 'source_ami=#{@src_ami}' \
-var 'masterless=#{@masterless}' \
-var 'purge_checkout=#{@purge_checkout}' \
#{packer_debug} aws/ec2_#{@ami_type}.json"
end
end
def log_fatal_and_exit ( code, text )
$stderr.puts "* "*10
$stderr.puts "#{$text_highlight}[FATAL] #{text}#{$text_reset}"
$stderr.puts "* "*10
exit code
end
def log_warning( text )
$stderr.puts "* "*10
$stderr.puts "#{$text_highlight}[WARNING] #{text}#{$text_reset}"
$stderr.puts "* "*10
end
def log_info( text )
$stderr.puts "* "*10
$stderr.puts "[INFO] #{text}"
$stderr.puts "* "*10
end
def log_debug( text )
$stderr.puts "* "*10
$stderr.puts "[DEBUG] #{text}"
$stderr.puts "* "*10
end
#
## Load defaults and parse options
#
options = OptparseCreateAMI.parse(ARGV)
##
## main class instantiation and methods:
##
ami = CreateAMI.new(options)
ami.create_ami
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment