cloud-mu 3.2.0 → 3.3.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.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +12 -1
  4. data/bin/mu-load-config.rb +2 -1
  5. data/bin/mu-run-tests +14 -2
  6. data/cloud-mu.gemspec +3 -3
  7. data/modules/mu.rb +2 -2
  8. data/modules/mu/adoption.rb +5 -5
  9. data/modules/mu/cleanup.rb +47 -25
  10. data/modules/mu/cloud.rb +29 -1
  11. data/modules/mu/cloud/dnszone.rb +0 -2
  12. data/modules/mu/cloud/resource_base.rb +9 -3
  13. data/modules/mu/cloud/wrappers.rb +4 -0
  14. data/modules/mu/config.rb +1 -1
  15. data/modules/mu/config/bucket.rb +31 -2
  16. data/modules/mu/config/cache_cluster.rb +1 -1
  17. data/modules/mu/config/cdn.rb +100 -0
  18. data/modules/mu/config/container_cluster.rb +1 -1
  19. data/modules/mu/config/database.rb +1 -1
  20. data/modules/mu/config/dnszone.rb +4 -3
  21. data/modules/mu/config/endpoint.rb +1 -0
  22. data/modules/mu/config/function.rb +16 -7
  23. data/modules/mu/config/job.rb +89 -0
  24. data/modules/mu/config/notifier.rb +7 -18
  25. data/modules/mu/config/ref.rb +53 -7
  26. data/modules/mu/config/server.rb +1 -1
  27. data/modules/mu/config/vpc.rb +1 -0
  28. data/modules/mu/defaults/AWS.yaml +26 -26
  29. data/modules/mu/deploy.rb +13 -0
  30. data/modules/mu/master.rb +21 -0
  31. data/modules/mu/mommacat.rb +1 -0
  32. data/modules/mu/mommacat/daemon.rb +13 -7
  33. data/modules/mu/providers/aws.rb +115 -16
  34. data/modules/mu/providers/aws/alarm.rb +2 -2
  35. data/modules/mu/providers/aws/bucket.rb +274 -40
  36. data/modules/mu/providers/aws/cache_cluster.rb +4 -4
  37. data/modules/mu/providers/aws/cdn.rb +782 -0
  38. data/modules/mu/providers/aws/collection.rb +2 -2
  39. data/modules/mu/providers/aws/container_cluster.rb +57 -37
  40. data/modules/mu/providers/aws/database.rb +11 -11
  41. data/modules/mu/providers/aws/dnszone.rb +24 -7
  42. data/modules/mu/providers/aws/endpoint.rb +535 -50
  43. data/modules/mu/providers/aws/firewall_rule.rb +6 -3
  44. data/modules/mu/providers/aws/folder.rb +1 -1
  45. data/modules/mu/providers/aws/function.rb +288 -125
  46. data/modules/mu/providers/aws/group.rb +9 -7
  47. data/modules/mu/providers/aws/habitat.rb +2 -2
  48. data/modules/mu/providers/aws/job.rb +466 -0
  49. data/modules/mu/providers/aws/loadbalancer.rb +9 -8
  50. data/modules/mu/providers/aws/log.rb +3 -3
  51. data/modules/mu/providers/aws/msg_queue.rb +12 -3
  52. data/modules/mu/providers/aws/nosqldb.rb +96 -5
  53. data/modules/mu/providers/aws/notifier.rb +135 -63
  54. data/modules/mu/providers/aws/role.rb +51 -37
  55. data/modules/mu/providers/aws/search_domain.rb +165 -29
  56. data/modules/mu/providers/aws/server.rb +12 -9
  57. data/modules/mu/providers/aws/server_pool.rb +26 -13
  58. data/modules/mu/providers/aws/storage_pool.rb +2 -2
  59. data/modules/mu/providers/aws/user.rb +4 -4
  60. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  61. data/modules/mu/providers/aws/vpc.rb +3 -3
  62. data/modules/mu/providers/azure/server.rb +2 -1
  63. data/modules/mu/providers/google.rb +1 -0
  64. data/modules/mu/providers/google/bucket.rb +1 -1
  65. data/modules/mu/providers/google/container_cluster.rb +1 -1
  66. data/modules/mu/providers/google/database.rb +1 -1
  67. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  68. data/modules/mu/providers/google/folder.rb +1 -1
  69. data/modules/mu/providers/google/function.rb +1 -1
  70. data/modules/mu/providers/google/group.rb +1 -1
  71. data/modules/mu/providers/google/habitat.rb +1 -1
  72. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  73. data/modules/mu/providers/google/role.rb +4 -2
  74. data/modules/mu/providers/google/server.rb +1 -1
  75. data/modules/mu/providers/google/server_pool.rb +1 -1
  76. data/modules/mu/providers/google/user.rb +1 -1
  77. data/modules/mu/providers/google/vpc.rb +1 -1
  78. data/modules/tests/aws-jobs-functions.yaml +46 -0
  79. data/modules/tests/centos6.yaml +4 -0
  80. data/modules/tests/centos7.yaml +4 -0
  81. data/modules/tests/ecs.yaml +2 -2
  82. data/modules/tests/eks.yaml +1 -1
  83. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  84. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  85. data/modules/tests/microservice_app.yaml +288 -0
  86. data/modules/tests/rds.yaml +5 -5
  87. data/modules/tests/regrooms/rds.yaml +5 -5
  88. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  89. data/modules/tests/super_complex_bok.yml +2 -2
  90. data/modules/tests/super_simple_bok.yml +2 -2
  91. metadata +12 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fd9a15a0e94a578919c0cb22bf5c95ee86b5b4a03fcc0ea1c35da2c161e8cf31
