cloud-mu 3.1.4 → 3.1.5

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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/ansible/roles/mu-windows/README.md +33 -0
  3. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  4. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  5. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  6. data/ansible/roles/mu-windows/tasks/main.yml +20 -0
  7. data/ansible/roles/mu-windows/tests/inventory +2 -0
  8. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  9. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  10. data/cloud-mu.gemspec +4 -2
  11. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  12. data/cookbooks/mu-tools/recipes/windows-client.rb +140 -144
  13. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  14. data/extras/image-generators/AWS/win2k12.yaml +16 -13
  15. data/extras/image-generators/AWS/win2k16.yaml +16 -13
  16. data/extras/image-generators/AWS/win2k19.yaml +19 -0
  17. data/modules/mu.rb +72 -9
  18. data/modules/mu/adoption.rb +14 -2
  19. data/modules/mu/cloud.rb +111 -10
  20. data/modules/mu/clouds/aws.rb +23 -7
  21. data/modules/mu/clouds/aws/container_cluster.rb +640 -692
  22. data/modules/mu/clouds/aws/dnszone.rb +49 -45
  23. data/modules/mu/clouds/aws/firewall_rule.rb +177 -214
  24. data/modules/mu/clouds/aws/role.rb +17 -8
  25. data/modules/mu/clouds/aws/search_domain.rb +1 -1
  26. data/modules/mu/clouds/aws/server.rb +734 -1027
  27. data/modules/mu/clouds/aws/userdata/windows.erb +2 -1
  28. data/modules/mu/clouds/aws/vpc.rb +297 -786
  29. data/modules/mu/clouds/aws/vpc_subnet.rb +286 -0
  30. data/modules/mu/clouds/google/bucket.rb +1 -1
  31. data/modules/mu/clouds/google/container_cluster.rb +21 -17
  32. data/modules/mu/clouds/google/function.rb +8 -2
  33. data/modules/mu/clouds/google/server.rb +102 -32
  34. data/modules/mu/clouds/google/vpc.rb +1 -1
  35. data/modules/mu/config.rb +12 -1
  36. data/modules/mu/config/server.yml +1 -0
  37. data/modules/mu/defaults/AWS.yaml +51 -28
  38. data/modules/mu/groomers/ansible.rb +54 -17
  39. data/modules/mu/groomers/chef.rb +13 -7
  40. data/modules/mu/master/ssl.rb +0 -1
  41. data/modules/mu/mommacat.rb +8 -0
  42. data/modules/tests/ecs.yaml +23 -0
  43. data/modules/tests/includes-and-params.yaml +2 -1
  44. data/modules/tests/server-with-scrub-muisms.yaml +1 -0
  45. data/modules/tests/win2k12.yaml +25 -0
  46. data/modules/tests/win2k16.yaml +25 -0
  47. data/modules/tests/win2k19.yaml +25 -0
  48. data/requirements.txt +1 -0
  49. metadata +50 -4
  50. data/extras/image-generators/AWS/windows.yaml +0 -18
  51. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -369,7 +369,7 @@ module MU
369
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, StandardError => 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
@@ -382,9 +382,8 @@ module MU
382
382
  # @param region [String]: The cloud provider region
383
383
  # @return [void]
