cloud-mu 3.1.3 → 3.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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
@@ -4,7 +4,7 @@ name: database-complex
4
4
  size: db.r4.large
5
5
  engine: postgres
6
6
  engine_version: 9.6.6
7
- storage: 5
7
+ storage: 21
8
8
  add_cluster_node: true
9
9
  allow_major_version_upgrade: true
10
10
  auto_minor_version_upgrade: false
@@ -24,6 +24,5 @@ vpc:
24
24
  name: <%= vpc_name %>
25
25
  size: <%= db_size %>
26
26
  engine: mariadb
27
- storage: 5
28
27
 
29
28
  <% 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/*/dnszone.rb
17
+ # Basket of Kittens config schema and parser logic. See modules/mu/providers/*/dnszone.rb
18
18
  class DNSZone
19
19
 
20
20
  # Base configuration schema for a DNSZone
@@ -60,7 +60,7 @@ module MU
60
60
  # @param default_type [String]: The type of record to make default (e.g. An, CNAME, etc)
61
61
  # @param need_zone [Boolean]: Whether to explicitly require a zone be declared
62
62
  # @return [Hash]
63
- def self.records_primitive(need_target: true, default_type: nil, need_zone: false)
63
+ def self.records_primitive(need_target: true, default_type: nil, need_zone: false, embedded_type: nil)
64
64
  dns_records_primitive = {
65
65
  "type" => "array",
66
66
  "maxItems" => 100,
@@ -107,8 +107,9 @@ module MU
107
107
  },
108
108
  "mu_type" => {
109
109
  "type" => "string",
110
- "description" => "The Mu resource type to search the deployment for.",
111
- "enum" => ["loadbalancer", "server", "database", "cache_cluster"]
110
+ "description" => "The mu type of a resource being targeted.",
111
+ "enum" => embedded_type ? [embedded_type] : ["loadbalancer", "server", "database", "cache_cluster", "endpoint", "cdn"],
112
+ "default" => embedded_type
112
113
  },
