jackal-cfn 0.1.2 → 0.1.4

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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,6 @@
1
+ # v0.1.4
2
+ * Include ami manager resource (handle deletion of amis only for now)
3
+
1
4
  # v0.1.2
2
5
  * Include request id in response content
3
6
  * Allow matching on class name to custom resource type
@@ -0,0 +1,109 @@
1
+ require 'jackal-cfn'
2
+
3
+ module Jackal
4
+ module Cfn
5
+ class Resource
6
+ # Manage AMI Resources
7
+ #
8
+ # Expected resource:
9
+ # {
10
+ # "Type": "Custom::AmiManager",
11
+ # "Properties": {
12
+ # "Parameters": {
13
+ # "InstanceId": "",
14
+ # "AmiId": "",
15
+ # "Action": "",
16
+ # "Region": ""
17
+ # }
18
+ # }
19
+ # }
20
+ #
21
+ # Required configuration:
22
+ # {
23
+ # "config": {
24
+ # "credentials": {
25
+ # "compute": {
26
+ # FOG_CREDENTIALS
27
+ # }
28
+ # }
29
+ # }
30
+ # }
31
+ class AmiManager < Resource
32
+
33
+ PHYSICAL_ID_JOINER = '__-__'
34
+
35
+ # Process message, send value back to CFN
36
+ #
37
+ # @param message [Carnivore::Message]
38
+ def execute(message)
39
+ payload = unpack(message)
40
+ parameters = transform_parameters(payload[:resource_properties]['Parameters'])
41
+ debug "Processing payload: #{payload.inspect}"
42
+ response = build_response(payload)
43
+ case payload[:request_type]
44
+ when :create
45
+ ami_response_update(response, parameters)
46
+ when :delete
47
+ destroy_ami(response, payload, parameters)
48
+ when :update
49
+ else
50
+ error "Unknown request type received: #{payload[:request_type].inspect}"
51
+ response['Status'] = 'FAILED'
52
+ response['Reason'] = 'Unknown request type received'
53
+ end
54
+ respond_to_stack(response, payload[:response_url])
55
+ completed(
56
+ new_payload(
57
+ config.fetch(:name, :ami_manager),
58
+ :cfn_resource => payload
59
+ ),
60
+ message
61
+ )
62
+ end
63
+
64
+ # Update the physical resource id to include the ami id
65
+ #
66
+ # @param response [Hash] cfn response
67
+ # @param parameters [Hash] resource parameters
68
+ # @return [Hash] updated response hash
69
+ def ami_response_update(response, parameters)
70
+ response['PhysicalResourceId'] = [
71
+ response['PhysicalResourceId'],
72
+ parameters[:ami_id]
73
+ ].join(PHYSICAL_ID_JOINER)
74
+ response
75
+ end
76
+
77
+ # Destroy the AMI referenced by the resource
78
+ #
79
+ # @param response [Hash] cfn response
80
+ # @param payload [Hash] message payload
81
+ # @param parameters [Hash] resource parameters
82
+ # @return [TrueClass]
83
+ def destroy_ami(response, payload, parameters)
84
+ ami_id = payload[:physical_resource_id].split(PHYSICAL_ID_JOINER).last
85
+ begin
86
+ compute_api(parameters[:region]).deregister_image(ami_id)
87
+ rescue Fog::Compute::AWS::Error => e
88
+ warn "Non-fatal error encountered on AMI removal: #{e.class}: #{e}"
89
+ response['Reason'] = e.message
90
+ rescue => e
91
+ error "Failed to remove AMI: #{e.class}: #{e}"
92
+ response['Status'] = 'FAILED'
93
+ response['Reason'] = e.message
94
+ end
95
+ true
96
+ end
97
+
98
+ # Build new compute api connection
99
+ #
100
+ # @param region [String] AWS region ami exists
101
+ # @return [Fog::Compute]
102
+ def compute_api(region)
103
+ Fog::Compute.new({:provider => :aws}.merge(config.fetch(:credentials, :compute, {}).merge(:region => region)))
104
+ end
105
+
106
+ end
107
+ end
108
+ end
109
+ end
@@ -5,8 +5,9 @@ module Jackal
5
5
  class Resource
