cloud-mu 3.1.5 → 3.1.6

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