Class: Ufo::Stack

Inherits:
Object
  • Object
show all
Extended by:
Memoist
Includes:
Settings, Helper
Defined in:
lib/ufo/stack/status.rb,
lib/ufo/stack.rb,
lib/ufo/stack/helper.rb,
lib/ufo/stack/builder.rb,
lib/ufo/stack/context.rb,
lib/ufo/stack/template_body.rb,
lib/ufo/stack/custom_properties.rb

Overview

CloudFormation status codes, full list:

https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/using-cfn-describing-stacks.html

CREATE_COMPLETE
ROLLBACK_COMPLETE
DELETE_COMPLETE
UPDATE_COMPLETE
UPDATE_ROLLBACK_COMPLETE

CREATE_FAILED
DELETE_FAILED
ROLLBACK_FAILED
UPDATE_ROLLBACK_FAILED

CREATE_IN_PROGRESS
DELETE_IN_PROGRESS
REVIEW_IN_PROGRESS
ROLLBACK_IN_PROGRESS
UPDATE_COMPLETE_CLEANUP_IN_PROGRESS
UPDATE_IN_PROGRESS
UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS
UPDATE_ROLLBACK_IN_PROGRESS

Defined Under Namespace

Modules: Helper Classes: Builder, Context, CustomProperties, Status, TemplateBody

Instance Method Summary collapse

Methods included from Settings

#cfn, #network, #settings

Methods included from Helper

#adjust_stack_name, #find_stack, #status

Methods included from Util

#default_cluster, #display_params, #execute, #pretty_time, #settings, #task_definition_arns, #user_params

Methods included from AwsService

#cloudformation, #cloudwatchlogs, #ec2, #ecr, #ecs, #elb

Constructor Details

#initialize(options) ⇒ Stack

Returns a new instance of Stack.



30
31
32
33
34
35
36
37
# File 'lib/ufo/stack.rb', line 30

def initialize(options)
  @options = options
  @task_definition = options[:task_definition]
  @rollback = options[:rollback]
  @service = options[:service]
  @cluster = @options[:cluster] || default_cluster(@service)
  @stack_name = adjust_stack_name(@cluster, options[:service])
end

Instance Method Details

#contextObject



127
128
129
130
131
132
133
134
135
# File 'lib/ufo/stack.rb', line 127

def context
  o = @options.merge(
    cluster: @cluster,
    stack_name: @stack_name,
    stack: @stack,
  )
  o[:rollback_definition_arn] = rollback_definition_arn if rollback_definition_arn
  Context.new(o)
end

#current_desired_countObject



143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/ufo/stack.rb', line 143

def current_desired_count
  # Cannot set ECS desired count when is scheduling_strategy DAEMON
  return '' if scheduling_strategy == "DAEMON"

  info = Info.new(@service, @options)
  service = info.service
  if service
    service.desired_count.to_s
  else
    "1" # new service default
  end
end

#deployObject



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/ufo/stack.rb', line 39

def deploy
  @stack = find_stack(@stack_name)
  if @stack && rollback_complete?(@stack)
    puts "Existing stack in ROLLBACK_COMPLETE state. Deleting stack before continuing."
    cloudformation.delete_stack(stack_name: @stack_name)
    status.wait
    status.reset
    @stack = nil # at this point stack has been deleted
  end

  exit_with_message(@stack) if @stack && !updatable?(@stack)

  @stack ? perform(:update) : perform(:create)

  stop_old_tasks if @options[:stop_old_task]

  return unless @options[:wait]
  status.wait

  puts status.rollback_error_message if status.update_rollback?

  status.success?
end

#exit_with_message(stack) ⇒ Object



175
176
177
178
179
180
181
# File 'lib/ufo/stack.rb', line 175

def exit_with_message(stack)
  region = `aws configure get region`.strip rescue "us-east-1"
  url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}#/stacks"
  puts "The stack is not in an updateable state: #{stack.stack_status.color(:yellow)}."
  puts "Here's the CloudFormation url to check for more details #{url}"
  exit 1
end

#handle_stack_error(e) ⇒ Object

Assume only first container_definition to get the container info. Stack:arn:aws:cloudformation:… is in ROLLBACK_COMPLETE state and can not be updated.



185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/ufo/stack.rb', line 185