4
- data.tar.gz: 04cdc9d9a70de97fdaf12021166b707921ec981d7ab2dc92cd1ebce491a9e130
3
+ metadata.gz: 87070670dfd1950848c17f897f49f087ada7a3155e3a5ef8e24ffb2c62583e7f
4
+ data.tar.gz: 8cee858ebde553ee022e56db98e90c62a28eb4a0b0247f74bd48a940956fe1c5
5
5
  SHA512:
6
- metadata.gz: adfef4f231a946a3929b7f8026a9e338658dadfef3482942a46e8fdb9b10c4ca7d68aa457d135815a3878882d88a103ec1ebacea4bcb1ae4c45b070d85ceee06
7
- data.tar.gz: 3a7b04d7d2486b05e8d413b78029b610bb7ba6bd8a33a1c24cf7b8bdf63dabaf9e0a1baab8408a66f995d05df719261248cbf27ee5b230a350109053ddb0420a
6
+ metadata.gz: 5ed8ad1fd6834f0b251079eb7d8b3f1805b0f88aa47486943f4bb7ad75196161c6c41a8e53364f755d46fe7fa6e51b8fb83ab518747369d07cdb0cb360689866
7
+ data.tar.gz: 53acff0bb1ea78fada8a9d8e6b3084ccb02209b42a1b19f99a80d792706cb55f1a2d5e97b48ee1fa54cc8b2ee8f7947feecd7a1465f24e18f9cdb86a499de8fa
data/Dockerfile CHANGED
@@ -8,7 +8,7 @@ RUN df -h
8
8
 
9
9
  RUN apt-get update
10
10
 
11
- RUN apt-get install -y ruby2.5-dev dnsutils ansible build-essential python-pip curl
11
+ RUN apt-get install -y ruby2.5-dev dnsutils ansible build-essential python-pip curl openssh-client
12
12
 
13
13
  RUN apt-get upgrade -y
14
14
 
@@ -45,6 +45,7 @@ $opt = Optimist::options do
45
45
  opt :habitats, "Limit scope of searches to the named accounts/projects/subscriptions, instead of search all habitats visible to our credentials.", :required => false, :type => :strings
46
46
  opt :regions, "Restrict to operating on a subset of available regions, instead of all that we know about.", :require => false, :type => :strings
47
47
  opt :scrub, "Whether to set scrub_mu_isms in the BoKs we generate", :default => $MU_CFG.has_key?('adopt_scrub_mu_isms') ? $MU_CFG['adopt_scrub_mu_isms'] : false
48
+ opt :pattern, "Only adopt resources whose resource name would match this pattern. Must be a valid regular expression. Alphabetical characters will be treated case-insensitively.", :required => false, :type => :string
48
49
  end
49
50
 
50
51
  ok = true
@@ -60,6 +61,16 @@ if $opt[:diff]
60
61
  $opt[:savedeploys] = false
61
62
  end
62
63
 
