cloud-mu 3.1.5 → 3.1.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +5 -1
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/tasks/main.yml +16 -0
- data/bin/mu-adopt +2 -1
- data/bin/mu-configure +16 -0
- data/bin/mu-node-manage +15 -16
- data/cloud-mu.gemspec +2 -2
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
- data/extras/clean-stock-amis +25 -19
- data/extras/image-generators/AWS/win2k12.yaml +2 -0
- data/extras/image-generators/AWS/win2k16.yaml +2 -0
- data/extras/image-generators/AWS/win2k19.yaml +2 -0
- data/modules/mommacat.ru +1 -1
- data/modules/mu.rb +6 -5
- data/modules/mu/adoption.rb +19 -4
- data/modules/mu/cleanup.rb +181 -293
- data/modules/mu/cloud.rb +58 -17
- data/modules/mu/clouds/aws.rb +36 -1
- data/modules/mu/clouds/aws/container_cluster.rb +30 -21
- data/modules/mu/clouds/aws/role.rb +1 -1
- data/modules/mu/clouds/aws/vpc.rb +5 -1
- data/modules/mu/clouds/azure.rb +10 -0
- data/modules/mu/clouds/cloudformation.rb +10 -0
- data/modules/mu/clouds/google.rb +18 -4
- data/modules/mu/clouds/google/bucket.rb +2 -2
- data/modules/mu/clouds/google/container_cluster.rb +10 -7
- data/modules/mu/clouds/google/database.rb +3 -3
- data/modules/mu/clouds/google/firewall_rule.rb +3 -3
- data/modules/mu/clouds/google/function.rb +3 -3
- data/modules/mu/clouds/google/loadbalancer.rb +4 -4
- data/modules/mu/clouds/google/role.rb +18 -9
- data/modules/mu/clouds/google/server.rb +16 -14
- data/modules/mu/clouds/google/server_pool.rb +4 -4
- data/modules/mu/clouds/google/user.rb +2 -2
- data/modules/mu/clouds/google/vpc.rb +9 -13
- data/modules/mu/config.rb +1 -1
- data/modules/mu/config/container_cluster.rb +5 -0
- data/modules/mu/config/doc_helpers.rb +1 -1
- data/modules/mu/config/ref.rb +12 -6
- data/modules/mu/config/schema_helpers.rb +8 -3
- data/modules/mu/config/server.rb +7 -0
- data/modules/mu/config/tail.rb +1 -0
- data/modules/mu/config/vpc.rb +15 -7
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +48 -48
- data/modules/mu/deploy.rb +1 -1
- data/modules/mu/groomer.rb +1 -1
- data/modules/mu/groomers/ansible.rb +69 -4
- data/modules/mu/groomers/chef.rb +48 -4
- data/modules/mu/master.rb +75 -3
- data/modules/mu/mommacat.rb +104 -855
- data/modules/mu/mommacat/naming.rb +28 -0
- data/modules/mu/mommacat/search.rb +463 -0
- data/modules/mu/mommacat/storage.rb +185 -183
- data/modules/tests/super_simple_bok.yml +1 -3
- metadata +8 -5
data/modules/mu/master.rb
CHANGED
@@ -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 "
|
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
|
@@ -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("#{
|
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("#{
|
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|
|
data/modules/mu/mommacat.rb
CHANGED
@@ -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.
|
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
|
@@ -187,54 +188,10 @@ module MU
|
|
187
188
|
end
|
188
189
|
|
189
190
|
if create and !@no_artifacts
|
190
|
-
|
191
|
-
|
192
|
-
|
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
|
-
|
191
|
+
initDeployDirectory
|
192
|
+
setDeploySecret
|
193
|
+
MU::MommaCat.setThreadContext(self) if set_context_to_me
|
236
194
|
save!
|
237
|
-
|
238
195
|
end
|
239
196
|
|
240
197
|
@appname ||= MU.appname
|
@@ -242,10 +199,8 @@ module MU
|
|
242
199
|
@environment ||= MU.environment
|
243
200
|
|
244
201
|
loadDeploy(set_context_to_me: set_context_to_me)
|
245
|
-
if !deploy_secret.nil?
|
246
|
-
|
247
|
-
raise DeployInitializeError, "Client request did not include a valid deploy authorization secret. Verify that userdata runs correctly?"
|
248
|
-
end
|
202
|
+
if !deploy_secret.nil? and !authKey(deploy_secret)
|
203
|
+
raise DeployInitializeError, "Client request did not include a valid deploy authorization secret. Verify that userdata runs correctly?"
|
249
204
|
end
|
250
205
|
|
251
206
|
|
@@ -257,86 +212,7 @@ module MU
|
|
257
212
|
# deploy, IF it already exists, which is to say if we're loading an
|
258
213
|
# existing deploy instead of creating a new one.
|
259
214
|
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
|
-
}
|
215
|
+
loadObjects(delay_descriptor_load)
|
340
216
|
end
|
341
217
|
|
342
218
|
@initializing = false
|
@@ -349,7 +225,7 @@ module MU
|
|
349
225
|
def cloudsUsed
|
350
226
|
seen = []
|
351
227
|
seen << @original_config['cloud'] if @original_config['cloud']
|
352
|
-
MU::Cloud.resource_types.
|
228
|
+
MU::Cloud.resource_types.each_value { |attrs|
|
353
229
|
type = attrs[:cfg_plural]
|
354
230
|
if @original_config[type]
|
355
231
|
@original_config[type].each { |resource|
|
@@ -369,18 +245,15 @@ module MU
|
|
369
245
|
# clouds = []
|
370
246
|
seen << @original_config['credentials'] if @original_config['credentials']
|
371
247
|
# defaultcloud = @original_config['cloud']
|
372
|
-
MU::Cloud.resource_types.
|
248
|
+
MU::Cloud.resource_types.each_value { |attrs|
|
373
249
|
type = attrs[:cfg_plural]
|
374
250
|
if @original_config[type]
|
375
251
|
@original_config[type].each { |resource|
|
376
252
|
if resource['credentials']
|
377
253
|
seen << resource['credentials']
|
378
254
|
else
|
379
|
-
|
380
|
-
|
381
|
-
else
|
382
|
-
Object.const_get("MU").const_get("Cloud").const_get(MU::Config.defaultCloud)
|
383
|
-
end
|
255
|
+
cloudconst = @original_config['cloud'] ? @original_config['cloud'] : MU::Config.defaultCloud
|
256
|
+
Object.const_get("MU").const_get("Cloud").const_get(cloudconst)
|
384
257
|
seen << cloudclass.credConfig(name_only: true)
|
385
258
|
end
|
386
259
|
}
|
@@ -404,7 +277,7 @@ module MU
|
|
404
277
|
end
|
405
278
|
end
|
406
279
|
|
407
|
-
MU::Cloud.resource_types.
|
280
|
+
MU::Cloud.resource_types.each_value { |attrs|
|
408
281
|
type = attrs[:cfg_plural]
|
409
282
|
if @original_config[type]
|
410
283
|
@original_config[type].each { |resource|
|
@@ -481,7 +354,7 @@ module MU
|
|
481
354
|
end
|
482
355
|
|
483
356
|
count = 0
|
484
|
-
MU::Cloud.resource_types.
|
357
|
+
MU::Cloud.resource_types.each_value { |data|
|
485
358
|
next if @original_config[data[:cfg_plural]].nil?
|
486
359
|
next if realtypes.size > 0 and (!negate and !realtypes.include?(data[:cfg_plural]))
|
487
360
|
@original_config[data[:cfg_plural]].each { |resource|
|
@@ -499,13 +372,13 @@ module MU
|
|
499
372
|
raise MuError, "Nil arguments to removeKitten are not allowed"
|
500
373
|
end
|
501
374
|
@kitten_semaphore.synchronize {
|
502
|
-
MU::Cloud.resource_types.
|
375
|
+
MU::Cloud.resource_types.each_value { |attrs|
|
503
376
|
type = attrs[:cfg_plural]
|
504
377
|
next if !@kittens.has_key?(type)
|
505
378
|
tmplitter = @kittens[type].values.dup
|
506
379
|
tmplitter.each { |nodeclass, data|
|
507
380
|
if data.is_a?(Hash)
|
508
|
-
data.
|
381
|
+
data.each_key { |mu_name|
|
509
382
|
if data == object
|
510
383
|
@kittens[type][nodeclass].delete(mu_name)
|
511
384
|
return
|
@@ -534,13 +407,12 @@ module MU
|
|
534
407
|
end
|
535
408
|
|
536
409
|
_shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
|
537
|
-
has_multiples = attrs[:has_multiples]
|
538
410
|
object.intoDeploy(self)
|
539
411
|
|
540
412
|
@kitten_semaphore.synchronize {
|
541
413
|
@kittens[type] ||= {}
|
542
414
|
@kittens[type][object.habitat] ||= {}
|
543
|
-
if has_multiples
|
415
|
+
if attrs[:has_multiples]
|
544
416
|
@kittens[type][object.habitat][name] ||= {}
|
545
417
|
@kittens[type][object.habitat][name][object.mu_name] = object
|
546
418
|
else
|
@@ -577,7 +449,7 @@ module MU
|
|
577
449
|
loadDeploy(true) # make sure we're not trampling deployment data
|
578
450
|
@secret_semaphore.synchronize {
|
579
451
|
if @secrets[type].nil?
|
580
|
-
raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.
|
452
|
+
raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.join(", ")})"
|
581
453
|
end
|
582
454
|
@secrets[type][instance_id] = encryptWithDeployKey(raw_secret)
|
583
455
|
}
|
@@ -593,7 +465,7 @@ module MU
|
|
593
465
|
@secret_semaphore.synchronize {
|
594
466
|
if @secrets[type].nil?
|
595
467
|
return nil if quiet
|
596
|
-
raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.
|
468
|
+
raise SecretError, "'#{type}' is not a valid secret type (valid types: #{@secrets.keys.join(", ")})"
|
597
469
|
end
|
598
470
|
if @secrets[type][instance_id].nil?
|
599
471
|
return nil if quiet
|
@@ -654,645 +526,88 @@ module MU
|
|
654
526
|
|
655
527
|
@@dummy_cache = {}
|
656
528
|
|
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
|
-
# AWS-style tags
|
980
|
-
elsif descriptor.respond_to?(:tags) and
|
981
|
-
descriptor.tags.is_a?(Array) and
|
982
|
-
descriptor.tags.first.respond_to?(:key) and
|
983
|
-
descriptor.tags.map { |t| t.key }.include?("Name")
|
984
|
-
descriptor.tags.select { |t| t.key == "Name" }.first.value
|
985
|
-
else
|
986
|
-
try = nil
|
987
|
-
# Various GCP fields
|
988
|
-
[:display_name, :name, (resourceclass.cfg_name+"_name").to_sym].each { |field|
|
989
|
-
if descriptor.respond_to?(field) and descriptor.send(field).is_a?(String)
|
990
|
-
try = descriptor.send(field)
|
991
|
-
break
|
992
|
-
end
|
993
|
-
|
994
|
-
}
|
995
|
-
try ||= if !tag_value.nil?
|
996
|
-
tag_value
|
997
|
-
else
|
998
|
-
kitten_cloud_id
|
999
|
-
end
|
1000
|
-
try
|
1001
|
-
end
|
1002
|
-
else
|
1003
|
-
name
|
1004
|
-
end
|
1005
|
-
if use_name.nil?
|
1006
|
-
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
|
1007
|
-
next
|
1008
|
-
end
|
1009
|
-
cfg = {
|
1010
|
-
"name" => use_name,
|
1011
|
-
"cloud" => cloud,
|
1012
|
-
"credentials" => creds
|
1013
|
-
}
|
1014
|
-
if !r.nil? and !resourceclass.isGlobal?
|
1015
|
-
cfg["region"] = r
|
1016
|
-
end
|
1017
|
-
|
1018
|
-
if !p.nil? and resourceclass.canLiveIn.include?(:Habitat)
|
1019
|
-
cfg["project"] = p
|
1020
|
-
end
|
1021
|
-
# If we can at least find the config from the deploy this will
|
1022
|
-
# belong with, use that, even if it's an ungroomed resource.
|
1023
|
-
if !calling_deploy.nil? and
|
1024
|
-
!calling_deploy.original_config.nil? and
|
1025
|
-
!calling_deploy.original_config[type+"s"].nil?
|
1026
|
-
calling_deploy.original_config[type+"s"].each { |s|
|
1027
|
-
if s["name"] == use_name
|
1028
|
-
cfg = s.dup
|
1029
|
-
break
|
1030
|
-
end
|
1031
|
-
}
|
1032
|
-
|
1033
|
-
newkitten = resourceclass.new(mommacat: calling_deploy, kitten_cfg: cfg, cloud_id: kitten_cloud_id)
|
1034
|
-
desc_semaphore.synchronize {
|
1035
|
-
matches << newkitten
|
1036
|
-
}
|
1037
|
-
else
|
1038
|
-
if !@@dummy_cache[cfg_plural] or !@@dummy_cache[cfg_plural][cfg.to_s]
|
1039
|
-
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
|
1040
|
-
resourceclass.new(mu_name: use_name, kitten_cfg: cfg, cloud_id: kitten_cloud_id.to_s, from_cloud_desc: descriptor)
|
1041
|
-
desc_semaphore.synchronize {
|
1042
|
-
@@dummy_cache[cfg_plural] ||= {}
|
1043
|
-
@@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)
|
1044
|
-
MU.log "findStray: Finished generating dummy '#{resourceclass.to_s}' cloudobj - #{sprintf("%.2fs", (Time.now-start))}", loglevel
|
1045
|
-
}
|
1046
|
-
end
|
1047
|
-
desc_semaphore.synchronize {
|
1048
|
-
matches << @@dummy_cache[cfg_plural][cfg.to_s]
|
1049
|
-
}
|
1050
|
-
end
|
1051
|
-
end
|
1052
|
-
}
|
1053
|
-
} }
|
1054
|
-
MU.log "findStray: tying up #{region_threads.size.to_s} region threads - #{sprintf("%.2fs", (Time.now-start))}", loglevel
|
1055
|
-
region_threads.each { |t|
|
1056
|
-
t.join
|
1057
|
-
}
|
1058
|
-
} }
|
1059
|
-
MU.log "findStray: tying up #{habitat_threads.size.to_s} habitat threads - #{sprintf("%.2fs", (Time.now-start))}", loglevel
|
1060
|
-
habitat_threads.each { |t|
|
1061
|
-
t.join
|
1062
|
-
}
|
1063
|
-
end
|
1064
|
-
}
|
1065
|
-
rescue StandardError => e
|
1066
|
-
MU.log e.inspect, MU::ERR, details: e.backtrace
|
1067
|
-
end
|
1068
|
-
MU.log "findStray: returning #{matches ? matches.size.to_s : "0"} matches - #{sprintf("%.2fs", (Time.now-start))}", loglevel
|
1069
|
-
|
1070
|
-
matches
|
1071
|
-
end
|
1072
|
-
|
1073
|
-
# Return the resource object of another member of this deployment
|
1074
|
-
# @param type [String,Symbol]: The type of resource
|
1075
|
-
# @param name [String]: The name of the resource as defined in its 'name' Basket of Kittens field
|
1076
|
-
# @param mu_name [String]: The fully-resolved and deployed name of the resource
|
1077
|
-
# @param cloud_id [String]: The cloud provider's unique identifier for this resource
|
1078
|
-
# @param created_only [Boolean]: Only return the littermate if its cloud_id method returns a value
|
1079
|
-
# @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.
|
1080
|
-
# @return [MU::Cloud]
|
1081
|
-
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: "")
|
1082
|
-
shortclass, cfg_name, cfg_plural, classname, attrs = MU::Cloud.getResourceNames(type)
|
1083
|
-
type = cfg_plural
|
1084
|
-
has_multiples = attrs[:has_multiples]
|
1085
|
-
|
1086
|
-
loglevel = debug ? MU::NOTICE : MU::DEBUG
|
1087
|
-
|
1088
|
-
argstring = [:type, :name, :mu_name, :cloud_id, :created_only, :credentials, :habitat, :has_multiples].reject { |a|
|
1089
|
-
binding.local_variable_get(a).nil?
|
1090
|
-
}.map { |v|
|
1091
|
-
v.to_s+": "+binding.local_variable_get(v).to_s
|
1092
|
-
}.join(", ")
|
1093
|
-
|
1094
|
-
# Fun times: if we specified a habitat, which we may also have done by
|
1095
|
-
# its shorthand sibling name, let's... call ourselves first to make sure
|
1096
|
-
# we're fishing for the right thing.
|
1097
|
-
if habitat
|
1098
|
-
if habitat.is_a?(MU::Config::Ref) and habitat.id
|
1099
|
-
habitat = habitat.id
|
1100
|
-
else
|
1101
|
-
MU.log indent+"findLitterMate(#{argstring}): Attempting to resolve habitat name #{habitat}", loglevel
|
1102
|
-
realhabitat = findLitterMate(type: "habitat", name: habitat, debug: debug, credentials: credentials, indent: indent+" ")
|
1103
|
-
if realhabitat and realhabitat.mu_name
|
1104
|
-
MU.log indent+"findLitterMate: Resolved habitat name #{habitat} to #{realhabitat.mu_name}", loglevel, details: [realhabitat.mu_name, realhabitat.cloud_id, realhabitat.config.keys]
|
1105
|
-
habitat = realhabitat.cloud_id
|
1106
|
-
elsif debug
|
1107
|
-
MU.log indent+"findLitterMate(#{argstring}): Failed to resolve habitat name #{habitat}", MU::WARN
|
1108
|
-
end
|
1109
|
-
end
|
1110
|
-
end
|
1111
|
-
|
1112
|
-
|
1113
|
-
@kitten_semaphore.synchronize {
|
1114
|
-
if !@kittens.has_key?(type)
|
1115
|
-
if debug
|
1116
|
-
MU.log indent+"NO SUCH KEY #{type} findLitterMate(#{argstring})", MU::WARN, details: @kittens.keys
|
1117
|
-
end
|
1118
|
-
return nil
|
1119
|
-
end
|
1120
|
-
MU.log indent+"START findLitterMate(#{argstring}), caller: #{caller[2]}", loglevel, details: @kittens[type].keys.map { |hab| hab.to_s+": "+@kittens[type][hab].keys.join(", ") }
|
1121
|
-
matches = []
|
1122
|
-
|
1123
|
-
@kittens[type].each { |habitat_group, sib_classes|
|
1124
|
-
next if habitat and habitat_group != habitat and !habitat_group.nil?
|
1125
|
-
sib_classes.each_pair { |sib_class, data|
|
1126
|
-
virtual_name = nil
|
1127
|
-
|
1128
|
-
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']
|
1129
|
-
virtual_name = data.config['virtual_name']
|
1130
|
-
elsif !name.nil? and name != sib_class
|
1131
|
-
next
|
1132
|
-
end
|
1133
|
-
if has_multiples
|
1134
|
-
if !name.nil?
|
1135
|
-
if return_all
|
1136
|
-
MU.log indent+"MULTI-MATCH RETURN_ALL findLitterMate(#{argstring})", loglevel, details: data.keys
|
1137
|
-
return data.dup
|
1138
|
-
end
|
1139
|
-
if data.size == 1 and (cloud_id.nil? or data.values.first.cloud_id == cloud_id)
|
1140
|
-
return data.values.first
|
1141
|
-
elsif mu_name.nil? and cloud_id.nil?
|
1142
|
-
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
|
1143
|
-
return data.values.first
|
1144
|
-
end
|
1145
|
-
end
|
1146
|
-
data.each_pair { |sib_mu_name, obj|
|
1147
|
-
if (!mu_name.nil? and mu_name == sib_mu_name) or
|
1148
|
-
(!cloud_id.nil? and cloud_id == obj.cloud_id) or
|
1149
|
-
(!credentials.nil? and credentials == obj.credentials)
|
1150
|
-
if !created_only or !obj.cloud_id.nil?
|
1151
|
-
if return_all
|
1152
|
-
MU.log indent+"MULTI-MATCH RETURN_ALL findLitterMate(#{argstring})", loglevel, details: data.keys
|
1153
|
-
return data.dup
|
1154
|
-
else
|
1155
|
-
MU.log indent+"MULTI-MATCH findLitterMate(#{argstring})", loglevel, details: data.keys
|
1156
|
-
return obj
|
1157
|
-
end
|
1158
|
-
end
|
1159
|
-
end
|
1160
|
-
}
|
1161
|
-
else
|
1162
|
-
|
1163
|
-
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
|
1164
|
-
|
1165
|
-
data_cloud_id = data.cloud_id.nil? ? nil : data.cloud_id.to_s
|
1166
|
-
|
1167
|
-
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
|
1168
|
-
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
|
1169
|
-
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
|
1170
|
-
|
1171
|
-
if (name.nil? or sib_class == name.to_s or virtual_name == name.to_s) and
|
1172
|
-
(cloud_id.nil? or cloud_id.to_s == data_cloud_id) and
|
1173
|
-
(credentials.nil? or data.credentials.nil? or credentials.to_s == data.credentials.to_s)
|
1174
|
-
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
|
1175
|
-
if !created_only or !data_cloud_id.nil?
|
1176
|
-
MU.log indent+"SINGLE MATCH findLitterMate(#{argstring})", loglevel, details: [data.mu_name, data_cloud_id, data.config.keys]
|
1177
|
-
matches << data
|
1178
|
-
end
|
1179
|
-
end
|
1180
|
-
end
|
1181
|
-
}
|
1182
|
-
}
|
1183
|
-
|
1184
|
-
return matches.first if matches.size == 1
|
1185
|
-
if return_all and matches.size > 1
|
1186
|
-
return matches
|
1187
|
-
end
|
1188
|
-
}
|
1189
|
-
|
1190
|
-
MU.log indent+"NO MATCH findLitterMate(#{argstring})", loglevel
|
1191
|
-
|
1192
|
-
return nil
|
1193
|
-
end
|
1194
|
-
|
1195
529
|
# Add or remove a resource's metadata to this deployment's structure and
|
1196
530
|
# flush it to disk.
|
1197
531
|
# @param type [String]: The type of resource (e.g. *server*, *database*).
|
1198
532
|
# @param key [String]: The name field of this resource.
|
533
|
+
# @param mu_name [String]: The mu_name of this resource.
|
1199
534
|
# @param data [Hash]: The resource's metadata.
|
535
|
+
# @param triggering_node [MU::Cloud]: A cloud object calling this notify, usually on behalf of itself
|
1200
536
|
# @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
|
1201
537
|
# @return [void]
|
1202
538
|
def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
|
1203
539
|
return if @no_artifacts
|
1204
|
-
MU::MommaCat.lock("deployment-notification")
|
1205
540
|
|
1206
|
-
|
1207
|
-
|
1208
|
-
end
|
541
|
+
begin
|
542
|
+
MU::MommaCat.lock("deployment-notification")
|
1209
543
|
|
1210
|
-
|
1211
|
-
|
544
|
+
if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
|
545
|
+
loadDeploy(true) # make sure we're saving the latest and greatest
|
546
|
+
end
|
1212
547
|
|
1213
|
-
|
1214
|
-
|
1215
|
-
if cfg_plural
|
1216
|
-
type = cfg_plural
|
1217
|
-
has_multiples = attrs[:has_multiples]
|
1218
|
-
end
|
548
|
+
_shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
|
549
|
+
has_multiples = attrs[:has_multiples] ? true : false
|
1219
550
|
|
1220
|
-
|
1221
|
-
|
1222
|
-
mu_name = data["mu_name"]
|
551
|
+
mu_name ||= if !data.nil? and !data["mu_name"].nil?
|
552
|
+
data["mu_name"]
|
1223
553
|
elsif !triggering_node.nil? and !triggering_node.mu_name.nil?
|
1224
|
-
|
554
|
+
triggering_node.mu_name
|
1225
555
|
end
|
1226
556
|
if mu_name.nil? and has_multiples
|
1227
|
-
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
|
1228
|
-
MU::MommaCat.unlock("deployment-notification")
|
557
|
+
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
|
1229
558
|
return
|
1230
559
|
end
|
1231
|
-
end
|
1232
560
|
|
1233
|
-
|
561
|
+
@need_deploy_flush = true
|
1234
562
|
|
1235
|
-
|
1236
|
-
|
1237
|
-
|
1238
|
-
|
1239
|
-
|
1240
|
-
end
|
1241
|
-
@notify_semaphore.synchronize {
|
1242
|
-
@deployment[type] ||= {}
|
1243
|
-
}
|
1244
|
-
if has_multiples
|
563
|
+
if !remove
|
564
|
+
if data.nil?
|
565
|
+
MU.log "MU::MommaCat.notify called to modify deployment struct, but no data provided", MU::WARN
|
566
|
+
return
|
567
|
+
end
|
1245
568
|
@notify_semaphore.synchronize {
|
1246
|
-
@deployment[type]
|
569
|
+
@deployment[type] ||= {}
|
1247
570
|
}
|
1248
|
-
# fix has_multiples classes that weren't tiered correctly
|
1249
|
-
if @deployment[type][key].is_a?(Hash) and @deployment[type][key].has_key?("mu_name")
|
1250
|
-
olddata = @deployment[type][key].dup
|
1251
|
-
@deployment[type][key][olddata["mu_name"]] = olddata
|
1252
|
-
end
|
1253
|
-
@deployment[type][key][mu_name] = data
|
1254
|
-
MU.log "Adding to @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: data
|
1255
|
-
else
|
1256
|
-
@deployment[type][key] = data
|
1257
|
-
MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
|
1258
|
-
end
|
1259
|
-
save!(key) if !delayed_save
|
1260
|
-
else
|
1261
|
-
have_deploy = true
|
1262
|
-
if @deployment[type].nil? or @deployment[type][key].nil?
|
1263
|
-
|
1264
571
|
if has_multiples
|
1265
|
-
|
572
|
+
@notify_semaphore.synchronize {
|
573
|
+
@deployment[type][key] ||= {}
|
574
|
+
}
|
575
|
+
@deployment[type][key][mu_name] = data
|
576
|
+
MU.log "Adding to @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: data
|
1266
577
|
else
|
1267
|
-
|
578
|
+
@deployment[type][key] = data
|
579
|
+
MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
|
580
|
+
end
|
581
|
+
save!(key) if !delayed_save
|
582
|
+
else
|
583
|
+
have_deploy = true
|
584
|
+
if @deployment[type].nil? or @deployment[type][key].nil?
|
585
|
+
MU.log "MU::MommaCat.notify called to remove #{type} #{key}#{has_multiples ? " "+mu_name : ""} deployment struct, but no such data exist", MU::DEBUG
|
586
|
+
return
|
1268
587
|
end
|
1269
|
-
MU::MommaCat.unlock("deployment-notification")
|
1270
588
|
|
1271
|
-
|
1272
|
-
|
589
|
+
if have_deploy
|
590
|
+
@notify_semaphore.synchronize {
|
591
|
+
if has_multiples
|
592
|
+
MU.log "Removing @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: @deployment[type][key][mu_name]
|
593
|
+
@deployment[type][key].delete(mu_name)
|
594
|
+
end
|
1273
595
|
|
1274
|
-
|
1275
|
-
|
1276
|
-
if has_multiples
|
1277
|
-
MU.log "Removing @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: @deployment[type][key][mu_name]
|
1278
|
-
@deployment[type][key].delete(mu_name)
|
1279
|
-
if @deployment[type][key].size == 0
|
596
|
+
if @deployment[type][key].empty? or !has_multiples
|
597
|
+
MU.log "Removing @deployment[#{type}][#{key}]", MU::DEBUG, details: @deployment[type][key]
|
1280
598
|
@deployment[type].delete(key)
|
1281
599
|
end
|
1282
|
-
else
|
1283
|
-
MU.log "Removing @deployment[#{type}][#{key}]", MU::DEBUG, details: @deployment[type][key]
|
1284
|
-
@deployment[type].delete(key)
|
1285
|
-
end
|
1286
|
-
if @deployment[type].size == 0
|
1287
|
-
@deployment.delete(type)
|
1288
|
-
end
|
1289
|
-
}
|
1290
|
-
end
|
1291
|
-
save! if !delayed_save
|
1292
600
|
|
601
|
+
if @deployment[type].empty?
|
602
|
+
@deployment.delete(type)
|
603
|
+
end
|
604
|
+
}
|
605
|
+
end
|
606
|
+
save! if !delayed_save
|
607
|
+
end
|
608
|
+
ensure
|
609
|
+
MU::MommaCat.unlock("deployment-notification")
|
1293
610
|
end
|
1294
|
-
|
1295
|
-
MU::MommaCat.unlock("deployment-notification")
|
1296
611
|
end
|
1297
612
|
|
1298
613
|
# Send a Slack notification to a deployment's administrators.
|
@@ -1331,13 +646,13 @@ module MU
|
|
1331
646
|
to << "#{admin['name']} <#{admin['email']}>"
|
1332
647
|
}
|
1333
648
|
end
|
1334
|
-
message = <<
|
649
|
+
message = <<MAIL_HEAD_END
|
1335
650
|
From: #{MU.handle} <root@localhost>
|
1336
651
|
To: #{to.join(",")}
|
1337
652
|
Subject: #{subject}
|
1338
653
|
|
1339
654
|
#{msg}
|
1340
|
-
|
655
|
+
MAIL_HEAD_END
|
1341
656
|
if !kitten.nil? and kitten.kind_of?(MU::Cloud)
|
1342
657
|
message = message + "\n\n**** #{kitten}:\n"
|
1343
658
|
if !kitten.report.nil?
|
@@ -1425,124 +740,58 @@ MESSAGE_END
|
|
1425
740
|
MU::Master::SSL.sign(csr_path, sans, for_user: MU.mu_user)
|
1426
741
|
end
|
1427
742
|
|
1428
|
-
# Make sure deployment data is synchronized to/from each
|
743
|
+
# Make sure deployment data is synchronized to/from each +Server+ in the
|
1429
744
|
# currently-loaded deployment.
|
745
|
+
# @param nodeclasses [Array<String>]
|
746
|
+
# @param triggering_node [String,MU::Cloud::Server]
|
747
|
+
# @param save_only [Boolean]
|
1430
748
|
def syncLitter(nodeclasses = [], triggering_node: nil, save_only: false)
|
1431
|
-
|
1432
|
-
# inferences from dependencies or something?
|
1433
|
-
|
1434
|
-
return if MU.syncLitterThread
|
749
|
+
return if MU.syncLitterThread # don't run recursively by accident
|
1435
750
|
return if !Dir.exist?(deploy_dir)
|
1436
|
-
svrs = MU::Cloud.resource_types[:Server][:cfg_plural] # legibility shorthand
|
1437
|
-
if !triggering_node.nil? and nodeclasses.size > 0
|
1438
|
-
nodeclasses.reject! { |n| n == triggering_node.to_s }
|
1439
|
-
return if nodeclasses.size == 0
|
1440
|
-
end
|
1441
|
-
|
1442
|
-
@kitten_semaphore.synchronize {
|
1443
|
-
if @kittens.nil? or
|
1444
|
-
@kittens[svrs].nil?
|
1445
|
-
MU.log "No #{svrs} as yet available in #{@deploy_id}", MU::DEBUG, details: @kittens
|
1446
|
-
return
|
1447
|
-
end
|
1448
751
|
|
752
|
+
if !triggering_node.nil? and triggering_node.is_a?(MU::Cloud::Server)
|
753
|
+
triggering_node = triggering_node.mu_name
|
754
|
+
end
|
1449
755
|
|
1450
|
-
|
1451
|
-
|
756
|
+
siblings = findLitterMate(type: "server", return_all: true)
|
757
|
+
return if siblings.nil? or siblings.empty?
|
1452
758
|
|
1453
759
|
update_servers = []
|
1454
|
-
|
1455
|
-
|
1456
|
-
|
1457
|
-
|
1458
|
-
|
1459
|
-
|
1460
|
-
|
1461
|
-
|
1462
|
-
|
1463
|
-
|
1464
|
-
|
1465
|
-
|
1466
|
-
|
1467
|
-
|
1468
|
-
|
1469
|
-
|
1470
|
-
|
1471
|
-
nodeclasses.each { |nodeclass|
|
1472
|
-
mates = findLitterMate(type: "server", name: nodeclass, return_all: true)
|
1473
|
-
litter.merge!(mates) if mates
|
1474
|
-
}
|
1475
|
-
litter.each_pair { |mu_name, node|
|
1476
|
-
if !triggering_node.nil? and (
|
1477
|
-
(triggering_node.is_a?(MU::Cloud::Server) and mu_name == triggering_node.mu_name) or
|
1478
|
-
(triggering_node.is_a?(String) and mu_name == triggering_node)
|
1479
|
-
)
|
1480
|
-
next
|
1481
|
-
end
|
1482
|
-
|
1483
|
-
if !node.deploydata or !node.deploydata.keys.include?('nodename')
|
1484
|
-
details = node.deploydata ? node.deploydata.keys : nil
|
1485
|
-
MU.log "#{mu_name} deploy data is missing (possibly retired or mid-bootstrap), so not syncing it", MU::WARN, details: details
|
1486
|
-
else
|
1487
|
-
update_servers << node
|
1488
|
-
end
|
1489
|
-
}
|
1490
|
-
end
|
1491
|
-
return if update_servers.size == 0
|
1492
|
-
|
1493
|
-
MU.log "Updating these nodes in #{@deploy_id}", MU::DEBUG, details: update_servers.map { |n| n.mu_name }
|
1494
|
-
|
1495
|
-
update_servers.each { |node|
|
1496
|
-
# Not clear where this pollution comes from, but let's stick a temp
|
1497
|
-
# fix in here.
|
1498
|
-
if node.deploydata['nodename'] != node.mu_name and
|
1499
|
-
!node.deploydata['nodename'].nil? and !node.deploydata['nodename'].emty?
|
1500
|
-
MU.log "Node #{node.mu_name} had wrong or missing nodename (#{node.deploydata['nodename']}), correcting", MU::WARN
|
1501
|
-
node.deploydata['nodename'] = node.mu_name
|
1502
|
-
if @deployment[svrs] and @deployment[svrs][node.config['name']] and
|
1503
|
-
@deployment[svrs][node.config['name']][node.mu_name]
|
1504
|
-
@deployment[svrs][node.config['name']][node.mu_name]['nodename'] = node.mu_name
|
1505
|
-
end
|
1506
|
-
save!
|
1507
|
-
end
|
760
|
+
siblings.each_pair { |mu_name, node|
|
761
|
+
next if mu_name == triggering_node or node.groomer.nil?
|
762
|
+
next if nodeclasses.size > 0 and !nodeclasses.include?(node.config['name'])
|
763
|
+
if !node.deploydata or !node.deploydata['nodename']
|
764
|
+
MU.log "#{mu_name} deploy data is missing (possibly retired or mid-bootstrap), so not syncing it", MU::NOTICE
|
765
|
+
next
|
766
|
+
end
|
767
|
+
|
768
|
+
if @deployment["servers"][node.config['name']][node.mu_name].nil? or
|
769
|
+
@deployment["servers"][node.config['name']][node.mu_name] != node.deploydata
|
770
|
+
@deployment["servers"][node.config['name']][node.mu_name] = node.deploydata
|
771
|
+
elsif !save_only
|
772
|
+
# Don't bother running grooms on nodes that don't need to be updated,
|
773
|
+
# unless we're just going to do a save.
|
774
|
+
next
|
775
|
+
end
|
776
|
+
update_servers << node
|
1508
777
|
}
|
1509
778
|
|
1510
|
-
|
1511
|
-
if !save_only
|
1512
|
-
skip = []
|
1513
|
-
update_servers.each { |node|
|
1514
|
-
if node.mu_name.nil? or node.deploydata.nil? or node.config.nil?
|
1515
|
-
MU.log "Missing mu_name #{node.mu_name}, deploydata, or config from #{node} in syncLitter", MU::ERR, details: node.deploydata
|
1516
|
-
next
|
1517
|
-
end
|
779
|
+
return if update_servers.empty?
|
1518
780
|
|
1519
|
-
|
1520
|
-
@deployment[svrs][node.config['name']][node.mu_name] = node.deploydata
|
1521
|
-
else
|
1522
|
-
skip << node
|
1523
|
-
end
|
1524
|
-
}
|
1525
|
-
update_servers = update_servers - skip
|
1526
|
-
end
|
781
|
+
MU.log "Updating nodes in #{@deploy_id}", MU::DEBUG, details: update_servers.map { |n| n.mu_name }
|
1527
782
|
|
1528
|
-
return if MU.inGem? || update_servers.size < 1
|
1529
783
|
threads = []
|
1530
|
-
parent_thread_id = Thread.current.object_id
|
1531
784
|
update_servers.each { |sibling|
|
1532
785
|
threads << Thread.new {
|
1533
786
|
Thread.abort_on_exception = true
|
1534
|
-
MU.dupGlobals(parent_thread_id)
|
1535
787
|
Thread.current.thread_variable_set("name", "sync-"+sibling.mu_name.downcase)
|
1536
788
|
MU.setVar("syncLitterThread", true)
|
1537
789
|
begin
|
1538
|
-
|
1539
|
-
|
1540
|
-
sibling.groomer.run(purpose: "Synchronizing sibling kittens") if !save_only
|
1541
|
-
end
|
790
|
+
sibling.groomer.saveDeployData
|
791
|
+
sibling.groomer.run(purpose: "Synchronizing sibling kittens") if !save_only
|
1542
792
|
rescue MU::Groomer::RunError => e
|
1543
|
-
MU.log "Sync of #{sibling.mu_name} failed
|
793
|
+
MU.log "Sync of #{sibling.mu_name} failed", MU::WARN, details: e.inspect
|
1544
794
|
end
|
1545
|
-
MU.purgeGlobals
|
1546
795
|
}
|
1547
796
|
}
|
1548
797
|
|