legionio 0.3.5 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/rspec.yml +45 -0
- data/.github/workflows/rubocop.yml +28 -0
- data/.github/workflows/sourcehawk-scan.yml +20 -0
- data/.gitignore +15 -0
- data/.rubocop.yml +90 -0
- data/CHANGELOG.md +4 -0
- data/CODE_OF_CONDUCT.md +75 -0
- data/CONTRIBUTING.md +55 -0
- data/Dockerfile +9 -0
- data/Gemfile +10 -0
- data/INDIVIDUAL_CONTRIBUTOR_LICENSE.md +30 -0
- data/LICENSE +201 -0
- data/NOTICE.txt +9 -0
- data/README.md +162 -0
- data/SECURITY.md +9 -0
- data/attribution.txt +1 -0
- data/docker_deploy.rb +10 -0
- data/exe/legion +4 -0
- data/exe/legionio +53 -0
- data/exe/lex_gen +5 -0
- data/legionio.gemspec +53 -0
- data/lib/legion.rb +24 -0
- data/lib/legion/cli.rb +56 -0
- data/lib/legion/cli/chain.rb +35 -0
- data/lib/legion/cli/cohort.rb +10 -0
- data/lib/legion/cli/function.rb +41 -0
- data/lib/legion/cli/lex/actor.rb +31 -0
- data/lib/legion/cli/lex/exchange.rb +32 -0
- data/lib/legion/cli/lex/message.rb +32 -0
- data/lib/legion/cli/lex/queue.rb +45 -0
- data/lib/legion/cli/lex/runner.rb +70 -0
- data/lib/legion/cli/lex/templates/actor.erb +6 -0
- data/lib/legion/cli/lex/templates/actor_spec.erb +0 -0
- data/lib/legion/cli/lex/templates/base/bitbucket.yml.erb +69 -0
- data/lib/legion/cli/lex/templates/base/gemfile.erb +3 -0
- data/lib/legion/cli/lex/templates/base/gemspec.erb +26 -0
- data/lib/legion/cli/lex/templates/base/gitignore.erb +11 -0
- data/lib/legion/cli/lex/templates/base/lex.erb +9 -0
- data/lib/legion/cli/lex/templates/base/lex_spec.erb +5 -0
- data/lib/legion/cli/lex/templates/base/lic.erb +21 -0
- data/lib/legion/cli/lex/templates/base/rakefile.erb +6 -0
- data/lib/legion/cli/lex/templates/base/readme.md.erb +2 -0
- data/lib/legion/cli/lex/templates/base/rubocop.yml.erb +15 -0
- data/lib/legion/cli/lex/templates/base/spec_helper.rb.erb +11 -0
- data/lib/legion/cli/lex/templates/base/version.erb +7 -0
- data/lib/legion/cli/lex/templates/exchange.erb +11 -0
- data/lib/legion/cli/lex/templates/exchange_spec.erb +0 -0
- data/lib/legion/cli/lex/templates/message.erb +23 -0
- data/lib/legion/cli/lex/templates/message_spec.erb +0 -0
- data/lib/legion/cli/lex/templates/queue.erb +12 -0
- data/lib/legion/cli/lex/templates/queue_helper.erb +24 -0
- data/lib/legion/cli/lex/templates/queue_spec.erb +11 -0
- data/lib/legion/cli/lex/templates/runner.erb +11 -0
- data/lib/legion/cli/lex/templates/runner_spec.erb +11 -0
- data/lib/legion/cli/relationship.rb +22 -0
- data/lib/legion/cli/task.rb +49 -0
- data/lib/legion/cli/trigger.rb +88 -0
- data/lib/legion/cli/version.rb +5 -0
- data/lib/legion/extensions.rb +229 -0
- data/lib/legion/extensions/actors/base.rb +47 -0
- data/lib/legion/extensions/actors/defaults.rb +28 -0
- data/lib/legion/extensions/actors/every.rb +48 -0
- data/lib/legion/extensions/actors/loop.rb +32 -0
- data/lib/legion/extensions/actors/nothing.rb +15 -0
- data/lib/legion/extensions/actors/once.rb +40 -0
- data/lib/legion/extensions/actors/poll.rb +87 -0
- data/lib/legion/extensions/actors/subscription.rb +139 -0
- data/lib/legion/extensions/builders/actors.rb +61 -0
- data/lib/legion/extensions/builders/base.rb +36 -0
- data/lib/legion/extensions/builders/helpers.rb +24 -0
- data/lib/legion/extensions/builders/runners.rb +62 -0
- data/lib/legion/extensions/core.rb +131 -0
- data/lib/legion/extensions/data.rb +61 -0
- data/lib/legion/extensions/data/migrator.rb +33 -0
- data/lib/legion/extensions/data/model.rb +8 -0
- data/lib/legion/extensions/helpers/base.rb +82 -0
- data/lib/legion/extensions/helpers/cache.rb +23 -0
- data/lib/legion/extensions/helpers/core.rb +41 -0
- data/lib/legion/extensions/helpers/data.rb +23 -0
- data/lib/legion/extensions/helpers/lex.rb +48 -0
- data/lib/legion/extensions/helpers/logger.rb +44 -0
- data/lib/legion/extensions/helpers/task.rb +60 -0
- data/lib/legion/extensions/helpers/transport.rb +44 -0
- data/lib/legion/extensions/transport.rb +159 -0
- data/lib/legion/lex.rb +89 -0
- data/lib/legion/process.rb +124 -0
- data/lib/legion/runner.rb +57 -0
- data/lib/legion/runner/log.rb +10 -0
- data/lib/legion/runner/status.rb +69 -0
- data/lib/legion/service.rb +130 -0
- data/lib/legion/supervision.rb +15 -0
- data/lib/legion/version.rb +3 -0
- data/sourcehawk.yml +4 -0
- metadata +142 -168
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'legion/extensions/<%= config[:lex] %>'
|
3
|
+
|
4
|
+
RSpec.configure do |config|
|
5
|
+
config.example_status_persistence_file_path = '.rspec_status'
|
6
|
+
config.disable_monkey_patching!
|
7
|
+
|
8
|
+
config.expect_with :rspec do |c|
|
9
|
+
c.syntax = :expect
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>
|
2
|
+
module Transport
|
3
|
+
module Exchanges
|
4
|
+
class <%= config[:name].split('_').collect(&:capitalize).join %> < Legion::Transport::Exchange
|
5
|
+
def exchange_name
|
6
|
+
'<%= config[:name].split('_').collect(&:capitalize).join %>'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
File without changes
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>
|
2
|
+
module Transport
|
3
|
+
module Messages
|
4
|
+
class <%= config[:name].split('_').collect(&:capitalize).join %> < Legion::Transport::Message
|
5
|
+
def initialize(payload, status, options = {})
|
6
|
+
@payload = payload
|
7
|
+
@options = options
|
8
|
+
@status = status
|
9
|
+
@routing_key = routing_key
|
10
|
+
validate
|
11
|
+
end
|
12
|
+
|
13
|
+
def routing_key
|
14
|
+
"<%= config[:lex].split('_').collect(&:capitalize).join %>.<%= config[:name].split('_').collect(&:capitalize).join %>"
|
15
|
+
end
|
16
|
+
|
17
|
+
def message(payload = @payload, _options = {})
|
18
|
+
Legion::JSON.dump(payload)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
File without changes
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Legion
|
2
|
+
module Extensions
|
3
|
+
module <%= config[:lex].split('_').collect(&:capitalize).join %>
|
4
|
+
module Transport
|
5
|
+
module Queues
|
6
|
+
class <%= config[:name].split('_').collect(&:capitalize).join %> < Legion::Transport::Queue
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Legion
|
2
|
+
module Transport
|
3
|
+
class Queue
|
4
|
+
def initialize(**opts)
|
5
|
+
end
|
6
|
+
|
7
|
+
def queue_name
|
8
|
+
'test_queue'
|
9
|
+
end
|
10
|
+
|
11
|
+
def acknowledge(delivery_tag)
|
12
|
+
true
|
13
|
+
end
|
14
|
+
|
15
|
+
def reject(delivery_tag, requeue: false, **)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
def queue_options
|
20
|
+
Concurrent::Hash.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'queue_helper'
|
3
|
+
require 'legion/extensions/<%= config[:lex] %>/transport/queues/<%= config[:name] %>'
|
4
|
+
|
5
|
+
RSpec.describe Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>::Transport::Queues::<%= config[:name].capitalize %> do
|
6
|
+
it { should be_a Legion::Transport::Queue }
|
7
|
+
it { should respond_to :queue_name }
|
8
|
+
it { should respond_to(:acknowledge).with(1).argument }
|
9
|
+
it { should respond_to(:reject).with(1).argument }
|
10
|
+
it { should respond_to :queue_options }
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Legion
|
2
|
+
module Extensions
|
3
|
+
module <%= config[:lex].split('_').collect(&:capitalize).join %>
|
4
|
+
module Runners
|
5
|
+
module <%= config[:name].split('_').collect(&:capitalize).join %>
|
6
|
+
extend Legion::Extensions::Helpers::Lex if Legion::Extensions.const_defined? 'Helpers::Lex'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'legion/extensions/<%= config[:lex] %>/runners/<%= config[:name] %>'
|
3
|
+
|
4
|
+
RSpec.describe Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>::Runners::<%= config[:name].capitalize %> do
|
5
|
+
it { should be_a Module }
|
6
|
+
Legion::Extensions::<%= config[:lex].capitalize %>::Runners::<%= config[:name].split('_').collect(&:capitalize).join %>.extend Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>::Runners::<%= config[:name].split('_').collect(&:capitalize).join %>
|
7
|
+
|
8
|
+
describe 'Functions should work with arguments' do
|
9
|
+
let(:test_class) { Class.new { extend Legion::Extensions::<%= config[:lex].split('_').collect(&:capitalize).join %>::Runners::<%= config[:name].split('_').collect(&:capitalize).join %> } }
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Legion
|
2
|
+
class Cli
|
3
|
+
class Relationship < Thor
|
4
|
+
desc 'create', 'creates a new relationship'
|
5
|
+
def create(_name, _type)
|
6
|
+
trigger_id = invoke('legion:cli:function:find', [], internal: true, capture: true) # rubocop:disable Lint/UselessAssignment
|
7
|
+
end
|
8
|
+
|
9
|
+
desc 'activate', 'actives a relationship'
|
10
|
+
def active; end
|
11
|
+
|
12
|
+
desc 'deactivate', 'deactivates a relationship'
|
13
|
+
def deactivate; end
|
14
|
+
|
15
|
+
desc 'modify', 'modify an existing relationship'
|
16
|
+
def modify; end
|
17
|
+
|
18
|
+
desc 'delete', 'deletes a relationship'
|
19
|
+
def delete; end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Legion
|
2
|
+
class Cli
|
3
|
+
class Task < Thor
|
4
|
+
package_name 'Legion'
|
5
|
+
|
6
|
+
option :limit, type: :numeric, required: true, default: 10, desc: 'how many tasks to return'
|
7
|
+
desc 'show', 'show'
|
8
|
+
option :status, type: :string, required: false, desc: 'search for specific status'
|
9
|
+
def show
|
10
|
+
Legion::Service.new(cache: false, crypt: false, extensions: false, log_level: 'error')
|
11
|
+
rows = [%w[id relationship function status]]
|
12
|
+
Legion::Data::Model::Task.limit(options[:limit]).order(:id).reverse_each do |row|
|
13
|
+
rows.push([row.values[:id], row.values[:relationship_id], row.values[:function_id], row.values[:status]])
|
14
|
+
end
|
15
|
+
|
16
|
+
print_table rows
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'test', 'test'
|
20
|
+
def status(id)
|
21
|
+
Legion::Service.new(cache: false, crypt: false, extensions: false, log_level: 'error')
|
22
|
+
say Legion::Data::Model::Task[id].values
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'logs', 'logs'
|
26
|
+
option :limit, type: :numeric, required: true, default: 10, desc: 'how many tasks to return'
|
27
|
+
def logs(id)
|
28
|
+
Legion::Service.new(cache: false, crypt: false, extensions: false, log_level: 'error')
|
29
|
+
rows = [%w[id node_id created entry]]
|
30
|
+
Legion::Data::Model::TaskLog.where(task_id: id).limit(options[:limit]).each do |row|
|
31
|
+
rows.push([row.values[:id], row.values[:node_id], row.values[:created], row.values[:entry]])
|
32
|
+
end
|
33
|
+
print_table rows
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'purge', 'purge'
|
37
|
+
def purge
|
38
|
+
Legion::Service.new(cache: false, crypt: false, extensions: false, log_level: 'error')
|
39
|
+
days = ask 'how many days do you want to keep?', default: 7
|
40
|
+
dataset = Legion::Data::Model::Task.where { created < DateTime.now - days.to_i }
|
41
|
+
yes? "This will delete #{dataset.count} tasks, continue?", :red
|
42
|
+
dataset.delete
|
43
|
+
say 'Done!'
|
44
|
+
end
|
45
|
+
|
46
|
+
default_task :show
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Legion
|
2
|
+
class Cli
|
3
|
+
class Trigger < Thor
|
4
|
+
desc 'queue', 'used to send a job directly to a worker via Legion::Transport'
|
5
|
+
option :extension, type: :string, required: false, desc: 'extension short name'
|
6
|
+
option :runner, type: :string, required: false, desc: 'runner short name'
|
7
|
+
option :function, type: :string, required: false, desc: 'function short name'
|
8
|
+
option :delay, type: :numeric, default: 0, desc: 'how long to wait before running the task'
|
9
|
+
def queue(*args) # rubocop:disable Metrics/AbcSize,Metrics/CyclomaticComplexity,Metrics/PerceivedComplexity,Metrics/MethodLength
|
10
|
+
Legion::Service.new(cache: false, crypt: false, extensions: false, log_level: 'error')
|
11
|
+
include Legion::Extensions::Helpers::Task
|
12
|
+
response = if options['extension'].is_a? String
|
13
|
+
options[:extension]
|
14
|
+
else
|
15
|
+
ask 'trigger extension?', limited_to: Legion::Data::Model::Extension.map(:name)
|
16
|
+
end
|
17
|
+
trigger_extension = Legion::Data::Model::Extension.where(name: response).first
|
18
|
+
runners = Legion::Data::Model::Runner.where(extension_id: trigger_extension.values[:id])
|
19
|
+
if runners.count == 1
|
20
|
+
trigger_runner = runners.first
|
21
|
+
say "Auto selecting #{trigger_runner.values[:name]} since it is the only option for runners"
|
22
|
+
else
|
23
|
+
response = options[:runner].is_a?(String) ? options[:runner] : ask('trigger runner?', limited_to: runners.map(:name))
|
24
|
+
trigger_runner = Legion::Data::Model::Runner.where(name: response).where(extension_id: trigger_extension.values[:id]).first
|
25
|
+
end
|
26
|
+
|
27
|
+
functions = Legion::Data::Model::Function.where(runner_id: trigger_runner.values[:id])
|
28
|
+
|
29
|
+
if functions.count == 1
|
30
|
+
trigger_function = functions.first
|
31
|
+
say "Auto selecting #{trigger_function.values[:name]} since it is the only option for functions"
|
32
|
+
else
|
33
|
+
response = if options[:function].is_a?(String)
|
34
|
+
options[:function]
|
35
|
+
else
|
36
|
+
ask('trigger function?',
|
37
|
+
limited_to: Legion::Data::Model::Function.where(runner_id: trigger_runner.values[:id]).map(:name))
|
38
|
+
end
|
39
|
+
trigger_function = Legion::Data::Model::Function.where(runner_id: trigger_runner.values[:id]).where(name: response).first
|
40
|
+
end
|
41
|
+
say "#{trigger_runner.values[:namespace]}.#{trigger_function.values[:name]} selected as trigger", :green, :italicized
|
42
|
+
payload = {}
|
43
|
+
auto_opts = {}
|
44
|
+
unless args.count.zero?
|
45
|
+
args.each do |arg|
|
46
|
+
test = arg.split(':')
|
47
|
+
auto_opts[test[0].to_sym] = test[1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
Legion::JSON.load(trigger_function.values[:args]).each do |arg, required|
|
52
|
+
next if %w[args payload opts options].include? arg.to_s
|
53
|
+
|
54
|
+
if auto_opts.key? arg
|
55
|
+
payload[arg.to_sym] = auto_opts[arg]
|
56
|
+
next
|
57
|
+
end
|
58
|
+
response = ask "#{required == 'keyreq' ? '[required]' : '[optional]'} #{arg} value:"
|
59
|
+
if response.empty? && required == 'keyreq'
|
60
|
+
say "Error! #{arg} is required and cannot be empty", :red
|
61
|
+
redo
|
62
|
+
end
|
63
|
+
payload[arg.to_sym] = response unless response.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
status = options[:delay].zero? ? 'task.queued' : 'task.delayed'
|
67
|
+
task = generate_task_id(function_id: trigger_function.values[:id], status: status, runner_id: trigger_runner.values[:id], args: payload,
|
68
|
+
delay: options[:delay])
|
69
|
+
|
70
|
+
unless options[:delay].zero?
|
71
|
+
say "Task: #{task[:task_id]} is queued and will be run in #{options[:delay]}s"
|
72
|
+
return true
|
73
|
+
end
|
74
|
+
|
75
|
+
routing_key = "#{trigger_extension.values[:exchange]}.#{trigger_runner.values[:queue]}.#{trigger_function.values[:name]}"
|
76
|
+
exchange = Legion::Transport::Messages::Dynamic.new(function: trigger_function.values[:name], function_id: trigger_function.values[:id],
|
77
|
+
routing_key: routing_key, args: payload)
|
78
|
+
exchange.options[:task_id] = task[:task_id]
|
79
|
+
exchange.publish if options[:delay].zero?
|
80
|
+
|
81
|
+
say "Task: #{task[:task_id]} was queued"
|
82
|
+
end
|
83
|
+
remove_command :generate_task_id
|
84
|
+
|
85
|
+
default_task :queue
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require 'legion/extensions/core'
|
2
|
+
require 'legion/runner'
|
3
|
+
|
4
|
+
module Legion
|
5
|
+
module Extensions
|
6
|
+
class << self
|
7
|
+
def setup
|
8
|
+
hook_extensions
|
9
|
+
end
|
10
|
+
|
11
|
+
def hook_extensions
|
12
|
+
@timer_tasks = []
|
13
|
+
@loop_tasks = []
|
14
|
+
@once_tasks = []
|
15
|
+
@poll_tasks = []
|
16
|
+
@subscription_tasks = []
|
17
|
+
@actors = []
|
18
|
+
|
19
|
+
find_extensions
|
20
|
+
load_extensions
|
21
|
+
end
|
22
|
+
|
23
|
+
def shutdown
|
24
|
+
return nil if @loaded_extensions.nil?
|
25
|
+
|
26
|
+
@subscription_tasks.each do |task|
|
27
|
+
task[:threadpool].shutdown
|
28
|
+
task[:threadpool].kill unless task[:threadpool].wait_for_termination(5)
|
29
|
+
end
|
30
|
+
|
31
|
+
@loop_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) }
|
32
|
+
@once_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) }
|
33
|
+
@timer_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) }
|
34
|
+
@poll_tasks.each { |task| task[:running_class].cancel if task[:running_class].respond_to?(:cancel) }
|
35
|
+
|
36
|
+
Legion::Logging.info 'Successfully shut down all actors'
|
37
|
+
end
|
38
|
+
|
39
|
+
def load_extensions
|
40
|
+
@extensions ||= {}
|
41
|
+
@loaded_extensions ||= []
|
42
|
+
@extensions.each do |extension, values|
|
43
|
+
if values.key(:enabled) && !values[:enabled]
|
44
|
+
Legion::Logging.info "Skipping #{extension} because it's disabled"
|
45
|
+
next
|
46
|
+
end
|
47
|
+
|
48
|
+
if Legion::Settings[:extensions].key?(extension.to_sym) && Legion::Settings[:extensions][extension.to_sym].key?(:enabled) && !Legion::Settings[:extensions][extension.to_sym][:enabled] # rubocop:disable Layout/LineLength
|
49
|
+
next
|
50
|
+
end
|
51
|
+
|
52
|
+
unless load_extension(extension, values)
|
53
|
+
Legion::Logging.warn("#{extension} failed to load")
|
54
|
+
next
|
55
|
+
end
|
56
|
+
@loaded_extensions.push(extension)
|
57
|
+
sleep(0.1)
|
58
|
+
end
|
59
|
+
Legion::Logging.info "#{@extensions.count} extensions loaded with subscription:#{@subscription_tasks.count},every:#{@timer_tasks.count},poll:#{@poll_tasks.count},once:#{@once_tasks.count},loop:#{@loop_tasks.count}"
|
60
|
+
end
|
61
|
+
|
62
|
+
def load_extension(extension, values) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/AbcSize
|
63
|
+
return unless gem_load(values[:gem_name], extension)
|
64
|
+
|
65
|
+
extension = Kernel.const_get(values[:extension_class])
|
66
|
+
extension.extend Legion::Extensions::Core unless extension.singleton_class.included_modules.include? Legion::Extensions::Core
|
67
|
+
|
68
|
+
min_version = Legion::Settings[:extensions][values[:extension_name]][:min_version] || nil
|
69
|
+
Legion::Logging.fatal values if min_version.is_a?(String) && Gem::Version.new(values[:version]) >= Gem::Version.new(min_version)
|
70
|
+
|
71
|
+
if extension.data_required? && Legion::Settings[:data][:connected] == false
|
72
|
+
Legion::Logging.warn "#{values[:extension_name]} requires Legion::Data but isn't enabled, skipping"
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
|
76
|
+
if extension.cache_required? && Legion::Settings[:cache][:connected] == false
|
77
|
+
Legion::Logging.warn "#{values[:extension_name]} requires Legion::Cache but isn't enabled, skipping"
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
if extension.crypt_required? && Legion::Settings[:crypt][:cs].nil?
|
82
|
+
Legion::Logging.warn "#{values[:extension_name]} requires Legion::Crypt but isn't ready, skipping"
|
83
|
+
return false
|
84
|
+
end
|
85
|
+
|
86
|
+
if extension.vault_required? && Legion::Settings[:crypt][:vault][:connected] == false
|
87
|
+
Legion::Logging.warn "#{values[:extension_name]} requires Legion::Crypt::Vault but isn't enabled, skipping"
|
88
|
+
return false
|
89
|
+
end
|
90
|
+
|
91
|
+
has_logger = extension.respond_to?(:log)
|
92
|
+
extension.autobuild
|
93
|
+
|
94
|
+
require 'legion/transport/messages/lex_register'
|
95
|
+
Legion::Transport::Messages::LexRegister.new(function: 'save', opts: extension.runners).publish
|
96
|
+
|
97
|
+
if extension.respond_to?(:meta_actors) && extension.meta_actors.is_a?(Array)
|
98
|
+
extension.meta_actors.each do |_key, actor|
|
99
|
+
extension.log.debug("hooking meta actor: #{actor}") if has_logger
|
100
|
+
hook_actor(**actor)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
extension.actors.each do |_key, actor|
|
105
|
+
extension.log.debug("hooking literal actor: #{actor}") if has_logger
|
106
|
+
hook_actor(**actor)
|
107
|
+
end
|
108
|
+
extension.log.info "Loaded v#{extension::VERSION}"
|
109
|
+
rescue StandardError => e
|
110
|
+
Legion::Logging.error e.message
|
111
|
+
Legion::Logging.error e.backtrace
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
def hook_actor(extension:, extension_name:, actor_class:, size: 1, **opts)
|
116
|
+
size = if Legion::Settings[:extensions].key?(extension_name.to_sym) && Legion::Settings[:extensions][extension_name.to_sym].key?(:workers)
|
117
|
+
Legion::Settings[:extensions][extension_name.to_sym][:workers]
|
118
|
+
elsif size.is_a? Integer
|
119
|
+
size
|
120
|
+
else
|
121
|
+
1
|
122
|
+
end
|
123
|
+
|
124
|
+
extension_hash = {
|
125
|
+
extension: extension,
|
126
|
+
extension_name: extension_name,
|
127
|
+
actor_class: actor_class,
|
128
|
+
size: size,
|
129
|
+
fallback_policy: :abort,
|
130
|
+
**opts
|
131
|
+
}
|
132
|
+
extension_hash[:running_class] = if actor_class.ancestors.include? Legion::Extensions::Actors::Subscription
|
133
|
+
actor_class
|
134
|
+
else
|
135
|
+
actor_class.new
|
136
|
+
end
|
137
|
+
|
138
|
+
return if extension_hash[:running_class].respond_to?(:enabled?) && !extension_hash[:running_class].enabled?
|
139
|
+
|
140
|
+
if actor_class.ancestors.include? Legion::Extensions::Actors::Every
|
141
|
+
@timer_tasks.push(extension_hash)
|
142
|
+
elsif actor_class.ancestors.include? Legion::Extensions::Actors::Once
|
143
|
+
@once_tasks.push(extension_hash)
|
144
|
+
elsif actor_class.ancestors.include? Legion::Extensions::Actors::Loop
|
145
|
+
@loop_tasks.push(extension_hash)
|
146
|
+
elsif actor_class.ancestors.include? Legion::Extensions::Actors::Poll
|
147
|
+
@poll_tasks.push(extension_hash)
|
148
|
+
elsif actor_class.ancestors.include? Legion::Extensions::Actors::Subscription
|
149
|
+
extension_hash[:threadpool] = Concurrent::FixedThreadPool.new(size)
|
150
|
+
size.times do
|
151
|
+
extension_hash[:threadpool].post do
|
152
|
+
klass = actor_class.new
|
153
|
+
if klass.respond_to?(:async)
|
154
|
+
klass.async.subscribe
|
155
|
+
else
|
156
|
+
klass.subscribe
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
@subscription_tasks.push(extension_hash)
|
161
|
+
else
|
162
|
+
Legion::Logging.fatal 'did not match any actor classes'
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def gem_load(gem_name, name)
|
167
|
+
require "#{Gem::Specification.find_by_name(gem_name).gem_dir}/lib/legion/extensions/#{name}"
|
168
|
+
true
|
169
|
+
rescue LoadError => e
|
170
|
+
Legion::Logging.error e.message
|
171
|
+
Legion::Logging.error e.backtrace
|
172
|
+
Legion::Logging.error "gem_path: #{gem_path}" unless gem_path.nil?
|
173
|
+
false
|
174
|
+
end
|
175
|
+
|
176
|
+
def find_extensions
|
177
|
+
@extensions ||= {}
|
178
|
+
Gem::Specification.all_names.each do |gem|
|
179
|
+
next unless gem[0..3] == 'lex-'
|
180
|
+
|
181
|
+
lex = gem.split('-')
|
182
|
+
@extensions[lex[1]] = { full_gem_name: gem,
|
183
|
+
gem_name: "lex-#{lex[1]}",
|
184
|
+
extension_name: lex[1],
|
185
|
+
version: lex[2],
|
186
|
+
extension_class: "Legion::Extensions::#{lex[1].split('_').collect(&:capitalize).join}" }
|
187
|
+
end
|
188
|
+
|
189
|
+
enabled = 0
|
190
|
+
requested = 0
|
191
|
+
|
192
|
+
Legion::Settings[:extensions].each do |extension, values|
|
193
|
+
next if @extensions.key? extension.to_s
|
194
|
+
next if values[:enabled] == false
|
195
|
+
|
196
|
+
requested += 1
|
197
|
+
next if values[:auto_install] == false
|
198
|
+
next if ENV['_'].include? 'bundle'
|
199
|
+
|
200
|
+
Legion::Logging.warn "#{extension} is missing, attempting to install automatically.."
|
201
|
+
install = Gem.install("lex-#{extension}", values[:version])
|
202
|
+
Legion::Logging.debug(install)
|
203
|
+
lex = Gem::Specification.find_by_name("lex-#{extension}")
|
204
|
+
|
205
|
+
@extensions[extension.to_s] = {
|
206
|
+
full_gem_name: "lex-#{extension}-#{lex.version}",
|
207
|
+
gem_name: "lex-#{extension}",
|
208
|
+
extension_name: extension.to_s,
|
209
|
+
version: lex.version,
|
210
|
+
extension_class: "Legion::Extensions::#{extension.to_s.split('_').collect(&:capitalize).join}"
|
211
|
+
}
|
212
|
+
|
213
|
+
enabled += 1
|
214
|
+
|
215
|
+
rescue StandardError, Gem::MissingSpecError => e
|
216
|
+
Legion::Logging.error "Failed to auto install #{extension}, e: #{e.message}"
|
217
|
+
end
|
218
|
+
return true if requested == enabled
|
219
|
+
|
220
|
+
Legion::Logging.warn "A total of #{requested - enabled} where skipped"
|
221
|
+
if ENV.key?('_') && ENV['_'].include?('bundle')
|
222
|
+
Legion::Logging.warn 'Please add them to your Gemfile since you are using bundler'
|
223
|
+
else
|
224
|
+
Legion::Logging.warn 'You must have auto_install_missing_lex set to true to auto install missing extensions'
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|