cloud-mu 3.1.4 → 3.3.1

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +16 -12
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -1
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +37 -12
  23. data/cloud-mu.gemspec +5 -3
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  27. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  28. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  29. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  30. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  31. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  32. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  33. data/extras/clean-stock-amis +25 -19
  34. data/extras/generate-stock-images +1 -0
  35. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  36. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  37. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  38. data/modules/mommacat.ru +1 -1
  39. data/modules/mu.rb +158 -107
  40. data/modules/mu/adoption.rb +386 -59
  41. data/modules/mu/cleanup.rb +214 -303
  42. data/modules/mu/cloud.rb +128 -1632
  43. data/modules/mu/cloud/database.rb +49 -0
  44. data/modules/mu/cloud/dnszone.rb +44 -0
  45. data/modules/mu/cloud/machine_images.rb +212 -0
  46. data/modules/mu/cloud/providers.rb +81 -0
  47. data/modules/mu/cloud/resource_base.rb +926 -0
  48. data/modules/mu/cloud/server.rb +40 -0
  49. data/modules/mu/cloud/server_pool.rb +1 -0
  50. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  51. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  52. data/modules/mu/cloud/wrappers.rb +169 -0
  53. data/modules/mu/config.rb +135 -82
  54. data/modules/mu/config/alarm.rb +2 -6
  55. data/modules/mu/config/bucket.rb +32 -3
  56. data/modules/mu/config/cache_cluster.rb +2 -2
  57. data/modules/mu/config/cdn.rb +100 -0
  58. data/modules/mu/config/collection.rb +1 -1
  59. data/modules/mu/config/container_cluster.rb +7 -2
  60. data/modules/mu/config/database.rb +84 -105
  61. data/modules/mu/config/database.yml +1 -2
  62. data/modules/mu/config/dnszone.rb +5 -4
  63. data/modules/mu/config/doc_helpers.rb +5 -6
  64. data/modules/mu/config/endpoint.rb +2 -1
  65. data/modules/mu/config/firewall_rule.rb +3 -19
  66. data/modules/mu/config/folder.rb +1 -1
  67. data/modules/mu/config/function.rb +17 -8
  68. data/modules/mu/config/group.rb +1 -1
  69. data/modules/mu/config/habitat.rb +1 -1
  70. data/modules/mu/config/job.rb +89 -0
  71. data/modules/mu/config/loadbalancer.rb +57 -11
  72. data/modules/mu/config/log.rb +1 -1
  73. data/modules/mu/config/msg_queue.rb +1 -1
  74. data/modules/mu/config/nosqldb.rb +1 -1
  75. data/modules/mu/config/notifier.rb +8 -19
  76. data/modules/mu/config/ref.rb +92 -14
  77. data/modules/mu/config/role.rb +1 -1
  78. data/modules/mu/config/schema_helpers.rb +38 -37
  79. data/modules/mu/config/search_domain.rb +1 -1
  80. data/modules/mu/config/server.rb +12 -13
  81. data/modules/mu/config/server.yml +1 -0
  82. data/modules/mu/config/server_pool.rb +3 -7
  83. data/modules/mu/config/storage_pool.rb +1 -1
  84. data/modules/mu/config/tail.rb +11 -0
  85. data/modules/mu/config/user.rb +1 -1
  86. data/modules/mu/config/vpc.rb +27 -23
  87. data/modules/mu/config/vpc.yml +0 -1
  88. data/modules/mu/defaults/AWS.yaml +91 -68
  89. data/modules/mu/defaults/Azure.yaml +1 -0
  90. data/modules/mu/defaults/Google.yaml +1 -0
  91. data/modules/mu/deploy.rb +33 -19
  92. data/modules/mu/groomer.rb +16 -1
  93. data/modules/mu/groomers/ansible.rb +123 -21
  94. data/modules/mu/groomers/chef.rb +64 -11
  95. data/modules/mu/logger.rb +120 -144
  96. data/modules/mu/master.rb +97 -4
  97. data/modules/mu/master/ssl.rb +0 -1
  98. data/modules/mu/mommacat.rb +154 -867
  99. data/modules/mu/mommacat/daemon.rb +23 -14
  100. data/modules/mu/mommacat/naming.rb +110 -3
  101. data/modules/mu/mommacat/search.rb +495 -0
  102. data/modules/mu/mommacat/storage.rb +225 -192
  103. data/modules/mu/{clouds → providers}/README.md +1 -1
  104. data/modules/mu/{clouds → providers}/aws.rb +281 -64
  105. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  106. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  107. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  108. data/modules/mu/providers/aws/cdn.rb +782 -0
  109. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  110. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
  111. data/modules/mu/providers/aws/database.rb +1744 -0
  112. data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
  113. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  114. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
  115. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  116. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  117. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  118. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  119. data/modules/mu/providers/aws/job.rb +466 -0
  120. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
  121. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  122. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  123. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  124. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  125. data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
  126. data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
  127. data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
  128. data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
  129. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  130. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  131. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  132. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  133. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  134. data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
  135. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  136. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  137. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  138. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  139. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  140. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  141. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  142. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  143. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  144. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  145. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  146. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  147. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  148. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  149. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  150. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  151. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  152. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  153. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  156. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  158. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  159. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  160. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  161. data/modules/mu/{clouds → providers}/google.rb +29 -6
  162. data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
  163. data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
  164. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  165. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  166. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  167. data/modules/mu/{clouds → providers}/google/function.rb +14 -8
  168. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  169. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  170. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  171. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  172. data/modules/mu/{clouds → providers}/google/server.rb +142 -55
  173. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  174. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  175. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  176. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  177. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  178. data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
  179. data/modules/tests/aws-jobs-functions.yaml +46 -0
  180. data/modules/tests/centos6.yaml +15 -0
  181. data/modules/tests/centos7.yaml +15 -0
  182. data/modules/tests/centos8.yaml +12 -0
  183. data/modules/tests/ecs.yaml +23 -0
  184. data/modules/tests/eks.yaml +1 -1
  185. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  186. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/microservice_app.yaml +288 -0
  189. data/modules/tests/rds.yaml +108 -0
  190. data/modules/tests/regrooms/rds.yaml +123 -0
  191. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  192. data/modules/tests/super_complex_bok.yml +2 -2
  193. data/modules/tests/super_simple_bok.yml +3 -5
  194. data/modules/tests/win2k12.yaml +25 -0
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +169 -93
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1974
  202. data/modules/mu/clouds/aws/endpoint.rb +0 -596
  203. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -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
  }
