cloud-mu 3.1.2 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (201) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +10 -13
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -3
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +135 -37
  23. data/cloud-mu.gemspec +22 -20
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +165 -111
  45. data/modules/mu/adoption.rb +401 -68
  46. data/modules/mu/cleanup.rb +199 -306
  47. data/modules/mu/cloud.rb +100 -1632
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +46 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +920 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +165 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +4 -4
  61. data/modules/mu/config/cache_cluster.rb +1 -1
  62. data/modules/mu/config/collection.rb +4 -4
  63. data/modules/mu/config/container_cluster.rb +9 -4
  64. data/modules/mu/config/database.rb +83 -104
  65. data/modules/mu/config/database.yml +1 -2
  66. data/modules/mu/config/dnszone.rb +6 -6
  67. data/modules/mu/config/doc_helpers.rb +516 -0
  68. data/modules/mu/config/endpoint.rb +4 -4
  69. data/modules/mu/config/firewall_rule.rb +103 -4
  70. data/modules/mu/config/folder.rb +4 -4
  71. data/modules/mu/config/function.rb +3 -3
  72. data/modules/mu/config/group.rb +4 -4
  73. data/modules/mu/config/habitat.rb +4 -4
  74. data/modules/mu/config/loadbalancer.rb +60 -14
  75. data/modules/mu/config/log.rb +4 -4
  76. data/modules/mu/config/msg_queue.rb +4 -4
  77. data/modules/mu/config/nosqldb.rb +4 -4
  78. data/modules/mu/config/notifier.rb +3 -3
  79. data/modules/mu/config/ref.rb +365 -0
  80. data/modules/mu/config/role.rb +4 -4
  81. data/modules/mu/config/schema_helpers.rb +509 -0
  82. data/modules/mu/config/search_domain.rb +4 -4
  83. data/modules/mu/config/server.rb +97 -70
  84. data/modules/mu/config/server.yml +1 -0
  85. data/modules/mu/config/server_pool.rb +5 -9
  86. data/modules/mu/config/storage_pool.rb +1 -1
  87. data/modules/mu/config/tail.rb +200 -0
  88. data/modules/mu/config/user.rb +4 -4
  89. data/modules/mu/config/vpc.rb +70 -27
  90. data/modules/mu/config/vpc.yml +0 -1
  91. data/modules/mu/defaults/AWS.yaml +83 -60
  92. data/modules/mu/defaults/Azure.yaml +1 -0
  93. data/modules/mu/defaults/Google.yaml +3 -2
  94. data/modules/mu/deploy.rb +30 -26
  95. data/modules/mu/groomer.rb +17 -2
  96. data/modules/mu/groomers/ansible.rb +188 -41
  97. data/modules/mu/groomers/chef.rb +116 -55
  98. data/modules/mu/logger.rb +127 -148
  99. data/modules/mu/master.rb +389 -2
  100. data/modules/mu/master/chef.rb +3 -4
  101. data/modules/mu/master/ldap.rb +3 -3
  102. data/modules/mu/master/ssl.rb +12 -3
  103. data/modules/mu/mommacat.rb +217 -2612
  104. data/modules/mu/mommacat/daemon.rb +397 -0
  105. data/modules/mu/mommacat/naming.rb +473 -0
  106. data/modules/mu/mommacat/search.rb +495 -0
  107. data/modules/mu/mommacat/storage.rb +722 -0
  108. data/modules/mu/{clouds → providers}/README.md +1 -1
  109. data/modules/mu/{clouds → providers}/aws.rb +271 -112
  110. data/modules/mu/{clouds → providers}/aws/alarm.rb +5 -3
  111. data/modules/mu/{clouds → providers}/aws/bucket.rb +26 -22
  112. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +33 -67
  113. data/modules/mu/{clouds → providers}/aws/collection.rb +24 -23
  114. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +681 -721
  115. data/modules/mu/providers/aws/database.rb +1744 -0
  116. data/modules/mu/{clouds → providers}/aws/dnszone.rb +64 -63
  117. data/modules/mu/{clouds → providers}/aws/endpoint.rb +22 -27
  118. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +214 -244
  119. data/modules/mu/{clouds → providers}/aws/folder.rb +7 -7
  120. data/modules/mu/{clouds → providers}/aws/function.rb +17 -22
  121. data/modules/mu/{clouds → providers}/aws/group.rb +23 -23
  122. data/modules/mu/{clouds → providers}/aws/habitat.rb +17 -14
  123. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +57 -48
  124. data/modules/mu/{clouds → providers}/aws/log.rb +15 -12
  125. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +17 -16
  126. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +18 -11
  127. data/modules/mu/{clouds → providers}/aws/notifier.rb +11 -6
  128. data/modules/mu/{clouds → providers}/aws/role.rb +112 -86
  129. data/modules/mu/{clouds → providers}/aws/search_domain.rb +39 -33
  130. data/modules/mu/{clouds → providers}/aws/server.rb +835 -1133
  131. data/modules/mu/{clouds → providers}/aws/server_pool.rb +56 -60
  132. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +24 -42
  133. data/modules/mu/{clouds → providers}/aws/user.rb +21 -22
  134. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  135. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  136. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  137. data/modules/mu/{clouds → providers}/aws/vpc.rb +523 -929
  138. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  139. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  140. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  141. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  142. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  143. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  144. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  145. data/modules/mu/{clouds → providers}/azure/server.rb +95 -48
  146. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  147. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  148. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  149. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  150. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  151. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  152. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  153. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  156. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  160. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  161. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  162. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  163. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  164. data/modules/mu/{clouds → providers}/google.rb +67 -30
  165. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  166. data/modules/mu/{clouds → providers}/google/container_cluster.rb +84 -77
  167. data/modules/mu/{clouds → providers}/google/database.rb +10 -20
  168. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  169. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  170. data/modules/mu/{clouds → providers}/google/function.rb +139 -167
  171. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  172. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  173. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +18 -20
  174. data/modules/mu/{clouds → providers}/google/role.rb +92 -58
  175. data/modules/mu/{clouds → providers}/google/server.rb +242 -155
  176. data/modules/mu/{clouds → providers}/google/server_pool.rb +25 -44
  177. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  178. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  179. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  180. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  181. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  182. data/modules/tests/bucket.yml +4 -0
  183. data/modules/tests/centos6.yaml +11 -0
  184. data/modules/tests/centos7.yaml +11 -0
  185. data/modules/tests/centos8.yaml +12 -0
  186. data/modules/tests/ecs.yaml +23 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/rds.yaml +108 -0
  189. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  190. data/modules/tests/regrooms/bucket.yml +19 -0
  191. data/modules/tests/regrooms/rds.yaml +123 -0
  192. data/modules/tests/server-with-scrub-muisms.yaml +1 -0
  193. data/modules/tests/super_simple_bok.yml +1 -3
  194. data/modules/tests/win2k12.yaml +17 -5
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +232 -154
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1985
@@ -344,8 +344,8 @@ module MU
344
344
  )
