cloud-mu 3.3.0 → 3.5.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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  3. data/bin/mu-aws-setup +41 -7
  4. data/bin/mu-azure-setup +36 -2
  5. data/bin/mu-configure +214 -119
  6. data/bin/mu-gcp-setup +37 -2
  7. data/bin/mu-node-manage +3 -0
  8. data/bin/mu-refresh-ssl +67 -0
  9. data/bin/mu-run-tests +14 -4
  10. data/bin/mu-self-update +30 -10
  11. data/bin/mu-upload-chef-artifacts +30 -26
  12. data/cloud-mu.gemspec +9 -7
  13. data/cookbooks/mu-master/attributes/default.rb +5 -1
  14. data/cookbooks/mu-master/metadata.rb +2 -2
  15. data/cookbooks/mu-master/recipes/default.rb +81 -26
  16. data/cookbooks/mu-master/recipes/init.rb +197 -62
  17. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  18. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  19. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  20. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  21. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  22. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  23. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  24. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  25. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  26. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  27. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  28. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  30. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  31. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  32. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  33. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  34. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  35. data/extras/Gemfile.lock.bootstrap +394 -0
  36. data/extras/bucketstubs/error.html +0 -0
  37. data/extras/bucketstubs/index.html +0 -0
  38. data/extras/clean-stock-amis +11 -3
  39. data/extras/generate-stock-images +6 -3
  40. data/extras/git_rpm/build.sh +20 -0
  41. data/extras/git_rpm/mugit.spec +53 -0
  42. data/extras/image-generators/AWS/centos7.yaml +19 -16
  43. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  44. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  45. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  46. data/extras/openssl_rpm/build.sh +19 -0
  47. data/extras/openssl_rpm/mussl.spec +46 -0
  48. data/extras/python_rpm/muthon.spec +14 -4
  49. data/extras/ruby_rpm/muby.spec +9 -5
  50. data/extras/sqlite_rpm/build.sh +19 -0
  51. data/extras/sqlite_rpm/muqlite.spec +47 -0
  52. data/install/installer +7 -5
  53. data/modules/mommacat.ru +2 -2
  54. data/modules/mu.rb +12 -5
  55. data/modules/mu/cloud/machine_images.rb +1 -1
  56. data/modules/mu/cloud/providers.rb +6 -1
  57. data/modules/mu/cloud/resource_base.rb +7 -4
  58. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  59. data/modules/mu/cloud/wrappers.rb +16 -7
  60. data/modules/mu/config.rb +28 -12
  61. data/modules/mu/config/database.rb +2 -2
  62. data/modules/mu/config/firewall_rule.rb +1 -1
  63. data/modules/mu/config/ref.rb +3 -3
  64. data/modules/mu/config/schema_helpers.rb +12 -3
  65. data/modules/mu/config/server.rb +10 -4
  66. data/modules/mu/config/server_pool.rb +2 -2
  67. data/modules/mu/config/vpc.rb +10 -10
  68. data/modules/mu/defaults/AWS.yaml +96 -96
  69. data/modules/mu/deploy.rb +27 -14
  70. data/modules/mu/groomers/chef.rb +2 -2
  71. data/modules/mu/master.rb +49 -3
  72. data/modules/mu/mommacat.rb +27 -9
  73. data/modules/mu/mommacat/naming.rb +2 -2
  74. data/modules/mu/mommacat/search.rb +16 -5
  75. data/modules/mu/mommacat/storage.rb +67 -32
  76. data/modules/mu/providers/aws.rb +185 -71
  77. data/modules/mu/providers/aws/alarm.rb +3 -3
  78. data/modules/mu/providers/aws/bucket.rb +19 -19
  79. data/modules/mu/providers/aws/cache_cluster.rb +22 -22
  80. data/modules/mu/providers/aws/cdn.rb +2 -2
  81. data/modules/mu/providers/aws/collection.rb +14 -14
  82. data/modules/mu/providers/aws/container_cluster.rb +27 -27
  83. data/modules/mu/providers/aws/database.rb +49 -45
  84. data/modules/mu/providers/aws/dnszone.rb +5 -5
  85. data/modules/mu/providers/aws/endpoint.rb +35 -35
  86. data/modules/mu/providers/aws/firewall_rule.rb +26 -23
  87. data/modules/mu/providers/aws/function.rb +35 -32
  88. data/modules/mu/providers/aws/group.rb +7 -7
  89. data/modules/mu/providers/aws/habitat.rb +2 -2
  90. data/modules/mu/providers/aws/job.rb +35 -32
  91. data/modules/mu/providers/aws/loadbalancer.rb +58 -37
  92. data/modules/mu/providers/aws/log.rb +14 -14
  93. data/modules/mu/providers/aws/msg_queue.rb +10 -10
  94. data/modules/mu/providers/aws/nosqldb.rb +8 -8
  95. data/modules/mu/providers/aws/notifier.rb +7 -7
  96. data/modules/mu/providers/aws/role.rb +69 -47
  97. data/modules/mu/providers/aws/search_domain.rb +10 -10
  98. data/modules/mu/providers/aws/server.rb +198 -110
  99. data/modules/mu/providers/aws/server_pool.rb +71 -119
  100. data/modules/mu/providers/aws/storage_pool.rb +17 -9
  101. data/modules/mu/providers/aws/user.rb +1 -1
  102. data/modules/mu/providers/aws/vpc.rb +106 -51
  103. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  104. data/modules/mu/providers/azure.rb +82 -16
  105. data/modules/mu/providers/azure/server.rb +18 -3
  106. data/modules/mu/providers/cloudformation/server.rb +1 -1
  107. data/modules/mu/providers/google.rb +20 -5
  108. data/modules/mu/providers/google/folder.rb +6 -2
  109. data/modules/mu/providers/google/function.rb +65 -30
  110. data/modules/mu/providers/google/role.rb +2 -1
  111. data/modules/mu/providers/google/vpc.rb +27 -2
  112. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  113. data/modules/tests/k8s.yaml +1 -1
  114. metadata +32 -15
