miasma-aws 0.3.4 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0cc0b7f33082a8144660f29aac8f48f73f2df07c
4
- data.tar.gz: 7a6122fa2188e2b7d516de4497d21aa5d6247f42
3
+ metadata.gz: caa36177f0ea2511cec15e3f84b55ab542845255
4
+ data.tar.gz: c2e70dcbce33f6514fd110a2d1e90f2baf148675
5
5
  SHA512:
6
- metadata.gz: 106e62613c03de1fb70e1cf28d0ffd183ba4ca1036dc2621d18d6a1b10c0b79f4e2090cb3464c735b5ae2ce69a76d15398a962fe5e1331512aae12c608911c27
7
- data.tar.gz: a5126856fe8f37891bceaf382482690c32d185926d1239d06221388789c25294b1be63628d59d7fcd4673dd96ff7c35fc85599e662d6b6c7cffd1bcf86112ce6
6
+ metadata.gz: 7aff9a89bb222d9a3a247d167a3536759b4008aa1bb6fc83ab5baf536292452a7524319cbd113ea998c2be0bf3ed92833af6336f7d58f37f375b2e0ed46546c8
7
+ data.tar.gz: 488ba3c0126a6d38795edd6d78f5e060b89370c9c34ababcd1f5996cff2bab4e428a924d0ecc947e0756e7686d8357bb3f4c60bbf02a357c1c717f866ce5df0d
@@ -1,3 +1,7 @@
1
+ # v0.3.6
2
+ * [enhancement] Support previous parameter reuse on stack update (#41)
3
+ * [feature] Add support for ECS task profile (#40)
4
+
1
5
  # v0.3.4
2
6
  * [fix] Support template url for stack models (#39)
3
7
 
data/README.md CHANGED
@@ -33,6 +33,10 @@ Miasma.api(
33
33
 
34
34
  * `aws_iam_instance_profile` - Extract and use instance IAM credentials
35
35
 
36
+ ### ECS task related attributes
37
+
38
+ * `aws_ecs_task_profile` - Extract and use ECS task IAM credentials
39
+
36
40
  ### Secure Token Service related:
37
41
 
38
42
  * `aws_sts_token` - Set STS token to use with current key ID and secret
@@ -3,6 +3,7 @@ require 'miasma'
3
3
  module Miasma
4
4
  module Contrib
5
5
  module Aws
6
+ # AWS API helpers
6
7
  module Api
7
8
  autoload :Sts, 'miasma-aws/api/sts'
8
9
  autoload :Iam, 'miasma-aws/api/iam'
@@ -4,12 +4,13 @@ module Miasma
4
4
  module Contrib
5
5
  module Aws
6
6
  module Api
7
+ # IAM helper class
7
8
  class Iam < Miasma::Types::Api
8
9
 
9
10
  # Service name of the API
10
- API_SERVICE = 'iam'
11
+ API_SERVICE = 'iam'.freeze
11
12
  # Supported version of the IAM API
12
- API_VERSION = '2010-05-08'
13
+ API_VERSION = '2010-05-08'.freeze
13
14
 
14
15
  include Contrib::AwsApiCore::ApiCommon
15
16
  include Contrib::AwsApiCore::RequestUtils
@@ -4,12 +4,13 @@ module Miasma
4
4
  module Contrib
5
5
  module Aws
6
6
  module Api
7
+ # STS helper class
7
8
  class Sts < Miasma::Types::Api
8
9
 
9
10
  # Service name of the API
10
- API_SERVICE = 'sts'
11
+ API_SERVICE = 'sts'.freeze
11
12
  # Supported version of the STS API
12
- API_VERSION = '2011-06-15'
13
+ API_VERSION = '2011-06-15'.freeze
13
14
 
14
15
  include Contrib::AwsApiCore::ApiCommon
15
16
  include Contrib::AwsApiCore::RequestUtils
@@ -1,4 +1,4 @@
1
1
  module MiasmaAws
2
2
  # Current library version
3
- VERSION = Gem::Version.new('0.3.4')
3
+ VERSION = Gem::Version.new('0.3.6')
4
4
  end
@@ -4,14 +4,17 @@ require 'miasma/utils/smash'
4
4
  require 'time'
5
5
  require 'openssl'
6
6
 
7
+ # Miasma
7
8
  module Miasma
8
9
  module Contrib
10
+ # AWS API implementations
9
11
  module Aws
10
12
  autoload :Api, 'miasma-aws/api'
11
13
  end
12
14
  # Core API for AWS access
13
15
  class AwsApiCore
14
16
 
17
+ # Utility methods for API requests
15
18
  module RequestUtils
16
19
 
17
20
  # Fetch all results when tokens are being used
@@ -34,6 +37,11 @@ module Miasma
34
37
  end
35
38
  set = result.get(*result_key.slice(0, 3))
36
39
  if(set.is_a?(Hash) && set['NextToken'])
40
+ [content].flatten.compact.each do |item|
41
+ if(item.is_a?(Hash))
42
+ item['NextToken'] = set['NextToken']
43
+ end
44
+ end
37
45
  list += all_result_pages(set['NextToken'], *result_key, &block)
38
46
  end
39
47
  list.compact
@@ -127,8 +135,8 @@ module Miasma
127
135
  # @param string [String] string to escape
128
136
  # @return [String] escaped string
129
137
  def safe_escape(string)
130
- string.to_s.gsub(/([^a-zA-Z0-9_.\-~])/) do
131
- '%' << $1.unpack('H2' * $1.bytesize).join('%').upcase
138
+ string.to_s.gsub(/([^a-zA-Z0-9_.\-~])/) do |match|
139
+ '%' << match.unpack('H2' * match.bytesize).join('%').upcase
132
140
  end
133
141
  end
134
142
 
@@ -168,7 +176,8 @@ module Miasma
168
176
  # @return [String] signature
169
177
  def generate(http_method, path, opts)
170
178
  signature = generate_signature(http_method, path, opts)
171
- "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
179
+ "#{algorithm} Credential=#{access_key}/#{credential_scope}, " \
180
+ "SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
172
181
  end
173
182
 
174
183
  # Generate URL with signed params
@@ -185,7 +194,10 @@ module Miasma
185
194
  'X-Amz-Credential' => "#{access_key}/#{credential_scope}"
186
195
  )
187
196
  )
188
- signature = generate_signature(http_method, path, opts.merge(:body => 'UNSIGNED-PAYLOAD'))
197
+ signature = generate_signature(
198
+ http_method, path,
199
+ opts.merge(:body => 'UNSIGNED-PAYLOAD')
200
+ )
189
201
  params = opts[:params].merge('X-Amz-Signature' => signature)
190
202
  "https://#{opts[:headers]['Host']}/#{path}?#{canonical_query(params)}"
191
203
  end
@@ -326,6 +338,7 @@ module Miasma
326
338
 
327
339
  end
328
340
 
341
+ # Common API setup
329
342
  module ApiCommon
330
343
 
331
344
  def self.included(klass)
@@ -340,17 +353,22 @@ module Miasma
340
353
  attribute :aws_sts_session_token, String
341
354
  attribute :aws_sts_session_token_code, [String, Proc, Method]
342
355
  attribute :aws_sts_mfa_serial_number, [String]
343
- attribute :aws_credentials_file, String, :required => true, :default => File.join(Dir.home, '.aws/credentials')
344
- attribute :aws_config_file, String, :required => true, :default => File.join(Dir.home, '.aws/config')
356
+ attribute :aws_credentials_file, String, :required => true,
357
+ :default => File.join(Dir.home, '.aws/credentials')
358
+ attribute :aws_config_file, String, :required => true,
359
+ :default => File.join(Dir.home, '.aws/config')
345
360
  attribute :aws_access_key_id, String, :required => true
346
361
  attribute :aws_secret_access_key, String, :required => true
347
362
  attribute :aws_iam_instance_profile, [TrueClass, FalseClass], :default => false
363
+ attribute :aws_ecs_task_profile, [TrueClass, FalseClass], :default => false
348
364
  attribute :aws_region, String, :required => true
349
365
  attribute :aws_host, String
350
366
  attribute :aws_bucket_region, String
351
367
  attribute :api_endpoint, String, :required => true, :default => 'amazonaws.com'
352
- attribute :euca_compat, Symbol, :allowed_values => [:path, :dns], :coerce => lambda{|v| v.is_a?(String) ? v.to_sym : v}
353
- attribute :euca_dns_map, Smash, :coerce => lambda{|v| v.to_smash}, :default => Smash.new
368
+ attribute :euca_compat, Symbol, :allowed_values => [:path, :dns],
369
+ :coerce => lambda{|v| v.is_a?(String) ? v.to_sym : v}
370
+ attribute :euca_dns_map, Smash, :coerce => lambda{|v| v.to_smash},
371
+ :default => Smash.new
354
372
  attribute :ssl_enabled, [TrueClass, FalseClass], :default => true
355
373
  end
356
374
 
@@ -361,11 +379,21 @@ module Miasma
361
379
  'role_arn' => 'aws_sts_role_arn',
362
380
  'aws_security_token' => 'aws_sts_token',
363
381
  'aws_session_token' => 'aws_sts_session_token'
364
- )
382
+ ).to_smash.freeze
383
+ )
384
+ klass.const_set(:INSTANCE_PROFILE_HOST, 'http://169.254.169.254'.freeze)
385
+ klass.const_set(
386
+ :INSTANCE_PROFILE_PATH,
387
+ 'latest/meta-data/iam/security-credentials'.freeze
388
+ )
389
+ klass.const_set(
390
+ :INSTANCE_PROFILE_AZ_PATH,
391
+ 'latest/meta-data/placement/availability-zone'.freeze
392
+ )
393
+ klass.const_set(:ECS_TASK_PROFILE_HOST, 'http://169.254.170.2'.freeze)
394
+ klass.const_set(
395
+ :ECS_TASK_PROFILE_PATH, ENV['AWS_CONTAINER_CREDENTIALS_RELATIVE_URI']
365
396
  )