6
6
  # Extract value from hash
7
7
  #
8
- # Expected resource properties:
8
+ # Expected resource:
9
9
  # {
10
+ # "Type": "Custom::HashExtractor",
10
11
  # "Properties": {
11
12
  # "Parameters": {
12
13
  # "Key": "path.to.value.in.hash",
@@ -16,21 +17,6 @@ module Jackal
16
17
  # }
17
18
  class HashExtractor < Resource
18
19
 
19
- # Setup the dependency requirements for the callback
20
- def setup(*_)
21
- require 'patron'
22
- end
23
-
24
- # Validity of message
25
- #
26
- # @param message [Carnivore::Message]
27
- # @return [Truthy, Falsey]
28
- # def valid?(message)
29
- # super do |payload|
30
- # payload.get('ResourceProperties', 'Action') == 'hash_extractor'
31
- # end
32
- # end
33
-
34
20
  # Process message, send value back to CFN
35
21
  #
36
22
  # @param message [Carnivore::Message]
@@ -8,6 +8,12 @@ module Jackal
8
8
  VALID_RESOURCE_STATUS = ['SUCCESS', 'FAILED']
9
9
 
10
10
  autoload :HashExtractor, 'jackal-cfn/resource/hash_extractor'
11
+ autoload :AmiManager, 'jackal-cfn/resource/ami_manager'
12
+
13
+ # Setup the dependency requirements for the callback
14
+ def setup(*_)
15
+ require 'patron'
16
+ end
11
17
 
12
18
  # Physical ID of the resource created
13
19
  #
@@ -92,8 +98,10 @@ module Jackal
92
98
  payload.fetch('Body', 'Message', payload)
93
99
  )
94
100
  )
101
+ payload = transform_parameters(payload)
95
102
  payload[:origin_type] = message[:message].get('Body', 'Type')
96
103
  payload[:origin_subject] = message[:message].get('Body', 'Subject')
104
+ payload[:request_type] = snakecase(payload[:request_type])
97
105
  payload
98
106
  end
99
107
 
@@ -103,10 +111,10 @@ module Jackal
103
111
  # @return [TrueClass, FalseClass]
104
112
  def valid?(message)
105
113
  super do |payload|
106
- resource_type = payload['ResourceType'].split('::').last
114
+ resource_type = payload[:resource_type].split('::').last
107
115
  result = payload[:origin_type] == 'Notification' &&
108
116
  payload[:origin_subject].downcase.include?('cloudformation custom resource') &&
109
- (resource_type == 'CustomResource' || resource_type == self.class.name.split('::').last)
117
+ resource_type == self.class.name.split('::').last
110
118
  if(result && block_given?)
111
119
  yield payload
112
120
  else
@@ -136,12 +144,19 @@ module Jackal
136
144
  def transform_parameters(params)
137
145
  Smash.new.tap do |new_hash|
138
146
  params.each do |key, value|
139
- key = key.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym
140
- new_hash[key] = value
147
+ new_hash[snakecase(key)] = value
141
148
  end
142
149
  end
143
150
  end
144
151
 
152
+ # Snake case string
153
+ #
154
+ # @param v [String]
155
+ # @return [Symbol]
156
+ def snakecase(v)
157
+ v.to_s.gsub(/(?<![A-Z])([A-Z])/, '_\1').sub(/^_/, '').downcase.to_sym
158
+ end
159
+
145
160
  end
146
161
  end
147
162
  end
@@ -1,6 +1,6 @@
1
1
  module Jackal
2
2
  module Cfn
3
3
  # Current version
4
- VERSION = Gem::Version.new('0.1.2')
4
+ VERSION = Gem::Version.new('0.1.4')
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jackal-cfn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-07-04 00:00:00.000000000 Z
12
+ date: 2014-07-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: jackal
@@ -54,6 +54,7 @@ files:
54
54
  - lib/jackal-cfn/version.rb
55
55
  - lib/jackal-cfn/resource.rb
56
56
  - lib/jackal-cfn/resource/hash_extractor.rb
57
+ - lib/jackal-cfn/resource/ami_manager.rb
57
58
  - jackal-cfn.gemspec
58
59
  - README.md
59
60
  - CHANGELOG.md