Module: Boucher
- Defined in:
- lib/boucher/io.rb,
lib/boucher/env.rb,
lib/boucher/util.rb,
lib/boucher/meals.rb,
lib/boucher/compute.rb,
lib/boucher/servers.rb,
lib/boucher/storage.rb,
lib/boucher/volumes.rb,
lib/boucher/addresses.rb,
lib/boucher/provision.rb,
lib/boucher/snapshots.rb
Defined Under Namespace
Modules: IO, Servers, Snapshots, Storage, Volumes
Constant Summary
collapse
- Config =
{
:branch => ENV["BRANCH"] || "master"
}
- EC2_CONFIG =
{
:provider => 'AWS',
:aws_secret_access_key => Boucher::Config[:aws_secret_access_key],
:aws_access_key_id => Boucher::Config[:aws_access_key_id],
:region => Boucher::Config[:aws_region]
}
- SERVER_TABLE_FORMAT =
"%-12s %-12s %-10s %-10s %-10s %-15s %-15s %-10s\n"
- S3_CONFIG =
{
:provider => 'AWS',
:aws_secret_access_key => Boucher::Config[:aws_secret_access_key],
:aws_access_key_id => Boucher::Config[:aws_access_key_id]
}
- FILE_TABLE_FORMAT =
"%-4s %-60s %-10s %-25s %-32s\n"
- VOLUME_TABLE_FORMAT =
"%-12s %-6s %-10s %-12s %-10s %-13s\n"
- ADDRESS_TABLE_FORMAT =
"%-15s %-12s\n"
- ADDRESS_OVERVIEW_TABLE_FORMAT =
"%12s %-15s %-12s\n"
- SNAPSHOT_TABLE_FORMAT =
"%-13s %-12s %-18s %-80s\n"
Class Method Summary
collapse
-
.address_overview ⇒ Object
-
.assert_env! ⇒ Object
-
.associate_addresses_for(meal, server) ⇒ Object
-
.associate_all_addresses ⇒ Object
-
.change_servers_state(servers, command, new_state) ⇒ Object
-
.compute ⇒ Object
-
.cook_meal(server, meal_name) ⇒ Object
-
.cook_recipe(server, recipe) ⇒ Object
-
.current_user ⇒ Object
-
.env_name ⇒ Object
-
.establish_server(server, meal_name) ⇒ Object
-
.find_server(meal, environment) ⇒ Object
-
.force_env!(name) ⇒ Object
-
.get_server(meal, environment, state) ⇒ Object
-
.json_to_meal(json) ⇒ Object
-
.meal(meal_name) ⇒ Object
-
.meals ⇒ Object
-
.print_address_overview(addresses) ⇒ Object
-
.print_addresses(addresses) ⇒ Object
-
.print_directory(directory) ⇒ Object
-
.print_file(file) ⇒ Object
-
.print_file_table_header ⇒ Object
-
.print_files(files) ⇒ Object
-
.print_server(server) ⇒ Object
-
.print_server_table_header ⇒ Object
-
.print_servers(servers) ⇒ Object
-
.print_snapshots(volumes) ⇒ Object
-
.print_volumes(volumes) ⇒ Object
-
.provision(meal) ⇒ Object
-
.resolve_servers(id_or_meal) ⇒ Object
-
.setup_meal(server, meal) ⇒ Object
-
.snapshots ⇒ Object
-
.ssh(server, command = nil) ⇒ Object
-
.ssh_command ⇒ Object
-
.ssh_open?(server) ⇒ Boolean
-
.storage ⇒ Object
-
.update_recipes(server) ⇒ Object
-
.verbose(*args) ⇒ Object
Class Method Details
.address_overview ⇒ Object
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
# File 'lib/boucher/addresses.rb', line 35
def self.address_overview
ips = {}
Boucher.compute.addresses.each do |ip|
ips[ip.public_ip] = {ip: ip.public_ip, server_id: ip.server_id}
end
Boucher.meals.each do |name, meal|
(meal[:elastic_ips] || []).each do |ip|
if ip.nil? || ip.size == 0
elsif ips[ip]
ips[ip][:meal] = name
else
ips[ip] = {meal: name, ip: ip}
end
end
end
ips
end
|
.assert_env! ⇒ Object
37
38
39
40
41
|
# File 'lib/boucher/env.rb', line 37
def self.assert_env!
unless ENV['BENV']
raise 'BENV must be set before running this command'
end
end
|
.associate_addresses_for(meal, server) ⇒ Object
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
# File 'lib/boucher/addresses.rb', line 54
def self.associate_addresses_for(meal, server)
ips = meal[:elastic_ips]
if ips.nil? || ips.empty?
puts "No Elastic IPs to associate for meal #{meal[:name]}."
return
end
ips.each do |ip|
address = Boucher.compute.addresses.get(ip)
if address
if address.server_id == server.id
puts "#{ip} already associated with #{meal[:name]}:#{server.id}"
else
puts "Associating #{ip} with #{meal[:name]}:#{server.id}"
address.server = server
end
else
puts "Elastic IP (#{ip}) not found. Skipping."
end
end
end
|
.associate_all_addresses ⇒ Object
75
76
77
78
79
80
81
82
83
84
85
86
87
88
|
# File 'lib/boucher/addresses.rb', line 75
def self.associate_all_addresses
meals = Boucher.meals
meals.each do |name, meal|
ips = meal[:elastic_ips]
if ips && ips.size > 0
begin
server = Boucher::Servers[name]
associate_addresses_for(meal, server)
rescue Boucher::Servers::NotFound => e
puts "Can't associate address to '#{name}' server because it can't be found."
end
end
end
end
|
.change_servers_state(servers, command, new_state) ⇒ Object
128
129
130
131
132
133
134
135
136
|
# File 'lib/boucher/servers.rb', line 128
def self.change_servers_state(servers, command, new_state)
print "#{command}-ing servers #{servers.map(&:id).join(", ")}..."
servers.each { |s| s.send(command.to_sym) }
servers.each { |s| s.wait_for { print "."; s.state == new_state }}
puts
Boucher.print_servers servers
puts
puts "The servers have been #{command}-ed."
end
|
.compute ⇒ Object
16
17
18
19
|
# File 'lib/boucher/compute.rb', line 16
def self.compute
@compute ||= Fog::Compute.new(EC2_CONFIG)
@compute
end
|
.cook_meal(server, meal_name) ⇒ Object
46
47
48
49
|
# File 'lib/boucher/compute.rb', line 46
def self.cook_meal(server, meal_name)
update_recipes(server)
ssh server, "cd infrastructure && sudo BENV=#{Boucher::Config[:env]} BRANCH=#{Boucher::Config[:branch]} chef-solo -c config/solo.rb -j config/#{meal_name}.json"
end
|
.cook_recipe(server, recipe) ⇒ Object
51
52
53
54
55
|
# File 'lib/boucher/compute.rb', line 51
def self.cook_recipe(server, recipe)
update_recipes(server)
ssh server, "echo '{\\\"run_list\\\": [\\\"recipe[#{recipe}]\\\"]}' > /tmp/single_recipe.json"
ssh server, "cd infrastructure && sudo BENV=#{Boucher::Config[:env]} BRANCH=#{Boucher::Config[:branch]} chef-solo -c config/solo.rb -j /tmp/single_recipe.json"
end
|
.current_user ⇒ Object
3
4
5
6
7
|
# File 'lib/boucher/util.rb', line 3
def self.current_user
`git config user.name`.strip
rescue
"unknown"
end
|
.env_name ⇒ Object
7
8
9
|
# File 'lib/boucher/env.rb', line 7
def self.env_name
Boucher::Config[:env] || ENV["BENV"] || :dev
end
|
.establish_server(server, meal_name) ⇒ Object
22
23
24
25
26
27
28
29
30
31
32
33
|
# File 'lib/boucher/provision.rb', line 22
def self.establish_server(server, meal_name)
meal = Boucher.meal(meal_name)
if server.nil?
Boucher.provision(meal)
elsif server.state == "stopped"
Boucher::Servers.start([server])
server.reload
Boucher.cook_meal_on_server(meal, server)
else
Boucher.cook_meal_on_server(meal, server)
end
end
|
.find_server(meal, environment) ⇒ Object
18
19
20
|
# File 'lib/boucher/provision.rb', line 18
def self.find_server(meal, environment)
get_server(meal, environment, "stopped") || get_server(meal, environment, "running")
end
|
.force_env!(name) ⇒ Object
23
24
25
26
27
28
29
30
31
32
33
34
35
|
# File 'lib/boucher/env.rb', line 23
def self.force_env!(name)
env = name.to_sym
Boucher::Config[:branch] = ENV["BRANCH"] || "master"
load File.expand_path(File.dirname(__FILE__) + "/../../config/env/shared.rb") unless defined?(Boucher::NO_LOAD_CONFIG)
file = File.expand_path(File.dirname(__FILE__) + "/../../config/env/#{env}.rb")
unless defined?(Boucher::NO_LOAD_CONFIG)
if File.exist?(file)
load file
else
require_relative "../../config/env/shared"
end
end
end
|
.get_server(meal, environment, state) ⇒ Object
10
11
12
13
14
15
16
|
# File 'lib/boucher/provision.rb', line 10
def self.get_server(meal, environment, state)
begin
Boucher::Servers.find(:env => environment.to_s, :meal => meal, :state => state)
rescue Boucher::Servers::NotFound
nil
end
end
|
.json_to_meal(json) ⇒ Object
8
9
10
11
12
13
14
|
# File 'lib/boucher/meals.rb', line 8
def self.json_to_meal(json)
template = ERB.new(json)
json = template.result(binding)
parser = JSON.parser.new(json, :symbolize_names => true)
config = parser.parse
config[:boucher] || {}
end
|
.meal(meal_name) ⇒ Object
28
29
30
31
32
|
# File 'lib/boucher/meals.rb', line 28
def self.meal(meal_name)
the_meal = meals[meal_name.to_sym]
raise "Missing meal: #{meal_name}" unless the_meal
return the_meal
end
|
.meals ⇒ Object
16
17
18
19
20
21
22
23
24
25
26
|
# File 'lib/boucher/meals.rb', line 16
def self.meals
if @meals.nil?
@meals = {}
Dir.glob(File.join("config", "*.json")).each do |file|
spec = json_to_meal(::IO.read(file))
meal_name = File.basename(file)[0...-5].to_sym
@meals[meal_name] = spec.merge(:name => meal_name)
end
end
@meals
end
|
.print_address_overview(addresses) ⇒ Object
22
23
24
25
26
27
28
29
30
31
32
33
|
# File 'lib/boucher/addresses.rb', line 22
def self.print_address_overview(addresses)
puts
printf ADDRESS_OVERVIEW_TABLE_FORMAT, "Meal", "Public IP", "Server ID"
puts ("-" * 43)
addresses.values.each do |address|
printf ADDRESS_OVERVIEW_TABLE_FORMAT,
address[:meal],
address[:ip],
address[:server_id]
end
end
|
.print_addresses(addresses) ⇒ Object
8
9
10
11
12
13
14
15
16
17
18
|
# File 'lib/boucher/addresses.rb', line 8
def self.print_addresses(addresses)
puts
printf ADDRESS_TABLE_FORMAT, "Public IP", "Server ID"
puts ("-" * 29)
addresses.each do |address|
printf ADDRESS_TABLE_FORMAT,
address.public_ip,
address.server_id
end
end
|
.print_directory(directory) ⇒ Object
35
36
37
38
39
40
41
42
|
# File 'lib/boucher/storage.rb', line 35
def self.print_directory(directory)
printf FILE_TABLE_FORMAT,
"dir",
directory.key,
"",
directory.creation_date,
""
end
|
.print_file(file) ⇒ Object
26
27
28
29
30
31
32
33
|
# File 'lib/boucher/storage.rb', line 26
def self.print_file(file)
printf FILE_TABLE_FORMAT,
"file",
file.key,
file.content_length,
file.last_modified,
file.etag
end
|
20
21
22
23
24
|
# File 'lib/boucher/storage.rb', line 20
def self.
puts
printf FILE_TABLE_FORMAT, "Type", "Key", "Size", "Last Modified", "etag"
puts ("-" * 156)
end
|
.print_files(files) ⇒ Object
44
45
46
47
48
49
50
51
52
53
54
55
56
|
# File 'lib/boucher/storage.rb', line 44
def self.print_files(files)
files.each do |file|
if file
if file.class.name =~ /Directory/
print_directory(file)
else
print_file(file)
end
end
end
puts
end
|
.print_server(server) ⇒ Object
13
14
15
16
17
18
19
20
21
22
23
|
# File 'lib/boucher/servers.rb', line 13
def self.print_server(server)
printf SERVER_TABLE_FORMAT,
server.id,
(server.tags["Env"] || "???")[0...12],
(server.tags["Meal"] || "???")[0...10],
(server.tags["Creator"] || "???")[0...10],
server.state,
server.public_ip_address,
server.private_ip_address,
server.flavor_id
end
|
7
8
9
10
11
|
# File 'lib/boucher/servers.rb', line 7
def self.
puts
printf SERVER_TABLE_FORMAT, "ID", "Environment", "Meal", "Creator", "State", "Public IP", "Private IP", "Inst. Size"
puts ("-" * 107)
end
|
.print_servers(servers) ⇒ Object
25
26
27
28
29
30
31
32
33
|
# File 'lib/boucher/servers.rb', line 25
def self.print_servers(servers)
sorted_servers = servers.sort_by { |s| [s.tags["Env"] || "?",
s.tags["Meal"] || "?"] }
sorted_servers.each do |server|
print_server(server) if server
end
puts
end
|
.print_snapshots(volumes) ⇒ Object
7
8
9
10
11
12
13
14
15
16
17
18
19
|
# File 'lib/boucher/snapshots.rb', line 7
def self.print_snapshots(volumes)
puts
printf SNAPSHOT_TABLE_FORMAT, "ID", "Volume ID", "Created At", "Description"
puts ("-" * 120)
volumes.each do |snapshot|
printf SNAPSHOT_TABLE_FORMAT,
snapshot.id,
snapshot.volume_id,
snapshot.created_at.strftime("%b %d %Y %H:%M"),
snapshot.description[0...80]
end
end
|
.print_volumes(volumes) ⇒ Object
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
# File 'lib/boucher/volumes.rb', line 8
def self.print_volumes(volumes)
puts
printf VOLUME_TABLE_FORMAT, "ID", "Size", "Server", "Device", "State", "Snapshot"
puts ("-" * 75)
volumes.each do |volume|
printf VOLUME_TABLE_FORMAT,
volume.id,
volume.size.to_s + "GB",
volume.server_id,
volume.device,
volume.state,
volume.snapshot_id
end
end
|
.provision(meal) ⇒ Object
35
36
37
38
39
40
41
42
43
|
# File 'lib/boucher/provision.rb', line 35
def self.provision(meal)
puts "Provisioning new #{meal[:name]} server..."
server = create_meal_server(meal)
wait_for_server_to_boot(server)
wait_for_server_to_accept_ssh(server)
attach_volumes(meal, server)
cook_meal_on_server(meal, server)
puts "\nThe new #{meal[:name]} server has been provisioned! id: #{server.id}"
end
|
.resolve_servers(id_or_meal) ⇒ Object
138
139
140
141
142
143
144
145
146
147
148
|
# File 'lib/boucher/servers.rb', line 138
def self.resolve_servers(id_or_meal)
if id_or_meal[0..1] == "i-"
puts "Retrieving server with id #{id_or_meal}..."
[Boucher::Servers.with_id(id_or_meal)]
else
puts "Searching for running #{id_or_meal} servers in #{Boucher.env_name} environment..."
servers = Boucher::Servers.search(:meal => id_or_meal, :env => Boucher.env_name, :state => "!terminated")
Boucher::print_servers(servers)
servers
end
end
|
.setup_meal(server, meal) ⇒ Object
34
35
36
37
38
39
40
41
42
43
44
45
|
# File 'lib/boucher/meals.rb', line 34
def self.setup_meal(server, meal)
server.image_id = meal[:image_id] || Boucher::Config[:default_image_id]
server.flavor_id = meal[:flavor_id] || Boucher::Config[:default_flavor_id]
server.groups = meal[:groups] || Boucher::Config[:default_groups]
server.key_name = meal[:key_name] || Boucher::Config[:aws_key_filename]
server.tags = {}
server.tags["Name"] = "#{meal[:name] || "base"} #{Time.new.strftime("%Y%m%d%H%M%S")}"
server.tags["Meal"] = meal[:name] || "base"
server.tags["CreatedAt"] = Time.new.strftime("%Y%m%d%H%M%S")
server.tags["Creator"] = current_user
server.tags["Env"] = Boucher::Config[:env]
end
|
.snapshots ⇒ Object
21
22
23
|
# File 'lib/boucher/compute.rb', line 21
def self.snapshots
@snapshots ||= compute.snapshots
end
|
.ssh(server, command = nil) ⇒ Object
25
26
27
28
29
30
31
32
33
34
35
|
# File 'lib/boucher/compute.rb', line 25
def self.ssh(server, command=nil)
command_arg = nil
if command
command_arg = "\"#{command}\""
end
command = "#{ssh_command} #{Boucher::Config[:username]}@#{server.dns_name} #{command_arg}"
verbose command
Kernel.system command
raise "command failed with code #{$?.exitstatus}" unless $?.success?
end
|
.ssh_command ⇒ Object
37
38
39
|
# File 'lib/boucher/compute.rb', line 37
def self.ssh_command
"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{Boucher::Config[:aws_key_filename]}.pem"
end
|
.ssh_open?(server) ⇒ Boolean
57
58
59
60
61
62
|
# File 'lib/boucher/compute.rb', line 57
def self.ssh_open?(server)
ssh server, "echo 'SSH is open for business!'"
true
rescue Exception => e
false
end
|
.storage ⇒ Object
13
14
15
16
|
# File 'lib/boucher/storage.rb', line 13
def self.storage
@store ||= Fog::Storage.new(S3_CONFIG)
@store
end
|
.update_recipes(server) ⇒ Object
41
42
43
44
|
# File 'lib/boucher/compute.rb', line 41
def self.update_recipes(server)
puts "Updating recipes on #{server.id}"
ssh server, "cd infrastructure && git checkout . && git clean -d -f && git pull && bundle"
end
|
.verbose(*args) ⇒ Object
19
20
21
22
23
|
# File 'lib/boucher/io.rb', line 19
def self.verbose(*args)
if ENV["VERBOSE"]
puts *args
end
end
|