366
- klass.const_set(:INSTANCE_PROFILE_HOST, 'http://169.254.169.254')
367
- klass.const_set(:INSTANCE_PROFILE_PATH, 'latest/meta-data/iam/security-credentials')
368
- klass.const_set(:INSTANCE_PROFILE_AZ_PATH, 'latest/meta-data/placement/availability-zone')
369
397
  end
370
398
 
371
399
  # Build new API for specified type using current provider / creds
@@ -406,7 +434,9 @@ module Miasma
406
434
  )
407
435
  end
408
436
  if(creds[:aws_iam_instance_profile])
409
- load_instance_credentials!(creds)
437
+ self.class.const_get(:ECS_TASK_PROFILE_PATH).nil? ?
438
+ load_instance_credentials!(creds) :
439
+ load_ecs_credentials(creds)
410
440
  end
411
441
  true
412
442
  end
@@ -418,7 +448,7 @@ module Miasma
418
448
  # @return [TrueClass]
419
449
  def after_setup(creds)
420
450
  skip = self.class.attributes.keys.map(&:to_s)
421
- creds.each do |k,v|
451
+ creds.each do |k, v|
422
452
  k = k.to_s
423
453
  if(k.start_with?('aws_') && !skip.include?(k))
424
454
  data[k] = v