345
345
  rescue Aws::Route53::Errors::LastVPCAssociation => e
346
346
  MU.log e.inspect, MU::WARN
347
- rescue Aws::Route53::Errors::VPCAssociationNotFound => e
348
- MU.log "VPC #{vpc_id} access to zone #{id} already revoked", MU::WARN
347
+ rescue Aws::Route53::Errors::VPCAssociationNotFound
348
+ MU.log "VPC #{vpc_id} access to zone #{id} already revoked", MU::NOTICE
349
349
  end
350
350
  end
351
351
  end
@@ -366,10 +366,10 @@ module MU
366
366
  # @param location [Hash<String>]: A parsed Hash of {MU::Config::BasketofKittens::dnszones::records::geo_location}.
367
367
  # @param set_identifier [String]: A unique string to differentiate otherwise-similar records. Normally auto-generated, should not need to specify.
368
368
  # @param alias_zone [String]: Zone ID of the target's hosted zone, when creating an alias (type R53ALIAS)
369
- def self.manageRecord(id, name, type, targets: nil, aliases: nil,
369
+ def self.manageRecord(id, name, type, targets: nil,
370
370
  ttl: 7200, delete: false, sync_wait: true, failover: nil,
371
371
  healthcheck: nil, region: nil, weight: nil, overwrite: true,
372
- location: nil, set_identifier: nil, alias_zone: nil)
372
+ location: nil, set_identifier: nil, alias_zone: nil, noop: false)
373
373
 
374
374
  MU.setVar("curRegion", region) if !region.nil?
375
375
  zone = MU::Cloud::DNSZone.find(cloud_id: id).values.first
@@ -380,6 +380,11 @@ module MU
380
380
  action = "UPSERT" if overwrite
381
381
  action = "DELETE" if delete
382
382
 
383
+ record_sets = MU::Cloud::AWS.route53.list_resource_record_sets(
384
+ hosted_zone_id: id,
385
+ start_record_name: name
386
+ ).resource_record_sets if delete
387
+
383
388
  if type == "R53ALIAS"
384
389
  target_zone = id
385
390
  target_name = targets[0].downcase
@@ -413,7 +418,15 @@ module MU
413
418
  }
414
419
  else
415
420
  rrsets = []
416
- if !targets.nil?
421
+ if delete
422
+ record_sets.each { |r|
423
+ if r.name == name and r.type == type
424
+ rrsets = MU.structToHash(r.resource_records)
425
+ end
426
+ }
427
+ end
428
+
429
+ if !targets.nil? and (!delete or rrsets.empty?)
417
430
  targets.each { |target|
418
431
  rrsets << {value: target}
419
432
  }
@@ -426,6 +439,7 @@ module MU
426
439
  resource_records: rrsets
427
440
  }
428
441
 
442
+
429
443
  if !healthcheck.nil?
430
444
  base_rrset[:health_check_id] = healthcheck
431
445
  end
@@ -445,12 +459,13 @@ module MU
445
459
 
446
460
  # Doing an UPSERT with a new set_identifier will fail with a record already exist error, so lets try and get it from an existing record.
447
461
  # This can be an issue with multiple secondary failover records
