Class: Miasma::Models::Orchestration::Aws

Inherits:
Miasma::Models::Orchestration show all
Includes:
Bogo::Logger::Helpers, Contrib::AwsApiCore::ApiCommon, Contrib::AwsApiCore::RequestUtils
Defined in:
lib/miasma/contrib/aws/orchestration.rb

Overview

AWS Orchestration API

Defined Under Namespace

Classes: Stack

Constant Summary collapse

API_SERVICE =

Service name of the API

"cloudformation".freeze
EUCA_API_SERVICE =

Service name of the eucalyptus API

"CloudFormation".freeze
API_VERSION =

Supported version of the AutoScaling API

"2010-05-15".freeze
STACK_STATES =

Valid stack lookup states

[
  "CREATE_COMPLETE", "CREATE_FAILED", "CREATE_IN_PROGRESS", "DELETE_FAILED",
  "DELETE_IN_PROGRESS", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED", "ROLLBACK_IN_PROGRESS",
  "UPDATE_COMPLETE", "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_IN_PROGRESS",
  "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_ROLLBACK_FAILED",
  "UPDATE_ROLLBACK_IN_PROGRESS", "REVIEW_IN_PROGRESS",
].map(&:freeze).freeze
RESOURCE_MAPPING =

Returns external to internal resource mapping.

Returns:

  • (Smash)

    external to internal resource mapping

Smash.new(
  "AWS::EC2::Instance" => Smash.new(
    :api => :compute,
    :collection => :servers,
  ),
  "AWS::ElasticLoadBalancing::LoadBalancer" => Smash.new(
    :api => :load_balancer,
    :collection => :balancers,
  ),
  "AWS::AutoScaling::AutoScalingGroup" => Smash.new(
    :api => :auto_scale,
    :collection => :groups,
  ),
  "AWS::CloudFormation::Stack" => Smash.new(
    :api => :orchestration,
    :collection => :stacks,
  ),
  "AWS::S3::Bucket" => Smash.new(
    :api => :storage,
    :collection => :buckets,
  ),
).to_smash(:freeze)

Instance Method Summary collapse

Methods included from Contrib::AwsApiCore::RequestUtils

#all_result_pages

Methods included from Contrib::AwsApiCore::ApiCommon

#after_setup, #api_for, #connect, #connection, #custom_setup, #endpoint, #extract_creds, #get_credential, #get_region, included, #load_aws_file, #load_ecs_credentials!, #load_instance_credentials!, #make_request, #perform_request_retry, #retryable_allowed?, #signer, #sts_assume_role!, #sts_assume_role_update_required?, #sts_attribute_update_required?, #sts_mfa_session!, #sts_mfa_session_update_required?, #update_request, #uri_escape

Instance Method Details

#changeset_name(stack) ⇒ String

Generate changeset name given stack. This is a unique name for miasma and ensures only one changeset is used/persisted for miasma interactions.

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (String)


464
465
466
# File 'lib/miasma/contrib/aws/orchestration.rb', line 464

def changeset_name(stack)
  stack.custom.fetch(:plan_name, "miasma-changeset-#{stack.name}")
end

#common_stack_params(stack) ⇒ Smash

Common parameters used for stack creation/update requests. This is currently shared between stack creation and plan creation

Parameters:

  • stack (Model::Orchestration::Stack)

Returns:

  • (Smash)


700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
# File 'lib/miasma/contrib/aws/orchestration.rb', line 700

