cloud-mu 3.1.6 → 3.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/bin/mu-adopt +4 -12
  3. data/bin/mu-azure-tests +57 -0
  4. data/bin/mu-cleanup +2 -4
  5. data/bin/mu-configure +37 -1
  6. data/bin/mu-deploy +3 -3
  7. data/bin/mu-findstray-tests +25 -0
  8. data/bin/mu-gen-docs +2 -4
  9. data/bin/mu-run-tests +23 -10
  10. data/cloud-mu.gemspec +2 -2
  11. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  12. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  13. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  14. data/extras/generate-stock-images +1 -0
  15. data/modules/mu.rb +82 -95
  16. data/modules/mu/adoption.rb +356 -56
  17. data/modules/mu/cleanup.rb +21 -20
  18. data/modules/mu/cloud.rb +79 -1753
  19. data/modules/mu/cloud/database.rb +49 -0
  20. data/modules/mu/cloud/dnszone.rb +46 -0
  21. data/modules/mu/cloud/machine_images.rb +212 -0
  22. data/modules/mu/cloud/providers.rb +81 -0
  23. data/modules/mu/cloud/resource_base.rb +920 -0
  24. data/modules/mu/cloud/server.rb +40 -0
  25. data/modules/mu/cloud/server_pool.rb +1 -0
  26. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  27. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  28. data/modules/mu/cloud/wrappers.rb +165 -0
  29. data/modules/mu/config.rb +122 -80
  30. data/modules/mu/config/alarm.rb +2 -6
  31. data/modules/mu/config/bucket.rb +1 -1
  32. data/modules/mu/config/cache_cluster.rb +1 -1
  33. data/modules/mu/config/collection.rb +1 -1
  34. data/modules/mu/config/container_cluster.rb +2 -2
  35. data/modules/mu/config/database.rb +83 -104
  36. data/modules/mu/config/database.yml +1 -2
  37. data/modules/mu/config/dnszone.rb +1 -1
  38. data/modules/mu/config/doc_helpers.rb +4 -5
  39. data/modules/mu/config/endpoint.rb +1 -1
  40. data/modules/mu/config/firewall_rule.rb +3 -19
  41. data/modules/mu/config/folder.rb +1 -1
  42. data/modules/mu/config/function.rb +1 -1
  43. data/modules/mu/config/group.rb +1 -1
  44. data/modules/mu/config/habitat.rb +1 -1
  45. data/modules/mu/config/loadbalancer.rb +57 -11
  46. data/modules/mu/config/log.rb +1 -1
  47. data/modules/mu/config/msg_queue.rb +1 -1
  48. data/modules/mu/config/nosqldb.rb +1 -1
  49. data/modules/mu/config/notifier.rb +1 -1
  50. data/modules/mu/config/ref.rb +30 -4
  51. data/modules/mu/config/role.rb +1 -1
  52. data/modules/mu/config/schema_helpers.rb +30 -34
  53. data/modules/mu/config/search_domain.rb +1 -1
  54. data/modules/mu/config/server.rb +4 -12
  55. data/modules/mu/config/server_pool.rb +3 -7
  56. data/modules/mu/config/storage_pool.rb +1 -1
  57. data/modules/mu/config/tail.rb +10 -0
  58. data/modules/mu/config/user.rb +1 -1
  59. data/modules/mu/config/vpc.rb +12 -17
  60. data/modules/mu/defaults/AWS.yaml +32 -32
  61. data/modules/mu/defaults/Azure.yaml +1 -0
  62. data/modules/mu/defaults/Google.yaml +1 -0
  63. data/modules/mu/deploy.rb +16 -15
  64. data/modules/mu/groomer.rb +15 -0
  65. data/modules/mu/groomers/chef.rb +3 -0
  66. data/modules/mu/logger.rb +120 -144
  67. data/modules/mu/master.rb +1 -1
  68. data/modules/mu/mommacat.rb +54 -25
  69. data/modules/mu/mommacat/daemon.rb +10 -7
  70. data/modules/mu/mommacat/naming.rb +82 -3
  71. data/modules/mu/mommacat/search.rb +47 -15
  72. data/modules/mu/mommacat/storage.rb +72 -41
  73. data/modules/mu/{clouds → providers}/README.md +1 -1
  74. data/modules/mu/{clouds → providers}/aws.rb +114 -47
  75. data/modules/mu/{clouds → providers}/aws/alarm.rb +1 -1
  76. data/modules/mu/{clouds → providers}/aws/bucket.rb +2 -2
  77. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +10 -46
  78. data/modules/mu/{clouds → providers}/aws/collection.rb +3 -3
  79. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +15 -33
  80. data/modules/mu/providers/aws/database.rb +1744 -0
  81. data/modules/mu/{clouds → providers}/aws/dnszone.rb +2 -5
  82. data/modules/mu/{clouds → providers}/aws/endpoint.rb +2 -11
  83. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +33 -29
  84. data/modules/mu/{clouds → providers}/aws/folder.rb +0 -0
  85. data/modules/mu/{clouds → providers}/aws/function.rb +2 -10
  86. data/modules/mu/{clouds → providers}/aws/group.rb +9 -13
  87. data/modules/mu/{clouds → providers}/aws/habitat.rb +1 -1
  88. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +41 -33
  89. data/modules/mu/{clouds → providers}/aws/log.rb +2 -2
  90. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +2 -8
  91. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +0 -0
  92. data/modules/mu/{clouds → providers}/aws/notifier.rb +0 -0
  93. data/modules/mu/{clouds → providers}/aws/role.rb +7 -7
  94. data/modules/mu/{clouds → providers}/aws/search_domain.rb +8 -13
  95. data/modules/mu/{clouds → providers}/aws/server.rb +55 -90
  96. data/modules/mu/{clouds → providers}/aws/server_pool.rb +10 -33
  97. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +19 -36
  98. data/modules/mu/{clouds → providers}/aws/user.rb +8 -12
  99. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  100. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  101. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  102. data/modules/mu/{clouds → providers}/aws/vpc.rb +135 -70
  103. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  104. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  105. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  106. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  107. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  108. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  109. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  110. data/modules/mu/{clouds → providers}/azure/server.rb +30 -23
  111. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  112. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  113. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  114. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  115. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  116. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  117. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  118. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  119. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  120. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  121. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  122. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  123. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  124. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  125. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  126. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  127. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  128. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  129. data/modules/mu/{clouds → providers}/google.rb +14 -6
  130. data/modules/mu/{clouds → providers}/google/bucket.rb +1 -1
  131. data/modules/mu/{clouds → providers}/google/container_cluster.rb +28 -13
  132. data/modules/mu/{clouds → providers}/google/database.rb +1 -8
  133. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +2 -2
  134. data/modules/mu/{clouds → providers}/google/folder.rb +4 -8
  135. data/modules/mu/{clouds → providers}/google/function.rb +3 -3
  136. data/modules/mu/{clouds → providers}/google/group.rb +8 -16
  137. data/modules/mu/{clouds → providers}/google/habitat.rb +3 -7
  138. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +1 -1
  139. data/modules/mu/{clouds → providers}/google/role.rb +42 -34
  140. data/modules/mu/{clouds → providers}/google/server.rb +25 -10
  141. data/modules/mu/{clouds → providers}/google/server_pool.rb +10 -10
  142. data/modules/mu/{clouds → providers}/google/user.rb +31 -21
  143. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  144. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  145. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  146. data/modules/mu/{clouds → providers}/google/vpc.rb +37 -2
  147. data/modules/tests/centos6.yaml +11 -0
  148. data/modules/tests/centos7.yaml +11 -0
  149. data/modules/tests/centos8.yaml +12 -0
  150. data/modules/tests/rds.yaml +108 -0
  151. data/modules/tests/regrooms/rds.yaml +123 -0
  152. data/spec/mu/clouds/azure_spec.rb +2 -2
  153. metadata +108 -89
  154. data/modules/mu/clouds/aws/database.rb +0 -1974