64
+ pattern = nil
65
+ if $opt[:pattern]
66
+ begin
67
+ pattern = Regexp.new($opt[:pattern], true)
68
+ rescue RegexpError => e
69
+ MU.log "Invalid --pattern option: #{e.message}", MU::ERR
70
+ exit 1
71
+ end
72
+ end
73
+
63
74
  types = []
64
75
  $opt[:types].each { |t|
65
76
  t_name = t.gsub(/-/, "_")
@@ -98,7 +109,7 @@ if !ok
98
109
  exit 1
99
110
  end
100
111
 
101
- adoption = MU::Adoption.new(clouds: clouds, types: types, parent: $opt[:parent], billing: $opt[:billing], sources: $opt[:sources], credentials: $opt[:credentials], group_by: $opt[:grouping].to_sym, savedeploys: $opt[:savedeploys], diff: $opt[:diff], habitats: $opt[:habitats], scrub_mu_isms: $opt[:scrub], regions: $opt[:regions], merge: $opt[:merge_changes])
112
+ adoption = MU::Adoption.new(clouds: clouds, types: types, parent: $opt[:parent], billing: $opt[:billing], sources: $opt[:sources], credentials: $opt[:credentials], group_by: $opt[:grouping].to_sym, savedeploys: $opt[:savedeploys], diff: $opt[:diff], habitats: $opt[:habitats], scrub_mu_isms: $opt[:scrub], regions: $opt[:regions], merge: $opt[:merge_changes], pattern: pattern)
102
113
  found = adoption.scrapeClouds
103
114
  if found.nil? or found.empty?
104
115
  MU.log "No resources found to adopt", MU::WARN, details: {"clouds" => clouds, "types" => types }
@@ -134,7 +134,7 @@ def loadMuConfig(default_cfg_overrides = nil)
134
134
  }
135
135
  end
136
136
 
137
- global_cfg = { "config_files" => [] }
137
+ global_cfg = { "config_files" => [], "overridden_keys" => [] }
138
138
  if File.exist?(cfgPath)
139
139
  global_cfg = YAML.load(File.read(cfgPath))
140
140
  global_cfg["config_files"] = [cfgPath]
@@ -147,6 +147,7 @@ def loadMuConfig(default_cfg_overrides = nil)
147
147
  if localfile
148
148
  global_cfg.merge!(localfile)
149
149
  global_cfg["config_files"] << "#{home}/.mu.yaml"
150
+ global_cfg["overridden_keys"] = localfile.keys
150
151
  end
151
152
  end
152
153
  if !global_cfg.has_key?("installdir")
@@ -34,6 +34,7 @@ Usage:
34
34
  #{$0} [-m <#>] [-f] [-v] [specific test BoK to run [...]]
35
35
  EOS
36
36
  opt :max_threads, "Environment to set on creation.", :require => false, :default => 3, :type => :integer
37
+ opt :max_retries, "Number of times to retry failed tests in --dryrun mode.", :require => false, :default => 2, :type => :integer
37
38
  opt :full, "Actually run deploys, instead of --dryrun", :require => false, :default => false
38
39
  opt :verbose, "Show more information while running", :require => false, :default => false
39
40
  end
@@ -121,8 +122,19 @@ def execCommand(cmd, results_stash)
121
122
  }
122
123
 
123
124
  ok = true