@@ -452,33 +482,82 @@ module Miasma
452
482
  data = {}
453
483
  end
454
484
  end
455
- creds[:aws_access_key_id] = data['AccessKeyId']
456
- creds[:aws_secret_access_key] = data['SecretAccessKey']
457
- creds[:aws_sts_token] = data['Token']
458
- creds[:aws_sts_token_expires] = Time.xmlschema(data['Expiration'])
485
+ creds.merge!(extract_creds(data))
459
486
  unless(creds[:aws_region])
460
- az = HTTP.get(
461
- [
462
- self.class.const_get(:INSTANCE_PROFILE_HOST),
463
- self.class.const_get(:INSTANCE_PROFILE_AZ_PATH)
464
- ].join('/')
465
- ).body.to_s.strip
466
- az.sub!(/[a-zA-Z]+$/, '')
467
- creds[:aws_region] = az
487
+ creds[:aws_region] = get_region
468
488
  end
469
489
  true
470
490
  end
471
491
 
492
+ # Attempt to load credentials from instance metadata
493
+ #
494
+ # @param creds [Hash]
495
+ # @return [TrueClass]
496
+ def load_ecs_credentials!(creds)
497
+ # As per docs ECS_TASK_PROFILE_PATH is defined as
498
+ # /credential_provider_version/credentials?id=task_UUID
499
+ # where AWS fills in the version and UUID.
500
+ # @see https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-iam-roles.html
501
+ data = HTTP.get(
502
+ [
503
+ self.class.const_get(:ECS_TASK_PROFILE_HOST),
504
+ self.class.const_get(:ECS_TASK_PROFILE_PATH)
505
+ ].join
506
+ ).body
507
+ unless(data.is_a?(Hash))
508
+ begin
509
+ data = MultiJson.load(data.to_s)
510
+ rescue MultiJson::ParseError
511
+ data = {}
512
+ end
513
+ end
514
+ creds.merge!(extract_creds(data))
515
+ unless(creds[:aws_region])
516
+ creds[:aws_region] = get_region
517
+ end
518
+ true
519
+ end
520
+
521
+ # Return hash with needed information to assume role
522
+ #
523
+ # @param data [Hash]
524
+ # @return [Hash]
525
+ def extract_creds(data)
526
+ c = Smash.new
527
+ c[:aws_access_key_id] = data['AccessKeyId']
528
+ c[:aws_secret_access_key] = data['SecretAccessKey']
529
+ c[:aws_sts_token] = data['Token']
530
+ c[:aws_sts_token_expires] = Time.xmlschema(data['Expiration'])
531
+ c[:aws_sts_role_arn] = data['RoleArn'] # used in ECS Role but not instance role
532
+ c
533
+ end
534
+
535
+ # Return region from meta-data service
536
+ #
537
+ # @return [String]
538
+ def get_region
539
+ az = HTTP.get(
540
+ [
541
+ self.class.const_get(:INSTANCE_PROFILE_HOST),
542
+ self.class.const_get(:INSTANCE_PROFILE_AZ_PATH)
543
+ ].join('/')
544
+ ).body.to_s.strip
545
+ az.sub!(/[a-zA-Z]+$/, '')
546
+ az
547
+ end
548
+
472
549
  def sts_mfa_session!(creds)
473
550
  if(sts_mfa_session_update_required?(creds))
474
551
  sts = Miasma::Contrib::Aws::Api::Sts.new(
475
552
  :aws_access_key_id => creds[:aws_access_key_id],
476
553
  :aws_secret_access_key => creds[:aws_secret_access_key],
477
554
  :aws_region => creds.fetch(:aws_sts_region, 'us-east-1'),
478
- :aws_credentials_file => creds.fetch(:aws_credentials_file, aws_credentials_file),
555
+ :aws_credentials_file => creds.fetch(
556
+ :aws_credentials_file, aws_credentials_file
557
+ ),
479
558
  :aws_config_file => creds.fetch(:aws_config_file, aws_config_file),
480
559
  :aws_profile_name => creds[:aws_profile_name],
481
- :aws_host => creds[:aws_sts_host],
560
+ :aws_host => creds[:aws_sts_host]
482
561
  )
