GoEC2 1.0.0

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/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ lib/installer/cert-6BM4YUMOROLWBDVJSJKTGZWXOQHJFOKG.pem
2
+ lib/installer/default-key-pair.pem
3
+ lib/installer/ec2-api-tools.tar.gz
4
+ lib/installer/pk-6BM4YUMOROLWBDVJSJKTGZWXOQHJFOKG.pem
5
+ lib/installer/use-ec2
6
+ pkg/*
data/GoEC2.gemspec ADDED
@@ -0,0 +1,51 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{GoEC2}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Within3", "Joe Fiorini", "Jonathan Penn"]
12
+ s.date = %q{2009-10-09}
13
+ s.default_executable = %q{goec2}
14
+ s.description = %q{Simple and opinionated helper for creating and managing Rubygem projects on GitHub}
15
+ s.email = %q{jfiorini@within3.com}
16
+ s.executables = ["goec2"]
17
+ s.files = [
18
+ ".gitignore",
19
+ "GoEC2.gemspec",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "bin/goec2",
23
+ "lib/installer/goec2",
24
+ "lib/installer/install-goec2",
25
+ "lib/installer/uninstall_goec2",
26
+ "lib/options.rb",
27
+ "lib/runner.rb",
28
+ "lib/utils.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/faithfulgeek/goec2}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.5}
34
+ s.summary = %q{Simple and opinionated helper for creating and managing Rubygem projects on GitHub}
35
+
36
+ if s.respond_to? :specification_version then
37
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
38
+ s.specification_version = 3
39
+
40
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
41
+ s.add_runtime_dependency(%q<sevenwire-forgery>, [">= 0.2.2"])
42
+ s.add_development_dependency(%q<jeweler>, [">= 1.2.1"])
43
+ else
44
+ s.add_dependency(%q<sevenwire-forgery>, [">= 0.2.2"])
45
+ s.add_dependency(%q<jeweler>, [">= 1.2.1"])
46
+ end
47
+ else
48
+ s.add_dependency(%q<sevenwire-forgery>, [">= 0.2.2"])
49
+ s.add_dependency(%q<jeweler>, [">= 1.2.1"])
50
+ end
51
+ end
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "GoEC2"
5
+ gemspec.summary = "Simple and opinionated helper for creating and managing Rubygem projects on GitHub"
6
+ gemspec.description = "Simple and opinionated helper for creating and managing Rubygem projects on GitHub"
7
+ gemspec.email = "jfiorini@within3.com"
8
+ gemspec.homepage = "http://github.com/faithfulgeek/goec2"
9
+ gemspec.authors = ["Within3", "Joe Fiorini", "Jonathan Penn"]
10
+ gemspec.add_dependency('sevenwire-forgery', '>=0.2.2')
11
+ gemspec.add_development_dependency('jeweler', '>=1.2.1')
12
+ end
13
+ Jeweler::GemcutterTasks.new
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/bin/goec2 ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+ require 'runner'
3
+
4
+ Runner.new.run(ARGV)
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'yaml'
4
+ require 'forgery'
5
+ require 'dictionaries'
6
+ require 'file_reader'
7
+ require 'optparse'
8
+
9
+ IDREGEX = /i-[0-9a-f]{8}/
10
+
11
+ EC2_SSH_KEY_NAME = ENV['EC2_KEYPAIR_NAME'] || 'default'
12
+ EC2_SSH_KEY_PATH = ENV['EC2_KEYPAIR_PATH'] || '~/.ec2/default-key-pair.pem'
13
+
14
+ #AMI_ID = "ami-5647a33f" # Amazon Base Fedora 8
15
+ #AMI_ID = "ami-c8ac48a1" # RightImage CentOS 5 Base
16
+ #AMI_ID = "ami-d510f1bc" # Within3 App Image
17
+ #AMI_ID = "ami-155cba7c" # RightImage CentOS 5.2 Base
18
+ #AMI_ID = "ami-4f1dfc26" # Within3 App Base Image
19
+ #AMI_ID = "ami-af6283c6" # Within3 App Base Image
20
+ AMI_ID = "ami-3f688956" # Within3 App Base Image
21
+
22
+ Instance = Struct.new(:line, :instance_id, :image, :status, :url, :name)
23
+
24
+ def main
25
+ case ARGV[0]
26
+ when /spinup/
27
+ options = {}
28
+ opts = OptionParser.new
29
+ opts.on('-n', '--name NAME') { |name| options[:name] = name }
30
+ opts.on('-s', '--size SIZE') { |size| options[:instance_size] = size }
31
+ opts.on('-f', '--user-data-file PATH') { |path| options[:user_data_file] = path }
32
+ opts.on('-a', '--ami-id AMI_ID') { |ami_id| options[:ami_id] = ami_id }
33
+ opts.on('-z', '--zone ZONE') { |zone| options[:zone] = zone }
34
+ opts.parse!(ARGV)
35
+ options[:name] ||= get_name_for_instance
36
+ options[:ami_id] ||= AMI_ID
37
+ options[:instance_size] ||= 'm1.large'
38
+ options[:zone] ||= 'us-east-1c'
39
+ puts "Spining up image with options: #{options.inspect}"
40
+ spinup(options)
41
+ when /spindown/
42
+ raise "Need an instance name" unless ARGV[1]
43
+ spindown ARGV[1]
44
+ when /status/
45
+ puts "Getting the status"
46
+ instances = if ARGV[1] then [get_instance(ARGV[1])].compact else get_instances end
47
+ instances.each do |instance|
48
+ puts "%s\t%s\t%s\t%s" % [instance.name, instance.instance_id, instance.status, instance.url]
49
+ end
50
+ when /ssh/
51
+ raise "Need an instance name" unless ARGV[1]
52
+ instance = get_instance(ARGV[1])
53
+ raise "Unknown instance name #{ARGV[1]}" unless instance
54
+ ssh instance.url
55
+ when /scp_up/
56
+ raise "Need an istance name" unless ARGV[1]
57
+ raise "Need a source path" unless ARGV[2]
58
+ raise "Need a destination path" unless ARGV[3]
59
+ instance = get_instance(ARGV[1])
60
+ scp_up instance, ARGV[2], ARGV[3]
61
+ when /webopen/
62
+ raise "Need an instance name" unless ARGV[1]
63
+ instance = get_instance(ARGV[1])
64
+ raise "Unknown instance name #{ARGV[1]}" unless instance
65
+ `open http://#{instance.url}`
66
+ when /output/
67
+ raise "Need an instance name" unless ARGV[1]
68
+ console_output get_instance(ARGV[1])
69
+ when /address/
70
+ raise "Need an instance name" unless ARGV[1]
71
+ instance = get_instance(ARGV[1])
72
+ puts instance.url
73
+ else
74
+ puts "Fail."
75
+ end
76
+ end
77
+
78
+
79
+ def run_instance(options={})
80
+ instance_size = options.delete(:instance_size)
81
+ image_id = options.delete(:ami_id)
82
+ name = options.delete(:name)
83
+ zone = options.delete(:zone)
84
+ user_data_file = options.delete(:user_data_file)
85
+ instance_options = "-t #{instance_size}"
86
+ instance_options += " -f #{user_data_file}" if user_data_file
87
+ instance_options += " -z #{zone}" if zone
88
+ instance = parse_aws_status_line_into_instance `ec2-run-instances #{image_id} -k #{EC2_SSH_KEY_NAME} #{instance_options}`.split("\n")[-1], name
89
+ write_instance_to_datastore(instance)
90
+ instance
91
+ end
92
+
93
+ def get_name_for_instance
94
+ Dictionaries.new[:company_names].random.downcase
95
+ end
96
+
97
+ def parse_aws_status_line_into_instance line, name=nil
98
+ columns = line.split("\t")
99
+ instance = Instance.new line, columns[1], columns[2], columns[5], columns[3], name
100
+ end
101
+
102
+ def get_instance(name)
103
+ instances = load_instances_from_datastore
104
+ instance_id = instances[name]
105
+ `ec2-describe-instances #{instance_id}`.split("\n").select{ |l| l =~ /^INSTANCE/ }.inject([]) do |result, line|
106
+ instance = parse_aws_status_line_into_instance(line, name)
107
+
108
+ if instance.status != 'terminated'
109
+ instance.name = name
110
+ result << instance
111
+ else
112
+ result
113
+ end
114
+ end.compact.first
115
+ end
116
+
117
+ def get_instances
118
+ instance_mappings = load_instances_from_datastore
119
+ `ec2-describe-instances`.split("\n").select{|l| l =~ /^INSTANCE/}.inject([]) do |result, line|
120
+ instance = parse_aws_status_line_into_instance(line)
121
+
122
+ if instance.status != "terminated"
123
+ instance.name = instance_mappings.find_name_by_instance_id(instance.instance_id)
124
+ result << instance
125
+ else
126
+ result
127
+ end
128
+ end
129
+ end
130
+
131
+ def terminate_instances *instances
132
+ `ec2-terminate-instances #{instances.map{|i| i.instance_id}.join(' ')}`
133
+ end
134
+
135
+ def ssh url
136
+ puts url
137
+ exec "ssh -i #{EC2_SSH_KEY_PATH} root@#{url}"
138
+ end
139
+
140
+ def scp_up instance, source, destination
141
+ exec "scp -i #{EC2_SSH_KEY_PATH} #{source} root@#{instance.url}:#{destination}"
142
+ end
143
+
144
+ def console_output instance
145
+ exec "ec2-get-console-output #{instance.instance_id}"
146
+ end
147
+
148
+ def spinup(options={})
149
+ puts "Spinning up image"
150
+ name = options[:name]
151
+ current_instance = instance = run_instance(options)
152
+ puts "Instance #{instance.instance_id} spinning up"
153
+
154
+ still_loading = true
155
+ while still_loading
156
+ puts "waiting...#{current_instance.line}"
157
+ sleep 2
158
+ current_instance = get_instance(name)
159
+ still_loading = current_instance.status == "pending"
160
+ end
161
+
162
+ puts "Instance running!"
163
+ puts "name: #{current_instance.name}, id: #{current_instance.instance_id}, url: #{current_instance.url}"
164
+ end
165
+
166
+ INSTANCE_MAPPINGS_FILENAME = File.expand_path("~/.ec2/instance_mappings.yml")
167
+ def write_instance_to_datastore(instance)
168
+ instances = load_instances_from_datastore || {}
169
+ instances.merge!({instance.name => instance.instance_id})
170
+ write_instances_to_datastore(instances)
171
+ end
172
+
173
+ def write_instances_to_datastore(instances)
174
+ File.open(INSTANCE_MAPPINGS_FILENAME, "w+") do |f|
175
+ f << instances.to_yaml
176
+ end
177
+ end
178
+
179
+ def load_instances_from_datastore
180
+ data = File.read(INSTANCE_MAPPINGS_FILENAME)
181
+ instances = YAML.load(data)
182
+
183
+ class << instances
184
+ def find_name_by_instance_id(instance_id)
185
+ select { |k,v| v == instance_id }[0]
186
+ end
187
+ end
188
+
189
+ instances
190
+ end
191
+
192
+ def delete_instance_from_datastore(name)
193
+ instances = load_instances_from_datastore
194
+ instances.delete(name)
195
+ write_instances_to_datastore(instances)
196
+ end
197
+
198
+ def spindown name
199
+ puts "Spinning down #{name}"
200
+ instance = get_instance(name)
201
+ raise "Unknown instance name #{name}" unless instance
202
+
203
+ terminate_instances instance
204
+
205
+ while instance
206
+ puts "waiting...#{instance.line}"
207
+ sleep 2
208
+ instance = get_instance(name)
209
+ end
210
+
211
+ delete_instance_from_datastore(name)
212
+
213
+ puts "Terminated #{name}"
214
+ end
215
+
216
+ main
@@ -0,0 +1,70 @@
1
+ #!/bin/bash
2
+
3
+ # Must be run with root permissions
4
+
5
+ set -e
6
+
7
+ export EC2_HOME=/usr/local/ec2
8
+ export JAVA_HOME="/System/Library/Frameworks/JavaVM.framework/Home"
9
+
10
+ ec2_dir=".ec2"
11
+
12
+ if [ ! -e /usr/local/ec2 ]; then
13
+ sudo mkdir -p $EC2_HOME
14
+ sudo tar -xf ec2-api-tools.tar.gz -C $EC2_HOME
15
+ fi
16
+
17
+ while getopts "v:" flag
18
+ do
19
+ case $flag in
20
+ v)
21
+ conf_dir=$OPTARG ;;
22
+ esac
23
+ done
24
+
25
+ if [ -z $conf_dir ]; then
26
+ mkdir ~/.ec2
27
+ else
28
+
29
+ case $conf_dir in
30
+ */) ;;
31
+ *) conf_dir=$conf_dir/
32
+ esac
33
+
34
+ if [ ! -e $conf_dir$ec2_dir ]; then
35
+ mkdir $conf_dir$ec2_dir
36
+ fi
37
+
38
+ ln -s $conf_dir$ec2_dir ~/
39
+ fi
40
+
41
+ sudo gem install sevenwire-forgery
42
+
43
+ cp pk-* cert-* default-key-pair.pem ~/$ec2_dir
44
+
45
+ sudo cp goec2 /usr/local/bin
46
+
47
+ touch ~/$ec2_dir/instance_mappings.yml
48
+
49
+ export EC2_PRIVATE_KEY=$(ls ~/$ec2_dir/pk-*)
50
+ export EC2_CERT=$(ls ~/$ec2_dir/cert-*)
51
+
52
+ echo "Please enter the name you'd like to use for your EC2 key pair:"
53
+ read keypair_name
54
+
55
+ $EC2_HOME/bin/ec2-add-keypair $keypair_name | grep -v KEYPAIR > ~/$ec2_dir/$keypair_name
56
+ chmod 400 ~/$ec2_dir/$keypair_name
57
+ chmod 400 ~/$ec2_dir/default-key-pair.pem
58
+
59
+ echo "
60
+ EC2_HOME=$EC2_HOME
61
+ EC2_PRIVATE_KEY=$(ls ~/$ec2_dir/pk-*)
62
+ EC2_CERT=$(ls ~/$ec2_dir/cert-*)
63
+ PATH=\"$PATH:$EC2_HOME/bin\"
64
+ JAVA_HOME=$JAVA_HOME
65
+ EC2_KEYPAIR_PATH=~/$ec2_dir/$keypair_name
66
+ EC2_KEYPAIR_NAME=$keypair_name
67
+ export EC2_HOME EC2_PRIVATE_KEY EC2_CERT PATH JAVA_HOME EC2_KEYPAIR_NAME EC2_KEYPAIR_PATH
68
+ " >> ~/$ec2_dir/use-ec2
69
+
70
+ echo "source ~/$ec2_dir/use-ec2" >> ~/.bash_profile
@@ -0,0 +1,17 @@
1
+ #!/bin/bash
2
+
3
+ ec2_dir=".ec2"
4
+
5
+ echo "Remove keypair? (yes/no)"
6
+ read should_remove_keypair
7
+
8
+ if [ $should_remove_keypair = "yes" ]; then
9
+ echo "What is the name of the keypair to remove?"
10
+ read keypair_name
11
+ source use-ec2
12
+ $EC2_HOME/bin/ec2-delete-keypair $keypair_name
13
+ fi
14
+
15
+ sudo rm /usr/local/bin/goec2
16
+ rm -rf ~/$ec2_dir
17
+
data/lib/options.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'optparse'
2
+
3
+ module Options
4
+
5
+ def opts
6
+ @opts ||= OptionParser.new
7
+ end
8
+
9
+ def parse_spinup_options(args, defaults={})
10
+ opts.on('-n', '--name NAME', "The name to use for the instance (note: this is local to this machine. It does not affect Amazon EC2 AT ALL") { |name| defaults[:name] = name }
11
+ opts.on('-s', '--size SIZE', "The size of the instance per EC2 documentation") { |size| defaults[:instance_size] = size }
12
+ opts.on('-f', '--user-data-file PATH', "Path to a user data file to pass into the instance") { |path| defaults[:user_data_file] = path }
13
+ opts.on('-a', '--ami-id AMI_ID', "The AMI ID of the image with which to boot this machine") { |ami_id| defaults[:ami_id] = ami_id }
14
+ opts.on('-z', '--zone ZONE', "The zone in which to boot this machine per EC2 documentation") { |zone| defaults[:zone] = zone }
15
+ opts.banner = "Usage: goec2 spinup [options]"
16
+ opts.parse!(args)
17
+ defaults
18
+ end
19
+
20
+ def parse_ssh_options(args, defaults={})
21
+ opts.on("-u", "--user [NAME]", "The user to pass to ssh") { |name| defaults[:user] = name }
22
+ opts.banner = "Usage: goec2 ssh INSTANCE_NAME [options]"
23
+ opts.parse!(args)
24
+ defaults
25
+ end
26
+
27
+ def parse_attach_volume_options(args, defaults={})
28
+ opts.on("-v", "--volume-id VOLUME_ID") { |volume_id| defaults[:volume_id] = volume_id }
29
+ opts.on("-d", "--device [DEVICE]") { |device| defaults[:device] = device }
30
+ opts.banner = "Usage: goec2 attach_volume INSTANCE_NAME [options]"
31
+ opts.parse!(args)
32
+ defaults
33
+ end
34
+ end
data/lib/runner.rb ADDED
@@ -0,0 +1,83 @@
1
+ require 'optparse'
2
+ require 'utils'
3
+ require 'options'
4
+
5
+ class Runner
6
+ include Utils
7
+ include Options
8
+
9
+ def run(args)
10
+ case args[0]
11
+ when /spinup/
12
+ options = parse_spinup_options(args,
13
+ :name => get_name_for_instance,
14
+ :ami_id => AMI_ID,
15
+ :instance_size => 'm1.large',
16
+ :zone => 'us-east-1c')
17
+ puts "Spining up image with options: #{options.inspect}"
18
+ spinup(options)
19
+ when /spindown/
20
+ raise "Need an instance name" unless args[1]
21
+ spindown args[1]
22
+ when /status/
23
+ puts "Getting the status"
24
+ instances = if args[1] then [get_instance(args[1])].compact else get_instances end
25
+ instances.each do |instance|
26
+ puts "%s\t%s\t%s\t%s" % [instance.name, instance.instance_id, instance.status, instance.url]
27
+ end
28
+ when /ssh/
29
+ instance_name = args.delete_at(1)
30
+ raise "Need an instance name" unless instance_name
31
+ instance = get_instance(instance_name)
32
+ raise "Unknown instance name #{instance_name}" unless instance
33
+ options = parse_ssh_options(args, :user => 'w3dev')
34
+ ssh instance.url, options
35
+ when /scp_up/
36
+ raise "Need an istance name" unless args[1]
37
+ raise "Need a source path" unless args[2]
38
+ raise "Need a destination path" unless args[3]
39
+ instance = get_instance(args[1])
40
+ scp_up instance, args[2], args[3]
41
+ when /webopen/
42
+ raise "Need an instance name" unless args[1]
43
+ instance = get_instance(args[1])
44
+ raise "Unknown instance name #{args[1]}" unless instance
45
+ `open http://#{instance.url}`
46
+ when /output/
47
+ raise "Need an instance name" unless args[1]
48
+ console_output get_instance(args[1])
49
+ when /address/
50
+ raise "Need an instance name" unless args[1]
51
+ instance = get_instance(args[1])
52
+ puts instance.url
53
+ when /attach_volume/
54
+ raise "Need an instance name" unless args[1]
55
+ instance_name = args.delete_at(1)
56
+ options = parse_attach_volume_options(args, :device => '/dev/sdf')
57
+ instance = get_instance(instance_name)
58
+ `ec2-attach-volume #{options[:volume_id]} -i #{instance.instance_id} -d #{options[:device]}`
59
+ else
60
+ puts banner
61
+ end
62
+ end
63
+
64
+ def banner
65
+ <<-EOS
66
+ Script enabling easier interaction with Amazon EC2 instances.
67
+
68
+ Usage: goec2 COMMAND [INSTANCE_NAME] [ARGS]
69
+
70
+ Commands:
71
+
72
+ spinup Tells EC2 service to launch a new instance. By default generates a random name to associate with that instance through goec2
73
+ spindown Terminates the specified instance
74
+ status Retrieves the status of all running EC2 instances
75
+ ssh SSH into an EC2 instance
76
+ scp_up Upload a file to an EC2 instance (goec2 scp_up INSTANCE_NAME SOURCE DEST)
77
+ webopen Open the EC2 instance in a web browser (Mac OSX only)
78
+ output Get console output for a given EC2 instance
79
+ address Get the public address for a given EC2 instance
80
+ attach_volume Attach an EBS volume to the given EC2 instance
81
+ EOS
82
+ end
83
+ end
data/lib/utils.rb ADDED
@@ -0,0 +1,152 @@
1
+ require 'forgery'
2
+ require 'dictionaries'
3
+ require 'yaml'
4
+ require 'file_reader'
5
+
6
+ EC2_SSH_KEY_NAME = ENV['EC2_KEYPAIR_NAME'] || 'default'
7
+ EC2_SSH_KEY_PATH = ENV['EC2_KEYPAIR_PATH'] || '~/.ec2/default-key-pair.pem'
8
+ AMI_ID = "ami-fceb0895" # Within3 App Base Image
9
+
10
+ Instance = Struct.new(:line, :instance_id, :image, :status, :url, :name)
11
+
12
+ module Utils
13
+ def run_instance(options={})
14
+ instance_size = options.delete(:instance_size)
15
+ image_id = options.delete(:ami_id)
16
+ name = options.delete(:name)
17
+ zone = options.delete(:zone)
18
+ user_data_file = options.delete(:user_data_file)
19
+ instance_options = "-t #{instance_size}"
20
+ instance_options += " -f #{user_data_file}" if user_data_file
21
+ instance_options += " -z #{zone}" if zone
22
+ instance = parse_aws_status_line_into_instance `ec2-run-instances #{image_id} -k #{EC2_SSH_KEY_NAME} #{instance_options}`.split("\n")[-1], name
23
+ write_instance_to_datastore(instance)
24
+ instance
25
+ end
26
+
27
+ def get_name_for_instance
28
+ Dictionaries.new[:company_names].random.downcase
29
+ end
30
+
31
+ def parse_aws_status_line_into_instance line, name=nil
32
+ columns = line.split("\t")
33
+ instance = Instance.new line, columns[1], columns[2], columns[5], columns[3], name
34
+ end
35
+
36
+ def get_instance(name)
37
+ instance_id = if name =~ /^i-\w{8}$/
38
+ name
39
+ else
40
+ load_instances_from_datastore[name]
41
+ end
42
+ `ec2-describe-instances #{instance_id}`.split("\n").select{ |l| l =~ /^INSTANCE/ }.inject([]) do |result, line|
43
+ instance = parse_aws_status_line_into_instance(line, name)
44
+
45
+ if instance.status != 'terminated'
46
+ instance.name = name
47
+ result << instance
48
+ else
49
+ result
50
+ end
51
+ end.compact.first
52
+ end
53
+
54
+ def get_instances
55
+ instance_mappings = load_instances_from_datastore
56
+ `ec2-describe-instances`.split("\n").select{|l| l =~ /^INSTANCE/}.inject([]) do |result, line|
57
+ instance = parse_aws_status_line_into_instance(line)
58
+
59
+ if instance.status != "terminated"
60
+ instance.name = instance_mappings.find_name_by_instance_id(instance.instance_id)
61
+ result << instance
62
+ else
63
+ result
64
+ end
65
+ end
66
+ end
67
+
68
+ def terminate_instances *instances
69
+ `ec2-terminate-instances #{instances.map{|i| i.instance_id}.join(' ')}`
70
+ end
71
+
72
+ def ssh(url, options={})
73
+ puts url
74
+ exec "ssh -i #{EC2_SSH_KEY_PATH} #{options[:user]}@#{url}"
75
+ end
76
+
77
+ def scp_up instance, source, destination
78
+ exec "scp -i #{EC2_SSH_KEY_PATH} #{source} root@#{instance.url}:#{destination}"
79
+ end
80
+
81
+ def console_output instance
82
+ exec "ec2-get-console-output #{instance.instance_id}"
83
+ end
84
+
85
+ def spinup(options={})
86
+ puts "Spinning up image"
87
+ name = options[:name]
88
+ current_instance = instance = run_instance(options)
89
+ puts "Instance #{instance.instance_id} spinning up"
90
+
91
+ still_loading = true
92
+ while still_loading
93
+ puts "waiting...#{current_instance.line}"
94
+ sleep 2
95
+ current_instance = get_instance(name)
96
+ still_loading = current_instance.status == "pending"
97
+ end
98
+
99
+ puts "Instance running!"
100
+ puts "name: #{current_instance.name}, id: #{current_instance.instance_id}, url: #{current_instance.url}"
101
+ end
102
+
103
+ INSTANCE_MAPPINGS_FILENAME = File.expand_path("~/.ec2/instance_mappings.yml")
104
+ def write_instance_to_datastore(instance)
105
+ instances = load_instances_from_datastore || {}
106
+ instances.merge!({instance.name => instance.instance_id})
107
+ write_instances_to_datastore(instances)
108
+ end
109
+
110
+ def write_instances_to_datastore(instances)
111
+ File.open(INSTANCE_MAPPINGS_FILENAME, "w+") do |f|
112
+ f << instances.to_yaml
113
+ end
114
+ end
115
+
116
+ def load_instances_from_datastore
117
+ data = File.read(INSTANCE_MAPPINGS_FILENAME)
118
+ instances = YAML.load(data)
119
+
120
+ class << instances
121
+ def find_name_by_instance_id(instance_id)
122
+ select { |k,v| v == instance_id }[0]
123
+ end
124
+ end
125
+
126
+ instances
127
+ end
128
+
129
+ def delete_instance_from_datastore(name)
130
+ instances = load_instances_from_datastore
131
+ instances.delete(name)
132
+ write_instances_to_datastore(instances)
133
+ end
134
+
135
+ def spindown name
136
+ puts "Spinning down #{name}"
137
+ instance = get_instance(name)
138
+ raise "Unknown instance name #{name}" unless instance
139
+
140
+ terminate_instances instance
141
+
142
+ while instance
143
+ puts "waiting...#{instance.line}"
144
+ sleep 2
145
+ instance = get_instance(name)
146
+ end
147
+
148
+ delete_instance_from_datastore(name)
149
+
150
+ puts "Terminated #{name}"
151
+ end
152
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: GoEC2
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Within3
8
+ - Joe Fiorini
9
+ - Jonathan Penn
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-10-09 00:00:00 -04:00
15
+ default_executable: goec2
16
+ dependencies:
17
+ - !ruby/object:Gem::Dependency
18
+ name: sevenwire-forgery
19
+ type: :runtime
20
+ version_requirement:
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: 0.2.2
26
+ version:
27
+ - !ruby/object:Gem::Dependency
28
+ name: jeweler
29
+ type: :development
30
+ version_requirement:
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 1.2.1
36
+ version:
37
+ description: Simple and opinionated helper for creating and managing Rubygem projects on GitHub
38
+ email: jfiorini@within3.com
39
+ executables:
40
+ - goec2
41
+ extensions: []
42
+
43
+ extra_rdoc_files: []
44
+
45
+ files:
46
+ - .gitignore
47
+ - GoEC2.gemspec
48
+ - Rakefile
49
+ - VERSION
50
+ - bin/goec2
51
+ - lib/installer/goec2
52
+ - lib/installer/install-goec2
53
+ - lib/installer/uninstall_goec2
54
+ - lib/options.rb
55
+ - lib/runner.rb
56
+ - lib/utils.rb
57
+ has_rdoc: true
58
+ homepage: http://github.com/faithfulgeek/goec2
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: "0"
71
+ version:
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: "0"
77
+ version:
78
+ requirements: []
79
+
80
+ rubyforge_project:
81
+ rubygems_version: 1.3.5
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: Simple and opinionated helper for creating and managing Rubygem projects on GitHub
85
+ test_files: []
86
+