boucher 0.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -63,18 +63,16 @@ If you use github, you'll have to generate ssh keys and add them to the github r
63
63
  cd ..
64
64
  git clone git@github.com:<github account name>/<your infratructure project name>.git infrastructure
65
65
 
66
- 10. Customize to your liking.
66
+ 11. Customize to your liking.
67
67
 
68
68
  * install your preferred vim dot files
69
69
  * etc...
70
70
 
71
-
72
-
73
-
74
-
75
71
  ## Usage
76
72
 
77
- ...
73
+ Run rake to see the list of tasks provided.
74
+
75
+ rake -T
78
76
 
79
77
  ## License
80
78
 
data/Rakefile CHANGED
@@ -8,3 +8,10 @@ end
8
8
 
9
9
  desc "Default: run the specs."
10
10
  task :default => :spec
11
+
12
+ desc "Push gem to rubygems.org"
13
+ task :push do
14
+ system "rm boucher-*.gem"
15
+ system "gem build boucher.gemspec"
16
+ system "gem push boucher-*.gem"
17
+ end
data/boucher.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "boucher"
5
- s.version = "0.1"
5
+ s.version = "0.1.1"
6
6
  s.authors = ["'Micah Micah'"]
7
7
  s.email = ["'micah@8thlight.com'"]
8
8
  s.homepage = "http://github.com/8thlight/boucher"
@@ -1,5 +1,5 @@
1
1
  require 'boucher/env'
2
- require 'boucher/classes'
2
+ require 'boucher/meals'
3
3
  require 'boucher/io'
4
4
  require 'fog'
5
5
  require 'retryable'
@@ -38,35 +38,14 @@ module Boucher
38
38
  "ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{Boucher::Config[:aws_key_filename]}.pem"
39
39
  end
40
40
 
41
- def self.download(server, remote_filepath, local_filepath)
42
- command = ["rsync", "-azb", "-e", ssh_command, "--delete-after", "#{Boucher::Config[:username]}@#{server.dns_name}:#{remote_filepath}", local_filepath]
43
- Kernel.system(*command)
44
- raise "command failed with code #{$?.exitstatus}: #{command.inspect}" unless $?.success?
45
- end
46
-
47
- def self.rsync(server, from, to)
48
- command = ["rsync", "-azb", "-e", ssh_command, "--delete-after",
49
- from, "#{Boucher::Config[:username]}@#{server.dns_name}:#{to}"]
50
- Kernel.system(*command)
51
- raise "command failed with code #{$?.exitstatus}: #{command.inspect}" unless $?.success?
52
- end
53
-
54
41
  def self.update_recipes(server)
55
42
  puts "Updating recipes on #{server.id}"
56
43
  ssh server, "cd infrastructure && git checkout . && git clean -d -f && git pull && bundle"
57
-
58
- %w[cookbooks config tasks].each do |folder|
59
- rsync server, "#{folder}/", "infrastructure/#{folder}/"
60
- end
61
44
  end
62
45
 
63
- def self.cook_meal(server, meal)
64
- Boucher::Nagios.remove_host(server)
65
-
46
+ def self.cook_meal(server, meal_name)
66
47
  update_recipes(server)
67
- ssh server, "cd infrastructure && sudo BUTCHER_ENV=#{Boucher::Config[:env]} BRANCH=#{Boucher::Config[:branch]} chef-solo -c config/solo.rb -j config/#{meal}.json"
68
-
69
- Boucher::Nagios.add_host(server)
48
+ ssh server, "cd infrastructure && sudo BUTCHER_ENV=#{Boucher::Config[:env]} BRANCH=#{Boucher::Config[:branch]} chef-solo -c config/solo.rb -j config/#{meal_name}.json"
70
49
  end
71
50
 
72
51
  def self.ssh_open?(server)
data/lib/boucher/env.rb CHANGED
@@ -38,9 +38,5 @@ module Boucher
38
38
  unless ENV['BUTCHER_ENV']
39
39
  raise 'BUTCHER_ENV must be set before running this command'
40
40
  end
41
-
42
- unless ENV['BRANCH']
43
- raise 'BRANCH must be set before running this command'
44
- end
45
41
  end
46
42
  end
data/lib/boucher/io.rb CHANGED
@@ -17,7 +17,7 @@ module Boucher
17
17
  end
18
18
 
19
19
  def self.verbose(*args)
20
- if ENV["VERBOSE"] != "false"
20
+ if ENV["VERBOSE"]
21
21
  puts *args
22
22
  end
23
23
  end
@@ -26,7 +26,7 @@ module Boucher
26
26
 
27
27
  def self.print_server_table_header
28
28
  puts
29
- printf SERVER_TABLE_FORMAT, "ID", "Environment", "Class", "Creator", "State", "Public IP", "Private IP", "Inst. Size"
29
+ printf SERVER_TABLE_FORMAT, "ID", "Environment", "Meal", "Creator", "State", "Public IP", "Private IP", "Inst. Size"
30
30
  puts ("-" * 120)
31
31
  end
32
32
 
@@ -34,7 +34,7 @@ module Boucher
34
34
  printf SERVER_TABLE_FORMAT,
35
35
  server.id,
36
36
  (server.tags["Env"] || "???")[0...12],
37
- (server.tags["Class"] || "???")[0...10],
37
+ (server.tags["Meal"] || "???")[0...10],
38
38
  (server.tags["Creator"] || "???")[0...10],
39
39
  server.state,
40
40
  server.public_ip_address,
@@ -45,7 +45,7 @@ module Boucher
45
45
  def self.print_servers(servers)
46
46
  print_server_table_header
47
47
  sorted_servers = servers.sort_by{|s| [s.tags["Env"] || "?",
48
- s.tags["Class"] || "?"]}
48
+ s.tags["Meal"] || "?"]}
49
49
  sorted_servers.each do |server|
50
50
  print_server(server) if server
51
51
  end
