legionio 1.7.19 → 1.7.21
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/.rubocop.yml +3 -0
- data/CHANGELOG.md +32 -0
- data/CLAUDE.md +1 -1
- data/README.md +4 -4
- data/lib/legion/api/default_settings.rb +1 -1
- data/lib/legion/api/identity_audit.rb +46 -0
- data/lib/legion/api/settings.rb +1 -1
- data/lib/legion/api.rb +2 -0
- data/lib/legion/cli/doctor/api_bind_check.rb +56 -0
- data/lib/legion/cli/doctor/mode_check.rb +40 -0
- data/lib/legion/cli/doctor_command.rb +4 -0
- data/lib/legion/extensions.rb +57 -1
- data/lib/legion/identity/broker.rb +159 -0
- data/lib/legion/identity/lease.rb +63 -0
- data/lib/legion/identity/lease_renewer.rb +82 -0
- data/lib/legion/identity/middleware.rb +79 -0
- data/lib/legion/identity/process.rb +121 -0
- data/lib/legion/identity/request.rb +71 -0
- data/lib/legion/mode.rb +76 -0
- data/lib/legion/process_role.rb +4 -2
- data/lib/legion/readiness.rb +16 -6
- data/lib/legion/service.rb +215 -8
- data/lib/legion/version.rb +1 -1
- data/lib/legion.rb +7 -0
- metadata +11 -1
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'concurrent'
|
|
4
|
+
|
|
5
|
+
module Legion
|
|
6
|
+
module Identity
|
|
7
|
+
class LeaseRenewer
|
|
8
|
+
attr_reader :provider_name
|
|
9
|
+
|
|
10
|
+
BACKOFF_SLEEP = 5
|
|
11
|
+
MIN_SLEEP = 1
|
|
12
|
+
DEFAULT_SLEEP = 60
|
|
13
|
+
|
|
14
|
+
def initialize(provider_name:, provider:, lease:)
|
|
15
|
+
@provider_name = provider_name
|
|
16
|
+
@provider = provider
|
|
17
|
+
@lease = Concurrent::AtomicReference.new(lease)
|
|
18
|
+
@stop = Concurrent::AtomicBoolean.new(false)
|
|
19
|
+
@thread = Thread.new { run_loop }
|
|
20
|
+
@thread.name = "lease-renewer-#{provider_name}"
|
|
21
|
+
@thread.abort_on_exception = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def current_lease
|
|
25
|
+
@lease.get
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def stop!
|
|
29
|
+
@stop.make_true
|
|
30
|
+
@thread&.wakeup rescue nil # rubocop:disable Style/RescueModifier
|
|
31
|
+
@thread&.join(5)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def alive?
|
|
35
|
+
@thread&.alive? || false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def run_loop
|
|
41
|
+
until @stop.true?
|
|
42
|
+
lease = @lease.get
|
|
43
|
+
sleep_time = compute_sleep(lease)
|
|
44
|
+
interruptible_sleep(sleep_time)
|
|
45
|
+
break if @stop.true?
|
|
46
|
+
|
|
47
|
+
renew
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def renew
|
|
52
|
+
new_lease = @provider.provide_token
|
|
53
|
+
@lease.set(new_lease) if new_lease&.valid?
|
|
54
|
+
rescue StandardError => e
|
|
55
|
+
log_renewal_failure(e)
|
|
56
|
+
interruptible_sleep(BACKOFF_SLEEP)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def compute_sleep(lease)
|
|
60
|
+
return DEFAULT_SLEEP if lease.nil? || lease.expires_at.nil? || lease.issued_at.nil?
|
|
61
|
+
|
|
62
|
+
remaining = lease.expires_at - Time.now
|
|
63
|
+
half_remaining = remaining / 2.0
|
|
64
|
+
[half_remaining, MIN_SLEEP].max
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def interruptible_sleep(seconds)
|
|
68
|
+
deadline = Time.now + seconds
|
|
69
|
+
sleep([1, deadline - Time.now].min) while Time.now < deadline && !@stop.true?
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def log_renewal_failure(error)
|
|
73
|
+
message = "[LeaseRenewer][#{@provider_name}] renewal failed: #{error.message}"
|
|
74
|
+
if defined?(Legion::Logging) && Legion::Logging.respond_to?(:warn)
|
|
75
|
+
Legion::Logging.warn(message)
|
|
76
|
+
else
|
|
77
|
+
$stderr.puts message # rubocop:disable Style/StderrPuts
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Identity
|
|
5
|
+
class Middleware
|
|
6
|
+
SKIP_PATHS = %w[/api/health /api/ready /api/openapi.json /metrics].freeze
|
|
7
|
+
LOOPBACK_BINDS = %w[127.0.0.1 ::1 localhost].freeze
|
|
8
|
+
|
|
9
|
+
def initialize(app, require_auth: false)
|
|
10
|
+
@app = app
|
|
11
|
+
@require_auth = require_auth
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def call(env)
|
|
15
|
+
return @app.call(env) if skip_path?(env['PATH_INFO'])
|
|
16
|
+
|
|
17
|
+
# Bridge from existing auth middleware
|
|
18
|
+
auth_claims = env['legion.auth']
|
|
19
|
+
auth_method = env['legion.auth_method']
|
|
20
|
+
|
|
21
|
+
env['legion.principal'] = if auth_claims
|
|
22
|
+
build_request(auth_claims, auth_method)
|
|
23
|
+
elsif @require_auth
|
|
24
|
+
# Auth middleware already handled 401 for protected paths;
|
|
25
|
+
# this is a safety net for any path that slipped through.
|
|
26
|
+
nil
|
|
27
|
+
else
|
|
28
|
+
# No auth required (loopback bind, lite mode, etc.).
|
|
29
|
+
# Set a system-level principal so audit trails always have an identity.
|
|
30
|
+
system_principal
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@app.call(env)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Returns whether the API should require authentication.
|
|
37
|
+
# Skips auth for lite mode and loopback binds (local dev / CI).
|
|
38
|
+
def self.require_auth?(bind:, mode:)
|
|
39
|
+
return false if mode == :lite
|
|
40
|
+
return false if LOOPBACK_BINDS.include?(bind)
|
|
41
|
+
|
|
42
|
+
true
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def skip_path?(path)
|
|
48
|
+
SKIP_PATHS.any? { |p| path.start_with?(p) }
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def build_request(claims, method)
|
|
52
|
+
Identity::Request.from_auth_context({
|
|
53
|
+
sub: claims[:sub] || claims[:worker_id] || claims[:owner_msid],
|
|
54
|
+
name: claims[:name] || claims[:sub],
|
|
55
|
+
kind: determine_kind(claims, method),
|
|
56
|
+
groups: Array(claims[:roles] || claims[:groups]),
|
|
57
|
+
source: method&.to_sym
|
|
58
|
+
})
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def determine_kind(claims, method)
|
|
62
|
+
return :service if claims[:scope] == 'worker' || claims[:worker_id]
|
|
63
|
+
return :human if method == 'kerberos' || claims[:scope] == 'human'
|
|
64
|
+
|
|
65
|
+
:human
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def system_principal
|
|
69
|
+
@system_principal ||= Identity::Request.new(
|
|
70
|
+
principal_id: 'system:local',
|
|
71
|
+
canonical_name: 'system',
|
|
72
|
+
kind: :service,
|
|
73
|
+
groups: [],
|
|
74
|
+
source: :local
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'socket'
|
|
4
|
+
require 'concurrent/atomic/atomic_reference'
|
|
5
|
+
require 'concurrent/atomic/atomic_boolean'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Identity
|
|
9
|
+
module Process
|
|
10
|
+
EMPTY_STATE = {
|
|
11
|
+
id: nil,
|
|
12
|
+
canonical_name: nil,
|
|
13
|
+
kind: nil,
|
|
14
|
+
persistent: false,
|
|
15
|
+
groups: [].freeze,
|
|
16
|
+
metadata: {}.freeze
|
|
17
|
+
}.freeze
|
|
18
|
+
|
|
19
|
+
class << self
|
|
20
|
+
def id
|
|
21
|
+
state = @state.get
|
|
22
|
+
state[:id] || Legion.instance_id
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def canonical_name
|
|
26
|
+
state = @state.get
|
|
27
|
+
state[:canonical_name] || 'anonymous'
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def kind
|
|
31
|
+
@state.get[:kind]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def mode
|
|
35
|
+
Legion::Mode.current
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def queue_prefix
|
|
39
|
+
name = canonical_name
|
|
40
|
+
case mode
|
|
41
|
+
when :worker then "worker.#{name}.#{Legion.instance_id}"
|
|
42
|
+
when :infra then "infra.#{name}.#{safe_hostname}"
|
|
43
|
+
when :lite then "lite.#{name}.#{Legion.instance_id}"
|
|
44
|
+
else "agent.#{name}.#{safe_hostname}"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def resolved?
|
|
49
|
+
@resolved.true?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def persistent?
|
|
53
|
+
@state.get[:persistent] == true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def identity_hash
|
|
57
|
+
{
|
|
58
|
+
id: id,
|
|
59
|
+
canonical_name: canonical_name,
|
|
60
|
+
kind: kind,
|
|
61
|
+
mode: mode,
|
|
62
|
+
queue_prefix: queue_prefix,
|
|
63
|
+
resolved: resolved?,
|
|
64
|
+
persistent: persistent?,
|
|
65
|
+
groups: @state.get[:groups] || [],
|
|
66
|
+
metadata: @state.get[:metadata] || {}
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def bind!(provider, identity_hash)
|
|
71
|
+
@provider = provider
|
|
72
|
+
@state.set({
|
|
73
|
+
id: identity_hash[:id],
|
|
74
|
+
canonical_name: identity_hash[:canonical_name],
|
|
75
|
+
kind: identity_hash[:kind],
|
|
76
|
+
persistent: identity_hash.fetch(:persistent, true),
|
|
77
|
+
groups: Array(identity_hash[:groups]).compact.freeze,
|
|
78
|
+
metadata: identity_hash[:metadata].is_a?(Hash) ? identity_hash[:metadata].dup.freeze : {}.freeze
|
|
79
|
+
})
|
|
80
|
+
@resolved.make_true
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def bind_fallback!
|
|
84
|
+
user = ENV.fetch('USER', 'anonymous')
|
|
85
|
+
@state.set({
|
|
86
|
+
id: nil,
|
|
87
|
+
canonical_name: user,
|
|
88
|
+
kind: :human,
|
|
89
|
+
persistent: false,
|
|
90
|
+
groups: [].freeze,
|
|
91
|
+
metadata: {}.freeze
|
|
92
|
+
})
|
|
93
|
+
@resolved.make_false
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def refresh_credentials
|
|
97
|
+
return unless defined?(@provider) && @provider.respond_to?(:refresh)
|
|
98
|
+
|
|
99
|
+
@provider.refresh
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def reset!
|
|
103
|
+
@state = Concurrent::AtomicReference.new(EMPTY_STATE.dup)
|
|
104
|
+
@resolved = Concurrent::AtomicBoolean.new(false)
|
|
105
|
+
@provider = nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def safe_hostname
|
|
111
|
+
::Socket.gethostname.downcase
|
|
112
|
+
.gsub(/[^a-z0-9]+/, '-')
|
|
113
|
+
.gsub(/\A-+|-+\z/, '')
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Initialize atomics at module definition time
|
|
118
|
+
reset!
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Identity
|
|
5
|
+
class Request
|
|
6
|
+
attr_reader :principal_id, :canonical_name, :kind, :groups, :source, :metadata
|
|
7
|
+
|
|
8
|
+
def initialize(principal_id:, canonical_name:, kind:, groups: [], source: nil, metadata: {}) # rubocop:disable Metrics/ParameterLists
|
|
9
|
+
@principal_id = principal_id
|
|
10
|
+
@canonical_name = canonical_name
|
|
11
|
+
@kind = kind
|
|
12
|
+
@groups = groups.freeze
|
|
13
|
+
@source = source
|
|
14
|
+
@metadata = metadata.freeze
|
|
15
|
+
freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Reads the already-resolved identity from the Rack env (set by middleware).
|
|
19
|
+
# Returns nil when the key is absent.
|
|
20
|
+
def self.from_env(env)
|
|
21
|
+
env['legion.principal']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Builds a Request from a parsed auth claims hash with symbol keys:
|
|
25
|
+
# { sub:, name:, preferred_username:, kind:, groups:, source: }
|
|
26
|
+
def self.from_auth_context(claims_hash)
|
|
27
|
+
raw_name = claims_hash[:name] || claims_hash[:preferred_username] || ''
|
|
28
|
+
canonical = raw_name.to_s.strip.downcase.gsub('.', '-')
|
|
29
|
+
|
|
30
|
+
new(
|
|
31
|
+
principal_id: claims_hash[:sub],
|
|
32
|
+
canonical_name: canonical,
|
|
33
|
+
kind: claims_hash[:kind] || :human,
|
|
34
|
+
groups: claims_hash[:groups] || [],
|
|
35
|
+
source: claims_hash[:source]
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def identity_hash
|
|
40
|
+
{
|
|
41
|
+
principal_id: principal_id,
|
|
42
|
+
canonical_name: canonical_name,
|
|
43
|
+
kind: kind,
|
|
44
|
+
groups: groups,
|
|
45
|
+
source: source
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Maps to RBAC principal format.
|
|
50
|
+
# :service workers are represented as :worker in RBAC.
|
|
51
|
+
def to_rbac_principal
|
|
52
|
+
{
|
|
53
|
+
identity: canonical_name,
|
|
54
|
+
type: kind == :service ? :worker : kind
|
|
55
|
+
}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Pipeline-compatible caller hash (matches legion-llm pipeline format).
|
|
59
|
+
def to_caller_hash
|
|
60
|
+
{
|
|
61
|
+
requested_by: {
|
|
62
|
+
id: principal_id,
|
|
63
|
+
identity: canonical_name,
|
|
64
|
+
type: kind,
|
|
65
|
+
credential: source
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/legion/mode.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Legion
|
|
4
|
+
module Mode
|
|
5
|
+
LEGACY_MAP = { full: :agent, api: :worker, router: :worker, worker: :worker, lite: :lite }.freeze
|
|
6
|
+
|
|
7
|
+
class << self
|
|
8
|
+
def current
|
|
9
|
+
raw = ENV['LEGION_MODE'] ||
|
|
10
|
+
settings_dig(:mode) ||
|
|
11
|
+
settings_dig(:process, :mode) ||
|
|
12
|
+
legacy_role
|
|
13
|
+
normalize(raw)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def agent?
|
|
17
|
+
current == :agent
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def worker?
|
|
21
|
+
current == :worker
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def infra?
|
|
25
|
+
current == :infra
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def lite?
|
|
29
|
+
current == :lite
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def normalize(raw)
|
|
35
|
+
return :agent if raw.nil?
|
|
36
|
+
|
|
37
|
+
sym = raw.to_s.downcase.strip.to_sym
|
|
38
|
+
return sym if %i[agent worker infra lite].include?(sym)
|
|
39
|
+
|
|
40
|
+
LEGACY_MAP.fetch(sym, :agent)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def legacy_role
|
|
44
|
+
settings_dig(:process, :role)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def fetch_setting_value(container, key)
|
|
48
|
+
value = container[key]
|
|
49
|
+
return value unless value.nil?
|
|
50
|
+
|
|
51
|
+
alternate_key = case key
|
|
52
|
+
when Symbol then key.to_s
|
|
53
|
+
when String then key.to_sym
|
|
54
|
+
end
|
|
55
|
+
return value if alternate_key.nil?
|
|
56
|
+
|
|
57
|
+
container[alternate_key]
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def settings_dig(*keys)
|
|
61
|
+
return nil unless defined?(Legion::Settings) && Legion::Settings.respond_to?(:[])
|
|
62
|
+
|
|
63
|
+
result = Legion::Settings
|
|
64
|
+
keys.each do |k|
|
|
65
|
+
return nil unless result.respond_to?(:[])
|
|
66
|
+
|
|
67
|
+
result = fetch_setting_value(result, k)
|
|
68
|
+
return nil if result.nil? && keys.last != k
|
|
69
|
+
end
|
|
70
|
+
result
|
|
71
|
+
rescue StandardError
|
|
72
|
+
nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/legion/process_role.rb
CHANGED
|
@@ -4,10 +4,12 @@ module Legion
|
|
|
4
4
|
module ProcessRole
|
|
5
5
|
ROLES = {
|
|
6
6
|
full: { transport: true, cache: true, data: true, extensions: true, api: true, llm: true, gaia: true, crypt: true, supervision: true },
|
|
7
|
+
agent: { transport: true, cache: true, data: true, extensions: true, api: true, llm: true, gaia: true, crypt: true, supervision: true },
|
|
7
8
|
api: { transport: true, cache: true, data: true, extensions: false, api: true, llm: false, gaia: false, crypt: true, supervision: false },
|
|
8
9
|
worker: { transport: true, cache: true, data: true, extensions: true, api: false, llm: true, gaia: true, crypt: true, supervision: true },
|
|
9
10
|
router: { transport: true, cache: true, data: false, extensions: true, api: false, llm: false, gaia: false, crypt: true, supervision: false },
|
|
10
|
-
lite: { transport: true, cache: true, data: true, extensions: true, api: true, llm: true, gaia: true, crypt: false, supervision: true }
|
|
11
|
+
lite: { transport: true, cache: true, data: true, extensions: true, api: true, llm: true, gaia: true, crypt: false, supervision: true },
|
|
12
|
+
infra: { transport: true, cache: true, data: true, extensions: true, api: true, llm: true, gaia: true, crypt: true, supervision: true }
|
|
11
13
|
}.freeze
|
|
12
14
|
|
|
13
15
|
def self.resolve(role_name)
|
|
@@ -21,7 +23,7 @@ module Legion
|
|
|
21
23
|
|
|
22
24
|
def self.current
|
|
23
25
|
settings = begin
|
|
24
|
-
Legion::Settings[:process]
|
|
26
|
+
defined?(Legion::Settings) ? Legion::Settings[:process] : nil
|
|
25
27
|
rescue StandardError => e
|
|
26
28
|
Legion::Logging.debug "ProcessRole#current failed to read process settings: #{e.message}" if defined?(Legion::Logging)
|
|
27
29
|
nil
|
data/lib/legion/readiness.rb
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'concurrent'
|
|
4
|
+
|
|
3
5
|
module Legion
|
|
4
6
|
module Readiness
|
|
5
|
-
|
|
7
|
+
REQUIRED_COMPONENTS = %i[settings crypt transport cache data extensions api].freeze
|
|
8
|
+
OPTIONAL_COMPONENTS = %i[rbac llm apollo gaia identity].freeze
|
|
9
|
+
COMPONENTS = (REQUIRED_COMPONENTS + OPTIONAL_COMPONENTS).freeze
|
|
6
10
|
DRAIN_TIMEOUT = 5
|
|
7
11
|
|
|
8
12
|
class << self
|
|
9
13
|
def status
|
|
10
|
-
@status ||=
|
|
14
|
+
@status ||= Concurrent::Hash.new
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def mark_ready(component)
|
|
@@ -20,14 +24,19 @@ module Legion
|
|
|
20
24
|
Legion::Logging.debug "[Readiness] #{component} is not ready" if defined?(Legion::Logging)
|
|
21
25
|
end
|
|
22
26
|
|
|
27
|
+
def mark_skipped(component)
|
|
28
|
+
status[component.to_sym] = :skipped
|
|
29
|
+
Legion::Logging.debug "[Readiness] #{component} skipped (optional)" if defined?(Legion::Logging)
|
|
30
|
+
end
|
|
31
|
+
|
|
23
32
|
def ready?(component = nil)
|
|
24
33
|
if component
|
|
25
|
-
result = status[component.to_sym]
|
|
34
|
+
result = [true, :skipped].include?(status[component.to_sym])
|
|
26
35
|
Legion::Logging.warn "[Readiness] #{component} is not ready" if !result && defined?(Legion::Logging)
|
|
27
36
|
return result
|
|
28
37
|
end
|
|
29
38
|
|
|
30
|
-
not_ready = COMPONENTS.reject { |c| status[c]
|
|
39
|
+
not_ready = COMPONENTS.reject { |c| [true, :skipped].include?(status[c]) }
|
|
31
40
|
not_ready.each { |c| Legion::Logging.warn "[Readiness] #{c} is not ready" } if !not_ready.empty? && defined?(Legion::Logging)
|
|
32
41
|
not_ready.empty?
|
|
33
42
|
end
|
|
@@ -43,12 +52,13 @@ module Legion
|
|
|
43
52
|
end
|
|
44
53
|
|
|
45
54
|
def reset
|
|
46
|
-
@status =
|
|
55
|
+
@status = nil
|
|
47
56
|
end
|
|
48
57
|
|
|
49
58
|
def to_h
|
|
50
59
|
COMPONENTS.to_h do |c|
|
|
51
|
-
|
|
60
|
+
val = status[c]
|
|
61
|
+
[c, [true, :skipped].include?(val)]
|
|
52
62
|
end
|
|
53
63
|
end
|
|
54
64
|
end
|