def common_stack_params(stack)
  params = Smash.new("StackName" => stack.name)
  if stack.dirty?(:parameters)
    initial_parameters = stack.data[:parameters] || {}
  else
    initial_parameters = {}
  end
  (stack.parameters || {}).each_with_index do |pair, idx|
    params["Parameters.member.#{idx + 1}.ParameterKey"] = pair.first
    if initial_parameters[pair.first] == pair.last
      params["Parameters.member.#{idx + 1}.UsePreviousValue"] = true
    else
      params["Parameters.member.#{idx + 1}.ParameterValue"] = pair.last
    end
  end
  (stack.capabilities || []).each_with_index do |cap, idx|
    params["Capabilities.member.#{idx + 1}"] = cap
  end
  (stack.notification_topics || []).each_with_index do |topic, idx|
    params["NotificationARNs.member.#{idx + 1}"] = topic
  end
  (stack.tags || {}).each_with_index do |tag, idx|
    params["Tags.member.#{idx + 1}.Key"] = tag.first
    params["Tags.member.#{idx + 1}.Value"] = tag.last
  end
  if stack.template_url
    params["TemplateURL"] = stack.template_url
  elsif !stack.dirty?(:template) && stack.persisted?
    params["UsePreviousTemplate"] = true
  else
    params["TemplateBody"] = MultiJson.dump(stack.template)
  end
  params
end

#event_all(stack, evt_id = nil) ⇒ Array<Models::Orchestration::Stack::Event>

Return all events for stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Array<Models::Orchestration::Stack::Event>)


641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
# File 'lib/miasma/contrib/aws/orchestration.rb', line 641

def event_all(stack, evt_id = nil)
  evt_id = stack.last_event_token if evt_id
  results = all_result_pages(evt_id, :body,
                             "DescribeStackEventsResponse", "DescribeStackEventsResult",
                             "StackEvents", "member") do |options|
    request(
      :method => :post,
      :path => "/",
      :form => options.merge(
        "Action" => "DescribeStackEvents",
        "StackName" => stack.id,
      ),
    )
  end
  events = results.map do |event|
    stack.last_event_token = event["NextToken"] if event["NextToken"]
    Stack::Event.new(
      stack,
      :id => event["EventId"],
      :resource_id => event["PhysicalResourceId"],
      :resource_name => event["LogicalResourceId"],
      :resource_logical_id => event["LogicalResourceId"],
      :resource_state => event["ResourceStatus"].downcase.to_sym,
      :resource_status => event["ResourceStatus"],
      :resource_status_reason => event["ResourceStatusReason"],
      :time => Time.parse(event["Timestamp"]).localtime,
    ).valid_state
  end
  if evt_id
    idx = events.index { |d| d.id == evt_id }
    idx ? events.slice(0, idx) : events
  else
    events
  end
end

#event_all_new(events) ⇒ Array<Models::Orchestration::Stack::Event>

Return all new events for event collection

Parameters:

  • events (Models::Orchestration::Stack::Events)

Returns:

  • (Array<Models::Orchestration::Stack::Event>)


681
682
683
# File 'lib/miasma/contrib/aws/orchestration.rb', line 681

def event_all_new(events)
  event_all(events.stack, events.all.first.id)
end

#event_reload(event) ⇒ Models::Orchestration::Event

Reload the stack event data from the API

Parameters:

  • resource (Models::Orchestration::Stack::Event)

Returns:

  • (Models::Orchestration::Event)


689
690
691
692
# File 'lib/miasma/contrib/aws/orchestration.rb', line 689

def event_reload(event)
  event.stack.events.reload
  event.stack.events.get(event.id)
end

#load_stack_data(stack = nil) ⇒ Array<Models::Orchestration::Stack>

Fetch stacks or update provided stack data

Parameters:

  • stack (Models::Orchestration::Stack) (defaults to: nil)

Returns:

  • (Array<Models::Orchestration::Stack>)


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
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/miasma/contrib/aws/orchestration.rb', line 65