@@ -0,0 +1,45 @@
1
+ require 'boucher/env'
2
+ require 'boucher/util'
3
+ require 'json'
4
+
5
+ module Boucher
6
+
7
+ def self.json_to_meal(json)
8
+ parser = JSON.parser.new(json, :symbolize_names => true)
9
+ config = parser.parse
10
+ config[:boucher] || {}
11
+ end
12
+
13
+ def self.meals
14
+ if @meals.nil?
15
+ @meals = {}
16
+ Dir.glob(File.join("config", "*.json")).each do |file|
17
+ spec = json_to_meal(::IO.read(file))
18
+ meal_name = File.basename(file)[0...-5].to_sym
19
+ @meals[meal_name] = spec.merge(:name => meal_name)
20
+ end
21
+ end
22
+ @meals
23
+ end
24
+
25
+ def self.meal(meal_name)
26
+ the_meal = meals[meal_name.to_sym]
27
+ raise "Missing meal: #{mean_name}" unless the_meal
28
+ return the_meal
29
+ end
30
+
31
+ def self.setup_meal(server, meal)
32
+ server.image_id = meal[:base_image_id] || Boucher::Config[:base_image_id]
33
+ server.flavor_id = meal[:flavor_id] || Boucher::Config[:default_instance_flavor_id]
34
+ server.groups = meal[:groups] || Boucher::Config[:default_instance_groups]
35
+ server.key_name = meal[:key_name] || Boucher::Config[:aws_key_filename]
36
+ server.tags = {}
37
+ server.tags["Name"] = "#{meal[:meal_name] || "base"} #{Time.new.strftime("%Y%m%d%H%M%S")}"
38
+ server.tags["Meal"] = meal[:meal_name] || "base"
39
+ server.tags["CreatedAt"] = Time.new.strftime("%Y%m%d%H%M%S")
40
+ server.tags["Creator"] = current_user
41
+ server.tags["Env"] = Boucher::Config[:env]
42
+ server.tags["Volumes"] = meal[:volumes]
43
+ end
44
+
45
+ end
@@ -2,82 +2,61 @@ require 'boucher/compute'
2
2
  require 'boucher/io'
3
3
  require 'boucher/servers'
4
4
  require 'boucher/volumes'
5
- require 'boucher/nagios'
6
5
  require 'retryable'
7
6
 
8
7
  module Boucher
9
- def self.each_required_server(&block)
10
- Boucher::Config[:servers].each do |server_class|
11
- server = find_server(server_class, Boucher::Config[:env])
12
- block.yield(server, server_class)
13
- end
14
- end
15
8
 
16
- def self.get_server(server_class, environment, state)
9
+ def self.get_server(meal, environment, state)
17
10
  begin
18
- Boucher::Servers.find(:env => environment.to_s, :class => server_class, :state => state)
11
+ Boucher::Servers.find(:env => environment.to_s, :meal => meal, :state => state)
19
12
  rescue Boucher::Servers::NotFound
20
13
  nil
21
14
  end
22
15
  end
23
16
 
24
- def self.find_server(server_class, environment)
25
- get_server(server_class, environment, "stopped") || get_server(server_class, environment, "running")
26
- end
27
-
28
- def self.establish_all_servers
29
- Boucher.each_required_server do |server, server_class|
30
- # Retries after 2, 4, 8, 16, 32, and 64 seconds
31
- retryable(:tries => 6, :sleep => lambda { |n| 2**n }) do
32
- # A RuntimeError will sometimes be thrown here, with a message of:
33
- # "command failed with code 255"
34
- Boucher.establish_server(server, server_class)
35
- end
36
- end
17
+ def self.find_server(meal, environment)
18
+ get_server(meal, environment, "stopped") || get_server(meal, environment, "running")
37
19
  end
38
20
 
39
- def self.establish_server(server, server_class)
21
+ def self.establish_server(server, meal_name)
22
+ meal = Boucher.meal(meal_name)
40
23
  if server.nil?
41
- Boucher.provision(server_class, Boucher.server_classes[server_class.to_sym])
24
+ Boucher.provision(meal)
42
25
  elsif server.state == "stopped"
43
26
  Boucher::Servers.start(server.id)
44
27
  server.reload
45
- Boucher.cook_meals_on_server(server_class, Boucher.server_classes[server_class.to_sym], server)
28
+ Boucher.cook_meal_on_server(meal, server)
46
29
  else
47
- Boucher.cook_meals_on_server(server_class, Boucher.server_classes[server_class.to_sym], server)
30
+ Boucher.cook_meal_on_server(meal, server)
48
31
  end
49
32
  end
50
33
 
51
- def self.provision(class_name, class_map)
52
- puts "Provisioning new #{class_name} server..."
53
- server = create_classified_server(class_map)
34
+ def self.provision(meal)
35
+ puts "Provisioning new #{meal[:name]} server..."
36
+ server = create_meal_server(meal)
54
37
  wait_for_server_to_boot(server)
55
38
  wait_for_server_to_accept_ssh(server)
56
- volumes = create_volumes(class_map, server)
39
+ volumes = create_volumes(meal, server)
57
40
  attach_volumes(volumes, server)
58
- cook_meals_on_server(class_name, class_map, server)
59
- puts "\nThe new #{class_name} server has been provisioned! id: #{server.id}"
41
+ cook_meal_on_server(meal, server)
42
+ puts "\nThe new #{meal[:name]} server has been provisioned! id: #{server.id}"
60
43
  end
61
44
 
62
- def self.attach_elastic_ips(class_name, server)
45
+ def self.attach_elastic_ips(meal, server)
63
46
  puts "Attaching elastic IPs..."
64
- return unless Boucher::Config[:elastic_ips] && Boucher::Config[:elastic_ips][class_name]
47
+ ips = meal[:elastic_ips] || []
65
48
 
66
- puts "Associating #{server.id} with #{Boucher::Config[:elastic_ips][class_name]}"
67
- compute.associate_address(server.id, Boucher::Config[:elastic_ips][class_name])
49
+ ips.each do |ip|
50
+ puts "Associating #{server.id} with #{ip}"
51
+ compute.associate_address(server.id, ip)
52
+ end
68
53
  end
