cloud-mu 1.9.0.pre.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (618) hide show
  1. checksums.yaml +7 -0
  2. data/Berksfile +56 -0
  3. data/Berksfile.lock +250 -0
  4. data/Jenkinsfile +184 -0
  5. data/LICENSE.md +37 -0
  6. data/README.md +26 -0
  7. data/bin/mu-aws-setup +376 -0
  8. data/bin/mu-cleanup +68 -0
  9. data/bin/mu-configure +1133 -0
  10. data/bin/mu-deploy +166 -0
  11. data/bin/mu-firewall-allow-clients +30 -0
  12. data/bin/mu-gcp-setup +200 -0
  13. data/bin/mu-gen-docs +34 -0
  14. data/bin/mu-gen-env +42 -0
  15. data/bin/mu-load-config.rb +158 -0
  16. data/bin/mu-node-manage +683 -0
  17. data/bin/mu-self-update +228 -0
  18. data/bin/mu-ssh +23 -0
  19. data/bin/mu-tunnel-nagios +144 -0
  20. data/bin/mu-upload-chef-artifacts +757 -0
  21. data/bin/mu-user-manage +275 -0
  22. data/cookbooks/awscli/LICENSE +37 -0
  23. data/cookbooks/awscli/README.md +58 -0
  24. data/cookbooks/awscli/attributes/default.rb +1 -0
  25. data/cookbooks/awscli/libraries/instance_metadata.rb +21 -0
  26. data/cookbooks/awscli/metadata.rb +20 -0
  27. data/cookbooks/awscli/recipes/default.rb +56 -0
  28. data/cookbooks/awscli/templates/default/config.erb +18 -0
  29. data/cookbooks/mu-activedirectory/CHANGELOG.md +13 -0
  30. data/cookbooks/mu-activedirectory/LICENSE +37 -0
  31. data/cookbooks/mu-activedirectory/README.md +6 -0
  32. data/cookbooks/mu-activedirectory/attributes/default.rb +98 -0
  33. data/cookbooks/mu-activedirectory/files/default/password-auth +32 -0
  34. data/cookbooks/mu-activedirectory/files/default/sshd_pol.pp +0 -0
  35. data/cookbooks/mu-activedirectory/files/default/sshd_pol.te +32 -0
  36. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.pp +0 -0
  37. data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.te +10 -0
  38. data/cookbooks/mu-activedirectory/files/default/system-auth +34 -0
  39. data/cookbooks/mu-activedirectory/files/default/winbindpol.pp +0 -0
  40. data/cookbooks/mu-activedirectory/files/default/winbindpol.te +37 -0
  41. data/cookbooks/mu-activedirectory/libraries/config.rb +106 -0
  42. data/cookbooks/mu-activedirectory/libraries/helper.rb +86 -0
  43. data/cookbooks/mu-activedirectory/metadata.rb +17 -0
  44. data/cookbooks/mu-activedirectory/providers/domain.rb +152 -0
  45. data/cookbooks/mu-activedirectory/providers/domain_controller.rb +89 -0
  46. data/cookbooks/mu-activedirectory/providers/domain_node.rb +275 -0
  47. data/cookbooks/mu-activedirectory/recipes/default.rb +8 -0
  48. data/cookbooks/mu-activedirectory/recipes/domain-controller.rb +44 -0
  49. data/cookbooks/mu-activedirectory/recipes/domain-node.rb +50 -0
  50. data/cookbooks/mu-activedirectory/recipes/domain.rb +43 -0
  51. data/cookbooks/mu-activedirectory/recipes/sssd.rb +185 -0
  52. data/cookbooks/mu-activedirectory/resources/domain.rb +25 -0
  53. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +25 -0
  54. data/cookbooks/mu-activedirectory/resources/domain_node.rb +20 -0
  55. data/cookbooks/mu-activedirectory/templates/default/dhclient-eth0.conf.erb +4 -0
  56. data/cookbooks/mu-activedirectory/templates/default/interface +0 -0
  57. data/cookbooks/mu-activedirectory/templates/default/krb5.conf.erb +23 -0
  58. data/cookbooks/mu-activedirectory/templates/default/ntp.conf.erb +56 -0
  59. data/cookbooks/mu-activedirectory/templates/default/smb.conf.erb +33 -0
  60. data/cookbooks/mu-activedirectory/templates/default/sssd.conf.erb +60 -0
  61. data/cookbooks/mu-activedirectory/templates/windows/Backup.xml.erb +20 -0
  62. data/cookbooks/mu-activedirectory/templates/windows/bkupInfo.xml.erb +1 -0
  63. data/cookbooks/mu-activedirectory/templates/windows/gpreprt.xml.erb +198 -0
  64. data/cookbooks/mu-activedirectory/templates/windows/gptmpl.inf.erb +12 -0
  65. data/cookbooks/mu-activedirectory/templates/windows/manifest.xml.erb +1 -0
  66. data/cookbooks/mu-firewall/CHANGELOG.md +11 -0
  67. data/cookbooks/mu-firewall/LICENSE +37 -0
  68. data/cookbooks/mu-firewall/README.md +5 -0
  69. data/cookbooks/mu-firewall/attributes/default.rb +3 -0
  70. data/cookbooks/mu-firewall/metadata.rb +16 -0
  71. data/cookbooks/mu-firewall/recipes/default.rb +10 -0
  72. data/cookbooks/mu-glusterfs/CHANGELOG.md +13 -0
  73. data/cookbooks/mu-glusterfs/LICENSE +37 -0
  74. data/cookbooks/mu-glusterfs/README.md +5 -0
  75. data/cookbooks/mu-glusterfs/attributes/default.rb +34 -0
  76. data/cookbooks/mu-glusterfs/metadata.rb +17 -0
  77. data/cookbooks/mu-glusterfs/recipes/client.rb +62 -0
  78. data/cookbooks/mu-glusterfs/recipes/default.rb +16 -0
  79. data/cookbooks/mu-glusterfs/recipes/samba.rb +57 -0
  80. data/cookbooks/mu-glusterfs/recipes/server.rb +200 -0
  81. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +71 -0
  82. data/cookbooks/mu-glusterfs/templates/default/smb.conf.erb +14 -0
  83. data/cookbooks/mu-jenkins/CHANGELOG.md +13 -0
  84. data/cookbooks/mu-jenkins/LICENSE +37 -0
  85. data/cookbooks/mu-jenkins/README.md +105 -0
  86. data/cookbooks/mu-jenkins/attributes/default.rb +42 -0
  87. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +73 -0
  88. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +44 -0
  89. data/cookbooks/mu-jenkins/metadata.rb +21 -0
  90. data/cookbooks/mu-jenkins/recipes/default.rb +195 -0
  91. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +54 -0
  92. data/cookbooks/mu-jenkins/recipes/public_key.rb +24 -0
  93. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +24 -0
  94. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +14 -0
  95. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +6 -0
  96. data/cookbooks/mu-master/CHANGELOG.md +13 -0
  97. data/cookbooks/mu-master/LICENSE +37 -0
  98. data/cookbooks/mu-master/README.md +6 -0
  99. data/cookbooks/mu-master/attributes/default.rb +95 -0
  100. data/cookbooks/mu-master/files/default/0-mu-log-server.conf +19 -0
  101. data/cookbooks/mu-master/files/default/addRSA.ldif +8 -0
  102. data/cookbooks/mu-master/files/default/check_mem.pl +197 -0
  103. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  104. data/cookbooks/mu-master/files/default/dirsrv_admin.pp +0 -0
  105. data/cookbooks/mu-master/files/default/dirsrv_admin.te +13 -0
  106. data/cookbooks/mu-master/files/default/nagios_selinux.pp +0 -0
  107. data/cookbooks/mu-master/files/default/nagios_selinux.te +51 -0
  108. data/cookbooks/mu-master/files/default/nagios_selinux_7.pp +0 -0
  109. data/cookbooks/mu-master/files/default/nagios_selinux_7.te +17 -0
  110. data/cookbooks/mu-master/files/default/pam_sshd +18 -0
  111. data/cookbooks/mu-master/files/default/ssl_enable.ldif +18 -0
  112. data/cookbooks/mu-master/files/default/syslogd_oddjobd.pp +0 -0
  113. data/cookbooks/mu-master/files/default/syslogd_oddjobd.te +10 -0
  114. data/cookbooks/mu-master/files/default/vimrc +19 -0
  115. data/cookbooks/mu-master/libraries/mu.rb +29 -0
  116. data/cookbooks/mu-master/metadata.rb +30 -0
  117. data/cookbooks/mu-master/providers/user.rb +41 -0
  118. data/cookbooks/mu-master/recipes/389ds.rb +164 -0
  119. data/cookbooks/mu-master/recipes/basepackages.rb +58 -0
  120. data/cookbooks/mu-master/recipes/caching_nameserver.rb +37 -0
  121. data/cookbooks/mu-master/recipes/default.rb +451 -0
  122. data/cookbooks/mu-master/recipes/eks-kubectl.rb +41 -0
  123. data/cookbooks/mu-master/recipes/firewall-holes.rb +70 -0
  124. data/cookbooks/mu-master/recipes/init.rb +542 -0
  125. data/cookbooks/mu-master/recipes/ssl-certs.rb +109 -0
  126. data/cookbooks/mu-master/recipes/sssd.rb +89 -0
  127. data/cookbooks/mu-master/recipes/update_nagios_only.rb +242 -0
  128. data/cookbooks/mu-master/recipes/vault.rb +111 -0
  129. data/cookbooks/mu-master/resources/user.rb +19 -0
  130. data/cookbooks/mu-master/templates/default/389-directory-setup.inf.erb +28 -0
  131. data/cookbooks/mu-master/templates/default/chef-server.rb.erb +18 -0
  132. data/cookbooks/mu-master/templates/default/dhclient-eth0.conf.erb +9 -0
  133. data/cookbooks/mu-master/templates/default/mu-momma-cat.erb +149 -0
  134. data/cookbooks/mu-master/templates/default/mu.rc.erb +9 -0
  135. data/cookbooks/mu-master/templates/default/openssl.cnf.erb +354 -0
  136. data/cookbooks/mu-master/templates/default/sssd.conf.erb +44 -0
  137. data/cookbooks/mu-master/templates/default/web_app.conf.erb +90 -0
  138. data/cookbooks/mu-mongo/CHANGELOG.md +13 -0
  139. data/cookbooks/mu-mongo/LICENSE +37 -0
  140. data/cookbooks/mu-mongo/README.md +5 -0
  141. data/cookbooks/mu-mongo/attributes/default.rb +22 -0
  142. data/cookbooks/mu-mongo/files/default/keyfile +16 -0
  143. data/cookbooks/mu-mongo/files/default/remove_nodes.js +5 -0
  144. data/cookbooks/mu-mongo/metadata.rb +17 -0
  145. data/cookbooks/mu-mongo/recipes/default.rb +149 -0
  146. data/cookbooks/mu-mongo/recipes/yum-update-rule.rb +18 -0
  147. data/cookbooks/mu-mongo/templates/default/mongo_create_openfema_db.js.erb +2 -0
  148. data/cookbooks/mu-mongo/templates/default/mongo_init.js.erb +1 -0
  149. data/cookbooks/mu-mongo/templates/default/mongo_logrotate.erb +14 -0
  150. data/cookbooks/mu-mongo/templates/default/mongo_replset_addnodes.js.erb +6 -0
  151. data/cookbooks/mu-mongo/templates/default/replset_init.js.erb +2 -0
  152. data/cookbooks/mu-openvpn/CHANGELOG.md +13 -0
  153. data/cookbooks/mu-openvpn/LICENSE +37 -0
  154. data/cookbooks/mu-openvpn/README.md +6 -0
  155. data/cookbooks/mu-openvpn/attributes/default.rb +119 -0
  156. data/cookbooks/mu-openvpn/metadata.rb +18 -0
  157. data/cookbooks/mu-openvpn/recipes/default.rb +108 -0
  158. data/cookbooks/mu-openvpn/templates/default/users.json.erb +42 -0
  159. data/cookbooks/mu-php54/CHANGELOG.md +12 -0
  160. data/cookbooks/mu-php54/LICENSE +37 -0
  161. data/cookbooks/mu-php54/README.md +0 -0
  162. data/cookbooks/mu-php54/files/centos/php.ini +1802 -0
  163. data/cookbooks/mu-php54/files/ubuntu/php.ini +1870 -0
  164. data/cookbooks/mu-php54/metadata.rb +21 -0
  165. data/cookbooks/mu-php54/recipes/default.rb +97 -0
  166. data/cookbooks/mu-splunk/CHANGELOG.md +37 -0
  167. data/cookbooks/mu-splunk/LICENSE +37 -0
  168. data/cookbooks/mu-splunk/README.md +451 -0
  169. data/cookbooks/mu-splunk/attributes/default.rb +95 -0
  170. data/cookbooks/mu-splunk/attributes/upgrade.rb +49 -0
  171. data/cookbooks/mu-splunk/definitions/splunk_installer.rb +103 -0
  172. data/cookbooks/mu-splunk/files/default/splunk-nocheck +10 -0
  173. data/cookbooks/mu-splunk/libraries/helpers.rb +72 -0
  174. data/cookbooks/mu-splunk/libraries/splunk_app_provider.rb +156 -0
  175. data/cookbooks/mu-splunk/libraries/splunk_app_resource.rb +43 -0
  176. data/cookbooks/mu-splunk/metadata.json +30 -0
  177. data/cookbooks/mu-splunk/metadata.rb +17 -0
  178. data/cookbooks/mu-splunk/recipes/client.rb +143 -0
  179. data/cookbooks/mu-splunk/recipes/default.rb +31 -0
  180. data/cookbooks/mu-splunk/recipes/disabled.rb +41 -0
  181. data/cookbooks/mu-splunk/recipes/install_forwarder.rb +23 -0
  182. data/cookbooks/mu-splunk/recipes/install_server.rb +23 -0
  183. data/cookbooks/mu-splunk/recipes/server.rb +53 -0
  184. data/cookbooks/mu-splunk/recipes/service.rb +95 -0
  185. data/cookbooks/mu-splunk/recipes/setup_auth.rb +49 -0
  186. data/cookbooks/mu-splunk/recipes/setup_ssl.rb +63 -0
  187. data/cookbooks/mu-splunk/recipes/upgrade.rb +94 -0
  188. data/cookbooks/mu-splunk/recipes/user.rb +34 -0
  189. data/cookbooks/mu-splunk/templates/default/base_logs_unix_inputs.conf.erb +26 -0
  190. data/cookbooks/mu-splunk/templates/default/inputs.conf.erb +13 -0
  191. data/cookbooks/mu-splunk/templates/default/outputs.conf.erb +9 -0
  192. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +74 -0
  193. data/cookbooks/mu-splunk/templates/default/system-web.conf.erb +7 -0
  194. data/cookbooks/mu-tools/CHANGELOG.md +12 -0
  195. data/cookbooks/mu-tools/LICENSE +37 -0
  196. data/cookbooks/mu-tools/README.md +188 -0
  197. data/cookbooks/mu-tools/attributes/default.rb +142 -0
  198. data/cookbooks/mu-tools/attributes/ebs_rolling_snapshots.rb +3 -0
  199. data/cookbooks/mu-tools/files/amazon/etc/freshclam.conf +235 -0
  200. data/cookbooks/mu-tools/files/centos/CentOS-Base.repo +52 -0
  201. data/cookbooks/mu-tools/files/centos/etc/bashrc +93 -0
  202. data/cookbooks/mu-tools/files/centos/etc/freshclam.conf +235 -0
  203. data/cookbooks/mu-tools/files/centos/etc/login.defs +72 -0
  204. data/cookbooks/mu-tools/files/centos/etc/profile +77 -0
  205. data/cookbooks/mu-tools/files/centos/etc/security/limits.conf +57 -0
  206. data/cookbooks/mu-tools/files/centos/etc/sysconfig/init +19 -0
  207. data/cookbooks/mu-tools/files/centos/etc/sysctl.conf +82 -0
  208. data/cookbooks/mu-tools/files/centos-6/README_MU +0 -0
  209. data/cookbooks/mu-tools/files/centos-6/etc/audit/stig.rules +173 -0
  210. data/cookbooks/mu-tools/files/centos-6/etc/bashrc +90 -0
  211. data/cookbooks/mu-tools/files/centos-6/etc/login.defs +70 -0
  212. data/cookbooks/mu-tools/files/centos-6/etc/pam.d/su +12 -0
  213. data/cookbooks/mu-tools/files/centos-6/etc/profile +83 -0
  214. data/cookbooks/mu-tools/files/centos-6/etc/securetty +12 -0
  215. data/cookbooks/mu-tools/files/centos-6/etc/sysconfig/init +30 -0
  216. data/cookbooks/mu-tools/files/centos-6/etc/sysctl.conf +40 -0
  217. data/cookbooks/mu-tools/files/default/Mu_CA.pem +34 -0
  218. data/cookbooks/mu-tools/files/default/PSWindowsUpdate.zip +0 -0
  219. data/cookbooks/mu-tools/files/default/ebs_snapshots.py +123 -0
  220. data/cookbooks/mu-tools/files/default/etc/BANNER +0 -0
  221. data/cookbooks/mu-tools/files/default/etc/BANNER-FEDERAL +19 -0
  222. data/cookbooks/mu-tools/files/default/gpo_no_uac.zip +0 -0
  223. data/cookbooks/mu-tools/files/default/mypol.pp +0 -0
  224. data/cookbooks/mu-tools/files/default/mypol.te +37 -0
  225. data/cookbooks/mu-tools/files/default/nrpe_c7.pp +0 -0
  226. data/cookbooks/mu-tools/files/default/nrpe_c7.te +31 -0
  227. data/cookbooks/mu-tools/files/default/nrpe_check_disk.pp +0 -0
  228. data/cookbooks/mu-tools/files/default/nrpe_check_disk.te +11 -0
  229. data/cookbooks/mu-tools/files/default/nrpe_disk.pp +0 -0
  230. data/cookbooks/mu-tools/files/default/nrpe_disk.te +10 -0
  231. data/cookbooks/mu-tools/files/default/nrpe_file.pp +0 -0
  232. data/cookbooks/mu-tools/files/default/nrpe_file.te +31 -0
  233. data/cookbooks/mu-tools/files/default/ntrights +0 -0
  234. data/cookbooks/mu-tools/files/default/serverclass.conf +18 -0
  235. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/app.conf +1 -0
  236. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/inputs.conf +13 -0
  237. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/app.conf +1 -0
  238. data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/inputs.conf +8 -0
  239. data/cookbooks/mu-tools/files/default/sshd_pol.pp +0 -0
  240. data/cookbooks/mu-tools/files/default/sshd_pol.te +32 -0
  241. data/cookbooks/mu-tools/files/redhat/etc/bashrc +93 -0
  242. data/cookbooks/mu-tools/files/redhat/etc/freshclam.conf +235 -0
  243. data/cookbooks/mu-tools/files/redhat/etc/login.defs +72 -0
  244. data/cookbooks/mu-tools/files/redhat/etc/profile +77 -0
  245. data/cookbooks/mu-tools/files/redhat/etc/security/limits.conf +57 -0
  246. data/cookbooks/mu-tools/files/redhat/etc/sysconfig/init +19 -0
  247. data/cookbooks/mu-tools/files/redhat/etc/sysctl.conf +82 -0
  248. data/cookbooks/mu-tools/files/redhat-6/README_MU +0 -0
  249. data/cookbooks/mu-tools/files/redhat-6/etc/audit/stig.rules +173 -0
  250. data/cookbooks/mu-tools/files/redhat-6/etc/bashrc +90 -0
  251. data/cookbooks/mu-tools/files/redhat-6/etc/login.defs +70 -0
  252. data/cookbooks/mu-tools/files/redhat-6/etc/pam.d/su +12 -0
  253. data/cookbooks/mu-tools/files/redhat-6/etc/profile +83 -0
  254. data/cookbooks/mu-tools/files/redhat-6/etc/securetty +12 -0
  255. data/cookbooks/mu-tools/files/redhat-6/etc/sysconfig/init +30 -0
  256. data/cookbooks/mu-tools/files/redhat-6/etc/sysctl.conf +40 -0
  257. data/cookbooks/mu-tools/files/redhat-7.1/etc/freshclam.conf +235 -0
  258. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/bash.bashrc +64 -0
  259. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/common-session +30 -0
  260. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/login.defs +338 -0
  261. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/profile +30 -0
  262. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/security/limits.conf +56 -0
  263. data/cookbooks/mu-tools/files/ubuntu-12.04/etc/sysctl.conf +60 -0
  264. data/cookbooks/mu-tools/libraries/helper.rb +292 -0
  265. data/cookbooks/mu-tools/metadata.rb +28 -0
  266. data/cookbooks/mu-tools/recipes/add_admin_ssh_keys.rb +35 -0
  267. data/cookbooks/mu-tools/recipes/apply_security.rb +440 -0
  268. data/cookbooks/mu-tools/recipes/aws_api.rb +23 -0
  269. data/cookbooks/mu-tools/recipes/base_repositories.rb +31 -0
  270. data/cookbooks/mu-tools/recipes/cisbenchmark.rb +59 -0
  271. data/cookbooks/mu-tools/recipes/clamav.rb +53 -0
  272. data/cookbooks/mu-tools/recipes/cloudinit.rb +58 -0
  273. data/cookbooks/mu-tools/recipes/configure_oracle_tools.rb +81 -0
  274. data/cookbooks/mu-tools/recipes/disable-requiretty.rb +22 -0
  275. data/cookbooks/mu-tools/recipes/ebs_rolling_snapshots.rb +75 -0
  276. data/cookbooks/mu-tools/recipes/efs.rb +70 -0
  277. data/cookbooks/mu-tools/recipes/eks.rb +160 -0
  278. data/cookbooks/mu-tools/recipes/gcloud.rb +98 -0
  279. data/cookbooks/mu-tools/recipes/google_api.rb +25 -0
  280. data/cookbooks/mu-tools/recipes/maldet.rb +67 -0
  281. data/cookbooks/mu-tools/recipes/nagios.rb +19 -0
  282. data/cookbooks/mu-tools/recipes/newclient.rb +23 -0
  283. data/cookbooks/mu-tools/recipes/nrpe.rb +115 -0
  284. data/cookbooks/mu-tools/recipes/python_pip.rb +35 -0
  285. data/cookbooks/mu-tools/recipes/retrieve_application.rb +51 -0
  286. data/cookbooks/mu-tools/recipes/rsyslog.rb +65 -0
  287. data/cookbooks/mu-tools/recipes/set_local_fw.rb +57 -0
  288. data/cookbooks/mu-tools/recipes/set_mu_hostname.rb +81 -0
  289. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +86 -0
  290. data/cookbooks/mu-tools/recipes/splunk-client.rb +69 -0
  291. data/cookbooks/mu-tools/recipes/splunk-server.rb +104 -0
  292. data/cookbooks/mu-tools/recipes/store_inspec_attr.rb +8 -0
  293. data/cookbooks/mu-tools/recipes/updates.rb +96 -0
  294. data/cookbooks/mu-tools/recipes/windows-client.rb +202 -0
  295. data/cookbooks/mu-tools/resources/aws_windows.rb +33 -0
  296. data/cookbooks/mu-tools/resources/disk.rb +88 -0
  297. data/cookbooks/mu-tools/resources/mommacat_request.rb +11 -0
  298. data/cookbooks/mu-tools/resources/scheduled_tasks.rb +29 -0
  299. data/cookbooks/mu-tools/resources/sshd_service.rb +45 -0
  300. data/cookbooks/mu-tools/resources/windows_users.rb +242 -0
  301. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +168 -0
  302. data/cookbooks/mu-tools/templates/centos-6/sshd_config.erb +212 -0
  303. data/cookbooks/mu-tools/templates/centos-7/sshd_config.erb +215 -0
  304. data/cookbooks/mu-tools/templates/default/0-mu-log-client.conf.erb +13 -0
  305. data/cookbooks/mu-tools/templates/default/conf.maldet.erb +137 -0
  306. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +30 -0
  307. data/cookbooks/mu-tools/templates/default/etc_pamd_password-auth.erb +14 -0
  308. data/cookbooks/mu-tools/templates/default/etc_pamd_system-auth.erb +14 -0
  309. data/cookbooks/mu-tools/templates/default/etc_sysconfig_network.erb +12 -0
  310. data/cookbooks/mu-tools/templates/default/kubeconfig.erb +29 -0
  311. data/cookbooks/mu-tools/templates/default/kubelet.service.erb +35 -0
  312. data/cookbooks/mu-tools/templates/default/maldet_scanall.sh.erb +15 -0
  313. data/cookbooks/mu-tools/templates/default/nrpe.cfg.erb +233 -0
  314. data/cookbooks/mu-tools/templates/redhat-6/sshd_config.erb +213 -0
  315. data/cookbooks/mu-tools/templates/redhat-7/sshd_config.erb +215 -0
  316. data/cookbooks/mu-tools/templates/ubuntu-12.04/sshd_config.erb +146 -0
  317. data/cookbooks/mu-tools/templates/ubuntu-14.04/sshd_config.erb +145 -0
  318. data/cookbooks/mu-tools/templates/windows/Backup.xml.erb +20 -0
  319. data/cookbooks/mu-tools/templates/windows/bkupInfo.xml.erb +1 -0
  320. data/cookbooks/mu-tools/templates/windows/gpreprt.xml.erb +214 -0
  321. data/cookbooks/mu-tools/templates/windows/gptmpl.inf.erb +12 -0
  322. data/cookbooks/mu-tools/templates/windows/manifest.xml.erb +1 -0
  323. data/cookbooks/mu-tools/templates/windows/set_ad_dns_scheduled_task.ps1.erb +6 -0
  324. data/cookbooks/mu-tools/templates/windows/sshd_config.erb +136 -0
  325. data/cookbooks/mu-utility/CHANGELOG.md +12 -0
  326. data/cookbooks/mu-utility/LICENSE +37 -0
  327. data/cookbooks/mu-utility/README.md +6 -0
  328. data/cookbooks/mu-utility/attributes/default.rb +1 -0
  329. data/cookbooks/mu-utility/libraries/matchers.rb +21 -0
  330. data/cookbooks/mu-utility/metadata.rb +16 -0
  331. data/cookbooks/mu-utility/recipes/apt.rb +23 -0
  332. data/cookbooks/mu-utility/recipes/cleanup_image_helper.rb +118 -0
  333. data/cookbooks/mu-utility/recipes/iptables.rb +26 -0
  334. data/cookbooks/mu-utility/recipes/luks.rb +18 -0
  335. data/cookbooks/mu-utility/recipes/nat.rb +104 -0
  336. data/cookbooks/mu-utility/recipes/php.rb +33 -0
  337. data/cookbooks/mu-utility/recipes/rdp_gateway.rb +83 -0
  338. data/cookbooks/mu-utility/recipes/remi.rb +44 -0
  339. data/cookbooks/mu-utility/recipes/vim.rb +26 -0
  340. data/cookbooks/mu-utility/recipes/windows_basics.rb +37 -0
  341. data/cookbooks/mu-utility/recipes/zip.rb +26 -0
  342. data/cookbooks/mu-utility/templates/default/BundleConfig.xml.erb +34 -0
  343. data/cookbooks/mu-utility/templates/default/config.xml.erb +60 -0
  344. data/cookbooks/nagios/Berksfile +8 -0
  345. data/cookbooks/nagios/CHANGELOG.md +589 -0
  346. data/cookbooks/nagios/CONTRIBUTING.md +11 -0
  347. data/cookbooks/nagios/LICENSE +37 -0
  348. data/cookbooks/nagios/README.md +328 -0
  349. data/cookbooks/nagios/TESTING.md +2 -0
  350. data/cookbooks/nagios/attributes/config.rb +171 -0
  351. data/cookbooks/nagios/attributes/default.rb +228 -0
  352. data/cookbooks/nagios/chefignore +102 -0
  353. data/cookbooks/nagios/definitions/command.rb +33 -0
  354. data/cookbooks/nagios/definitions/contact.rb +33 -0
  355. data/cookbooks/nagios/definitions/contactgroup.rb +33 -0
  356. data/cookbooks/nagios/definitions/host.rb +33 -0
  357. data/cookbooks/nagios/definitions/hostdependency.rb +33 -0
  358. data/cookbooks/nagios/definitions/hostescalation.rb +34 -0
  359. data/cookbooks/nagios/definitions/hostgroup.rb +33 -0
  360. data/cookbooks/nagios/definitions/nagios_conf.rb +38 -0
  361. data/cookbooks/nagios/definitions/resource.rb +33 -0
  362. data/cookbooks/nagios/definitions/service.rb +33 -0
  363. data/cookbooks/nagios/definitions/servicedependency.rb +33 -0
  364. data/cookbooks/nagios/definitions/serviceescalation.rb +34 -0
  365. data/cookbooks/nagios/definitions/servicegroup.rb +33 -0
  366. data/cookbooks/nagios/definitions/timeperiod.rb +33 -0
  367. data/cookbooks/nagios/libraries/base.rb +314 -0
  368. data/cookbooks/nagios/libraries/command.rb +91 -0
  369. data/cookbooks/nagios/libraries/contact.rb +230 -0
  370. data/cookbooks/nagios/libraries/contactgroup.rb +112 -0
  371. data/cookbooks/nagios/libraries/custom_option.rb +36 -0
  372. data/cookbooks/nagios/libraries/data_bag_helper.rb +23 -0
  373. data/cookbooks/nagios/libraries/default.rb +90 -0
  374. data/cookbooks/nagios/libraries/host.rb +412 -0
  375. data/cookbooks/nagios/libraries/hostdependency.rb +181 -0
  376. data/cookbooks/nagios/libraries/hostescalation.rb +173 -0
  377. data/cookbooks/nagios/libraries/hostgroup.rb +119 -0
  378. data/cookbooks/nagios/libraries/nagios.rb +282 -0
  379. data/cookbooks/nagios/libraries/resource.rb +59 -0
  380. data/cookbooks/nagios/libraries/service.rb +455 -0
  381. data/cookbooks/nagios/libraries/servicedependency.rb +215 -0
  382. data/cookbooks/nagios/libraries/serviceescalation.rb +195 -0
  383. data/cookbooks/nagios/libraries/servicegroup.rb +144 -0
  384. data/cookbooks/nagios/libraries/timeperiod.rb +160 -0
  385. data/cookbooks/nagios/libraries/users_helper.rb +54 -0
  386. data/cookbooks/nagios/metadata.rb +25 -0
  387. data/cookbooks/nagios/recipes/_load_databag_config.rb +153 -0
  388. data/cookbooks/nagios/recipes/_load_default_config.rb +241 -0
  389. data/cookbooks/nagios/recipes/apache.rb +48 -0
  390. data/cookbooks/nagios/recipes/default.rb +204 -0
  391. data/cookbooks/nagios/recipes/nginx.rb +82 -0
  392. data/cookbooks/nagios/recipes/pagerduty.rb +143 -0
  393. data/cookbooks/nagios/recipes/server_package.rb +40 -0
  394. data/cookbooks/nagios/recipes/server_source.rb +164 -0
  395. data/cookbooks/nagios/templates/default/apache2.conf.erb +96 -0
  396. data/cookbooks/nagios/templates/default/cgi.cfg.erb +266 -0
  397. data/cookbooks/nagios/templates/default/commands.cfg.erb +13 -0
  398. data/cookbooks/nagios/templates/default/contacts.cfg.erb +37 -0
  399. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +25 -0
  400. data/cookbooks/nagios/templates/default/hosts.cfg.erb +15 -0
  401. data/cookbooks/nagios/templates/default/htpasswd.users.erb +6 -0
  402. data/cookbooks/nagios/templates/default/nagios.cfg.erb +22 -0
  403. data/cookbooks/nagios/templates/default/nginx.conf.erb +62 -0
  404. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +185 -0
  405. data/cookbooks/nagios/templates/default/resource.cfg.erb +27 -0
  406. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +15 -0
  407. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +14 -0
  408. data/cookbooks/nagios/templates/default/services.cfg.erb +14 -0
  409. data/cookbooks/nagios/templates/default/templates.cfg.erb +31 -0
  410. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +13 -0
  411. data/cookbooks/s3fs/CHANGELOG.md +13 -0
  412. data/cookbooks/s3fs/LICENSE +37 -0
  413. data/cookbooks/s3fs/README.md +6 -0
  414. data/cookbooks/s3fs/attributes/default.rb +15 -0
  415. data/cookbooks/s3fs/files/default/fuse-2.9.3.zip +0 -0
  416. data/cookbooks/s3fs/metadata.rb +16 -0
  417. data/cookbooks/s3fs/recipes/default.rb +91 -0
  418. data/data_bags/demo/app.json +7 -0
  419. data/data_bags/nagios_services/chef.json +6 -0
  420. data/data_bags/nagios_services/linux_diskspace.json +5 -0
  421. data/data_bags/nagios_services/momma_cat.json +6 -0
  422. data/data_bags/nagios_services/mu-master-memory.json +5 -0
  423. data/data_bags/nagios_services/nagios_ui.json +6 -0
  424. data/data_bags/nagios_services/node_ssh.json +6 -0
  425. data/data_bags/nagios_services/ssh.json +6 -0
  426. data/demo/lambda_test.yaml +29 -0
  427. data/environments/DEV.json +8 -0
  428. data/environments/PROD.json +8 -0
  429. data/environments/dev.json +8 -0
  430. data/environments/development.json +8 -0
  431. data/environments/prod.json +8 -0
  432. data/extras/README.md +1 -0
  433. data/extras/admin-role-binding.yaml +16 -0
  434. data/extras/admin-user.yaml +6 -0
  435. data/extras/aws-auth-cm.yaml.erb +12 -0
  436. data/extras/clean-stock-amis +48 -0
  437. data/extras/git-fix-permissions-hook +12 -0
  438. data/extras/gitlab-eks-helper.sh.erb +20 -0
  439. data/extras/image-generators/README.md +2 -0
  440. data/extras/image-generators/aws/centos6.yaml +18 -0
  441. data/extras/image-generators/aws/centos7-govcloud.yaml +24 -0
  442. data/extras/image-generators/aws/centos7.yaml +17 -0
  443. data/extras/image-generators/aws/rhel7.yaml +17 -0
  444. data/extras/image-generators/aws/win2k12.yaml +16 -0
  445. data/extras/image-generators/aws/win2k16.yaml +16 -0
  446. data/extras/image-generators/aws/windows.yaml +18 -0
  447. data/extras/image-generators/gcp/centos6.yaml +17 -0
  448. data/extras/lambda_waf_domain_blacklist.py +103 -0
  449. data/extras/platform_berksfile_base +50 -0
  450. data/extras/ruby_rpm/build.sh +17 -0
  451. data/extras/ruby_rpm/muby.spec +44 -0
  452. data/extras/vault_tools/README.md +6 -0
  453. data/extras/vault_tools/export_vaults.sh +3 -0
  454. data/extras/vault_tools/recreate_vaults.sh +5 -0
  455. data/extras/vault_tools/test_vaults.sh +5 -0
  456. data/install/README.md +8 -0
  457. data/install/cfn_create_mu_master.json +1034 -0
  458. data/install/chef-server.rb.erb +19 -0
  459. data/install/deprecated-bash-library.sh +1891 -0
  460. data/install/images/Usage.png +0 -0
  461. data/install/installer +71 -0
  462. data/install/jenkinskeys.rb +8 -0
  463. data/install/user-dot-murc.erb +14 -0
  464. data/modules/html.erb +19 -0
  465. data/modules/mommacat.ru +426 -0
  466. data/modules/mu/cleanup.rb +339 -0
  467. data/modules/mu/cloud.rb +1446 -0
  468. data/modules/mu/clouds/README.md +201 -0
  469. data/modules/mu/clouds/aws/alarm.rb +319 -0
  470. data/modules/mu/clouds/aws/cache_cluster.rb +1010 -0
  471. data/modules/mu/clouds/aws/collection.rb +373 -0
  472. data/modules/mu/clouds/aws/container_cluster.rb +667 -0
  473. data/modules/mu/clouds/aws/database.rb +1836 -0
  474. data/modules/mu/clouds/aws/dnszone.rb +911 -0
  475. data/modules/mu/clouds/aws/firewall_rule.rb +641 -0
  476. data/modules/mu/clouds/aws/folder.rb +92 -0
  477. data/modules/mu/clouds/aws/function.rb +349 -0
  478. data/modules/mu/clouds/aws/group.rb +251 -0
  479. data/modules/mu/clouds/aws/loadbalancer.rb +888 -0
  480. data/modules/mu/clouds/aws/log.rb +363 -0
  481. data/modules/mu/clouds/aws/msg_queue.rb +480 -0
  482. data/modules/mu/clouds/aws/notification.rb +139 -0
  483. data/modules/mu/clouds/aws/role.rb +656 -0
  484. data/modules/mu/clouds/aws/search_domain.rb +646 -0
  485. data/modules/mu/clouds/aws/server.rb +2294 -0
  486. data/modules/mu/clouds/aws/server_pool.rb +1388 -0
  487. data/modules/mu/clouds/aws/storage_pool.rb +495 -0
  488. data/modules/mu/clouds/aws/user.rb +382 -0
  489. data/modules/mu/clouds/aws/userdata/README.md +4 -0
  490. data/modules/mu/clouds/aws/userdata/linux.erb +179 -0
  491. data/modules/mu/clouds/aws/userdata/windows.erb +278 -0
  492. data/modules/mu/clouds/aws/vpc.rb +1943 -0
  493. data/modules/mu/clouds/aws.rb +1009 -0
  494. data/modules/mu/clouds/cloudformation/alarm.rb +146 -0
  495. data/modules/mu/clouds/cloudformation/cache_cluster.rb +167 -0
  496. data/modules/mu/clouds/cloudformation/collection.rb +117 -0
  497. data/modules/mu/clouds/cloudformation/database.rb +278 -0
  498. data/modules/mu/clouds/cloudformation/dnszone.rb +274 -0
  499. data/modules/mu/clouds/cloudformation/firewall_rule.rb +308 -0
  500. data/modules/mu/clouds/cloudformation/loadbalancer.rb +193 -0
  501. data/modules/mu/clouds/cloudformation/log.rb +170 -0
  502. data/modules/mu/clouds/cloudformation/server.rb +370 -0
  503. data/modules/mu/clouds/cloudformation/server_pool.rb +279 -0
  504. data/modules/mu/clouds/cloudformation/vpc.rb +322 -0
  505. data/modules/mu/clouds/cloudformation.rb +733 -0
  506. data/modules/mu/clouds/docker.rb +30 -0
  507. data/modules/mu/clouds/google/container_cluster.rb +290 -0
  508. data/modules/mu/clouds/google/database.rb +152 -0
  509. data/modules/mu/clouds/google/firewall_rule.rb +267 -0
  510. data/modules/mu/clouds/google/group.rb +164 -0
  511. data/modules/mu/clouds/google/loadbalancer.rb +479 -0
  512. data/modules/mu/clouds/google/server.rb +1510 -0
  513. data/modules/mu/clouds/google/server_pool.rb +274 -0
  514. data/modules/mu/clouds/google/user.rb +266 -0
  515. data/modules/mu/clouds/google/userdata/README.md +4 -0
  516. data/modules/mu/clouds/google/userdata/linux.erb +137 -0
  517. data/modules/mu/clouds/google/userdata/windows.erb +275 -0
  518. data/modules/mu/clouds/google/vpc.rb +890 -0
  519. data/modules/mu/clouds/google.rb +811 -0
  520. data/modules/mu/config/README.md +11 -0
  521. data/modules/mu/config/alarm.rb +271 -0
  522. data/modules/mu/config/cache_cluster.rb +172 -0
  523. data/modules/mu/config/collection.rb +87 -0
  524. data/modules/mu/config/container_cluster.rb +103 -0
  525. data/modules/mu/config/container_cluster.yml +36 -0
  526. data/modules/mu/config/database.rb +458 -0
  527. data/modules/mu/config/database.yml +26 -0
  528. data/modules/mu/config/dnszone.rb +327 -0
  529. data/modules/mu/config/firewall_rule.rb +118 -0
  530. data/modules/mu/config/folder.rb +70 -0
  531. data/modules/mu/config/function.rb +140 -0
  532. data/modules/mu/config/group.rb +64 -0
  533. data/modules/mu/config/loadbalancer.rb +482 -0
  534. data/modules/mu/config/log.rb +47 -0
  535. data/modules/mu/config/log.yml +6 -0
  536. data/modules/mu/config/msg_queue.rb +47 -0
  537. data/modules/mu/config/msg_queue.yml +9 -0
  538. data/modules/mu/config/notification.rb +44 -0
  539. data/modules/mu/config/project.rb +71 -0
  540. data/modules/mu/config/role.rb +102 -0
  541. data/modules/mu/config/search_domain.rb +61 -0
  542. data/modules/mu/config/search_domain.yml +25 -0
  543. data/modules/mu/config/server.rb +587 -0
  544. data/modules/mu/config/server.yml +8 -0
  545. data/modules/mu/config/server_pool.rb +216 -0
  546. data/modules/mu/config/server_pool.yml +71 -0
  547. data/modules/mu/config/storage_pool.rb +145 -0
  548. data/modules/mu/config/user.rb +78 -0
  549. data/modules/mu/config/vpc.rb +743 -0
  550. data/modules/mu/config/vpc.yml +6 -0
  551. data/modules/mu/config.rb +2000 -0
  552. data/modules/mu/defaults/README.md +2 -0
  553. data/modules/mu/defaults/amazon_images.yaml +121 -0
  554. data/modules/mu/defaults/google_images.yaml +16 -0
  555. data/modules/mu/deploy.rb +686 -0
  556. data/modules/mu/groomer.rb +123 -0
  557. data/modules/mu/groomers/README.md +58 -0
  558. data/modules/mu/groomers/chef.rb +1024 -0
  559. data/modules/mu/kittens.rb +11319 -0
  560. data/modules/mu/logger.rb +208 -0
  561. data/modules/mu/master/README.md +27 -0
  562. data/modules/mu/master/chef.rb +471 -0
  563. data/modules/mu/master/ldap.rb +1005 -0
  564. data/modules/mu/master.rb +415 -0
  565. data/modules/mu/mommacat.rb +2703 -0
  566. data/modules/mu-load-config.rb +1 -0
  567. data/modules/mu.rb +724 -0
  568. data/modules/scratchpad.erb +1 -0
  569. data/modules/tests/super_complex_bok.yml +41 -0
  570. data/modules/tests/super_simple_bok.yml +40 -0
  571. data/mu.gemspec +62 -0
  572. data/roles/demo-dbservice-configure.json +19 -0
  573. data/roles/demo-portal-configure.json +19 -0
  574. data/roles/mu-master-jenkins.json +24 -0
  575. data/roles/mu-master-nagios-only.json +13 -0
  576. data/roles/mu-master.json +12 -0
  577. data/roles/mu-node.json +19 -0
  578. data/roles/mu-splunk-server.json +13 -0
  579. data/roles/mu-splunk.json +13 -0
  580. data/test/clean_up.py +25 -0
  581. data/test/demo-test-profile/README.md +3 -0
  582. data/test/demo-test-profile/controls/flask.rb +84 -0
  583. data/test/demo-test-profile/inspec.lock +7 -0
  584. data/test/demo-test-profile/inspec.yml +11 -0
  585. data/test/etco-test-profile/README.md +3 -0
  586. data/test/etco-test-profile/controls/all-in-one.rb +182 -0
  587. data/test/etco-test-profile/inspec.lock +7 -0
  588. data/test/etco-test-profile/inspec.yml +11 -0
  589. data/test/exec_inspec.py +246 -0
  590. data/test/exec_mu_install.py +241 -0
  591. data/test/exec_retry.py +44 -0
  592. data/test/mu-master-test/README.md +3 -0
  593. data/test/mu-master-test/controls/all_in_one.rb +557 -0
  594. data/test/mu-master-test/inspec.lock +3 -0
  595. data/test/mu-master-test/inspec.yml +11 -0
  596. data/test/mu-tools-test/README.md +3 -0
  597. data/test/mu-tools-test/controls/base.rb +265 -0
  598. data/test/mu-tools-test/inspec.lock +3 -0
  599. data/test/mu-tools-test/inspec.yml +8 -0
  600. data/test/simple-server-php-test/README.md +3 -0
  601. data/test/simple-server-php-test/controls/apachephp.rb +25 -0
  602. data/test/simple-server-php-test/controls/example.rb +19 -0
  603. data/test/simple-server-php-test/inspec.lock +7 -0
  604. data/test/simple-server-php-test/inspec.yml +12 -0
  605. data/test/simple-server-rails-test/README.md +3 -0
  606. data/test/simple-server-rails-test/controls/rails.rb +188 -0
  607. data/test/simple-server-rails-test/inspec.lock +7 -0
  608. data/test/simple-server-rails-test/inspec.yml +11 -0
  609. data/test/simple-windows-test/README.md +3 -0
  610. data/test/simple-windows-test/controls/windows.rb +20 -0
  611. data/test/simple-windows-test/inspec.lock +7 -0
  612. data/test/simple-windows-test/inspec.yml +11 -0
  613. data/test/smoke_test.rb +75 -0
  614. data/test/wordpress-test/README.md +3 -0
  615. data/test/wordpress-test/controls/wordpress.rb +97 -0
  616. data/test/wordpress-test/inspec.lock +7 -0
  617. data/test/wordpress-test/inspec.yml +11 -0
  618. metadata +979 -0
