cloud-mu 3.1.5 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) 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 +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -0,0 +1,169 @@
1
+ # Copyright:: Copyright (c) 2020 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module MU
16
+ # Plugins under this namespace serve as interfaces to cloud providers and
17
+ # other provisioning layers.
18
+ class Cloud
19
+
20
+ # In this file: generic class method wrappers for all resource types.
21
+
22
+ @@resource_types.keys.each { |name|
23
+ Object.const_get("MU").const_get("Cloud").const_get(name).class_eval {
24
+
25
+ def self.shortname
26
+ name.sub(/.*?::([^:]+)$/, '\1')
27
+ end
28
+
29
+ def self.cfg_plural
30
+ MU::Cloud.resource_types[shortname.to_sym][:cfg_plural]
31
+ end
32
+
33
+ def self.has_multiples
34
+ MU::Cloud.resource_types[shortname.to_sym][:has_multiples]
35
+ end
36
+
37
+ def self.cfg_name
38
+ MU::Cloud.resource_types[shortname.to_sym][:cfg_name]
39
+ end
40
+
41
+ def self.can_live_in_vpc
42
+ MU::Cloud.resource_types[shortname.to_sym][:can_live_in_vpc]
43
+ end
44
+
45
+ def self.waits_on_parent_completion
46
+ MU::Cloud.resource_types[shortname.to_sym][:waits_on_parent_completion]
47
+ end
48
+
49
+ def self.deps_wait_on_my_creation
50
+ MU::Cloud.resource_types[shortname.to_sym][:deps_wait_on_my_creation]
51
+ end
52
+
53
+ # Defaults any resources that don't declare their release-readiness to
54
+ # ALPHA. That'll learn 'em.
55
+ def self.quality
56
+ MU::Cloud::ALPHA
57
+ end
58
+
59
+ # Return a list of "container" artifacts, by class, that apply to this
60
+ # resource type in a cloud provider. This is so methods that call find
61
+ # know whether to call +find+ with identifiers for parent resources.
62
+ # This is similar in purpose to the +isGlobal?+ resource class method,
63
+ # which tells our search functions whether or not a resource scopes to
64
+ # a region. In almost all cases this is one-entry list consisting of
65
+ # +:Habitat+. Notable exceptions include most implementations of
66
+ # +Habitat+, which either reside inside a +:Folder+ or nothing at all;
67
+ # whereas a +:Folder+ tends to not have any containing parent. Very few
68
+ # resource implementations will need to override this.
69
+ # A +nil+ entry in this list is interpreted as "this resource can be
70
+ # global."
71
+ # @return [Array<Symbol,nil>]
72
+ def self.canLiveIn
73
+ if self.shortname == "Folder"
74
+ [nil, :Folder]
75
+ elsif self.shortname == "Habitat"
76
+ [:Folder]
77
+ else
78
+ [:Habitat]
79
+ end
80
+ end
81
+
82
+ def self.find(*flags)
83
+ allfound = {}
84
+
85
+ MU::Cloud.availableClouds.each { |cloud|
86
+ begin
87
+ args = flags.first
88
+ next if args[:cloud] and args[:cloud] != cloud
89
+ # skip this cloud if we have a region argument that makes no
90
+ # sense there
91
+ cloudbase = MU::Cloud.cloudClass(cloud)
92
+ next if cloudbase.listCredentials.nil? or cloudbase.listCredentials.empty? or cloudbase.credConfig(args[:credentials]).nil?
93
+ if args[:region] and cloudbase.respond_to?(:listRegions)
94
+ if !cloudbase.listRegions(credentials: args[:credentials])
95
+ MU.log "Failed to get region list for credentials #{args[:credentials]} in cloud #{cloud}", MU::ERR, details: caller
96
+ else
97
+ next if !cloudbase.listRegions(credentials: args[:credentials]).include?(args[:region])
98
+ end
99
+ end
100
+ begin
101
+ cloudclass = MU::Cloud.resourceClass(cloud, shortname)
102
+ rescue MU::MuError
103
+ next
104
+ end
105
+
106
+ found = cloudclass.find(args)
107
+ if !found.nil?
108
+ if found.is_a?(Hash)
109
+ allfound.merge!(found)
110
+ else
111
+ raise MuError, "#{cloudclass}.find returned a non-Hash result"
112
+ end
113
+ end
114
+ rescue MuCloudResourceNotImplemented
115
+ end
116
+ }
117
+ allfound
118
+ end
119
+
120
+ # Wrapper for the cleanup class method of underlying cloud object implementations.
121
+ def self.cleanup(*flags)
122
+ ok = true
123
+ params = flags.first
124
+ clouds = MU::Cloud.supportedClouds
125
+ if params[:cloud]
126
+ clouds = [params[:cloud]]
127
+ params.delete(:cloud)
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
133
+
134
+ clouds.each { |cloud|
135
+ begin
136
+ cloudclass = MU::Cloud.resourceClass(cloud, shortname)
137
+
138
+ if cloudclass.isGlobal?
139
+ params.delete(:region)
140
+ end
141
+
142
+ raise MuCloudResourceNotImplemented if !cloudclass.respond_to?(:cleanup) or cloudclass.method(:cleanup).owner.to_s != "#<Class:#{cloudclass}>"
143
+ MU.log "Invoking #{cloudclass}.cleanup from #{shortname}", MU::DEBUG, details: flags
144
+ cloudclass.cleanup(params)
145
+ rescue MuCloudResourceNotImplemented
146
+ MU.log "No #{cloud} implementation of #{shortname}.cleanup, skipping", MU::DEBUG, details: flags
147
+ rescue StandardError => e
148
+ in_msg = cloud
149
+ if params and params[:region]
150
+ in_msg += " "+params[:region]
151
+ end
152
+ if params and params[:flags] and params[:flags]["project"] and !params[:flags]["project"].empty?
153
+ in_msg += " project "+params[:flags]["project"]
154
+ end
155
+ MU.log "Skipping #{shortname} cleanup method in #{in_msg} due to #{e.class.name}: #{e.message}", MU::WARN, details: e.backtrace
156
+ ok = false
157
+ end
158
+ }
159
+ MU::MommaCat.unlockAll
160
+
161
+ ok
162
+ end
163
+
164
+ } # end dynamic class generation block
165
+ } # end resource type iteration
166
+
167
+ end
168
+
169
+ end
@@ -77,7 +77,7 @@ module MU
77
77
  if config.is_a?(Hash)