data/modules/mu/deploy.rb CHANGED
@@ -269,6 +269,7 @@ module MU
269
269
  cloudclass = MU::Cloud.cloudClass(cloud)
270
270
  cloudclass.initDeploy(@mommacat)
271
271
  }
272
+ @mommacat.writeDeploySecret
272
273
 
273
274
  # Kick off threads to create each of our new servers.
274
275
  @my_threads << Thread.new {
@@ -437,10 +438,10 @@ module MU
437
438
  MU.log "Failed to generate AWS cost-calculation URL. Skipping.", MU::WARN, details: "Deployment uses a feature not available in CloudFormation layer.", verbosity: MU::Logger::NORMAL
438
439
  ensure
439
440
  MU.setLogging(@verbosity)
440
- MU.log "Deployment #{MU.deploy_id} \"#{MU.handle}\" complete", details: deployment, verbosity: @verbosity
441
+ MU.log "Deployment #{MU.deploy_id} \"#{MU.handle}\" #{@updating ? "updated" : "complete"}", details: deployment, verbosity: @verbosity
441
442
  end
442
443
  else
443
- MU.log "Deployment #{MU.deploy_id} \"#{MU.handle}\" complete", details: deployment, verbosity: @verbosity
444
+ MU.log "Deployment #{MU.deploy_id} \"#{MU.handle}\" #{@updating ? "updated" : "complete"}", details: deployment, verbosity: @verbosity
444
445
  end
445
446
 
446
447
 
@@ -450,7 +451,7 @@ module MU
450
451
  }
451
452
  end
452
453
 
453
- @mommacat.sendAdminSlack("Deploy completed succesfully", msg: MU.summary.join("\n"))
454
+ @mommacat.sendAdminSlack("Deploy #{MU.deploy_id} \"#{MU.handle}\" #{@updating ? "updated" : "complete"}", msg: MU.summary.join("\n"))
454
455
  end
455
456
 
456
457
  private
@@ -535,8 +536,9 @@ MESSAGE_END
535
536
  #########################################################################
536
537
  def addDependentThread(parent, child)
537
538
  @dependency_semaphore.synchronize {
538
- @dependency_threads[child] = Array.new if !@dependency_threads[child]
539
+ @dependency_threads[child] ||= []
539
540
  @dependency_threads[child] << parent
541
+ @dependency_threads[child].uniq!
540
542
  MU.log "Thread #{child} will wait on #{parent}", MU::DEBUG, details: @dependency_threads[child]
541
543
  }
542
544
  end
