cloud-mu 2.1.0beta → 3.0.0beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (291) hide show
  1. checksums.yaml +5 -5
  2. data/Berksfile +4 -5
  3. data/Berksfile.lock +179 -0
  4. data/README.md +1 -6
  5. data/ansible/roles/geerlingguy.firewall/templates/firewall.bash.j2 +0 -0
  6. data/ansible/roles/mu-installer/README.md +33 -0
  7. data/ansible/roles/mu-installer/defaults/main.yml +2 -0
  8. data/ansible/roles/mu-installer/handlers/main.yml +2 -0
  9. data/ansible/roles/mu-installer/meta/main.yml +60 -0
  10. data/ansible/roles/mu-installer/tasks/main.yml +13 -0
  11. data/ansible/roles/mu-installer/tests/inventory +2 -0
  12. data/ansible/roles/mu-installer/tests/test.yml +5 -0
  13. data/ansible/roles/mu-installer/vars/main.yml +2 -0
  14. data/bin/mu-adopt +125 -0
  15. data/bin/mu-aws-setup +4 -4
  16. data/bin/mu-azure-setup +265 -0
  17. data/bin/mu-azure-tests +43 -0
  18. data/bin/mu-cleanup +20 -8
  19. data/bin/mu-configure +224 -98
  20. data/bin/mu-deploy +8 -3
  21. data/bin/mu-gcp-setup +16 -8
  22. data/bin/mu-gen-docs +92 -8
  23. data/bin/mu-load-config.rb +52 -12
  24. data/bin/mu-momma-cat +36 -0
  25. data/bin/mu-node-manage +34 -27
  26. data/bin/mu-self-update +2 -2
  27. data/bin/mu-ssh +12 -8
  28. data/bin/mu-upload-chef-artifacts +11 -4
  29. data/bin/mu-user-manage +3 -0
  30. data/cloud-mu.gemspec +8 -11
  31. data/cookbooks/firewall/libraries/helpers_iptables.rb +2 -2
  32. data/cookbooks/firewall/metadata.json +1 -1
  33. data/cookbooks/firewall/recipes/default.rb +5 -9
  34. data/cookbooks/mu-firewall/attributes/default.rb +2 -0
  35. data/cookbooks/mu-firewall/metadata.rb +1 -1
  36. data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +0 -0
  37. data/cookbooks/mu-master/Berksfile +2 -2
  38. data/cookbooks/mu-master/files/default/check_mem.pl +0 -0
  39. data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
  40. data/cookbooks/mu-master/metadata.rb +5 -4
  41. data/cookbooks/mu-master/recipes/389ds.rb +1 -1
  42. data/cookbooks/mu-master/recipes/basepackages.rb +30 -10
  43. data/cookbooks/mu-master/recipes/default.rb +59 -7
  44. data/cookbooks/mu-master/recipes/firewall-holes.rb +1 -1
  45. data/cookbooks/mu-master/recipes/init.rb +65 -47
  46. data/cookbooks/mu-master/recipes/{eks-kubectl.rb → kubectl.rb} +4 -10
  47. data/cookbooks/mu-master/recipes/sssd.rb +2 -1
  48. data/cookbooks/mu-master/recipes/update_nagios_only.rb +6 -6
  49. data/cookbooks/mu-master/templates/default/web_app.conf.erb +2 -2
  50. data/cookbooks/mu-master/templates/mods/ldap.conf.erb +4 -0
  51. data/cookbooks/mu-php54/Berksfile +1 -2
  52. data/cookbooks/mu-php54/metadata.rb +4 -5
  53. data/cookbooks/mu-php54/recipes/default.rb +1 -1
  54. data/cookbooks/mu-splunk/templates/default/splunk-init.erb +0 -0
  55. data/cookbooks/mu-tools/Berksfile +3 -2
  56. data/cookbooks/mu-tools/files/default/Mu_CA.pem +33 -0
  57. data/cookbooks/mu-tools/libraries/helper.rb +20 -8
  58. data/cookbooks/mu-tools/metadata.rb +5 -2
  59. data/cookbooks/mu-tools/recipes/apply_security.rb +2 -3
  60. data/cookbooks/mu-tools/recipes/eks.rb +1 -1
  61. data/cookbooks/mu-tools/recipes/gcloud.rb +5 -30
  62. data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
  63. data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -0
  64. data/cookbooks/mu-tools/recipes/selinux.rb +19 -0
  65. data/cookbooks/mu-tools/recipes/split_var_partitions.rb +0 -1
  66. data/cookbooks/mu-tools/recipes/windows-client.rb +256 -122
  67. data/cookbooks/mu-tools/resources/disk.rb +3 -1
  68. data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +1 -1
  69. data/cookbooks/mu-tools/templates/default/etc_hosts.erb +1 -1
  70. data/cookbooks/mu-tools/templates/default/{kubeconfig.erb → kubeconfig-eks.erb} +0 -0
  71. data/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb +27 -0
  72. data/cookbooks/mu-tools/templates/windows-10/sshd_config.erb +137 -0
  73. data/cookbooks/mu-utility/recipes/nat.rb +4 -0
  74. data/extras/alpha.png +0 -0
  75. data/extras/beta.png +0 -0
  76. data/extras/clean-stock-amis +2 -2
  77. data/extras/generate-stock-images +131 -0
  78. data/extras/git-fix-permissions-hook +0 -0
  79. data/extras/image-generators/AWS/centos6.yaml +17 -0
  80. data/extras/image-generators/{aws → AWS}/centos7-govcloud.yaml +0 -0
  81. data/extras/image-generators/{aws → AWS}/centos7.yaml +0 -0
  82. data/extras/image-generators/{aws → AWS}/rhel7.yaml +0 -0
  83. data/extras/image-generators/{aws → AWS}/win2k12.yaml +0 -0
  84. data/extras/image-generators/{aws → AWS}/win2k16.yaml +0 -0
  85. data/extras/image-generators/{aws → AWS}/windows.yaml +0 -0
  86. data/extras/image-generators/{gcp → Google}/centos6.yaml +1 -0
  87. data/extras/image-generators/Google/centos7.yaml +18 -0
  88. data/extras/python_rpm/build.sh +0 -0
  89. data/extras/release.png +0 -0
  90. data/extras/ruby_rpm/build.sh +0 -0
  91. data/extras/ruby_rpm/muby.spec +1 -1
  92. data/install/README.md +43 -5
  93. data/install/deprecated-bash-library.sh +0 -0
  94. data/install/installer +1 -1
  95. data/install/jenkinskeys.rb +0 -0
  96. data/install/mu-master.yaml +55 -0
  97. data/modules/mommacat.ru +41 -7
  98. data/modules/mu.rb +444 -149
  99. data/modules/mu/adoption.rb +500 -0
  100. data/modules/mu/cleanup.rb +235 -158
  101. data/modules/mu/cloud.rb +675 -138
  102. data/modules/mu/clouds/aws.rb +156 -24
  103. data/modules/mu/clouds/aws/alarm.rb +4 -14
  104. data/modules/mu/clouds/aws/bucket.rb +60 -18
  105. data/modules/mu/clouds/aws/cache_cluster.rb +8 -20
  106. data/modules/mu/clouds/aws/collection.rb +12 -22
  107. data/modules/mu/clouds/aws/container_cluster.rb +209 -118
  108. data/modules/mu/clouds/aws/database.rb +120 -45
  109. data/modules/mu/clouds/aws/dnszone.rb +7 -18
  110. data/modules/mu/clouds/aws/endpoint.rb +5 -15
  111. data/modules/mu/clouds/aws/firewall_rule.rb +144 -72
  112. data/modules/mu/clouds/aws/folder.rb +4 -11
  113. data/modules/mu/clouds/aws/function.rb +6 -16
  114. data/modules/mu/clouds/aws/group.rb +4 -12
  115. data/modules/mu/clouds/aws/habitat.rb +11 -13
  116. data/modules/mu/clouds/aws/loadbalancer.rb +40 -28
  117. data/modules/mu/clouds/aws/log.rb +5 -13
  118. data/modules/mu/clouds/aws/msg_queue.rb +9 -24
  119. data/modules/mu/clouds/aws/nosqldb.rb +4 -12
  120. data/modules/mu/clouds/aws/notifier.rb +6 -13
  121. data/modules/mu/clouds/aws/role.rb +69 -40
  122. data/modules/mu/clouds/aws/search_domain.rb +17 -20
  123. data/modules/mu/clouds/aws/server.rb +184 -94
  124. data/modules/mu/clouds/aws/server_pool.rb +33 -38
  125. data/modules/mu/clouds/aws/storage_pool.rb +5 -12
  126. data/modules/mu/clouds/aws/user.rb +59 -33
  127. data/modules/mu/clouds/aws/userdata/linux.erb +18 -30
  128. data/modules/mu/clouds/aws/userdata/windows.erb +9 -9
  129. data/modules/mu/clouds/aws/vpc.rb +214 -145
  130. data/modules/mu/clouds/azure.rb +978 -44
  131. data/modules/mu/clouds/azure/container_cluster.rb +413 -0
  132. data/modules/mu/clouds/azure/firewall_rule.rb +500 -0
  133. data/modules/mu/clouds/azure/habitat.rb +167 -0
  134. data/modules/mu/clouds/azure/loadbalancer.rb +205 -0
  135. data/modules/mu/clouds/azure/role.rb +211 -0
  136. data/modules/mu/clouds/azure/server.rb +810 -0
  137. data/modules/mu/clouds/azure/user.rb +257 -0
  138. data/modules/mu/clouds/azure/userdata/README.md +4 -0
  139. data/modules/mu/clouds/azure/userdata/linux.erb +137 -0
  140. data/modules/mu/clouds/azure/userdata/windows.erb +275 -0
  141. data/modules/mu/clouds/azure/vpc.rb +782 -0
  142. data/modules/mu/clouds/cloudformation.rb +12 -9
  143. data/modules/mu/clouds/cloudformation/firewall_rule.rb +5 -13
  144. data/modules/mu/clouds/cloudformation/server.rb +10 -1
  145. data/modules/mu/clouds/cloudformation/server_pool.rb +1 -0
  146. data/modules/mu/clouds/cloudformation/vpc.rb +0 -2
  147. data/modules/mu/clouds/google.rb +554 -117
  148. data/modules/mu/clouds/google/bucket.rb +173 -32
  149. data/modules/mu/clouds/google/container_cluster.rb +1112 -157
  150. data/modules/mu/clouds/google/database.rb +24 -47
  151. data/modules/mu/clouds/google/firewall_rule.rb +344 -89
  152. data/modules/mu/clouds/google/folder.rb +156 -79
  153. data/modules/mu/clouds/google/group.rb +272 -82
  154. data/modules/mu/clouds/google/habitat.rb +177 -52
  155. data/modules/mu/clouds/google/loadbalancer.rb +9 -34
  156. data/modules/mu/clouds/google/role.rb +1211 -0
  157. data/modules/mu/clouds/google/server.rb +491 -227
  158. data/modules/mu/clouds/google/server_pool.rb +233 -48
  159. data/modules/mu/clouds/google/user.rb +479 -125
  160. data/modules/mu/clouds/google/userdata/linux.erb +3 -3
  161. data/modules/mu/clouds/google/userdata/windows.erb +9 -9
  162. data/modules/mu/clouds/google/vpc.rb +381 -223
  163. data/modules/mu/config.rb +689 -214
  164. data/modules/mu/config/bucket.rb +1 -1
  165. data/modules/mu/config/cache_cluster.rb +1 -1
  166. data/modules/mu/config/cache_cluster.yml +0 -4
  167. data/modules/mu/config/container_cluster.rb +18 -9
  168. data/modules/mu/config/database.rb +6 -23
  169. data/modules/mu/config/firewall_rule.rb +9 -15
  170. data/modules/mu/config/folder.rb +22 -21
  171. data/modules/mu/config/habitat.rb +22 -21
  172. data/modules/mu/config/loadbalancer.rb +2 -2
  173. data/modules/mu/config/role.rb +9 -40
  174. data/modules/mu/config/server.rb +26 -5
  175. data/modules/mu/config/server_pool.rb +1 -1
  176. data/modules/mu/config/storage_pool.rb +2 -2
  177. data/modules/mu/config/user.rb +4 -0
  178. data/modules/mu/config/vpc.rb +350 -110
  179. data/modules/mu/defaults/{amazon_images.yaml → AWS.yaml} +37 -39
  180. data/modules/mu/defaults/Azure.yaml +17 -0
  181. data/modules/mu/defaults/Google.yaml +24 -0
  182. data/modules/mu/defaults/README.md +1 -1
  183. data/modules/mu/deploy.rb +168 -125
  184. data/modules/mu/groomer.rb +2 -1
  185. data/modules/mu/groomers/ansible.rb +104 -32
  186. data/modules/mu/groomers/chef.rb +96 -44
  187. data/modules/mu/kittens.rb +20602 -0
  188. data/modules/mu/logger.rb +38 -11
  189. data/modules/mu/master.rb +90 -8
  190. data/modules/mu/master/chef.rb +2 -3
  191. data/modules/mu/master/ldap.rb +0 -1
  192. data/modules/mu/master/ssl.rb +250 -0
  193. data/modules/mu/mommacat.rb +917 -513
  194. data/modules/scratchpad.erb +1 -1
  195. data/modules/tests/super_complex_bok.yml +0 -0
  196. data/modules/tests/super_simple_bok.yml +0 -0
  197. data/roles/mu-master.json +2 -1
  198. data/spec/azure_creds +5 -0
  199. data/spec/mu.yaml +56 -0
  200. data/spec/mu/clouds/azure_spec.rb +164 -27
  201. data/spec/spec_helper.rb +5 -0
  202. data/test/clean_up.py +0 -0
  203. data/test/exec_inspec.py +0 -0
  204. data/test/exec_mu_install.py +0 -0
  205. data/test/exec_retry.py +0 -0
  206. data/test/smoke_test.rb +0 -0
  207. metadata +90 -118
  208. data/cookbooks/mu-jenkins/Berksfile +0 -14
  209. data/cookbooks/mu-jenkins/CHANGELOG.md +0 -13
  210. data/cookbooks/mu-jenkins/LICENSE +0 -37
  211. data/cookbooks/mu-jenkins/README.md +0 -105
  212. data/cookbooks/mu-jenkins/attributes/default.rb +0 -42
  213. data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +0 -73
  214. data/cookbooks/mu-jenkins/files/default/deploy_config.xml +0 -44
  215. data/cookbooks/mu-jenkins/metadata.rb +0 -21
  216. data/cookbooks/mu-jenkins/recipes/default.rb +0 -195
  217. data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +0 -54
  218. data/cookbooks/mu-jenkins/recipes/public_key.rb +0 -24
  219. data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +0 -24
  220. data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +0 -14
  221. data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +0 -6
  222. data/cookbooks/nagios/Berksfile +0 -11
  223. data/cookbooks/nagios/CHANGELOG.md +0 -589
  224. data/cookbooks/nagios/CONTRIBUTING.md +0 -11
  225. data/cookbooks/nagios/LICENSE +0 -37
  226. data/cookbooks/nagios/README.md +0 -328
  227. data/cookbooks/nagios/TESTING.md +0 -2
  228. data/cookbooks/nagios/attributes/config.rb +0 -171
  229. data/cookbooks/nagios/attributes/default.rb +0 -228
  230. data/cookbooks/nagios/chefignore +0 -102
  231. data/cookbooks/nagios/definitions/command.rb +0 -33
  232. data/cookbooks/nagios/definitions/contact.rb +0 -33
  233. data/cookbooks/nagios/definitions/contactgroup.rb +0 -33
  234. data/cookbooks/nagios/definitions/host.rb +0 -33
  235. data/cookbooks/nagios/definitions/hostdependency.rb +0 -33
  236. data/cookbooks/nagios/definitions/hostescalation.rb +0 -34
  237. data/cookbooks/nagios/definitions/hostgroup.rb +0 -33
  238. data/cookbooks/nagios/definitions/nagios_conf.rb +0 -38
  239. data/cookbooks/nagios/definitions/resource.rb +0 -33
  240. data/cookbooks/nagios/definitions/service.rb +0 -33
  241. data/cookbooks/nagios/definitions/servicedependency.rb +0 -33
  242. data/cookbooks/nagios/definitions/serviceescalation.rb +0 -34
  243. data/cookbooks/nagios/definitions/servicegroup.rb +0 -33
  244. data/cookbooks/nagios/definitions/timeperiod.rb +0 -33
  245. data/cookbooks/nagios/libraries/base.rb +0 -314
  246. data/cookbooks/nagios/libraries/command.rb +0 -91
  247. data/cookbooks/nagios/libraries/contact.rb +0 -230
  248. data/cookbooks/nagios/libraries/contactgroup.rb +0 -112
  249. data/cookbooks/nagios/libraries/custom_option.rb +0 -36
  250. data/cookbooks/nagios/libraries/data_bag_helper.rb +0 -23
  251. data/cookbooks/nagios/libraries/default.rb +0 -90
  252. data/cookbooks/nagios/libraries/host.rb +0 -412
  253. data/cookbooks/nagios/libraries/hostdependency.rb +0 -181
  254. data/cookbooks/nagios/libraries/hostescalation.rb +0 -173
  255. data/cookbooks/nagios/libraries/hostgroup.rb +0 -119
  256. data/cookbooks/nagios/libraries/nagios.rb +0 -282
  257. data/cookbooks/nagios/libraries/resource.rb +0 -59
  258. data/cookbooks/nagios/libraries/service.rb +0 -455
  259. data/cookbooks/nagios/libraries/servicedependency.rb +0 -215
  260. data/cookbooks/nagios/libraries/serviceescalation.rb +0 -195
  261. data/cookbooks/nagios/libraries/servicegroup.rb +0 -144
  262. data/cookbooks/nagios/libraries/timeperiod.rb +0 -160
  263. data/cookbooks/nagios/libraries/users_helper.rb +0 -54
  264. data/cookbooks/nagios/metadata.rb +0 -25
  265. data/cookbooks/nagios/recipes/_load_databag_config.rb +0 -153
  266. data/cookbooks/nagios/recipes/_load_default_config.rb +0 -241
  267. data/cookbooks/nagios/recipes/apache.rb +0 -48
  268. data/cookbooks/nagios/recipes/default.rb +0 -204
  269. data/cookbooks/nagios/recipes/nginx.rb +0 -82
  270. data/cookbooks/nagios/recipes/pagerduty.rb +0 -143
  271. data/cookbooks/nagios/recipes/server_package.rb +0 -40
  272. data/cookbooks/nagios/recipes/server_source.rb +0 -164
  273. data/cookbooks/nagios/templates/default/apache2.conf.erb +0 -96
  274. data/cookbooks/nagios/templates/default/cgi.cfg.erb +0 -266
  275. data/cookbooks/nagios/templates/default/commands.cfg.erb +0 -13
  276. data/cookbooks/nagios/templates/default/contacts.cfg.erb +0 -37
  277. data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +0 -25
  278. data/cookbooks/nagios/templates/default/hosts.cfg.erb +0 -15
  279. data/cookbooks/nagios/templates/default/htpasswd.users.erb +0 -6
  280. data/cookbooks/nagios/templates/default/nagios.cfg.erb +0 -22
  281. data/cookbooks/nagios/templates/default/nginx.conf.erb +0 -62
  282. data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +0 -185
  283. data/cookbooks/nagios/templates/default/resource.cfg.erb +0 -27
  284. data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +0 -15
  285. data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +0 -14
  286. data/cookbooks/nagios/templates/default/services.cfg.erb +0 -14
  287. data/cookbooks/nagios/templates/default/templates.cfg.erb +0 -31
  288. data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +0 -13
  289. data/extras/image-generators/aws/centos6.yaml +0 -18
  290. data/modules/mu/defaults/google_images.yaml +0 -16
  291. data/roles/mu-master-jenkins.json +0 -24