69
54
 
70
55
  private
71
56
 
72
- def self.cook_meals_on_server(class_name, class_map, server)
73
- return unless class_map[:meals]
74
-
75
- class_map[:meals].each do |meal|
76
- meal = meal.call if meal.is_a?(Proc)
77
- Boucher.cook_meal(server, meal)
78
- end
79
-
80
- attach_elastic_ips(class_name, server)
57
+ def self.cook_meal_on_server(meal, server)
58
+ Boucher.cook_meal(server, meal[:name])
59
+ attach_elastic_ips(meal, server)
81
60
  end
82
61
 
83
62
  def self.wait_for_server_to_accept_ssh(server)
@@ -93,16 +72,16 @@ module Boucher
93
72
  Boucher.print_servers([server])
94
73
  end
95
74
 
96
- def self.create_classified_server(class_map)
75
+ def self.create_meal_server(meal)
97
76
  server = compute.servers.new(:tags => {})
98
- Boucher.classify(server, class_map)
77
+ Boucher.setup_meal(server, meal)
99
78
  server.save
100
79
  Boucher.print_servers([server])
101
80
  server
102
81
  end
103
82
 
104
- def self.create_volumes(class_map, server)
105
- Array(class_map[:volumes]).map do |volume_name|
83
+ def self.create_volumes(meal, server)
84
+ Array(meal[:volumes]).map do |volume_name|
106
85
  attributes = Boucher.volume_configs[volume_name]
107
86
  snapshot = snapshots.get(attributes[:snapshot])
108
87
  puts "Creating volume from snapshot #{snapshot.id}..."
@@ -16,7 +16,7 @@ module Boucher
16
16
  cultivate(@instance)
17
17
  end
18
18
 
19
- %w{all of_class in_env in_state search find [] with_id}.each do |m|
19
+ %w{all of_meal in_env in_state search find [] with_id}.each do |m|
20
20
  module_eval "def #{m}(*args); instance.#{m}(*args); end"
21
21
  end
22
22
 
@@ -32,7 +32,7 @@ module Boucher
32
32
 
33
33
  def search(options={})
34
34
  servers = self
35
- servers = servers.of_class(options[:class]) if options[:class]
35
+ servers = servers.of_meal(options[:meal]) if options[:meal]
36
36
  servers = servers.in_env(options[:env]) if options[:env]
37
37
  servers = servers.in_state(options[:state]) if options[:state]
38
38
  servers
@@ -55,8 +55,8 @@ module Boucher
55
55
  Servers.cultivate(self.find_all {|s| s.state == state.to_s })
56
56
  end
57
57
 
58
- def of_class(klass)
59
- Servers.cultivate(self.find_all {|s| s.tags["Class"] == klass.to_s })
58
+ def of_meal(meal)
59
+ Servers.cultivate(self.find_all {|s| s.tags["Meal"] == meal.to_s })
60
60
  end
61
61
 
62
62
  def self.start(server_id)
@@ -68,7 +68,6 @@ module Boucher
68
68
  end
69
69
 
70
70
  def self.terminate(server)
71
- Boucher::Nagios.remove_host(server)
72
71
  volumes = server.volumes
73
72
  volumes_to_destroy = volumes.select {|v| !v.delete_on_termination}
74
73
 
@@ -85,8 +84,8 @@ module Boucher
85
84
  Servers.cultivate(self.find_all {|s| s.id == server_id}).first
86
85
  end
87
86
 
88
- def [](klass)
89
- find(:env => Boucher::Config[:env], :class => klass, :state => "running")
87
+ def [](meal)
88
+ find(:env => Boucher::Config[:env], :meal => meal, :state => "running")
90
89
  end
91
90
  end
92
91
  end
@@ -1,5 +1,5 @@
1
1
  require 'boucher/env'
2
- require 'boucher/classes'
2
+ require 'boucher/meals'
3
3
  require 'fog'
4
4
 
5
5
  module Boucher
@@ -1,11 +1,10 @@
1
1
  require 'boucher/compute'
2
2
  require 'boucher/env'
3
3
  require 'boucher/io'
4
- require 'boucher/classes'
4
+ require 'boucher/meals'
5
5
  require 'boucher/provision'
6
6
  require 'boucher/servers'
7
7
  require 'boucher/volumes'
8
- require 'boucher/nagios'
9
8
 
10
9
  desc "Starts a console with the Boucher modules loaded"
11
10
  task :console do
@@ -1,11 +1,10 @@
1
1
  require 'boucher/compute'
2
2
  require 'boucher/env'
3
3
  require 'boucher/io'
4
- require 'boucher/classes'
4
+ require 'boucher/meals'
5
5
  require 'boucher/provision'
6
6
  require 'boucher/servers'
7
7
  require 'boucher/volumes'
8
- require 'boucher/nagios'
9
8
  require 'retryable'
10
9
 
11
10
  def meals
@@ -40,9 +39,9 @@ namespace :servers do
40
39
  server_listing("in the '#{args.env}' environment", Boucher::Servers.in_env(args.env))
41
40
  end
42
41
 
43
- desc "List AWS servers in specified class"
44
- task :of_class, [:klass] do |t, args|
45
- server_listing("of class '#{args.klass}'", Boucher::Servers.of_class(args.klass))
42
+ desc "List AWS servers of the specified meal"
43
+ task :of_meal, [:meal] do |t, args|
44
+ server_listing("of meal '#{args.meal}'", Boucher::Servers.of_meal(args.meal))
46
45
  end
47
46
 
48
47
  desc "Terminates the specified server"
@@ -67,7 +66,6 @@ web console and click Instance Actions -> Change Termination Protection -> Yes."
67
66
  desc "Stops the specified server"
68
67
  task :stop, [:server_id] do |t, args|
69
68
  server = Boucher.compute.servers.get(args.server_id)
70
- Boucher::Nagios.remove_host(server)
71
69
  Boucher::Servers.stop(args.server_id)
