cloud-mu 3.1.5 → 3.1.6

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +2 -1
  7. data/bin/mu-configure +16 -0
  8. data/bin/mu-node-manage +15 -16
  9. data/cloud-mu.gemspec +2 -2
  10. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  11. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  12. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  13. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  14. data/extras/clean-stock-amis +25 -19
  15. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  16. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  17. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  18. data/modules/mommacat.ru +1 -1
  19. data/modules/mu.rb +6 -5
  20. data/modules/mu/adoption.rb +19 -4
  21. data/modules/mu/cleanup.rb +181 -293
  22. data/modules/mu/cloud.rb +58 -17
  23. data/modules/mu/clouds/aws.rb +36 -1
  24. data/modules/mu/clouds/aws/container_cluster.rb +30 -21
  25. data/modules/mu/clouds/aws/role.rb +1 -1
  26. data/modules/mu/clouds/aws/vpc.rb +5 -1
  27. data/modules/mu/clouds/azure.rb +10 -0
  28. data/modules/mu/clouds/cloudformation.rb +10 -0
  29. data/modules/mu/clouds/google.rb +18 -4
  30. data/modules/mu/clouds/google/bucket.rb +2 -2
  31. data/modules/mu/clouds/google/container_cluster.rb +10 -7
  32. data/modules/mu/clouds/google/database.rb +3 -3
  33. data/modules/mu/clouds/google/firewall_rule.rb +3 -3
  34. data/modules/mu/clouds/google/function.rb +3 -3
  35. data/modules/mu/clouds/google/loadbalancer.rb +4 -4
  36. data/modules/mu/clouds/google/role.rb +18 -9
  37. data/modules/mu/clouds/google/server.rb +16 -14
  38. data/modules/mu/clouds/google/server_pool.rb +4 -4
  39. data/modules/mu/clouds/google/user.rb +2 -2
  40. data/modules/mu/clouds/google/vpc.rb +9 -13
  41. data/modules/mu/config.rb +1 -1
  42. data/modules/mu/config/container_cluster.rb +5 -0
  43. data/modules/mu/config/doc_helpers.rb +1 -1
  44. data/modules/mu/config/ref.rb +12 -6
  45. data/modules/mu/config/schema_helpers.rb +8 -3
  46. data/modules/mu/config/server.rb +7 -0
  47. data/modules/mu/config/tail.rb +1 -0
  48. data/modules/mu/config/vpc.rb +15 -7
  49. data/modules/mu/config/vpc.yml +0 -1
  50. data/modules/mu/defaults/AWS.yaml +48 -48
  51. data/modules/mu/deploy.rb +1 -1
  52. data/modules/mu/groomer.rb +1 -1
  53. data/modules/mu/groomers/ansible.rb +69 -4
  54. data/modules/mu/groomers/chef.rb +48 -4
  55. data/modules/mu/master.rb +75 -3
  56. data/modules/mu/mommacat.rb +104 -855
  57. data/modules/mu/mommacat/naming.rb +28 -0
  58. data/modules/mu/mommacat/search.rb +463 -0
  59. data/modules/mu/mommacat/storage.rb +185 -183
  60. data/modules/tests/super_simple_bok.yml +1 -3
  61. metadata +8 -5
@@ -474,7 +474,6 @@ module MU
474
474
  MU.log %Q{How to interact with your GKE cluster\nkubectl --kubeconfig "#{kube_conf}" get events --all-namespaces\nkubectl --kubeconfig "#{kube_conf}" get all\nkubectl --kubeconfig "#{kube_conf}" create -f some_k8s_deploy.yml\nkubectl --kubeconfig "#{kube_conf}" get nodes}, MU::SUMMARY
475
475
  end
476
476
 
477
-
478
477
  # Locate an existing ContainerCluster or ContainerClusters and return an array containing matching GCP resource descriptors for those that match.
479
478
  # @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching ContainerClusters
480
479
  def self.find(**args)
@@ -747,15 +746,15 @@ module MU
747
746
  # @return [void]
748
747
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
749
748
 
750
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
751
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
749
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
750
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
752
751
  clusters = []
753
752
 
754
753
  # Make sure we catch regional *and* zone clusters
