Created
February 13, 2014 12:42
-
-
Save madAndroid/8974434 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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