124
- output = %x{#{cmd} 2>&1}
125
- ok = false if $?.exitstatus != 0
125
+ retries = 0
126
+ begin
127
+ output = %x{#{cmd} 2>&1}
128
+ if $?.exitstatus != 0
129
+ ok = false
130
+ retries += 1
131
+ if $opts[:verbose] and !$opts[:full] and retries <= $opts[:max_retries]
132
+ puts "#{cmd} RETRY #{retries.to_s}".light_red
133
+ end
134
+ else
135
+ ok = true
136
+ end
137
+ end while !ok and !$opts[:full] and retries <= $opts[:max_retries]
126
138
 
127
139
  results_stash["output"] += output
128
140
 
@@ -17,8 +17,8 @@ end
17
17
 
18
18
  Gem::Specification.new do |s|
19
19
  s.name = 'cloud-mu'
20
- s.version = '3.2.0'
21
- s.date = '2020-06-16'
20
+ s.version = '3.3.0'
21
+ s.date = '2020-09-22'
22
22
  s.require_paths = ['modules']
23
23
  s.required_ruby_version = '>= 2.4'
24
24
  s.summary = "The eGTLabs Mu toolkit for unified cloud deployments"
@@ -57,7 +57,7 @@ EOF
57
57
  s.add_runtime_dependency 'rack', "~> 2.0"
58
58
  s.add_runtime_dependency 'ruby-graphviz', "~> 1.2"
59
59
  s.add_runtime_dependency 'rubocop', '~> 0.58'
60
- s.add_runtime_dependency 'rubyzip', "~> 2.0"
60
+ s.add_runtime_dependency 'rubyzip', "~> 2.3"
61
61
  s.add_runtime_dependency 'simple-password-gen', "~> 0.1"
62
62
  s.add_runtime_dependency 'slack-notifier', "~> 2.3"
63
63
  s.add_runtime_dependency 'solve', '~> 4.0'
@@ -299,8 +299,8 @@ module MU
299
299
  # Wrapper class for temporary Exceptions. Gives our internals something to
300
300
  # inherit that will log a notice message appropriately before bubbling up.
301
301
  class MuNonFatal < StandardError
302
- def initialize(message = nil, silent: false)
303
- MU.log message, MU::NOTICE if !message.nil? and !silent
302
+ def initialize(message = nil, silent: false, details: nil)
303
+ MU.log message, MU::NOTICE, details: details if !message.nil? and !silent
304
304
  if MU.verbosity == MU::Logger::SILENT
305
305
  super ""
306
306
  else
@@ -30,8 +30,7 @@ module MU
30
30
  :omnibus => "Jam everything into one monolothic configuration"
31
31
  }
32
32
 
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)
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, regions: [], merge: false, pattern: nil)
35
34
  @scraped = {}
36
35
  @clouds = clouds
37
36
  @types = types
@@ -49,6 +48,7 @@ module MU
49
48
  @habitats ||= []
50
49
  @scrub_mu_isms = scrub_mu_isms
51
50
  @merge = merge
51
+ @pattern = pattern
52
52
  end
53
53
 
54
54
  # Walk cloud providers with available credentials to discover resources
@@ -127,6 +127,7 @@ module MU
127
127
  if obj.habitat and !cloudclass.listHabitats(credset).include?(obj.habitat)
128
128
  next
129
129
  end
130
+
130
131
  # XXX apply any filters (e.g. MU-ID tags)
131
132
  if obj.cloud_id.nil?
132
133
  MU.log "This damn thing gave me no cloud id, what do I even do with that", MU::ERR, details: obj
@@ -292,7 +293,7 @@ module MU
292
293
  start = Time.now
293
294
 
294
295
  kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats, types: @types)
295
- if kitten_cfg
296
+ if kitten_cfg and (!@pattern or @pattern.match(kitten_cfg['name']))
296
297
  print "."
297
298
  kitten_cfg.delete("credentials") if @target_creds