File without changes
@@ -2,7 +2,7 @@
2
2
 
3
3
  BOLD=`tput bold`
4
4
  NORM=`tput sgr0`
5
- CHEF_CLIENT_VERSION="14.11.21"
5
+ CHEF_CLIENT_VERSION="14.13.11"
6
6
  if [ "$MU_BRANCH" == "" ];then
7
7
  MU_BRANCH="master"
8
8
  mydir="`dirname $0`"
File without changes
@@ -0,0 +1,55 @@
1
+ ---
2
+ appname: mu
3
+ parameters:
4
+ - name: cloud
5
+ default: <%= MU.myCloud || "AWS" %>
6
+ valid_values:
7
+ <% MU::Cloud.availableClouds.each { |c| %>
8
+ - <%= c %>
9
+ <% } %>
10
+ - name: public
11
+ default: true
12
+ - name: name
13
+ default: mu-master
14
+ scrub_mu_isms: true
15
+ servers:
16
+ - name: <%= name %>
17
+ groomer: Ansible
18
+ run_list:
19
+ - mu-installer
20
+ platform: centos7
21
+ cloud: <%= cloud %>
22
+ <% if cloud == "AWS" %>
23
+ size: t2.medium
24
+ <% elsif cloud == "Azure" %>
25
+ size: Standard_DS1_v2
26
+ <% elsif cloud == "Google" %>
27
+ size: n1-standard-1
28
+ <% end %>
29
+ vpc:
30
+ name: <%= name %>-vpc
31
+ <% if public == "true" %>
32
+ subnet_pref: public
33
+ static_ip:
34
+ assign_ip: true
35
+ associate_public_ip: true
36
+ <% else %>
37
+ subnet_pref: private
38
+ <% end %>
39
+ <% if cloud == "AWS" %>
40
+ canned_iam_policies:
41
+ - AdministratorAccess
42
+ <% elsif cloud == "Azure" %>
43
+ roles:
44
+ - Owner
45
+ <% elsif cloud == "Google" %>
46
+ roles:
47
+ - role:
48
+ id: roles/owner
49
+ <% end %>
50
+ vpcs:
51
+ - name: <%= name %>-vpc
52
+ cloud: <%= cloud %>
53
+ <% if public %>
54
+ create_bastion: false
55
+ <% end %>
@@ -32,7 +32,12 @@ $LOAD_PATH << "#{$MUDIR}/modules"
32
32
  require File.realpath(File.expand_path(File.dirname(__FILE__)+"/mu-load-config.rb"))
