cloud-mu 3.1.3 → 3.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,394 @@
|
|
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
|
+
# MommaCat is in charge of managing metadata about resources we've created,
|
18
|
+
# as well as orchestrating amongst them and bootstrapping nodes outside of
|
19
|
+
# the normal synchronous deploy sequence invoked by *mu-deploy*.
|
20
|
+
class MommaCat
|
21
|
+
|
22
|
+
# Check a provided deploy key against our stored version. The instance has
|
23
|
+
# in theory accessed a secret via S3 and encrypted it with the deploy's
|
24
|
+
# public key. If it decrypts correctly, we assume this instance is indeed
|
25
|
+
# one of ours.
|
26
|
+
# @param ciphertext [String]: The text to decrypt.
|
27
|
+
# return [Boolean]: Whether the provided text was encrypted with the correct key
|
28
|
+
def authKey(ciphertext)
|
29
|
+
if @private_key.nil? or @deploy_secret.nil?
|
30
|
+
MU.log "Missing auth metadata, can't authorize node in authKey", MU::ERR
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
my_key = OpenSSL::PKey::RSA.new(@private_key)
|
34
|
+
|
35
|
+
begin
|
36
|
+
if my_key.private_decrypt(ciphertext).force_encoding("UTF-8") == @deploy_secret.force_encoding("UTF-8")
|
37
|
+
MU.log "Matched ciphertext for #{MU.deploy_id}", MU::INFO
|
38
|
+
return true
|
39
|
+
else
|
40
|
+
MU.log "Mis-matched ciphertext for #{MU.deploy_id}", MU::ERR
|
41
|
+
return false
|
42
|
+
end
|
43
|
+
rescue OpenSSL::PKey::RSAError => e
|
44
|
+
MU.log "Error decrypting provided ciphertext using private key from #{deploy_dir}/private_key: #{e.message}", MU::ERR, details: ciphertext
|
45
|
+
return false
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Run {MU::Cloud::Server#postBoot} and {MU::Cloud::Server#groom} on a node.
|
50
|
+
# @param cloud_id [OpenStruct]: The cloud provider's identifier for this node.
|
51
|
+
# @param name [String]: The MU resource name of the node being created.
|
52
|
+
# @param mu_name [String]: The full #{MU::MommaCat.getResourceName} name of the server we're grooming, if it's been initialized already.
|
53
|
+
# @param type [String]: The type of resource that created this node (either *server* or *serverpool*).
|
54
|
+
def groomNode(cloud_id, name, type, mu_name: nil, reraise_fail: false, sync_wait: true)
|
55
|
+
if cloud_id.nil?
|
56
|
+
raise GroomError, "MU::MommaCat.groomNode requires a {MU::Cloud::Server} object"
|
57
|
+
end
|
58
|
+
if name.nil? or name.empty?
|
59
|
+
raise GroomError, "MU::MommaCat.groomNode requires a resource name"
|
60
|
+
end
|
61
|
+
if type.nil? or type.empty?
|
62
|
+
raise GroomError, "MU::MommaCat.groomNode requires a resource type"
|
63
|
+
end
|
64
|
+
|
65
|
+
if !MU::MommaCat.lock(cloud_id+"-mommagroom", true)
|
66
|
+
MU.log "Instance #{cloud_id} on #{MU.deploy_id} (#{type}: #{name}) is already being groomed, ignoring this extra request.", MU::NOTICE
|
67
|
+
MU::MommaCat.unlockAll
|
68
|
+
if !MU::MommaCat.locks.nil? and MU::MommaCat.locks.size > 0
|
69
|
+
puts "------------------------------"
|
70
|
+
puts "Open flock() locks:"
|
71
|
+
pp MU::MommaCat.locks
|
72
|
+
puts "------------------------------"
|
73
|
+
end
|
74
|
+
return
|
75
|
+
end
|
76
|
+
loadDeploy
|
77
|
+
|
78
|
+
# XXX this is to stop Net::SSH from killing our entire stack when it
|
79
|
+
# throws an exception. See ECAP-139 in JIRA. Far as we can tell, it's
|
80
|
+
# just not entirely thread safe.
|
81
|
+
Thread.handle_interrupt(Net::SSH::Disconnect => :never) {
|
82
|
+
begin
|
83
|
+
Thread.handle_interrupt(Net::SSH::Disconnect => :immediate) {
|
84
|
+
MU.log "(Probably harmless) Caught a Net::SSH::Disconnect in #{Thread.current.inspect}", MU::DEBUG, details: Thread.current.backtrace
|
85
|
+
}
|
86
|
+
ensure
|
87
|
+
end
|
88
|
+
}
|
89
|
+
|
90
|
+
if @original_config[type+"s"].nil?
|
91
|
+
raise GroomError, "I see no configured resources of type #{type} (bootstrap request for #{name} on #{@deploy_id})"
|
92
|
+
end
|
93
|
+
kitten = nil
|
94
|
+
|
95
|
+
kitten = findLitterMate(type: "server", name: name, mu_name: mu_name, cloud_id: cloud_id)
|
96
|
+
if !kitten.nil?
|
97
|
+
MU.log "Re-grooming #{mu_name}", details: kitten.deploydata
|
98
|
+
else
|
99
|
+
first_groom = true
|
100
|
+
@original_config[type+"s"].each { |svr|
|
101
|
+
if svr['name'] == name
|
102
|
+
svr["instance_id"] = cloud_id
|
103
|
+
|
104
|
+
# This will almost always be true in server pools, but lets be safe. Somewhat problematic because we are only
|
105
|
+
# looking at deploy_id, but we still know this is our DNS record and not a custom one.
|
106
|
+
if svr['dns_records'] && !svr['dns_records'].empty?
|
107
|
+
svr['dns_records'].each { |dnsrec|
|
108
|
+
if dnsrec.has_key?("name") && dnsrec['name'].start_with?(MU.deploy_id.downcase)
|
109
|
+
MU.log "DNS record for #{MU.deploy_id.downcase}, #{name} is probably wrong, deleting", MU::WARN, details: dnsrec
|
110
|
+
dnsrec.delete('name')
|
111
|
+
dnsrec.delete('target')
|
112
|
+
end
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
kitten = MU::Cloud::Server.new(mommacat: self, kitten_cfg: svr, cloud_id: cloud_id)
|
117
|
+
mu_name = kitten.mu_name if mu_name.nil?
|
118
|
+
MU.log "Grooming #{mu_name} for the first time", details: svr
|
119
|
+
break
|
120
|
+
end
|
121
|
+
}
|
122
|
+
end
|
123
|
+
|
124
|
+
begin
|
125
|
+
# This is a shared lock with MU::Cloud::AWS::Server.create, to keep from
|
126
|
+
# stomping on synchronous deploys that are still running. This
|
127
|
+
# means we're going to wait here if this instance is still being
|
128
|
+
# bootstrapped by "regular" means.
|
129
|
+
if !MU::MommaCat.lock(cloud_id+"-create", true)
|
130
|
+
MU.log "#{mu_name} is still in mid-creation, skipping", MU::NOTICE
|
131
|
+
MU::MommaCat.unlockAll
|
132
|
+
if !MU::MommaCat.locks.nil? and MU::MommaCat.locks.size > 0
|
133
|
+
puts "------------------------------"
|
134
|
+
puts "Open flock() locks:"
|
135
|
+
pp MU::MommaCat.locks
|
136
|
+
puts "------------------------------"
|
137
|
+
end
|
138
|
+
return
|
139
|
+
end
|
140
|
+
MU::MommaCat.unlock(cloud_id+"-create")
|
141
|
+
|
142
|
+
if !kitten.postBoot(cloud_id)
|
143
|
+
MU.log "#{mu_name} is already being groomed, skipping", MU::NOTICE
|
144
|
+
MU::MommaCat.unlockAll
|
145
|
+
if !MU::MommaCat.locks.nil? and MU::MommaCat.locks.size > 0
|
146
|
+
puts "------------------------------"
|
147
|
+
puts "Open flock() locks:"
|
148
|
+
pp MU::MommaCat.locks
|
149
|
+
puts "------------------------------"
|
150
|
+
end
|
151
|
+
return
|
152
|
+
end
|
153
|
+
|
154
|
+
# This is a shared lock with MU::Deploy.createResources, simulating the
|
155
|
+
# thread logic that tells MU::Cloud::AWS::Server.deploy to wait until
|
156
|
+
# its dependencies are ready. We don't, for example, want to start
|
157
|
+
# deploying if we rely on an RDS instance that isn't ready yet. We can
|
158
|
+
# release this immediately, once we successfully grab it.
|
159
|
+
MU::MommaCat.lock("#{kitten.cloudclass.name}_#{kitten.config["name"]}-dependencies")
|
160
|
+
MU::MommaCat.unlock("#{kitten.cloudclass.name}_#{kitten.config["name"]}-dependencies")
|
161
|
+
|
162
|
+
kitten.groom
|
163
|
+
rescue StandardError => e
|
164
|
+
MU::MommaCat.unlockAll
|
165
|
+
if e.class.name != "MU::Cloud::AWS::Server::BootstrapTempFail" and !File.exist?(deploy_dir+"/.cleanup."+cloud_id) and !File.exist?(deploy_dir+"/.cleanup")
|
166
|
+
MU.log "Grooming FAILED for #{kitten.mu_name} (#{e.inspect})", MU::ERR, details: e.backtrace
|
167
|
+
sendAdminSlack("Grooming FAILED for `#{kitten.mu_name}` with `#{e.message}` :crying_cat_face:", msg: e.backtrace.join("\n"))
|
168
|
+
sendAdminMail("Grooming FAILED for #{kitten.mu_name} on #{MU.appname} \"#{MU.handle}\" (#{MU.deploy_id})",
|
169
|
+
msg: e.inspect,
|
170
|
+
data: e.backtrace,
|
171
|
+
debug: true
|
172
|
+
)
|
173
|
+
raise e if reraise_fail
|
174
|
+
else
|
175
|
+
MU.log "Grooming of #{kitten.mu_name} interrupted by cleanup or planned reboot"
|
176
|
+
end
|
177
|
+
return
|
178
|
+
end
|
179
|
+
|
180
|
+
if !@deployment['servers'].nil? and !sync_wait
|
181
|
+
syncLitter(@deployment["servers"].keys, triggering_node: kitten)
|
182
|
+
end
|
183
|
+
MU::MommaCat.unlock(cloud_id+"-mommagroom")
|
184
|
+
if MU.myCloud == "AWS"
|
185
|
+
MU::Cloud::AWS.openFirewallForClients # XXX add the other clouds, or abstract
|
186
|
+
end
|
187
|
+
MU::MommaCat.getLitter(MU.deploy_id)
|
188
|
+
MU::Master.syncMonitoringConfig(false)
|
189
|
+
MU.log "Grooming complete for '#{name}' mu_name on \"#{MU.handle}\" (#{MU.deploy_id})"
|
190
|
+
FileUtils.touch(MU.dataDir+"/deployments/#{MU.deploy_id}/#{name}_done.txt")
|
191
|
+
MU::MommaCat.unlockAll
|
192
|
+
if first_groom
|
193
|
+
sendAdminSlack("Grooming complete for #{mu_name} :heart_eyes_cat:")
|
194
|
+
sendAdminMail("Grooming complete for '#{name}' (#{mu_name}) on deploy \"#{MU.handle}\" (#{MU.deploy_id})")
|
195
|
+
end
|
196
|
+
return
|
197
|
+
end
|
198
|
+
|
199
|
+
@cleanup_threads = []
|
200
|
+
|
201
|
+
# Iterate over all known deployments and look for instances that have been
|
202
|
+
# terminated, but not yet cleaned up, then clean them up.
|
203
|
+
def self.cleanTerminatedInstances(debug = false)
|
204
|
+
loglevel = debug ? MU::NOTICE : MU::DEBUG
|
205
|
+
MU::MommaCat.lock("clean-terminated-instances", false, true)
|
206
|
+
MU.log "Checking for harvested instances in need of cleanup", loglevel
|
207
|
+
parent_thread_id = Thread.current.object_id
|
208
|
+
purged = 0
|
209
|
+
|
210
|
+
MU::MommaCat.listDeploys.each { |deploy_id|
|
211
|
+
next if File.exist?(deploy_dir(deploy_id)+"/.cleanup")
|
212
|
+
MU.log "Checking for dead wood in #{deploy_id}", loglevel
|
213
|
+
need_reload = false
|
214
|
+
@cleanup_threads << Thread.new {
|
215
|
+
MU.dupGlobals(parent_thread_id)
|
216
|
+
deploy = MU::MommaCat.getLitter(deploy_id, set_context_to_me: true)
|
217
|
+
purged_this_deploy = 0
|
218
|
+
MU.log "#{deploy_id} has some kittens in it", loglevel, details: deploy.kittens.keys
|
219
|
+
if deploy.kittens.has_key?("servers")
|
220
|
+
MU.log "#{deploy_id} has some servers declared", loglevel, details: deploy.object_id
|
221
|
+
deploy.kittens["servers"].values.each { |nodeclasses|
|
222
|
+
nodeclasses.each_pair { |nodeclass, servers|
|
223
|
+
deletia = []
|
224
|
+
MU.log "Checking status of servers under '#{nodeclass}'", loglevel, details: servers.keys
|
225
|
+
servers.each_pair { |mu_name, server|
|
226
|
+
server.describe
|
227
|
+
if !server.cloud_id
|
228
|
+
MU.log "Checking for presence of #{mu_name}, but unable to fetch its cloud_id", MU::WARN, details: server
|
229
|
+
elsif !server.active?
|
230
|
+
next if File.exist?(deploy_dir(deploy_id)+"/.cleanup-"+server.cloud_id)
|
231
|
+
deletia << mu_name
|
232
|
+
need_reload = true
|
233
|
+
MU.log "Cleaning up metadata for #{server} (#{nodeclass}), formerly #{server.cloud_id}, which appears to have been terminated", MU::NOTICE
|
234
|
+
begin
|
235
|
+
server.destroy
|
236
|
+
deploy.sendAdminMail("Retired metadata for terminated node #{mu_name}")
|
237
|
+
deploy.sendAdminSlack("Retired metadata for terminated node `#{mu_name}`")
|
238
|
+
rescue StandardError => e
|
239
|
+
MU.log "Saw #{e.message} while retiring #{mu_name}", MU::ERR, details: e.backtrace
|
240
|
+
next
|
241
|
+
end
|
242
|
+
MU.log "Cleanup of metadata for #{server} (#{nodeclass}), formerly #{server.cloud_id} complete", MU::NOTICE
|
243
|
+
purged = purged + 1
|
244
|
+
purged_this_deploy = purged_this_deploy + 1
|
245
|
+
end
|
246
|
+
}
|
247
|
+
deletia.each { |mu_name|
|
248
|
+
servers.delete(mu_name)
|
249
|
+
}
|
250
|
+
if purged_this_deploy > 0
|
251
|
+
# XXX triggering_node needs to take more than one node name
|
252
|
+
deploy.syncLitter(servers.keys, triggering_node: deletia.first)
|
253
|
+
end
|
254
|
+
}
|
255
|
+
}
|
256
|
+
end
|
257
|
+
if need_reload
|
258
|
+
MU.log "Saving modified deploy #{deploy_id}", loglevel
|
259
|
+
deploy.save!
|
260
|
+
MU::MommaCat.getLitter(deploy_id)
|
261
|
+
end
|
262
|
+
MU.purgeGlobals
|
263
|
+
}
|
264
|
+
}
|
265
|
+
@cleanup_threads.each { |t|
|
266
|
+
t.join
|
267
|
+
}
|
268
|
+
MU.log "cleanTerminatedInstances threads complete", loglevel
|
269
|
+
MU::MommaCat.unlock("clean-terminated-instances", true)
|
270
|
+
@cleanup_threads = []
|
271
|
+
|
272
|
+
if purged > 0
|
273
|
+
if MU.myCloud == "AWS"
|
274
|
+
MU::Cloud::AWS.openFirewallForClients # XXX add the other clouds, or abstract
|
275
|
+
end
|
276
|
+
MU::Master.syncMonitoringConfig
|
277
|
+
GC.start
|
278
|
+
end
|
279
|
+
MU.log "cleanTerminatedInstances returning", loglevel
|
280
|
+
end
|
281
|
+
|
282
|
+
# Path to the log file used by the Momma Cat daemon
|
283
|
+
# @return [String]
|
284
|
+
def self.daemonLogFile
|
285
|
+
base = (Process.uid == 0 and !MU.localOnly) ? "/var" : MU.dataDir
|
286
|
+
"#{base}/log/mu-momma-cat.log"
|
287
|
+
end
|
288
|
+
|
289
|
+
# Path to the PID file used by the Momma Cat daemon
|
290
|
+
# @return [String]
|
291
|
+
def self.daemonPidFile
|
292
|
+
base = (Process.uid == 0 and !MU.localOnly) ? "/var" : MU.dataDir
|
293
|
+
"#{base}/run/mommacat.pid"
|
294
|
+
end
|
295
|
+
|
296
|
+
# Start the Momma Cat daemon and return the exit status of the command used
|
297
|
+
# @return [Integer]
|
298
|
+
def self.start
|
299
|
+
if MU.inGem? and MU.muCfg['disable_mommacat']
|
300
|
+
return
|
301
|
+
end
|
302
|
+
base = (Process.uid == 0 and !MU.localOnly) ? "/var" : MU.dataDir
|
303
|
+
[base, "#{base}/log", "#{base}/run"].each { |dir|
|
304
|
+
if !Dir.exist?(dir)
|
305
|
+
MU.log "Creating #{dir}"
|
306
|
+
Dir.mkdir(dir)
|
307
|
+
end
|
308
|
+
}
|
309
|
+
return 0 if status
|
310
|
+
|
311
|
+
MU.log "Starting Momma Cat on port #{MU.mommaCatPort}, logging to #{daemonLogFile}, PID file #{daemonPidFile}"
|
312
|
+
origdir = Dir.getwd
|
313
|
+
Dir.chdir(MU.myRoot+"/modules")
|
314
|
+
|
315
|
+
# XXX what's the safest way to find the 'bundle' executable in both gem and non-gem installs?
|
316
|
+
if MU.inGem?
|
317
|
+
cmd = %Q{thin --threaded --daemonize --port #{MU.mommaCatPort} --pid #{daemonPidFile} --log #{daemonLogFile} --ssl --ssl-key-file #{MU.muCfg['ssl']['key']} --ssl-cert-file #{MU.muCfg['ssl']['cert']} --ssl-disable-verify --tag mu-momma-cat -R mommacat.ru start}
|
318
|
+
else
|
319
|
+
cmd = %Q{bundle exec thin --threaded --daemonize --port #{MU.mommaCatPort} --pid #{daemonPidFile} --log #{daemonLogFile} --ssl --ssl-key-file #{MU.muCfg['ssl']['key']} --ssl-cert-file #{MU.muCfg['ssl']['cert']} --ssl-disable-verify --tag mu-momma-cat -R mommacat.ru start}
|
320
|
+
end
|
321
|
+
|
322
|
+
MU.log cmd, MU::NOTICE
|
323
|
+
|
324
|
+
retries = 0
|
325
|
+
begin
|
326
|
+
output = %x{#{cmd}}
|
327
|
+
sleep 1
|
328
|
+
retries += 1
|
329
|
+
if retries >= 10
|
330
|
+
MU.log "MommaCat failed to start (command was #{cmd}, working directory #{MU.myRoot}/modules)", MU::WARN, details: output
|
331
|
+
pp caller
|
332
|
+
return $?.exitstatus
|
333
|
+
end
|
334
|
+
end while !status
|
335
|
+
|
336
|
+
Dir.chdir(origdir)
|
337
|
+
|
338
|
+
if $?.exitstatus != 0
|
339
|
+
exit 1
|
340
|
+
end
|
341
|
+
|
342
|
+
return $?.exitstatus
|
343
|
+
end
|
344
|
+
|
345
|
+
# Return true if the Momma Cat daemon appears to be running
|
346
|
+
# @return [Boolean]
|
347
|
+
def self.status
|
348
|
+
if MU.inGem? and MU.muCfg['disable_mommacat']
|
349
|
+
return true
|
350
|
+
end
|
351
|
+
if File.exist?(daemonPidFile)
|
352
|
+
pid = File.read(daemonPidFile).chomp.to_i
|
353
|
+
begin
|
354
|
+
Process.getpgid(pid)
|
355
|
+
MU.log "Momma Cat running with pid #{pid.to_s}"
|
356
|
+
return true
|
357
|
+
rescue Errno::ESRCH
|
358
|
+
end
|
359
|
+
end
|
360
|
+
MU.log "Momma Cat daemon not running", MU::NOTICE, details: daemonPidFile
|
361
|
+
false
|
362
|
+
end
|
363
|
+
|
364
|
+
# Stop the Momma Cat daemon, if it's running
|
365
|
+
def self.stop
|
366
|
+
if File.exist?(daemonPidFile)
|
367
|
+
pid = File.read(daemonPidFile).chomp.to_i
|
368
|
+
MU.log "Stopping Momma Cat with pid #{pid.to_s}"
|
369
|
+
Process.kill("INT", pid)
|
370
|
+
killed = false
|
371
|
+
begin
|
372
|
+
Process.getpgid(pid)
|
373
|
+
sleep 1
|
374
|
+
rescue Errno::ESRCH
|
375
|
+
killed = true
|
376
|
+
end while killed
|
377
|
+
MU.log "Momma Cat with pid #{pid.to_s} stopped", MU::DEBUG, details: daemonPidFile
|
378
|
+
|
379
|
+
begin
|
380
|
+
File.unlink(daemonPidFile)
|
381
|
+
rescue Errno::ENOENT
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# (Re)start the Momma Cat daemon and return the exit status of the start command
|
387
|
+
# @return [Integer]
|
388
|
+
def self.restart
|
389
|
+
stop
|
390
|
+
start
|
391
|
+
end
|
392
|
+
|
393
|
+
end #class
|
394
|
+
end #module
|
@@ -0,0 +1,366 @@
|
|
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
|
+
# MommaCat is in charge of managing metadata about resources we've created,
|
18
|
+
# as well as orchestrating amongst them and bootstrapping nodes outside of
|
19
|
+
# the normal synchronous deploy sequence invoked by *mu-deploy*.
|
20
|
+
class MommaCat
|
21
|
+
|
22
|
+
# Generate a three-character string which can be used to unique-ify the
|
23
|
+
# names of resources which might potentially collide, e.g. Windows local
|
24
|
+
# hostnames, Amazon Elastic Load Balancers, or server pool instances.
|
25
|
+
# @return [String]: A three-character string consisting of two alphnumeric
|
26
|
+
# characters (uppercase) and one number.
|
27
|
+
def self.genUniquenessString
|
28
|
+
begin
|
29
|
+
candidate = SecureRandom.base64(2).slice(0..1) + SecureRandom.random_number(9).to_s
|
30
|
+
candidate.upcase!
|
31
|
+
end while candidate.match(/[^A-Z0-9]/)
|
32
|
+
return candidate
|
33
|
+
end
|
34
|
+
|
35
|
+
@unique_map_semaphore = Mutex.new
|
36
|
+
@name_unique_str_map = {}
|
37
|
+
# Keep a map of the uniqueness strings we assign to various full names, in
|
38
|
+
# case we want to reuse them later.
|
39
|
+
# @return [Hash<String>]
|
40
|
+
def self.name_unique_str_map
|
41
|
+
@name_unique_str_map
|
42
|
+
end
|
43
|
+
|
44
|
+
# Keep a map of the uniqueness strings we assign to various full names, in
|
45
|
+
# case we want to reuse them later.
|
46
|
+
# @return [Mutex]
|
47
|
+
def self.unique_map_semaphore
|
48
|
+
@unique_map_semaphore
|
49
|
+
end
|
50
|
+
|
51
|
+
# Generate a name string for a resource, incorporate the MU identifier
|
52
|
+
# for this deployment. Will dynamically shorten the name to fit for
|
53
|
+
# restrictive uses (e.g. Windows local hostnames, Amazon Elastic Load
|
54
|
+
# Balancers).
|
55
|
+
# @param name [String]: The shorthand name of the resource, usually the value of the "name" field in an Mu resource declaration.
|
56
|
+
# @param max_length [Integer]: The maximum length of the resulting resource name.
|
57
|
+
# @param need_unique_string [Boolean]: Whether to forcibly append a random three-character string to the name to ensure it's unique. Note that this behavior will be automatically invoked if the name must be truncated.
|
58
|
+
# @param scrub_mu_isms [Boolean]: Don't bother with generating names specific to this deployment. Used to generate generic CloudFormation templates, amongst other purposes.
|
59
|
+
# @param disallowed_chars [Regexp]: A pattern of characters that are illegal for this resource name, such as +/[^a-zA-Z0-9-]/+
|
60
|
+
# @return [String]: A full name string for this resource
|
61
|
+
def getResourceName(name, max_length: 255, need_unique_string: false, use_unique_string: nil, reuse_unique_string: false, scrub_mu_isms: @original_config['scrub_mu_isms'], disallowed_chars: nil)
|
62
|
+
if name.nil?
|
63
|
+
raise MuError, "Got no argument to MU::MommaCat.getResourceName"
|
64
|
+
end
|
65
|
+
if @appname.nil? or @environment.nil? or @timestamp.nil? or @seed.nil?
|
66
|
+
MU.log "getResourceName: Missing global deploy variables in thread #{Thread.current.object_id}, using bare name '#{name}' (appname: #{@appname}, environment: #{@environment}, timestamp: #{@timestamp}, seed: #{@seed}, deploy_id: #{@deploy_id}", MU::WARN, details: caller
|
67
|
+
return name
|
68
|
+
end
|
69
|
+
need_unique_string = false if scrub_mu_isms
|
70
|
+
|
71
|
+
muname = nil
|
72
|
+
if need_unique_string
|
73
|
+
reserved = 4
|
74
|
+
else
|
75
|
+
reserved = 0
|
76
|
+
end
|
77
|
+
|
78
|
+
# First, pare down the base name string until it will fit
|
79
|
+
basename = @appname.upcase + "-" + @environment.upcase + "-" + @timestamp + "-" + @seed.upcase + "-" + name.upcase
|
80
|
+
if scrub_mu_isms
|
81
|
+
basename = @appname.upcase + "-" + @environment.upcase + name.upcase
|
82
|
+
end
|
83
|
+
|
84
|
+
subchar = if disallowed_chars
|
85
|
+
if "-".match(disallowed_chars)
|
86
|
+
if !"_".match(disallowed_chars)
|
87
|
+
"_"
|
88
|
+
else
|
89
|
+
""
|
90
|
+
end
|
91
|
+
else
|
92
|
+
"-"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if disallowed_chars
|
97
|
+
basename.gsub!(disallowed_chars, subchar) if disallowed_chars
|
98
|
+
end
|
99
|
+
attempts = 0
|
100
|
+
begin
|
101
|
+
if (basename.length + reserved) > max_length
|
102
|
+
MU.log "Stripping name down from #{basename}[#{basename.length.to_s}] (reserved: #{reserved.to_s}, max_length: #{max_length.to_s})", MU::DEBUG
|
103
|
+
if basename == @appname.upcase + "-" + @seed.upcase + "-" + name.upcase
|
104
|
+
# If we've run out of stuff to strip, truncate what's left and
|
105
|
+
# just leave room for the deploy seed and uniqueness string. This
|
106
|
+
# is the bare minimum, and probably what you'll see for most Windows
|
107
|
+
# hostnames.
|
108
|
+
basename = name.upcase + "-" + @appname.upcase
|
109
|
+
basename.slice!((max_length-(reserved+3))..basename.length)
|
110
|
+
basename.sub!(/-$/, "")
|
111
|
+
basename = basename + "-" + @seed.upcase
|
112
|
+
basename.gsub!(disallowed_chars, subchar) if disallowed_chars
|
113
|
+
else
|
114
|
+
# If we have to strip anything, assume we've lost uniqueness and
|
115
|
+
# will have to compensate with #genUniquenessString.
|
116
|
+
need_unique_string = true
|
117
|
+
reserved = 4
|
118
|
+
basename.sub!(/-[^-]+-#{@seed.upcase}-#{Regexp.escape(name.upcase)}$/, "")
|
119
|
+
basename = basename + "-" + @seed.upcase + "-" + name.upcase
|
120
|
+
basename.gsub!(disallowed_chars, subchar) if disallowed_chars
|
121
|
+
end
|
122
|
+
end
|
123
|
+
attempts += 1
|
124
|
+
raise MuError, "Failed to generate a reasonable name getResourceName(#{name}, max_length: #{max_length.to_s}, need_unique_string: #{need_unique_string.to_s}, use_unique_string: #{use_unique_string.to_s}, reuse_unique_string: #{reuse_unique_string.to_s}, scrub_mu_isms: #{scrub_mu_isms.to_s}, disallowed_chars: #{disallowed_chars})" if attempts > 10
|
125
|
+
end while (basename.length + reserved) > max_length
|
126
|
+
|
127
|
+
# Finally, apply our short random differentiator, if it's needed.
|
128
|
+
if need_unique_string
|
129
|
+
# Preferentially use a requested one, if it's not already in use.
|
130
|
+
if !use_unique_string.nil?
|
131
|
+
muname = basename + "-" + use_unique_string
|
132
|
+
if !allocateUniqueResourceName(muname) and !reuse_unique_string
|
133
|
+
MU.log "Requested to use #{use_unique_string} as differentiator when naming #{name}, but the name #{muname} is unavailable.", MU::WARN
|
134
|
+
muname = nil
|
135
|
+
end
|
136
|
+
end
|
137
|
+
if !muname
|
138
|
+
begin
|
139
|
+
unique_string = MU::MommaCat.genUniquenessString
|
140
|
+
muname = basename + "-" + unique_string
|
141
|
+
end while !allocateUniqueResourceName(muname)
|
142
|
+
MU::MommaCat.unique_map_semaphore.synchronize {
|
143
|
+
MU::MommaCat.name_unique_str_map[muname] = unique_string
|
144
|
+
}
|
145
|
+
end
|
146
|
+
else
|
147
|
+
muname = basename
|
148
|
+
end
|
149
|
+
muname.gsub!(disallowed_chars, subchar) if disallowed_chars
|
150
|
+
|
151
|
+
return muname
|
152
|
+
end
|
153
|
+
|
154
|
+
# List the name/value pairs for our mandatory standard set of resource tags, which
|
155
|
+
# should be applied to all taggable cloud provider resources.
|
156
|
+
# @return [Hash<String,String>]
|
157
|
+
def self.listStandardTags
|
158
|
+
return {} if !MU.deploy_id
|
159
|
+
{
|
160
|
+
"MU-ID" => MU.deploy_id,
|
161
|
+
"MU-APP" => MU.appname,
|
162
|
+
"MU-ENV" => MU.environment,
|
163
|
+
"MU-MASTER-IP" => MU.mu_public_ip
|
164
|
+
}
|
165
|
+
end
|
166
|
+
# List the name/value pairs for our mandatory standard set of resource tags
|
167
|
+
# for this deploy.
|
168
|
+
# @return [Hash<String,String>]
|
169
|
+
def listStandardTags
|
170
|
+
{
|
171
|
+
"MU-ID" => @deploy_id,
|
172
|
+
"MU-APP" => @appname,
|
173
|
+
"MU-ENV" => @environment,
|
174
|
+
"MU-MASTER-IP" => MU.mu_public_ip
|
175
|
+
}
|
176
|
+
end
|
177
|
+
|
178
|
+
# List the name/value pairs of our optional set of resource tags which
|
179
|
+
# should be applied to all taggable cloud provider resources.
|
180
|
+
# @return [Hash<String,String>]
|
181
|
+
def self.listOptionalTags
|
182
|
+
return {
|
183
|
+
"MU-HANDLE" => MU.handle,
|
184
|
+
"MU-MASTER-NAME" => Socket.gethostname,
|
185
|
+
"MU-OWNER" => MU.mu_user
|
186
|
+
}
|
187
|
+
end
|
188
|
+
|
189
|
+
# Make sure the given node has proper DNS entries, /etc/hosts entries,
|
190
|
+
# SSH config entries, etc.
|
191
|
+
# @param server [MU::Cloud::Server]: The {MU::Cloud::Server} we'll be setting up.
|
192
|
+
# @param sync_wait [Boolean]: Whether to wait for DNS to fully synchronize before returning.
|
193
|
+
def self.nameKitten(server, sync_wait: false)
|
194
|
+
node, config, _deploydata = server.describe
|
195
|
+
|
196
|
+
mu_zone = nil
|
197
|
+
# XXX GCP!
|
198
|
+
if MU::Cloud::AWS.hosted? and !MU::Cloud::AWS.isGovCloud?
|
199
|
+
zones = MU::Cloud::DNSZone.find(cloud_id: "platform-mu")
|
200
|
+
mu_zone = zones.values.first if !zones.nil?
|
201
|
+
end
|
202
|
+
if !mu_zone.nil?
|
203
|
+
MU::Cloud::DNSZone.genericMuDNSEntry(name: node, target: server.canonicalIP, cloudclass: MU::Cloud::Server, sync_wait: sync_wait)
|
204
|
+
else
|
205
|
+
MU::Master.addInstanceToEtcHosts(server.canonicalIP, node)
|
206
|
+
end
|
207
|
+
|
208
|
+
## TO DO: Do DNS registration of "real" records as the last stage after the groomer completes
|
209
|
+
if config && config['dns_records'] && !config['dns_records'].empty?
|
210
|
+
dnscfg = config['dns_records'].dup
|
211
|
+
dnscfg.each { |dnsrec|
|
212
|
+
if !dnsrec.has_key?('name')
|
213
|
+
dnsrec['name'] = node.downcase
|
214
|
+
dnsrec['name'] = "#{dnsrec['name']}.#{MU.environment.downcase}" if dnsrec["append_environment_name"] && !dnsrec['name'].match(/\.#{MU.environment.downcase}$/)
|
215
|
+
end
|
216
|
+
|
217
|
+
if !dnsrec.has_key?("target")
|
218
|
+
# Default to register public endpoint
|
219
|
+
public = true
|
220
|
+
|
221
|
+
if dnsrec.has_key?("target_type")
|
222
|
+
# See if we have a preference for pubic/private endpoint
|
223
|
+
public = dnsrec["target_type"] == "private" ? false : true
|
224
|
+
end
|
225
|
+
|
226
|
+
dnsrec["target"] =
|
227
|
+
if dnsrec["type"] == "CNAME"
|
228
|
+
if public
|
229
|
+
# Make sure we have a public canonical name to register. Use the private one if we don't
|
230
|
+
server.cloud_desc.public_dns_name.empty? ? server.cloud_desc.private_dns_name : server.cloud_desc.public_dns_name
|
231
|
+
else
|
232
|
+
# If we specifically requested to register the private canonical name lets use that
|
233
|
+
server.cloud_desc.private_dns_name
|
234
|
+
end
|
235
|
+
elsif dnsrec["type"] == "A"
|
236
|
+
if public
|
237
|
+
# Make sure we have a public IP address to register. Use the private one if we don't
|
238
|
+
server.cloud_desc.public_ip_address ? server.cloud_desc.public_ip_address : server.cloud_desc.private_ip_address
|
239
|
+
else
|
240
|
+
# If we specifically requested to register the private IP lets use that
|
241
|
+
server.cloud_desc.private_ip_address
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
}
|
246
|
+
if !MU::Cloud::AWS.isGovCloud?
|
247
|
+
MU::Cloud::DNSZone.createRecordsFromConfig(dnscfg)
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
MU::Master.removeHostFromSSHConfig(node)
|
252
|
+
if server and server.canonicalIP
|
253
|
+
MU::Master.removeIPFromSSHKnownHosts(server.canonicalIP)
|
254
|
+
end
|
255
|
+
# XXX add names paramater with useful stuff
|
256
|
+
MU::Master.addHostToSSHConfig(
|
257
|
+
server,
|
258
|
+
ssh_owner: server.deploy.mu_user,
|
259
|
+
ssh_dir: Etc.getpwnam(server.deploy.mu_user).dir+"/.ssh"
|
260
|
+
)
|
261
|
+
end
|
262
|
+
|
263
|
+
# Manufactures a human-readable deployment name from the random
|
264
|
+
# two-character seed in MU-ID. Cat-themed when possible.
|
265
|
+
# @param seed [String]: A two-character seed from which we'll generate a name.
|
266
|
+
# @return [String]: Two words
|
267
|
+
def self.generateHandle(seed)
|
268
|
+
word_one=word_two=nil
|
269
|
+
|
270
|
+
# Unless we've got two letters that don't have corresponding cat-themed
|
271
|
+
# words, we'll insist that our generated handle have at least one cat
|
272
|
+
# element to it.
|
273
|
+
require_cat_words = true
|
274
|
+
if @catwords.select { |word| word.match(/^#{seed[0]}/i) }.size == 0 and
|
275
|
+
@catwords.select { |word| word.match(/^#{seed[1]}/i) }.size == 0
|
276
|
+
require_cat_words = false
|
277
|
+
MU.log "Got an annoying pair of letters #{seed}, not forcing cat-theming", MU::DEBUG
|
278
|
+
end
|
279
|
+
allnouns = @catnouns + @jaegernouns
|
280
|
+
alladjs = @catadjs + @jaegeradjs
|
281
|
+
|
282
|
+
tries = 0
|
283
|
+
begin
|
284
|
+
# Try to avoid picking something "nouny" for the first word
|
285
|
+
source = @catadjs + @catmixed + @jaegeradjs + @jaegermixed
|
286
|
+
first_ltr = source.select { |word| word.match(/^#{seed[0]}/i) }
|
287
|
+
if !first_ltr or first_ltr.size == 0
|
288
|
+
first_ltr = @words.select { |word| word.match(/^#{seed[0]}/i) }
|
289
|
+
end
|
290
|
+
word_one = first_ltr.shuffle.first
|
291
|
+
|
292
|
+
# If we got a paired set that happen to match our letters, go with it
|
293
|
+
if !word_one.nil? and word_one.match(/-#{seed[1]}/i)
|
294
|
+
word_one, word_two = word_one.split(/-/)
|
295
|
+
else
|
296
|
+
source = @words
|
297
|
+
if @catwords.include?(word_one)
|
298
|
+
source = @jaegerwords
|
299
|
+
elsif require_cat_words
|
300
|
+
source = @catwords
|
301
|
+
end
|
302
|
+
second_ltr = source.select { |word| word.match(/^#{seed[1]}/i) and !word.match(/-/i) }
|
303
|
+
word_two = second_ltr.shuffle.first
|
304
|
+
end
|
305
|
+
tries = tries + 1
|
306
|
+
end while tries < 50 and (word_one.nil? or word_two.nil? or word_one.match(/-/) or word_one == word_two or (allnouns.include?(word_one) and allnouns.include?(word_two)) or (alladjs.include?(word_one) and alladjs.include?(word_two)) or (require_cat_words and !@catwords.include?(word_one) and !@catwords.include?(word_two) and !@catwords.include?(word_one+"-"+word_two)))
|
307
|
+
|
308
|
+
if tries >= 50 and (word_one.nil? or word_two.nil?)
|
309
|
+
MU.log "I failed to generated a valid handle from #{seed}, faking it", MU::ERR
|
310
|
+
return "#{seed[0].capitalize} #{seed[1].capitalize}"
|
311
|
+
end
|
312
|
+
|
313
|
+
return "#{word_one.capitalize} #{word_two.capitalize}"
|
314
|
+
end
|
315
|
+
|
316
|
+
private
|
317
|
+
|
318
|
+
# Check to see whether a given resource name is unique across all
|
319
|
+
# deployments on this Mu server. We only enforce this for certain classes
|
320
|
+
# of names. If the name in question is available, add it to our cache of
|
321
|
+
# said names. See #{MU::MommaCat.getResourceName}
|
322
|
+
# @param name [String]: The name to attempt to allocate.
|
323
|
+
# @return [Boolean]: True if allocation was successful.
|
324
|
+
def allocateUniqueResourceName(name)
|
325
|
+
raise MuError, "Cannot call allocateUniqueResourceName without an active deployment" if @deploy_id.nil?
|
326
|
+
path = File.expand_path(MU.dataDir+"/deployments")
|
327
|
+
File.open(path+"/unique_ids", File::CREAT|File::RDWR, 0600) { |f|
|
328
|
+
existing = []
|
329
|
+
f.flock(File::LOCK_EX)
|
330
|
+
f.readlines.each { |line|
|
331
|
+
existing << line.chomp
|
332
|
+
}
|
333
|
+
begin
|
334
|
+
existing.each { |used|
|
335
|
+
if used.match(/^#{name}:/)
|
336
|
+
if !used.match(/^#{name}:#{@deploy_id}$/)
|
337
|
+
MU.log "#{name} is already reserved by another resource on this Mu server.", MU::WARN, details: caller
|
338
|
+
return false
|
339
|
+
else
|
340
|
+
return true
|
341
|
+
end
|
342
|
+
end
|
343
|
+
}
|
344
|
+
f.puts name+":"+@deploy_id
|
345
|
+
return true
|
346
|
+
ensure
|
347
|
+
f.flock(File::LOCK_UN)
|
348
|
+
end
|
349
|
+
}
|
350
|
+
end
|
351
|
+
|
352
|
+
# 2019-06-03 adding things from https://aiweirdness.com/post/185339301987/once-again-a-neural-net-tries-to-name-cats
|
353
|
+
@catadjs = %w{fuzzy ginger lilac chocolate xanthic wiggly itty chonky norty slonky floofy heckin bebby}
|
354
|
+
@catnouns = %w{bastet biscuits bobcat catnip cheetah chonk dot felix hamb hambina jaguar kitty leopard lion lynx maru mittens moggy neko nip ocelot panther patches paws phoebe purr queen roar saber sekhmet skogkatt socks sphinx spot tail tiger tom whiskers wildcat yowl floof beans ailurophile dander dewclaw grimalkin kibble quick tuft misty simba slonk mew quat eek ziggy whiskeridoo cromch monch screm}
|
355
|
+
@catmixed = %w{abyssinian angora bengal birman bobtail bombay burmese calico chartreux cheshire cornish-rex curl devon egyptian-mau feline furever fumbs havana himilayan japanese-bobtail javanese khao-manee maine-coon manx marmalade mau munchkin norwegian pallas persian peterbald polydactyl ragdoll russian-blue savannah scottish-fold serengeti shorthair siamese siberian singapura snowshoe stray tabby tonkinese tortoiseshell turkish-van tuxedo uncia caterwaul lilac-point chocolate-point mackerel maltese knead whitenose vorpal chewie-bean chicken-whiskey fish-especially thelonious-monsieur tom-glitter serendipitous-kill sparky-buttons nip-nops murder-mittens bite}
|
356
|
+
@catwords = @catadjs + @catnouns + @catmixed
|
357
|
+
|
358
|
+
@jaegeradjs = %w{azure fearless lucky olive vivid electric grey yarely violet ivory jade cinnamon crimson tacit umber mammoth ultra iron zodiac}
|
359
|
+
@jaegernouns = %w{horizon hulk ultimatum yardarm watchman whilrwind wright rhythm ocean enigma eruption typhoon jaeger brawler blaze vandal excalibur paladin juliet kaleidoscope romeo}
|
360
|
+
@jaegermixed = %w{alpha ajax amber avenger brave bravo charlie chocolate chrome corinthian dancer danger dash delta duet echo edge elite eureka foxtrot guardian gold hyperion illusion imperative india intercept kilo lancer night nova november oscar omega pacer quickstrike rogue ronin striker tango titan valor victor vulcan warder xenomorph xenon xray xylem yankee yell yukon zeal zero zoner zodiac}
|
361
|
+
@jaegerwords = @jaegeradjs + @jaegernouns + @jaegermixed
|
362
|
+
|
363
|
+
@words = @catwords + @jaegerwords
|
364
|
+
|
365
|
+
end #class
|
366
|
+
end #module
|