298
299
  class_semaphore.synchronize {
@@ -791,8 +792,7 @@ module MU
791
792
  elsif hashcfg["id"] and !hashcfg["name"]
792
793
  hashcfg.delete("deploy_id")
793
794
  else
794
- pp parent.cloud_desc
795
- raise Incomplete, "Failed to resolve reference on behalf of #{parent}"
795
+ raise Incomplete.new "Failed to resolve reference on behalf of #{parent}", details: hashcfg
796
796
  end
797
797
  hashcfg.delete("deploy_id") if hashcfg['deploy_id'] == deploy.deploy_id
798
798
 
@@ -32,7 +32,7 @@ module MU
32
32
 
33
33
  # Resource types, in the order in which we generally have to clean them up
34
34
  # to disentangle them from one another.
35
- 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"]
35
+ TYPES_IN_ORDER = ["Collection", "CDN", "Endpoint", "Function", "ServerPool", "ContainerCluster", "SearchDomain", "Server", "MsgQueue", "Database", "CacheCluster", "StoragePool", "LoadBalancer", "NoSQLDB", "FirewallRule", "Alarm", "Notifier", "Log", "Job", "VPC", "Role", "Group", "User", "Bucket", "DNSZone", "Collection"]
36
36
 
37
37
  # Purge all resources associated with a deployment.
38
38
  # @param deploy_id [String]: The identifier of the deployment to remove (typically seen in the MU-ID tag on a resource).
@@ -52,6 +52,7 @@ module MU
52
52
  @onlycloud = onlycloud
53
53
  @skipcloud = skipcloud
54
54
  @ignoremaster = ignoremaster
55
+ @deploy_id = deploy_id
55
56
 
56
57
  if @skipcloud and @onlycloud # you actually mean noop
57
58
  @onlycloud = @skipcloud = false
@@ -217,51 +218,69 @@ module MU
217
218
  cloudclass = MU::Cloud.cloudClass(cloud)
218
219
  habitatclass = MU::Cloud.resourceClass(cloud, "Habitat")
219
220
 
220
- projects = []
221
- if habitats
222
- projects = habitats
223
- else
221
+ if !habitats
222
+ habitats = []
224
223
  if $MU_CFG and $MU_CFG[cloud.downcase] and
225
224
  $MU_CFG[cloud.downcase][credset] and
226
225
  $MU_CFG[cloud.downcase][credset]["project"]
227
226
  # XXX GCP credential schema needs an array for projects
228
- projects << $MU_CFG[cloud.downcase][credset]["project"]
227
+ habitats << $MU_CFG[cloud.downcase][credset]["project"]
229
228
  end
230
229
  begin
231
- projects.concat(cloudclass.listHabitats(credset, use_cache: false))
230
+ habitats.concat(cloudclass.listHabitats(credset, use_cache: false))
232
231
  rescue NoMethodError
233
232
  end
234
233
  end
235
234
 
236
- if projects == []
237
- projects << "" # dummy
235
+ if habitats == []
236
+ habitats << "" # dummy
238
237
  MU.log "Checking for #{cloud}/#{credset} resources from #{MU.deploy_id} in #{region}", MU::NOTICE
239
238
  end
240
- projects.uniq!
239
+ habitats.uniq!
241
240
 
242
241
  # We do these in an order that unrolls dependent resources
243
242
  # sensibly, and we hit :Collection twice because AWS
244
243
  # CloudFormation sometimes fails internally.
245
- projectthreads = []
246
- projects.each { |project|
247
- if habitats and !habitats.empty? and project != ""
248
- next if !habitats.include?(project)
244
+ habitat_threads = []
245
+ habitats.each { |habitat|
246
+ if habitats and !habitats.empty? and habitat != ""
247
+ next if !habitats.include?(habitat)
249
248
  end
250
- if @habitatsused and !@habitatsused.empty? and project != ""
251
- next if !@habitatsused.include?(project)
249
+ if @habitatsused and !@habitatsused.empty? and habitat != ""
250
+ next if !@habitatsused.include?(habitat)
252
251
  end
253
- next if !habitatclass.isLive?(project, credset)
252
+ next if !habitatclass.isLive?(habitat, credset)
254
253
 
255
- projectthreads << Thread.new {
254
+ habitat_threads << Thread.new {
255
+ Thread.current.thread_variable_set("name", "#{cloud}/#{credset}/#{habitat}/#{region}")
256
256
  Thread.abort_on_exception = false
257
- if !cleanHabitat(cloud, credset, region, project, global_vs_region_semaphore, global_done)
257
+ if !cleanHabitat(cloud, credset, region, habitat, global_vs_region_semaphore, global_done)
258
258
  had_failures = true
259
259
  end
260
260
  } # TYPES_IN_ORDER.each { |t|
261
- } # projects.each { |project|
262
- projectthreads.each do |t|
263
- t.join
264
- end
261
+ } # habitats.each { |habitat|
262
+
263
+ last_checkin = Time.now
264
+ begin
265
+ deletia = []
266
+ habitat_threads.each { |t|
267
+ if !t.status
268
+ t.join
269
+ deletia << t
270
+ end
271
+ }
272
+ deletia.each { |t|
273
+ habitat_threads.delete(t)
274
+ }
275
+ if (Time.now - last_checkin) > 120
276
+ list = habitat_threads.map { |t|
277
+ t.thread_variable_get("name") + (t.thread_variable_get("type") ? "/"+t.thread_variable_get("type") : "")
278
+ }
279
+ MU.log "Waiting on #{habitat_threads.size.to_s} habitat#{habitat_threads.size > 1 ? "s" : ""} in region #{region}", MU::NOTICE, details: list
280
+ last_checkin = Time.now
281
+ end
282
+ sleep 10 if !habitat_threads.empty?
283
+ end while !habitat_threads.empty?
265
284
 
266
285
  had_failures
267
286
  end
@@ -311,7 +330,8 @@ module MU
311
330
  next
312
331
  end
313
332
  }
314
- had_failures = true
333
+
334
+ had_failures
315
335
  end
316
336
  private_class_method :cleanHabitat
317
337
 
@@ -322,6 +342,7 @@ module MU
322
342
  # @param flags [Hash]:
323
343
  # @param region [String]:
324
344
  def self.call_cleanup(type, credset, provider, flags, region)
345
+ Thread.current.thread_variable_set("type", type)
325
346
  if @mommacat.nil? or @mommacat.numKittens(types: [type]) > 0
326
347
  if @mommacat
327
348
 
@@ -344,7 +365,8 @@ module MU
344
365
  region: region,
345
366
  cloud: provider,
346
367
  flags: flags,
347
- credentials: credset
368
+ credentials: credset,
369
+ deploy_id: @deploy_id
348
370
  )