384
384
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
385
- filters = nil
386
- if flags and flags["vpc_id"]
387
- filters = [
385
+ filters = if flags and flags["vpc_id"]
386
+ [
388
387
  {name: "vpc-id", values: [flags["vpc_id"]]}
389
388
  ]
390
389
  else
@@ -394,6 +393,7 @@ module MU
394
393
  if !ignoremaster
395
394
  filters << {name: "tag:MU-MASTER-IP", values: [MU.mu_public_ip]}
396
395
  end
396
+ filters
397
397
  end
398
398
 
399
399
  # Some services create sneaky rogue ENIs which then block removal of
@@ -408,136 +408,111 @@ module MU
408
408
  MU.log "Revoking rules in EC2 Security Group #{sg.group_name} (#{sg.group_id})"
409
409
 
410
410
  if !noop
411
- ingress_to_revoke = Array.new
412
- egress_to_revoke = Array.new
413
- sg.ip_permissions.each { |hole|
414
- ingress_to_revoke << MU.structToHash(hole)
415
- ingress_to_revoke.each { |rule|
416
- if !rule[:user_id_group_pairs].nil? and rule[:user_id_group_pairs] .size == 0
417
- rule.delete(:user_id_group_pairs)
418
- elsif !rule[:user_id_group_pairs].nil?
419
- rule[:user_id_group_pairs].each { |group_ref|
420
- group_ref = MU.structToHash(group_ref)
421
- group_ref.delete(:group_name) if group_ref[:group_id]
422
- }
423
- end
411
+ revoke_rules(sg, region: region, credentials: credentials)
412
+ revoke_rules(sg, egress: true, region: region, credentials: credentials)
413
+ end
414
+ }
424
415
 
425
- if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
426
- rule.delete(:ip_ranges)
427
- end
416
+ resp.data.security_groups.each { |sg|
417
+ next if sg.group_name == "default"
418
+ MU.log "Removing EC2 Security Group #{sg.group_name}"
428
419
 
429
- if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
430
- rule.delete(:prefix_list_ids)
431
- end
432
-
433
- if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
434
- rule.delete(:ipv_6_ranges)
435
- end
436
- }
437
- }
438
- sg.ip_permissions_egress.each { |hole|
439
- egress_to_revoke << MU.structToHash(hole)
440
- egress_to_revoke.each { |rule|
441
- if !rule[:user_id_group_pairs].nil? and rule[:user_id_group_pairs].size == 0
442
- rule.delete(:user_id_group_pairs)
443
- elsif !rule[:user_id_group_pairs].nil?
444
- rule[:user_id_group_pairs].each { |group_ref|
445
- group_ref = MU.structToHash(group_ref)
446
- group_ref.delete(:group_name) if group_ref[:group_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::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
437
+ MU::Cloud::AWS.ec2(credentials: credentials, region: region).modify_network_interface_attribute(
438
+ network_interface_id: iface.network_interface_id,
439
+ groups: iface_groups
440
+ )
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
447
446
  }
448
447
  end
448
+ end
449
+ end
450
+ }
449
451
 
450
- if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
451
- rule.delete(:ip_ranges)
452
- end
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
453
461
 
454
- if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
455
- rule.delete(:prefix_list_ids)
456
- end
462
+ }
463
+ end
457
464
 
458
- if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
459
- rule.delete(:ipv_6_ranges)
460
- end
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]
461
479
  }
462
- }
463
- begin
480
+ end
464
481
 
465
- if ingress_to_revoke.size > 0
466
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_ingress(
467
- group_id: sg.group_id,
468
- ip_permissions: ingress_to_revoke
469
- )
470
- end
471
- if egress_to_revoke.size > 0
472
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).revoke_security_group_egress(
473
- group_id: sg.group_id,
474
- ip_permissions: egress_to_revoke
475
- )
476
- end
477
- rescue Aws::EC2::Errors::InvalidPermissionNotFound
478
- MU.log "Rule in #{sg.group_id} disappeared before I could remove it", MU::WARN
482
+ if !rule[:ip_ranges].nil? and rule[:ip_ranges].size == 0
483
+ rule.delete(:ip_ranges)
479
484
  end
480
- end
481
- }
482
485
 
