lex-node 0.3.0 → 0.3.2
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/CHANGELOG.md +12 -0
- data/CLAUDE.md +1 -1
- data/README.md +1 -1
- data/lib/legion/extensions/node/actors/push_key.rb +1 -1
- data/lib/legion/extensions/node/helpers/rabbitmq.rb +128 -0
- data/lib/legion/extensions/node/transport/messages/beat.rb +9 -0
- data/lib/legion/extensions/node/version.rb +1 -1
- data/lib/legion/extensions/node.rb +1 -0
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 175c70421d225a25f82b9ead14a05e86bf144fcf34bb55c90ce77555c8274d69
|
|
4
|
+
data.tar.gz: fb186fbbeb5da2da48ba9ac7ec57621d36189520bf3b0fa4dc1e43d22de9bf41
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab54030dfc8acc9b1dc941cd283552f9fd0d502dd1066a7999cc79443cb9b3493c31656e613432c0c44e16358346f2795535ef076af59bad9895fbcf6620ad78
|
|
7
|
+
data.tar.gz: 050a882c62acf8b4f44e0816964a91a351d3d907de445b535c2275ad9dd183a589c338929f259261124bbec01546181321b23a388b21833a6563da59432ac6dc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.3.2] - 2026-03-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- `Helpers::Rabbitmq` module: queries RabbitMQ Management API for cluster health metrics (node count, quorum queue leaders, shovel link status)
|
|
7
|
+
- Beat heartbeat message now includes `rabbitmq_cluster` section with node count, quorum leaders, and shovel links
|
|
8
|
+
- 15 new specs covering all Rabbitmq helper methods (111 total, 0 failures)
|
|
9
|
+
|
|
10
|
+
## [0.3.1] - 2026-03-19
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
- `push_key` actor referenced removed `Runners::Crypt` (consolidated into `Runners::Node` in v0.3.0); updated actor and spec to use `Runners::Node`
|
|
14
|
+
|
|
3
15
|
## [0.3.0] - 2026-03-18
|
|
4
16
|
|
|
5
17
|
### Fixed
|
data/CLAUDE.md
CHANGED
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Node identity and cluster management for [LegionIO](https://github.com/LegionIO/LegionIO). Handles heartbeat broadcasting, dynamic configuration distribution, cluster secret exchange, Vault token management, and RSA public key distribution between nodes.
|
|
4
4
|
|
|
5
|
-
**Version**: 0.3.
|
|
5
|
+
**Version**: 0.3.1
|
|
6
6
|
|
|
7
7
|
This is a core LEX installed by default with LegionIO.
|
|
8
8
|
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
require 'json'
|
|
5
|
+
require 'uri'
|
|
6
|
+
|
|
7
|
+
module Legion
|
|
8
|
+
module Extensions
|
|
9
|
+
module Node
|
|
10
|
+
module Helpers
|
|
11
|
+
module Rabbitmq
|
|
12
|
+
module_function
|
|
13
|
+
|
|
14
|
+
def cluster_health(settings: nil)
|
|
15
|
+
settings ||= resolve_settings
|
|
16
|
+
http = build_http(settings)
|
|
17
|
+
{
|
|
18
|
+
node_count: fetch_node_count(http, settings),
|
|
19
|
+
quorum_leaders: fetch_quorum_leaders(http, settings),
|
|
20
|
+
shovel_links: fetch_shovel_links(http, settings)
|
|
21
|
+
}
|
|
22
|
+
rescue Errno::ECONNREFUSED, Errno::ETIMEDOUT, SocketError, Net::OpenTimeout, Net::ReadTimeout => e
|
|
23
|
+
log_warn("RabbitMQ management API unreachable: #{e.message}")
|
|
24
|
+
{ node_count: unreachable, quorum_leaders: unreachable, shovel_links: unreachable }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def fetch_node_count(http, settings)
|
|
28
|
+
body = api_get(http, '/api/nodes', settings)
|
|
29
|
+
return unreachable unless body
|
|
30
|
+
|
|
31
|
+
running = body.count { |n| n['running'] }
|
|
32
|
+
total = body.size
|
|
33
|
+
status = if running == total
|
|
34
|
+
'ok'
|
|
35
|
+
else
|
|
36
|
+
(running.positive? ? 'warn' : 'critical')
|
|
37
|
+
end
|
|
38
|
+
{ status: status, running: running, total: total }
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def fetch_quorum_leaders(http, settings)
|
|
42
|
+
vhost = settings.dig(:connection, :vhost) || '/'
|
|
43
|
+
encoded_vhost = URI.encode_www_form_component(vhost)
|
|
44
|
+
body = api_get(http, "/api/queues/#{encoded_vhost}", settings)
|
|
45
|
+
return unreachable unless body
|
|
46
|
+
|
|
47
|
+
quorum_queues = body.select { |q| q['type'] == 'quorum' }
|
|
48
|
+
return { status: 'ok', quorum_queues: 0, leaders_on_this_node: 0 } if quorum_queues.empty?
|
|
49
|
+
|
|
50
|
+
node_name = resolve_node_name(http, settings)
|
|
51
|
+
leaders_here = quorum_queues.count { |q| q['leader'] == node_name }
|
|
52
|
+
{ status: 'ok', quorum_queues: quorum_queues.size, leaders_on_this_node: leaders_here }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def fetch_shovel_links(http, settings)
|
|
56
|
+
vhost = settings.dig(:connection, :vhost) || '/'
|
|
57
|
+
encoded_vhost = URI.encode_www_form_component(vhost)
|
|
58
|
+
body = api_get(http, "/api/shovels/#{encoded_vhost}", settings)
|
|
59
|
+
return unreachable unless body
|
|
60
|
+
|
|
61
|
+
running = body.count { |s| s['state'] == 'running' }
|
|
62
|
+
total = body.size
|
|
63
|
+
status = if total.zero?
|
|
64
|
+
'ok'
|
|
65
|
+
else
|
|
66
|
+
(running == total ? 'ok' : 'warn')
|
|
67
|
+
end
|
|
68
|
+
{ status: status, total: total, running: running }
|
|
69
|
+
rescue StandardError
|
|
70
|
+
{ status: 'ok', total: 0, running: 0 }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def build_http(settings)
|
|
74
|
+
host = settings.dig(:connection, :host) || '127.0.0.1'
|
|
75
|
+
port = settings[:management_port] || 15_672
|
|
76
|
+
http = Net::HTTP.new(host, port)
|
|
77
|
+
http.open_timeout = 3
|
|
78
|
+
http.read_timeout = 5
|
|
79
|
+
http
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def api_get(http, path, settings)
|
|
83
|
+
req = Net::HTTP::Get.new(path)
|
|
84
|
+
req.basic_auth(
|
|
85
|
+
settings.dig(:connection, :user) || 'guest',
|
|
86
|
+
settings.dig(:connection, :password) || 'guest'
|
|
87
|
+
)
|
|
88
|
+
response = http.request(req)
|
|
89
|
+
return nil unless response.code.start_with?('2')
|
|
90
|
+
|
|
91
|
+
::JSON.parse(response.body)
|
|
92
|
+
rescue StandardError
|
|
93
|
+
nil
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def resolve_node_name(http, settings)
|
|
97
|
+
body = api_get(http, '/api/whoami', settings)
|
|
98
|
+
return nil unless body
|
|
99
|
+
|
|
100
|
+
nodes = api_get(http, '/api/nodes', settings)
|
|
101
|
+
return nil unless nodes
|
|
102
|
+
|
|
103
|
+
nodes.first&.dig('name')
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def resolve_settings
|
|
107
|
+
return Legion::Settings[:transport].to_h if defined?(Legion::Settings) && Legion::Settings.respond_to?(:[])
|
|
108
|
+
|
|
109
|
+
{ connection: { host: '127.0.0.1', user: 'guest', password: 'guest', vhost: '/' },
|
|
110
|
+
management_port: 15_672 }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def unreachable
|
|
114
|
+
{ status: 'unknown', detail: 'management api unreachable' }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def log_warn(msg)
|
|
118
|
+
if defined?(Legion::Logging)
|
|
119
|
+
Legion::Logging.warn { msg }
|
|
120
|
+
elsif defined?(Legion::Transport) && Legion::Transport.respond_to?(:logger)
|
|
121
|
+
Legion::Transport.logger.warn(msg)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -33,6 +33,7 @@ module Legion
|
|
|
33
33
|
hash[:version] = Legion::VERSION if defined?(Legion::VERSION)
|
|
34
34
|
hash[:metrics] = collect_metrics
|
|
35
35
|
hash[:hosted_worker_ids] = collect_worker_ids
|
|
36
|
+
hash[:rabbitmq_cluster] = collect_rabbitmq_cluster
|
|
36
37
|
hash
|
|
37
38
|
end
|
|
38
39
|
|
|
@@ -76,6 +77,14 @@ module Legion
|
|
|
76
77
|
(::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - BOOT_TIME).round(0)
|
|
77
78
|
end
|
|
78
79
|
|
|
80
|
+
def collect_rabbitmq_cluster
|
|
81
|
+
return { status: 'unknown', detail: 'helper not loaded' } unless defined?(Legion::Extensions::Node::Helpers::Rabbitmq)
|
|
82
|
+
|
|
83
|
+
Legion::Extensions::Node::Helpers::Rabbitmq.cluster_health
|
|
84
|
+
rescue StandardError => e
|
|
85
|
+
{ status: 'unknown', detail: e.message }
|
|
86
|
+
end
|
|
87
|
+
|
|
79
88
|
def collect_worker_ids
|
|
80
89
|
return [] unless defined?(Legion::DigitalWorker)
|
|
81
90
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: lex-node
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Esity
|
|
@@ -53,6 +53,7 @@ files:
|
|
|
53
53
|
- lib/legion/extensions/node/data_test/migrations/002_node_history_table.rb
|
|
54
54
|
- lib/legion/extensions/node/data_test/migrations/003_legion_version_colume.rb
|
|
55
55
|
- lib/legion/extensions/node/data_test/migrations/004_node_extensions.rb
|
|
56
|
+
- lib/legion/extensions/node/helpers/rabbitmq.rb
|
|
56
57
|
- lib/legion/extensions/node/runners/beat.rb
|
|
57
58
|
- lib/legion/extensions/node/runners/node.rb
|
|
58
59
|
- lib/legion/extensions/node/runners/vault.rb
|