cloud-mu 3.5.0 → 3.6.3

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 (245) hide show
  1. checksums.yaml +4 -4
  2. data/Berksfile +5 -2
  3. data/Berksfile.lock +135 -0
  4. data/ansible/roles/mu-base/README.md +33 -0
  5. data/ansible/roles/mu-base/defaults/main.yml +2 -0
  6. data/ansible/roles/mu-base/files/check_apm.cfg +1 -0
  7. data/ansible/roles/mu-base/files/check_apm.sh +18 -0
  8. data/ansible/roles/mu-base/files/check_disk.cfg +1 -0
  9. data/ansible/roles/mu-base/files/check_elastic_shards.cfg +1 -0
  10. data/ansible/roles/mu-base/files/check_elastic_shards.sh +12 -0
  11. data/ansible/roles/mu-base/files/check_logstash.cfg +1 -0
  12. data/ansible/roles/mu-base/files/check_logstash.sh +14 -0
  13. data/ansible/roles/mu-base/files/check_mem.cfg +1 -0
  14. data/ansible/roles/mu-base/files/check_updates.cfg +1 -0
  15. data/ansible/roles/mu-base/files/logrotate.conf +35 -0
  16. data/ansible/roles/mu-base/files/nrpe-apm-sudo +1 -0
  17. data/ansible/roles/mu-base/files/nrpe-elasticshards-sudo +2 -0
  18. data/ansible/roles/mu-base/handlers/main.yml +5 -0
  19. data/ansible/roles/mu-base/meta/main.yml +53 -0
  20. data/ansible/roles/mu-base/tasks/main.yml +113 -0
  21. data/ansible/roles/mu-base/templates/nrpe.cfg.j2 +231 -0
  22. data/ansible/roles/mu-base/tests/inventory +2 -0
  23. data/ansible/roles/mu-base/tests/test.yml +5 -0
  24. data/ansible/roles/mu-base/vars/main.yml +1 -0
  25. data/ansible/roles/mu-compliance/README.md +33 -0
  26. data/ansible/roles/mu-compliance/defaults/main.yml +2 -0
  27. data/ansible/roles/mu-compliance/files/U_MS_Windows_Server_2016_V2R1_STIG_SCAP_1-2_Benchmark.xml +15674 -0
  28. data/ansible/roles/mu-compliance/files/U_MS_Windows_Server_2019_V2R1_STIG_SCAP_1-2_Benchmark.xml +17553 -0
  29. data/ansible/roles/mu-compliance/handlers/main.yml +2 -0
  30. data/ansible/roles/mu-compliance/meta/main.yml +53 -0
  31. data/ansible/roles/mu-compliance/tasks/main.yml +45 -0
  32. data/ansible/roles/mu-compliance/tests/inventory +2 -0
  33. data/ansible/roles/mu-compliance/tests/test.yml +5 -0
  34. data/ansible/roles/mu-compliance/vars/main.yml +4 -0
  35. data/ansible/roles/mu-elastic/README.md +51 -0
  36. data/ansible/roles/mu-elastic/defaults/main.yml +2 -0
  37. data/ansible/roles/mu-elastic/files/jvm.options +93 -0
  38. data/ansible/roles/mu-elastic/handlers/main.yml +10 -0
  39. data/ansible/roles/mu-elastic/meta/main.yml +52 -0
  40. data/ansible/roles/mu-elastic/tasks/main.yml +186 -0
  41. data/ansible/roles/mu-elastic/templates/elasticsearch.yml.j2 +110 -0
  42. data/ansible/roles/mu-elastic/templates/kibana.yml.j2 +131 -0
  43. data/ansible/roles/mu-elastic/templates/password_set.expect.j2 +19 -0
  44. data/ansible/roles/mu-elastic/tests/inventory +2 -0
  45. data/ansible/roles/mu-elastic/tests/test.yml +5 -0
  46. data/ansible/roles/mu-elastic/vars/main.yml +2 -0
  47. data/ansible/roles/mu-logstash/README.md +51 -0
  48. data/ansible/roles/mu-logstash/defaults/main.yml +2 -0
  49. data/ansible/roles/mu-logstash/files/02-beats-input.conf +5 -0
  50. data/ansible/roles/mu-logstash/files/10-rails-filter.conf +16 -0
  51. data/ansible/roles/mu-logstash/files/jvm.options +84 -0
  52. data/ansible/roles/mu-logstash/files/logstash.yml +304 -0
  53. data/ansible/roles/mu-logstash/handlers/main.yml +20 -0
  54. data/ansible/roles/mu-logstash/meta/main.yml +52 -0
  55. data/ansible/roles/mu-logstash/tasks/main.yml +254 -0
  56. data/ansible/roles/mu-logstash/templates/20-cloudtrail.conf.j2 +28 -0
  57. data/ansible/roles/mu-logstash/templates/30-elasticsearch-output.conf.j2 +19 -0
  58. data/ansible/roles/mu-logstash/templates/apm-server.yml.j2 +33 -0
  59. data/ansible/roles/mu-logstash/templates/heartbeat.yml.j2 +29 -0
  60. data/ansible/roles/mu-logstash/templates/nginx/apm.conf.j2 +25 -0
  61. data/ansible/roles/mu-logstash/templates/nginx/default.conf.j2 +56 -0
  62. data/ansible/roles/mu-logstash/templates/nginx/elastic.conf.j2 +27 -0
  63. data/ansible/roles/mu-logstash/tests/inventory +2 -0
  64. data/ansible/roles/mu-logstash/tests/test.yml +5 -0
  65. data/ansible/roles/mu-logstash/vars/main.yml +2 -0
  66. data/ansible/roles/mu-rdp/README.md +33 -0
  67. data/ansible/roles/mu-rdp/meta/main.yml +53 -0
  68. data/ansible/roles/mu-rdp/tasks/main.yml +9 -0
  69. data/ansible/roles/mu-rdp/tests/inventory +2 -0
  70. data/ansible/roles/mu-rdp/tests/test.yml +5 -0
  71. data/ansible/roles/mu-windows/tasks/main.yml +3 -0
  72. data/bin/mu-ansible-secret +1 -1
  73. data/bin/mu-aws-setup +4 -3
  74. data/bin/mu-azure-setup +5 -5
  75. data/bin/mu-configure +25 -17
  76. data/bin/mu-firewall-allow-clients +1 -0
  77. data/bin/mu-gcp-setup +3 -3
  78. data/bin/mu-load-config.rb +1 -0
  79. data/bin/mu-node-manage +66 -33
  80. data/bin/mu-self-update +2 -2
  81. data/bin/mu-upload-chef-artifacts +6 -1
  82. data/bin/mu-user-manage +1 -1
  83. data/cloud-mu.gemspec +25 -23
  84. data/cookbooks/firewall/CHANGELOG.md +417 -224
  85. data/cookbooks/firewall/LICENSE +202 -0
  86. data/cookbooks/firewall/README.md +153 -126
  87. data/cookbooks/firewall/TODO.md +6 -0
  88. data/cookbooks/firewall/attributes/firewalld.rb +7 -0
  89. data/cookbooks/firewall/attributes/iptables.rb +3 -3
  90. data/cookbooks/firewall/chefignore +115 -0
  91. data/cookbooks/firewall/libraries/helpers.rb +5 -0
  92. data/cookbooks/firewall/libraries/helpers_firewalld.rb +1 -1
  93. data/cookbooks/firewall/libraries/helpers_firewalld_dbus.rb +72 -0
  94. data/cookbooks/firewall/libraries/helpers_iptables.rb +3 -3
  95. data/cookbooks/firewall/libraries/helpers_nftables.rb +170 -0
  96. data/cookbooks/firewall/libraries/helpers_ufw.rb +7 -0
  97. data/cookbooks/firewall/libraries/helpers_windows.rb +8 -9
  98. data/cookbooks/firewall/libraries/provider_firewall_firewalld.rb +9 -9
  99. data/cookbooks/firewall/libraries/provider_firewall_iptables.rb +7 -7
  100. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu.rb +12 -8
  101. data/cookbooks/firewall/libraries/provider_firewall_iptables_ubuntu1404.rb +13 -9
  102. data/cookbooks/firewall/libraries/provider_firewall_rule.rb +1 -1
  103. data/cookbooks/firewall/libraries/provider_firewall_ufw.rb +5 -5
  104. data/cookbooks/firewall/libraries/provider_firewall_windows.rb +4 -4
  105. data/cookbooks/firewall/libraries/resource_firewall_rule.rb +3 -3
  106. data/cookbooks/firewall/metadata.json +40 -1
  107. data/cookbooks/firewall/metadata.rb +15 -0
  108. data/cookbooks/firewall/recipes/default.rb +7 -7
  109. data/cookbooks/firewall/recipes/disable_firewall.rb +1 -1
  110. data/cookbooks/firewall/recipes/firewalld.rb +87 -0
  111. data/cookbooks/firewall/renovate.json +18 -0
  112. data/cookbooks/firewall/resources/firewalld.rb +28 -0
  113. data/cookbooks/firewall/resources/firewalld_config.rb +39 -0
  114. data/cookbooks/firewall/resources/firewalld_helpers.rb +106 -0
  115. data/cookbooks/firewall/resources/firewalld_icmptype.rb +88 -0
  116. data/cookbooks/firewall/resources/firewalld_ipset.rb +104 -0
  117. data/cookbooks/firewall/resources/firewalld_policy.rb +115 -0
  118. data/cookbooks/firewall/resources/firewalld_service.rb +98 -0
  119. data/cookbooks/firewall/resources/firewalld_zone.rb +118 -0
  120. data/cookbooks/firewall/resources/nftables.rb +71 -0
  121. data/cookbooks/firewall/resources/nftables_rule.rb +113 -0
  122. data/cookbooks/mu-activedirectory/Berksfile +1 -1
  123. data/cookbooks/mu-activedirectory/metadata.rb +1 -1
  124. data/cookbooks/mu-firewall/metadata.rb +2 -2
  125. data/cookbooks/mu-master/Berksfile +4 -3
  126. data/cookbooks/mu-master/attributes/default.rb +5 -2
  127. data/cookbooks/mu-master/files/default/check_elastic.sh +761 -0
  128. data/cookbooks/mu-master/files/default/check_kibana.rb +45 -0
  129. data/cookbooks/mu-master/libraries/mu.rb +24 -0
  130. data/cookbooks/mu-master/metadata.rb +5 -5
  131. data/cookbooks/mu-master/recipes/default.rb +31 -20
  132. data/cookbooks/mu-master/recipes/firewall-holes.rb +5 -0
  133. data/cookbooks/mu-master/recipes/init.rb +58 -19
  134. data/cookbooks/mu-master/recipes/update_nagios_only.rb +251 -178
  135. data/cookbooks/mu-master/templates/default/nagios.conf.erb +5 -11
  136. data/cookbooks/mu-master/templates/default/web_app.conf.erb +3 -0
  137. data/cookbooks/mu-php54/Berksfile +1 -1
  138. data/cookbooks/mu-php54/metadata.rb +2 -2
  139. data/cookbooks/mu-tools/Berksfile +2 -3
  140. data/cookbooks/mu-tools/attributes/default.rb +3 -4
  141. data/cookbooks/mu-tools/files/amazon/etc/bashrc +90 -0
  142. data/cookbooks/mu-tools/files/amazon/etc/login.defs +292 -0
  143. data/cookbooks/mu-tools/files/amazon/etc/profile +77 -0
  144. data/cookbooks/mu-tools/files/amazon/etc/security/limits.conf +63 -0
  145. data/cookbooks/mu-tools/files/amazon/etc/sysconfig/init +19 -0
  146. data/cookbooks/mu-tools/files/amazon/etc/sysctl.conf +82 -0
  147. data/cookbooks/mu-tools/files/amazon-2023/etc/login.defs +294 -0
  148. data/cookbooks/mu-tools/files/default/logrotate.conf +35 -0
  149. data/cookbooks/mu-tools/files/default/nrpe_conf_d.pp +0 -0
  150. data/cookbooks/mu-tools/libraries/helper.rb +21 -9
  151. data/cookbooks/mu-tools/metadata.rb +4 -4
  152. data/cookbooks/mu-tools/recipes/apply_security.rb +3 -2
  153. data/cookbooks/mu-tools/recipes/aws_api.rb +23 -5
  154. data/cookbooks/mu-tools/recipes/base_repositories.rb +4 -1
  155. data/cookbooks/mu-tools/recipes/gcloud.rb +56 -56
  156. data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
  157. data/cookbooks/mu-tools/recipes/nrpe.rb +20 -2
  158. data/cookbooks/mu-tools/recipes/rsyslog.rb +12 -1
  159. data/cookbooks/mu-tools/recipes/set_local_fw.rb +1 -1
  160. data/data_bags/nagios_services/apm_backend_connect.json +5 -0
  161. data/data_bags/nagios_services/apm_listen.json +5 -0
  162. data/data_bags/nagios_services/elastic_shards.json +5 -0
  163. data/data_bags/nagios_services/logstash.json +5 -0
  164. data/data_bags/nagios_services/rhel7_updates.json +8 -0
  165. data/extras/image-generators/AWS/centos7.yaml +1 -0
  166. data/extras/image-generators/AWS/rhel7.yaml +21 -0
  167. data/extras/image-generators/AWS/win2k12r2.yaml +1 -0
  168. data/extras/image-generators/AWS/win2k16.yaml +1 -0
  169. data/extras/image-generators/AWS/win2k19.yaml +1 -0
  170. data/extras/list-stock-amis +0 -0
  171. data/extras/ruby_rpm/muby.spec +8 -5
  172. data/extras/vault_tools/export_vaults.sh +1 -1
  173. data/extras/vault_tools/recreate_vaults.sh +0 -0
  174. data/extras/vault_tools/test_vaults.sh +0 -0
  175. data/install/deprecated-bash-library.sh +1 -1
  176. data/install/installer +4 -2
  177. data/modules/mommacat.ru +3 -1
  178. data/modules/mu/adoption.rb +1 -1
  179. data/modules/mu/cloud/dnszone.rb +2 -2
  180. data/modules/mu/cloud/machine_images.rb +26 -25
  181. data/modules/mu/cloud/resource_base.rb +213 -182
  182. data/modules/mu/cloud/server_pool.rb +1 -1
  183. data/modules/mu/cloud/ssh_sessions.rb +7 -5
  184. data/modules/mu/cloud/wrappers.rb +2 -2
  185. data/modules/mu/cloud.rb +1 -1
  186. data/modules/mu/config/bucket.rb +1 -1
  187. data/modules/mu/config/function.rb +6 -1
  188. data/modules/mu/config/loadbalancer.rb +24 -2
  189. data/modules/mu/config/ref.rb +12 -0
  190. data/modules/mu/config/role.rb +1 -1
  191. data/modules/mu/config/schema_helpers.rb +42 -9
  192. data/modules/mu/config/server.rb +43 -27
  193. data/modules/mu/config/tail.rb +19 -10
  194. data/modules/mu/config.rb +6 -5
  195. data/modules/mu/defaults/AWS.yaml +78 -114
  196. data/modules/mu/deploy.rb +9 -2
  197. data/modules/mu/groomer.rb +12 -4
  198. data/modules/mu/groomers/ansible.rb +104 -20
  199. data/modules/mu/groomers/chef.rb +15 -6
  200. data/modules/mu/master.rb +9 -4
  201. data/modules/mu/mommacat/daemon.rb +4 -2
  202. data/modules/mu/mommacat/naming.rb +1 -2
  203. data/modules/mu/mommacat/storage.rb +7 -2
  204. data/modules/mu/mommacat.rb +33 -6
  205. data/modules/mu/providers/aws/database.rb +161 -8
  206. data/modules/mu/providers/aws/dnszone.rb +11 -6
  207. data/modules/mu/providers/aws/endpoint.rb +81 -6
  208. data/modules/mu/providers/aws/firewall_rule.rb +254 -172
  209. data/modules/mu/providers/aws/function.rb +65 -3
  210. data/modules/mu/providers/aws/loadbalancer.rb +39 -28
  211. data/modules/mu/providers/aws/log.rb +2 -1
  212. data/modules/mu/providers/aws/role.rb +25 -7
  213. data/modules/mu/providers/aws/server.rb +36 -12
  214. data/modules/mu/providers/aws/server_pool.rb +237 -127
  215. data/modules/mu/providers/aws/storage_pool.rb +7 -1
  216. data/modules/mu/providers/aws/user.rb +1 -1
  217. data/modules/mu/providers/aws/userdata/linux.erb +6 -2
  218. data/modules/mu/providers/aws/userdata/windows.erb +7 -5
  219. data/modules/mu/providers/aws/vpc.rb +49 -25
  220. data/modules/mu/providers/aws.rb +13 -8
  221. data/modules/mu/providers/azure/container_cluster.rb +1 -1
  222. data/modules/mu/providers/azure/loadbalancer.rb +2 -2
  223. data/modules/mu/providers/azure/server.rb +5 -2
  224. data/modules/mu/providers/azure/userdata/linux.erb +1 -1
  225. data/modules/mu/providers/azure.rb +11 -8
  226. data/modules/mu/providers/cloudformation/dnszone.rb +1 -1
  227. data/modules/mu/providers/google/container_cluster.rb +15 -2
  228. data/modules/mu/providers/google/folder.rb +2 -1
  229. data/modules/mu/providers/google/function.rb +130 -4
  230. data/modules/mu/providers/google/habitat.rb +2 -1
  231. data/modules/mu/providers/google/loadbalancer.rb +407 -160
  232. data/modules/mu/providers/google/role.rb +16 -3
  233. data/modules/mu/providers/google/server.rb +5 -1
  234. data/modules/mu/providers/google/user.rb +25 -18
  235. data/modules/mu/providers/google/userdata/linux.erb +1 -1
  236. data/modules/mu/providers/google/vpc.rb +53 -7
  237. data/modules/mu/providers/google.rb +39 -39
  238. data/modules/mu.rb +8 -8
  239. data/modules/tests/elk.yaml +46 -0
  240. data/test/mu-master-test/controls/all_in_one.rb +1 -1
  241. metadata +207 -112
  242. data/cookbooks/firewall/CONTRIBUTING.md +0 -2
  243. data/cookbooks/firewall/MAINTAINERS.md +0 -19
  244. data/cookbooks/firewall/libraries/matchers.rb +0 -30
  245. data/extras/image-generators/AWS/rhel71.yaml +0 -17
