cloud-mu 3.1.6 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/mu-adopt +4 -12
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +37 -1
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-run-tests +23 -10
- data/cloud-mu.gemspec +2 -2
- data/cookbooks/mu-tools/libraries/helper.rb +1 -1
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/extras/generate-stock-images +1 -0
- data/modules/mu.rb +82 -95
- data/modules/mu/adoption.rb +356 -56
- data/modules/mu/cleanup.rb +21 -20
- data/modules/mu/cloud.rb +79 -1753
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +46 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +920 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +165 -0
- data/modules/mu/config.rb +122 -80
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +1 -1
- data/modules/mu/config/cache_cluster.rb +1 -1
- data/modules/mu/config/collection.rb +1 -1
- data/modules/mu/config/container_cluster.rb +2 -2
- data/modules/mu/config/database.rb +83 -104
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +1 -1
- data/modules/mu/config/doc_helpers.rb +4 -5
- data/modules/mu/config/endpoint.rb +1 -1
- data/modules/mu/config/firewall_rule.rb +3 -19
- data/modules/mu/config/folder.rb +1 -1
- data/modules/mu/config/function.rb +1 -1
- data/modules/mu/config/group.rb +1 -1
- data/modules/mu/config/habitat.rb +1 -1
- data/modules/mu/config/loadbalancer.rb +57 -11
- data/modules/mu/config/log.rb +1 -1
- data/modules/mu/config/msg_queue.rb +1 -1
- data/modules/mu/config/nosqldb.rb +1 -1
- data/modules/mu/config/notifier.rb +1 -1
- data/modules/mu/config/ref.rb +30 -4
- data/modules/mu/config/role.rb +1 -1
- data/modules/mu/config/schema_helpers.rb +30 -34
- data/modules/mu/config/search_domain.rb +1 -1
- data/modules/mu/config/server.rb +4 -12
- data/modules/mu/config/server_pool.rb +3 -7
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +10 -0
- data/modules/mu/config/user.rb +1 -1
- data/modules/mu/config/vpc.rb +12 -17
- data/modules/mu/defaults/AWS.yaml +32 -32
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +1 -0
- data/modules/mu/deploy.rb +16 -15
- data/modules/mu/groomer.rb +15 -0
- data/modules/mu/groomers/chef.rb +3 -0
- data/modules/mu/logger.rb +120 -144
- data/modules/mu/master.rb +1 -1
- data/modules/mu/mommacat.rb +54 -25
- data/modules/mu/mommacat/daemon.rb +10 -7
- data/modules/mu/mommacat/naming.rb +82 -3
- data/modules/mu/mommacat/search.rb +47 -15
- data/modules/mu/mommacat/storage.rb +72 -41
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +114 -47
- data/modules/mu/{clouds → providers}/aws/alarm.rb +1 -1
- data/modules/mu/{clouds → providers}/aws/bucket.rb +2 -2
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +10 -46
- data/modules/mu/{clouds → providers}/aws/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +15 -33
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +2 -5
- data/modules/mu/{clouds → providers}/aws/endpoint.rb +2 -11
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +33 -29
- data/modules/mu/{clouds → providers}/aws/folder.rb +0 -0
- data/modules/mu/{clouds → providers}/aws/function.rb +2 -10
- data/modules/mu/{clouds → providers}/aws/group.rb +9 -13
- data/modules/mu/{clouds → providers}/aws/habitat.rb +1 -1
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +41 -33
- data/modules/mu/{clouds → providers}/aws/log.rb +2 -2
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +2 -8
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +0 -0
- data/modules/mu/{clouds → providers}/aws/notifier.rb +0 -0
- data/modules/mu/{clouds → providers}/aws/role.rb +7 -7
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +8 -13
- data/modules/mu/{clouds → providers}/aws/server.rb +55 -90
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +10 -33
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +19 -36
- data/modules/mu/{clouds → providers}/aws/user.rb +8 -12
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/aws/vpc.rb +135 -70
- data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
- data/modules/mu/{clouds → providers}/azure.rb +4 -1
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
- data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
- data/modules/mu/{clouds → providers}/azure/server.rb +30 -23
- data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
- data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +14 -6
- data/modules/mu/{clouds → providers}/google/bucket.rb +1 -1
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +28 -13
- data/modules/mu/{clouds → providers}/google/database.rb +1 -8
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +2 -2
- data/modules/mu/{clouds → providers}/google/folder.rb +4 -8
- data/modules/mu/{clouds → providers}/google/function.rb +3 -3
- data/modules/mu/{clouds → providers}/google/group.rb +8 -16
- data/modules/mu/{clouds → providers}/google/habitat.rb +3 -7
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +1 -1
- data/modules/mu/{clouds → providers}/google/role.rb +42 -34
- data/modules/mu/{clouds → providers}/google/server.rb +25 -10
- data/modules/mu/{clouds → providers}/google/server_pool.rb +10 -10
- data/modules/mu/{clouds → providers}/google/user.rb +31 -21
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +37 -2
- data/modules/tests/centos6.yaml +11 -0
- data/modules/tests/centos7.yaml +11 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +108 -89
- data/modules/mu/clouds/aws/database.rb +0 -1974
data/modules/mu/adoption.rb
CHANGED
|
@@ -30,7 +30,8 @@ module MU
|
|
|
30
30
|
:omnibus => "Jam everything into one monolothic configuration"
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
|
|
34
|
+
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, regions: [], merge: false)
|
|
34
35
|
@scraped = {}
|
|
35
36
|
@clouds = clouds
|
|
36
37
|
@types = types
|
|
@@ -44,8 +45,10 @@ module MU
|
|
|
44
45
|
@savedeploys = savedeploys
|
|
45
46
|
@diff = diff
|
|
46
47
|
@habitats = habitats
|
|
48
|
+
@regions = regions
|
|
47
49
|
@habitats ||= []
|
|
48
50
|
@scrub_mu_isms = scrub_mu_isms
|
|
51
|
+
@merge = merge
|
|
49
52
|
end
|
|
50
53
|
|
|
51
54
|
# Walk cloud providers with available credentials to discover resources
|
|
@@ -53,7 +56,7 @@ module MU
|
|
|
53
56
|
@default_parent = nil
|
|
54
57
|
|
|
55
58
|
@clouds.each { |cloud|
|
|
56
|
-
cloudclass =
|
|
59
|
+
cloudclass = MU::Cloud.cloudClass(cloud)
|
|
57
60
|
next if cloudclass.listCredentials.nil?
|
|
58
61
|
|
|
59
62
|
if cloud == "Google" and !@parent and @target_creds
|
|
@@ -65,7 +68,6 @@ module MU
|
|
|
65
68
|
|
|
66
69
|
cloudclass.listCredentials.each { |credset|
|
|
67
70
|
next if @sources and !@sources.include?(credset)
|
|
68
|
-
|
|
69
71
|
cfg = cloudclass.credConfig(credset)
|
|
70
72
|
if cfg and cfg['restrict_to_habitats']
|
|
71
73
|
cfg['restrict_to_habitats'] << cfg['project'] if cfg['project']
|
|
@@ -90,7 +92,7 @@ module MU
|
|
|
90
92
|
|
|
91
93
|
@types.each { |type|
|
|
92
94
|
begin
|
|
93
|
-
resclass =
|
|
95
|
+
resclass = MU::Cloud.resourceClass(cloud, type)
|
|
94
96
|
rescue ::MU::Cloud::MuCloudResourceNotImplemented
|
|
95
97
|
next
|
|
96
98
|
end
|
|
@@ -106,6 +108,7 @@ module MU
|
|
|
106
108
|
credentials: credset,
|
|
107
109
|
allow_multi: true,
|
|
108
110
|
habitats: @habitats.dup,
|
|
111
|
+
region: @regions,
|
|
109
112
|
dummy_ok: true,
|
|
110
113
|
skip_provider_owned: true,
|
|
111
114
|
# debug: false#,
|
|
@@ -114,7 +117,9 @@ module MU
|
|
|
114
117
|
|
|
115
118
|
if found and found.size > 0
|
|
116
119
|
if resclass.cfg_plural == "habitats"
|
|
117
|
-
found.reject! { |h|
|
|
120
|
+
found.reject! { |h|
|
|
121
|
+
!cloudclass.listHabitats(credset).include?(h.cloud_id)
|
|
122
|
+
}
|
|
118
123
|
end
|
|
119
124
|
MU.log "Found #{found.size.to_s} raw #{resclass.cfg_plural} in #{cloud}"
|
|
120
125
|
@scraped[type] ||= {}
|
|
@@ -123,6 +128,10 @@ module MU
|
|
|
123
128
|
next
|
|
124
129
|
end
|
|
125
130
|
# XXX apply any filters (e.g. MU-ID tags)
|
|
131
|
+
if obj.cloud_id.nil?
|
|
132
|
+
MU.log "This damn thing gave me no cloud id, what do I even do with that", MU::ERR, details: obj
|
|
133
|
+
exit
|
|
134
|
+
end
|
|
126
135
|
@scraped[type][obj.cloud_id] = obj
|
|
127
136
|
}
|
|
128
137
|
end
|
|
@@ -200,7 +209,36 @@ module MU
|
|
|
200
209
|
prefix = "mu" if prefix.empty? # so that appnames aren't ever empty
|
|
201
210
|
end
|
|
202
211
|
|
|
212
|
+
# Find any previous deploys with this particular profile, which we'll use
|
|
213
|
+
# later for --diff.
|
|
214
|
+
@existing_deploys = {}
|
|
215
|
+
@existing_deploys_by_id = {}
|
|
216
|
+
@origins = {}
|
|
217
|
+
@types_found_in = {}
|
|
218
|
+
groupings.each_pair { |appname, types|
|
|
219
|
+
allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
|
|
220
|
+
next if (types & allowed_types).size == 0
|
|
221
|
+
origin = {
|
|
222
|
+
"appname" => prefix+appname,
|
|
223
|
+
"types" => (types & allowed_types).sort,
|
|
224
|
+
"habitats" => @habitats.sort,
|
|
225
|
+
"group_by" => @group_by.to_s
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@existing_deploys[appname] = MU::MommaCat.findMatchingDeploy(origin)
|
|
229
|
+
if @existing_deploys[appname]
|
|
230
|
+
@existing_deploys_by_id[@existing_deploys[appname].deploy_id] = @existing_deploys[appname]
|
|
231
|
+
@origins[appname] = origin
|
|
232
|
+
origin['types'].each { |t|
|
|
233
|
+
@types_found_in[t] = @existing_deploys[appname]
|
|
234
|
+
}
|
|
235
|
+
end
|
|
236
|
+
}
|
|
237
|
+
|
|
203
238
|
groupings.each_pair { |appname, types|
|
|
239
|
+
allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
|
|
240
|
+
next if (types & allowed_types).size == 0
|
|
241
|
+
|
|
204
242
|
bok = { "appname" => prefix+appname }
|
|
205
243
|
if @scrub_mu_isms
|
|
206
244
|
bok["scrub_mu_isms"] = true
|
|
@@ -210,26 +248,23 @@ module MU
|
|
|
210
248
|
end
|
|
211
249
|
|
|
212
250
|
count = 0
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
deploy = MU::MommaCat.findMatchingDeploy(origin)
|
|
223
|
-
if @diff and !deploy
|
|
224
|
-
MU.log "--diff was set but I failed to find a deploy like me to compare to", MU::ERR, details: origin
|
|
225
|
-
exit 1
|
|
251
|
+
if @diff
|
|
252
|
+
if !@existing_deploys[appname]
|
|
253
|
+
MU.log "--diff was set but I failed to find a deploy like '#{appname}' to compare to (have #{@existing_deploys.keys.join(", ")})", MU::ERR, details: @origins[appname]
|
|
254
|
+
exit 1
|
|
255
|
+
else
|
|
256
|
+
MU.log "Will diff current live resources against #{@existing_deploys[appname].deploy_id}", MU::NOTICE, details: @origins[appname]
|
|
257
|
+
end
|
|
226
258
|
end
|
|
227
259
|
|
|
228
260
|
threads = []
|
|
261
|
+
timers = {}
|
|
262
|
+
walltimers = {}
|
|
229
263
|
@clouds.each { |cloud|
|
|
230
264
|
@scraped.each_pair { |type, resources|
|
|
265
|
+
typestart = Time.now
|
|
231
266
|
res_class = begin
|
|
232
|
-
MU::Cloud.
|
|
267
|
+
MU::Cloud.resourceClass(cloud, type)
|
|
233
268
|
rescue MU::Cloud::MuCloudResourceNotImplemented
|
|
234
269
|
# XXX I don't think this can actually happen
|
|
235
270
|
next
|
|
@@ -237,6 +272,7 @@ module MU
|
|
|
237
272
|
next if !types.include?(res_class.cfg_plural)
|
|
238
273
|
|
|
239
274
|
bok[res_class.cfg_plural] ||= []
|
|
275
|
+
timers[type] ||= {}
|
|
240
276
|
|
|
241
277
|
class_semaphore = Mutex.new
|
|
242
278
|
|
|
@@ -253,13 +289,18 @@ module MU
|
|
|
253
289
|
end
|
|
254
290
|
end
|
|
255
291
|
threads << Thread.new(obj_thr) { |obj|
|
|
292
|
+
start = Time.now
|
|
256
293
|
|
|
257
|
-
kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats)
|
|
294
|
+
kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats, types: @types)
|
|
258
295
|
if kitten_cfg
|
|
259
296
|
print "."
|
|
260
297
|
kitten_cfg.delete("credentials") if @target_creds
|
|
261
298
|
class_semaphore.synchronize {
|
|
262
299
|
bok[res_class.cfg_plural] << kitten_cfg
|
|
300
|
+
if !kitten_cfg['cloud_id']
|
|
301
|
+
MU.log "No cloud id in this #{res_class.cfg_name} kitten!", MU::ERR, details: kitten_cfg
|
|
302
|
+
end
|
|
303
|
+
timers[type][kitten_cfg['cloud_id']] = (Time.now - start)
|
|
263
304
|
}
|
|
264
305
|
count += 1
|
|
265
306
|
end
|
|
@@ -270,6 +311,7 @@ module MU
|
|
|
270
311
|
threads.each { |t|
|
|
271
312
|
t.join
|
|
272
313
|
}
|
|
314
|
+
|
|
273
315
|
puts ""
|
|
274
316
|
bok[res_class.cfg_plural].sort! { |a, b|
|
|
275
317
|
strs = [a, b].map { |x|
|
|
@@ -291,24 +333,30 @@ module MU
|
|
|
291
333
|
bok[res_class.cfg_plural].each { |sibling|
|
|
292
334
|
next if kitten_cfg == sibling
|
|
293
335
|
if sibling['name'] == kitten_cfg['name']
|
|
294
|
-
MU.
|
|
295
|
-
if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
|
|
296
|
-
kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['parent'].id
|
|
297
|
-
elsif kitten_cfg['project']
|
|
298
|
-
kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['project']
|
|
299
|
-
elsif kitten_cfg['region']
|
|
300
|
-
kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['region']
|
|
301
|
-
elsif kitten_cfg['cloud_id']
|
|
302
|
-
kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
|
|
303
|
-
else
|
|
304
|
-
raise MU::Config::DuplicateNameError, "Saw duplicate #{res_class.cfg_name} name #{sibling['name']} and couldn't come up with a good way to differentiate them"
|
|
305
|
-
end
|
|
336
|
+
MU::Adoption.deDuplicateName(kitten_cfg, res_class)
|
|
306
337
|
MU.log "De-duplication: Renamed #{res_class.cfg_name} name '#{sibling['name']}' => '#{kitten_cfg['name']}'", MU::NOTICE
|
|
307
338
|
break
|
|
308
339
|
end
|
|
309
340
|
}
|
|
310
341
|
}
|
|
342
|
+
walltimers[type] ||= 0
|
|
343
|
+
walltimers[type] += (Time.now - typestart)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
timers.each_pair { |type, resources|
|
|
348
|
+
next if resources.empty?
|
|
349
|
+
total = resources.values.sum
|
|
350
|
+
top_5 = resources.keys.sort { |a, b|
|
|
351
|
+
resources[b] <=> resources[a]
|
|
352
|
+
}.slice(0, 5).map { |k|
|
|
353
|
+
k.to_s+": "+sprintf("%.2fs", resources[k])
|
|
311
354
|
}
|
|
355
|
+
if walltimers[type] < 45
|
|
356
|
+
MU.log "Kittened #{resources.size.to_s} eligible #{type}s in #{sprintf("%.2fs", walltimers[type])}"
|
|
357
|
+
else
|
|
358
|
+
MU.log "Kittened #{resources.size.to_s} eligible #{type}s in #{sprintf("%.2fs", walltimers[type])} (CPU time #{sprintf("%.2fs", total)}, avg #{sprintf("%.2fs", total/resources.size)}). Top 5:", MU::NOTICE, details: top_5
|
|
359
|
+
end
|
|
312
360
|
}
|
|
313
361
|
|
|
314
362
|
# No matching resources isn't necessarily an error
|
|
@@ -317,23 +365,36 @@ module MU
|
|
|
317
365
|
# Now walk through all of the Refs in these objects, resolve them, and minimize
|
|
318
366
|
# their config footprint
|
|
319
367
|
MU.log "Minimizing footprint of #{count.to_s} found resources", MU::DEBUG
|
|
320
|
-
@boks[bok['appname']] = vacuum(bok, origin: origin, save: @savedeploys)
|
|
321
368
|
|
|
322
|
-
|
|
369
|
+
generated_deploy = generateStubDeploy(bok)
|
|
370
|
+
@boks[bok['appname']] = vacuum(bok, origin: @origins[appname], deploy: generated_deploy, save: @savedeploys)
|
|
371
|
+
|
|
372
|
+
if @diff and !@existing_deploys[appname]
|
|
323
373
|
MU.log "diff flag set, but no comparable deploy provided for #{bok['appname']}", MU::ERR
|
|
324
374
|
exit 1
|
|
325
375
|
end
|
|
326
376
|
|
|
327
|
-
if
|
|
328
|
-
|
|
377
|
+
if @diff
|
|
378
|
+
prev_vacuumed = vacuum(@existing_deploys[appname].original_config, deploy: @existing_deploys[appname], keep_missing: true, copy_from: generated_deploy)
|
|
379
|
+
prevcfg = MU::Config.manxify(prev_vacuumed)
|
|
329
380
|
if !prevcfg
|
|
330
|
-
MU.log "#{
|
|
381
|
+
MU.log "#{@existing_deploys[appname].deploy_id} didn't have a working original config for me to compare", MU::ERR
|
|
331
382
|
exit 1
|
|
332
383
|
end
|
|
333
384
|
newcfg = MU::Config.manxify(@boks[bok['appname']])
|
|
385
|
+
report = prevcfg.diff(newcfg)
|
|
386
|
+
|
|
387
|
+
if report
|
|
388
|
+
|
|
389
|
+
if MU.muCfg['adopt_change_notify']
|
|
390
|
+
notifyChanges(@existing_deploys[appname], report.freeze)
|
|
391
|
+
end
|
|
392
|
+
if @merge
|
|
393
|
+
MU.log "Saving changes to #{@existing_deploys[appname].deploy_id}"
|
|
394
|
+
@existing_deploys[appname].updateBasketofKittens(newcfg, save_now: true)
|
|
395
|
+
end
|
|
396
|
+
end
|
|
334
397
|
|
|
335
|
-
prevcfg.diff(newcfg)
|
|
336
|
-
exit
|
|
337
398
|
end
|
|
338
399
|
}
|
|
339
400
|
@boks
|
|
@@ -341,6 +402,183 @@ module MU
|
|
|
341
402
|
|
|
342
403
|
private
|
|
343
404
|
|
|
405
|
+
# @param tier [Hash]
|
|
406
|
+
# @param parent_key [String]
|
|
407
|
+
def crawlChangeReport(tier, parent_key = nil, indent: "")
|
|
408
|
+
report = []
|
|
409
|
+
if tier.is_a?(Array)
|
|
410
|
+
tier.each { |a|
|
|
411
|
+
sub_report = crawlChangeReport(a, parent_key)
|
|
412
|
+
report.concat(sub_report) if sub_report and !sub_report.empty?
|
|
413
|
+
}
|
|
414
|
+
elsif tier.is_a?(Hash)
|
|
415
|
+
if tier[:action]
|
|
416
|
+
preposition = if tier[:action] == :added
|
|
417
|
+
"to"
|
|
418
|
+
elsif tier[:action] == :removed
|
|
419
|
+
"from"
|
|
420
|
+
else
|
|
421
|
+
"in"
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
name = ""
|
|
425
|
+
type_of = parent_key.sub(/s$|\[.*/, '') if parent_key
|
|
426
|
+
loc = tier[:habitat]
|
|
427
|
+
|
|
428
|
+
if tier[:value] and tier[:value].is_a?(Hash)
|
|
429
|
+
name, loc = MU::MommaCat.getChunkName(tier[:value], type_of)
|
|
430
|
+
elsif parent_key
|
|
431
|
+
name = parent_key
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
path_str = []
|
|
435
|
+
slack_path_str = ""
|
|
436
|
+
if tier[:parents] and tier[:parents].size > 2
|
|
437
|
+
path = tier[:parents].clone
|
|
438
|
+
slack_path_str += "#{preposition} \*"+path.join(" ⇨ ")+"\*" if path.size > 0
|
|
439
|
+
path.shift
|
|
440
|
+
path.shift
|
|
441
|
+
path.pop if path.last == name
|
|
442
|
+
for c in (0..(path.size-1)) do
|
|
443
|
+
path_str << (" " * (c+2)) + (path[c] || "<nil>")
|
|
444
|
+
end
|
|
445
|
+
end
|
|
446
|
+
path_str << "" if !path_str.empty?
|
|
447
|
+
|
|
448
|
+
plain = (name ? name : type_of) if name or type_of
|
|
449
|
+
plain ||= "" # XXX but this is a problem
|
|
450
|
+
slack = "`"+plain+"`"
|
|
451
|
+
|
|
452
|
+
plain += " ("+loc+")" if loc and !loc.empty?
|
|
453
|
+
color = plain
|
|
454
|
+
|
|
455
|
+
if tier[:action] == :added
|
|
456
|
+
color = "+ ".green + plain
|
|
457
|
+
plain = "+ " + plain
|
|
458
|
+
slack += " added"
|
|
459
|
+
elsif tier[:action] == :removed
|
|
460
|
+
color = "- ".red + plain
|
|
461
|
+
plain = "- " + plain
|
|
462
|
+
slack += " removed"
|
|
463
|
+
end
|
|
464
|
+
|
|
465
|
+
slack += " #{tier[:action]} #{preposition} \*#{loc}\*" if loc and !loc.empty? and [Array, Hash].include?(tier[:value].class)
|
|
466
|
+
|
|
467
|
+
plain = path_str.join(" => \n") + indent + plain
|
|
468
|
+
color = path_str.join(" => \n") + indent + color
|
|
469
|
+
|
|
470
|
+
slack += " "+slack_path_str if !slack_path_str.empty?
|
|
471
|
+
myreport = {
|
|
472
|
+
"slack" => slack,
|
|
473
|
+
"plain" => plain,
|
|
474
|
+
"color" => color
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
append = ""
|
|
478
|
+
if tier[:value] and (tier[:value].is_a?(Array) or tier[:value].is_a?(Hash))
|
|
479
|
+
if tier[:value].is_a?(Hash)
|
|
480
|
+
if name
|
|
481
|
+
tier[:value].delete("entity")
|
|
482
|
+
tier[:value].delete(name.sub(/\[.*/, '')) if name
|
|
483
|
+
end
|
|
484
|
+
if (tier[:value].keys - ["id", "name", "type"]).size > 0
|
|
485
|
+
myreport["details"] = tier[:value].clone
|
|
486
|
+
append = PP.pp(tier[:value], '').gsub(/(^|\n)/, '\1'+indent)
|
|
487
|
+
end
|
|
488
|
+
else
|
|
489
|
+
append = indent+"["+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s.light_blue }.join(", ")+"]"
|
|
490
|
+
slack += " #{tier[:action].to_s}: "+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s }.join(", ")
|
|
491
|
+
end
|
|
492
|
+
else
|
|
493
|
+
tier[:value] ||= "<nil>"
|
|
494
|
+
if ![:removed].include?(tier[:action])
|
|
495
|
+
myreport["slack"] += ". New #{tier[:field] ? "`"+tier[:field]+"`" : :value}: \*#{tier[:value]}\*"
|
|
496
|
+
else
|
|
497
|
+
myreport["slack"] += " (was \*#{tier[:value]}\*)"
|
|
498
|
+
end
|
|
499
|
+
append = tier[:value].to_s.bold
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
if append and !append.empty?
|
|
503
|
+
myreport["plain"] += " =>\n "+indent+append
|
|
504
|
+
myreport["color"] += " =>\n "+indent+append
|
|
505
|
+
end
|
|
506
|
+
|
|
507
|
+
report << myreport if tier[:action]
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Just because we've got changes at this level doesn't mean there aren't
|
|
511
|
+
# more further down.
|
|
512
|
+
tier.each_pair { |k, v|
|
|
513
|
+
next if !(v.is_a?(Hash) or v.is_a?(Array))
|
|
514
|
+
sub_report = crawlChangeReport(v, k, indent: indent+" ")
|
|
515
|
+
report.concat(sub_report) if sub_report and !sub_report.empty?
|
|
516
|
+
}
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
report
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
def notifyChanges(deploy, report)
|
|
524
|
+
snippet_threshold = (MU.muCfg['adopt_change_notify'] && MU.muCfg['adopt_change_notify']['slack_snippet_threshold']) || 5
|
|
525
|
+
|
|
526
|
+
report.each_pair { |res_type, resources|
|
|
527
|
+
shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(res_type, false)
|
|
528
|
+
next if !shortclass # we don't really care about Mu metadata changes
|
|
529
|
+
resources.each_pair { |name, data|
|
|
530
|
+
if MU::MommaCat.getChunkName(data[:value], res_type).first.nil?
|
|
531
|
+
symbol = if data[:action] == :added
|
|
532
|
+
"+".green
|
|
533
|
+
elsif data[:action] == :removed
|
|
534
|
+
"-".red
|
|
535
|
+
else
|
|
536
|
+
"~".yellow
|
|
537
|
+
end
|
|
538
|
+
puts (symbol+" "+res_type+"["+name+"]")
|
|
539
|
+
end
|
|
540
|
+
|
|
541
|
+
noun = shortclass ? shortclass.to_s : res_type.capitalize
|
|
542
|
+
verb = if data[:action]
|
|
543
|
+
data[:action].to_s
|
|
544
|
+
else
|
|
545
|
+
"modified"
|
|
546
|
+
end
|
|
547
|
+
|
|
548
|
+
changes = crawlChangeReport(data.freeze, res_type)
|
|
549
|
+
|
|
550
|
+
slacktext = "#{noun} \*#{name}\* was #{verb}"
|
|
551
|
+
if data[:habitat]
|
|
552
|
+
slacktext += " in \*#{data[:habitat]}\*"
|
|
553
|
+
end
|
|
554
|
+
snippets = []
|
|
555
|
+
|
|
556
|
+
if [:added, :removed].include?(data[:action]) and data[:value]
|
|
557
|
+
snippets << { text: "```"+JSON.pretty_generate(data[:value])+"```" }
|
|
558
|
+
else
|
|
559
|
+
changes.each { |c|
|
|
560
|
+
slacktext += "\n • "+c["slack"]
|
|
561
|
+
if c["details"]
|
|
562
|
+
details = JSON.pretty_generate(c["details"])
|
|
563
|
+
snippets << { text: "```"+JSON.pretty_generate(c["details"])+"```" }
|
|
564
|
+
end
|
|
565
|
+
}
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
changes.each { |c|
|
|
569
|
+
puts c["color"]
|
|
570
|
+
}
|
|
571
|
+
puts ""
|
|
572
|
+
|
|
573
|
+
if MU.muCfg['adopt_change_notify'] and MU.muCfg['adopt_change_notify']['slack']
|
|
574
|
+
deploy.sendAdminSlack(slacktext, scrub_mu_isms: MU.muCfg['adopt_scrub_mu_isms'], snippets: snippets, noop: false)
|
|
575
|
+
end
|
|
576
|
+
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
end
|
|
581
|
+
|
|
344
582
|
def scrubSchemaDefaults(conf_chunk, schema_chunk, depth = 0, type: nil)
|
|
345
583
|
return if schema_chunk.nil?
|
|
346
584
|
|
|
@@ -372,8 +610,7 @@ module MU
|
|
|
372
610
|
# theory
|
|
373
611
|
realschema = if type and schema_chunk["items"] and schema_chunk["items"]["properties"] and item["cloud"] and MU::Cloud.supportedClouds.include?(item['cloud'])
|
|
374
612
|
|
|
375
|
-
|
|
376
|
-
_toplevel_required, cloudschema = cloudclass.schema(self)
|
|
613
|
+
_toplevel_required, cloudschema = MU::Cloud.resourceClass(item['cloud'], type).schema(self)
|
|
377
614
|
|
|
378
615
|
newschema = schema_chunk["items"].dup
|
|
379
616
|
newschema["properties"].merge!(cloudschema)
|
|
@@ -397,8 +634,7 @@ module MU
|
|
|
397
634
|
# Do the same for our main objects: if they all use the same credentials,
|
|
398
635
|
# for example, remove the explicit +credentials+ attributes and set that
|
|
399
636
|
# value globally, once.
|
|
400
|
-
def vacuum(bok, origin: nil, save: false, deploy: nil)
|
|
401
|
-
deploy ||= generateStubDeploy(bok)
|
|
637
|
+
def vacuum(bok, origin: nil, save: false, deploy: nil, copy_from: nil, keep_missing: false)
|
|
402
638
|
|
|
403
639
|
globals = {
|
|
404
640
|
'cloud' => {},
|
|
@@ -418,11 +654,24 @@ module MU
|
|
|
418
654
|
end
|
|
419
655
|
}
|
|
420
656
|
obj = deploy.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
|
|
657
|
+
inject_metadata = save
|
|
658
|
+
if obj.nil? and copy_from
|
|
659
|
+
obj = copy_from.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
|
|
660
|
+
if obj
|
|
661
|
+
inject_metadata = true
|
|
662
|
+
obj.intoDeploy(deploy, force: true)
|
|
663
|
+
end
|
|
664
|
+
end
|
|
665
|
+
|
|
421
666
|
begin
|
|
422
667
|
raise Incomplete if obj.nil?
|
|
668
|
+
if inject_metadata
|
|
669
|
+
deploydata = obj.notify
|
|
670
|
+
deploy.notify(attrs[:cfg_plural], resource['name'], deploydata, triggering_node: obj)
|
|
671
|
+
end
|
|
423
672
|
new_cfg = resolveReferences(resource, deploy, obj)
|
|
424
673
|
new_cfg.delete("cloud_id")
|
|
425
|
-
cred_cfg = MU::Cloud.
|
|
674
|
+
cred_cfg = MU::Cloud.cloudClass(obj.cloud).credConfig(obj.credentials)
|
|
426
675
|
if cred_cfg['region'] == new_cfg['region']
|
|
427
676
|
new_cfg.delete('region')
|
|
428
677
|
end
|
|
@@ -432,6 +681,11 @@ module MU
|
|
|
432
681
|
end
|
|
433
682
|
processed << new_cfg
|
|
434
683
|
rescue Incomplete
|
|
684
|
+
if keep_missing
|
|
685
|
+
processed << resource
|
|
686
|
+
else
|
|
687
|
+
MU.log "#{attrs[:cfg_name]} #{resource['name']} didn't show up from findLitterMate", MU::WARN, details: deploy.original_config[attrs[:cfg_plural]].reject { |r| r['name'] != "" }
|
|
688
|
+
end
|
|
435
689
|
end
|
|
436
690
|
}
|
|
437
691
|
|
|
@@ -442,24 +696,23 @@ module MU
|
|
|
442
696
|
|
|
443
697
|
# Pare out global values like +cloud+ or +region+ that appear to be
|
|
444
698
|
# universal in the deploy we're creating.
|
|
445
|
-
|
|
699
|
+
scrub_globals = Proc.new { |h, field|
|
|
446
700
|
if h.is_a?(Hash)
|
|
447
701
|
newhash = {}
|
|
448
702
|
h.each_pair { |k, v|
|
|
449
703
|
next if k == field
|
|
450
|
-
newhash[k] = scrub_globals(v, field)
|
|
704
|
+
newhash[k] = scrub_globals.call(v, field)
|
|
451
705
|
}
|
|
452
706
|
h = newhash
|
|
453
707
|
elsif h.is_a?(Array)
|
|
454
708
|
newarr = []
|
|
455
709
|
h.each { |v|
|
|
456
|
-
newarr << scrub_globals(v, field)
|
|
710
|
+
newarr << scrub_globals.call(v, field)
|
|
457
711
|
}
|
|
458
|
-
h = newarr
|
|
712
|
+
h = newarr.uniq
|
|
459
713
|
end
|
|
460
|
-
|
|
461
714
|
h
|
|
462
|
-
|
|
715
|
+
}
|
|
463
716
|
|
|
464
717
|
globals.each_pair { |field, counts|
|
|
465
718
|
next if counts.size != 1
|
|
@@ -469,7 +722,7 @@ module MU
|
|
|
469
722
|
if bok[attrs[:cfg_plural]]
|
|
470
723
|
new_resources = []
|
|
471
724
|
bok[attrs[:cfg_plural]].each { |resource|
|
|
472
|
-
new_resources << scrub_globals(resource, field)
|
|
725
|
+
new_resources << scrub_globals.call(resource, field)
|
|
473
726
|
}
|
|
474
727
|
bok[attrs[:cfg_plural]] = new_resources
|
|
475
728
|
end
|
|
@@ -487,11 +740,33 @@ module MU
|
|
|
487
740
|
end
|
|
488
741
|
|
|
489
742
|
def resolveReferences(cfg, deploy, parent)
|
|
743
|
+
mask_deploy_id = false
|
|
744
|
+
|
|
745
|
+
check_deploy_id = Proc.new { |cfgblob|
|
|
746
|
+
(deploy and
|
|
747
|
+
(cfgblob.is_a?(MU::Config::Ref) or cfgblob.is_a?(Hash)) and
|
|
748
|
+
cfgblob['deploy_id'] and
|
|
749
|
+
cfgblob['deploy_id'] != deploy.deploy_id and
|
|
750
|
+
@diff and
|
|
751
|
+
@types_found_in[cfgblob['type']] and
|
|
752
|
+
@types_found_in[cfgblob['type']].deploy_id == cfgblob['deploy_id']
|
|
753
|
+
)
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
mask_deploy_id = check_deploy_id.call(cfg)
|
|
757
|
+
|
|
490
758
|
if cfg.is_a?(MU::Config::Ref)
|
|
491
|
-
|
|
759
|
+
if mask_deploy_id
|
|
760
|
+
cfg.delete("deploy_id")
|
|
761
|
+
cfg.delete("mommacat")
|
|
762
|
+
cfg.kitten(deploy)
|
|
763
|
+
else
|
|
764
|
+
cfg.kitten(deploy) || cfg.kitten
|
|
765
|
+
end
|
|
766
|
+
|
|
492
767
|
hashcfg = cfg.to_h
|
|
493
768
|
|
|
494
|
-
if cfg.kitten
|
|
769
|
+
if cfg.kitten
|
|
495
770
|
littermate = deploy.findLitterMate(type: cfg.type, name: cfg.name, cloud_id: cfg.id, habitat: cfg.habitat)
|
|
496
771
|
|
|
497
772
|
if littermate and littermate.config['name']
|
|
@@ -522,7 +797,7 @@ module MU
|
|
|
522
797
|
hashcfg.delete("deploy_id") if hashcfg['deploy_id'] == deploy.deploy_id
|
|
523
798
|
|
|
524
799
|
if parent and parent.config
|
|
525
|
-
cred_cfg = MU::Cloud.
|
|
800
|
+
cred_cfg = MU::Cloud.cloudClass(parent.cloud).credConfig(parent.credentials)
|
|
526
801
|
|
|
527
802
|
if parent.config['region'] == hashcfg['region'] or
|
|
528
803
|
cred_cfg['region'] == hashcfg['region']
|
|
@@ -581,7 +856,12 @@ module MU
|
|
|
581
856
|
MU.log "Dropping unresolved value", MU::WARN, details: value
|
|
582
857
|
end
|
|
583
858
|
}
|
|
584
|
-
cfg = new_array
|
|
859
|
+
cfg = new_array.uniq
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
if mask_deploy_id or check_deploy_id.call(cfg)
|
|
863
|
+
cfg.delete("deploy_id")
|
|
864
|
+
MU.log "#{parent} in #{deploy.deploy_id} references something in #{@types_found_in[cfg['type']].deploy_id}, ditching extraneous deploy_id", MU::DEBUG, details: cfg.to_h
|
|
585
865
|
end
|
|
586
866
|
|
|
587
867
|
cfg
|
|
@@ -631,6 +911,10 @@ module MU
|
|
|
631
911
|
|
|
632
912
|
if !@scraped[typename][kitten['cloud_id']]
|
|
633
913
|
MU.log "No object in scraped tree for #{attrs[:cfg_name]} #{kitten['cloud_id']} (#{kitten['name']})", MU::ERR, details: kitten
|
|
914
|
+
if kitten['cloud_id'].nil?
|
|
915
|
+
pp caller
|
|
916
|
+
exit
|
|
917
|
+
end
|
|
634
918
|
next
|
|
635
919
|
end
|
|
636
920
|
|
|
@@ -641,7 +925,8 @@ module MU
|
|
|
641
925
|
deploy.addKitten(
|
|
642
926
|
attrs[:cfg_plural],
|
|
643
927
|
kitten['name'],
|
|
644
|
-
@scraped[typename][kitten['cloud_id']]
|
|
928
|
+
@scraped[typename][kitten['cloud_id']],
|
|
929
|
+
do_notify: true
|
|
645
930
|
)
|
|
646
931
|
}
|
|
647
932
|
end
|
|
@@ -650,6 +935,21 @@ module MU
|
|
|
650
935
|
deploy
|
|
651
936
|
end
|
|
652
937
|
|
|
938
|
+
def self.deDuplicateName(kitten_cfg, res_class)
|
|
939
|
+
orig_name = kitten_cfg['name'].dup
|
|
940
|
+
if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
|
|
941
|
+
kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['parent'].id
|
|
942
|
+
elsif kitten_cfg['project']
|
|
943
|
+
kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['project']
|
|
944
|
+
elsif kitten_cfg['region']
|
|
945
|
+
kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['region']
|
|
946
|
+
elsif kitten_cfg['cloud_id']
|
|
947
|
+
kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
|
|
948
|
+
else
|
|
949
|
+
raise MU::Config::DuplicateNameError, "Saw duplicate #{res_class.cfg_name} name #{orig_name} and couldn't come up with a good way to differentiate them"
|
|
950
|
+
end
|
|
951
|
+
end
|
|
952
|
+
|
|
653
953
|
# Go through everything we've scraped and update our mappings of cloud ids
|
|
654
954
|
# and bare name fields, so that resources can reference one another
|
|
655
955
|
# portably by name.
|