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.
- checksums.yaml +4 -4
- data/Dockerfile +10 -2
- data/bin/mu-adopt +5 -1
- data/bin/mu-load-config.rb +2 -3
- data/bin/mu-run-tests +112 -27
- data/cloud-mu.gemspec +20 -20
- data/cookbooks/mu-tools/libraries/helper.rb +2 -1
- data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
- data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
- data/cookbooks/mu-tools/resources/disk.rb +1 -1
- data/extras/image-generators/Google/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +1 -1
- data/modules/mommacat.ru +5 -15
- data/modules/mu.rb +10 -14
- data/modules/mu/adoption.rb +20 -14
- data/modules/mu/cleanup.rb +13 -9
- data/modules/mu/cloud.rb +26 -26
- data/modules/mu/clouds/aws.rb +100 -59
- data/modules/mu/clouds/aws/alarm.rb +4 -2
- data/modules/mu/clouds/aws/bucket.rb +25 -21
- data/modules/mu/clouds/aws/cache_cluster.rb +25 -23
- data/modules/mu/clouds/aws/collection.rb +21 -20
- data/modules/mu/clouds/aws/container_cluster.rb +47 -26
- data/modules/mu/clouds/aws/database.rb +57 -68
- data/modules/mu/clouds/aws/dnszone.rb +14 -14
- data/modules/mu/clouds/aws/endpoint.rb +20 -16
- data/modules/mu/clouds/aws/firewall_rule.rb +19 -16
- data/modules/mu/clouds/aws/folder.rb +7 -7
- data/modules/mu/clouds/aws/function.rb +15 -12
- data/modules/mu/clouds/aws/group.rb +14 -10
- data/modules/mu/clouds/aws/habitat.rb +16 -13
- data/modules/mu/clouds/aws/loadbalancer.rb +16 -15
- data/modules/mu/clouds/aws/log.rb +13 -10
- data/modules/mu/clouds/aws/msg_queue.rb +15 -8
- data/modules/mu/clouds/aws/nosqldb.rb +18 -11
- data/modules/mu/clouds/aws/notifier.rb +11 -6
- data/modules/mu/clouds/aws/role.rb +87 -70
- data/modules/mu/clouds/aws/search_domain.rb +30 -19
- data/modules/mu/clouds/aws/server.rb +102 -72
- data/modules/mu/clouds/aws/server_pool.rb +47 -28
- data/modules/mu/clouds/aws/storage_pool.rb +5 -6
- data/modules/mu/clouds/aws/user.rb +13 -10
- data/modules/mu/clouds/aws/vpc.rb +135 -121
- data/modules/mu/clouds/azure.rb +16 -9
- data/modules/mu/clouds/azure/container_cluster.rb +2 -3
- data/modules/mu/clouds/azure/firewall_rule.rb +10 -10
- data/modules/mu/clouds/azure/habitat.rb +8 -6
- data/modules/mu/clouds/azure/loadbalancer.rb +5 -5
- data/modules/mu/clouds/azure/role.rb +8 -10
- data/modules/mu/clouds/azure/server.rb +65 -25
- data/modules/mu/clouds/azure/user.rb +5 -7
- data/modules/mu/clouds/azure/vpc.rb +12 -15
- data/modules/mu/clouds/cloudformation.rb +8 -7
- data/modules/mu/clouds/cloudformation/vpc.rb +2 -4
- data/modules/mu/clouds/google.rb +39 -24
- data/modules/mu/clouds/google/bucket.rb +9 -11
- data/modules/mu/clouds/google/container_cluster.rb +27 -42
- data/modules/mu/clouds/google/database.rb +6 -9
- data/modules/mu/clouds/google/firewall_rule.rb +11 -10
- data/modules/mu/clouds/google/folder.rb +16 -9
- data/modules/mu/clouds/google/function.rb +127 -161
- data/modules/mu/clouds/google/group.rb +21 -18
- data/modules/mu/clouds/google/habitat.rb +18 -15
- data/modules/mu/clouds/google/loadbalancer.rb +14 -16
- data/modules/mu/clouds/google/role.rb +48 -31
- data/modules/mu/clouds/google/server.rb +105 -105
- data/modules/mu/clouds/google/server_pool.rb +12 -31
- data/modules/mu/clouds/google/user.rb +67 -13
- data/modules/mu/clouds/google/vpc.rb +58 -65
- data/modules/mu/config.rb +89 -1738
- data/modules/mu/config/bucket.rb +3 -3
- data/modules/mu/config/collection.rb +3 -3
- data/modules/mu/config/container_cluster.rb +2 -2
- data/modules/mu/config/dnszone.rb +5 -5
- data/modules/mu/config/doc_helpers.rb +517 -0
- data/modules/mu/config/endpoint.rb +3 -3
- data/modules/mu/config/firewall_rule.rb +118 -3
- data/modules/mu/config/folder.rb +3 -3
- data/modules/mu/config/function.rb +2 -2
- data/modules/mu/config/group.rb +3 -3
- data/modules/mu/config/habitat.rb +3 -3
- data/modules/mu/config/loadbalancer.rb +3 -3
- data/modules/mu/config/log.rb +3 -3
- data/modules/mu/config/msg_queue.rb +3 -3
- data/modules/mu/config/nosqldb.rb +3 -3
- data/modules/mu/config/notifier.rb +2 -2
- data/modules/mu/config/ref.rb +333 -0
- data/modules/mu/config/role.rb +3 -3
- data/modules/mu/config/schema_helpers.rb +508 -0
- data/modules/mu/config/search_domain.rb +3 -3
- data/modules/mu/config/server.rb +86 -58
- data/modules/mu/config/server_pool.rb +2 -2
- data/modules/mu/config/tail.rb +189 -0
- data/modules/mu/config/user.rb +3 -3
- data/modules/mu/config/vpc.rb +44 -4
- data/modules/mu/defaults/Google.yaml +2 -2
- data/modules/mu/deploy.rb +13 -10
- data/modules/mu/groomer.rb +1 -1
- data/modules/mu/groomers/ansible.rb +69 -24
- data/modules/mu/groomers/chef.rb +52 -44
- data/modules/mu/logger.rb +17 -14
- data/modules/mu/master.rb +317 -2
- data/modules/mu/master/chef.rb +3 -4
- data/modules/mu/master/ldap.rb +3 -3
- data/modules/mu/master/ssl.rb +12 -2
- data/modules/mu/mommacat.rb +85 -1766
- data/modules/mu/mommacat/daemon.rb +394 -0
- data/modules/mu/mommacat/naming.rb +366 -0
- data/modules/mu/mommacat/storage.rb +689 -0
- data/modules/tests/bucket.yml +4 -0
- data/modules/tests/{win2k12.yaml → needwork/win2k12.yaml} +0 -0
- data/modules/tests/regrooms/aws-iam.yaml +201 -0
- data/modules/tests/regrooms/bucket.yml +19 -0
- metadata +112 -102
|
@@ -57,10 +57,10 @@ module MU
|
|
|
57
57
|
end
|
|
58
58
|
|
|
59
59
|
# Generic pre-processing of {MU::Config::BasketofKittens::endpoints}, bare and unvalidated.
|
|
60
|
-
# @param
|
|
61
|
-
# @param
|
|
60
|
+
# @param _endpoint [Hash]: The resource to process and validate
|
|
61
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
62
62
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
63
|
-
def self.validate(
|
|
63
|
+
def self.validate(_endpoint, _configurator)
|
|
64
64
|
ok = true
|
|
65
65
|
|
|
66
66
|
ok
|
|
@@ -100,14 +100,129 @@ module MU
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Generic pre-processing of {MU::Config::BasketofKittens::firewall_rules}, bare and unvalidated.
|
|
103
|
-
# @param
|
|
104
|
-
# @param
|
|
103
|
+
# @param _acl [Hash]: The resource to process and validate
|
|
104
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
105
105
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
106
|
-
def self.validate(
|
|
106
|
+
def self.validate(_acl, _configurator)
|
|
107
107
|
ok = true
|
|
108
108
|
ok
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
end
|
|
112
|
+
|
|
113
|
+
# FirewallRules can reference other FirewallRules, which means we need to do
|
|
114
|
+
# an extra pass to make sure we get all intra-stack dependencies correct.
|
|
115
|
+
# @param acl [Hash]: The configuration hash for the FirewallRule to check
|
|
116
|
+
# @return [Hash]
|
|
117
|
+
def resolveIntraStackFirewallRefs(acl, delay_validation = false)
|
|
118
|
+
acl["rules"].each { |acl_include|
|
|
119
|
+
if acl_include['sgs']
|
|
120
|
+
acl_include['sgs'].each { |sg_ref|
|
|
121
|
+
if haveLitterMate?(sg_ref, "firewall_rules")
|
|
122
|
+
acl["dependencies"] ||= []
|
|
123
|
+
found = false
|
|
124
|
+
acl["dependencies"].each { |dep|
|
|
125
|
+
if dep["type"] == "firewall_rule" and dep["name"] == sg_ref
|
|
126
|
+
dep["no_create_wait"] = true
|
|
127
|
+
found = true
|
|
128
|
+
end
|
|
129
|
+
}
|
|
130
|
+
if !found
|
|
131
|
+
acl["dependencies"] << {
|
|
132
|
+
"type" => "firewall_rule",
|
|
133
|
+
"name" => sg_ref,
|
|
134
|
+
"no_create_wait" => true
|
|
135
|
+
}
|
|
136
|
+
end
|
|
137
|
+
siblingfw = haveLitterMate?(sg_ref, "firewall_rules")
|
|
138
|
+
if !siblingfw["#MU_VALIDATED"]
|
|
139
|
+
# XXX raise failure somehow
|
|
140
|
+
insertKitten(siblingfw, "firewall_rules", delay_validation: delay_validation)
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
}
|
|
144
|
+
end
|
|
145
|
+
}
|
|
146
|
+
acl
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Generate configuration for the general-purpose admin firewall rulesets
|
|
150
|
+
# (security groups in AWS). Note that these are unique to regions and
|
|
151
|
+
# individual VPCs (as well as Classic, which is just a degenerate case of
|
|
152
|
+
# a VPC for our purposes.
|
|
153
|
+
# @param vpc [Hash]: A VPC reference as defined in our config schema. This originates with the calling resource, so we'll peel out just what we need (a name or cloud id of a VPC).
|
|
154
|
+
# @param admin_ip [String]: Optional string of an extra IP address to allow blanket access to the calling resource.
|
|
155
|
+
# @param cloud [String]: The parent resource's cloud plugin identifier
|
|
156
|
+
# @param region [String]: Cloud provider region, if applicable.
|
|
157
|
+
# @return [Hash<String>]: A dependency description that the calling resource can then add to itself.
|
|
158
|
+
def adminFirewallRuleset(vpc: nil, admin_ip: nil, region: nil, cloud: nil, credentials: nil, rules_only: false)
|
|
159
|
+
if !cloud or (cloud == "AWS" and !region)
|
|
160
|
+
raise MuError, "Cannot call adminFirewallRuleset without specifying the parent's region and cloud provider"
|
|
161
|
+
end
|
|
162
|
+
hosts = Array.new
|
|
163
|
+
hosts << "#{MU.my_public_ip}/32" if MU.my_public_ip
|
|
164
|
+
hosts << "#{MU.my_private_ip}/32" if MU.my_private_ip
|
|
165
|
+
hosts << "#{MU.mu_public_ip}/32" if MU.mu_public_ip
|
|
166
|
+
hosts << "#{admin_ip}/32" if admin_ip
|
|
167
|
+
hosts.uniq!
|
|
168
|
+
|
|
169
|
+
rules = []
|
|
170
|
+
if cloud == "Google"
|
|
171
|
+
rules = [
|
|
172
|
+
{ "ingress" => true, "proto" => "all", "hosts" => hosts },
|
|
173
|
+
{ "egress" => true, "proto" => "all", "hosts" => hosts }
|
|
174
|
+
]
|
|
175
|
+
else
|
|
176
|
+
rules = [
|
|
177
|
+
{ "proto" => "tcp", "port_range" => "0-65535", "hosts" => hosts },
|
|
178
|
+
{ "proto" => "udp", "port_range" => "0-65535", "hosts" => hosts },
|
|
179
|
+
{ "proto" => "icmp", "port_range" => "-1", "hosts" => hosts }
|
|
180
|
+
]
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
resclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get("FirewallRule")
|
|
184
|
+
|
|
185
|
+
if rules_only
|
|
186
|
+
return rules
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
name = "admin"
|
|
190
|
+
name += credentials.to_s if credentials
|
|
191
|
+
realvpc = nil
|
|
192
|
+
if vpc
|
|
193
|
+
realvpc = {}
|
|
194
|
+
['vpc_name', 'vpc_id'].each { |p|
|
|
195
|
+
if vpc[p]
|
|
196
|
+
vpc[p.sub(/^vpc_/, '')] = vpc[p]
|
|
197
|
+
vpc.delete(p)
|
|
198
|
+
end
|
|
199
|
+
}
|
|
200
|
+
['cloud', 'id', 'name', 'deploy_id', 'habitat', 'credentials'].each { |field|
|
|
201
|
+
realvpc[field] = vpc[field] if !vpc[field].nil?
|
|
202
|
+
}
|
|
203
|
+
if !realvpc['id'].nil? and !realvpc['id'].empty?
|
|
204
|
+
# Stupid kludge for Google cloud_ids which are sometimes URLs and
|
|
205
|
+
# sometimes not. Requirements are inconsistent from scenario to
|
|
206
|
+
# scenario.
|
|
207
|
+
name = name + "-" + realvpc['id'].gsub(/.*\//, "")
|
|
208
|
+
realvpc['id'] = getTail("id", value: realvpc['id'], prettyname: "Admin Firewall Ruleset #{name} Target VPC", cloudtype: "AWS::EC2::VPC::Id") if realvpc["id"].is_a?(String)
|
|
209
|
+
elsif !realvpc['name'].nil?
|
|
210
|
+
name = name + "-" + realvpc['name']
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
acl = {"name" => name, "rules" => rules, "vpc" => realvpc, "cloud" => cloud, "admin" => true, "credentials" => credentials }
|
|
216
|
+
if cloud == "Google" and acl["vpc"] and acl["vpc"]["habitat"]
|
|
217
|
+
acl['project'] = acl["vpc"]["habitat"]["id"] || acl["vpc"]["habitat"]["name"]
|
|
218
|
+
end
|
|
219
|
+
acl.delete("vpc") if !acl["vpc"]
|
|
220
|
+
if !resclass.isGlobal? and !region.nil? and !region.empty?
|
|
221
|
+
acl["region"] = region
|
|
222
|
+
end
|
|
223
|
+
@admin_firewall_rules << acl if !@admin_firewall_rules.include?(acl)
|
|
224
|
+
return {"type" => "firewall_rule", "name" => name}
|
|
225
|
+
end
|
|
226
|
+
|
|
112
227
|
end
|
|
113
228
|
end
|
data/modules/mu/config/folder.rb
CHANGED
|
@@ -59,10 +59,10 @@ module MU
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
# Generic pre-processing of {MU::Config::BasketofKittens::folder}, bare and unvalidated.
|
|
62
|
-
# @param
|
|
63
|
-
# @param
|
|
62
|
+
# @param _folder [Hash]: The resource to process and validate
|
|
63
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
64
64
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
65
|
-
def self.validate(
|
|
65
|
+
def self.validate(_folder, _configurator)
|
|
66
66
|
ok = true
|
|
67
67
|
ok
|
|
68
68
|
end
|
|
@@ -99,9 +99,9 @@ module MU
|
|
|
99
99
|
|
|
100
100
|
# Generic pre-processing of {MU::Config::BasketofKittens::functions}, bare and unvalidated.
|
|
101
101
|
# @param function [Hash]: The resource to process and validate
|
|
102
|
-
# @param
|
|
102
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
103
103
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
104
|
-
def self.validate(function,
|
|
104
|
+
def self.validate(function, _configurator)
|
|
105
105
|
ok = true
|
|
106
106
|
if !function['code']
|
|
107
107
|
ok = false
|
data/modules/mu/config/group.rb
CHANGED
|
@@ -51,10 +51,10 @@ module MU
|
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
# Generic pre-processing of {MU::Config::BasketofKittens::group}, bare and unvalidated.
|
|
54
|
-
# @param
|
|
55
|
-
# @param
|
|
54
|
+
# @param _group [Hash]: The resource to process and validate
|
|
55
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
56
56
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
57
|
-
def self.validate(
|
|
57
|
+
def self.validate(_group, _configurator)
|
|
58
58
|
ok = true
|
|
59
59
|
ok
|
|
60
60
|
end
|
|
@@ -38,10 +38,10 @@ module MU
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
# Generic pre-processing of {MU::Config::BasketofKittens::habitat}, bare and unvalidated.
|
|
41
|
-
# @param
|
|
42
|
-
# @param
|
|
41
|
+
# @param _habitat [Hash]: The resource to process and validate
|
|
42
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
43
43
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
44
|
-
def self.validate(
|
|
44
|
+
def self.validate(_habitat, _configurator)
|
|
45
45
|
ok = true
|
|
46
46
|
ok
|
|
47
47
|
end
|
|
@@ -383,9 +383,9 @@ module MU
|
|
|
383
383
|
|
|
384
384
|
# Generic pre-processing of {MU::Config::BasketofKittens::loadbalancers}, bare and unvalidated.
|
|
385
385
|
# @param lb [Hash]: The resource to process and validate
|
|
386
|
-
# @param
|
|
386
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
387
387
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
388
|
-
def self.validate(lb,
|
|
388
|
+
def self.validate(lb, _configurator)
|
|
389
389
|
ok = true
|
|
390
390
|
# Convert old-school listener declarations into target groups and health
|
|
391
391
|
# checks, for which AWS and Google both have equivalents.
|
|
@@ -446,7 +446,7 @@ module MU
|
|
|
446
446
|
else
|
|
447
447
|
found = false
|
|
448
448
|
lb['targetgroups'].each { |tg|
|
|
449
|
-
if
|
|
449
|
+
if tg['name'] == action['targetgroup']
|
|
450
450
|
found = true
|
|
451
451
|
break
|
|
452
452
|
end
|
data/modules/mu/config/log.rb
CHANGED
|
@@ -36,10 +36,10 @@ module MU
|
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
# Generic pre-processing of {MU::Config::BasketofKittens::logs}, bare and unvalidated.
|
|
39
|
-
# @param
|
|
40
|
-
# @param
|
|
39
|
+
# @param _log [Hash]: The resource to process and validate
|
|
40
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
41
41
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
42
|
-
def self.validate(
|
|
42
|
+
def self.validate(_log, _configurator)
|
|
43
43
|
ok = true
|
|
44
44
|
ok
|
|
45
45
|
end
|
|
@@ -34,10 +34,10 @@ module MU
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
# Generic pre-processing of {MU::Config::BasketofKittens::msg_queues}, bare and unvalidated.
|
|
37
|
-
# @param
|
|
38
|
-
# @param
|
|
37
|
+
# @param _queue [Hash]: The resource to process and validate
|
|
38
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
39
39
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
40
|
-
def self.validate(
|
|
40
|
+
def self.validate(_queue, _configurator)
|
|
41
41
|
ok = true
|
|
42
42
|
ok
|
|
43
43
|
end
|
|
@@ -35,10 +35,10 @@ module MU
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
# Generic pre-processing of {MU::Config::BasketofKittens::nosqldbs}, bare and unvalidated.
|
|
38
|
-
# @param
|
|
39
|
-
# @param
|
|
38
|
+
# @param _db [Hash]: The resource to process and validate
|
|
39
|
+
# @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
|
|
40
40
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
41
|
-
def self.validate(
|
|
41
|
+
def self.validate(_db, _configurator)
|
|
42
42
|
ok = true
|
|
43
43
|
|
|
44
44
|
ok
|
|
@@ -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,333 @@
|
|
|
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
|
+
# 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}.
|
|
134
|
+
# @param aliases [Array<Hash>]: Key => value mappings to set backwards-compatibility aliases for attributes, such as the ubiquitous +vpc_id+ (+vpc_id+ => +id+).
|
|
135
|
+
# @return [Hash]
|
|
136
|
+
def self.schema(aliases = [], type: nil, parent_obj: nil, desc: nil, omit_fields: [])
|
|
137
|
+
parent_obj ||= caller[1].gsub(/.*?\/([^\.\/]+)\.rb:.*/, '\1')
|
|
138
|
+
desc ||= "Reference a #{type ? "'#{type}' resource" : "resource" } from this #{parent_obj ? "'#{parent_obj}'" : "" } resource"
|
|
139
|
+
schema = {
|
|
140
|
+
"type" => "object",
|
|
141
|
+
"#MU_REFERENCE" => true,
|
|
142
|
+
"minProperties" => 1,
|
|
143
|
+
"description" => desc,
|
|
144
|
+
"properties" => {
|
|
145
|
+
"id" => {
|
|
146
|
+
"type" => "string",
|
|
147
|
+
"description" => "Cloud identifier of a resource we want to reference, typically used when leveraging resources not managed by MU"
|
|
148
|
+
},
|
|
149
|
+
"name" => {
|
|
150
|
+
"type" => "string",
|
|
151
|
+
"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+."
|
|
152
|
+
},
|
|
153
|
+
"type" => {
|
|
154
|
+
"type" => "string",
|
|
155
|
+
"description" => "The resource type we're attempting to reference.",
|
|
156
|
+
"enum" => MU::Cloud.resource_types.values.map { |t| t[:cfg_plural] }
|
|
157
|
+
},
|
|
158
|
+
"deploy_id" => {
|
|
159
|
+
"type" => "string",
|
|
160
|
+
"description" => "Our target resource should be found in this Mu deploy."
|
|
161
|
+
},
|
|
162
|
+
"credentials" => MU::Config.credentials_primitive,
|
|
163
|
+
"region" => MU::Config.region_primitive,
|
|
164
|
+
"cloud" => MU::Config.cloud_primitive,
|
|
165
|
+
"tag" => {
|
|
166
|
+
"type" => "object",
|
|
167
|
+
"description" => "If the target resource supports tagging and our resource implementations +find+ method supports it, we can attempt to locate it by tag.",
|
|
168
|
+
"properties" => {
|
|
169
|
+
"key" => {
|
|
170
|
+
"type" => "string",
|
|
171
|
+
"description" => "The tag or label key to search against"
|
|
172
|
+
},
|
|
173
|
+
"value" => {
|
|
174
|
+
"type" => "string",
|
|
175
|
+
"description" => "The tag or label value to match"
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if !["folders", "habitats"].include?(type)
|
|
182
|
+
schema["properties"]["habitat"] = MU::Config::Habitat.reference
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
if omit_fields
|
|
186
|
+
omit_fields.each { |f|
|
|
187
|
+
schema["properties"].delete(f)
|
|
188
|
+
}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
if !type.nil?
|
|
192
|
+
schema["required"] = ["type"]
|
|
193
|
+
schema["properties"]["type"]["default"] = type
|
|
194
|
+
schema["properties"]["type"]["enum"] = [type]
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
aliases.each { |a|
|
|
198
|
+
a.each_pair { |k, v|
|
|
199
|
+
if schema["properties"][v]
|
|
200
|
+
schema["properties"][k] = schema["properties"][v].dup
|
|
201
|
+
schema["properties"][k]["description"] = "Alias for <tt>#{v}</tt>"
|
|
202
|
+
else
|
|
203
|
+
MU.log "Reference schema alias #{k} wants to alias #{v}, but no such attribute exists", MU::WARN, details: caller[4]
|
|
204
|
+
end
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
schema
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Decompose into a plain-jane {MU::Config::BasketOfKittens} hash fragment,
|
|
212
|
+
# of the sort that would have been used to declare this reference in the
|
|
213
|
+
# first place.
|
|
214
|
+
def to_h
|
|
215
|
+
me = { }
|
|
216
|
+
|
|
217
|
+
self.instance_variables.each { |var|
|
|
218
|
+
next if [:@obj, :@mommacat, :@tag_key, :@tag_value].include?(var)
|
|
219
|
+
val = self.instance_variable_get(var)
|
|
220
|
+
next if val.nil?
|
|
221
|
+
val = val.to_h if val.is_a?(MU::Config::Ref)
|
|
222
|
+
me[var.to_s.sub(/^@/, '')] = val
|
|
223
|
+
}
|
|
224
|
+
if @tag_key and !@tag_key.empty?
|
|
225
|
+
me['tag'] = {
|
|
226
|
+
'key' => @tag_key,
|
|
227
|
+
'value' => @tag_value
|
|
228
|
+
}
|
|
229
|
+
end
|
|
230
|
+
me
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Getter for the #{id} instance variable that attempts to populate it if
|
|
234
|
+
# it's not set.
|
|
235
|
+
# @return [String,nil]
|
|
236
|
+
def id
|
|
237
|
+
return @id if @id
|
|
238
|
+
kitten # if it's not defined, attempt to define it
|
|
239
|
+
@id
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Alias for {id}
|
|
243
|
+
# @return [String,nil]
|
|
244
|
+
def cloud_id
|
|
245
|
+
id
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
# Return a {MU::Cloud} object for this reference. This is only meant to be
|
|
249
|
+
# called in a live deploy, which is to say that if called during initial
|
|
250
|
+
# configuration parsing, results may be incorrect.
|
|
251
|
+
# @param mommacat [MU::MommaCat]: A deploy object which will be searched for the referenced resource if provided, before restoring to broader, less efficient searches.
|
|
252
|
+
def kitten(mommacat = @mommacat, shallow: false)
|
|
253
|
+
return nil if !@cloud or !@type
|
|
254
|
+
|
|
255
|
+
if @obj
|
|
256
|
+
@deploy_id ||= @obj.deploy_id
|
|
257
|
+
@id ||= @obj.cloud_id
|
|
258
|
+
@name ||= @obj.config['name']
|
|
259
|
+
return @obj
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
if mommacat
|
|
263
|
+
@obj = mommacat.findLitterMate(type: @type, name: @name, cloud_id: @id, credentials: @credentials, debug: false)
|
|
264
|
+
if @obj # initialize missing attributes, if we can
|
|
265
|
+
@id ||= @obj.cloud_id
|
|
266
|
+
@mommacat ||= mommacat
|
|
267
|
+
@obj.intoDeploy(@mommacat) # make real sure these are set
|
|
268
|
+
@deploy_id ||= mommacat.deploy_id
|
|
269
|
+
if !@name
|
|
270
|
+
if @obj.config and @obj.config['name']
|
|
271
|
+
@name = @obj.config['name']
|
|
272
|
+
elsif @obj.mu_name
|
|
273
|
+
if @type == "folders"
|
|
274
|
+
MU.log "would assign name '#{@obj.mu_name}' in ref to this folder if I were feeling aggressive", MU::WARN, details: self.to_h
|
|
275
|
+
end
|
|
276
|
+
# @name = @obj.mu_name
|
|
277
|
+
end
|
|
278
|
+
end
|
|
279
|
+
return @obj
|
|
280
|
+
else
|
|
281
|
+
# MU.log "Failed to find a live '#{@type.to_s}' object named #{@name}#{@id ? " (#{@id})" : "" }#{ @habitat ? " in habitat #{@habitat}" : "" }", MU::WARN, details: self
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
if !@obj and !(@cloud == "Google" and @id and @type == "users" and MU::Cloud::Google::User.cannedServiceAcctName?(@id)) and !shallow
|
|
286
|
+
|
|
287
|
+
begin
|
|
288
|
+
hab_arg = if @habitat.nil?
|
|
289
|
+
[nil]
|
|
290
|
+
elsif @habitat.is_a?(MU::Config::Ref)
|
|
291
|
+
[@habitat.id]
|
|
292
|
+
elsif @habitat.is_a?(Hash)
|
|
293
|
+
[@habitat["id"]]
|
|
294
|
+
else
|
|
295
|
+
[@habitat.to_s]
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
found = MU::MommaCat.findStray(
|
|
299
|
+
@cloud,
|
|
300
|
+
@type,
|
|
301
|
+
name: @name,
|
|
302
|
+
cloud_id: @id,
|
|
303
|
+
deploy_id: @deploy_id,
|
|
304
|
+
region: @region,
|
|
305
|
+
habitats: hab_arg,
|
|
306
|
+
credentials: @credentials,
|
|
307
|
+
dummy_ok: (["habitats", "folders", "users", "groups", "vpcs"].include?(@type))
|
|
308
|
+
)
|
|
309
|
+
@obj ||= found.first if found
|
|
310
|
+
rescue ThreadError => e
|
|
311
|
+
# Sometimes MommaCat calls us in a potential deadlock situation;
|
|
312
|
+
# don't be the cause of a fatal error if so, we don't need this
|
|
313
|
+
# object that badly.
|
|
314
|
+
raise e if !e.message.match(/recursive locking/)
|
|
315
|
+
rescue SystemExit
|
|
316
|
+
# XXX this is temporary, to cope with some debug stuff that's in findStray
|
|
317
|
+
# for the nonce
|
|
318
|
+
return
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
if @obj
|
|
323
|
+
@deploy_id ||= @obj.deploy_id
|
|
324
|
+
@id ||= @obj.cloud_id
|
|
325
|
+
@name ||= @obj.config['name']
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
@obj
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|