cloud-mu 3.1.5 → 3.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -30,6 +30,21 @@ module MU
30
30
  ["Chef", "Ansible"]
31
31
  end
32
32
 
33
+ # List of known/supported groomers which are installed and appear to be working
34
+ # @return [Array<String>]
35
+ def self.availableGroomers
36
+ available = []
37
+ MU::Groomer.supportedGroomers.each { |groomer|
38
+ begin
39
+ groomerbase = loadGroomer(groomer)
40
+ available << groomer if groomerbase.available?
41
+ rescue LoadError
42
+ end
43
+ }
44
+
45
+ available
46
+ end
47
+
33
48
  # Instance methods that any Groomer plugin must implement
34
49
  def self.requiredMethods
35
50
  [:preClean, :bootstrap, :haveBootstrapped?, :run, :saveDeployData, :getSecret, :saveSecret, :deleteSecret, :reinstall]
@@ -37,7 +52,7 @@ module MU
37
52
 
38
53
  # Class methods that any Groomer plugin must implement
39
54
  def self.requiredClassMethods
40
- [:getSecret, :cleanup, :saveSecret, :deleteSecret]
55
+ [:getSecret, :cleanup, :saveSecret, :deleteSecret, :available?]
41
56
  end
42
57
 
43
58
  class Ansible;
@@ -24,6 +24,10 @@ module MU
24
24
  class NoAnsibleExecError < MuError;
25
25
  end
26
26
 
27
+ # One or more Python dependencies missing
28
+ class AnsibleLibrariesError < MuError;
29
+ end
30
+
27
31
  # Location in which we'll find our Ansible executables. This only applies
28
32
  # to full-grown Mu masters; minimalist gem installs will have to make do
29
33
  # with whatever Ansible executables they can find in $PATH.
@@ -40,6 +44,10 @@ module MU
40
44
  @ansible_path = node.deploy.deploy_dir+"/ansible"
41
45
  @ansible_execs = MU::Groomer::Ansible.ansibleExecDir
42
46
 
47
+ if !MU::Groomer::Ansible.checkPythonDependencies(@server.windows?)
48
+ raise AnsibleLibrariesError, "One or more python dependencies not available"
49
+ end
50
+
43
51
  if !@ansible_execs or @ansible_execs.empty?
44
52
  raise NoAnsibleExecError, "No Ansible executables found in visible paths"
45
53
  end
@@ -54,6 +62,10 @@ module MU
54
62
  installRoles
55
63
  end
56
64
 
65
+ # Are Ansible executables and key libraries present and accounted for?
66
+ def self.available?(windows = false)
67
+ MU::Groomer::Ansible.checkPythonDependencies(windows)
68
+ end
57
69
 
58
70
  # Indicate whether our server has been bootstrapped with Ansible
59
71
  def haveBootstrapped?
@@ -245,7 +257,7 @@ module MU
245
257
  "#{@server.config['name']}.yml"
246
258
  end
247
259
 
248
- cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
260
+ cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.windows? ? @server.canonicalIP : @server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
249
261
 
250
262
  retries = 0
251
263
  begin
@@ -294,7 +306,7 @@ module MU
294
306
  # Bootstrap our server with Ansible- basically, just make sure this node
295
307
  # is listed in our deployment's Ansible inventory.
296
308
  def bootstrap
297
- @inventory.add(@server.config['name'], @server.mu_name)
309
+ @inventory.add(@server.config['name'], @server.windows? ? @server.canonicalIP : @server.mu_name)
298
310
  play = {
299
311
  "hosts" => @server.config['name']
300
312
  }
@@ -387,11 +399,18 @@ module MU
387
399
  allvars['deployment']
388
400
  end
389
401
 
402
+ # Nuke everything associated with a deploy. Since we're just some files
403
+ # in the deploy directory, this doesn't have to do anything.
404
+ def self.cleanup(deploy_id, noop = false)
405
+ # deploy = MU::MommaCat.new(MU.deploy_id)
406
+ # inventory = Inventory.new(deploy)
407
+ end
408
+
390
409
  # Expunge Ansible resources associated with a node.