def load_stack_data(stack = nil)
  d_params = Smash.new("Action" => "DescribeStacks")
  l_params = Smash.new("Action" => "ListStacks")
  # TODO: Need to update to allow passing in list of desired
  #       states for lookup or to remove state filtering. This
  #       used to work with earlier versions with sfn but something
  #       has changed breaking the integration.
  STACK_STATES.each_with_index do |state, idx|
    l_params["StackStatusFilter.member.#{idx + 1}"] = state.to_s.upcase
  end
  if stack
    logger.debug("loading stack information for `#{stack.id}`")
    d_params["StackName"] = stack.id
    descriptions = all_result_pages(nil, :body,
                                    "DescribeStacksResponse", "DescribeStacksResult",
                                    "Stacks", "member") do |options|
      request(
        :method => :post,
        :path => "/",
        :form => options.merge(d_params),
      )
    end
  else
    logger.debug("loading stack listing information")
    lists = all_result_pages(nil, :body,
                             "ListStacksResponse", "ListStacksResult",
                             "StackSummaries", "member") do |options|
      request(
        :method => :post,
        :path => "/",
        :form => options.merge(l_params),
      )
    end
    descriptions = []
  end
  (lists || descriptions).map do |stk|
    if lists
      desc = descriptions.detect do |d_stk|
        d_stk["StackId"] == stk["StackId"]
      end || Smash.new
      stk.merge!(desc)
    end
    if stack
      next if stack.id != stk["StackId"] && stk["StackId"].split("/")[1] != stack.id
    end
    state = stk["StackStatus"].downcase.to_sym
    unless Miasma::Models::Orchestration::VALID_RESOURCE_STATES.include?(state)
      parts = state.to_s.split("_")
      state = [parts.first, *parts.slice(-2, parts.size)].join("_").to_sym
      unless Miasma::Models::Orchestration::VALID_RESOURCE_STATES.include?(parts)
        state = :unknown
      end
    end
    new_stack = stack || Stack.new(self)
    new_stack.load_data(
      :id => stk["StackId"],
      :name => stk["StackName"],
      :capabilities => [stk.get("Capabilities", "member")].flatten(1).compact,
      :description => stk["Description"],
      :created => stk["CreationTime"],
      :updated => stk["LastUpdatedTime"],
      :notification_topics => [stk.get("NotificationARNs", "member")].flatten(1).compact,
      :timeout_in_minutes => stk["TimeoutInMinutes"] ? stk["TimeoutInMinutes"].to_i : nil,
      :status => stk["StackStatus"],
      :status_reason => stk["StackStatusReason"],
      :state => state,
      :template_description => stk["TemplateDescription"],
      :disable_rollback => !!stk["DisableRollback"],
      :outputs => [stk.get("Outputs", "member")].flatten(1).compact.map { |o|
        Smash.new(
          :key => o["OutputKey"],
          :value => o["OutputValue"],
          :description => o["Description"],
        )
      },
      :tags => Smash[
        [stk.fetch("Tags", "member", [])].flatten(1).map { |param|
          [param["Key"], param["Value"]]
        }
      ],
      :parameters => Smash[
        [stk.fetch("Parameters", "member", [])].flatten(1).map { |param|
          [param["ParameterKey"], param["ParameterValue"]]
        }
      ],
      :custom => Smash.new(
        :stack_policy => stk["StackPolicyBody"],
        :stack_policy_url => stk["StackPolicyURL"],
      ),
    ).valid_state
    logger.debug("loaded stack information `#{new_stack.inspect}`")
    new_stack
  end
end

#resource_all(stack) ⇒ Array<Models::Orchestration::Stack::Resource>

Return all resources for stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Array<Models::Orchestration::Stack::Resource>)


585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/miasma/contrib/aws/orchestration.rb', line 585

def resource_all(stack)
  all_result_pages(nil, :body,
                   "ListStackResourcesResponse", "ListStackResourcesResult",
                   "StackResourceSummaries", "member") do |options|
    request(
      :method => :post,
      :path => "/",
      :form => options.merge(
        Smash.new(
          "Action" => "ListStackResources",
          "StackName" => stack.id,
        )
      ),
    )
  end.map do |res|
    Stack::Resource.new(
      stack,
      :id => res["PhysicalResourceId"],
      :name => res["LogicalResourceId"],
      :logical_id => res["LogicalResourceId"],
      :type => res["ResourceType"],
      :state => res["ResourceStatus"].downcase.to_sym,
      :status => res["ResourceStatus"],
      :updated => res["LastUpdatedTimestamp"],
    ).valid_state
  end
