cloud-mu 3.1.4 → 3.3.1

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +16 -12
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -1
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +37 -12
  23. data/cloud-mu.gemspec +5 -3
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  27. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  28. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  29. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  30. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  31. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  32. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  33. data/extras/clean-stock-amis +25 -19
  34. data/extras/generate-stock-images +1 -0
  35. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  36. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  37. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  38. data/modules/mommacat.ru +1 -1
  39. data/modules/mu.rb +158 -107
  40. data/modules/mu/adoption.rb +386 -59
  41. data/modules/mu/cleanup.rb +214 -303
  42. data/modules/mu/cloud.rb +128 -1632
  43. data/modules/mu/cloud/database.rb +49 -0
  44. data/modules/mu/cloud/dnszone.rb +44 -0
  45. data/modules/mu/cloud/machine_images.rb +212 -0
  46. data/modules/mu/cloud/providers.rb +81 -0
  47. data/modules/mu/cloud/resource_base.rb +926 -0
  48. data/modules/mu/cloud/server.rb +40 -0
  49. data/modules/mu/cloud/server_pool.rb +1 -0
  50. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  51. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  52. data/modules/mu/cloud/wrappers.rb +169 -0
  53. data/modules/mu/config.rb +135 -82
  54. data/modules/mu/config/alarm.rb +2 -6
  55. data/modules/mu/config/bucket.rb +32 -3
  56. data/modules/mu/config/cache_cluster.rb +2 -2
  57. data/modules/mu/config/cdn.rb +100 -0
  58. data/modules/mu/config/collection.rb +1 -1
  59. data/modules/mu/config/container_cluster.rb +7 -2
  60. data/modules/mu/config/database.rb +84 -105
  61. data/modules/mu/config/database.yml +1 -2
  62. data/modules/mu/config/dnszone.rb +5 -4
  63. data/modules/mu/config/doc_helpers.rb +5 -6
  64. data/modules/mu/config/endpoint.rb +2 -1
  65. data/modules/mu/config/firewall_rule.rb +3 -19
  66. data/modules/mu/config/folder.rb +1 -1
  67. data/modules/mu/config/function.rb +17 -8
  68. data/modules/mu/config/group.rb +1 -1
  69. data/modules/mu/config/habitat.rb +1 -1
  70. data/modules/mu/config/job.rb +89 -0
  71. data/modules/mu/config/loadbalancer.rb +57 -11
  72. data/modules/mu/config/log.rb +1 -1
  73. data/modules/mu/config/msg_queue.rb +1 -1
  74. data/modules/mu/config/nosqldb.rb +1 -1
  75. data/modules/mu/config/notifier.rb +8 -19
  76. data/modules/mu/config/ref.rb +92 -14
  77. data/modules/mu/config/role.rb +1 -1
  78. data/modules/mu/config/schema_helpers.rb +38 -37
  79. data/modules/mu/config/search_domain.rb +1 -1
  80. data/modules/mu/config/server.rb +12 -13
  81. data/modules/mu/config/server.yml +1 -0
  82. data/modules/mu/config/server_pool.rb +3 -7
  83. data/modules/mu/config/storage_pool.rb +1 -1
  84. data/modules/mu/config/tail.rb +11 -0
  85. data/modules/mu/config/user.rb +1 -1
  86. data/modules/mu/config/vpc.rb +27 -23
  87. data/modules/mu/config/vpc.yml +0 -1
  88. data/modules/mu/defaults/AWS.yaml +91 -68
  89. data/modules/mu/defaults/Azure.yaml +1 -0
  90. data/modules/mu/defaults/Google.yaml +1 -0
  91. data/modules/mu/deploy.rb +33 -19
  92. data/modules/mu/groomer.rb +16 -1
  93. data/modules/mu/groomers/ansible.rb +123 -21
  94. data/modules/mu/groomers/chef.rb +64 -11
  95. data/modules/mu/logger.rb +120 -144
  96. data/modules/mu/master.rb +97 -4
  97. data/modules/mu/master/ssl.rb +0 -1
  98. data/modules/mu/mommacat.rb +154 -867
  99. data/modules/mu/mommacat/daemon.rb +23 -14
  100. data/modules/mu/mommacat/naming.rb +110 -3
  101. data/modules/mu/mommacat/search.rb +495 -0
  102. data/modules/mu/mommacat/storage.rb +225 -192
  103. data/modules/mu/{clouds → providers}/README.md +1 -1
  104. data/modules/mu/{clouds → providers}/aws.rb +281 -64
  105. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  106. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  107. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  108. data/modules/mu/providers/aws/cdn.rb +782 -0
  109. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  110. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
  111. data/modules/mu/providers/aws/database.rb +1744 -0
  112. data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
  113. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  114. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
  115. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  116. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  117. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  118. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  119. data/modules/mu/providers/aws/job.rb +466 -0
  120. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
  121. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  122. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  123. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  124. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  125. data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
  126. data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
  127. data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
  128. data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
  129. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  130. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  131. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  132. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  133. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  134. data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
  135. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  136. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  137. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  138. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  139. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  140. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  141. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  142. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  143. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  144. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  145. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  146. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  147. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  148. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  149. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  150. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  151. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  152. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  153. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  156. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  158. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  159. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  160. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  161. data/modules/mu/{clouds → providers}/google.rb +29 -6
  162. data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
  163. data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
  164. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  165. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  166. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  167. data/modules/mu/{clouds → providers}/google/function.rb +14 -8
  168. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  169. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  170. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  171. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  172. data/modules/mu/{clouds → providers}/google/server.rb +142 -55
  173. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  174. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  175. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  176. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  177. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  178. data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
  179. data/modules/tests/aws-jobs-functions.yaml +46 -0
  180. data/modules/tests/centos6.yaml +15 -0
  181. data/modules/tests/centos7.yaml +15 -0
  182. data/modules/tests/centos8.yaml +12 -0
  183. data/modules/tests/ecs.yaml +23 -0
  184. data/modules/tests/eks.yaml +1 -1
  185. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  186. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/microservice_app.yaml +288 -0
  189. data/modules/tests/rds.yaml +108 -0
  190. data/modules/tests/regrooms/rds.yaml +123 -0
  191. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  192. data/modules/tests/super_complex_bok.yml +2 -2
  193. data/modules/tests/super_simple_bok.yml +3 -5
  194. data/modules/tests/win2k12.yaml +25 -0
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +169 -93
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1974
  202. data/modules/mu/clouds/aws/endpoint.rb +0 -596
  203. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -386,6 +386,7 @@ module MU
386
386
  best = nil
387
387
  best_version = nil