@@ -602,7 +602,7 @@ module MU
602
602
  return
603
603
  end
604
604
  if ssh_key_name.nil? or ssh_key_name.empty?
605
- 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
606
606
  return
607
607
  end
608
608
 
@@ -167,6 +167,7 @@ module MU
167
167
  @need_deploy_flush = false
168
168
  @node_cert_semaphore = Mutex.new
169
169
  @deployment = deployment_data
170
+
170
171
  @deployment['mu_public_ip'] = MU.mu_public_ip
171
172
  @private_key = nil
172
173
  @public_key = nil
@@ -182,6 +183,7 @@ module MU
182
183
  @appname ||= @original_config['name'] if @original_config
183
184
  @timestamp = timestamp
184
185
  @environment = environment
186
+ @original_config['environment'] ||= @environment if @original_config
185
187
 
186
188
  if set_context_to_me
187
189
  MU::MommaCat.setThreadContext(self)
@@ -253,8 +255,7 @@ module MU
253
255
  seen << resource['credentials']
254
256
  else
255
257
  cloudconst = @original_config['cloud'] ? @original_config['cloud'] : MU::Config.defaultCloud
256
- Object.const_get("MU").const_get("Cloud").const_get(cloudconst)
257
- seen << cloudclass.credConfig(name_only: true)
258
+ seen << MU::Cloud.cloudClass(cloudconst).credConfig(name_only: true)
258
259
  end