483
562
  creds.merge!(
484
563
  sts.mfa_session(
@@ -500,7 +579,9 @@ module Miasma
500
579
  :aws_access_key_id => get_credential(:access_key_id, creds),
501
580
  :aws_secret_access_key => get_credential(:secret_access_key, creds),
502
581
  :aws_region => creds.fetch(:aws_sts_region, 'us-east-1'),
503
- :aws_credentials_file => creds.fetch(:aws_credentials_file, aws_credentials_file),
582
+ :aws_credentials_file => creds.fetch(
583
+ :aws_credentials_file, aws_credentials_file
584
+ ),
504
585
  :aws_config_file => creds.fetch(:aws_config_file, aws_config_file),
505
586
  :aws_host => creds[:aws_sts_host],
506
587
  :aws_sts_token => creds[:aws_sts_session_token]
@@ -521,7 +602,7 @@ module Miasma
521
602
  # @param profile [String] name of profile to load
522
603
  # @return [Smash]
523
604
  def load_aws_file(file_path, profile)
524
- if(File.exists?(file_path))
605
+ if(File.exist?(file_path))
525
606
  l_config = Smash.new.tap do |creds|
526
607
  key = nil
527
608
  File.readlines(file_path).each_with_index do |line, idx|
@@ -529,13 +610,18 @@ module Miasma
529
610
  next if line.empty? || line.start_with?('#')
530
611
  if(line.start_with?('['))
531
612
  unless(line.end_with?(']'))
532
- raise ArgumentError.new("Failed to parse aws file! (#{file_path} line #{idx + 1})")
613
+ raise ArgumentError.new(
614
+ "Failed to parse aws file! (#{file_path} line #{idx + 1})"
615
+ )
533
616
  end
534
617
  key = line.tr('[]', '').strip.sub(/^profile /, '')
535
618
  creds[key] = Smash.new
536
619
  else
537
620
  unless(key)
538
- raise ArgumentError.new("Failed to parse aws file! (#{file_path} line #{idx + 1}) - No section defined!")
621
+ raise ArgumentError.new(
622
+ "Failed to parse aws file! (#{file_path} line #{idx + 1}) " \
623
+ '- No section defined!'
624
+ )
539
625
  end
540
626
  line_args = line.split('=', 2).map(&:strip)
541
627
  line_args.first.replace(
@@ -545,14 +631,18 @@ module Miasma
545
631
  )
546
632
  if(line_args.last.start_with?('"'))
547
633
  unless(line_args.last.end_with?('"'))
548
- raise ArgumentError.new("Failed to parse aws file! (#{file_path} line #{idx + 1})")
634
+ raise ArgumentError.new(
635
+ "Failed to parse aws file! (#{file_path} line #{idx + 1})"
636
+ )
549
637
  end
550
638
  line_args.last.replace(line_args.last[1..-2]) # NOTE: strip quoted values
551
639
  end
552
640
  begin
553
641
  creds[key].merge!(Smash[*line_args])
554
642
  rescue => e
555
- raise ArgumentError.new("Failed to parse aws file! (#{file_path} line #{idx + 1})")
643
+ raise ArgumentError.new(
644
+ "Failed to parse aws file! (#{file_path} line #{idx + 1})"
645
+ )
556
646
  end
557
647
  end
558
648
  end
@@ -659,7 +749,8 @@ module Miasma
659
749
  dest, options = request_args
660
750
  path = URI.parse(dest).path
661
751
  options = options ? options.to_smash : Smash.new
662
- options[:headers] = Smash[connection.default_options.headers.to_a].merge(options.fetch(:headers, Smash.new))
752
+ options[:headers] = Smash[connection.default_options.headers.to_a].
753
+ merge(options.fetch(:headers, Smash.new))
663
754
  if(self.class::API_VERSION)
664
755
  if(options[:form])
665
756
  options.set(:form, 'Version', self.class::API_VERSION)
@@ -687,7 +778,7 @@ module Miasma
687
778
  end
688
779
  signature = signer.generate(http_method, path, options)
689
780
  update_request(connection, options)
690
- options = Hash[options.map{|k,v|[k.to_sym,v]}]
781
+ options = Hash[options.map{|k, v| [k.to_sym, v] }]
691
782
  connection.auth(signature).send(http_method, dest, options)
692
783
  end
693
784
 
@@ -705,8 +796,12 @@ module Miasma
705
796
  # @return [TrueClass, FalseClass]
706
797
  # @note update check only applied if assuming role
707
798
  def sts_mfa_session_update_required?(args={})
708
- if(args.fetch(:aws_sts_session_token_code, attributes[:aws_sts_session_token_code]))
709
- expiry = args.fetch(:aws_sts_session_token_expires, attributes[:aws_sts_session_token_expires])
799
+ if(args.fetch(:aws_sts_session_token_code,
800
+ attributes[:aws_sts_session_token_code]))
801
+ expiry = args.fetch(
802
+ :aws_sts_session_token_expires,
803
+ attributes[:aws_sts_session_token_expires]
804
+ )
710
805
  expiry.nil? || expiry <= Time.now - 15
711
806
  else
712
807
  false
@@ -3,12 +3,13 @@ require 'miasma'
3
3
  module Miasma
4
4
  module Models
5
5
  class AutoScale
6
+ # AWS autoscaling API
6
7
  class Aws < AutoScale
7
8
 
8
9
  # Service name of the API
9
- API_SERVICE = 'autoscaling'
10
+ API_SERVICE = 'autoscaling'.freeze
10
11
  # Supported version of the AutoScaling API
11
- API_VERSION = '2011-01-01'
12
+ API_VERSION = '2011-01-01'.freeze
12
13
 
13
14
  include Contrib::AwsApiCore::ApiCommon
14
15
  include Contrib::AwsApiCore::RequestUtils
@@ -49,7 +50,10 @@ module Miasma
49
50
  if(group)
50
51
  params.merge('AutoScalingGroupNames.member.1' => group.id || group.name)
51
52
  end
52
- result = all_result_pages(nil, :body, 'DescribeAutoScalingGroupsResponse', 'DescribeAutoScalingGroupsResult', 'AutoScalingGroups', 'member') do |options|
53
+ result = all_result_pages(nil, :body,
54
+ 'DescribeAutoScalingGroupsResponse', 'DescribeAutoScalingGroupsResult',
55
+ 'AutoScalingGroups', 'member'
56
+ ) do |options|
53
57
  request(
54
58
  :method => :post,
55
59
  :path => '/',
@@ -65,14 +69,15 @@ module Miasma
65
69
  :minimum_size => grp['MinSize'],
66
70
  :maximum_size => grp['MaxSize'],
67
71
  :status => grp['Status'],
68
- :load_balancers => [grp.get('LoadBalancerNames', 'member')].flatten(1).compact.map{|i|
72
+ :load_balancers => [
73
+ grp.get('LoadBalancerNames', 'member')
74
+ ].flatten(1).compact.map{|i|
69
75
  Group::Balancer.new(self, :id => i, :name => i)
70
76
  }
71
77
  ).valid_state
72
78
  end
73
79
  end
74
80
 
75
-
76
81
  # Return all auto scale groups
77
82
  #
78
83
  # @param options [Hash] filter
@@ -8,9 +8,9 @@ module Miasma
8
8
  class Aws < Compute
9
9
 
10
10
  # Service name of the API
11
- API_SERVICE = 'ec2'
11
+ API_SERVICE = 'ec2'.freeze
12
12
  # Supported version of the EC2 API
13
- API_VERSION = '2014-06-15'
13
+ API_VERSION = '2014-06-15'.freeze
14
14
 
15
15
  include Contrib::AwsApiCore::ApiCommon
16
16
  include Contrib::AwsApiCore::RequestUtils
@@ -23,7 +23,7 @@ module Miasma
23
23
  'terminated' => :terminated,
24
24
  'stopping' => :pending,
25
25
  'stopped' => :stopped
26
- )
26
+ ).to_smash(:freeze)
27
27
 
28
28
  # @todo catch bad lookup and clear model
29
29
  def server_reload(server)
@@ -35,15 +35,24 @@ module Miasma
35
35
  'InstanceId.1' => server.id
36
36
  }