388
388
  paths.uniq.each { |path|
389
+ path.sub!(/^~/, MY_HOME)
389
390
  if File.exist?(path+"/kubectl")
390
391
  version = %x{#{path}/kubectl version --short --client}.chomp.sub(/.*Client version:\s+v/i, '')
391
392
  next if !$?.success?
@@ -546,7 +547,7 @@ module MU
546
547
  rescue Errno::ECONNRESET, Errno::ECONNREFUSED
547
548
  end
548
549
  if response != "ok"
549
- MU.log "Error adding #{public_ip} to /etc/hosts via MommaCat request", MU::ERR
550
+ MU.log "Unable to add #{public_ip} to /etc/hosts via MommaCat request", MU::WARN
550
551
  end
551
552
  return
552
553
  end
@@ -601,7 +602,7 @@ module MU
601
602
  return
602
603
  end
603
604
  if ssh_key_name.nil? or ssh_key_name.empty?
604
- MU.log "Failed to extract ssh_key_name for #{ssh_key_name.mu_name} in addHostToSSHConfig", MU::ERR
605
+ MU.log "Failed to extract ssh_key_name for #{server.mu_name} in addHostToSSHConfig", MU::ERR
605
606
  return
606
607
  end
607
608
 
@@ -709,6 +710,77 @@ module MU
709
710
  end
710
711
  end
711
712
 
713
+ # Evict ssh keys associated with a particular deploy from our ssh config
714
+ # and key directory.
715
+ # @param deploy_id [String]
716
+ # @param noop [Boolean]
717
+ def self.purgeDeployFromSSH(deploy_id, noop: false)
718
+ myhome = Etc.getpwuid(Process.uid).dir
719
+ sshdir = "#{myhome}/.ssh"
720
+ sshconf = "#{sshdir}/config"
721
+ ssharchive = "#{sshdir}/archive"
722
+
723
+ Dir.mkdir(sshdir, 0700) if !Dir.exist?(sshdir) and !noop
724
+ Dir.mkdir(ssharchive, 0700) if !Dir.exist?(ssharchive) and !noop
725
+
726
+ keyname = "deploy-#{deploy_id}"
727
+ if File.exist?("#{sshdir}/#{keyname}")
728
+ MU.log "Moving #{sshdir}/#{keyname} to #{ssharchive}/#{keyname}"
729
+ if !noop
730
+ File.rename("#{sshdir}/#{keyname}", "#{ssharchive}/#{keyname}")
731
+ end
732
+ end
733
+ if File.exist?(sshconf) and File.open(sshconf).read.match(/\/deploy\-#{deploy_id}$/)
734
+ MU.log "Expunging #{deploy_id} from #{sshconf}"
735
+ if !noop
736
+ FileUtils.copy(sshconf, "#{ssharchive}/config-#{deploy_id}")
737
+ File.open(sshconf, File::CREAT|File::RDWR, 0600) { |f|
738
+ f.flock(File::LOCK_EX)
739
+ newlines = Array.new
740
+ delete_block = false
741
+ f.readlines.each { |line|
742
+ if line.match(/^Host #{deploy_id}\-/)
743
+ delete_block = true
744
+ elsif line.match(/^Host /)
745
+ delete_block = false
746
+ end
747
+ newlines << line if !delete_block
748
+ }
749
+ f.rewind
750
+ f.truncate(0)
751
+ f.puts(newlines)
752
+ f.flush
753
+ f.flock(File::LOCK_UN)
754
+ }
755
+ end
756
+ end
757
+ # XXX refactor with above? They're similar, ish.
758
+ hostsfile = "/etc/hosts"
759
+ if File.open(hostsfile).read.match(/ #{deploy_id}\-/)
760
+ if Process.uid == 0
761
+ MU.log "Expunging traces of #{deploy_id} from #{hostsfile}"
762
+ if !noop
763
+ FileUtils.copy(hostsfile, "#{hostsfile}.cleanup-#{deploy_id}")
764
+ File.open(hostsfile, File::CREAT|File::RDWR, 0644) { |f|
765
+ f.flock(File::LOCK_EX)
766
+ newlines = Array.new
767
+ f.readlines.each { |line|
768
+ newlines << line if !line.match(/ #{deploy_id}\-/)
769
+ }
770
+ f.rewind
771
+ f.truncate(0)
772
+ f.puts(newlines)
773
+ f.flush
774
+ f.flock(File::LOCK_UN)
775
+ }
776
+ end
777
+ else
778
+ MU.log "Residual /etc/hosts entries for #{deploy_id} must be removed by root user", MU::WARN
779
+ end
780
+ end
781
+
782
+ end
783
+
712
784
  # Ensure that the Nagios configuration local to the MU master has been
713
785
  # updated, and make sure Nagios has all of the ssh keys it needs to tunnel
714
786
  # to client nodes.
@@ -738,7 +810,7 @@ module MU
738
810
  ssh_conf.puts " IdentityFile #{NAGIOS_HOME}/.ssh/id_rsa"
739
811
  ssh_conf.puts " StrictHostKeyChecking no"
740
812
  ssh_conf.close
741
- FileUtils.cp("#{@myhome}/.ssh/id_rsa", "#{NAGIOS_HOME}/.ssh/id_rsa")
813
+ FileUtils.cp("#{Etc.getpwuid(Process.uid).dir}/.ssh/id_rsa", "#{NAGIOS_HOME}/.ssh/id_rsa")
742
814
  File.chown(Etc.getpwnam("nagios").uid, Etc.getpwnam("nagios").gid, "#{NAGIOS_HOME}/.ssh/id_rsa")
743
815
  threads = []
744
816
 
@@ -751,7 +823,7 @@ module MU
751
823
  MU.log "Failed to extract ssh key name from #{deploy_id} in syncMonitoringConfig", MU::ERR if deploy.kittens.has_key?("servers")
752
824
  next
753
825
  end
754
- FileUtils.cp("#{@myhome}/.ssh/#{deploy.ssh_key_name}", "#{NAGIOS_HOME}/.ssh/#{deploy.ssh_key_name}")
826
+ FileUtils.cp("#{Etc.getpwuid(Process.uid).dir}/.ssh/#{deploy.ssh_key_name}", "#{NAGIOS_HOME}/.ssh/#{deploy.ssh_key_name}")
755
827
  File.chown(Etc.getpwnam("nagios").uid, Etc.getpwnam("nagios").gid, "#{NAGIOS_HOME}/.ssh/#{deploy.ssh_key_name}")
756
828
  if deploy.kittens.has_key?("servers")
757
829
  deploy.kittens["servers"].values.each { |nodeclasses|
@@ -808,5 +880,26 @@ module MU
808
880
  end
809
881
  end
810
882
 
883
+ # Recursively zip a directory
884
+ # @param srcdir [String]
885
+ # @param outfile [String]
886
+ def self.zipDir(srcdir, outfile)
887
+ require 'zip'
888
+ ::Zip::File.open(outfile, ::Zip::File::CREATE) { |zipfile|
889
+ addpath = Proc.new { |zip_path, parent_path|
890
+ Dir.entries(parent_path).reject{ |d| [".", ".."].include?(d) }.each { |entry|
891
+ src = File.join(parent_path, entry)
892
+ dst = File.join(zip_path, entry).sub(/^\//, '')
893
+ if File.directory?(src)
894
+ addpath.call(dst, src)
895
+ else
896
+ zipfile.add(dst, src)
897
+ end
898
+ }
899
+ }
900
+ addpath.call("", srcdir)
901
+ }
902
+ end
903
+
811
904
  end
812
905
  end
@@ -160,7 +160,6 @@ module MU
160
160
 
161
161
  key = getKey(name, for_user: for_user)
162
162
 
163
- puts cn_str
164
163
  cn = OpenSSL::X509::Name.parse(cn_str)
165
164
 
166
165
  # If we're generating our local CA, we're not really doing a CSR, but
@@ -19,6 +19,7 @@ require 'stringio'
19
19
  require 'securerandom'
20
20
  require 'timeout'
21
21
  require 'mu/mommacat/storage'
22
+ require 'mu/mommacat/search'
22
23
  require 'mu/mommacat/daemon'
23
24
  require 'mu/mommacat/naming'
24
25
 
@@ -154,7 +155,7 @@ module MU
154
155
  if @mu_user == "root"
155
156
  @chef_user = "mu"
156
157
  else
157
- @chef_user = @mu_user.dup.gsub(/\./, "")
158
+ @chef_user = @mu_user.dup.delete(".")
158
159
  @mu_user = "root" if @mu_user == "mu"
159
160
  end
160
161
  @kitten_semaphore = Mutex.new
@@ -166,6 +167,7 @@ module MU
166
167
  @need_deploy_flush = false
167
168
  @node_cert_semaphore = Mutex.new
168
169
  @deployment = deployment_data
170
+
169
171
  @deployment['mu_public_ip'] = MU.mu_public_ip
170
172
  @private_key = nil
171
173
  @public_key = nil
@@ -181,60 +183,17 @@ module MU
181
183
  @appname ||= @original_config['name'] if @original_config
182
184
  @timestamp = timestamp
183
185
  @environment = environment
186
+ @original_config['environment'] ||= @environment if @original_config
184
187
 
185
188
  if set_context_to_me
186
189
  MU::MommaCat.setThreadContext(self)
187
190
  end
188
191
 
189
192
  if create and !@no_artifacts
190
- if !Dir.exist?(MU.dataDir+"/deployments")
191
- MU.log "Creating #{MU.dataDir}/deployments", MU::DEBUG
192
- Dir.mkdir(MU.dataDir+"/deployments", 0700)
193
- end
194
- path = File.expand_path(MU.dataDir+"/deployments")+"/"+@deploy_id
195
- if !Dir.exist?(path)
196
- MU.log "Creating #{path}", MU::DEBUG
197
- Dir.mkdir(path, 0700)
198
- end
199
- if @original_config.nil? or !@original_config.is_a?(Hash)
200
- raise DeployInitializeError, "New MommaCat repository requires config hash"
201
- end
202
- credsets = {}
203
-
204
- MU::Cloud.resource_types.values.each { |attrs|
205
- if !@original_config[attrs[:cfg_plural]].nil? and @original_config[attrs[:cfg_plural]].size > 0
206
- @original_config[attrs[:cfg_plural]].each { |resource|
207
-
208
- credsets[resource['cloud']] ||= []
209
- credsets[resource['cloud']] << resource['credentials']
210
- @clouds[resource['cloud']] = 0 if !@clouds.has_key?(resource['cloud'])
211
- @clouds[resource['cloud']] = @clouds[resource['cloud']] + 1
212
-
213
- }
214
- end
215
- }
216
-
217
- @ssh_key_name, @ssh_private_key, @ssh_public_key = self.SSHKey
218
- if !File.exist?(deploy_dir+"/private_key")
219
- @private_key, @public_key = createDeployKey
220
- end
221
- MU.log "Creating deploy secret for #{MU.deploy_id}"
222
- @deploy_secret = Password.random(256)
223
- if !@original_config['scrub_mu_isms'] and !@no_artifacts
224
- credsets.each_pair { |cloud, creds|
225
- creds.uniq!
226
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
227
- creds.each { |credentials|
228
- cloudclass.writeDeploySecret(@deploy_id, @deploy_secret, credentials: credentials)
229
- }
230
- }
231
- end
232
- if set_context_to_me
233
- MU::MommaCat.setThreadContext(self)
234
- end
235
-
193
+ initDeployDirectory
194
+ setDeploySecret
195
+ MU::MommaCat.setThreadContext(self) if set_context_to_me
236
196
  save!
237
-
238
197
  end
239
198
 
240
199
  @appname ||= MU.appname
@@ -242,10 +201,8 @@ module MU
242
201
  @environment ||= MU.environment
243
202
 
244
203
  loadDeploy(set_context_to_me: set_context_to_me)
245
- if !deploy_secret.nil?
246
- if !authKey(deploy_secret)
247
- raise DeployInitializeError, "Client request did not include a valid deploy authorization secret. Verify that userdata runs correctly?"
248
- end
204
+ if !deploy_secret.nil? and !authKey(deploy_secret)
205
+ raise DeployInitializeError, "Client request did not include a valid deploy authorization secret. Verify that userdata runs correctly?"
249
206
  end
250
207
 
251
208
 
@@ -257,86 +214,7 @@ module MU
257
214
  # deploy, IF it already exists, which is to say if we're loading an
258
215
  # existing deploy instead of creating a new one.
259
216
  if !create and @deployment and @original_config and !skip_resource_objects
260
-
261
- MU::Cloud.resource_types.each_pair { |res_type, attrs|
262
- type = attrs[:cfg_plural]
263
- if @deployment.has_key?(type)
264
-
265
- @deployment[type].each_pair { |res_name, data|
266
- orig_cfg = nil
267
- if @original_config.has_key?(type)
268
- @original_config[type].each { |resource|
269
- if resource["name"] == res_name
270
- orig_cfg = resource
271
- break
272
- end
273
- }
274
- end
275
-
276
- # Some Server objects originated from ServerPools, get their
277
- # configs from there
278
- if type == "servers" and orig_cfg.nil? and
279
- @original_config.has_key?("server_pools")
280
- @original_config["server_pools"].each { |resource|
281
- if resource["name"] == res_name
282
- orig_cfg = resource
283
- break
284
- end
285
- }
286
- end
287
-
288
- if orig_cfg.nil?
289
- MU.log "Failed to locate original config for #{attrs[:cfg_name]} #{res_name} in #{@deploy_id}", MU::WARN if !["firewall_rules", "databases", "storage_pools", "cache_clusters", "alarms"].include?(type) # XXX shaddap
290
- next
291
- end
292
-
293
- if orig_cfg['vpc'] and orig_cfg['vpc'].is_a?(Hash)
294
- ref = if orig_cfg['vpc']['id'] and orig_cfg['vpc']['id'].is_a?(Hash)
295
- orig_cfg['vpc']['id']['mommacat'] = self
296
- MU::Config::Ref.get(orig_cfg['vpc']['id'])
297
- else
298
- orig_cfg['vpc']['mommacat'] = self
299
- MU::Config::Ref.get(orig_cfg['vpc'])
300
- end
301
- orig_cfg['vpc'].delete('mommacat')
302
- orig_cfg['vpc'] = ref if ref.kitten(shallow: true)
303
- end
304
-
305
- begin
306
- # Load up MU::Cloud objects for all our kittens in this deploy
307
- orig_cfg['environment'] = @environment # not always set in old deploys
308
- if attrs[:has_multiples]
309
- data.keys.each { |mu_name|
310
- attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: mu_name, delay_descriptor_load: delay_descriptor_load)
311
- }
312
- else
313
- # XXX hack for old deployments, this can go away some day
314
- if data['mu_name'].nil? or data['mu_name'].empty?
315
- if res_type.to_s == "LoadBalancer" and !data['awsname'].nil?
316
- data['mu_name'] = data['awsname'].dup
317
- elsif res_type.to_s == "FirewallRule" and !data['group_name'].nil?
318
- data['mu_name'] = data['group_name'].dup
319
- elsif res_type.to_s == "Database" and !data['identifier'].nil?
320
- data['mu_name'] = data['identifier'].dup.upcase
321
- elsif res_type.to_s == "VPC"
322
- # VPC names are deterministic, just generate the things
323
- data['mu_name'] = getResourceName(data['name'])
324
- end
325
- end
326
- if data['mu_name'].nil?
327
- raise MuError, "Unable to find or guess a Mu name for #{res_type}: #{res_name} in #{@deploy_id}"
328
- end
329
- attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: data['mu_name'], cloud_id: data['cloud_id'])
330
- end
331
- rescue StandardError => e
332
- if e.class != MU::Cloud::MuCloudResourceNotImplemented
333
- MU.log "Failed to load an existing resource of type '#{type}' in #{@deploy_id}: #{e.inspect}", MU::WARN, details: e.backtrace
334
- end
335
- end
336
- }
337
-
338
- end
339
- }
217
+ loadObjects(delay_descriptor_load)
340
218
  end
341
219
 
342
220
  @initializing = false
@@ -349,7 +227,7 @@ module MU
349
227
  def cloudsUsed
350
228
  seen = []
351
229
  seen << @original_config['cloud'] if @original_config['cloud']
352
- MU::Cloud.resource_types.values.each { |attrs|
230
+ MU::Cloud.resource_types.each_value { |attrs|
353
231
  type = attrs[:cfg_plural]
354
232
  if @original_config[type]
355
233
  @original_config[type].each { |resource|
@@ -369,19 +247,15 @@ module MU
369
247
  # clouds = []
370
248
  seen << @original_config['credentials'] if @original_config['credentials']
371
249
  # defaultcloud = @original_config['cloud']
372
- MU::Cloud.resource_types.values.each { |attrs|
250
+ MU::Cloud.resource_types.each_value { |attrs|
373
251
  type = attrs[:cfg_plural]
374
252
  if @original_config[type]
375
253
  @original_config[type].each { |resource|
376
254
  if resource['credentials']
377
255
  seen << resource['credentials']
378
256
  else
379
- cloudclass = if @original_config['cloud']
380
- Object.const_get("MU").const_get("Cloud").const_get(@original_config['cloud'])
381
- else
382
- Object.const_get("MU").const_get("Cloud").const_get(MU::Config.defaultCloud)
383
- end
384
- seen << cloudclass.credConfig(name_only: true)
257
+ cloudconst = @original_config['cloud'] ? @original_config['cloud'] : MU::Config.defaultCloud
258
+ seen << MU::Cloud.cloudClass(cloudconst).credConfig(name_only: true)
385
259
  end
386
260
  }
387
261
  end
@@ -404,7 +278,7 @@ module MU
404
278
  end
405
279
  end
406
280
 
407
- MU::Cloud.resource_types.values.each { |attrs|
281
+ MU::Cloud.resource_types.each_value { |attrs|
408
282
  type = attrs[:cfg_plural]
409
283
  if @original_config[type]
410
284
  @original_config[type].each { |resource|
@@ -416,11 +290,10 @@ module MU
416
290
  habitats << hab_ref.id
417
291
  end
418
292
  elsif resource['cloud']
419
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
420
293
  # XXX this should be a general method implemented by each cloud
421
294
  # provider
422
295
  if resource['cloud'] == "Google"
423
- habitats << cloudclass.defaultProject(resource['credentials'])
296
+ habitats << MU::Cloud.cloudClass(resource['cloud']).defaultProject(resource['credentials'])
424
297
  end
425
298
  end
426
299
  }
@@ -444,13 +317,11 @@ module MU
444
317
  if @original_config[type]
445
318
  @original_config[type].each { |resource|
446
319
  if resource['cloud']
447
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
448
- resclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']).const_get(res_type.to_s)
449
- if resclass.isGlobal?
320
+ if MU::Cloud.resourceClass(resource['cloud'], res_type).isGlobal?
450
321
  # XXX why was I doing this, urgh
451
322
  next
452
323
  elsif !resource['region']
453
- regions << cloudclass.myRegion
324
+ regions << MU::Cloud.cloudClass(resource['cloud']).myRegion(resource['credentials'])
454
325
  end
455
326
  end
456
327
  if resource['region']
@@ -481,7 +352,7 @@ module MU
481
352
  end
482
353
 
483
354
  count = 0
484
- MU::Cloud.resource_types.values.each { |data|
355
+ MU::Cloud.resource_types.each_value { |data|
485
356
  next if @original_config[data[:cfg_plural]].nil?
486
357
  next if realtypes.size > 0 and (!negate and !realtypes.include?(data[:cfg_plural]))
487
358
  @original_config[data[:cfg_plural]].each { |resource|
@@ -499,13 +370,13 @@ module MU
499
370
  raise MuError, "Nil arguments to removeKitten are not allowed"
500
371
  end
501
372
  @kitten_semaphore.synchronize {
502
- MU::Cloud.resource_types.values.each { |attrs|
373
+ MU::Cloud.resource_types.each_value { |attrs|
503
374
  type = attrs[:cfg_plural]
504
375
  next if !@kittens.has_key?(type)
505
376
  tmplitter = @kittens[type].values.dup
506
377
  tmplitter.each { |nodeclass, data|
507
378
  if data.is_a?(Hash)
508
- data.keys.each { |mu_name|
379
+ data.each_key { |mu_name|
509
380
  if data == object
510
381
  @kittens[type][nodeclass].delete(mu_name)
511
382
  return
@@ -528,25 +399,37 @@ module MU
528
399
  # @param type [String]:
529
400
  # @param name [String]:
530
401
  # @param object [MU::Cloud]:
531
- def addKitten(type, name, object)
402
+ def addKitten(type, name, object, do_notify: false)
532
403
  if !type or !name or !object or !object.mu_name
533
404
  raise MuError, "Nil arguments to addKitten are not allowed (got type: #{type}, name: #{name}, and '#{object}' to add)"
534
405
  end
535
406
 
536
407
  _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
537
- has_multiples = attrs[:has_multiples]
538
408
  object.intoDeploy(self)
539
409
 
540
- @kitten_semaphore.synchronize {
410
+ add_block = Proc.new {
541
411
  @kittens[type] ||= {}
542
412
  @kittens[type][object.habitat] ||= {}
543
- if has_multiples
413
+ if attrs[:has_multiples]
544
414
  @kittens[type][object.habitat][name] ||= {}
545
415
  @kittens[type][object.habitat][name][object.mu_name] = object
546
416
  else
547
417
  @kittens[type][object.habitat][name] = object
548
418
  end
419
+ if do_notify
420
+ notify(type, name, object.notify, triggering_node: object, delayed_save: true)
421
+ end
549
422
  }
423
+
424
+ begin
425
+ @kitten_semaphore.synchronize {
426
+ add_block.call()
427
+ }
428
+ rescue ThreadError => e
429
+ # already locked by a parent call to this method, so this should be safe
430
+ raise e if !e.message.match(/recursive locking/)
431
+ add_block.call()
432
+ end
550
433
  end
551
434
 
552
435
  # Encrypt a string with the deployment's public key.
@@ -577,7 +460,7 @@ module MU
577
460
  loadDeploy(true) # make sure we're not trampling deployment data
578
461
  @secret_semaphore.synchronize {
579
462
  if @secrets[type].nil?
580
- raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.to_s})"
463
+ raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.join(", ")})"
581
464
  end
582
465
  @secrets[type][instance_id] = encryptWithDeployKey(raw_secret)
583
466
  }
@@ -593,7 +476,7 @@ module MU
593
476
  @secret_semaphore.synchronize {
594
477
  if @secrets[type].nil?
595
478
  return nil if quiet
596
- raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.to_s})"
479
+ raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.join(", ")})"
597
480
  end
598
481
  if @secrets[type][instance_id].nil?
599
482
  return nil if quiet
@@ -654,656 +537,124 @@ module MU
654
537
 
655
538
  @@dummy_cache = {}
656
539
 
657
- # Locate a resource that's either a member of another deployment, or of no
658
- # deployment at all, and return a {MU::Cloud} object for it.
659
- # @param cloud [String]: The Cloud provider to use.
660
- # @param type [String]: The resource type. Can be the full class name, symbolic name, or Basket of Kittens configuration shorthand for the resource type.
661
- # @param deploy_id [String]: The identifier of an outside deploy to search.
662
- # @param name [String]: The name of the resource as defined in its 'name' Basket of Kittens field, typically used in conjunction with deploy_id.
663
- # @param mu_name [String]: The fully-resolved and deployed name of the resource, typically used in conjunction with deploy_id.
664
- # @param cloud_id [String]: A cloud provider identifier for this resource.
665
- # @param region [String]: The cloud provider region
666
- # @param tag_key [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_value.
667
- # @param tag_value [String]: A cloud provider tag to help identify the resource, used in conjunction with tag_key.
668
- # @param allow_multi [Boolean]: Permit an array of matching resources to be returned (if applicable) instead of just one.
669
- # @param dummy_ok [Boolean]: Permit return of a faked {MU::Cloud} object if we don't have enough information to identify a real live one.
670
- # @param flags [Hash]: Other cloud or resource type specific options to pass to that resource's find() method
671
- # @return [Array<MU::Cloud>]
672
- def self.findStray(
673
- cloud,
674
- type,
675
- deploy_id: nil,
676
- name: nil,
677
- mu_name: nil,
678
- cloud_id: nil,
679
- credentials: nil,
680
- region: nil,
681
- tag_key: nil,
682
- tag_value: nil,
683
- allow_multi: false,
684
- calling_deploy: MU.mommacat,
685
- flags: {},
686
- habitats: [],
687
- dummy_ok: false,
688
- debug: false,
689
- no_deploy_search: false
690
- )
691
- start = Time.now
692
- callstr = "findStray(cloud: #{cloud}, type: #{type}, deploy_id: #{deploy_id}, calling_deploy: #{calling_deploy.deploy_id if !calling_deploy.nil?}, name: #{name}, cloud_id: #{cloud_id}, tag_key: #{tag_key}, tag_value: #{tag_value}, credentials: #{credentials}, habitats: #{habitats ? habitats.to_s : "[]"}, dummy_ok: #{dummy_ok.to_s}, flags: #{flags.to_s}) from #{caller[0]}"
693
- # callstack = caller.dup
694
-
695
- return nil if cloud == "CloudFormation" and !cloud_id.nil?
696
- shortclass, _cfg_name, cfg_plural, classname, _attrs = MU::Cloud.getResourceNames(type)
697
- if !MU::Cloud.supportedClouds.include?(cloud) or shortclass.nil?
698
- MU.log "findStray was called with bogus cloud argument '#{cloud}'", MU::WARN, details: callstr
699
- return nil
700
- end
701
-
702
- begin
703
- # TODO this is dumb as hell, clean this up.. and while we're at it
704
- # .dup everything so we don't mangle referenced values from the caller
705
- deploy_id = deploy_id.to_s if deploy_id.class.to_s == "MU::Config::Tail"
706
- name = name.to_s if name.class.to_s == "MU::Config::Tail"
707
- cloud_id = cloud_id.to_s if !cloud_id.nil?
708
- mu_name = mu_name.to_s if mu_name.class.to_s == "MU::Config::Tail"
709
- tag_key = tag_key.to_s if tag_key.class.to_s == "MU::Config::Tail"
710
- tag_value = tag_value.to_s if tag_value.class.to_s == "MU::Config::Tail"
711
- type = cfg_plural
712
- resourceclass = MU::Cloud.loadCloudType(cloud, shortclass)
713
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
714
-
715
- credlist = if credentials
716
- [credentials]
717
- else
718
- cloudclass.listCredentials
719
- end
720
-
721
- if (tag_key and !tag_value) or (!tag_key and tag_value)
722
- raise MuError, "Can't call findStray with only one of tag_key and tag_value set, must be both or neither"
723
- end
724
- # Help ourselves by making more refined parameters out of mu_name, if
725
- # they weren't passed explicitly
726
- if mu_name
727
- if !tag_key and !tag_value
728
- # XXX "Name" is an AWS-ism, perhaps those plugins should do this bit?
729
- tag_key="Name"
730
- tag_value=mu_name
731
- end
732
- # We can extract a deploy_id from mu_name if we don't have one already
733
- if !deploy_id and mu_name
734
- deploy_id = mu_name.sub(/^(\w+-\w+-\d{10}-[A-Z]{2})-/, '\1')
735
- end
736
- end
737
- loglevel = debug ? MU::NOTICE : MU::DEBUG
738
-
739
- MU.log callstr, loglevel, details: caller
740
-
741
- # See if the thing we're looking for is a member of the deploy that's
742
- # asking after it.
743
- if !deploy_id.nil? and !calling_deploy.nil? and
744
- calling_deploy.deploy_id == deploy_id and (!name.nil? or !mu_name.nil?)
745
- handle = calling_deploy.findLitterMate(type: type, name: name, mu_name: mu_name, cloud_id: cloud_id, credentials: credentials)
746
- return [handle] if !handle.nil?
747
- end
748
-
749
- kittens = {}
750
- # Search our other deploys for matching resources
751
- if !no_deploy_search and (deploy_id or name or mu_name or cloud_id)
752
- MU.log "findStray: searching my deployments (#{cfg_plural}, name: #{name}, deploy_id: #{deploy_id}, mu_name: #{mu_name}) - #{sprintf("%.2fs", (Time.now-start))}", loglevel
753
-
754
- # Check our in-memory cache of live deploys before resorting to
755
- # metadata
756
- littercache = nil
757
- # Sometimes we're called inside a locked thread, sometimes not. Deal
758
- # with locking gracefully.
759
- begin
760
- @@litter_semaphore.synchronize {
761
- littercache = @@litters.dup
762
- }
763
- rescue ThreadError => e
764
- raise e if !e.message.match(/recursive locking/)
765
- littercache = @@litters.dup
766
- end
767
-
768
- littercache.each_pair { |cur_deploy, momma|
769
- next if deploy_id and deploy_id != cur_deploy
770
-
771
- straykitten = momma.findLitterMate(type: type, cloud_id: cloud_id, name: name, mu_name: mu_name, credentials: credentials, created_only: true)
772
- if straykitten
773
- MU.log "Found matching kitten #{straykitten.mu_name} in-memory - #{sprintf("%.2fs", (Time.now-start))}", loglevel
774
- # Peace out if we found the exact resource we want
775
- if cloud_id and straykitten.cloud_id.to_s == cloud_id.to_s
776
- return [straykitten]
777
- elsif mu_name and straykitten.mu_name == mu_name
778
- return [straykitten]
779
- else
780
- kittens[straykitten.cloud_id] ||= straykitten
781
- end
782
- end
783
- }
784
-
785
- mu_descs = MU::MommaCat.getResourceMetadata(cfg_plural, name: name, deploy_id: deploy_id, mu_name: mu_name)
786
- MU.log "findStray: #{mu_descs.size.to_s} deploys had matches - #{sprintf("%.2fs", (Time.now-start))}", loglevel
787
-
788
- mu_descs.each_pair { |cur_deploy_id, matches|
789
- MU.log "findStray: #{cur_deploy_id} had #{matches.size.to_s} initial matches - #{sprintf("%.2fs", (Time.now-start))}", loglevel
790
- next if matches.nil? or matches.size == 0
791
-
792
- momma = MU::MommaCat.getLitter(cur_deploy_id)
793
-
794
- straykitten = nil
795
-
796
- # If we found exactly one match in this deploy, use its metadata to
797
- # guess at resource names we weren't told.
798
- if matches.size > 1 and cloud_id
799
- MU.log "findStray: attempting to narrow down multiple matches with cloud_id #{cloud_id} - #{sprintf("%.2fs", (Time.now-start))}", loglevel
800
- straykitten = momma.findLitterMate(type: type, cloud_id: cloud_id, credentials: credentials, created_only: true)
801
- elsif matches.size == 1 and name.nil? and mu_name.nil?
802
- if cloud_id.nil?
803
- straykitten = momma.findLitterMate(type: type, name: matches.first["name"], cloud_id: matches.first["cloud_id"], credentials: credentials)
804
- else
805
- MU.log "findStray: fetching single match with cloud_id #{cloud_id} - #{sprintf("%.2fs", (Time.now-start))}", loglevel
806
- straykitten = momma.findLitterMate(type: type, name: matches.first["name"], cloud_id: cloud_id, credentials: credentials)
807
- end
808
- # elsif !flags.nil? and !flags.empty? # XXX eh, maybe later
809
- # # see if we can narrow it down further with some flags
810
- # filtered = []
811
- # matches.each { |m|
812
- # f = resourceclass.find(cloud_id: m['mu_name'], flags: flags)
813
- # filtered << m if !f.nil? and f.size > 0
814
- # MU.log "RESULT FROM find(cloud_id: #{m['mu_name']}, flags: #{flags})", MU::WARN, details: f
815
- # }
816
- # if filtered.size == 1
817
- # straykitten = momma.findLitterMate(type: type, name: matches.first["name"], cloud_id: filtered.first['cloud_id'])
818
- # end
819
- else
820
- # There's more than one of this type of resource in the target
821
- # deploy, so see if findLitterMate can narrow it down for us
822
- straykitten = momma.findLitterMate(type: type, name: name, mu_name: mu_name, cloud_id: cloud_id, credentials: credentials)
823
- end
824
-
825
- next if straykitten.nil?
826
- straykitten.intoDeploy(momma)
827
-
828
- if straykitten.cloud_id.nil?
829
- MU.log "findStray: kitten #{straykitten.mu_name} came back with nil cloud_id", MU::WARN
830
- next
831
- end
832
-
833
- kittens[straykitten.cloud_id] ||= straykitten
834
-
835
- # Peace out if we found the exact resource we want
836
- if cloud_id and straykitten.cloud_id.to_s == cloud_id.to_s
837
- return [straykitten]
838
- # ...or if we've validated our one possible match
839
- elsif !cloud_id and mu_descs.size == 1 and matches.size == 1
840
- return [straykitten]
841
- elsif credentials and credlist.size == 1 and straykitten.credentials == credentials
842
- return [straykitten]
843
- end
844
- }
845
-
846
-
847
- # if !mu_descs.nil? and mu_descs.size > 0 and !deploy_id.nil? and !deploy_id.empty? and !mu_descs.first.empty?
848
- # MU.log "I found descriptions that might match #{resourceclass.cfg_plural} name: #{name}, deploy_id: #{deploy_id}, mu_name: #{mu_name}, but couldn't isolate my target kitten", MU::WARN, details: caller
849
- # puts File.read(deploy_dir(deploy_id)+"/deployment.json")
850
- # end
851
-
852
- # We can't refine any further by asking the cloud provider...
853
- if !cloud_id and !tag_key and !tag_value and kittens.size > 1
854
- if !allow_multi
855
- raise MuError, "Multiple matches in MU::MommaCat.findStray where none allowed from deploy_id: '#{deploy_id}', name: '#{name}', mu_name: '#{mu_name}' (#{caller[0]})"
856
- else
857
- return kittens.values
858
- end
859
- end
860
- end
861
-
862
- matches = []
863
-
864
- found_the_thing = false
865
- credlist.each { |creds|
866
- break if found_the_thing
867
- if cloud_id or (tag_key and tag_value) or !flags.empty? or allow_multi
868
-
869
- regions = begin
870
- region ? [region] : cloudclass.listRegions(credentials: creds)
871
- rescue NoMethodError # Not all cloud providers have regions
872
- [nil]
873
- end
874
-
875
- # ..not all resource types care about regions either
876
- if resourceclass.isGlobal?
877
- regions = [nil]
878
- end
879
-
880
- # Decide what habitats (accounts/projects/subscriptions) we'll
881
- # search, if applicable for this resource type.
882
- habitats ||= []
883
- begin
884
- if flags["project"] # backwards-compat
885
- habitats << flags["project"]
886
- end
887
- if habitats.empty?
888
- if resourceclass.canLiveIn.include?(nil)
889
- habitats << nil
890
- end
891
- if resourceclass.canLiveIn.include?(:Habitat)
892
- habitats.concat(cloudclass.listProjects(creds))
893
- end
894
- end
895
- rescue NoMethodError # we only expect this to work on Google atm
896
- end
897
-
898
- if habitats.empty?
899
- habitats << nil
900
- end
901
- habitats.uniq!
902
-
903
- habitat_threads = []
904
- desc_semaphore = Mutex.new
905
-
906
- cloud_descs = {}
907
- habitats.each { |hab|
908
- begin
909
- habitat_threads.each { |t| t.join(0.1) }
910
- habitat_threads.reject! { |t| t.nil? or !t.status }
911
- sleep 1 if habitat_threads.size > 5
912
- end while habitat_threads.size > 5
913
- habitat_threads << Thread.new(hab) { |p|
914
- MU.log "findStray: Searching #{p} (#{habitat_threads.size.to_s} habitat threads running) - #{sprintf("%.2fs", (Time.now-start))}", loglevel
915
- cloud_descs[p] = {}
916
- region_threads = []
917
- regions.each { |reg| region_threads << Thread.new(reg) { |r|
918
- MU.log "findStray: Searching #{r} in #{p} (#{region_threads.size.to_s} region threads running) - #{sprintf("%.2fs", (Time.now-start))}", loglevel
919
- MU.log "findStray: calling #{classname}.find(cloud_id: #{cloud_id}, region: #{r}, tag_key: #{tag_key}, tag_value: #{tag_value}, flags: #{flags}, credentials: #{creds}, project: #{p}) - #{sprintf("%.2fs", (Time.now-start))}", loglevel
920
- found = resourceclass.find(cloud_id: cloud_id, region: r, tag_key: tag_key, tag_value: tag_value, flags: flags, credentials: creds, habitat: p)
921
- MU.log "findStray: #{found ? found.size.to_s : "nil"} results - #{sprintf("%.2fs", (Time.now-start))}", loglevel
922
-
923
- if found
924
- desc_semaphore.synchronize {
925
- cloud_descs[p][r] = found
926
- }
927
- end
928
- # Stop if you found the thing by a specific cloud_id
929
- if cloud_id and found and !found.empty?
930
- found_the_thing = true
931
- Thread.exit
932
- end
933
- } }
934
- begin
935
- region_threads.each { |t| t.join(0.1) }
936
- region_threads.reject! { |t| t.nil? or !t.status }
937
- if region_threads.size > 0
938
- MU.log "#{region_threads.size.to_s} regions still running in #{p}", loglevel
939
- sleep 3
940
- end
941
- end while region_threads.size > 0
942
- }
943
- }
944
- begin
945
- habitat_threads.each { |t| t.join(0.1) }
946
- habitat_threads.reject! { |t| t.nil? or !t.status }
947
- if habitat_threads.size > 0
948
- MU.log "#{habitat_threads.size.to_s} habitats still running", loglevel
949
- sleep 3
950
- end
951
- end while habitat_threads.size > 0
952
-
953
- habitat_threads = []
954
- habitats.each { |hab| habitat_threads << Thread.new(hab) { |p|
955
- region_threads = []
956
- regions.each { |reg| region_threads << Thread.new(reg) { |r|
957
- next if cloud_descs[p][r].nil?
958
- cloud_descs[p][r].each_pair { |kitten_cloud_id, descriptor|
959
-
960
- # We already have a MU::Cloud object for this guy, use it
961
- if kittens.has_key?(kitten_cloud_id)
962
- desc_semaphore.synchronize {
963
- matches << kittens[kitten_cloud_id]
964
- }
965
- elsif kittens.size == 0
966
- if !dummy_ok
967
- next
968
- end
969
-
970
- # If we don't have a MU::Cloud object, manufacture a dummy
971
- # one. Give it a fake name if we have to and have decided
972
- # that's ok. Wild inferences from the cloud descriptor are
973
- # ok to try here.
974
- use_name = if (name.nil? or name.empty?)
975
- if !dummy_ok
976
- nil
977
- elsif !mu_name.nil?
978
- mu_name
979
- else
980
- try = nil
981
- [:display_name, :name, (resourceclass.cfg_name+"_name").to_sym].each { |field|
982
- if descriptor.respond_to?(field) and descriptor.send(field).is_a?(String)
983
- try = descriptor.send(field)
984
- break
985
- end
986
-
987
- }
988
- try ||= if !tag_value.nil?
989
- tag_value
990
- else
991
- kitten_cloud_id
992
- end
993
- try
994
- end
995
- else
996
- name
997
- end
998
- if use_name.nil?
999
- MU.log "Found cloud provider data for #{cloud} #{type} #{kitten_cloud_id}, but without a name I can't manufacture a proper #{type} object to return - #{sprintf("%.2fs", (Time.now-start))}", loglevel, details: caller
1000
- next
1001
- end
1002
- cfg = {
1003
- "name" => use_name,
1004
- "cloud" => cloud,
1005
- "credentials" => creds
1006
- }
1007
- if !r.nil? and !resourceclass.isGlobal?
1008
- cfg["region"] = r
1009
- end
1010
-
1011
- if !p.nil? and resourceclass.canLiveIn.include?(:Habitat)
1012
- cfg["project"] = p
1013
- end
1014
- # If we can at least find the config from the deploy this will
1015
- # belong with, use that, even if it's an ungroomed resource.
1016
- if !calling_deploy.nil? and
1017
- !calling_deploy.original_config.nil? and
1018
- !calling_deploy.original_config[type+"s"].nil?
1019
- calling_deploy.original_config[type+"s"].each { |s|
1020
- if s["name"] == use_name
1021
- cfg = s.dup
1022
- break
1023
- end
1024
- }
1025
-
1026
- newkitten = resourceclass.new(mommacat: calling_deploy, kitten_cfg: cfg, cloud_id: kitten_cloud_id)
1027
- desc_semaphore.synchronize {
1028
- matches << newkitten
1029
- }
1030
- else
1031
- if !@@dummy_cache[cfg_plural] or !@@dummy_cache[cfg_plural][cfg.to_s]
1032
- MU.log "findStray: Generating dummy '#{resourceclass.to_s}' cloudobj with name: #{use_name}, cloud_id: #{kitten_cloud_id.to_s} - #{sprintf("%.2fs", (Time.now-start))}", loglevel, details: cfg
1033
- resourceclass.new(mu_name: use_name, kitten_cfg: cfg, cloud_id: kitten_cloud_id.to_s, from_cloud_desc: descriptor)
1034
- desc_semaphore.synchronize {
1035
- @@dummy_cache[cfg_plural] ||= {}
1036
- @@dummy_cache[cfg_plural][cfg.to_s] = resourceclass.new(mu_name: use_name, kitten_cfg: cfg, cloud_id: kitten_cloud_id.to_s, from_cloud_desc: descriptor)
1037
- MU.log "findStray: Finished generating dummy '#{resourceclass.to_s}' cloudobj - #{sprintf("%.2fs", (Time.now-start))}", loglevel
1038
- }
1039
- end
1040
- desc_semaphore.synchronize {
1041
- matches << @@dummy_cache[cfg_plural][cfg.to_s]
1042
- }
1043
- end
1044
- end
1045
- }
1046
- } }
1047
- MU.log "findStray: tying up #{region_threads.size.to_s} region threads - #{sprintf("%.2fs", (Time.now-start))}", loglevel
1048
- region_threads.each { |t|
1049
- t.join
1050
- }
1051
- } }
1052
- MU.log "findStray: tying up #{habitat_threads.size.to_s} habitat threads - #{sprintf("%.2fs", (Time.now-start))}", loglevel
1053
- habitat_threads.each { |t|
1054
- t.join
1055
- }
1056
- end
1057
- }
1058
- rescue StandardError => e
1059
- MU.log e.inspect, MU::ERR, details: e.backtrace
1060
- end
1061
- MU.log "findStray: returning #{matches ? matches.size.to_s : "0"} matches - #{sprintf("%.2fs", (Time.now-start))}", loglevel
1062
-
1063
- matches
1064
- end
1065
-
1066
- # Return the resource object of another member of this deployment
1067
- # @param type [String,Symbol]: The type of resource
1068
- # @param name [String]: The name of the resource as defined in its 'name' Basket of Kittens field
1069
- # @param mu_name [String]: The fully-resolved and deployed name of the resource
1070
- # @param cloud_id [String]: The cloud provider's unique identifier for this resource
1071
- # @param created_only [Boolean]: Only return the littermate if its cloud_id method returns a value
1072
- # @param return_all [Boolean]: Return a Hash of matching objects indexed by their mu_name, instead of a single match. Only valid for resource types where has_multiples is true.
1073
- # @return [MU::Cloud]
1074
- def findLitterMate(type: nil, name: nil, mu_name: nil, cloud_id: nil, created_only: false, return_all: false, credentials: nil, habitat: nil, debug: false, indent: "")
1075
- shortclass, cfg_name, cfg_plural, classname, attrs = MU::Cloud.getResourceNames(type)
1076
- type = cfg_plural
1077
- has_multiples = attrs[:has_multiples]
1078
-
1079
- loglevel = debug ? MU::NOTICE : MU::DEBUG
1080
-
1081
- argstring = [:type, :name, :mu_name, :cloud_id, :created_only, :credentials, :habitat, :has_multiples].reject { |a|
1082
- binding.local_variable_get(a).nil?
1083
- }.map { |v|
1084
- v.to_s+": "+binding.local_variable_get(v).to_s
1085
- }.join(", ")
1086
-
1087
- # Fun times: if we specified a habitat, which we may also have done by
1088
- # its shorthand sibling name, let's... call ourselves first to make sure
1089
- # we're fishing for the right thing.
1090
- if habitat
1091
- if habitat.is_a?(MU::Config::Ref) and habitat.id
1092
- habitat = habitat.id
1093
- else
1094
- MU.log indent+"findLitterMate(#{argstring}): Attempting to resolve habitat name #{habitat}", loglevel
1095
- realhabitat = findLitterMate(type: "habitat", name: habitat, debug: debug, credentials: credentials, indent: indent+" ")
1096
- if realhabitat and realhabitat.mu_name
1097
- MU.log indent+"findLitterMate: Resolved habitat name #{habitat} to #{realhabitat.mu_name}", loglevel, details: [realhabitat.mu_name, realhabitat.cloud_id, realhabitat.config.keys]
1098
- habitat = realhabitat.cloud_id
1099
- elsif debug
1100
- MU.log indent+"findLitterMate(#{argstring}): Failed to resolve habitat name #{habitat}", MU::WARN
1101
- end
1102
- end
1103
- end
1104
-
1105
-
1106
- @kitten_semaphore.synchronize {
1107
- if !@kittens.has_key?(type)
1108
- if debug
1109
- MU.log indent+"NO SUCH KEY #{type} findLitterMate(#{argstring})", MU::WARN, details: @kittens.keys
1110
- end
1111
- return nil
1112
- end
1113
- MU.log indent+"START findLitterMate(#{argstring}), caller: #{caller[2]}", loglevel, details: @kittens[type].keys.map { |hab| hab.to_s+": "+@kittens[type][hab].keys.join(", ") }
1114
- matches = []
1115
-
1116
- @kittens[type].each { |habitat_group, sib_classes|
1117
- next if habitat and habitat_group != habitat and !habitat_group.nil?
1118
- sib_classes.each_pair { |sib_class, data|
1119
- virtual_name = nil
1120
-
1121
- if !has_multiples and data and !data.is_a?(Hash) and data.config and data.config.is_a?(Hash) and data.config['virtual_name'] and name == data.config['virtual_name']
1122
- virtual_name = data.config['virtual_name']
1123
- elsif !name.nil? and name != sib_class
1124
- next
1125
- end
1126
- if has_multiples
1127
- if !name.nil?
1128
- if return_all
1129
- MU.log indent+"MULTI-MATCH RETURN_ALL findLitterMate(#{argstring})", loglevel, details: data.keys
1130
- return data.dup
1131
- end
1132
- if data.size == 1 and (cloud_id.nil? or data.values.first.cloud_id == cloud_id)
1133
- return data.values.first
1134
- elsif mu_name.nil? and cloud_id.nil?
1135
- MU.log indent+"#{@deploy_id}: Found multiple matches in findLitterMate based on #{type}: #{name}, and not enough info to narrow down further. Returning an arbitrary result. Caller: #{caller[2]}", MU::WARN, details: data.keys
1136
- return data.values.first
1137
- end
1138
- end
1139
- data.each_pair { |sib_mu_name, obj|
1140
- if (!mu_name.nil? and mu_name == sib_mu_name) or
1141
- (!cloud_id.nil? and cloud_id == obj.cloud_id) or
1142
- (!credentials.nil? and credentials == obj.credentials)
1143
- if !created_only or !obj.cloud_id.nil?
1144
- if return_all
1145
- MU.log indent+"MULTI-MATCH RETURN_ALL findLitterMate(#{argstring})", loglevel, details: data.keys
1146
- return data.dup
1147
- else
1148
- MU.log indent+"MULTI-MATCH findLitterMate(#{argstring})", loglevel, details: data.keys
1149
- return obj
1150
- end
1151
- end
1152
- end
1153
- }
1154
- else
1155
-
1156
- MU.log indent+"CHECKING AGAINST findLitterMate #{habitat_group}/#{type}/#{sib_class} data.cloud_id: #{data.cloud_id}, data.credentials: #{data.credentials}, sib_class: #{sib_class}, virtual_name: #{virtual_name}", loglevel, details: argstring
1157
-
1158
- data_cloud_id = data.cloud_id.nil? ? nil : data.cloud_id.to_s
1159
-
1160
- MU.log indent+"(name.nil? or sib_class == name or virtual_name == name)", loglevel, details: (name.nil? or sib_class == name or virtual_name == name).to_s
1161
- MU.log indent+"(cloud_id.nil? or cloud_id[#{cloud_id.class.name}:#{cloud_id.to_s}] == data_cloud_id[#{data_cloud_id.class.name}:#{data_cloud_id}])", loglevel, details: (cloud_id.nil? or cloud_id == data_cloud_id).to_s
1162
- MU.log indent+"(credentials.nil? or data.credentials.nil? or credentials[#{credentials.class.name}:#{credentials}] == data.credentials[#{data.credentials.class.name}:#{data.credentials}])", loglevel, details: (credentials.nil? or data.credentials.nil? or credentials == data.credentials).to_s
1163
-
1164
- if (name.nil? or sib_class == name.to_s or virtual_name == name.to_s) and
1165
- (cloud_id.nil? or cloud_id.to_s == data_cloud_id) and
1166
- (credentials.nil? or data.credentials.nil? or credentials.to_s == data.credentials.to_s)
1167
- MU.log indent+"OUTER MATCH PASSED, NEED !created_only (#{created_only.to_s}) or !data_cloud_id.nil? (#{data_cloud_id})", loglevel, details: (cloud_id.nil? or cloud_id == data_cloud_id).to_s
1168
- if !created_only or !data_cloud_id.nil?
1169
- MU.log indent+"SINGLE MATCH findLitterMate(#{argstring})", loglevel, details: [data.mu_name, data_cloud_id, data.config.keys]
1170
- matches << data
1171
- end
1172
- end
1173
- end
1174
- }
1175
- }
1176
-
1177
- return matches.first if matches.size == 1
1178
- if return_all and matches.size > 1
1179
- return matches
1180
- end
1181
- }
1182
-
1183
- MU.log indent+"NO MATCH findLitterMate(#{argstring})", loglevel
1184
-
1185
- return nil
1186
- end
1187
-
1188
540
  # Add or remove a resource's metadata to this deployment's structure and
1189
541
  # flush it to disk.
1190
542
  # @param type [String]: The type of resource (e.g. *server*, *database*).
1191
543
  # @param key [String]: The name field of this resource.
544
+ # @param mu_name [String]: The mu_name of this resource.
1192
545
  # @param data [Hash]: The resource's metadata.
546
+ # @param triggering_node [MU::Cloud]: A cloud object calling this notify, usually on behalf of itself
1193
547
  # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
1194
548
  # @return [void]
1195
549
  def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
1196
- return if @no_artifacts
1197
- MU::MommaCat.lock("deployment-notification")
1198
550
 
1199
- if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
1200
- loadDeploy(true) # make sure we're saving the latest and greatest
1201
- end
551
+ begin
552
+ MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
1202
553
 
1203
- _shortclass, _cfg_name, cfg_plural, _classname, attrs = MU::Cloud.getResourceNames(type)
1204
- has_multiples = false
554
+ if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
555
+ loadDeploy(true) # make sure we're saving the latest and greatest
556
+ end
1205
557
 
1206
- # it's not always the case that we're logging data for a legal resource
1207
- # type, though that's what we're usually for
1208
- if cfg_plural
1209
- type = cfg_plural
1210
- has_multiples = attrs[:has_multiples]
1211
- end
558
+ _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
559
+ has_multiples = attrs[:has_multiples] ? true : false
1212
560
 
1213
- if mu_name.nil?
1214
- if !data.nil? and !data["mu_name"].nil?
1215
- mu_name = data["mu_name"]
561
+ mu_name ||= if !data.nil? and !data["mu_name"].nil?
562
+ data["mu_name"]
1216
563
  elsif !triggering_node.nil? and !triggering_node.mu_name.nil?
1217
- mu_name = triggering_node.mu_name
564
+ triggering_node.mu_name
1218
565
  end
1219
566
  if mu_name.nil? and has_multiples
1220
- MU.log "MU::MommaCat.notify called to modify deployment struct for a type (#{type}) with :has_multiples, but no mu_name available to look under #{key}. Call was #{caller[0]}", MU::WARN, details: data
1221
- MU::MommaCat.unlock("deployment-notification")
567
+ MU.log "MU::MommaCat.notify called to modify deployment struct for a type (#{type}) with :has_multiples, but no mu_name available to look under #{key}. Call was #{caller(1..1)}", MU::WARN, details: data
1222
568
  return
1223
569
  end
1224
- end
1225
570
 
1226
- @need_deploy_flush = true
571
+ @need_deploy_flush = true
1227
572
 
1228
- if !remove
1229
- if data.nil?
1230
- MU.log "MU::MommaCat.notify called to modify deployment struct, but no data provided", MU::WARN
1231
- MU::MommaCat.unlock("deployment-notification")
1232
- return
1233
- end
1234
- @notify_semaphore.synchronize {
1235
- @deployment[type] ||= {}
1236
- }
1237
- if has_multiples
573
+ if !remove
574
+ if data.nil?
575
+ MU.log "MU::MommaCat.notify called to modify deployment struct, but no data provided", MU::WARN
576
+ return
577
+ end
1238
578
  @notify_semaphore.synchronize {
1239
- @deployment[type][key] ||= {}
579
+ @deployment[type] ||= {}
1240
580
  }
1241
- # fix has_multiples classes that weren't tiered correctly
1242
- if @deployment[type][key].is_a?(Hash) and @deployment[type][key].has_key?("mu_name")
1243
- olddata = @deployment[type][key].dup
1244
- @deployment[type][key][olddata["mu_name"]] = olddata
1245
- end
1246
- @deployment[type][key][mu_name] = data
1247
- MU.log "Adding to @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: data
1248
- else
1249
- @deployment[type][key] = data
1250
- MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
1251
- end
1252
- save!(key) if !delayed_save
1253
- else
1254
- have_deploy = true
1255
- if @deployment[type].nil? or @deployment[type][key].nil?
1256
-
1257
581
  if has_multiples
1258
- MU.log "MU::MommaCat.notify called to remove #{type} #{key} #{mu_name} deployment struct, but no such data exist", MU::DEBUG
582
+ @notify_semaphore.synchronize {
583
+ @deployment[type][key] ||= {}
584
+ }
585
+ @deployment[type][key][mu_name] = data
586
+ MU.log "Adding to @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: data
1259
587
  else
1260
- MU.log "MU::MommaCat.notify called to remove #{type} #{key} deployment struct, but no such data exist", MU::DEBUG
588
+ @deployment[type][key] = data
589
+ MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
590
+ end
591
+ save!(key) if !delayed_save and !@no_artifacts
592
+ else
593
+ have_deploy = true
594
+ if @deployment[type].nil? or @deployment[type][key].nil?
595
+ MU.log "MU::MommaCat.notify called to remove #{type} #{key}#{has_multiples ? " "+mu_name : ""} deployment struct, but no such data exist", MU::DEBUG
596
+ return
1261
597
  end
1262
- MU::MommaCat.unlock("deployment-notification")
1263
598
 
1264
- return
1265
- end
599
+ if have_deploy
600
+ @notify_semaphore.synchronize {
601
+ if has_multiples
602
+ MU.log "Removing @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: @deployment[type][key][mu_name]
603
+ @deployment[type][key].delete(mu_name)
604
+ end
1266
605
 
1267
- if have_deploy
1268
- @notify_semaphore.synchronize {
1269
- if has_multiples
1270
- MU.log "Removing @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: @deployment[type][key][mu_name]
1271
- @deployment[type][key].delete(mu_name)
1272
- if @deployment[type][key].size == 0
606
+ if @deployment[type][key].empty? or !has_multiples
607
+ MU.log "Removing @deployment[#{type}][#{key}]", MU::DEBUG, details: @deployment[type][key]
1273
608
  @deployment[type].delete(key)
1274
609
  end
1275
- else
1276
- MU.log "Removing @deployment[#{type}][#{key}]", MU::DEBUG, details: @deployment[type][key]
1277
- @deployment[type].delete(key)
1278
- end
1279
- if @deployment[type].size == 0
1280
- @deployment.delete(type)
1281
- end
1282
- }
1283
- end
1284
- save! if !delayed_save
1285
610
 
611
+ if @deployment[type].empty?
612
+ @deployment.delete(type)
613
+ end
614
+ }
615
+ end
616
+ save! if !delayed_save and !@no_artifacts
617
+ end
618
+ ensure
619
+ MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
1286
620
  end
1287
-
1288
- MU::MommaCat.unlock("deployment-notification")
1289
621
  end
1290
622
 
1291
623
  # Send a Slack notification to a deployment's administrators.
1292
624
  # @param subject [String]: The subject line of the message.
1293
625
  # @param msg [String]: The message body.
1294
626
  # @return [void]
1295
- def sendAdminSlack(subject, msg: "")
1296
- if $MU_CFG['slack'] and $MU_CFG['slack']['webhook'] and
1297
- (!$MU_CFG['slack']['skip_environments'] or !$MU_CFG['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 })
627
+ def sendAdminSlack(subject, msg: "", scrub_mu_isms: true, snippets: [], noop: false)
628
+ if MU.muCfg['slack'] and MU.muCfg['slack']['webhook'] and
629
+ (!MU.muCfg['slack']['skip_environments'] or !MU.muCfg['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 })
1298
630
  require 'slack-notifier'
1299
- slack = Slack::Notifier.new $MU_CFG['slack']['webhook']
631
+ slackargs = nil
632
+ keyword_args = { channel: MU.muCfg['slack']['channel'] }
633
+ begin
634
+ slack = Slack::Notifier.new MU.muCfg['slack']['webhook']
635
+ prefix = scrub_mu_isms ? subject : "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}"
636
+
637
+ text = if msg and !msg.empty?
638
+ "#{prefix}:\n\n```#{msg}```"
639
+ else
640
+ prefix
641
+ end
1300
642
 
1301
- if msg and !msg.empty?
1302
- slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}:\n\n```#{msg}\n```", channel: $MU_CFG['slack']['channel']
1303
- else
1304
- slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}", channel: $MU_CFG['slack']['channel']
643
+ if snippets and snippets.size > 0
644
+ keyword_args[:attachments] = snippets
645
+ end
646
+
647
+ if !noop
648
+ slack.ping(text, **keyword_args)
649
+ else
650
+ MU.log "Would send to #{MU.muCfg['slack']['channel']}", MU::NOTICE, details: [ text, keyword_args ]
651
+ end
652
+ rescue Slack::Notifier::APIError => e
653
+ MU.log "Failed to send message to slack: #{e.message}", MU::ERR, details: keyword_args
654
+ return false
1305
655
  end
1306
656
  end
657
+ true
1307
658
  end
1308
659
 
1309
660
  # Send an email notification to a deployment's administrators.
@@ -1324,13 +675,13 @@ module MU
1324
675
  to << "#{admin['name']} <#{admin['email']}>"
1325
676
  }
1326
677
  end
1327
- message = <<MESSAGE_END
678
+ message = <<MAIL_HEAD_END
1328
679
  From: #{MU.handle} <root@localhost>
1329
680
  To: #{to.join(",")}
1330
681
  Subject: #{subject}
1331
682
 
1332
683
  #{msg}
1333
- MESSAGE_END
684
+ MAIL_HEAD_END
1334
685
  if !kitten.nil? and kitten.kind_of?(MU::Cloud)
1335
686
  message = message + "\n\n**** #{kitten}:\n"
1336
687
  if !kitten.report.nil?
@@ -1418,124 +769,59 @@ MESSAGE_END
1418
769
  MU::Master::SSL.sign(csr_path, sans, for_user: MU.mu_user)
1419
770
  end
1420
771
 
1421
- # Make sure deployment data is synchronized to/from each node in the
772
+ # Make sure deployment data is synchronized to/from each +Server+ in the
1422
773
  # currently-loaded deployment.
774
+ # @param nodeclasses [Array<String>]
775
+ # @param triggering_node [String,MU::Cloud::Server]
776
+ # @param save_only [Boolean]
1423
777
  def syncLitter(nodeclasses = [], triggering_node: nil, save_only: false)
1424
- # XXX take some config logic to decide what nodeclasses to hit? like, make
1425
- # inferences from dependencies or something?
1426
-
1427
- return if MU.syncLitterThread
778
+ return if MU.syncLitterThread # don't run recursively by accident
1428
779
  return if !Dir.exist?(deploy_dir)
1429
- svrs = MU::Cloud.resource_types[:Server][:cfg_plural] # legibility shorthand
1430
- if !triggering_node.nil? and nodeclasses.size > 0
1431
- nodeclasses.reject! { |n| n == triggering_node.to_s }
1432
- return if nodeclasses.size == 0
1433
- end
1434
-
1435
- @kitten_semaphore.synchronize {
1436
- if @kittens.nil? or
1437
- @kittens[svrs].nil?
1438
- MU.log "No #{svrs} as yet available in #{@deploy_id}", MU::DEBUG, details: @kittens
1439
- return
1440
- end
1441
780
 
781
+ if !triggering_node.nil? and triggering_node.is_a?(MU::Cloud::Server)
782
+ triggering_node = triggering_node.mu_name
783
+ end
1442
784
 
1443
- MU.log "Updating these node classes in #{@deploy_id}", MU::DEBUG, details: nodeclasses
1444
- }
785
+ siblings = findLitterMate(type: "server", return_all: true)
786
+ return if siblings.nil? or (siblings.respond_to?(:empty?) and siblings.empty?)
1445
787
 
1446
788
  update_servers = []
1447
- if nodeclasses.nil? or nodeclasses.size == 0
1448
- litter = findLitterMate(type: "server", return_all: true)
1449
- return if litter.nil?
1450
- litter.each_pair { |mu_name, node|
1451
- if !triggering_node.nil? and (
1452
- (triggering_node.is_a?(MU::Cloud::Server) and mu_name == triggering_node.mu_name) or
1453
- (triggering_node.is_a?(String) and mu_name == triggering_node)
1454
- )
1455
- next
1456
- end
1457
-
1458
- if !node.groomer.nil?
1459
- update_servers << node
1460
- end
1461
- }
1462
- else
1463
- litter = {}
1464
- nodeclasses.each { |nodeclass|
1465
- mates = findLitterMate(type: "server", name: nodeclass, return_all: true)
1466
- litter.merge!(mates) if mates
1467
- }
1468
- litter.each_pair { |mu_name, node|
1469
- if !triggering_node.nil? and (
1470
- (triggering_node.is_a?(MU::Cloud::Server) and mu_name == triggering_node.mu_name) or
1471
- (triggering_node.is_a?(String) and mu_name == triggering_node)
1472
- )
1473
- next
1474
- end
1475
-
1476
- if !node.deploydata or !node.deploydata.keys.include?('nodename')
1477
- details = node.deploydata ? node.deploydata.keys : nil
1478
- MU.log "#{mu_name} deploy data is missing (possibly retired or mid-bootstrap), so not syncing it", MU::WARN, details: details
1479
- else
1480
- update_servers << node
1481
- end
1482
- }
1483
- end
1484
- return if update_servers.size == 0
1485
-
1486
- MU.log "Updating these nodes in #{@deploy_id}", MU::DEBUG, details: update_servers.map { |n| n.mu_name }
1487
-
1488
- update_servers.each { |node|
1489
- # Not clear where this pollution comes from, but let's stick a temp
1490
- # fix in here.
1491
- if node.deploydata['nodename'] != node.mu_name and
1492
- !node.deploydata['nodename'].nil? and !node.deploydata['nodename'].emty?
1493
- MU.log "Node #{node.mu_name} had wrong or missing nodename (#{node.deploydata['nodename']}), correcting", MU::WARN
1494
- node.deploydata['nodename'] = node.mu_name
1495
- if @deployment[svrs] and @deployment[svrs][node.config['name']] and
1496
- @deployment[svrs][node.config['name']][node.mu_name]
1497
- @deployment[svrs][node.config['name']][node.mu_name]['nodename'] = node.mu_name
1498
- end
1499
- save!
1500
- end
789
+ siblings.each_pair { |mu_name, node|
790
+ next if mu_name == triggering_node or node.groomer.nil?
791
+ next if nodeclasses.size > 0 and !nodeclasses.include?(node.config['name'])
792
+ if !node.deploydata or !node.deploydata['nodename']
793
+ MU.log "#{mu_name} deploy data is missing (possibly retired or mid-bootstrap), so not syncing it", MU::NOTICE
794
+ next
795
+ end
796
+
797
+ if @deployment["servers"][node.config['name']][node.mu_name].nil? or
798
+ @deployment["servers"][node.config['name']][node.mu_name] != node.deploydata
799
+ @deployment["servers"][node.config['name']][node.mu_name] = node.deploydata
800
+ elsif !save_only
801
+ # Don't bother running grooms on nodes that don't need to be updated,
802
+ # unless we're just going to do a save.
803
+ next
804
+ end
805
+ update_servers << node
1501
806
  }
1502
807
 
1503
- # Merge everyone's deploydata together
1504
- if !save_only
1505
- skip = []
1506
- update_servers.each { |node|
1507
- if node.mu_name.nil? or node.deploydata.nil? or node.config.nil?
1508
- MU.log "Missing mu_name #{node.mu_name}, deploydata, or config from #{node} in syncLitter", MU::ERR, details: node.deploydata
1509
- next
1510
- end
808
+ return if update_servers.empty?
1511
809
 
1512
- if !@deployment[svrs][node.config['name']].has_key?(node.mu_name) or @deployment[svrs][node.config['name']][node.mu_name] != node.deploydata
1513
- @deployment[svrs][node.config['name']][node.mu_name] = node.deploydata
1514
- else
1515
- skip << node
1516
- end
1517
- }
1518
- update_servers = update_servers - skip
1519
- end
810
+ MU.log "Updating nodes in #{@deploy_id}", MU::DEBUG, details: update_servers.map { |n| n.mu_name }
1520
811
 
1521
- return if MU.inGem? || update_servers.size < 1
1522
812
  threads = []
1523
- parent_thread_id = Thread.current.object_id
1524
813
  update_servers.each { |sibling|
814
+ next if sibling.config.has_key?("groom") and !sibling.config["groom"]
1525
815
  threads << Thread.new {
1526
816
  Thread.abort_on_exception = true
1527
- MU.dupGlobals(parent_thread_id)
1528
817
  Thread.current.thread_variable_set("name", "sync-"+sibling.mu_name.downcase)
1529
818
  MU.setVar("syncLitterThread", true)
1530
819
  begin
1531
- if sibling.config['groom'].nil? or sibling.config['groom']
1532
- sibling.groomer.saveDeployData
1533
- sibling.groomer.run(purpose: "Synchronizing sibling kittens") if !save_only
1534
- end
820
+ sibling.groomer.saveDeployData
821
+ sibling.groomer.run(purpose: "Synchronizing sibling kittens") if !save_only
1535
822
  rescue MU::Groomer::RunError => e
1536
- MU.log "Sync of #{sibling.mu_name} failed: #{e.inspect}", MU::WARN
823
+ MU.log "Sync of #{sibling.mu_name} failed", MU::WARN, details: e.inspect
1537
824
  end
1538
- MU.purgeGlobals
1539
825
  }
1540
826
  }
1541
827
 
@@ -1568,6 +854,7 @@ MESSAGE_END
1568
854
  MU::Master::SSL.bootstrap
1569
855
  sans = []
1570
856
  sans << canonical_ip if canonical_ip
857
+ sans << resource.mu_name.downcase if resource.mu_name and resource.mu_name != cert_cn
1571
858
  # XXX were there other names we wanted to include?
1572
859
  key = MU::Master::SSL.getKey(cert_cn, keysize: keysize)
1573
860
  cert, pfx_cert = MU::Master::SSL.getCert(cert_cn, "/CN=#{cert_cn}/O=Mu/C=US", sans: sans, pfx: is_windows)
@@ -1581,7 +868,7 @@ MESSAGE_END
1581
868
  end
1582
869
 
1583
870
  if resource and resource.config and resource.config['cloud']
1584
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource.config['cloud'])
871
+ cloudclass = MU::Cloud.cloudClass(resource.config['cloud'])
1585
872
 
1586
873
  cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
1587
874
  cloudclass.writeDeploySecret(@deploy_id, key.to_pem, cert_cn+".key", credentials: resource.config['credentials'])