391
410
  # @param node [String]: The Mu name of the node in question.
392
411
  # @param _vaults_to_clean [Array<Hash>]: Dummy argument, part of this method's interface but not used by the Ansible layer
393
412
  # @param noop [Boolean]: Skip actual deletion, just state what we'd do
394
- def self.cleanup(node, _vaults_to_clean = [], noop = false)
413
+ def self.purge(node, _vaults_to_clean = [], noop = false)
395
414
  deploy = MU::MommaCat.new(MU.deploy_id)
396
415
  inventory = Inventory.new(deploy)
397
416
  # ansible_path = deploy.deploy_dir+"/ansible"
@@ -427,6 +446,50 @@ module MU
427
446
  output
428
447
  end
429
448
 
449
+ # Hunt down and return a path for a Python executable
450
+ # @return [String]
451
+ def self.pythonExecDir
452
+ path = nil
453
+
454
+ if File.exist?(BINDIR+"/python")
455
+ path = BINDIR
456
+ else
457
+ paths = [ansibleExecDir]
458
+ paths.concat(ENV['PATH'].split(/:/))
459
+ paths << "/usr/bin" # not always in path, esp in pared-down Docker images
460
+ paths.reject! { |p| p.nil? }
461
+ paths.uniq.each { |bindir|
462
+ if File.exist?(bindir+"/python")
463
+ path = bindir
464
+ break
465
+ end
466
+ }
467
+ end
468
+ path
469
+ end
470
+
471
+ # Make sure what's in our Python requirements.txt is reflected in the
472
+ # Python we're about to run for Ansible
473
+ def self.checkPythonDependencies(windows = false)
474
+ return nil if !ansibleExecDir
475
+
476
+ execline = File.readlines(ansibleExecDir+"/ansible-playbook").first.chomp.sub(/^#!/, '')
477
+ if !execline
478
+ MU.log "Unable to extract a Python executable from #{ansibleExecDir}/ansible-playbook", MU::ERR
479
+ return false
480
+ end
481
+
482
+ require 'tempfile'
483
+ f = Tempfile.new("pythoncheck")
484
+ f.puts "import ansible"
485
+ f.puts "import winrm" if windows
486
+ f.close
487
+
488
+ system(%Q{#{execline} #{f.path}})
489
+ f.unlink
490
+ $?.exitstatus == 0 ? true : false
491
+ end
492
+
430
493
  # Hunt down and return a path for Ansible executables
431
494
  # @return [String]
432
495
  def self.ansibleExecDir
@@ -434,7 +497,9 @@ module MU
434
497
  if File.exist?(BINDIR+"/ansible-playbook")
435
498
  path = BINDIR
436
499
  else
437
- ENV['PATH'].split(/:/).each { |bindir|
500
+ paths = ENV['PATH'].split(/:/)
501
+ paths << "/usr/bin"
502
+ paths.uniq.each { |bindir|
438
503
  if File.exist?(bindir+"/ansible-playbook")
439
504
  path = bindir
440
505
  if !File.exist?(bindir+"/ansible-vault")
@@ -35,6 +35,12 @@ module MU
35
35
  end
36
36
  }
37
37
 
38
+ # Are the Chef libraries present and accounted for?
39
+ def self.available?(windows = false)
40
+ loadChefLib
41
+ @chefloaded
42
+ end
43
+
38
44
  @chefloaded = false
39
45
  @chefload_semaphore = Mutex.new
40
46
  # Autoload is too brain-damaged to get Chef's subclasses/submodules, so
@@ -362,7 +368,7 @@ module MU
362
368
  }
363
369
 
364
370
  if resp.exitcode == 1 and output_lines.join("\n").match(/Chef Client finished/)
365
- MU.log "resp.exit code 1"
371
+ MU.log output_lines.last
366
372
  elsif resp.exitcode != 0
367
373
  raise MU::Cloud::BootstrapTempFail if resp.exitcode == 35 or output_lines.join("\n").match(/REBOOT_SCHEDULED| WARN: Reboot requested:|Rebooting server at a recipe's request|Chef::Exceptions::Reboot/)
368
374
  raise MU::Groomer::RunError, output_lines.slice(output_lines.length-50, output_lines.length).join("")
@@ -619,15 +625,16 @@ module MU
619
625
  kb.name_args = [@server.mu_name]
620
626
  kb.config[:manual] = true
621
627
  kb.config[:winrm_transport] = :ssl
622
- kb.config[:host] = @server.mu_name
623
628
  kb.config[:winrm_port] = 5986
624
629
  kb.config[:session_timeout] = timeout
625
630
  kb.config[:operation_timeout] = timeout
626
631
  if retries % 2 == 0
632
+ kb.config[:host] = canonical_addr
627
633
  kb.config[:winrm_authentication_protocol] = :basic
628
634
  kb.config[:winrm_user] = @server.config['windows_admin_username']
629
635
  kb.config[:winrm_password] = @server.getWindowsAdminPassword
630
636
  else
637
+ kb.config[:host] = @server.mu_name
631
638
  kb.config[:winrm_authentication_protocol] = :cert
632
639
  kb.config[:winrm_client_cert] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.crt"
633
640
  kb.config[:winrm_client_key] = "#{MU.mySSLDir}/#{@server.mu_name}-winrm.key"
@@ -681,7 +688,7 @@ module MU
681
688
  preClean(false) # it's ok for this to fail
682
689
  rescue StandardError => e
683
690
  end
684
- MU::Groomer::Chef.cleanup(@server.mu_name, nodeonly: true)
691
+ MU::Groomer::Chef.purge(@server.mu_name, nodeonly: true)
685
692
  @config['forced_preclean'] = true
686
693
  @server.reboot if @server.windows? # *sigh*
687
694
  end
@@ -798,12 +805,52 @@ retry
798
805
  end
799
806
  end
800
807
 
808
+ # Purge Chef resources matching a particular deploy
809
+ # @param deploy_id [String]
810
+ # @param noop [Boolean]
811
+ def self.cleanup(deploy_id, noop = false)
812
+ return nil if deploy_id.nil? or deploy_id.empty?
813
+ begin
814
+ if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
815
+ ::Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
816
+ end
817
+ deadnodes = []
818
+ ::Chef::Config[:environment] ||= MU.environment
819
+ q = ::Chef::Search::Query.new
820
+ begin
821
+ q.search("node", "tags_MU-ID:#{deploy_id}").each { |item|
822
+ next if item.is_a?(Integer)
823
+ item.each { |node|
824
+ deadnodes << node.name
825
+ }
826
+ }
827
+ rescue Net::HTTPServerException
828
+ end
829
+
830
+ begin
831
+ q.search("node", "name:#{deploy_id}-*").each { |item|
832
+ next if item.is_a?(Integer)
833
+ item.each { |node|
834
+ deadnodes << node.name
835
+ }
836
+ }
837
+ rescue Net::HTTPServerException
838
+ end
839
+ MU.log "Missed some Chef resources in node cleanup, purging now", MU::NOTICE if deadnodes.size > 0
840
+ deadnodes.uniq.each { |node|
841
+ MU::Groomer::Chef.purge(node, [], noop)
842
+ }
843
+ rescue LoadError
844
+ end
845
+
846
+ end
847
+
801
848
  # Expunge Chef resources associated with a node.
802
849
  # @param node [String]: The Mu name of the node in question.
803
850
  # @param vaults_to_clean [Array<Hash>]: Some vaults to expunge
804
851
  # @param noop [Boolean]: Skip actual deletion, just state what we'd do
805
852
  # @param nodeonly [Boolean]: Just delete the node and its keys, but leave other artifacts
806
- def self.cleanup(node, vaults_to_clean = [], noop = false, nodeonly: false)
853
+ def self.purge(node, vaults_to_clean = [], noop = false, nodeonly: false)
807
854
  loadChefLib
808
855
  MU.log "Deleting Chef resources associated with #{node}"
809
856
  if !nodeonly
@@ -33,6 +33,33 @@ module MU
33
33
  # Show DEBUG log entries and extra call stack and threading info
34
34
  LOUD = 2.freeze
35
35
 
36
+ # stash a hash map for color outputs
37
+ COLORMAP = {
38
+ MU::DEBUG => { :html => "orange", :ansi => :yellow },
39
+ MU::INFO => { :html => "green", :ansi => :green },
40
+ MU::NOTICE => { :html => "yellow", :ansi => :yellow },
41
+ MU::WARN => { :html => "orange", :ansi => :light_red },
42
+ MU::ERR => { :html => "red", :ansi => :red }
43
+ }.freeze
44
+
45
+ # minimum log verbosity at which we'll print various types of messages
46
+ PRINT_MSG_IF = {
47
+ MU::DEBUG => { :msg => LOUD, :details => LOUD },
48
+ MU::INFO => { :msg => NORMAL, :details => LOUD },
49
+ MU::NOTICE => { :msg => nil, :details => QUIET },
50
+ MU::WARN => { :msg => nil, :details => SILENT },
51
+ MU::ERR => { :msg => nil, :details => nil }
52
+ }.freeze
53
+
54
+ # Syslog equivalents of our log levels
55
+ SYSLOG_MAP = {
56
+ MU::DEBUG => Syslog::LOG_DEBUG,
57
+ MU::INFO => Syslog::LOG_NOTICE,
58
+ MU::NOTICE => Syslog::LOG_NOTICE,
59
+ MU::WARN => Syslog::LOG_WARNING,
60
+ MU::ERR => Syslog::LOG_ERR
61
+ }.freeze
62
+
36
63
  attr_accessor :verbosity
37
64
  @verbosity = MU::Logger::NORMAL
38
65
  @quiet = false
@@ -76,59 +103,28 @@ module MU
76
103
  html ||= @html
77
104
  handle ||= @handle
78
105
  color ||= @color
79
- return if verbosity == MU::Logger::SILENT
80
- return if verbosity < MU::Logger::LOUD and level == DEBUG
81
- return if verbosity < MU::Logger::NORMAL and level == INFO
82
106
 
83
- # By which we mean, "get the filename (with the .rb stripped off) which
84
- # originated the call to this method. Which, for our purposes, is the
85
- # MU subclass that called us. Useful information. And it looks like Perl.
86
- mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
87
- bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
88
- caller_name = caller[1]
107
+ if verbosity == MU::Logger::SILENT or (verbosity < MU::Logger::LOUD and level == DEBUG) or (verbosity < MU::Logger::NORMAL and level == INFO)
108
+ return
109
+ end
89
110
 
90
- caller_name.sub!(/:.*/, "")
91
- caller_name.sub!(/^\.\//, "")
92
- caller_name.sub!(/^#{mod_root}/, "")
93
- caller_name.sub!(/^#{bin_root}/, "")
94
- caller_name.sub!(/\.r[ub]$/, "")
95
- caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
96
- caller_name.sub!(/^modules\//, "")
111
+ if level == SUMMARY
112
+ @summary << msg
113
+ return
114
+ end
115
+
116
+ caller_name = extract_caller_name(caller[1])
97
117
 
98
118
  time = Time.now.strftime("%b %d %H:%M:%S").to_s
99
119
 
100
120
  Syslog.open("Mu/"+caller_name, Syslog::LOG_PID, Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3) if !Syslog.opened?
101
- if !details.nil?
102
- if details.is_a?(Hash) and details.has_key?(:details)
103
- details = details[:details]
104
- end
105
- details = PP.pp(details, '') if !details.is_a?(String)
106
- end
107
- details = "<pre>"+details+"</pre>" if html
108
- # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
109
- # ugly line feeds too.
110
- if !details.nil?
111
- details = details.dup # in case it's frozen or something
112
- details.gsub!(/\\n/, "\n")
113
- details.gsub!(/(\\r|\r)/, "")
114
- end
121
+
122
+ details = format_details(details, html)
115
123
 
116
124
  msg = msg.first if msg.is_a?(Array)
117
125
  msg = "" if msg == nil
118
126
  msg = msg.to_s if !msg.is_a?(String) and msg.respond_to?(:to_s)
119
127
 
120
- # wrapper for writing a log entry to multiple filehandles
121
- # @param handles [Array<IO>]
122
- # @param msgs [Array<String>]
123
- def write(handles = [], msgs = [])
124
- return if handles.nil? or msgs.nil?
125
- handles.each { |h|
126
- msgs.each { |m|
127
- h.puts m
128
- }
129
- }
130
- end
131
-
132
128
  @@log_semaphere.synchronize {
133
129
  handles = [handle]
134
130
  extra_logfile = if deploy and deploy.deploy_dir and Dir.exist?(deploy.deploy_dir)
@@ -137,110 +133,41 @@ module MU
137
133
  handles << extra_logfile if extra_logfile
138
134
  msgs = []
139
135
 
140
- case level
141
- when SUMMARY
142
- @summary << msg
143
- when DEBUG
144
- if verbosity >= MU::Logger::LOUD
145
- if html
146
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
147
- html_out "&nbsp;#{details}" if details
148
- elsif color
149
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
150
- msgs << "#{details}".white.on_black if details
151
- else
152
- msgs << "#{time} - #{caller_name} - #{msg}"
153
- msgs << "#{details}" if details
154
- end
155
- Syslog.log(Syslog::LOG_DEBUG, msg.gsub(/%/, ''))
156
- Syslog.log(Syslog::LOG_DEBUG, details.gsub(/%/, '')) if details
157
- end
158
- when INFO
159
- if verbosity >= MU::Logger::NORMAL
160
- if html
161
- html_out "#{time} - #{caller_name} - #{msg}", "green"
162
- elsif color
163
- msgs << "#{time} - #{caller_name} - #{msg}".green.on_black
164
- else
165
- msgs << "#{time} - #{caller_name} - #{msg}"
166
- end
167
- if verbosity >= MU::Logger::LOUD
168
- if html
169
- html_out "&nbsp;#{details}"
170
- elsif color
171
- msgs << "#{details}".white.on_black if details
172
- else
173
- msgs << "#{details}" if details
174
- end
175
- end
176
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
177
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
178
- end
179
- when NOTICE
180
- if html
181
- html_out "#{time} - #{caller_name} - #{msg}", "yellow"
182
- elsif color
183
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
184
- else
185
- msgs << "#{time} - #{caller_name} - #{msg}"
186
- end
187
- if verbosity >= MU::Logger::QUIET
188
- if html
189
- html_out "#{caller_name} - #{msg}"
190
- elsif color
191
- msgs << "#{details}".white.on_black if details
192
- else
193
- msgs << "#{details}" if details
194
- end
195
- end
196
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
197
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
198
- when WARN
199
- if html
200
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
201
- elsif color
202
- msgs << "#{time} - #{caller_name} - #{msg}".light_red.on_black
203
- else
204
- msgs << "#{time} - #{caller_name} - #{msg}"
205
- end
206
- if verbosity >= MU::Logger::SILENT
207
- if html
208
- html_out "#{caller_name} - #{msg}"
209
- elsif color
210
- msgs << "#{details}".white.on_black if details
211
- else
212
- msgs << "#{details}" if details
213
- end
214
- end
215
- Syslog.log(Syslog::LOG_WARNING, msg.gsub(/%/, ''))
216
- Syslog.log(Syslog::LOG_WARNING, details.gsub(/%/, '')) if details
217
- when ERR
218
- if html
219
- html_out "#{time} - #{caller_name} - #{msg}", "red"
220
- html_out "&nbsp;#{details}" if details
221
- elsif color
222
- msgs << "#{time} - #{caller_name} - #{msg}".red.on_black
223
- msgs << "#{details}".white.on_black if details
224
- else
225
- msgs << "#{time} - #{caller_name} - #{msg}"
226
- msgs << "#{details}" if details
227
- end
228
- Syslog.log(Syslog::LOG_ERR, msg.gsub(/%/, ''))
229
- Syslog.log(Syslog::LOG_ERR, details.gsub(/%/, '')) if details
136
+ if !PRINT_MSG_IF[level][:msg] or level >= PRINT_MSG_IF[level][:msg]
137
+ if html
138
+ html_out "#{time} - #{caller_name} - #{msg}", COLORMAP[level][:html]
139
+ else
140
+ str = "#{time} - #{caller_name} - #{msg}"
141
+ str = str.send(COLORMAP[level][:ansi]).on_black if color
142
+ msgs << str
143
+ end
144
+ Syslog.log(SYSLOG_MAP[level], msg.gsub(/%/, ''))
145
+ end
146
+
147
+ if details and (!PRINT_MSG_IF[level][:details] or level >= PRINT_MSG_IF[level][:details])
148
+ if html
149
+ html_out "&nbsp;#{details}"
230
150
  else
231
- if html
232
- html_out "#{time} - #{caller_name} - #{msg}"
233
- html_out "&nbsp;#{details}" if details
234
- elsif color
235
- msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
236
- msgs << "#{details}".white.on_black if details
237
- else
238
- msgs << "#{time} - #{caller_name} - #{msg}"
239
- msgs << "#{details}" if details
240
- end
241
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
242
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
151
+ details = details.white.on_black if color
152
+ msgs << details
153
+ end
154
+ Syslog.log(SYSLOG_MAP[level], details.gsub(/%/, ''))
243
155
  end
156
+
157
+ # else
158
+ # if html
159
+ # html_out "#{time} - #{caller_name} - #{msg}"
160
+ # html_out "&nbsp;#{details}" if details
161
+ # elsif color
162
+ # msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
163
+ # msgs << "#{details}".white.on_black if details
164
+ # else
165
+ # msgs << "#{time} - #{caller_name} - #{msg}"
166
+ # msgs << "#{details}" if details
167
+ # end
168
+ # Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
169
+ # Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
170
+
244
171
  write(handles, msgs)
245
172
 
246
173
  extra_logfile.close if extra_logfile
@@ -250,6 +177,43 @@ module MU
250
177
 
251
178
  private
252
179
 
180
+ def format_details(details, html = false)
181
+ return if details.nil?
182
+
183
+ if details.is_a?(Hash) and details.has_key?(:details)
184
+ details = details[:details]
185
+ end
186
+ details = PP.pp(details, '') if !details.is_a?(String)
187
+
188
+ details = "<pre>"+details+"</pre>" if html
189
+ # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
190
+ # ugly line feeds too.
191
+
192
+ details = details.dup # in case it's frozen or something
193
+ details.gsub!(/\\n/, "\n")
194
+ details.gsub!(/(\\r|\r)/, "")
195
+
196
+ details
197
+ end
198
+
199
+ # By which we mean, "get the filename (with the .rb stripped off) which
200
+ # originated the call to this method. Which, for our purposes, is the
201
+ # MU subclass that called us. Useful information. And it looks like Perl.
202
+ def extract_caller_name(caller_name)
203
+ return nil if !caller_name or !caller_name.is_a?(String)
204
+ mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
205
+ bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
206
+
207
+ caller_name.sub!(/:.*/, "")
208
+ caller_name.sub!(/^\.\//, "")
209
+ caller_name.sub!(/^#{mod_root}/, "")
210
+ caller_name.sub!(/^#{bin_root}/, "")
211
+ caller_name.sub!(/\.r[ub]$/, "")
212
+ caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
213
+ caller_name.sub!(/^modules\//, "")
214
+ caller_name
215
+ end
216
+
253
217
  # Output a log message as HTML.
254
218
  #
255
219
  # @param msg [String]: The log message to print
@@ -259,5 +223,17 @@ module MU
259
223
  @handle.puts "<span style='color:#{rgb.css_rgb};'>#{msg}</span>"
260
224
  end
261
225
 
226
+ # wrapper for writing a log entry to multiple filehandles
227
+ # @param handles [Array<IO>]
228
+ # @param msgs [Array<String>]
229
+ def write(handles = [], msgs = [])
230
+ return if handles.nil? or msgs.nil?
231
+ handles.each { |h|
232
+ msgs.each { |m|
233
+ h.puts m
234
+ }
235
+ }
236
+ end
237
+
262
238
  end #class
263
239
  end #module