37
37
  )
38
- srv = result.get(:body, 'DescribeInstancesResponse', 'reservationSet', 'item', 'instancesSet', 'item')
38
+ srv = result.get(:body,
39
+ 'DescribeInstancesResponse', 'reservationSet',
40
+ 'item', 'instancesSet', 'item'
41
+ )
39
42
  server.load_data(
40
43
  :id => srv[:instanceId],
41
- :name => [srv.fetch(:tagSet, :item, [])].flatten.map{|tag| tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'}.compact.first,
44
+ :name => [srv.fetch(:tagSet, :item, [])].flatten.map{|tag|
45
+ tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'
46
+ }.compact.first,
42
47
  :image_id => srv[:imageId],
43
48
  :flavor_id => srv[:instanceType],
44
49
  :state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
45
- :addresses_private => [Server::Address.new(:version => 4, :address => srv[:privateIpAddress])],
46
- :addresses_public => [Server::Address.new(:version => 4, :address => srv[:ipAddress])],
50
+ :addresses_private => [
51
+ Server::Address.new(:version => 4, :address => srv[:privateIpAddress])
52
+ ],
53
+ :addresses_public => [
54
+ Server::Address.new(:version => 4, :address => srv[:ipAddress])
55
+ ],
47
56
  :status => srv.get(:instanceState, :name),
48
57
  :key_name => srv[:keyName]
49
58
  )
@@ -80,7 +89,9 @@ module Miasma
80
89
  'MaxCount' => 1
81
90
  }
82
91
  )
83
- server.id = result.get(:body, 'RunInstancesResponse', 'instancesSet', 'item', 'instanceId')
92
+ server.id = result.get(:body,
93
+ 'RunInstancesResponse', 'instancesSet', 'item', 'instanceId'
94
+ )
84
95
  server.valid_state
85
96
  request(
86
97
  :method => :post,
@@ -99,7 +110,9 @@ module Miasma
99
110
 
100
111
  # @todo need to add auto pagination helper (as common util)
101
112
  def server_all
102
- results = all_result_pages(nil, :body, 'DescribeInstancesResponse', 'reservationSet', 'item') do |options|
113
+ results = all_result_pages(nil, :body,
114
+ 'DescribeInstancesResponse', 'reservationSet', 'item'
115
+ ) do |options|
103
116
  request(
104
117
  :method => :post,
105
118
  :path => '/',
@@ -108,17 +121,23 @@ module Miasma
108
121
  )
109
122
  )
110
123
  end
111
- results.map do |srv|
112
- [srv[:instancesSet][:item]].flatten.compact.map do |srv|
124
+ results.map do |server|
125
+ [server[:instancesSet][:item]].flatten.compact.map do |srv|
113
126
  Server.new(
114
127
  self,
115
128
  :id => srv[:instanceId],
116
- :name => srv.fetch(:tagSet, :item, []).map{|tag| tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'}.compact.first,
129
+ :name => srv.fetch(:tagSet, :item, []).map{|tag|
130
+ tag[:value] if tag.is_a?(Hash) && tag[:key] == 'Name'
131
+ }.compact.first,
117
132
  :image_id => srv[:imageId],
118
133
  :flavor_id => srv[:instanceType],
119
134
  :state => SERVER_STATE_MAP.fetch(srv.get(:instanceState, :name), :pending),
120
- :addresses_private => [Server::Address.new(:version => 4, :address => srv[:privateIpAddress])],
121
- :addresses_public => [Server::Address.new(:version => 4, :address => srv[:ipAddress])],
135
+ :addresses_private => [
136
+ Server::Address.new(:version => 4, :address => srv[:privateIpAddress])
137
+ ],
138
+ :addresses_public => [
139
+ Server::Address.new(:version => 4, :address => srv[:ipAddress])
140
+ ],
122
141
  :status => srv.get(:instanceState, :name),
123
142
  :key_name => srv[:keyName]
124
143
  ).valid_state
@@ -129,5 +148,4 @@ module Miasma
129
148
  end
130
149
  end
131
150
  end
132
-
133
151
  end
@@ -3,15 +3,16 @@ require 'miasma'
3
3
  module Miasma
4
4
  module Models
5
5
  class LoadBalancer
6
+ # AWS load balancer API
6
7
  class Aws < LoadBalancer
7
8
 
8
9
  include Contrib::AwsApiCore::ApiCommon
9
10
  include Contrib::AwsApiCore::RequestUtils
10
11
 
11
12
  # Service name of API
12
- API_SERVICE = 'elasticloadbalancing'
13
+ API_SERVICE = 'elasticloadbalancing'.freeze
13
14
  # Supported version of the ELB API
14
- API_VERSION = '2012-06-01'
15
+ API_VERSION = '2012-06-01'.freeze
15
16
 
16
17
  # Save load balancer
17
18
  #
@@ -23,7 +24,7 @@ module Miasma
23
24
  'LoadBalancerName' => balancer.name
24
25
  )