end

#resource_reload(resource) ⇒ Models::Orchestration::Resource

Reload the stack resource data from the API

Parameters:

  • resource (Models::Orchestration::Stack::Resource)

Returns:

  • (Models::Orchestration::Resource)


617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
# File 'lib/miasma/contrib/aws/orchestration.rb', line 617

def resource_reload(resource)
  result = request(
    :method => :post,
    :path => "/",
    :form => Smash.new(
      "LogicalResourceId" => resource.logical_id,
      "StackName" => resource.stack.name,
    ),
  ).get(:body,
        "DescribeStackResourceResponse", "DescribeStackResourceResult",
        "StackResourceDetail")
  resource.updated = result["LastUpdatedTimestamp"]
  resource.type = result["ResourceType"]
  resource.state = result["ResourceStatus"].downcase.to_sym
  resource.status = result["ResourceStatus"]
  resource.status_reason = result["ResourceStatusReason"]
  resource.valid_state
  resource
end

#stack_allArray<Models::Orchestration::Stack>

TODO:

check if we need any mappings on state set

Return all stacks

Parameters:

  • options (Hash)

    filter

Returns:

  • (Array<Models::Orchestration::Stack>)


577
578
579
# File 'lib/miasma/contrib/aws/orchestration.rb', line 577

def stack_all
  load_stack_data
end

#stack_destroy(stack) ⇒ TrueClass, FalseClass

Delete the stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (TrueClass, FalseClass)


493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
# File 'lib/miasma/contrib/aws/orchestration.rb', line 493

def stack_destroy(stack)
  if stack.persisted?
    logger.debug("deleting stack `#{stack.id}`")
    request(
      :method => :post,
      :path => "/",
      :form => Smash.new(
        "Action" => "DeleteStack",
        "StackName" => stack.id,
      ),
    )
    true
  else
    logger.debug("stack not persisted. delete is no-op `#{stack.name}`")
    false
  end
end

#stack_get(ident) ⇒ Stack

Return single stack

Parameters:

  • ident (String)

    name or ID

Returns:



565
566
567
568
569
570
# File 'lib/miasma/contrib/aws/orchestration.rb', line 565

def stack_get(ident)
  i = Stack.new(self)
  i.id = ident
  i.reload
  i.name ? i : nil
end

#stack_plan(stack) ⇒ Models::Orchestration::Stack

TODO:

Needs to include the rolearn and resourcetypes at some point but more thought on how to integrate

Generate a new stack plan from the API

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Models::Orchestration::Stack)


210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/miasma/contrib/aws/orchestration.rb', line 210

def stack_plan(stack)
  logger.debug("generating plan for stack `#{stack.id}`")
  params = common_stack_params(stack)
  plan_name = changeset_name(stack)
  if stack.persisted? && stack.state != :unknown
    logger.debug("plan will update stack")
    changeset_type = "UPDATE"
  else
    logger.debug("plan will create stack")
    changeset_type = "CREATE"
  end
  result = request(
    :path => "/",
    :method => :post,
    :form => params.merge(Smash.new(
      "Action" => "CreateChangeSet",
      "ChangeSetName" => plan_name,
      "StackName" => stack.name,
      "ChangeSetType" => changeset_type,
    )),
  )
  stack.reload
  # Ensure we have the same plan name in use after reload
  stack.custom = stack.custom.dup
  stack.custom[:plan_name] = plan_name
  stack.plan
end

#stack_plan_all(stack) ⇒ Array<Models::Orchestration::Stack::Plan>

Load all plans associated to given stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Array<Models::Orchestration::Stack::Plan>)


432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
# File 'lib/miasma/contrib/aws/orchestration.rb', line 432