259
260
  }
260
261
  end
@@ -289,11 +290,10 @@ module MU
289
290
  habitats << hab_ref.id
290
291
  end
291
292
  elsif resource['cloud']
292
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
293
293
  # XXX this should be a general method implemented by each cloud
294
294
  # provider
295
295
  if resource['cloud'] == "Google"
296
- habitats << cloudclass.defaultProject(resource['credentials'])
296
+ habitats << MU::Cloud.cloudClass(resource['cloud']).defaultProject(resource['credentials'])
297
297
  end
298
298
  end
299
299
  }
@@ -317,13 +317,11 @@ module MU
317
317
  if @original_config[type]
318
318
  @original_config[type].each { |resource|
319
319
  if resource['cloud']
320
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
321
- resclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']).const_get(res_type.to_s)
322
- if resclass.isGlobal?
320
+ if MU::Cloud.resourceClass(resource['cloud'], res_type).isGlobal?
323
321
  # XXX why was I doing this, urgh
324
322
  next
325
323
  elsif !resource['region']
326
- regions << cloudclass.myRegion
324
+ regions << MU::Cloud.cloudClass(resource['cloud']).myRegion(resource['credentials'])
327
325
  end
328
326
  end
329
327
  if resource['region']
@@ -401,7 +399,7 @@ module MU
401
399
  # @param type [String]:
402
400
  # @param name [String]:
403
401
  # @param object [MU::Cloud]:
404
- def addKitten(type, name, object)
402
+ def addKitten(type, name, object, do_notify: false)
405
403
  if !type or !name or !object or !object.mu_name
406
404
  raise MuError, "Nil arguments to addKitten are not allowed (got type: #{type}, name: #{name}, and '#{object}' to add)"
407
405
  end
@@ -409,7 +407,7 @@ module MU
409
407
  _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
410
408
  object.intoDeploy(self)
411
409
 
412
- @kitten_semaphore.synchronize {
410
+ add_block = Proc.new {
413
411
  @kittens[type] ||= {}
414
412
  @kittens[type][object.habitat] ||= {}
415
413
  if attrs[:has_multiples]
@@ -418,7 +416,20 @@ module MU
418
416
  else
419
417
  @kittens[type][object.habitat][name] = object
420
418
  end
419
+ if do_notify
420
+ notify(type, name, object.notify, triggering_node: object, delayed_save: true)
421
+ end
421
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
422
433
  end
423
434
 
424
435
  # Encrypt a string with the deployment's public key.
@@ -536,10 +547,9 @@ module MU
536
547
  # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
537
548
  # @return [void]
538
549
  def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
539
- return if @no_artifacts
540
550
 
541
551
  begin
542
- MU::MommaCat.lock("deployment-notification")
552
+ MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
543
553
 
544
554
  if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
545
555
  loadDeploy(true) # make sure we're saving the latest and greatest
@@ -578,7 +588,7 @@ module MU
578
588
  @deployment[type][key] = data
579
589
  MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
580
590
  end
581
- save!(key) if !delayed_save
591
+ save!(key) if !delayed_save and !@no_artifacts
582
592
  else
583
593
  have_deploy = true
584
594
  if @deployment[type].nil? or @deployment[type][key].nil?
@@ -603,10 +613,10 @@ module MU
603
613
  end
604
614
  }
605
615
  end
606
- save! if !delayed_save
616
+ save! if !delayed_save and !@no_artifacts
607
617
  end
608
618
  ensure
609
- MU::MommaCat.unlock("deployment-notification")
619
+ MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
610
620
  end
611
621
  end
612
622
 
@@ -614,18 +624,37 @@ module MU
614
624
  # @param subject [String]: The subject line of the message.
615
625
  # @param msg [String]: The message body.
616
626
  # @return [void]
617
- def sendAdminSlack(subject, msg: "")
618
- if $MU_CFG['slack'] and $MU_CFG['slack']['webhook'] and
619
- (!$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 })
620
630
  require 'slack-notifier'
621
- 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
622
642
 
623
- if msg and !msg.empty?
624
- slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}:\n\n```#{msg}\n```", channel: $MU_CFG['slack']['channel']
625
- else
626
- 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
627
655
  end
