clouds 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.md +97 -0
  2. data/bin/clouds +162 -0
  3. data/clouds.rdoc +5 -0
  4. data/lib/clouds/version.rb +3 -0
  5. data/lib/clouds.rb +227 -0
  6. metadata +188 -0
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # clouds
2
+
3
+ This is a simple tool that aims to ease the handling of [AWS CloudFormation](https://aws.amazon.com/cloudformation/) stacks, following the [infrastructure as code](http://sdarchitect.wordpress.com/2012/12/13/infrastructure-as-code/) philosophy in ways that are not possible with the AWS console. Even though you can achieve pretty much the same using the AWS command line tools, `clouds` aims to be much easier to use, cleaner and more specialized in handling CloudFormation stacks as code.
4
+
5
+ ## Features
6
+ - Upload templates and parameters from the current directory into AWS with a single intuitive command. They need to be located in stacks/stack_name/{template.json,parameters.yaml}
7
+ - List existing stacks defined in AWS and also those only defined locally
8
+ - Dump one or all of the stacks from your currently selected AWS account into the current directory (preferably stored in a version control system) under the 'stacks' directory, including both the template JSON code and the parameters that were used for launching it, which will then be saved in a human-friendly YAML file.
9
+ - Perform updates on the AWS stacks once you modified the dumped data (template or parameters)
10
+ - Validate templates for JSON correctness when uploading or updating a stack. The error messages should help you debug syntax errors.
11
+ - Clone existing stacks to create new ones. It defaults to local clone, which then needs another update call to get in effect. This is an easy way to migrate infrastructure code and later to promote changes between environments, and can also be used easily with projects running in different AWS accounts, especially if using a simple AWS profile switcher shell alias like implemented on oh-my-zsh.
12
+ - Perform cost calculations when creating a new stack.
13
+ - Delete existing stacks with a simple command
14
+ - For authentication it can use AWS account credentials defined using either the AWS_ACCESS_KEY_ID&AWS_SECRET_ACCESS_KEY combination, or the AWS_DEFAULT_PROFILE environment variables
15
+ - Able to use temporary credentials, if the AWS_SECURITY_TOKEN variable is defined in the environment.
16
+
17
+ ## Installation
18
+
19
+ gem install clouds
20
+
21
+ or using a [bundler](http://bundler.io/) Gemfile:
22
+
23
+ gem 'clouds'
24
+
25
+ Note: On systems running Ruby 1.8.x such as RHEL5 and clones, you need to be aware of the incompatibility between the more recent versions of `nokogiri`(a dependency of the `aws-sdk`, that `clouds` indirectly depends on) and Ruby 1.8. This is pretty well-documented [here](http://ruby.awsblog.com/post/Tx2T9MFQJK7U74N/AWS-SDK-for-Ruby-and-Nokogiri). **You will need to manually install the latest compatible version of nokogiri and the AWS SDK for Ruby as documented on that page.**
26
+
27
+ ## Running
28
+ Execute this in your shell:
29
+
30
+ clouds -h
31
+
32
+ In order to do real stuff you need a profile to be defined in .aws/config for the aws command line tool, and to have it referenced by the AWS_DEFAULT_PROFILE environment variable. The AWS_ACCESS_KEY_ID&AWS_SECRET_ACCESS_KEY combination are also supported. You can also use temporary credentials, if the AWS_SECURITY_TOKEN variable is properly defined in the environment when using the temporary access key and secret.
33
+
34
+ export AWS_DEFAULT_PROFILE=my_aws_profile
35
+ or
36
+
37
+ export AWS_ACCESS_KEY_ID=my_key_id
38
+ export AWS_SECRET_ACCESS_KEY=my_key_secret
39
+
40
+ The oh-my-zsh users can also use the `asp` command implemented in the [AWS zsh plugin](https://github.com/robbyrussell/oh-my-zsh/wiki/Plugins#aws), which makes it very easy to switch between different profiles defined in .aws/config.
41
+
42
+ ## Use cases
43
+
44
+ Dump all the stacks from your account into the current directory
45
+
46
+ clouds dump --all
47
+
48
+ Once you edit a stack source code, this command would update it on AWS
49
+
50
+ clouds update stack_name
51
+
52
+ Clone a stack (locally only, so you can perform some changes)
53
+
54
+ clouds clone stack new_stack
55
+
56
+ Upload the cloned stack to AWS CloudFormation
57
+
58
+ clouds update new_stack -c
59
+
60
+ Delete the new stack (needs --force)
61
+
62
+ clouds delete new_stack
63
+
64
+
65
+ ## Development
66
+
67
+ ### Build requirements
68
+ - Ruby, rubygems and rake (some might already be there on Ruby > 1.9)
69
+ - Development headers for libxml2 and libxslt (-devel packages)
70
+ - Bundler
71
+
72
+ gem install bundler
73
+
74
+ ### Install dependencies
75
+ Only in case you didn't do it before
76
+
77
+ bundle install
78
+
79
+ ### Running for development
80
+
81
+ bundle exec bin/clouds
82
+
83
+ ### Updating the installed gem
84
+
85
+ rake repackage
86
+ sudo gem install pkg/clouds-0.1.0.gem
87
+
88
+ ### Build instructions
89
+
90
+ Simply run (once you have the stated requirements satisfied)
91
+
92
+ rake package
93
+
94
+ ### Installing your own gem
95
+
96
+ sudo gem install pkg/clouds-0.1.0.gem
97
+
data/bin/clouds ADDED
@@ -0,0 +1,162 @@
1
+ #!/usr/bin/env ruby
2
+ # For development, you need to use `bundle exec bin/clouds` to run this code
3
+ # At install-time, RubyGems will make sure the lib, etc. are in the load path
4
+
5
+ require 'gli'
6
+ require 'clouds'
7
+
8
+ include GLI::App
9
+
10
+ program_desc 'Tool that eases handling of AWS Cloudformation stacks as code'
11
+
12
+ version Clouds::VERSION
13
+
14
+ desc 'Dry run, nothing would be done NOT IMPLEMENTED YET'
15
+ switch [:d,:dry], :negatable => false
16
+
17
+
18
+ desc 'Force overwrites of existing data'
19
+ switch [:f,:force], :negatable => false
20
+
21
+ desc 'List CloudFormation stacks from your account'
22
+ arg_name 'Arguments to list'
23
+ command :list do |c|
24
+
25
+ c.desc 'List all the stacks available within your account'
26
+
27
+ c.action do |global_options,options,args|
28
+
29
+ raise 'This command takes no arguments.' unless args.empty?
30
+ list_stacks()
31
+ end
32
+ end
33
+
34
+ desc 'Dump CloudFormation stacks from your account into the current directory'
35
+ arg_name 'Arguments to dump'
36
+ command :dump do |c|
37
+
38
+ c.desc 'Dump all the stacks available within your account into the current directory'
39
+ c.switch [:a,:all], :negatable => false
40
+ c.desc 'Force overwrites of existing data'
41
+ c.switch [:f,:force], :negatable => false
42
+
43
+ c.action do |global_options,options,args|
44
+ if args.empty? && options[:all]
45
+ puts 'dumping all stacks'
46
+ dump_all_stacks(global_options[:force] || options[:force])
47
+ elsif args.length >= 1
48
+ puts "dumping the following stacks: #{args.inspect}"
49
+ dump_stacks(args, global_options[:force] || options[:force])
50
+ else # no arguments and --all flag
51
+ raise 'This command needs a list of stacks, or the --all flag in order to dump all the stacks.'
52
+ end
53
+ end
54
+ end
55
+
56
+ desc 'Edit a CloudFormation stack locally'
57
+ arg_name 'Arguments to edit'
58
+ command :edit do |c|
59
+
60
+ c.desc 'Force overwrites of existing data'
61
+ c.switch [:p,:parameters], :negatable => false
62
+ c.switch [:t,:template], :negatable => false
63
+
64
+ c.action do |global_options,options,args|
65
+ if args.length == 1
66
+ stack = args[0]
67
+ if options[:parameters]
68
+ puts 'editing parameters'
69
+ system("#{ENV['EDITOR']} #{get_parameters_path(stack)}")
70
+ elsif options[:template]
71
+ puts 'editing template'
72
+ system("#{ENV['EDITOR']} #{get_template_path(stack)}")
73
+ else
74
+ puts options.inspect
75
+ puts 'editing both the template and the parameters'
76
+ system("#{ENV['EDITOR']} #{get_template_path(stack)} #{get_parameters_path(stack)}")
77
+ end
78
+ else
79
+ raise 'This command needs a single stack as an argument.'
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ desc 'Clones an existing stack into a new one'
86
+ arg_name 'Arguments: stack new_stack'
87
+ command :clone do |c|
88
+ c.desc 'Force overwrites of existing data'
89
+ c.switch [:f,:force], :negatable => false
90
+
91
+ c.desc 'Commit changes to AWS, otherwise just copy the stack locally'
92
+ c.switch [:c,:commit], :negatable => false
93
+
94
+ c.action do |global_options,options,args|
95
+ if args.length == 2
96
+ clone_stack(args.first, args.last, global_options[:force] || options[:force], options['commit'])
97
+ else
98
+ raise 'Needed arguments: source_stack destination_stack'
99
+ end
100
+ end
101
+ end
102
+
103
+ desc 'Updated a list of CloudFormation stacks based on information from the current directory'
104
+ arg_name 'Arguments to update'
105
+ command :update do |c|
106
+
107
+ c.desc 'Update all the stacks within the current directory'
108
+ c.switch [:a,:all], :negatable => false
109
+ c.desc 'Create AWS stacks if the required sources exist locally'
110
+ c.switch [:c,:create_missing], :negatable => false
111
+
112
+ c.action do |global_options,options,args|
113
+ if args.empty? && options[:all]
114
+ puts 'dumping all stacks'
115
+ update_all_stacks(options[:create_missing])
116
+ elsif args.length >= 1
117
+ puts "updating the following stacks: #{args.inspect}"
118
+ update_stacks(args,options[:create_missing])
119
+ else # no arguments and --all flag
120
+ raise 'This command needs a list of stacks, or the --all flag in order to update all the stacks.'
121
+ end
122
+ end
123
+ end
124
+
125
+ #TODO: support deletion of multiple stacks, and of all stacks.
126
+ desc 'Deletes a stack'
127
+ arg_name 'Arguments: stack'
128
+ command :delete do |c|
129
+ c.desc 'Force deletion of the running stack'
130
+ c.switch [:f,:force], :negatable => false
131
+ c.action do |global_options,options,args|
132
+ raise "This command might impact running environments and it needs the --force flag, hopefully you know what you are doing" unless global_options[:force] || options[:force]
133
+ if args.length == 1
134
+ delete_stack(args.first)
135
+ else
136
+ raise 'Needed arguments: stack'
137
+ end
138
+ end
139
+ end
140
+
141
+ pre do |global,command,options,args|
142
+ # Pre logic here
143
+ # Return true to proceed; false to abort and not call the
144
+ # chosen command
145
+ # Use skips_pre before a command to skip this block
146
+ # on that command only
147
+ true
148
+ end
149
+
150
+ post do |global,command,options,args|
151
+ # Post logic here
152
+ # Use skips_post before a command to skip this
153
+ # block on that command only
154
+ end
155
+
156
+ on_error do |exception|
157
+ # Error logic here
158
+ # return false to skip default error handling
159
+ true
160
+ end
161
+
162
+ exit run(ARGV)
data/clouds.rdoc ADDED
@@ -0,0 +1,5 @@
1
+ = clouds
2
+
3
+ Generate this with
4
+ clouds rdoc
5
+ After you have described your command line interface
@@ -0,0 +1,3 @@
1
+ module Clouds
2
+ VERSION = '0.1.0'
3
+ end
data/lib/clouds.rb ADDED
@@ -0,0 +1,227 @@
1
+ require 'clouds/version.rb'
2
+
3
+ require 'aws-sdk'
4
+
5
+ require 'inifile'
6
+ require 'fileutils'
7
+ require 'yaml'
8
+
9
+ def configure(profile=nil)
10
+ if profile.nil? && ENV['AWS_ACCESS_KEY_ID'] && ENV['AWS_SECRET_ACCESS_KEY']
11
+ @aws_access_key_id = ENV['AWS_ACCESS_KEY_ID']
12
+ @aws_secret_access_key = ENV['AWS_SECRET_ACCESS_KEY']
13
+ @aws_session_token = ENV['AWS_SECURITY_TOKEN']
14
+ @region = ENV['AWS_DEFAULT_REGION'] || 'us-east-1'
15
+ else
16
+ profile = profile || ENV['AWS_DEFAULT_PROFILE']
17
+ aws_config_file = File.join(File.expand_path('~'), '.aws', 'config')
18
+
19
+ if profile.nil?
20
+ profile = "default"
21
+ puts "Using the default profile"
22
+ else
23
+ profile = "profile #{profile}"
24
+ puts "Using the profile '#{profile}'"
25
+ end
26
+ aws_config = IniFile.load(aws_config_file)[profile]
27
+
28
+ @aws_access_key_id = aws_config['aws_access_key_id']
29
+ @aws_secret_access_key = aws_config['aws_secret_access_key']
30
+ @region = aws_config['region']
31
+ end
32
+
33
+ proxy = ENV['http_proxy'] || ENV['HTTP_PROXY']
34
+
35
+ AWS.config({:access_key_id => @aws_access_key_id,
36
+ :secret_access_key => @aws_secret_access_key,
37
+ :session_token => @aws_session_token,
38
+ :region => @region,
39
+ :proxy_uri => proxy,
40
+ })
41
+
42
+ @cfn = AWS::CloudFormation.new
43
+ end
44
+
45
+ def write_file(file_name, content, force)
46
+ puts "Creating #{file_name}"
47
+ if File.exist?(file_name) and force == false
48
+ raise 'The file already exists, use --force to override'
49
+ end
50
+ File.open(file_name, 'w') {|f| f.write(content)}
51
+ puts "Created #{file_name}"
52
+ end
53
+
54
+ def read_file(file_name)
55
+ begin
56
+ File.open(file_name,'rb').read
57
+ rescue => e
58
+ puts e
59
+ end
60
+ end
61
+
62
+ def get_stack_directory(stack_name)
63
+ File.join(File.expand_path('.'),'stacks', stack_name)
64
+ end
65
+
66
+ def create_stack_directory(stack_name)
67
+ FileUtils.mkdir_p(get_stack_directory(stack_name))
68
+ end
69
+
70
+
71
+ def get_template_path(stack_name)
72
+ File.join(get_stack_directory(stack_name), 'template.json')
73
+ end
74
+
75
+ def get_parameters_path(stack_name)
76
+ File.join(get_stack_directory(stack_name), 'parameters.yaml')
77
+ end
78
+
79
+ def list_stacks()
80
+ configure()
81
+ max_stack_length = 0
82
+ stacks = Hash.new()
83
+
84
+ local_stacks = list_local_stacks()
85
+ local_stacks.each do |s|
86
+ max_stack_length = s.length if s.length > max_stack_length
87
+ stacks[s] = 'LOCAL-ONLY'
88
+ end
89
+
90
+ begin
91
+ @cfn.stacks.each do |stack|
92
+ stacks[stack.name] = stack.status
93
+ max_stack_length = stack.name.length if stack.name.length > max_stack_length
94
+ end
95
+ rescue => e
96
+ puts e
97
+ end
98
+ puts 'Stack list and stack status:'
99
+ stacks.keys.sort.each do |key|
100
+ printf("%#{max_stack_length}s %s\n",key, stacks[key])
101
+ end
102
+ end
103
+
104
+ def list_local_stacks()
105
+ list = []
106
+ return [] unless File.directory?('stacks')
107
+ Dir.foreach('stacks') do |item|
108
+ next if item == '.' or item == '..'
109
+ list << item
110
+ end
111
+ list
112
+ end
113
+
114
+ def dump_stacks(stack_list, force=false)
115
+ stack_list.each do |stack_name|
116
+ configure()
117
+ begin
118
+ stack = @cfn.stacks[stack_name]
119
+ puts "Dumping stack #{stack.name}"
120
+
121
+ template_content = stack.template
122
+ parameters = stack.parameters
123
+ create_stack_directory(stack_name)
124
+ write_file(get_template_path(stack_name), template_content, force)
125
+ write_file(get_parameters_path(stack_name), parameters.to_yaml, force)
126
+ rescue => e
127
+ puts "dump failed: #{e}"
128
+ end
129
+ end
130
+ end
131
+
132
+ def dump_all_stacks(force=false)
133
+ configure()
134
+ begin
135
+ @cfn.stacks.each do |stack|
136
+ dump_stacks([stack.name],force)
137
+ end
138
+ rescue => e
139
+ puts e
140
+ end
141
+ end
142
+
143
+ def update_stack(stack_name, create_if_missing=false)
144
+ configure()
145
+ stack=nil
146
+
147
+ template_content = read_file(get_template_path(stack_name))
148
+ parameters_content = read_file(get_parameters_path(stack_name))
149
+
150
+ begin
151
+ parameters_hash = YAML.load(parameters_content)
152
+ rescue => e
153
+ puts e
154
+ raise e
155
+ end
156
+
157
+ raise 'Empty stack template' if template_content.nil? || template_content.empty?
158
+
159
+ template_validation = @cfn.validate_template(template_content)
160
+ raise template_validation[:message] unless template_validation[:message].nil?
161
+
162
+ begin
163
+ if @cfn.stacks[stack_name].exists?
164
+ puts "Updating stack #{stack_name}"
165
+ stack = @cfn.stacks[stack_name]
166
+ stack_capabilities = stack.capabilities
167
+ stack.update(:template => template_content,
168
+ :parameters => parameters_hash,
169
+ :capabilities => stack_capabilities)
170
+ elsif create_if_missing
171
+ puts "Creating stack #{stack_name}"
172
+ stack = @cfn.stacks.create(stack_name,
173
+ template_content,
174
+ { :parameters => parameters_hash,
175
+ :capabilities => ['CAPABILITY_IAM']})
176
+ else
177
+ puts "Skipping stack #{stack_name} since it's not defined in this AWS account, if the stack exists locally you might use the -c flag"
178
+ end
179
+ unless stack.nil?
180
+ estimated_cost = stack.estimate_template_cost()
181
+ puts "Estimated costs for the stack #{stack_name} is #{estimated_cost}"
182
+ end
183
+ rescue => e
184
+ puts e
185
+ end
186
+ end
187
+
188
+ def update_stacks(stack_list, create_if_missing=false)
189
+ stack_list.each do |stack|
190
+ update_stack(stack, create_if_missing)
191
+ end
192
+ end
193
+
194
+ def update_all_stacks()
195
+ Dir.foreach('stacks') do |stack|
196
+ next if item == '.' or item == '..'
197
+ update_stack(stack)
198
+ end
199
+ end
200
+
201
+
202
+ def clone_stack(stack, new_stack, force=false, commit=false)
203
+ if File.exist?(get_stack_directory(new_stack)) and force == false
204
+ raise 'The stack already exists, use --force to override'
205
+ elsif force == true
206
+ FileUtils.rm_rf(get_stack_directory(new_stack))
207
+ end
208
+ FileUtils.cp_r(get_stack_directory(stack), get_stack_directory(new_stack))
209
+ if commit
210
+ puts 'Committing changes to AWS'
211
+ update_stack(new_stack, true)
212
+ else
213
+ puts 'Only copied the stack template and parameters locally, use the --commit flag in order to do commit the changes to AWS'
214
+ end
215
+ end
216
+
217
+ def delete_stack(stack_name)
218
+ configure()
219
+ FileUtils.rm_rf(get_stack_directory(stack_name))
220
+ if @cfn.stacks[stack_name].exists?
221
+ puts "Deleting stack #{stack_name}"
222
+ @cfn.stacks[stack_name].delete
223
+ else
224
+ puts "Stack #{stack_name} does not exist, nothing to delete here..."
225
+ end
226
+ end
227
+
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clouds
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Cristian Magherusan-Stanciu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
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: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 10.2.2
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: 10.2.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: rdoc
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: 4.1.1
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: 4.1.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: aruba
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 0.5.4
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 0.5.4
78
+ - !ruby/object:Gem::Dependency
79
+ name: nokogiri
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: 1.6.1
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: 1.6.1
94
+ - !ruby/object:Gem::Dependency
95
+ name: gli
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ~>
100
+ - !ruby/object:Gem::Version
101
+ version: 2.9.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: 2.9.0
110
+ - !ruby/object:Gem::Dependency
111
+ name: aws-sdk
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ~>
116
+ - !ruby/object:Gem::Version
117
+ version: 1.38.0
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ~>
124
+ - !ruby/object:Gem::Version
125
+ version: 1.38.0
126
+ - !ruby/object:Gem::Dependency
127
+ name: inifile
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ~>
132
+ - !ruby/object:Gem::Version
133
+ version: 2.0.2
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ~>
140
+ - !ruby/object:Gem::Version
141
+ version: 2.0.2
142
+ description: A layer of syntax sugar around the AWS Cloudformation API that allows
143
+ you to handle Cloudformation-defined infrastructure as code with a simple tool
144
+ email: cristian.magherusan-stanciu@here.com
145
+ executables:
146
+ - clouds
147
+ extensions: []
148
+ extra_rdoc_files:
149
+ - README.md
150
+ - clouds.rdoc
151
+ files:
152
+ - bin/clouds
153
+ - lib/clouds/version.rb
154
+ - lib/clouds.rb
155
+ - README.md
156
+ - clouds.rdoc
157
+ homepage: http://here.com
158
+ licenses:
159
+ - GPL-2
160
+ post_install_message:
161
+ rdoc_options:
162
+ - --title
163
+ - clouds
164
+ - --main
165
+ - README.md
166
+ - -ri
167
+ require_paths:
168
+ - lib
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ none: false
172
+ requirements:
173
+ - - ! '>='
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ requirements: []
183
+ rubyforge_project:
184
+ rubygems_version: 1.8.23
185
+ signing_key:
186
+ specification_version: 3
187
+ summary: AWS Cloudformation API
188
+ test_files: []