jackal-cfn 0.1.2 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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