628
656
  end
657
+ true
629
658
  end
630
659
 
631
660
  # Send an email notification to a deployment's administrators.
@@ -754,7 +783,7 @@ MAIL_HEAD_END
754
783
  end
755
784
 
756
785
  siblings = findLitterMate(type: "server", return_all: true)
757
- return if siblings.nil? or siblings.empty?
786
+ return if siblings.nil? or (siblings.respond_to?(:empty?) and siblings.empty?)
758
787
 
759
788
  update_servers = []
760
789
  siblings.each_pair { |mu_name, node|
@@ -838,7 +867,7 @@ MAIL_HEAD_END
838
867
  end
839
868
 
840
869
  if resource and resource.config and resource.config['cloud']
841
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource.config['cloud'])
870
+ cloudclass = MU::Cloud.cloudClass(resource.config['cloud'])
842
871
 
843
872
  cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
844
873
  cloudclass.writeDeploySecret(@deploy_id, key.to_pem, cert_cn+".key", credentials: resource.config['credentials'])
@@ -33,7 +33,7 @@ module MU
33
33
  my_key = OpenSSL::PKey::RSA.new(@private_key)
34
34
 
35
35
  begin
36
- if my_key.private_decrypt(ciphertext).force_encoding("UTF-8") == @deploy_secret.force_encoding("UTF-8")
36
+ if my_key.private_decrypt(ciphertext).force_encoding("UTF-8").chomp == @deploy_secret.force_encoding("UTF-8").chomp
37
37
  MU.log "Matched ciphertext for #{MU.deploy_id}", MU::INFO
38
38
  return true
39
39
  else
@@ -165,11 +165,11 @@ module MU
165
165
  if e.class.name != "MU::Cloud::AWS::Server::BootstrapTempFail" and !File.exist?(deploy_dir+"/.cleanup."+cloud_id) and !File.exist?(deploy_dir+"/.cleanup")
166
166
  MU.log "Grooming FAILED for #{kitten.mu_name} (#{e.inspect})", MU::ERR, details: e.backtrace
167
167
  sendAdminSlack("Grooming FAILED for `#{kitten.mu_name}` with `#{e.message}` :crying_cat_face:", msg: e.backtrace.join("\n"))
168
- sendAdminMail("Grooming FAILED for #{kitten.mu_name} on #{MU.appname} \"#{MU.handle}\" (#{MU.deploy_id})",
169
- msg: e.inspect,
170
- data: e.backtrace,
171
- debug: true
172
- )
168
+ sendAdminMail("Grooming FAILED for #{kitten.mu_name} on #{MU.appname} \"#{MU.handle}\" (#{MU.deploy_id})",
169
+ msg: e.inspect,
170
+ data: e.backtrace,
171
+ debug: true
172
+ )
173
173
  raise e if reraise_fail
174
174
  else
175
175
  MU.log "Grooming of #{kitten.mu_name} interrupted by cleanup or planned reboot"
@@ -342,6 +342,8 @@ module MU
342
342
  return $?.exitstatus
343
343
  end
344
344
 
345
+ @@notified_on_pid = {}
346
+
345
347
  # Return true if the Momma Cat daemon appears to be running
346
348
  # @return [Boolean]
347
349
  def self.status
@@ -352,7 +354,8 @@ module MU
352
354
  pid = File.read(daemonPidFile).chomp.to_i
353
355
  begin
354
356
  Process.getpgid(pid)