25
26
  availability_zones.each_with_index do |az, i|
26
- params["AvailabilityZones.member.#{i+1}"] = az
27
+ params["AvailabilityZones.member.#{i + 1}"] = az
27
28
  end
28
29
  if(balancer.listeners)
29
30
  balancer.listeners.each_with_index do |listener, i|
@@ -47,7 +48,9 @@ module Miasma
47
48
  )
48
49
  )
49
50
  balancer.public_addresses = [
50
- :address => result.get(:body, 'CreateLoadBalancerResponse', 'CreateLoadBalancerResult', 'DNSName')
51
+ :address => result.get(:body,
52
+ 'CreateLoadBalancerResponse', 'CreateLoadBalancerResult', 'DNSName'
53
+ )
51
54
  ]
52
55
  balancer.load_data(:id => balancer.name).valid_state
53
56
  if(balancer.health_check)
@@ -114,9 +117,12 @@ module Miasma
114
117
  def load_balancer_data(balancer=nil)
115
118
  params = Smash.new('Action' => 'DescribeLoadBalancers')
116
119
  if(balancer)
117
- params.merge!('LoadBalancerNames.member.1' => balancer.id || balancer.name)
120
+ params['LoadBalancerNames.member.1'] = balancer.id || balancer.name
118
121
  end
119
- result = all_result_pages(nil, :body, 'DescribeLoadBalancersResponse', 'DescribeLoadBalancersResult', 'LoadBalancerDescriptions', 'member') do |options|
122
+ result = all_result_pages(nil, :body,
123
+ 'DescribeLoadBalancersResponse', 'DescribeLoadBalancersResult',
124
+ 'LoadBalancerDescriptions', 'member'
125
+ ) do |options|
120
126
  request(
121
127
  :method => :post,
122
128
  :path => '/',
@@ -124,7 +130,10 @@ module Miasma
124
130
  )
125
131
  end
126
132
  if(balancer)
127
- health_result = all_result_pages(nil, :body, 'DescribeInstanceHealthResponse', 'DescribeInstanceHealthResult', 'InstanceStates', 'member') do |options|
133
+ health_result = all_result_pages(nil, :body,
134
+ 'DescribeInstanceHealthResponse', 'DescribeInstanceHealthResult',
135
+ 'InstanceStates', 'member'
136
+ ) do |options|
128
137
  request(
129
138
  :method => :post,
130
139
  :path => '/',
@@ -156,7 +165,13 @@ module Miasma
156
165
  ).merge(
157
166
  health_result.nil? ? {} : Smash.new(
158
167
  :server_states => health_result.nil? ? nil : health_result.map{|i|
159
- Balancer::ServerState.new(self.api_for(:compute), :id => i['InstanceId'], :status => i['State'], :reason => i['ReasonCode'], :state => i['State'] == 'InService' ? :up : :down)
168
+ Balancer::ServerState.new(
169
+ self.api_for(:compute),
170
+ :id => i['InstanceId'],
171
+ :status => i['State'],
172
+ :reason => i['ReasonCode'],
173
+ :state => i['State'] == 'InService' ? :up : :down
174
+ )
160
175
  }
161
176
  )
162
177
  )
@@ -206,7 +221,9 @@ module Miasma
206
221
  :form => Smash.new(
207
222
  'Action' => 'DescribeAvailabilityZones'
208
223
  )
209
- ).fetch(:body, 'DescribeAvailabilityZonesResponse', 'availabilityZoneInfo', 'item', [])
224
+ ).fetch(:body,
225
+ 'DescribeAvailabilityZonesResponse', 'availabilityZoneInfo', 'item', []
226
+ )
210
227
  [res].flatten.compact.map do |item|
211
228
  if(item['zoneState'] == 'available')
212
229
  item['zoneName']
@@ -3,6 +3,7 @@ require 'miasma'
3
3
  module Miasma
4
4
  module Models
5
5
  class Orchestration
6
+ # AWS Orchestration API
6
7
  class Aws < Orchestration
7
8
 
8
9
  # Extended stack model to provide AWS specific stack options
@@ -12,20 +13,20 @@ module Miasma
12
13
  end
13
14
 
14
15
  # Service name of the API
15
- API_SERVICE = 'cloudformation'
16
+ API_SERVICE = 'cloudformation'.freeze
16
17
  # Service name of the eucalyptus API
17
- EUCA_API_SERVICE = 'CloudFormation'
18
+ EUCA_API_SERVICE = 'CloudFormation'.freeze
18
19
  # Supported version of the AutoScaling API
19
- API_VERSION = '2010-05-15'
20
+ API_VERSION = '2010-05-15'.freeze
20
21
 
21
22
  # Valid stack lookup states
22
23
  STACK_STATES = [
23
- "CREATE_COMPLETE", "CREATE_FAILED", "CREATE_IN_PROGRESS", "DELETE_FAILED",
24
- "DELETE_IN_PROGRESS", "ROLLBACK_COMPLETE", "ROLLBACK_FAILED", "ROLLBACK_IN_PROGRESS",
25
- "UPDATE_COMPLETE", "UPDATE_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_IN_PROGRESS",
26
- "UPDATE_ROLLBACK_COMPLETE", "UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS", "UPDATE_ROLLBACK_FAILED",
27
- "UPDATE_ROLLBACK_IN_PROGRESS"
28
- ]
24
+ 'CREATE_COMPLETE', 'CREATE_FAILED', 'CREATE_IN_PROGRESS', 'DELETE_FAILED',
25
+ 'DELETE_IN_PROGRESS', 'ROLLBACK_COMPLETE', 'ROLLBACK_FAILED', 'ROLLBACK_IN_PROGRESS',
26
+ 'UPDATE_COMPLETE', 'UPDATE_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_IN_PROGRESS',
27
+ 'UPDATE_ROLLBACK_COMPLETE', 'UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS', 'UPDATE_ROLLBACK_FAILED',
28
+ 'UPDATE_ROLLBACK_IN_PROGRESS'
29
+ ].map(&:freeze).freeze
29
30
 