@@ -148,8 +148,11 @@ module MU
148
148
  end
149
149
 
150
150
  @mu_name.gsub(/(--|-$)/i, "").gsub(/(_)/, "-").gsub!(/^[^a-z]/i, "")
151
+ @cloud_id ||= @mu_name # XXX ??? MMMFFF
152
+
151
153
  if @config.has_key?("parameter_group_family")
152
154
  @config["parameter_group_name"] ||= @mu_name
155
+ @config["parameter_group_name"].downcase!
153
156
  end
154
157
 
155
158
  if args[:from_cloud_desc] and args[:from_cloud_desc].is_a?(Aws::RDS::Types::DBCluster)
@@ -510,8 +513,11 @@ dependencies
510
513
 
511
514
  if create
512
515
  MU.log "Creating a #{cluster ? "cluster" : "database" } parameter group #{@config["parameter_group_name"]}"
513
-
514
- MU::Cloud::AWS.rds(region: @region, credentials: @credentials).send(cluster ? :create_db_cluster_parameter_group : :create_db_parameter_group, params)
516
+ begin
517
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).send(cluster ? :create_db_cluster_parameter_group : :create_db_parameter_group, params)
518
+ rescue Aws::RDS::Errors::DBParameterGroupAlreadyExists => e
519
+ MU.log e.message, MU::WARN
520
+ end
515
521
  end
