boucher 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,75 @@
1
+ require_relative "../spec_helper"
2
+ require 'boucher/compute'
3
+ require 'boucher/nagios'
4
+ require 'ostruct'
5
+
6
+ describe "Boucher Cloud" do
7
+
8
+ after do
9
+ Boucher::Config[:env] = "dev"
10
+ end
11
+
12
+ it "knows connections options" do
13
+ connection = Boucher.compute
14
+ connection.should_not == nil
15
+ Boucher::EC2_CONFIG[:aws_secret_access_key].should == "secret key"
16
+ Boucher::EC2_CONFIG[:aws_access_key_id].should == "public id"
17
+ end
18
+
19
+ it "ssh's a command" do
20
+ server = OpenStruct.new(:dns_name => "test_dns")
21
+
22
+ system "echo > /dev/null" # to populate $?
23
+ Kernel.should_receive(:system).with("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{Boucher::Config[:aws_key_filename]}.pem #{Boucher::Config[:username]}@test_dns \"some command\"")
24
+
25
+ Boucher.ssh(server, "some command")
26
+ end
27
+
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
+ it "opens an ssh shell" do
45
+ server = OpenStruct.new(:dns_name => "test_dns")
46
+
47
+ system "echo > /dev/null" # to populate $?
48
+ Kernel.should_receive(:system).with("ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i #{Boucher::Config[:aws_key_filename]}.pem #{Boucher::Config[:username]}@test_dns ")
49
+
50
+ Boucher.ssh(server)
51
+ end
52
+
53
+ it "updates recipes and rsyncs local changes" do
54
+ server = OpenStruct.new(:id => "test_id")
55
+ 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
+
60
+ Boucher.update_recipes(server)
61
+ end
62
+
63
+ it "cooks a meal" do
64
+ server = OpenStruct.new(:id => "test_id")
65
+
66
+ Boucher.should_receive(:update_recipes).with(server)
67
+ 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
+ Boucher::Config[:branch] = "branch_name"
71
+ Boucher::Config[:env] = "env_name"
72
+
73
+ Boucher.cook_meal(server, "meal_name")
74
+ end
75
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Environment" do
4
+ describe "env" do
5
+ before do
6
+ @old_env = ENV['BUTCHER_ENV']
7
+ end
8
+
9
+ after do
10
+ ENV['BUTCHER_ENV'] = @old_env
11
+ end
12
+
13
+ it "defaults to :dev" do
14
+ ENV.delete('BUTCHER_ENV')
15
+ #load File.expand_path(File.dirname(__FILE__) + "/../../lib/boucher/env.rb")
16
+ Boucher::env_name.should == :dev
17
+ end
18
+
19
+ it "can be set via an environment variable" do
20
+ ENV['BUTCHER_ENV'] = "ci"
21
+ load File.expand_path(File.dirname(__FILE__) + "/../../lib/boucher/env.rb")
22
+ Boucher::env_name.should == "ci"
23
+ end
24
+ end
25
+
26
+ describe "branch" do
27
+ before do
28
+ @old_branch = ENV['BRANCH']
29
+ ENV.delete('BRANCH')
30
+ end
31
+
32
+ after do
33
+ ENV['BRANCH'] = @old_branch
34
+ end
35
+
36
+ it "defaults to master" do
37
+ Boucher.force_env!(:dev)
38
+ Boucher::Config[:branch].should == "master"
39
+ end
40
+
41
+ it "can be set via an environment variable" do
42
+ ENV['BRANCH'] = "some_branch"
43
+ Boucher.force_env!(:dev)
44
+ Boucher::Config[:branch].should == "some_branch"
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,68 @@
1
+ require_relative "../spec_helper"
2
+ require "boucher/io"
3
+ require 'ostruct'
4
+
5
+ describe "Boucher IO" do
6
+
7
+ before do
8
+ Boucher::IO.mock!
9
+ end
10
+
11
+ it "prints server table header" do
12
+ Boucher.print_server_table_header
13
+
14
+ output = $stdout.string
15
+ puts "output: #{output}"
16
+ %w{ID Env Class Creator State Public IP Private IP}.each do |header|
17
+ output.should include(header)
18
+ end
19
+ end
20
+
21
+ it "prints a server" do
22
+ server = OpenStruct.new(
23
+ :id => "test_id",
24
+ :tags => {"Env" => "test_env", "Class" => "test_class", "Creator" => "Joe"},
25
+ :state => "test_state",
26
+ :public_ip_address => "1.2.3.4")
27
+
28
+ Boucher.print_server server
29
+ output = $stdout.string
30
+
31
+ output.should include("test_id")
32
+ output.should include("test_")
33
+ output.should include("test_class")
34
+ output.should include("Joe")
35
+ output.should include("test_state")
36
+ output.should include("1.2.3.4")
37
+ end
38
+
39
+ it "prints servers ordered by Env, then Class" do
40
+ server_1 = OpenStruct.new(
41
+ :id => "test_id_1",
42
+ :tags => {"Env" => "test_env", "Class" => "test_class", "Creator" => "Joe"},
43
+ :state => "test_state",
44
+ :public_ip_address => "1.2.3.4")
45
+
46
+ server_2 = OpenStruct.new(
47
+ :id => "test_id_2",
48
+ :tags => {"Env" => "better_env", "Class" => "test_class_1", "Creator" => "Joe"},
49
+ :state => "test_state",
50
+ :public_ip_address => "1.2.3.5")
51
+
52
+ server_3 = OpenStruct.new(
53
+ :id => "test_id_3",
54
+ :tags => {"Env" => "better_env", "Class" => "test_class_2", "Creator" => "Joe"},
55
+ :state => "test_state",
56
+ :public_ip_address => "1.2.3.6")
57
+
58
+ Boucher.print_servers([server_1, server_2, server_3])
59
+ output = $stdout.string
60
+
61
+ lines = output.split("\n")
62
+ lines[0].should == ""
63
+ lines[2].should include("------")
64
+ lines[3].should include("test_id_2")
65
+ lines[4].should include("test_id_3")
66
+ lines[5].should include("test_id_1")
67
+ end
68
+ end
@@ -0,0 +1,77 @@
1
+ require_relative "../spec_helper"
2
+ require 'boucher/provision'
3
+
4
+ describe "Boucher Provisioning" do
5
+
6
+ before do
7
+ Boucher::Config[:elastic_ips] = nil
8
+ end
9
+
10
+ after do
11
+ Boucher::Config[:env] = "test"
12
+ end
13
+
14
+ describe "establish server" do
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")
18
+
19
+ Boucher.establish_server nil, "class_of_server"
20
+ end
21
+
22
+ it "starts a server if it is stopped" do
23
+ server = mock(:id => "the id", :state => "stopped")
24
+ Boucher.stub(:server_classes).and_return({:class_of_server => "server details"})
25
+ Boucher.should_receive(:change_server_state).with("the id", :start, "running")
26
+ server.should_receive(:reload)
27
+ Boucher.should_receive(:cook_meals_on_server).with("class_of_server", "server details", server)
28
+
29
+ Boucher.establish_server server, "class_of_server"
30
+ end
31
+
32
+ it "attaches elastic IPs if the server was stopped" do
33
+ Boucher::Config[:elastic_ips] = { "class_of_server" => "1.2.3.4" }
34
+ server = mock(:id => "the id", :state => "stopped", :reload => nil)
35
+ Boucher.stub(:server_classes).and_return({:class_of_server => {:meals => []}})
36
+ Boucher.stub(:change_server_state)
37
+ Boucher.compute.should_receive(:associate_address).with(anything, "1.2.3.4")
38
+
39
+ Boucher.establish_server server, "class_of_server"
40
+ end
41
+
42
+ it "cooks meals on server if it is up and running" do
43
+ 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)
46
+
47
+ Boucher.establish_server running_server, "class_of_server"
48
+ end
49
+ end
50
+
51
+ describe "provision" do
52
+ it "provisions a server" do
53
+ Boucher.stub!(:ssh)
54
+ Boucher.should_receive(:classify)
55
+ Boucher.should_receive(:cook_meal).with(anything, "foo")
56
+
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"}]}
66
+ end
67
+
68
+ it "provisions a server with elastic IP" do
69
+ Boucher.stub!(:ssh)
70
+ Boucher::Config[:elastic_ips] = { "foo" => "1.2.3.4" }
71
+ Boucher.should_receive(:classify)
72
+ Boucher.compute.should_receive(:associate_address).with(anything, "1.2.3.4")
73
+
74
+ Boucher.provision "foo", {:meals => []}
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,79 @@
1
+ require_relative "../spec_helper"
2
+ require 'boucher/servers'
3
+ require 'ostruct'
4
+
5
+ describe "Boucher::Servers" do
6
+
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")]
12
+ }
13
+
14
+ before do
15
+ @env = Boucher::Config[:env]
16
+ Boucher.compute.stub(:servers).and_return(remote_servers)
17
+ end
18
+
19
+ after do
20
+ Boucher::Config[:env] = @env
21
+ end
22
+
23
+ it "finds all servers" do
24
+ Boucher::Servers.all.size.should == 4
25
+ Boucher::Servers.all.should == remote_servers
26
+ end
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"]
32
+ end
33
+
34
+ it "finds with env servers" do
35
+ Boucher::Servers.in_env("blah").should == []
36
+ Boucher::Servers.in_env("test").map(&:id).should == ["s1", "s2"]
37
+ Boucher::Servers.in_env("dev").map(&:id).should == ["s3", "s4"]
38
+ end
39
+
40
+ it "finds servers in a given state" do
41
+ Boucher::Servers.in_state("running").map(&:id).should == ["s4"]
42
+ Boucher::Servers.in_state("terminated").map(&:id).should == ["s3"]
43
+ Boucher::Servers.in_state("pending").map(&:id).should == ["s2"]
44
+ Boucher::Servers.in_state("stopped").map(&:id).should == ["s1"]
45
+ end
46
+
47
+ it "finds the first matching server" do
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"
51
+ Boucher::Servers.find(:env => "test").id.should == "s1"
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
59
+ end
60
+
61
+ it "raises an error if find returns no results" do
62
+ expect { Boucher::Servers.find(:class => "blah") }.to raise_error(Boucher::Servers::NotFound)
63
+ end
64
+
65
+ it "gets a server based on current env when all the servers are running" do
66
+ Boucher::Config[:env] = "test"
67
+ expect { Boucher::Servers["foo"] }.to raise_error
68
+ expect { Boucher::Servers["bar"] }.to raise_error
69
+
70
+ Boucher::Config[:env] = "dev"
71
+ expect { Boucher::Servers["foo"] }.to raise_error
72
+ Boucher::Servers["bar"].id.should == "s4"
73
+ end
74
+
75
+ it "stops a server" do
76
+ Boucher.should_receive(:change_server_state).with("the id", :stop, "stopped")
77
+ Boucher::Servers.stop("the id")
78
+ end
79
+ end
@@ -0,0 +1,47 @@
1
+ require_relative "../spec_helper"
2
+ require 'boucher/storage'
3
+ require 'ostruct'
4
+
5
+ describe "Boucher Storage" do
6
+
7
+ it "knows connections options" do
8
+ connection = Boucher.storage
9
+ connection.should_not == nil
10
+ Boucher::S3_CONFIG[:aws_secret_access_key].should == Boucher::Config[:aws_secret_access_key]
11
+ Boucher::S3_CONFIG[:aws_access_key_id].should == Boucher::Config[:aws_access_key_id]
12
+ end
13
+
14
+ context "with dir" do
15
+ before do
16
+ Boucher.storage.directories.create(:key => "test_bucket")
17
+ File.open("test_file.txt", "w") { |f| f.write "wakka wakka" }
18
+ end
19
+
20
+ after do
21
+ File.delete("test_file.txt") if File.exists?("test_file.txt")
22
+ File.delete("test_file2.txt") if File.exists?("test_file2.txt")
23
+ end
24
+
25
+ it "puts a file" do
26
+ Boucher::Storage.put("test_bucket", "test_file", "test_file.txt")
27
+ dir = Boucher.storage.directories.get("test_bucket")
28
+ file = dir.files.get("test_file")
29
+ file.body.should == "wakka wakka"
30
+ end
31
+
32
+ it "gets a file" do
33
+ Boucher::Storage.put("test_bucket", "test_file", "test_file.txt")
34
+ time = Time.now
35
+ Time.stub!(:now).and_return(time)
36
+ Kernel.should_receive(:system) do |*args|
37
+ args[0].should == "curl"
38
+ args[1].should == Boucher.storage.directories.get("test_bucket").files.get_https_url("test_file", time + 3600)
39
+ args[2].should == "-o"
40
+ args[3].should == "test_file2.txt"
41
+ end
42
+
43
+ Boucher::Storage.get("test_bucket", "test_file", "test_file2.txt")
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,36 @@
1
+ # MDM - Need to set config because it's never loaded in test
2
+ module Boucher
3
+ NO_LOAD_CONFIG = true
4
+ Config = {
5
+ :aws_secret_access_key => "secret key",
6
+ :aws_access_key_id => "public id",
7
+ :aws_key_filename => "test_key"
8
+ }
9
+ end
10
+
11
+ require 'fog'
12
+ require 'boucher/io'
13
+ require 'boucher/env'
14
+
15
+ Boucher.force_env!("dev")
16
+
17
+ Fog.mock!
18
+ Boucher::IO.mock!
19
+
20
+
21
+ # MDM - Monkey patch wait_for methods so the tests are FASTER!
22
+ module Fog
23
+ def self.wait_for(timeout=Fog.timeout, interval=1)
24
+ yield
25
+ end
26
+ end
27
+
28
+ require 'fog/core/model'
29
+
30
+ module Fog
31
+ class Model
32
+ def wait_for(timeout=Fog.timeout, interval=1, &block)
33
+ yield
34
+ end
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,152 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boucher
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ! '''Micah Micah'''
9
+ autorequire: boucher/tasks
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.2.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.2.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: fog
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.6.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.6.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: retryable
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.3.1
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.3.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: pry
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.10
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.10
78
+ description: AWS system deployment and management
79
+ email:
80
+ - ! '''micah@8thlight.com'''
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - .gitmodules
87
+ - .rvmrc
88
+ - Gemfile
89
+ - Gemfile.lock
90
+ - LICENSE
91
+ - README.md
92
+ - Rakefile
93
+ - boucher.gemspec
94
+ - lib/boucher/ci.rb
95
+ - lib/boucher/classes.rb
96
+ - lib/boucher/compute.rb
97
+ - lib/boucher/env.rb
98
+ - lib/boucher/io.rb
99
+ - lib/boucher/nagios.rb
100
+ - lib/boucher/provision.rb
101
+ - lib/boucher/servers.rb
102
+ - lib/boucher/storage.rb
103
+ - lib/boucher/tasks.rb
104
+ - lib/boucher/tasks/console.rake
105
+ - lib/boucher/tasks/env.rake
106
+ - lib/boucher/tasks/nagios.rake
107
+ - lib/boucher/tasks/servers.rake
108
+ - lib/boucher/tasks/storage.rake
109
+ - lib/boucher/tasks/volumes.rake
110
+ - lib/boucher/util.rb
111
+ - lib/boucher/volumes.rb
112
+ - spec/boucher/classes_spec.rb
113
+ - spec/boucher/compute_spec.rb
114
+ - spec/boucher/env_spec.rb
115
+ - spec/boucher/io_spec.rb
116
+ - spec/boucher/provision_spec.rb
117
+ - spec/boucher/servers_spec.rb
118
+ - spec/boucher/storage_spec.rb
119
+ - spec/spec_helper.rb
120
+ homepage: http://github.com/8thlight/boucher
121
+ licenses: []
122
+ post_install_message:
123
+ rdoc_options: []
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ none: false
128
+ requirements:
129
+ - - ! '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ! '>='
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 1.8.24
141
+ signing_key:
142
+ specification_version: 3
143
+ summary: AWS system deployment and management
144
+ test_files:
145
+ - spec/boucher/classes_spec.rb
146
+ - spec/boucher/compute_spec.rb
147
+ - spec/boucher/env_spec.rb
148
+ - spec/boucher/io_spec.rb
149
+ - spec/boucher/provision_spec.rb
150
+ - spec/boucher/servers_spec.rb
151
+ - spec/boucher/storage_spec.rb
152
+ - spec/spec_helper.rb