Module: Stacker
Instance Attribute Summary collapse
-
#credentials ⇒ Object
Returns the value of attribute credentials.
-
#region ⇒ Object
Returns the value of attribute region.
Instance Method Summary collapse
- #all_stack_names ⇒ Object
- #all_stacks ⇒ Object
-
#cloud_formation ⇒ Object
lazy CloudFormation client.
-
#cloud_formation_params=(params) ⇒ Object
set custom cloudformation parameters.
- #create_or_update_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
- #create_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
- #delete_stack(stack_name, timeout_in_minutes = 60) ⇒ Object
- #estimate_template_cost(template, parameters, parent_stack_name = nil) ⇒ Object
- #find_stack(stack_name) ⇒ Object
- #get_stack_events(stack_name_or_id) ⇒ Object
- #get_stack_output(stack_name) ⇒ Object
- #get_stack_outputs(stack_name) ⇒ Object
- #get_stack_resources(stack_name) ⇒ Object
- #get_template(stack_name) ⇒ Object
- #template_body(template) ⇒ Object
- #transform_input(input) ⇒ Object
- #transform_output(output) ⇒ Object
- #update_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
- #validate_template(template) ⇒ Object
- #wait_for_stack(stack_name, operation, seen_events = Set.new, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
Instance Attribute Details
#credentials ⇒ Object
Returns the value of attribute credentials.
14 15 16 |
# File 'lib/autostacker24/stacker.rb', line 14 def credentials @credentials end |
#region ⇒ Object
Returns the value of attribute region.
14 15 16 |
# File 'lib/autostacker24/stacker.rb', line 14 def region @region end |
Instance Method Details
#all_stack_names ⇒ Object
141 142 143 |
# File 'lib/autostacker24/stacker.rb', line 141 def all_stack_names all_stacks.map{|s| s.stack_name} end |
#all_stacks ⇒ Object
145 146 147 |
# File 'lib/autostacker24/stacker.rb', line 145 def all_stacks cloud_formation.describe_stacks.stacks end |
#cloud_formation ⇒ Object
lazy CloudFormation client
182 183 184 185 186 187 188 189 190 |
# File 'lib/autostacker24/stacker.rb', line 182 def cloud_formation # lazy CloudFormation client unless @lazy_cloud_formation params = @cloud_formation_params || {} params[:credentials] = @credentials if @credentials params[:region] = @region if @region @lazy_cloud_formation = Aws::CloudFormation::Client.new(params) end @lazy_cloud_formation end |
#cloud_formation_params=(params) ⇒ Object
set custom cloudformation parameters
32 33 34 35 36 37 |
# File 'lib/autostacker24/stacker.rb', line 32 def cloud_formation_params=(params) unless params == @cloud_formation_params @lazy_cloud_formation = nil @cloud_formation_params = params end end |
#create_or_update_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
39 40 41 42 43 44 45 |
# File 'lib/autostacker24/stacker.rb', line 39 def create_or_update_stack(stack_name, template, parameters, parent_stack_name = nil, = nil, timeout_in_minutes = DEFAULT_TIMEOUT) if find_stack(stack_name).nil? create_stack(stack_name, template, parameters, parent_stack_name, , timeout_in_minutes) else update_stack(stack_name, template, parameters, parent_stack_name, , timeout_in_minutes) end end |
#create_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
47 48 49 50 51 52 53 54 55 56 |
# File 'lib/autostacker24/stacker.rb', line 47 def create_stack(stack_name, template, parameters, parent_stack_name = nil, = nil, timeout_in_minutes = DEFAULT_TIMEOUT) merge_and_validate(template, parameters, parent_stack_name) cloud_formation.create_stack(stack_name: stack_name, template_body: template_body(template), on_failure: 'DELETE', parameters: transform_input(parameters), capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'], tags: ) wait_for_stack(stack_name, :create, Set.new, timeout_in_minutes) end |
#delete_stack(stack_name, timeout_in_minutes = 60) ⇒ Object
98 99 100 101 102 |
# File 'lib/autostacker24/stacker.rb', line 98 def delete_stack(stack_name, timeout_in_minutes = 60) seen_events = get_stack_events(stack_name).map {|e| e[:event_id]} cloud_formation.delete_stack(stack_name: stack_name) wait_for_stack(stack_name, :delete, seen_events, timeout_in_minutes) end |
#estimate_template_cost(template, parameters, parent_stack_name = nil) ⇒ Object
149 150 151 152 |
# File 'lib/autostacker24/stacker.rb', line 149 def estimate_template_cost(template, parameters, parent_stack_name = nil) merge_and_validate(template, parameters, parent_stack_name) cloud_formation.estimate_template_cost(:template_body => template_body(template), :parameters => transform_input(parameters)) end |
#find_stack(stack_name) ⇒ Object
134 135 136 137 138 139 |
# File 'lib/autostacker24/stacker.rb', line 134 def find_stack(stack_name) cloud_formation.describe_stacks(stack_name: stack_name).stacks.first rescue Aws::CloudFormation::Errors::ValidationError => error raise error unless error. =~ /does not exist/i # may be flaky, do more research in API nil end |
#get_stack_events(stack_name_or_id) ⇒ Object
178 179 180 |
# File 'lib/autostacker24/stacker.rb', line 178 def get_stack_events(stack_name_or_id) cloud_formation.describe_stack_events(stack_name: stack_name_or_id).data.stack_events end |
#get_stack_output(stack_name) ⇒ Object
158 159 160 161 162 |
# File 'lib/autostacker24/stacker.rb', line 158 def get_stack_output(stack_name) stack = find_stack(stack_name) raise "stack #{stack_name} not found" unless stack transform_output(stack.outputs).freeze end |
#get_stack_outputs(stack_name) ⇒ Object
154 155 156 |
# File 'lib/autostacker24/stacker.rb', line 154 def get_stack_outputs(stack_name) get_stack_output(stack_name) end |
#get_stack_resources(stack_name) ⇒ Object
173 174 175 176 |
# File 'lib/autostacker24/stacker.rb', line 173 def get_stack_resources(stack_name) resources = cloud_formation.describe_stack_resources(stack_name: stack_name).data.stack_resources resources.inject({}){|map, resource| map.merge(resource.logical_resource_id.to_sym => resource)}.freeze end |
#get_template(stack_name) ⇒ Object
130 131 132 |
# File 'lib/autostacker24/stacker.rb', line 130 def get_template(stack_name) cloud_formation.get_template(stack_name: stack_name).template_body end |
#template_body(template) ⇒ Object
192 193 194 195 196 197 198 199 200 201 202 |
# File 'lib/autostacker24/stacker.rb', line 192 def template_body(template) wd = Dir.getwd() if File.exists?(template) # template is a valid filename td = File.dirname(template) template = File.read(template) Dir.chdir(td) end AutoStacker24::Preprocessor.preprocess(template) ensure Dir.chdir(wd) end |
#transform_input(input) ⇒ Object
168 169 170 171 |
# File 'lib/autostacker24/stacker.rb', line 168 def transform_input(input) input.each{|k,v| raise "#{k} must not be nil" if v.nil? } input.inject([]) { |m, kv| m << {parameter_key: kv[0].to_s, parameter_value: kv[1].to_s} } end |
#transform_output(output) ⇒ Object
164 165 166 |
# File 'lib/autostacker24/stacker.rb', line 164 def transform_output(output) output.inject({}) { |m, o| m.merge(o.output_key.to_sym => o.output_value) } end |
#update_stack(stack_name, template, parameters, parent_stack_name = nil, tags = nil, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/autostacker24/stacker.rb', line 58 def update_stack(stack_name, template, parameters, parent_stack_name = nil, = nil, timeout_in_minutes = DEFAULT_TIMEOUT) seen_events = get_stack_events(stack_name).map {|e| e[:event_id]} begin merge_and_validate(template, parameters, parent_stack_name) cloud_formation.update_stack(stack_name: stack_name, template_body: template_body(template), parameters: transform_input(parameters), capabilities: ['CAPABILITY_IAM', 'CAPABILITY_NAMED_IAM'], tags: ) rescue Aws::CloudFormation::Errors::ValidationError => error raise error unless error. =~ /No updates are to be performed/i # may be flaky, do more research in API puts "stack #{stack_name} is already up to date" find_stack(stack_name) else wait_for_stack(stack_name, :update, seen_events, timeout_in_minutes) end end |
#validate_template(template) ⇒ Object
94 95 96 |
# File 'lib/autostacker24/stacker.rb', line 94 def validate_template(template) cloud_formation.validate_template(template_body: template_body(template)) end |
#wait_for_stack(stack_name, operation, seen_events = Set.new, timeout_in_minutes = DEFAULT_TIMEOUT) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/autostacker24/stacker.rb', line 104 def wait_for_stack(stack_name, operation, seen_events = Set.new, timeout_in_minutes = DEFAULT_TIMEOUT) stop_time = Time.now + timeout_in_minutes * 60 finished = /(CREATE_COMPLETE|UPDATE_COMPLETE|DELETE_COMPLETE|ROLLBACK_COMPLETE|ROLLBACK_FAILED|CREATE_FAILED|DELETE_FAILED)$/ puts "waiting for #{operation} stack #{stack_name}" stack_id = find_stack(stack_name)[:stack_id] while Time.now < stop_time sleep(5) stack = find_stack(stack_name) status = stack ? stack.stack_status : 'DELETE_COMPLETE' expected_status = case operation when :create then /CREATE_COMPLETE$/ when :update then /UPDATE_COMPLETE$/ when :delete then /DELETE_COMPLETE$/ end new_events = get_stack_events(stack_id).select{|e| !seen_events.include?(e[:event_id])}.sort_by{|e| e[:timestamp]} new_events.each do |e| seen_events << e[:event_id] puts "#{e[:timestamp]}\t#{e[:resource_status].ljust(20)}\t#{e[:resource_type].ljust(40)}\t#{e[:logical_resource_id].ljust(30)}\t#{e[:resource_status_reason]}" end return true if status =~ expected_status raise "#{operation} #{stack_name} failed, current status #{status}" if status =~ finished end raise "waiting for #{operation} stack #{stack_name} timed out after #{timeout_in_minutes} minutes" end |