convection 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d7f560ff0230cc8a78bdc3e59c57d2b5befbfe37
4
- data.tar.gz: 54969c701ca627f77a22a38a67e70aa43e1c93a8
3
+ metadata.gz: b3de07eb04f40d2d56959e6df5572e98f8447ce7
4
+ data.tar.gz: d0925f4ae7e4f4093606a7cf1fb05bb643172bed
5
5
  SHA512:
6
- metadata.gz: 1db320b7089911c0e28b80709348897131f11f4a15e3529e406c78d09760cb65d0aa8df505e98c1f2d84e707b3383311cc5c044198cc9f5ba25648beb5d3b01d
7
- data.tar.gz: 11e1bce3c3781d8899f6ad2c5ac1a0acc6ba1ec3766160c0e1cc0c96f279330831b67121aab7e8173cf5ea55fae9f52ed3d1260be86aa17274a0410be0a9cd95
6
+ metadata.gz: 69a32d02abbc59d1bdf62703de9cd72ceb8cb8a88c5efc69a4790d2fee51f21c248e976f76eb0ff0db3ea39819136709c807343b04d49a822fa4878798ba4bc2
7
+ data.tar.gz: dc372422b28b5eab1d908b98cb798cff7b42c54f483b5ce86498d1c408b46f0fa79f703292ee8a7315101d2365897f0438f21c854a3608180b67988416ff5344
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.2
1
+ 2.3.0
data/bin/convection CHANGED
@@ -28,6 +28,34 @@ module Convection
28
28
  end
29
29
  end
30
30
 
31
+ desc 'delete STACK', 'Delete stack(s) from your cloud'
32
+ option :stack_group, :type => :string, :desc => 'The name of a stack group defined in your cloudfile to delete'
33
+ option :stacks, :type => :array, :desc => 'A ordered space separated list of stacks to delete'
34
+ def delete(stack = nil)
35
+ @cloud.configure(File.absolute_path(options['cloudfile'], @cwd))
36
+ print_status = lambda do |event, *errors|
37
+ say_status(*event.to_thor)
38
+ errors.flatten.each do |error|
39
+ say "* #{ error.message }"
40
+ error.backtrace.each { |b| say " #{ b }" }
41
+ end unless errors.nil?
42
+ end
43
+
44
+ stacks = @cloud.stacks_until(stack, options, &print_status)
45
+ if stacks.empty?
46
+ say_status(:delete_failed, 'No stacks found matching the provided input (STACK, --stack-group, and/or --stacks).', :red)
47
+ return
48
+ end
49
+ say_status(:delete, "Deleting the following stack(s): #{stacks.map(&:name).join(', ')}", :red)
50
+
51
+ confirmation = ask('Are you sure you want to delete the above stack(s)?', limited_to: %w(yes no))
52
+ if confirmation.eql?('yes')
53
+ @cloud.delete(stacks, &print_status)
54
+ else
55
+ say_status(:delete_aborted, 'Aborted deletion of the above stack(s).', :green)
56
+ end
57
+ end
58
+
31
59
  desc 'diff STACK', 'Show changes that will be applied by converge'
32
60
  option :verbose, :type => :boolean, :aliases => '--v', :desc => 'Show stack progress'
33
61
  option :'very-verbose', :type => :boolean, :aliases => '--vv', :desc => 'Show unchanged stacks'
