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

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

Constant Summary collapse

API_SERVICE =

Service name of the API

'cloudformation'
API_VERSION =

Supported version of the AutoScaling API

'2010-05-15'
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"
]
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
  )
)

Constants inherited from Miasma::Models::Orchestration

VALID_RESOURCE_STATES

Instance Method Summary collapse

Methods included from Contrib::AwsApiCore::RequestUtils

#all_result_pages

Methods included from Contrib::AwsApiCore::ApiCommon

#api_for, #connect, #connection, #endpoint, included, #make_request, #update_request, #uri_escape

Methods inherited from Miasma::Models::Orchestration

#stacks

Methods inherited from Types::Api

#api_for, #connect, #connection, #endpoint, #format_response, #initialize, #make_request, #provider, #request

Methods included from Utils::Memoization

#_memo, #clear_memoizations!, #memoize, #unmemoize

Methods included from Utils::Lazy

included

Constructor Details

This class inherits a constructor from Miasma::Types::Api

Instance Method Details

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

Return all events for stack



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
# File 'lib/miasma/contrib/aws/orchestration.rb', line 298

def event_all(stack, evt_id=nil)
  results = all_result_pages(nil, :body, 'DescribeStackEventsResponse', 'DescribeStackEventsResult', 'StackEvents', 'member') do |options|
    request(
      :path => '/',
      :params => options.merge(
        'Action' => 'DescribeStackEvents',
        'StackName' => stack.id
      )
    )
  end
  events = results.map do |event|
    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'])
    ).valid_state
  end
  if(evt_id)
    idx = events.index{|d| e.id == evt_id}
    idx = idx ? idx + 1 : 0
    events.slice(idx, events.size)
  else
    events
  end
end

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

Return all new events for event collection



334
335
336
# File 'lib/miasma/contrib/aws/orchestration.rb', line 334

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:

Returns:

  • (Models::Orchestration::Event)


342
343
344
345
# File 'lib/miasma/contrib/aws/orchestration.rb', line 342

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:

Returns:



45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/miasma/contrib/aws/orchestration.rb', line 45

def load_stack_data(stack=nil)
  d_params = Smash.new('Action' => 'DescribeStacks')
  l_params = Smash.new('Action' => 'ListStacks')
  STACK_STATES.each_with_index do |state, idx|
    l_params["StackStatusFilter.member.#{idx + 1}"] = state.to_s.upcase
  end
  if(stack)
    d_params['StackName'] = stack.id
    descriptions = all_result_pages(nil, :body, 'DescribeStacksResponse', 'DescribeStacksResult', 'Stacks', 'member') do |options|
      request(
        :path => '/',
        :params => options.merge(d_params)
      )
    end
  else
    descriptions = []
  end
  lists = all_result_pages(nil, :body, 'ListStacksResponse', 'ListStacksResult', 'StackSummaries', 'member') do |options|
    request(
      :path => '/',
      :params => options.merge(l_params)
    )
  end.map do |stk|
    desc = descriptions.detect do |d_stk|
      d_stk['StackId'] == stk['StackId']
    end || Smash.new
    stk.merge!(desc)
    if(stack)
      next if stack.id != stk['StackId'] && stk['StackId'].split('/')[1] != stack.id
    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 => stk['StackStatus'].downcase.to_sym,
      :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']
        )
      },
      :parameters => Smash[
        [stk.fetch('Parameters', 'member', [])].flatten(1).map{|param|
          [param['ParameterKey'], param['ParameterValue']]
        }
      ]
    ).valid_state
  end
end

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

Return all resources for stack



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
# File 'lib/miasma/contrib/aws/orchestration.rb', line 259

def resource_all(stack)
  results = all_result_pages(nil, :body, 'DescribeStackResourcesResponse', 'DescribeStackResourcesResult', 'StackResources', 'member') do |options|
    request(
      :path => '/',
      :params => options.merge(
        Smash.new(
          'Action' => 'DescribeStackResources',
          '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'],
      :status_reason => res['ResourceStatusReason'],
      :updated => res['Timestamp']
    ).valid_state
  end
end

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

Reload the stack resource data from the API

Parameters:

Returns:

  • (Models::Orchestration::Resource)


289
290
291
292
# File 'lib/miasma/contrib/aws/orchestration.rb', line 289

def resource_reload(resource)
  resource.stack.resources.reload
  resource.stack.resources.get(resource.id)
end

#stack_allArray<Models::Orchestration::Stack>

TODO:

check if we need any mappings on state set

Return all stacks

Parameters:

  • options (Hash)

    filter

Returns:



251
252
253
# File 'lib/miasma/contrib/aws/orchestration.rb', line 251

def stack_all
  load_stack_data
end

#stack_destroy(stack) ⇒ TrueClass, FalseClass

Delete the stack

Parameters:

Returns:

  • (TrueClass, FalseClass)


177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/miasma/contrib/aws/orchestration.rb', line 177

def stack_destroy(stack)
  if(stack.persisted?)
    request(
      :path => '/',
      :params => Smash.new(
        'Action' => 'DeleteStack',
        'StackName' => stack.id
      )
    )
    true
  else
    false
  end
end

#stack_get(ident) ⇒ Stack

Return single stack

Parameters:

  • ident (String)

    name or ID

Returns:



239
240
241
242
243
244
# File 'lib/miasma/contrib/aws/orchestration.rb', line 239

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

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

Reload the stack data from the API



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'lib/miasma/contrib/aws/orchestration.rb', line 157

def stack_reload(stack)
  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



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
# File 'lib/miasma/contrib/aws/orchestration.rb', line 110

def stack_save(stack)
  params = Smash.new('StackName' => stack.name)
  (stack.parameters || {}).each_with_index do |pair, idx|
    params["Parameters.member.#{idx + 1}.ParameterKey"] = pair.first
    params["Parameters.member.#{idx + 1}.ParameterValue"] = pair.last
  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
  if(stack.template.empty?)
    params['UsePreviousTemplate'] = true
  else
    params['TemplateBody'] = MultiJson.dump(stack.template)
  end
  if(stack.persisted?)
    result = request(
      :path => '/',
      :method => :post,
      :params => 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,
      :params => Smash.new(
        'Action' => 'CreateStack',
        'DisableRollback' => !!stack.disable_rollback
      ).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



196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
# File 'lib/miasma/contrib/aws/orchestration.rb', line 196

def stack_template_load(stack)
  if(stack.persisted?)
    result = request(
      :path => '/',
      :params => Smash.new(
        'Action' => 'GetTemplate',
        'StackName' => stack.id
      )
    )
    MultiJson.load(
      result.get(:body, 'GetTemplateResponse', 'GetTemplateResult', 'TemplateBody')
    ).to_smash
  else
    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



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/miasma/contrib/aws/orchestration.rb', line 217

def stack_template_validate(stack)
  begin
    result = request(
      :method => :post,
      :path => '/',
      :params => Smash.new(
        'Action' => 'ValidateTemplate',
        'TemplateBody' => MultiJson.dump(stack.template)
      )
    )
    nil
  rescue Error::ApiError::RequestError => e
    MultiXml.parse(e.response.body.to_s).to_smash.get(
      'ErrorResponse', 'Error', 'Message'
    )
  end
end