30
31
  include Contrib::AwsApiCore::ApiCommon
31
32
  include Contrib::AwsApiCore::RequestUtils
@@ -48,7 +49,7 @@ module Miasma
48
49
  :api => :orchestration,
49
50
  :collection => :stacks
50
51
  )
51
- )
52
+ ).to_smash(:freeze)
52
53
 
53
54
  # Fetch stacks or update provided stack data
54
55
  #
@@ -62,7 +63,10 @@ module Miasma
62
63
  end
63
64
  if(stack)
64
65
  d_params['StackName'] = stack.id
65
- descriptions = all_result_pages(nil, :body, 'DescribeStacksResponse', 'DescribeStacksResult', 'Stacks', 'member') do |options|
66
+ descriptions = all_result_pages(nil, :body,
67
+ 'DescribeStacksResponse', 'DescribeStacksResult',
68
+ 'Stacks', 'member'
69
+ ) do |options|
66
70
  request(
67
71
  :method => :post,
68
72
  :path => '/',
@@ -70,7 +74,10 @@ module Miasma
70
74
  )
71
75
  end
72
76
  else
73
- lists = all_result_pages(nil, :body, 'ListStacksResponse', 'ListStacksResult', 'StackSummaries', 'member') do |options|
77
+ lists = all_result_pages(nil, :body,
78
+ 'ListStacksResponse', 'ListStacksResult',
79
+ 'StackSummaries', 'member'
80
+ ) do |options|
74
81
  request(
75
82
  :method => :post,
76
83
  :path => '/',
@@ -143,9 +150,18 @@ module Miasma
143
150
  # @return [Models::Orchestration::Stack]
144
151
  def stack_save(stack)
145
152
  params = Smash.new('StackName' => stack.name)
153
+ if(stack.dirty?(:parameters))
154
+ initial_parameters = stack.data[:parameters] || {}
155
+ else
156
+ initial_parameters = {}
157
+ end
146
158
  (stack.parameters || {}).each_with_index do |pair, idx|
147
159
  params["Parameters.member.#{idx + 1}.ParameterKey"] = pair.first
148
- params["Parameters.member.#{idx + 1}.ParameterValue"] = pair.last
160
+ if(initial_parameters[pair.first] == pair.last)
161
+ params["Parameters.member.#{idx + 1}.UsePreviousValue"] = true
162
+ else
163
+ params["Parameters.member.#{idx + 1}.ParameterValue"] = pair.last
164
+ end
149
165
  end
150
166
  (stack.capabilities || []).each_with_index do |cap, idx|
151
167
  params["Capabilities.member.#{idx + 1}"] = cap
@@ -171,12 +187,10 @@ module Miasma
171
187
  end
172
188
  if(stack.template_url)
173
189
  params['TemplateURL'] = stack.template_url
190
+ elsif(!stack.dirty?(:template) && stack.persisted?)
191
+ params['UsePreviousTemplate'] = true
174
192
  else
175
- if(!stack.dirty?(:template) && stack.persisted?)
176
- params['UsePreviousTemplate'] = true
177
- else
178
- params['TemplateBody'] = MultiJson.dump(stack.template)
179
- end
193
+ params['TemplateBody'] = MultiJson.dump(stack.template)
180
194
  end
181
195
  if(stack.persisted?)
182
196
  result = request(
@@ -316,7 +330,10 @@ module Miasma
316
330
  # @param stack [Models::Orchestration::Stack]
317
331
  # @return [Array<Models::Orchestration::Stack::Resource>]
318
332
  def resource_all(stack)
319
- results = all_result_pages(nil, :body, 'ListStackResourcesResponse', 'ListStackResourcesResult', 'StackResourceSummaries', 'member') do |options|
333
+ results = all_result_pages(nil, :body,
334
+ 'ListStackResourcesResponse', 'ListStackResourcesResult',
335
+ 'StackResourceSummaries', 'member'
336
+ ) do |options|
320
337
  request(
321
338
  :method => :post,
322
339
  :path => '/',
@@ -353,7 +370,10 @@ module Miasma
353
370
  'LogicalResourceId' => resource.logical_id,
354
371
  'StackName' => resource.stack.name
355
372
  )
356
- ).get(:body, 'DescribeStackResourceResponse', 'DescribeStackResourceResult', 'StackResourceDetail')
373
+ ).get(:body,
374
+ 'DescribeStackResourceResponse', 'DescribeStackResourceResult',
375
+ 'StackResourceDetail'
376
+ )
357
377
  resource.updated = result['LastUpdatedTimestamp']
358
378
  resource.type = result['ResourceType']
359
379
  resource.state = result['ResourceStatus'].downcase.to_sym
@@ -368,7 +388,11 @@ module Miasma
368
388
  # @param stack [Models::Orchestration::Stack]
369
389
  # @return [Array<Models::Orchestration::Stack::Event>]
370
390
  def event_all(stack, evt_id=nil)
371
- results = all_result_pages(nil, :body, 'DescribeStackEventsResponse', 'DescribeStackEventsResult', 'StackEvents', 'member') do |options|
391
+ evt_id = stack.custom[:last_event_token] if evt_id == true
392
+ results = all_result_pages(evt_id, :body,
393
+ 'DescribeStackEventsResponse', 'DescribeStackEventsResult',
394
+ 'StackEvents', 'member'
395
+ ) do |options|
372
396
  request(
373
397
  :method => :post,
374
398
  :path => '/',
@@ -379,6 +403,7 @@ module Miasma
379
403
  )