def stack_plan_all(stack)
  logger.debug("loading all plans for stack `#{stack.id}`")
  all_result_pages(nil, :body,
                   "ListChangeSetsResponse", "ListChangeSetsResult",
                   "Summaries", "member") do |options|
    request(
      :method => :post,
      :path => "/",
      :form => options.merge(
        Smash.new(
          "Action" => "ListChangeSets",
          "StackName" => stack.id || stack.name,
        )
      ),
    )
  end.map do |res|
    stack = Stack.new(self,
                      id: res["StackId"],
                      name: res["StackName"])
    stack.custom = {:plan_name => res["ChangeSetName"],
                    :plan_id => res["ChangeSetId"]}
    stack.plan
  end
end

#stack_plan_destroy(stack) ⇒ Models::Orchestration::Stack

Delete the plan attached to the stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Models::Orchestration::Stack)


378
379
380
381
382
383
384
385
386
387
388
389
390
391
# File 'lib/miasma/contrib/aws/orchestration.rb', line 378

def stack_plan_destroy(stack)
  logger.debug("deleting plan `#{stack.plan.id}` for stack `#{stack.id}`")
  request(
    :path => "/",
    :method => :post,
    :form => Smash.new(
      "Action" => "DeleteChangeSet",
      "ChangeSetName" => stack.plan.id,
      "StackName" => stack.name,
    ),
  )
  stack.plan = nil
  stack.valid_state
end

#stack_plan_execute(stack) ⇒ Model::Orchestration::Stack

Apply the plan attached to the stack

Parameters:

  • stack (Model::Orchestration::Stack)

Returns:

  • (Model::Orchestration::Stack)


397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/miasma/contrib/aws/orchestration.rb', line 397

def stack_plan_execute(stack)
  logger.debug("applying plan `#{stack.plan.id}` to stack `#{stack.id}`")
  request(
    :path => "/",
    :method => :post,
    :form => Smash.new(
      "Action" => "ExecuteChangeSet",
      "ChangeSetName" => stack.plan.id,
      "StackName" => stack.name,
    ),
  )
  stack.reload
end

#stack_plan_load(stack) ⇒ Models::Orchestration::Stack::Plan

Load the plan for the stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Models::Orchestration::Stack::Plan)


242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
# File 'lib/miasma/contrib/aws/orchestration.rb', line 242