@@ -567,6 +569,7 @@ MESSAGE_END
567
569
 
568
570
  MU.log "Setting dependencies for #{name}", MU::DEBUG, details: resource["dependencies"]
569
571
  if !resource["dependencies"].nil? then
572
+
570
573
  resource["dependencies"].each { |dependency|
571
574
  parent_class = MU::Cloud.loadBaseType(dependency['type'])
572
575
 
@@ -576,31 +579,41 @@ MESSAGE_END
576
579
  parent = parent_type+"_"+dependency["name"]+"_create"
577
580
  addDependentThread(parent, "#{name}_groom")
578
581
 
582
+ # if we've explicitly declared each end of the dependency, roll
583
+ # with that and don't meddle further
584
+ if dependency["my_phase"] and dependency["their_phase"]
585
+ parent = parent_type+"_"+dependency["name"]+"_"+dependency["their_phase"]
586
+ addDependentThread(parent, name+"_"+dependency["my_phase"])
587
+ next
588
+ end
589
+
579
590
  # should our creation thread also wait on our parent's create?
580
- if !dependency["no_create_wait"] and
591
+ if dependency["my_phase"] == "create" and
581
592
  (resource["#MU_CLOUDCLASS"].waits_on_parent_completion or
582
- dependency['phase'] == "create" or
583
- parent_class.deps_wait_on_my_creation)
593
+ parent_class.deps_wait_on_my_creation
594
+ )
584
595
  addDependentThread(parent, "#{name}_create")
585
596
  end
586
597
 
587
598
 
588
599
  # how about our groom thread waiting on our parents' grooms?
589
- if (dependency['phase'] == "groom" or resource["#MU_CLOUDCLASS"].waits_on_parent_completion) and parent_class.instance_methods(false).include?(:groom)
600
+ if (dependency['their_phase'] == "groom" or resource["#MU_CLOUDCLASS"].waits_on_parent_completion) and parent_class.instance_methods(false).include?(:groom)
590
601
  parent = parent_type+"_"+dependency["name"]+"_groom"
591
602
  addDependentThread(parent, "#{name}_groom")
592
- if !dependency["no_create_wait"] and (
603
+ if dependency["my_phase"] == "groom" and
604
+ (dependency['their_phase'] == "create" or
605
+ (!dependency['their_phase'] and
593
606
  parent_class.deps_wait_on_my_creation or
594
- resource["#MU_CLOUDCLASS"].waits_on_parent_completion or
595
- dependency['phase'] == "groom"
607
+ resource["#MU_CLOUDCLASS"].waits_on_parent_completion)
596
608
  )
597
609
  addDependentThread(parent, "#{name}_create")
598
610
  end
599
611
  end
600
612
  }
601
613
  end
602
- MU.log "Thread dependencies #{res_type}[#{name}]", MU::DEBUG, details: { "create" => @dependency_threads["#{name}_create"], "groom" => @dependency_threads["#{name}_groom"] }
603
- @dependency_threads["#{name}_groom"]=["#{name}_create", "mu_groom_container"]
614
+ @dependency_threads["#{name}_groom"].concat(["#{name}_create", "mu_groom_container"])
615
+ @dependency_threads["#{name}_groom"].uniq!
616
+ MU.log "Thread dependencies #{res_type}[#{name}]", MU::DEBUG, details: { "create" => @dependency_threads["#{name}_create"], "groom" => @dependency_threads["#{name}_groom"] } if res_type == "role" and resource['name'] == "dynamostream-to-es"
604
617
  }
605
618
  end
606
619
 
@@ -634,7 +647,7 @@ MESSAGE_END
634
647
  begin
635
648
  if myservice['#MUOBJECT'].nil?
636
649
  if @mommacat
637
- ext_obj = @mommacat.findLitterMate(type: myservice["#MU_CLOUDCLASS"].cfg_plural, name: myservice['name'], credentials: myservice['credentials'], created_only: true, return_all: false)
650
+ ext_obj = @mommacat.findLitterMate(type: myservice["#MU_CLOUDCLASS"].cfg_plural, name: myservice['name'], credentials: myservice['credentials'], created_only: true, return_all: false, ignore_missing: !@updating)
638
651
  if @updating and ext_obj
639
652
  ext_obj.config!(myservice)
640
653
  end
