jackal-cfn 0.2.8 → 0.2.10

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: 1f9789fbb8ee01ff50a47c77185056c6e52c8937
4
- data.tar.gz: 179c2cc85930686d222e3928f05d6e1ec6408a01
3
+ metadata.gz: 9fddaa1ca5447da20d6c856d2ccdf3ba4d3c89d9
4
+ data.tar.gz: acddccbce932860d09c048ba04627df5744cd4b3
5
5
  SHA512:
6
- metadata.gz: 6f9ea369c29581843ea001e10b38747e3b76e12f1cd3756c38f5c5115615380a9922ddb6201bf99ab384da638e3daae677a433482f90b783923eddb96fc86712
7
- data.tar.gz: 93ce81bd66869a378ae651b5fe1ccc6dbce264d52535a9ce66e995834de57722ba8aad980f9b7675786fc3f24a0e433daaa385b24d0325ce4e12e82636e61919
6
+ metadata.gz: 6d5f2187fab6e8d42da3ab3351b53720b1cd351a95df76ffe40390e50786a4f457fef306f6b00b93de1dabba807bcdb1f87e1c0c5b08b67d3f9dd8985d6c5b86
7
+ data.tar.gz: 9b5b9c770c12c9158bdc2ad3bf03041b7200b3aacdaf35f2bbec8c561673646d0b1a2ec63d1acea196e8b246b29468397497291f6b7fb006e6ac96359e2447f6
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # v0.2.10
2
+ * Properly handle no parameters in properties
3
+ * Add new JackalStack resource
4
+
1
5
  # v0.2.8
2
6
  * Fix optional instance halt to wait for ami AVAILABLE state
3
7
 
data/README.md CHANGED
@@ -1,23 +1,319 @@
1
1
  # Jackal CFN
2
2
 
3
- CFN proxy to the jackal subsystem.
3
+ Provides jackal integration for AWS CloudFormation custom
4
+ resources and stack event notifications.
4
5
 
5
- ## Supported payload generation
6
+ ## Requirements
6
7
 
7
- ### Events
8
+ This library currently uses the `patron` gem for sending
9
+ notifications to AWS S3. It requires the curl development
10
+ libraries to be available, so ensure it is installed.
8
11
 
