Class: MiasmaTerraform::Stack

Inherits:
Object
  • Object
show all
Defined in:
lib/miasma-terraform/stack.rb

Defined Under Namespace

Classes: Action, Error

Constant Summary collapse

REQUIRED_ATTRIBUTES =
[:name, :container]
@@running_actions =
[]

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Stack

Returns a new instance of Stack.



205
206
207
208
209
210
211
212
213
214
# File 'lib/miasma-terraform/stack.rb', line 205

def initialize(opts={})
  @options = opts.to_smash
  init!
  @actions = []
  @name = @options[:name]
  @container = @options[:container]
  @scrub_destroyed = @options.fetch(:scrub_destroyed, false)
  @directory = File.join(container, name)
  @bin = @options.fetch(:bin, 'terraform')
end

Instance Attribute Details

#actionsObject (readonly)

Returns the value of attribute actions.



198
199
200
# File 'lib/miasma-terraform/stack.rb', line 198

def actions
  @actions
end

#binObject (readonly)

Returns the value of attribute bin.



202
203
204
# File 'lib/miasma-terraform/stack.rb', line 202

def bin
  @bin
end

#containerObject (readonly)

Returns the value of attribute container.



200
201
202
# File 'lib/miasma-terraform/stack.rb', line 200

def container
  @container
end

#directoryObject (readonly)

Returns the value of attribute directory.



199
200
201
# File 'lib/miasma-terraform/stack.rb', line 199

def directory
  @directory
end

#nameObject (readonly)

Returns the value of attribute name.



201
202
203
# File 'lib/miasma-terraform/stack.rb', line 201

def name
  @name
end

#scrub_destroyedObject (readonly)

Returns the value of attribute scrub_destroyed.



203
204
205
# File 'lib/miasma-terraform/stack.rb', line 203

def scrub_destroyed
  @scrub_destroyed
end

Class Method Details

.cleanup_actions!Object

Wait for all actions to complete



32
33
34
35
# File 'lib/miasma-terraform/stack.rb', line 32

def self.cleanup_actions!
  @@running_actions.map(&:complete!)
  nil
end

.deregister_action(action) ⇒ NilClass

Deregister action from at exit cleanup

Parameters:

Returns:

  • (NilClass)


26
27
28
29
# File 'lib/miasma-terraform/stack.rb', line 26

def self.deregister_action(action)
  @@running_actions.delete(action)
  nil
end

.list(container) ⇒ Array<String>

Returns:

  • (Array<String>)


38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/miasma-terraform/stack.rb', line 38

def self.list(container)
  if(container.to_s.empty?)
    raise ArgumentError.new 'Container directory must be set!'
  end
  if(File.directory?(container))
    Dir.new(container).map do |entry|
      next if entry.start_with?('.')
      entry if File.directory?(File.join(container, entry))
    end.compact
  else
    []
  end
end

.register_action(action) ⇒ NilClass

Register action to be cleaned up at exit

Parameters:

Returns:

  • (NilClass)


15
16
17
18
19
20
# File 'lib/miasma-terraform/stack.rb', line 15

def self.register_action(action)
  unless(@@running_actions.include?(action))
    @@running_actions.push(action)
  end
  nil
end

Instance Method Details

#active?TrueClass, FalseClass

Returns stack is currently active.

Returns:

  • (TrueClass, FalseClass)

    stack is currently active



222
223
224
225
226
# File 'lib/miasma-terraform/stack.rb', line 222

def active?
  actions.any? do |action|
    action.waiter.alive?
  end
end

#destroy!TrueClass

Returns destroy this stack.

Returns:

  • (TrueClass)

    destroy this stack



373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
# File 'lib/miasma-terraform/stack.rb', line 373

def destroy!
  must_exist do
    lock_stack
    action = run_action('destroy -force')
    action.on_start do |_|
      update_info do |info|
        info[:state] = "delete_in_progress"
        info
      end
    end
    action.on_complete do |*_|
      unlock_stack
    end
    action.on_complete do |result, _|
      unless(result.success?)
        update_info do |info|
          info[:state] = "delete_failed"
          info
        end
      else
        update_info do |info|
          info[:state] = "delete_complete"
          info
        end
        FileUtils.rm_rf(directory) if scrub_destroyed
      end
    end
    action.start!
  end
  true
end

#eventsArray<Hash>

Returns events list.

Returns:

  • (Array<Hash>)

    events list



297
298
299
300
301
302
303
304
305
306
307
# File 'lib/miasma-terraform/stack.rb', line 297

