razorrisk-cassini-utilities-cassid 0.8.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +5 -0
- data/README.md +2 -0
- data/bin/cassid +279 -0
- data/bin/razorrisk-service-cassid +90 -0
- data/lib/razor_risk/cassini/utilities/cassid/configuration.rb +367 -0
- data/lib/razor_risk/cassini/utilities/cassid/constants.rb +48 -0
- data/lib/razor_risk/cassini/utilities/cassid/main.rb +643 -0
- data/lib/razor_risk/cassini/utilities/cassid/service_controller.rb +149 -0
- data/lib/razor_risk/cassini/utilities/cassid/validations.rb +460 -0
- data/lib/razor_risk/cassini/utilities/cassid/version.rb +54 -0
- data/lib/razor_risk/cassini/utilities/cassid.rb +11 -0
- metadata +210 -0
@@ -0,0 +1,643 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
|
4
|
+
# ##########################################################################
|
5
|
+
#
|
6
|
+
# Copyright (c) 2019 Razor Risk Technologies Pty Limited. All rights reserved.
|
7
|
+
#
|
8
|
+
# ##########################################################################
|
9
|
+
|
10
|
+
# ##########################################################
|
11
|
+
# requires
|
12
|
+
|
13
|
+
require 'razor_risk/cassini/cli'
|
14
|
+
require 'razor_risk/cassini/diagnostics/util_functions'
|
15
|
+
require 'razor_risk/cassini/util/program_execution_util'
|
16
|
+
require 'razor_risk/cassini/utilities/cassid/validations'
|
17
|
+
require 'razor_risk/core/diagnostics/logger'
|
18
|
+
|
19
|
+
require 'pantheios'
|
20
|
+
require 'nokogiri'
|
21
|
+
|
22
|
+
require 'base64'
|
23
|
+
require 'yaml'
|
24
|
+
|
25
|
+
|
26
|
+
# ##########################################################
|
27
|
+
# includes
|
28
|
+
|
29
|
+
include ::RazorRisk::Cassini::Utilities::CassiD::Validations
|
30
|
+
include ::RazorRisk::Cassini::Util::ProgramExecutionUtil
|
31
|
+
|
32
|
+
|
33
|
+
# ##########################################################
|
34
|
+
# modules
|
35
|
+
|
36
|
+
module RazorRisk
|
37
|
+
module Cassini
|
38
|
+
module Utilities
|
39
|
+
module CassiD
|
40
|
+
module Main
|
41
|
+
|
42
|
+
|
43
|
+
# ##########################################################
|
44
|
+
# includes
|
45
|
+
|
46
|
+
include ::Pantheios
|
47
|
+
include ::RazorRisk::Core::Diagnostics::Logger
|
48
|
+
|
49
|
+
|
50
|
+
# ##########################################################
|
51
|
+
# types
|
52
|
+
|
53
|
+
class CassiDException < ::StandardError; end
|
54
|
+
|
55
|
+
|
56
|
+
# ##########################################################
|
57
|
+
# constants
|
58
|
+
|
59
|
+
DEFAULT_LOG_DIRECTORY = File.join(Dir.pwd, 'logs')
|
60
|
+
DEFAULT_LOG_THRESHOLD = [ :informational, :informational ]
|
61
|
+
|
62
|
+
DEFAULT_CASSID_WAIT = 10
|
63
|
+
DEFAULT_SS_WAIT = 4
|
64
|
+
|
65
|
+
VALID_PORTS = (1024...60000)
|
66
|
+
|
67
|
+
ERROR_EXCEPTIONS = [ ::ArgumentError, ::NameError, ::NoMethodError, ::TypeError ]
|
68
|
+
|
69
|
+
|
70
|
+
# ##########################################################################
|
71
|
+
# functions
|
72
|
+
|
73
|
+
$children = []
|
74
|
+
|
75
|
+
def self.combine_uri a0, a1
|
76
|
+
|
77
|
+
trace ParamNames[ :a0, :a1 ], a0, a1
|
78
|
+
|
79
|
+
a0 = a0[0...-1] if '/' == a0[-1]
|
80
|
+
a1 = a1[1..-1] if '/' == a1[0]
|
81
|
+
|
82
|
+
"#{a0}/#{a1}"
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.kill_children
|
86
|
+
|
87
|
+
log :informational, "Killing child processes..."
|
88
|
+
|
89
|
+
$children.each do |c|
|
90
|
+
begin
|
91
|
+
Process.kill('KILL', c.pid)
|
92
|
+
rescue Errno::ESRCH
|
93
|
+
rescue
|
94
|
+
log :informational, "Failed to kill child process #{c.pid}"
|
95
|
+
else
|
96
|
+
log :informational, "Killed child process #{c.pid}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.process_cli *args
|
102
|
+
|
103
|
+
options = {
|
104
|
+
max_restarts: 0,
|
105
|
+
log_threshold: []
|
106
|
+
}
|
107
|
+
|
108
|
+
climate = LibCLImate::Climate.new do |cl|
|
109
|
+
|
110
|
+
cl.add_option(
|
111
|
+
'--microservice-multiplier',
|
112
|
+
alias: '-m',
|
113
|
+
help: "overrides the 'us_multiplier' configuration setting to the given number. Must be greater than 0"
|
114
|
+
) do |o, a|
|
115
|
+
|
116
|
+
options[:us_multiplier] = Integer(o.value, nil: true)
|
117
|
+
unless options[:us_multiplier] and options[:us_multiplier] > 0
|
118
|
+
raise CassiDException.new("invalid value for microservice multiplier; use --help for usage")
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
cl.add_flag(
|
123
|
+
'--monitor-startup',
|
124
|
+
help: 'monitors child process statuses during initialisation'
|
125
|
+
) do
|
126
|
+
options[:monitor_startup] = true
|
127
|
+
end
|
128
|
+
|
129
|
+
cl.add_flag(
|
130
|
+
'--pedantic',
|
131
|
+
help: 'conducts checks that the configuration is correct'
|
132
|
+
) do
|
133
|
+
options[:pedantic] = true
|
134
|
+
end
|
135
|
+
|
136
|
+
cl.add_option(
|
137
|
+
'--max-restarts',
|
138
|
+
help: 'maximum number of times to restart a microservice'
|
139
|
+
) do |o, a|
|
140
|
+
unless (val = Integer(o.value, nil: true)) < 0
|
141
|
+
options[:max_restarts] = val
|
142
|
+
else
|
143
|
+
raise CassiDException.new("max retries must be 0 or a positive integer; use --help for usage")
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Only added here as it may get passed through from cassid
|
148
|
+
cl.add_option('--exit-listener-port', alias: '-e',)
|
149
|
+
|
150
|
+
cl.add_option(
|
151
|
+
'--ruby-path',
|
152
|
+
alias: '-m',
|
153
|
+
help: 'absolute path to the ruby executable'
|
154
|
+
) do |o, a|
|
155
|
+
options[:ruby_path] = o.value
|
156
|
+
end
|
157
|
+
|
158
|
+
cl.option_log_threshold(
|
159
|
+
options,
|
160
|
+
no_program_name: true,
|
161
|
+
no_benchmark: true,
|
162
|
+
limit: -2
|
163
|
+
)
|
164
|
+
|
165
|
+
cl.usage_values = '<config-yaml-file>'
|
166
|
+
|
167
|
+
cl.info_lines = [
|
168
|
+
|
169
|
+
'Startup Daemon for Cassini',
|
170
|
+
::RazorRisk::Cassini::CLI.Copyright(2018),
|
171
|
+
:version,
|
172
|
+
'',
|
173
|
+
'Executes a Cassini instance based on a configuration file',
|
174
|
+
'',
|
175
|
+
]
|
176
|
+
end
|
177
|
+
|
178
|
+
r = climate.run(args)
|
179
|
+
|
180
|
+
unless r.values[0]
|
181
|
+
raise CassiDException.new(
|
182
|
+
'configuration file not specified; use --help for usage'
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
config_path = File.expand_path(r.values[0])
|
187
|
+
|
188
|
+
[ config_path, options ]
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.init_logging program_name, log_directory, log_threshold
|
192
|
+
|
193
|
+
::Pantheios::Core.program_name = program_name if program_name
|
194
|
+
log_directory = log_directory || DEFAULT_LOG_DIRECTORY
|
195
|
+
log_threshold = log_threshold || DEFAULT_LOG_THRESHOLD
|
196
|
+
|
197
|
+
setup_diagnostic_logging(
|
198
|
+
::Pantheios::Core.program_name,
|
199
|
+
log_directory,
|
200
|
+
log_threshold,
|
201
|
+
no_benchmark_log: true
|
202
|
+
)
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.load_config config_path
|
206
|
+
|
207
|
+
trace ParamNames[ :config_path ], config_path
|
208
|
+
|
209
|
+
config = nil
|
210
|
+
|
211
|
+
begin
|
212
|
+
raise CassiDException.new(
|
213
|
+
"configuration file '#{config_path}' does not exist or is not a file"
|
214
|
+
) unless File.file?(config_path)
|
215
|
+
|
216
|
+
yaml = ::YAML.load_file config_path
|
217
|
+
|
218
|
+
raise CassiDException.new(
|
219
|
+
"file '#{config_path}' does not contain a valid configuration"
|
220
|
+
) unless valid_configuration?(yaml)
|
221
|
+
|
222
|
+
config = Configuration.new yaml
|
223
|
+
rescue *ERROR_EXCEPTIONS => x
|
224
|
+
log :violation, "unexpected exception (#{x.class}): '#{x.message}': #{x.backtrace}"
|
225
|
+
raise
|
226
|
+
rescue CassiDException
|
227
|
+
raise
|
228
|
+
rescue => x
|
229
|
+
log :debug, "exception (#{x.class}): '#{x}'"
|
230
|
+
raise CassiDException.new(
|
231
|
+
"configuration file '#{config_path}' is not YAML or could not be loaded"
|
232
|
+
)
|
233
|
+
end
|
234
|
+
|
235
|
+
config
|
236
|
+
end
|
237
|
+
|
238
|
+
def self.pedantic_check clarite_config, config
|
239
|
+
|
240
|
+
trace ParamNames[ :clarite_config, :config ], clarite_config, config
|
241
|
+
|
242
|
+
log :informational, 'Checking configuration ...'
|
243
|
+
|
244
|
+
failed = false
|
245
|
+
|
246
|
+
msg = "Checking ClarITe configuration file '#{clarite_config}' is valid XML"
|
247
|
+
config_xml = nil
|
248
|
+
begin
|
249
|
+
config_xml = ::Nokogiri.XML(File.open(clarite_config)) do |c|
|
250
|
+
c.noblanks.strict
|
251
|
+
end
|
252
|
+
log :informational, "\t#{msg}: PASSED"
|
253
|
+
rescue
|
254
|
+
failed = true
|
255
|
+
log :critical, "\t#{msg}: FAILED"
|
256
|
+
end
|
257
|
+
|
258
|
+
msg = "Checking Razor alias or environment only"
|
259
|
+
unless config.razor.alias and config.razor.environment
|
260
|
+
log :informational, "\t#{msg}: PASSED"
|
261
|
+
else
|
262
|
+
failed = true
|
263
|
+
log :critical, "\t#{msg}: FAILED"
|
264
|
+
end
|
265
|
+
|
266
|
+
space = config.razor.space
|
267
|
+
environment = config.razor.environment
|
268
|
+
if (environment or space) and config_xml
|
269
|
+
|
270
|
+
msg = "Checking"
|
271
|
+
msg += " environment '#{environment}'" if environment
|
272
|
+
msg += " and" if environment and space
|
273
|
+
msg += " space '#{space}'" if space
|
274
|
+
msg += " is present within the ClarITe configuration file"
|
275
|
+
|
276
|
+
match = config_xml.xpath('//razor_server').any? do |razor_server|
|
277
|
+
|
278
|
+
server_space = razor_server.at_xpath('@space').text
|
279
|
+
server_env = razor_server.at_xpath('@environment').text
|
280
|
+
|
281
|
+
(space ? server_space == space : true) &&
|
282
|
+
(environment ? server_env == environment : true)
|
283
|
+
end
|
284
|
+
|
285
|
+
if match
|
286
|
+
log :informational, "\t#{msg}: PASSED"
|
287
|
+
else
|
288
|
+
failed = true
|
289
|
+
log :critical, "\t#{msg}: FAILED"
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
raise CassiDException.new(
|
294
|
+
'failed one or more configuration checks'
|
295
|
+
) if failed
|
296
|
+
end
|
297
|
+
|
298
|
+
def self.children_running?
|
299
|
+
$children.none? do |child|
|
300
|
+
!child.running?
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.restart_children max_restarts
|
305
|
+
$children.each do |child|
|
306
|
+
|
307
|
+
unless child.running?
|
308
|
+
unless max_restarts < child.starts
|
309
|
+
child.start
|
310
|
+
log :notice, "Restarted #{child.name}..."
|
311
|
+
else
|
312
|
+
msg = "Microservice #{child.name} has failed too many times (#{child.starts})"
|
313
|
+
log :critical, msg
|
314
|
+
raise CassiDException.new msg
|
315
|
+
end
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
Microservice = Struct.new(:name, :command, :pid) do
|
321
|
+
|
322
|
+
def start
|
323
|
+
@starts ||= 0
|
324
|
+
@starts += 1
|
325
|
+
self.pid = Process.spawn(command.cmd)
|
326
|
+
end
|
327
|
+
|
328
|
+
def running?
|
329
|
+
begin
|
330
|
+
Process.waitpid(pid, Process::WNOHANG).nil?
|
331
|
+
rescue Errno::ECHILD
|
332
|
+
false
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def starts
|
337
|
+
@starts
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
def self.init config_path, **options
|
342
|
+
|
343
|
+
trace ParamNames[ :config_path, :options ], config_path, options
|
344
|
+
|
345
|
+
begin
|
346
|
+
|
347
|
+
config = load_config config_path
|
348
|
+
|
349
|
+
# validate configuration elements:
|
350
|
+
#
|
351
|
+
# - root_dir is evaluated relative to the config file
|
352
|
+
#
|
353
|
+
# - clarite_config is evaluated relative to the root_dir, the current dir,
|
354
|
+
# the config file
|
355
|
+
|
356
|
+
root_dir = validate_cassini_root_dir(config.cassini.root_dir, config_path) or raise CassiDException.new "value of 'cassini/root_dir' - '#{config.cassini.root_dir}' - is invalid, or cannot be determined through inference"
|
357
|
+
auth_mode = validate_auth_mode(config.cassini.auth_mode) or raise CassiDException.new "value of 'cassini/auth_mode' - '#{config.cassini.auth_mode}' - is not valid"
|
358
|
+
auth_test_mode = config.cassini.auth_test_mode
|
359
|
+
ces_svc_path = validate_service_path(config.cassini.ces['service_path'], root_dir, '.', config_path) or raise CassiDException.new "value of 'cassini/ces/service_path' - '#{config.cassini.ces['service_path']}' - missing or invalid, or cannot be determined through inference"
|
360
|
+
first_port = config.cassini.first_port and VALID_PORTS.include?(first_port) or raise CassiDException.new "value of 'cassini/first_port' - '#{config.cassini.first_port}' - is not a valid port number"
|
361
|
+
host = config.cassini.host or raise CassiDException.new "must supply 'cassini/host'"
|
362
|
+
host_ces = host
|
363
|
+
host_usvc = '0.0.0.0' == host ? 'localhost' : host
|
364
|
+
us_multiplier = options[:us_multiplier]
|
365
|
+
us_multiplier ||= config.cassini.us_multiplier and (1..100).include?(config.cassini.us_multiplier) or raise CassiDException.new "value of 'cassini/us_multiplier' - '#{config.cassini.us_multiplier}' - is not a valid multiplier"
|
366
|
+
|
367
|
+
tls = validate_tls(config.cassini.tls, config_path) or raise CassiDException.new "value of 'cassini/tls' - '#{config.cassini.tls}' - is neither the path of an existing cert file that has an accompanying key file, nor is it an object containing keys 'cert' and 'key' that specify existing certificate and key files"
|
368
|
+
web_server = config.cassini.web_server
|
369
|
+
web_ui_server = config.cassini.web_ui_server
|
370
|
+
|
371
|
+
detach_children = config.control.detach
|
372
|
+
ss_wait = config.control.ss_wait || DEFAULT_SS_WAIT
|
373
|
+
cassid_wait = config.control.cassid_wait || DEFAULT_CASSID_WAIT
|
374
|
+
|
375
|
+
log_thr_console = (options[:log_threshold][0] || config.diagnostics.console['threshold']).to_sym
|
376
|
+
log_thr_main = (options[:log_threshold][1] || config.diagnostics.main['threshold']).to_sym
|
377
|
+
log_dir = options[:log_directory] || config.diagnostics.directory
|
378
|
+
|
379
|
+
clarite_config = validate_clarite_config(config.razor.clarite_config, root_dir, '.', config_path) or raise CassiDException.new "ClarITe configuration file '#{config.razor.clarite_config}' could not be found relative to root-directory, current directory or directory of configuration file"
|
380
|
+
executable = validate_executable(config.razor.executable, root_dir, config_path) or raise CassiDException.new "value of 'executable' - '#{config.razor.executable}' - could not be round relative to root-directory, current directory, or directory of configuration file"
|
381
|
+
|
382
|
+
init_logging(
|
383
|
+
options[:program_name],
|
384
|
+
log_dir,
|
385
|
+
[ log_thr_console, log_thr_main ],
|
386
|
+
)
|
387
|
+
|
388
|
+
if :jwt == auth_mode
|
389
|
+
|
390
|
+
ss = config.cassini.secret_server or raise CassiDException.new "'cassini/secret_server' is required when using JWT authentication"
|
391
|
+
|
392
|
+
# There are four possible elements:
|
393
|
+
#
|
394
|
+
# - 'host' (::String) [Optional] If not specified, the top-level
|
395
|
+
# 'host' is used
|
396
|
+
# - 'port' (::integer) The port on which to contact the
|
397
|
+
# secret-server. [Optional] if 'secrets_path' and 'service_path'
|
398
|
+
# are specified
|
399
|
+
# - 'secrets_path' (::String) [Optional] The secrets file path. If
|
400
|
+
# specified, then 'service_path' must also be specified, and
|
401
|
+
# together they indicate that the (Razor Risk) SecretServer
|
402
|
+
# utility is to be launched
|
403
|
+
# - 'service_path' (::String) [Optional] The secrets server utility
|
404
|
+
# path. If specified, then 'secrets_path' must also be specified,
|
405
|
+
# and together they indicate that the (Razor Risk) SecretServer
|
406
|
+
# utility is to be launched
|
407
|
+
|
408
|
+
ss_host = ss.host || host
|
409
|
+
|
410
|
+
if ss.port
|
411
|
+
|
412
|
+
ss_port = Integer(ss.port, nil: true) and VALID_PORTS.include?(ss.port) or raise CassiDException.new "value of 'cassini/secret_server/port' - '#{ss.port}' - is not a valid port number"
|
413
|
+
end
|
414
|
+
|
415
|
+
if ss.secrets_path
|
416
|
+
|
417
|
+
ss_secrets_path = validate_relative_path(ss.secrets_path, root_dir, '.', config_path) or raise CassiDException.new "secrets file path '#{ss.secrets_path}' could not be found relative to root-directory, current directory or directory of configuration file"
|
418
|
+
end
|
419
|
+
|
420
|
+
if ss.service_path
|
421
|
+
|
422
|
+
ss_service_path = validate_relative_path(ss.service_path, root_dir, '.', config_path) or raise CassiDException.new "service path '#{ss.service_path}' could not be found relative to root-directory, current directory or directory of configuration file"
|
423
|
+
end
|
424
|
+
|
425
|
+
raise CassiDException.new "A Login server is required when the authorisation mode is 'jwt'" unless config.cassini.microservices.dig('stock', 'login')
|
426
|
+
|
427
|
+
else
|
428
|
+
|
429
|
+
ss_host = nil
|
430
|
+
ss_port = nil
|
431
|
+
ss_secrets_path = nil
|
432
|
+
ss_service_path = nil
|
433
|
+
end
|
434
|
+
|
435
|
+
# ##########################################################################
|
436
|
+
# pedantic
|
437
|
+
|
438
|
+
pedantic_check(clarite_config, config) if options[:pedantic]
|
439
|
+
|
440
|
+
# ##########################################################################
|
441
|
+
# main
|
442
|
+
|
443
|
+
port = first_port
|
444
|
+
|
445
|
+
common_args = [ auth_mode, executable, root_dir, clarite_config, host_usvc ]
|
446
|
+
ces_args = [ auth_mode, nil, root_dir, nil, host_ces ]
|
447
|
+
|
448
|
+
ms_options = {
|
449
|
+
razor_environment: config.razor.environment,
|
450
|
+
razor_alias: config.razor.alias,
|
451
|
+
razor_space: config.razor.space,
|
452
|
+
}
|
453
|
+
|
454
|
+
ch_options = {
|
455
|
+
log_threshold: [ log_thr_console, log_thr_main ],
|
456
|
+
log_directory: log_dir,
|
457
|
+
web_server: web_server,
|
458
|
+
auth_test_mode: auth_test_mode,
|
459
|
+
ruby_path: options[:ruby_path],
|
460
|
+
}
|
461
|
+
|
462
|
+
ces_routes = {}
|
463
|
+
|
464
|
+
if :jwt == auth_mode
|
465
|
+
|
466
|
+
ss_port ||= port += 1
|
467
|
+
|
468
|
+
ssc = make_secretserver_command(root_dir, ss_secrets_path, ss_host, ss_port, **ch_options, **ms_options, program_name: 'ss', path: ss_service_path)
|
469
|
+
|
470
|
+
$children << Microservice.new('secret-server', ssc, nil)
|
471
|
+
|
472
|
+
ch_options[:secret_server] = "http://#{ss_host}:#{ss_port}/"
|
473
|
+
|
474
|
+
ch_options[:credentials_encoding_algorithm] = 'AES-256-CBC'
|
475
|
+
ch_options[:jwt_encoding_algorithm] = 'HS256'
|
476
|
+
end
|
477
|
+
|
478
|
+
# N x M microservices
|
479
|
+
|
480
|
+
config.cassini.microservices.each do |category, svcs|
|
481
|
+
|
482
|
+
svcs.each do |service, characteristics|
|
483
|
+
|
484
|
+
external_route = characteristics['external_route']
|
485
|
+
service_path = validate_service_path(characteristics['service_path'], root_dir, '.', config_path)
|
486
|
+
routes = characteristics['routes']
|
487
|
+
|
488
|
+
raise CassiDException.new "invalid configuration (#{characteristics})" if external_route.nil? || service_path.nil? || routes.nil?
|
489
|
+
|
490
|
+
raise CassiDException.new "invalid configuration: the external route '#{external_route}' has already been specified" if ces_routes.has_key?(external_route)
|
491
|
+
|
492
|
+
ex_route_spec = ces_routes[external_route] = {}
|
493
|
+
|
494
|
+
(0...us_multiplier).each do |n|
|
495
|
+
|
496
|
+
port += 1
|
497
|
+
usc = make_microservice_command service, *common_args, port, '', **ch_options, **ms_options, program_name: "#{service}-#{n}", path: service_path
|
498
|
+
|
499
|
+
routes.each do |route|
|
500
|
+
|
501
|
+
internal = route['internal']
|
502
|
+
security = route['security'] || false
|
503
|
+
|
504
|
+
uri = combine_uri usc.uri.to_s, internal
|
505
|
+
|
506
|
+
verbs = route['verbs'] || []
|
507
|
+
verb = route['verb']
|
508
|
+
verbs << verb if verb
|
509
|
+
|
510
|
+
ex_route_spec[verbs] = [] unless ex_route_spec.has_key?(verbs)
|
511
|
+
|
512
|
+
ex_route_spec[verbs] << {
|
513
|
+
|
514
|
+
route: uri,
|
515
|
+
security: security,
|
516
|
+
}
|
517
|
+
end
|
518
|
+
|
519
|
+
$children << Microservice.new("#{service}-#{n}", usc, nil)
|
520
|
+
end
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
config.cassini.external_services.each do |service, characteristics|
|
525
|
+
|
526
|
+
external_route = characteristics['external_route']
|
527
|
+
uri = characteristics['uri']
|
528
|
+
routes = characteristics['routes']
|
529
|
+
|
530
|
+
raise CassiDException.new(
|
531
|
+
"invalid configuration for #{service} (#{characteristics})"
|
532
|
+
) if external_route.nil? || uri.nil?
|
533
|
+
raise CassiDException.new(
|
534
|
+
"invalid configuration for #{service}: the external route '#{external_route}' has already been specified"
|
535
|
+
) if ces_routes.has_key?(external_route)
|
536
|
+
|
537
|
+
ex_route_spec = ces_routes[external_route] = {}
|
538
|
+
routes.each do |route|
|
539
|
+
|
540
|
+
internal = route['internal']
|
541
|
+
security = route['security'] || false
|
542
|
+
|
543
|
+
route_uri = combine_uri uri, internal
|
544
|
+
|
545
|
+
verbs = route['verbs'] || []
|
546
|
+
verb = route['verb']
|
547
|
+
verbs << verb if verb
|
548
|
+
|
549
|
+
ex_route_spec[verbs] = [] unless ex_route_spec.has_key?(verbs)
|
550
|
+
|
551
|
+
ex_route_spec[verbs] << {
|
552
|
+
route: route_uri,
|
553
|
+
security: security,
|
554
|
+
}
|
555
|
+
end
|
556
|
+
end if config.cassini.external_services
|
557
|
+
|
558
|
+
# 1 x CES
|
559
|
+
|
560
|
+
yaml = ::YAML.dump ces_routes
|
561
|
+
routes_b64 = Base64.encode64(yaml).split.join
|
562
|
+
|
563
|
+
ces_addnl = [
|
564
|
+
|
565
|
+
'--accept-string-routes',
|
566
|
+
]
|
567
|
+
|
568
|
+
unless tls.empty?
|
569
|
+
|
570
|
+
ces_addnl << [ '--tls-certificate-file', tls[0] ]
|
571
|
+
ces_addnl << [ '--tls-public-key-file', tls[1] ]
|
572
|
+
end
|
573
|
+
|
574
|
+
ces_opts = {
|
575
|
+
|
576
|
+
routes_config: "base64:#{routes_b64}",
|
577
|
+
additional_arguments: ces_addnl,
|
578
|
+
uri_scheme: tls.empty? ? nil : 'https',
|
579
|
+
}
|
580
|
+
|
581
|
+
us_CES = make_microservice_command 'ces', *ces_args, first_port, nil, **ch_options, **ces_opts, path: ces_svc_path, web_ui_server: web_ui_server
|
582
|
+
|
583
|
+
$children << Microservice.new('ClientEdgeService', us_CES, nil)
|
584
|
+
|
585
|
+
# start the child processes
|
586
|
+
|
587
|
+
log :notice, 'starting child processes ...'
|
588
|
+
|
589
|
+
$children.each_with_index do |child, index0|
|
590
|
+
|
591
|
+
child.start
|
592
|
+
|
593
|
+
if 0 == index0 && :jwt == auth_mode
|
594
|
+
|
595
|
+
# wait for the secret server to start up
|
596
|
+
sleep(ss_wait)
|
597
|
+
end
|
598
|
+
end
|
599
|
+
|
600
|
+
log :notice, 'waiting for child processes to be ready ...'
|
601
|
+
|
602
|
+
sleep(cassid_wait)
|
603
|
+
|
604
|
+
if options[:monitor_startup]
|
605
|
+
unless children_running?
|
606
|
+
msg = 'One or more child process(es) has failed during startup'
|
607
|
+
log :critical, msg
|
608
|
+
raise CassiDException.new msg
|
609
|
+
end
|
610
|
+
end
|
611
|
+
|
612
|
+
# Output status
|
613
|
+
|
614
|
+
log :notice, "Cassini is now up and running, and able to receive calls at '#{us_CES.uri}'"
|
615
|
+
|
616
|
+
if detach_children
|
617
|
+
log :warning, "Detach mode has been depreciated in favour of service installation"
|
618
|
+
end
|
619
|
+
|
620
|
+
rescue *ERROR_EXCEPTIONS => x
|
621
|
+
log :violation, "unexpected exception (#{x.class}): '#{x.message}': #{x.backtrace}"
|
622
|
+
raise
|
623
|
+
rescue CassiDException
|
624
|
+
raise
|
625
|
+
rescue => x
|
626
|
+
log :alert, "exception (#{x.class}): '#{x.message}': #{x.backtrace}"
|
627
|
+
raise CassiDException.new 'Unexpected failure'
|
628
|
+
end
|
629
|
+
end
|
630
|
+
|
631
|
+
|
632
|
+
# ##########################################################
|
633
|
+
# modules
|
634
|
+
|
635
|
+
end # module Main
|
636
|
+
end # module CassiD
|
637
|
+
end # module Utilities
|
638
|
+
end # module Cassini
|
639
|
+
end # module RazorRisk
|
640
|
+
|
641
|
+
# ############################## end of file ############################# #
|
642
|
+
|
643
|
+
|