poolparty 0.0.4

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.
Files changed (57) hide show
  1. data/CHANGELOG +4 -0
  2. data/Manifest +55 -0
  3. data/README.txt +113 -0
  4. data/Rakefile +18 -0
  5. data/bin/instance +54 -0
  6. data/bin/pool +33 -0
  7. data/config/config.yml +23 -0
  8. data/config/create_proxy_ami.sh +582 -0
  9. data/config/haproxy.conf +29 -0
  10. data/config/heartbeat.conf +9 -0
  11. data/config/heartbeat_authkeys.conf +2 -0
  12. data/config/monit/haproxy.monit.conf +7 -0
  13. data/config/monit/nginx.monit.conf +0 -0
  14. data/config/monit.conf +8 -0
  15. data/config/nginx.conf +24 -0
  16. data/lib/core/array.rb +10 -0
  17. data/lib/core/exception.rb +9 -0
  18. data/lib/core/kernel.rb +9 -0
  19. data/lib/core/module.rb +22 -0
  20. data/lib/core/object.rb +14 -0
  21. data/lib/core/string.rb +49 -0
  22. data/lib/core/time.rb +41 -0
  23. data/lib/modules/callback.rb +55 -0
  24. data/lib/modules/ec2_wrapper.rb +74 -0
  25. data/lib/modules/safe_instance.rb +31 -0
  26. data/lib/pool_party/application.rb +133 -0
  27. data/lib/pool_party/init.rb +4 -0
  28. data/lib/pool_party/master.rb +189 -0
  29. data/lib/pool_party/monitors/cpu.rb +18 -0
  30. data/lib/pool_party/monitors/memory.rb +21 -0
  31. data/lib/pool_party/monitors/web.rb +18 -0
  32. data/lib/pool_party/monitors.rb +13 -0
  33. data/lib/pool_party/optioner.rb +16 -0
  34. data/lib/pool_party/os/ubuntu.rb +78 -0
  35. data/lib/pool_party/os.rb +11 -0
  36. data/lib/pool_party/remote_instance.rb +180 -0
  37. data/lib/pool_party/remoting.rb +112 -0
  38. data/lib/pool_party/scheduler.rb +93 -0
  39. data/lib/pool_party/tasks.rb +220 -0
  40. data/lib/pool_party.rb +69 -0
  41. data/lib/s3/s3_object_store_folders.rb +44 -0
  42. data/poolparty.gemspec +55 -0
  43. data/spec/application_spec.rb +32 -0
  44. data/spec/callback_spec.rb +65 -0
  45. data/spec/helpers/ec2_mock.rb +56 -0
  46. data/spec/helpers/remote_instance_mock.rb +11 -0
  47. data/spec/kernel_spec.rb +11 -0
  48. data/spec/master_spec.rb +147 -0
  49. data/spec/monitor_spec.rb +16 -0
  50. data/spec/optioner_spec.rb +22 -0
  51. data/spec/poolparty_spec.rb +8 -0
  52. data/spec/remote_instance_spec.rb +29 -0
  53. data/spec/remoting_spec.rb +75 -0
  54. data/spec/spec_helper.rb +38 -0
  55. data/spec/string_spec.rb +28 -0
  56. data/test/test_pool_party.rb +0 -0
  57. metadata +171 -0