72
70
  end
73
71
 
@@ -75,7 +73,6 @@ web console and click Instance Actions -> Change Termination Protection -> Yes."
75
73
  task :start, [:server_id] do |t, args|
76
74
  Boucher::Servers.start(args.server_id)
77
75
  server = Boucher.compute.servers.get(args.server_id)
78
- Boucher::Nagios.add_host(server)
79
76
  end
80
77
 
81
78
  desc "Open an SSH session with the specified server"
@@ -87,11 +84,9 @@ web console and click Instance Actions -> Change Termination Protection -> Yes."
87
84
 
88
85
  desc "Download a file from the server"
89
86
  task :download, [:server_id, :filepath] do |t, args|
90
- puts "Downloading #{args.filepath}"
91
-
92
- server = Boucher.compute.servers.get(args.server_id)
87
+ server = Boucher.compute.servers.get(args.server_id)
93
88
  remote_path = args.filepath
94
- local_path = File.expand_path(File.join("..", "..", File.basename(args.filepath)), __FILE__)
89
+ local_path = File.expand_path(File.join("..", "..", File.basename(args.filepath)), __FILE__)
95
90
 
96
91
  Boucher.download(server, remote_path, local_path)
97
92
  end
@@ -101,27 +96,22 @@ web console and click Instance Actions -> Change Termination Protection -> Yes."
101
96
  system "chmod 0600 *.pem"
102
97
  end
103
98
 
104
- desc "Provision new server [#{Boucher.server_classes.keys.sort.join(', ')}]"
105
- task :provision, [:klass] do |t, args|
106
- class_map = Boucher.server_classes[args.klass.to_sym]
107
- Boucher.provision(args.klass, class_map)
99
+ desc "Provision new server [#{Boucher.meals.keys.sort.join(', ')}]"
100
+ task :provision, [:meal] do |t, args|
101
+ meal = Boucher.meal(args.meal)
102
+ Boucher.provision(meal)
108
103
  end
109
104
 
110
- desc "Provision new, or chef existing server of the specified class"
111
- task :establish, [:klass] do |t, args|
112
- Boucher.assert_env!
113
- server = Boucher.find_server(args.klass, ENV['BUTCHER_ENV'])
114
- Boucher.establish_server(server, args.klass)
105
+ desc "Provision new, or chef existing server of the specified meal"
106
+ task :establish, [:meal] do |t, args|
107
+ server = Boucher.find_server(args.meal, ENV['BUTCHER_ENV'])
108
+ Boucher.establish_server(server, args.meal)
115
109
  end
116
110
 
117
- namespace :chef do
118
- meals.each do |s_class|
119
- desc "Cook #{s_class} meal"
120
- task s_class, [:server_id] do |t, args|
121
- Boucher.assert_env!
122
- server = Boucher.compute.servers.get(args.server_id)
123
- Boucher.cook_meal(server, s_class)
124
- end
125
- end
111
+ desc "Cook the given meal on the given server"
112
+ task :chef, [:meal, :server_id] do |t, args|
113
+ server = Boucher.compute.servers.get(args.server_id)
114
+ Boucher.cook_meal(server, args.meal)
126
115
  end
127
116
  end
117
+
@@ -1,6 +1,5 @@
1
1
  require_relative "../spec_helper"
2
2
  require 'boucher/compute'
3
- require 'boucher/nagios'
4
3
  require 'ostruct'
5
4
 
6
5
  describe "Boucher Cloud" do
@@ -25,22 +24,6 @@ describe "Boucher Cloud" do
25
24
  Boucher.ssh(server, "some command")
26
25
  end
27
26
 
28
- it "downloads a file" do
29
- server = stub(:dns_name => "test_dns")
30
- Kernel.should_receive(:system).with("rsync", "-azb", "-e", Boucher.ssh_command, "--delete-after", "#{Boucher::Config[:username]}@test_dns:/usr/lib/a_file.txt", "/usr/local/local_file.txt")
31
- Boucher.download(server, "/usr/lib/a_file.txt", "/usr/local/local_file.txt")
32
- end
33
-
34
- it "rsyncs files" do
35
- server = stub(:dns_name => "test_dns")
36
-
37
- system "echo > /dev/null" # to populate $?
38
- Kernel.should_receive(:system).with("rsync", "-azb", "-e", Boucher.ssh_command,
39
- "--delete-after", "foo", "#{Boucher::Config[:username]}@test_dns:bar")
40
-
41
- Boucher.rsync(server, "foo", "bar")
42
- end
43
-
44
27
  it "opens an ssh shell" do
45
28
  server = OpenStruct.new(:dns_name => "test_dns")
46
29
 
@@ -50,12 +33,9 @@ describe "Boucher Cloud" do
50
33
  Boucher.ssh(server)
51
34
  end
52
35
 
53
- it "updates recipes and rsyncs local changes" do
36
+ it "updates recipes" do
54
37
  server = OpenStruct.new(:id => "test_id")
55
38
  Boucher.should_receive(:ssh).with(server, "cd infrastructure && git checkout . && git clean -d -f && git pull && bundle")
56
- Boucher.should_receive(:rsync).with(server, "cookbooks/", "infrastructure/cookbooks/")
57
- Boucher.should_receive(:rsync).with(server, "config/", "infrastructure/config/")
58
- Boucher.should_receive(:rsync).with(server, "tasks/", "infrastructure/tasks/")
59
39
 
60
40
  Boucher.update_recipes(server)
61
41
  end
@@ -65,8 +45,6 @@ describe "Boucher Cloud" do
65
45
 
66
46
  Boucher.should_receive(:update_recipes).with(server)
67
47
  Boucher.should_receive(:ssh).with(server, "cd infrastructure && sudo BUTCHER_ENV=env_name BRANCH=branch_name chef-solo -c config/solo.rb -j config/meal_name.json")
68
- Boucher::Nagios.should_receive(:remove_host).with(server)
69
- Boucher::Nagios.should_receive(:add_host).with(server)
70
48
  Boucher::Config[:branch] = "branch_name"
