cloud-mu 3.1.3 → 3.1.4

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +10 -2
  3. data/bin/mu-adopt +5 -1
  4. data/bin/mu-load-config.rb +2 -3
  5. data/bin/mu-run-tests +112 -27
  6. data/cloud-mu.gemspec +20 -20
  7. data/cookbooks/mu-tools/libraries/helper.rb +2 -1
  8. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  9. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  10. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  11. data/extras/image-generators/Google/centos6.yaml +1 -0
  12. data/extras/image-generators/Google/centos7.yaml +1 -1
  13. data/modules/mommacat.ru +5 -15
  14. data/modules/mu.rb +10 -14
  15. data/modules/mu/adoption.rb +20 -14
  16. data/modules/mu/cleanup.rb +13 -9
  17. data/modules/mu/cloud.rb +26 -26
  18. data/modules/mu/clouds/aws.rb +100 -59
  19. data/modules/mu/clouds/aws/alarm.rb +4 -2
  20. data/modules/mu/clouds/aws/bucket.rb +25 -21
  21. data/modules/mu/clouds/aws/cache_cluster.rb +25 -23
  22. data/modules/mu/clouds/aws/collection.rb +21 -20
  23. data/modules/mu/clouds/aws/container_cluster.rb +47 -26
  24. data/modules/mu/clouds/aws/database.rb +57 -68
  25. data/modules/mu/clouds/aws/dnszone.rb +14 -14
  26. data/modules/mu/clouds/aws/endpoint.rb +20 -16
  27. data/modules/mu/clouds/aws/firewall_rule.rb +19 -16
  28. data/modules/mu/clouds/aws/folder.rb +7 -7
  29. data/modules/mu/clouds/aws/function.rb +15 -12
  30. data/modules/mu/clouds/aws/group.rb +14 -10
  31. data/modules/mu/clouds/aws/habitat.rb +16 -13
  32. data/modules/mu/clouds/aws/loadbalancer.rb +16 -15
  33. data/modules/mu/clouds/aws/log.rb +13 -10
  34. data/modules/mu/clouds/aws/msg_queue.rb +15 -8
  35. data/modules/mu/clouds/aws/nosqldb.rb +18 -11
  36. data/modules/mu/clouds/aws/notifier.rb +11 -6
  37. data/modules/mu/clouds/aws/role.rb +87 -70
  38. data/modules/mu/clouds/aws/search_domain.rb +30 -19
  39. data/modules/mu/clouds/aws/server.rb +102 -72
  40. data/modules/mu/clouds/aws/server_pool.rb +47 -28
  41. data/modules/mu/clouds/aws/storage_pool.rb +5 -6
  42. data/modules/mu/clouds/aws/user.rb +13 -10
  43. data/modules/mu/clouds/aws/vpc.rb +135 -121
  44. data/modules/mu/clouds/azure.rb +16 -9
  45. data/modules/mu/clouds/azure/container_cluster.rb +2 -3
  46. data/modules/mu/clouds/azure/firewall_rule.rb +10 -10
  47. data/modules/mu/clouds/azure/habitat.rb +8 -6
  48. data/modules/mu/clouds/azure/loadbalancer.rb +5 -5
  49. data/modules/mu/clouds/azure/role.rb +8 -10
  50. data/modules/mu/clouds/azure/server.rb +65 -25
  51. data/modules/mu/clouds/azure/user.rb +5 -7
  52. data/modules/mu/clouds/azure/vpc.rb +12 -15
  53. data/modules/mu/clouds/cloudformation.rb +8 -7
  54. data/modules/mu/clouds/cloudformation/vpc.rb +2 -4
  55. data/modules/mu/clouds/google.rb +39 -24
  56. data/modules/mu/clouds/google/bucket.rb +9 -11
  57. data/modules/mu/clouds/google/container_cluster.rb +27 -42
  58. data/modules/mu/clouds/google/database.rb +6 -9
  59. data/modules/mu/clouds/google/firewall_rule.rb +11 -10
  60. data/modules/mu/clouds/google/folder.rb +16 -9
  61. data/modules/mu/clouds/google/function.rb +127 -161
  62. data/modules/mu/clouds/google/group.rb +21 -18
  63. data/modules/mu/clouds/google/habitat.rb +18 -15
  64. data/modules/mu/clouds/google/loadbalancer.rb +14 -16
  65. data/modules/mu/clouds/google/role.rb +48 -31
  66. data/modules/mu/clouds/google/server.rb +105 -105
  67. data/modules/mu/clouds/google/server_pool.rb +12 -31
  68. data/modules/mu/clouds/google/user.rb +67 -13
  69. data/modules/mu/clouds/google/vpc.rb +58 -65
  70. data/modules/mu/config.rb +89 -1738
  71. data/modules/mu/config/bucket.rb +3 -3
  72. data/modules/mu/config/collection.rb +3 -3
  73. data/modules/mu/config/container_cluster.rb +2 -2
  74. data/modules/mu/config/dnszone.rb +5 -5
  75. data/modules/mu/config/doc_helpers.rb +517 -0
  76. data/modules/mu/config/endpoint.rb +3 -3
  77. data/modules/mu/config/firewall_rule.rb +118 -3
  78. data/modules/mu/config/folder.rb +3 -3
  79. data/modules/mu/config/function.rb +2 -2
  80. data/modules/mu/config/group.rb +3 -3
  81. data/modules/mu/config/habitat.rb +3 -3
  82. data/modules/mu/config/loadbalancer.rb +3 -3
  83. data/modules/mu/config/log.rb +3 -3
  84. data/modules/mu/config/msg_queue.rb +3 -3
  85. data/modules/mu/config/nosqldb.rb +3 -3
  86. data/modules/mu/config/notifier.rb +2 -2
  87. data/modules/mu/config/ref.rb +333 -0
  88. data/modules/mu/config/role.rb +3 -3
  89. data/modules/mu/config/schema_helpers.rb +508 -0
  90. data/modules/mu/config/search_domain.rb +3 -3
  91. data/modules/mu/config/server.rb +86 -58
  92. data/modules/mu/config/server_pool.rb +2 -2
  93. data/modules/mu/config/tail.rb +189 -0
  94. data/modules/mu/config/user.rb +3 -3
  95. data/modules/mu/config/vpc.rb +44 -4
  96. data/modules/mu/defaults/Google.yaml +2 -2
  97. data/modules/mu/deploy.rb +13 -10
  98. data/modules/mu/groomer.rb +1 -1
  99. data/modules/mu/groomers/ansible.rb +69 -24
  100. data/modules/mu/groomers/chef.rb +52 -44
  101. data/modules/mu/logger.rb +17 -14
  102. data/modules/mu/master.rb +317 -2
  103. data/modules/mu/master/chef.rb +3 -4
  104. data/modules/mu/master/ldap.rb +3 -3
  105. data/modules/mu/master/ssl.rb +12 -2
  106. data/modules/mu/mommacat.rb +85 -1766
  107. data/modules/mu/mommacat/daemon.rb +394 -0
  108. data/modules/mu/mommacat/naming.rb +366 -0
  109. data/modules/mu/mommacat/storage.rb +689 -0
  110. data/modules/tests/bucket.yml +4 -0
  111. data/modules/tests/{win2k12.yaml → needwork/win2k12.yaml} +0 -0
  112. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  113. data/modules/tests/regrooms/bucket.yml +19 -0
  114. metadata +112 -102
@@ -47,10 +47,10 @@ module MU
47
47
  end
48
48
 
49
49
  # Generic pre-processing of {MU::Config::BasketofKittens::search_domains}, bare and unvalidated.
50
- # @param dom [Hash]: The resource to process and validate
51
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
50
+ # @param _dom [Hash]: The resource to process and validate
51
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
52
52
  # @return [Boolean]: True if validation succeeded, False otherwise
53
- def self.validate(dom, configurator)
53
+ def self.validate(_dom, _configurator)
54
54
  ok = true
55
55
  # This resource basically only exists in AWS, so the validation lives
56
56
  # there. If some other provider comes up with it we can factor
@@ -17,6 +17,65 @@ module MU
17
17
  # Basket of Kittens config schema and parser logic. See modules/mu/clouds/*/server.rb
18
18
  class Server
19
19
 
20
+ # Verify that a server or server_pool has a valid LDAP config referencing
21
+ # valid Vaults for credentials.
22
+ # @param server [Hash]
23
+ def self.checkVaultRefs(server)
24
+ ok = true
25
+ server['vault_access'] = [] if server['vault_access'].nil?
26
+ server['groomer'] ||= self.defaultGroomer
27
+ groomclass = MU::Groomer.loadGroomer(server['groomer'])
28
+
29
+ begin
30
+ if !server['active_directory'].nil?
31
+ ["domain_admin_vault", "domain_join_vault"].each { |vault_class|
32
+ server['vault_access'] << {
33
+ "vault" => server['active_directory'][vault_class]['vault'],
34
+ "item" => server['active_directory'][vault_class]['item']
35
+ }
36
+ item = groomclass.getSecret(
37
+ vault: server['active_directory'][vault_class]['vault'],
38
+ item: server['active_directory'][vault_class]['item'],
39
+ )
40
+ ["username_field", "password_field"].each { |field|
41
+ if !item.has_key?(server['active_directory'][vault_class][field])
42
+ ok = false
43
+ MU.log "I don't see a value named #{field} in Chef Vault #{server['active_directory'][vault_class]['vault']}:#{server['active_directory'][vault_class]['item']}", MU::ERR
44
+ end
45
+ }
46
+ }
47
+ end
48
+
49
+ if !server['windows_auth_vault'].nil?
50
+ server['use_cloud_provider_windows_password'] = false
51
+
52
+ server['vault_access'] << {
53
+ "vault" => server['windows_auth_vault']['vault'],
54
+ "item" => server['windows_auth_vault']['item']
55
+ }
56
+ item = groomclass.getSecret(
57
+ vault: server['windows_auth_vault']['vault'],
58
+ item: server['windows_auth_vault']['item']
59
+ )
60
+ ["password_field", "ec2config_password_field", "sshd_password_field"].each { |field|
61
+ if !item.has_key?(server['windows_auth_vault'][field])
62
+ MU.log "No value named #{field} in Chef Vault #{server['windows_auth_vault']['vault']}:#{server['windows_auth_vault']['item']}, will use a generated password.", MU::NOTICE
63
+ server['windows_auth_vault'].delete(field)
64
+ end
65
+ }
66
+ end
67
+ # Check all of the non-special ones while we're at it
68
+ server['vault_access'].each { |v|
69
+ next if v['vault'] == "splunk" and v['item'] == "admin_user"
70
+ item = groomclass.getSecret(vault: v['vault'], item: v['item'])
71
+ }
72
+ rescue MuError
73
+ MU.log "Can't load a Chef Vault I was configured to use. Does it exist?", MU::ERR
74
+ ok = false
75
+ end
76
+ return ok
77
+ end
78
+
20
79
  # Generate schema for a storage volume
