cloud-mu 3.1.4 → 3.1.5

Sign up to get free protection for your applications and to get access to all the features.
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