Class: Jackal::Cfn::JackalStack
- Defined in:
- lib/jackal-cfn/resource/jackal_stack.rb
Overview
Manage AMI Resources
Expected resource:
{
"Type": "Custom::JackalStack",
"Properties": {
"Parameters": {
STACK_PARAMETERS
},
"Location": LOCATION,
"TemplateURL": "URL"
}
}
Required configuration:
{
"config": {
"jackal_stack": {
"credentials": {
"storage": {
AWS_CREDENTIALS
},
LOCATION: {
"provider": "NAME",
MIAMSA_CREDENTIALS
}
}
}
}
}
Constant Summary collapse
- LOCATION_JOINER =
'__~__'
Constants inherited from Resource
Resource::VALID_RESOURCE_STATUS
Instance Method Summary collapse
-
#create_stack(response, resource, properties, parameters, message) ⇒ TrueClass, FalseClass
Create a new stack and update the response values.
-
#destroy_stack(response, resource, message) ⇒ Object
Destroy the stack.
-
#execute(message) ⇒ Object
Perform requested stack action.
-
#fetch_template(endpoint) ⇒ Hash
Fetch a template from a storage bucket.
-
#generate_stack_name(resource) ⇒ String
Generate the remote stack name via the resource information.
-
#remote_api(location) ⇒ Miasma::Models::Orchestration
Build orchestration API connection for provided location.
-
#request_destroy(stack_resource_id) ⇒ Miasma::Models::Orchestration::Stack, FalseClass
Send a stack delete request.
-
#setup(*_) ⇒ Object
Load miasma for stack building.
-
#storage_api(bucket_region) ⇒ Miasma::Models::Storage
Build API connection to base template storage bucket.
-
#update_stack(response, resource, properties, parameters, message) ⇒ TrueClass, FalseClass
Update an existing stack and update the response values.
Methods inherited from Resource
#build_response, #failure_wrap, inherited, #physical_resource_id, #respond_to_stack, #unpack, #valid?
Methods included from Utils::Http
Methods included from Utils
#snakecase, #transform_parameters
Instance Method Details
#create_stack(response, resource, properties, parameters, message) ⇒ TrueClass, FalseClass
Create a new stack and update the response values
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 139 def create_stack(response, resource, properties, parameters, ) stack = remote_api(properties[:location]).stacks.build( :name => generate_stack_name(resource), :template => properties.fetch(:stack, fetch_template(properties[:template_url])), :parameters => Hash[parameters.map{|k,v| [Bogo::Utility.camel(k), v] }] ) stack.save until(stack.state.to_s.end_with?('complete')) .touch! debug "Waiting for created stack to reach completion..." sleep 5 stack.reload end if(stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed')) stack.outputs.each do |output| response['Data']["Outputs.#{output.key}"] = output.value end response['PhysicalResourceId'] = [ properties[:location], stack.id ].join(LOCATION_JOINER) true else response['Status'] = 'FAILED' response['Reason'] = 'Stack creation failed!' stack.destroy false end end |
#destroy_stack(response, resource, message) ⇒ Object
Destroy the stack
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 221 def destroy_stack(response, resource, ) stack = request_destroy(resource[:physical_resource_id]) unless(stack) properties = rekey_hash(resource[:resource_properties]) stack = request_destroy( [ properties[:location], generate_stack_name(resource) ].join(LOCATION_JOINER) ) end if(stack) until(stack.state.nil? || stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed')) info "Waiting for stack destruction (#{stack.name})..." .touch! sleep 5 stack.reload end if(stack.state.to_s.end_with?('failed')) response['Status'] = 'FAILED' response['Reason'] = 'Failed to delete remote stack!' end end end |
#execute(message) ⇒ Object
Perform requested stack action
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 47 def execute() failure_wrap() do |payload| cfn_resource = rekey_hash(payload.get(:data, :cfn_resource)) properties = rekey_hash(cfn_resource[:resource_properties]) parameters = rekey_hash(properties[:parameters]) cfn_response = build_response(cfn_resource) case cfn_resource[:request_type].to_sym when :create create_stack(cfn_response, cfn_resource, properties, parameters, ) when :update update_stack(cfn_response, cfn_resource, properties, parameters, ) when :delete destroy_stack(cfn_response, cfn_resource, ) else error "Unknown request type received: #{cfn_resource[:request_type].inspect}" cfn_response['Status'] = 'FAILED' cfn_response['Reason'] = 'Unknown request type received' end respond_to_stack(cfn_response, cfn_resource[:response_url]) job_completed(:jackal_cfn, payload, ) end end |
#fetch_template(endpoint) ⇒ Hash
Fetch a template from a storage bucket
105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 105 def fetch_template(endpoint) url = URI.parse(endpoint) region = url.host.split('.').first.split('-', 2).last if(region == 's3') region = 'us-east-1' end bucket, path = url.path.sub('/', '').split('/', 2) MultiJson.load( storage_api(region).buckets.get( bucket.sub('/', '') ).files.get(path).body.read ) end |
#generate_stack_name(resource) ⇒ String
Generate the remote stack name via the resource information
123 124 125 126 127 128 129 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 123 def generate_stack_name(resource) [ 'JackalStack', resource[:logical_resource_id], resource[:stack_id].split('/').last ].join('-') end |
#remote_api(location) ⇒ Miasma::Models::Orchestration
Build orchestration API connection for provided location
88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 88 def remote_api(location) l_config = config.get(:jackal_stack, :credentials, location) if(l_config) Miasma.api( :type => :orchestration, :provider => l_config[:provider], :credentials => l_config ) else raise ArgumentError.new "Unknown target location provided `#{location}`!" end end |
#request_destroy(stack_resource_id) ⇒ Miasma::Models::Orchestration::Stack, FalseClass
Send a stack delete request
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 250 def request_destroy(stack_resource_id) location, stack_id = stack_resource_id.split(LOCATION_JOINER, 2) if(stack_id) begin info "Sending stack destruction request to: #{stack_id} in: #{location}" stack = remote_api(location).stacks.get(stack_id) stack.destroy stack rescue => e error "Stack destruction request failed! #{e.class}: #{e.}" false end else warn "No stack ID registered in resource. Skipping destroy: #{stack_resource_id}" false end end |
#setup(*_) ⇒ Object
Load miasma for stack building
40 41 42 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 40 def setup(*_) require 'miasma' end |
#storage_api(bucket_region) ⇒ Miasma::Models::Storage
Build API connection to base template storage bucket
74 75 76 77 78 79 80 81 82 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 74 def storage_api(bucket_region) Miasma.api( :type => :storage, :provider => :aws, :credentials => config.get(:jackal_stack, :credentials, :storage).merge( :aws_bucket_region => bucket_region ) ) end |
#update_stack(response, resource, properties, parameters, message) ⇒ TrueClass, FalseClass
Update an existing stack and update the response values
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/jackal-cfn/resource/jackal_stack.rb', line 177 def update_stack(response, resource, properties, parameters, ) c_location, stack_id = resource[:physical_resource_id].split('-', 2) if(c_location != properties[:location]) warn "Stack resource has changed location! #{c_location} -> #{properties[:location]}" warn "Starting destruction of existing resource: #{stack_id}" if(destroy_stack(response, resource, )) info "Destruction of stack `#{stack_id}` complete. Creating replacement stack." create_stack(response, resource, properties, parameters, ) else error "Failed to destroy existing stack for replacement `#{stack_id}`" end else stack = remote_api(c_location).stacks.get(stack_id) if(stack) info "Stack resource update on: #{stack_id}" stack.template = fetch_template(properties['TemplateURL']) stack.parameters = Hash[parameters.map{|k,v| [Bogo::Utility.camel(k), v] }] stack.save until(stack.state.to_s.end_with?('complete') || stack.state.to_s.end_with?('failed')) debug "Waiting for created stack to reach completion..." sleep 5 stack.reload end if(stack.state.to_s.end_with?('complete')) stack.outputs.each do |output| response['Data']["Outputs.#{output.key}"] = output.value end response['PhysicalResourceId'] = stack.id else response['Status'] = 'FAILED' response['Reason'] = 'Stack update failed!' end else response['Status'] = 'FAILED' response['Reason'] = "No stack was found matching request: #{stack_id}" end end end |