cloud-mu 3.2.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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")