cloud-mu 3.1.3 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  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 +21 -13
  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 +4 -4
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +147 -37
  23. data/cloud-mu.gemspec +22 -20
  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 +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +158 -111
  45. data/modules/mu/adoption.rb +404 -71
  46. data/modules/mu/cleanup.rb +221 -306
  47. data/modules/mu/cloud.rb +129 -1633
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +44 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +926 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +169 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +32 -3
  61. data/modules/mu/config/cache_cluster.rb +2 -2
  62. data/modules/mu/config/cdn.rb +100 -0
  63. data/modules/mu/config/collection.rb +4 -4
  64. data/modules/mu/config/container_cluster.rb +9 -4
  65. data/modules/mu/config/database.rb +84 -105
  66. data/modules/mu/config/database.yml +1 -2
  67. data/modules/mu/config/dnszone.rb +10 -9
  68. data/modules/mu/config/doc_helpers.rb +516 -0
  69. data/modules/mu/config/endpoint.rb +5 -4
  70. data/modules/mu/config/firewall_rule.rb +103 -4
  71. data/modules/mu/config/folder.rb +4 -4
  72. data/modules/mu/config/function.rb +19 -10
  73. data/modules/mu/config/group.rb +4 -4
  74. data/modules/mu/config/habitat.rb +4 -4
  75. data/modules/mu/config/job.rb +89 -0
  76. data/modules/mu/config/loadbalancer.rb +60 -14
  77. data/modules/mu/config/log.rb +4 -4
  78. data/modules/mu/config/msg_queue.rb +4 -4
  79. data/modules/mu/config/nosqldb.rb +4 -4
  80. data/modules/mu/config/notifier.rb +10 -21
  81. data/modules/mu/config/ref.rb +411 -0
  82. data/modules/mu/config/role.rb +4 -4
  83. data/modules/mu/config/schema_helpers.rb +509 -0
  84. data/modules/mu/config/search_domain.rb +4 -4
  85. data/modules/mu/config/server.rb +98 -71
  86. data/modules/mu/config/server.yml +1 -0
  87. data/modules/mu/config/server_pool.rb +5 -9
  88. data/modules/mu/config/storage_pool.rb +1 -1
  89. data/modules/mu/config/tail.rb +200 -0
  90. data/modules/mu/config/user.rb +4 -4
  91. data/modules/mu/config/vpc.rb +71 -27
  92. data/modules/mu/config/vpc.yml +0 -1
  93. data/modules/mu/defaults/AWS.yaml +91 -68
  94. data/modules/mu/defaults/Azure.yaml +1 -0
  95. data/modules/mu/defaults/Google.yaml +3 -2
  96. data/modules/mu/deploy.rb +43 -26
  97. data/modules/mu/groomer.rb +17 -2
  98. data/modules/mu/groomers/ansible.rb +188 -41
  99. data/modules/mu/groomers/chef.rb +116 -55
  100. data/modules/mu/logger.rb +127 -148
  101. data/modules/mu/master.rb +410 -2
  102. data/modules/mu/master/chef.rb +3 -4
  103. data/modules/mu/master/ldap.rb +3 -3
  104. data/modules/mu/master/ssl.rb +12 -3
  105. data/modules/mu/mommacat.rb +218 -2612
  106. data/modules/mu/mommacat/daemon.rb +403 -0
  107. data/modules/mu/mommacat/naming.rb +473 -0
  108. data/modules/mu/mommacat/search.rb +495 -0
  109. data/modules/mu/mommacat/storage.rb +722 -0
  110. data/modules/mu/{clouds → providers}/README.md +1 -1
  111. data/modules/mu/{clouds → providers}/aws.rb +380 -122
  112. data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
  113. data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
  114. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
  115. data/modules/mu/providers/aws/cdn.rb +782 -0
  116. data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
  117. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
  118. data/modules/mu/providers/aws/database.rb +1744 -0
  119. data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
  120. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  121. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
  122. data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
  123. data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
  124. data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
  125. data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
  126. data/modules/mu/providers/aws/job.rb +466 -0
  127. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
  128. data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
  129. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
  130. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
  131. data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
  132. data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
  133. data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
  134. data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
  135. data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
  136. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
  137. data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
  138. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  139. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  140. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  141. data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
  142. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  143. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  144. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  145. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  146. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  147. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  148. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  149. data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
  150. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  151. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  152. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  153. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  154. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  155. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  156. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  160. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  161. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  162. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  163. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  164. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  165. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  166. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  167. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  168. data/modules/mu/{clouds → providers}/google.rb +68 -30
  169. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  170. data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
  171. data/modules/mu/{clouds → providers}/google/database.rb +11 -21
  172. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  173. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  174. data/modules/mu/{clouds → providers}/google/function.rb +140 -168
  175. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  176. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  177. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
  178. data/modules/mu/{clouds → providers}/google/role.rb +94 -58
  179. data/modules/mu/{clouds → providers}/google/server.rb +243 -156
  180. data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
  181. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  182. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  183. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  184. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  185. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  186. data/modules/tests/aws-jobs-functions.yaml +46 -0
  187. data/modules/tests/bucket.yml +4 -0
  188. data/modules/tests/centos6.yaml +15 -0
  189. data/modules/tests/centos7.yaml +15 -0
  190. data/modules/tests/centos8.yaml +12 -0
  191. data/modules/tests/ecs.yaml +23 -0
  192. data/modules/tests/eks.yaml +1 -1
  193. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  194. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  195. data/modules/tests/includes-and-params.yaml +2 -1
  196. data/modules/tests/microservice_app.yaml +288 -0
  197. data/modules/tests/rds.yaml +108 -0
  198. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  199. data/modules/tests/regrooms/bucket.yml +19 -0
  200. data/modules/tests/regrooms/rds.yaml +123 -0
  201. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  202. data/modules/tests/super_complex_bok.yml +2 -2
  203. data/modules/tests/super_simple_bok.yml +3 -5
  204. data/modules/tests/win2k12.yaml +17 -5
  205. data/modules/tests/win2k16.yaml +25 -0
  206. data/modules/tests/win2k19.yaml +25 -0
  207. data/requirements.txt +1 -0
  208. data/spec/mu/clouds/azure_spec.rb +2 -2
  209. metadata +240 -154
  210. data/extras/image-generators/AWS/windows.yaml +0 -18
  211. data/modules/mu/clouds/aws/database.rb +0 -1985
  212. data/modules/mu/clouds/aws/endpoint.rb +0 -592
@@ -14,7 +14,7 @@
14
14
 
15
15
  module MU
16
16
  class Config
17
- # Basket of Kittens config schema and parser logic. See modules/mu/clouds/*/log.rb
17
+ # Basket of Kittens config schema and parser logic. See modules/mu/providers/*/log.rb
18
18
  class Log
19
19
 
20
20
  # Base configuration schema for a Log
@@ -36,10 +36,10 @@ module MU
36
36
  end
37
37
 
38
38
  # Generic pre-processing of {MU::Config::BasketofKittens::logs}, bare and unvalidated.
39
- # @param log [Hash]: The resource to process and validate
40
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
39
+ # @param _log [Hash]: The resource to process and validate
40
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
41
41
  # @return [Boolean]: True if validation succeeded, False otherwise
42
- def self.validate(log, configurator)
42
+ def self.validate(_log, _configurator)
43
43
  ok = true
44
44
  ok
45
45
  end
@@ -14,7 +14,7 @@
14
14
 
15
15
  module MU
16
16
  class Config
17
- # Basket of Kittens config schema and parser logic. See modules/mu/clouds/*/msg_queue.rb
17
+ # Basket of Kittens config schema and parser logic. See modules/mu/providers/*/msg_queue.rb
18
18
  class MsgQueue
19
19
 
20
20
  # Base configuration schema for a MsgQueue
@@ -34,10 +34,10 @@ module MU
34
34
  end
35
35
 
36
36
  # Generic pre-processing of {MU::Config::BasketofKittens::msg_queues}, bare and unvalidated.
37
- # @param queue [Hash]: The resource to process and validate
38
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
37
+ # @param _queue [Hash]: The resource to process and validate
38
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
39
39
  # @return [Boolean]: True if validation succeeded, False otherwise
40
- def self.validate(queue, configurator)
40
+ def self.validate(_queue, _configurator)
41
41
  ok = true
42
42
  ok
43
43
  end
@@ -14,7 +14,7 @@
14
14
 
15
15
  module MU
16
16
  class Config
17
- # Basket of Kittens config schema and parser logic. See modules/mu/clouds/*/nosqldb.rb
17
+ # Basket of Kittens config schema and parser logic. See modules/mu/providers/*/nosqldb.rb
18
18
  class NoSQLDB
19
19
 
20
20
  # Base configuration schema for a Bucket
@@ -35,10 +35,10 @@ module MU
35
35
  end
36
36
 
37
37
  # Generic pre-processing of {MU::Config::BasketofKittens::nosqldbs}, bare and unvalidated.
38
- # @param db [Hash]: The resource to process and validate
39
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
38
+ # @param _db [Hash]: The resource to process and validate
39
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
40
40
  # @return [Boolean]: True if validation succeeded, False otherwise
41
- def self.validate(db, configurator)
41
+ def self.validate(_db, _configurator)
42
42
  ok = true
43
43
 
44
44
  ok
@@ -14,7 +14,7 @@
14
14
 
15
15
  module MU
16
16
  class Config
17
- # Basket of Kittens config schema and parser logic. See modules/mu/clouds/*/notifier.rb
17
+ # Basket of Kittens config schema and parser logic. See modules/mu/providers/*/notifier.rb
18
18
  class Notifier
19
19
 
20
20
  # Base configuration schema for a Notifier
@@ -36,12 +36,12 @@ module MU
36
36
  "items" => {
37
37
  "type" => "object",
38
38
  "description" => "A list of people or resources which should receive notifications",
39
- "required" => ["endpoint"],
40
39
  "properties" => {
41
40
  "endpoint" => {
42
41
  "type" => "string",
43
- "description" => "The endpoint which should be subscribed to this notifier, typically an email address or SMS-enabled phone number."
44
- }
42
+ "description" => "Shorthand for an endpoint which should be subscribed to this notifier, typically an email address or SMS-enabled phone number. For complex cases, such as referencing an AWS Lambda function defined elsewhere in your Mu stack, use +resource+ instead."
43
+ },
44
+ "resource" => MU::Config::Ref.schema(desc: "A cloud resource that is a valid notification target for this notifier. For simple use cases, such as external email addresses or SMS, use +endpoint+ instead.")
45
45
  }
46
46
  }
47
47
  }
@@ -51,28 +51,17 @@ module MU
51
51
 
52
52
  # Generic pre-processing of {MU::Config::BasketofKittens::notifiers}, bare and unvalidated.
53
53
  # @param notifier [Hash]: The resource to process and validate
54
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
54
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
55
55
  # @return [Boolean]: True if validation succeeded, False otherwise
56
- def self.validate(notifier, configurator)
56
+ def self.validate(notifier, _configurator)
57
57
  ok = true
58
58
 
59
+
59
60
  if notifier['subscriptions']