@@ -180,8 +180,19 @@ module MU
180
180
  $file_format = MU::Config.guessFormat(path)
181
181
  $yaml_refs = {}
182
182
  erb = ERB.new(File.read(path), nil, "<>")
183
+ erb.filename = path
183
184
 
184
- raw_text = erb.result(erb_binding)
185
+ begin
186
+ raw_text = erb.result(erb_binding)
187
+ rescue NameError => e
188
+ loc = e.backtrace[0].sub(/:(\d+):.*/, ':\1')
189
+ msg = if e.message.match(/wrong constant name Config.getTail PLACEHOLDER ([^\s]+) REDLOHECALP/)
190
+ "Variable '#{Regexp.last_match[1]}' referenced in config, but not defined. Missing required parameter?"
191
+ else
192
+ e.message
193
+ end
194
+ raise ValidationError, msg+" at "+loc
195
+ end
185
196
  raw_json = nil
186
197
 
187
198
  # If we're working in YAML, do some magic to make includes work better.
@@ -419,6 +430,39 @@ module MU
419
430
  @config.freeze
420
431
  end
421
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
+
422
466
  # See if a given resource is configured in the current stack
423
467
  # @param name [String]: The name of the resource being checked
424
468
  # @param type [String]: The type of resource being checked
@@ -474,7 +518,8 @@ module MU
474
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
475
519
  def insertKitten(descriptor, type, delay_validation = false, ignore_duplicates: false, overwrite: false)
476
520
  append = false
477
- # start = Time.now
521
+ start = Time.now
522
+
478
523
  shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
479
524
  MU.log "insertKitten on #{cfg_name} #{descriptor['name']} (delay_validation: #{delay_validation.to_s})", MU::DEBUG, details: caller[0]
480
525
 
@@ -514,7 +559,7 @@ module MU
514
559
  # cloud-specific schema.
515
560
  schemaclass = Object.const_get("MU").const_get("Config").const_get(shortclass)
516
561
  myschema = Marshal.load(Marshal.dump(MU::Config.schema["properties"][cfg_plural]["items"]))
517
- 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)
518
563
  if more_schema
519
564
  MU::Config.schemaMerge(myschema["properties"], more_schema, descriptor["cloud"])
520
565
  end
@@ -533,7 +578,7 @@ module MU
533
578
  end
534
579
 
535
580
  # Make sure a sensible region has been targeted, if applicable
536
- classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"])
581
+ classobj = MU::Cloud.cloudClass(descriptor["cloud"])
537
582
  if descriptor["region"]
