lib/stack.rb in aws-kicker-0.0.3 vs lib/stack.rb in aws-kicker-0.0.4
- old
+ new
@@ -1,14 +1,25 @@
require 'pp'
require 'fog'
module Stack
+ Logger = Logger.new(STDOUT)
+ Logger.level = ::Logger::INFO
+ Logger.datetime_format = "%Y-%m-%d %H:%M:%S"
+ Logger.formatter = proc do |severity, datetime, progname, msg|
+ "#{datetime} #{severity}: #{msg}\n"
+ end
+
+ @@gemhome = File.absolute_path(File.realpath(File.dirname(File.expand_path(__FILE__)) + '/..'))
+
def Stack.load_config(configfile, stack)
config_raw = File.read(configfile)
- eval(config_raw)
+ eval(config_raw)
+
config = StackConfig::Stacks[stack]
+ config[:stackhome] = File.dirname(File.expand_path(configfile))
config
end
def Stack.connect(config)
connection = Fog::Compute.new({ :provider => config[:provider],
@@ -17,35 +28,40 @@
:region => config[:region] })
connection
end
def Stack.populate_config(config)
- # build out the full config for each node, supplying defaults from the
+
+ config[:find_file_paths] = Array.new if config[:find_file_paths].nil?
+
+ # build out the full config for each node, supplying defaults from the
# global config if explicitly supplied
config[:node_details] = Hash.new if config[:node_details].nil?
config[:roles].each do |role, role_details|
fqdn = role.to_s + '.' + config[:dns_domain]
-
- config[:node_details][fqdn] = {
+
+ config[:node_details][fqdn] = {
# set the node details from the role, if not specified in the role, use the config global
# (takes advantage of left to right evaluation of ||)
:flavor_id => (role_details[:flavor_id] || config[:flavor_id]),
:count => (role_details[:count] || 1),
:publish_private_ip => (role_details[:publish_private_ip] || false),
- :dns_wildcard => (role_details[:dns_wildcard] || false)
+ :dns_wildcard => (role_details[:dns_wildcard] || false),
+ :bootstrap => (role_details[:bootstrap] || 'user-data.sh'),
+ :cloud_config_yaml => (role_details[:cloud_config_yaml] || 'cloud-init.yaml'),
}
end
end
def Stack.generate_hostnames(config)
stack_hostnames = Array.new
config[:roles].each do |role, role_details|
fqdn = role.to_s + '.' + config[:dns_domain]
stack_hostnames << fqdn
end
- stack_hostnames
+ stack_hostnames
end
def Stack.deploy_all(config)
# generate the hostnames & details from the config & apply defaults where required
Stack.populate_config(config)
@@ -62,21 +78,29 @@
puts "Skipping #{fqdn} as it already exists"
next
end
# Ubuntu 8.04/Hardy doesn't do full cloud-init, so we have to script setting the hostname
- user_data = File.read('user-data.sh')
+ libdir = File.realpath(@@gemhome + '/lib')
+
+ bootstrap_abs = Stack.find_file(config, config[:node_details][fqdn][:bootstrap])
+ cloud_config_yaml_abs = Stack.find_file(config, config[:node_details][fqdn][:cloud_config_yaml])
+
+ multipart_cmd = "#{libdir}/write-mime-multipart #{bootstrap_abs} #{cloud_config_yaml_abs}"
+ user_data = `#{multipart_cmd}`
+
user_data.gsub!(/rentpro-unconfigured/, hostname)
user_data.gsub!(/rentpro-stage.local/, config[:dns_domain])
+
# pp multipart
#
puts "Bootstraping new instance - #{fqdn}, in #{config[:availability_zone]}, flavor #{config[:node_details][fqdn][:flavor_id]}, image_id #{config[:image_id]}"
- server = connection.servers.create({
+ server = connection.servers.create({
:name => fqdn,
:hostname => fqdn,
- :availability_zone => config[:availability_zone],
+ :availability_zone => config[:availability_zone],
:flavor_id => config[:node_details][fqdn][:flavor_id],
:image_id => config[:image_id],
:key_name => config[:keypair],
:user_data => user_data,
:tags => { 'Name' => fqdn },
@@ -84,14 +108,15 @@
print "Waiting for instance to be ready..."
server.wait_for { ready? }
puts "#{role.to_s} is booted, #{server.public_ip_address}/#{server.private_ip_address}"
+ if false
# create/update the public & private DNS for this host
Stack.update_dns(role.to_s + '-public.' + config[:dns_domain], server.public_ip_address, config)
Stack.update_dns(role.to_s + '-private.' + config[:dns_domain], server.private_ip_address, config)
-
+
# create the dns
if (role_details[:publish_private_ip] == true && (!role_details[:publish_private_ip].nil?))
ip_address = server.private_ip_address
else
ip_address = server.public_ip_address
@@ -101,52 +126,53 @@
# is this a wildcard DNS host, then claim the *.domain.net
if (role_details[:dns_wildcard] == true && (!role_details[:dns_wildcard].nil?))
wildcard = "*." + config[:dns_domain]
Stack.update_dns(wildcard, ip_address, config)
end
+ end
end
end
def Stack.update_dns(fqdn, ip_address, config)
# now register it in DNS
dns = Fog::DNS.new({ :provider => config[:provider],
:aws_access_key_id => config[:aws_access_key_id],
:aws_secret_access_key => config[:aws_secret_access_key] })
-
+
# pp dns.get_hosted_zone(config[:dns_id])
-
+
bmtw = dns.zones.get(config[:dns_id])
-
- record = bmtw.records.get(fqdn)
- if record
- record.modify(:value => ip_address) if record
+
+ record = bmtw.records.get(fqdn)
+ if record
+ record.modify(:value => ip_address) if record
else
bmtw.records.create(:value => ip_address, :name => fqdn, :type => 'A')
end
end
def Stack.show_dns(config)
# now register it in DNS
dns = Fog::DNS.new({ :provider => config[:provider],
:aws_access_key_id => config[:aws_access_key_id],
:aws_secret_access_key => config[:aws_secret_access_key] })
-
+
zone = dns.zones.get(config[:dns_id])
if zone.records.empty?
puts "No DNS records found in #{config[:dns_domain]}"
else
printf("%40s %20s %5s %5s\n", 'fqdn', 'value', 'type', 'ttl')
zone.records.each do |record|
printf("%40s %20s %5s %5d\n", record.name, record.value, record.type, record.ttl)
end
- end
+ end
end
def Stack.get_running(config)
# create a connection
connection = Stack.connect(config)
-
+
# generate all the names that this stack will use
stack_hostnames = Stack.generate_hostnames(config)
# Amazon EC2, use the tags hash to find hostnames
running_instances = Hash.new
@@ -157,37 +183,37 @@
if stack_hostnames.include?(hostname)
running_instances[hostname] = instance
end
end
end
- running_instances
+ running_instances
end
def Stack.show_running(config)
running_instances = Stack.get_running(config)
running_instances.each do |instance, instance_details|
# display some details
puts "#{instance} id=#{instance_details.id} flavor_id=#{instance_details.flavor_id} public_ip=#{instance_details.public_ip_address} private_ip=#{instance_details.private_ip_address}"
end
end
-
+
def Stack.delete_node(config, fqdn)
running_instances = Stack.get_running(config)
if running_instances[fqdn].nil?
puts "ERROR: #{fqdn} isn't running!"
exit
else
- connection = Stack.connect(config)
+ Stack.connect(config)
pp running_instances[fqdn]
running_instances[fqdn].destroy
end
end
def Stack.show_details(config)
# create a connection
connection = Stack.connect(config)
-
+
pp connection.describe_regions
pp connection.describe_availability_zones
pp connection.servers
@@ -203,11 +229,11 @@
end
end
def shutdown_all(config)
# shutdown all instances
- connection.servers.select do |server|
+ connection.servers.select do |server|
puts "Running server:"
# pp server
# server.ready? && server.destroy
end
end
@@ -216,7 +242,43 @@
# sanity check
# check credentials, keys, flavor, image, dns etc
connection = Stack.connect(config)
pp connection
pp connection.images.get(config[:image_id])
+ end
+
+ def Stack.find_file(config, filename)
+ # find a file, using the standard path precedence
+ # 1) cwd
+ # 2) stackhome
+ # 2) stackhome + find_file_paths
+ # 3) gemhome/lib
+
+ if filename.nil? || filename.empty?
+ raise ArgumentError
+ end
+
+ dirs = [ '.' ] # current directory
+ dirs.push(config[:stackhome])
+ config[:find_file_paths].each { |fp| dirs.push(File.join(config[:stackhome], fp)) }
+ dirs.push(File.join(@@gemhome, 'lib'))
+ dirs.push('') # find absolute paths
+ dirs.flatten!
+
+ Logger.debug "find_file, looking for #{filename} in #{dirs}"
+ filename_fqp = ''
+ dirs.each do |dir|
+ fqp = File.join(dir, filename)
+ Logger.debug "find_file: checking #{fqp}"
+ if File.file?(fqp)
+ Logger.debug "find_file: found #{fqp}!"
+ filename_fqp = File.expand_path(fqp)
+ end
+ end
+
+ if filename_fqp.empty?
+ Logger.warn "couldn't find #{filename} in #{dirs}"
+ filename_fqp = nil
+ end
+ filename_fqp
end
end