cloud-mu 3.2.0 → 3.5.0

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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  4. data/bin/mu-adopt +12 -1
  5. data/bin/mu-aws-setup +41 -7
  6. data/bin/mu-azure-setup +34 -0
  7. data/bin/mu-configure +214 -119
  8. data/bin/mu-gcp-setup +37 -2
  9. data/bin/mu-load-config.rb +2 -1
  10. data/bin/mu-node-manage +3 -0
  11. data/bin/mu-refresh-ssl +67 -0
  12. data/bin/mu-run-tests +28 -6
  13. data/bin/mu-self-update +30 -10
  14. data/bin/mu-upload-chef-artifacts +30 -26
  15. data/cloud-mu.gemspec +10 -8
  16. data/cookbooks/mu-master/attributes/default.rb +5 -1
  17. data/cookbooks/mu-master/metadata.rb +2 -2
  18. data/cookbooks/mu-master/recipes/default.rb +81 -26
  19. data/cookbooks/mu-master/recipes/init.rb +197 -62
  20. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  21. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  22. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  23. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  24. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  25. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  26. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  27. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  28. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  30. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  31. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  32. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  33. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  34. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  35. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  36. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  37. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  38. data/extras/Gemfile.lock.bootstrap +394 -0
  39. data/extras/bucketstubs/error.html +0 -0
  40. data/extras/bucketstubs/index.html +0 -0
  41. data/extras/clean-stock-amis +11 -3
  42. data/extras/generate-stock-images +6 -3
  43. data/extras/git_rpm/build.sh +20 -0
  44. data/extras/git_rpm/mugit.spec +53 -0
  45. data/extras/image-generators/AWS/centos7.yaml +19 -16
  46. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  47. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  48. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  49. data/extras/openssl_rpm/build.sh +19 -0
  50. data/extras/openssl_rpm/mussl.spec +46 -0
  51. data/extras/python_rpm/muthon.spec +14 -4
  52. data/extras/ruby_rpm/muby.spec +9 -5
  53. data/extras/sqlite_rpm/build.sh +19 -0
  54. data/extras/sqlite_rpm/muqlite.spec +47 -0
  55. data/install/installer +7 -5
  56. data/modules/mommacat.ru +2 -2
  57. data/modules/mu.rb +14 -7
  58. data/modules/mu/adoption.rb +5 -5
  59. data/modules/mu/cleanup.rb +47 -25
  60. data/modules/mu/cloud.rb +29 -1
  61. data/modules/mu/cloud/dnszone.rb +0 -2
  62. data/modules/mu/cloud/machine_images.rb +1 -1
  63. data/modules/mu/cloud/providers.rb +6 -1
  64. data/modules/mu/cloud/resource_base.rb +16 -7
  65. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  66. data/modules/mu/cloud/wrappers.rb +20 -7
  67. data/modules/mu/config.rb +28 -12
  68. data/modules/mu/config/bucket.rb +31 -2
  69. data/modules/mu/config/cache_cluster.rb +1 -1
  70. data/modules/mu/config/cdn.rb +100 -0
  71. data/modules/mu/config/container_cluster.rb +1 -1
  72. data/modules/mu/config/database.rb +3 -3
  73. data/modules/mu/config/dnszone.rb +4 -3
  74. data/modules/mu/config/endpoint.rb +1 -0
  75. data/modules/mu/config/firewall_rule.rb +1 -1
  76. data/modules/mu/config/function.rb +16 -7
  77. data/modules/mu/config/job.rb +89 -0
  78. data/modules/mu/config/notifier.rb +7 -18
  79. data/modules/mu/config/ref.rb +55 -9
  80. data/modules/mu/config/schema_helpers.rb +12 -3
  81. data/modules/mu/config/server.rb +11 -5
  82. data/modules/mu/config/server_pool.rb +2 -2
  83. data/modules/mu/config/vpc.rb +11 -10
  84. data/modules/mu/defaults/AWS.yaml +106 -106
  85. data/modules/mu/deploy.rb +40 -14
  86. data/modules/mu/groomers/chef.rb +2 -2
  87. data/modules/mu/master.rb +70 -3
  88. data/modules/mu/mommacat.rb +28 -9
  89. data/modules/mu/mommacat/daemon.rb +13 -7
  90. data/modules/mu/mommacat/naming.rb +2 -2
  91. data/modules/mu/mommacat/search.rb +16 -5
  92. data/modules/mu/mommacat/storage.rb +67 -32
  93. data/modules/mu/providers/aws.rb +298 -85
  94. data/modules/mu/providers/aws/alarm.rb +5 -5
  95. data/modules/mu/providers/aws/bucket.rb +284 -50
  96. data/modules/mu/providers/aws/cache_cluster.rb +26 -26
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/providers/aws/collection.rb +16 -16
  99. data/modules/mu/providers/aws/container_cluster.rb +84 -64
  100. data/modules/mu/providers/aws/database.rb +59 -55
  101. data/modules/mu/providers/aws/dnszone.rb +29 -12
  102. data/modules/mu/providers/aws/endpoint.rb +535 -50
  103. data/modules/mu/providers/aws/firewall_rule.rb +32 -26
  104. data/modules/mu/providers/aws/folder.rb +1 -1
  105. data/modules/mu/providers/aws/function.rb +300 -134
  106. data/modules/mu/providers/aws/group.rb +16 -14
  107. data/modules/mu/providers/aws/habitat.rb +4 -4
  108. data/modules/mu/providers/aws/job.rb +469 -0
  109. data/modules/mu/providers/aws/loadbalancer.rb +67 -45
  110. data/modules/mu/providers/aws/log.rb +17 -17
  111. data/modules/mu/providers/aws/msg_queue.rb +22 -13
  112. data/modules/mu/providers/aws/nosqldb.rb +99 -8
  113. data/modules/mu/providers/aws/notifier.rb +137 -65
  114. data/modules/mu/providers/aws/role.rb +119 -83
  115. data/modules/mu/providers/aws/search_domain.rb +166 -30
  116. data/modules/mu/providers/aws/server.rb +209 -118
  117. data/modules/mu/providers/aws/server_pool.rb +95 -130
  118. data/modules/mu/providers/aws/storage_pool.rb +19 -11
  119. data/modules/mu/providers/aws/user.rb +5 -5
  120. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  121. data/modules/mu/providers/aws/vpc.rb +109 -54
  122. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  123. data/modules/mu/providers/azure.rb +78 -12
  124. data/modules/mu/providers/azure/server.rb +20 -4
  125. data/modules/mu/providers/cloudformation/server.rb +1 -1
  126. data/modules/mu/providers/google.rb +21 -5
  127. data/modules/mu/providers/google/bucket.rb +1 -1
  128. data/modules/mu/providers/google/container_cluster.rb +1 -1
  129. data/modules/mu/providers/google/database.rb +1 -1
  130. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  131. data/modules/mu/providers/google/folder.rb +7 -3
  132. data/modules/mu/providers/google/function.rb +66 -31
  133. data/modules/mu/providers/google/group.rb +1 -1
  134. data/modules/mu/providers/google/habitat.rb +1 -1
  135. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  136. data/modules/mu/providers/google/role.rb +6 -3
  137. data/modules/mu/providers/google/server.rb +1 -1
  138. data/modules/mu/providers/google/server_pool.rb +1 -1
  139. data/modules/mu/providers/google/user.rb +1 -1
  140. data/modules/mu/providers/google/vpc.rb +28 -3
  141. data/modules/tests/aws-jobs-functions.yaml +46 -0
  142. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  143. data/modules/tests/centos6.yaml +4 -0
  144. data/modules/tests/centos7.yaml +4 -0
  145. data/modules/tests/ecs.yaml +2 -2
  146. data/modules/tests/eks.yaml +1 -1
  147. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  148. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  149. data/modules/tests/k8s.yaml +1 -1
  150. data/modules/tests/microservice_app.yaml +288 -0
  151. data/modules/tests/rds.yaml +5 -5
  152. data/modules/tests/regrooms/rds.yaml +5 -5
  153. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  154. data/modules/tests/super_complex_bok.yml +2 -2
  155. data/modules/tests/super_simple_bok.yml +2 -2
  156. metadata +42 -17