538
583
  valid_regions = classobj.listRegions
539
584
  if !valid_regions.include?(descriptor["region"])
@@ -546,11 +591,7 @@ module MU
546
591
  if descriptor['project'].nil?
547
592
  descriptor.delete('project')
548
593
  elsif haveLitterMate?(descriptor['project'], "habitats")
549
- descriptor['dependencies'] ||= []
550
- descriptor['dependencies'] << {
551
- "type" => "habitat",
552
- "name" => descriptor['project']
553
- }
594
+ MU::Config.addDependency(descriptor, descriptor['project'], "habitat")
554
595
  end
555
596
  end
556
597
 
@@ -578,21 +619,16 @@ module MU
578
619
  if !descriptor["vpc"]["name"].nil? and
579
620
  haveLitterMate?(descriptor["vpc"]["name"], "vpcs") and
580
621
  descriptor["vpc"]['deploy_id'].nil? and
581
- descriptor["vpc"]['id'].nil?
582
- descriptor["dependencies"] << {
583
- "type" => "vpc",
584
- "name" => descriptor["vpc"]["name"],
585
- }
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")
586
625
  siblingvpc = haveLitterMate?(descriptor["vpc"]["name"], "vpcs")
587
626
 
588
627
  if siblingvpc and siblingvpc['bastion'] and
589
628
  ["server", "server_pool", "container_cluster"].include?(cfg_name) and
590
629
  !descriptor['bastion']
591
- if descriptor['name'] != siblingvpc['bastion'].to_h['name']
592
- descriptor["dependencies"] << {
593
- "type" => "server",
594
- "name" => siblingvpc['bastion'].to_h['name']
595
- }
630
+ if descriptor['name'] != siblingvpc['bastion']['name']
631
+ MU::Config.addDependency(descriptor, siblingvpc['bastion']['name'], "server")
596
632
  end
597
633
  end
598
634
 
@@ -654,7 +690,6 @@ module MU
654
690
  if (descriptor['ingress_rules'] or
655
691
  ["server", "server_pool", "database", "cache_cluster"].include?(cfg_name))
656
692
  descriptor['ingress_rules'] ||= []
657
- fw_classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get("FirewallRule")
658
693
 
659
694
  acl = haveLitterMate?(fwname, "firewall_rules")
660
695
  already_exists = !acl.nil?
@@ -665,7 +700,7 @@ module MU
665
700
  "region" => descriptor['region'],
666
701
  "credentials" => descriptor["credentials"]
667
702
  }
668
- if !fw_classobj.isGlobal?
703
+ if !MU::Cloud.resourceClass(descriptor["cloud"], "FirewallRule").isGlobal?
669
704
  acl['region'] = descriptor['region']
670
705
  acl['region'] ||= classobj.myRegion(acl['credentials'])
671
706
  else
@@ -691,10 +726,7 @@ module MU
691
726
  if !descriptor["loadbalancers"].nil?
692
727
  descriptor["loadbalancers"].each { |lb|
693
728
  if !lb["concurrent_load_balancer"].nil?
694
- descriptor["dependencies"] << {
695
- "type" => "loadbalancer",
696
- "name" => lb["concurrent_load_balancer"]
697
- }
729
+ MU::Config.addDependency(descriptor, lb["concurrent_load_balancer"], "loadbalancer")
698
730
  end
699
731
  }
700
732
  end
@@ -703,10 +735,7 @@ module MU
703
735
  if !descriptor["storage_pools"].nil?
704
736
  descriptor["storage_pools"].each { |sp|
705
737
  if sp["name"]
706
- descriptor["dependencies"] << {
707
- "type" => "storage_pool",
708
- "name" => sp["name"]
709
- }
738
+ MU::Config.addDependency(descriptor, sp["name"], "storage_pool")
710
739
  end
711
740
  }
712
741
  end
@@ -717,10 +746,7 @@ module MU
717
746
  next if !acl_include["name"] and !acl_include["rule_name"]
718
747
  acl_include["name"] ||= acl_include["rule_name"]
719
748
  if haveLitterMate?(acl_include["name"], "firewall_rules")
720
- descriptor["dependencies"] << {
721
- "type" => "firewall_rule",
722
- "name" => acl_include["name"]
723
- }
749
+ MU::Config.addDependency(descriptor, acl_include["name"], "firewall_rule", no_create_wait: (cfg_name == "vpc"))
724
750
  elsif acl_include["name"]