355
- MU.log "Momma Cat running with pid #{pid.to_s}"
357
+ MU.log "Momma Cat running with pid #{pid.to_s}", (@@notified_on_pid[pid] ? MU::DEBUG : MU::INFO) # shush
358
+ @@notified_on_pid[pid] = true
356
359
  return true
357
360
  rescue Errno::ESRCH
358
361
  end
@@ -19,6 +19,16 @@ module MU
19
19
  # the normal synchronous deploy sequence invoked by *mu-deploy*.
20
20
  class MommaCat
21
21
 
22
+ # Lookup table to translate the word "habitat" back to its
23
+ # provider-specific jargon
24
+ HABITAT_SYNONYMS = {
25
+ "AWS" => "account",
26
+ "CloudFormation" => "account",
27
+ "Google" => "project",
28
+ "Azure" => "subscription",
29
+ "VMWare" => "sddc"
30
+ }
31
+
22
32
  # Given a cloud provider's native descriptor for a resource, make some
23
33
  # reasonable guesses about what the thing's name should be.
24
34
  def self.guessName(desc, resourceclass, cloud_id: nil, tag_value: nil)
@@ -47,6 +57,74 @@ module MU
47
57
 
48
58
  end
49
59
 
60
+ # Given a piece of a BoK resource descriptor Hash, come up with shorthand
61
+ # strings to give it a name for human readers. If nothing reasonable can be
62
+ # extracted, returns nil.
63
+ # @param obj [Hash]
64
+ # @param array_of [String]
65
+ # @param habitat_translate [String]
66
+ # @return [Array<String,nil>]
67
+ def self.getChunkName(obj, array_of = nil, habitat_translate: nil)
68
+ return [nil, nil] if obj.nil?
69
+ if [String, Integer, Boolean].include?(obj.class)
70
+ return [obj, nil]
71
+ end
72
+ obj_type = array_of || obj['type']
73
+ obj_name = obj['name'] || obj['id'] || obj['mu_name'] || obj['cloud_id']
74
+
75
+ name_string = if obj_name
76
+ if obj_type
77
+ "#{obj_type}[#{obj_name}]"
78
+ else
79
+ obj_name.dup
80
+ end
81
+ else
82
+ found_it = nil
83
+ using = nil
84
+ ["entity", "role"].each { |subtype|
85
+ if obj[subtype] and obj[subtype].is_a?(Hash)
86
+ found_it = if obj[subtype]["id"]
87
+ obj[subtype]['id'].dup
88
+ elsif obj[subtype]["type"] and obj[subtype]["name"]
89
+ "#{obj[subtype]['type']}[#{obj[subtype]['name']}]"
90
+ end
91
+ break
92
+ end
93
+ }
94
+ found_it
95
+ end
96
+ if name_string
97
+ name_string.gsub!(/\[.+?\](\[.+?\]$)/, '\1')
98
+ if habitat_translate and HABITAT_SYNONYMS[habitat_translate]
99
+ name_string.sub!(/^habitats?\[(.+?)\]/i, HABITAT_SYNONYMS[habitat_translate]+'[\1]')
100
+ end
101
+ end
102
+
103
+ location_list = []
104
+
105
+ location = if obj['project']
106
+ obj['project']
107
+ elsif obj['habitat'] and (obj['habitat']['id'] or obj['habitat']['name'])
108
+ obj['habitat']['name'] || obj['habitat']['id']
109
+ else
110
+ hab_str = nil
111
+ ['projects', 'habitats'].each { |key|
112
+
113
+ if obj[key] and obj[key].is_a?(Array)
114
+ location_list = obj[key].sort.map { |p|
115
+ (p["name"] || p["id"]).gsub(/^.*?[^\/]+\/([^\/]+)$/, '\1')
116
+ }
117
+ hab_str = location_list.join(", ")
118
+ name_string.gsub!(/^.*?[^\/]+\/([^\/]+)$/, '\1') if name_string
119
+ break
120
+ end
121
+ }
122
+ hab_str
123
+ end
124
+
125
+ [name_string, location, location_list]
126
+ end
127
+
50
128
  # Generate a three-character string which can be used to unique-ify the
51
129
  # names of resources which might potentially collide, e.g. Windows local
52
130
  # hostnames, Amazon Elastic Load Balancers, or server pool instances.
@@ -218,17 +296,18 @@ module MU
218
296
  # SSH config entries, etc.