71
49
  Boucher::Config[:env] = "env_name"
72
50
 
@@ -13,7 +13,7 @@ describe "Boucher IO" do
13
13
 
14
14
  output = $stdout.string
15
15
  puts "output: #{output}"
16
- %w{ID Env Class Creator State Public IP Private IP}.each do |header|
16
+ %w{ID Env Meal Creator State Public IP Private IP}.each do |header|
17
17
  output.should include(header)
18
18
  end
19
19
  end
@@ -21,7 +21,7 @@ describe "Boucher IO" do
21
21
  it "prints a server" do
22
22
  server = OpenStruct.new(
23
23
  :id => "test_id",
24
- :tags => {"Env" => "test_env", "Class" => "test_class", "Creator" => "Joe"},
24
+ :tags => {"Env" => "test_env", "Meal" => "test_class", "Creator" => "Joe"},
25
25
  :state => "test_state",
26
26
  :public_ip_address => "1.2.3.4")
27
27
 
@@ -36,22 +36,22 @@ describe "Boucher IO" do
36
36
  output.should include("1.2.3.4")
37
37
  end
38
38
 
39
- it "prints servers ordered by Env, then Class" do
39
+ it "prints servers ordered by Env, then Meal" do
40
40
  server_1 = OpenStruct.new(
41
41
  :id => "test_id_1",
42
- :tags => {"Env" => "test_env", "Class" => "test_class", "Creator" => "Joe"},
42
+ :tags => {"Env" => "test_env", "Meal" => "test_class", "Creator" => "Joe"},
43
43
  :state => "test_state",
44
44
  :public_ip_address => "1.2.3.4")
45
45
 
46
46
  server_2 = OpenStruct.new(
47
47
  :id => "test_id_2",
48
- :tags => {"Env" => "better_env", "Class" => "test_class_1", "Creator" => "Joe"},
48
+ :tags => {"Env" => "better_env", "Meal" => "test_class_1", "Creator" => "Joe"},
49
49
  :state => "test_state",
50
50
  :public_ip_address => "1.2.3.5")
51
51
 
52
52
  server_3 = OpenStruct.new(
53
53
  :id => "test_id_3",
54
- :tags => {"Env" => "better_env", "Class" => "test_class_2", "Creator" => "Joe"},
54
+ :tags => {"Env" => "better_env", "Meal" => "test_class_2", "Creator" => "Joe"},
55
55
  :state => "test_state",
56
56
  :public_ip_address => "1.2.3.6")
57
57
 
@@ -1,8 +1,8 @@
1
1
  require_relative "../spec_helper"
2
- require 'boucher/classes'
2
+ require 'boucher/meals'
3
3
  require 'ostruct'
4
4
 
5
- describe "Boucher Server Classes" do
5
+ describe "Boucher Server Meals" do
6
6
 
7
7
  before do
8
8
  Boucher.stub(:current_user).and_return("joe")
@@ -11,21 +11,24 @@ describe "Boucher Server Classes" do
11
11
 
12
12
  it "pull classification from json" do
13
13
  json = "{\"boucher\": {\"foo\": 1,\n \"bar\": 2}}"
14
- Boucher.json_to_class(json).should == {:foo => 1, :bar => 2}
14
+ Boucher.json_to_meal(json).should == {:foo => 1, :bar => 2}
15
+
16
+ json = "{}"
17
+ Boucher.json_to_meal(json).should == {}
15
18
  end
16
19
 
17
20
  it "can classify base server" do