755
- found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['project']}/locations/#{region}")
754
+ found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['habitat']}/locations/#{region}")
756
755
  clusters.concat(found.clusters) if found and found.clusters
757
756
  MU::Cloud::Google.listAZs(region).each { |az|
758
- found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['project']}/locations/#{az}")
757
+ found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['habitat']}/locations/#{az}")
759
758
  clusters.concat(found.clusters) if found and found.clusters
760
759
  }
761
760
 
@@ -1097,7 +1096,7 @@ module MU
1097
1096
  }
1098
1097
  if !match
1099
1098
  MU.log "No version matching #{cluster['kubernetes']['version']} available, will try floating minor revision", MU::WARN
1100
- cluster['kubernetes']['version'].sub!(/^(\d+\.\d+\.).*/i, '\1')
1099
+ cluster['kubernetes']['version'].sub!(/^(\d+\.\d+)\..*/i, '\1')
1101
1100
  master_versions.each { |v|
1102
1101
  if v.match(/^#{Regexp.quote(cluster['kubernetes']['version'])}/)
1103
1102
  match = true
@@ -1145,6 +1144,10 @@ module MU
1145
1144
  cluster['instance_type'] = MU::Cloud::Google::Server.validateInstanceType(cluster["instance_type"], cluster["region"], project: cluster['project'], credentials: cluster['credentials'])
1146
1145
  ok = false if cluster['instance_type'].nil?
1147
1146
 
1147
+ if !MU::Master.kubectl
1148
+ MU.log "Since I can't find a kubectl executable, you will have to handle all service account, user, and role bindings manually!", MU::WARN
1149
+ end
1150
+
1148
1151
  ok
1149
1152
  end
1150
1153
 
@@ -1236,7 +1239,7 @@ module MU
1236
1239
  # Take this opportunity to ensure that the 'client' service account
1237
1240
  # used by certificate authentication exists and has appropriate
1238
1241
  # privilege
1239
- if @username and @password
1242
+ if @username and @password and MU::Master.kubectl
1240
1243
  File.open(client_binding, "w"){ |k|
1241
1244
  k.puts <<-EOF
1242
1245
  kind: ClusterRoleBinding
@@ -108,13 +108,13 @@ module MU
108
108
  # @param region [String]: The cloud provider region in which to operate
109
109
  # @return [void]
110
110
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
111
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
111
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
112
112
 
113
- # instances = MU::Cloud::Google.sql(credentials: credentials).list_instances(flags['project'], filter: %Q{userLabels.mu-id:"#{MU.deploy_id.downcase}"})
113
+ # instances = MU::Cloud::Google.sql(credentials: credentials).list_instances(flags['habitat'], filter: %Q{userLabels.mu-id:"#{MU.deploy_id.downcase}"})
114
114
  # if instances and instances.items
115
115
  # instances.items.each { |instance|
116
116
  # MU.log "Deleting Cloud SQL instance #{instance.name}"
117
- # MU::Cloud::Google.sql(credentials: credentials).delete_instance(flags['project'], instance.name) if !noop
117
+ # MU::Cloud::Google.sql(credentials: credentials).delete_instance(flags['habitat'], instance.name) if !noop
118
118
  # }
119
119
  # end
120
120
  end
@@ -208,8 +208,8 @@ end
208
208
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
209
209
  # @return [void]
210
210
  def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
211
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
212
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
211
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
212
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
213
213
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
214
214
  if !ignoremaster and MU.mu_public_ip
215
215
  filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
@@ -218,7 +218,7 @@ end
218
218
 
219
219
  MU::Cloud::Google.compute(credentials: credentials).delete(
220
220
  "firewall",
221
- flags["project"],
221
+ flags["habitat"],
222
222
  nil,
223
223
  noop
224
224
  )
@@ -234,10 +234,10 @@ module example.com/cloudfunction
234
234
  # @param region [String]: The cloud provider region
235
235
  # @return [void]
236
236
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
237
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
238
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
237
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
238
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
239
239
  # Make sure we catch regional *and* zone functions
240
- found = MU::Cloud::Google::Function.find(credentials: credentials, region: region, project: flags["project"])
240
+ found = MU::Cloud::Google::Function.find(credentials: credentials, region: region, project: flags["habitat"])
241
241
  found.each_pair { |cloud_id, desc|
242
242
  if (desc.description and desc.description == MU.deploy_id) or
243
243
  (desc.labels and desc.labels["mu-id"] == MU.deploy_id.downcase and (ignoremaster or desc.labels["mu-master-ip"] == MU.mu_public_ip.gsub(/\./, "_"))) or
@@ -147,8 +147,8 @@ module MU
147
147
  # @param region [String]: The cloud provider region
148
148
  # @return [void]
149
149
  def self.cleanup(noop: false, ignoremaster: false, region: nil, credentials: nil, flags: {})
150
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
151
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
150
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
151
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
152
152
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
153
153
  if !ignoremaster and MU.mu_public_ip
154
154
  filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
@@ -159,7 +159,7 @@ module MU
159
159
  ["forwarding_rule", "region_backend_service"].each { |type|
160
160
  MU::Cloud::Google.compute(credentials: credentials).delete(
161
161
  type,
162
- flags["project"],
162
+ flags["habitat"],
163
163
  region,
164
164
  noop
165
165
  )
@@ -170,7 +170,7 @@ module MU
170
170
  ["global_forwarding_rule", "target_http_proxy", "target_https_proxy", "url_map", "backend_service", "health_check", "http_health_check", "https_health_check"].each { |type|
171
171
  MU::Cloud::Google.compute(credentials: credentials).delete(
172
172
  type,
173
- flags["project"],
173
+ flags["habitat"],
174
174
  nil,
175
175
  noop
176
176
  )
@@ -731,25 +731,34 @@ module MU
731
731
  bindings[scopetype].each_pair { |scope_id, entity_types|
732
732
  # If we've been given a habitat filter, skip over bindings
733
733
  # that don't match it.
734
- if scopetype == "projects" and args[:habitats] and
735
- !args[:habitats].empty? and
736
- !args[:habitats].include?(scope_id)
737
- next
734
+ if scopetype == "projects"
735
+ if (args[:habitats] and !args[:habitats].empty? and
736
+ !args[:habitats].include?(scope_id)) or
737
+ !MU::Cloud::Google.listHabitats(@credentials).include?(scope_id)
738
+ next
739
+ end
738
740
  end
739
741
 
740
742
  entity_types.each_pair { |entity_type, entities|
741
743
  mu_entitytype = (entity_type == "serviceAccount" ? "user" : entity_type)+"s"
742
744
  entities.each { |entity|
745
+ foreign = if entity_type == "serviceAccount" and entity.match(/@(.*?)\.iam\.gserviceaccount\.com/)
746
+ !MU::Cloud::Google.listHabitats(@credentials).include?(Regexp.last_match[1])
747
+ end
743
748
  entity_ref = if entity_type == "organizations"
744
749
  { "id" => ((org == my_org.name and @config['credentials']) ? @config['credentials'] : org) }
745
750
  elsif entity_type == "domain"
746
751
  { "id" => entity }
747
752
  else
748
- MU::Config::Ref.get(
749
- id: entity,
750
- cloud: "Google",
751
- type: mu_entitytype
752
- )
753
+ if foreign
754
+ { "id" => entity }
755
+ else
756
+ MU::Config::Ref.get(
757
+ id: entity,
758
+ cloud: "Google",
759
+ type: mu_entitytype
760
+ )
761
+ end
753
762
  end
754
763
  refmap ||= {}
755
764
  refmap[entity_ref] ||= {}
@@ -1016,7 +1016,6 @@ next if !create
1016
1016
  item: @config['windows_auth_vault']['item'],
1017
1017
  field: @config["windows_auth_vault"]["password_field"]
1018
1018
  )
1019
- MU.log "RETURNINATING FROM CACHE", MU::WARN, details: win_admin_password
1020
1019
  return win_admin_password if win_admin_password
1021
1020
  rescue MU::Groomer::MuNoSuchSecret, MU::Groomer::RunError
1022
1021
  end
@@ -1276,8 +1275,8 @@ MU.log "RETURNINATING FROM CACHE", MU::WARN, details: win_admin_password
1276
1275
  # @param region [String]: The cloud provider region
1277
1276
  # @return [void]
1278
1277
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1279
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
1280
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
1278
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
1279
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
1281
1280
 
1282
1281
  # XXX make damn sure MU.deploy_id is set
1283
1282
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
@@ -1288,13 +1287,12 @@ MU.log "RETURNINATING FROM CACHE", MU::WARN, details: win_admin_password
1288
1287
  MU::Cloud::Google.listAZs(region).each { |az|
1289
1288
  disks = []
1290
1289
  resp = MU::Cloud::Google.compute(credentials: credentials).list_instances(
1291
- flags["project"],
1290
+ flags["habitat"],
1292
1291
  az,
1293
1292
  filter: filter
1294
1293
  )
1295
1294
  if !resp.items.nil? and resp.items.size > 0
1296
1295
  resp.items.each { |instance|
1297
- saname = instance.tags.items.first.gsub(/[^a-z]/, "") # XXX this nonsense again
1298
1296
  MU.log "Terminating instance #{instance.name}"
1299
1297
  if !instance.disks.nil? and instance.disks.size > 0
1300
1298
  instance.disks.each { |disk|
@@ -1302,17 +1300,21 @@ MU.log "RETURNINATING FROM CACHE", MU::WARN, details: win_admin_password
1302
1300
  }
1303
1301
  end
1304
1302
  MU::Cloud::Google.compute(credentials: credentials).delete_instance(
1305
- flags["project"],
1303
+ flags["habitat"],
1306
1304
  az,
1307
1305
  instance.name
1308
1306
  ) if !noop
1309
- MU.log "Removing service account #{saname}"
1310
- begin
1311
- MU::Cloud::Google.iam(credentials: credentials).delete_project_service_account(
1312
- "projects/#{flags["project"]}/serviceAccounts/#{saname}@#{flags["project"]}.iam.gserviceaccount.com"
1313
- ) if !noop
1314
- rescue ::Google::Apis::ClientError => e
1315
- raise e if !e.message.match(/^notFound: /)
1307
+ if instance.service_accounts
1308
+ instance.service_accounts.each { |sa|
1309
+ MU.log "Removing service account #{sa.email}"
1310
+ begin
1311
+ MU::Cloud::Google.iam(credentials: credentials).delete_project_service_account(
1312
+ "projects/#{flags["habitat"]}/serviceAccounts/#{sa.email}"
1313
+ ) if !noop
1314
+ rescue ::Google::Apis::ClientError => e
1315
+ raise e if !e.message.match(/^notFound: /)
1316
+ end
1317
+ }
1316
1318
  end
1317
1319
  # XXX wait-loop on pending?
1318
1320
  # pp deletia
@@ -1325,7 +1327,7 @@ MU.log "RETURNINATING FROM CACHE", MU::WARN, details: win_admin_password
1325
1327
  # XXX honor snapshotting
1326
1328
  MU::Cloud::Google.compute(credentials: credentials).delete(
1327
1329
  "disk",
1328
- flags["project"],
1330
+ flags["habitat"],
1329
1331
  az,
1330
1332
  noop
1331
1333
  ) if !noop
@@ -432,8 +432,8 @@ end
432
432
  # @param region [String]: The cloud provider region
433
433
  # @return [void]
434
434
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
435
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
436
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
435
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
436
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
437
437
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
438
438
  if !ignoremaster and MU.mu_public_ip
439
439
  filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
@@ -444,7 +444,7 @@ end
444
444
  ["region_autoscaler", "region_instance_group_manager"].each { |type|
445
445
  MU::Cloud::Google.compute(credentials: credentials).delete(
446
446
  type,
447
- flags["project"],
447
+ flags["habitat"],
448
448
  region,
449
449
  noop
450
450
  )
@@ -452,7 +452,7 @@ end
452
452
  else
453
453
  MU::Cloud::Google.compute(credentials: credentials).delete(
454
454
  "instance_template",
455
- flags["project"],
455
+ flags["habitat"],
456
456
  noop
457
457
  )
458
458
  end
@@ -281,9 +281,9 @@ module MU
281
281
  end
282
282
  end
283
283
 
284
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
284
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
285
285
  resp = MU::Cloud::Google.iam(credentials: credentials).list_project_service_accounts(
286
- "projects/"+flags["project"]
286
+ "projects/"+flags["habitat"]
287
287
  )
288
288
 
289
289
  if resp and resp.accounts and MU.deploy_id
@@ -113,7 +113,7 @@ module MU
113
113
  # Describe this VPC
114
114
  # @return [Hash]
115
115
  def notify
116
- base = MU.structToHash(cloud_desc)
116
+ base = MU.structToHash(cloud_desc, stringify_keys: true)
117
117
  base["cloud_id"] = @cloud_id
118
118
  base["project_id"] = habitat_id
119
119
  base.merge!(@config.to_h)
@@ -301,14 +301,10 @@ end
301
301
  @deploy.deployment["vpcs"][@config['name']]["subnets"] and
302
302
  @deploy.deployment["vpcs"][@config['name']]["subnets"].size > 0
303
303
  @deploy.deployment["vpcs"][@config['name']]["subnets"].each { |desc|
304
- subnet = {}
305
- subnet["ip_block"] = desc['ip_block']
306
- subnet["name"] = desc["name"]
304
+ subnet = desc.clone
307
305
  subnet['mu_name'] = @config['scrub_mu_isms'] ? @cloud_id+subnet['name'].downcase : MU::Cloud::Google.nameStr(@deploy.getResourceName(subnet['name'], max_length: 61))
308
- subnet["cloud_id"] = desc['cloud_id']
309
306
  subnet["cloud_id"] ||= desc['self_link'].gsub(/.*?\/([^\/]+)$/, '\1')
310
307
  subnet["cloud_id"] ||= subnet['mu_name']
311
- subnet['az'] = desc["az"]
312
308
  subnet['az'] ||= desc["region"].gsub(/.*?\/([^\/]+)$/, '\1')
313
309
  @subnets << MU::Cloud::Google::VPC::Subnet.new(self, subnet, precache_description: false)
314
310
  }
@@ -542,15 +538,15 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
542
538
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
543
539
  # @return [void]
544
540
  def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
545
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
546
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
541
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
542
+ return if !MU::Cloud::Google::Habitat.isLive?(flags["habitat"], credentials)
547
543
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
548
544
  if !ignoremaster and MU.mu_public_ip
549
545
  filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
550
546
  end
551
547
  MU.log "Placeholder: Google VPC artifacts do not support labels, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: filter
552
548
 
553
- purge_subnets(noop, project: flags['project'], credentials: credentials)
549
+ purge_subnets(noop, project: flags['habitat'], credentials: credentials)
554
550
  ["route", "network"].each { |type|
555
551
  # XXX tagged routes aren't showing up in list, and the networks that own them
556
552
  # fail to delete silently
@@ -559,7 +555,7 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
559
555
  begin
560
556
  MU::Cloud::Google.compute(credentials: credentials).delete(
561
557
  type,
562
- flags["project"],
558
+ flags["habitat"],
563
559
  nil,
564
560
  noop
565
561
  )
@@ -569,13 +565,13 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
569
565
  MU.log e.message, MU::WARN
570
566
  if e.message.match(/Failed to delete network (.+)/)
571
567
  network_name = Regexp.last_match[1]
572
- fwrules = MU::Cloud::Google::FirewallRule.find(project: flags['project'], credentials: credentials)
568
+ fwrules = MU::Cloud::Google::FirewallRule.find(project: flags['habitat'], credentials: credentials)
573
569
  fwrules.reject! { |_name, desc|
574
570
  !desc.network.match(/.*?\/#{Regexp.quote(network_name)}$/)
575
571
  }
576
572
  fwrules.keys.each { |name|
577
573
  MU.log "Attempting to delete firewall rule #{name} so that VPC #{network_name} can be removed", MU::NOTICE
578
- MU::Cloud::Google.compute(credentials: credentials).delete_firewall(flags['project'], name)
574
+ MU::Cloud::Google.compute(credentials: credentials).delete_firewall(flags['habitat'], name)
579
575
  }
580
576
  end
581
577
  end
@@ -1120,7 +1116,7 @@ MU.log "ROUTES TO #{target_instance.name}", MU::WARN, details: resp
1120
1116
  # Describe this VPC Subnet
1121
1117
  # @return [Hash]
1122
1118
  def notify
1123
- MU.structToHash(cloud_desc)
1119
+ MU.structToHash(cloud_desc, stringify_keys: true)
1124
1120
  end
1125
1121
 
1126
1122
  # Return the +self_link+ to this subnet
@@ -77,7 +77,7 @@ module MU
77
77
  if config.is_a?(Hash)
78
78
  newhash = {}
79
79
  config.each_pair { |key, val|
80
- next if remove_runtime_keys and key.match(/^#MU_/)
80
+ next if remove_runtime_keys and (key.nil? or key.match(/^#MU_/))
81
81
  next if val.is_a?(Array) and val.empty?
82
82
  newhash[key] = self.manxify(val, remove_runtime_keys: remove_runtime_keys)
83
83
  }
@@ -104,6 +104,11 @@ module MU
104
104
  cluster["min_size"] ||= [cluster["instance_count"], cluster["min_size"]].reject { |c| c.nil? }.min
105
105
  end
106
106
 
107
+ if cluster['kubernetes_resources'] and !MU::Master.kubectl
108
+ MU.log "Cannot apply kubernetes resources without a working kubectl executable", MU::ERR
109
+ ok = false
110
+ end
111
+
107
112
  ok
108
113
  end
109
114
 
@@ -239,7 +239,7 @@ $CONFIGURABLES
239
239
 
240
240
  if class_hierarchy.size == 1
241
241
 
242
- _shortclass, cfg_name, cfg_plural, _classname = MU::Cloud.getResourceNames(name)
242
+ _shortclass, cfg_name, cfg_plural, _classname = MU::Cloud.getResourceNames(name, false)
243
243
  if cfg_name
244
244
  example_path = MU.myRoot+"/modules/mu/config/"+cfg_name+".yml"
245
245
  if File.exist?(example_path)
@@ -255,7 +255,7 @@ module MU
255
255
  if @obj
256
256
  @deploy_id ||= @obj.deploy_id
257
257
  @id ||= @obj.cloud_id
258
- @name ||= @obj.config['name']
258
+ @name ||= @obj.config['name'] if @obj.config
259
259
  return @obj
260
260
  end
261
261
 
@@ -266,6 +266,7 @@ module MU
266
266
  @mommacat ||= mommacat
267
267
  @obj.intoDeploy(@mommacat) # make real sure these are set
268
268
  @deploy_id ||= mommacat.deploy_id
269
+
269
270
  if !@name
270
271
  if @obj.config and @obj.config['name']
271
272
  @name = @obj.config['name']
@@ -283,6 +284,7 @@ end
283
284
  end
284
285
 
285
286
  if !@obj and !(@cloud == "Google" and @id and @type == "users" and MU::Cloud::Google::User.cannedServiceAcctName?(@id)) and !shallow
287
+ try_deploy_id = @deploy_id
286
288
 
287
289
  begin
288
290
  hab_arg = if @habitat.nil?
@@ -300,22 +302,26 @@ end
300
302
  @type,
301
303
  name: @name,
302
304
  cloud_id: @id,
303
- deploy_id: @deploy_id,
305
+ deploy_id: try_deploy_id,
304
306
  region: @region,
305
307
  habitats: hab_arg,
306
308
  credentials: @credentials,
307
309
  dummy_ok: (["habitats", "folders", "users", "groups", "vpcs"].include?(@type))
308
310
  )
309
311
  @obj ||= found.first if found
312
+ rescue MU::MommaCat::MultipleMatches => e
313
+ if try_deploy_id.nil? and MU.deploy_id
314
+ MU.log "Attempting to narrow down #{@cloud} #{@type} to #{MU.deploy_id}", MU::NOTICE
315
+ try_deploy_id = MU.deploy_id
316
+ retry
317
+ else
318
+ raise e
319
+ end
310
320
  rescue ThreadError => e
311
321
  # Sometimes MommaCat calls us in a potential deadlock situation;
312
322
  # don't be the cause of a fatal error if so, we don't need this
313
323
  # object that badly.
314
324
  raise e if !e.message.match(/recursive locking/)
315
- rescue SystemExit
316
- # XXX this is temporary, to cope with some debug stuff that's in findStray
317
- # for the nonce
318
- return
319
325
  end
320
326
  end
321
327