cloud-mu 3.0.0beta → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +17 -8
- data/ansible/roles/mu-nat/README.md +33 -0
- data/ansible/roles/mu-nat/defaults/main.yml +3 -0
- data/ansible/roles/mu-nat/handlers/main.yml +2 -0
- data/ansible/roles/mu-nat/meta/main.yml +60 -0
- data/ansible/roles/mu-nat/tasks/main.yml +65 -0
- data/ansible/roles/mu-nat/tests/inventory +2 -0
- data/ansible/roles/mu-nat/tests/test.yml +5 -0
- data/ansible/roles/mu-nat/vars/main.yml +2 -0
- data/bin/mu-cleanup +2 -1
- data/bin/mu-configure +950 -948
- data/bin/mu-gen-docs +6 -0
- data/cloud-mu.gemspec +2 -2
- data/cookbooks/mu-tools/recipes/gcloud.rb +8 -1
- data/modules/mommacat.ru +1 -1
- data/modules/mu.rb +31 -39
- data/modules/mu/cloud.rb +11 -1
- data/modules/mu/clouds/aws.rb +8 -3
- data/modules/mu/clouds/aws/alarm.rb +5 -8
- data/modules/mu/clouds/aws/bucket.rb +15 -9
- data/modules/mu/clouds/aws/cache_cluster.rb +60 -26
- data/modules/mu/clouds/aws/collection.rb +4 -4
- data/modules/mu/clouds/aws/container_cluster.rb +50 -33
- data/modules/mu/clouds/aws/database.rb +25 -21
- data/modules/mu/clouds/aws/dnszone.rb +12 -14
- data/modules/mu/clouds/aws/endpoint.rb +5 -8
- data/modules/mu/clouds/aws/firewall_rule.rb +9 -4
- data/modules/mu/clouds/aws/folder.rb +4 -7
- data/modules/mu/clouds/aws/function.rb +5 -8
- data/modules/mu/clouds/aws/group.rb +5 -8
- data/modules/mu/clouds/aws/habitat.rb +2 -5
- data/modules/mu/clouds/aws/loadbalancer.rb +12 -16
- data/modules/mu/clouds/aws/log.rb +6 -9
- data/modules/mu/clouds/aws/msg_queue.rb +16 -19
- data/modules/mu/clouds/aws/nosqldb.rb +27 -18
- data/modules/mu/clouds/aws/notifier.rb +6 -9
- data/modules/mu/clouds/aws/role.rb +4 -7
- data/modules/mu/clouds/aws/search_domain.rb +50 -23
- data/modules/mu/clouds/aws/server.rb +20 -14
- data/modules/mu/clouds/aws/server_pool.rb +22 -12
- data/modules/mu/clouds/aws/storage_pool.rb +9 -14
- data/modules/mu/clouds/aws/user.rb +5 -8
- data/modules/mu/clouds/aws/userdata/linux.erb +7 -1
- data/modules/mu/clouds/aws/vpc.rb +16 -14
- data/modules/mu/clouds/azure.rb +1 -1
- data/modules/mu/clouds/azure/container_cluster.rb +1 -1
- data/modules/mu/clouds/azure/server.rb +16 -2
- data/modules/mu/clouds/azure/user.rb +1 -1
- data/modules/mu/clouds/azure/userdata/linux.erb +84 -80
- data/modules/mu/clouds/azure/vpc.rb +32 -13
- data/modules/mu/clouds/cloudformation/server.rb +1 -1
- data/modules/mu/clouds/google.rb +2 -3
- data/modules/mu/clouds/google/container_cluster.rb +9 -1
- data/modules/mu/clouds/google/firewall_rule.rb +6 -0
- data/modules/mu/clouds/google/role.rb +1 -3
- data/modules/mu/clouds/google/server.rb +25 -4
- data/modules/mu/clouds/google/user.rb +1 -1
- data/modules/mu/clouds/google/userdata/linux.erb +9 -5
- data/modules/mu/clouds/google/vpc.rb +102 -21
- data/modules/mu/config.rb +250 -49
- data/modules/mu/config/alarm.rb +1 -0
- data/modules/mu/config/container_cluster.yml +0 -1
- data/modules/mu/config/database.yml +4 -1
- data/modules/mu/config/search_domain.yml +4 -3
- data/modules/mu/config/server.rb +7 -3
- data/modules/mu/config/server.yml +4 -1
- data/modules/mu/config/server_pool.yml +2 -0
- data/modules/mu/config/vpc.rb +42 -29
- data/modules/mu/deploy.rb +12 -5
- data/modules/mu/groomers/ansible.rb +4 -1
- data/modules/mu/groomers/chef.rb +5 -1
- data/modules/mu/kittens.rb +60 -11
- data/modules/mu/logger.rb +6 -4
- data/modules/mu/mommacat.rb +39 -19
- data/modules/mu/mu.yaml.rb +276 -0
- metadata +13 -4
data/modules/mu/config.rb
CHANGED
@@ -212,23 +212,25 @@ module MU
|
|
212
212
|
# layers that don't care about the metadata in Tails.
|
213
213
|
# @param config [Hash]: The configuration tree to convert
|
214
214
|
# @return [Hash]: The modified configuration
|
215
|
-
def self.manxify(config)
|
215
|
+
def self.manxify(config, remove_runtime_keys: false)
|
216
216
|
if config.is_a?(Hash)
|
217
217
|
newhash = {}
|
218
218
|
config.each_pair { |key, val|
|
219
|
-
|
219
|
+
next if remove_runtime_keys and key.match(/^#MU_/)
|
220
|
+
next if val.is_a?(Array) and val.empty?
|
221
|
+
newhash[key] = self.manxify(val, remove_runtime_keys: remove_runtime_keys)
|
220
222
|
}
|
221
223
|
config = newhash
|
222
224
|
elsif config.is_a?(Array)
|
223
225
|
newarray = []
|
224
226
|
config.each { |val|
|
225
|
-
newarray << self.manxify(val)
|
227
|
+
newarray << self.manxify(val, remove_runtime_keys: remove_runtime_keys)
|
226
228
|
}
|
227
229
|
config = newarray
|
228
230
|
elsif config.is_a?(MU::Config::Tail)
|
229
231
|
return config.to_s
|
230
232
|
elsif config.is_a?(MU::Config::Ref)
|
231
|
-
return config.to_h
|
233
|
+
return self.manxify(config.to_h, remove_runtime_keys: remove_runtime_keys)
|
232
234
|
end
|
233
235
|
return config
|
234
236
|
end
|
@@ -238,7 +240,7 @@ module MU
|
|
238
240
|
# @param config [Hash]
|
239
241
|
# @return [Hash]
|
240
242
|
def self.stripConfig(config)
|
241
|
-
MU::Config.manxify(Marshal.load(Marshal.dump(MU.structToHash(config.dup))))
|
243
|
+
MU::Config.manxify(Marshal.load(Marshal.dump(MU.structToHash(config.dup))), remove_runtime_keys: true)
|
242
244
|
end
|
243
245
|
|
244
246
|
# A wrapper class for resources to refer to other resources, whether they
|
@@ -334,7 +336,7 @@ module MU
|
|
334
336
|
end
|
335
337
|
|
336
338
|
if @deploy_id and !@mommacat
|
337
|
-
@mommacat = MU::MommaCat.
|
339
|
+
@mommacat = MU::MommaCat.getLitter(@deploy_id, set_context_to_me: false)
|
338
340
|
elsif @mommacat and !@deploy_id
|
339
341
|
@deploy_id = @mommacat.deploy_id
|
340
342
|
end
|
@@ -569,6 +571,7 @@ return
|
|
569
571
|
|
570
572
|
def initialize(name, value, prettyname = nil, cloudtype = "String", valid_values = [], description = "", is_list_element = false, prefix: "", suffix: "", pseudo: false, runtimecode: nil, index: 0)
|
571
573
|
@name = name
|
574
|
+
@bindings = {}
|
572
575
|
@value = value
|
573
576
|
@valid_values = valid_values
|
574
577
|
@pseudo = pseudo
|
@@ -764,7 +767,7 @@ return
|
|
764
767
|
|
765
768
|
# Make sure our parameter values are all available in the local namespace
|
766
769
|
# that ERB will be using, minus any that conflict with existing variables
|
767
|
-
erb_binding = get_binding
|
770
|
+
erb_binding = get_binding(@@tails.keys.sort)
|
768
771
|
@@tails.each_pair { |key, tail|
|
769
772
|
next if !tail.is_a?(MU::Config::Tail) or tail.is_list_element
|
770
773
|
# XXX figure out what to do with lists
|
@@ -801,7 +804,7 @@ return
|
|
801
804
|
|
802
805
|
begin
|
803
806
|
config = JSON.parse(raw_json)
|
804
|
-
if param_pass
|
807
|
+
if param_pass and config.is_a?(Hash)
|
805
808
|
config.keys.each { |key|
|
806
809
|
if key != "parameters"
|
807
810
|
if key == "appname" and @@parameters["myAppName"].nil?
|
@@ -812,7 +815,7 @@ return
|
|
812
815
|
config.delete(key)
|
813
816
|
end
|
814
817
|
}
|
815
|
-
|
818
|
+
elsif config.is_a?(Hash)
|
816
819
|
config.delete("parameters")
|
817
820
|
end
|
818
821
|
rescue JSON::ParserError => e
|
@@ -833,6 +836,7 @@ return
|
|
833
836
|
|
834
837
|
attr_reader :kittens
|
835
838
|
attr_reader :updating
|
839
|
+
attr_reader :existing_deploy
|
836
840
|
attr_reader :kittencfg_semaphore
|
837
841
|
|
838
842
|
# Load, resolve, and validate a configuration file ("Basket of Kittens").
|
@@ -858,6 +862,9 @@ return
|
|
858
862
|
@admin_firewall_rules = []
|
859
863
|
@skipinitialupdates = skipinitialupdates
|
860
864
|
@updating = updating
|
865
|
+
if @updating
|
866
|
+
@existing_deploy = MU::MommaCat.new(@updating)
|
867
|
+
end
|
861
868
|
@default_credentials = default_credentials
|
862
869
|
|
863
870
|
ok = true
|
@@ -1035,21 +1042,80 @@ return
|
|
1035
1042
|
end
|
1036
1043
|
end
|
1037
1044
|
|
1045
|
+
# Generate a documentation-friendly dummy Ruby class for our mu.yaml main
|
1046
|
+
# config.
|
1047
|
+
def self.emitConfigAsRuby
|
1048
|
+
example = %Q{---
|
1049
|
+
public_address: 1.2.3.4
|
1050
|
+
mu_admin_email: egtlabs@eglobaltech.com
|
1051
|
+
mu_admin_name: Joe Schmoe
|
1052
|
+
mommacat_port: 2260
|
1053
|
+
banner: My Example Mu Master
|
1054
|
+
mu_repository: git://github.com/cloudamatic/mu.git
|
1055
|
+
repos:
|
1056
|
+
- https://github.com/cloudamatic/mu_demo_platform
|
1057
|
+
allow_invade_foreign_vpcs: true
|
1058
|
+
ansible_dir:
|
1059
|
+
aws:
|
1060
|
+
egtdev:
|
1061
|
+
region: us-east-1
|
1062
|
+
log_bucket_name: egt-mu-log-bucket
|
1063
|
+
default: true
|
1064
|
+
name: egtdev
|
1065
|
+
personal:
|
1066
|
+
region: us-east-2
|
1067
|
+
log_bucket_name: my-mu-log-bucket
|
1068
|
+
name: personal
|
1069
|
+
google:
|
1070
|
+
egtlabs:
|
1071
|
+
project: egt-labs-admin
|
1072
|
+
credentials_file: /opt/mu/etc/google.json
|
1073
|
+
region: us-east4
|
1074
|
+
log_bucket_name: hexabucket-761234
|
1075
|
+
default: true
|
1076
|
+
}
|
1077
|
+
mu_yaml_schema = eval(%Q{
|
1078
|
+
$NOOP = true
|
1079
|
+
load "#{MU.myRoot}/bin/mu-configure"
|
1080
|
+
$CONFIGURABLES
|
1081
|
+
})
|
1082
|
+
return if mu_yaml_schema.nil? or !mu_yaml_schema.is_a?(Hash)
|
1083
|
+
muyamlpath = "#{MU.myRoot}/modules/mu/mu.yaml.rb"
|
1084
|
+
MU.log "Converting mu.yaml schema to Ruby objects in #{muyamlpath}"
|
1085
|
+
muyaml_rb = File.new(muyamlpath, File::CREAT|File::TRUNC|File::RDWR, 0644)
|
1086
|
+
muyaml_rb.puts "# Configuration schema for mu.yaml. See also {https://github.com/cloudamatic/mu/wiki/Configuration the Mu wiki}."
|
1087
|
+
muyaml_rb.puts "#"
|
1088
|
+
muyaml_rb.puts "# Example:"
|
1089
|
+
muyaml_rb.puts "#"
|
1090
|
+
muyaml_rb.puts "# <pre>"
|
1091
|
+
example.split(/\n/).each { |line|
|
1092
|
+
muyaml_rb.puts "# "+line+" " # markdooooown
|
1093
|
+
}
|
1094
|
+
muyaml_rb.puts "# </pre>"
|
1095
|
+
muyaml_rb.puts "module MuYAML"
|
1096
|
+
muyaml_rb.puts "\t# The configuration file format for Mu's main config file."
|
1097
|
+
self.printMuYamlSchema(muyaml_rb, [], { "subtree" => mu_yaml_schema })
|
1098
|
+
muyaml_rb.puts "end"
|
1099
|
+
muyaml_rb.close
|
1100
|
+
end
|
1101
|
+
|
1038
1102
|
# Take the schema we've defined and create a dummy Ruby class tree out of
|
1039
1103
|
# it, basically so we can leverage Yard to document it.
|
1040
1104
|
def self.emitSchemaAsRuby
|
1041
1105
|
kittenpath = "#{MU.myRoot}/modules/mu/kittens.rb"
|
1042
1106
|
MU.log "Converting Basket of Kittens schema to Ruby objects in #{kittenpath}"
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1107
|
+
kitten_rb = File.new(kittenpath, File::CREAT|File::TRUNC|File::RDWR, 0644)
|
1108
|
+
kitten_rb.puts "### THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT ###"
|
1109
|
+
kitten_rb.puts "#"
|
1110
|
+
kitten_rb.puts "#"
|
1111
|
+
kitten_rb.puts "#"
|
1112
|
+
kitten_rb.puts "module MU"
|
1113
|
+
kitten_rb.puts "class Config"
|
1114
|
+
kitten_rb.puts "\t# The configuration file format for Mu application stacks."
|
1115
|
+
self.printSchema(kitten_rb, ["BasketofKittens"], MU::Config.docSchema)
|
1116
|
+
kitten_rb.puts "end"
|
1117
|
+
kitten_rb.puts "end"
|
1118
|
+
kitten_rb.close
|
1053
1119
|
|
1054
1120
|
end
|
1055
1121
|
|
@@ -1066,7 +1132,6 @@ return
|
|
1066
1132
|
subnet_bits = cidr.netmask.prefix_len
|
1067
1133
|
begin
|
1068
1134
|
subnet_bits += 1
|
1069
|
-
|
1070
1135
|
if subnet_bits > max_mask
|
1071
1136
|
MU.log "Can't subdivide #{cidr.to_s} into #{subnets_desired.to_s}", MU::ERR
|
1072
1137
|
raise MuError, "Subnets smaller than /#{max_mask} not permitted"
|
@@ -1148,7 +1213,7 @@ return
|
|
1148
1213
|
# an extra pass to make sure we get all intra-stack dependencies correct.
|
1149
1214
|
# @param acl [Hash]: The configuration hash for the FirewallRule to check
|
1150
1215
|
# @return [Hash]
|
1151
|
-
def resolveIntraStackFirewallRefs(acl)
|
1216
|
+
def resolveIntraStackFirewallRefs(acl, delay_validation = false)
|
1152
1217
|
acl["rules"].each { |acl_include|
|
1153
1218
|
if acl_include['sgs']
|
1154
1219
|
acl_include['sgs'].each { |sg_ref|
|
@@ -1171,7 +1236,7 @@ return
|
|
1171
1236
|
siblingfw = haveLitterMate?(sg_ref, "firewall_rules")
|
1172
1237
|
if !siblingfw["#MU_VALIDATED"]
|
1173
1238
|
# XXX raise failure somehow
|
1174
|
-
insertKitten(siblingfw, "firewall_rules")
|
1239
|
+
insertKitten(siblingfw, "firewall_rules", delay_validation: delay_validation)
|
1175
1240
|
end
|
1176
1241
|
end
|
1177
1242
|
}
|
@@ -1185,10 +1250,15 @@ return
|
|
1185
1250
|
# @param type [String]: The type of resource being added
|
1186
1251
|
# @param delay_validation [Boolean]: Whether to hold off on calling the resource's validateConfig method
|
1187
1252
|
# @param ignore_duplicates [Boolean]: Do not raise an exception if we attempt to insert a resource with a +name+ field that's already in use
|
1188
|
-
def insertKitten(descriptor, type, delay_validation = false, ignore_duplicates: false)
|
1253
|
+
def insertKitten(descriptor, type, delay_validation = false, ignore_duplicates: false, overwrite: false)
|
1189
1254
|
append = false
|
1190
1255
|
start = Time.now
|
1191
1256
|
shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
|
1257
|
+
MU.log "insertKitten on #{cfg_name} #{descriptor['name']} (delay_validation: #{delay_validation.to_s})", MU::DEBUG, details: caller[0]
|
1258
|
+
|
1259
|
+
if overwrite
|
1260
|
+
removeKitten(descriptor['name'], type)
|
1261
|
+
end
|
1192
1262
|
|
1193
1263
|
if !ignore_duplicates and haveLitterMate?(descriptor['name'], cfg_name)
|
1194
1264
|
# raise DuplicateNameError, "A #{shortclass} named #{descriptor['name']} has already been inserted into this configuration"
|
@@ -1284,7 +1354,7 @@ return
|
|
1284
1354
|
siblingvpc = haveLitterMate?(descriptor["vpc"]["name"], "vpcs")
|
1285
1355
|
|
1286
1356
|
if siblingvpc and siblingvpc['bastion'] and
|
1287
|
-
["server", "server_pool"].include?(cfg_name) and
|
1357
|
+
["server", "server_pool", "container_cluster"].include?(cfg_name) and
|
1288
1358
|
!descriptor['bastion']
|
1289
1359
|
if descriptor['name'] != siblingvpc['bastion'].to_h['name']
|
1290
1360
|
descriptor["dependencies"] << {
|
@@ -1298,7 +1368,7 @@ return
|
|
1298
1368
|
# resolved before we can proceed
|
1299
1369
|
if ["server", "server_pool", "loadbalancer", "database", "cache_cluster", "container_cluster", "storage_pool"].include?(cfg_name)
|
1300
1370
|
if !siblingvpc["#MU_VALIDATED"]
|
1301
|
-
ok = false if !insertKitten(siblingvpc, "vpcs")
|
1371
|
+
ok = false if !insertKitten(siblingvpc, "vpcs", overwrite: overwrite)
|
1302
1372
|
end
|
1303
1373
|
end
|
1304
1374
|
if !MU::Config::VPC.processReference(descriptor['vpc'],
|
@@ -1349,13 +1419,15 @@ return
|
|
1349
1419
|
# Does it have generic ingress rules?
|
1350
1420
|
fwname = cfg_name+descriptor['name']
|
1351
1421
|
|
1352
|
-
if
|
1353
|
-
|
1354
|
-
["server", "server_pool", "database"].include?(cfg_name))
|
1422
|
+
if (descriptor['ingress_rules'] or
|
1423
|
+
["server", "server_pool", "database", "cache_cluster"].include?(cfg_name))
|
1355
1424
|
descriptor['ingress_rules'] ||= []
|
1356
1425
|
fw_classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get("FirewallRule")
|
1357
1426
|
|
1358
|
-
acl =
|
1427
|
+
acl = haveLitterMate?(fwname, "firewall_rules")
|
1428
|
+
already_exists = !acl.nil?
|
1429
|
+
|
1430
|
+
acl ||= {
|
1359
1431
|
"name" => fwname,
|
1360
1432
|
"rules" => descriptor['ingress_rules'],
|
1361
1433
|
"region" => descriptor['region'],
|
@@ -1375,10 +1447,11 @@ return
|
|
1375
1447
|
["optional_tags", "tags", "cloud", "project"].each { |param|
|
1376
1448
|
acl[param] = descriptor[param] if descriptor[param]
|
1377
1449
|
}
|
1378
|
-
descriptor["add_firewall_rules"]
|
1450
|
+
descriptor["add_firewall_rules"] ||= []
|
1379
1451
|
descriptor["add_firewall_rules"] << {"rule_name" => fwname, "type" => "firewall_rules" } # XXX why the duck is there a type argument required here?
|
1380
|
-
|
1381
|
-
|
1452
|
+
|
1453
|
+
acl = resolveIntraStackFirewallRefs(acl, delay_validation)
|
1454
|
+
ok = false if !insertKitten(acl, "firewall_rules", delay_validation, overwrite: already_exists)
|
1382
1455
|
end
|
1383
1456
|
|
1384
1457
|
# Does it declare association with any sibling LoadBalancers?
|
@@ -1413,10 +1486,6 @@ return
|
|
1413
1486
|
"type" => "firewall_rule",
|
1414
1487
|
"name" => acl_include["rule_name"]
|
1415
1488
|
}
|
1416
|
-
siblingfw = haveLitterMate?(acl_include["rule_name"], "firewall_rules")
|
1417
|
-
if !siblingfw["#MU_VALIDATED"]
|
1418
|
-
ok = false if !insertKitten(siblingfw, "firewall_rules", delay_validation)
|
1419
|
-
end
|
1420
1489
|
elsif acl_include["rule_name"]
|
1421
1490
|
MU.log shortclass.to_s+" #{descriptor['name']} depends on FirewallRule #{acl_include["rule_name"]}, but no such rule declared.", MU::ERR
|
1422
1491
|
ok = false
|
@@ -1428,13 +1497,14 @@ return
|
|
1428
1497
|
if descriptor["alarms"] && !descriptor["alarms"].empty?
|
1429
1498
|
descriptor["alarms"].each { |alarm|
|
1430
1499
|
alarm["name"] = "#{cfg_name}-#{descriptor["name"]}-#{alarm["name"]}"
|
1431
|
-
alarm['dimensions']
|
1500
|
+
alarm['dimensions'] ||= []
|
1501
|
+
alarm["namespace"] ||= descriptor['name']
|
1432
1502
|
alarm["credentials"] = descriptor["credentials"]
|
1433
1503
|
alarm["#TARGETCLASS"] = cfg_name
|
1434
1504
|
alarm["#TARGETNAME"] = descriptor['name']
|
1435
1505
|
alarm['cloud'] = descriptor['cloud']
|
1436
1506
|
|
1437
|
-
ok = false if !insertKitten(alarm, "alarms", true)
|
1507
|
+
ok = false if !insertKitten(alarm, "alarms", true, overwrite: overwrite)
|
1438
1508
|
}
|
1439
1509
|
descriptor.delete("alarms")
|
1440
1510
|
end
|
@@ -1863,12 +1933,22 @@ return
|
|
1863
1933
|
|
1864
1934
|
# (see #include)
|
1865
1935
|
def include(file)
|
1866
|
-
MU::Config.include(file, get_binding, param_pass = @param_pass)
|
1936
|
+
MU::Config.include(file, get_binding(@@tails.keys.sort), param_pass = @param_pass)
|
1937
|
+
end
|
1938
|
+
|
1939
|
+
@@bindings = {}
|
1940
|
+
# Keep a cache of bindings we've created as sandbox contexts for ERB
|
1941
|
+
# processing, so we don't keep reloading the entire Mu library inside new
|
1942
|
+
# ones.
|
1943
|
+
def self.global_bindings
|
1944
|
+
@@bindings
|
1867
1945
|
end
|
1868
1946
|
|
1869
1947
|
# Namespace magic to pass to ERB's result method.
|
1870
|
-
def get_binding
|
1871
|
-
|
1948
|
+
def get_binding(keyset)
|
1949
|
+
# return MU::Config.global_bindings[keyset] if MU::Config.global_bindings[keyset]
|
1950
|
+
MU::Config.global_bindings[keyset] = binding
|
1951
|
+
MU::Config.global_bindings[keyset]
|
1872
1952
|
end
|
1873
1953
|
|
1874
1954
|
def applySchemaDefaults(conf_chunk = config, schema_chunk = schema, depth = 0, siblings = nil, type: nil)
|
@@ -2231,10 +2311,131 @@ return
|
|
2231
2311
|
# end
|
2232
2312
|
end
|
2233
2313
|
|
2314
|
+
# Emit our mu.yaml schema in a format that YARD can comprehend and turn into
|
2315
|
+
# documentation.
|
2316
|
+
def self.printMuYamlSchema(muyaml_rb, class_hierarchy, schema, in_array = false, required = false, prefix: nil)
|
2317
|
+
return if schema.nil?
|
2318
|
+
if schema["subtree"]
|
2319
|
+
printme = Array.new
|
2320
|
+
# order sub-elements by whether they're required, so we can use YARD's
|
2321
|
+
# grouping tags on them
|
2322
|
+
have_required = schema["subtree"].keys.any? { |k| schema["subtree"][k]["required"] }
|
2323
|
+
prop_list = schema["subtree"].keys.sort { |a, b|
|
2324
|
+
if schema["subtree"][a]["required"] and !schema["subtree"][b]["required"]
|
2325
|
+
-1
|
2326
|
+
elsif !schema["subtree"][a]["required"] and schema["subtree"][b]["required"]
|
2327
|
+
1
|
2328
|
+
else
|
2329
|
+
a <=> b
|
2330
|
+
end
|
2331
|
+
}
|
2332
|
+
|
2333
|
+
req = false
|
2334
|
+
printme << "# @!group Optional parameters" if !have_required
|
2335
|
+
prop_list.each { |name|
|
2336
|
+
prop = schema["subtree"][name]
|
2337
|
+
if prop["required"]
|
2338
|
+
printme << "# @!group Required parameters" if !req
|
2339
|
+
req = true
|
2340
|
+
else
|
2341
|
+
if req
|
2342
|
+
printme << "# @!endgroup"
|
2343
|
+
printme << "# @!group Optional parameters"
|
2344
|
+
end
|
2345
|
+
req = false
|
2346
|
+
end
|
2347
|
+
|
2348
|
+
printme << self.printMuYamlSchema(muyaml_rb, class_hierarchy+ [name], prop, false, req)
|
2349
|
+
}
|
2350
|
+
printme << "# @!endgroup"
|
2351
|
+
|
2352
|
+
desc = (schema['desc'] || schema['title'])
|
2353
|
+
|
2354
|
+
tabs = 1
|
2355
|
+
class_hierarchy.each { |classname|
|
2356
|
+
if classname == class_hierarchy.last and desc
|
2357
|
+
muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "# #{desc}\n"
|
2358
|
+
end
|
2359
|
+
muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "class #{classname}"
|
2360
|
+
tabs = tabs + 1
|
2361
|
+
}
|
2362
|
+
printme.each { |lines|
|
2363
|
+
if !lines.nil? and lines.is_a?(String)
|
2364
|
+
lines.lines.each { |line|
|
2365
|
+
muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + line
|
2366
|
+
}
|
2367
|
+
end
|
2368
|
+
}
|
2369
|
+
|
2370
|
+
class_hierarchy.each { |classname|
|
2371
|
+
tabs = tabs - 1
|
2372
|
+
muyaml_rb.puts ["\t"].cycle(tabs).to_a.join('') + "end"
|
2373
|
+
}
|
2374
|
+
|
2375
|
+
# And now that we've dealt with our children, pass our own rendered
|
2376
|
+
# commentary back up to our caller.
|
2377
|
+
name = class_hierarchy.last
|
2378
|
+
if in_array
|
2379
|
+
type = "Array<#{class_hierarchy.join("::")}>"
|
2380
|
+
else
|
2381
|
+
type = class_hierarchy.join("::")
|
2382
|
+
end
|
2383
|
+
|
2384
|
+
docstring = "\n"
|
2385
|
+
docstring = docstring + "# **REQUIRED**\n" if required
|
2386
|
+
# docstring = docstring + "# **"+schema["prefix"]+"**\n" if schema["prefix"]
|
2387
|
+
docstring = docstring + "# #{desc.gsub(/\n/, "\n#")}\n" if desc
|
2388
|
+
docstring = docstring + "#\n"
|
2389
|
+
docstring = docstring + "# @return [#{type}]\n"
|
2390
|
+
docstring = docstring + "# @see #{class_hierarchy.join("::")}\n"
|
2391
|
+
docstring = docstring + "attr_accessor :#{name}"
|
2392
|
+
return docstring
|
2393
|
+
|
2394
|
+
else
|
2395
|
+
in_array = schema["array"]
|
2396
|
+
name = class_hierarchy.last
|
2397
|
+
type = if schema['boolean']
|
2398
|
+
"Boolean"
|
2399
|
+
else
|
2400
|
+
"String"
|
2401
|
+
end
|
2402
|
+
if in_array
|
2403
|
+
type = "Array<#{type}>"
|
2404
|
+
end
|
2405
|
+
docstring = "\n"
|
2406
|
+
|
2407
|
+
prefixes = []
|
2408
|
+
prefixes << "# **REQUIRED**" if schema["required"] and schema['default'].nil?
|
2409
|
+
# prefixes << "# **"+schema["prefix"]+"**" if schema["prefix"]
|
2410
|
+
prefixes << "# **Default: `#{schema['default']}`**" if !schema['default'].nil?
|
2411
|
+
if !schema['pattern'].nil?
|
2412
|
+
# XXX unquoted regex chars confuse the hell out of YARD. How do we
|
2413
|
+
# quote {}[] etc in YARD-speak?
|
2414
|
+
prefixes << "# **Must match pattern `#{schema['pattern'].to_s.gsub(/\n/, "\n#")}`**"
|
2415
|
+
end
|
2416
|
+
|
2417
|
+
desc = (schema['desc'] || schema['title'])
|
2418
|
+
if prefixes.size > 0
|
2419
|
+
docstring += prefixes.join(",\n")
|
2420
|
+
if desc and desc.size > 1
|
2421
|
+
docstring += " - "
|
2422
|
+
end
|
2423
|
+
docstring += "\n"
|
2424
|
+
end
|
2425
|
+
|
2426
|
+
docstring = docstring + "# #{desc.gsub(/\n/, "\n#")}\n" if !desc.nil?
|
2427
|
+
docstring = docstring + "#\n"
|
2428
|
+
docstring = docstring + "# @return [#{type}]\n"
|
2429
|
+
docstring = docstring + "attr_accessor :#{name}"
|
2430
|
+
|
2431
|
+
return docstring
|
2432
|
+
end
|
2433
|
+
|
2434
|
+
end
|
2234
2435
|
|
2235
|
-
# Emit our Basket of
|
2436
|
+
# Emit our Basket of Kittens schema in a format that YARD can comprehend
|
2236
2437
|
# and turn into documentation.
|
2237
|
-
def self.printSchema(
|
2438
|
+
def self.printSchema(kitten_rb, class_hierarchy, schema, in_array = false, required = false, prefix: nil)
|
2238
2439
|
return if schema.nil?
|
2239
2440
|
if schema["type"] == "object"
|
2240
2441
|
printme = Array.new
|
@@ -2263,7 +2464,7 @@ return
|
|
2263
2464
|
req = false
|
2264
2465
|
end
|
2265
2466
|
|
2266
|
-
printme << self.printSchema(
|
2467
|
+
printme << self.printSchema(kitten_rb, class_hierarchy+ [name], prop, false, req, prefix: schema["prefix"])
|
2267
2468
|
}
|
2268
2469
|
printme << "# @!endgroup"
|
2269
2470
|
end
|
@@ -2271,22 +2472,22 @@ return
|
|
2271
2472
|
tabs = 1
|
2272
2473
|
class_hierarchy.each { |classname|
|
2273
2474
|
if classname == class_hierarchy.last and !schema['description'].nil?
|
2274
|
-
|
2475
|
+
kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "# #{schema['description']}\n"
|
2275
2476
|
end
|
2276
|
-
|
2477
|
+
kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "class #{classname}"
|
2277
2478
|
tabs = tabs + 1
|
2278
2479
|
}
|
2279
2480
|
printme.each { |lines|
|
2280
2481
|
if !lines.nil? and lines.is_a?(String)
|
2281
2482
|
lines.lines.each { |line|
|
2282
|
-
|
2483
|
+
kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + line
|
2283
2484
|
}
|
2284
2485
|
end
|
2285
2486
|
}
|
2286
2487
|
|
2287
2488
|
class_hierarchy.each { |classname|
|
2288
2489
|
tabs = tabs - 1
|
2289
|
-
|
2490
|
+
kitten_rb.puts ["\t"].cycle(tabs).to_a.join('') + "end"
|
2290
2491
|
}
|
2291
2492
|
|
2292
2493
|
# And now that we've dealt with our children, pass our own rendered
|
@@ -2309,7 +2510,7 @@ return
|
|
2309
2510
|
return docstring
|
2310
2511
|
|
2311
2512
|
elsif schema["type"] == "array"
|
2312
|
-
return self.printSchema(
|
2513
|
+
return self.printSchema(kitten_rb, class_hierarchy, schema['items'], true, required, prefix: prefix)
|
2313
2514
|
else
|
2314
2515
|
name = class_hierarchy.last
|
2315
2516
|
if schema['type'].nil?
|