9
- Stack events are injected with the following
10
- payload structure:
12
+ ## Usage
13
+
14
+ There are two ways to use this library. The first is to process
15
+ events and resources into proper payloads and inject them into the
16
+ pipeline. The other is to re-process a formatted payload.
17
+
18
+ ### Pipeline Injection
19
+
20
+ Configuration for pipeline injection of resource and event notifications
21
+ is very straightforward:
22
+
23
+ ```json
24
+ {
25
+ "jackal": {
26
+ "cfn": {
27
+ "config": {
28
+ },
29
+ "sources": {
30
+ "input": {
31
+ "type": "sqs",
32
+ "args": {
33
+ SQS_CONFIG
34
+ }
35
+ },
36
+ "output": {
37
+ OUTPUT_SOURCE
38
+ }
39
+ },
40
+ "callbacks": [
41
+ "Jackal::Cfn::Resource",
42
+ "Jackal::Cfn::Event"
43
+ ]
44
+ }
45
+ }
46
+ }
47
+ ```
48
+
49
+ With this configuration in place resources and events will be received,
50
+ formatted, and delivered to the `OUTPUT_SOURCE`. It will be the job of
51
+ a later service to handle reply notifications (for resources) in this
52
+ style of usage.
53
+
54
+ ### Message Reprocessing
55
+
56
+ When using the re-processing configuration, messages do not continue
57
+ down the pipeline. Instead, the original message is received, formatted,
58
+ and then re-delivered to the originating source (generally an SQS queue).
59
+ The message will be fetched again (this time properly formatted) and
60
+ any matching callbacks will be executed. A sample configuration may look
61
+ something like this:
62
+
63
+ ```json
64
+ {
65
+ "jackal": {
66
+ "cfn": {
67
+ "config": {
68
+ "reprocess": true,
69
+ "ami": {
70
+ "credentials": {
71
+ CREDENTIALS
72
+ }
73
+ }
74
+ },
75
+ "sources": {
76
+ "input": {
77
+ "type": "sqs",
78
+ "args": {
79
+ SQS_CONFIG
80
+ }
81
+ },
82
+ "output": {
83
+ OUTPUT_SOURCE
84
+ }
85
+ },
86
+ "callbacks": [
87
+ "Jackal::Cfn::Resource",
88
+ "Jackal::Cfn::AmiRegister"
89
+ ]
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ The important item to note is the `"reprocess": true` which enables the
96
+ automatic re-processing of messages.
97
+
98
+ ## Custom Resources
99
+
100
+ This library provides support for creating new custom resources. Creation
101
+ is as simple as subclassing:
102
+
103
+ ```ruby
104
+ module Jackal
105
+ module Cfn
106
+ class LocalPrinter < Jackal::Cfn::Resource
107
+
108
+ def execute(message)
109
+ failure_wrap(message) do |payload|
110
+ cfn_resource = rekey_hash(payload.get(:data, :cfn_resource))
111
+ cfn_response = build_response(cfn_resource)
112
+ info "CFN Resource: #{cfn_resource.inspect}"
113
+ info "CFN Response: #{cfn_response.inspect}"
114
+ respond_to_stack(cfn_response, cfn_resource[:response_url])
115
+ end
116
+ end
117
+
118
+ end
119
+ end
120
+ end
121
+ ```
122
+
123
+ This will match `Custom::LocalPrinter` resource requests that are received. It
124
+ will print information into the log about the request and then send a successful
125
+ response back to the stack.
126
+
127
+ ### Builtin Custom Resources
128
+
129
+ This library provides a few custom resources builtin. These can be used directly or
130
+ as examples for building new custom resources.
131
+
132
+ #### `Jackal::Cfn::AmiRegister`
133
+
134
+ Generates and registers an AMI based on an EC2 resource. This allows for building a
135
+ single EC2 resource within a stack that is fully configured, creating and registering
136
+ an AMI based on that EC2 resource, and using the new AMI for instances created within
137
+ an ASG. It is an integrated way to speed up ASG instance launches, but keep all resources
138
+ for the stack fully managed.
139
+
140
+ Resource usage:
11
141
 
12
142
  ```json