@@ -0,0 +1,220 @@
1
+ module PoolParty
2
+ module TaskCommands
3
+ # Run the command on the local system
4
+ def run(cmd)
5
+ system(cmd.runnable)
6
+ end
7
+ # Basic setup action
8
+ def setup_application
9
+ Application.options({:config_file => (ENV["CONFIG_FILE"] || ENV["config"]) })
10
+ end
11
+ end
12
+ class Tasks
13
+ include TaskCommands
14
+ # Setup and define all the tasks
15
+ def initialize
16
+ yield self if block_given?
17
+ define_tasks!
18
+ end
19
+ # Define the tasks in the rakefile
20
+ def define_tasks!
21
+ # Tasks dealing with only an instance
22
+ namespace(:instance) do
23
+ # Find the instance we want to deal with
24
+ # interface can be: num=0, i=0, inst=0, 0
25
+ # defaults to the master instance (0)
26
+ task :init do
27
+ num = (ENV['num'] || ENV["i"] || ENV["inst"] || ARGV.shift || 0).to_i
28
+ raise Exception.new("Please set the number of the instance (i.e. num=1, i=1, or as an argument)") unless num
29
+ @node = PoolParty::Master.new.get_node(num)
30
+ end
31
+ # Ssh into the node
32
+ desc "Remotely login to the remote instance"
33
+ task :ssh => [:init] do
34
+ @node.ssh
35
+ end
36
+ # Send a file to the remote instance
37
+ # as designated by src='' and dest=''
38
+ desc "Send a file to the remote instance"
39
+ task :scp => [:init] do
40
+ @node.scp ENV['src'], ENV['dest']
41
+ end
42
+ # Execute a command on the remote instance as designated
43
+ # by cmd=''
44
+ desc "Execute cmd on a remote instance"
45
+ task :exec => [:init] do
46
+ cmd = ENV['cmd'] || "ls -l"
47
+ puts @node.ssh(cmd.runnable)
48
+ end
49
+ # Restart all the services monitored by monit
50
+ desc "Restart all the services"
51
+ task :reload => [:init] do
52
+ @node.restart_with_monit
53
+ end
54
+ # Start all the services monitored by monit
55
+ desc "Start all services"
56
+ task :load => [:init] do
57
+ @node.start_with_monit
58
+ end
59
+ # Stop the services monitored by monit
60
+ desc "Stop all services"
61
+ task :stop => [:init] do
62
+ @node.stop_with_monit
63
+ end
64
+ # Install the required services on this node
65
+ desc "Install stack on this node"
66
+ task :install => :init do
67
+ @node.install_stack
68
+ end
69
+ # Turnoff this instance
70
+ desc "Teardown instance"
71
+ task :shutdown => :init do
72
+ `ec2-terminate-instances #{@node.instance_id}`
73
+ end
74
+ # Configure this node and start the services
75
+ desc "Configure the stack on this node"
76
+ task :configure => :init do
77
+ @node.configure
78
+ @node.restart_with_monit
79
+ end
80
+ end
81
+
82
+ namespace(:dev) do
83
+ task :init do
84
+ setup_application
85
+ end
86
+ # Setup a basic development environment for the user
87
+ desc "Setup development environment specify the config_file"
88
+ task :setup => :init do
89
+ keyfilename = ".#{Application.keypair}_pool_keys"
90
+ run <<-EOR
91
+ echo 'export ACCESS_KEY=\"#{Application.access_key}\"' > $HOME/#{keyfilename}
92
+ echo 'export SECRET_ACCESS_KEY=\"#{Application.secret_access_key}\"' >> $HOME/#{keyfilename}
93
+ echo 'export EC2_HOME=\"#{Application.ec2_dir}\"' >> $HOME/#{keyfilename}
94
+ echo 'export KEYPAIR_NAME=\"#{Application.keypair}\"' >> $HOME/#{keyfilename}
95
+ echo 'export CONFIG_FILE=\"#{Application.config_file}\"' >> $HOME/#{keyfilename}
96
+ EOR
97
+ end
98
+ end
99
+ # Cloud tasks
100
+ namespace(:cloud) do
101
+ # Setup
102
+ task :init do
103
+ setup_application
104
+ raise Exception.new("You must specify your access_key and secret_access_key") unless Application.access_key && Application.secret_access_key
105
+ end
106
+ # Install the stack on all of the nodes
107
+ desc "Prepare all servers"
108
+ task :prepare => :init do
109
+ PoolParty::Master.new.nodes.each do |node|
110
+ node.install_stack
111
+ end
112
+ end
113
+ # Start the cloud
114
+ desc "Start the cloud"
115
+ task :start => :init do
116
+ PoolParty::Master.new.start_cloud!
117
+ end
118
+ # Reload the cloud with the new updated data
119
+ desc "Reload all instances with updated data"
120
+ task :reload => :init do
121
+ PoolParty::Master.new.nodes.each do |node|
122
+ node.configure
123
+ node.restart_with_monit
124
+ end
125
+ end
126
+ # List the cloud
127
+ desc "List cloud"
128
+ task :list => :init do
129
+ PoolParty::Master.new.list
130
+ end
131
+ # Shutdown the cloud
132
+ desc "Shutdown the entire cloud"
133
+ task :shutdown => :init do
134
+ PoolParty::Master.new.request_termination_of_all_instances
135
+ end
136
+ # Maintain the cloud in a background process
137
+ desc "Maintain the cloud (run on the master)"
138
+ task :maintain => :init do
139
+ begin
140
+ PoolParty::Master.new.start_monitor!
141
+ rescue Exception => e
142
+ puts "There was an error starting the monitor: #{e}"
143
+ end
144
+ end
145
+ # Deploy task.
146
+ # TODO: Find a beautiful way of updating the user-defined configuration
147
+ # data
148
+ desc "Deploy web application from production git repos specified in config file"
149
+ task :deploy => :init do
150
+ puts "Deploying web app on nginx"
151
+ end
152
+ end
153
+
154
+ # Nearly antiquated tasks
155
+ namespace(:ec2) do
156
+ task :init do
157
+ Application.options
158
+ end
159
+ # Start a new instance in the cloud
160
+ desc "Add and start an instance to the pool"
161
+ task :start_new_instance => [:init] do
162
+ puts PoolParty::Remoting.new.launch_new_instance!
163
+ end
164
+ # Stop all the instances via command-line
165
+ desc "Stop all running instances"
166
+ task :stop_running_instances => [:init] do
167
+ Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-terminate-instances`}
168
+ end
169
+ # Reboot the instances via commandline
170
+ desc "Restart all running instances"
171
+ task :restart_running_instances => [:init] do
172
+ Thread.new {`ec2-describe-instances | grep INSTANCE | grep running | awk '{print $2}' | xargs ec2-reboot-instances`}
173
+ end
174
+ end
175
+ # Tasks to be run on the server
176
+ namespace(:server) do
177
+ task :init do
178
+ PoolParty::Coordinator.init(false)
179
+ end
180
+ # bundle, upload and register your bundle on the server
181
+ desc "Bundle, upload and register your ami"
182
+ task :all => [:bundle, :upload, :register] do
183
+ puts "== your ami is ready"
184
+ end
185
+ # Cleanup the /mnt directory
186
+ desc "Clean the /mnt directory"
187
+ task :clean_mnt do
188
+ `rm -rf /mnt/image* img*`
189
+ end
190
+ # Before we can bundle, we have to make sure we have the cert and pk files
191
+ desc "Ensure the required bundle files are present in /mnt"
192
+ task :check_bundle_files do
193
+ raise Exception.new("You must have a private key in your /mnt directory") unless File.exists?("/mnt/pk-*.pem")
194
+ raise Exception.new("You must have your access key in your /mnt directory") unless File.exists?("/mnt/cert-*.pem")
195
+ end
196
+ # Bundle the image
197
+ desc "Bundle this image into the /mnt directory"
198
+ task :bundle => [:clean_mnt, :check_bundle_files] do
199
+ puts `ec2-bundle-vol -k /mnt/pk-*.pem -u '#{Planner.user_id}' -d /mnt -c /mnt/cert-*.pem -r i386`
200
+ end
201
+ # Upload the bundle into the app_name bucket
202
+ desc "Upload the bundle to your bucket with a unique name: deletes old ami"
203
+ task :upload => [:init, :delete_bucket] do
204
+ puts `ec2-upload-bundle -b #{Planner.app_name} -m /mnt/image.manifest.xml -a #{Planner.access_key} -s #{Planner.secret_access_key}`
205
+ end
206
+ # Register the bucket with amazon and get back an ami
207
+ desc "Register the bundle with amazon"
208
+ task :register do
209
+ puts `ec2-register -K /mnt/pk-*.pem -C /mnt/cert-*.pem #{Planner.app_name}/image.manifest.xml`
210
+ end
211
+ # Delete the bucket
212
+ desc "Delete the bucket with the bundle under tha app name"
213
+ task :delete_bucket do
214
+ Planner.app_name.delete_bucket
215
+ end
216
+ end
217
+ end
218
+
219
+ end
220
+ end
data/lib/pool_party.rb ADDED
@@ -0,0 +1,69 @@
1
+ =begin rdoc
2
+ The main file, contains the client and the server application methods
3
+ =end
4
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
5
+
6
+ # rubygems
7
+ require 'rubygems'
8
+ require "aws/s3"
9
+ require "sqs"
10
+ require "EC2"
11
+ require 'thread'
12
+ require "pp"
13
+ require "tempfile"
14
+ require "aska"
15
+ begin
16
+ require 'fastthread'
17
+ require 'thin'
18
+ rescue LoadError
19
+ end
20
+
21
+ ## Load PoolParty
22
+ pwd = File.dirname(__FILE__)
23
+
24
+ # Load the required files
25
+ # If there is an init file, load that, otherwise
26
+ # require all the files in each directory
27
+ %w(core modules s3 pool_party).each do |dir|
28
+ Dir["#{pwd}/#{dir}"].each do |dir|
29
+ begin
30
+ require File.join(dir, "init")
31
+ rescue LoadError => e
32
+ Dir["#{pwd}/#{File.basename(dir)}/**"].each {|file| require File.join(dir, File.basename(file))}
33
+ end
34
+ end
35
+ end
36
+
37
+ module PoolParty
38
+ module Version
39
+ MAJOR = '0'
40
+ MINOR = '0'
41
+ REVISION = '4'
42
+ def self.combined
43
+ [MAJOR, MINOR, REVISION].join('.')
44
+ end
45
+ end
46
+ # PoolParty options
47
+ def options(opts={})
48
+ Application.options(opts)
49
+ end
50
+ # Are we working in verbose-mode
51
+ def verbose?
52
+ Application.verbose == true
53
+ end
54
+ # Send a message if we are in verbose-mode
55
+ def message(msg="")
56
+ pp "-- #{msg}" if verbose?
57
+ end
58
+ # Root directory of the application
59
+ def root_dir
60
+ File.dirname(__FILE__)
61
+ end
62
+ # Write string to a tempfile
63
+ def write_to_temp_file(str="")
64
+ tempfile = Tempfile.new("rand#{rand(1000)}-#{rand(1000)}")
65
+ tempfile.print(str)
66
+ tempfile.flush
67
+ tempfile
68
+ end
69
+ end
@@ -0,0 +1,44 @@
1
+ =begin rdoc
2
+ S3 overloads
3
+ =end
4
+ module AWS
5
+ module S3
6
+ class S3Object
7
+ class << self
8
+
9
+ alias :original_store :store
10
+ def store(key, data, bucket = nil, options = {})
11
+ store_folders(key, bucket, options) if options[:use_virtual_directories]
12
+ original_store(key, data, bucket, options)
13
+ end
14
+
15
+ def streamed_store(key, filepath, bucket = nil, options = {})
16
+ store_folders(key, bucket, options) if options[:use_virtual_directories]
17
+ store(key,File.open(filepath), bucket)
18
+ end
19
+
20
+ def store_directory(directory, bucket, options = {})
21
+ Dir[File.join(directory, "*")].each do |file|
22
+ streamed_store("#{File.basename(File.dirname(file))}/#{File.basename(file)}", file, bucket, options.update(:use_virtual_directories => true))
23
+ end
24
+ end
25
+
26
+ def store_folders(key, bucket = nil, options = {})
27
+ folders = key.split("/")
28
+ folders.slice!(0)
29
+ folders.pop
30
+ current_folder = "/"
31
+ folders.each {|folder|
32
+ current_folder += folder
33
+ store_folder(current_folder, bucket, options)
34
+ current_folder += "/"
35
+ }
36
+ end
37
+
38
+ def store_folder(key, bucket = nil, options = {})
39
+ original_store(key + "_$folder$", "", bucket, options) # store the magic entry that emulates a folder
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
data/poolparty.gemspec ADDED
@@ -0,0 +1,55 @@
1
+
2
+ # Gem::Specification for Poolparty-0.0.4
3
+ # Originally generated by Echoe
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = %q{poolparty}
7
+ s.version = "0.0.4"
8
+
9
+ s.specification_version = 2 if s.respond_to? :specification_version=
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.authors = ["Ari Lerner"]
13
+ s.date = %q{2008-05-28}
14
+ s.description = %q{Run your entire application off EC2, managed and auto-scaling}
15
+ s.email = %q{ari.lerner@citrusbyte.com}
16
+ s.executables = ["instance", "pool"]
17
+ s.extra_rdoc_files = ["bin/instance", "bin/pool", "CHANGELOG", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/safe_instance.rb", "lib/pool_party/application.rb", "lib/pool_party/init.rb", "lib/pool_party/master.rb", "lib/pool_party/monitors/cpu.rb", "lib/pool_party/monitors/memory.rb", "lib/pool_party/monitors/web.rb", "lib/pool_party/monitors.rb", "lib/pool_party/optioner.rb", "lib/pool_party/os/ubuntu.rb", "lib/pool_party/os.rb", "lib/pool_party/remote_instance.rb", "lib/pool_party/remoting.rb", "lib/pool_party/scheduler.rb", "lib/pool_party/tasks.rb", "lib/pool_party.rb", "lib/s3/s3_object_store_folders.rb", "README.txt"]
18
+ s.files = ["bin/instance", "bin/pool", "CHANGELOG", "config/config.yml", "config/create_proxy_ami.sh", "config/haproxy.conf", "config/heartbeat.conf", "config/heartbeat_authkeys.conf", "config/monit/haproxy.monit.conf", "config/monit/nginx.monit.conf", "config/monit.conf", "config/nginx.conf", "lib/core/array.rb", "lib/core/exception.rb", "lib/core/kernel.rb", "lib/core/module.rb", "lib/core/object.rb", "lib/core/string.rb", "lib/core/time.rb", "lib/modules/callback.rb", "lib/modules/ec2_wrapper.rb", "lib/modules/safe_instance.rb", "lib/pool_party/application.rb", "lib/pool_party/init.rb", "lib/pool_party/master.rb", "lib/pool_party/monitors/cpu.rb", "lib/pool_party/monitors/memory.rb", "lib/pool_party/monitors/web.rb", "lib/pool_party/monitors.rb", "lib/pool_party/optioner.rb", "lib/pool_party/os/ubuntu.rb", "lib/pool_party/os.rb", "lib/pool_party/remote_instance.rb", "lib/pool_party/remoting.rb", "lib/pool_party/scheduler.rb", "lib/pool_party/tasks.rb", "lib/pool_party.rb", "lib/s3/s3_object_store_folders.rb", "Manifest", "Rakefile", "README.txt", "spec/application_spec.rb", "spec/callback_spec.rb", "spec/helpers/ec2_mock.rb", "spec/helpers/remote_instance_mock.rb", "spec/kernel_spec.rb", "spec/master_spec.rb", "spec/monitor_spec.rb", "spec/optioner_spec.rb", "spec/poolparty_spec.rb", "spec/remote_instance_spec.rb", "spec/remoting_spec.rb", "spec/spec_helper.rb", "spec/string_spec.rb", "test/test_pool_party.rb", "poolparty.gemspec"]
19
+ s.has_rdoc = true
20
+ s.homepage = %q{http://blog.citrusbyte.com}
21
+ s.post_install_message = %q{For more information, check http://poolpartyrb.com
22
+ *** Ari Lerner @ <ari.lerner@citrusbyte.com> ***}
23
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Poolparty", "--main", "README.txt"]
24
+ s.require_paths = ["lib"]
25
+ s.rubyforge_project = %q{poolparty}
26
+ s.rubygems_version = %q{1.0.1}
27
+ s.summary = %q{Run your entire application off EC2, managed and auto-scaling}
28
+ s.test_files = ["test/test_pool_party.rb"]
29
+
30
+ s.add_dependency(%q<aws-s3>, [">= 0"])
31
+ s.add_dependency(%q<amazon-ec2>, [">= 0"])
32
+ s.add_dependency(%q<aska>, [">= 0"])
33
+ end
34
+
35
+
36
+ # # Original Rakefile source (requires the Echoe gem):
37
+ #
38
+ # require 'rubygems'
39
+ # require 'echoe'
40
+ # require 'lib/pool_party'
41
+ #
42
+ # task :default => :test
43
+ #
44
+ # Echoe.new("poolparty") do |p|
45
+ # p.author = "Ari Lerner"
46
+ # p.email = "ari.lerner@citrusbyte.com"
47
+ # p.summary = "Run your entire application off EC2, managed and auto-scaling"
48
+ # p.url = "http://blog.citrusbyte.com"
49
+ # p.docs_host = "www.poolpartyrb.com"
50
+ # p.dependencies = %w(aws-s3 amazon-ec2 aska)
51
+ # p.install_message = "For more information, check http://poolpartyrb.com\n*** Ari Lerner @ <ari.lerner@citrusbyte.com> ***"
52
+ # p.include_rakefile = true
53
+ # end
54
+ #
55
+ # PoolParty::Tasks.new
@@ -0,0 +1,32 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Application" do
4
+ before(:each) do
5
+ end
6
+ it "should have the root_dir defined" do
7
+ Application.root_dir.should_not be_nil
8
+ end
9
+ it "should be able to call on the haproxy_config_file" do
10
+ Application.haproxy_config_file.should_not be_nil
11
+ end
12
+ it "should be able to find the client_port" do
13
+ Application.options.should_receive(:client_port).and_return(7788)
14
+ Application.client_port.should == 7788
15
+ end
16
+ it "should always have haproxy in the managed services list" do
17
+ Application.managed_services =~ /haproxy/
18
+ end
19
+ it "should be able to say it is in development mode if it is in dev mode" do
20
+ Application.stub!(:environment).and_return("development")
21
+ Application.development?.should == true
22
+ end
23
+ it "should be able to say it is in production if it is in production" do
24
+ Application.stub!(:environment).and_return("production")
25
+ Application.production?.should == true
26
+ end
27
+ it "should be able to say it's keypair path is in the $HOME/ directory" do
28
+ Application.stub!(:ec2_dir).and_return("~/.ec2")
29
+ Application.stub!(:keypair).and_return("poolparty")
30
+ Application.keypair_path.should == "~/.ec2/id_rsa-poolparty"
31
+ end
32
+ end
@@ -0,0 +1,65 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ class TestCallbacks
4
+ include Callbacks
5
+ attr_reader :str
6
+ def hello
7
+ string << "hello "
8
+ end
9
+ def world
10
+ string << "world"
11
+ end
12
+ def thanks
13
+ string << ", thank you"
14
+ end
15
+ before :world, :hello
16
+ after :world, :thanks
17
+ def pop
18
+ string << "pop"
19
+ end
20
+ def boom
21
+ string << " goes boom"
22
+ end
23
+ after :pop, :boom
24
+ def string
25
+ @str ||= String.new
26
+ end
27
+ end
28
+ describe "Callbacks" do
29
+ before(:each) do
30
+ @klass = TestCallbacks.new
31
+ end
32
+ it "should retain it's class identifier" do
33
+ @klass.class.should == TestCallbacks
34
+ end
35
+ it "should callback the method before the method runs" do
36
+ @klass.world.should == "hello world, thank you"
37
+ end
38
+ it "should callback the method before the method runs" do
39
+ @klass.pop.should == "pop goes boom"
40
+ end
41
+ end
42
+ class TestMultipleCallbacks
43
+ include Callbacks
44
+ attr_reader :str
45
+ def hi
46
+ string << "hi, "
47
+ end
48
+ def hello
49
+ string << "hello "
50
+ end
51
+ def world
52
+ string << "world"
53
+ end
54
+ def string
55
+ @str ||= String.new
56
+ end
57
+ before :world, :hi
58
+ before :world, :hello
59
+ end
60
+ describe "Multiple callbacks" do
61
+ before(:each) do
62
+ @klass = TestMultipleCallbacks.new
63
+ end
64
+ it "should be able to have multiple callbacks on the same call"
65
+ end
@@ -0,0 +1,56 @@
1
+ module PoolParty
2
+ module EC2Mock
3
+ def launch_new_instance!
4
+ letter = ("a".."z").to_a[instances.size] # For unique instance_ids
5
+ h = {:instance_id => "i-58ba56#{letter}", :ip => "ip-127-0-0-1.aws.amazonaws.com", :status => "pending", :launching_time => Time.now }
6
+ instances << h
7
+ Thread.new {wait 0.1;h[:status] = "running"} # Simulate the startup time
8
+ return h
9
+ end
10
+ # Shutdown the instance by instance_id
11
+ def terminate_instance!(instance_id)
12
+ instances.select {|a| a[:instance_id] == instance_id}[0][:status] = "terminating"
13
+ end
14
+ # Instance description
15
+ def describe_instance(id)
16
+ item = instances.select {|a| a[:instance_id] == id}[0]
17
+ EC2ResponseObject.get_hash_from_response(item)
18
+ end
19
+ # Get instance by id
20
+ def get_instance_by_id(id)
21
+ get_instances_description.select {|a| a.instance_id == id}[0] rescue nil
22
+ end
23
+ # Get the s3 description for the response in a hash format
24
+ def get_instances_description
25
+ instances
26
+ end
27
+ # Fake the ec2 connection
28
+ def ec2
29
+ @ec2 ||= EC2::Base.new(:access_key_id => "not a key", :secret_access_key => "not a key")
30
+ end
31
+ # Some basic instances, not totally necessary
32
+ def instances
33
+ @instances ||= []
34
+ end
35
+ end
36
+ class EC2ResponseObject
37
+ def self.get_descriptions(resp)
38
+ rs = resp.DescribeInstancesResponse.reservationSet.item
39
+ rs = rs.respond_to?(:instancesSet) ? rs.instancesSet : rs
40
+ out = begin
41
+ rs.reject {|a| a.empty? }.collect {|r| EC2ResponseObject.get_hash_from_response(r.instancesSet.item)}.reject {|a| a.nil? }
42
+ rescue Exception => e
43
+ begin
44
+ # Really weird bug with amazon's ec2 gem
45
+ rs.reject {|a| a.empty? }.collect {|r| EC2ResponseObject.get_hash_from_response(r)}.reject {|a| a.nil? }
46
+ rescue Exception => e
47
+ []
48
+ end
49
+ end
50
+ out
51
+ end
52
+ def self.get_hash_from_response(resp)
53
+ {:instance_id => resp.instanceId, :ip => resp.dnsName, :status => resp.instanceState.name, :launching_time => resp.launchTime} rescue nil
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,11 @@
1
+ module PoolParty
2
+ module RemoteInstanceMock
3
+ def scp(src="", dest="")
4
+ true
5
+ end
6
+ # Ssh into the instance or run a command, if the cmd is set
7
+ def ssh(cmd="")
8
+ true
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe "Kernel extensions" do
4
+ before(:each) do
5
+ @host = Master.new
6
+ end
7
+ it "should eval the string into time" do
8
+ @host.should_receive(:sleep).once.and_return true
9
+ @host.wait "10.seconds"
10
+ end
11
+ end