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 +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
|