516
522
 
517
523
 
@@ -546,11 +552,58 @@ dependencies
546
552
  end
547
553
  end
548
554
 
555
+ # If we've declared special option group options, ensure that we have
556
+ # an option group of the same name as our database instance with the
557
+ # correct settings.
558
+ # @return [String]: The name of the option group our database should use
559
+ def manageOptionGroup
560
+ if @config['option_group_options'] and !@config['option_group_options'].empty?
561
+ og = begin
562
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).describe_option_groups(
563
+ option_group_name: @mu_name
564
+ ).option_groups_list.first
565
+ rescue Aws::RDS::Errors::OptionGroupNotFoundFault
566
+ MU.log "Creating Option Group #{@mu_name} for #{@config['engine']} #{@config['major_version']} database #{@mu_name}"
567
+
568
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).create_option_group(
569
+ engine_name: @config['engine'],
570
+ major_engine_version: @config['major_version'],
571
+ option_group_name: @mu_name,
572
+ option_group_description: @deploy_id
573
+ ).option_group
574
+ end
575
+ need = []
576
+ ext_options = og.options.map { |o| o.option_name }
577
+ @config['option_group_options'].each { |opt|
578
+ if !ext_options.include?(opt)
579
+ need << { option_name: opt }
580
+ end
581
+ }
582
+
583
+ if !need.empty?
584
+ MU.log "Modifying Option Group #{@mu_name}", MU::NOTICE, need
585
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).modify_option_group(
586
+ option_group_name: @mu_name,
587
+ apply_immediately: true,
588
+ options_to_include: need
589
+ )
590
+ end
591
+
592
+ @mu_name
593
+ # Otherwise if we've declared a non-default option group, ensure that
594
+ # we're using it.
595
+ elsif @config['option_group']
596
+ @config['option_group']
597
+ else
598
+ nil
599
+ end
600
+ end
601
+
549
602
  # Called automatically by {MU::Deploy#createResources}