@@ -0,0 +1,148 @@
1
+ # Creating a custom resource collection
2
+ **NOTE**: Examples in this file can be found in `example/web-service-resource-collection`.
3
+
4
+ This guide will walk you through creating a custom resource collection.
5
+
6
+ For the purpose of this guide we'll create a resource collection to provision a web server listening on port 80 with security group ingress rules to only allow specific CIDR ranges.
7
+
8
+ ## Attach your custom resource collection to the Template DSL
9
+ The below code snippet does the following:
10
+ * Defines `WebService` as a subclass of `Convection::Model::Template::ResourceCollection` (this provides a standard initialize function and mixins in convection helper functions).
11
+ * Defines `Template#web_service` which can later be called in the Convection template configuration.
12
+
13
+ ```ruby
14
+ class WebService < Convection::Model::Template::ResourceCollection
15
+ # Attach this class to the Template DSL with the method name "web_service".
16
+ attach_to_dsl(:web_service)
17
+ end
18
+ ```
19
+
20
+ ## Override `ResourceCollection#execute`
21
+ When rendering resource collections convection internally calls `#run_definition` to resolve the configuration block passed in during template definition. After this call `#execute` is called. This method should be overridden for resource collections to call other [basic] convection resources.
22
+
23
+ ```ruby
24
+ class WebService < Convection::Model::Template::ResourceCollection
25
+ # ...
26
+
27
+ def execute
28
+ # Preserve scope by exposing a local "web_service" variable.
29
+ web_service = self
30
+ generate_security_groups(web_service)
31
+ generate_ec2_instance(web_service)
32
+ end
33
+
34
+ def generate_ec2_instance(web_service)
35
+ # TODO: Actually generate the ec2 instance resource!
36
+ end
37
+
38
+ def generate_security_groups(web_service)
39
+ # TODO: Actually generate the ec2 security group resource!
40
+ end
41
+ end
42
+ ```
43
+
44
+ ### Generating a security group for our web service
45
+ The below code can be used to generate a security group with a few tags and ingress rules defined for TCP over port 80 on any CIDR range in a CSV list from the `ALLOWED_CIDR_RANGES` environment variable.
46
+
47
+ ```ruby
48
+ def cidr_ranges
49
+ return ENV['ALLOWED_CIDR_RANGES'].split(/[, ]+/) if ENV.key?('ALLOWED_CIDR_RANGES')
50
+
51
+ raise ArgumentError, "You must export $ALLOWED_CIDR_RANGES to diff/converge #{stack.cloud_name}."
52
+ end
53
+
54
+ def generate_security_groups(web_service)
55
+ ec2_security_group "#{web_service.name}SecurityGroup" do
56
+ description "EC2 Security Group for the #{web_service.name} web service."
57
+
58
+ web_service.cidr_ranges.each do |range|
59
+ ingress_rule(:tcp, 80, range)
60
+ end
61
+
62
+ tag 'Name', "sg-#{web_service.name}-#{stack.cloud}".downcase
63
+ tag 'Service', web_service.name
64
+ tag 'Stack', stack.cloud
65
+
66
+ with_output
67
+ end
68
+ end
69
+ ```
70
+
71
+ ### Generating a web server running on port 80
72
+ #### Create an EC2 instance
73
+ ##### Support defining the image ID and instance user data via the template DSL
74
+ ```ruby
75
+ class WebService < Convection::Model::Template::ResourceCollection
76
+ # ...
77
+
78
+ attribute :ec2_instance_image_id
79
+ attribute :user_data
80
+ end
81
+ ```
82
+
83
+ ##### Generate the EC2 instance resource
84
+ ```ruby
85
+ def generate_ec2_instance(web_service)
86
+ ec2_instance "#{name}Frontend" do
87
+ image_id web_service.ec2_instance_image_id
88
+ security_group fn_ref("#{web_service.name}SecurityGroup")
89
+
90
+ tag 'Name', "#{web_service.name}Frontend"
91
+ tag 'Stack', stack.cloud
92
+
93
+ user_data base64(web_service.user_data)
94
+
95
+ with_output 'Hostname', get_att(name, 'PublicDnsName') do
96
+ description 'The public hostname of this web service.'
97
+ end
98
+
99
+ with_output 'HttpEndpoint', join('', 'http://', get_att(name, 'PublicDnsName')) do
100
+ description 'The URL to visit this web service at.'
101
+ end
102
+ end
103
+ end
104
+ ```
105
+
106
+ ## Define your convection template using your resource collection
107
+ ### Call your custom resource like any other resource
108
+ ```ruby
109
+ require_relative '../path/to/your/resources/web_service.rb'
110
+
111
+ EXAMPLE_DOT_ORG = Convection.template do
112
+ description 'An example website to demonstrate using custom resource collections.'
113
+
114
+ web_service 'ExampleDotOrg' do
115
+ # Set the instance ID attribute so the instance can be converged.
116
+ ec2_instance_image_id 'ami-45026036' # ubuntu <3!
117
+
118
+ user_data <<~USER_DATA
119
+ #!/bin/bash
120
+ echo 'Hello World!'
121
+ USER_DATA
122
+ end
123
+ end
124
+ ```
125
+
126
+ ### Set the user data up as a shell script to install nginx
127
+ A quick and simple way to expose an HTTP server on port 80 is to install nginx.
128
+
129
+ You can do this with the bash script embedded as user data in your template like so:
130
+ ```ruby
131
+ user_data <<~USER_DATA
132
+ #!/bin/bash
133
+ apt-get update
134
+ apt-get install -y unzip curl nginx
135
+ service nginx start
136
+ update-rc.d nginx defaults
137
+ USER_DATA
138
+ ```
139
+
140
+ ## Converge your template
141
+ 1. Export your AWS credentials.
142
+ 2. Export the `ALLOWED_CIDR_RANGES` we use for setting our ingress rules.
143
+ * This should be set to your external IP address so the instance allows connections from your machine.
144
+ 3. Run `convection diff` to show what will be converged.
145
+ 4. Run `convection converge` to deploy your stack.
146
+ 5. Open the stack outputs section CloudFormation for this stack.
147
+ 6. Click on the HttpEndpoint value.
148
+ 7. Look at the "Welcome to nginx!" page.
data/docs/template.html CHANGED
@@ -102,6 +102,7 @@
102
102
  <li class="list-group-item"><a href="/{{NAME}}/deleting-stacks">Deleting Stacks</a></li>