def stack_plan_load(stack)
  if stack.attributes[:plan]
    plan = stack.attributes[:plan]
  else
    plan = Stack::Plan.new(stack, name: changeset_name(stack))
  end
  if stack.custom[:plan_name]
    if stack.custom[:plan_name] != plan.name
      plan.name = stack.custom[:plan_name]
    else
      plan.name = changeset_name(stack)
    end
  end
  logger.debug("loading plan `#{plan.name}` for stack `#{stack.id}`")
  result = nil
  Bogo::Retry.build(:linear, max_attempts: 10, wait_interval: 5, ui: Bogo::Ui.new) do
    begin
      result = request(
        :path => "/",
        :method => :post,
        :form => Smash.new(
          "Action" => "DescribeChangeSet",
          "ChangeSetName" => plan.name,
          "StackName" => stack.name,
        ),
      )
    rescue Error::ApiError::RequestError => e
      # Plan does not exist
      if e.response.code == 404
        logger.warn("plan `#{plan.name}` does not exist for stack `#{stack.id}`")
        return nil
      end
      # Stack does not exist
      if e.response.code == 400 && e.message.include?("ValidationError: Stack")
        logger.warn("stack `#{stack.id}` does not exist")
        return nil
      end
      raise
    end
    status = result.get(:body, "DescribeChangeSetResponse", "DescribeChangeSetResult", "ExecutionStatus")
    if status != "AVAILABLE"
      logger.debug("plan `#{plan.name}` is not available (status: `#{status}`)")
      raise "Plan execution is not yet available"
    end
  end.run!
  res = result.get(:body, "DescribeChangeSetResponse", "DescribeChangeSetResult")
  plan.id = res["ChangeSetId"]
  plan.name = res["ChangeSetName"]
  plan.custom = {
    :execution_status => res["ExecutionStatus"],
    :stack_name => res["StackName"],
    :stack_id => res["StackId"],
    :status => res["Status"],
  }
  plan.state = res["ExecutionStatus"].downcase.to_sym
  plan.parameters = Smash[
    [res.get("Parameters", "member")].compact.flatten.map { |param|
      [param["ParameterKey"], param["ParameterValue"]]
    }
  ]
  plan.created_at = res["CreationTime"]
  plan.template = stack_plan_template(plan, :processed)
  items = {:add => [], :replace => [], :remove => [], :unknown => [], :interrupt => []}
  [res.get("Changes", "member")].compact.flatten.each do |chng|
    if chng["Type"] == "Resource"
      item_diffs = []
      [chng.get("ResourceChange", "Details", "member")].compact.flatten.each do |d|
        item_path = [
          d.get("Target", "Attribute"),
          d.get("Target", "Name"),
        ].compact
        original_value = stack.template.get("Resources", chng.get("ResourceChange", "LogicalResourceId"), *item_path)
        if original_value.is_a?(Hash) && (stack.parameters || {}).key?(original_value["Ref"])
          original_value = stack.parameters[original_value["Ref"]]
        end
        new_value = plan.template.get("Resources", chng.get("ResourceChange", "LogicalResourceId"), *item_path)
        if new_value.is_a?(Hash) && plan.parameters.key?(new_value["Ref"])
          new_value = plan.parameters[new_value["Ref"]]
        end
        diff = Stack::Plan::Diff.new(
          :name => item_path.join("."),
          :current => original_value.inspect,
          :proposed => new_value.inspect,
        )

        unless item_diffs.detect { |d| d.name == diff.name && d.current == diff.current && d.proposed == diff.proposed }
          item_diffs << diff
        end
      end
      type = case chng.get("ResourceChange", "Action").to_s.downcase
             when "add"
               :add
             when "modify"
               chng.get("ResourceChange", "Replacement") == "True" ?
                 :replace : :interrupt
             when "remove"
               :remove
             else
               :unknown
             end
      items[type] << Stack::Plan::Item.new(
        :name => chng.get("ResourceChange", "LogicalResourceId"),
        :type => chng.get("ResourceChange", "ResourceType"),
        :diffs => item_diffs.sort_by(&:name),
      )
    end
  end.compact
  items.each do |type, list|
    plan.send("#{type}=", list.sort_by(&:name))
  end
  if plan.custom[:stack_id]
    stack.id = plan.custom[:stack_id]
    stack.valid_state
  end
  logger.debug("plan `#{plan.name}` loaded for stack `#{stack.id}` - `#{plan.inspect}`")
  stack.plan = plan.valid_state
end

#stack_plan_reload(plan) ⇒ Model::Orchestration::Stack::Plan

Reload the plan

Parameters:

  • plan (Model::Orchestration::Stack::Plan)

Returns:

  • (Model::Orchestration::Stack::Plan)


415
416
417
418
419
420
421
422
423
424
425
426
# File 'lib/miasma/contrib/aws/orchestration.rb', line 415

def stack_plan_reload(plan)
  logger.debug("reloading plan `#{plan.id}`")
  if plan.stack.plan == plan
    stack_plan_load(plan.stack)
  else
    stack = Stack.new(self,
                      id: plan.custom[:stack_id],
                      name: plan.custom[:stack_name])
    stack.dirty[:plan] = plan
    stack_plan_load(stack)
  end
end

#stack_plan_template(plan, state) ⇒ Object



360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/miasma/contrib/aws/orchestration.rb', line 360

def stack_plan_template(plan, state)
  logger.debug("loading template for plan `#{plan.name}`")
  result = request(
    :path => "/",
    :method => :post,
    :form => Smash.new(
      "Action" => "GetTemplate",
      "ChangeSetName" => plan.id,
      "TemplateStage" => state.to_s.capitalize,
    ),
  )
  MultiJson.load(result.get(:body, "GetTemplateResponse", "GetTemplateResult", "TemplateBody")).to_smash