143
+ {
144
+ "Type": "Custom::AmiRegister",
145
+ "Properties": {
146
+ "Parameters": {
147
+ "Name": String,
148
+ "InstanceId": String,
149
+ "Description": String,
150
+ "NoReboot": Boolean,
151
+ "BlockDeviceMappings": Array,
152
+ "HaltInstance": Boolean,
153
+ "Region": String
154
+ }
155
+ }
156
+ }
13
157
  ```
14
158
 
15
- ### Resources
159
+ Resource Response:
160
+
161
+ ```json
162
+ {
163
+ "AmiId": String
164
+ }
165
+ ```
166
+
167
+ Configuration:
168
+
169
+ ```json
170
+ {
171
+ "jackal": {
172
+ "cfn": {
173
+ "config": {
174
+ "ami": {
175
+ "credentials": {
176
+ "compute": {
177
+ FOG_CREDENTIALS
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ }
184
+ }
185
+ ```
186
+
187
+ #### `Jackal::Cfn::AmiManager`
188
+
189
+ This resource is a simplification of the `AmiRegister` resource. The
190
+ `AmiManager` is used to ensure an AMI is removed from the system when
191
+ a stack is destroyed. This allows for customized AMI generation to be
192
+ integrated and ensure that once the stack is destroyed all custom AMIs
193
+ for that stack are destroyed as well.
194
+
195
+ Resource usage:
196
+
197
+ ```json
198
+ {
199
+ "Type": "Custom::AmiManager",
200
+ "Properties": {
201
+ "Parameters": {
202
+ "AmiId": "",
203
+ "Region": ""
204
+ }
205
+ }
206
+ }
207
+ ```
208
+
209
+ Resource Response:
210
+
211
+ ```json
212
+ {
213
+ }
214
+ ```
215
+
216
+ Configuration:
217
+
218
+ ```json
219
+ {
220
+ "jackal": {
221
+ "cfn": {
222
+ "config": {
223
+ "ami": {
224
+ "credentials": {
225
+ "compute": {
226
+ FOG_CREDENTIALS
227
+ }
228
+ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+ }
234
+ ```
235
+
236
+ #### `Jackal::Cfn::HashExtractor`
237
+
238
+ This resource will extract a nested hash value from a JSON string. Useful
239
+ for when a result may be serialized JSON and a value from that structure
240
+ is required elsewhere.
241
+
242
+ Resource usage:
243
+
244
+ ```json
245
+ {
246
+ "Type": "Custom::HashExtractor",
247
+ "Properties": {
248
+ "Parameters": {
249
+ "Key": "path.to.value.in.hash",
250
+ "Value": JSON
251
+ }
252
+ }
253
+ }
254
+ ```
255
+
256
+ Resource Response:
257
+
258
+ ```json
259
+ {
260
+ "Payload": VALUE
261
+ }
262
+ ```
263
+
264
+ #### `Jackal::Cfn::JackalStack`
265
+
266
+ This resource provides an integration point for building stacks
267
+ on remote endpoints. Remote end points are provided via configuration
268
+ and referenced via the `Location` property in the custom resource.
269
+
270
+ Resource usage:
271
+
272
+ ```json
273
+ {
274
+ "Type": "Custom::JackalStack",
275
+ "Properties": {
276
+ "Parameters": {
277
+ STACK_PARAMETERS
278
+ },
279
+ "Location": LOCATION,
280
+ "TemplateURL": URL
281
+ }
282
+ }
283
+ ```
284
+
285
+ Resource Response:
286
+
287
+ The outputs of the stack will be proxied:
288
+
289
+ ```json
290
+ {
291
+ "Outputs.OUTPUT_NAME": "OUTPUT_VALUE"
292
+ }
293
+ ```
16
294
 
17
- Custom stack resources are injected with the
18
- following structure:
295
+ Configuration:
19
296
 
20
297
  ```json
298
+ {
299
+ "jackal": {
300
+ "cfn": {
301
+ "config": {
302
+ "jackal_stack": {
303
+ "credentials": {
304
+ "storage": {
305
+ TEMPLATE_S3_CREDENTIALS
306
+ },
307
+ LOCATION: {
308
+ "provider": NAME,
309
+ MIASMA_CREDENTIALS
310
+ }
311
+ }
312
+ }
313
+ }
314
+ }
315
+ }
316
+ }
21
317
  ```
22
318
 
23
319
  ## Info
data/jackal-cfn.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.require_path = 'lib'
12
12
  s.license = 'Apache 2.0'
13
13
  s.add_dependency 'jackal'
14
+ s.add_dependency 'miasma'
14
15
  s.add_dependency 'patron'
15
16
  s.files = Dir['lib/**/*'] + %w(jackal-cfn.gemspec README.md CHANGELOG.md CONTRIBUTING.md LICENSE)
16
17
  end
@@ -9,9 +9,7 @@ module Jackal
9
9
  # "Type": "Custom::AmiManager",
10
10
  # "Properties": {
11
11
  # "Parameters": {
12
- # "InstanceId": "",
13
12
  # "AmiId": "",
14
- # "Action": "",
15
13
  # "Region": ""
16
14
  # }
17
15
  # }
@@ -10,7 +10,7 @@ module Jackal
10
10
  # "Properties": {
11
11
  # "Parameters": {
12
12
  # "Key": "path.to.value.in.hash",
13
- # "Value": Hash_or_JSON_string
13
+ # "Value": JSON
14
14
  # }
15
15
  # }
16
16
  # }
@@ -0,0 +1,248 @@
1
+ require 'jackal-cfn'
2
+
3
+ module Jackal
4
+ module Cfn
5
+ # Manage AMI Resources
6
+ #
7
+ # Expected resource:
8
+ # {
9
+ # "Type": "Custom::JackalStack",
10
+ # "Properties": {
11
+ # "Parameters": {
12
+ # STACK_PARAMETERS
13
+ # },
14
+ # "Location": LOCATION,
15
+ # "TemplateURL": "URL"
16
+ # }
17
+ # }
18
+ #
19
+ # Required configuration:
20
+ # {
21
+ # "config": {
22
+ # "jackal_stack": {
23
+ # "credentials": {
24
+ # "storage": {
25
+ # AWS_CREDENTIALS
26
+ # },
27
+ # LOCATION: {
28
+ # "provider": "NAME",
29
+ # MIAMSA_CREDENTIALS
30
+ # }
31
+ # }
32
+ # }
33
+ # }
34
+ # }
35
+ class JackalStack < Jackal::Cfn::Resource
36
+
37
+ # Load miasma for stack building
38
+ def setup(*_)
39
+ require 'miasma'
40
+ end
41
+
42
+ # Perform requested stack action
43
+ #
44
+ # @param message [Carnivore::Message]
45
+ def execute(message)
46
+ failure_wrap(message) do |payload|
47
+ cfn_resource = rekey_hash(payload.get(:data, :cfn_resource))
48
+ properties = rekey_hash(cfn_resource[:resource_properties])
49
+ parameters = rekey_hash(properties[:parameters])
50
+ cfn_response = build_response(cfn_resource)
51
+ case cfn_resource[:request_type].to_sym
52
+ when :create
53
+ create_stack(cfn_response, cfn_resource, properties, parameters, message)
54
+ when :update
55
+ update_stack(cfn_response, cfn_resource, properties, parameters, message)
56
+ when :delete
57
+ destroy_stack(cfn_response, cfn_resource, message)
58
+ else
59
+ error "Unknown request type received: #{cfn_resource[:request_type].inspect}"
60
+ cfn_response['Status'] = 'FAILED'
61
+ cfn_response['Reason'] = 'Unknown request type received'
62
+ end
63
+ respond_to_stack(cfn_response, cfn_resource[:response_url])
64
+ job_completed(:jackal_cfn, payload, message)
65
+ end
66
+ end
67
+
68
+ # Build API connection to base template storage bucket
69
+ #
70
+ # @param bucket_region [String] location of bucket
71
+ # @return [Miasma::Models::Storage]
72
+ def storage_api(bucket_region)
73
+ Miasma.api(
74
+ :type => :storage,
75
+ :provider => :aws,
76
+ :credentials => config.get(:jackal_stack, :credentials, :storage).merge(
77
+ :aws_bucket_region => bucket_region
78
+ )
79
+ )
80
+ end
81
+
82
+ # Build orchestration API connection for provided location
83
+ #
84
+ # @param location [String, Symbol]
85
+ # @return [Miasma::Models::Orchestration]
86
+ def remote_api(location)
87
+ l_config = config.get(:jackal_stack, :credentials, location)
88
+ if(l_config)
89
+ Miasma.api(
90
+ :type => :orchestration,
91
+ :provider => l_config[:provider],
92
+ :credentials => l_config
93
+ )
94
+ else
95
+ raise ArgumentError.new "Unknown target location provided `#{location}`!"
96
+ end
97
+ end
98
+
99
+ # Fetch a template from a storage bucket
100
+ #
101
+ # @param endpoint [String] URL to template
102
+ # @return [Hash] loaded template data
103
+ def fetch_template(endpoint)
104
+ url = URI.parse(endpoint)
105
+ region = url.host.split('.').first.split('-', 2).last
106
+ if(region == 's3')
107
+ region = 'us-east-1'
108
+ end
109
+ bucket, path = url.path.sub('/', '').split('/', 2)
110
+ MultiJson.load(
111
+ storage_api(region).buckets.get(
112
+ bucket.sub('/', '')
113
+ ).files.get(path).body.read
114
+ )
115
+ end
116
+
117
+ # Create a new stack and update the response values
118
+ #
119
+ # @param response [Hash] response data of action
120
+ # @param resource [Hash] request resource
121
+ # @param properties [Hash] properties of request resource
122
+ # @param parameters [Hash] parmeters provided via properties
123
+ # @param message [Carnivore::Message] original message
124
+ # @return [TrueClass, FalseClass]
125
+ def create_stack(response, resource, properties, parameters, message)
126
+ stack = remote_api(properties[:location]).stacks.build(
127
+ :name => [
128
+ 'JackalStack',
129
+ resource[:logical_resource_id],
130
+ resource[:stack_id].split('/').last
131
+ ].join('-'),
132
+ :template => properties.fetch(:stack, fetch_template(properties[:template_url])),
133
+ :parameters => Hash[parameters.map{|k,v| [Bogo::Utility.camel(k), v] }]
134
+ )
135
+ stack.save
136
+ until(stack.state.to_s.end_with?('complete'))
137
+ message.touch!
138
+ debug "Waiting for created stack to reach completion..."
139
+ sleep 5
140
+ stack.reload
141
+ end
142
+ if(stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed'))
143
+ stack.outputs.each do |output|
144
+ response['Data']["Outputs.#{output.key}"] = output.value
145
+ end
146
+ response['PhysicalResourceId'] = "#{properties[:location]}-#{stack.id}"
147
+ true
148
+ else
149
+ response['Status'] = 'FAILED'
150
+ response['Reason'] = 'Stack creation failed!'
151
+ stack.destroy
152
+ false
153
+ end
154
+ end
155
+
156
+ # Update an existing stack and update the response values
157
+ #
158
+ # @param response [Hash] response data of action
159
+ # @param resource [Hash] request resource
160
+ # @param properties [Hash] properties of request resource
161
+ # @param parameters [Hash] parmeters provided via properties
162
+ # @param message [Carnivore::Message] original message
163
+ # @return [TrueClass, FalseClass]
164
+ def update_stack(response, resource, properties, parameters, message)
165
+ c_location, stack_id = resource[:physical_resource_id].split('-', 2)
166
+ if(c_location != properties[:location])
167
+ warn "Stack resource has changed location! #{c_location} -> #{properties[:location]}"
168
+ warn "Starting destruction of existing resource: #{stack_id}"
169
+ if(destroy_stack(response, resource, message))
170
+ info "Destruction of stack `#{stack_id}` complete. Creating replacement stack."
171
+ create_stack(response, resource, properties, parameters, message)
172
+ else
173
+ error "Failed to destroy existing stack for replacement `#{stack_id}`"
174
+ end
175
+ else
176
+ stack = remote_api(c_location).stacks.get(stack_id)
177
+ if(stack)
178
+ info "Stack resource update on: #{stack_id}"
179
+ stack.template = fetch_template(properties['TemplateURL'])
180
+ stack.parameters = Hash[parameters.map{|k,v| [Bogo::Utility.camel(k), v] }]
181
+ stack.save
182
+ until(stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed'))
183
+ debug "Waiting for created stack to reach completion..."
184
+ sleep 5
185
+ stack.reload
186
+ end
187
+ if(stack.state.to_s.end_with?('complete'))
188
+ stack.outputs.each do |output|
189
+ response['Data']["Outputs.#{output.key}"] = output.value
190
+ end
191
+ response['PhysicalResourceId'] = stack.id
192
+ else
193
+ response['Status'] = 'FAILED'
194
+ response['Reason'] = 'Stack update failed!'
195
+ end
196
+ else
197
+ response['Status'] = 'FAILED'
198
+ response['Reason'] = "No stack was found matching request: #{stack_id}"
199
+ end
200
+ end
201
+ end
202
+
203
+ # Destroy the stack
204
+ #
205
+ # @param response [Hash] response data of action
206
+ # @param resource [Hash] request resource
207
+ # @param message [Carnivore::Message] original message
208
+ def destroy_stack(response, resource, message)
209
+ stack = request_destroy(resource[:physical_resource_id])
210
+ if(stack)
211
+ until(stack.state.nil? || stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed'))
212
+ info "Waiting for stack destruction (#{stack.name})..."
213
+ message.touch!
214
+ sleep 5
215
+ stack.reload
216
+ end
217
+ if(stack.state.to_s.end_with?('failed'))
218
+ response['Status'] = 'FAILED'
219
+ response['Reason'] = 'Failed to delete remote stack!'
220
+ end
221
+ end
222
+ end
223
+
224
+ # Send a stack delete request
225
+ #
226
+ # @param stack_resource_id [String] physical resource ID
227
+ # @return [Miasma::Models::Orchestration::Stack, FalseClass]
228
+ def request_destroy(stack_resource_id)
229
+ location, stack_id = stack_resource_id.split('-', 2)
230
+ if(stack_id)
231
+ begin
232
+ info "Sending stack destruction request to: #{stack_id} in: #{location}"
233
+ stack = remote_api(location).stacks.get(stack_id)
234
+ stack.destroy
235
+ stack
236
+ rescue => e
237
+ error "Stack destruction request failed! #{e.class}: #{e.message}"
238
+ false
239
+ end
240
+ else
241
+ warn "No stack ID registered in resource. Skipping destroy: #{stack_resource_id}"
242
+ false
243
+ end
244
+ end
245
+
246
+ end
247
+ end
248
+ end
@@ -175,12 +175,12 @@ module Jackal
175
175
  payload = unpack(message)
176
176
  yield payload
177
177
  rescue => e
178
- error "Unexpected error encountered processing custom resource - #{e.class}: #{e}"
178
+ error "Unexpected error encountered processing custom resource - #{e.class}: #{e.message}"
179
179
  debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
180
180
  cfn_resource = payload.get(:data, :cfn_resource)
181
181
  cfn_response = build_response(cfn_resource)
182
182
  cfn_response['Status'] = 'FAILED'
183
- cfn_response['Reason'] = "Unexpected error encountered [#{e}]"
183
+ cfn_response['Reason'] = "Unexpected error encountered [#{e.message}]"
184
184
  respond_to_stack(cfn_response, cfn_resource[:response_url])
185
185
  message.confirm!
186
186
  end
@@ -13,7 +13,7 @@ module Jackal
13
13
  # @return [Hash] new hash with snake cased toplevel keys
14
14
  def transform_parameters(params)
15
15
  Smash.new.tap do |new_hash|
16
- params.each do |key, value|
16
+ (params || []).each do |key, value|
17
17
  new_hash[snakecase(key)] = value
18
18
  end
19
19
  end
@@ -1,6 +1,6 @@
1
1
  module Jackal
2
2
  module Cfn
3
3
  # Current version
4
- VERSION = Gem::Version.new('0.2.8')
4
+ VERSION = Gem::Version.new('0.2.10')
5
5
  end
6
6
  end
data/lib/jackal-cfn.rb CHANGED
@@ -12,5 +12,6 @@ module Jackal
12
12
  autoload :HashExtractor, 'jackal-cfn/resource/hash_extractor'
13
13
  autoload :AmiManager, 'jackal-cfn/resource/ami_manager'
14
14
  autoload :AmiRegister, 'jackal-cfn/resource/ami_register'
15
+ autoload :JackalStack, 'jackal-cfn/resource/jackal_stack'
15
16
  end
16
17
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jackal-cfn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-08-21 00:00:00.000000000 Z
11
+ date: 2015-09-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jackal
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: miasma
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: patron
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -56,6 +70,7 @@ files:
56
70
  - lib/jackal-cfn/resource/ami_manager.rb
57
71
  - lib/jackal-cfn/resource/ami_register.rb
58
72
  - lib/jackal-cfn/resource/hash_extractor.rb
73
+ - lib/jackal-cfn/resource/jackal_stack.rb
59
74
  - lib/jackal-cfn/utils.rb
60
75
  - lib/jackal-cfn/utils/fog.rb
61
76
  - lib/jackal-cfn/utils/http.rb