113
114
  "target_type" => {
114
115
  "description" => "If the target is a public or a private resource. This only applies to servers/server_pools when using automatic DNS registration. If set to public but the target only has a private address, the private address will be used",
@@ -312,15 +313,15 @@ module MU
312
313
  end
313
314
 
314
315
  # Generic pre-processing of {MU::Config::BasketofKittens::dnszones}, bare and unvalidated.
315
- # @param zone [Hash]: The resource to process and validate
316
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
316
+ # @param _zone [Hash]: The resource to process and validate
317
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
317
318
  # @return [Boolean]: True if validation succeeded, False otherwise
318
- def self.validate(zone, configurator)
319
+ def self.validate(_zone, _configurator)
319
320
  ok = true
320
- ok
321
- end
322
321
  # TODO non-local VPCs are valid, but require an account field, which insertKitten doesn't know anything about
323
322
  # if !zone['account'].nil? and zone['account'] != MU.account_number
323
+ ok
324
+ end
324
325
 
325
326
  end
326
327
  end
@@ -0,0 +1,516 @@
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
+ # Accessor for our Basket of Kittens schema definition, with various
21
+ # cloud-specific details merged so we can generate documentation for them.
22
+ def self.docSchema
23
+ docschema = Marshal.load(Marshal.dump(@@schema))
24
+ only_children = {}
25
+ MU::Cloud.resource_types.each_pair { |classname, attrs|
26
+ MU::Cloud.supportedClouds.each { |cloud|
27
+ begin
28
+ require "mu/providers/#{cloud.downcase}/#{attrs[:cfg_name]}"
29
+ rescue LoadError
30
+ next
31
+ end
32
+ _required, res_schema = MU::Cloud.resourceClass(cloud, classname).schema(self)
33
+ docschema["properties"][attrs[:cfg_plural]]["items"]["description"] ||= ""
34
+ docschema["properties"][attrs[:cfg_plural]]["items"]["description"] += "\n#\n# `#{cloud}`: "+MU::Cloud.resourceClass(cloud, classname).quality
35
+ res_schema.each { |key, cfg|
36
+ if !docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
37
+ only_children[attrs[:cfg_plural]] ||= {}
38
+ only_children[attrs[:cfg_plural]][key] ||= {}
39
+ only_children[attrs[:cfg_plural]][key][cloud] = cfg
40
+ end
41
+ }
42
+ }
43
+ }
44
+
45
+ # recursively chase down description fields in arrays and objects of our
46
+ # schema and prepend stuff to them for documentation
47
+ def self.prepend_descriptions(prefix, cfg)
48
+ cfg["prefix"] = prefix
49
+ if cfg["type"] == "array" and cfg["items"]
50
+ cfg["items"] = prepend_descriptions(prefix, cfg["items"])
51
+ elsif cfg["type"] == "object" and cfg["properties"]
52
+ cfg["properties"].keys.each { |key|
53
+ cfg["properties"][key] = prepend_descriptions(prefix, cfg["properties"][key])
54
+ }
55
+ end
56
+ cfg
57
+ end
58
+
59
+ MU::Cloud.resource_types.each_pair { |classname, attrs|
60
+ MU::Cloud.supportedClouds.each { |cloud|
61
+ res_class = nil
62
+ begin
63
+ res_class = MU::Cloud.resourceClass(cloud, classname)
64
+ rescue MU::Cloud::MuCloudResourceNotImplemented
65
+ next
66
+ end
67
+ required, res_schema = res_class.schema(self)
68
+ next if required.size == 0 and res_schema.size == 0
69
+ res_schema.each { |key, cfg|
70
+ cfg["description"] ||= ""
71
+ if !cfg["description"].empty?
72
+ cfg["description"] = "\n# +"+cloud.upcase+"+: "+cfg["description"]
73
+ end
74
+ if docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
75
+ schemaMerge(docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key], cfg, cloud)
76
+ docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["description"] ||= ""
77
+ docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["description"] += "\n"+(cfg["description"].match(/^#/) ? "" : "# ")+cfg["description"]
78
+ MU.log "Munging #{cloud}-specific #{classname.to_s} schema into BasketofKittens => #{attrs[:cfg_plural]} => #{key}", MU::DEBUG, details: docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
79
+ else
80
+ if only_children[attrs[:cfg_plural]][key]
81
+ prefix = only_children[attrs[:cfg_plural]][key].keys.map{ |x| x.upcase }.join(" & ")+" ONLY"
82
+ cfg["description"].gsub!(/^\n#/, '') # so we don't leave the description blank in the "optional parameters" section
83
+ cfg = prepend_descriptions(prefix, cfg)
84
+ end
85
+
86
+ docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key] = cfg
87
+ end
88
+ docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["clouds"] = {}
89
+ docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["clouds"][cloud] = cfg
90
+ }
91
+
92
+ docschema['required'].concat(required)
93
+ docschema['required'].uniq!
94
+ }
95
+ }
96
+
97
+ docschema
98
+ end
99
+
100
+ # Output the dependencies of this BoK stack as a directed acyclic graph.
101
+ # Very useful for debugging.
102
+ def visualizeDependencies
103
+ # GraphViz won't like MU::Config::Tail, pare down to plain Strings
104
+ config = MU::Config.stripConfig(@config)
105
+ begin
106
+ g = GraphViz.new(:G, :type => :digraph)
107
+ # Generate a GraphViz node for each resource in this stack
108
+ nodes = {}
109
+ MU::Cloud.resource_types.each_pair { |classname, attrs|
110
+ nodes[attrs[:cfg_name]] = {}
111
+ if config.has_key?(attrs[:cfg_plural]) and config[attrs[:cfg_plural]]
112
+ config[attrs[:cfg_plural]].each { |resource|
113
+ nodes[attrs[:cfg_name]][resource['name']] = g.add_nodes("#{classname}: #{resource['name']}")
114
+ }
115
+ end
116
+ }
117
+ # Now add edges corresponding to the dependencies they list
118
+ MU::Cloud.resource_types.values.each { |attrs|
119
+ if config.has_key?(attrs[:cfg_plural]) and config[attrs[:cfg_plural]]
120
+ config[attrs[:cfg_plural]].each { |resource|
121
+ if resource.has_key?("dependencies")
122
+ me = nodes[attrs[:cfg_name]][resource['name']]
123
+ resource["dependencies"].each { |dep|
124
+ parent = nodes[dep['type']][dep['name']]
125
+ g.add_edges(me, parent)
126
+ }
127
+ end
128
+ }
129
+ end
130
+ }
131
+ # Spew some output?
132
+ MU.log "Emitting dependency graph as /tmp/#{config['appname']}.jpg", MU::NOTICE
133
+ g.output(:jpg => "/tmp/#{config['appname']}.jpg")
134
+ rescue StandardError => e
135
+ MU.log "Failed to generate GraphViz dependency tree: #{e.inspect}. This should only matter to developers.", MU::WARN, details: e.backtrace
136
+ end
137
+ end
138
+
139
+ # Generate a documentation-friendly dummy Ruby class for our mu.yaml main
140
+ # config.
141
+ def self.emitConfigAsRuby
142
+ example = %Q{---
143
+ public_address: 1.2.3.4
144
+ mu_admin_email: egtlabs@eglobaltech.com
145
+ mu_admin_name: Joe Schmoe
146
+ mommacat_port: 2260
147
+ banner: My Example Mu Master
148
+ mu_repository: git://github.com/cloudamatic/mu.git
149
+ repos:
150
+ - https://github.com/cloudamatic/mu_demo_platform
151
+ allow_invade_foreign_vpcs: true
152
+ ansible_dir:
153
+ aws:
154
+ egtdev:
155
+ region: us-east-1
156
+ log_bucket_name: egt-mu-log-bucket
157
+ default: true
158
+ name: egtdev
159
+ personal:
160
+ region: us-east-2
161
+ log_bucket_name: my-mu-log-bucket
162
+ name: personal
163
+ google:
164
+ egtlabs:
165
+ project: egt-labs-admin
166
+ credentials_file: /opt/mu/etc/google.json
167
+ region: us-east4
168
+ log_bucket_name: hexabucket-761234
169
+ default: true
170
+ }
171
+ mu_yaml_schema = eval(%Q{
172
+ $NOOP = true
173
+ load "#{MU.myRoot}/bin/mu-configure"
174
+ $CONFIGURABLES
175
+ })
176
+ return if mu_yaml_schema.nil? or !mu_yaml_schema.is_a?(Hash)
177
+ muyamlpath = "#{MU.myRoot}/modules/mu/mu.yaml.rb"
178
+ MU.log "Converting mu.yaml schema to Ruby objects in #{muyamlpath}"
179
+ muyaml_rb = File.new(muyamlpath, File::CREAT|File::TRUNC|File::RDWR, 0644)
180
+ muyaml_rb.puts "# Configuration schema for mu.yaml. See also {https://github.com/cloudamatic/mu/wiki/Configuration the Mu wiki}."
181
+ muyaml_rb.puts "#"
182
+ muyaml_rb.puts "# Example:"
183
+ muyaml_rb.puts "#"
184
+ muyaml_rb.puts "# <pre>"
185
+ example.split(/\n/).each { |line|
186
+ muyaml_rb.puts "# "+line+" " # markdooooown
187
+ }
188
+ muyaml_rb.puts "# </pre>"
189
+ muyaml_rb.puts "module MuYAML"
190
+ muyaml_rb.puts "\t# The configuration file format for Mu's main config file."
191
+ MU::Config.printMuYamlSchema(muyaml_rb, [], { "subtree" => mu_yaml_schema })
192
+ muyaml_rb.puts "end"
193
+ muyaml_rb.close
194
+ end
195
+
196
+ # Take the schema we've defined and create a dummy Ruby class tree out of
197
+ # it, basically so we can leverage Yard to document it.
198
+ def self.emitSchemaAsRuby
199
+ kittenpath = "#{MU.myRoot}/modules/mu/kittens.rb"
200
+ MU.log "Converting Basket of Kittens schema to Ruby objects in #{kittenpath}"
201
+ kitten_rb = File.new(kittenpath, File::CREAT|File::TRUNC|File::RDWR, 0644)
202
+ kitten_rb.puts "### THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT ###"
203
+ kitten_rb.puts "#"
204
+ kitten_rb.puts "#"
205
+ kitten_rb.puts "#"
206
+ kitten_rb.puts "module MU"
207
+ kitten_rb.puts "class Config"
208
+ kitten_rb.puts "\t# The configuration file format for Mu application stacks."
209
+ self.printSchema(kitten_rb, ["BasketofKittens"], MU::Config.docSchema)
210
+ kitten_rb.puts "end"
211
+ kitten_rb.puts "end"
212
+ kitten_rb.close
213
+
214
+ end
215
+
216
+ # Emit our Basket of Kittens schema in a format that YARD can comprehend
217
+ # and turn into documentation.
218
+ def self.printSchema(kitten_rb, class_hierarchy, schema, in_array = false, required = false, prefix: nil)
219
+ return if schema.nil?
220
+
221
+ if schema["type"] == "object"
222
+ printme = []
223
+
224
+ if !schema["properties"].nil?
225
+ # order sub-elements by whether they're required, so we can use YARD's
226
+ # grouping tags on them
227
+ if !schema["required"].nil? and schema["required"].size > 0
228
+ prop_list = schema["properties"].keys.sort_by { |name|
229
+ schema["required"].include?(name) ? 0 : 1
230
+ }
231
+ else
232
+ prop_list = schema["properties"].keys
233
+ end
234
+ req = false
235
+ printme << "# @!group Optional parameters" if schema["required"].nil? or schema["required"].size == 0
236
+ prop_list.each { |name|
237
+ prop = schema["properties"][name]
238
+
239
+ if class_hierarchy.size == 1
240
+
241
+ _shortclass, cfg_name, cfg_plural, _classname = MU::Cloud.getResourceNames(name, false)
242
+ if cfg_name
243
+ example_path = MU.myRoot+"/modules/mu/config/"+cfg_name+".yml"
244
+ if File.exist?(example_path)
245
+ example = "#\n# Examples:\n#\n"
246
+ # XXX these variables are all parameters from the BoKs in
247
+ # modules/tests. A really clever implementation would read
248
+ # and parse them to get default values, perhaps, instead of
249
+ # hard-coding them here.
250
+ instance_type = "t2.medium"
251
+ db_size = "db.t2.medium"
252
+ vpc_name = "some_vpc"
253
+ logs_name = "some_loggroup"
254
+ queues_name = "some_queue"
255
+ server_pools_name = "some_server_pool"
256
+ ["simple", "complex"].each { |complexity|
257
+ erb = ERB.new(File.read(example_path), nil, "<>")
258
+ example += "# !!!yaml\n"
259
+ example += "# ---\n"
260
+ example += "# appname: #{complexity}\n"
261
+ example += "# #{cfg_plural}:\n"
262
+ firstline = true
263
+ erb.result(binding).split(/\n/).each { |l|
264
+ l.chomp!
265
+ l.sub!(/#.*/, "") if !l.match(/#(?:INTERNET|NAT|DENY)/)
266
+ next if l.empty? or l.match(/^\s+$/)
267
+ if firstline
268
+ l = "- "+l
269
+ firstline = false
270
+ else
271
+ l = " "+l
272
+ end
273
+ example += "# "+l+" "+"\n"
274
+ }
275
+ example += "# &nbsp;\n#\n" if complexity == "simple"
276
+ }
277
+ schema["properties"][name]["items"]["description"] ||= ""
278
+ if !schema["properties"][name]["items"]["description"].empty?
279
+ schema["properties"][name]["items"]["description"] += "\n"
280
+ end
281
+ schema["properties"][name]["items"]["description"] += example
282
+ end
283
+ end
284
+ end
285
+
286
+ if !schema["required"].nil? and schema["required"].include?(name)
287
+ printme << "# @!group Required parameters" if !req
288
+ req = true
289
+ else
290
+ if req
291
+ printme << "# @!endgroup"
292
+ printme << "# @!group Optional parameters"
293
+ end
294
+ req = false
295
+ end
296
+
297
+ printme << self.printSchema(kitten_rb, class_hierarchy+ [name], prop, false, req, prefix: schema["prefix"])
298
+ }
299
+ printme << "# @!endgroup"
300
+ end
301
+
302
+ tabs = 1
303
+ class_hierarchy.each { |classname|
304
+ if classname == class_hierarchy.last and !schema['description'].nil?
305
+ kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "# #{schema['description']}\n"
306
+ end
307
+ kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "class #{classname}"
308
+ tabs = tabs + 1
309
+ }
310
+ printme.each { |lines|
311
+ if !lines.nil? and lines.is_a?(String)
312
+ lines.lines.each { |line|
313
+ kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + line
314
+ }
315
+ end
316
+ }
317
+
318
+ i = class_hierarchy.size
319
+ until i == 0 do
320
+ tabs = tabs - 1
321
+ kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "end"
322
+ i -= 1
323
+ end
324
+
325
+ # And now that we've dealt with our children, pass our own rendered
326
+ # commentary back up to our caller.
327
+ name = class_hierarchy.last
328
+ if in_array
329
+ type = "Array<#{class_hierarchy.join("::")}>"
330
+ else
331
+ type = class_hierarchy.join("::")
332
+ end
333
+
334
+ docstring = "\n"
335
+ docstring = docstring + "# **REQUIRED**\n" if required
336
+ docstring = docstring + "# **"+schema["prefix"]+"**\n" if schema["prefix"]
337
+ docstring = docstring + "# #{schema['description'].gsub(/\n/, "\n#")}\n" if !schema['description'].nil?
338
+ docstring = docstring + "#\n"
339
+ docstring = docstring + "# @return [#{type}]\n"
340
+ docstring = docstring + "# @see #{class_hierarchy.join("::")}\n"
341
+ docstring = docstring + "attr_accessor :#{name}"
342
+ return docstring
343
+
344
+ elsif schema["type"] == "array"
345
+ return self.printSchema(kitten_rb, class_hierarchy, schema['items'], true, required, prefix: prefix)
346
+ else
347
+ name = class_hierarchy.last
348
+ if schema['type'].nil?
349
+ MU.log "Couldn't determine schema type in #{class_hierarchy.join(" => ")}", MU::WARN, details: schema
350
+ return nil
351
+ end
352
+ if in_array
353
+ type = "Array<#{schema['type'].capitalize}>"
354
+ else
355
+ type = schema['type'].capitalize
356
+ end
357
+ docstring = "\n"
358
+
359
+ prefixes = []
360
+ prefixes << "# **REQUIRED**" if required and schema['default'].nil?
361
+ prefixes << "# **"+schema["prefix"]+"**" if schema["prefix"]
362
+ prefixes << "# **Default: `#{schema['default']}`**" if !schema['default'].nil?
363
+ if !schema['enum'].nil? and !schema["enum"].empty?
364
+ prefixes << "# **Must be one of: `#{schema['enum'].join(', ')}`**"
365
+ elsif !schema['pattern'].nil?
366
+ # XXX unquoted regex chars confuse the hell out of YARD. How do we
367
+ # quote {}[] etc in YARD-speak?
368
+ prefixes << "# **Must match pattern `#{schema['pattern'].gsub(/\n/, "\n#")}`**"
369
+ end
370
+
371
+ if prefixes.size > 0
372
+ docstring += prefixes.join(",\n")
373
+ if schema['description'] and schema['description'].size > 1
374
+ docstring += " - "
375
+ end
376
+ docstring += "\n"
377
+ end
378
+
379
+ docstring = docstring + "# #{schema['description'].gsub(/\n/, "\n#")}\n" if !schema['description'].nil?
380
+ docstring = docstring + "#\n"
381
+ docstring = docstring + "# @return [#{type}]\n"
382
+ docstring = docstring + "attr_accessor :#{name}"
383
+
384
+ return docstring
385
+ end
386
+ end
387
+
388
+ # Emit our mu.yaml schema in a format that YARD can comprehend and turn into
389
+ # documentation.
390
+ def self.printMuYamlSchema(muyaml_rb, class_hierarchy, schema, in_array = false, required = false)
391
+ return if schema.nil?
392
+ if schema["subtree"]
393
+ printme = Array.new
394
+ # order sub-elements by whether they're required, so we can use YARD's
395
+ # grouping tags on them
396
+ have_required = schema["subtree"].keys.any? { |k| schema["subtree"][k]["required"] }
397
+ prop_list = schema["subtree"].keys.sort { |a, b|
398
+ if schema["subtree"][a]["required"] and !schema["subtree"][b]["required"]
399
+ -1
400
+ elsif !schema["subtree"][a]["required"] and schema["subtree"][b]["required"]
401
+ 1
402
+ else
403
+ a <=> b
404
+ end
405
+ }
406
+
407
+ req = false
408
+ printme << "# @!group Optional parameters" if !have_required
409
+ prop_list.each { |name|
410
+ prop = schema["subtree"][name]
411
+ if prop["required"]
412
+ printme << "# @!group Required parameters" if !req
413
+ req = true
414
+ else
415
+ if req
416
+ printme << "# @!endgroup"
417
+ printme << "# @!group Optional parameters"
418
+ end
419
+ req = false
420
+ end
421
+
422
+ printme << self.printMuYamlSchema(muyaml_rb, class_hierarchy+ [name], prop, false, req)
423
+ }
424
+ printme << "# @!endgroup"
425
+
426
+ desc = (schema['desc'] || schema['title'])
427
+
428
+ tabs = 1
429
+ class_hierarchy.each { |classname|
430
+ if classname == class_hierarchy.last and desc
431
+ muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "# #{desc}\n"
432
+ end
433
+ muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "class #{classname}"
434
+ tabs = tabs + 1
435
+ }
436
+ printme.each { |lines|
437
+ if !lines.nil? and lines.is_a?(String)
438
+ lines.lines.each { |line|
439
+ muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + line
440
+ }
441
+ end
442
+ }
443
+
444
+ # class_hierarchy.each { |classname|
445
+ # tabs = tabs - 1
446
+ # muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "end"
447
+ # }
448
+ i = class_hierarchy.size
449
+ until i == 0 do
450
+ tabs = tabs - 1
451
+ muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "end"
452
+ i -= 1
453
+ end
454
+
455
+ # And now that we've dealt with our children, pass our own rendered
456
+ # commentary back up to our caller.
457
+ name = class_hierarchy.last
458
+ if in_array
459
+ type = "Array<#{class_hierarchy.join("::")}>"
460
+ else
461
+ type = class_hierarchy.join("::")
462
+ end
463
+
464
+ docstring = "\n"
465
+ docstring = docstring + "# **REQUIRED**\n" if required
466
+ # docstring = docstring + "# **"+schema["prefix"]+"**\n" if schema["prefix"]
467
+ docstring = docstring + "# #{desc.gsub(/\n/, "\n#")}\n" if desc
468
+ docstring = docstring + "#\n"
469
+ docstring = docstring + "# @return [#{type}]\n"
470
+ docstring = docstring + "# @see #{class_hierarchy.join("::")}\n"
471
+ docstring = docstring + "attr_accessor :#{name}"
472
+ return docstring
473
+
474
+ else
475
+ in_array = schema["array"]
476
+ name = class_hierarchy.last
477
+ type = if schema['boolean']
478
+ "Boolean"
479
+ else
480
+ "String"
481
+ end
482
+ if in_array
483
+ type = "Array<#{type}>"
484
+ end
485
+ docstring = "\n"
486
+
487
+ prefixes = []
488
+ prefixes << "# **REQUIRED**" if schema["required"] and schema['default'].nil?
489
+ # prefixes << "# **"+schema["prefix"]+"**" if schema["prefix"]
490
+ prefixes << "# **Default: `#{schema['default']}`**" if !schema['default'].nil?
491
+ if !schema['pattern'].nil?
492
+ # XXX unquoted regex chars confuse the hell out of YARD. How do we
493
+ # quote {}[] etc in YARD-speak?
494
+ prefixes << "# **Must match pattern `#{schema['pattern'].to_s.gsub(/\n/, "\n#")}`**"
495
+ end
496
+
497
+ desc = (schema['desc'] || schema['title'])
498
+ if prefixes.size > 0
499
+ docstring += prefixes.join(",\n")
500
+ if desc and desc.size > 1
501
+ docstring += " - "
502
+ end
503
+ docstring += "\n"
504
+ end
505
+
506
+ docstring = docstring + "# #{desc.gsub(/\n/, "\n#")}\n" if !desc.nil?
507
+ docstring = docstring + "#\n"
508
+ docstring = docstring + "# @return [#{type}]\n"
509
+ docstring = docstring + "attr_accessor :#{name}"
510
+
511
+ return docstring
512
+ end
513
+ end
514
+
515
+ end #class
516
+ end #module