550
603
  def groom
551
604
  cloud_desc(use_cache: false)
552
605
  manageSubnetGroup if @vpc
553
- manageDbParameterGroup(@config["create_cluster"], create: false)
606
+ manageDbParameterGroup(@config["create_cluster"])
554
607
 
555
608
  noun = @config['create_cluster'] ? "cluster" : "instance"
556
609
 
@@ -575,6 +628,55 @@ dependencies
575
628
  mods[:vpc_security_group_ids] = @config["vpc_security_group_ids"]
576
629
  end
577
630
 
631
+ option_group_name = manageOptionGroup
632
+ if cloud_desc.option_group_memberships.first.option_group_name != option_group_name
633
+ mods[:option_group_name] = option_group_name
634
+ end
635
+
636
+ # If we've declared special option group options, ensure that we have
637
+ # an option group of the same name as our database instance with the
638
+ # correct settings.
639
+ if @config['option_group_options'] and !@config['option_group_options'].empty?
640
+ og = begin
641
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).describe_option_groups(
642
+ option_group_name: @mu_name
643
+ ).option_groups_list.first
644
+ rescue Aws::RDS::Errors::OptionGroupNotFoundFault
645
+ MU.log "Creating Option Group #{@mu_name} for #{@config['engine']} #{@config['major_version']} database #{@mu_name}"
646
+
647
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).create_option_group(
648
+ engine_name: @config['engine'],
649
+ major_engine_version: @config['major_version'],
650
+ option_group_name: @mu_name,
651
+ option_group_description: @deploy_id
652
+ ).option_group
653
+ end
654
+ need = []
655
+ ext_options = og.options.map { |o| o.option_name }
656
+ @config['option_group_options'].each { |opt|
657
+ if !ext_options.include?(opt)
658
+ need << { option_name: opt }
659
+ end
660
+ }
661
+
662
+ if !need.empty?
663
+ MU.log "Modifying Option Group #{@mu_name}", MU::NOTICE, need
664
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).modify_option_group(
665
+ option_group_name: @mu_name,
666
+ apply_immediately: true,
667
+ options_to_include: need
668
+ )
669
+ end
670
+
671
+ if cloud_desc.option_group_memberships.first.option_group_name != @mu_name
672
+ mods[:option_group_name] = @mu_name
673
+ end
674
+
675
+ # Otherwise if we've declared a non-default option group, ensure that
676
+ # we're using it.
677
+ elsif @config['option_group'] and cloud_desc.option_group_memberships.first.option_group_name != @config['option_group']
678
+ mods[:option_group_name] = @config['option_group']
679
+ end
578
680
 