103
103
  <li class="list-group-item"><a href="/{{NAME}}/canceling-stack-updates">Canceling Stack Updates</a></li>
104
104
  <li class="list-group-item"><a href="/{{NAME}}/adding-new-resource-coverage">Adding New Resource Coverage</a></li>
105
+ <li class="list-group-item"><a href="/{{NAME}}/creating-a-custom-resource-collection">Creating a custom resource collection</a></li>
105
106
  <li class="list-group-item"><a href="/{{NAME}}/stackgroups">Stackgroups</a></li>
106
107
 
107
108
  </ul>
@@ -0,0 +1,10 @@
1
+ # Cloudfile
2
+ require_relative './templates/example_dot_org.rb'
3
+
4
+ user = ENV['USER'] || 'anon'
5
+ aws_region = ENV['AWS_REGION'] || 'us-east-1'
6
+
7
+ name "#{user}-web-service-example"
8
+ region aws_region
9
+
10
+ stack 'example-org', Templates::EXAMPLE_DOT_ORG
@@ -0,0 +1,63 @@
1
+ require 'convection'
2
+
3
+ class WebService < Convection::Model::Template::ResourceCollection
4
+ attach_to_dsl(:web_service)
5
+
6
+ attribute :ec2_instance_image_id
7
+ attribute :user_data
8
+
9
+ def execute
10
+ web_service = self
11
+
12
+ generate_security_groups(web_service)
13
+ generate_ec2_instance(web_service)
14
+ end
15
+
16
+ def cidr_ranges
17
+ @cidr_ranges ||= allowed_csv_ranges.split(/[, ]+/)
18
+ end
19
+
20
+ # Resource generator methods
21
+
22
+ def generate_ec2_instance(web_service)
23
+ ec2_instance "#{name}Frontend" do
24
+ image_id web_service.ec2_instance_image_id
25
+ security_group fn_ref("#{web_service.name}SecurityGroup")
26
+
27
+ tag 'Name', "#{web_service.name}Frontend"
28
+ tag 'Stack', stack.cloud
29
+
30
+ user_data base64(web_service.user_data)
31
+
32
+ with_output 'Hostname', get_att(name, 'PublicDnsName') do
33
+ description 'The public hostname of this web service.'
34
+ end
35
+
36
+ with_output 'HttpEndpoint', join('', 'http://', get_att(name, 'PublicDnsName')) do
37
+ description 'The URL to visit this web service at.'
38
+ end
39
+ end
40
+ end
41
+
42
+ def generate_security_groups(web_service)
43
+ ec2_security_group "#{web_service.name}SecurityGroup" do
44
+ description "EC2 Security Group for the #{web_service.name} web service."
45
+
46
+ web_service.cidr_ranges.each do |range|
47
+ ingress_rule(:tcp, 80, range)
48
+ end
49
+
50
+ tag 'Name', "sg-#{web_service.name}-#{stack.cloud}".downcase
51
+ tag 'Service', web_service.name
52
+ tag 'Stack', stack.cloud
53
+
54
+ with_output
55
+ end
56
+ end
57
+
58
+ def allowed_csv_ranges
59
+ return ENV['ALLOWED_CIDR_RANGES'] if ENV.key?('ALLOWED_CIDR_RANGES')
60
+
61
+ raise ArgumentError, "You must export $ALLOWED_CIDR_RANGES to diff/converge #{stack.cloud_name}."
62
+ end
63
+ end
@@ -0,0 +1,20 @@
1
+ require 'convection'
2
+ require_relative '../resources/web_service.rb'
3
+
4
+ module Templates
5
+ EXAMPLE_DOT_ORG = Convection.template do
6
+ description 'An example website to demonstrate using custom resource collections.'
7
+
8
+ web_service 'ExampleDotOrg' do
9
+ ec2_instance_image_id 'ami-45026036'
10
+
11
+ user_data <<~USER_DATA
12
+ #!/bin/bash
13
+ apt-get update
14
+ apt-get install -y unzip curl nginx
15
+ service nginx start
16
+ update-rc.d nginx defaults
17
+ USER_DATA
18
+ end
19
+ end
20
+ end
@@ -49,6 +49,20 @@ module Convection
49
49
  end