@@ -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",
@@ -880,5 +886,66 @@ module MU
880
886
  end
881
887
  end
882
888
 
889
+ # Recursively zip a directory
890
+ # @param srcdir [String]
891
+ # @param outfile [String]
892
+ def self.zipDir(srcdir, outfile)
893
+ require 'zip'
894
+ ::Zip::File.open(outfile, ::Zip::File::CREATE) { |zipfile|
895
+ addpath = Proc.new { |zip_path, parent_path|
896
+ Dir.entries(parent_path).reject{ |d| [".", ".."].include?(d) }.each { |entry|
897
+ src = File.join(parent_path, entry)
898
+ dst = File.join(zip_path, entry).sub(/^\//, '')
899
+ if File.directory?(src)
900
+ addpath.call(dst, src)
901
+ else
902
+ zipfile.add(dst, src)
903
+ end
904
+ }
905
+ }
906
+ addpath.call("", srcdir)
907
+ }
908
+ end
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
+
883
950
  end
884
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
 
@@ -811,6 +828,7 @@ MAIL_HEAD_END
811
828
 
812
829
  threads = []
813
830
  update_servers.each { |sibling|
831
+ next if sibling.config.has_key?("groom") and !sibling.config["groom"]
814
832
  threads << Thread.new {
815
833
  Thread.abort_on_exception = true
816
834
  Thread.current.thread_variable_set("name", "sync-"+sibling.mu_name.downcase)
@@ -869,13 +887,13 @@ MAIL_HEAD_END
869
887
  if resource and resource.config and resource.config['cloud']
870
888
  cloudclass = MU::Cloud.cloudClass(resource.config['cloud'])
871
889
 
872
- cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
873
- 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'])
874
892
  if pfx_cert
875
- 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'])
876
894
  end
877
895
  if winrm_cert
878
- 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'])
879
897
  end
880
898
  end
881
899
 
@@ -895,6 +913,7 @@ MAIL_HEAD_END
895
913
  ###########################################################################
896
914
  ###########################################################################
897
915
  def setThreadContextToMe
916
+
898
917
  ["appname", "environment", "timestamp", "seed", "handle"].each { |var|
899
918
  @deployment[var] ||= instance_variable_get("@#{var}".to_sym)
900
919
  if @deployment[var]
@@ -288,8 +288,8 @@ module MU
288
288
 
289
289
  # Path to the PID file used by the Momma Cat daemon
290
290
  # @return [String]
291
- def self.daemonPidFile
292
- base = (Process.uid == 0 and !MU.localOnly) ? "/var" : MU.dataDir
291
+ def self.daemonPidFile(root = false)
292
+ base = ((Process.uid == 0 or root) and !MU.localOnly) ? "/var" : MU.dataDir
293
293
  "#{base}/run/mommacat.pid"
294
294
  end
295
295
 
@@ -306,8 +306,14 @@ module MU
306
306
  Dir.mkdir(dir)
307
307
  end
308
308
  }
309
- return 0 if status
309
+ if (Process.uid != 0 and
310
+ (!$MU_CFG['overridden_keys'] or !$MU_CFG['overridden_keys'].include?("mommacat_port")) and
311
+ status(true)
312
+ ) or status
313
+ return 0
314
+ end
310
315
 
316
+ File.unlink(daemonPidFile) if File.exists?(daemonPidFile)
311
317
  MU.log "Starting Momma Cat on port #{MU.mommaCatPort}, logging to #{daemonLogFile}, PID file #{daemonPidFile}"
312
318
  origdir = Dir.getwd
313
319
  Dir.chdir(MU.myRoot+"/modules")
@@ -346,12 +352,12 @@ module MU
346
352
 
347
353
  # Return true if the Momma Cat daemon appears to be running
348
354
  # @return [Boolean]
349
- def self.status
355
+ def self.status(root = false)
350
356
  if MU.inGem? and MU.muCfg['disable_mommacat']
351
357
  return true
352
358
  end
353
- if File.exist?(daemonPidFile)
354
- pid = File.read(daemonPidFile).chomp.to_i
359
+ if File.exist?(daemonPidFile(root))
360
+ pid = File.read(daemonPidFile(root)).chomp.to_i
355
361
  begin
356
362
  Process.getpgid(pid)
357
363
  MU.log "Momma Cat running with pid #{pid.to_s}", (@@notified_on_pid[pid] ? MU::DEBUG : MU::INFO) # shush
@@ -360,7 +366,7 @@ module MU
360
366
  rescue Errno::ESRCH
361
367
  end
362
368
  end
363
- MU.log "Momma Cat daemon not running", MU::NOTICE, details: daemonPidFile
369
+ MU.log "Momma Cat daemon not running", MU::NOTICE, details: daemonPidFile(root)
364
370
  false
365
371
  end
366
372
 
@@ -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")