579
681
  if @config['cloudwatch_logs'] and cloud_desc.enabled_cloudwatch_logs_exports.sort != @config['cloudwatch_logs'].sort
580
682
  mods[:cloudwatch_logs_export_configuration] = {
@@ -614,7 +716,9 @@ dependencies
614
716
  mods[:apply_immediately] = true
615
717
  mods[:allow_major_version_upgrade] = true
616
718
  wait_until_available
617
- MU::Cloud::AWS.rds(region: @region, credentials: @credentials).send("modify_db_#{noun}".to_sym, mods)
719
+ MU.retrier([Aws::RDS::Errors::InvalidDBInstanceState, Aws::RDS::Errors::InvalidParameterValue], wait: 60, max: 15) {
720
+ MU::Cloud::AWS.rds(region: @region, credentials: @credentials).send("modify_db_#{noun}".to_sym, mods)
721
+ }
618
722
  wait_until_available
619
723
  end
620
724
 
@@ -812,7 +916,7 @@ dependencies
812
916
 
813
917
  threads = threaded_resource_purge(:describe_db_subnet_groups, :db_subnet_groups, :db_subnet_group_name, "subgrp", region, credentials, ignoremaster, known: flags['known'], deploy_id: deploy_id) { |id|
814
918
  MU.log "Deleting RDS subnet group #{id}"
815
- MU.retrier([Aws::RDS::Errors::InvalidDBSubnetGroupStateFault], wait: 30, max: 5, ignoreme: [Aws::RDS::Errors::DBSubnetGroupNotFoundFault]) {
919
+ MU.retrier([Aws::RDS::Errors::InvalidDBSubnetGroupStateFault], wait: 60, max: 10, ignoreme: [Aws::RDS::Errors::DBSubnetGroupNotFoundFault]) {
816
920
  MU::Cloud::AWS.rds(region: region, credentials: credentials).delete_db_subnet_group(db_subnet_group_name: id) if !noop
817
921
  }
818
922
  }
@@ -820,7 +924,7 @@ dependencies
820
924
  ["db", "db_cluster"].each { |type|
821
925
  threads.concat threaded_resource_purge("describe_#{type}_parameter_groups".to_sym, "#{type}_parameter_groups".to_sym, "#{type}_parameter_group_name".to_sym, (type == "db" ? "pg" : "cluster-pg"), region, credentials, ignoremaster, known: flags['known'], deploy_id: deploy_id) { |id|
822
926
  MU.log "Deleting RDS #{type} parameter group #{id}"
823
- MU.retrier([Aws::RDS::Errors::InvalidDBParameterGroupState], wait: 30, max: 5, ignoreme: [Aws::RDS::Errors::DBParameterGroupNotFound]) {
927
+ MU.retrier([Aws::RDS::Errors::InvalidDBParameterGroupState], wait: 60, max: 10, ignoreme: [Aws::RDS::Errors::DBParameterGroupNotFound]) {
824
928
  MU::Cloud::AWS.rds(region: region, credentials: credentials).send("delete_#{type}_parameter_group", { "#{type}_parameter_group_name".to_sym => id }) if !noop
825
929
  }
826
930
  }
@@ -862,7 +966,6 @@ dependencies
862
966
  }
863
967
  }
864
968
 
865
-
866
969
  schema = {
867
970
  "db_parameter_group_parameters" => rds_parameters_primitive,
868
971
  "cluster_parameter_group_parameters" => rds_parameters_primitive,
@@ -881,8 +984,21 @@ dependencies
881
984
  "type" => "string",
882
985
  "default" => "gp2"
883
986
  },
987
+ "option_group" => {
988
+ "description" => "The name of a non-default RDS option group to use with this instance/cluster (this will be ignored if +option_group_options+ is declared).",
989
+ "type" => "string"
990
+ },
991
+ "option_group_options" => {
992
+ "description" => "One or more option group options to enable, with default settings. If specified, +option_group+ will be overridden.",
993
+ "type" => "array",
994
+ "items" => {
995
+ "type" => "string",
996
+ "description" => "Option group identifiers supported by the relevant database engine, such as +MARIADB_AUDIT_PLUGIN+ for MariaDB/MySQL. See https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/USER_WorkingWithOptionGroups.html for current options."
997
+ }
998
+ },
884
999
  "cloudwatch_logs" => {
885
1000
  "type" => "array",
1001
+ "description" => "Enable output of logs to CloudWatch",
886
1002
  "items" => {
887
1003
  "type" => "string",
888
1004
  "enum" => ["audit", "error", "general", "slowquery", "profiler", "postgresql", "alert", "listener", "trace", "upgrade", "agent"]
@@ -950,10 +1066,13 @@ dependencies
950
1066
  "versions" => [],
951
1067
  "families" => [],
952
1068
  "features" => {},
1069
+ "options" => {},
953
1070
  "raw" => {}
954
1071
  }
955
1072
  engines[version.engine]['versions'] << version.engine_version
956
1073
  engines[version.engine]['families'] << version.db_parameter_group_family
1074
+ engines[version.engine]['options'][version.major_engine_version] ||= MU::Cloud::AWS.rds().describe_option_group_options(engine_name: version.engine, major_engine_version: version.major_engine_version).option_group_options.map { |o| o.name }
1075
+
957
1076
  engines[version.engine]['raw'][version.engine_version] = version
958
1077
  [:supports_read_replica, :supports_log_exports_to_cloudwatch_logs].each { |feature|
959
1078
  if version.respond_to?(feature) and version.send(feature) == true
@@ -976,7 +1095,7 @@ dependencies
976
1095
  @@engine_cache[credentials][region] = engines
977
1096
  return engine ? @@engine_cache[credentials][region][engine] : @@engine_cache[credentials][region]
978
1097
  end
979
- private_class_method :get_supported_engines
1098
+ # private_class_method :get_supported_engines
980
1099
 
981
1100
  # Make sure any source database/cluster/snapshot we've asked for exists
982
1101
  # and is valid.
@@ -1039,6 +1158,28 @@ dependencies
1039
1158
  end
1040
1159
  private_class_method :validate_master_password
1041
1160
 
1161
+ def self.valid_option_group_options?(db)
1162
+ return true if !db['option_group_options'] or db['option_group_options'].empty?
1163
+
1164
+ engine = get_supported_engines(db['region'], db['credentials'], engine: db['engine'])
1165
+ if engine.nil? or !engine['options'] or !engine['raw'][db['engine_version']]
1166
+ return true # we can't be sure, so let the API sort it out later
1167
+ end
1168
+ major_version = engine['raw'][db['engine_version']].major_engine_version
1169
+ return true if !engine['options'][major_version]
1170
+
1171
+ ok = true
1172
+ db['option_group_options'].each { |opt|
1173
+ if !engine['options'][major_version].include?(opt)
1174
+ MU.log "#{opt} not a valid Option Group entry for #{db['engine']} #{major_version}", MU::ERR, details: engine['options'][major_version]
1175
+ ok = false
1176
+ end
1177
+ }
1178
+
1179
+ ok
1180
+ end
1181
+ private_class_method :valid_option_group_options?
1182
+
1042
1183
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::databases}, bare and unvalidated.
1043
1184
  # @param db [Hash]: The resource to process and validate
1044
1185
  # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a ember
@@ -1054,6 +1195,17 @@ dependencies
1054
1195
 
1055
1196
  ok = false if !valid_cloudwatch_logs?(db)
1056
1197
 
1198
+ ok = false if !valid_option_group_options?(db)
1199
+
1200
+ engine = self.get_supported_engines(
1201
+ db['region'],
1202
+ db['credentials'],
1203
+ engine: db['engine']
1204
+ )
1205
+ if engine and engine['raw'] and engine['raw'][db['engine_version']]
1206
+ db['major_version'] = engine['raw'][db['engine_version']].major_engine_version
1207
+ end
1208
+
1057
1209
  db["license_model"] ||=
1058
1210
  if ["postgres", "postgresql", "aurora-postgresql"].include?(db["engine"])
1059
1211
  "postgresql-license"
@@ -1346,6 +1498,7 @@ dependencies
1346
1498
 
1347
1499
  MU.retrier([Aws::RDS::Errors::InvalidParameterValue, Aws::RDS::Errors::DBSubnetGroupNotFoundFault], max: 10, wait: 15) {
1348
1500
  if %w{existing_snapshot new_snapshot}.include?(@config["creation_style"])
1501
+ params[:option_group_name] = manageOptionGroup
1349
1502
  clean_parent_opts.call
1350
1503
  MU.log "Creating database #{noun} #{@cloud_id} from snapshot #{@config["snapshot_id"]}"
1351
1504
  MU::Cloud::AWS.rds(region: @region, credentials: @credentials).send("restore_db_#{noun}_from_#{noun == "instance" ? "db_" : ""}snapshot".to_sym, params)
@@ -588,12 +588,17 @@ module MU
588
588
  ip = nil
589
589
 
590
590
  records = []
591
- lookup = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(
592
- hosted_zone_id: mu_zone.id,
593
- start_record_name: "#{dns_name}.platform-mu",
594
- start_record_type: record_type,
595
- max_items: 1
596
- ).resource_record_sets
591
+ begin
592
+ lookup = MU::Cloud::AWS.route53(credentials: credentials).list_resource_record_sets(
593
+ hosted_zone_id: mu_zone.id,
594
+ start_record_name: "#{dns_name}.platform-mu",
595
+ start_record_type: record_type,
596
+ max_items: 1
597
+ ).resource_record_sets
598
+ rescue Aws::Route53::Errors::InvalidInput => e
599
+ MU.log "Failed to look up record during #{delete ? "delete" : "add"}: "+e.message, MU::ERR, details: { "hosted_zone_id" => mu_zone.id, "start_record_name" => "#{dns_name}.platform-mu", "start_record_type" => record_type }
600
+ return nil
601
+ end
597
602
 
598
603
  lookup.each { |record|
599
604
  if record.name.match(/^#{dns_name}\.platform-mu/i) and record.type == record_type
@@ -13,14 +13,54 @@ module MU
13
13
 
14
14
  # Called automatically by {MU::Deploy#createResources}
15
15
  def create
16
- resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_rest_api(
16
+ desc = {
17
17
  name: @mu_name,
18
18
  description: @deploy.deploy_id,
19
19
  endpoint_configuration: {
20
- types: ["REGIONAL"] # XXX expose in BoK ["REGIONAL", "EDGE", "PRIVATE"]
20
+ types: [@config['endpoint_type']]
21
21
  },
22
22
  tags: @tags
23
- )
23
+ }
24
+
25
+ # XXX NLB? what the fork?
26
+ # if @vpc
27
+ # MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_vpc_link(
28
+ # name: @mu_name,
29
+ # target_arns: [Required] The ARN of the network load balancer of the VPC targeted by the VPC link. The network load balancer must be owned by the same AWS account of the API owner.
30
+ # tags: @tags
31
+ # )
32
+ # end
33
+
34
+ # XXX this is incomplete; need to cover non-VPC case, IP ranges, and fall back on account number if all else fails
35
+ if @config['endpoint_type'] == "PRIVATE"
36
+ desc[:policy] = JSON.generate(
37
+ {
38
+ "Version" => "2012-10-17",
39
+ "Statement" => [
40
+ {
41
+ "Effect" => "Deny",
42
+ "Principal" => "*",
43
+ "Action" => "execute-api:Invoke",
44
+ "Resource" => "arn:aws:execute-api:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:*/*/*/*",
45
+ "Condition" => {
46
+ "StringNotEquals" => {
47
+ "aws =>sourceVpc": @vpc.cloud_id
48
+ }
49
+ }
50
+ },
51
+ {
52
+ "Effect" => "Allow",
53
+ "Principal" => "*",
54
+ "Action" => "execute-api:Invoke",
55
+ "Resource" => "arn:aws:execute-api:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:*/*/*/*"
56
+ }
57
+ ]
58
+ }
59
+ )
60
+ end
61
+
62
+ resp = MU::Cloud::AWS.apig(region: @region, credentials: @credentials).create_rest_api(desc)
63
+
24
64
  @cloud_id = resp.id
25
65
  generate_methods(false)
26
66
  end
@@ -629,6 +669,11 @@ MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resource(
629
669
  def self.schema(_config)
630
670
  toplevel_required = []
631
671
  schema = {
672
+ "endpoint_type" => {
673
+ "default" => "REGIONAL",
674
+ "type" => "string",
675
+ "enum" => ["REGIONAL", "EDGE", "PRIVATE"]
676
+ },
632
677
  "domain_names" => {
633
678
  "type" => "array",
634
679
  "items" => {
@@ -791,7 +836,7 @@ MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resource(
791
836
  "type" => {
792
837
  "type" => "string",
793
838
  "description" => "A Mu resource type, for integrations with a sibling resource (e.g. a function), or the string +aws_generic+, which we can use in combination with +aws_generic_action+ to integrate with arbitrary AWS services.",
794
- "enum" => ["aws_generic"].concat(MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }.sort)
839
+ "enum" => ["aws_generic", "mock"].concat(MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }.sort)
795
840
  },
796
841
  "aws_generic_action" => {
797
842
  "type" => "string",
@@ -863,6 +908,23 @@ MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resource(
863
908
  "arn:#{MU::Cloud::AWS.isGovCloud?(@region) ? "aws-us-gov" : "aws"}:execute-api:#{@region}:#{MU::Cloud::AWS.credToAcct(@credentials)}:#{@cloud_id}"
864
909
  end
865
910
 
911
+ # Go fish for the account-wide CloudWatch Logs role that grants APIG
912
+ # permissions to generate logs. This appears to have disappeared from
913
+ # the web console.
914
+ def self.findCloudWatchLogsRole(credentials = nil)
915
+ roles = MU::Cloud.resourceClass("AWS", "Role").find(credentials: credentials)
916
+ roles.each_pair { |id, r|
917
+ next if r.is_a?(Aws::IAM::Types::Policy)
918
+ attached_policies = MU::Cloud::AWS.iam(credentials: @credentials).list_attached_role_policies(
919
+ role_name: r.role_name
920
+ ).attached_policies
921
+ if attached_policies.size == 1 and attached_policies.first.policy_arn == "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs"
922
+ return r.arn
923
+ end
924
+ }
925
+ nil
926
+ end
927
+
866
928
 
867
929
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::endpoints}, bare and unvalidated.
868
930
  # @param endpoint [Hash]: The resource to process and validate
@@ -893,9 +955,22 @@ MU::Cloud::AWS.apig(region: @region, credentials: @credentials).get_resource(
893
955
 
894
956
  if endpoint['access_logs']
895
957
  resp = MU::Cloud::AWS.apig(credentials: endpoint['credentials'], region: endpoint['region']).get_account
958
+
896
959
  if !resp.cloudwatch_role_arn
897
- MU.log "Endpoint '#{endpoint['name']}' is configured to use CloudWatch Logs, but the account-wide API Gateway log role is not configured", MU::ERR, details: "https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudwatch-logs/"
898
- ok = false
960
+ logs_role = findCloudWatchLogsRole
961
+ if logs_role
962
+ MU.log "Updating API Gateway account-wide to add log role #{logs_role}", MU::NOTICE
963
+ MU::Cloud::AWS.apig(credentials: endpoint['credentials'], region: endpoint['region']).update_account(
964
+ patch_operations: [
965
+ op: "replace",
966
+ path: "/cloudwatchRoleArn",
967
+ value: logs_role
968
+ ]
969
+ )
970
+ else
971
+ MU.log "Endpoint '#{endpoint['name']}' is configured to use CloudWatch Logs, but the account-wide API Gateway log role is not configured", MU::ERR, details: "https://aws.amazon.com/premiumsupport/knowledge-center/api-gateway-cloudwatch-logs/"
972
+ ok = false
973
+ end
899
974
  else
900
975
  roles = MU::Cloud::AWS::Role.find(cloud_id: resp.cloudwatch_role_arn, credentials: endpoint['credentials'], region: endpoint['region'])
901
976
  if roles.empty?