483
- resp.data.security_groups.each { |sg|
484
- next if sg.group_name == "default"
485
- MU.log "Removing EC2 Security Group #{sg.group_name}"
486
+ if !rule[:prefix_list_ids].nil? and rule[:prefix_list_ids].size == 0
487
+ rule.delete(:prefix_list_ids)
488
+ end
486
489
 
487
- retries = 0
488
- begin
489
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).delete_security_group(group_id: sg.group_id) if !noop
490
- rescue Aws::EC2::Errors::CannotDelete => e
491
- MU.log e.message, MU::WARN
492
- rescue Aws::EC2::Errors::InvalidGroupNotFound
493
- MU.log "EC2 Security Group #{sg.group_name} disappeared before I could delete it!", MU::WARN
494
- rescue Aws::EC2::Errors::DependencyViolation, Aws::EC2::Errors::InvalidGroupInUse
495
- if retries < 10
496
- MU.log "EC2 Security Group #{sg.group_name} is still in use, waiting...", MU::NOTICE
497
- # try to get out from under loose network interfaces with which
498
- # we're associated
499
- if sg.vpc_id
500
- # get the default SG for this VPC
501
- default_resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_security_groups(
502
- filters: [
503
- { name: "group-name", values: ["default"] },
504
- { name: "vpc-id", values: [sg.vpc_id] }
505
- ]
506
- ).security_groups
507
- if default_resp and default_resp.size == 1
508
- default_sg = default_resp.first.group_id
509
- eni_resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_network_interfaces(
510
- filters: [ {name: "group-id", values: [sg.group_id]} ]
511
- )
512
- if eni_resp and eni_resp.data and
513
- eni_resp.data.network_interfaces
514
- eni_resp.data.network_interfaces.each { |iface|
515
- iface_groups = iface.groups.map { |if_sg| if_sg.group_id }
516
- iface_groups.delete(sg.group_id)
517
- iface_groups << default_sg if iface_groups.empty?
518
- MU.log "Attempting to remove #{sg.group_id} (#{sg.group_name}) from ENI #{iface.network_interface_id}"
519
- begin
520
- MU::Cloud::AWS.ec2(credentials: credentials, region: region).modify_network_interface_attribute(
521
- network_interface_id: iface.network_interface_id,
522
- groups: iface_groups
523
- )
524
- rescue ::Aws::EC2::Errors::AuthFailure
525
- 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"
526
- end
527
- }
528
- end
529
- end
530
- end
490
+ if !rule[:ipv_6_ranges].nil? and rule[:ipv_6_ranges].size == 0
491
+ rule.delete(:ipv_6_ranges)
492
+ end
493
+ }
494
+ }
531
495
 
532
- sleep 10
533
- retries = retries + 1
534
- retry
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
+ )
535
503
  else
536
- 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
+ )
537
508
  end
509
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
510
+ MU.log "Rule in #{sg.group_id} disappeared before I could remove it", MU::WARN
538
511
  end
539
- }
512
+ end
513
+
540
514
  end
515
+ private_class_method :revoke_rules
541
516
 
542
517
  # Cloud-specific configuration properties.
543
518
  # @param _config [MU::Config]: The calling MU::Config object
@@ -719,32 +694,7 @@ module MU
719
694
 
720
695
  private
721
696
 
722
- #########################################################################
723
- # Manufacture an EC2 security group. The second parameter, rules, is an
724
- # "ingress_rules" structure parsed and validated by MU::Config.
725
- #########################################################################
726
- def setRules(rules, add_to_self: false, ingress: true, egress: false)
727
- describe
728
- # XXX warn about attempt to set rules before we exist
729
- return if rules.nil? or rules.size == 0 or !@cloud_id
730
-
731
- # add_to_self means that this security is a "member" of its own rules
732
- # (which is to say, objects that have this SG are allowed in my these
733
- # rules)
734
- if add_to_self
735
- rules.each { |rule|
736
- if rule['sgs'].nil? or !rule['sgs'].include?(@cloud_id)
737
- new_rule = rule.clone
738
- new_rule.delete('hosts')
739
- rule['sgs'] = Array.new if rule['sgs'].nil?
740
- rule['sgs'] << @cloud_id
741
- end
742
- }
743
- end
744
-
745
- ec2_rules = convertToEc2(rules)
746
- ext_permissions = MU.structToHash(cloud_desc.ip_permissions)
747
-
697
+ def purge_extraneous_rules(ec2_rules, ext_permissions)
748
698
  # Purge any old rules that we're sure we created (check the comment)