725
751
  MU.log shortclass.to_s+" #{descriptor['name']} depends on FirewallRule #{acl_include["name"]}, but no such rule declared.", MU::ERR
726
752
  ok = false
@@ -820,7 +846,7 @@ module MU
820
846
  # Run the cloud class's deeper validation, unless we've already failed
821
847
  # on stuff that will cause spurious alarms further in
822
848
  if ok
823
- 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)
824
850
  original_descriptor = MU::Config.stripConfig(descriptor)
825
851
  passed = parser.validateConfig(descriptor, self)
826
852
 
@@ -830,7 +856,7 @@ module MU
830
856
  end
831
857
 
832
858
  # Make sure we've been configured with the right credentials
833
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(descriptor['cloud'])
859
+ cloudbase = MU::Cloud.cloudClass(descriptor['cloud'])
834
860
  credcfg = cloudbase.credConfig(descriptor['credentials'])
835
861
  if !credcfg or credcfg.empty?
836
862
  raise ValidationError, "#{descriptor['cloud']} #{cfg_name} #{descriptor['name']} declares credential set #{descriptor['credentials']}, but no such credentials exist for that cloud provider"
@@ -846,60 +872,92 @@ module MU
846
872
  @kittens[cfg_plural] << descriptor if append
847
873
  }
848
874
 
875
+ MU.log "insertKitten completed #{cfg_name} #{descriptor['name']} in #{sprintf("%.2fs", Time.now-start)}", MU::DEBUG
876
+
849
877
  ok
850
878
  end
851
879
 
852
880
  # For our resources which specify intra-stack dependencies, make sure those
853
881
  # dependencies are actually declared.
854
- # TODO check for loops
855
- def self.check_dependencies(config)
882
+ def check_dependencies
856
883
  ok = true
857
884
 
858
- config.each_pair { |type, values|
859
- if values.instance_of?(Array)
860
- values.each { |resource|
861
- if resource.kind_of?(Hash) and !resource["dependencies"].nil?
862
- append = []
863
- delete = []
864
- resource["dependencies"].each { |dependency|
865
- _shortclass, cfg_name, cfg_plural, _classname = MU::Cloud.getResourceNames(dependency["type"])
866
- found = false
867
- names_seen = []
868
- if !config[cfg_plural].nil?
869
- config[cfg_plural].each { |service|
870
- names_seen << service["name"].to_s
871
- found = true if service["name"].to_s == dependency["name"].to_s
872
- if service["virtual_name"]
873
- names_seen << service["virtual_name"].to_s
874
- if service["virtual_name"].to_s == dependency["name"].to_s
875
- found = true
876
- append_me = dependency.dup
877
- append_me['name'] = service['name']
878
- append << append_me
879
- delete << dependency
880
- end
881
- 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
882
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
883
928
  end
884
- if !found
885
- 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 ]
886
948
  ok = false
887
949
  end
888
950
  }
889
- if append.size > 0
890
- append.uniq!
891
- resource["dependencies"].concat(append)
892
- end
893
- if delete.size > 0
894
- delete.each { |delete_me|
895
- resource["dependencies"].delete(delete_me)
896
- }
897
- end
898
951
  end
899
952
  }
900
- end
953
+ resource["dependencies"].reject! { |dep| deleteme.include?(dep) }
954
+ resource["dependencies"].concat(addme)
955
+ resource["dependencies"].uniq!
956
+
957
+ }
901
958
  }
902
- return ok
959
+
960
+ ok
903
961
  end
904
962
 
905
963
  # Ugly text-manipulation to recursively resolve some placeholder strings
@@ -1180,12 +1238,7 @@ module MU
1180
1238
  "port" => db["port"],
1181
1239
  "sgs" => [cfg_name+server['name']]
1182
1240
  }
1183
-
1184
- ruleset["dependencies"] << {
1185
- "name" => cfg_name+server['name'],
1186
- "type" => "firewall_rule",
1187
- "no_create_wait" => true
1188
- }
1241
+ MU::Config.addDependency(ruleset, cfg_name+server['name'], "firewall_rule", no_create_wait: true)
1189
1242
  end
1190
1243
  }
1191
1244
  }
@@ -1203,7 +1256,7 @@ module MU
1203
1256
  types.each { |type|
1204
1257
  config[type] = @kittens[type] if @kittens[type].size > 0
1205
1258
  }
1206
- ok = false if !MU::Config.check_dependencies(config)
1259
+ ok = false if !check_dependencies
1207
1260
 
1208
1261
  # TODO enforce uniqueness of resource names
1209
1262
  raise ValidationError if !ok