cloudpatrol 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = Cloudpatrol
2
+
3
+ :include:cloudpatrol.rdoc
data/bin/cloudpatrol ADDED
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env ruby
2
+ require 'gli'
3
+ require 'json'
4
+
5
+ begin # Remove this begin/rescue before distributing the app
6
+ require 'cloudpatrol'
7
+ rescue LoadError
8
+ exit 64
9
+ end
10
+
11
+ include GLI::App
12
+
13
+ program_desc "Helps you keep Amazon cloud clean"
14
+
15
+ version Cloudpatrol::VERSION
16
+
17
+ result = []
18
+
19
+ desc 'Specify AWS Access Key ID'
20
+ arg_name 'string'
21
+ flag :aws_access_key_id
22
+
23
+ desc 'Specify AWS Secret Access Key'
24
+ arg_name 'string'
25
+ flag :aws_secret_access_key
26
+
27
+ desc 'Specify region'
28
+ arg_name 'string'
29
+ flag :region
30
+
31
+ desc 'Specify config file'
32
+ flag :config_file
33
+
34
+ desc 'Display performance log of the task'
35
+ default_value false
36
+ switch [ :log ]
37
+
38
+ desc 'AWS Identity and Access Management'
39
+ command "iam" do |c|
40
+ c.desc "Deletes all IAM users that do not have MFA enabled. Usernames that begin with underscore (_) are kept."
41
+ c.command "clean-users" do |subc|
42
+ subc.action do |global_options,options,args|
43
+ result = Cloudpatrol.perform($aws_credentials, nil, :IAM, :clean_users)
44
+ puts "Deleted #{result[:task].count} users" if result[:task]
45
+ end
46
+ end
47
+ end
48
+
49
+ desc 'AWS Elastic Compute Cloud'
50
+ command "ec2" do |c|
51
+ c.desc "specifies maximum age"
52
+ c.default_value 5
53
+ c.arg_name "number"
54
+ c.flag [ :d, :days ]
55
+
56
+ # This action needs a feature for constant monitoring of creation time.
57
+ # c.desc "Deletes AWS EC2 key pairs that have existed for configurable number of days"
58
+ # c.command "clean-keypairs" do |subc|
59
+ # subc.action do |global_options,options,args|
60
+ # puts "doesn't work"
61
+ # end
62
+ # end
63
+
64
+ c.desc "Deletes AWS EC2 Instances that have existed for configurable number of days"
65
+ c.command "clean-instances" do |subc|
66
+ subc.action do |global_options,options,args|
67
+ days = options[:days] or $config["ec2_instance_age"] or raise "Specify max age for an instance with -d option or --config_file"
68
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :clean_instances, days.to_i)
69
+ puts "Deleted #{result[:task].count} instances" if result[:task]
70
+ end
71
+ end
72
+
73
+ c.desc "Deletes Security Groups that are no longer being used by AWS EC2 Instances (notice that Security Groups featured in any inbound permission rules will not be deleted)"
74
+ c.command "clean-security-groups" do |subc|
75
+ subc.action do |global_options,options,args|
76
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :clean_security_groups)
77
+ puts "Deleted #{result[:task].count} security groups" if result[:task]
78
+ end
79
+ end
80
+
81
+ c.desc "Deletes ports assigned to the default security group"
82
+ c.command "clean-ports-in-default" do |subc|
83
+ subc.action do |global_options,options,args|
84
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :clean_ports_in_default)
85
+ puts "Deleted #{result[:task].count} rules" if result[:task]
86
+ end
87
+ end
88
+
89
+ c.desc "Starts AWS EC2 Instances"
90
+ c.command "start-instances" do |subc|
91
+ subc.action do |global_options,options,args|
92
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :start_instances)
93
+ puts "Started #{result[:task].count} instances" if result[:task]
94
+ end
95
+ end
96
+
97
+ c.desc "Stops AWS EC2 Instances"
98
+ c.command "stop-instances" do |subc|
99
+ subc.action do |global_options,options,args|
100
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :stop_instances)
101
+ puts "Stopped #{result[:task].count} instances" if result[:task]
102
+ end
103
+ end
104
+
105
+ c.desc "Releases EC2 Elastic IPs not associated with any instance"
106
+ c.command "clean-elastic-ips" do |subc|
107
+ subc.action do |global_options, options, args|
108
+ result = Cloudpatrol.perform($aws_credentials, nil, :EC2, :clean_elastic_ips)
109
+ puts "Deleted #{result[:task].count} Elastic IPs" if result[:task]
110
+ end
111
+ end
112
+ end
113
+
114
+ desc 'AWS OpsWorks'
115
+ command "opsworks" do |c|
116
+ c.desc "specifies maximum age"
117
+ c.default_value 5
118
+ c.arg_name "number"
119
+ c.flag [ :d, :days ]
120
+
121
+ c.desc "Deletes AWS OpsWorks apps that have existed for configurable number of days"
122
+ c.command "clean-apps" do |subc|
123
+ subc.action do |global_options,options,args|
124
+ days = options[:days] or $config["opsworks_app_age"] or raise "Specify max age for an app with -d option or --config_file"
125
+ result = Cloudpatrol.perform($aws_credentials, nil, :OpsWorks, :clean_apps, days.to_i)
126
+ puts "Deleted #{result[:task].count} OpsWorks apps" if result[:task]
127
+ end
128
+ end
129
+
130
+ c.desc "Deletes AWS OpsWorks instances that have existed for configurable number of days"
131
+ c.command "clean-instances" do |subc|
132
+ subc.action do |global_options,options,args|
133
+ days = options[:days] or $config["opsworks_instance_age"] or raise "Specify max age for an instance with -d option or --config_file"
134
+ result = Cloudpatrol.perform($aws_credentials, nil, :OpsWorks, :clean_instances, days.to_i)
135
+ puts "Deleted #{result[:task].count} OpsWorks instances" if result[:task]
136
+ end
137
+ end
138
+
139
+ c.desc "Deletes AWS OpsWorks layers that have existed for configurable number of days"
140
+ c.command "clean-layers" do |subc|
141
+ subc.action do |global_options,options,args|
142
+ days = options[:days] or $config["opsworks_layer_age"] or raise "Specify max age for a layer with -d option or --config_file"
143
+ result = Cloudpatrol.perform($aws_credentials, nil, :OpsWorks, :clean_layers, days.to_i)
144
+ puts "Deleted #{result[:task].count} OpsWorks layers" if result[:task]
145
+ end
146
+ end
147
+
148
+ c.desc "Deletes AWS OpsWorks stacks that have existed for configurable number of days"
149
+ c.command "clean-stacks" do |subc|
150
+ subc.action do |global_options,options,args|
151
+ days = options[:days] or $config["opsworks_stack_age"] or raise "Specify max age for a stack with -d option or --config_file"
152
+ result = Cloudpatrol.perform($aws_credentials, nil, :OpsWorks, :clean_stacks, days.to_i)
153
+ puts "Deleted #{result[:task].count} OpsWorks stacks" if result[:task]
154
+ end
155
+ end
156
+ end
157
+
158
+ desc 'AWS CloudFormation'
159
+ command "cloudformation" do |c|
160
+ c.desc "specifies maximum age"
161
+ c.default_value 5
162
+ c.arg_name "number"
163
+ c.flag [ :d, :days ]
164
+
165
+ c.desc "Deletes AWS CloudFormation stacks that have existed for configurable number of days"
166
+ c.command "clean-stacks" do |subc|
167
+ subc.action do |global_options,options,args|
168
+ days = options[:days] or $config["cloudformation_stack_age"] or raise "Specify max age for a stack with -d option or --config_file"
169
+ result = Cloudpatrol.perform($aws_credentials, nil, :CloudFormation, :clean_stacks, days.to_i)
170
+ puts "Deleted #{result[:task].count} CloudFormation stacks" if result[:task]
171
+ end
172
+ end
173
+ end
174
+
175
+ desc 'CloudPatrol log'
176
+ command "log" do |c|
177
+ c.desc "overrides default table name"
178
+ c.default_value "cloudpatrol-log"
179
+ c.flag [ :t, :table ]
180
+
181
+ c.action do |global_options,options,args|
182
+ table_name = options[:table] || "cloudpatrol-log"
183
+ result = Cloudpatrol.get_log($aws_credentials, table_name)
184
+ if result[:success]
185
+ puts result[:log]
186
+ else
187
+ puts result[:error]
188
+ end
189
+ end
190
+ end
191
+
192
+ pre do |global, command, options, args|
193
+ $aws_credentials = {}
194
+ $config = {}
195
+
196
+ if global[:config_file]
197
+ if File.readable?(global[:config_file])
198
+ $config = JSON.parse(File.read(global[:config_file])) rescue raise(ArgumentError, "invalid config file")
199
+ else
200
+ raise ArgumentError, "--config_file: no such file"
201
+ end
202
+ end
203
+
204
+ $aws_credentials[:access_key_id] = global[:aws_access_key_id] || $config["aws_access_key_id"] || raise(ArgumentError, "--aws_access_key_id or --config_file is required")
205
+ $aws_credentials[:secret_access_key] = global[:aws_secret_access_key] || $config["aws_secret_access_key"] || raise(ArgumentError, "--aws_secret_access_key or --config_file is required")
206
+ $aws_credentials[:region] = global[:region] or "us-east-1"
207
+ true
208
+ end
209
+
210
+ post do |global, command, options, args|
211
+ puts result[:formatted] if global[:log] and command.name != "log"
212
+ end
213
+
214
+ on_error do |exception|
215
+ # Error logic here
216
+ # return false to skip default error handling
217
+ true
218
+ end
219
+
220
+ exit run(ARGV)
data/cloudpatrol.rdoc ADDED
File without changes
@@ -0,0 +1,68 @@
1
+ require 'cloudpatrol/task.rb'
2
+ require 'cloudpatrol/version.rb'
3
+ require 'core_ext/integer.rb'
4
+
5
+ module Cloudpatrol
6
+ def self.perform aws_credentials, table_name, klass, method, *args
7
+ response = {}
8
+ table_name ||= "cloudpatrol-log"
9
+
10
+ response[:task] = begin
11
+ response[:formatted] = Task.const_get(klass).new(aws_credentials).send(method, *args)
12
+ rescue AWS::Errors::Base => e
13
+ response[:formatted] = "AWS error: #{e}"
14
+ false
15
+ rescue
16
+ response[:formatted] = "Unknown error"
17
+ false
18
+ end
19
+
20
+ response[:log] = begin
21
+ Task::DynamoDB.new(aws_credentials, table_name).log({ class: klass, method: method, args: args }, response[:formatted])
22
+ rescue
23
+ puts "Failed to write log to DynamoDB"
24
+ false
25
+ end
26
+
27
+ response
28
+ end
29
+
30
+ def self.get_log aws_credentials, table_name = "cloudpatrol-log"
31
+ gate = ::AWS::DynamoDB.new(aws_credentials)
32
+ response = {}
33
+ table = gate.tables[table_name]
34
+ if table.exists? and table.status == :active
35
+ table.load_schema
36
+ response[:log] = []
37
+ table.items.each do |item|
38
+ response[:log] << item.attributes.to_hash
39
+ end
40
+ response[:log].map! do |item|
41
+ {
42
+ time: Time.parse(item["time"]),
43
+ action: item["action"],
44
+ response: item["response"]
45
+ }
46
+ end
47
+ response[:log].sort!{ |x,y| x[:time] <=> y[:time] }
48
+ else
49
+ response[:success] = false
50
+ response[:error] = "Table doesn't exist"
51
+ end
52
+ rescue AWS::Errors::Base => e
53
+ response[:success] = false
54
+ response[:error] = "AWS error: #{e}"
55
+ rescue
56
+ response[:success] = false
57
+ response[:error] = "Unknown error"
58
+ else
59
+ response[:success] = if response[:log].empty?
60
+ response[:error] = "Log is empty"
61
+ false
62
+ else
63
+ true
64
+ end
65
+ ensure
66
+ return response
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ require 'cloudpatrol/task/cloud_formation.rb'
2
+ require 'cloudpatrol/task/dynamo_db.rb'
3
+ require 'cloudpatrol/task/ec2.rb'
4
+ require 'cloudpatrol/task/iam.rb'
5
+ require 'cloudpatrol/task/ops_works.rb'
6
+
7
+ module Cloudpatrol
8
+ module Task
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ require 'aws'
2
+
3
+ module Cloudpatrol
4
+ module Task
5
+ class CloudFormation
6
+ def initialize cred
7
+ @gate = ::AWS::CloudFormation.new(cred)
8
+ end
9
+
10
+ def clean_stacks allowed_age
11
+ deleted = []
12
+ @gate.stacks.each do |stack|
13
+ if (Time.now - stack.creation_time).to_i > allowed_age.days
14
+ deleted << stack.inspect
15
+ stack.delete
16
+ end
17
+ end
18
+ deleted
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ require 'securerandom'
2
+ require 'aws'
3
+
4
+ module Cloudpatrol
5
+ module Task
6
+ class DynamoDB
7
+ def initialize cred, table_name
8
+ @gate = ::AWS::DynamoDB.new(cred)
9
+ @table = @gate.tables[table_name]
10
+
11
+ unless @table.exists?
12
+ puts "Creating DynamoDB table \"#{table_name}\", wait a while..."
13
+ @table = @gate.tables.create(table_name, 1, 1)
14
+ sleep 1 while @table.status == :creating
15
+ puts "Table created"
16
+ end
17
+ rescue
18
+ @table = nil
19
+ end
20
+
21
+ def log action, response
22
+ raise unless @table
23
+
24
+ if @table.status == :active
25
+ item = @table.items.create(
26
+ id: SecureRandom.uuid,
27
+ action: action.to_s,
28
+ response: response.to_s,
29
+ time: Time.now.to_s
30
+ )
31
+ else
32
+ nil
33
+ end
34
+ rescue
35
+ false
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,81 @@
1
+ require 'aws'
2
+
3
+ module Cloudpatrol
4
+ module Task
5
+ class EC2
6
+ def initialize cred
7
+ @gate = ::AWS::EC2.new(cred)
8
+ end
9
+
10
+ def start_instances
11
+ result = []
12
+ @gate.instances.each do |instance|
13
+ result << instance.inspect
14
+ instance.start
15
+ end
16
+ result
17
+ end
18
+
19
+ def stop_instances
20
+ result = []
21
+ @gate.instances.each do |instance|
22
+ result << instance.inspect
23
+ instance.stop
24
+ end
25
+ result
26
+ end
27
+
28
+ def clean_instances allowed_age
29
+ deleted = []
30
+ @gate.instances.each do |instance|
31
+ if (Time.now - instance.launch_time).to_i > allowed_age.days and instance.status != :terminated
32
+ deleted << instance.inspect
33
+ instance.delete
34
+ end
35
+ end
36
+ deleted
37
+ end
38
+
39
+ def clean_security_groups
40
+ deleted = []
41
+ protected_groups = []
42
+ @gate.security_groups.each do |sg|
43
+ sg.ip_permissions.each do |perm|
44
+ perm.groups.each do |dependent_sg|
45
+ protected_groups << dependent_sg
46
+ end
47
+ end
48
+ end
49
+ @gate.security_groups.each do |sg|
50
+ if !protected_groups.include?(sg) and sg.exists? and sg.instances.count == 0 and sg.name != "default"
51
+ deleted << sg.inspect
52
+ sg.delete
53
+ end
54
+ end
55
+ deleted
56
+ end
57
+
58
+ def clean_ports_in_default
59
+ deleted = []
60
+ @gate.security_groups.filter("group-name", "default").each do |sg|
61
+ sg.ingress_ip_permissions.each do |perm|
62
+ deleted << { port_range: perm.port_range }
63
+ perm.revoke
64
+ end
65
+ end
66
+ deleted
67
+ end
68
+
69
+ def clean_elastic_ips
70
+ deleted = []
71
+ @gate.elastic_ips.each do |ip|
72
+ unless ip.instance
73
+ deleted << ip.inspect
74
+ ip.release
75
+ end
76
+ end
77
+ deleted
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,22 @@
1
+ require 'aws'
2
+
3
+ module Cloudpatrol
4
+ module Task
5
+ class IAM
6
+ def initialize cred
7
+ @gate = ::AWS::IAM.new(cred)
8
+ end
9
+
10
+ def clean_users
11
+ deleted = []
12
+ @gate.users.each do |user|
13
+ unless user.name =~ /^_/ or user.mfa_devices.count > 0
14
+ deleted << user.inspect
15
+ user.delete!
16
+ end
17
+ end
18
+ deleted
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,61 @@
1
+ require 'aws'
2
+
3
+ module Cloudpatrol
4
+ module Task
5
+ class OpsWorks
6
+ def initialize cred
7
+ @gate = ::AWS::OpsWorks::Client.new(cred)
8
+ end
9
+
10
+ def clean_apps allowed_age
11
+ deleted = []
12
+ @gate.describe_stacks[:stacks].each do |stack|
13
+ @gate.describe_apps(stack_id: stack[:stack_id])[:apps].each do |app|
14
+ if (Time.now - Time.parse(app[:created_at])).to_i > allowed_age.days
15
+ deleted << app
16
+ @gate.delete_app app_id: app[:app_id]
17
+ end
18
+ end
19
+ end
20
+ deleted
21
+ end
22
+
23
+ def clean_instances allowed_age
24
+ deleted = []
25
+ @gate.describe_stacks[:stacks].each do |stack|
26
+ @gate.describe_instances(stack_id: stack[:stack_id])[:instances].each do |instance|
27
+ if (Time.now - Time.parse(instance[:created_at])).to_i > allowed_age.days
28
+ deleted << instance
29
+ @gate.delete_instance instance_id: instance[:instance_id]
30
+ end
31
+ end
32
+ end
33
+ deleted
34
+ end
35
+
36
+ def clean_layers allowed_age
37
+ deleted = []
38
+ @gate.describe_stacks[:stacks].each do |stack|
39
+ @gate.describe_layers(stack_id: stack[:stack_id])[:layers].each do |layer|
40
+ if (Time.now - Time.parse(layer[:created_at])).to_i > allowed_age.days
41
+ deleted << layer
42
+ @gate.delete_layer layer_id: layer[:layer_id]
43
+ end
44
+ end
45
+ end
46
+ deleted
47
+ end
48
+
49
+ def clean_stacks allowed_age
50
+ deleted = []
51
+ @gate.describe_stacks[:stacks].each do |stack|
52
+ if (Time.now - Time.parse(stack[:created_at])).to_i > allowed_age.days
53
+ deleted << stack
54
+ @gate.delete_stack stack_id: stack[:stack_id]
55
+ end
56
+ end
57
+ deleted
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Cloudpatrol
2
+ VERSION = '0.1.3'
3
+ end
@@ -0,0 +1,5 @@
1
+ class Integer
2
+ def days
3
+ self * 24 * 60 * 60
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cloudpatrol
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Stelligent
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-15 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'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rdoc
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: aruba
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: gli
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 2.6.0
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: 2.6.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: json
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: aws-sdk
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - '='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.11.0
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - '='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.11.0
110
+ description:
111
+ email: developers@stelligent.com
112
+ executables:
113
+ - cloudpatrol
114
+ extensions: []
115
+ extra_rdoc_files:
116
+ - README.rdoc
117
+ - cloudpatrol.rdoc
118
+ files:
119
+ - bin/cloudpatrol
120
+ - lib/cloudpatrol/task/cloud_formation.rb
121
+ - lib/cloudpatrol/task/dynamo_db.rb
122
+ - lib/cloudpatrol/task/ec2.rb
123
+ - lib/cloudpatrol/task/iam.rb
124
+ - lib/cloudpatrol/task/ops_works.rb
125
+ - lib/cloudpatrol/task.rb
126
+ - lib/cloudpatrol/version.rb
127
+ - lib/core_ext/integer.rb
128
+ - lib/cloudpatrol.rb
129
+ - README.rdoc
130
+ - cloudpatrol.rdoc
131
+ homepage: http://stelligent.com
132
+ licenses: []
133
+ post_install_message:
134
+ rdoc_options:
135
+ - --title
136
+ - cloudpatrol
137
+ - --main
138
+ - README.rdoc
139
+ - -ri
140
+ require_paths:
141
+ - lib
142
+ - lib
143
+ required_ruby_version: !ruby/object:Gem::Requirement
144
+ none: false
145
+ requirements:
146
+ - - ! '>='
147
+ - !ruby/object:Gem::Version
148
+ version: 1.9.3
149
+ required_rubygems_version: !ruby/object:Gem::Requirement
150
+ none: false
151
+ requirements:
152
+ - - ! '>='
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ requirements: []
156
+ rubyforge_project:
157
+ rubygems_version: 1.8.23
158
+ signing_key:
159
+ specification_version: 3
160
+ summary: Command-line tool that helps you keep Amazon cloud clean
161
+ test_files: []