def events
  must_exist do
    load_info.fetch(:events, []).map do |item|
      new_item = item.dup
      parts = item[:resource_name].to_s.split('.')
      new_item[:resource_name] = parts[1]
      new_item[:resource_type] = parts[0]
      new_item
    end
  end
end

#exists?TrueClass, FalseClass

Returns stack currently exists.

Returns:

  • (TrueClass, FalseClass)

    stack currently exists



217
218
219
# File 'lib/miasma-terraform/stack.rb', line 217

def exists?
  File.directory?(directory)
end

#infoHash

Returns stack information.

Returns:

  • (Hash)

    stack information



339
340
341
342
343
344
345
346
347
348
349
350
351
352
# File 'lib/miasma-terraform/stack.rb', line 339

def info
  must_exist do
    stack_data = load_info
    Smash.new(
      :id => name,
      :name => name,
      :state => stack_data[:state].to_s,
      :status => stack_data[:state].to_s.upcase,
      :updated_time => stack_data[:updated_at],
      :creation_time => stack_data[:created_at],
      :outputs => outputs
    )
  end
end

#outputsHash

Returns stack outputs.

Returns:

  • (Hash)

    stack outputs



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# File 'lib/miasma-terraform/stack.rb', line 310

def outputs
  must_exist do
    if(has_state?)
      action = run_action('output -json', :auto_start)
      action.complete!
      successful_action(action) do
        result = JSON.parse(action.stdout.read).to_smash.map do |key, info|
          [key, info[:value]]
        end
        Smash[result]
      end
    else
      Smash.new
    end
  end
end

#resourcesArray<Hash>

Returns resource list.

Returns:

  • (Array<Hash>)

    resource list



259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# File 'lib/miasma-terraform/stack.rb', line 259

def resources
  must_exist do
    if(has_state?)
      action = run_action('state list', :auto_start)
      # wait for action to complete
      action.complete!
      successful_action(action) do
        resource_lines = action.stdout.read.split("\n").find_all do |line|
          line.match(/^[^\s]/)
        end
        resource_lines.map do |line|
          parts = line.split('.')
          resource_info = Smash.new(
            :type => parts[0],
            :name => parts[1],
            :status => 'UPDATE_COMPLETE'
          )
          action = run_action("state show #{line}", :auto_start)
          action.complete!
          successful_action(action) do
            info = Smash.new
            action.stdout.read.split("\n").each do |line|
              parts = line.split('=').map(&:strip)
              next if parts.size != 2
              info[parts[0]] = parts[1]
            end
            resource_info[:physical_id] = info[:id] if info[:id]
          end
          resource_info
        end
      end
    else
      []
    end
  end
end

#save(opts = {}) ⇒ Object

Save the TF stack



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/miasma-terraform/stack.rb', line 229

def save(opts={})
  save_opts = opts.to_smash
  type = exists? ? "update" : "create"
  lock_stack
  write_file(tf_path, save_opts[:template].to_json)
  write_file(tfvars_path, save_opts[:parameters].to_json)
  action = run_action('apply')
  store_events(action)
  action.on_start do |_|
    update_info do |info|
      info["state"] = "#{type}_in_progress"
      info
    end
  end
  action.on_complete do |status, this_action|
    update_info do |info|
      if(type == "create")
        info["created_at"] = (Time.now.to_f * 1000).floor
      end
      info["updated_at"] = (Time.now.to_f * 1000).floor
      info["state"] = status.success? ? "#{type}_complete" : "#{type}_failed"
      info
    end
    unlock_stack
  end
  action.start!
  true
end

#templateString

Returns current stack template.

Returns:

  • (String)

    current stack template



328
329
330
331
332
333
334
335
336
# File 'lib/miasma-terraform/stack.rb', line 328

def template
  must_exist do
    if(File.exists?(tf_path))
      File.read(tf_path)
    else
      "{}"
    end
  end
end

#validate(template) ⇒ Object



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
# File 'lib/miasma-terraform/stack.rb', line 354

def validate(template)
  errors = []
  root_path = Dir.mktmpdir('miasma-')
  template_path = File.join(root_path, 'main.tf')
  File.write(template_path, template)
  action = run_action('validate')
  action.options[:chdir] = root_path
  action.on_io do |line, type|
    if(line.start_with?('*'))
      errors << line
    end
  end.on_complete do |_|
    FileUtils.rm_rf(root_path)
  end
  action.complete!
  errors
end