349
371
  else
350
372
  true
@@ -148,6 +148,12 @@ module MU
148
148
  # Stub base class; real implementations generated at runtime
149
149
  class NoSQLDB;
150
150
  end
151
+ # Stub base class; real implementations generated at runtime
152
+ class Job;
153
+ end
154
+ # Stub base class; real implementations generated at runtime
155
+ class CDN;
156
+ end
151
157
 
152
158
  # Denotes a resource implementation which is missing significant
153
159
  # functionality or is largely untested.
@@ -436,7 +442,29 @@ module MU
436
442
  :cfg_plural => "nosqldbs",
437
443
  :interface => self.const_get("NoSQLDB"),
438
444
  :deps_wait_on_my_creation => true,
439
- :waits_on_parent_completion => true,
445
+ :waits_on_parent_completion => false,
446
+ :class => @@generic_class_methods,
447
+ :instance => @@generic_instance_methods + [:groom]
448
+ },
449
+ :Job => {
450
+ :has_multiples => false,
451
+ :can_live_in_vpc => false,
452
+ :cfg_name => "job",
453
+ :cfg_plural => "jobs",
454
+ :interface => self.const_get("Job"),
455
+ :deps_wait_on_my_creation => true,
456
+ :waits_on_parent_completion => false,
457
+ :class => @@generic_class_methods,
458
+ :instance => @@generic_instance_methods + [:groom]
459
+ },
460
+ :CDN => {
461
+ :has_multiples => false,
462
+ :can_live_in_vpc => false,
463
+ :cfg_name => "cdn",
464
+ :cfg_plural => "cdns",
465
+ :interface => self.const_get("CDN"),
466
+ :deps_wait_on_my_creation => true,
467
+ :waits_on_parent_completion => false,
440
468
  :class => @@generic_class_methods,
441
469
  :instance => @@generic_instance_methods + [:groom]
442
470
  }
@@ -29,8 +29,6 @@ module MU
29
29
 
30
30
  # Wrapper for {MU::Cloud::AWS::DNSZone.manageRecord}. Spawns threads to create all
31
31
  # requested records in background and returns immediately.
32
- # @param cfg [Array]: An array of parsed {MU::Config::BasketofKittens::dnszones::records} objects.
33
- # @param target [String]: Optional target for the records to be created. Overrides targets embedded in cfg records.
34
32
  def self.createRecordsFromConfig(*flags)
35
33
  cloudclass = MU::Cloud.resourceClass(MU::Config.defaultCloud, "DNSZone")
36
34
  if !flags.nil? and flags.size == 1
@@ -227,6 +227,10 @@ module MU
227
227
  }
228
228
  end
229
229
 
230
+ MU::MommaCat.listOptionalTags.each_pair { |k, v|
231
+ @tags[k] ||= v if v
232
+ }
233
+
230
234
  if @cloudparentclass.respond_to?(:resourceInitHook)
231
235
  @cloudparentclass.resourceInitHook(self, @deploy)
232
236
  end
@@ -265,6 +269,7 @@ module MU
265
269
  attr_accessor :mu_windows_name # XXX might be ok as reader now
266
270
  end
267
271
  end
272
+ @tags["Name"] ||= @mu_name if @mu_name
268
273
  end
269
274
 
270
275
  end