749
699
  # but which are no longer configured.
750
700
  ext_permissions.each { |ext_rule|
@@ -781,97 +731,110 @@ module MU
781
731
  ip_permissions: [ext_rule]
782
732
  )
783
733
  end
784
-
785
734
  }
735
+ end
786
736
 
787
- # Creating an empty security group is ok, so don't freak out if we get
788
- # a null rule list.
789
- if !ec2_rules.nil?
790
- ec2_rules.uniq!
791
- retries = 0
792
- ec2_rules.each { |rule|
793
- haverule = nil
794
- different = false
795
- ext_permissions.each { |ext_rule|
796
- if rule[:from_port] == ext_rule[:from_port] and
797
- rule[:to_port] == ext_rule[:to_port] and
798
- rule[:ip_protocol] == ext_rule[:ip_protocol]
799
- haverule = ext_rule
800
- ext_rule.keys.each { |k|
801
- if ext_rule[k].nil? or ext_rule[k] == []
802
- haverule.delete(k)
803
- end
804
- different = true if rule[k] != ext_rule[k]
805
- }
806
- break
807
- end
808
- }
809
- if haverule and !different
810
- MU.log "Security Group rule already up-to-date in #{@mu_name}", MU::DEBUG, details: rule
811
- next
737
+ #########################################################################
738
+ # Manufacture an EC2 security group. The second parameter, rules, is an
739
+ # "ingress_rules" structure parsed and validated by MU::Config.
740
+ #########################################################################
741
+ def setRules(rules, add_to_self: false, ingress: true, egress: false)
742
+ describe
743
+ # XXX warn about attempt to set rules before we exist
744
+ return if rules.nil? or rules.size == 0 or !@cloud_id
745
+
746
+ # add_to_self means that this security is a "member" of its own rules
747
+ # (which is to say, objects that have this SG are allowed in my these
748
+ # rules)
749
+ if add_to_self
750
+ rules.each { |rule|
751
+ if rule['sgs'].nil? or !rule['sgs'].include?(@cloud_id)
752
+ new_rule = rule.clone
753
+ new_rule.delete('hosts')
754
+ rule['sgs'] = Array.new if rule['sgs'].nil?
755
+ rule['sgs'] << @cloud_id
812
756
  end
757
+ }
758
+ end
813
759
 
814
- MU.log "Setting #{ingress ? "ingress" : "egress"} rule in Security Group #{@mu_name} (#{@cloud_id})", MU::NOTICE, details: rule
815
- begin
816
-
817
- if ingress
818
- if haverule
819
- begin
820
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
821
- group_id: @cloud_id,
822
- ip_permissions: [haverule]
823
- )
824
- rescue Aws::EC2::Errors::InvalidPermissionNotFound => e
825
- end
760
+ ec2_rules = convertToEc2(rules)
761
+ return if ec2_rules.nil?
762
+
763
+ ext_permissions = MU.structToHash(cloud_desc.ip_permissions)
764
+
765
+ purge_extraneous_rules(ec2_rules, ext_permissions)
766
+
767
+ ec2_rules.uniq!
768
+ ec2_rules.each { |rule|
769
+ haverule = nil
770
+ different = false
771
+ ext_permissions.each { |ext_rule|
772
+ if rule[:from_port] == ext_rule[:from_port] and
773
+ rule[:to_port] == ext_rule[:to_port] and
774
+ rule[:ip_protocol] == ext_rule[:ip_protocol]
775
+ haverule = ext_rule
776
+ ext_rule.keys.each { |k|
777
+ if ext_rule[k].nil? or ext_rule[k] == []
778
+ haverule.delete(k)
826
779
  end
780
+ different = true if rule[k] != ext_rule[k]
781
+ }
782
+ break
783
+ end
784
+ }
785
+ if haverule and !different
786
+ MU.log "Security Group rule already up-to-date in #{@mu_name}", MU::DEBUG, details: rule
787
+ next
788
+ end
789
+
790
+ MU.log "Setting #{ingress ? "ingress" : "egress"} rule in Security Group #{@mu_name} (#{@cloud_id})", MU::NOTICE, details: rule
791
+
792
+ MU.retrier([Aws::EC2::Errors::InvalidGroupNotFound], max: 10, wait: 10, ignoreme: [Aws::EC2::Errors::InvalidPermissionDuplicate]) {
793
+ if ingress
794
+ if haverule
827
795
  begin
828
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
796
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_ingress(
829
797
  group_id: @cloud_id,
830
- ip_permissions: [rule]
798
+ ip_permissions: [haverule]
831
799
  )
832
- rescue Aws::EC2::Errors::InvalidParameterCombination => e
833
- MU.log "FirewallRule #{@mu_name} had a bogus rule: #{e.message}", MU::ERR, details: rule
834
- raise e
800
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
835
801
  end
836
802
  end
837
-
838
- if egress
839
- if haverule
840
- begin
841
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_egress(
842
- group_id: @cloud_id,
843
- ip_permissions: [haverule]
844
- )
845
- rescue Aws::EC2::Errors::InvalidPermissionNotFound => e
846
- end
847
- end
848
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
803
+ begin
804
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_ingress(
849
805
  group_id: @cloud_id,
850
806
  ip_permissions: [rule]
851
807
  )
808
+ rescue Aws::EC2::Errors::InvalidParameterCombination => e
809
+ MU.log "FirewallRule #{@mu_name} had a bogus rule: #{e.message}", MU::ERR, details: rule
810
+ raise e
852
811
  end
812
+ end
853
813
 
854
- rescue Aws::EC2::Errors::InvalidGroupNotFound => e
855
- MU.log "#{@mu_name} (#{@cloud_id}) does not yet exist", MU::WARN
856
- retries = retries + 1
857
- if retries < 10
858
- sleep 10
859
- retry
860
- else
861
- raise MuError, "#{@mu_name} does not exist", e.backtrace
814
+ if egress
815
+ if haverule
816
+ begin
817
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).revoke_security_group_egress(
818
+ group_id: @cloud_id,
819
+ ip_permissions: [haverule]
820
+ )
821
+ rescue Aws::EC2::Errors::InvalidPermissionNotFound
822
+ end
862
823
  end
863
- rescue Aws::EC2::Errors::InvalidPermissionDuplicate => e
864
- MU.log "Attempt to add duplicate rule to #{@mu_name}", MU::DEBUG, details: rule
824
+ MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).authorize_security_group_egress(
825
+ group_id: @cloud_id,
826
+ ip_permissions: [rule]
827
+ )
865
828
  end
866
829
  }
867
- end
830
+ }
868
831
 
869
832
  end
870
833
 
871
- #########################################################################
872
- # Convert our config languages description of firewall rules into Amazon's.
873
- # This rule structure is as defined in MU::Config.
874
- #########################################################################
834
+ #######################################################################
835
+ # Convert our config languages description of firewall rules into
836
+ # Amazon's. Our rule structure is as defined in MU::Config.
837
+ #######################################################################
875
838
  def convertToEc2(rules)
876
839
  ec2_rules = []
877
840
  if rules != nil
@@ -995,8 +958,8 @@ module MU
995
958
  ec2_rules << ec2_rule
996
959
  }
997
960
  end
998
- ec2_rules.uniq!
999
- return ec2_rules
961
+
962
+ ec2_rules.uniq
1000
963
  end
1001
964
 
1002
965
  end #class