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
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -51,7 +51,7 @@ Signal.trap("URG") do
51
51
  end
52
52
 
53
53
  begin
54
- MU::MommaCat.syncMonitoringConfig(false)
54
+ MU::Master.syncMonitoringConfig(false)
55
55
  rescue StandardError => e
56
56
  MU.log e.inspect, MU::ERR, details: e.backtrace
57
57
  # ...but don't die!
@@ -273,8 +273,8 @@ module MU
273
273
  # Wrapper class for fatal Exceptions. Gives our internals something to
274
274
  # inherit that will log an error message appropriately before bubbling up.
275
275
  class MuError < StandardError
276
- def initialize(message = nil)
277
- MU.log message, MU::ERR, details: caller[2] if !message.nil?
276
+ def initialize(message = nil, silent: false)
277
+ MU.log message, MU::ERR, details: caller[2] if !message.nil? and !silent
278
278
  if MU.verbosity == MU::Logger::SILENT
279
279
  super ""
280
280
  else
@@ -286,8 +286,8 @@ module MU
286
286
  # Wrapper class for temporary Exceptions. Gives our internals something to
287
287
  # inherit that will log a notice message appropriately before bubbling up.
288
288
  class MuNonFatal < StandardError
289
- def initialize(message = nil)
290
- MU.log message, MU::NOTICE if !message.nil?
289
+ def initialize(message = nil, silent: false)
290
+ MU.log message, MU::NOTICE if !message.nil? and !silent
291
291
  if MU.verbosity == MU::Logger::SILENT
292
292
  super ""
293
293
  else
@@ -598,9 +598,10 @@ module MU
598
598
  end
599
599
 
600
600
  # Shortcut to invoke {MU::Logger#log}
601
- def self.log(msg, level = MU::INFO, details: nil, html: false, verbosity: nil, color: true)
601
+ def self.log(msg, level = MU::INFO, shorthand_details = nil, details: nil, html: false, verbosity: nil, color: true)
602
602
  return if (level == MU::DEBUG and verbosity and verbosity <= MU::Logger::LOUD)
603
603
  return if verbosity and verbosity == MU::Logger::SILENT
604
+ details ||= shorthand_details
604
605
 
605
606
  if (level == MU::ERR or
606
607
  level == MU::WARN or
@@ -30,7 +30,7 @@ module MU
30
30
  :omnibus => "Jam everything into one monolothic configuration"
31
31
  }
32
32
 
33
- def initialize(clouds: MU::Cloud.supportedClouds, types: MU::Cloud.resource_types.keys, parent: nil, billing: nil, sources: nil, credentials: nil, group_by: :logical, savedeploys: false, diff: false, habitats: [])
33
+ def initialize(clouds: MU::Cloud.supportedClouds, types: MU::Cloud.resource_types.keys, parent: nil, billing: nil, sources: nil, credentials: nil, group_by: :logical, savedeploys: false, diff: false, habitats: [], scrub_mu_isms: false)
34
34
  @scraped = {}
35
35
  @clouds = clouds
36
36
  @types = types
@@ -45,6 +45,7 @@ module MU
45
45
  @diff = diff
46
46
  @habitats = habitats
47
47
  @habitats ||= []
48
+ @scrub_mu_isms = scrub_mu_isms
48
49
  end
49
50
 
50
51
  # Walk cloud providers with available credentials to discover resources
@@ -65,6 +66,11 @@ module MU
65
66
  cloudclass.listCredentials.each { |credset|
66
67
  next if @sources and !@sources.include?(credset)
67
68
 
69
+ cfg = cloudclass.credConfig(credset)
70
+ if cfg and cfg['restrict_to_habitats']
71
+ cfg['restrict_to_habitats'] << cfg['project'] if cfg['project']
72
+ end
73
+
68
74
  if @parent
69
75
  # TODO handle different inputs (cloud_id, etc)
70
76
  # TODO do something about vague matches
@@ -101,15 +107,21 @@ module MU
101
107
  allow_multi: true,
102
108
  habitats: @habitats.dup,
103
109
  dummy_ok: true,
104
- debug: false,
105
- flags: { "skip_provider_owned" => true }
110
+ skip_provider_owned: true,
111
+ # debug: false#,
106
112
  )
107
113
 
108
114
 
109
115
  if found and found.size > 0
