lex-node 0.3.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a8bce7f974591cba97508bd1d4a9033315f4207050da3996f082438267c1de9c
4
- data.tar.gz: 3af059ccb67edb50d52c749adbd8014cd29c9d75af4fa3b85c84c678a453301b
3
+ metadata.gz: 175c70421d225a25f82b9ead14a05e86bf144fcf34bb55c90ce77555c8274d69
4
+ data.tar.gz: fb186fbbeb5da2da48ba9ac7ec57621d36189520bf3b0fa4dc1e43d22de9bf41
5
5
  SHA512:
6
- metadata.gz: 5dd82b1f0b200c50a7d4b3cedc639e73ad47b27335a8405182b6f2c1a9f6372930c51d1b3c52bcd0403198593d6b22679e989e83f944e75b28a59d86a19ce339
7
- data.tar.gz: 5a3c3d9ffdccca307b9e8981b73f18021eabdfb2ee44167231b75290d7c8dd33fd2474e08db6ccb377eede6fb634e5274de5c558fe77aa914a9818a96c88ebc9
6
+ metadata.gz: ab54030dfc8acc9b1dc941cd283552f9fd0d502dd1066a7999cc79443cb9b3493c31656e613432c0c44e16358346f2795535ef076af59bad9895fbcf6620ad78
7
+ data.tar.gz: 050a882c62acf8b4f44e0816964a91a351d3d907de445b535c2275ad9dd183a589c338929f259261124bbec01546181321b23a388b21833a6563da59432ac6dc
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
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
+
3
10
  ## [0.3.1] - 2026-03-19
4
11
 
5
12
  ### Fixed
data/CLAUDE.md CHANGED
@@ -10,7 +10,7 @@ Core Legion Extension responsible for node identity within a LegionIO cluster. H
10
10
 
11
11
  **GitHub**: https://github.com/LegionIO/lex-node
12
12
  **License**: MIT
13
- **Version**: 0.3.0
13
+ **Version**: 0.3.1
14
14
 
15
15
  ## Architecture
16
16
 
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.0
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
 
@@ -3,7 +3,7 @@
3
3
  module Legion
4
4
  module Extensions
5
5
  module Node
6
- VERSION = '0.3.1'
6
+ VERSION = '0.3.2'
7
7
  end
8
8
  end
9
9
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'legion/extensions/node/version'
4
+ require 'legion/extensions/node/helpers/rabbitmq'
4
5
 
5
6
  module Legion
6
7
  module Extensions
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.1
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