miasma-aws 0.3.4 → 0.3.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: