Module: Stacker

Extended by:
Stacker
Included in:
Stacker
Defined in:
lib/autostacker24/stacker.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#credentialsObject

Returns the value of attribute credentials.



8
9
10
# File 'lib/autostacker24/stacker.rb', line 8

def credentials
  @credentials
end

#regionObject

Returns the value of attribute region.



8
9
10
# File 'lib/autostacker24/stacker.rb', line 8

def region
  @region
end

Instance Method Details

#all_stack_namesObject



111
112
113
# File 'lib/autostacker24/stacker.rb', line 111

def all_stack_names
  all_stacks.map{|s| s.stack_name}
end

#all_stacksObject



115
116
117
# File 'lib/autostacker24/stacker.rb', line 115

def all_stacks
  cloud_formation.describe_stacks.stacks
end

#cloud_formationObject

lazy CloudFormation client



148
149
150
151
152
153
154
155
156
# File 'lib/autostacker24/stacker.rb', line 148

def cloud_formation # lazy CloudFormation client
  unless @lazy_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

#create_or_update_stack(stack_name, template, parameters, parent_stack_name = nil) ⇒ Object



25
26
27
28
29
30
31
# File 'lib/autostacker24/stacker.rb', line 25

def create_or_update_stack(stack_name, template, parameters, parent_stack_name = nil)
  if find_stack(stack_name).nil?
    create_stack(stack_name, template, parameters, parent_stack_name)
  else
    update_stack(stack_name, template, parameters, parent_stack_name)
  end
end

#create_stack(stack_name, template, parameters, parent_stack_name = nil) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/autostacker24/stacker.rb', line 33

def create_stack(stack_name, template, parameters, parent_stack_name = nil)
  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'])
  wait_for_stack(stack_name, :create)
end

#delete_stack(stack_name) ⇒ Object



80
81
82
83
# File 'lib/autostacker24/stacker.rb', line 80

def delete_stack(stack_name)
  cloud_formation.delete_stack(stack_name: stack_name)
  wait_for_stack(stack_name, :delete)
end

#estimate_template_cost(template, parameters) ⇒ Object



119
120
121
# File 'lib/autostacker24/stacker.rb', line 119

def estimate_template_cost(template, parameters)
  cloud_formation.estimate_template_cost(:template_body => template_body(template), :parameters => transform_input(parameters))
end

#find_stack(stack_name) ⇒ Object



104
105
106
107
108
109
# File 'lib/autostacker24/stacker.rb', line 104

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.message =~ /does not exist/i # may be flaky, do more research in API
  nil
end

#get_stack_output(stack_name) ⇒ Object



128
129
130
131
132
# File 'lib/autostacker24/stacker.rb', line 128

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



123
124
125
126
# File 'lib/autostacker24/stacker.rb', line 123

def get_stack_outputs(stack_name)
  puts 'get_stack_outputs is obsolete, please use get_stack_output'
  get_stack_output(stack_name)
end

#get_stack_resources(stack_name) ⇒ Object



143
144
145
146
# File 'lib/autostacker24/stacker.rb', line 143

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

#template_body(template) ⇒ Object



158
159
160
161
# File 'lib/autostacker24/stacker.rb', line 158

def template_body(template)
  template = File.read(template) if File.exists?(template)
  AutoStacker24::Preprocessor.preprocess(template)
end

#transform_input(input) ⇒ Object



138
139
140
141
# File 'lib/autostacker24/stacker.rb', line 138

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



134
135
136
# File 'lib/autostacker24/stacker.rb', line 134

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) ⇒ Object



43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/autostacker24/stacker.rb', line 43

def update_stack(stack_name, template, parameters, parent_stack_name = nil)
  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'])
  rescue Aws::CloudFormation::Errors::ValidationError => error
    raise error unless error.message =~ /No updates are to be performed/i # may be flaky, do more research in API
    find_stack(stack_name)
  else
    wait_for_stack(stack_name, :update)
  end
end

#validate_template(template) ⇒ Object



76
77
78
# File 'lib/autostacker24/stacker.rb', line 76

def validate_template(template)
  cloud_formation.validate_template(template_body: template_body(template))
end

#wait_for_stack(stack_name, operation, timeout_in_minutes = 15) ⇒ Object



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/autostacker24/stacker.rb', line 85

def wait_for_stack(stack_name, operation, timeout_in_minutes = 15)
  stop_time = Time.now + timeout_in_minutes * 60
  finished = /(CREATE_COMPLETE|UPDATE_COMPLETE|DELETE_COMPLETE|ROLLBACK_COMPLETE|ROLLBACK_FAILED|CREATE_FAILED|DELETE_FAILED)$/
  while Time.now < stop_time
    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
    return true if status =~ expected_status
    raise "#{operation} #{stack_name} failed, current status #{status}" if status =~ finished
    puts "waiting for #{operation} stack #{stack_name}, current status #{status}"
    sleep(7)
  end
  raise "waiting for #{operation} stack #{stack_name} timed out after #{timeout_in_minutes} minutes"
end