18
- some_class = {:class_name => "base",
21
+ some_class = {:meal_name => "base",
19
22
  :meals => ["base"]}
20
23
  Boucher::Config[:default_instance_flavor_id] = 'm1.small'
21
24
  Boucher::Config[:default_instance_groups] = ["SSH"]
22
- Boucher.classify(@server, some_class)
25
+ Boucher.setup_meal(@server, some_class)
23
26
 
24
27
  @server.image_id.should == Boucher::Config[:base_image_id]
25
28
  @server.flavor_id.should == 'm1.small'
26
29
  @server.groups.should == ["SSH"]
27
30
  @server.key_name.should == "test_key"
28
- @server.tags["Class"].should == "base"
31
+ @server.tags["Meal"].should == "base"
29
32
  @server.tags["Name"].should_not == nil
30
33
  @server.tags["Creator"].should == "joe"
31
34
  @server.tags["Env"].should == Boucher::Config[:env]
@@ -13,65 +13,59 @@ describe "Boucher Provisioning" do
13
13
 
14
14
  describe "establish server" do
15
15
  it "provisions a server if one does not exist" do
16
- Boucher.stub(:server_classes).and_return({:class_of_server => "server details"})
17
- Boucher.should_receive(:provision).with("class_of_server", "server details")
16
+ Boucher.should_receive(:meal).with("some_meal").and_return(:meal)
17
+ Boucher.should_receive(:provision).with(:meal)
18
18
 
19
- Boucher.establish_server nil, "class_of_server"
19
+ Boucher.establish_server nil, "some_meal"
20
20
  end
21
21
 
22
22
  it "starts a server if it is stopped" do
23
23
  server = mock(:id => "the id", :state => "stopped")
24
- Boucher.stub(:server_classes).and_return({:class_of_server => "server details"})
24
+ meal = {:name => "some_meal"}
25
+ Boucher.stub(:meal).and_return(meal)
25
26
  Boucher.should_receive(:change_server_state).with("the id", :start, "running")
26
27
  server.should_receive(:reload)
27
- Boucher.should_receive(:cook_meals_on_server).with("class_of_server", "server details", server)
28
+ Boucher.should_receive(:cook_meal_on_server).with(meal, server)
28
29
 
29
- Boucher.establish_server server, "class_of_server"
30
+ Boucher.establish_server server, "some_meal"
30
31
  end
31
32
 
32
33
  it "attaches elastic IPs if the server was stopped" do
33
- Boucher::Config[:elastic_ips] = { "class_of_server" => "1.2.3.4" }
34
34
  server = mock(:id => "the id", :state => "stopped", :reload => nil)
35
- Boucher.stub(:server_classes).and_return({:class_of_server => {:meals => []}})
35
+ Boucher.stub(:meal).and_return({:name => "some_meal", :elastic_ips => %w(1.2.3.4)})
36
36
  Boucher.stub(:change_server_state)
37
+ Boucher.stub(:cook_meal)
37
38
  Boucher.compute.should_receive(:associate_address).with(anything, "1.2.3.4")
38
39
 
39
- Boucher.establish_server server, "class_of_server"
40
+ Boucher.establish_server server, "meal_name"
40
41
  end
41
42
 
42
43
  it "cooks meals on server if it is up and running" do
43
44
  running_server = mock(:id => "the id", :state => "running")
44
- Boucher.stub(:server_classes).and_return({:class_of_server => "server details"})
45
- Boucher.should_receive(:cook_meals_on_server).with("class_of_server", "server details", running_server)
45
+ meal = {:name => "some_meal"}
46
+ Boucher.stub(:meal).and_return(meal)
47
+ Boucher.should_receive(:cook_meal_on_server).with(meal, running_server)
46
48
 
47
- Boucher.establish_server running_server, "class_of_server"
49
+ Boucher.establish_server running_server, "some_meal"
48
50
  end
49
51
  end
50
52
 
51
53
  describe "provision" do
52
54
  it "provisions a server" do
53
55
  Boucher.stub!(:ssh)
54
- Boucher.should_receive(:classify)
55
- Boucher.should_receive(:cook_meal).with(anything, "foo")
56
+ Boucher.should_receive(:setup_meal)
57
+ Boucher.should_receive(:cook_meal).with(anything, "some_meal")
56
58
 
57
- Boucher.provision "foo", {:meals => ["foo"]}
58
- end
59
-
60
- it "provisions a server with Procs as meals" do
61
- Boucher.stub!(:ssh)
62
- Boucher.should_receive(:classify)
63
- Boucher.should_receive(:cook_meal).with(anything, "foo")
64
-
65
- Boucher.provision "foo", {:meals => [lambda{"foo"}]}
59
+ Boucher.provision :name => "some_meal"
66
60
  end
67
61
 
68
62
  it "provisions a server with elastic IP" do
69
63
  Boucher.stub!(:ssh)
70
- Boucher::Config[:elastic_ips] = { "foo" => "1.2.3.4" }
71
- Boucher.should_receive(:classify)
64
+ Boucher.should_receive(:setup_meal)
65
+ Boucher.stub(:cook_meal)
72
66
  Boucher.compute.should_receive(:associate_address).with(anything, "1.2.3.4")
73
67
 
74
- Boucher.provision "foo", {:meals => []}
68
+ Boucher.provision :name => "some_meal", :elastic_ips => %w(1.2.3.4)
75
69
  end
76
70
  end
77
71
  end
@@ -5,10 +5,10 @@ require 'ostruct'
5
5
  describe "Boucher::Servers" do
6
6
 
7
7
  let(:remote_servers) {
8
- [OpenStruct.new(:id => "s1", :tags => {"Env" => "test", "Class" => "foo"}, :state => "stopped"),
9
- OpenStruct.new(:id => "s2", :tags => {"Env" => "test", "Class" => "bar"}, :state => "pending"),
10
- OpenStruct.new(:id => "s3", :tags => {"Env" => "dev", "Class" => "foo"}, :state => "terminated"),
11
- OpenStruct.new(:id => "s4", :tags => {"Env" => "dev", "Class" => "bar"}, :state => "running")]
8
+ [OpenStruct.new(:id => "s1", :tags => {"Env" => "test", "Meal" => "foo"}, :state => "stopped"),
9
+ OpenStruct.new(:id => "s2", :tags => {"Env" => "test", "Meal" => "bar"}, :state => "pending"),
10
+ OpenStruct.new(:id => "s3", :tags => {"Env" => "dev", "Meal" => "foo"}, :state => "terminated"),
11
+ OpenStruct.new(:id => "s4", :tags => {"Env" => "dev", "Meal" => "bar"}, :state => "running")]
12
12
  }
13
13
 
14
14
  before do
@@ -25,10 +25,10 @@ describe "Boucher::Servers" do
25
25
  Boucher::Servers.all.should == remote_servers
26
26
  end
27
27
 
28
- it "finds classed servers" do
29
- Boucher::Servers.of_class("blah").should == []
30
- Boucher::Servers.of_class(:foo).map(&:id).should == ["s1", "s3"]
31
- Boucher::Servers.of_class("bar").map(&:id).should == ["s2", "s4"]
28
+ it "finds mealed servers" do
29
+ Boucher::Servers.of_meal("blah").should == []
30
+ Boucher::Servers.of_meal(:foo).map(&:id).should == ["s1", "s3"]
31
+ Boucher::Servers.of_meal("bar").map(&:id).should == ["s2", "s4"]
32
32
  end
33
33
 
34
34
  it "finds with env servers" do
@@ -46,20 +46,20 @@ describe "Boucher::Servers" do
46
46
 
47
47
  it "finds the first matching server" do
48
48
  Boucher::Servers.find.id.should == "s1"
49
- Boucher::Servers.find(:class => "foo").id.should == "s1"
50
- Boucher::Servers.find(:class => "bar").id.should == "s2"
49
+ Boucher::Servers.find(:meal => "foo").id.should == "s1"
50
+ Boucher::Servers.find(:meal => "bar").id.should == "s2"
51
51
  Boucher::Servers.find(:env => "test").id.should == "s1"
52
52
  Boucher::Servers.find(:env => "dev").id.should == "s3"
53
- Boucher::Servers.find(:class => "foo", :env => "test").id.should == "s1"
54
- Boucher::Servers.find(:class => "foo", :env => "dev").id.should == "s3"
55
- Boucher::Servers.find(:class => "bar", :env => "test").id.should == "s2"
56
- Boucher::Servers.find(:class => "bar", :env => "dev").id.should == "s4"
57
- expect { Boucher::Servers.find(:class => "blah", :env => "dev") }.to raise_error
58
- expect { Boucher::Servers.find(:class => "foo", :env => "blah") }.to raise_error
53
+ Boucher::Servers.find(:meal => "foo", :env => "test").id.should == "s1"
54
+ Boucher::Servers.find(:meal => "foo", :env => "dev").id.should == "s3"
55
+ Boucher::Servers.find(:meal => "bar", :env => "test").id.should == "s2"
56
+ Boucher::Servers.find(:meal => "bar", :env => "dev").id.should == "s4"
57
+ expect { Boucher::Servers.find(:meal => "blah", :env => "dev") }.to raise_error
58
+ expect { Boucher::Servers.find(:meal => "foo", :env => "blah") }.to raise_error
59
59
  end
60
60
 
61
61
  it "raises an error if find returns no results" do
62
- expect { Boucher::Servers.find(:class => "blah") }.to raise_error(Boucher::Servers::NotFound)
62
+ expect { Boucher::Servers.find(:meal => "blah") }.to raise_error(Boucher::Servers::NotFound)
63
63
  end
64
64
 
65
65
  it "gets a server based on current env when all the servers are running" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: boucher
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.1'
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire: boucher/tasks
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-25 00:00:00.000000000 Z
12
+ date: 2012-09-26 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rake
@@ -91,28 +91,24 @@ files:
91
91
  - README.md
92
92
  - Rakefile
93
93
  - boucher.gemspec
94
- - lib/boucher/ci.rb
95
- - lib/boucher/classes.rb
96
94
  - lib/boucher/compute.rb
97
95
  - lib/boucher/env.rb
98
96
  - lib/boucher/io.rb
99
- - lib/boucher/nagios.rb
97
+ - lib/boucher/meals.rb
100
98
  - lib/boucher/provision.rb
101
99
  - lib/boucher/servers.rb
102
100
  - lib/boucher/storage.rb
103
101
  - lib/boucher/tasks.rb
104
102
  - lib/boucher/tasks/console.rake
105
- - lib/boucher/tasks/env.rake
106
- - lib/boucher/tasks/nagios.rake
107
103
  - lib/boucher/tasks/servers.rake
108
104
  - lib/boucher/tasks/storage.rake
109
105
  - lib/boucher/tasks/volumes.rake
110
106
  - lib/boucher/util.rb
111
107
  - lib/boucher/volumes.rb
112
- - spec/boucher/classes_spec.rb
113
108
  - spec/boucher/compute_spec.rb
114
109
  - spec/boucher/env_spec.rb
115
110
  - spec/boucher/io_spec.rb
111
+ - spec/boucher/meals_spec.rb
116
112
  - spec/boucher/provision_spec.rb
117
113
  - spec/boucher/servers_spec.rb
118
114
  - spec/boucher/storage_spec.rb
@@ -129,6 +125,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
129
125
  - - ! '>='
130
126
  - !ruby/object:Gem::Version
131
127
  version: '0'
128
+ segments:
129
+ - 0
130
+ hash: -3836006143599038364
132
131
  required_rubygems_version: !ruby/object:Gem::Requirement
133
132
  none: false
134
133
  requirements:
@@ -142,10 +141,10 @@ signing_key:
142
141
  specification_version: 3
143
142
  summary: AWS system deployment and management
144
143
  test_files:
145
- - spec/boucher/classes_spec.rb
146
144
  - spec/boucher/compute_spec.rb
147
145
  - spec/boucher/env_spec.rb
148
146
  - spec/boucher/io_spec.rb
147
+ - spec/boucher/meals_spec.rb
149
148
  - spec/boucher/provision_spec.rb
150
149
  - spec/boucher/servers_spec.rb
151
150
  - spec/boucher/storage_spec.rb
data/lib/boucher/ci.rb DELETED
@@ -1,18 +0,0 @@
1
- module Boucher
2
- module CI
3
- def self.each_server(&block)
4
- threads = []
5
- Boucher.each_required_server do |server, server_class|
6
- thread = Thread.new { block.yield(server, server_class) }
7
- threads << thread
8
- end
9
- threads.each { |t| t.join }
10
- end
11
-
12
- def self.terminate_server(server_class)
13
- server = Boucher.get_server(server_class, "ci", "running")
14
- Boucher.change_server_state server.id, :destroy, "terminated" if server
15
- end
16
- end
17
- end
18
-
@@ -1,39 +0,0 @@
1
- require 'boucher/env'
2
- require 'boucher/util'
3
- require 'json'
4
-
5
- module Boucher
6
-
7
- def self.json_to_class(json)
8
- parser = JSON.parser.new(json, :symbolize_names => true)
9
- config = parser.parse
10
- config[:boucher]
11
- end
12
-
13
- def self.server_classes
14
- if @server_classes.nil?
15
- @server_classes = {}
16
- Dir.glob(File.join("config", "*.json")).each do |file|
17
- spec = json_to_class(IO.read(file))
18
- class_name = File.basename(file).to_sym
19
- @server_classes[class_name] = spec
20
- end
21
- end
22
- @server_classes
23
- end
24
-
25
- def self.classify(server, class_spec)
26
- server.image_id = class_spec[:base_image_id] || Boucher::Config[:base_image_id]
27
- server.flavor_id = class_spec[:flavor_id] || Boucher::Config[:default_instance_flavor_id]
28
- server.groups = class_spec[:groups] || Boucher::Config[:default_instance_groups]
29
- server.key_name = class_spec[:key_name] || Boucher::Config[:aws_key_filename]
30
- server.tags = {}
31
- server.tags["Name"] = "#{class_spec[:class_name] || "base"} #{Time.new.strftime("%Y%m%d%H%M%S")}"
32
- server.tags["Class"] = class_spec[:class_name] || "base"
33
- server.tags["CreatedAt"] = Time.new.strftime("%Y%m%d%H%M%S")
34
- server.tags["Creator"] = current_user
35
- server.tags["Env"] = Boucher::Config[:env]
36
- server.tags["Volumes"] = class_spec[:volumes]
37
- end
38
-
39
- end
@@ -1,50 +0,0 @@
1
- require 'boucher/servers'
2
- require 'boucher/compute'
3
-
4
- module Boucher
5
- module Nagios
6
- def self.remove_host(host)
7
- return if host.tags["Class"] == "nagios_server"
8
-
9
- monitors_for(host.tags["Env"]).each do |monitor|
10
- commands = [
11
- "cd /home/#{Boucher::Config[:username]}/infrastructure",
12
- "sudo rake nagios:remove_host[#{host.id}]",
13
- "sudo /etc/init.d/nagios3 restart"
14
- ]
15
- Boucher.ssh(monitor, commands.join(" && "))
16
- end
17
- end
18
-
19
- def self.add_host(host)
20
- return if checks_for(host.tags["Class"]).empty?
21
-
22
- monitors_for(host.tags["Env"]).each do |monitor|
23
- commands = [
24
- "cd /home/#{Boucher::Config[:username]}/infrastructure",
25
- "sudo rake nagios:add_host[#{host.id},#{host.public_ip_address},#{host.tags["Class"]}]",
26
- "sudo /etc/init.d/nagios3 restart"
27
- ]
28
-
29
- Boucher.ssh(monitor, commands.join(" && "))
30
- end
31
- end
32
-
33
- def self.checks_for(server_class)
34
- config_path = File.expand_path("../../../config/#{server_class}.json", __FILE__)
35
- return [] unless File.exists? config_path
36
- server_configuration = JSON.parse(File.read config_path)
37
- server_configuration["nagios"] || []
38
- end
39
-
40
- private
41
-
42
- def self.monitors_for(env)
43
- Boucher::Servers.all.select do |monitor|
44
- monitor.tags["Class"] == "nagios_server" &&
45
- monitor.tags["Env"] == env &&
46
- monitor.dns_name
47
- end
48
- end
49
- end
50
- end
@@ -1,42 +0,0 @@
1
- require 'boucher/env'
2
- require 'boucher/provision'
3
- require 'retryable'
4
-
5
- namespace :env do
6
-
7
- desc "Attaches elastic IPs"
8
- task :elastic_ips, [:env] do |t, args|
9
- Boucher.force_env!(args.env)
10
- Boucher.each_required_server do |server, server_class|
11
- Boucher.attach_elastic_ips(server_class, server)
12
- end
13
- end
14
-
15
- desc "Starts and deploys all the servers for the specified environment"
16
- task :start, [:env] do |t, args|
17
- Boucher.force_env!(args.env)
18
- Boucher.assert_env!
19
- Boucher.establish_all_servers
20
- end
21
-
22
- desc "Stops all the servers for the specified environment"
23
- task :stop, [:env] do |t, args|
24
- Boucher.force_env!(args.env)
25
-
26
- Boucher.each_required_server do |server, server_class|
27
- if server
28
- Boucher::Servers.stop(server.id)
29
- else
30
- puts "No #{server_class} server found for #{Boucher::Config[:env]} environment."
31
- end
32
- end
33
- end
34
-
35
- desc "Terminates all servers for the specified environment"
36
- task :terminate, [:env] do |t, args|
37
- Boucher.force_env!(args.env)
38
- Boucher.each_required_server do |server, server_class|
39
- Boucher::Servers.terminate(server) if server
40
- end
41
- end
42
- end
@@ -1,74 +0,0 @@
1
- require 'json'
2
- require 'fileutils'
3
-
4
- def make_config_path(host_name)
5
- "/etc/nagios3/conf.d/#{host_name}.cfg"
6
- end
7
-
8
- def host_entry(host_name, ip)
9
- <<-HOST
10
- define host {
11
- host_name #{host_name}
12
- alias #{host_name}
13
- address #{ip}
14
- check_command check_ssh
15
- notification_interval 0
16
- notification_period 24x7
17
- max_check_attempts 10
18
- notification_options d,u,r
19
- notifications_enabled 1
20
- }
21
- HOST
22
- end
23
-
24
- def service_entry(host_name, ip, plugin)
25
- <<-SERVICE
26
- define command {
27
- command_name #{host_name}-#{plugin}
28
- command_line /usr/lib/nagios/plugins/check_nrpe -H #{ip} -c "#{plugin}"
29
- }
30
-
31
- define service {
32
- use generic-service
33
- host_name #{host_name}
34
- check_command #{host_name}-#{plugin}
35
- service_description #{plugin}
36
- normal_check_interval 5
37
- check_period 24x7
38
- notifications_enabled 1
39
- }
40
- SERVICE
41
- end
42
-
43
- namespace :nagios do
44
- desc "Remove a nagios host from this machine"
45
- task :remove_host, [:name] do |t, args|
46
- FileUtils.rm_f make_config_path(args.name)
47
- end
48
-
49
- desc "Adds a nagios host to be monitored by this machine"
50
- task :add_host, [:name, :ip, :klass] do |t, args|
51
- checks = Boucher::Nagios.checks_for(args.klass)
52
- return if checks.empty?
53
-
54
- File.open(make_config_path(args.name), "w") do |file|
55
- host_name = "#{args.klass}-#{args.name}"
56
-
57
- file.puts host_entry(host_name, args.ip)
58
-
59
- checks.each do |plugin, command|
60
- file.puts
61
- file.puts service_entry(host_name, args.ip, plugin)
62
- end
63
- end
64
- end
65
-
66
- desc "Opens the nagios web console"
67
- task :open do
68
- server = Boucher::Servers["nagios_server"]
69
- url = "http://#{server.public_ip_address}/nagios3"
70
- puts "Nagios lives at #{url}"
71
- puts "Login using nagiosadmin / 'we are many lonely souls'"
72
- `open #{url}`
73
- end
74
- end