legionio 0.3.5 → 1.2.0
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/.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
|