@@ -893,10 +898,11 @@ module MU
893
898
  elsif method == :notify
894
899
  if retval.nil?
895
900
  MU.log self.to_s+" didn't return any metadata from notify", MU::WARN, details: @cloudobj.cloud_desc
901
+ else
902
+ retval['cloud_id'] = @cloudobj.cloud_id.to_s if !@cloudobj.cloud_id.nil?
903
+ retval['mu_name'] = @cloudobj.mu_name if !@cloudobj.mu_name.nil?
904
+ @deploy.notify(self.class.cfg_plural, @config['name'], retval, triggering_node: @cloudobj, delayed_save: @delayed_save) if !@deploy.nil?
896
905
  end
897
- retval['cloud_id'] = @cloudobj.cloud_id.to_s if !@cloudobj.cloud_id.nil?
898
- retval['mu_name'] = @cloudobj.mu_name if !@cloudobj.mu_name.nil?
899
- @deploy.notify(self.class.cfg_plural, @config['name'], retval, triggering_node: @cloudobj, delayed_save: @delayed_save) if !@deploy.nil?
900
906
  end
901
907
  @method_semaphore.synchronize {
902
908
  @method_locks.delete(method)
@@ -126,6 +126,10 @@ module MU
126
126
  clouds = [params[:cloud]]
127
127
  params.delete(:cloud)
128
128
  end
129
+ params[:deploy_id] ||= MU.deploy_id
130
+ if !params[:deploy_id] or params[:deploy_id].empty?
131
+ raise MuError, "Can't call cleanup methods without a deploy id"
132
+ end
129
133
 
130
134
  clouds.each { |cloud|
131
135
  begin
@@ -437,7 +437,7 @@ module MU
437
437
  # @param type [String]
438
438
  # @param phase [String]
439
439
  # @param no_create_wait [Boolean]
440
- def self.addDependency(resource, name, type, phase: nil, no_create_wait: false)
440
+ def self.addDependency(resource, name, type, phase: "create", no_create_wait: false)
441
441
  if ![nil, "create", "groom"].include?(phase)
442
442
  raise MuError, "Invalid phase '#{phase}' while adding dependency #{type} #{name} to #{resource['name']}"
443
443
  end
@@ -50,6 +50,24 @@ module MU
50
50
  "default" => "index.html",
51
51
  "description" => "If +web_enabled+, return this object when \"diretory\" (a path not ending in a key/object) is invoked."
52
52
  },
53
+ "upload" => {
54
+ "type" => "array",
55
+ "items" => {
56
+ "type" => "object",
57
+ "description" => "Upload objects to a bucket, where supported",
58
+ "required" => ["source", "destination"],
59
+ "properties" => {
60
+ "source" => {
61
+ "type" => "string",
62
+ "description" => "A file or directory to upload. If a directory is specified, it will be recursively mirrored to the bucket +destination+."
63
+ },
64
+ "destination" => {
65
+ "type" => "string",
66
+ "description" => "Path to which +source+ file(s) will be uploaded in the bucket, relative to +/+"
67
+ }
68
+ }
69
+ }
70
+ },
53
71
  "policies" => {
54
72
  "type" => "array",
55
73
  "items" => MU::Config::Role.policy_primitive(subobjects: true, grant_to: true, permissions_optional: true, targets_optional: true)
@@ -59,12 +77,23 @@ module MU
59
77
  end
60
78
 
61
79
  # Generic pre-processing of {MU::Config::BasketofKittens::buckets}, bare and unvalidated.
62
- # @param _bucket [Hash]: The resource to process and validate
80
+ # @param bucket [Hash]: The resource to process and validate
63
81
  # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
64
82
  # @return [Boolean]: True if validation succeeded, False otherwise
65
- def self.validate(_bucket, _configurator)
83
+ def self.validate(bucket, _configurator)
66
84
  ok = true
67
85
 
86
+ if bucket['upload']
87
+ bucket['upload'].each { |batch|
88
+ if !File.exists?(batch['source'])
89
+ MU.log "Bucket '#{bucket['name']}' specifies upload for file/directory that does not exist", MU::ERR, details: batch
90
+ ok = false
91
+ next
92
+ end
93
+ batch['source'] = File.realpath(File.expand_path(batch['source']))
94
+ }
95
+ end
96
+
68
97
  ok
69
98
  end
70
99