380
404
  end
381
405
  events = results.map do |event|
406
+ stack.custom[:last_event_token] = event['NextToken'] if event['NextToken']
382
407
  Stack::Event.new(
383
408
  stack,
384
409
  :id => event['EventId'],
@@ -393,8 +418,7 @@ module Miasma
393
418
  end
394
419
  if(evt_id)
395
420
  idx = events.index{|d| d.id == evt_id}
396
- idx ? idx + 1 : 0
397
- events.slice(0, idx)
421
+ idx ? events.slice(0, idx) : events
398
422
  else
399
423
  events
400
424
  end
@@ -5,14 +5,15 @@ require 'miasma'
5
5
  module Miasma
6
6
  module Models
7
7
  class Storage
8
+ # AWS storage API
8
9
  class Aws < Storage
9
10
 
10
11
  # Service name of the API
11
- API_SERVICE = 's3'
12
+ API_SERVICE = 's3'.freeze
12
13
  # Service name of the API in eucalyptus
13
- EUCA_API_SERVICE = 'objectstorage'
14
+ EUCA_API_SERVICE = 'objectstorage'.freeze
14
15
  # Supported version of the Storage API
15
- API_VERSION = '2006-03-01'
16
+ API_VERSION = '2006-03-01'.freeze
16
17
 
17
18
  include Contrib::AwsApiCore::ApiCommon
18
19
  include Contrib::AwsApiCore::RequestUtils
@@ -39,7 +40,9 @@ module Miasma
39
40
  end
40
41
  set = result.get(*result_key.slice(0, 2))
41
42
  if(set.is_a?(Hash) && set['IsTruncated'] && set['Contents'])
42
- content_key = (set['Contents'].respond_to?(:last) ? set['Contents'].last : set['Contents'])['Key']
43
+ content_key = (
44
+ set['Contents'].respond_to?(:last) ? set['Contents'].last : set['Contents']
45
+ )['Key']
43
46
  list += all_result_pages(content_key, *result_key, &block)
44
47
  end
45
48
  list.compact
@@ -256,7 +259,8 @@ module Miasma
256
259
  unless(headers.empty?)
257
260
  args[:headers] = headers
258
261
  end
259
- if(file.attributes[:body].respond_to?(:read) && file.attributes[:body].size >= Storage::MAX_BODY_SIZE_FOR_STRINGIFY)
262
+ if(file.attributes[:body].respond_to?(:read) &&
263
+ file.attributes[:body].size >= Storage::MAX_BODY_SIZE_FOR_STRINGIFY)
260
264
  upload_id = request(
261
265
  args.merge(
262
266
  Smash.new(
@@ -11,12 +11,12 @@ Gem::Specification.new do |s|
11
11
  s.license = 'Apache 2.0'
12
12
  s.require_path = 'lib'
13
13
  s.add_runtime_dependency 'miasma', '>= 0.3.1', '< 0.5'
14
- s.add_development_dependency 'rake'
15
- s.add_development_dependency 'rubocop'
14
+ s.add_development_dependency 'rake', '~> 10.5.0'
15
+ s.add_development_dependency 'rubocop', '0.37.2'
16
16
  s.add_development_dependency 'pry'
17
17
  s.add_development_dependency 'vcr'
18
18
  s.add_development_dependency 'mocha'
19
- s.add_development_dependency 'webmock'
19
+ s.add_development_dependency 'webmock', '~> 1.23.0'
20
20
  s.add_development_dependency 'minitest'
21
21
  s.add_development_dependency 'minitest-vcr'
22
22
  s.files = Dir['lib/**/*'] + %w(miasma-aws.gemspec README.md CHANGELOG.md LICENSE)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miasma-aws
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Roberts
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-04-23 00:00:00.000000000 Z
11
+ date: 2016-10-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: miasma
@@ -34,30 +34,30 @@ dependencies:
34
34
  name: rake
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '0'
39
+ version: 10.5.0
40
40
  type: :development
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0'
46
+ version: 10.5.0
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rubocop
49
49
  requirement: !ruby/object:Gem::Requirement
50
50
  requirements:
51
- - - ">="
51
+ - - '='
52
52
  - !ruby/object:Gem::Version
53
- version: '0'
53
+ version: 0.37.2
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
57
57
  requirements:
58
- - - ">="
58
+ - - '='
59
59
  - !ruby/object:Gem::Version
60
- version: '0'
60
+ version: 0.37.2
61
61
  - !ruby/object:Gem::Dependency
62
62
  name: pry
63
63
  requirement: !ruby/object:Gem::Requirement
@@ -104,16 +104,16 @@ dependencies:
104
104
  name: webmock
105
105
  requirement: !ruby/object:Gem::Requirement
106
106
  requirements:
107
- - - ">="
107
+ - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '0'
109
+ version: 1.23.0
110
110
  type: :development
111
111
  prerelease: false
112
112
  version_requirements: !ruby/object:Gem::Requirement
113
113
  requirements:
114
- - - ">="
114
+ - - "~>"
115
115
  - !ruby/object:Gem::Version
116
- version: '0'
116
+ version: 1.23.0
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: minitest
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -183,9 +183,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
183
183
  version: '0'
184
184
  requirements: []
185
185
  rubyforge_project:
186
- rubygems_version: 2.4.8
186
+ rubygems_version: 2.5.1
187
187
  signing_key:
188
188
  specification_version: 4
189
189
  summary: Smoggy AWS API
190
190
  test_files: []
191
- has_rdoc: