cloud-mu 3.1.5 → 3.3.2

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 (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