21
80
  # @return [Hash]
22
81
  def self.storage_primitive
@@ -332,9 +391,8 @@ module MU
332
391
  },
333
392
  "userdata_script" => userdata_primitive,
334
393
  "windows_admin_username" => {
335
- "type" => "string",
336
- "default" => "Administrator",
337
- "description" => "Use an alternate Windows account for Administrator functions. Will change the name of the Administrator account, if it has not already been done."
394
+ "type" => "string",
395
+ "description" => "Use an alternate Windows account for Administrator functions. Will change the name of the Administrator account, if it has not already been done."
338
396
  },
339
397
  "windows_auth_vault" => {
340
398
  "type" => "object",
@@ -370,60 +428,30 @@ module MU
370
428
  }
371
429
  },
372
430
  "ssh_user" => {
373
- "type" => "string",
374
- "default" => "root",
375
- "default_if" => [
376
- {
377
- "key_is" => "platform",
378
- "value_is" => "windows",
379
- "set" => "Administrator"
380
- },
381
- {
382
- "key_is" => "platform",
383
- "value_is" => "win2k12",
384
- "set" => "Administrator"
385
- },
386
- {
387
- "key_is" => "platform",
388
- "value_is" => "win2k12r2",
389
- "set" => "Administrator"
390
- },
391
- {
392
- "key_is" => "platform",
393
- "value_is" => "win2k16",
394
- "set" => "Administrator"
395
- },
396
- {
397
- "key_is" => "platform",
398
- "value_is" => "ubuntu",
399
- "set" => "ubuntu"
400
- },
401
- {
402
- "key_is" => "platform",
403
- "value_is" => "ubuntu14",
404
- "set" => "ubuntu"
405
- },
406
- {
407
- "key_is" => "platform",
408
- "value_is" => "centos7",
409
- "set" => "centos"
410
- },
411
- {
412
- "key_is" => "platform",
413
- "value_is" => "rhel7",
414
- "set" => "ec2-user"
415
- },
416
- {
417
- "key_is" => "platform",
418
- "value_is" => "rhel71",
419
- "set" => "ec2-user"
420
- },
421
- {
422
- "key_is" => "platform",
423
- "value_is" => "amazon",
424
- "set" => "ec2-user"
425
- }
426
- ]
431
+ "type" => "string",
432
+ "default" => "root",
433
+ "default_if" => [
434
+ {
435
+ "key_is" => "platform",
436
+ "value_is" => "centos",
437
+ "set" => "centos"
438
+ },
439
+ {
440
+ "key_is" => "platform",
441
+ "value_is" => "centos6",
442
+ "set" => "centos"
443
+ },
444
+ {
445
+ "key_is" => "platform",
446
+ "value_is" => "centos7",
447
+ "set" => "centos"
448
+ },
449
+ {
450
+ "key_is" => "platform",
451
+ "value_is" => "centos8",
452
+ "set" => "centos"
453
+ }
454
+ ]
427
455
  },