def handle_stack_error(e)
  case e.message
  when /state and can not be updated/
    puts "The #{@stack_name} stack is in a state that cannot be updated. Deleted the stack and try again."
    puts "ERROR: #{e.message}"
    if message.include?('UPDATE_ROLLBACK_FAILED')
      puts "You might be able to do a 'Continue Update Rollback' and skip some resources to get the stack back into a good state."
    end
    region = `aws configure get region`.strip rescue 'us-east-1'
    url = "https://console.aws.amazon.com/cloudformation/home?region=#{region}"
    puts "Here's the CloudFormation console url: #{url}"
    exit 1
  when /No updates are to be performed/
    puts "There are no updates to be performed. Exiting.".color(:yellow)
    exit 1
  else
    raise
  end
end

#notification_arnsObject



86
87
88
# File 'lib/ufo/stack.rb', line 86

def notification_arns
  settings[:notification_arns] || []
end

#parametersObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/ufo/stack.rb', line 90

def parameters
  create_elb, elb_target_group = context.elb_options

  elb_subnets = network[:elb_subnets] && !network[:elb_subnets].empty? ?
                network[:elb_subnets] :
                network[:ecs_subnets]

  params = {
    Vpc: network[:vpc],
    ElbSubnets: elb_subnets.join(','),
    EcsSubnets: network[:ecs_subnets].join(','),

    CreateElb: create_elb,
    ElbTargetGroup: elb_target_group,
    ElbEipIds: context.elb_eip_ids,

    EcsDesiredCount: current_desired_count,
    EcsSchedulingStrategy: scheduling_strategy,
  }

  params = Ufo::Utils::Squeezer.new(params).squeeze
  params.map do |k,v|
    if v == :use_previous_value
      { parameter_key: k, use_previous_value: true }
    else
      { parameter_key: k, parameter_value: v }
    end
  end
end

#perform(action) ⇒ Object



63
64
65
66
67
68
69
# File 'lib/ufo/stack.rb', line 63

def perform(action)
  puts "#{action[0..-2].capitalize}ing stack #{@stack_name.color(:green)}..."
  # Example: cloudformation.send("update_stack", stack_options)
  cloudformation.send("#{action}_stack", stack_options)
rescue Aws::CloudFormation::Errors::ValidationError => e
  handle_stack_error(e)
end

#rollback_complete?(stack) ⇒ Boolean

Returns:

  • (Boolean)


205
206
207
# File 'lib/ufo/stack.rb', line 205

def rollback_complete?(stack)
  stack.stack_status == 'ROLLBACK_COMPLETE'
end

#rollback_definition_arnObject



156
157
158
159
160
# File 'lib/ufo/stack.rb', line 156

def rollback_definition_arn
  return unless @rollback
  resp = ecs.describe_task_definition(task_definition: @task_definition)
  resp.task_definition.task_definition_arn
end

#save_templateObject

Store template in tmp in case for debugging



164
165
166
167
168
169
170
171
172
173
# File 'lib/ufo/stack.rb', line 164

def save_template
  path = "/tmp/ufo/#{@stack_name}/stack.yml"
  FileUtils.mkdir_p(File.dirname(path))
  IO.write(path, template_body)
  puts "Generated template saved at: #{path}"

  path = "/tmp/ufo/#{@stack_name}/parameters.yml"
  IO.write(path, JSON.pretty_generate(parameters))
  puts "Generated parameters saved at: #{path}"
end

#scheduling_strategyObject



138
139
140
141
# File 'lib/ufo/stack.rb', line 138

def scheduling_strategy
  strategy = @options[:scheduling_strategy] || context.scheduling_strategy
  strategy.upcase
end

#stack_optionsObject



71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/ufo/stack.rb', line 71

def stack_options
  save_template
  if ENV['SAVE_TEMPLATE_EXIT']
    puts "Template saved. Exiting."
    exit 1
  end
  {
    capabilities: ["CAPABILITY_IAM"],
    notification_arns: notification_arns,
    parameters: parameters,
    stack_name: @stack_name,
    template_body: template_body,
  }
end

#template_bodyObject

do not memoize template_body it can change for a rename retry



122
123
124
# File 'lib/ufo/stack.rb', line 122

def template_body
  TemplateBody.new(context).build
end

#updatable?(stack) ⇒ Boolean

Returns:

  • (Boolean)


209
210
211
# File 'lib/ufo/stack.rb', line 209

def updatable?(stack)
  stack.stack_status =~ /_COMPLETE$/ || stack.stack_status == 'UPDATE_ROLLBACK_FAILED'
end