convection 0.4.1 → 0.4.2
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.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/bin/convection +28 -0
- data/docs/creating-a-custom-resource-collection.md +148 -0
- data/docs/template.html +1 -0
- data/example/web-service-resource-collection/Cloudfile +10 -0
- data/example/web-service-resource-collection/resources/web_service.rb +63 -0
- data/example/web-service-resource-collection/templates/example_dot_org.rb +20 -0
- data/lib/convection/control/cloud.rb +35 -0
- data/lib/convection/control/stack.rb +11 -0
- metadata +6 -3
- data/example/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3de07eb04f40d2d56959e6df5572e98f8447ce7
|
4
|
+
data.tar.gz: d0925f4ae7e4f4093606a7cf1fb05bb643172bed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 69a32d02abbc59d1bdf62703de9cd72ceb8cb8a88c5efc69a4790d2fee51f21c248e976f76eb0ff0db3ea39819136709c807343b04d49a822fa4878798ba4bc2
|
7
|
+
data.tar.gz: dc372422b28b5eab1d908b98cb798cff7b42c54f483b5ce86498d1c408b46f0fa79f703292ee8a7315101d2365897f0438f21c854a3608180b67988416ff5344
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
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,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.
|
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-
|
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
|
data/example/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
2.2.2
|