cloud-mu 3.1.2 → 3.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Dockerfile +15 -3
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +10 -13
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +2 -3
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +135 -37
- data/cloud-mu.gemspec +22 -20
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +3 -2
- data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/disk.rb +1 -1
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/extras/image-generators/Google/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +1 -1
- data/modules/mommacat.ru +6 -16
- data/modules/mu.rb +165 -111
- data/modules/mu/adoption.rb +401 -68
- data/modules/mu/cleanup.rb +199 -306
- data/modules/mu/cloud.rb +100 -1632
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +46 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +920 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +165 -0
- data/modules/mu/config.rb +171 -1767
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +4 -4
- data/modules/mu/config/cache_cluster.rb +1 -1
- data/modules/mu/config/collection.rb +4 -4
- data/modules/mu/config/container_cluster.rb +9 -4
- data/modules/mu/config/database.rb +83 -104
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +6 -6
- data/modules/mu/config/doc_helpers.rb +516 -0
- data/modules/mu/config/endpoint.rb +4 -4
- data/modules/mu/config/firewall_rule.rb +103 -4
- data/modules/mu/config/folder.rb +4 -4
- data/modules/mu/config/function.rb +3 -3
- data/modules/mu/config/group.rb +4 -4
- data/modules/mu/config/habitat.rb +4 -4
- data/modules/mu/config/loadbalancer.rb +60 -14
- data/modules/mu/config/log.rb +4 -4
- data/modules/mu/config/msg_queue.rb +4 -4
- data/modules/mu/config/nosqldb.rb +4 -4
- data/modules/mu/config/notifier.rb +3 -3
- data/modules/mu/config/ref.rb +365 -0
- data/modules/mu/config/role.rb +4 -4
- data/modules/mu/config/schema_helpers.rb +509 -0
- data/modules/mu/config/search_domain.rb +4 -4
- data/modules/mu/config/server.rb +97 -70
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +5 -9
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +200 -0
- data/modules/mu/config/user.rb +4 -4
- data/modules/mu/config/vpc.rb +70 -27
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +83 -60
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +3 -2
- data/modules/mu/deploy.rb +30 -26
- data/modules/mu/groomer.rb +17 -2
- data/modules/mu/groomers/ansible.rb +188 -41
- data/modules/mu/groomers/chef.rb +116 -55
- data/modules/mu/logger.rb +127 -148
- data/modules/mu/master.rb +389 -2
- data/modules/mu/master/chef.rb +3 -4
- data/modules/mu/master/ldap.rb +3 -3
- data/modules/mu/master/ssl.rb +12 -3
- data/modules/mu/mommacat.rb +217 -2612
- data/modules/mu/mommacat/daemon.rb +397 -0
- data/modules/mu/mommacat/naming.rb +473 -0
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +722 -0
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +271 -112
- data/modules/mu/{clouds → providers}/aws/alarm.rb +5 -3
- data/modules/mu/{clouds → providers}/aws/bucket.rb +26 -22
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +33 -67
- data/modules/mu/{clouds → providers}/aws/collection.rb +24 -23
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +681 -721
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +64 -63
- data/modules/mu/{clouds → providers}/aws/endpoint.rb +22 -27
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +214 -244
- data/modules/mu/{clouds → providers}/aws/folder.rb +7 -7
- data/modules/mu/{clouds → providers}/aws/function.rb +17 -22
- data/modules/mu/{clouds → providers}/aws/group.rb +23 -23
- data/modules/mu/{clouds → providers}/aws/habitat.rb +17 -14
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +57 -48
- data/modules/mu/{clouds → providers}/aws/log.rb +15 -12
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +17 -16
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +18 -11
- data/modules/mu/{clouds → providers}/aws/notifier.rb +11 -6
- data/modules/mu/{clouds → providers}/aws/role.rb +112 -86
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +39 -33
- data/modules/mu/{clouds → providers}/aws/server.rb +835 -1133
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +56 -60
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +24 -42
- data/modules/mu/{clouds → providers}/aws/user.rb +21 -22
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +523 -929
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +29 -9
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
- data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
- data/modules/mu/{clouds → providers}/azure/server.rb +95 -48
- data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
- data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +67 -30
- data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +84 -77
- data/modules/mu/{clouds → providers}/google/database.rb +10 -20
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
- data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
- data/modules/mu/{clouds → providers}/google/function.rb +139 -167
- data/modules/mu/{clouds → providers}/google/group.rb +29 -34
- data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +18 -20
- data/modules/mu/{clouds → providers}/google/role.rb +92 -58
- data/modules/mu/{clouds → providers}/google/server.rb +242 -155
- data/modules/mu/{clouds → providers}/google/server_pool.rb +25 -44
- data/modules/mu/{clouds → providers}/google/user.rb +95 -31
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
- data/modules/tests/bucket.yml +4 -0
- data/modules/tests/centos6.yaml +11 -0
- data/modules/tests/centos7.yaml +11 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- data/modules/tests/includes-and-params.yaml +2 -1
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/aws-iam.yaml +201 -0
- data/modules/tests/regrooms/bucket.yml +19 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +1 -0
- data/modules/tests/super_simple_bok.yml +1 -3
- data/modules/tests/win2k12.yaml +17 -5
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +232 -154
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1985
@@ -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/
|
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
|
@@ -51,9 +51,9 @@ 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
|
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,
|
56
|
+
def self.validate(notifier, _configurator)
|
57
57
|
ok = true
|
58
58
|
|
59
59
|
if notifier['subscriptions']
|
@@ -0,0 +1,365 @@
|
|
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
|
+
kitten(shallow: true) if @mommacat # try to populate the actual cloud object for this
|
125
|
+
end
|
126
|
+
|
127
|
+
# Comparison operator
|
128
|
+
def <=>(other)
|
129
|
+
return 1 if other.nil?
|
130
|
+
self.to_s <=> other.to_s
|
131
|
+
end
|
132
|
+
|
133
|
+
# Lets callers access us like a {Hash}
|
134
|
+
# @param attribute [String,Symbol]
|
135
|
+
def [](attribute)
|
136
|
+
if respond_to?(attribute.to_sym)
|
137
|
+
send(attribute.to_sym)
|
138
|
+
else
|
139
|
+
nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Unset an attribute. Sort of. We can't actually do that, so nil it out
|
144
|
+
# and we get the behavior we want.
|
145
|
+
def delete(attribute)
|
146
|
+
attribute = ("@"+attribute).to_sym if attribute.to_s !~ /^@/
|
147
|
+
instance_variable_set(attribute.to_sym, nil)
|
148
|
+
end
|
149
|
+
|
150
|
+
# 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}.
|
151
|
+
# @param aliases [Array<Hash>]: Key => value mappings to set backwards-compatibility aliases for attributes, such as the ubiquitous +vpc_id+ (+vpc_id+ => +id+).
|
152
|
+
# @return [Hash]
|
153
|
+
def self.schema(aliases = [], type: nil, parent_obj: nil, desc: nil, omit_fields: [])
|
154
|
+
parent_obj ||= caller[1].gsub(/.*?\/([^\.\/]+)\.rb:.*/, '\1')
|
155
|
+
desc ||= "Reference a #{type ? "'#{type}' resource" : "resource" } from this #{parent_obj ? "'#{parent_obj}'" : "" } resource"
|
156
|
+
schema = {
|
157
|
+
"type" => "object",
|
158
|
+
"#MU_REFERENCE" => true,
|
159
|
+
"minProperties" => 1,
|
160
|
+
"description" => desc,
|
161
|
+
"properties" => {
|
162
|
+
"id" => {
|
163
|
+
"type" => "string",
|
164
|
+
"description" => "Cloud identifier of a resource we want to reference, typically used when leveraging resources not managed by MU"
|
165
|
+
},
|
166
|
+
"name" => {
|
167
|
+
"type" => "string",
|
168
|
+
"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+."
|
169
|
+
},
|
170
|
+
"type" => {
|
171
|
+
"type" => "string",
|
172
|
+
"description" => "The resource type we're attempting to reference.",
|
173
|
+
"enum" => MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }
|
174
|
+
},
|
175
|
+
"deploy_id" => {
|
176
|
+
"type" => "string",
|
177
|
+
"description" => "Our target resource should be found in this Mu deploy."
|
178
|
+
},
|
179
|
+
"credentials" => MU::Config.credentials_primitive,
|
180
|
+
"region" => MU::Config.region_primitive,
|
181
|
+
"cloud" => MU::Config.cloud_primitive,
|
182
|
+
"tag" => {
|
183
|
+
"type" => "object",
|
184
|
+
"description" => "If the target resource supports tagging and our resource implementations +find+ method supports it, we can attempt to locate it by tag.",
|
185
|
+
"properties" => {
|
186
|
+
"key" => {
|
187
|
+
"type" => "string",
|
188
|
+
"description" => "The tag or label key to search against"
|
189
|
+
},
|
190
|
+
"value" => {
|
191
|
+
"type" => "string",
|
192
|
+
"description" => "The tag or label value to match"
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
}
|
197
|
+
}
|
198
|
+
if !["folders", "habitats"].include?(type)
|
199
|
+
schema["properties"]["habitat"] = MU::Config::Habitat.reference
|
200
|
+
end
|
201
|
+
|
202
|
+
if omit_fields
|
203
|
+
omit_fields.each { |f|
|
204
|
+
schema["properties"].delete(f)
|
205
|
+
}
|
206
|
+
end
|
207
|
+
|
208
|
+
if !type.nil?
|
209
|
+
schema["required"] = ["type"]
|
210
|
+
schema["properties"]["type"]["default"] = type
|
211
|
+
schema["properties"]["type"]["enum"] = [type]
|
212
|
+
end
|
213
|
+
|
214
|
+
aliases.each { |a|
|
215
|
+
a.each_pair { |k, v|
|
216
|
+
if schema["properties"][v]
|
217
|
+
schema["properties"][k] = schema["properties"][v].dup
|
218
|
+
schema["properties"][k]["description"] = "Alias for <tt>#{v}</tt>"
|
219
|
+
else
|
220
|
+
MU.log "Reference schema alias #{k} wants to alias #{v}, but no such attribute exists", MU::WARN, details: caller[4]
|
221
|
+
end
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
schema
|
226
|
+
end
|
227
|
+
|
228
|
+
# Decompose into a plain-jane {MU::Config::BasketOfKittens} hash fragment,
|
229
|
+
# of the sort that would have been used to declare this reference in the
|
230
|
+
# first place.
|
231
|
+
def to_h
|
232
|
+
me = { }
|
233
|
+
|
234
|
+
self.instance_variables.each { |var|
|
235
|
+
next if [:@obj, :@mommacat, :@tag_key, :@tag_value].include?(var)
|
236
|
+
val = self.instance_variable_get(var)
|
237
|
+
next if val.nil?
|
238
|
+
val = val.to_h if val.is_a?(MU::Config::Ref)
|
239
|
+
me[var.to_s.sub(/^@/, '')] = val
|
240
|
+
}
|
241
|
+
if @tag_key and !@tag_key.empty?
|
242
|
+
me['tag'] = {
|
243
|
+
'key' => @tag_key,
|
244
|
+
'value' => @tag_value
|
245
|
+
}
|
246
|
+
end
|
247
|
+
me
|
248
|
+
end
|
249
|
+
|
250
|
+
# Getter for the #{id} instance variable that attempts to populate it if
|
251
|
+
# it's not set.
|
252
|
+
# @return [String,nil]
|
253
|
+
def id
|
254
|
+
return @id if @id
|
255
|
+
kitten # if it's not defined, attempt to define it
|
256
|
+
@id
|
257
|
+
end
|
258
|
+
|
259
|
+
# Alias for {id}
|
260
|
+
# @return [String,nil]
|
261
|
+
def cloud_id
|
262
|
+
id
|
263
|
+
end
|
264
|
+
|
265
|
+
# Return a {MU::Cloud} object for this reference. This is only meant to be
|
266
|
+
# called in a live deploy, which is to say that if called during initial
|
267
|
+
# configuration parsing, results may be incorrect.
|
268
|
+
# @param mommacat [MU::MommaCat]: A deploy object which will be searched for the referenced resource if provided, before restoring to broader, less efficient searches.
|
269
|
+
def kitten(mommacat = @mommacat, shallow: false, debug: false)
|
270
|
+
return nil if !@cloud or !@type
|
271
|
+
loglevel = debug ? MU::NOTICE : MU::DEBUG
|
272
|
+
|
273
|
+
if @obj
|
274
|
+
@deploy_id ||= @obj.deploy_id
|
275
|
+
@id ||= @obj.cloud_id
|
276
|
+
@name ||= @obj.config['name'] if @obj.config
|
277
|
+
return @obj
|
278
|
+
end
|
279
|
+
|
280
|
+
if mommacat and !caller.grep(/`findLitterMate'/) # XXX the dumbest
|
281
|
+
MU.log "Looking for #{@type} #{@name} #{@id} in deploy #{mommacat.deploy_id}", loglevel
|
282
|
+
begin
|
283
|
+
@obj = mommacat.findLitterMate(type: @type, name: @name, cloud_id: @id, credentials: @credentials, debug: debug)
|
284
|
+
rescue StandardError => e
|
285
|
+
if e.message =~ /deadlock/
|
286
|
+
MU.log "Saw a recursive deadlock trying to fetch kitten for Ref object in deploy #{mmommacat.deploy_id}", MU::ERR, details: to_h
|
287
|
+
end
|
288
|
+
raise e
|
289
|
+
end
|
290
|
+
if @obj # initialize missing attributes, if we can
|
291
|
+
@id ||= @obj.cloud_id
|
292
|
+
@mommacat ||= mommacat
|
293
|
+
@obj.intoDeploy(@mommacat) # make real sure these are set
|
294
|
+
@deploy_id ||= mommacat.deploy_id
|
295
|
+
|
296
|
+
if !@name
|
297
|
+
if @obj.config and @obj.config['name']
|
298
|
+
@name = @obj.config['name']
|
299
|
+
elsif @obj.mu_name
|
300
|
+
if @type == "folders"
|
301
|
+
MU.log "would assign name '#{@obj.mu_name}' in ref to this folder if I were feeling aggressive", MU::WARN, details: self.to_h
|
302
|
+
end
|
303
|
+
# @name = @obj.mu_name
|
304
|
+
end
|
305
|
+
end
|
306
|
+
return @obj
|
307
|
+
else
|
308
|
+
# MU.log "Failed to find a live '#{@type.to_s}' object named #{@name}#{@id ? " (#{@id})" : "" }#{ @habitat ? " in habitat #{@habitat}" : "" }", MU::WARN, details: self
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
if !@obj and !(@cloud == "Google" and @id and @type == "users" and MU::Cloud.resourceClass("Google", "User").cannedServiceAcctName?(@id)) and !shallow
|
313
|
+
try_deploy_id = @deploy_id
|
314
|
+
|
315
|
+
begin
|
316
|
+
hab_arg = if @habitat.nil?
|
317
|
+
[nil]
|
318
|
+
elsif @habitat.is_a?(MU::Config::Ref)
|
319
|
+
[@habitat.id]
|
320
|
+
elsif @habitat.is_a?(Hash)
|
321
|
+
[@habitat["id"]]
|
322
|
+
else
|
323
|
+
[@habitat.to_s]
|
324
|
+
end
|
325
|
+
|
326
|
+
found = MU::MommaCat.findStray(
|
327
|
+
@cloud,
|
328
|
+
@type,
|
329
|
+
name: @name,
|
330
|
+
cloud_id: @id,
|
331
|
+
deploy_id: try_deploy_id,
|
332
|
+
region: @region,
|
333
|
+
habitats: hab_arg,
|
334
|
+
credentials: @credentials,
|
335
|
+
dummy_ok: (["habitats", "folders", "users", "groups", "vpcs"].include?(@type))
|
336
|
+
)
|
337
|
+
@obj ||= found.first if found
|
338
|
+
rescue MU::MommaCat::MultipleMatches => e
|
339
|
+
if try_deploy_id.nil? and MU.deploy_id
|
340
|
+
MU.log "Attempting to narrow down #{@cloud} #{@type} to #{MU.deploy_id}", MU::NOTICE
|
341
|
+
try_deploy_id = MU.deploy_id
|
342
|
+
retry
|
343
|
+
else
|
344
|
+
raise e
|
345
|
+
end
|
346
|
+
rescue ThreadError => e
|
347
|
+
# Sometimes MommaCat calls us in a potential deadlock situation;
|
348
|
+
# don't be the cause of a fatal error if so, we don't need this
|
349
|
+
# object that badly.
|
350
|
+
raise e if !e.message.match(/recursive locking/)
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
if @obj
|
355
|
+
@deploy_id ||= @obj.deploy_id
|
356
|
+
@id ||= @obj.cloud_id
|
357
|
+
@name ||= @obj.config['name']
|
358
|
+
end
|
359
|
+
|
360
|
+
@obj
|
361
|
+
end
|
362
|
+
|
363
|
+
end
|
364
|
+
end
|
365
|
+
end
|
data/modules/mu/config/role.rb
CHANGED
@@ -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/
|
17
|
+
# Basket of Kittens config schema and parser logic. See modules/mu/providers/*/role.rb
|
18
18
|
class Role
|
19
19
|
|
20
20
|
# Base configuration schema for a Group
|
@@ -131,10 +131,10 @@ module MU
|
|
131
131
|
end
|
132
132
|
|
133
133
|
# Generic pre-processing of {MU::Config::BasketofKittens::role}, bare and unvalidated.
|
134
|
-
# @param
|
135
|
-
# @param
|
134
|
+
# @param _role [Hash]: The resource to process and validate
|
135
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
136
136
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
137
|
-
def self.validate(
|
137
|
+
def self.validate(_role, _configurator)
|
138
138
|
ok = true
|
139
139
|
ok
|
140
140
|
end
|
@@ -0,0 +1,509 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2014 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
|
+
# The default cloud provider for new resources. Must exist in MU.supportedClouds
|
21
|
+
# return [String]
|
22
|
+
def self.defaultCloud
|
23
|
+
configured = {}
|
24
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
25
|
+
if $MU_CFG[cloud.downcase] and !$MU_CFG[cloud.downcase].empty?
|
26
|
+
configured[cloud] = $MU_CFG[cloud.downcase].size
|
27
|
+
configured[cloud] += 0.5 if MU::Cloud.cloudClass(cloud).hosted? # tiebreaker
|
28
|
+
end
|
29
|
+
}
|
30
|
+
if configured.size > 0
|
31
|
+
return configured.keys.sort { |a, b|
|
32
|
+
configured[b] <=> configured[a]
|
33
|
+
}.first
|
34
|
+
else
|
35
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
36
|
+
return cloud if MU::Cloud.cloudClass(cloud).hosted?
|
37
|
+
}
|
38
|
+
return MU::Cloud.supportedClouds.first
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# The default grooming agent for new resources. Must exist in MU.supportedGroomers.
|
43
|
+
def self.defaultGroomer
|
44
|
+
MU.localOnly ? "Ansible" : "Chef"
|
45
|
+
end
|
46
|
+
|
47
|
+
# Accessor for our Basket of Kittens schema definition
|
48
|
+
def self.schema
|
49
|
+
@@schema
|
50
|
+
end
|
51
|
+
|
52
|
+
# Deep merge a configuration hash so we can meld different cloud providers'
|
53
|
+
# schemas together, while preserving documentation differences
|
54
|
+
def self.schemaMerge(orig, new, cloud)
|
55
|
+
if new.is_a?(Hash)
|
56
|
+
new.each_pair { |k, v|
|
57
|
+
if cloud and k == "description" and v.is_a?(String) and !v.match(/\b#{Regexp.quote(cloud.upcase)}\b/) and !v.empty?
|
58
|
+
new[k] = "+"+cloud.upcase+"+: "+v
|
59
|
+
end
|
60
|
+
if orig and orig.has_key?(k)
|
61
|
+
elsif orig
|
62
|
+
orig[k] = new[k]
|
63
|
+
else
|
64
|
+
orig = new
|
65
|
+
end
|
66
|
+
schemaMerge(orig[k], new[k], cloud)
|
67
|
+
}
|
68
|
+
elsif orig.is_a?(Array) and new
|
69
|
+
orig.concat(new)
|
70
|
+
orig.uniq!
|
71
|
+
elsif new.is_a?(String)
|
72
|
+
orig ||= ""
|
73
|
+
orig += "\n" if !orig.empty?
|
74
|
+
orig += "+#{cloud.upcase}+: "+new
|
75
|
+
else
|
76
|
+
# XXX I think this is a NOOP?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@@allregions = []
|
81
|
+
@@loadfails = []
|
82
|
+
MU::Cloud.availableClouds.each { |cloud|
|
83
|
+
next if @@loadfails.include?(cloud)
|
84
|
+
begin
|
85
|
+
regions = MU::Cloud.cloudClass(cloud).listRegions()
|
86
|
+
@@allregions.concat(regions) if regions
|
87
|
+
rescue MU::MuError => e
|
88
|
+
@@loadfails << cloud
|
89
|
+
MU.log e.message, MU::WARN
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
# Configuration chunk for choosing a provider region
|
94
|
+
# @return [Hash]
|
95
|
+
def self.region_primitive
|
96
|
+
if !@@allregions or @@allregions.empty?
|
97
|
+
@@allregions = []
|
98
|
+
MU::Cloud.availableClouds.each { |cloud|
|
99
|
+
next if @@loadfails.include?(cloud)
|
100
|
+
cloudclass = MU::Cloud.cloudClass(cloud)
|
101
|
+
begin
|
102
|
+
return @@allregions if !cloudclass.listRegions()
|
103
|
+
@@allregions.concat(cloudclass.listRegions())
|
104
|
+
rescue MU::MuError => e
|
105
|
+
@@loadfails << cloud
|
106
|
+
MU.log e.message, MU::WARN
|
107
|
+
end
|
108
|
+
}
|
109
|
+
end
|
110
|
+
{
|
111
|
+
"type" => "string",
|
112
|
+
"enum" => @@allregions
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
# Configuration chunk for choosing a set of cloud credentials
|
117
|
+
# @return [Hash]
|
118
|
+
def self.credentials_primitive
|
119
|
+
{
|
120
|
+
"type" => "string",
|
121
|
+
"description" => "Specify a non-default set of credentials to use when authenticating to cloud provider APIs, as listed in `mu.yaml` under each provider's subsection. If "
|
122
|
+
}
|
123
|
+
end
|
124
|
+
|
125
|
+
# Configuration chunk for creating resource tags as an array of key/value
|
126
|
+
# pairs.
|
127
|
+
# @return [Hash]
|
128
|
+
def self.optional_tags_primitive
|
129
|
+
{
|
130
|
+
"type" => "boolean",
|
131
|
+
"description" => "Tag the resource with our optional tags (+MU-HANDLE+, +MU-MASTER-NAME+, +MU-OWNER+).",
|
132
|
+
"default" => true
|
133
|
+
}
|
134
|
+
end
|
135
|
+
|
136
|
+
# Configuration chunk for creating resource tags as an array of key/value
|
137
|
+
# pairs.
|
138
|
+
# @return [Hash]
|
139
|
+
def self.tags_primitive
|
140
|
+
{
|
141
|
+
"type" => "array",
|
142
|
+
"minItems" => 1,
|
143
|
+
"items" => {
|
144
|
+
"description" => "Tags to apply to this resource. Will apply at the cloud provider level and in node groomers, where applicable.",
|
145
|
+
"type" => "object",
|
146
|
+
"title" => "tags",
|
147
|
+
"required" => ["key", "value"],
|
148
|
+
"additionalProperties" => false,
|
149
|
+
"properties" => {
|
150
|
+
"key" => {
|
151
|
+
"type" => "string",
|
152
|
+
},
|
153
|
+
"value" => {
|
154
|
+
"type" => "string",
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
# Configuration chunk for choosing a cloud provider
|
162
|
+
# @return [Hash]
|
163
|
+
def self.cloud_primitive
|
164
|
+
{
|
165
|
+
"type" => "string",
|
166
|
+
# "default" => MU::Config.defaultCloud, # applyInheritedDefaults does this better
|
167
|
+
"enum" => MU::Cloud.supportedClouds
|
168
|
+
}
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
# JSON-schema for resource dependencies
|
173
|
+
# @return [Hash]
|
174
|
+
def self.dependencies_primitive
|
175
|
+
{
|
176
|
+
"type" => "array",
|
177
|
+
"items" => {
|
178
|
+
"type" => "object",
|
179
|
+
"description" => "Declare other objects which this resource requires. This resource will wait until the others are available to create itself.",
|
180
|
+
"required" => ["name", "type"],
|
181
|
+
"additionalProperties" => false,
|
182
|
+
"properties" => {
|
183
|
+
"name" => {"type" => "string"},
|
184
|
+
"type" => {
|
185
|
+
"type" => "string",
|
186
|
+
"enum" => MU::Cloud.resource_types.values.map { |v| v[:cfg_name] }
|
187
|
+
},
|
188
|
+
"phase" => {
|
189
|
+
"type" => "string",
|
190
|
+
"description" => "Which part of the creation process of the resource we depend on should we wait for before starting our own creation? Defaults are usually sensible, but sometimes you want, say, a Server to wait on another Server to be completely ready (through its groom phase) before starting up.",
|
191
|
+
"enum" => ["create", "groom"]
|
192
|
+
},
|
193
|
+
"no_create_wait" => {
|
194
|
+
"type" => "boolean",
|
195
|
+
"default" => false,
|
196
|
+
"description" => "By default, it's assumed that we want to wait on our parents' creation phase, in addition to whatever is declared in this stanza. Setting this flag will bypass waiting on our parent resource's creation, so that our create or groom phase can instead depend only on the parent's groom phase. "
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
}
|
201
|
+
end
|
202
|
+
|
203
|
+
# Have a default value available for config schema elements that take an
|
204
|
+
# email address.
|
205
|
+
# @return [String]
|
206
|
+
def self.notification_email
|
207
|
+
if MU.chef_user == "mu"
|
208
|
+
ENV['MU_ADMIN_EMAIL']
|
209
|
+
else
|
210
|
+
MU.userEmail
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# Load and validate the schema for an individual resource class, optionally
|
215
|
+
# merging cloud-specific schema components.
|
216
|
+
# @param type [String]: The resource type to load
|
217
|
+
# @param cloud [String]: A specific cloud, whose implementation's schema of this resource we will merge
|
218
|
+
# @return [Hash]
|
219
|
+
def self.loadResourceSchema(type, cloud: nil)
|
220
|
+
valid = true
|
221
|
+
shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(type)
|
222
|
+
schemaclass = Object.const_get("MU").const_get("Config").const_get(shortclass)
|
223
|
+
|
224
|
+
[:schema, :validate].each { |method|
|
225
|
+
if !schemaclass.respond_to?(method)
|
226
|
+
MU.log "MU::Config::#{type}.#{method.to_s} doesn't seem to be implemented", MU::ERR
|
227
|
+
return [nil, false] if method == :schema
|
228
|
+
valid = false
|
229
|
+
end
|
230
|
+
}
|
231
|
+
|
232
|
+
schema = schemaclass.schema.dup
|
233
|
+
|
234
|
+
schema["properties"]["virtual_name"] = {
|
235
|
+
"description" => "Internal use.",
|
236
|
+
"type" => "string"
|
237
|
+
}
|
238
|
+
schema["properties"]["dependencies"] = MU::Config.dependencies_primitive
|
239
|
+
schema["properties"]["cloud"] = MU::Config.cloud_primitive
|
240
|
+
schema["properties"]["credentials"] = MU::Config.credentials_primitive
|
241
|
+
schema["title"] = type.to_s
|
242
|
+
|
243
|
+
if cloud
|
244
|
+
cloudclass = MU::Cloud.resourceClass(cloud, type)
|
245
|
+
|
246
|
+
if cloudclass.respond_to?(:schema)
|
247
|
+
_reqd, cloudschema = cloudclass.schema
|
248
|
+
cloudschema.each { |key, cfg|
|
249
|
+
if schema["properties"][key]
|
250
|
+
schemaMerge(schema["properties"][key], cfg, cloud)
|
251
|
+
else
|
252
|
+
schema["properties"][key] = cfg.dup
|
253
|
+
end
|
254
|
+
}
|
255
|
+
else
|
256
|
+
MU.log "MU::Cloud::#{cloud}::#{type}.#{method.to_s} doesn't seem to be implemented", MU::ERR
|
257
|
+
valid = false
|
258
|
+
end
|
259
|
+
|
260
|
+
end
|
261
|
+
|
262
|
+
return [schema, valid]
|
263
|
+
end
|
264
|
+
|
265
|
+
private
|
266
|
+
|
267
|
+
def applySchemaDefaults(conf_chunk = config, schema_chunk = schema, depth = 0, siblings = nil, type: nil)
|
268
|
+
return if schema_chunk.nil?
|
269
|
+
|
270
|
+
if conf_chunk != nil and schema_chunk["properties"].kind_of?(Hash) and conf_chunk.is_a?(Hash)
|
271
|
+
|
272
|
+
if schema_chunk["properties"]["creation_style"].nil? or
|
273
|
+
schema_chunk["properties"]["creation_style"] != "existing"
|
274
|
+
schema_chunk["properties"].each_pair { |key, subschema|
|
275
|
+
shortclass = if conf_chunk[key]
|
276
|
+
shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(key, false)
|
277
|
+
shortclass
|
278
|
+
else
|
279
|
+
nil
|
280
|
+
end
|
281
|
+
|
282
|
+
new_val = applySchemaDefaults(conf_chunk[key], subschema, depth+1, conf_chunk, type: shortclass).dup
|
283
|
+
if !new_val.nil?
|
284
|
+
begin
|
285
|
+
conf_chunk[key] = Marshal.load(Marshal.dump(new_val))
|
286
|
+
rescue TypeError
|
287
|
+
conf_chunk[key] = new_val.clone
|
288
|
+
end
|
289
|
+
end
|
290
|
+
}
|
291
|
+
end
|
292
|
+
elsif schema_chunk["type"] == "array" and conf_chunk.kind_of?(Array)
|
293
|
+
conf_chunk.map! { |item|
|
294
|
+
# If we're working on a resource type, go get implementation-specific
|
295
|
+
# schema information so that we set those defaults correctly.
|
296
|
+
realschema = if type and schema_chunk["items"] and schema_chunk["items"]["properties"] and item["cloud"] and MU::Cloud.supportedClouds.include?(item['cloud'])
|
297
|
+
|
298
|
+
_toplevel_required, cloudschema = MU::Cloud.resourceClass(item["cloud"], type).schema(self)
|
299
|
+
|
300
|
+
newschema = schema_chunk["items"].dup
|
301
|
+
MU::Config.schemaMerge(newschema["properties"], cloudschema, item["cloud"])
|
302
|
+
newschema
|
303
|
+
else
|
304
|
+
schema_chunk["items"].dup
|
305
|
+
end
|
306
|
+
|
307
|
+
applySchemaDefaults(item, realschema, depth+1, conf_chunk, type: type).dup
|
308
|
+
}
|
309
|
+
else
|
310
|
+
if conf_chunk.nil? and !schema_chunk["default_if"].nil? and !siblings.nil?
|
311
|
+
schema_chunk["default_if"].each { |cond|
|
312
|
+
if siblings[cond["key_is"]] == cond["value_is"]
|
313
|
+
return Marshal.load(Marshal.dump(cond["set"]))
|
314
|
+
end
|
315
|
+
}
|
316
|
+
end
|
317
|
+
if conf_chunk.nil? and schema_chunk["default"] != nil
|
318
|
+
return Marshal.load(Marshal.dump(schema_chunk["default"]))
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
return conf_chunk
|
323
|
+
end
|
324
|
+
|
325
|
+
# Given a bare hash describing a resource, insert default values which can
|
326
|
+
# be inherited from its parent or from the root of the BoK.
|
327
|
+
# @param kitten [Hash]: A resource descriptor
|
328
|
+
# @param type [String]: The type of resource this is ("servers" etc)
|
329
|
+
def applyInheritedDefaults(kitten, type)
|
330
|
+
return if !kitten.is_a?(Hash)
|
331
|
+
kitten['cloud'] ||= @config['cloud']
|
332
|
+
kitten['cloud'] ||= MU::Config.defaultCloud
|
333
|
+
|
334
|
+
if !MU::Cloud.supportedClouds.include?(kitten['cloud'])
|
335
|
+
return
|
336
|
+
end
|
337
|
+
|
338
|
+
cloudclass = MU::Cloud.cloudClass(kitten['cloud'])
|
339
|
+
|
340
|
+
resclass = MU::Cloud.resourceClass(kitten['cloud'], type)
|
341
|
+
|
342
|
+
schema_fields = ["us_only", "scrub_mu_isms", "credentials", "billing_acct"]
|
343
|
+
if !resclass.isGlobal?
|
344
|
+
kitten['region'] ||= @config['region']
|
345
|
+
kitten['region'] ||= cloudclass.myRegion(kitten['credentials'])
|
346
|
+
schema_fields << "region"
|
347
|
+
end
|
348
|
+
|
349
|
+
kitten['credentials'] ||= @config['credentials']
|
350
|
+
kitten['credentials'] ||= cloudclass.credConfig(name_only: true)
|
351
|
+
|
352
|
+
kitten['us_only'] ||= @config['us_only']
|
353
|
+
kitten['us_only'] ||= false
|
354
|
+
|
355
|
+
kitten['scrub_mu_isms'] ||= @config['scrub_mu_isms']
|
356
|
+
kitten['scrub_mu_isms'] ||= false
|
357
|
+
|
358
|
+
if kitten['cloud'] == "Google"
|
359
|
+
# TODO this should be cloud-generic (handle AWS accounts, Azure subscriptions)
|
360
|
+
if resclass.canLiveIn.include?(:Habitat)
|
361
|
+
kitten["project"] ||= MU::Cloud::Google.defaultProject(kitten['credentials'])
|
362
|
+
schema_fields << "project"
|
363
|
+
end
|
364
|
+
if kitten['region'].nil? and !kitten['#MU_CLOUDCLASS'].nil? and
|
365
|
+
!resclass.isGlobal? and
|
366
|
+
![MU::Cloud::VPC, MU::Cloud::FirewallRule].include?(kitten['#MU_CLOUDCLASS'])
|
367
|
+
if MU::Cloud::Google.myRegion((kitten['credentials'])).nil?
|
368
|
+
raise ValidationError, "Google '#{type}' resource '#{kitten['name']}' declared without a region, but no default Google region declared in mu.yaml under #{kitten['credentials'].nil? ? "default" : kitten['credentials']} credential set"
|
369
|
+
end
|
370
|
+
kitten['region'] ||= MU::Cloud::Google.myRegion
|
371
|
+
end
|
372
|
+
elsif kitten["cloud"] == "AWS" and !resclass.isGlobal? and !kitten['region']
|
373
|
+
if MU::Cloud::AWS.myRegion.nil?
|
374
|
+
raise ValidationError, "AWS resource declared without a region, but no default AWS region found"
|
375
|
+
end
|
376
|
+
kitten['region'] ||= MU::Cloud::AWS.myRegion
|
377
|
+
end
|
378
|
+
|
379
|
+
|
380
|
+
kitten['billing_acct'] ||= @config['billing_acct'] if @config['billing_acct']
|
381
|
+
|
382
|
+
kitten["dependencies"] ||= []
|
383
|
+
|
384
|
+
# Make sure the schema knows about these "new" fields, so that validation
|
385
|
+
# doesn't trip over them.
|
386
|
+
schema_fields.each { |field|
|
387
|
+
if @@schema["properties"][field]
|
388
|
+
MU.log "Adding #{field} to schema for #{type} #{kitten['cloud']}", MU::DEBUG, details: @@schema["properties"][field]
|
389
|
+
@@schema["properties"][type]["items"]["properties"][field] ||= @@schema["properties"][field]
|
390
|
+
end
|
391
|
+
}
|
392
|
+
end
|
393
|
+
|
394
|
+
CIDR_PATTERN = "^\\d+\\.\\d+\\.\\d+\\.\\d+\/[0-9]{1,2}$"
|
395
|
+
CIDR_DESCRIPTION = "CIDR-formatted IP block, e.g. 1.2.3.4/32"
|
396
|
+
CIDR_PRIMITIVE = {
|
397
|
+
"type" => "string",
|
398
|
+
"pattern" => CIDR_PATTERN,
|
399
|
+
"description" => CIDR_DESCRIPTION
|
400
|
+
}
|
401
|
+
|
402
|
+
|
403
|
+
@@schema = {
|
404
|
+
"$schema" => "http://json-schema.org/draft-04/schema#",
|
405
|
+
"title" => "MU Application",
|
406
|
+
"type" => "object",
|
407
|
+
"description" => "A MU application stack, consisting of at least one resource.",
|
408
|
+
"required" => ["admins", "appname"],
|
409
|
+
"properties" => {
|
410
|
+
"appname" => {
|
411
|
+
"type" => "string",
|
412
|
+
"description" => "A name for your application stack. Should be short, but easy to differentiate from other applications.",
|
413
|
+
},
|
414
|
+
"scrub_mu_isms" => {
|
415
|
+
"type" => "boolean",
|
416
|
+
"description" => "When 'cloud' is set to 'CloudFormation,' use this flag to strip out Mu-specific artifacts (tags, standard userdata, naming conventions, etc) to yield a clean, source-agnostic template. Setting this flag here will override declarations in individual resources."
|
417
|
+
},
|
418
|
+
"project" => {
|
419
|
+
"type" => "string",
|
420
|
+
"description" => "**GOOGLE ONLY**: The project into which to deploy resources"
|
421
|
+
},
|
422
|
+
"billing_acct" => {
|
423
|
+
"type" => "string",
|
424
|
+
"description" => "**GOOGLE ONLY**: Billing account ID to associate with a newly-created Google Project. If not specified, will attempt to locate a billing account associated with the default project for our credentials.",
|
425
|
+
},
|
426
|
+
"region" => MU::Config.region_primitive,
|
427
|
+
"credentials" => MU::Config.credentials_primitive,
|
428
|
+
"us_only" => {
|
429
|
+
"type" => "boolean",
|
430
|
+
"description" => "For resources which span regions, restrict to regions inside the United States",
|
431
|
+
"default" => false
|
432
|
+
},
|
433
|
+
"conditions" => {
|
434
|
+
"type" => "array",
|
435
|
+
"items" => {
|
436
|
+
"type" => "object",
|
437
|
+
"required" => ["name", "cloudcode"],
|
438
|
+
"description" => "CloudFormation-specific. Define Conditions as in http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html. Arguments must use the cloudCode() macro.",
|
439
|
+
"properties" => {
|
440
|
+
"name" => { "required" => true, "type" => "string" },
|
441
|
+
"cloudcode" => { "required" => true, "type" => "string" },
|
442
|
+
}
|
443
|
+
}
|
444
|
+
},
|
445
|
+
"parameters" => {
|
446
|
+
"type" => "array",
|
447
|
+
"items" => {
|
448
|
+
"type" => "object",
|
449
|
+
"title" => "parameter",
|
450
|
+
"description" => "Parameters to be substituted elsewhere in this Basket of Kittens as ERB variables (<%= varname %>)",
|
451
|
+
"additionalProperties" => false,
|
452
|
+
"properties" => {
|
453
|
+
"name" => { "required" => true, "type" => "string" },
|
454
|
+
"default" => { "type" => "string" },
|
455
|
+
"list_of" => {
|
456
|
+
"type" => "string",
|
457
|
+
"description" => "Treat the value as a comma-separated list of values with this key name, equivalent to CloudFormation's various List<> types. For example, set to 'subnet_id' to pass values as an array of subnet identifiers as the 'subnets' argument of a VPC stanza."
|
458
|
+
},
|
459
|
+
"prettyname" => {
|
460
|
+
"type" => "string",
|
461
|
+
"description" => "An alternative name to use when generating parameter fields in, for example, CloudFormation templates"
|
462
|
+
},
|
463
|
+
"description" => {"type" => "string"},
|
464
|
+
"cloudtype" => {
|
465
|
+
"type" => "string",
|
466
|
+
"description" => "A platform-specific string describing the type of validation to use for this parameter. E.g. when generating a CloudFormation template, set to AWS::EC2::Image::Id to validate input as an AMI identifier."
|
467
|
+
},
|
468
|
+
"required" => {
|
469
|
+
"type" => "boolean",
|
470
|
+
"default" => true
|
471
|
+
},
|
472
|
+
"valid_values" => {
|
473
|
+
"type" => "array",
|
474
|
+
"description" => "List of valid values for this parameter. Can only be a list of static strings, for now.",
|
475
|
+
"items" => {
|
476
|
+
"type" => "string"
|
477
|
+
}
|
478
|
+
}
|
479
|
+
}
|
480
|
+
}
|
481
|
+
},
|
482
|
+
# TODO availability zones (or an array thereof)
|
483
|
+
|
484
|
+
"admins" => {
|
485
|
+
"type" => "array",
|
486
|
+
"items" => {
|
487
|
+
"type" => "object",
|
488
|
+
"title" => "admin",
|
489
|
+
"description" => "Administrative contacts for this application stack. Will be automatically set to invoking Mu user, if not specified.",
|
490
|
+
"required" => ["name", "email"],
|
491
|
+
"additionalProperties" => false,
|
492
|
+
"properties" => {
|
493
|
+
"name" => {"type" => "string"},
|
494
|
+
"email" => {"type" => "string"},
|
495
|
+
"public_key" => {
|
496
|
+
"type" => "string",
|
497
|
+
"description" => "An OpenSSH-style public key string. This will be installed on all instances created in this deployment."
|
498
|
+
}
|
499
|
+
}
|
500
|
+
},
|
501
|
+
"minItems" => 1,
|
502
|
+
"uniqueItems" => true
|
503
|
+
}
|
504
|
+
},
|
505
|
+
"additionalProperties" => false
|
506
|
+
}
|
507
|
+
|
508
|
+
end #class
|
509
|
+
end #module
|