60
61
  notifier['subscriptions'].each { |sub|
61
- if !sub["type"]
62
- if sub["endpoint"].match(/^http:/i)
63
- sub["type"] = "http"
64
- elsif sub["endpoint"].match(/^https:/i)
65
- sub["type"] = "https"
66
- elsif sub["endpoint"].match(/^sqs:/i)
67
- sub["type"] = "sqs"
68
- elsif sub["endpoint"].match(/^\+?[\d\-]+$/)
69
- sub["type"] = "sms"
70
- elsif sub["endpoint"].match(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
71
- sub["type"] = "email"
72
- else
73
- MU.log "Notifier #{notifier['name']} subscription #{sub['endpoint']} did not specify a type, and I'm unable to guess one", MU::ERR
74
- ok = false
75
- end
62
+ if !sub['endpoint'] and !sub['resource']
63
+ MU.log "Notifier '#{notifier['name']}' must specify either resource or endpoint in subscription", MU::ERR, details: sub
64
+ ok = false
76
65
  end
77
66
  }
78
67
  end
@@ -0,0 +1,411 @@
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
+
17
+ # Methods and structures for parsing Mu's configuration files. See also {MU::Config::BasketofKittens}.
18
+ class Config
19
+
20
+ # A wrapper class for resources to refer to other resources, whether they
21
+ # be a sibling object in the current deploy, an object in another deploy,
22
+ # or a plain cloud id from outside of Mu.
23
+ class Ref
24
+ attr_reader :name
25
+ attr_reader :type
26
+ attr_reader :cloud
27
+ attr_reader :deploy_id
28
+ attr_reader :region
29
+ attr_reader :credentials
30
+ attr_reader :habitat
31
+ attr_reader :mommacat
32
+ attr_reader :tag_key
33
+ attr_reader :tag_value
34
+ attr_reader :obj
35
+
36
+ @@refs = []
37
+ @@ref_semaphore = Mutex.new
38
+
39
+ # Little bit of a factory pattern... given a hash of options for a {MU::Config::Ref} objects, first see if we have an existing one that matches our more immutable attributes (+cloud+, +id+, etc). If we do, return that. If we do not, create one, add that to our inventory, and return that instead.
40
+ # @param cfg [Hash]:
41
+ # @return [MU::Config::Ref]
42
+ def self.get(cfg)
43
+ return cfg if cfg.is_a?(MU::Config::Ref)
44
+ checkfields = cfg.keys.map { |k| k.to_sym }
45
+ required = [:id, :type]
46
+
47
+ @@ref_semaphore.synchronize {
48
+ @@refs.each { |ref|
49
+ saw_mismatch = false
50
+ saw_match = false
51
+ needed_values = []
52
+ checkfields.each { |field|
53
+ next if !cfg[field]
54
+ ext_value = ref.instance_variable_get("@#{field.to_s}".to_sym)
55
+ if !ext_value
56
+ needed_values << field
57
+ next
58
+ end
59
+ if cfg[field] != ext_value
60
+ saw_mismatch = true
61
+ elsif required.include?(field) and cfg[field] == ext_value
62
+ saw_match = true
63
+ end
64
+ }
65
+ if saw_match and !saw_mismatch
66
+ # populate empty fields we got from this request
67
+ if needed_values.size > 0
68
+ newref = ref.dup
69
+ needed_values.each { |field|
70
+ newref.instance_variable_set("@#{field.to_s}".to_sym, cfg[field])
71
+ if !newref.respond_to?(field)
72
+ newref.singleton_class.instance_eval { attr_reader field.to_sym }
73
+ end
74
+ }
75
+ @@refs << newref
76
+ return newref
77
+ else
78
+ return ref
79
+ end
80
+ end
81
+ }
82
+
83
+ }
84
+
85
+ # if we get here, there was no match
86
+ newref = MU::Config::Ref.new(cfg)
87
+ @@ref_semaphore.synchronize {
88
+ @@refs << newref
89
+ return newref
90
+ }
91
+ end
92
+
93
+ # A way of dynamically defining +attr_reader+ without leaking memory
94
+ def self.define_reader(name)
95
+ define_method(name) {
96
+ instance_variable_get("@#{name.to_s}")
97
+ }
98
+ end
99
+
100
+ # @param cfg [Hash]: A Basket of Kittens configuration hash containing
101
+ # lookup information for a cloud object
102
+ def initialize(cfg)
103
+ cfg.keys.each { |field|
104
+ next if field == "tag"
105
+ if !cfg[field].nil?
106
+ self.instance_variable_set("@#{field}".to_sym, cfg[field])
107
+ elsif !cfg[field.to_sym].nil?
108
+ self.instance_variable_set("@#{field.to_s}".to_sym, cfg[field.to_sym])
109
+ end
110
+ MU::Config::Ref.define_reader(field)
111
+ }
112
+ if cfg['tag'] and cfg['tag']['key'] and
113
+ !cfg['tag']['key'].empty? and cfg['tag']['value']
114
+ @tag_key = cfg['tag']['key']
115
+ @tag_value = cfg['tag']['value']
116
+ end
117
+
118
+ if @deploy_id and !@mommacat
119
+ @mommacat = MU::MommaCat.getLitter(@deploy_id, set_context_to_me: false)
120
+ elsif @mommacat and !@deploy_id
121
+ @deploy_id = @mommacat.deploy_id
122
+ end
123
+
124
+ # canonicalize the 'type' argument
125
+ _shortclass, _cfg_name, cfg_plural, _classname, _attrs = MU::Cloud.getResourceNames(@type, false)
126
+ @type = cfg_plural if cfg_plural
127
+
128
+ kitten(shallow: true) if @mommacat # try to populate the actual cloud object for this
129
+ end
130
+
131
+ # Comparison operator
132
+ def <=>(other)
133
+ return 1 if other.nil?
134
+ self.to_s <=> other.to_s
135
+ end
136
+
137
+ # Lets callers access us like a {Hash}
138
+ # @param attribute [String,Symbol]
139
+ def [](attribute)
140
+ if respond_to?(attribute.to_sym)
141
+ send(attribute.to_sym)
142
+ else
143
+ nil
144
+ end
145
+ end
146
+
147
+ # Lets callers set attributes like a {Hash}
148
+ # @param attribute [String,Symbol]
149
+ def []=(attribute, value)
150
+ instance_variable_set("@#{attribute.to_s}".to_sym, value)
151
+ self.class.define_reader(attribute)
152
+ end
153
+
154
+ # Unset an attribute. Sort of. We can't actually do that, so nil it out
155
+ # and we get the behavior we want.
156
+ def delete(attribute)
157
+ attribute = ("@"+attribute).to_sym if attribute.to_s !~ /^@/
158
+ instance_variable_set(attribute.to_sym, nil)
159
+ end
160
+
161
+ # Base configuration schema for declared kittens referencing other cloud objects. This is essentially a set of filters that we're going to pass to {MU::MommaCat.findStray}.
162
+ # @param aliases [Array<Hash>]: Key => value mappings to set backwards-compatibility aliases for attributes, such as the ubiquitous +vpc_id+ (+vpc_id+ => +id+).
163
+ # @return [Hash]
164
+ def self.schema(aliases = [], type: nil, parent_obj: nil, desc: nil, omit_fields: [], any_type: false)
165
+ parent_obj ||= caller[1].gsub(/.*?\/([^\.\/]+)\.rb:.*/, '\1')
166
+ desc ||= "Reference a #{type ? "'#{type}' resource" : "resource" } from this #{parent_obj ? "'#{parent_obj}'" : "" } resource"
167
+ schema = {
168
+ "type" => "object",
169
+ "#MU_REFERENCE" => true,
170
+ "minProperties" => 1,
171
+ "description" => desc,
172
+ "properties" => {
173
+ "id" => {
174
+ "type" => "string",
175
+ "description" => "Cloud identifier of a resource we want to reference, typically used when leveraging resources not managed by MU"
176
+ },
177
+ "name" => {
178
+ "type" => "string",
179
+ "description" => "The short (internal Mu) name of a resource we're attempting to reference. Typically used when referring to a sibling resource elsewhere in the same deploy, or in another known Mu deploy in conjunction with +deploy_id+."
180
+ },
181
+ "type" => {
182
+ "type" => "string",
183
+ "description" => "The resource type we're attempting to reference.",
184
+ "enum" => MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }
185
+ },
186
+ "deploy_id" => {
187
+ "type" => "string",
188
+ "description" => "Our target resource should be found in this Mu deploy."
189
+ },
190
+ "credentials" => MU::Config.credentials_primitive,
191
+ "region" => MU::Config.region_primitive,
192
+ "cloud" => MU::Config.cloud_primitive,
193
+ "tag" => {
194
+ "type" => "object",
195
+ "description" => "If the target resource supports tagging and our resource implementations +find+ method supports it, we can attempt to locate it by tag.",
196
+ "properties" => {
197
+ "key" => {
198
+ "type" => "string",
199
+ "description" => "The tag or label key to search against"
200
+ },
201
+ "value" => {
202
+ "type" => "string",
203
+ "description" => "The tag or label value to match"
204
+ }
205
+ }
206
+ }
207
+ }
208
+ }
209
+ if !["folders", "habitats"].include?(type)
210
+ schema["properties"]["habitat"] = MU::Config::Habitat.reference
211
+ end
212
+
213
+ if omit_fields
214
+ omit_fields.each { |f|
215
+ schema["properties"].delete(f)
216
+ }
217
+ end
218
+
219
+ if any_type
220
+ schema["properties"]["type"].delete("enum")
221
+ elsif !type.nil?
222
+ schema["required"] = ["type"]
223
+ schema["properties"]["type"]["default"] = type
224
+ schema["properties"]["type"]["enum"] = [type]
225
+ end
226
+
227
+ aliases.each { |a|
228
+ a.each_pair { |k, v|
229
+ if schema["properties"][v]
230
+ schema["properties"][k] = schema["properties"][v].dup
231
+ schema["properties"][k]["description"] = "Alias for <tt>#{v}</tt>"
232
+ else
233
+ MU.log "Reference schema alias #{k} wants to alias #{v}, but no such attribute exists", MU::WARN, details: caller[4]
234
+ end
235
+ }
236
+ }
237
+
238
+ schema
239
+ end
240
+
241
+ # Is our +@type+ attribute a Mu-supported type, or some rando string?
242
+ # @return [Boolean]
243
+ def is_mu_type?
244
+ _shortclass, _cfg_name, type, _classname, _attrs = MU::Cloud.getResourceNames(@type, false)
245
+ !type.nil?
246
+ end
247
+
248
+ # Decompose into a plain-jane {MU::Config::BasketOfKittens} hash fragment,
249
+ # of the sort that would have been used to declare this reference in the
250
+ # first place.
251
+ def to_h
252
+ me = { }
253
+
254
+ self.instance_variables.each { |var|
255
+ next if [:@obj, :@mommacat, :@tag_key, :@tag_value].include?(var)
256
+ val = self.instance_variable_get(var)
257
+ next if val.nil?
258
+ val = val.to_h if val.is_a?(MU::Config::Ref)
259
+ me[var.to_s.sub(/^@/, '')] = val
260
+ }
261
+ if @tag_key and !@tag_key.empty?
262
+ me['tag'] = {
263
+ 'key' => @tag_key,
264
+ 'value' => @tag_value
265
+ }
266
+ end
267
+ me
268
+ end
269
+
270
+ # Getter for the #{id} instance variable that attempts to populate it if
271
+ # it's not set.
272
+ # @return [String,nil]
273
+ def id
274
+ return @id if @id
275
+ kitten # if it's not defined, attempt to define it
276
+ @id
277
+ end
278
+
279
+ # Alias for {id}
280
+ # @return [String,nil]
281
+ def cloud_id
282
+ id
283
+ end
284
+
285
+ # Return a {MU::Cloud} object for this reference. This is only meant to be
286
+ # called in a live deploy, which is to say that if called during initial
287
+ # configuration parsing, results may be incorrect.
288
+ # @param mommacat [MU::MommaCat]: A deploy object which will be searched for the referenced resource if provided, before restoring to broader, less efficient searches.
289
+ def kitten(mommacat = @mommacat, shallow: false, debug: false, cloud: nil)
290
+ cloud ||= @cloud
291
+ return nil if !cloud or !@type
292
+
293
+ _shortclass, _cfg_name, cfg_plural, _classname, _attrs = MU::Cloud.getResourceNames(@type, false)
294
+ if cfg_plural
295
+ @type = cfg_plural # make sure this is the thing we expect
296
+ else
297
+ return nil # we don't do non-muish resources
298
+ end
299
+
300
+ loglevel = debug ? MU::NOTICE : MU::DEBUG
301
+
302
+ if debug
303
+ MU.log "this mf kitten", MU::WARN, details: caller
304
+ end
305
+
306
+ if @obj
307
+ @deploy_id ||= @obj.deploy_id
308
+ @id ||= @obj.cloud_id
309
+ @name ||= @obj.config['name'] if @obj.config
310
+ return @obj
311
+ end
312
+
313
+ if mommacat and !caller.grep(/`findLitterMate'/) # XXX the dumbest
314
+ MU.log "Looking for #{@type} #{@name} #{@id} in deploy #{mommacat.deploy_id}", loglevel
315
+ begin
316
+ @obj = mommacat.findLitterMate(type: @type, name: @name, cloud_id: @id, credentials: @credentials, debug: debug)
317
+ rescue StandardError => e
318
+ if e.message =~ /deadlock/
319
+ MU.log "Saw a recursive deadlock trying to fetch kitten for Ref object in deploy #{mmommacat.deploy_id}", MU::ERR, details: to_h
320
+ end
321
+ raise e
322
+ end
323
+ if @obj # initialize missing attributes, if we can
324
+ @id ||= @obj.cloud_id
325
+ @mommacat ||= mommacat
326
+ @obj.intoDeploy(@mommacat) # make real sure these are set
327
+ @deploy_id ||= mommacat.deploy_id
328
+
329
+ if !@name
330
+ if @obj.config and @obj.config['name']
331
+ @name = @obj.config['name']
332
+ elsif @obj.mu_name
333
+ if @type == "folders"
334
+ MU.log "would assign name '#{@obj.mu_name}' in ref to this folder if I were feeling aggressive", MU::WARN, details: self.to_h
335
+ end
336
+ # @name = @obj.mu_name
337
+ end
338
+ end
339
+ return @obj
340
+ else
341
+ # MU.log "Failed to find a live '#{@type.to_s}' object named #{@name}#{@id ? " (#{@id})" : "" }#{ @habitat ? " in habitat #{@habitat}" : "" }", MU::WARN, details: self
342
+ end
343
+ end
344
+
345
+ if !@obj and !(cloud == "Google" and @id and @type == "users" and MU::Cloud.resourceClass("Google", "User").cannedServiceAcctName?(@id)) and !shallow
346
+ try_deploy_id = @deploy_id
347
+
348
+ begin
349
+ hab_arg = if @habitat.nil?
350
+ [nil]
351
+ elsif @habitat.is_a?(MU::Config::Ref)
352
+ [@habitat.id]
353
+ elsif @habitat.is_a?(Hash)
354
+ [@habitat["id"]]
355
+ else
356
+ [@habitat.to_s]
357
+ end
358
+
359
+ MU.log "Ref#kitten calling findStray", loglevel, details: {
360
+ cloud: cloud,
361
+ type: @type,
362
+ name: @name,
363
+ cloud_id: @id,
364
+ deploy_id: try_deploy_id,
365
+ region: @region,
366
+ habitats: hab_arg,
367
+ credentials: @credentials,
368
+ dummy_ok: (["habitats", "folders", "users", "groups", "vpcs"].include?(@type))
369
+ }
370
+
371
+ found = MU::MommaCat.findStray(
372
+ cloud,
373
+ @type,
374
+ name: @name,
375
+ cloud_id: @id,
376
+ deploy_id: try_deploy_id,
377
+ region: @region,
378
+ habitats: hab_arg,
379
+ credentials: @credentials,
380
+ dummy_ok: (["habitats", "folders", "users", "groups", "vpcs"].include?(@type))
381
+ )
382
+ MU.log "Ref#kitten results from findStray", loglevel, details: found
383
+ @obj ||= found.first if found
384
+ rescue MU::MommaCat::MultipleMatches => e
385
+ if try_deploy_id.nil? and MU.deploy_id
386
+ MU.log "Attempting to narrow down #{cloud} #{@type} to #{MU.deploy_id}", MU::NOTICE
387
+ try_deploy_id = MU.deploy_id
388
+ retry
389
+ else
390
+ raise e
391
+ end
392
+ rescue ThreadError => e
393
+ # Sometimes MommaCat calls us in a potential deadlock situation;
394
+ # don't be the cause of a fatal error if so, we don't need this
395
+ # object that badly.
396
+ raise e if !e.message.match(/recursive locking/)
397
+ end
398
+ end
399
+
400
+ if @obj
401
+ @deploy_id ||= @obj.deploy_id
402
+ @id ||= @obj.cloud_id
403
+ @name ||= @obj.config['name']
404
+ end
405
+
406
+ @obj
407
+ end
408
+
409
+ end
410
+ end
411
+ end