78
78
  newhash = {}
79
79
  config.each_pair { |key, val|
80
- next if remove_runtime_keys and key.match(/^#MU_/)
80
+ next if remove_runtime_keys and (key.nil? or key.match(/^#MU_/))
81
81
  next if val.is_a?(Array) and val.empty?
82
82
  newhash[key] = self.manxify(val, remove_runtime_keys: remove_runtime_keys)
83
83
  }
@@ -430,6 +430,39 @@ module MU
430
430
  @config.freeze
431
431
  end
432
432
 
433
+ # Insert a dependency into the config hash of a resource, with sensible
434
+ # error checking and de-duplication.
435
+ # @param resource [Hash]
436
+ # @param name [String]
437
+ # @param type [String]
438
+ # @param phase [String]
439
+ # @param no_create_wait [Boolean]
440
+ def self.addDependency(resource, name, type, phase: "create", no_create_wait: false)
441
+ if ![nil, "create", "groom"].include?(phase)
442
+ raise MuError, "Invalid phase '#{phase}' while adding dependency #{type} #{name} to #{resource['name']}"
443
+ end
444
+ resource['dependencies'] ||= []
445
+ _shortclass, cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(type)
446
+
447
+ resource['dependencies'].each { |dep|
448
+ if dep['type'] == cfg_name and dep['name'].to_s == name.to_s
449
+ dep["no_create_wait"] = no_create_wait
450
+ dep["phase"] = phase if phase
451
+ return
452
+ end
453
+ }
454
+
455
+ newdep = {
456
+ "type" => cfg_name,
457
+ "name" => name.to_s,
458
+ "no_create_wait" => no_create_wait
459
+ }
460
+ newdep["phase"] = phase if phase
461
+
462
+ resource['dependencies'] << newdep
463
+
464
+ end
465
+
433
466
  # See if a given resource is configured in the current stack
434
467
  # @param name [String]: The name of the resource being checked
435
468
  # @param type [String]: The type of resource being checked
@@ -485,7 +518,8 @@ module MU
485
518
  # @param ignore_duplicates [Boolean]: Do not raise an exception if we attempt to insert a resource with a +name+ field that's already in use
486
519
  def insertKitten(descriptor, type, delay_validation = false, ignore_duplicates: false, overwrite: false)
487
520
  append = false
488
- # start = Time.now
521
+ start = Time.now
522
+
489
523
  shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
490
524
  MU.log "insertKitten on #{cfg_name} #{descriptor['name']} (delay_validation: #{delay_validation.to_s})", MU::DEBUG, details: caller[0]
491
525
 
@@ -525,7 +559,7 @@ module MU
525
559
  # cloud-specific schema.
526
560
  schemaclass = Object.const_get("MU").const_get("Config").const_get(shortclass)
527
561
  myschema = Marshal.load(Marshal.dump(MU::Config.schema["properties"][cfg_plural]["items"]))
528
- more_required, more_schema = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get(shortclass.to_s).schema(self)
562
+ more_required, more_schema = MU::Cloud.resourceClass(descriptor["cloud"], type).schema(self)
529
563
  if more_schema
530
564
  MU::Config.schemaMerge(myschema["properties"], more_schema, descriptor["cloud"])
531
565
  end
@@ -544,7 +578,7 @@ module MU
544
578
  end
545
579
 
546
580
  # Make sure a sensible region has been targeted, if applicable
547
- classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"])
581
+ classobj = MU::Cloud.cloudClass(descriptor["cloud"])
548
582
  if descriptor["region"]
549
583
  valid_regions = classobj.listRegions
550
584
  if !valid_regions.include?(descriptor["region"])
@@ -557,11 +591,7 @@ module MU
557
591
  if descriptor['project'].nil?
558
592
  descriptor.delete('project')
559
593
  elsif haveLitterMate?(descriptor['project'], "habitats")
560
- descriptor['dependencies'] ||= []
561
- descriptor['dependencies'] << {
562
- "type" => "habitat",
563
- "name" => descriptor['project']
564
- }
594
+ MU::Config.addDependency(descriptor, descriptor['project'], "habitat")
565
595
  end
566
596
  end
567
597
 
@@ -589,21 +619,16 @@ module MU
589
619
  if !descriptor["vpc"]["name"].nil? and
590
620
  haveLitterMate?(descriptor["vpc"]["name"], "vpcs") and
591
621
  descriptor["vpc"]['deploy_id'].nil? and
592
- descriptor["vpc"]['id'].nil?
593
- descriptor["dependencies"] << {
594
- "type" => "vpc",
595
- "name" => descriptor["vpc"]["name"],
596
- }
622
+ descriptor["vpc"]['id'].nil? and
623
+ !(cfg_name == "vpc" and descriptor['name'] == descriptor['vpc']['name'])
624
+ MU::Config.addDependency(descriptor, descriptor['vpc']['name'], "vpc")
597
625
  siblingvpc = haveLitterMate?(descriptor["vpc"]["name"], "vpcs")
598
626
 
599
627
  if siblingvpc and siblingvpc['bastion'] and
600
628
  ["server", "server_pool", "container_cluster"].include?(cfg_name) and
601
629
  !descriptor['bastion']
602
- if descriptor['name'] != siblingvpc['bastion'].to_h['name']
603
- descriptor["dependencies"] << {
604
- "type" => "server",
605
- "name" => siblingvpc['bastion'].to_h['name']
606
- }
630
+ if descriptor['name'] != siblingvpc['bastion']['name']
631
+ MU::Config.addDependency(descriptor, siblingvpc['bastion']['name'], "server")
607
632
  end
608
633
  end
609
634
 
@@ -665,7 +690,6 @@ module MU
665
690
  if (descriptor['ingress_rules'] or
666
691
  ["server", "server_pool", "database", "cache_cluster"].include?(cfg_name))
667
692
  descriptor['ingress_rules'] ||= []
668
- fw_classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get("FirewallRule")
669
693
 
670
694
  acl = haveLitterMate?(fwname, "firewall_rules")
671
695
  already_exists = !acl.nil?
@@ -676,7 +700,7 @@ module MU
676
700
  "region" => descriptor['region'],
677
701
  "credentials" => descriptor["credentials"]
678
702
  }
679
- if !fw_classobj.isGlobal?
703
+ if !MU::Cloud.resourceClass(descriptor["cloud"], "FirewallRule").isGlobal?
680
704
  acl['region'] = descriptor['region']
681
705
  acl['region'] ||= classobj.myRegion(acl['credentials'])
682
706
  else
@@ -702,10 +726,7 @@ module MU
702
726
  if !descriptor["loadbalancers"].nil?
703
727
  descriptor["loadbalancers"].each { |lb|
704
728
  if !lb["concurrent_load_balancer"].nil?
705
- descriptor["dependencies"] << {
706
- "type" => "loadbalancer",
707
- "name" => lb["concurrent_load_balancer"]
708
- }
729
+ MU::Config.addDependency(descriptor, lb["concurrent_load_balancer"], "loadbalancer")
709
730
  end
710
731
  }
711
732
  end
@@ -714,10 +735,7 @@ module MU
714
735
  if !descriptor["storage_pools"].nil?
715
736
  descriptor["storage_pools"].each { |sp|
716
737
  if sp["name"]
717
- descriptor["dependencies"] << {
718
- "type" => "storage_pool",
719
- "name" => sp["name"]
720
- }
738
+ MU::Config.addDependency(descriptor, sp["name"], "storage_pool")
721
739
  end
722
740
  }
723
741
  end
@@ -728,10 +746,7 @@ module MU
728
746
  next if !acl_include["name"] and !acl_include["rule_name"]
729
747
  acl_include["name"] ||= acl_include["rule_name"]
730
748
  if haveLitterMate?(acl_include["name"], "firewall_rules")
731
- descriptor["dependencies"] << {
732
- "type" => "firewall_rule",
733
- "name" => acl_include["name"]
734
- }
749
+ MU::Config.addDependency(descriptor, acl_include["name"], "firewall_rule", no_create_wait: (cfg_name == "vpc"))
735
750
  elsif acl_include["name"]
736
751
  MU.log shortclass.to_s+" #{descriptor['name']} depends on FirewallRule #{acl_include["name"]}, but no such rule declared.", MU::ERR
737
752
  ok = false
@@ -831,7 +846,7 @@ module MU
831
846
  # Run the cloud class's deeper validation, unless we've already failed
832
847
  # on stuff that will cause spurious alarms further in
833
848
  if ok
834
- parser = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get(shortclass.to_s)
849
+ parser = MU::Cloud.resourceClass(descriptor['cloud'], type)
835
850
  original_descriptor = MU::Config.stripConfig(descriptor)
836
851
  passed = parser.validateConfig(descriptor, self)
837
852
 
@@ -841,7 +856,7 @@ module MU
841
856
  end
842
857
 
843
858
  # Make sure we've been configured with the right credentials
844
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(descriptor['cloud'])
859
+ cloudbase = MU::Cloud.cloudClass(descriptor['cloud'])
845
860
  credcfg = cloudbase.credConfig(descriptor['credentials'])
846
861
  if !credcfg or credcfg.empty?
847
862
  raise ValidationError, "#{descriptor['cloud']} #{cfg_name} #{descriptor['name']} declares credential set #{descriptor['credentials']}, but no such credentials exist for that cloud provider"
@@ -857,60 +872,92 @@ module MU
857
872
  @kittens[cfg_plural] << descriptor if append
858
873
  }
859
874
 
875
+ MU.log "insertKitten completed #{cfg_name} #{descriptor['name']} in #{sprintf("%.2fs", Time.now-start)}", MU::DEBUG
876
+
860
877
  ok
861
878
  end
862
879
 
863
880
  # For our resources which specify intra-stack dependencies, make sure those
864
881
  # dependencies are actually declared.
865
- # TODO check for loops
866
- def self.check_dependencies(config)
882
+ def check_dependencies
867
883
  ok = true
868
884
 
869
- config.each_pair { |type, values|
870
- if values.instance_of?(Array)
871
- values.each { |resource|
872
- if resource.kind_of?(Hash) and !resource["dependencies"].nil?
873
- append = []
874
- delete = []
875
- resource["dependencies"].each { |dependency|
876
- _shortclass, cfg_name, cfg_plural, _classname = MU::Cloud.getResourceNames(dependency["type"])
877
- found = false
878
- names_seen = []
879
- if !config[cfg_plural].nil?
880
- config[cfg_plural].each { |service|
881
- names_seen << service["name"].to_s
882
- found = true if service["name"].to_s == dependency["name"].to_s
883
- if service["virtual_name"]
884
- names_seen << service["virtual_name"].to_s
885
- if service["virtual_name"].to_s == dependency["name"].to_s
886
- found = true
887
- append_me = dependency.dup
888
- append_me['name'] = service['name']
889
- append << append_me
890
- delete << dependency
891
- end
892
- end
885
+ @config.each_pair { |type, values|
886
+ next if !values.instance_of?(Array)
887
+ _shortclass, cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(type, false)
888
+ next if !cfg_name
889
+ values.each { |resource|
890
+ next if !resource.kind_of?(Hash) or resource["dependencies"].nil?
891
+ addme = []
892
+ deleteme = []
893
+
894
+ resource["dependencies"].each { |dependency|
895
+ # make sure the thing we depend on really exists
896
+ sibling = haveLitterMate?(dependency['name'], dependency['type'])
897
+ if !sibling
898
+ MU.log "Missing dependency: #{type}{#{resource['name']}} needs #{cfg_name}{#{dependency['name']}}", MU::ERR
899
+ ok = false
900
+ next
901
+ end
902
+
903
+ # Fudge dependency declarations to quash virtual_names that we know
904
+ # are extraneous. Note that wee can't do all virtual names here; we
905
+ # have no way to guess which of a collection of resources is the
906
+ # real correct one.
907
+ if sibling['virtual_name'] == dependency['name']
908
+ real_resources = []
909
+ found_exact = false
910
+ resource["dependencies"].each { |dep_again|
911
+ if dep_again['type'] == dependency['type'] and sibling['name'] == dep_again['name']
912
+ dependency['name'] = sibling['name']
913
+ found_exact = true
914
+ break
915
+ end
916
+ }
917
+ if !found_exact
918
+ all_siblings = haveLitterMate?(dependency['name'], dependency['type'], has_multiple: true)
919
+ if all_siblings.size > 0
920
+ all_siblings.each { |s|
921
+ newguy = dependency.clone
922
+ newguy['name'] = s['name']
923
+ addme << newguy
893
924
  }
925
+ deleteme << dependency
926
+ MU.log "Expanding dependency which maps to virtual resources to all matching real resources", MU::NOTICE, details: { sibling['virtual_name'] => addme }
927
+ next
894
928
  end
895
- if !found
896
- MU.log "Missing dependency: #{type}{#{resource['name']}} needs #{cfg_name}{#{dependency['name']}}", MU::ERR, details: names_seen
929
+ end
930
+ end
931
+
932
+ # Check for a circular relationship that will lead to a deadlock
933
+ # when creating resource. This only goes one layer deep, and does
934
+ # not consider groom-phase deadlocks.
935
+ if dependency['phase'] == "groom" or dependency['no_create_wait'] or (
936
+ !MU::Cloud.resourceClass(sibling['cloud'], type).deps_wait_on_my_creation and
937
+ !MU::Cloud.resourceClass(resource['cloud'], type).waits_on_parent_completion
938
+ )
939
+ next
940
+ end
941
+
942
+ if sibling['dependencies']
943
+ sibling['dependencies'].each { |sib_dep|
944
+ next if sib_dep['type'] != cfg_name or sib_dep['no_create_wait']
945
+ cousin = haveLitterMate?(sib_dep['name'], sib_dep['type'])
946
+ if cousin and cousin['name'] == resource['name']
947
+ MU.log "Circular dependency between #{type} #{resource['name']} <=> #{dependency['type']} #{dependency['name']}", MU::ERR, details: [ resource['name'] => dependency, sibling['name'] => sib_dep ]
897
948
  ok = false
898
949
  end
899
950
  }
900
- if append.size > 0
901
- append.uniq!
902
- resource["dependencies"].concat(append)
903
- end
904
- if delete.size > 0
905
- delete.each { |delete_me|
906
- resource["dependencies"].delete(delete_me)
907
- }
908
- end
909
951
  end
910
952
  }
911
- end
953
+ resource["dependencies"].reject! { |dep| deleteme.include?(dep) }
954
+ resource["dependencies"].concat(addme)
955
+ resource["dependencies"].uniq!
956
+
957
+ }
912
958
  }
913
- return ok
959
+
960
+ ok
914
961
  end
915
962
 
916
963
  # Ugly text-manipulation to recursively resolve some placeholder strings
@@ -1191,12 +1238,7 @@ module MU
1191
1238
  "port" => db["port"],
1192
1239
  "sgs" => [cfg_name+server['name']]
1193
1240
  }
1194
-
1195
- ruleset["dependencies"] << {
1196
- "name" => cfg_name+server['name'],
1197
- "type" => "firewall_rule",
1198
- "no_create_wait" => true
1199
- }
1241
+ MU::Config.addDependency(ruleset, cfg_name+server['name'], "firewall_rule", no_create_wait: true)
1200
1242
  end
1201
1243
  }
1202
1244
  }
@@ -1214,7 +1256,7 @@ module MU
1214
1256
  types.each { |type|
1215
1257
  config[type] = @kittens[type] if @kittens[type].size > 0
1216
1258
  }
1217
- ok = false if !MU::Config.check_dependencies(config)
1259
+ ok = false if !check_dependencies
1218
1260
 
1219
1261
  # TODO enforce uniqueness of resource names
1220
1262
  raise ValidationError if !ok