50
50
  end
51
51
 
52
+ def stacks_until(last_stack_name, options = {}, &block)
53
+ stacks = filter_deck(options, &block).values
54
+ return stacks if last_stack_name.nil?
55
+
56
+ last_stack = stacks.find { |stack| stack.name == last_stack_name }
57
+ unless last_stack
58
+ block.call(Model::Event.new(:error, "Stacks filter did not include last stack #{ last_stack }", :error)) if block
59
+ return []
60
+ end
61
+
62
+ last_stack_index = stacks.index(last_stack)
63
+ stacks[0..last_stack_index]
64
+ end
65
+
52
66
  def converge(to_stack, options = {}, &block)
53
67
  if to_stack && !stacks.include?(to_stack)
54
68
  block.call(Model::Event.new(:error, "Undefined Stack #{ to_stack }", :error)) if block
@@ -73,6 +87,27 @@ module Convection
73
87
  end
74
88
  end
75
89
 
90
+ def delete(stacks, &block)
91
+ stacks.each do |stack|
92
+ unless stack.exist?
93
+ block.call(Model::Event.new(:delete_skipped, "Stack #{ stack.cloud_name } does not exist remotely", :warn))
94
+ next
95
+ end
96
+
97
+ block.call(Model::Event.new(:deleted, "Delete remote stack #{ stack.cloud_name }", :info)) if block
98
+ stack.delete(&block)
99
+
100
+ if stack.error?
101
+ block.call(Model::Event.new(:error, "Error deleting stack #{ stack.name }", :error), stack.errors) if block
102
+ break
103
+ end
104
+
105
+ break unless stack.delete_success?
106
+
107
+ sleep rand @cloudfile.splay || 2
108
+ end
109
+ end
110
+
76
111
  def diff(to_stack, options = {}, &block)
77
112
  if to_stack && !stacks.include?(to_stack)
78
113
  block.call(Model::Event.new(:error, "Undefined Stack #{ to_stack }", :error)) if block
@@ -209,6 +209,17 @@ module Convection
209
209
  [CREATE_COMPLETE, ROLLBACK_COMPLETE, UPDATE_COMPLETE, UPDATE_ROLLBACK_COMPLETE].include?(status)
210
210
  end
211
211
 
212
+ # @return [Boolean] whether the CloudFormation Stack is in one of
213
+ # the several *_COMPLETE states.
214
+ def delete_complete?
215
+ DELETE_COMPLETE == status
216
+ end
217
+
218
+ # @return [Boolean] whether the Stack state is now {#delete_complete?} (with no errors present).
219
+ def delete_success?
220
+ !error? && delete_complete?
221
+ end
222
+
212
223
  # @return [Boolean] whether the CloudFormation Stack is in one of
213
224
  # the several *_FAILED states.
214
225
  def fail?
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: convection
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Manero
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2016-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: aws-sdk
@@ -92,6 +92,7 @@ files:
92
92
  - convection.gemspec
93
93
  - docs/adding-new-resource-coverage.md
94
94
  - docs/canceling-stack-updates.md
95
+ - docs/creating-a-custom-resource-collection.md
95
96
  - docs/deleting-stacks.md
96
97
  - docs/getting-started.md
97
98
  - docs/index.md
@@ -100,13 +101,15 @@ files:
100
101
  - docs/stackgroups.md
101
102
  - docs/stacks.md
102
103
  - docs/template.html
103
- - example/.ruby-version
104
104
  - example/demo-cloud/Cloudfile
105
105
  - example/getting-started-guide/Cloudfile
106
106
  - example/getting-started-guide/vpc.rb
107
107
  - example/stacks/Cloudfile
108
108
  - example/stacks/tasks/lookup_vpc_task.rb
109
109
  - example/stacks/templates/vpc.rb
110
+ - example/web-service-resource-collection/Cloudfile
111
+ - example/web-service-resource-collection/resources/web_service.rb
112
+ - example/web-service-resource-collection/templates/example_dot_org.rb
110
113
  - ext/resource_generator.sh
111
114
  - lib/convection.rb
112
115
  - lib/convection/control/cloud.rb
@@ -1 +0,0 @@
1
- 2.2.2