@@ -0,0 +1,1836 @@
1
+ # Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ autoload :Net, 'net/ssh/gateway'
16
+
17
+ module MU
18
+ class Cloud
19
+ class AWS
20
+ # A database as configured in {MU::Config::BasketofKittens::databases}
21
+ class Database < MU::Cloud::Database
22
+ @deploy = nil
23
+ @config = nil
24
+ attr_reader :mu_name
25
+ attr_reader :cloud_id
26
+ attr_reader :config
27
+ attr_reader :groomer
28
+
29
+ @cloudformation_data = {}
30
+ attr_reader :cloudformation_data
31
+
32
+ # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
33
+ # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::databases}
34
+ def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
35
+ @deploy = mommacat
36
+ @config = MU::Config.manxify(kitten_cfg)
37
+ @cloud_id ||= cloud_id
38
+ # @mu_name = mu_name ? mu_name : @deploy.getResourceName(@config["name"])
39
+ @config["groomer"] = MU::Config.defaultGroomer unless @config["groomer"]
40
+ @groomclass = MU::Groomer.loadGroomer(@config["groomer"])
41
+
42
+ if !mu_name.nil?
43
+ @mu_name = mu_name
44
+ else
45
+ @mu_name ||=
46
+ if @config and @config['engine'] and @config["engine"].match(/^sqlserver/)
47
+ @deploy.getResourceName(@config["name"], max_length: 15)
48
+ else
49
+ @deploy.getResourceName(@config["name"], max_length: 63)
50
+ end
51
+
52
+ @mu_name.gsub(/(--|-$)/i, "").gsub(/(_)/, "-").gsub!(/^[^a-z]/i, "")
53
+ end
54
+ end
55
+
56
+ # Called automatically by {MU::Deploy#createResources}
57
+ # @return [String]: The cloud provider's identifier for this database instance.
58
+ def create
59
+ # RDS is picky, we can't just use our regular node names for things like
60
+ # the default schema or username. And it varies from engine to engine.
61
+ basename = @config["name"]+@deploy.timestamp+MU.seed.downcase
62
+ basename.gsub!(/[^a-z0-9]/i, "")
63
+ @config["db_name"] = MU::Cloud::AWS::Database.getName(basename, type: "dbname", config: @config)
64
+ @config['master_user'] = MU::Cloud::AWS::Database.getName(basename, type: "dbuser", config: @config) unless @config['master_user']
65
+
66
+ # Lets make sure automatic backups are enabled when DB instance is deployed in Multi-AZ so failover actually works. Maybe default to 1 instead?
67
+ if @config['multi_az_on_create'] or @config['multi_az_on_deploy'] or @config["create_cluster"]
68
+ if @config["backup_retention_period"].nil? or @config["backup_retention_period"] == 0
69
+ @config["backup_retention_period"] = 35
70
+ MU.log "Multi-AZ deployment specified but backup retention period disabled or set to 0. Changing to #{@config["backup_retention_period"]} ", MU::WARN
71
+ end
72
+
73
+ if @config["preferred_backup_window"].nil?
74
+ @config["preferred_backup_window"] = "05:00-05:30"
75
+ MU.log "Multi-AZ deployment specified but no backup window specified. Changing to #{@config["preferred_backup_window"]} ", MU::WARN
76
+ end
77
+ end
78
+
79
+ @config["snapshot_id"] =
80
+ if @config["creation_style"] == "existing_snapshot"
81
+ getExistingSnapshot ? getExistingSnapshot : createNewSnapshot
82
+ elsif @config["creation_style"] == "new_snapshot"
83
+ createNewSnapshot
84
+ end
85
+
86
+ @config['source_identifier'] = @config['identifier'] if @config["creation_style"] == "point_in_time"
87
+ @config['identifier'] = @mu_name unless @config["creation_style"] == "existing"
88
+ @config["subnet_group_name"] = @mu_name
89
+ MU.log "Using the database identifier #{@config['identifier']}"
90
+
91
+
92
+ if @config["create_cluster"]
93
+ getPassword
94
+ createSubnetGroup
95
+
96
+ if @config.has_key?("parameter_group_family")
97
+ @config["parameter_group_name"] = @config['identifier']
98
+ createDBClusterParameterGroup
99
+ end
100
+
101
+ @cloud_id = createDbCluster
102
+ elsif @config["add_cluster_node"]
103
+ cluster = nil
104
+ rr = @config["member_of_cluster"]
105
+ cluster = @deploy.findLitterMate(type: "database", name: rr['db_name']) if rr['db_name']
106
+
107
+ if cluster.nil?
108
+ tag_key, tag_value = rr['tag'].split(/=/, 2) if !rr['tag'].nil?
109
+ found = MU::MommaCat.findStray(
110
+ rr['cloud'],
111
+ "database",
112
+ deploy_id: rr["deploy_id"],
113
+ cloud_id: rr["db_id"],
114
+ tag_key: tag_key,
115
+ tag_value: tag_value,
116
+ region: rr["region"],
117
+ dummy_ok: true
118
+ )
119
+ cluster = found.first if found.size == 1
120
+ end
121
+
122
+ raise MuError, "Couldn't resolve cluster node reference to a unique live Database in #{@mu_name}" if cluster.nil? || cluster.cloud_id.nil?
123
+ @config['cluster_identifier'] = cluster.cloud_id.downcase
124
+ # We're overriding @config["subnet_group_name"] because we need each cluster member to use the cluster's subnet group instead of a unique subnet group
125
+ @config["subnet_group_name"] = @config['cluster_identifier']
126
+ @config["creation_style"] = "new" if @config["creation_style"] != "new"
127
+
128
+ if @config.has_key?("parameter_group_family")
129
+ @config["parameter_group_name"] = @config['identifier']
130
+ createDBParameterGroup
131
+ end
132
+
133
+ @cloud_id = createDb
134
+ else
135
+ source_db = nil
136
+ if @config['read_replica_of']
137
+ rr = @config['read_replica_of']
138
+ source_db = @deploy.findLitterMate(type: "database", name: rr['db_name']) if rr['db_name']
139
+
140
+ if source_db.nil?
141
+ tag_key, tag_value = rr['tag'].split(/=/, 2) if !rr['tag'].nil?
142
+ found = MU::MommaCat.findStray(
143
+ rr['cloud'],
144
+ "database",
145
+ deploy_id: rr["deploy_id"],
146
+ cloud_id: rr["db_id"],
147
+ tag_key: tag_key,
148
+ tag_value: tag_value,
149
+ region: rr["region"],
150
+ dummy_ok: true
151
+ )
152
+ source_db = found.first if found.size == 1
153
+ end
154
+
155
+ raise MuError, "Couldn't resolve read replica reference to a unique live Database in #{@mu_name}" if source_db.nil? or source_db.cloud_id.nil?
156
+ @config['source_identifier'] = source_db.cloud_id
157
+ end
158
+
159
+ getPassword
160
+ if source_db.nil? or @config['region'] != source_db.config['region']
161
+ createSubnetGroup
162
+ else
163
+ MU.log "Note: Read Replicas automatically reside in the same subnet group as the source database, if they're both in the same region. This replica may not land in the VPC you intended.", MU::WARN
164
+ end
165
+
166
+ if @config.has_key?("parameter_group_family")
167
+ @config["parameter_group_name"] = @config['identifier']
168
+ createDBParameterGroup
169
+ end
170
+
171
+ @cloud_id = createDb
172
+ end
173
+ end
174
+
175
+ # Canonical Amazon Resource Number for this resource
176
+ # @return [String]
177
+ def arn
178
+ cloud_desc.db_instance_arn
179
+ end
180
+
181
+
182
+ # Locate an existing Database or Databases and return an array containing matching AWS resource descriptors for those that match.
183
+ # @param cloud_id [String]: The cloud provider's identifier for this resource.
184
+ # @param region [String]: The cloud provider region
185
+ # @param tag_key [String]: A tag key to search.
186
+ # @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
187
+ # @param flags [Hash]: Optional flags
188
+ # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching Databases
189
+ def self.find(cloud_id: nil, region: MU.curRegion, tag_key: "Name", tag_value: nil, flags: {})
190
+ map = {}
191
+ if cloud_id
192
+ db = MU::Cloud::AWS::Database.getDatabaseById(cloud_id, region: region)
193
+ map[cloud_id] = db if db
194
+ end
195
+
196
+ if tag_value
197
+ MU::Cloud::AWS.rds(region).describe_db_instances.db_instances.each { |db|
198
+ resp = MU::Cloud::AWS.rds(region).list_tags_for_resource(
199
+ resource_name: MU::Cloud::AWS::Database.getARN(db.db_instance_identifier, "db", "rds", region: region)
200
+ )
201
+ if resp && resp.tag_list && !resp.tag_list.empty?
202
+ resp.tag_list.each { |tag|
203
+ map[db.db_instance_identifier] = db if tag.key == tag_key and tag.value == tag_value
204
+ }
205
+ end
206
+ }
207
+ end
208
+
209
+ return map
210
+ end
211
+
212
+ # Construct an Amazon Resource Name for an RDS resource. The RDS API is
213
+ # peculiar, and we often need this identifier in order to do things that
214
+ # the other APIs can do with shorthand.
215
+ # @param resource [String]: The name of the resource
216
+ # @param resource_type [String]: The type of the resource (one of `db, es, og, pg, ri, secgrp, snapshot, subgrp`)
217
+ # @param client_type [String]: The name of the client (eg. elasticache, rds, ec2, s3)
218
+ # @param region [String]: The region in which the resource resides.
219
+ # @param account_number [String]: The account in which the resource resides.
220
+ # @return [String]
221
+ def self.getARN(resource, resource_type, client_type, region: MU.curRegion, account_number: MU.account_number)
222
+ aws_str = MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws"
223
+ "arn:#{aws_str}:#{client_type}:#{region}:#{account_number}:#{resource_type}:#{resource}"
224
+ end
225
+
226
+ # Construct all our tags.
227
+ # @return [Array]: All our standard tags and any custom tags.
228
+ def allTags
229
+ tags = []
230
+ MU::MommaCat.listStandardTags.each_pair { |name, value|
231
+ tags << {key: name, value: value}
232
+ }
233
+
234
+ if @config['optional_tags']
235
+ MU::MommaCat.listOptionalTags.each_pair { |name, value|
236
+ tags << {key: name, value: value}
237
+ }
238
+ end
239
+
240
+ if @config['tags']
241
+ @config['tags'].each { |tag|
242
+ tags << {key: tag['key'], value: tag['value']}
243
+ }
244
+ end
245
+
246
+ return tags
247
+ end
248
+
249
+ # Add our standard tag set to an Amazon RDS resource.
250
+ # @param resource [String]: The name of the resource
251
+ # @param resource_type [String]: The type of the resource (one of `db, es, og, pg, ri, secgrp, snapshot, subgrp`)
252
+ # @param region [String]: The cloud provider region
253
+ def addStandardTags(resource, resource_type, region: MU.curRegion)
254
+ MU.log "Adding tags to RDS resource #{resource}: #{allTags}"
255
+ MU::Cloud::AWS.rds(region).add_tags_to_resource(
256
+ resource_name: MU::Cloud::AWS::Database.getARN(resource, resource_type, "rds", region: region),
257
+ tags: allTags
258
+ )
259
+ end
260
+
261
+ # Getting the password for the master user, and saving it in a database / cluster specif vault
262
+ def getPassword
263
+ if @config['password'].nil?
264
+ if @config['auth_vault'] && !@config['auth_vault'].empty?
265
+ @config['password'] = @groomclass.getSecret(
266
+ vault: @config['auth_vault']['vault'],
267
+ item: @config['auth_vault']['item'],
268
+ field: @config['auth_vault']['password_field']
269
+ )
270
+ else
271
+ # Should we use random instead?
272
+ @config['password'] = Password.pronounceable(10..12)
273
+ end
274
+ end
275
+
276
+ creds = {
277
+ "username" => @config["master_user"],
278
+ "password" => @config["password"]
279
+ }
280
+ @groomclass.saveSecret(vault: @mu_name, item: "database_credentials", data: creds)
281
+ end
282
+
283
+ # Create the database described in this instance
284
+ # @return [String]: The cloud provider's identifier for this database instance.
285
+ def createDb
286
+ # Shared configuration elements between most database creation styles
287
+ config = {
288
+ db_instance_identifier: @config['identifier'],
289
+ db_instance_class: @config["size"],
290
+ engine: @config["engine"],
291
+ auto_minor_version_upgrade: @config["auto_minor_version_upgrade"],
292
+ license_model: @config["license_model"],
293
+ db_subnet_group_name: @config["subnet_group_name"],
294
+ publicly_accessible: @config["publicly_accessible"],
295
+ copy_tags_to_snapshot: true,
296
+ tags: allTags
297
+ }
298
+
299
+ unless @config["add_cluster_node"]
300
+ config[:storage_type] = @config["storage_type"]
301
+ config[:port] = @config["port"] if @config["port"]
302
+ config[:iops] = @config["iops"] if @config['storage_type'] == "io1"
303
+ config[:multi_az] = @config['multi_az_on_create']
304
+ end
305
+
306
+ if @config["creation_style"] == "new"
307
+ unless @config["add_cluster_node"]
308
+ config[:preferred_backup_window] = @config["preferred_backup_window"]
309
+ config[:backup_retention_period] = @config["backup_retention_period"]
310
+ config[:storage_encrypted] = @config["storage_encrypted"]
311
+ config[:allocated_storage] = @config["storage"]
312
+ config[:db_name] = @config["db_name"]
313
+ config[:master_username] = @config['master_user']
314
+ config[:master_user_password] = @config['password']
315
+ config[:vpc_security_group_ids] = @config["vpc_security_group_ids"]
316
+ end
317
+
318
+ config[:engine_version] = @config["engine_version"]
319
+ config[:preferred_maintenance_window] = @config["preferred_maintenance_window"] if @config["preferred_maintenance_window"]
320
+ config[:db_parameter_group_name] = @config["parameter_group_name"] if @config["parameter_group_name"]
321
+ config[:db_cluster_identifier] = @config["cluster_identifier"] if @config["add_cluster_node"]
322
+ end
323
+
324
+ if %w{existing_snapshot new_snapshot}.include?(@config["creation_style"])
325
+ config[:db_snapshot_identifier] = @config["snapshot_id"]
326
+ end
327
+
328
+ if @config["creation_style"] == "point_in_time"
329
+ point_in_time_config = config
330
+ point_in_time_config.delete(:db_instance_identifier)
331
+ point_in_time_config[:source_db_instance_identifier] = @config['source_identifier']
332
+ point_in_time_config[:target_db_instance_identifier] = @config['identifier']
333
+ point_in_time_config[:restore_time] = @config['restore_time'] unless @config["restore_time"] == "latest"
334
+ point_in_time_config[:use_latest_restorable_time] = true if @config['restore_time'] == "latest"
335
+ end
336
+
337
+ if @config["read_replica_of"] || @config["create_read_replica"]
338
+ srcdb = @config['source_identifier']
339
+ if @config["read_replica_of"]["region"] and @config['region'] != @config["read_replica_of"]["region"]
340
+ srcdb = MU::Cloud::AWS::Database.getARN(@config['source_identifier'], "db", "rds", region: @config["read_replica_of"]["region"])
341
+ end
342
+ read_replica_struct = {
343
+ db_instance_identifier: @config['identifier'],
344
+ source_db_instance_identifier: srcdb,
345
+ db_instance_class: @config["size"],
346
+ auto_minor_version_upgrade: @config["auto_minor_version_upgrade"],
347
+ publicly_accessible: @config["publicly_accessible"],
348
+ tags: allTags,
349
+ db_subnet_group_name: @config["subnet_group_name"],
350
+ storage_type: @config["storage_type"]
351
+ }
352
+
353
+ read_replica_struct[:port] = @config["port"] if @config["port"]
354
+ read_replica_struct[:iops] = @config["iops"] if @config['storage_type'] == "io1"
355
+ end
356
+
357
+ # Creating DB instance
358
+ attempts = 0
359
+
360
+ begin
361
+ if %w{existing_snapshot new_snapshot}.include?(@config["creation_style"])
362
+ MU.log "Creating database instance #{@config['identifier']} from snapshot #{@config["snapshot_id"]}"
363
+ resp = MU::Cloud::AWS.rds(@config['region']).restore_db_instance_from_db_snapshot(config)
364
+ elsif @config["creation_style"] == "point_in_time"
365
+ MU.log "Creating database instance #{@config['identifier']} based on point in time backup #{@config['restore_time']} of #{@config['source_identifier']}"
366
+ resp = MU::Cloud::AWS.rds(@config['region']).restore_db_instance_to_point_in_time(point_in_time_config)
367
+ elsif @config["read_replica_of"]
368
+ MU.log "Creating read replica database instance #{@config['identifier']} for #{@config['source_identifier']}"
369
+ begin
370
+ resp = MU::Cloud::AWS.rds(@config['region']).create_db_instance_read_replica(read_replica_struct)
371
+ rescue Aws::RDS::Errors::DBSubnetGroupNotAllowedFault => e
372
+ MU.log "Being forced to use source database's subnet group: #{e.message}", MU::WARN
373
+ read_replica_struct.delete(:db_subnet_group_name)
374
+ resp = MU::Cloud::AWS.rds(@config['region']).create_db_instance_read_replica(read_replica_struct)
375
+ end
376
+ elsif @config["creation_style"] == "new"
377
+ MU.log "Creating database instance #{@config['identifier']}"
378
+ resp = MU::Cloud::AWS.rds(@config['region']).create_db_instance(config)
379
+ end
380
+ rescue Aws::RDS::Errors::InvalidParameterValue => e
381
+ if attempts < 5
382
+ MU.log "Got #{e.inspect} creating #{@config['identifier']}, will retry a few times in case of transient errors.", MU::WARN
383
+ attempts += 1
384
+ sleep 10
385
+ retry
386
+ else
387
+ raise MuError, "Exhausted retries trying to create database instance #{@config['identifier']}: e.inspect"
388
+ end
389
+ end
390
+
391
+ wait_start_time = Time.now
392
+ retries = 0
393
+
394
+ begin
395
+ MU::Cloud::AWS.rds(@config['region']).wait_until(:db_instance_available, db_instance_identifier: @config['identifier']) do |waiter|
396
+ # Does create_db_instance implement wait_until_available ?
397
+ waiter.max_attempts = nil
398
+ waiter.before_attempt do |attempts|
399
+ MU.log "Waiting for RDS database #{@config['identifier']} to be ready..", MU::NOTICE if attempts % 10 == 0
400
+ end
401
+ waiter.before_wait do |attempts, resp|
402
+ throw :success if resp.db_instances.first.db_instance_status == "available"
403
+ throw :failure if Time.now - wait_start_time > 3600
404
+ end
405
+ end
406
+ rescue Aws::Waiters::Errors::TooManyAttemptsError => e
407
+ raise MuError, "Waited #{(Time.now - wait_start_time).round/60*(retries+1)} minutes for #{@config['identifier']} to become available, giving up. #{e}" if retries > 2
408
+ wait_start_time = Time.now
409
+ retries += 1
410
+ retry
411
+ end
412
+
413
+ database = MU::Cloud::AWS::Database.getDatabaseById(@config['identifier'], region: @config['region'])
414
+ MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: database.db_instance_identifier, target: "#{database.endpoint.address}.", cloudclass: MU::Cloud::Database, sync_wait: @config['dns_sync_wait'])
415
+ MU.log "Database #{@config['name']} is at #{database.endpoint.address}", MU::SUMMARY
416
+ MU.log "knife vault show #{@config['auth_vault']['vault']} #{@config['auth_vault']['item']} for Database #{@config['name']} credentials", MU::SUMMARY
417
+
418
+ # If referencing an existing DB, insert this deploy's DB security group so it can access db
419
+ if @config["creation_style"] == 'existing'
420
+ vpc_sg_ids = []
421
+ database.vpc_security_groups.each { |vpc_sg|
422
+ vpc_sg_ids << vpc_sg.vpc_security_group_id
423
+ }
424
+
425
+ localdeploy_rule = @deploy.findLitterMate(type: "firewall_rule", name: "database"+@config['name'])
426
+ if localdeploy_rule.nil?
427
+ raise MU::MuError, "Database #{@config['name']} failed to find its generic security group 'database#{@config['name']}'"
428
+ end
429
+ MU.log "Found this deploy's DB security group: #{localdeploy_rule.cloud_id}", MU::DEBUG
430
+ vpc_sg_ids << localdeploy_rule.cloud_id
431
+ mod_config = Hash.new
432
+ mod_config[:vpc_security_group_ids] = vpc_sg_ids
433
+ mod_config[:db_instance_identifier] = @config["identifier"]
434
+
435
+ MU::Cloud::AWS.rds(@config['region']).modify_db_instance(mod_config)
436
+ MU.log "Modified database #{@config['identifier']} with new security groups: #{mod_config}", MU::NOTICE
437
+ end
438
+
439
+ # When creating from a snapshot, some of the create arguments aren't
440
+ # applicable- but we can apply them after the fact with a modify.
441
+ if %w{existing_snapshot new_snapshot point_in_time}.include?(@config["creation_style"]) or @config["read_replica_of"]
442
+ mod_config = Hash.new
443
+ if !@config["read_replica_of"]
444
+ mod_config[:preferred_backup_window] = @config["preferred_backup_window"]
445
+ mod_config[:backup_retention_period] = @config["backup_retention_period"]
446
+ mod_config[:engine_version] = @config["engine_version"]
447
+ mod_config[:allow_major_version_upgrade] = @config["allow_major_version_upgrade"] if @config['allow_major_version_upgrade']
448
+ mod_config[:db_parameter_group_name] = @config["parameter_group_name"] if @config["parameter_group_name"]
449
+ mod_config[:master_user_password] = @config['password']
450
+ mod_config[:allocated_storage] = @config["storage"] if @config["storage"]
451
+ end
452
+ mod_config[:db_instance_identifier] = database.db_instance_identifier
453
+ mod_config[:preferred_maintenance_window] = @config["preferred_maintenance_window"] if @config["preferred_maintenance_window"]
454
+ mod_config[:vpc_security_group_ids] = @config["vpc_security_group_ids"]
455
+ mod_config[:apply_immediately] = true
456
+
457
+ MU::Cloud::AWS.rds(@config['region']).modify_db_instance(mod_config)
458
+ wait_start_time = Time.now
459
+ retries = 0
460
+
461
+ begin
462
+ MU::Cloud::AWS.rds(@config['region']).wait_until(:db_instance_available, db_instance_identifier: @config['identifier']) do |waiter|
463
+ # Does create_db_instance implement wait_until_available ?
464
+ waiter.max_attempts = nil
465
+ waiter.before_attempt do |attempts|
466
+ MU.log "Waiting for RDS database #{@config['identifier'] } to be ready..", MU::NOTICE if attempts % 10 == 0
467
+ end
468
+ waiter.before_wait do |attempts, resp|
469
+ throw :success if resp.db_instances.first.db_instance_status == "available"
470
+ throw :failure if Time.now - wait_start_time > 2400
471
+ end
472
+ end
473
+ rescue Aws::Waiters::Errors::TooManyAttemptsError => e
474
+ raise MuError, "Waited #{(Time.now - wait_start_time).round/60*(retries+1)} minutes for #{@config['identifier']} to become available, giving up. #{e}" if retries > 2
475
+ wait_start_time = Time.now
476
+ retries += 1
477
+ retry
478
+ end
479
+ end
480
+
481
+ # Maybe wait for DB instance to be in available state. DB should still be writeable at this state
482
+ if @config['allow_major_version_upgrade'] && @config["creation_style"] == "new"
483
+ MU.log "Setting major database version upgrade on #{@config['identifier']}'"
484
+ MU::Cloud::AWS.rds(@config['region']).modify_db_instance(
485
+ db_instance_identifier: @config['identifier'],
486
+ apply_immediately: true,
487
+ allow_major_version_upgrade: true
488
+ )
489
+ end
490
+
491
+ MU.log "Database #{@config['identifier']} is ready to use"
492
+ return database.db_instance_identifier
493
+ end
494
+
495
+ # Create the database cluster described in this instance
496
+ # @return [String]: The cloud provider's identifier for this database cluster.
497
+ def createDbCluster
498
+ cluster_config_struct = {
499
+ db_cluster_identifier: @config['identifier'],
500
+ # downcasing @config["subnet_group_name"] becuase the API is choking on upper case.
501
+ db_subnet_group_name: @config["subnet_group_name"].downcase,
502
+ vpc_security_group_ids: @config["vpc_security_group_ids"],
503
+ tags: allTags
504
+ }
505
+ cluster_config_struct[:port] = @config["port"] if @config["port"]
506
+
507
+ if %w{existing_snapshot new_snapshot}.include?(@config["creation_style"])
508
+ cluster_config_struct[:snapshot_identifier] = @config["snapshot_id"]
509
+ cluster_config_struct[:engine] = @config["engine"]
510
+ cluster_config_struct[:engine_version] = @config["engine_version"]
511
+ cluster_config_struct[:database_name] = @config["db_name"]
512
+ end
513
+
514
+ if @config["creation_style"] == "new"
515
+ cluster_config_struct[:backup_retention_period] = @config["backup_retention_period"]
516
+ cluster_config_struct[:database_name] = @config["db_name"]
517
+ cluster_config_struct[:db_cluster_parameter_group_name] = @config["parameter_group_name"]
518
+ cluster_config_struct[:engine] = @config["engine"]
519
+ cluster_config_struct[:engine_version] = @config["engine_version"]
520
+ cluster_config_struct[:master_username] = @config["master_user"]
521
+ cluster_config_struct[:master_user_password] = @config["password"]
522
+ cluster_config_struct[:preferred_backup_window] = @config["preferred_backup_window"]
523
+ cluster_config_struct[:preferred_maintenance_window] = @config["preferred_maintenance_window"]
524
+ end
525
+
526
+ if @config["creation_style"] == "point_in_time"
527
+ cluster_config_struct[:source_db_cluster_identifier] = @config["source_identifier"]
528
+ cluster_config_struct[:restore_to_time] = @config["restore_time"] unless @config["restore_time"] == "latest"
529
+ cluster_config_struct[:use_latest_restorable_time] = true if @config["restore_time"] == "latest"
530
+ end
531
+
532
+ attempts = 0
533
+ begin
534
+ resp =
535
+ if @config["creation_style"] == "new"
536
+ MU.log "Creating new database cluster #{@config['identifier']}"
537
+ MU::Cloud::AWS.rds(@config['region']).create_db_cluster(cluster_config_struct)
538
+ elsif %w{existing_snapshot new_snapshot}.include?(@config["creation_style"])
539
+ MU.log "Creating new database cluster #{@config['identifier']} from snapshot #{@config["snapshot_id"]}"
540
+ MU::Cloud::AWS.rds(@config['region']).restore_db_cluster_from_snapshot(cluster_config_struct)
541
+ elsif @config["creation_style"] == "point_in_time"
542
+ MU.log "Creating new database cluster #{@config['identifier']} from point in time backup #{@config["restore_time"]} of #{@config["source_identifier"]}"
543
+ MU::Cloud::AWS.rds(@config['region']).restore_db_cluster_to_point_in_time(cluster_config_struct)
544
+ end
545
+ rescue Aws::RDS::Errors::InvalidParameterValue => e
546
+ if attempts < 5
547
+ MU.log "Got #{e.inspect} while creating database cluster #{@config['identifier']}, will retry a few times in case of transient errors.", MU::WARN
548
+ attempts += 1
549
+ sleep 10
550
+ retry
551
+ else
552
+ raise MuError, "Exhausted retries trying to create database cluster #{@config['identifier']}", MU::ERR, details: e.inspect
553
+ end
554
+ end
555
+
556
+ attempts = 0
557
+ loop do
558
+ MU.log "Waiting for #{@config['identifier']} to become available", MU::NOTICE if attempts % 5 == 0
559
+ attempts += 1
560
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(@config['identifier'], region: @config['region'])
561
+ break unless cluster.status != "available"
562
+ sleep 30
563
+ end
564
+
565
+ if %w{existing_snapshot new_snapshot point_in_time}.include?(@config["creation_style"])
566
+ modify_db_cluster_struct = {
567
+ db_cluster_identifier: @config['identifier'],
568
+ apply_immediately: true,
569
+ backup_retention_period: @config["backup_retention_period"],
570
+ db_cluster_parameter_group_name: @config["parameter_group_name"],
571
+ master_user_password: @config["password"],
572
+ preferred_backup_window: @config["preferred_backup_window"]
573
+ }
574
+
575
+ modify_db_cluster_struct[:preferred_maintenance_window] = @config["preferred_maintenance_window"] if @config["preferred_maintenance_window"]
576
+ MU::Cloud::AWS.rds(@config['region']).modify_db_cluster(modify_db_cluster_struct)
577
+
578
+ attempts = 0
579
+ loop do
580
+ MU.log "Waiting for #{@config['identifier']} to become available", MU::NOTICE if attempts % 5 == 0
581
+ attempts += 1
582
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(@config['identifier'], region: @config['region'])
583
+ break unless cluster.status != "available"
584
+ sleep 30
585
+ end
586
+ end
587
+
588
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(@config['identifier'], region: @config['region'])
589
+ MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: cluster.db_cluster_identifier, target: "#{cluster.endpoint}.", cloudclass: MU::Cloud::Database, sync_wait: @config['dns_sync_wait'])
590
+ return cluster.db_cluster_identifier
591
+ end
592
+
593
+ # Create a subnet group for a database.
594
+ def createSubnetGroup
595
+ # Finding subnets, creating security groups/adding holes, create subnet group
596
+ subnet_ids = []
597
+ vpc_id = nil
598
+ if @config['vpc'] and !@config['vpc'].empty?
599
+ raise MuError, "Didn't find the VPC specified in #{@config["vpc"]}" unless @vpc
600
+
601
+ vpc_id = @vpc.cloud_id
602
+ # Getting subnet IDs
603
+ subnets =
604
+ if @config["vpc"]["subnets"].empty?
605
+ @vpc.subnets
606
+ else
607
+ subnet_objects= []
608
+ @config["vpc"]["subnets"].each { |subnet|
609
+ sobj = @vpc.getSubnet(cloud_id: subnet["subnet_id"], name: subnet["subnet_name"])
610
+ if sobj.nil?
611
+ MU.log "Got nil result from @vpc.getSubnet(cloud_id: #{subnet["subnet_id"]}, name: #{subnet["subnet_name"]})", MU::WARN
612
+ else
613
+ subnet_objects << sobj
614
+ end
615
+ }
616
+ subnet_objects
617
+ end
618
+
619
+ subnets.each{ |subnet|
620
+ next if subnet.nil?
621
+ if @config["publicly_accessible"]
622
+ subnet_ids << subnet.cloud_id if !subnet.private?
623
+ elsif !@config["publicly_accessible"]
624
+ subnet_ids << subnet.cloud_id if subnet.private?
625
+ end
626
+ }
627
+ else
628
+ # If we didn't specify a VPC try to figure out if the account has a default VPC
629
+ vpc_id = nil
630
+ subnets = []
631
+ MU::Cloud::AWS.ec2(@config['region']).describe_vpcs.vpcs.each { |vpc|
632
+ if vpc.is_default
633
+ vpc_id = vpc.vpc_id
634
+ subnets = MU::Cloud::AWS.ec2(@config['region']).describe_subnets(
635
+ filters: [
636
+ {
637
+ name: "vpc-id",
638
+ values: [vpc_id]
639
+ }
640
+ ]
641
+ ).subnets
642
+ break
643
+ end
644
+ }
645
+
646
+ if !subnets.empty?
647
+ mu_subnets = []
648
+ subnets.each { |subnet|
649
+ subnet_ids << subnet.subnet_id
650
+ mu_subnets << {"subnet_id" => subnet.subnet_id}
651
+ }
652
+
653
+ @config['vpc'] = {
654
+ "vpc_id" => vpc_id,
655
+ "subnets" => mu_subnets
656
+ }
657
+ # Default VPC has only public subnets by default so setting publicly_accessible = true
658
+ @config["publicly_accessible"] = true
659
+ using_default_vpc = true
660
+ MU.log "Using default VPC for cache cluster #{@config['identifier']}"
661
+ end
662
+ end
663
+
664
+ if @config['creation_style'] == "existing"
665
+ srcdb = MU::Cloud::AWS.rds(@config['region']).describe_db_instances(
666
+ db_instance_identifier: @config['identifier']
667
+ )
668
+ srcdb_vpc = srcdb.db_instances.first.db_subnet_group.vpc_id
669
+ if srcdb_vpc != vpc_id
670
+ MU.log "#{self} is deploying into #{vpc_id}, but our source database, #{@config['identifier']}, is in #{srcdb_vpc}", MU::ERR
671
+ raise MuError, "Can't use 'existing' to deploy into a different VPC from the source database; try 'new_snapshot' instead"
672
+ end
673
+ end
674
+
675
+ if subnet_ids.empty?
676
+ raise MuError, "Couldn't find subnets in #{@vpc} to add to #{@config["subnet_group_name"]}. Make sure the subnets are valid and publicly_accessible is set correctly"
677
+ else
678
+ # Create subnet group
679
+ resp = MU::Cloud::AWS.rds(@config['region']).create_db_subnet_group(
680
+ db_subnet_group_name: @config["subnet_group_name"],
681
+ db_subnet_group_description: @config["subnet_group_name"],
682
+ subnet_ids: subnet_ids,
683
+ tags: allTags
684
+ )
685
+ @config["subnet_group_name"] = resp.db_subnet_group.db_subnet_group_name
686
+
687
+ if @dependencies.has_key?('firewall_rule')
688
+ @config["vpc_security_group_ids"] = []
689
+ @dependencies['firewall_rule'].values.each { |sg|
690
+ @config["vpc_security_group_ids"] << sg.cloud_id
691
+ }
692
+ end
693
+ end
694
+
695
+ # Find NAT and create holes in security groups.
696
+ if @config["vpc"]["nat_host_name"] || @config["vpc"]["nat_host_id"] || @config["vpc"]["nat_host_tag"] || @config["vpc"]["nat_host_ip"]
697
+ nat = @nat
698
+ if nat.is_a?(Struct) && nat.nat_gateway_id && nat.nat_gateway_id.start_with?("nat-")
699
+ MU.log "Using NAT Gateway, not modifying security groups"
700
+ else
701
+ nat_name, nat_conf, nat_deploydata = @nat.describe
702
+ @deploy.kittens['firewall_rules'].each_pair { |name, acl|
703
+ # XXX if a user doesn't set up dependencies correctly, this can die horribly on a NAT that's still in mid-creation. Fix this... possibly in the config parser.
704
+ if acl.config["admin"]
705
+ acl.addRule([nat_deploydata["private_ip_address"]], proto: "tcp")
706
+ acl.addRule([nat_deploydata["private_ip_address"]], proto: "udp")
707
+ break
708
+ end
709
+ }
710
+ end
711
+ end
712
+ end
713
+
714
+ # Create a database cluster parameter group.
715
+ def createDBClusterParameterGroup
716
+ MU.log "Creating a cluster parameter group #{@config["parameter_group_name"]}"
717
+
718
+ MU::Cloud::AWS.rds(@config['region']).create_db_cluster_parameter_group(
719
+ db_cluster_parameter_group_name: @config["parameter_group_name"],
720
+ db_parameter_group_family: @config["parameter_group_family"],
721
+ description: "Parameter group for #{@config["parameter_group_family"]}",
722
+ tags: allTags
723
+ )
724
+
725
+ if @config["cluster_parameter_group_parameters"] && !@config["cluster_parameter_group_parameters"].empty?
726
+ params = []
727
+ @config["cluster_parameter_group_parameters"].each { |item|
728
+ params << {parameter_name: item['name'], parameter_value: item['value'], apply_method: item['apply_method']}
729
+ }
730
+
731
+ MU.log "Modifiying cluster parameter group #{@config["parameter_group_name"]}"
732
+ MU::Cloud::AWS.rds(@config['region']).modify_db_cluster_parameter_group(
733
+ db_cluster_parameter_group_name: @config["parameter_group_name"],
734
+ parameters: params
735
+ )
736
+ end
737
+ end
738
+
739
+ # Create a database parameter group.
740
+ def createDBParameterGroup
741
+ MU.log "Creating a database parameter group #{@config["parameter_group_name"]}"
742
+ MU::Cloud::AWS.rds(@config['region']).create_db_parameter_group(
743
+ db_parameter_group_name: @config["parameter_group_name"],
744
+ db_parameter_group_family: @config["parameter_group_family"],
745
+ description: "Parameter group for #{@config["parameter_group_family"]}",
746
+ tags: allTags
747
+ )
748
+
749
+ if @config["db_parameter_group_parameters"] && !@config["db_parameter_group_parameters"].empty?
750
+ params = []
751
+ @config["db_parameter_group_parameters"].each { |item|
752
+ params << {parameter_name: item['name'], parameter_value: item['value'], apply_method: item['apply_method']}
753
+ }
754
+
755
+ MU.log "Modifiying database parameter group #{@config["parameter_group_name"]}"
756
+ MU::Cloud::AWS.rds(@config['region']).modify_db_parameter_group(
757
+ db_parameter_group_name: @config["parameter_group_name"],
758
+ parameters: params
759
+ )
760
+ end
761
+ end
762
+
763
+ # Retrieve a complete description of a database cluster parameter group.
764
+ # @param param_group_id [String]: The cloud provider's identifier for this parameter group.
765
+ # @param region [String]: The cloud provider region
766
+ # @return [OpenStruct]
767
+ def self.getDBClusterParameterGroup(param_group_id, region: MU.curRegion)
768
+ MU::Cloud::AWS.rds(region).describe_db_cluster_parameter_groups(db_cluster_parameter_group_name: param_group_id).db_cluster_parameter_groups.first
769
+ # rescue DBClusterParameterGroupNotFound => e
770
+ # Of course the API will return DBParameterGroupNotFound instead of the documented DBClusterParameterGroupNotFound error.
771
+ rescue Aws::RDS::Errors::DBParameterGroupNotFound => e
772
+ #we're fine returning nil
773
+ end
774
+
775
+ # Retrieve a complete description of a database parameter group.
776
+ # @param param_group_id [String]: The cloud provider's identifier for this parameter group.
777
+ # @param region [String]: The cloud provider region
778
+ # @return [OpenStruct]
779
+ def self.getDBParameterGroup(param_group_id, region: MU.curRegion)
780
+ MU::Cloud::AWS.rds(region).describe_db_parameter_groups(db_parameter_group_name: param_group_id).db_parameter_groups.first
781
+ rescue Aws::RDS::Errors::DBParameterGroupNotFound => e
782
+ #we're fine returning nil
783
+ end
784
+
785
+ # Retrieve a complete description of a database subnet group.
786
+ # @param subnet_id [String]: The cloud provider's identifier for this subnet group.
787
+ # @param region [String]: The cloud provider region
788
+ # @return [OpenStruct]
789
+ def self.getSubnetGroup(subnet_id, region: MU.curRegion)
790
+ MU::Cloud::AWS.rds(region).describe_db_subnet_groups(db_subnet_group_name: subnet_id).db_subnet_groups.first
791
+ rescue Aws::RDS::Errors::DBSubnetGroupNotFoundFault => e
792
+ #we're fine returning nil
793
+ end
794
+
795
+ # Called automatically by {MU::Deploy#createResources}
796
+ def groom
797
+ unless @config["create_cluster"]
798
+ database = MU::Cloud::AWS::Database.getDatabaseById(@config['identifier'], region: @config['region'])
799
+
800
+ # Run SQL on deploy
801
+ if @config['run_sql_on_deploy']
802
+ MU.log "Running initial SQL commands on #{@config['name']}", details: @config['run_sql_on_deploy']
803
+
804
+ # check if DB is private or public
805
+ if !database.publicly_accessible
806
+ # This doesn't necessarily mean what we think it does. publicly_accessible = true means resolve to public address.
807
+ # publicly_accessible can still be set to true even when only private subnets are included in the subnet group. We try to solve this during creation.
808
+ is_private = true
809
+ else
810
+ is_private = false
811
+ end
812
+
813
+ #Setting up connection params
814
+ ssh_keydir = Etc.getpwuid(Process.uid).dir+"/.ssh"
815
+ keypairname, ssh_private_key, ssh_public_key = @deploy.SSHKey
816
+ if is_private and @vpc
817
+ if @config['vpc']['nat_host_name']
818
+ begin
819
+ proxy_cmd = "ssh -q -o StrictHostKeyChecking=no -W %h:%p #{nat_ssh_user}@#{nat_host_name}"
820
+ gateway = Net::SSH::Gateway.new(
821
+ @config['vpc']['nat_host_name'],
822
+ @config['vpc']['nat_ssh_user'],
823
+ :keys => [ssh_keydir+"/"+keypairname],
824
+ :keys_only => true,
825
+ :auth_methods => ['publickey'],
826
+ # :verbose => :info
827
+ )
828
+ port = gateway.open(database.endpoint.address, database.endpoint.port)
829
+ address = "127.0.0.1"
830
+ MU.log "Tunneling #{@config['engine']} connection through #{nat_host_name} via local port #{port}", MU::DEBUG
831
+ rescue IOError => e
832
+ MU.log "Got #{e.inspect} while connecting to #{@config['identifier']} through NAT #{nat_host_name}", MU::ERR
833
+ end
834
+ else
835
+ MU.log "Can't run initial SQL commands! Database #{@config['identifier']} is not publicly accessible, but we have no NAT host for connecting to it", MU::WARN, details: @config['run_sql_on_deploy']
836
+ end
837
+ else
838
+ port = database.endpoint.port
839
+ address = database.endpoint.address
840
+ end
841
+
842
+ # Running SQL on deploy
843
+ if @config['engine'] == "postgres"
844
+ autoload :PG, 'pg'
845
+ begin
846
+ conn = PG::Connection.new(
847
+ :host => address,
848
+ :port => port,
849
+ :user => @config['master_user'],
850
+ :dbname => database.db_name,
851
+ :password => @config['password']
852
+ )
853
+ @config['run_sql_on_deploy'].each { |cmd|
854
+ MU.log "Running #{cmd} on database #{@config['name']}"
855
+ conn.exec(cmd)
856
+ }
857
+ conn.finish
858
+ rescue PG::Error => e
859
+ MU.log "Failed to run initial SQL commands on #{@config['name']} via #{address}:#{port}: #{e.inspect}", MU::WARN, details: conn
860
+ end
861
+ elsif @config['engine'] == "mysql"
862
+ autoload :Mysql, 'mysql'
863
+ MU.log "Initiating mysql connection to #{address}:#{port} as #{@config['master_user']}"
864
+ conn = Mysql.new(address, @config['master_user'], @config['password'], "mysql", port)
865
+ @config['run_sql_on_deploy'].each { |cmd|
866
+ MU.log "Running #{cmd} on database #{@config['name']}"
867
+ conn.query(cmd)
868
+ }
869
+ conn.close
870
+ end
871
+
872
+ # close the SQL on deploy sessions
873
+ if is_private
874
+ begin
875
+ gateway.close(port)
876
+ rescue IOError => e
877
+ MU.log "Failed to close ssh session to NAT after running sql_on_deploy", MU::ERR, details: e.inspect
878
+ end
879
+ end
880
+ end
881
+
882
+ # set multi-az on deploy
883
+ if @config['multi_az_on_deploy']
884
+ if !database.multi_az
885
+ MU.log "Setting multi-az on #{@config['identifier']}"
886
+ attempts = 0
887
+ begin
888
+ MU::Cloud::AWS.rds(@config['region']).modify_db_instance(
889
+ db_instance_identifier: @config['identifier'],
890
+ apply_immediately: true,
891
+ multi_az: true
892
+ )
893
+ rescue Aws::RDS::Errors::InvalidParameterValue, Aws::RDS::Errors::InvalidDBInstanceState => e
894
+ if attempts < 15
895
+ MU.log "Got #{e.inspect} while setting Multi-AZ on #{@config['identifier']}, retrying."
896
+ attempts += 1
897
+ sleep 15
898
+ retry
899
+ else
900
+ MU.log "Couldn't set Multi-AZ on #{@config['identifier']} after several retries, giving up. #{e.inspect}", MU::ERR
901
+ end
902
+ end
903
+ end
904
+ end
905
+ end
906
+ end
907
+
908
+ # Generate database user, database identifier, database name based on engine-specific constraints
909
+ # @return [String]: Name
910
+ def self.getName(basename, type: 'dbname', config: nil)
911
+ if type == 'dbname'
912
+ # Apply engine-specific db name constraints
913
+ if config["engine"].match(/^oracle/)
914
+ (MU.seed.downcase+config["name"])[0..7]
915
+ elsif config["engine"].match(/^sqlserver/)
916
+ nil
917
+ elsif config["engine"].match(/^mysql/)
918
+ basename[0..63]
919
+ elsif config["engine"].match(/^aurora/)
920
+ (MU.seed.downcase+config["name"])[0..7]
921
+ else
922
+ basename
923
+ end
924
+ elsif type == 'dbuser'
925
+ # Apply engine-specific master username constraints
926
+ if config["engine"].match(/^oracle/)
927
+ basename[0..29].gsub(/[^a-z0-9]/i, "")
928
+ elsif config["engine"].match(/^sqlserver/)
929
+ basename[0..127].gsub(/[^a-z0-9]/i, "")
930
+ elsif config["engine"].match(/^mysql/)
931
+ basename[0..15].gsub(/[^a-z0-9]/i, "")
932
+ elsif config["engine"].match(/^aurora/)
933
+ basename[0..15].gsub(/[^a-z0-9]/i, "")
934
+ else
935
+ basename.gsub(/[^a-z0-9]/i, "")
936
+ end
937
+ end
938
+ end
939
+
940
+ # Permit a host to connect to the given database instance.
941
+ # @param cidr [String]: The CIDR-formatted IP address or block to allow access.
942
+ # @return [void]
943
+ def allowHost(cidr)
944
+ # If we're an old, Classic-style database with RDS-specific
945
+ # authorization, punch holes in that.
946
+ if !cloud_desc.db_security_groups.empty?
947
+ cloud_desc.db_security_groups.each { |rds_sg|
948
+ begin
949
+ MU::Cloud::AWS.rds(@config['region']).authorize_db_security_group_ingress(
950
+ db_security_group_name: rds_sg.db_security_group_name,
951
+ cidrip: cidr
952
+ )
953
+ rescue Aws::RDS::Errors::AuthorizationAlreadyExists => e
954
+ MU.log "CIDR #{cidr} already in database instance #{@cloud_id} security group", MU::WARN
955
+ end
956
+ }
957
+ end
958
+
959
+ # Otherwise go get our generic EC2 ruleset and punch a hole in it
960
+ if @dependencies.has_key?('firewall_rule')
961
+ @dependencies['firewall_rule'].values.each { |sg|
962
+ sg.addRule([cidr], proto: "tcp", port: cloud_desc.endpoint.port)
963
+ break
964
+ }
965
+ end
966
+ end
967
+
968
+ # Retrieve the complete cloud provider description of a database instance.
969
+ # @param db_id [String]: The cloud provider's identifier for this database.
970
+ # @param region [String]: The cloud provider region
971
+ # @return [OpenStruct]
972
+ def self.getDatabaseById(db_id, region: MU.curRegion)
973
+ raise MuError, "You must provide a db_id" if db_id.nil?
974
+ MU::Cloud::AWS.rds(region).describe_db_instances(db_instance_identifier: db_id).db_instances.first
975
+ rescue Aws::RDS::Errors::DBInstanceNotFound => e
976
+ # We're fine with this returning nil when searching for a database instance the doesn't exist.
977
+ end
978
+
979
+ # Retrieve the complete cloud provider description of a database cluster.
980
+ # @param db_cluster_id [String]: The cloud provider's identifier for this database cluster.
981
+ # @param region [String]: The cloud provider region
982
+ # @return [OpenStruct]
983
+ def self.getDatabaseClusterById(db_cluster_id, region: MU.curRegion)
984
+ MU::Cloud::AWS.rds(region).describe_db_clusters(db_cluster_identifier: db_cluster_id).db_clusters.first
985
+ rescue Aws::RDS::Errors::DBClusterNotFoundFault => e
986
+ # We're fine with this returning nil when searching for a database cluster the doesn't exist.
987
+ end
988
+
989
+ # Register a description of this database instance with this deployment's metadata.
990
+ # Register read replicas as separate instances, while we're
991
+ # at it.
992
+ def notify
993
+ my_dbs = [@config]
994
+ if @config['read_replica']
995
+ @config['read_replica']['creation_style'] = "read_replica"
996
+ @config['read_replica']['password'] = @config["password"]
997
+ my_dbs << @config['read_replica']
998
+ end
999
+
1000
+ deploy_struct = {}
1001
+ my_dbs.each { |db|
1002
+ deploy_struct =
1003
+ if db["create_cluster"]
1004
+ db["identifier"] = @mu_name.downcase if db["identifier"].nil?
1005
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(db["identifier"], region: db['region'])
1006
+ # DNS records for the "real" zone should always be registered as late as possible so override_existing only overwrites the records after the resource is ready to use.
1007
+ if db['dns_records']
1008
+ db['dns_records'].each { |dnsrec|
1009
+ dnsrec['name'] = cluster.db_cluster_identifier if !dnsrec.has_key?('name')
1010
+ dnsrec['name'] = "#{dnsrec['name']}.#{MU.environment.downcase}" if dnsrec["append_environment_name"] && !dnsrec['name'].match(/\.#{MU.environment.downcase}$/)
1011
+ }
1012
+ end
1013
+ # XXX this should be a call to @deploy.nameKitten
1014
+ MU::Cloud::AWS::DNSZone.createRecordsFromConfig(db['dns_records'], target: cluster.endpoint)
1015
+
1016
+ vpc_sg_ids = []
1017
+ cluster.vpc_security_groups.each { |vpc_sg|
1018
+ vpc_sg_ids << vpc_sg.vpc_security_group_id
1019
+ }
1020
+
1021
+ {
1022
+ "allocated_storage" => cluster.allocated_storage,
1023
+ "parameter_group" => cluster.db_cluster_parameter_group,
1024
+ "subnet_group" => cluster.db_subnet_group,
1025
+ "identifier" => cluster.db_cluster_identifier,
1026
+ "region" => db['region'],
1027
+ "engine" => cluster.engine,
1028
+ "engine_version" => cluster.engine_version,
1029
+ "backup_retention_period" => cluster.backup_retention_period,
1030
+ "preferred_backup_window" => cluster.preferred_backup_window,
1031
+ "preferred_maintenance_window" => cluster.preferred_maintenance_window,
1032
+ "endpoint" => cluster.endpoint,
1033
+ "port" => cluster.port,
1034
+ "username" => cluster.master_username,
1035
+ "vpc_sgs" => vpc_sg_ids,
1036
+ "azs" => cluster.availability_zones,
1037
+ "vault_name" => cluster.db_cluster_identifier.upcase,
1038
+ "vault_item" => "database_credentials",
1039
+ "password_field" => "password",
1040
+ "create_style" => db['creation_style'],
1041
+ "db_name" => cluster.database_name,
1042
+ "db_cluster_members" => cluster.db_cluster_members
1043
+ }
1044
+ else
1045
+ db["identifier"] = @mu_name.downcase if db["identifier"].nil? # Is this still valid if we have read replicas?
1046
+ database = MU::Cloud::AWS::Database.getDatabaseById(db["identifier"], region: db['region'])
1047
+ # DNS records for the "real" zone should always be registered as late as possible so override_existing only overwrites the records after the resource is ready to use.
1048
+ unless db["add_cluster_node"]
1049
+ # It isn't necessarily clear what we should do with DNS records of cluster members. Probably need to expose this to the BoK somehow.
1050
+ if db['dns_records']
1051
+ db['dns_records'].each { |dnsrec|
1052
+ dnsrec['name'] = database.db_instance_identifier if !dnsrec.has_key?('name')
1053
+ dnsrec['name'] = "#{dnsrec['name']}.#{MU.environment.downcase}" if dnsrec["append_environment_name"] && !dnsrec['name'].match(/\.#{MU.environment.downcase}$/)
1054
+ }
1055
+ # XXX this should be a call to @deploy.nameKitten
1056
+ MU::Cloud::AWS::DNSZone.createRecordsFromConfig(db['dns_records'], target: database.endpoint.address)
1057
+ end
1058
+ end
1059
+
1060
+ database = cloud_desc
1061
+
1062
+ vpc_sg_ids = Array.new
1063
+ database.vpc_security_groups.each { |vpc_sg|
1064
+ vpc_sg_ids << vpc_sg.vpc_security_group_id
1065
+ }
1066
+
1067
+ rds_sg_ids = Array.new
1068
+ database.db_security_groups.each { |rds_sg|
1069
+ rds_sg_ids << rds_sg.db_security_group_name
1070
+ }
1071
+
1072
+ subnet_ids = []
1073
+ if database.db_subnet_group and database.db_subnet_group.subnets
1074
+ database.db_subnet_group.subnets.each { |subnet|
1075
+ subnet_ids << subnet.subnet_identifier
1076
+ }
1077
+ end
1078
+
1079
+ {
1080
+ "identifier" => database.db_instance_identifier,
1081
+ "region" => db['region'],
1082
+ "engine" => database.engine,
1083
+ "engine_version" => database.engine_version,
1084
+ "backup_retention_period" => database.backup_retention_period,
1085
+ "preferred_backup_window" => database.preferred_backup_window,
1086
+ "preferred_maintenance_window" => database.preferred_maintenance_window,
1087
+ "auto_minor_version_upgrade" => database.auto_minor_version_upgrade,
1088
+ "storage_encrypted" => database.storage_encrypted,
1089
+ "endpoint" => database.endpoint.address,
1090
+ "port" => database.endpoint.port,
1091
+ "username" => database.master_username,
1092
+ "rds_sgs" => rds_sg_ids,
1093
+ "vpc_sgs" => vpc_sg_ids,
1094
+ "az" => database.availability_zone,
1095
+ "vault_name" => database.db_instance_identifier.upcase,
1096
+ "vault_item" => "database_credentials",
1097
+ "password_field" => "password",
1098
+ "create_style" => db['creation_style'],
1099
+ "db_name" => database.db_name,
1100
+ "multi_az" => database.multi_az,
1101
+ "publicly_accessible" => database.publicly_accessible,
1102
+ "ca_certificate_identifier" => database.ca_certificate_identifier,
1103
+ "subnets" => subnet_ids,
1104
+ "read_replica_source_db" => database.read_replica_source_db_instance_identifier,
1105
+ "read_replica_instance_identifiers" => database.read_replica_db_instance_identifiers,
1106
+ "cluster_identifier" => database.db_cluster_identifier,
1107
+ "size" => database.db_instance_class,
1108
+ "storage" => database.allocated_storage
1109
+ }
1110
+ end
1111
+ MU.log "Deploy structure is now #{deploy_struct}", MU::DEBUG
1112
+ }
1113
+
1114
+ raise MuError, "Can't find any deployment metadata" if deploy_struct.empty?
1115
+ return deploy_struct
1116
+ end
1117
+
1118
+ # Generate a snapshot from the database described in this instance.
1119
+ # @return [String]: The cloud provider's identifier for the snapshot.
1120
+ def createNewSnapshot
1121
+ snap_id = @deploy.getResourceName(@config["name"]) + Time.new.strftime("%M%S").to_s
1122
+
1123
+ attempts = 0
1124
+ begin
1125
+ snapshot =
1126
+ if @config["create_cluster"]
1127
+ MU::Cloud::AWS.rds(@config['region']).create_db_cluster_snapshot(
1128
+ db_cluster_snapshot_identifier: snap_id,
1129
+ db_cluster_identifier: @config["identifier"],
1130
+ tags: allTags
1131
+ )
1132
+ else
1133
+ MU::Cloud::AWS.rds(@config['region']).create_db_snapshot(
1134
+ db_snapshot_identifier: snap_id,
1135
+ db_instance_identifier: @config["identifier"],
1136
+ tags: allTags
1137
+ )
1138
+ end
1139
+ rescue Aws::RDS::Errors::InvalidDBInstanceState, Aws::RDS::Errors::InvalidDBClusterStateFault => e
1140
+ raise MuError, e.inspect if attempts >= 10
1141
+ attempts += 1
1142
+ sleep 60
1143
+ retry
1144
+ end
1145
+
1146
+ attempts = 0
1147
+ loop do
1148
+ MU.log "Waiting for RDS snapshot of #{@config["identifier"]} to be ready...", MU::NOTICE if attempts % 20 == 0
1149
+ MU.log "Waiting for RDS snapshot of #{@config["identifier"]} to be ready...", MU::DEBUG
1150
+ snapshot_resp =
1151
+ if @config["create_cluster"]
1152
+ MU::Cloud::AWS.rds(@config['region']).describe_db_cluster_snapshots(db_cluster_snapshot_identifier: snap_id)
1153
+ else
1154
+ MU::Cloud::AWS.rds(@config['region']).describe_db_snapshots(db_snapshot_identifier: snap_id)
1155
+ end
1156
+
1157
+ if @config["create_cluster"]
1158
+ break unless snapshot_resp.db_cluster_snapshots.first.status != "available"
1159
+ else
1160
+ break unless snapshot_resp.db_snapshots.first.status != "available"
1161
+ end
1162
+ attempts += 1
1163
+ sleep 15
1164
+ end
1165
+
1166
+ return snap_id
1167
+ end
1168
+
1169
+ # Fetch the latest snapshot of the database described in this instance.
1170
+ # @return [String]: The cloud provider's identifier for the snapshot.
1171
+ def getExistingSnapshot
1172
+ resp =
1173
+ if @config["create_cluster"]
1174
+ MU::Cloud::AWS.rds(@config['region']).describe_db_cluster_snapshots(db_cluster_snapshot_identifier: @config["identifier"])
1175
+ else
1176
+ MU::Cloud::AWS.rds(@config['region']).describe_db_snapshots(db_snapshot_identifier: @config["identifier"])
1177
+ end
1178
+
1179
+ snapshots = @config["create_cluster"] ? resp.db_cluster_snapshots : resp.db_snapshots
1180
+
1181
+ if snapshots.empty?
1182
+ nil
1183
+ else
1184
+ sorted_snapshots = snapshots.sort_by { |snap| snap.snapshot_create_time }
1185
+ @config["create_cluster"] ? sorted_snapshots.last.db_cluster_snapshot_identifier : sorted_snapshots.last.db_snapshot_identifier
1186
+ end
1187
+ end
1188
+
1189
+ # Called by {MU::Cleanup}. Locates resources that were created by the
1190
+ # currently-loaded deployment, and purges them.
1191
+ # @param noop [Boolean]: If true, will only print what would be done
1192
+ # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1193
+ # @param region [String]: The cloud provider region in which to operate
1194
+ # @return [void]
1195
+ def self.cleanup(skipsnapshots: false, noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
1196
+ resp = MU::Cloud::AWS.rds(region).describe_db_instances
1197
+ threads = []
1198
+
1199
+ resp.db_instances.each { |db|
1200
+ db_id = db.db_instance_identifier
1201
+ arn = MU::Cloud::AWS::Database.getARN(db.db_instance_identifier, "db", "rds", region: region)
1202
+ tags = MU::Cloud::AWS.rds(region).list_tags_for_resource(resource_name: arn).tag_list
1203
+
1204
+ found_muid = false
1205
+ found_master = false
1206
+ tags.each { |tag|
1207
+ found_muid = true if tag.key == "MU-ID" && tag.value == MU.deploy_id
1208
+ found_master = true if tag.key == "MU-MASTER-IP" && tag.value == MU.mu_public_ip
1209
+ }
1210
+ next if !found_muid
1211
+
1212
+ delete =
1213
+ if ignoremaster && found_muid
1214
+ true
1215
+ elsif !ignoremaster && found_muid && found_master
1216
+ true
1217
+ else
1218
+ false
1219
+ end
1220
+
1221
+ if delete
1222
+ parent_thread_id = Thread.current.object_id
1223
+ threads << Thread.new(db) { |mydb|
1224
+ MU.dupGlobals(parent_thread_id)
1225
+ Thread.abort_on_exception = true
1226
+ MU::Cloud::AWS::Database.terminate_rds_instance(mydb, noop: noop, skipsnapshots: skipsnapshots, region: region, deploy_id: MU.deploy_id, cloud_id: db.db_instance_identifier, mu_name: db.db_instance_identifier.upcase)
1227
+ }
1228
+ end
1229
+ }
1230
+
1231
+ # Wait for all of the databases to finish cleanup before proceeding
1232
+ threads.each { |t|
1233
+ t.join
1234
+ }
1235
+
1236
+ # Cleanup database clusters
1237
+ threads = []
1238
+ resp = MU::Cloud::AWS.rds(region).describe_db_clusters
1239
+ resp.db_clusters.each { |cluster|
1240
+ cluster_id = cluster.db_cluster_identifier
1241
+ arn = MU::Cloud::AWS::Database.getARN(cluster_id, "cluster", "rds", region: region)
1242
+ tags = MU::Cloud::AWS.rds(region).list_tags_for_resource(resource_name: arn).tag_list
1243
+
1244
+ found_muid = false
1245
+ found_master = false
1246
+ tags.each { |tag|
1247
+ found_muid = true if tag.key == "MU-ID" && tag.value == MU.deploy_id
1248
+ found_master = true if tag.key == "MU-MASTER-IP" && tag.value == MU.mu_public_ip
1249
+ }
1250
+ next if !found_muid
1251
+
1252
+ delete =
1253
+ if ignoremaster && found_muid
1254
+ true
1255
+ elsif !ignoremaster && found_muid && found_master
1256
+ true
1257
+ else
1258
+ false
1259
+ end
1260
+
1261
+ if delete
1262
+ parent_thread_id = Thread.current.object_id
1263
+ threads << Thread.new(cluster) { |mydbcluster|
1264
+ MU.dupGlobals(parent_thread_id)
1265
+ Thread.abort_on_exception = true
1266
+ MU::Cloud::AWS::Database.terminate_rds_cluster(mydbcluster, noop: noop, skipsnapshots: skipsnapshots, region: region, deploy_id: MU.deploy_id, cloud_id: cluster_id, mu_name: cluster_id.upcase)
1267
+ }
1268
+ end
1269
+ }
1270
+
1271
+ # Wait for all of the database clusters to finish cleanup before proceeding
1272
+ threads.each { |t|
1273
+ t.join
1274
+ }
1275
+
1276
+ threads = []
1277
+ # Cleanup database subnet group
1278
+ MU::Cloud::AWS.rds(region).describe_db_subnet_groups.db_subnet_groups.each { |sub_group|
1279
+ sub_group_id = sub_group.db_subnet_group_name
1280
+ arn = MU::Cloud::AWS::Database.getARN(sub_group_id, "subgrp", "rds", region: region)
1281
+ tags = MU::Cloud::AWS.rds(region).list_tags_for_resource(resource_name: arn).tag_list
1282
+
1283
+ found_muid = false
1284
+ found_master = false
1285
+ tags.each { |tag|
1286
+ found_muid = true if tag.key == "MU-ID" && tag.value == MU.deploy_id
1287
+ found_master = true if tag.key == "MU-MASTER-IP" && tag.value == MU.mu_public_ip
1288
+ }
1289
+ next if !found_muid
1290
+
1291
+ delete =
1292
+ if ignoremaster && found_muid
1293
+ true
1294
+ elsif !ignoremaster && found_muid && found_master
1295
+ true
1296
+ else
1297
+ false
1298
+ end
1299
+
1300
+ if delete
1301
+ parent_thread_id = Thread.current.object_id
1302
+ threads << Thread.new(sub_group) { |mysubgroup|
1303
+ MU.dupGlobals(parent_thread_id)
1304
+ Thread.abort_on_exception = true
1305
+ MU::Cloud::AWS::Database.delete_subnet_group(sub_group_id, region: region) unless noop
1306
+ }
1307
+ end
1308
+ }
1309
+
1310
+ # Cleanup database parameter group
1311
+ MU::Cloud::AWS.rds(region).describe_db_parameter_groups.db_parameter_groups.each { |param_group|
1312
+ param_group_id = param_group.db_parameter_group_name
1313
+ arn = MU::Cloud::AWS::Database.getARN(param_group_id, "pg", "rds", region: region)
1314
+ tags = MU::Cloud::AWS.rds(region).list_tags_for_resource(resource_name: arn).tag_list
1315
+
1316
+ found_muid = false
1317
+ found_master = false
1318
+ tags.each { |tag|
1319
+ found_muid = true if tag.key == "MU-ID" && tag.value == MU.deploy_id
1320
+ found_master = true if tag.key == "MU-MASTER-IP" && tag.value == MU.mu_public_ip
1321
+ }
1322
+ next if !found_muid
1323
+
1324
+ delete =
1325
+ if ignoremaster && found_muid
1326
+ true
1327
+ elsif !ignoremaster && found_muid && found_master
1328
+ true
1329
+ else
1330
+ false
1331
+ end
1332
+
1333
+ if delete
1334
+ parent_thread_id = Thread.current.object_id
1335
+ threads << Thread.new(param_group) { |myparamgroup|
1336
+ MU.dupGlobals(parent_thread_id)
1337
+ Thread.abort_on_exception = true
1338
+ MU::Cloud::AWS::Database.delete_db_parameter_group(param_group_id, region: region) unless noop
1339
+ }
1340
+ end
1341
+ }
1342
+
1343
+ # Cleanup database cluster parameter group
1344
+ MU::Cloud::AWS.rds(region).describe_db_cluster_parameter_groups.db_cluster_parameter_groups.each { |param_group|
1345
+ param_group_id = param_group.db_cluster_parameter_group_name
1346
+ arn = MU::Cloud::AWS::Database.getARN(param_group_id, "cluster-pg", "rds", region: region)
1347
+ tags = MU::Cloud::AWS.rds(region).list_tags_for_resource(resource_name: arn).tag_list
1348
+
1349
+ found_muid = false
1350
+ found_master = false
1351
+ tags.each { |tag|
1352
+ found_muid = true if tag.key == "MU-ID" && tag.value == MU.deploy_id
1353
+ found_master = true if tag.key == "MU-MASTER-IP" && tag.value == MU.mu_public_ip
1354
+ }
1355
+ next if !found_muid
1356
+
1357
+ delete =
1358
+ if ignoremaster && found_muid
1359
+ true
1360
+ elsif !ignoremaster && found_muid && found_master
1361
+ true
1362
+ else
1363
+ false
1364
+ end
1365
+
1366
+ if delete
1367
+ parent_thread_id = Thread.current.object_id
1368
+ threads << Thread.new(param_group) { |myparamgroup|
1369
+ MU.dupGlobals(parent_thread_id)
1370
+ Thread.abort_on_exception = true
1371
+ MU::Cloud::AWS::Database.delete_db_cluster_parameter_group(param_group_id, region: region) unless noop
1372
+ }
1373
+ end
1374
+ }
1375
+
1376
+ # Wait for all of the databases subnet/parameter groups to finish cleanup before proceeding
1377
+ threads.each { |t|
1378
+ t.join
1379
+ }
1380
+ end
1381
+
1382
+ # Cloud-specific configuration properties.
1383
+ # @param config [MU::Config]: The calling MU::Config object
1384
+ # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
1385
+ def self.schema(config)
1386
+ toplevel_required = []
1387
+ rds_parameters_primitive = {
1388
+ "type" => "array",
1389
+ "minItems" => 1,
1390
+ "items" => {
1391
+ "description" => "The database parameter group parameter to change and when to apply the change.",
1392
+ "type" => "object",
1393
+ "title" => "Database Parameter",
1394
+ "required" => ["name", "value"],
1395
+ "additionalProperties" => false,
1396
+ "properties" => {
1397
+ "name" => {
1398
+ "type" => "string"
1399
+ },
1400
+ "value" => {
1401
+ "type" => "string"
1402
+ },
1403
+ "apply_method" => {
1404
+ "enum" => ["pending-reboot", "immediate"],
1405
+ "default" => "immediate",
1406
+ "type" => "string"
1407
+ }
1408
+ }
1409
+ }
1410
+ }
1411
+
1412
+ schema = {
1413
+ "db_parameter_group_parameters" => rds_parameters_primitive,
1414
+ "cluster_parameter_group_parameters" => rds_parameters_primitive,
1415
+ "license_model" => {
1416
+ "type" => "string",
1417
+ "enum" => ["license-included", "bring-your-own-license", "general-public-license", "postgresql-license"]
1418
+ },
1419
+ "ingress_rules" => {
1420
+ "items" => {
1421
+ "properties" => {
1422
+ "sgs" => {
1423
+ "type" => "array",
1424
+ "items" => {
1425
+ "description" => "Other AWS Security Groups; resources that are associated with this group will have this rule applied to their traffic",
1426
+ "type" => "string"
1427
+ }
1428
+ },
1429
+ "lbs" => {
1430
+ "type" => "array",
1431
+ "items" => {
1432
+ "description" => "AWS Load Balancers which will have this rule applied to their traffic",
1433
+ "type" => "string"
1434
+ }
1435
+ }
1436
+ }
1437
+ }
1438
+ }
1439
+ }
1440
+ [toplevel_required, schema]
1441
+ end
1442
+
1443
+ # Cloud-specific pre-processing of {MU::Config::BasketofKittens::databases}, bare and unvalidated.
1444
+ # @param db [Hash]: The resource to process and validate
1445
+ # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
1446
+ # @return [Boolean]: True if validation succeeded, False otherwise
1447
+ def self.validateConfig(db, configurator)
1448
+ ok = true
1449
+
1450
+ db_cluster_engines = %w{aurora}
1451
+ db["create_cluster"] =
1452
+ if db_cluster_engines.include?(db["engine"])
1453
+ true
1454
+ else
1455
+ false
1456
+ end
1457
+
1458
+ db["license_model"] ||=
1459
+ if db["engine"] == "postgres"
1460
+ "postgresql-license"
1461
+ elsif db["engine"] == "mysql"
1462
+ "general-public-license"
1463
+ else
1464
+ "license-included"
1465
+ end
1466
+
1467
+ if db["create_read_replica"] or db['read_replica_of']
1468
+ if db["engine"] != "postgres" and db["engine"] != "mysql"
1469
+ MU.log "Read replica(s) database instances only supported for postgres and mysql. #{db["engine"]} not supported.", MU::ERR
1470
+ ok = false
1471
+ end
1472
+ end
1473
+
1474
+ if db["creation_style"] == "existing"
1475
+ begin
1476
+ MU::Cloud::AWS.rds(db['region']).describe_db_instances(
1477
+ db_instance_identifier: db['identifier']
1478
+ )
1479
+ rescue Aws::RDS::Errors::DBInstanceNotFound => e
1480
+ MU.log "Source database #{db['identifier']} was specified for #{db['name']}, but no such database exists in #{db['region']}", MU::ERR
1481
+ ok = false
1482
+ end
1483
+ end
1484
+
1485
+ if !db['password'].nil? and (db['password'].length < 8 or db['password'].match(/[\/\\@\s]/))
1486
+ MU.log "Database password '#{db['password']}' doesn't meet RDS requirements. Must be > 8 chars and have only ASCII characters other than /, @, \", or [space].", MU::ERR
1487
+ ok = false
1488
+ end
1489
+ if db["multi_az_on_create"] and db["multi_az_on_deploy"]
1490
+ MU.log "Both of multi_az_on_create and multi_az_on_deploy cannot be true", MU::ERR
1491
+ ok = false
1492
+ end
1493
+ if db.has_key?("db_parameter_group_parameters") || db.has_key?("cluster_parameter_group_parameters")
1494
+ if db["parameter_group_family"].nil?
1495
+ MU.log "parameter_group_family must be set when setting db_parameter_group_parameters", MU::ERR
1496
+ ok = false
1497
+ end
1498
+ end
1499
+ # Adding rules for Database instance storage. This varies depending on storage type and database type.
1500
+ if !db["storage"].nil? and (db["storage_type"] == "standard" or db["storage_type"] == "gp2")
1501
+ if db["engine"] == "postgres" or db["engine"] == "mysql"
1502
+ if !(5..6144).include? db["storage"]
1503
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 5 to 6144 GB for #{db["storage_type"]} volume types", MU::ERR
1504
+ ok = false
1505
+ end
1506
+ elsif %w{oracle-se1 oracle-se oracle-ee}.include? db["engine"]
1507
+ if !(10..6144).include? db["storage"]
1508
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 10 to 6144 GB for #{db["storage_type"]} volume types", MU::ERR
1509
+ ok = false
1510
+ end
1511
+ elsif %w{sqlserver-ex sqlserver-web}.include? db["engine"]
1512
+ if !(20..4096).include? db["storage"]
1513
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 20 to 4096 GB for #{db["storage_type"]} volume types", MU::ERR
1514
+ ok = false
1515
+ end
1516
+ elsif %w{sqlserver-ee sqlserver-se}.include? db["engine"]
1517
+ if !(200..4096).include? db["storage"]
1518
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 200 to 4096 GB for #{db["storage_type"]} volume types", MU::ERR
1519
+ ok = false
1520
+ end
1521
+ end
1522
+ elsif db["storage_type"] == "io1"
1523
+ if %w{postgres mysql oracle-se1 oracle-se oracle-ee}.include? db["engine"]
1524
+ if !(100..6144).include? db["storage"]
1525
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 100 to 6144 GB for #{db["storage_type"]} volume types", MU::ERR
1526
+ ok = false
1527
+ end
1528
+ elsif %w{sqlserver-ex sqlserver-web}.include? db["engine"]
1529
+ if !(100..4096).include? db["storage"]
1530
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 100 to 4096 GB for #{db["storage_type"]} volume types", MU::ERR
1531
+ ok = false
1532
+ end
1533
+ elsif %w{sqlserver-ee sqlserver-se}.include? db["engine"]
1534
+ if !(200..4096).include? db["storage"]
1535
+ MU.log "Database storage size is set to #{db["storage"]}. #{db["engine"]} only supports storage sizes between 200 to 4096 GB #{db["storage_type"]} volume types", MU::ERR
1536
+ ok = false
1537
+ end
1538
+ end
1539
+ end
1540
+
1541
+ if db["vpc"]
1542
+ if db["vpc"]["subnet_pref"] == "all_public" and !db['publicly_accessible']
1543
+ MU.log "Setting publicly_accessible to true on database '#{db['name']}', since deploying into public subnets.", MU::WARN
1544
+ db['publicly_accessible'] = true
1545
+ elsif db["vpc"]["subnet_pref"] == "all_private" and db['publicly_accessible']
1546
+ MU.log "Setting publicly_accessible to false on database '#{db['name']}', since deploying into private subnets.", MU::NOTICE
1547
+ db['publicly_accessible'] = false
1548
+ end
1549
+ end
1550
+
1551
+ ok
1552
+ end
1553
+
1554
+ private
1555
+
1556
+ # Remove an RDS database and associated artifacts
1557
+ # @param db [OpenStruct]: The cloud provider's description of the database artifact
1558
+ # @return [void]
1559
+ def self.terminate_rds_instance(db, noop: false, skipsnapshots: false, region: MU.curRegion, deploy_id: MU.deploy_id, mu_name: nil, cloud_id: nil)
1560
+ raise MuError, "terminate_rds_instance requires a non-nil database descriptor" if db.nil?
1561
+ db_id = db.db_instance_identifier
1562
+
1563
+ database_obj = MU::MommaCat.findStray(
1564
+ "AWS",
1565
+ "database",
1566
+ region: region,
1567
+ deploy_id: deploy_id,
1568
+ cloud_id: cloud_id,
1569
+ mu_name: mu_name
1570
+ ).first
1571
+
1572
+ subnet_group = nil
1573
+ begin
1574
+ subnet_group = db.db_subnet_group.db_subnet_group_name if db.db_subnet_group
1575
+ rescue NoMethodError
1576
+ # ignorable for non-VPC databases
1577
+ end
1578
+
1579
+ rdssecgroups = Array.new
1580
+ begin
1581
+ secgroup = MU::Cloud::AWS.rds(region).describe_db_security_groups(db_security_group_name: db_id)
1582
+ rescue Aws::RDS::Errors::DBSecurityGroupNotFound
1583
+ # this is normal in VPC world
1584
+ end
1585
+
1586
+ rdssecgroups << db_id if !secgroup.nil?
1587
+ parameter_group = db.db_parameter_groups.first.db_parameter_group_name
1588
+
1589
+ # We can use an AWS waiter for this.
1590
+ unless db.db_instance_status == "available"
1591
+ loop do
1592
+ MU.log "Waiting for #{db_id} to be in a removable state...", MU::NOTICE
1593
+ db = MU::Cloud::AWS::Database.getDatabaseById(db_id, region: region)
1594
+ break unless %w{creating modifying backing-up}.include?(db.db_instance_status)
1595
+ sleep 60
1596
+ end
1597
+ end
1598
+
1599
+ MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: db_id, target: db.endpoint.address, cloudclass: MU::Cloud::Database, delete: true)
1600
+
1601
+ if %w{deleting deleted}.include?(db.db_instance_status)
1602
+ MU.log "#{db_id} has already been terminated", MU::WARN
1603
+ else
1604
+ def self.dbSkipSnap(db_id, region)
1605
+ # We're calling this several times so lets declare it once
1606
+ MU.log "Terminating #{db_id} (not saving final snapshot)"
1607
+ MU::Cloud::AWS.rds(region).delete_db_instance(db_instance_identifier: db_id, skip_final_snapshot: true)
1608
+ end
1609
+
1610
+ def self.dbCreateSnap(db_id, region)
1611
+ MU.log "Terminating #{db_id} (final snapshot: #{db_id}-mufinal)"
1612
+ MU::Cloud::AWS.rds(region).delete_db_instance(db_instance_identifier: db_id, final_db_snapshot_identifier: "#{db_id}-mufinal", skip_final_snapshot: false)
1613
+ end
1614
+
1615
+ if !noop
1616
+ retries = 0
1617
+ begin
1618
+ if db.db_cluster_identifier || db.read_replica_source_db_instance_identifier
1619
+ # make sure we don't create final snapshot for a database instance that is part of a cluster, or if it's a read replica database instance
1620
+ dbSkipSnap(db_id, region)
1621
+ else
1622
+ skipsnapshots ? dbSkipSnap(db_id, region) : dbCreateSnap(db_id, region)
1623
+ end
1624
+ rescue Aws::RDS::Errors::InvalidDBInstanceState => e
1625
+ if retries < 5
1626
+ MU.log "#{db_id} is not in a removable state, retrying several times #{e.inspect}", MU::WARN
1627
+ retries += 1
1628
+ sleep 30
1629
+ retry
1630
+ else
1631
+ MU.log "#{db_id} is not in a removable state after several retries, giving up. #{e.inspect}", MU::ERR
1632
+ end
1633
+ rescue Aws::RDS::Errors::DBSnapshotAlreadyExists
1634
+ dbSkipSnap(db_id, region)
1635
+ MU.log "Snapshot of #{db_id} already exists", MU::WARN
1636
+ rescue Aws::RDS::Errors::SnapshotQuotaExceeded
1637
+ dbSkipSnap(db_id, region)
1638
+ MU.log "Snapshot quota exceeded while deleting #{db_id}", MU::ERR
1639
+ end
1640
+ end
1641
+ end
1642
+
1643
+ begin
1644
+ attempts = 0
1645
+ loop do
1646
+ MU.log "Waiting for #{db_id} termination to complete", MU::NOTICE if attempts % 6 == 0
1647
+ del_db = MU::Cloud::AWS::Database.getDatabaseById(db_id, region: region)
1648
+ break if del_db.nil? || del_db.db_instance_status == "deleted"
1649
+ sleep 10
1650
+ attempts += 1
1651
+ end
1652
+ rescue Aws::RDS::Errors::DBInstanceNotFound
1653
+ # we are ok with this
1654
+ end
1655
+
1656
+ # RDS security groups can depend on EC2 security groups, do these last
1657
+ begin
1658
+ rdssecgroups.each { |sg|
1659
+ MU.log "Removing RDS Security Group #{sg}"
1660
+ MU::Cloud::AWS.rds(region).delete_db_security_group(db_security_group_name: sg) if !noop
1661
+ }
1662
+ rescue Aws::RDS::Errors::DBSecurityGroupNotFound
1663
+ MU.log "RDS Security Group #{sg} disappeared before we could remove it", MU::WARN
1664
+ end
1665
+
1666
+ # Cleanup the database vault
1667
+ grommer =
1668
+ if database_obj
1669
+ database_obj.config.has_key?("groomer") ? database_obj.config["groomer"] : MU::Config.defaultGroomer
1670
+ else
1671
+ MU::Config.defaultGroomer
1672
+ end
1673
+
1674
+ groomclass = MU::Groomer.loadGroomer(grommer)
1675
+ groomclass.deleteSecret(vault: db_id.upcase) if !noop
1676
+ MU.log "#{db_id} has been terminated"
1677
+ end
1678
+
1679
+ # Remove an RDS database cluster and associated artifacts
1680
+ # @param cluster [OpenStruct]: The cloud provider's description of the database artifact
1681
+ # @return [void]
1682
+ def self.terminate_rds_cluster(cluster, noop: false, skipsnapshots: false, region: MU.curRegion, deploy_id: MU.deploy_id, mu_name: nil, cloud_id: nil)
1683
+ raise MuError, "terminate_rds_cluster requires a non-nil database cluster descriptor" if cluster.nil?
1684
+ cluster_id = cluster.db_cluster_identifier
1685
+
1686
+ cluster_obj = MU::MommaCat.findStray(
1687
+ "AWS",
1688
+ "database",
1689
+ region: region,
1690
+ deploy_id: deploy_id,
1691
+ cloud_id: cloud_id,
1692
+ mu_name: mu_name
1693
+ ).first
1694
+
1695
+ subnet_group = cluster.db_subnet_group
1696
+ cluster_parameter_group = cluster.db_cluster_parameter_group
1697
+
1698
+ # We can use an AWS waiter for this.
1699
+ unless cluster.status == "available"
1700
+ loop do
1701
+ MU.log "Waiting for #{cluster_id} to be in a removable state...", MU::NOTICE
1702
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(cluster_id, region: region)
1703
+ break unless %w{creating modifying backing-up}.include?(cluster.status)
1704
+ sleep 60
1705
+ end
1706
+ end
1707
+
1708
+ MU::Cloud::AWS::DNSZone.genericMuDNSEntry(name: cluster_id, target: cluster.endpoint, cloudclass: MU::Cloud::Database, delete: true)
1709
+
1710
+ if %w{deleting deleted}.include?(cluster.status)
1711
+ MU.log "#{cluster_id} has already been terminated", MU::WARN
1712
+ else
1713
+ unless noop
1714
+ def self.clusterSkipSnap(cluster_id, region)
1715
+ # We're calling this several times so lets declare it once
1716
+ MU.log "Terminating #{cluster_id}. Not saving final snapshot"
1717
+ MU::Cloud::AWS.rds(region).delete_db_cluster(db_cluster_identifier: cluster_id, skip_final_snapshot: true)
1718
+ end
1719
+
1720
+ def self.clusterCreateSnap(cluster_id, region)
1721
+ MU.log "Terminating #{cluster_id}. Saving final snapshot: #{cluster_id}-mufinal"
1722
+ MU::Cloud::AWS.rds(region).delete_db_cluster(db_cluster_identifier: cluster_id, skip_final_snapshot: false, final_db_snapshot_identifier: "#{cluster_id}-mufinal")
1723
+ end
1724
+
1725
+ retries = 0
1726
+ begin
1727
+ skipsnapshots ? clusterSkipSnap(cluster_id, region) : clusterCreateSnap(cluster_id, region)
1728
+ rescue Aws::RDS::Errors::InvalidDBClusterStateFault => e
1729
+ if retries < 5
1730
+ MU.log "#{cluster_id} is not in a removable state, retrying several times", MU::WARN
1731
+ retries += 1
1732
+ sleep 30
1733
+ retry
1734
+ else
1735
+ MU.log "#{cluster_id} is not in a removable state after several retries, giving up. #{e.inspect}", MU::ERR
1736
+ end
1737
+ rescue Aws::RDS::Errors::DBClusterSnapshotAlreadyExistsFault
1738
+ clusterSkipSnap(cluster_id, region)
1739
+ MU.log "Snapshot of #{cluster_id} already exists", MU::WARN
1740
+ rescue Aws::RDS::Errors::DBClusterQuotaExceeded
1741
+ clusterSkipSnap(cluster_id, region)
1742
+ MU.log "Snapshot quota exceeded while deleting #{cluster_id}", MU::ERR
1743
+ end
1744
+ end
1745
+ end
1746
+
1747
+ # We're wating until getDatabaseClusterById returns nil. This assumes the database cluster object doesn't linger around in "deleted" state for a while.
1748
+ loop do
1749
+ MU.log "Waiting for #{cluster_id} to terminate", MU::NOTICE
1750
+ cluster = MU::Cloud::AWS::Database.getDatabaseClusterById(cluster_id, region: region)
1751
+ break unless cluster
1752
+ sleep 30
1753
+ end
1754
+
1755
+ # Cleanup the cluster vault
1756
+ grommer =
1757
+ if cluster_obj
1758
+ cluster_obj.config.has_key?("groomer") ? cluster_obj.config["groomer"] : MU::Config.defaultGroomer
1759
+ else
1760
+ MU::Config.defaultGroomer
1761
+ end
1762
+
1763
+ groomclass = MU::Groomer.loadGroomer(grommer)
1764
+ groomclass.deleteSecret(vault: cluster_id.upcase) if !noop
1765
+
1766
+ MU.log "#{cluster_id} has been terminated"
1767
+ end
1768
+
1769
+ # Remove a database subnet group.
1770
+ # @param subnet_group_id [string]: The cloud provider's ID of the database subnet group.
1771
+ # @param region [String]: The cloud provider's region in which to operate.
1772
+ # @return [void]
1773
+ def self.delete_subnet_group(subnet_group_id, region: MU.curRegion)
1774
+ retries ||= 0
1775
+ MU.log "Deleting DB subnet group #{subnet_group_id}"
1776
+ MU::Cloud::AWS.rds(region).delete_db_subnet_group(db_subnet_group_name: subnet_group_id)
1777
+ rescue Aws::RDS::Errors::DBSubnetGroupNotFoundFault => e
1778
+ MU.log "DB subnet group #{subnet_group_id} disappeared before we could remove it", MU::WARN
1779
+ rescue Aws::RDS::Errors::InvalidDBSubnetGroupStateFault=> e
1780
+ if retries < 5
1781
+ MU.log "DB subnet group #{subnet_group_id} is not in a removable state, retrying", MU::WARN
1782
+ retries += 1
1783
+ sleep 30
1784
+ retry
1785
+ else
1786
+ MU.log "#{subnet_group_id} is not in a removable state after several retries, giving up. #{e.inspect}", MU::ERR
1787
+ end
1788
+ end
1789
+
1790
+ # Remove a database parameter group.
1791
+ # @param parameter_group_id [string]: The cloud provider's ID of the database parameter group.
1792
+ # @param region [String]: The cloud provider's region in which to operate.
1793
+ # @return [void]
1794
+ def self.delete_db_parameter_group(parameter_group_id, region: MU.curRegion)
1795
+ retries ||= 0
1796
+ MU.log "Deleting DB parameter group #{parameter_group_id}"
1797
+ MU::Cloud::AWS.rds(region).delete_db_parameter_group(db_parameter_group_name: parameter_group_id)
1798
+ rescue Aws::RDS::Errors::DBParameterGroupNotFound
1799
+ MU.log "DB parameter group #{parameter_group_id} disappeared before we could remove it", MU::WARN
1800
+ rescue Aws::RDS::Errors::InvalidDBParameterGroupState => e
1801
+ if retries < 5
1802
+ MU.log "DB parameter group #{parameter_group_id} is not in a removable state, retrying", MU::WARN
1803
+ retries += 1
1804
+ sleep 30
1805
+ retry
1806
+ else
1807
+ MU.log "DB parameter group #{parameter_group_id} is not in a removable state after several retries, giving up. #{e.inspect}", MU::ERR
1808
+ end
1809
+ end
1810
+
1811
+ # Remove a database cluster parameter group.
1812
+ # @param parameter_group_id [string]: The cloud provider's ID of the database cluster parameter group.
1813
+ # @param region [String]: The cloud provider's region in which to operate.
1814
+ # @return [void]
1815
+ def self.delete_db_cluster_parameter_group(parameter_group_id, region: MU.curRegion)
1816
+ retries ||= 0
1817
+ MU.log "Deleting cluster parameter group #{parameter_group_id}"
1818
+ MU::Cloud::AWS.rds(region).delete_db_cluster_parameter_group(db_cluster_parameter_group_name: parameter_group_id)
1819
+ # AWS API sucks. instead of returning the documented error DBClusterParameterGroupNotFoundFault it errors out with DBParameterGroupNotFound.
1820
+ rescue Aws::RDS::Errors::DBParameterGroupNotFound
1821
+ MU.log "Cluster parameter group #{parameter_group_id} disappeared before we could remove it", MU::WARN
1822
+ rescue Aws::RDS::Errors::InvalidDBParameterGroupState => e
1823
+ if retries < 5
1824
+ MU.log "Cluster parameter group #{parameter_group_id} is not in a removable state, retrying", MU::WARN
1825
+ retries += 1
1826
+ sleep 30
1827
+ retry
1828
+ else
1829
+ MU.log "Cluster parameter group #{parameter_group_id} is not in a removable state after several retries, giving up. #{e.inspect}", MU::ERR
1830
+ end
1831
+ end
1832
+
1833
+ end #class
1834
+ end #class
1835
+ end
1836
+ end #module