@@ -70,8 +70,8 @@ module MU
70
70
  # XXX kludge to get at knife-windows when it's installed from
71
71
  # a git repo and bundler sticks it somewhere in a corner
72
72
  $LOAD_PATH.each { |path|
73
- if path.match(/\/gems\/aws\-sdk\-core\-\d+\.\d+\.\d+\/lib$/)
74
- addpath = path.sub(/\/gems\/aws\-sdk\-core\-\d+\.\d+\.\d+\/lib$/, "")+"/bundler/gems"
73
+ if path.match(/\/gems\/chef\-\d+\.\d+\.\d+\/lib$/)
74
+ addpath = path.sub(/\/gems\/chef\-\d+\.\d+\.\d+\/lib$/, "")+"/bundler/gems"
75
75
  Dir.glob(addpath+"/knife-windows-*").each { |version|
76
76
  $LOAD_PATH << version+"/lib"
77
77
  }
data/modules/mu/master.rb CHANGED
@@ -195,9 +195,12 @@ module MU
195
195
  temp_dev = "/dev/#{ramdisk}"
196
196
 
197
197
  if !File.open("/etc/mtab").read.match(/ #{path} /)
198
- realdevice = device.dup
199
- if MU::Cloud::Google.hosted?
200
- realdevice = "/dev/disk/by-id/google-"+device.gsub(/.*?\/([^\/]+)$/, '\1')
198
+ realdevice = if MU::Cloud::Google.hosted?
199
+ "/dev/disk/by-id/google-"+device.gsub(/.*?\/([^\/]+)$/, '\1')
200
+ elsif MU::Cloud::AWS.hosted?
201
+ MU::Cloud::AWS.realDevicePath(device.dup)
202
+ else
203
+ device.dup
201
204
  end
202
205
  alias_device = cryptfile ? "/dev/mapper/"+path.gsub(/[^0-9a-z_\-]/i, "_") : realdevice
203
206
 
@@ -216,6 +219,9 @@ module MU
216
219
  tag_name: "Name",
217
220
  tag_value: "#{$MU_CFG['hostname']} #{path}"
218
221
  )
222
+ # the device might be on some arbitrary NVMe slot
223
+ realdevice = MU::Cloud::AWS.realDevicePath(realdevice)
224
+ alias_device = cryptfile ? "/dev/mapper/"+path.gsub(/[^0-9a-z_\-]/i, "_") : realdevice
219
225
  elsif MU::Cloud::Google.hosted?
220
226
  dummy_svr = MU::Cloud::Google::Server.new(
221
227
  mu_name: "MU-MASTER",
@@ -901,5 +907,45 @@ module MU
901
907
  }
902
908
  end
903
909
 
910
+ # Just list our block devices
911
+ # @return [Array<String>]
912
+ def self.listBlockDevices
913
+ if File.executable?("/bin/lsblk")
914
+ %x{/bin/lsblk -i -p -r -n | egrep ' disk( |$)'}.each_line.map { |l|
915
+ l.chomp.sub(/ .*/, '')
916
+ }
917
+ else
918
+ # XXX something dumber
919
+ nil
920
+ end
921
+ end
922
+
923
+
924
+ # Retrieve the UUID of a block device, if available
925
+ # @param dev [String]
926
+ def self.diskUUID(dev)
927
+ realdev = if MU::Cloud::Google.hosted?
928
+ "/dev/disk/by-id/google-"+dev.gsub(/.*?\/([^\/]+)$/, '\1')
929
+ elsif MU::Cloud::AWS.hosted?
930
+ MU::Cloud::AWS.realDevicePath(dev)
931
+ else
932
+ dev
933
+ end
934
+ %x{/sbin/blkid #{realdev} -o export | grep ^UUID=}.chomp
935
+ end
936
+
937
+ # Determine whether we're running in an NVMe-enabled environment
938
+ def self.nvme?
939
+ if File.executable?("/bin/lsblk")
940
+ %x{/bin/lsblk -i -p -r -n}.each_line { |l|
941
+ return true if l =~ /^\/dev\/nvme\d/
942
+ }
943
+ else
944
+ return true if File.exists?("/dev/nvme0n1")
945
+ end
946
+ false
947
+ end
948
+
949
+
904
950
  end
905
951
  end
@@ -173,6 +173,7 @@ module MU
173
173
  @public_key = nil
174
174
  @secrets = Hash.new
175
175
  @secrets['instance_secret'] = Hash.new
176
+ @secrets['windows_admin_password'] = Hash.new
176
177
  @ssh_key_name = ssh_key_name
177
178
  @ssh_private_key = ssh_private_key
178
179
  @ssh_public_key = ssh_public_key
@@ -512,6 +513,8 @@ module MU
512
513
  @ssh_private_key = File.read("#{ssh_dir}/#{@ssh_key_name}")
513
514
  @ssh_private_key.chomp!
514
515
 
516
+ # XXX the following mess belongs in cloud layers, probably in their initDeploy
517
+ # methods
515
518
  if numKittens(clouds: ["AWS"], types: ["Server", "ServerPool", "ContainerCluster"]) > 0
516
519
  creds_used = []
517
520
  ["servers", "server_pools", "container_clusters"].each { |type|
@@ -547,15 +550,26 @@ module MU
547
550
  # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
548
551
  # @return [void]
549
552
  def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
553
+ no_write = (@no_artifacts or !caller.grep(/\/mommacat\.rb:\d+:in `notify'/).empty?)
550
554
 
551
555
  begin
552
- MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
556
+ if !no_write
557
+ if !MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id, retries: 300)
558
+ raise MuError, "Failed to get deployment-notifcation lock for #{@deploy_id}"
559
+ end
560
+ end
553
561
 
554
562
  if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
555
563
  loadDeploy(true) # make sure we're saving the latest and greatest
556
564
  end
557
565
 
558
- _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
566
+ @timestamp ||= @deployment['timestamp']
567
+ @seed ||= @deployment['seed']
568
+ @appname ||= @deployment['appname']
569
+ @handle ||= @deployment['handle']
570
+
571
+ _shortclass, _cfg_name, mu_type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
572
+ type = mu_type if mu_type
559
573
  has_multiples = attrs[:has_multiples] ? true : false
560
574
 
561
575
  mu_name ||= if !data.nil? and !data["mu_name"].nil?
@@ -569,6 +583,7 @@ module MU
569
583
  end
570
584
 
571
585
  @need_deploy_flush = true
586
+ @last_modified = Time.now
572
587
 
573
588
  if !remove
574
589
  if data.nil?
@@ -588,7 +603,9 @@ module MU
588
603
  @deployment[type][key] = data
589
604
  MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
590
605
  end
591
- save!(key) if !delayed_save and !@no_artifacts
606
+ if !delayed_save and !no_write
607
+ save!(key)
608
+ end
592
609
  else
593
610
  have_deploy = true
594
611
  if @deployment[type].nil? or @deployment[type][key].nil?
@@ -613,10 +630,10 @@ module MU
613
630
  end
614
631
  }
615
632
  end
616
- save! if !delayed_save and !@no_artifacts
633
+ save! if !delayed_save and !no_write
617
634
  end
618
635
  ensure
619
- MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts
636
+ MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !no_write
620
637
  end
621
638
  end
622
639
 
@@ -870,13 +887,13 @@ MAIL_HEAD_END
870
887
  if resource and resource.config and resource.config['cloud']
871
888
  cloudclass = MU::Cloud.cloudClass(resource.config['cloud'])
872
889
 
873
- cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
874
- cloudclass.writeDeploySecret(@deploy_id, key.to_pem, cert_cn+".key", credentials: resource.config['credentials'])
890
+ cloudclass.writeDeploySecret(self, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
891
+ cloudclass.writeDeploySecret(self, key.to_pem, cert_cn+".key", credentials: resource.config['credentials'])
875
892
  if pfx_cert
876
- cloudclass.writeDeploySecret(@deploy_id, pfx_cert.to_der, cert_cn+".pfx", credentials: resource.config['credentials'])
893
+ cloudclass.writeDeploySecret(self, pfx_cert.to_der, cert_cn+".pfx", credentials: resource.config['credentials'])
877
894
  end
878
895
  if winrm_cert
879
- cloudclass.writeDeploySecret(@deploy_id, winrm_cert.to_pem, cert_cn+"-winrm.crt", credentials: resource.config['credentials'])
896
+ cloudclass.writeDeploySecret(self, winrm_cert.to_pem, cert_cn+"-winrm.crt", credentials: resource.config['credentials'])
880
897
  end
881
898
  end
882
899
 
@@ -896,6 +913,7 @@ MAIL_HEAD_END
896
913
  ###########################################################################
897
914
  ###########################################################################
898
915
  def setThreadContextToMe
916
+
899
917
  ["appname", "environment", "timestamp", "seed", "handle"].each { |var|
900
918
  @deployment[var] ||= instance_variable_get("@#{var}".to_sym)
901
919
  if @deployment[var]
@@ -164,7 +164,7 @@ module MU
164
164
  # @param scrub_mu_isms [Boolean]: Don't bother with generating names specific to this deployment. Used to generate generic CloudFormation templates, amongst other purposes.
165
165
  # @param disallowed_chars [Regexp]: A pattern of characters that are illegal for this resource name, such as +/[^a-zA-Z0-9-]/+
166
166
  # @return [String]: A full name string for this resource
167
- def getResourceName(name, max_length: 255, need_unique_string: false, use_unique_string: nil, reuse_unique_string: false, scrub_mu_isms: @original_config['scrub_mu_isms'], disallowed_chars: nil)
167
+ def getResourceName(name, max_length: 255, need_unique_string: false, use_unique_string: nil, reuse_unique_string: false, scrub_mu_isms: @original_config['scrub_mu_isms'], disallowed_chars: nil, never_gen_unique: false)
168
168
  if name.nil?
169
169
  raise MuError, "Got no argument to MU::MommaCat.getResourceName"
170
170
  end
@@ -219,7 +219,7 @@ module MU
219
219
  else
220
220
  # If we have to strip anything, assume we've lost uniqueness and
221
221
  # will have to compensate with #genUniquenessString.
222
- need_unique_string = true
222
+ need_unique_string = true if !never_gen_unique
223
223
  reserved = 4
224
224
  basename.sub!(/-[^-]+-#{@seed.upcase}-#{Regexp.escape(name.upcase)}$/, "")
225
225
  basename = basename + "-" + @seed.upcase + "-" + name.upcase
@@ -107,8 +107,17 @@ 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)
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)
110
+ cur_habitats = []
111
+
112
+ if habitats and !habitats.empty? and habitats != [nil]
113
+ valid_habitats = cloudclass.listHabitats(creds)
114
+ cur_habitats = (habitats & valid_habitats)
115
+ next if cur_habitats.empty?
116
+ else
117
+ cur_habitats = cloudclass.listHabitats(creds)
118
+ end
119
+
120
+ cloud_descs = search_cloud_provider(type, cloud, cur_habitats, region, cloud_id: cloud_id, tag_key: tag_key, tag_value: tag_value, credentials: creds, flags: flags)
112
121
 
113
122
  cloud_descs.each_pair.each { |p, regions|
114
123
  regions.each_pair.each { |r, results|
@@ -138,7 +147,7 @@ module MU
138
147
  # @param created_only [Boolean]: Only return the littermate if its cloud_id method returns a value
139
148
  # @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.
140
149
  # @return [MU::Cloud]
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)
150
+ def findLitterMate(type: nil, name: nil, mu_name: nil, cloud_id: nil, created_only: false, return_all: false, credentials: nil, habitat: nil, ignore_missing: false, debug: false, **flags)
142
151
  _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
143
152
 
144
153
  # If we specified a habitat, which we may also have done by its shorthand
@@ -173,8 +182,10 @@ module MU
173
182
  end
174
183
  end
175
184
  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
185
+ if !ignore_missing
186
+ 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
187
+ @object_load_fails = true
188
+ end
178
189
  return nil
179
190
  end
180
191
  end
@@ -123,11 +123,12 @@ module MU
123
123
  MU::Cloud.resource_types.each_pair { |res_type, attrs|
124
124
  next if !@deployment.has_key?(attrs[:cfg_plural])
125
125
  deletia = []
126
+ # existing_deploys
126
127
  @deployment[attrs[:cfg_plural]].each_pair { |res_name, data|
127
128
  orig_cfg = findResourceConfig(attrs[:cfg_plural], res_name, (scrub_with || @original_config))
128
129
 
129
- if orig_cfg.nil?
130
- MU.log "#{res_type} #{res_name} no longer configured, will remove deployment metadata", MU::NOTICE
130
+ if orig_cfg.nil? and (!data['mu_name'] or data['mu_name'] =~ /^#{Regexp.quote(@deploy_id)}/)
131
+ MU.log "#{res_type} #{res_name} no longer configured, will remove deployment metadata", MU::NOTICE, details: data
131
132
  deletia << res_name
132
133
  end
133
134
  }
@@ -176,7 +177,7 @@ module MU
176
177
  # @param id [String]: The lock identifier to release.
177
178
  # @param nonblock [Boolean]: Whether to block while waiting for the lock. In non-blocking mode, we simply return false if the lock is not available.
178
179
  # return [false, nil]
179
- def self.lock(id, nonblock = false, global = false, deploy_id: MU.deploy_id)
180
+ def self.lock(id, nonblock = false, global = false, retries: 0, deploy_id: MU.deploy_id)
180
181
  raise MuError, "Can't pass a nil id to MU::MommaCat.lock" if id.nil?
181
182
 
182
183
  if !global
@@ -189,6 +190,7 @@ module MU
189
190
  MU.log "Creating #{lockdir}", MU::DEBUG
190
191
  Dir.mkdir(lockdir, 0700)
191
192
  end
193
+ nonblock = true if retries > 0
192
194
 
193
195
  @lock_semaphore.synchronize {
194
196
  if @locks[Thread.current.object_id].nil?
@@ -197,11 +199,39 @@ module MU
197
199
 
198
200
  @locks[Thread.current.object_id][id] = File.open("#{lockdir}/#{id}.lock", File::CREAT|File::RDWR, 0600)
199
201
  }
200
- MU.log "Getting a lock on #{lockdir}/#{id}.lock (thread #{Thread.current.object_id})...", MU::DEBUG
202
+
203
+ MU.log "Getting a lock on #{lockdir}/#{id}.lock (thread #{Thread.current.object_id})...", MU::DEBUG, details: caller
204
+ show_relevant = Proc.new {
205
+ @lock_semaphore.synchronize {
206
+ @locks.each_pair { |thread_id, lock|
207
+ lock.each_pair { |lockid, lockpath|
208
+ if lockid == id
209
+ thread = Thread.list.select { |t| t.object_id == thread_id }.first
210
+ if thread.object_id != Thread.current.object_id
211
+ MU.log "#{thread_id} sitting on #{id} (#{thread.thread_variables.map { |v| "#{v.to_s}: #{thread.thread_variable_get(v).to_s}" }.join(", ")})", MU::WARN, thread.backtrace
212
+ end
213
+ end
214
+ }
215
+ }
216
+ }
217
+ }
218
+
201
219
  begin
202
220
  if nonblock
203
221
  if !@locks[Thread.current.object_id][id].flock(File::LOCK_EX|File::LOCK_NB)
204
- return false
222
+ if retries > 0
223
+ success = false
224
+ MU.retrier([], loop_if: Proc.new { !success }, loop_msg: "Waiting for lock on #{lockdir}/#{id}.lock...", max: retries, wait: 1, logmsg_interval: 0) { |cur_retries, _wait|
225
+ success = @locks[Thread.current.object_id][id].flock(File::LOCK_EX|File::LOCK_NB)
226
+ if !success and cur_retries > 0 and (cur_retries % 45) == 0
227
+ show_relevant.call
228
+ end
229
+ }
230
+ show_relevant.call if !success
231
+ return success
232
+ else
233
+ return false
234
+ end
205
235
  end
206
236
  else
207
237
  @locks[Thread.current.object_id][id].flock(File::LOCK_EX)
@@ -390,6 +420,7 @@ module MU
390
420
  deploy.flock(File::LOCK_UN)
391
421
  deploy.close
392
422
  @need_deploy_flush = false
423
+ @last_modified = nil
393
424
  MU::MommaCat.updateLitter(@deploy_id, self)
394
425
  end
395
426
 
@@ -512,6 +543,22 @@ module MU
512
543
  return Dir.exist?(deploy_path)
513
544
  end
514
545
 
546
+ # Write our shared deploy secret out to wherever the cloud provider layers
547
+ # like to stash it.
548
+ def writeDeploySecret
549
+ return if !@deploy_secret
550
+ credsets = credsUsed
551
+ return if !credsets
552
+ if !@original_config['scrub_mu_isms'] and !@no_artifacts
553
+ cloudsUsed.each { |cloud|
554
+ credsets.each { |credentials|
555
+ next if MU::Cloud.cloudClass(cloud).credConfig(credentials).nil? # XXX this is a dumb way to check this, should be able to get credsUsed by cloud
556
+ MU::Cloud.cloudClass(cloud).writeDeploySecret(self, @deploy_secret, credentials: credentials)
557
+ }
558
+ }
559
+ end
560
+ end
561
+
515
562
  private
516
563
 
517
564
  def writeFile(filename, contents)
@@ -522,30 +569,8 @@ module MU
522
569
 
523
570
  # Helper for +initialize+
524
571
  def setDeploySecret
525
- credsets = {}
526
- MU::Cloud.resource_types.values.each { |attrs|
527
- if !@original_config[attrs[:cfg_plural]].nil? and @original_config[attrs[:cfg_plural]].size > 0
528
- @original_config[attrs[:cfg_plural]].each { |resource|
529
-
530
- credsets[resource['cloud']] ||= []
531
- credsets[resource['cloud']] << resource['credentials']
532
- @clouds[resource['cloud']] = 0 if !@clouds.has_key?(resource['cloud'])
533
- @clouds[resource['cloud']] = @clouds[resource['cloud']] + 1
534
-
535
- }
536
- end
537
- }
538
-
539
572
  MU.log "Creating deploy secret for #{MU.deploy_id}"
540
573
  @deploy_secret = Password.random(256)
541
- if !@original_config['scrub_mu_isms'] and !@no_artifacts
542
- credsets.each_pair { |cloud, creds|
543
- creds.uniq!
544
- creds.each { |credentials|
545
- MU::Cloud.cloudClass(cloud).writeDeploySecret(@deploy_id, @deploy_secret, credentials: credentials)
546
- }
547
- }
548
- end
549
574
  end
550
575
 
551
576
  def loadObjects(delay_descriptor_load)
@@ -618,6 +643,14 @@ module MU
618
643
  def loadDeployFromCache(set_context_to_me = true)
619
644
  return false if !File.size?(deploy_dir+"/deployment.json")
620
645
 
646
+ lastmod = File.mtime("#{deploy_dir}/deployment.json")
647
+ if @last_modified and lastmod < @last_modified
648
+ MU.log "#{deploy_dir}/deployment.json last written at #{lastmod}, live meta at #{@last_modified}, not loading", MU::WARN if @last_modified
649
+ # this is a weird place for this
650
+ setThreadContextToMe if set_context_to_me
651
+ return true
652
+ end
653
+
621
654
  deploy = File.open("#{deploy_dir}/deployment.json", File::RDONLY)
622
655
  MU.log "Getting lock to read #{deploy_dir}/deployment.json", MU::DEBUG
623
656
  # deploy.flock(File::LOCK_EX)
@@ -629,6 +662,7 @@ module MU
629
662
 
630
663
  begin
631
664
  @deployment = JSON.parse(File.read("#{deploy_dir}/deployment.json"))
665
+ # XXX is it worthwhile to merge fuckery?
632
666
  rescue JSON::ParserError => e
633
667
  MU.log "JSON parse failed on #{deploy_dir}/deployment.json", MU::ERR, details: e.message
634
668
  end
@@ -638,20 +672,21 @@ module MU
638
672
 
639
673
  setThreadContextToMe if set_context_to_me
640
674
 
641
- @timestamp = @deployment['timestamp']
642
- @seed = @deployment['seed']
643
- @appname = @deployment['appname']
644
- @handle = @deployment['handle']
645
-
646
675
  true
647
676
  end
648
677
 
678
+
649
679
  ###########################################################################
650
680
  ###########################################################################
651
681
  def loadDeploy(deployment_json_only = false, set_context_to_me: true)
652
682
  MU::MommaCat.deploy_struct_semaphore.synchronize {
653
683
  success = loadDeployFromCache(set_context_to_me)
654
684
 
685
+ @timestamp ||= @deployment['timestamp']
686
+ @seed ||= @deployment['seed']
687
+ @appname ||= @deployment['appname']
688
+ @handle ||= @deployment['handle']
689
+
655
690
  return if deployment_json_only and success
656
691
 
657
692
  if File.exist?(deploy_dir+"/private_key")