219
297
  # @param server [MU::Cloud::Server]: The {MU::Cloud::Server} we'll be setting up.
220
298
  # @param sync_wait [Boolean]: Whether to wait for DNS to fully synchronize before returning.
221
- def self.nameKitten(server, sync_wait: false)
299
+ def self.nameKitten(server, sync_wait: false, no_dns: false)
222
300
  node, config, _deploydata = server.describe
223
301
 
224
302
  mu_zone = nil
225
303
  # XXX GCP!
226
- if MU::Cloud::AWS.hosted? and !MU::Cloud::AWS.isGovCloud?
304
+ if !no_dns and MU::Cloud::AWS.hosted? and !MU::Cloud::AWS.isGovCloud?
227
305
  zones = MU::Cloud::DNSZone.find(cloud_id: "platform-mu")
228
306
  mu_zone = zones.values.first if !zones.nil?
229
307
  end
308
+
230
309
  if !mu_zone.nil?
231
- MU::Cloud::DNSZone.genericMuDNSEntry(name: node, target: server.canonicalIP, cloudclass: MU::Cloud::Server, sync_wait: sync_wait)
310
+ MU::Cloud::DNSZone.genericMuDNSEntry(name: node.gsub(/[^a-z0-9!"\#$%&'\(\)\*\+,\-\/:;<=>\?@\[\]\^_`{\|}~\.]/, '-').gsub(/--|^-/, ''), target: server.canonicalIP, cloudclass: MU::Cloud::Server, sync_wait: sync_wait)
232
311
  else
233
312
  MU::Master.addInstanceToEtcHosts(server.canonicalIP, node)
234
313
  end
@@ -60,7 +60,7 @@ module MU
60
60
  )
61
61
  _shortclass, _cfg_name, type, _classname, _attrs = MU::Cloud.getResourceNames(type, true)
62
62
 
63
- cloudclass = MU::Cloud.assertSupportedCloud(cloud)
63
+ cloudclass = MU::Cloud.cloudClass(cloud)
64
64
  return nil if cloudclass.virtual?
65
65
 
66
66
  if (tag_key and !tag_value) or (!tag_key and tag_value)
@@ -107,6 +107,7 @@ module MU
107
107
  matches = []
108
108
 
109
109
  credlist.each { |creds|
110
+ # next if region and region.is_a?(Array) and !region.empty? and !region.include?(r)
110
111
  cloud_descs = search_cloud_provider(type, cloud, habitats, region, cloud_id: cloud_id, tag_key: tag_key, tag_value: tag_value, credentials: creds, flags: flags)
111
112
 
112
113
  cloud_descs.each_pair.each { |p, regions|
@@ -127,6 +128,8 @@ module MU
127
128
  matches
128
129
  end
129
130
 
131
+ @object_load_fails = false
132
+
130
133
  # Return the resource object of another member of this deployment
131
134
  # @param type [String,Symbol]: The type of resource
132
135
  # @param name [String]: The name of the resource as defined in its 'name' Basket of Kittens field
@@ -135,7 +138,7 @@ module MU
135
138
  # @param created_only [Boolean]: Only return the littermate if its cloud_id method returns a value
136
139
  # @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.
137
140
  # @return [MU::Cloud]
138
- def findLitterMate(type: nil, name: nil, mu_name: nil, cloud_id: nil, created_only: false, return_all: false, credentials: nil, habitat: nil, **flags)
141
+ def findLitterMate(type: nil, name: nil, mu_name: nil, cloud_id: nil, created_only: false, return_all: false, credentials: nil, habitat: nil, debug: false, **flags)
139
142
  _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
140
143
 
141
144
  # If we specified a habitat, which we may also have done by its shorthand
@@ -159,17 +162,33 @@ module MU
159
162
  }
160
163
 
161
164
  @kitten_semaphore.synchronize {
162
- return nil if !@kittens.has_key?(type)
163
- matches = []
164
165
 
166
+ if !@kittens.has_key?(type)
167
+ return nil if !@original_config or @original_config[type].nil? or @original_config[type].empty?
168
+ begin
169
+ loadObjects(false)
170
+ rescue ThreadError => e
171
+ if e.message !~ /deadlock/
172
+ raise e
173
+ end
174
+ end
175
+ if @object_load_fails or !@kittens[type]
176
+ MU.log "#{@deploy_id}'s original config has #{@original_config[type].size == 1 ? "a" : @original_config[type].size.to_s} #{type}, but loadObjects could not populate anything from deployment metadata", MU::ERR if !@object_load_fails
177
+ @object_load_fails = true
178
+ return nil
179
+ end
180
+ end
181
+ matches = {}
165
182
  @kittens[type].each { |habitat_group, sib_classes|
166
183
  next if habitat and habitat_group and habitat_group != habitat
167
184
  sib_classes.each_pair { |sib_class, cloud_objs|
185
+
168
186
  if attrs[:has_multiples]
169
187
  next if !name.nil? and name != sib_class or cloud_objs.empty?
170
188
  if !name.nil?
171
189
  if return_all
172
- return cloud_objs.dup
190
+ matches.merge!(cloud_objs.clone)
191
+ next
173
192
  elsif cloud_objs.size == 1 and does_match.call(cloud_objs.values.first)
174
193
  return cloud_objs.values.first
175
194
  end
@@ -177,19 +196,24 @@ module MU
177
196
 
178
197
  cloud_objs.each_value { |obj|
179
198
  if does_match.call(obj)
180
- return (return_all ? cloud_objs.clone : obj.clone)
199
+ if return_all
200
+ matches.merge!(cloud_objs.clone)
201
+ else
202
+ return obj.clone
203
+ end
181
204
  end
182
205
  }
183
- # has_multiples is false
206
+ # has_multiples is false, "cloud_objs" is actually a singular object
184
207
  elsif (name.nil? and does_match.call(cloud_objs)) or [sib_class, cloud_objs.virtual_name(name)].include?(name.to_s)
185
- matches << cloud_objs.clone
208
+ matches[cloud_objs.config['name']] = cloud_objs.clone
186
209
  end
187
210
  }
188
211
  }
189
212
 
190
- return matches.first if matches.size == 1
213
+ return matches if return_all and matches.size >= 1
214
+
215
+ return matches.values.first if matches.size == 1
191
216
 
192
- return matches if return_all and matches.size > 1
193
217
  }
194
218
 
195
219
  return nil
@@ -213,7 +237,7 @@ module MU
213
237
  end
214
238
 
215
239
  def self.generate_dummy_object(type, cloud, name, mu_name, cloud_id, desc, region, habitat, tag_value, calling_deploy, credentials)
216
- resourceclass = MU::Cloud.loadCloudType(cloud, type)
240
+ resourceclass = MU::Cloud.resourceClass(cloud, type)
217
241
 
218
242
  use_name = if (name.nil? or name.empty?)
219
243
  if !mu_name.nil?
@@ -269,15 +293,23 @@ module MU
269
293
  private_class_method :generate_dummy_object
270
294
 
271
295
  def self.search_cloud_provider(type, cloud, habitats, region, cloud_id: nil, tag_key: nil, tag_value: nil, credentials: nil, flags: nil)
272
- cloudclass = MU::Cloud.assertSupportedCloud(cloud)
273
- resourceclass = MU::Cloud.loadCloudType(cloud, type)
296
+ cloudclass = MU::Cloud.cloudClass(cloud)
297
+ resourceclass = MU::Cloud.resourceClass(cloud, type)
274
298
 
275
299
  # Decide what regions we'll search, if applicable for this resource
276
300
  # type.
277
301
  regions = if resourceclass.isGlobal?
278
302
  [nil]
279
303
  else
280
- region ? [region] : cloudclass.listRegions(credentials: credentials)
304
+ if region
305
+ if region.is_a?(Array) and !region.empty?
306
+ region
307
+ else
308
+ [region]
309
+ end
310
+ else
311
+ cloudclass.listRegions(credentials: credentials)
312
+ end
281
313
  end
282
314
 
283
315
  # Decide what habitats (accounts/projects/subscriptions) we'll
@@ -288,7 +320,7 @@ module MU
288
320
  habitats << nil
289
321
  end
290
322
  if resourceclass.canLiveIn.include?(:Habitat)
291
- habitats.concat(cloudclass.listHabitats(credentials))
323
+ habitats.concat(cloudclass.listHabitats(credentials, use_cache: false))
292
324
  end
293
325
  end
294
326
  habitats << nil if habitats.empty?