33
33
  require 'mu'
34
34
 
35
- MU::Groomer::Chef.loadChefLib # pre-cache this so we don't take a hit on a user-interactive need
35
+ begin
36
+ MU::Groomer::Chef.loadChefLib # pre-cache this so we don't take a hit on a user-interactive need
37
+ $ENABLE_SCRATCHPAD = true
38
+ rescue LoadError
39
+ MU.log "Chef libraries not available, disabling Scratchpad", MU::WARN
40
+ end
36
41
  #MU.setLogging($opts[:verbose], $opts[:web])
37
42
  if MU.myCloud == "AWS"
38
43
  MU::Cloud::AWS.openFirewallForClients # XXX add the other clouds, or abstract
@@ -57,7 +62,7 @@ Thread.new {
57
62
  MU.dupGlobals(parent_thread_id)
58
63
  begin
59
64
  MU::MommaCat.cleanTerminatedInstances
60
- MU::Master.cleanExpiredScratchpads
65
+ MU::Master.cleanExpiredScratchpads if $ENABLE_SCRATCHPAD
61
66
  sleep 60
62
67
  rescue Exception => e
63
68
  MU.log "Error in cleanTerminatedInstances thread: #{e.inspect}", MU::ERR, details: e.backtrace
@@ -199,6 +204,17 @@ app = proc do |env|
199
204
  ]
