sneakers-queue-migrator 0.1.0 → 0.1.1
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/Gemfile.lock +1 -1
- data/lib/sneakers/migrator/version.rb +1 -1
- data/lib/sneakers/migrator.rb +91 -75
- data/renovate.json +6 -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: cf52a4bc1cb067db797ea5ea317fdbaf21bbcf30c1770b9324dce3e809387b95
|
4
|
+
data.tar.gz: 0ae00f97a9ba327a9edd0ff546f9bcd26101ecee8104029d0e9d0115830cadea
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 67a61910c8b7a2acd86ab0c14f097390f76af0d6bf1f6ec240df4bb75f49aa953e325852b0270356e35b2e7b521e28adeb41143586bce104dafbd0f7335cf3c7
|
7
|
+
data.tar.gz: 3a8ea11e7cb1683017b3f246378fa377029221a1b10385f518b0d9d65f41684dad5c7de48f6fd2ccf476fdb72990f88c37ebc34cc6086c571d04447bf6d0f132
|
data/Gemfile.lock
CHANGED
data/lib/sneakers/migrator.rb
CHANGED
@@ -25,16 +25,26 @@ module Sneakers
|
|
25
25
|
raise Error, "You must have either the 'sneakers' or 'kicks' gem installed to use subscriber discovery."
|
26
26
|
end
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
|
+
# Load all subscribers (recursively)
|
29
30
|
subscriber_paths.each do |path|
|
30
|
-
Dir[File.join(path, "*.rb")].each
|
31
|
+
Dir[File.join(path, "**", "*.rb")].sort.each do |f|
|
32
|
+
logger.puts "Migrator: Loading subscriber file: #{f}"
|
33
|
+
require f
|
34
|
+
end
|
31
35
|
end
|
36
|
+
|
32
37
|
# Find all worker classes for the loaded framework
|
33
38
|
subscribers = ObjectSpace.each_object(Class).select do |klass|
|
34
39
|
klass.included_modules.include?(::Sneakers::Worker)
|
35
40
|
rescue StandardError
|
36
41
|
false
|
37
42
|
end
|
43
|
+
|
44
|
+
# output found subscribers
|
45
|
+
logger.puts "Migrator: Found #{subscribers.size} subscribers:"
|
46
|
+
subscribers.each { |s| logger.puts " - #{s.name}" }
|
47
|
+
|
38
48
|
queues = subscribers.map do |klass|
|
39
49
|
opts = klass.instance_variable_get(:@queue_opts) || {}
|
40
50
|
name = klass.instance_variable_get(:@queue_name) ||
|
@@ -59,90 +69,98 @@ module Sneakers
|
|
59
69
|
all_queues = fetch_all_queues(amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass)
|
60
70
|
|
61
71
|
queues.each do |q|
|
62
|
-
|
63
|
-
|
64
|
-
if queue_info.nil?
|
72
|
+
existing_queue_info = all_queues.find { |info| info["name"] == q[:name] }
|
73
|
+
if existing_queue_info.nil?
|
65
74
|
logger.puts "Migrator: Queue #{q[:name]} does not exist, creating..."
|
66
75
|
ch.queue(q[:name], durable: q[:durable], arguments: q[:arguments])
|
67
76
|
next
|
68
77
|
end
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
logger.puts "Migrator: Current args: #{current_args.inspect}"
|
73
|
-
logger.puts "Migrator: Desired args: #{desired_args.inspect}"
|
74
|
-
if queue_args_match?({ arguments: current_args }, desired_args)
|
75
|
-
logger.puts "Migrator: Queue #{q[:name]} arguments match, nothing to do."
|
76
|
-
next
|
77
|
-
end
|
78
|
-
logger.puts "Migrator: Queue #{q[:name]} arguments differ, migrating..."
|
78
|
+
migrate_queue!(q, ch, amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, logger, existing_queue_info)
|
79
|
+
end
|
80
|
+
end
|
79
81
|
|
80
|
-
|
81
|
-
bindings = fetch_queue_bindings(amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, queue_info)
|
82
|
-
logger.puts "Migrator: Found bindings: #{bindings.inspect}"
|
82
|
+
private
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
def migrate_queue!(q, ch, amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, logger, existing_queue_info)
|
85
|
+
tmp_queue_name = "#{q[:name]}_tmp"
|
86
|
+
current_args = (existing_queue_info["arguments"] || {}).transform_keys(&:to_s)
|
87
|
+
desired_args = (q[:arguments] || {}).transform_keys(&:to_s)
|
88
|
+
logger.puts "Migrator: Checking queue: #{q[:name]}"
|
89
|
+
if queue_args_match?({ arguments: current_args }, desired_args)
|
90
|
+
logger.puts "Migrator: Queue #{q[:name]} arguments match, nothing to do."
|
91
|
+
return
|
92
|
+
end
|
93
|
+
logger.puts "Migrator: Current args: #{current_args.inspect}"
|
94
|
+
logger.puts "Migrator: Desired args: #{desired_args.inspect}"
|
87
95
|
|
88
|
-
|
89
|
-
ch.queue(tmp_queue_name).bind(q[:exchange], routing_key: rk)
|
90
|
-
# unbind the original queue from the exchange
|
91
|
-
logger.puts "Migrator: Unbinding queue #{q[:name]} from exchange #{q[:exchange]} with routing key #{rk}"
|
92
|
-
ch.queue(q[:name], passive: true).unbind(q[:exchange], routing_key: rk)
|
93
|
-
end
|
94
|
-
count = 0
|
95
|
-
queue = ch.queue(q[:name], passive: true)
|
96
|
-
loop do
|
97
|
-
delivery_info, headers, payload = queue.pop
|
98
|
-
break unless payload
|
99
|
-
|
100
|
-
logger.puts "Migrator: Moving message to #{tmp_queue_name}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
|
101
|
-
safe_basic_publish(ch, payload, tmp_queue_name, headers)
|
102
|
-
count += 1
|
103
|
-
end
|
104
|
-
logger.puts "Migrator: Moved #{count} messages to #{tmp_queue_name}"
|
105
|
-
ch.queue(q[:name], passive: true).delete
|
106
|
-
|
107
|
-
# create new queue with desired arguments
|
108
|
-
logger.puts "Migrator: Recreating queue #{q[:name]} with new arguments: #{q[:arguments].inspect}"
|
109
|
-
ch.queue(q[:name], durable: q[:durable], arguments: q[:arguments])
|
110
|
-
|
111
|
-
# --- Restore bindings ---
|
112
|
-
bindings.each do |binding|
|
113
|
-
# Only restore if it's a binding from an exchange (not from a queue)
|
114
|
-
next unless binding["source"] && !binding["source"].empty?
|
115
|
-
|
116
|
-
ch.queue(q[:name]).bind(binding["source"], routing_key: binding["routing_key"])
|
117
|
-
logger.puts "Migrator: Restored binding: exchange=#{binding["source"]}, routing_key=#{binding["routing_key"]}"
|
118
|
-
# now drop the tmp queue binding
|
119
|
-
ch.queue(tmp_queue_name).unbind(binding["source"], routing_key: binding["routing_key"])
|
120
|
-
rescue StandardError => e
|
121
|
-
logger.puts "Migrator: Failed to restore binding: #{e.class}: #{e.message}"
|
122
|
-
end
|
96
|
+
vhost = existing_queue_info["vhost"] || "/"
|
123
97
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
delivery_info, headers, payload = tmp_queue.pop
|
128
|
-
break unless payload
|
98
|
+
# --- Fetch and store bindings (exchange/routing_key pairs) ---
|
99
|
+
bindings = fetch_queue_bindings(amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, vhost, existing_queue_info["name"])
|
100
|
+
logger.puts "Migrator: Found bindings: #{bindings.inspect}"
|
129
101
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
ch.queue(tmp_queue_name,
|
136
|
-
|
102
|
+
ch.queue(tmp_queue_name, durable: existing_queue_info["durable"], arguments: desired_args)
|
103
|
+
Array(q[:routing_keys]).each do |rk|
|
104
|
+
next if rk.nil? || rk.empty?
|
105
|
+
|
106
|
+
# bind the tmp queue first so new messages go somewhere
|
107
|
+
ch.queue(tmp_queue_name).bind(q[:exchange], routing_key: rk)
|
108
|
+
# unbind the original queue from the exchange
|
109
|
+
logger.puts "Migrator: Unbinding queue #{q[:name]} from exchange #{q[:exchange]} with routing key #{rk}"
|
110
|
+
ch.queue(q[:name], passive: true).unbind(q[:exchange], routing_key: rk)
|
111
|
+
end
|
112
|
+
count = 0
|
113
|
+
queue = ch.queue(q[:name], passive: true)
|
114
|
+
loop do
|
115
|
+
delivery_info, headers, payload = queue.pop
|
116
|
+
break unless payload
|
117
|
+
|
118
|
+
logger.puts "Migrator: Moving message to #{tmp_queue_name}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
|
119
|
+
safe_basic_publish(ch, payload, tmp_queue_name, headers)
|
120
|
+
count += 1
|
121
|
+
end
|
122
|
+
logger.puts "Migrator: Moved #{count} messages to #{tmp_queue_name}"
|
123
|
+
ch.queue(q[:name], passive: true).delete
|
124
|
+
|
125
|
+
# create new queue with desired arguments
|
126
|
+
logger.puts "Migrator: Recreating queue #{q[:name]} with new arguments: #{q[:arguments].inspect}"
|
127
|
+
ch.queue(q[:name], durable: q[:durable], arguments: q[:arguments])
|
128
|
+
|
129
|
+
# --- Restore bindings ---
|
130
|
+
bindings.each do |binding|
|
131
|
+
# Only restore if it's a binding from an exchange (not from a queue)
|
132
|
+
next unless binding["source"] && !binding["source"].empty?
|
133
|
+
|
134
|
+
ch.queue(q[:name]).bind(binding["source"], routing_key: binding["routing_key"])
|
135
|
+
logger.puts "Migrator: Restored binding: exchange=#{binding["source"]}, routing_key=#{binding["routing_key"]}"
|
136
|
+
# now drop the tmp queue binding
|
137
|
+
ch.queue(tmp_queue_name).unbind(binding["source"], routing_key: binding["routing_key"])
|
138
|
+
rescue StandardError => e
|
139
|
+
logger.puts "Migrator: Failed to restore binding: #{e.class}: #{e.message}"
|
137
140
|
end
|
138
|
-
|
139
|
-
|
141
|
+
|
142
|
+
count = 0
|
143
|
+
tmp_queue = ch.queue(tmp_queue_name, passive: true)
|
144
|
+
loop do
|
145
|
+
delivery_info, headers, payload = tmp_queue.pop
|
146
|
+
break unless payload
|
147
|
+
|
148
|
+
logger.puts "Migrator: Moving message back to #{q[:name]}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
|
149
|
+
safe_basic_publish(ch, payload, q[:name], headers)
|
150
|
+
count += 1
|
151
|
+
end
|
152
|
+
logger.puts "Migrator: Moved #{count} messages to #{q[:name]}"
|
153
|
+
ch.queue(tmp_queue_name, passive: true).delete
|
154
|
+
logger.puts "Migrator: Migration for #{q[:name]} complete."
|
155
|
+
rescue Bunny::NotFound => e
|
156
|
+
logger.puts "Migrator: Queue #{q[:name]} not found, skipping migration. Error: #{e.message}"
|
157
|
+
rescue StandardError => e
|
158
|
+
logger.puts "Migrator: Error migrating queue #{q[:name]}: #{e.class}: #{e.message}"
|
159
|
+
raise
|
140
160
|
end
|
141
161
|
|
142
162
|
# Fetches all bindings for a queue using the RabbitMQ Management API
|
143
|
-
def fetch_queue_bindings(api_url, user, pass,
|
144
|
-
vhost = queue_info["vhost"] || "/"
|
145
|
-
queue_name = queue_info["name"]
|
163
|
+
def fetch_queue_bindings(api_url, user, pass, vhost, queue_name)
|
146
164
|
uri = URI.parse(api_url)
|
147
165
|
scheme = uri.scheme || "http"
|
148
166
|
host = uri.host
|
@@ -160,8 +178,6 @@ module Sneakers
|
|
160
178
|
JSON.parse(res.body)
|
161
179
|
end
|
162
180
|
|
163
|
-
private
|
164
|
-
|
165
181
|
def fetch_all_queues(api_url, user, pass)
|
166
182
|
uri = URI.parse(api_url)
|
167
183
|
scheme = uri.scheme || "http"
|
data/renovate.json
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sneakers-queue-migrator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Gane
|
@@ -54,6 +54,7 @@ files:
|
|
54
54
|
- docker-compose.yml
|
55
55
|
- lib/sneakers/migrator.rb
|
56
56
|
- lib/sneakers/migrator/version.rb
|
57
|
+
- renovate.json
|
57
58
|
- sneakers-queue-migrator.gemspec
|
58
59
|
homepage: https://gitlab.nexdev.uk/nexus-mods/public/sneakers-queue-migrator
|
59
60
|
licenses: []
|