sproutr 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010 Peter van Hardenberg
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,65 @@
1
+ #Sprout
2
+ ##EC2 made stupid simple.
3
+
4
+ ##Introduction
5
+
6
+ sproutr is a thor based EC2 instance management library which abstracts the Amazon EC2 API and provides an interactive interface for designing, launching, and managing running instances.
7
+
8
+ sproutr is based around the idea of helping you define an instance, launch it, then create as many copies of that as you need. Currently sproutr supports the following tasks:
9
+ sproutr clone --instance=one two three # Clone N number of running instance
10
+ sproutr create_ami --ami=AMI --desc=DESC --name=NAME # Create an EBS Ami from a running or stopped instance
11
+ sproutr define # define a new instance
12
+ sproutr delete_snapshot --snapshot=one two three # Deletes the given snapshot(s) use --snapshot=
13
+ sproutr describe --ami=one two three # Describe a specific instance
14
+ sproutr destroy --ami=one two three # Alias to terminate
15
+ sproutr help [TASK] # Describe available tasks or one specific task
16
+ sproutr launch --config-file=CONFIG_FILE # launch an instance from the specified config directory
17
+ sproutr list # list all the instances and ami's in your ec2 account
18
+ sproutr list_amis # list all the ami's in your ec2 account
19
+ sproutr list_instances # list all the instances in your ec2 account
20
+ sproutr list_snapshots # Alias to list_snapshots
21
+ sproutr list_snapshots # Lists all the snapshots available and their status
22
+ sproutr restart --ami=one two three # Restart the specified instance(s), requires --ami=
23
+ sproutr snapshot --ami=one two three # Create snapshot
24
+ sproutr start --ami=one two three # Start the specified instance(s), requires --ami=
25
+ sproutr stop --ami=one two three # Stop the specified instance(s), requires --ami=
26
+ sproutr terminate --ami=one two three # Terminate the specified instance, requires --ami=
27
+
28
+ ##Configuration
29
+
30
+ sproutr relies on the Swirl library, which needs to be passed your AWS credentials to do its magic. sproutr therefore requires that you provide a .swirlrc file in your home directory (~/) that contains:
31
+ ~/.swirl:
32
+ ---
33
+ :default:
34
+ :aws_access_key_id: my_access_key
35
+ :aws_secret_access_key: my_secret_key
36
+
37
+ ##Usage
38
+
39
+ You can use sproutr to manage your instances from the commandline. You should create an instance which will serve as your "sproutr" and be converted into an AMI.
40
+ Once you have tested this instance, create a snapshot of the instance, then use it by AMI-Id to launch new instances with their own individual configuration.
41
+
42
+ Here's a simple example from the command line. Begin by invoking the define task.
43
+
44
+ $ bin/sproutr define
45
+
46
+ sproutr's define task will walk you through the process of determining the name, instance size, starting AMI etc.
47
+ Key to the sproutr experience is the way handles two key features: Chef and Volumes:
48
+ sproutr builds the instance with knowledge of, and the ability to execute arbitrary chef cookbooks/recipes.
49
+ While defining an instance you're given the opportunity to specify additional packages, cookbooks and recipes to have installed
50
+ Currently only Debian (ubuntu, mint, etc.) based distributions are supported.
51
+ Additionally, be aware that any volumes you specify will be aggregated together using mdadm and lvm into one logical volume.
52
+
53
+ Once defined, you can launch the instance via
54
+
55
+ $ sproutr launch --definition=filename.json
56
+
57
+ You can monitor the instance's fabrication process via
58
+
59
+ $ sproutr list
60
+
61
+ The instance you created will boot, install your selected packages on top of the stock AMI you selected, then download and cook all the cookbooks and recipes you selected.
62
+
63
+ ##Inspiration and Thanks
64
+
65
+ sproutr is a rewrite and extension of the stem gem, a product (so far as I can tell) a gift of the Heroku development / operations team.
data/bin/sproutr ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.dirname(__FILE__) + "/../lib"
3
+ require 'sproutr'
4
+
5
+
6
+
data/lib/sproutr.rb ADDED
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.dirname(__FILE__)
3
+ require 'rubygems'
4
+ require 'swirl/aws'
5
+ require 'json'
6
+ require 'thor'
7
+ require 'ap'
8
+ require 'terminal-table/import'
9
+ require 'sproutr/utilities'
10
+ require 'sproutr/instance'
11
+ require 'sproutr/cloud'
12
+ require 'sproutr/ami'
13
+ require 'sproutr/definition'
14
+
15
+ class Sproutr < Thor
16
+ include Thor::Actions
17
+ map "-l" => :list
18
+
19
+ def initialize(*args)
20
+ super
21
+ @ec2 ||= Swirl::AWS.new :ec2, load_config
22
+ end
23
+
24
+ desc "clone", "Clone N number of running instance"
25
+ method_option :instance, :type => :array, :required => true
26
+ method_option :ami, :type => :string
27
+ method_option :start, :type => :boolean
28
+ method_option :tags, :type => :hash
29
+
30
+ def clone
31
+ options[:instance].each do |ami_to_clone|
32
+ if options[:ami] then
33
+ ami_id = options[:ami]
34
+ else
35
+ ami_id = @ec2.call("CreateImage", "InstanceId" => ami_to_clone, "Name" => "AMI-#{ami_to_clone}-#{Time.now.to_i}",
36
+ "Description" => "AMI created from #{ami_to_clone} at #{Time.now}", "NoReboot" => "true")["imageId"]
37
+ end
38
+ new_config = clone_ami_config(@ec2.call("DescribeInstances", "InstanceId" => ami_to_clone)["reservationSet"][0]["instancesSet"][0], ami_id)
39
+ until ami_done?(ami_id) do
40
+ say "Ami has not completed so this clone can not yet be started. Sleeping 30 seconds", :red
41
+ sleep 30
42
+ end
43
+ new_instance = invoke_launch(new_config)
44
+ say "Created and started #{new_instance}", :green
45
+ tag_instance(new_instance, options[:tags]) if options[:tags]
46
+ end
47
+ end
48
+
49
+ desc "create_ami", "Create an EBS Ami from a running or stopped instance"
50
+ method_option :ami, :type => :string, :required => true
51
+ method_option :name, :type => :string, :required => true
52
+ method_option :desc, :type => :string, :required => true
53
+
54
+ def create_ami
55
+ @ec2.call "CreateImage", "InstanceId" => options[:ami], "Name" => options[:name], "Description" => options[:desc], "NoReboot" => "true"
56
+ end
57
+
58
+ desc "define", "define a new instance"
59
+
60
+ def define
61
+ definition = Definition.new do |d|
62
+ d.name = demand "What do you want to call this definition (Required): "
63
+ d.size = demand "What size instance do you want (Required): "
64
+ d.ami = demand "What base AMI image would you like to use (Required): "
65
+ ami_info = Cloud.new.describe_image(d.ami)
66
+ break unless yes? "Is this the Image -- #{ami_info["imagesSet"][0]["name"]} -- you wish to build from?", :red
67
+ d.packages = ask "What additional packages do you want installed (Optional): ", :green
68
+ d.gems = ask "What gems do you want to install on this machine by default (Optional): ", :green
69
+ d.chef_cookbooks = ask "Please name the chef cookbooks you wish to automatically download (Optional): ", :green
70
+ d.chef_recipes = ask "Enter the recipes you wish chef to run after cookbooks are installed (Optional): ", :green
71
+ d.user_data = demand "Please copy/paste your Userdata.sh here now. Note, sproutr will automatically add in the
72
+ requisite code to download and install Cheff cookbooks, and aggregate additional volumes
73
+ into a singluar raid array (Required): "
74
+ d.tags = ask "Please enter the tags you'd like in name:value format (Optional, Note that the Name will automatically be specified): ", :green
75
+ d.volumes = ask "Please enter the number of additional volumes (Optional, Note that these additional volumes will be RAID/LVM enabled as 1 *logical* volume): ", :green
76
+ d.volume_size = ask "Please enter the size of each component volume in Gigabytes (Optional, Currently all volumes are identical in size): ", :green
77
+ d.availability_zone = demand "Please enter the availability zone you wish to instantiate this machine in: "
78
+ d.key_name = demand "Which key-pair would you like to use? use the key name: "
79
+ end
80
+ definition.save_to_file(definition.name+"_definition.json")
81
+ end
82
+
83
+ desc "delete_snapshot", "Deletes the given snapshot(s) use --snapshot="
84
+ method_option :snapshot, :type => :array, :required => true
85
+
86
+ def delete_snapshot
87
+ options[:snapshot].each do |snapshot_id|
88
+ result = @ec2.call "DeleteSnapshot", "SnapshotId" => snapshot_id
89
+ say result["return"], :green
90
+ end
91
+ end
92
+
93
+ desc "describe", "Describe a specific instance"
94
+ method_option :ami, :type => :array, :required => true
95
+
96
+ def describe
97
+ options[:ami].each do |ami|
98
+ ap @ec2.call("DescribeInstances", "InstanceId" => ami)["reservationSet"][0]["instancesSet"][0]
99
+ end
100
+ end
101
+
102
+ desc "launch", "launch an instance from the specified config directory"
103
+ method_option :config_file, :type => :string, :required => true
104
+
105
+ def launch
106
+ config = JSON.parse(File.new(options[:config_file]).read) if File.exists? options[:config_file]
107
+ throw "Failed to read config file, or config file does not specify an ImageId" unless config["ami"]
108
+ say invoke_launch(validate_launch_config(config)), :blue
109
+ end
110
+
111
+ desc "list", "list all the instances and ami's in your ec2 account"
112
+
113
+ def list
114
+ invoke :list_instances
115
+ invoke :list_amis
116
+ end
117
+
118
+ desc "list_amis", "list all the ami's in your ec2 account"
119
+
120
+ def list_amis
121
+ @ami_images = Cloud.new.get_images
122
+ ami_table = table do |a|
123
+ a.headings = 'Name', 'AMI Id', 'Type', 'State', 'Size', 'Architecture', 'Public?', 'Description'
124
+ @ami_images.each do |ami|
125
+ a << [ami.name, ami.imageId, ami.rootDeviceType, ami.imageState, ami.blockDeviceMapping[0]["ebs"]["volumeSize"],
126
+ ami.architecture, ami.isPublic, ami.description]
127
+ end
128
+ end
129
+ puts ami_table
130
+ end
131
+
132
+ desc "list_instances", "list all the instances in your ec2 account"
133
+
134
+ def list_instances
135
+ instances = Cloud.new.get_instances
136
+ instance_table = table do |i|
137
+ i.headings = 'Name', 'Instance Id', 'Status', 'ip Address', 'Instance Type', 'AMI Image', 'Availablity Zone', 'DNS Cname'
138
+ instances.each do |instance|
139
+ i << [((instance.tagSet.nil?) ? "Name Not Set" : instance.tagSet[0]["value"]),
140
+ instance.instanceId, instance.instanceState["name"], instance.ipAddress,
141
+ instance.instanceType, instance.imageId, instance.placement["availabilityZone"], instance.dnsName]
142
+ end
143
+ end
144
+ puts instance_table
145
+ end
146
+
147
+ desc "list_snapshots", "Lists all the snapshots available and their status"
148
+
149
+ def list_snapshots
150
+ snapshots = @ec2.call("DescribeSnapshots", "Owner" => "self")["snapshotSet"]
151
+ snapshots_table = table do |s|
152
+ s.headings = snapshots.first.keys
153
+ snapshots.each do |snap|
154
+ s << snap.values
155
+ end
156
+ end
157
+ puts snapshots_table
158
+ end
159
+
160
+ desc "restart", "Restart the specified instance(s), requires --ami="
161
+ method_option :ami, :type => :array, :required => true
162
+
163
+ def restart
164
+ options[:ami].each { |ami| Instance::call_on("RebootInstances", ami) }
165
+ end
166
+
167
+ desc "snapshot", "Create snapshot"
168
+ method_option :ami, :type => :array, :required => true
169
+ method_options :desc => "Snapshot created by sproutr"
170
+
171
+ def snapshot
172
+ options[:ami].each do |ami|
173
+ instance_volumes = @ec2.call("DescribeInstances", "InstanceId" => ami)["reservationSet"][0]["instancesSet"][0]["blockDeviceMapping"]
174
+ instance_volumes.each { |volume| ap @ec2.call("CreateSnapshot", "VolumeId" => volume["ebs"]["volumeId"], "Description" => options[:desc]) }
175
+ end
176
+ end
177
+
178
+
179
+ desc "start", "Start the specified instance(s), requires --ami="
180
+ method_option :ami, :type => :array, :required => true
181
+
182
+ def start
183
+ options[:ami].each { |ami| Instance::call_on("StartInstances", ami) }
184
+ end
185
+
186
+ desc "stop", "Stop the specified instance(s), requires --ami="
187
+ method_option :ami, :type => :array, :required => true
188
+
189
+ def stop
190
+ options[:ami].each { |ami| Instance::call_on("StopInstances", ami) if yes? "Do you really want to stop the #{instance} instance? ", :red }
191
+ end
192
+
193
+
194
+ desc "terminate", "Terminate the specified instance, requires --ami="
195
+ method_option :ami, :type => :array, :required => true
196
+
197
+ def terminate
198
+ options[:ami].each do |ami|
199
+ verify = ask "Do you really want to terminate the #{ami} instance? ", :red
200
+ Instance::call_on("TerminateInstances", ami) if verify.downcase == "y" || verify.downcase == "yes"
201
+ end
202
+ end
203
+
204
+ desc "destroy", "Alias to terminate"
205
+ method_option :ami, :type => :array, :required => true
206
+ alias :destroy :terminate
207
+
208
+
209
+ desc "list_snapshots", "Alias to list_snapshots"
210
+ alias :list_snapshot :list_snapshots
211
+
212
+ end
213
+ Sproutr.start
@@ -0,0 +1,12 @@
1
+ class Ami
2
+ def initialize(ami)
3
+ ami.keys.each do |key|
4
+ var = "@#{key}"
5
+ self.instance_variable_set(var.to_sym, ami[key])
6
+ end
7
+ end
8
+
9
+ def method_missing(sym, *args, &block)
10
+ self.instance_variable_get "@#{sym}".to_sym
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ require 'swirl/aws'
2
+ require 'sproutr/utilities'
3
+
4
+ class Cloud
5
+
6
+ class << self
7
+ attr_accessor :instances, :images
8
+ end
9
+
10
+ def initialize
11
+ @ec2 ||= Swirl::AWS.new :ec2, load_config
12
+ end
13
+
14
+ def get_instances
15
+ aws_instances = @ec2.call "DescribeInstances"
16
+ @instances = Array.new
17
+ aws_instances["reservationSet"].each { |reservation| @instances << Instance.new(reservation["instancesSet"][0]) }
18
+ @instances
19
+ end
20
+
21
+ def get_images
22
+ aws_images = @ec2.call "DescribeImages", "Owner" => "self"
23
+ images = aws_images["imagesSet"].select { |img| img["name"] } rescue nil
24
+ @images = Array.new
25
+ images.each { |image| @images << Ami.new(image) }
26
+ @images
27
+ end
28
+
29
+ def describe_image(ami)
30
+ @ec2.call "DescribeImages", "ImageId" => ami
31
+ end
32
+
33
+ end
@@ -0,0 +1,26 @@
1
+ require 'json'
2
+
3
+ class Definition
4
+ FILE_PREFIX = "~/Sprout_configs/machine_definitions/"
5
+ %x{mkdir -p #{FILE_PREFIX} } unless File.directory? FILE_PREFIX
6
+ attr_accessor :name, :gems, :user_data, :packages, :size, :chef_cookbooks, :chef_recipes, :ami, :tags, :volumes, :volume_size, :availability_zone, :key_name, :chef_install
7
+
8
+ def initialize
9
+ yield self if block_given?
10
+ end
11
+
12
+ def load_from_file(filename)
13
+ definition_file = File.open(FILE_PREFIX + filename, 'r')
14
+ JSON.parse(definition_file.read).each do |element, value|
15
+ instance_variable_set(element, value)
16
+ end
17
+ end
18
+
19
+ def save_to_file(filename)
20
+ hash_of_definition = Hash.new
21
+ instance_variables.each do |var|
22
+ hash_of_definition[var.to_s.gsub("@","")] = instance_variable_get(var)
23
+ end
24
+ File.open(FILE_PREFIX + filename, 'w') {|f| f.write(hash_of_definition.to_json) }
25
+ end
26
+ end
@@ -0,0 +1,18 @@
1
+ class Instance
2
+
3
+ def initialize(instance)
4
+ instance.keys.each do |key|
5
+ var = "@#{key}"
6
+
7
+ self.instance_variable_set(var.to_sym, instance[key])
8
+ end
9
+ end
10
+
11
+ def self.call_on(action, ami)
12
+ @ec2.call action, "InstanceId" => ami
13
+ end
14
+
15
+ def method_missing(sym, *args, &block)
16
+ self.instance_variable_get "@#{sym}".to_sym
17
+ end
18
+ end
@@ -0,0 +1,108 @@
1
+ def load_config
2
+ account = "default"
3
+ swirl_config = "#{ENV["HOME"]}/.swirl"
4
+ account = account.to_sym
5
+
6
+ if File.exists?(swirl_config)
7
+ data = YAML.load_file(swirl_config)
8
+ else
9
+ abort("You are required to write a proper YAML config file called .swirl file in your home directory")
10
+ end
11
+
12
+ if data.key?(account)
13
+ data[account]
14
+ else
15
+ abort("I don't see the account you're looking for")
16
+ end
17
+ end
18
+
19
+ def invoke_launch(config)
20
+ response = @ec2.call "RunInstances", config
21
+ response["instancesSet"].first["instanceId"]
22
+ end
23
+
24
+ def validate_launch_config(config)
25
+ instance_options = {
26
+ "SecurityGroup.#" => config["groups"] || [],
27
+ "MinCount" => "1",
28
+ "MaxCount" => "1",
29
+ "KeyName" => config["key_name"] || "Production",
30
+ "InstanceType" => config["instance_type"] || "m1.small",
31
+ "ImageId" => config["ami"]
32
+ }
33
+
34
+ if config["volumes"]
35
+ devices = []
36
+ sizes = []
37
+ config["volumes"].each do |v|
38
+ puts "Adding a volume of #{v["size"]} to be mounted at #{v["device"]}."
39
+ devices << v["device"]
40
+ sizes << v["size"].to_s
41
+ end
42
+
43
+ instance_options.merge! "BlockDeviceMapping.#.Ebs.VolumeSize" => sizes, "BlockDeviceMapping.#.DeviceName" => devices
44
+ end
45
+
46
+ end
47
+
48
+ def clone_ami_config(config, new_ami)
49
+ throw "No config provided" unless config
50
+
51
+ instance_options = {
52
+ "SecurityGroup.#" => config["groups"] || [],
53
+ "MinCount" => "1",
54
+ "MaxCount" => "1",
55
+ "KeyName" => config["key_name"] || "Production",
56
+ "InstanceType" => config["instance_type"] || "m1.small",
57
+ "ImageId" => new_ami
58
+ }
59
+
60
+ devices = []
61
+ sizes = []
62
+ config["blockDeviceMapping"].each do |block_device|
63
+ volume = @ec2.call "DescribeVolumes", "VolumeId" => block_device["ebs"]["volumeId"]
64
+ next if volume["volumeSet"][0]["attachmentSet"][0]["device"] == "/dev/sda1"
65
+ say "Adding volume #{volume["volumeSet"][0]["attachmentSet"][0]["device"]} with a size of #{volume["volumeSet"][0]["size"]}", :green
66
+ devices << volume["volumeSet"][0]["attachmentSet"][0]["device"]
67
+ sizes << volume["volumeSet"][0]["size"]
68
+ end
69
+
70
+ instance_options.merge! "BlockDeviceMapping.#.Ebs.VolumeSize" => sizes, "BlockDeviceMapping.#.DeviceName" => devices
71
+
72
+ end
73
+
74
+ def ami_done?(ami)
75
+ ami_list = @ec2.call "DescribeImages", "Owner" => "self"
76
+ images = ami_list["imagesSet"].select { |img| img["name"] } rescue nil
77
+ images = images.map { |image| image["imageId"] if image["imageState"] == "available" }
78
+ (images.include? ami) ? true : false
79
+ end
80
+
81
+ def parse_tags(tags)
82
+ if tags && !tags.empty?
83
+ if tags.is_a? Hash
84
+ {"Tag.#.Key" => tags.keys.map(&:to_s),
85
+ "Tag.#.Value" => tags.values.map(&:to_s)}
86
+ elsif tags.is_a? Array
87
+ {
88
+ "Tag.#.Key" => tags.map(&:to_s),
89
+ "Tag.#.Value" => (1..tags.size).map { '' }
90
+ }
91
+ else
92
+ {"Tag.1.Key" => tags.to_s, "Tag.1.Value" => ''}
93
+ end
94
+ end
95
+ end
96
+
97
+ def tag_instance(instance_id, tags)
98
+ instance_id = [instance_id] unless instance_id.is_a? Array
99
+ @ec2.call("CreateTags", parse_tags(tags).merge("ResourceId" => instance_id))
100
+ end
101
+
102
+ def demand(question)
103
+ answer = nil
104
+ while answer.nil? or answer.strip == ""
105
+ answer = ask question, :green
106
+ end
107
+ answer
108
+ end
metadata ADDED
@@ -0,0 +1,185 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sproutr
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.2
6
+ platform: ruby
7
+ authors:
8
+ - Kevin Poorman
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-24 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: swirl
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ~>
23
+ - !ruby/object:Gem::Version
24
+ version: 1.7.5
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: thor
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ~>
34
+ - !ruby/object:Gem::Version
35
+ version: 0.14.6
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: terminal-table
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ version: 1.4.2
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: awesome_print
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ~>
56
+ - !ruby/object:Gem::Version
57
+ version: 0.3.2
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ - !ruby/object:Gem::Dependency
61
+ name: json
62
+ prerelease: false
63
+ requirement: &id005 !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ type: :runtime
70
+ version_requirements: *id005
71
+ - !ruby/object:Gem::Dependency
72
+ name: rspec
73
+ prerelease: false
74
+ requirement: &id006 !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ~>
78
+ - !ruby/object:Gem::Version
79
+ version: 2.5.0
80
+ type: :development
81
+ version_requirements: *id006
82
+ - !ruby/object:Gem::Dependency
83
+ name: rspec-core
84
+ prerelease: false
85
+ requirement: &id007 !ruby/object:Gem::Requirement
86
+ none: false
87
+ requirements:
88
+ - - ~>
89
+ - !ruby/object:Gem::Version
90
+ version: 2.5.0
91
+ type: :development
92
+ version_requirements: *id007
93
+ - !ruby/object:Gem::Dependency
94
+ name: rspec-expectations
95
+ prerelease: false
96
+ requirement: &id008 !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 2.5.0
102
+ type: :development
103
+ version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: rspec-mocks
106
+ prerelease: false
107
+ requirement: &id009 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ version: 2.5.0
113
+ type: :development
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: vcr
117
+ prerelease: false
118
+ requirement: &id010 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ~>
122
+ - !ruby/object:Gem::Version
123
+ version: 1.6.0
124
+ type: :development
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: webmock
128
+ prerelease: false
129
+ requirement: &id011 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ~>
133
+ - !ruby/object:Gem::Version
134
+ version: 1.6.2
135
+ type: :development
136
+ version_requirements: *id011
137
+ description: Elegant, interactive EC2 Management
138
+ email:
139
+ - kjp@brightleafsoftware.com
140
+ executables:
141
+ - sproutr
142
+ extensions: []
143
+
144
+ extra_rdoc_files: []
145
+
146
+ files:
147
+ - LICENSE
148
+ - README.md
149
+ - lib/sproutr/ami.rb
150
+ - lib/sproutr/cloud.rb
151
+ - lib/sproutr/definition.rb
152
+ - lib/sproutr/instance.rb
153
+ - lib/sproutr/utilities.rb
154
+ - lib/sproutr.rb
155
+ - bin/sproutr
156
+ has_rdoc: true
157
+ homepage: https://github.com/noeticpenguin/sproutr
158
+ licenses: []
159
+
160
+ post_install_message:
161
+ rdoc_options: []
162
+
163
+ require_paths:
164
+ - lib
165
+ required_ruby_version: !ruby/object:Gem::Requirement
166
+ none: false
167
+ requirements:
168
+ - - ">="
169
+ - !ruby/object:Gem::Version
170
+ version: "0"
171
+ required_rubygems_version: !ruby/object:Gem::Requirement
172
+ none: false
173
+ requirements:
174
+ - - ">="
175
+ - !ruby/object:Gem::Version
176
+ version: "0"
177
+ requirements: []
178
+
179
+ rubyforge_project:
180
+ rubygems_version: 1.6.2
181
+ signing_key:
182
+ specification_version: 2
183
+ summary: An Ec2 management system designed to help you quickly define a new instance, create N clones of said instance and start/stop them
184
+ test_files: []
185
+