200
205
  begin
201
206
  if !env.nil? and !env['REQUEST_PATH'].nil? and env['REQUEST_PATH'].match(/^\/scratchpad/)
207
+ if !$ENABLE_SCRATCHPAD
208
+ msg = "Scratchpad disabled in non-Chef Mu installations"
209
+ return [
210
+ 504,
211
+ {
212
+ 'Content-Type' => 'text/html',
213
+ 'Content-Length' => msg.length.to_s
214
+ },
215
+ [msg]
216
+ ]
217
+ end
202
218
  itemname = env['REQUEST_PATH'].sub(/^\/scratchpad\//, "")
203
219
  begin
204
220
  if itemname.sub!(/\/secret$/, "")
@@ -257,7 +273,7 @@ app = proc do |env|
257
273
  [page]
258
274
  ]
259
275
  end
260
- rescue MU::Groomer::Chef::MuNoSuchSecret
276
+ rescue MU::Groomer::MuNoSuchSecret
261
277
  page = nil
262
278
  if $MU_CFG.has_key?('scratchpad') and
263
279
  $MU_CFG['scratchpad'].has_key?("template_path") and
@@ -287,6 +303,7 @@ app = proc do |env|
287
303
  ]
288
304
  end
289
305
  elsif !env.nil? and !env['REQUEST_PATH'].nil? and env['REQUEST_PATH'].match(/^\/rest\//)
306
+
290
307
  action, filter, path = env['REQUEST_PATH'].sub(/^\/rest\/?/, "").split(/\//, 3)
291
308
  # Don't give away the store. This can't be public until we can
292
309
  # authenticate and access-control properly.
@@ -295,7 +312,23 @@ app = proc do |env|
295
312
  next
296
313
  end
297
314
 
298
- if action == "deploy"
315
+ if action == "hosts_add"
316
+ if Process.uid != 0
317
+ returnval = throw500 "Service not available"
318
+ elsif !filter or !path
319
+ returnval = throw404 env['REQUEST_PATH']
320
+ else
321
+ MU::MommaCat.addInstanceToEtcHosts(path, filter)
322
+ returnval = [
323
+ 200,
324
+ {
325
+ 'Content-Type' => 'text/plain',
326
+ 'Content-Length' => 2
327
+ },
328
+ ["ok"]
329
+ ]
330
+ end
331
+ elsif action == "deploy"
299
332
  returnval = throw404 env['REQUEST_PATH'] if !filter
300
333
  MU.log "Loading deploy data for #{filter} #{path}"
301
334
  kittenpile = MU::MommaCat.getLitter(filter)
@@ -313,9 +346,9 @@ app = proc do |env|
313
346
  200,
314
347
  {
315
348
  'Content-Type' => 'text/plain',
316
- 'Content-Length' => MU.adminBucketName.length.to_s
349
+ 'Content-Length' => MU.adminBucketName(filter, credentials: path).length.to_s
317
350
  },
318
- [MU.adminBucketName]
351
+ [MU.adminBucketName(filter, credentials: path)]
319
352
  ]
320
353
  else
321
354
  returnval = throw404 env['REQUEST_PATH']
@@ -389,7 +422,8 @@ app = proc do |env|
389
422
  if instance.respond_to?(:addVolume)
390
423
  # XXX make sure we handle mangled input safely
391
424
  params = JSON.parse(Base64.decode64(req["add_volume"]))
392
- instance.addVolume(params["dev"], params["size"])
425
+ MU.log "ADDVOLUME REQUEST", MU::WARN, details: params
426
+ instance.addVolume(params["dev"], params["size"], delete_on_termination: params["delete_on_termination"])
393
427
  else
394
428
  returnval = throw500 "I don't know how to add a volume for #{instance}"
395
429
  ok = false
@@ -36,20 +36,252 @@ class Object
36
36
  end
37
37
  end
38
38
 
39
+ # Mu extensions to Ruby's {Hash} type for internal Mu use
40
+ class Hash
41
+
42
+ # Strip extraneous fields out of a {MU::Config} hash to make it suitable for
43
+ # shorthand printing, such as with <tt>mu-adopt --diff</tt>
44
+ def self.bok_minimize(o)
45
+ if o.is_a?(Hash)
46
+ newhash = o.reject { |k, v|
47
+ !v.is_a?(Array) and !v.is_a?(Hash) and !["name", "id", "cloud_id"].include?(k)
48
+ }
49
+ # newhash.delete("cloud_id") if newhash["name"] or newhash["id"]
50
+ newhash.each_pair { |k, v|
51
+ newhash[k] = bok_minimize(v)
52
+ }
53
+ newhash.reject! { |_k, v| v.nil? or v.empty? }
54
+ newhash = newhash.values.first if newhash.size == 1
55
+ return newhash
56
+ elsif o.is_a?(Array)
57
+ newarray = []
58
+ o.each { |v|
59
+ newvalue = bok_minimize(v)
60
+ newarray << newvalue if !newvalue.nil? and !newvalue.empty?
61
+ }
62
+ newarray = newarray.first if newarray.size == 1
63
+ return newarray
64
+ end
65
+
66
+ o
67
+ end
68
+
69
+ # A comparison function for sorting arrays of hashes
70
+ def <=>(other)
71
+ return 1 if other.nil? or self.size > other.size
72
+ return -1 if other.size > self.size
73
+ # Sort any array children we have
74
+ self.each_pair { |k, v|
75
+ self[k] = v.sort if v.is_a?(Array)
76
+ }
77
+ other.each_pair { |k, v|
78
+ other[k] = v.sort if v.is_a?(Array)
79
+ }
80
+ return 0 if self == other # that was easy!
81
+ # compare elements and decide who's "bigger" based on their totals?
82
+ 0
83
+ end
84
+
85
+ # Recursively compare two hashes
86
+ def diff(with, on = self, level: 0, parents: [])
87
+ return if with.nil? and on.nil?
88
+ if with.nil? or on.nil? or with.class != on.class
89
+ return # XXX ...however we're flagging differences
90
+ end
91
+ return if on == with
92
+
93
+ tree = ""
94
+ indentsize = 0
95
+ parents.each { |p|
96
+ tree += (" " * indentsize) + p + " => \n"
97
+ indentsize += 2
98
+ }
99
+ indent = (" " * indentsize)
100
+
101
+ changes = []
102
+ if on.is_a?(Hash)
103
+ on_unique = (on.keys - with.keys)
104
+ with_unique = (with.keys - on.keys)
105
+ shared = (with.keys & on.keys)
106
+ shared.each { |k|
107
+ diff(with[k], on[k], level: level+1, parents: parents + [k])
108
+ }
109
+ on_unique.each { |k|
110
+ changes << "- ".red+PP.pp({k => on[k] }, '')
111
+ }
112
+ with_unique.each { |k|
113
+ changes << "+ ".green+PP.pp({k => with[k]}, '')
114
+ }
115
+ elsif on.is_a?(Array)
116
+ return if with == on
117
+ # special case- Basket of Kittens lists of declared resources of a type;
118
+ # we use this to decide if we can compare two array elements as if they
119
+ # should be equivalent
120
+ # We also implement comparison operators for {Hash} and our various
121
+ # custom objects which we might find in here so that we can get away with
122
+ # sorting arrays full of weird, non-primitive types.
123
+ done = []
124
+ # before_a = on.dup
125
+ # after_a = on.dup.sort
126
+ # before_b = with.dup
127
+ # after_b = with.dup.sort
128
+ on.sort.each { |elt|
129
+ if elt.is_a?(Hash) and elt['name'] or elt['entity']# or elt['cloud_id']
130
+ with.sort.each { |other_elt|
131
+ if (elt['name'] and other_elt['name'] == elt['name']) or
132
+ (elt['name'].nil? and !elt["id"].nil? and elt["id"] == other_elt["id"]) or
133
+ (elt['name'].nil? and elt["id"].nil? and
134
+ !elt["entity"].nil? and !other_elt["entity"].nil? and
135
+ (
136
+ (elt["entity"]["id"] and elt["entity"]["id"] == other_elt["entity"]["id"]) or
137
+ (elt["entity"]["name"] and elt["entity"]["name"] == other_elt["entity"]["name"])
138
+ )
139
+ )
140
+ break if elt == other_elt
141
+ done << elt
142
+ done << other_elt
143
+ namestr = if elt['type']
144
+ "#{elt['type']}[#{elt['name']}]"
145
+ elsif elt['name']
146
+ elt['name']
147
+ elsif elt['entity'] and elt["entity"]["id"]
148
+ elt['entity']['id']
149
+ end
150
+
151
+ diff(other_elt, elt, level: level+1, parents: parents + [namestr])
152
+ break
153
+ end
154
+ }
155
+ end
156
+ }
157
+ on_unique = (on - with) - done
158
+ with_unique = (with - on) - done
159
+ # if on_unique.size > 0 or with_unique.size > 0
160
+ # if before_a != after_a
161
+ # MU.log "A BEFORE", MU::NOTICE, details: before_a
162
+ # MU.log "A AFTER", MU::NOTICE, details: after_a
163
+ # end
164
+ # if before_b != after_b
165
+ # MU.log "B BEFORE", MU::NOTICE, details: before_b
166
+ # MU.log "B AFTER", MU::NOTICE, details: after_b
167
+ # end
168
+ # end
169
+ on_unique.each { |e|
170
+ changes << if e.is_a?(Hash)
171
+ "- ".red+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
172
+ else
173
+ "- ".red+e.to_s
174
+ end
175
+ }
176
+ with_unique.each { |e|
177
+ changes << if e.is_a?(Hash)
178
+ "+ ".green+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
179
+ else
180
+ "+ ".green+e.to_s
181
+ end
182
+ }
183
+ else
184
+ if on != with
185
+ changes << "-".red+" #{on.to_s}"
186
+ changes << "+".green+" #{with.to_s}"
187
+ end
188
+ end
189
+
190
+ if changes.size > 0
191
+ puts tree
192
+ changes.each { |c|
193
+ puts indent+c
194
+ }
195
+ end
196
+ end
197
+
198
+ # Implement a merge! that just updates each hash leaf as needed, not
199
+ # trashing the branch on the way there.
200
+ def deep_merge!(with, on = self)
201
+
202
+ if on and with and with.is_a?(Hash)
203
+ with.each_pair { |k, v|
204
+ if !on[k] or !on[k].is_a?(Hash)
205
+ on[k] = v
206
+ else
207
+ deep_merge!(with[k], on[k])
208
+ end
209
+ }
210
+ elsif with
211
+ on = with
212
+ end
213
+
214
+ on
215
+ end
216
+ end
217
+
39
218
  ENV['HOME'] = Etc.getpwuid(Process.uid).dir
40
219
 
41
220
  require 'mu/logger'
42
221
  module MU
43
222
 
223
+ # Subclass core thread so we can gracefully handle it when we hit system
224
+ # thread limits. Back off and wait makes sense for us, since most of our
225
+ # threads are terminal (in the dependency sense) and this is unlikely to get
226
+ # us deadlocks.
227
+ class Thread < ::Thread
228
+ @@mu_global_threads = []
229
+ @@mu_global_thread_semaphore = Mutex.new
230
+
231
+ def initialize(*args, &block)
232
+ @@mu_global_thread_semaphore.synchronize {
233
+ @@mu_global_threads.reject! { |t| t.nil? or !t.status }
234
+ }
235
+ newguy = nil
236
+ start = Time.now
237
+ begin
238
+ newguy = super(*args, &block)
239
+ if newguy.nil?
240
+ MU.log "I somehow got a nil trying to create a thread", MU::WARN, details: caller
241
+ sleep 1
242
+ end
243
+ rescue ::ThreadError => e
244
+ if e.message.match(/Resource temporarily unavailable/)
245
+ toomany = @@mu_global_threads.size
246
+ MU.log "Hit the wall at #{toomany.to_s} threads, waiting until there are fewer", MU::WARN
247
+ if @@mu_global_threads.size >= toomany
248
+ sleep 1
249
+ begin
250
+ @@mu_global_thread_semaphore.synchronize {
251
+ @@mu_global_threads.each { |t|
252
+ next if t == ::Thread.current
253
+ t.join(0.1)
254
+ }
255
+ @@mu_global_threads.reject! { |t| t.nil? or !t.status }
256
+ }
257
+ if (Time.now - start) > 150
258
+ MU.log "Failed to get a free thread slot after 150 seconds- are we in a deadlock situation?", MU::ERR, details: caller
259
+ raise e
260
+ end
261
+ end while @@mu_global_threads.size >= toomany
262
+ end
263
+ retry
264
+ else
265
+ raise e
266
+ end
267
+ end while newguy.nil?
268
+
269
+ @@mu_global_thread_semaphore.synchronize {
270
+ @@mu_global_threads << newguy
271
+ }
272
+
273
+ end
274
+ end
275
+
44
276
  # Wrapper class for fatal Exceptions. Gives our internals something to
45
277
  # inherit that will log an error message appropriately before bubbling up.
46
278
  class MuError < StandardError
47
279
  def initialize(message = nil)
48
- MU.log message, MU::ERR if !message.nil?
280
+ MU.log message, MU::ERR, details: caller[2] if !message.nil?
49
281
  if MU.verbosity == MU::Logger::SILENT
50
- super
51
- else
52
282
  super ""
283
+ else
284
+ super message
53
285
  end
54
286
  end
55
287
  end
@@ -60,9 +292,9 @@ module MU
60
292
  def initialize(message = nil)
61
293
  MU.log message, MU::NOTICE if !message.nil?
62
294
  if MU.verbosity == MU::Logger::SILENT
63
- super
64
- else
65
295
  super ""
296
+ else
297
+ super message
66
298
  end
67
299
  end
68
300
  end
@@ -80,11 +312,37 @@ module MU
80
312
  @@myRoot
81
313
  end
82
314
 
315
+ # utility routine for sorting semantic versioning strings
316
+ def self.version_sort(a, b)
317
+ a_parts = a.split(/[^a-z0-9]/)
318
+ b_parts = b.split(/[^a-z0-9]/)
319
+ for i in 0..a_parts.size
320
+ matchval = if a_parts[i] and b_parts[i] and
321
+ a_parts[i].match(/^\d+/) and b_parts[i].match(/^\d+/)
322
+ a_parts[i].to_i <=> b_parts[i].to_i
323
+ elsif a_parts[i] and !b_parts[i]
324
+ 1
325
+ elsif !a_parts[i] and b_parts[i]
326
+ -1
327
+ else
328
+ a_parts[i] <=> b_parts[i]
329
+ end
330
+ return matchval if matchval != 0
331
+ end
332
+ 0
333
+ end
334
+
83
335
  # Front our global $MU_CFG hash with a read-only copy
84
336
  def self.muCfg
85
337
  Marshal.load(Marshal.dump($MU_CFG)).freeze
86
338
  end
87
339
 
340
+ # Returns true if we're running without a full systemwide Mu Master install,
341
+ # typically as a gem.
342
+ def self.localOnly
343
+ ((Gem.paths and Gem.paths.home and File.realpath(File.expand_path(File.dirname(__FILE__))).match(/^#{Gem.paths.home}/)) or !Dir.exist?("/opt/mu"))
344
+ end
345
+
88
346
  # The main (root) Mu user's data directory.
89
347
  @@mainDataDir = File.expand_path(@@myRoot+"/../var")
90
348
  # The main (root) Mu user's data directory.
@@ -133,6 +391,7 @@ module MU
133
391
  # Copy the set of global variables in use by another thread, typically our
134
392
  # parent thread.
135
393
  def self.dupGlobals(parent_thread_id)
394
+ @@globals[parent_thread_id] ||= {}
136
395
  @@globals[parent_thread_id].each_pair { |name, value|
137
396
  setVar(name, value)
138
397
  }
@@ -215,13 +474,15 @@ module MU
215
474
  @myDataDir = @@mainDataDir if @myDataDir.nil?
216
475
  # Mu's deployment metadata directory.
217
476
  def self.dataDir(for_user = MU.mu_user)
218
- if for_user.nil? or for_user.empty? or for_user == "mu" or for_user == "root"
477
+ if !localOnly and
478
+ ((Process.uid == 0 and (for_user.nil? or for_user.empty?)) or
479
+ for_user == "mu" or for_user == "root")
219
480
  return @myDataDir
220
481
  else
221
482
  for_user ||= MU.mu_user
222
483
  basepath = Etc.getpwnam(for_user).dir+"/.mu"
223
- Dir.mkdir(basepath, 0755) if !Dir.exists?(basepath)
224
- Dir.mkdir(basepath+"/var", 0755) if !Dir.exists?(basepath+"/var")
484
+ Dir.mkdir(basepath, 0755) if !Dir.exist?(basepath)
485
+ Dir.mkdir(basepath+"/var", 0755) if !Dir.exist?(basepath+"/var")
225
486
  return basepath+"/var"
226
487
  end
227
488
  end
@@ -233,14 +494,24 @@ module MU
233
494
  end
234
495
  @@globals[Thread.current.object_id]['verbosity']
235
496
  end
497
+
498
+ # The color logging flag merits a default value.
499
+ def self.color
500
+ if @@globals[Thread.current.object_id].nil? or @@globals[Thread.current.object_id]['color'].nil?
501
+ MU.setVar("color", true)
502
+ end
503
+ @@globals[Thread.current.object_id]['color']
504
+ end
236
505
 
237
506
  # Set parameters parameters for calls to {MU#log}
238
- def self.setLogging(verbosity, webify_logs = false, handle = STDOUT)
507
+ def self.setLogging(verbosity, webify_logs = false, handle = STDOUT, color = true)
239
508
  MU.setVar("verbosity", verbosity)
240
- @@logger ||= MU::Logger.new(verbosity, webify_logs, handle)
509
+ MU.setVar("color", color)
510
+ @@logger ||= MU::Logger.new(verbosity, webify_logs, handle, color)
241
511
  @@logger.html = webify_logs
242
512
  @@logger.verbosity = verbosity
243
513
  @@logger.handle = handle
514
+ @@logger.color = color
244
515
  end
245
516
 
246
517
  setLogging(MU::Logger::NORMAL, false)
@@ -252,7 +523,7 @@ module MU
252
523
  end
253
524
 
254
525
  # Shortcut to invoke {MU::Logger#log}
255
- def self.log(msg, level = MU::INFO, details: nil, html: html = false, verbosity: MU.verbosity)
526
+ def self.log(msg, level = MU::INFO, details: nil, html: false, verbosity: MU.verbosity, color: true)
256
527
  return if (level == MU::DEBUG and verbosity <= MU::Logger::LOUD)
257
528
  return if verbosity == MU::Logger::SILENT
258
529
 
@@ -275,9 +546,9 @@ module MU
275
546
  extra = Hash.new if extra.nil?
276
547
  extra[:details] = details
277
548
  end
278
- @@logger.log(msg, level, details: extra, verbosity: MU::Logger::LOUD, html: html)
549
+ @@logger.log(msg, level, details: extra, verbosity: MU::Logger::LOUD, html: html, color: color)
279
550
  else
280
- @@logger.log(msg, level, html: html, verbosity: verbosity)
551
+ @@logger.log(msg, level, html: html, verbosity: verbosity, color: color)
281
552
  end
282
553
  end
283
554
 
@@ -308,49 +579,66 @@ module MU
308
579
  require 'mu/groomer'
309
580
 
310
581
  # Little hack to initialize library-only environments' config files
311
- if !$MU_CFG
312
- require "#{@@myRoot}/bin/mu-load-config.rb"
313
-
314
- if !$MU_CFG['auto_detection_done'] and (!$MU_CFG['multiuser'] or !cfgExists?)
315
- MU.log "Auto-detecting cloud providers"
316
- new_cfg = $MU_CFG.dup
317
- examples = {}
318
- MU::Cloud.supportedClouds.each { |cloud|
319
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
320
- begin
321
- if cloudclass.hosted? and !$MU_CFG[cloud.downcase]
322
- cfg_blob = cloudclass.hosted_config
323
- if cfg_blob
324
- new_cfg[cloud.downcase] = cfg_blob
325
- MU.log "Adding #{cloud} stanza to #{cfgPath}", MU::NOTICE
326
- end
327
- elsif !$MU_CFG[cloud.downcase] and !cloudclass.config_example.nil?
328
- examples[cloud.downcase] = cloudclass.config_example
582
+ def self.detectCloudProviders
583
+ MU.log "Auto-detecting cloud providers"
584
+ new_cfg = $MU_CFG.dup
585
+ examples = {}
586
+ MU::Cloud.supportedClouds.each { |cloud|
587
+ cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
588
+ begin
589
+ if cloudclass.hosted? and !$MU_CFG[cloud.downcase]
590
+ cfg_blob = cloudclass.hosted_config
591
+ if cfg_blob
592
+ new_cfg[cloud.downcase] = cfg_blob
593
+ MU.log "Adding auto-detected #{cloud} stanza", MU::NOTICE
329
594
  end
330
- rescue NoMethodError => e
331
- # missing .hosted? is normal for dummy layers like CloudFormation
332
- MU.log e.message, MU::WARN
595
+ elsif !$MU_CFG[cloud.downcase] and !cloudclass.config_example.nil?
596
+ examples[cloud.downcase] = cloudclass.config_example
333
597
  end
334
- }
335
- new_cfg['auto_detection_done'] = true
336
- if new_cfg != $MU_CFG or !cfgExists?
337
- MU.log "Generating #{cfgPath}"
338
- saveMuConfig(new_cfg, examples) # XXX and reload it
598
+ rescue NoMethodError => e
599
+ # missing .hosted? is normal for dummy layers like CloudFormation
600
+ MU.log e.message, MU::WARN
339
601
  end
602
+ }
603
+ new_cfg['auto_detection_done'] = true
604
+ if new_cfg != $MU_CFG or !cfgExists?
605
+ MU.log "Generating #{cfgPath}"
606
+ saveMuConfig(new_cfg, examples) # XXX and reload it
340
607
  end
608
+ new_cfg
609
+ end
610
+
611
+ if !$MU_CFG
612
+ require "#{@@myRoot}/bin/mu-load-config.rb"
613
+ if !$MU_CFG['auto_detection_done'] and (!$MU_CFG['multiuser'] or !cfgExists?)
614
+ detectCloudProviders
615
+ end
616
+ end
617
+
618
+ @@mommacat_port = 2260
619
+ if !$MU_CFG.nil? and !$MU_CFG['mommacat_port'].nil? and
620
+ !$MU_CFG['mommacat_port'] != "" and $MU_CFG['mommacat_port'].to_i > 0 and
621
+ $MU_CFG['mommacat_port'].to_i < 65536
622
+ @@mommacat_port = $MU_CFG['mommacat_port'].to_i
623
+ end
624
+ # The port on which the Momma Cat daemon should listen for requests
625
+ # @return [Integer]
626
+ def self.mommaCatPort
627
+ @@mommacat_port
341
628
  end
342
629
 
343
630
  @@my_private_ip = nil
344
631
  @@my_public_ip = nil
345
632
  @@mu_public_addr = nil
346
633
  @@mu_public_ip = nil
347
- if $MU_CFG['aws'] # XXX this should be abstracted to elsewhere
634
+ if MU::Cloud::AWS.hosted?
348
635
  @@my_private_ip = MU::Cloud::AWS.getAWSMetaData("local-ipv4")
349
636
  @@my_public_ip = MU::Cloud::AWS.getAWSMetaData("public-ipv4")
350
637
  @@mu_public_addr = @@my_public_ip
351
638
  @@mu_public_ip = @@my_public_ip
352
639
  end
353
- if !$MU_CFG.nil? and !$MU_CFG['public_address'].nil? and !$MU_CFG['public_address'].empty? and @@my_public_ip != $MU_CFG['public_address']
640
+ if !$MU_CFG.nil? and !$MU_CFG['public_address'].nil? and
641
+ !$MU_CFG['public_address'].empty? and @@my_public_ip != $MU_CFG['public_address']
354
642
  @@mu_public_addr = $MU_CFG['public_address']
355
643
  if !@@mu_public_addr.match(/^\d+\.\d+\.\d+\.\d+$/)
356
644
  resolver = Resolv::DNS.new
@@ -400,7 +688,9 @@ module MU
400
688
  def self.userEmail(user = MU.mu_user)
401
689
  @userlist ||= MU::Master.listUsers
402
690
  user = "mu" if user == "root"
403
- if Dir.exists?("#{MU.mainDataDir}/users/#{user}")
691
+ if Dir.exist?("#{MU.mainDataDir}/users/#{user}") and
692
+ File.readable?("#{MU.mainDataDir}/users/#{user}/email") and
693
+ File.size?("#{MU.mainDataDir}/users/#{user}/email")
404
694
  return File.read("#{MU.mainDataDir}/users/#{user}/email").chomp
405
695
  elsif @userlist.has_key?(user)
406
696
  return @userlist[user]['email']
@@ -413,7 +703,9 @@ module MU
413
703
  # Fetch the real-world name of a given Mu user
414
704
  def self.userName(user = MU.mu_user)
415
705
  @userlist ||= MU::Master.listUsers
416
- if Dir.exists?("#{MU.mainDataDir}/users/#{user}")
706
+ if Dir.exist?("#{MU.mainDataDir}/users/#{user}") and
707
+ File.readable?("#{MU.mainDataDir}/users/#{user}/realname") and
708
+ File.size?("#{MU.mainDataDir}/users/#{user}/realname")
417
709
  return File.read("#{MU.mainDataDir}/users/#{user}/realname").chomp
418
710
  elsif @userlist.has_key?(user)
419
711
  return @userlist[user]['email']
@@ -430,6 +722,14 @@ module MU
430
722
  ["Chef", "Ansible"]
431
723
  end
432
724
 
725
+ # The version of Chef we will install on nodes.
726
+ @@chefVersion = "14.0.190"
727
+ # The version of Chef we will install on nodes.
728
+ # @return [String]
729
+ def self.chefVersion
730
+ @@chefVersion
731
+ end
732
+
433
733
  MU.supportedGroomers.each { |groomer|
434
734
  require "mu/groomers/#{groomer.downcase}"
435
735
  }
@@ -451,6 +751,8 @@ module MU
451
751
  @@myRegion_var = zone.gsub(/^.*?\/|\-\d+$/, "")
452
752
  elsif MU::Cloud::AWS.hosted?
453
753
  @@myRegion_var ||= MU::Cloud::AWS.myRegion
754
+ elsif MU::Cloud::Azure.hosted?
755
+ @@myRegion_var ||= MU::Cloud::Azure.myRegion
454
756
  else
455
757
  @@myRegion_var = nil
456
758
  end
@@ -458,6 +760,7 @@ module MU
458
760
  end
459
761
 
460
762
  require 'mu/config'
763
+ require 'mu/adoption'
461
764
 
462
765
  # Figure out what cloud provider we're in, if any.
463
766
  # @return [String]: Google, AWS, etc. Returns nil if we don't seem to be in a cloud.
@@ -468,6 +771,10 @@ module MU
468
771
  elsif MU::Cloud::AWS.hosted?
469
772
  @@myInstanceId = MU::Cloud::AWS.getAWSMetaData("instance-id")
470
773
  return "AWS"
774
+ elsif MU::Cloud::Azure.hosted?
775
+ metadata = MU::Cloud::Azure.get_metadata()["compute"]
776
+ @@myInstanceId = MU::Cloud::Azure::Id.new("/subscriptions/"+metadata["subscriptionId"]+"/resourceGroups/"+metadata["resourceGroupName"]+"/providers/Microsoft.Compute/virtualMachines/"+metadata["name"])
777
+ return "Azure"
471
778
  end
472
779
  nil
473
780
  end
@@ -513,63 +820,105 @@ module MU
513
820
  @@myAZ_var
514
821
  end
515
822
 
516
- @@myCloudDescriptor = nil
517
- if MU::Cloud::Google.hosted?
518
- @@myCloudDescriptor = MU::Cloud::Google.compute.get_instance(
519
- MU::Cloud::Google.myProject,
520
- MU.myAZ,
521
- MU.myInstanceId
522
- )
523
- elsif MU::Cloud::AWS.hosted?
823
+ # Recursively turn a Ruby OpenStruct into a Hash
824
+ # @param struct [OpenStruct]
825
+ # @param stringify_keys [Boolean]
826
+ # @return [Hash]
827
+ def self.structToHash(struct, stringify_keys: false)
828
+ google_struct = false
524
829
  begin
525
- @@myCloudDescriptor = MU::Cloud::AWS.ec2(region: MU.myRegion).describe_instances(instance_ids: [MU.myInstanceId]).reservations.first.instances.first
526
- rescue Aws::EC2::Errors::InvalidInstanceIDNotFound => e
527
- rescue Aws::Errors::MissingCredentialsError => e
528
- MU.log "I'm hosted in AWS, but I can't make API calls. Does this instance have an appropriate IAM profile?", MU::WARN
830
+ google_struct = struct.class.ancestors.include?(::Google::Apis::Core::Hashable)
831
+ rescue NameError
529
832
  end
530
- end
531
833
 
834
+ aws_struct = false
835
+ begin
836
+ aws_struct = struct.class.ancestors.include?(::Seahorse::Client::Response)
837
+ rescue NameError
838
+ end
532
839
 
533
- @@myVPC_var = nil
534
- # The VPC/Network in which this Mu master resides
535
- # XXX account for Google and non-cloud situations
536
- def self.myVPC
537
- return nil if MU.myCloudDescriptor.nil?
840
+ azure_struct = false
538
841
  begin
539
- if MU::Cloud::AWS.hosted?
540
- @@myVPC_var ||= MU.myCloudDescriptor.vpc_id
541
- elsif MU::Cloud::Google.hosted?
542
- @@myVPC_var = MU.myCloudDescriptor.network_interfaces.first.network.gsub(/.*?\/([^\/]+)$/, '\1')
842
+ azure_struct = struct.class.ancestors.include?(::MsRestAzure) or struct.class.name.match(/Azure::.*?::Mgmt::.*?::Models::/)
843
+ rescue NameError
844
+ end
845
+
846
+ if struct.is_a?(Struct) or struct.class.ancestors.include?(Struct) or
847
+ google_struct or aws_struct or azure_struct
848
+
849
+ hash = if azure_struct
850
+ MU::Cloud::Azure.respToHash(struct)
543
851
  else
544
- nil
852
+ struct.to_h
853
+ end
854
+
855
+ if stringify_keys
856
+ newhash = {}
857
+ hash.each_pair { |k, v|
858
+ newhash[k.to_s] = v
859
+ }
860
+ hash = newhash
545
861
  end
546
- rescue Aws::EC2::Errors::InternalError => e
547
- MU.log "Got #{e.inspect} on MU::Cloud::AWS.ec2(region: #{MU.myRegion}).describe_instances(instance_ids: [#{@@myInstanceId}])", MU::WARN
548
- sleep 10
862
+
863
+ hash.each_pair { |key, value|
864
+ hash[key] = self.structToHash(value, stringify_keys: stringify_keys)
865
+ }
866
+ return hash
867
+ elsif struct.is_a?(MU::Config::Ref)
868
+ struct = struct.to_h
869
+ elsif struct.is_a?(MU::Cloud::Azure::Id)
870
+ struct = struct.to_s
871
+ elsif struct.is_a?(Hash)
872
+ if stringify_keys
873
+ newhash = {}
874
+ struct.each_pair { |k, v|
875
+ newhash[k.to_s] = v
876
+ }
877
+ struct = newhash
878
+ end
879
+ struct.each_pair { |key, value|
880
+ struct[key] = self.structToHash(value, stringify_keys: stringify_keys)
881
+ }
882
+ return struct
883
+ elsif struct.is_a?(Array)
884
+ struct.map! { |elt|
885
+ self.structToHash(elt, stringify_keys: stringify_keys)
886
+ }
887
+ elsif struct.is_a?(String)
888
+ # Cleanse weird encoding problems
889
+ return struct.dup.to_s.force_encoding("ASCII-8BIT").encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
890
+ else
891
+ return struct
549
892
  end
550
- @@myVPC_var
551
893
  end
552
894
 
553
- @@mySubnets_var = nil
554
- # The AWS Subnets associated with the VPC this MU Master is in
555
- # XXX account for Google and non-cloud situations
556
- def self.mySubnets
557
- @@mySubnets_var ||= MU::Cloud::AWS.ec2(region: MU.myRegion).describe_subnets(
558
- filters: [
559
- {
560
- name: "vpc-id",
561
- values: [MU.myVPC]
562
- }
563
- ]
564
- ).subnets
895
+ @@myCloudDescriptor = nil
896
+ if MU.myCloud
897
+ found = MU::MommaCat.findStray(MU.myCloud, "server", cloud_id: @@myInstanceId, dummy_ok: true, region: MU.myRegion)
898
+ if !found.nil? and found.size == 1
899
+ @@myCloudDescriptor = found.first.cloud_desc
900
+ end
565
901
  end
566
902
 
567
- # The version of Chef we will install on nodes.
568
- @@chefVersion = "14.0.190"
569
- # The version of Chef we will install on nodes.
570
- # @return [String]
571
- def self.chefVersion;
572
- @@chefVersion
903
+
904
+ @@myVPCObj_var = nil
905
+ # The VPC/Network in which this Mu master resides
906
+ def self.myVPCObj
907
+ return nil if MU.myCloud.nil?
908
+ return @@myVPCObj_var if @@myVPCObj_var
909
+ cloudclass = const_get("MU").const_get("Cloud").const_get(MU.myCloud)
910
+ @@myVPCObj_var ||= cloudclass.myVPCObj
911
+ @@myVPCObj_var
912
+ end
913
+
914
+ @@myVPC_var = nil
915
+ # The VPC/Network in which this Mu master resides
916
+ def self.myVPC
917
+ return nil if MU.myCloud.nil?
918
+ return @@myVPC_var if @@myVPC_var
919
+ my_vpc_desc = MU.myVPCObj
920
+ @@myVPC_var ||= my_vpc_desc.cloud_id if my_vpc_desc
921
+ @@myVPC_var
573
922
  end
574
923
 
575
924
  # Mu's SSL certificate directory
@@ -590,20 +939,20 @@ module MU
590
939
  # @return [Boolean]
591
940
  def self.hashCmp(hash1, hash2, missing_is_default: false)
592
941
  return false if hash1.nil?
593
- hash2.each_pair { |k, v|
942
+ hash2.keys.each { |k|
594
943
  if hash1[k].nil?
595
944
  return false
596
945
  end
597
946
  }
598
947
  if !missing_is_default
599
- hash1.each_pair { |k, v|
948
+ hash1.keys.each { |k|
600
949
  if hash2[k].nil?
601
950
  return false
602
951
  end
603
952
  }
604
953
  end
605
954
 
606
- hash1.each_pair { |k, v|
955
+ hash1.keys.each { |k|
607
956
  if hash1[k].is_a?(Array)
608
957
  return false if !missing_is_default and hash2[k].nil?
609
958
  if !hash2[k].nil?
@@ -654,75 +1003,21 @@ module MU
654
1003
  end
655
1004
 
656
1005
 
657
- # Recursively turn a Ruby OpenStruct into a Hash
658
- # @param struct [OpenStruct]
659
- # @param stringify_keys [Boolean]
660
- # @return [Hash]
661
- def self.structToHash(struct, stringify_keys: false)
662
- google_struct = false
663
- begin
664
- google_struct = struct.class.ancestors.include?(::Google::Apis::Core::Hashable)
665
- rescue NameError
666
- end
667
-
668
- aws_struct = false
669
- begin
670
- aws_struct = struct.class.ancestors.include?(::Seahorse::Client::Response)
671
- rescue NameError
672
- end
673
-
674
- if struct.is_a?(Struct) or struct.class.ancestors.include?(Struct) or
675
- google_struct or aws_struct
676
-
677
- hash = struct.to_h
678
- if stringify_keys
679
- newhash = {}
680
- hash.each_pair { |k, v|
681
- newhash[k.to_s] = v
682
- }
683
- hash = newhash
684
- end
685
-
686
- hash.each_pair { |key, value|
687
- hash[key] = self.structToHash(value, stringify_keys: stringify_keys)
688
- }
689
- return hash
690
- elsif struct.is_a?(Hash)
691
- if stringify_keys
692
- newhash = {}
693
- struct.each_pair { |k, v|
694
- newhash[k.to_s] = v
695
- }
696
- struct = newhash
697
- end
698
- struct.each_pair { |key, value|
699
- struct[key] = self.structToHash(value, stringify_keys: stringify_keys)
700
- }
701
- return struct
702
- elsif struct.is_a?(Array)
703
- struct.map! { |elt|
704
- self.structToHash(elt, stringify_keys: stringify_keys)
705
- }
706
- else
707
- return struct
708
- end
709
- end
710
-
711
1006
  # Generate a random password which will satisfy the complexity requirements of stock Amazon Windows AMIs.
712
1007
  # return [String]: A password string.
713
- def self.generateWindowsPassword
1008
+ def self.generateWindowsPassword(safe_pattern: '~!@#%^&*_-+=`|(){}[]:;<>,.?', retries: 25)
714
1009
  # We have dopey complexity requirements, be stringent here.
715
1010
  # I'll be nice and not condense this into one elegant-but-unreadable regular expression
716
1011
  attempts = 0
717
- safe_metachars = Regexp.escape('~!@#%^&*_-+=`|(){}[]:;<>,.?')
1012
+ safe_metachars = Regexp.escape(safe_pattern)
718
1013
  begin
719
- if attempts > 25
1014
+ if attempts > retries
720
1015
  MU.log "Failed to generate an adequate Windows password after #{attempts}", MU::ERR
721
1016
  raise MuError, "Failed to generate an adequate Windows password after #{attempts}"
722
1017
  end
723
1018
  winpass = Password.random(14..16)
724
1019
  attempts += 1
725
- end while winpass.nil? or !winpass.match(/[A-Z]/) or !winpass.match(/[a-z]/) or !winpass.match(/\d/) or !winpass.match(/[#{safe_metachars}]/) or winpass.match(/[^\w\d#{safe_metachars}]/)
1020
+ end while winpass.nil? or !winpass.match(/^[a-z]/i) or !winpass.match(/[A-Z]/) or !winpass.match(/[a-z]/) or !winpass.match(/\d/) or !winpass.match(/[#{safe_metachars}]/) or winpass.match(/[^\w\d#{safe_metachars}]/)
726
1021
 
727
1022
  MU.log "Generated Windows password after #{attempts} attempts", MU::DEBUG
728
1023
  return winpass