448
- if (location || failover || region || weight) && set_identifier.nil?
449
- record_sets = MU::Cloud::AWS.route53.list_resource_record_sets(
462
+ if (location || failover || region || weight) and set_identifier.nil?
463
+ record_sets ||= MU::Cloud::AWS.route53.list_resource_record_sets(
450
464
  hosted_zone_id: id,
451
465
  start_record_name: name
452
466
  ).resource_record_sets
453
467
 
468
+
454
469
  record_sets.each { |r|
455
470
  if r.name == name
456
471
  if location && location == r.location
@@ -497,18 +512,23 @@ module MU
497
512
  MU.log "Adding DNS record #{name} => #{targets} (#{type}) to #{id}", details: params
498
513
  end
499
514
 
500
- begin
515
+ return if noop
516
+
517
+ on_retry = Proc.new { |e|
518
+ if (delete and e.message.match(/but it was not found/)) or
519
+ (!delete and e.message.match(/(it|name) already exists/))
520
+ MU.log e.message, MU::DEBUG, details: params
521
+ return
522
+ elsif e.class == Aws::Route53::Errors::InvalidChangeBatch
523
+ MU.log "Problem managing entry for #{name}", MU::ERR, details: params
524
+ raise MuError, e.inspect
525
+ end
526
+ }
527
+
528
+ change_id = nil
529
+ MU.retrier([Aws::Route53::Errors::PriorRequestNotComplete, Aws::Route53::Errors::InvalidChangeBatch], wait: 15, max: 10, on_retry: on_retry) {
501
530
  change_id = MU::Cloud::AWS.route53.change_resource_record_sets(params).change_info.id
502
- rescue Aws::Route53::Errors::PriorRequestNotComplete => e
503
- sleep 10
504
- retry
505
- rescue Aws::Route53::Errors::InvalidChangeBatch, Aws::Route53::Errors::InvalidInput, Exception => e
506
- return if e.message.match(/ but it already exists/) and !delete
507
- MU.log "Failed to change DNS records, #{e.inspect}", MU::ERR, details: params
508
- raise e if !delete
509
- MU.log "Record #{name} (#{type}) in #{id} can't be deleted. Already removed? #{e.inspect}", MU::WARN, details: params if delete
510
- return
511
- end
531
+ }
512
532
 
513
533
  if sync_wait
514
534
  attempts = 0
@@ -535,23 +555,27 @@ module MU
535
555
  # @param delete [Boolean]: Remove this entry instead of creating it.
536
556
  # @param cloudclass [Object]: The resource's Mu class.
537
557
  # @param sync_wait [Boolean]: Wait for DNS entry to propagate across zone.
538
- def self.genericMuDNSEntry(name: nil, target: nil, cloudclass: nil, noop: false, delete: false, sync_wait: true)
539
- return nil if name.nil? or target.nil? or cloudclass.nil?
540
- mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu").values.first
558
+ def self.genericMuDNSEntry(name: nil, target: nil, cloudclass: nil, noop: false, delete: false, sync_wait: true, credentials: nil)
559
+ return nil if name.nil? or cloudclass.nil?
560
+ return nil if target.nil? and !delete
561
+ mu_zone = MU::Cloud::DNSZone.find(cloud_id: "platform-mu", credentials: credentials).values.first
541
562
  raise MuError, "Couldn't isolate platform-mu DNS zone" if mu_zone.nil?
542
563
 
543
564
  if !mu_zone.nil? and !MU.myVPC.nil?
544
565
  subdomain = cloudclass.cfg_name
545
566
  dns_name = name.downcase+"."+subdomain
546
567
  dns_name += "."+MU.myInstanceId if MU.myInstanceId
568
+
547
569
  record_type = "CNAME"
548
570
  record_type = "A" if target.match(/^\d+\.\d+\.\d+\.\d+/)
549
571
  ip = nil
550
572
 
551
- lookup = MU::Cloud::AWS.route53.list_resource_record_sets(
552
- hosted_zone_id: mu_zone.id,
553
- start_record_name: "#{dns_name}.platform-mu",
554
- start_record_type: record_type
573
+ records = []
574
+ lookup = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(
575
+ hosted_zone_id: mu_zone.id,
576
+ start_record_name: "#{dns_name}.platform-mu",
577
+ start_record_type: record_type,
578
+ max_items: 1
555
579
  ).resource_record_sets
556
580
 
557
581
  lookup.each { |record|
@@ -572,34 +596,14 @@ module MU
572
596
  # MU.log "'#{dns_name}.platform-mu' does not resolve.", MU::DEBUG, details: e.inspect
573
597
  # end
574
598
 
575
- if ip == target
576
- return "#{dns_name}.platform-mu" if !delete
577
- elsif noop
578
- return nil
599
+ if ip == target and !delete
600
+ return "#{dns_name}.platform-mu"
579
601
  end
580
602
 
581
603
  sync_wait = false if delete
582
604
 
583
605
  record_type = "R53ALIAS" if cloudclass == MU::Cloud::AWS::LoadBalancer
584
- attempts = 0
585
- begin
586
- MU::Cloud::AWS::DNSZone.manageRecord(mu_zone.id, dns_name, record_type, targets: [target], delete: delete, sync_wait: sync_wait)
587
- rescue Aws::Route53::Errors::PriorRequestNotComplete => e
588
- MU.log "Route53 was still processing a request, waiting", MU::WARN, details: e
589
- sleep 15
590
- retry
591
- rescue Aws::Route53::Errors::InvalidChangeBatch => e
592
- if e.inspect.match(/alias target name does not lie within the target zone/) and attempts < 5
593
- MU.log e.inspect, MU::WARN
594
- sleep 15
595
- attempts = attempts + 1
596
- retry
597
- elsif !e.inspect.match(/(it|name) already exists/)
598
- raise MuError, "Problem managing entry for #{dns_name} -> #{target}: #{e.inspect}"
599
- else
600
- MU.log "#{dns_name} already exists", MU::DEBUG, details: e.inspect
601
- end
602
- end
606
+ MU::Cloud::AWS::DNSZone.manageRecord(mu_zone.id, dns_name, record_type, targets: [target], delete: delete, sync_wait: sync_wait, noop: noop)
603
607
  return "#{dns_name}.platform-mu"
604
608
  else
605
609
  return nil
@@ -663,7 +667,8 @@ module MU
663
667
  # Called by {MU::Cleanup}. Locates resources that were created by the
664
668
  # currently-loaded deployment, and purges them.
665
669
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
666
- checks_to_clean = []
670
+ MU.log "AWS::DNSZone.cleanup: need to support flags['known']", MU::DEBUG, details: flags
671
+
667
672
  threads = []
668
673
  MU::Cloud::AWS.route53(credentials: credentials).list_health_checks.health_checks.each { |check|
669
674
  begin
@@ -692,19 +697,19 @@ module MU
692
697
  threads << Thread.new(check) { |mycheck|
693
698
  MU.dupGlobals(parent_thread_id)
694
699
  Thread.abort_on_exception = true
695
- MU.log "Removing health check #{check.id}"
700
+ MU.log "Removing health check #{mycheck.id}"
696
701
  retries = 5
697
702
  begin
698
- MU::Cloud::AWS.route53(credentials: credentials).delete_health_check(health_check_id: check.id) if !noop
703
+ MU::Cloud::AWS.route53(credentials: credentials).delete_health_check(health_mycheck_id: mycheck.id) if !noop
699
704
  rescue Aws::Route53::Errors::NoSuchHealthCheck => e
700
- MU.log "Health Check '#{check.id}' disappeared before I could remove it", MU::WARN, details: e.inspect
705
+ MU.log "Health Check '#{mycheck.id}' disappeared before I could remove it", MU::WARN, details: e.inspect
701
706
  rescue Aws::Route53::Errors::InvalidInput => e
702
707
  if e.message.match(/is still referenced from parent health check/) && retries <= 5
703
708
  sleep 5
704
709
  retries += 1
705
710
  retry
706
711
  else
707
- MU.log "Health Check #{check.id} still has a parent health check associated with it, skipping", MU::WARN, details: e.inspect
712
+ MU.log "Health Check #{mycheck.id} still has a parent health check associated with it, skipping", MU::WARN, details: e.inspect
708
713
  end
709
714
  end
710
715
  }
@@ -719,7 +724,7 @@ module MU
719
724
  }
720
725
 
721
726
  zones = MU::Cloud::DNSZone.find(deploy_id: MU.deploy_id, region: region)
722
- zones.each_pair { |id, zone|
727
+ zones.values.each { |zone|
723
728
  MU.log "Purging DNS Zone '#{zone.name}' (#{zone.id})"
724
729
  if !noop
725
730
  begin
@@ -727,7 +732,6 @@ module MU
727
732
  rrsets = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(hosted_zone_id: zone.id)
728
733
  rrsets.resource_record_sets.each { |rrset|
729
734
  next if zone.name == rrset.name and (rrset.type == "NS" or rrset.type == "SOA")
730
- records = []
731
735
  MU::Cloud::AWS.route53(credentials: credentials).change_resource_record_sets(
732
736
  hosted_zone_id: zone.id,
733
737
  change_batch: {
@@ -791,9 +795,9 @@ module MU
791
795
  end
792
796
 
793
797
  # Cloud-specific configuration properties.
794
- # @param config [MU::Config]: The calling MU::Config object
798
+ # @param _config [MU::Config]: The calling MU::Config object
795
799
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
796
- def self.schema(config)
800
+ def self.schema(_config)
797
801
  toplevel_required = []
798
802
  schema = {}
799
803
  [toplevel_required, schema]
@@ -801,9 +805,9 @@ module MU
801
805
 
802
806
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::dnszones}, bare and unvalidated.
803
807
  # @param zone [Hash]: The resource to process and validate
804
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
808
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
805
809
  # @return [Boolean]: True if validation succeeded, False otherwise
806
- def self.validateConfig(zone, configurator)
810
+ def self.validateConfig(zone, _configurator)
807
811
  ok = true
808
812
 
809
813
  if !zone["records"].nil?
@@ -821,10 +825,7 @@ module MU
821
825
  end
822
826
 
823
827
  if !record['mu_type'].nil?
824
- zone["dependencies"] << {
825
- "type" => record['mu_type'],
826
- "name" => record['target']
827
- }
828
+ MU::Config.addDependency(zone, record['target'], record['mu_type'])
828
829
  end
829
830
 
830
831
  if record.has_key?('healthchecks') && !record['healthchecks'].empty?
@@ -116,15 +116,15 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
116
116
  end
117
117
 
118
118
  if m['integrate_with']
119
- role_arn = if m['iam_role']
120
- if m['iam_role'].match(/^arn:/)
121
- m['iam_role']
122
- else
123
- sib_role = @deploy.findLitterMate(name: m['iam_role'], type: "roles")
124
- sib_role.cloudobj.arn
119
+ # role_arn = if m['iam_role']
120
+ # if m['iam_role'].match(/^arn:/)
121
+ # m['iam_role']
122
+ # else
123
+ # sib_role = @deploy.findLitterMate(name: m['iam_role'], type: "roles")
124
+ # sib_role.cloudobj.arn
125
125
  # XXX make this more like get_role_arn in Function, or just use Role.find?
126
- end
127
- end
126
+ # end
127
+ # end
128
128
 
129
129
  function_obj = nil
130
130
 
@@ -198,13 +198,12 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
198
198
  generate_methods
199
199
 
200
200
  MU.log "Deploying API Gateway #{@config['name']} to #{@config['deploy_to']}"
201
- resp = MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials']).create_deployment(
201
+ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials']).create_deployment(
202
202
  rest_api_id: @cloud_id,
203
203
  stage_name: @config['deploy_to']
204
204
  # cache_cluster_enabled: false,
205
205
  # cache_cluster_size: 0.5,
206
206
  )
207
- deployment_id = resp.id
208
207
  # this automatically creates a stage with the same name, so we don't
209
208
  # have to deal with that
210
209
 
@@ -220,11 +219,14 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
220
219
 
221
220
  end
222
221
 
222
+ @cloud_desc_cache = nil
223
223
  # @return [Struct]
224
- def cloud_desc
225
- MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials']).get_rest_api(
224
+ def cloud_desc(use_cache: true)
225
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
226
+ @cloud_desc_cache = MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials']).get_rest_api(
226
227
  rest_api_id: @cloud_id
227
228
  )
229
+ @cloud_desc_cache
228
230
  end
229
231
 
230
232
  # Return the metadata for this API
@@ -241,6 +243,9 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
241
243
  # @param region [String]: The cloud provider region
242
244
  # @return [void]
243
245
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
246
+ MU.log "AWS::Endpoint.cleanup: need to support flags['known']", MU::DEBUG, details: flags
247
+ MU.log "Placeholder: AWS Endpoint artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
248
+
244
249
  resp = MU::Cloud::AWS.apig(region: region, credentials: credentials).get_rest_apis
245
250
  if resp and resp.items
246
251
  resp.items.each { |api|
@@ -279,9 +284,9 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
279
284
  end
280
285
 
281
286
  # Cloud-specific configuration properties.
282
- # @param config [MU::Config]: The calling MU::Config object
287
+ # @param _config [MU::Config]: The calling MU::Config object
283
288
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
284
- def self.schema(config)
289
+ def self.schema(_config)
285
290
  toplevel_required = []
286
291
  schema = {
287
292
  "deploy_to" => {
@@ -467,11 +472,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
467
472
  endpoint['methods'].each { |m|
468
473
  if m['integrate_with'] and m['integrate_with']['name']
469
474
  if m['integrate_with']['type'] != "aws_generic"
470
- endpoint['dependencies'] ||= []
471
- endpoint['dependencies'] << {
472
- "type" => m['integrate_with']['type'],
473
- "name" => m['integrate_with']['name']
474
- }
475
+ MU::Config.addDependency(endpoint, m['integrate_with']['name'], m['integrate_with']['type'])
475
476
  end
476
477
 
477
478
  m['integrate_with']['backend_http_method'] ||= m['type']
@@ -520,13 +521,8 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
520
521
  end
521
522
  configurator.insertKitten(roledesc, "roles")
522
523
 
523
- endpoint['dependencies'] ||= []
524
524
  m['iam_role'] = endpoint['name']+"-"+m['integrate_with']['name']
525
-
526
- endpoint['dependencies'] << {
527
- "type" => "role",
528
- "name" => endpoint['name']+"-"+m['integrate_with']['name']
529
- }
525
+ MU::Config.addDependency(endpoint, m['iam_role'], "role")
530
526
  end
531
527
  end
532
528
  }
@@ -538,8 +534,6 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
538
534
  ok
539
535
  end
540
536
 
541
- private
542
-
543
537
  def self.cors_option_integrations(path)
544
538
  {
545
539
  "type" => "OPTIONS",
@@ -585,6 +579,7 @@ MU::Cloud::AWS.apig(region: @config['region'], credentials: @config['credentials
585
579
  }
586
580
  }
587
581
  end
582
+ private_class_method :cors_option_integrations
588
583
 
589
584
  end
590
585
  end
@@ -18,7 +18,7 @@ module MU
18
18
  class AWS
19
19
  # A firewall ruleset as configured in {MU::Config::BasketofKittens::firewall_rules}
20
20
  class FirewallRule < MU::Cloud::FirewallRule
21
- require "mu/clouds/aws/vpc"
21
+ require "mu/providers/aws/vpc"
22
22
 
23
23
  @admin_sgs = Hash.new
24
24
  @admin_sg_semaphore = Mutex.new
@@ -54,13 +54,12 @@ module MU
54
54
 
55
55
  secgroup = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_security_group(sg_struct)
56
56
  @cloud_id = secgroup.group_id
57
- rescue Aws::EC2::Errors::InvalidGroupDuplicate => e
57
+ rescue Aws::EC2::Errors::InvalidGroupDuplicate
58
58
  MU.log "EC2 Security Group #{groupname} already exists, using it", MU::NOTICE
59
59
  filters = [{name: "group-name", values: [groupname]}]
60
60
  filters << {name: "vpc-id", values: [vpc_id]} if !vpc_id.nil?
61
61
 
62
62
  secgroup = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_security_groups(filters: filters).security_groups.first
63
- deploy_id = @deploy.deploy_id if !@deploy_id.nil?
64
63
  if secgroup.nil?
65
64
  raise MuError, "Failed to locate security group named #{groupname}, even though EC2 says it already exists", caller
66
65
  end
@@ -69,24 +68,24 @@ module MU
69
68
 
70
69
  begin
71
70
  MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_security_groups(group_ids: [secgroup.group_id])
72
- rescue Aws::EC2::Errors::InvalidGroupNotFound => e
71
+ rescue Aws::EC2::Errors::InvalidGroupNotFound
73
72
  MU.log "#{secgroup.group_id} not yet ready, waiting...", MU::NOTICE
74
73
  sleep 10
75
74
  retry
76
75
  end
77
76
 
78
77
  MU::Cloud::AWS.createStandardTags(secgroup.group_id, region: @config['region'], credentials: @config['credentials'])
79
- MU::MommaCat.createTag(secgroup.group_id, "Name", groupname, region: @config['region'], credentials: @config['credentials'])
78
+ MU::Cloud::AWS.createTag(secgroup.group_id, "Name", groupname, region: @config['region'], credentials: @config['credentials'])
80
79
 
81
80
  if @config['optional_tags']
82
81
  MU::MommaCat.listOptionalTags.each { |key, value|
83
- MU::MommaCat.createTag(secgroup.group_id, key, value, region: @config['region'], credentials: @config['credentials'])
82
+ MU::Cloud::AWS.createTag(secgroup.group_id, key, value, region: @config['region'], credentials: @config['credentials'])
84
83
  }
85
84
  end
86
85
 
87
86
  if @config['tags']
88
87
  @config['tags'].each { |tag|
89
- MU::MommaCat.createTag(secgroup.group_id, tag['key'], tag['value'], region: @config['region'], credentials: @config['credentials'])
88
+ MU::Cloud::AWS.createTag(secgroup.group_id, tag['key'], tag['value'], region: @config['region'], credentials: @config['credentials'])
90
89
  }
91
90
  end
92
91
 
@@ -180,7 +179,7 @@ module MU
180
179
  ip_permissions: ec2_rule
181
180
  )
182
181
  end
183
- rescue Aws::EC2::Errors::InvalidPermissionDuplicate => e
182
+ rescue Aws::EC2::Errors::InvalidPermissionDuplicate
184
183
  MU.log "Attempt to add duplicate rule to #{@cloud_id}", MU::DEBUG, details: ec2_rule
185
184
  # Ensure that, at least, the description field gets updated on
186
185
  # existing rules
@@ -246,7 +245,7 @@ module MU
246
245
  # Reverse-map our cloud description into a runnable config hash.
247
246
  # We assume that any values we have in +@config+ are placeholders, and
248
247
  # calculate our own accordingly based on what's live in the cloud.
249
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
248
+ def toKitten(**_args)
250
249
  bok = {
251
250
  "cloud" => "AWS",
252
251
  "credentials" => @config['credentials'],
@@ -383,9 +382,8 @@ module MU
383
382
  # @param region [String]: The cloud provider region
384
383
  # @return [void]
385
384
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
386
- filters = nil
387
- if flags and flags["vpc_id"]
388
- filters = [
385
+ filters = if flags and flags["vpc_id"]
386
+ [
389
387
  {name: "vpc-id", values: [flags["vpc_id"]]}
390
388
  ]
391
389
  else
@@ -395,11 +393,12 @@ module MU
395
393
  if !ignoremaster
396
394
  filters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
397
395
  end
396
+ filters
398
397
  end
399
398
 
400
399
  # Some services create sneaky rogue ENIs which then block removal of
401
400
  # associated security groups. Find them and fry them.
402
- MU::Cloud::AWS::VPC.purge_interfaces(noop, filters, region: region, credentials: credentials)
401
+ MU::Cloud.resourceClass("AWS", "VPC").purge_interfaces(noop, filters, region: region, credentials: credentials)
403
402
 
404
403
  resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_security_groups(
405
404
  filters: filters
@@ -409,75 +408,8 @@ module MU
409
408
  MU.log "Revoking rules in EC2 Security Group #{sg.group_name} (#{sg.group_id})"
410
409
 
411
410
  if !noop
412
- ingress_to_revoke = Array.new
413
- egress_to_revoke = Array.new
414
- sg.ip_permissions.each { |hole|
415
- ingress_to_revoke << MU.structToHash(hole)
416
- ingress_to_revoke.each { |rule|
417
- if !rule[:user_id_group_pairs].nil? and rule[:user_id_group_pairs] .size == 0
418
- rule.delete(:user_id_group_pairs)
419
- elsif !rule[:user_id_group_pairs].nil?
420
- rule[:user_id_group_pairs].each { |group_ref|
421
- group_ref = MU.structToHash(group_ref)
422
- group_ref.delete(:group_name) if group_ref[:group_id]
423
- }
424
- end
425
-
426
- if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
427
- rule.delete(:ip_ranges)
428
- end
429
-
430
- if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
431
- rule.delete(:prefix_list_ids)
432
- end
433
-
434
- if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
435
- rule.delete(:ipv_6_ranges)
436
- end
437
- }
438
- }
439
- sg.ip_permissions_egress.each { |hole|
440
- egress_to_revoke << MU.structToHash(hole)
441
- egress_to_revoke.each { |rule|
442
- if !rule[:user_id_group_pairs].nil? and rule[:user_id_group_pairs].size == 0
443
- rule.delete(:user_id_group_pairs)
444
- elsif !rule[:user_id_group_pairs].nil?
445
- rule[:user_id_group_pairs].each { |group_ref|
446
- group_ref = MU.structToHash(group_ref)
447
- group_ref.delete(:group_name) if group_ref[:group_id]
448
- }
449
- end
450
-
451
- if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
452
- rule.delete(:ip_ranges)
453
- end
454
-
455
- if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
456
- rule.delete(:prefix_list_ids)
457
- end
458
-
459
- if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
460
- rule.delete(:ipv_6_ranges)
461
- end
462
- }
463
- }
464
- begin
465
-
466
- if ingress_to_revoke.size > 0
467
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_ingress(
468
- group_id: sg.group_id,
469
- ip_permissions: ingress_to_revoke
470
- )
471
- end
472
- if egress_to_revoke.size > 0
473
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_egress(
474
- group_id: sg.group_id,
475
- ip_permissions: egress_to_revoke
476
- )
477
- end
478
- rescue Aws::EC2::Errors::InvalidPermissionNotFound
479
- MU.log "Rule in #{sg.group_id} disappeared before I could remove it", MU::WARN
480
- end
411
+ revoke_rules(sg, region: region, credentials: credentials)
412
+ revoke_rules(sg, egress: true, region: region, credentials: credentials)
481
413
  end
482
414
  }
483
415
 
@@ -485,61 +417,132 @@ module MU
485
417
  next if sg.group_name == "default"
486
418
  MU.log "Removing EC2 Security Group #{sg.group_name}"
487
419
 
488
- retries = 0
489
- begin
490
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).delete_security_group(group_id: sg.group_id) if !noop
491
- rescue Aws::EC2::Errors::CannotDelete => e
492
- MU.log e.message, MU::WARN
493
- rescue Aws::EC2::Errors::InvalidGroupNotFound
494
- MU.log "EC2 Security Group #{sg.group_name} disappeared before I could delete it!", MU::WARN
495
- rescue Aws::EC2::Errors::DependencyViolation, Aws::EC2::Errors::InvalidGroupInUse
496
- if retries < 10
497
- MU.log "EC2 Security Group #{sg.group_name} is still in use, waiting...", MU::NOTICE
498
- # try to get out from under loose network interfaces with which
499
- # we're associated
500
- if sg.vpc_id
501
- # get the default SG for this VPC
502
- default_resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_security_groups(
503
- filters: [
504
- { name: "group-name", values: ["default"] },
505
- { name: "vpc-id", values: [sg.vpc_id] }
506
- ]
507
- ).security_groups
508
- if default_resp and default_resp.size == 1
509
- default_sg = default_resp.first.group_id
510
- eni_resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_network_interfaces(
511
- filters: [ {name: "group-id", values: [sg.group_id]} ]
512
- )
513
- if eni_resp and eni_resp.data and
514
- eni_resp.data.network_interfaces
515
- eni_resp.data.network_interfaces.each { |iface|
516
- iface_groups = iface.groups.map { |sg| sg.group_id }
517
- iface_groups.delete(sg.group_id)
518
- iface_groups << default_sg if iface_groups.empty?
519
- MU.log "Attempting to remove #{sg.group_id} from ENI #{iface.network_interface_id}"
420
+ on_retry = Proc.new {
421
+ # try to get out from under loose network interfaces with which
422
+ # we're associated
423
+ if sg.vpc_id
424
+ default_sg = MU::Cloud.resourceClass("AWS", "VPC").getDefaultSg(sg.vpc_id, region: region, credentials: credentials)
425
+ if default_sg
426
+ eni_resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_network_interfaces(
427
+ filters: [ {name: "group-id", values: [sg.group_id]} ]
428
+ )
429
+ if eni_resp and eni_resp.data and
430
+ eni_resp.data.network_interfaces
431
+ eni_resp.data.network_interfaces.each { |iface|
432
+ iface_groups = iface.groups.map { |if_sg| if_sg.group_id }
433
+ iface_groups.delete(sg.group_id)
434
+ iface_groups << default_sg if iface_groups.empty?
435
+ MU.log "Attempting to remove #{sg.group_id} (#{sg.group_name}) from ENI #{iface.network_interface_id}"
436
+ begin
520
437
  MU::Cloud::AWS.ec2(credentials: credentials, region: region).modify_network_interface_attribute(
521
438
  network_interface_id: iface.network_interface_id,
522
439
  groups: iface_groups
523
440
  )
524
- }
525
- end
441
+ rescue ::Aws::EC2::Errors::InvalidNetworkInterfaceIDNotFound
442
+ # fine by me
443
+ rescue ::Aws::EC2::Errors::AuthFailure
444
+ MU.log "Permission denied attempting to trim Security Group list for #{iface.network_interface_id}", MU::WARN, details: iface.groups.map { |g| g.group_name }.join(",")+" => default"
445
+ end
446
+ }
526
447
  end
527
448
  end
449
+ end
450
+ }
528
451
 
529
- sleep 10
530
- retries = retries + 1
531
- retry
452
+ if !noop
453
+ MU.retrier([Aws::EC2::Errors::DependencyViolation, Aws::EC2::Errors::InvalidGroupInUse], ignoreme: [Aws::EC2::Errors::InvalidGroupNotFound], max: 10, wait: 10, on_retry: on_retry) {
454
+ begin
455
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).delete_security_group(group_id: sg.group_id)
456
+ rescue Aws::EC2::Errors::CannotDelete => e
457
+ MU.log e.message, MU::WARN
458
+ end
459
+ }
460
+ end
461
+
462
+ }
463
+ end
464
+
465
+ def self.revoke_rules(sg, egress: false, region: MU.myregion, credentials: nil)
466
+ holes = sg.send(egress ? :ip_permissions_egress : :ip_permissions)
467
+
468
+ to_revoke = []
469
+
470
+ holes.each { |hole|
471
+ to_revoke << MU.structToHash(hole)
472
+ to_revoke.each { |rule|
473
+ if !rule[:user_id_group_pairs].nil? and rule[:user_id_group_pairs].size == 0
474
+ rule.delete(:user_id_group_pairs)
475
+ elsif !rule[:user_id_group_pairs].nil?
476
+ rule[:user_id_group_pairs].each { |group_ref|
477
+ group_ref = MU.structToHash(group_ref)
478
+ group_ref.delete(:group_name) if group_ref[:group_id]
479
+ }
480
+ end
481
+
482
+ if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
483
+ rule.delete(:ip_ranges)
484
+ end
485
+
486
+ if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
487
+ rule.delete(:prefix_list_ids)
488
+ end
489
+
490
+ if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
491
+ rule.delete(:ipv_6_ranges)
492
+ end
493
+ }
494
+ }
495
+
496
+ if to_revoke.size > 0
497
+ begin
498
+ if egress
499
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_egress(
500
+ group_id: sg.group_id,
501
+ ip_permissions: to_revoke
502
+ )
532
503
  else
533
- MU.log "Failed to delete #{sg.group_name}", MU::ERR
504
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_ingress(
505
+ group_id: sg.group_id,
506
+ ip_permissions: to_revoke
507
+ )
534
508
  end
509
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
510
+ MU.log "Rule in #{sg.group_id} disappeared before I could remove it", MU::WARN
535
511
  end
512
+ end
513
+
514
+ end
515
+ private_class_method :revoke_rules
516
+
517
+ # Return an AWS-specific chunk of schema commonly used in the +ingress_rules+ parameter of other resource types.
518
+ # @return [Hash]
519
+ def self.ingressRuleAddtlSchema
520
+ {
521
+ "items" => {
522
+ "properties" => {
523
+ "sgs" => {
524
+ "type" => "array",
525
+ "items" => {
526
+ "description" => "Other AWS Security Groups; resources that are associated with this group will have this rule applied to their traffic",
527
+ "type" => "string"
528
+ }
529
+ },
530
+ "lbs" => {
531
+ "type" => "array",
532
+ "items" => {
533
+ "description" => "AWS Load Balancers which will have this rule applied to their traffic",
534
+ "type" => "string"
535
+ }
536
+ }
537
+ }
538
+ }
536
539
  }
537
540
  end
538
541
 
539
542
  # Cloud-specific configuration properties.
540
- # @param config [MU::Config]: The calling MU::Config object
543
+ # @param _config [MU::Config]: The calling MU::Config object
541
544
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
542
- def self.schema(config)
545
+ def self.schema(_config)
543
546
  toplevel_required = []
544
547
  schema = {
545
548
  "rules" => {
@@ -645,36 +648,16 @@ module MU
645
648
 
646
649
  if rule['firewall_rules']
647
650
  rule['firewall_rules'].each { |sg|
648
- if sg.is_a?(MU::Config::Ref) and sg.name
649
- acl["dependencies"] << {
650
- "type" => "firewall_rule",
651
- "name" => sg.name,
652
- "no_create_wait" => true
653
- }
654
- elsif sg['name'] and !sg['deploy_id']
655
- acl["dependencies"] << {
656
- "type" => "firewall_rule",
657
- "name" => sg['name'],
658
- "no_create_wait" => true
659
- }
651
+ if sg['name'] and !sg['deploy_id']
652
+ MU::Config.addDependency(acl, sg['name'], "firewall_rule", no_create_wait: true)
660
653
  end
661
654
  }
662
655
  end
663
656
 
664
657
  if rule['loadbalancers']
665
658
  rule['loadbalancers'].each { |lb|
666
- if lb.is_a?(MU::Config::Ref) and lb.name
667
- acl["dependencies"] << {
668
- "type" => "loadbalancer",
669
- "name" => lb.name,
670
- "phase" => "groom"
671
- }
672
- elsif lb['name'] and !lb['deploy_id']
673
- acl["dependencies"] << {
674
- "type" => "loadbalancer",
675
- "name" => lb['name'],
676
- "phase" => "groom"
677
- }
659
+ if lb['name'] and !lb['deploy_id']
660
+ MU::Config.addDependency(acl, lb['name'], "loadbalancer", phase: "groom")
678
661
  end
679
662
  }
680
663
  end
@@ -716,32 +699,7 @@ module MU
716
699
 
717
700
  private
718
701
 
719
- #########################################################################
720
- # Manufacture an EC2 security group. The second parameter, rules, is an
721
- # "ingress_rules" structure parsed and validated by MU::Config.
722
- #########################################################################
723
- def setRules(rules, add_to_self: false, ingress: true, egress: false)
724
- describe
725
- # XXX warn about attempt to set rules before we exist
726
- return if rules.nil? or rules.size == 0 or !@cloud_id
727
-
728
- # add_to_self means that this security is a "member" of its own rules
729
- # (which is to say, objects that have this SG are allowed in my these
730
- # rules)
731
- if add_to_self
732
- rules.each { |rule|
733
- if rule['sgs'].nil? or !rule['sgs'].include?(@cloud_id)
734
- new_rule = rule.clone
735
- new_rule.delete('hosts')
736
- rule['sgs'] = Array.new if rule['sgs'].nil?
737
- rule['sgs'] << @cloud_id
738
- end
739
- }
740
- end
741
-
742
- ec2_rules = convertToEc2(rules)
743
- ext_permissions = MU.structToHash(cloud_desc.ip_permissions)
744
-
702
+ def purge_extraneous_rules(ec2_rules, ext_permissions)
745
703
  # Purge any old rules that we're sure we created (check the comment)
746
704
  # but which are no longer configured.
747
705
  ext_permissions.each { |ext_rule|
@@ -778,97 +736,109 @@ module MU
778
736
  ip_permissions: [ext_rule]
779
737
  )
780
738
  end
781
-
782
739
  }
740
+ end
783
741
 
784
- # Creating an empty security group is ok, so don't freak out if we get
785
- # a null rule list.
786
- if !ec2_rules.nil?
787
- ec2_rules.uniq!
788
- retries = 0
789
- ec2_rules.each { |rule|
790
- haverule = nil
791
- different = false
792
- ext_permissions.each { |ext_rule|
793
- if rule[:from_port] == ext_rule[:from_port] and
794
- rule[:to_port] == ext_rule[:to_port] and
795
- rule[:ip_protocol] == ext_rule[:ip_protocol]
796
- haverule = ext_rule
797
- ext_rule.keys.each { |k|
798
- if ext_rule[k].nil? or ext_rule[k] == []
799
- haverule.delete(k)
800
- end
801
- different = true if rule[k] != ext_rule[k]
802
- }
803
- break
804
- end
805
- }
806
- if haverule and !different
807
- MU.log "Security Group rule already up-to-date in #{@mu_name}", MU::DEBUG, details: rule
808
- next
742
+ #########################################################################
743
+ # Manufacture an EC2 security group. The second parameter, rules, is an
744
+ # "ingress_rules" structure parsed and validated by MU::Config.
745
+ #########################################################################
746
+ def setRules(rules, add_to_self: false, ingress: true, egress: false)
747
+ # XXX warn about attempt to set rules before we exist
748
+ return if rules.nil? or rules.size == 0 or !@cloud_id
749
+
750
+ # add_to_self means that this security is a "member" of its own rules
751
+ # (which is to say, objects that have this SG are allowed in my these
752
+ # rules)
753
+ if add_to_self
754
+ rules.each { |rule|
755
+ if rule['sgs'].nil? or !rule['sgs'].include?(@cloud_id)
756
+ new_rule = rule.clone
757
+ new_rule.delete('hosts')
758
+ rule['sgs'] = Array.new if rule['sgs'].nil?
759
+ rule['sgs'] << @cloud_id
809
760
  end
761
+ }
762
+ end
810
763
 
811
- MU.log "Setting #{ingress ? "ingress" : "egress"} rule in Security Group #{@mu_name} (#{@cloud_id})", MU::NOTICE, details: rule
812
- begin
813
-
814
- if ingress
815
- if haverule
816
- begin
817
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
818
- group_id: @cloud_id,
819
- ip_permissions: [haverule]
820
- )
821
- rescue Aws::EC2::Errors::InvalidPermissionNotFound => e
822
- end
764
+ ec2_rules = convertToEc2(rules)
765
+ return if ec2_rules.nil?
766
+
767
+ ext_permissions = MU.structToHash(cloud_desc(use_cache: false).ip_permissions)
768
+
769
+ purge_extraneous_rules(ec2_rules, ext_permissions)
770
+
771
+ ec2_rules.uniq!
772
+ ec2_rules.each { |rule|
773
+ haverule = nil
774
+ different = false
775
+ ext_permissions.each { |ext_rule|
776
+ if rule[:from_port] == ext_rule[:from_port] and
777
+ rule[:to_port] == ext_rule[:to_port] and
778
+ rule[:ip_protocol] == ext_rule[:ip_protocol]
779
+ haverule = ext_rule
780
+ ext_rule.keys.each { |k|
781
+ if ext_rule[k].nil? or ext_rule[k] == []
782
+ haverule.delete(k)
823
783
  end
784
+ different = true if rule[k] != ext_rule[k]
785
+ }
786
+ break
787
+ end
788
+ }
789
+ if haverule and !different
790
+ MU.log "Security Group rule already up-to-date in #{@mu_name}", MU::DEBUG, details: rule
791
+ next
792
+ end
793
+
794
+ MU.log "Setting #{ingress ? "ingress" : "egress"} rule in Security Group #{@mu_name} (#{@cloud_id})", MU::NOTICE, details: rule
795
+
796
+ MU.retrier([Aws::EC2::Errors::InvalidGroupNotFound], max: 10, wait: 10, ignoreme: [Aws::EC2::Errors::InvalidPermissionDuplicate]) {
797
+ if ingress
798
+ if haverule
824
799
  begin
825
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
800
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
826
801
  group_id: @cloud_id,
827
- ip_permissions: [rule]
802
+ ip_permissions: [haverule]
828
803
  )
829
- rescue Aws::EC2::Errors::InvalidParameterCombination => e
830
- MU.log "FirewallRule #{@mu_name} had a bogus rule: #{e.message}", MU::ERR, details: rule
831
- raise e
804
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
832
805
  end
833
806
  end
834
-
835
- if egress
836
- if haverule
837
- begin
838
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_egress(
839
- group_id: @cloud_id,
840
- ip_permissions: [haverule]
841
- )
842
- rescue Aws::EC2::Errors::InvalidPermissionNotFound => e
843
- end
844
- end
845
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
807
+ begin
808
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
846
809
  group_id: @cloud_id,
847
810
  ip_permissions: [rule]
848
811
  )
812
+ rescue Aws::EC2::Errors::InvalidParameterCombination => e
813
+ MU.log "FirewallRule #{@mu_name} had a bogus rule: #{e.message}", MU::ERR, details: rule
814
+ raise e
849
815
  end
816
+ end
850
817
 
851
- rescue Aws::EC2::Errors::InvalidGroupNotFound => e
852
- MU.log "#{@mu_name} (#{@cloud_id}) does not yet exist", MU::WARN
853
- retries = retries + 1
854
- if retries < 10
855
- sleep 10
856
- retry
857
- else
858
- raise MuError, "#{@mu_name} does not exist", e.backtrace
818
+ if egress
819
+ if haverule
820
+ begin
821
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_egress(
822
+ group_id: @cloud_id,
823
+ ip_permissions: [haverule]
824
+ )
825
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
826
+ end
859
827
  end
860
- rescue Aws::EC2::Errors::InvalidPermissionDuplicate => e
861
- MU.log "Attempt to add duplicate rule to #{@mu_name}", MU::DEBUG, details: rule
828
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
829
+ group_id: @cloud_id,
830
+ ip_permissions: [rule]
831
+ )
862
832
  end
863
833
  }
864
- end
834
+ }
865
835
 
866
836
  end
867
837
 
868
- #########################################################################
869
- # Convert our config languages description of firewall rules into Amazon's.
870
- # This rule structure is as defined in MU::Config.
871
- #########################################################################
838
+ #######################################################################
839
+ # Convert our config languages description of firewall rules into
840
+ # Amazon's. Our rule structure is as defined in MU::Config.
841
+ #######################################################################
872
842
  def convertToEc2(rules)
873
843
  ec2_rules = []
874
844
  if rules != nil
@@ -992,8 +962,8 @@ module MU
992
962
  ec2_rules << ec2_rule
993
963
  }
994
964
  end
995
- ec2_rules.uniq!
996
- return ec2_rules
965
+
966
+ ec2_rules.uniq
997
967
  end
998
968
 
999
969
  end #class