428
456
  "use_cloud_provider_windows_password" => {
429
457
  "type" => "boolean",
@@ -595,7 +623,7 @@ module MU
595
623
  server['ingress_rules'] ||= []
596
624
  server['vault_access'] ||= []
597
625
  server['vault_access'] << {"vault" => "splunk", "item" => "admin_user"}
598
- ok = false if !MU::Config.check_vault_refs(server)
626
+ ok = false if !MU::Config::Server.checkVaultRefs(server)
599
627
 
600
628
  if server["cloud"] != "Azure"
601
629
  server['dependencies'] << configurator.adminFirewallRuleset(vpc: server['vpc'], region: server['region'], cloud: server['cloud'], credentials: server['credentials'])
@@ -178,7 +178,7 @@ module MU
178
178
  pool['ingress_rules'] ||= []
179
179
  pool['vault_access'] ||= []
180
180
  pool['vault_access'] << {"vault" => "splunk", "item" => "admin_user"}
181
- ok = false if !MU::Config.check_vault_refs(pool)
181
+ ok = false if !MU::Config::Server.checkVaultRefs(pool)
182
182
 
183
183
  if !pool['scrub_mu_isms'] and pool["cloud"] != "Azure"
184
184
  pool['dependencies'] << configurator.adminFirewallRuleset(vpc: pool['vpc'], region: pool['region'], cloud: pool['cloud'], credentials: pool['credentials'])
@@ -202,7 +202,7 @@ module MU
202
202
  # ok = false if !insertKitten(alarm, "alarms")
203
203
  # }
204
204
  # end
205
- if pool["basis"]["server"] != nil
205
+ if pool["basis"] and pool["basis"]["server"]
206
206
  pool["dependencies"] << {"type" => "server", "name" => pool["basis"]["server"]}
207
207
  end
208
208
  if !pool['static_ip'].nil? and !pool['ip'].nil?
@@ -0,0 +1,189 @@
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 for config leaves that came from ERB parameters instead of raw
21
+ # YAML or JSON. Will behave like a string for things that expect that
22
+ # sort of thing. Code that needs to know that this leaf was the result of
23
+ # a parameter will be able to tell by the object class being something
24
+ # other than a plain string, array, or hash.
25
+ class Tail
26
+ @value = nil
27
+ @name = nil
28
+ @prettyname = nil
29
+ @description = nil
30
+ @prefix = ""
31
+ @suffix = ""
32
+ @is_list_element = false
33
+ @pseudo = false
34
+ @runtimecode = nil
35
+ @valid_values = []
36
+ @index = 0
37
+ attr_reader :description
38
+ attr_reader :pseudo
39
+ attr_reader :index
40
+ attr_reader :value
41
+ attr_reader :runtimecode
42
+ attr_reader :valid_values
43
+ attr_reader :is_list_element
44
+
45
+ def initialize(name, value, prettyname = nil, cloudtype = "String", valid_values = [], description = "", is_list_element = false, prefix: "", suffix: "", pseudo: false, runtimecode: nil, index: 0)
46
+ @name = name
47
+ @bindings = {}
48
+ @value = value
49
+ @valid_values = valid_values
50
+ @pseudo = pseudo
51
+ @index = index
52
+ @runtimecode = runtimecode
53
+ @cloudtype = cloudtype
54
+ @is_list_element = is_list_element
55
+ @description ||=
56
+ if !description.nil?
57
+ description
58
+ else
59
+ ""
60
+ end
61
+ @prettyname ||=
62
+ if !prettyname.nil?
63
+ prettyname
64
+ else
65
+ @name.capitalize.gsub(/[^a-z0-9]/i, "")
66
+ end
67
+ @prefix = prefix if !prefix.nil?
68
+ @suffix = suffix if !suffix.nil?
69
+ end
70
+
71
+ # Return the parameter name of this Tail
72
+ def getName
73
+ @name
74
+ end
75
+ # Return the platform-specific cloud type of this Tail
76
+ def getCloudType
77
+ @cloudtype
78
+ end
79
+ # Return the human-friendly name of this Tail
80
+ def getPrettyName
81
+ @prettyname
82
+ end
83
+ # Walk like a String
84
+ def to_s
85
+ @prefix.to_s+@value.to_s+@suffix.to_s
86
+ end
87
+ # Quack like a String
88
+ def to_str
89
+ to_s
90
+ end
91
+ # Upcase like a String
92
+ def upcase
93
+ to_s.upcase
94
+ end
95
+ # Downcase like a String
96
+ def downcase
97
+ to_s.downcase
98
+ end
99
+ # Check for emptiness like a String
100
+ def empty?
101
+ to_s.empty?
102
+ end
103
+ # Match like a String
104
+ def match(*args)
105
+ to_s.match(*args)
106
+ end
107
+ # Check for equality like a String
108
+ def ==(o)
109
+ (o.class == self.class or o.class == "String") && o.to_s == to_s
110
+ end
111
+ # Concatenate like a string
112
+ def +(o)
113
+ return to_s if o.nil?
114
+ to_s + o.to_s
115
+ end
116
+ # Perform global substitutions like a String
117
+ def gsub(*args)
118
+ to_s.gsub(*args)
119
+ end
120
+ end
121
+
122
+ # Wrapper method for creating a {MU::Config::Tail} object as a reference to
123
+ # a parameter that's valid in the loaded configuration.
124
+ # @param param [<String>]: The name of the parameter to which this should be tied.
125
+ # @param value [<String>]: The value of the parameter to return when asked
126
+ # @param prettyname [<String>]: A human-friendly parameter name to be used when generating CloudFormation templates and the like
127
+ # @param cloudtype [<String>]: A platform-specific identifier used by cloud layers to identify a parameter's type, e.g. AWS::EC2::VPC::Id
128
+ # @param valid_values [Array<String>]: A list of acceptable String values for the given parameter.
129
+ # @param description [<String>]: A long-form description of what the parameter does.
130
+ # @param list_of [<String>]: Indicates that the value should be treated as a member of a list (array) by the cloud layer.
131
+ # @param prefix [<String>]: A static String that should be prefixed to the stored value when queried
132
+ # @param suffix [<String>]: A static String that should be appended to the stored value when queried
133
+ # @param pseudo [<Boolean>]: This is a pseudo-parameter, automatically provided, and not available as user input.
134
+ # @param runtimecode [<String>]: Actual code to allow the cloud layer to interpret literally in its own idiom, e.g. '"Ref" : "AWS::StackName"' for CloudFormation
135
+ def getTail(param, value: nil, prettyname: nil, cloudtype: "String", valid_values: [], description: nil, list_of: nil, prefix: "", suffix: "", pseudo: false, runtimecode: nil)
136
+ if value.nil?
137
+ if @@parameters.nil? or !@@parameters.has_key?(param)
138
+ MU.log "Parameter '#{param}' (#{param.class.name}) referenced in config but not provided (#{caller[0]})", MU::DEBUG, details: @@parameters
139
+ return nil
140
+ # raise DeployParamError
141
+ else
142
+ value = @@parameters[param]
143
+ end
144
+ end
145
+ if !prettyname.nil?
146
+ prettyname.gsub!(/[^a-z0-9]/i, "") # comply with CloudFormation restrictions
147
+ end
148
+ if value.is_a?(MU::Config::Tail)
149
+ MU.log "Parameter #{param} is using a nested parameter as a value. This rarely works, depending on the target cloud. YMMV.", MU::WARN
150
+ tail = MU::Config::Tail.new(param, value, prettyname, cloudtype, valid_values, description, prefix: prefix, suffix: suffix, pseudo: pseudo, runtimecode: runtimecode)
151
+ elsif !list_of.nil? or (@@tails.has_key?(param) and @@tails[param].is_a?(Array))
152
+ tail = []
153
+ count = 0
154
+ value.split(/\s*,\s*/).each { |subval|
155
+ if @@tails.has_key?(param) and !@@tails[param][count].nil?
156
+ subval = @@tails[param][count].values.first.to_s if subval.nil?
157
+ list_of = @@tails[param][count].values.first.getName if list_of.nil?
158
+ prettyname = @@tails[param][count].values.first.getPrettyName if prettyname.nil?
159
+ description = @@tails[param][count].values.first.description if description.nil?
160
+ valid_values = @@tails[param][count].values.first.valid_values if valid_values.nil? or valid_values.empty?
161
+ cloudtype = @@tails[param][count].values.first.getCloudType if @@tails[param][count].values.first.getCloudType != "String"
162
+ end
163
+ prettyname = param.capitalize if prettyname.nil?
164
+ tail << { list_of => MU::Config::Tail.new(list_of, subval, prettyname, cloudtype, valid_values, description, true, pseudo: pseudo, index: count) }
165
+ count = count + 1
166
+ }
167
+ else
168
+ if @@tails.has_key?(param)
169
+ pseudo = @@tails[param].pseudo
170
+ value = @@tails[param].to_s if value.nil?
171
+ prettyname = @@tails[param].getPrettyName if prettyname.nil?
172
+ description = @@tails[param].description if description.nil?
173
+ valid_values = @@tails[param].valid_values if valid_values.nil? or valid_values.empty?
174
+ cloudtype = @@tails[param].getCloudType if @@tails[param].getCloudType != "String"
175
+ end
176
+ tail = MU::Config::Tail.new(param, value, prettyname, cloudtype, valid_values, description, prefix: prefix, suffix: suffix, pseudo: pseudo, runtimecode: runtimecode)
177
+ end
178
+
179
+ if valid_values and valid_values.size > 0 and value
180
+ if !valid_values.include?(value)
181
+ raise DeployParamError, "Invalid parameter value '#{value}' supplied for '#{param}'"
182
+ end
183
+ end
184
+ @@tails[param] = tail
185
+
186
+ tail
187
+ end
188
+ end
189
+ end
@@ -68,10 +68,10 @@ module MU
68
68
  end
69
69
 
70
70
  # Generic pre-processing of {MU::Config::BasketofKittens::user}, bare and unvalidated.
71
- # @param user [Hash]: The resource to process and validate
72
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
71
+ # @param _user [Hash]: The resource to process and validate
72
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
73
73
  # @return [Boolean]: True if validation succeeded, False otherwise
74
- def self.validate(user, configurator)
74
+ def self.validate(_user, _configurator)
75
75
  ok = true
76
76
 
77
77
  ok
@@ -418,7 +418,6 @@ module MU
418
418
  if !vpc['ip_block']
419
419
  if configurator.updating and configurator.existing_deploy and
420
420
  configurator.existing_deploy.original_config['vpcs']
421
- pieces = []
422
421
  configurator.existing_deploy.original_config['vpcs'].each { |v|
423
422
  if v['name'] == vpc['name']
424
423
  vpc['ip_block'] = v['ip_block']
@@ -799,7 +798,7 @@ MU.log "VPC lookup cache hit", MU::WARN, details: vpc_block
799
798
  end
800
799
  @@reference_cache[vpc_block] ||= ext_vpc if ok
801
800
  end
802
- rescue Exception => e
801
+ rescue StandardError => e
803
802
  raise MuError, e.inspect, e.backtrace
804
803
  ensure
805
804
  if !ext_vpc and vpc_block['cloud'] != "CloudFormation"
@@ -877,7 +876,7 @@ MU.log "VPC lookup cache hit", MU::WARN, details: vpc_block
877
876
  tag_key, tag_value = vpc_block['tag'].split(/=/, 2) if !vpc_block['tag'].nil?
878
877
  begin
879
878
  ext_subnet = ext_vpc.getSubnet(cloud_id: vpc_block['subnet_id'], name: vpc_block['subnet_name'], tag_key: tag_key, tag_value: tag_value)
880
- rescue MuError => e
879
+ rescue MuError
881
880
  end
882
881
 
883
882
  if ext_subnet.nil?
@@ -918,7 +917,6 @@ MU.log "VPC lookup cache hit", MU::WARN, details: vpc_block
918
917
  public_subnets = []
919
918
  public_subnets_map = {}
920
919
  subnet_ptr = "subnet_id"
921
- all_subnets = []
922
920
  if !is_sibling
923
921
  pub = priv = 0
924
922
  raise MuError, "No subnets found in #{ext_vpc}" if ext_vpc.subnets.nil?
@@ -1068,5 +1066,47 @@ MU.log "VPC lookup cache hit", MU::WARN, details: vpc_block
1068
1066
  end
1069
1067
 
1070
1068
  end
1069
+
1070
+ # Take an IP block and split it into a more-or-less arbitrary number of
1071
+ # subnets.
1072
+ # @param ip_block [String]: CIDR of the network to subdivide
1073
+ # @param subnets_desired [Integer]: Number of subnets we want back
1074
+ # @param max_mask [Integer]: The highest netmask we're allowed to use for a subnet (various by cloud provider)
1075
+ # @return [MU::Config::Tail]: Resulting subnet tails, or nil if an error occurred.
1076
+ def divideNetwork(ip_block, subnets_desired, max_mask = 28)
1077
+ cidr = NetAddr::IPv4Net.parse(ip_block.to_s)
1078
+
1079
+ # Ugly but reliable method of landing on the right subnet size
1080
+ subnet_bits = cidr.netmask.prefix_len
1081
+ begin
1082
+ subnet_bits += 1
1083
+ if subnet_bits > max_mask
1084
+ MU.log "Can't subdivide #{cidr.to_s} into #{subnets_desired.to_s}", MU::ERR
1085
+ raise MuError, "Subnets smaller than /#{max_mask} not permitted"
1086
+ end
1087
+ end while cidr.subnet_count(subnet_bits) < subnets_desired
1088
+
1089
+ if cidr.subnet_count(subnet_bits) > subnets_desired
1090
+ MU.log "Requested #{subnets_desired.to_s} subnets from #{cidr.to_s}, leaving #{(cidr.subnet_count(subnet_bits)-subnets_desired).to_s} unused /#{subnet_bits.to_s}s available", MU::NOTICE
1091
+ end
1092
+
1093
+ begin
1094
+ subnets = []
1095
+ (0..subnets_desired).each { |x|
1096
+ subnets << cidr.nth_subnet(subnet_bits, x).to_s
1097
+ }
1098
+ rescue RuntimeError => e
1099
+ if e.message.match(/exceeds subnets available for allocation/)
1100
+ MU.log e.message, MU::ERR
1101
+ MU.log "I'm attempting to create #{subnets_desired} subnets (one public and one private for each Availability Zone), of #{subnet_size} addresses each, but that's too many for a /#{cidr.netmask.prefix_len} network. Either declare a larger network, or explicitly declare a list of subnets with few enough entries to fit.", MU::ERR
1102
+ return nil
1103
+ else
1104
+ raise e
1105
+ end
1106
+ end
1107
+
1108
+ subnets = getTail("subnetblocks", value: subnets.join(","), cloudtype: "CommaDelimitedList", description: "IP Address ranges to be used for VPC subnets", prettyname: "SubnetIpBlocks", list_of: "ip_block").map { |tail| tail["ip_block"] }
1109
+ subnets
1110
+ end
1071
1111
  end
1072
1112
  end