end

#stack_reload(stack) ⇒ Models::Orchestration::Stack

Reload the stack data from the API

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Models::Orchestration::Stack)


472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/miasma/contrib/aws/orchestration.rb', line 472

def stack_reload(stack)
  logger.debug("reloading stack `#{stack.id}`")
  if stack.persisted?
    ustack = Stack.new(self)
    ustack.id = stack.id
    load_stack_data(ustack)
    if ustack.data[:name]
      stack.load_data(ustack.attributes).valid_state
    else
      stack.status = "DELETE_COMPLETE"
      stack.state = :delete_complete
      stack.valid_state
    end
  end
  stack
end

#stack_save(stack) ⇒ Models::Orchestration::Stack

Save the stack

Parameters:

  • stack (Models::Orchestration::Stack)

Returns:

  • (Models::Orchestration::Stack)


164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/miasma/contrib/aws/orchestration.rb', line 164

def stack_save(stack)
  logger.debug("saving stack information `#{stack.inspect}`")
  params = common_stack_params(stack)
  if stack.custom[:stack_policy_body]
    params["StackPolicyBody"] = MultiJson.dump(stack.custom[:stack_policy_body])
  end
  if stack.custom[:stack_policy_url]
    params["StackPolicyURL"] = stack.custom[:stack_policy_url]
  end
  unless stack.disable_rollback.nil?
    params["OnFailure"] = stack.disable_rollback ? "DO_NOTHING" : "ROLLBACK"
  end
  if stack.on_failure
    params["OnFailure"] = stack.on_failure == "nothing" ? "DO_NOTHING" : stack.on_failure.upcase
  end
  if stack.persisted?
    result = request(
      :path => "/",
      :method => :post,
      :form => Smash.new(
        "Action" => "UpdateStack",
      ).merge(params),
    )
    stack
  else
    if stack.timeout_in_minutes
      params["TimeoutInMinutes"] = stack.timeout_in_minutes
    end
    result = request(
      :path => "/",
      :method => :post,
      :form => Smash.new(
        "Action" => "CreateStack",
      ).merge(params),
    )
    stack.id = result.get(:body, "CreateStackResponse", "CreateStackResult", "StackId")
    stack.valid_state
  end
end

#stack_template_load(stack) ⇒ Smash

Fetch stack template

Parameters:

Returns:

  • (Smash)

    stack template



515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
# File 'lib/miasma/contrib/aws/orchestration.rb', line 515

def stack_template_load(stack)
  if stack.persisted?
    logger.debug("loading template for stack `#{stack.id}`")
    result = request(
      :method => :post,
      :path => "/",
      :form => Smash.new(
        "Action" => "GetTemplate",
        "StackName" => stack.id,
      ),
    )
    template = result.get(:body, "GetTemplateResponse", "GetTemplateResult", "TemplateBody")
    template.nil? ? Smash.new : MultiJson.load(template)
  else
    logger.debug("no template for non-persisted stack `#{stack.name}`")
    Smash.new
  end
end

#stack_template_validate(stack) ⇒ NilClass, String

Validate stack template

Parameters:

Returns:

  • (NilClass, String)

    nil if valid, string error message if invalid



538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
# File 'lib/miasma/contrib/aws/orchestration.rb', line 538

def stack_template_validate(stack)
  begin
    if stack.template_url
      params = Smash.new("TemplateURL" => stack.template_url)
    else
      params = Smash.new("TemplateBody" => MultiJson.dump(stack.template))
    end
    result = request(
      :method => :post,
      :path => "/",
      :form => params.merge(
        "Action" => "ValidateTemplate",
      ),
    )
    nil
  rescue Error::ApiError::RequestError => e
    logger.error("template validate error - #{e.response.body}")
    MultiXml.parse(e.response.body.to_s).to_smash.get(
      "ErrorResponse", "Error", "Message"
    )
  end
end