Class: Automan::Cloudformation::Launcher

Inherits:
Base
  • Object
show all
Includes:
Mixins::Utils
Defined in:
lib/automan/cloudformation/launcher.rb

Constant Summary

Constants included from Mixins::AwsCaller

Mixins::AwsCaller::S3_PROTO

Instance Attribute Summary

Attributes inherited from Base

#logger, #wait

Attributes included from Mixins::AwsCaller

#as, #cfn, #eb, #ec, #ec2, #elb, #r53, #rds, #s3

Instance Method Summary collapse

Methods included from Mixins::Utils

#region_from_az

Methods inherited from Base

add_option, #log_options, #wait_until

Methods included from Mixins::AwsCaller

#account, #configure_aws, #looks_like_s3_path?, #parse_s3_path, #s3_object_exists?, #s3_read

Constructor Details

#initialize(options = {}) ⇒ Launcher

Returns a new instance of Launcher.



19
20
21
22
23
24
25
26
27
28
29
# File 'lib/automan/cloudformation/launcher.rb', line 19

def initialize(options={})
  @wait_for_completion = false
  super
  @wait = Wait.new({
    delay: 120,
    attempts: 15, # 15 x 2m == 30m
    debug: true,
    rescuer:  WaitRescuer.new(),
    logger:   @logger
  })
end

Instance Method Details

#launchObject



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/automan/cloudformation/launcher.rb', line 123

def launch
  opts = {
    parameters: parameters
  }
  opts[:capabilities] = ['CAPABILITY_IAM'] if enable_iam
  opts[:disable_rollback] = disable_rollback

  logger.info "launching stack #{name}"
  cfn.stacks.create name, template_handle(template), opts

  if wait_for_completion
    logger.info "waiting for stack #{name} to launch"
    wait_until { stack_launch_complete? }
  end
end

#launch_or_updateObject



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/automan/cloudformation/launcher.rb', line 174

def launch_or_update

  if !manifest.nil?
    read_manifest
  end

  log_options

  validate_parameters

  if stack_exists?

    logger.info "stack #{name} exists"
    if enable_update == true
      update
    else
      raise StackExistsError, "stack #{name} already exists"
    end

  else
    launch
  end

end

#manifest_exists?Boolean

Returns:

  • (Boolean)


31
32
33
# File 'lib/automan/cloudformation/launcher.rb', line 31

def manifest_exists?
  s3_object_exists? manifest
end

#parse_template_parametersObject



60
61
62
# File 'lib/automan/cloudformation/launcher.rb', line 60

def parse_template_parameters
  cfn.validate_template( template_handle(template) )
end

#read_manifestObject



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/automan/cloudformation/launcher.rb', line 35

def read_manifest
  if !manifest_exists?
    raise MissingManifestError, "Manifest #{manifest} does not exist"
  end

  # read manifest from s3 location
  json = s3_read(manifest)

  # parse it from json
  data = JSON.parse json

  # merge manifest parameters into cli parameters
  parameters.merge!(data)
end

#stack_exists?Boolean

Returns:

  • (Boolean)


93
94
95
# File 'lib/automan/cloudformation/launcher.rb', line 93

def stack_exists?
  cfn.stacks[name].exists?
end

#stack_launch_complete?Boolean

Returns:

  • (Boolean)


101
102
103
104
105
106
107
108
109
110
# File 'lib/automan/cloudformation/launcher.rb', line 101

def stack_launch_complete?
  case stack_status
  when 'CREATE_COMPLETE'
    true
  when 'CREATE_FAILED', /^ROLLBACK_/
    raise StackCreationError, "Stack #{name} failed to launch"
  else
    false
  end
end

#stack_statusObject



97
98
99
# File 'lib/automan/cloudformation/launcher.rb', line 97

def stack_status
  cfn.stacks[name].status
end

#stack_update_complete?Boolean

Returns:

  • (Boolean)


112
113
114
115
116
117
118
119
120
121
# File 'lib/automan/cloudformation/launcher.rb', line 112

def stack_update_complete?
  case stack_status
  when 'UPDATE_COMPLETE'
    true
  when 'UPDATE_FAILED', /^UPDATE_ROLLBACK_/
    raise StackUpdateError, "Stack #{name} failed to update"
  else
    false
  end
end

#template_handle(template_path) ⇒ Object



50
51
52
53
54
55
56
57
58
# File 'lib/automan/cloudformation/launcher.rb', line 50

def template_handle(template_path)
  if looks_like_s3_path? template_path
    bucket, key = parse_s3_path template_path
    o = s3.buckets[bucket].objects[key]
    return o.public_url().to_s()
  else
    return File.read(template_path)
  end
end

#updateObject



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
168
169
170
171
172
# File 'lib/automan/cloudformation/launcher.rb', line 143

def update
  opts = {
    template: template_handle(template),
    parameters: parameters
  }
  opts[:capabilities] = ['CAPABILITY_IAM'] if enable_iam

  logger.info "updating stack #{name}"

  no_updates_needed = false

  # if cfn determines that no updates are to be performed,
  # it raises a ValidationError
  begin
    update_stack(name, opts)
  rescue AWS::CloudFormation::Errors::ValidationError => e
    if e.message != "No updates are to be performed."
      raise e
    else
      logger.info e.message
      no_updates_needed = true
    end
  end

  if !no_updates_needed && wait_for_completion
    logger.info "waiting for stack #{name} to update"
    wait_until { stack_update_complete? }
  end

end

#update_stack(name, opts) ⇒ Object



139
140
141
# File 'lib/automan/cloudformation/launcher.rb', line 139

def update_stack(name, opts)
  cfn.stacks[name].update( opts )
end

#validate_parametersObject



64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/automan/cloudformation/launcher.rb', line 64

def validate_parameters
  if parameters.nil?
    raise MissingParametersError, "there are no parameters!"
  end

  template_params_hash = parse_template_parameters

  if template_params_hash.has_key? :code
    mesg = template_params_hash[:message]
    raise BadTemplateError, "template did not validate: #{mesg}"
  end

  # make sure any template parameters w/o a default are specified
  template_params = template_params_hash[:parameters]
  required_params = template_params.select { |x| !x.has_key? :default_value}
  required_params.each do |rp|
    unless parameters.has_key? rp[:parameter_key]
      raise MissingParametersError, "required parameter #{rp[:parameter_key]} was not specified"
    end
  end

  # add iam capabilities if needed
  if template_params_hash.has_key?(:capabilities) &&
    template_params_hash[:capabilities].include?('CAPABILITY_IAM')
    self.enable_iam = true
  end

end