116
+ if resclass.cfg_plural == "habitats"
117
+ found.reject! { |h| !cloudclass.listHabitats(credset).include?(h) }
118
+ end
110
119
  MU.log "Found #{found.size.to_s} raw #{resclass.cfg_plural} in #{cloud}"
111
120
  @scraped[type] ||= {}
112
121
  found.each { |obj|
122
+ if obj.habitat and !cloudclass.listHabitats(credset).include?(obj.habitat)
123
+ next
124
+ end
113
125
  # XXX apply any filters (e.g. MU-ID tags)
114
126
  @scraped[type][obj.cloud_id] = obj
115
127
  }
@@ -190,6 +202,9 @@ module MU
190
202
 
191
203
  groupings.each_pair { |appname, types|
192
204
  bok = { "appname" => prefix+appname }
205
+ if @scrub_mu_isms
206
+ bok["scrub_mu_isms"] = true
207
+ end
193
208
  if @target_creds
194
209
  bok["credentials"] = @target_creds
195
210
  end
@@ -333,7 +348,7 @@ module MU
333
348
  deletia = []
334
349
  schema_chunk["properties"].each_pair { |key, subschema|
335
350
  next if !conf_chunk[key]
336
- shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(key)
351
+ shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(key, false)
337
352
 
338
353
  if subschema["default_if"]
339
354
  subschema["default_if"].each { |cond|
@@ -30,6 +30,8 @@ module MU
30
30
  @onlycloud = false
31
31
  @skipcloud = false
32
32
 
33
+ TYPES_IN_ORDER = ["Collection", "Endpoint", "Function", "ServerPool", "ContainerCluster", "SearchDomain", "Server", "MsgQueue", "Database", "CacheCluster", "StoragePool", "LoadBalancer", "NoSQLDB", "FirewallRule", "Alarm", "Notifier", "Log", "VPC", "Role", "Group", "User", "Bucket", "DNSZone", "Collection"]
34
+
33
35
  # Purge all resources associated with a deployment.
34
36
  # @param deploy_id [String]: The identifier of the deployment to remove (typically seen in the MU-ID tag on a resource).
35
37
  # @param noop [Boolean]: Do not delete resources, merely list what would be deleted.
@@ -54,14 +56,7 @@ module MU
54
56
  @noop = true
55
57
  end
56
58
 
57
- if MU.mu_user != "mu"
58
- MU.setVar("dataDir", Etc.getpwnam(MU.mu_user).dir+"/.mu/var")
59
- else
60
- MU.setVar("dataDir", MU.mainDataDir)
61
- end
62
-
63
-
64
- types_in_order = ["Collection", "Endpoint", "Function", "ServerPool", "ContainerCluster", "SearchDomain", "Server", "MsgQueue", "Database", "CacheCluster", "StoragePool", "LoadBalancer", "NoSQLDB", "FirewallRule", "Alarm", "Notifier", "Log", "VPC", "Role", "Group", "User", "Bucket", "DNSZone", "Collection"]
59
+ MU.setVar("dataDir", (MU.mu_user == "mu" ? MU.mainDataDir : Etc.getpwnam(MU.mu_user).dir+"/.mu/var"))
65
60
 
66
61
  # Load up our deployment metadata
67
62
  if !mommacat.nil?
@@ -82,172 +77,24 @@ module MU
82
77
  rescue StandardError => e
83
78
  MU.log "Can't load a deploy record for #{deploy_id} (#{e.inspect}), cleaning up resources by guesswork", MU::WARN, details: e.backtrace
84
79
  MU.setVar("deploy_id", deploy_id)
85
-
86
80
  end
87
81
  end
88
82
 
89
- regionsused = @mommacat.regionsUsed if @mommacat
90
- credsused = @mommacat.credsUsed if @mommacat
91
- habitatsused = @mommacat.habitatsUsed if @mommacat
83
+ @regionsused = @mommacat.regionsUsed if @mommacat
84
+ @credsused = @mommacat.credsUsed if @mommacat
85
+ @habitatsused = @mommacat.habitatsUsed if @mommacat
92
86
 
93
87
  if !@skipcloud
94
- creds = {}
95
- MU::Cloud.availableClouds.each { |cloud|
96
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
97
- if $MU_CFG[cloud.downcase] and $MU_CFG[cloud.downcase].size > 0
98
- creds[cloud] ||= {}
99
- cloudclass.listCredentials.each { |credset|
100
- next if credsets and credsets.size > 0 and !credsets.include?(credset)
101
- next if credsused and credsused.size > 0 and !credsused.include?(credset)
102
- MU.log "Will scan #{cloud} with credentials #{credset}"
103
- creds[cloud][credset] = cloudclass.listRegions(credentials: credset)
104
- }
105
- else
106
- if cloudclass.hosted?
107
- creds[cloud] ||= {}
108
- creds[cloud]["#default"] = cloudclass.listRegions
109
- end
110
- end
111
- }
88
+ creds = listUsedCredentials(credsets)
112
89
 
113
- parent_thread_id = Thread.current.object_id
114
90
  cloudthreads = []
115
- keyname = "deploy-#{MU.deploy_id}"
91
+
116
92
  had_failures = false
117
93
 
118
94
  creds.each_pair { |provider, credsets_outer|
119
95
  cloudthreads << Thread.new(provider, credsets_outer) { |cloud, credsets_inner|
120
- MU.dupGlobals(parent_thread_id)
121
96
  Thread.abort_on_exception = false
122
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
123
- habitatclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get("Habitat")
124
- credsets_inner.each_pair { |credset, acct_regions|
125
- next if credsused and !credsused.include?(credset)
126
- global_vs_region_semaphore = Mutex.new
127
- global_done = {}
128
- regionthreads = []
129
- acct_regions.each { |r|
130
- if regionsused
131
- if regionsused.size > 0
132
- next if !regionsused.include?(r)
133
- else
134
- next if r != cloudclass.myRegion(credset)
135
- end
136
- end
137
- if regions and !regions.empty?
138
- next if !regions.include?(r)
139
- MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{r}...", MU::NOTICE
140
- end
141
- regionthreads << Thread.new {
142
- MU.dupGlobals(parent_thread_id)
143
- Thread.abort_on_exception = false
144
- MU.setVar("curRegion", r)
145
- projects = []
146
- if habitats
147
- projects = habitats
148
- else
149
- if $MU_CFG and $MU_CFG[cloud.downcase] and
150
- $MU_CFG[cloud.downcase][credset] and
151
- $MU_CFG[cloud.downcase][credset]["project"]
152
- # XXX GCP credential schema needs an array for projects
153
- projects << $MU_CFG[cloud.downcase][credset]["project"]
154
- end
155
- begin
156
- projects.concat(cloudclass.listProjects(credset))
157
- rescue NoMethodError
158
- end
159
- end
160
-
161
- if projects == []
162
- projects << "" # dummy
163
- MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{r}", MU::NOTICE
164
- end
165
- projects.uniq!
166
-
167
- # We do these in an order that unrolls dependent resources
168
- # sensibly, and we hit :Collection twice because AWS
169
- # CloudFormation sometimes fails internally.
170
- projectthreads = []
171
- projects.each { |project|
172
- if habitats and !habitats.empty? and project != ""
173
- next if !habitats.include?(project)
174
- end
175
- if habitatsused and !habitatsused.empty? and project != ""
176
- next if !habitatsused.include?(project)
177
- end
178
- next if !habitatclass.isLive?(project, credset)
179
-
180
- projectthreads << Thread.new {
181
- MU.dupGlobals(parent_thread_id)
182
- MU.setVar("curRegion", r)
183
- Thread.abort_on_exception = false
184
- if project != ""
185
- MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{r}, project #{project}", MU::NOTICE
186
- end
187
-
188
- MU.dupGlobals(parent_thread_id)
189
- flags = {
190
- "project" => project,
191
- "onlycloud" => @onlycloud,
192
- "skipsnapshots" => @skipsnapshots,
193
- }
194
- types_in_order.each { |t|
195
- begin
196
- skipme = false
197
- global_vs_region_semaphore.synchronize {
198
- MU::Cloud.loadCloudType(cloud, t)
199
- if Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(t).isGlobal?
200
- global_done[project] ||= []
201
- if !global_done[project].include?(t)
202
- global_done[project] << t
203
- flags['global'] = true
204
- else
205
- skipme = true
206
- end
207
- end
208
- }
209
- next if skipme
210
- rescue MU::Cloud::MuDefunctHabitat, MU::Cloud::MuCloudResourceNotImplemented => e
211
- next
212
- rescue MU::MuError, NoMethodError => e
213
- MU.log "While checking mu/clouds/#{cloud.downcase}/#{cloudclass.cfg_name} for global-ness in cleanup: "+e.message, MU::WARN
214
- next
215
- rescue ::Aws::EC2::Errors::AuthFailure, ::Google::Apis::ClientError => e
216
- MU.log e.message+" in "+r, MU::ERR
217
- next
218
- end
219
-
220
- begin
221
- if !self.call_cleanup(t, credset, cloud, flags, r)
222
- had_failures = true
223
- end
224
- rescue MU::Cloud::MuDefunctHabitat, MU::Cloud::MuCloudResourceNotImplemented => e
225
- next
226
- end
227
- }
228
- } # types_in_order.each { |t|
229
- } # projects.each { |project|
230
- projectthreads.each do |t|
231
- t.join
232
- end
233
-
234
- # XXX move to MU::AWS
235
- if cloud == "AWS"
236
- resp = MU::Cloud::AWS.ec2(region: r, credentials: credset).describe_key_pairs(
237
- filters: [{name: "key-name", values: [keyname]}]
238
- )
239
- resp.data.key_pairs.each { |keypair|
240
- MU.log "Deleting key pair #{keypair.key_name} from #{r}"
241
- MU::Cloud::AWS.ec2(region: r, credentials: credset).delete_key_pair(key_name: keypair.key_name) if !@noop
242
- }
243
- end
244
- } # regionthreads << Thread.new {
245
- } # acct_regions.each { |r|
246
- regionthreads.each do |t|
247
- t.join
248
- end
249
-
250
- } # credsets.each_pair { |credset, acct_regions|
97
+ cleanCloud(cloud, habitats, regions, credsets_inner)
251
98
  } # cloudthreads << Thread.new(provider, credsets) { |cloud, credsets_outer|
252
99
  cloudthreads.each do |t|
253
100
  t.join
@@ -259,22 +106,19 @@ module MU
259
106
  # once they're all done.
260
107
  creds.each_pair { |provider, credsets_inner|
261
108
  credsets_inner.keys.each { |credset|
262
- next if credsused and !credsused.include?(credset)
109
+ next if @credsused and !@credsused.include?(credset)
263
110
  ["Habitat", "Folder"].each { |t|
264
111
  flags = {
265
112
  "onlycloud" => @onlycloud,
266
113
  "skipsnapshots" => @skipsnapshots
267
114
  }
268
- if !self.call_cleanup(t, credset, provider, flags, nil)
115
+ if !call_cleanup(t, credset, provider, flags, nil)
269
116
  had_failures = true
270
117
  end
271
118
  }
272
119
  }
273
120
  }
274
121
 
275
- MU::Cloud::Google.removeDeploySecretsAndRoles(MU.deploy_id)
276
- # XXX port AWS equivalent behavior and add a MU::Cloud wrapper
277
-
278
122
  creds.each_pair { |provider, credsets_inner|
279
123
  cloudclass = Object.const_get("MU").const_get("Cloud").const_get(provider)
280
124
  credsets_inner.keys.each { |c|
@@ -284,44 +128,16 @@ module MU
284
128
  end
285
129
 
286
130
  # Scrub any residual Chef records with matching tags
287
- if !@onlycloud and (@mommacat.nil? or @mommacat.numKittens(types: ["Server", "ServerPool"]) > 0) and !(Gem.paths and Gem.paths.home and !Dir.exist?("/opt/mu/lib"))
288
- begin
289
- MU::Groomer::Chef.loadChefLib
290
- if File.exist?(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
291
- Chef::Config.from_file(Etc.getpwuid(Process.uid).dir+"/.chef/knife.rb")
292
- end
293
- deadnodes = []
294
- Chef::Config[:environment] = MU.environment
295
- q = Chef::Search::Query.new
296
- begin
297
- q.search("node", "tags_MU-ID:#{MU.deploy_id}").each { |item|
298
- next if item.is_a?(Integer)
299
- item.each { |node|
300
- deadnodes << node.name
301
- }
302
- }
303
- rescue Net::HTTPServerException
304
- end
305
-
306
- begin
307
- q.search("node", "name:#{MU.deploy_id}-*").each { |item|
308
- next if item.is_a?(Integer)
309
- item.each { |node|
310
- deadnodes << node.name
311
- }
312
- }
313
- rescue Net::HTTPServerException
314
- end
315
- MU.log "Missed some Chef resources in node cleanup, purging now", MU::NOTICE if deadnodes.size > 0
316
- deadnodes.uniq.each { |node|
317
- MU::Groomer::Chef.cleanup(node, [], noop)
318
- }
319
- rescue LoadError
320
- end
131
+ if !@onlycloud and (@mommacat.nil? or @mommacat.numKittens(types: ["Server", "ServerPool"]) > 0) and !@noop
132
+ MU.supportedGroomers.each { |g|
133
+ groomer = MU::Groomer.loadGroomer(g)
134
+ groomer.cleanup(MU.deploy_id, @noop)
135
+ }
321
136
  end
322
137
 
323
138
  if had_failures
324
139
  MU.log "Had cleanup failures, exiting", MU::ERR
140
+ File.unlink("#{deploy_dir}/.cleanup") if !@noop
325
141
  exit 1
326
142
  end
327
143
 
@@ -329,99 +145,174 @@ module MU
329
145
  @mommacat.purge!
330
146
  end
331
147
 
332
- myhome = Etc.getpwuid(Process.uid).dir
333
- sshdir = "#{myhome}/.ssh"
334
- sshconf = "#{sshdir}/config"
335
- ssharchive = "#{sshdir}/archive"
336
-
337
- Dir.mkdir(sshdir, 0700) if !Dir.exist?(sshdir) and !@noop
338
- Dir.mkdir(ssharchive, 0700) if !Dir.exist?(ssharchive) and !@noop
148
+ if !@onlycloud
149
+ MU::Master.purgeDeployFromSSH(MU.deploy_id, noop: @noop)
150
+ end
339
151
 
340
- keyname = "deploy-#{MU.deploy_id}"
341
- if File.exist?("#{sshdir}/#{keyname}")
342
- MU.log "Moving #{sshdir}/#{keyname} to #{ssharchive}/#{keyname}"
343
- if !@noop
344
- File.rename("#{sshdir}/#{keyname}", "#{ssharchive}/#{keyname}")
345
- end
152
+ if !@noop and !@skipcloud and (@mommacat.nil? or @mommacat.numKittens(types: ["Server", "ServerPool"]) > 0)
153
+ # MU::Master.syncMonitoringConfig
346
154
  end
347
155
 
348
- if File.exist?(sshconf) and File.open(sshconf).read.match(/\/deploy\-#{MU.deploy_id}$/)
349
- MU.log "Expunging #{MU.deploy_id} from #{sshconf}"
350
- if !@noop
351
- FileUtils.copy(sshconf, "#{ssharchive}/config-#{MU.deploy_id}")
352
- File.open(sshconf, File::CREAT|File::RDWR, 0600) { |f|
353
- f.flock(File::LOCK_EX)
354
- newlines = Array.new
355
- delete_block = false
356
- f.readlines.each { |line|
357
- if line.match(/^Host #{MU.deploy_id}\-/)
358
- delete_block = true
359
- elsif line.match(/^Host /)
360
- delete_block = false
361
- end
362
- newlines << line if !delete_block
363
- }
364
- f.rewind
365
- f.truncate(0)
366
- f.puts(newlines)
367
- f.flush
368
- f.flock(File::LOCK_UN)
156
+ end
157
+
158
+ def self.listUsedCredentials(credsets)
159
+ creds = {}
160
+ MU::Cloud.availableClouds.each { |cloud|
161
+ cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
162
+ if $MU_CFG[cloud.downcase] and $MU_CFG[cloud.downcase].size > 0
163
+ creds[cloud] ||= {}
164
+ cloudclass.listCredentials.each { |credset|
165
+ next if credsets and credsets.size > 0 and !credsets.include?(credset)
166
+ next if @credsused and @credsused.size > 0 and !@credsused.include?(credset)
167
+ MU.log "Will scan #{cloud} with credentials #{credset}"
168
+ creds[cloud][credset] = cloudclass.listRegions(credentials: credset)
369
169
  }
170
+ else
171
+ if cloudclass.hosted?
172
+ creds[cloud] ||= {}
173
+ creds[cloud]["#default"] = cloudclass.listRegions
174
+ end
370
175
  end
371
- end
372
-
373
- # XXX refactor with above? They're similar, ish.
374
- hostsfile = "/etc/hosts"
375
- if File.open(hostsfile).read.match(/ #{MU.deploy_id}\-/)
376
- if Process.uid == 0
377
- MU.log "Expunging traces of #{MU.deploy_id} from #{hostsfile}"
378
- if !@noop
379
- FileUtils.copy(hostsfile, "#{hostsfile}.cleanup-#{deploy_id}")
380
- File.open(hostsfile, File::CREAT|File::RDWR, 0644) { |f|
381
- f.flock(File::LOCK_EX)
382
- newlines = Array.new
383
- f.readlines.each { |line|
384
- newlines << line if !line.match(/ #{MU.deploy_id}\-/)
385
- }
386
- f.rewind
387
- f.truncate(0)
388
- f.puts(newlines)
389
- f.flush
390
- f.flock(File::LOCK_UN)
391
- }
176
+ }
177
+ creds
178
+ end
179
+ private_class_method :listUsedCredentials
180
+
181
+ def self.cleanCloud(cloud, habitats, regions, credsets)
182
+ cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
183
+ credsets.each_pair { |credset, acct_regions|
184
+ next if @credsused and !@credsused.include?(credset)
185
+ global_vs_region_semaphore = Mutex.new
186
+ global_done = {}
187
+ regionthreads = []
188
+ acct_regions.each { |r|
189
+ if @regionsused
190
+ if @regionsused.size > 0
191
+ next if !@regionsused.include?(r)
192
+ else
193
+ next if r != cloudclass.myRegion(credset)
194
+ end
392
195
  end
393
- else
394
- MU.log "Residual /etc/hosts entries for #{MU.deploy_id} must be removed by root user", MU::WARN
196
+ if regions and !regions.empty?
197
+ next if !regions.include?(r)
198
+ MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{r}...", MU::NOTICE
199
+ end
200
+ regionthreads << Thread.new {
201
+ Thread.abort_on_exception = false
202
+ MU.setVar("curRegion", r)
203
+ cleanRegion(cloud, credset, r, global_vs_region_semaphore, global_done, habitats)
204
+ } # regionthreads << Thread.new {
205
+ } # acct_regions.each { |r|
206
+ regionthreads.each do |t|
207
+ t.join
395
208
  end
396
- end
209
+ }
210
+ end
211
+ private_class_method :cleanCloud
397
212
 
398
- if !@noop and !@skipcloud
399
- if $MU_CFG['aws'] and $MU_CFG['aws']['account_number']
400
- MU::Cloud::AWS.s3(region: MU.myRegion).delete_object(
401
- bucket: MU.adminBucketName,
402
- key: "#{MU.deploy_id}-secret"
403
- )
213
+ def self.cleanRegion(cloud, credset, region, global_vs_region_semaphore, global_done, habitats)
214
+ had_failures = false
215
+ cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
216
+ habitatclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get("Habitat")
217
+
218
+ projects = []
219
+ if habitats
220
+ projects = habitats
221
+ else
222
+ if $MU_CFG and $MU_CFG[cloud.downcase] and
223
+ $MU_CFG[cloud.downcase][credset] and
224
+ $MU_CFG[cloud.downcase][credset]["project"]
225
+ # XXX GCP credential schema needs an array for projects
226
+ projects << $MU_CFG[cloud.downcase][credset]["project"]
404
227
  end
405
- if $MU_CFG['google'] and $MU_CFG['google']['project']
406
- begin
407
- MU::Cloud::Google.storage.delete_object(
408
- MU.adminBucketName,
409
- "#{MU.deploy_id}-secret"
410
- )
411
- rescue ::Google::Apis::ClientError => e
412
- raise e if !e.message.match(/^notFound: /)
413
- end
228
+ begin
229
+ projects.concat(cloudclass.listHabitats(credset))
230
+ rescue NoMethodError
231
+ end
232
+ end
233
+
234
+ if projects == []
235
+ projects << "" # dummy
236
+ MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{region}", MU::NOTICE
237
+ end
238
+ projects.uniq!
239
+
240
+ # We do these in an order that unrolls dependent resources
241
+ # sensibly, and we hit :Collection twice because AWS
242
+ # CloudFormation sometimes fails internally.
243
+ projectthreads = []
244
+ projects.each { |project|
245
+ if habitats and !habitats.empty? and project != ""
246
+ next if !habitats.include?(project)
414
247
  end
415
- if MU.myCloud == "AWS"
416
- MU::Cloud::AWS.openFirewallForClients # XXX add the other clouds, or abstract
248
+ if @habitatsused and !@habitatsused.empty? and project != ""
249
+ next if !@habitatsused.include?(project)
417
250
  end
251
+ next if !habitatclass.isLive?(project, credset)
252
+
253
+ projectthreads << Thread.new {
254
+ Thread.abort_on_exception = false
255
+ if !cleanHabitat(cloud, credset, region, project, global_vs_region_semaphore, global_done)
256
+ had_failures = true
257
+ end
258
+ } # TYPES_IN_ORDER.each { |t|
259
+ } # projects.each { |project|
260
+ projectthreads.each do |t|
261
+ t.join
418
262
  end
419
263
 
420
- if !@noop and !@skipcloud and (@mommacat.nil? or @mommacat.numKittens(types: ["Server", "ServerPool"]) > 0)
421
- # MU::MommaCat.syncMonitoringConfig
264
+ had_failures
265
+ end
266
+ private_class_method :cleanRegion
267
+
268
+ def self.cleanHabitat(cloud, credset, region, habitat, global_vs_region_semaphore, global_done)
269
+ had_failures = false
270
+ if habitat != ""
271
+ MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{region}, habitat #{habitat}", MU::NOTICE
422
272
  end
423
273
 
274
+ flags = {
275
+ "habitat" => habitat,
276
+ "onlycloud" => @onlycloud,
277
+ "skipsnapshots" => @skipsnapshots,
278
+ }
279
+ TYPES_IN_ORDER.each { |t|
280
+ begin
281
+ skipme = false
282
+ global_vs_region_semaphore.synchronize {
283
+ MU::Cloud.loadCloudType(cloud, t)
284
+ if Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(t).isGlobal?
285
+ global_done[habitat] ||= []
286
+ if !global_done[habitat].include?(t)
287
+ global_done[habitat] << t
288
+ flags['global'] = true
289
+ else
290
+ skipme = true
291
+ end
292
+ end
293
+ }
294
+ next if skipme
295
+ rescue MU::Cloud::MuDefunctHabitat, MU::Cloud::MuCloudResourceNotImplemented
296
+ next
297
+ rescue MU::MuError, NoMethodError => e
298
+ MU.log "While checking mu/clouds/#{cloud.downcase}/#{cloudclass.cfg_name} for global-ness in cleanup: "+e.message, MU::WARN
299
+ next
300
+ rescue ::Aws::EC2::Errors::AuthFailure, ::Google::Apis::ClientError => e
301
+ MU.log e.message+" in "+region, MU::ERR
302
+ next
303
+ end
304
+
305
+ begin
306
+ if !call_cleanup(t, credset, cloud, flags, region)
307
+ had_failures = true
308
+ end
309
+ rescue MU::Cloud::MuDefunctHabitat, MU::Cloud::MuCloudResourceNotImplemented
310
+ next
311
+ end
312
+ }
313
+ had_failures = true
424
314
  end
315
+ private_class_method :cleanHabitat
425
316
 
426
317
  # Wrapper for dynamically invoking resource type cleanup methods.
427
318
  # @param type [String]:
@@ -444,24 +335,21 @@ module MU
444
335
  flags['known'] << found.cloud_id
445
336
  end
446
337
  end
447
- # begin
448
- resclass = Object.const_get("MU").const_get("Cloud").const_get(type)
449
-
450
- resclass.cleanup(
451
- noop: @noop,
452
- ignoremaster: @ignoremaster,
453
- region: region,
454
- cloud: provider,
455
- flags: flags,
456
- credentials: credset
457
- )
458
- # rescue ::Seahorse::Client::NetworkingError => e
459
- # MU.log "Service not available in AWS region #{r}, skipping", MU::DEBUG, details: e.message
460
- # end
338
+ resclass = Object.const_get("MU").const_get("Cloud").const_get(type)
339
+
340
+ resclass.cleanup(
341
+ noop: @noop,
342
+ ignoremaster: @ignoremaster,
343
+ region: region,
344
+ cloud: provider,
345
+ flags: flags,
346
+ credentials: credset
347
+ )
461
348
  else
462
349
  true
463
350
  end
464
-
465
351
  end
352
+ private_class_method :call_cleanup
353
+
466
354
  end #class
467
355
  end #module