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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc0b131523f71a4bd19d4d5d0275fbe7e47814e55ad51b0d927b2672ed7578cb
4
- data.tar.gz: 3ee9adb466b69696575011fd7a4a192cac88f9370226b33566cb07a084a637f8
3
+ metadata.gz: cf52a4bc1cb067db797ea5ea317fdbaf21bbcf30c1770b9324dce3e809387b95
4
+ data.tar.gz: 0ae00f97a9ba327a9edd0ff546f9bcd26101ecee8104029d0e9d0115830cadea
5
5
  SHA512:
6
- metadata.gz: 663174974230df88b4c71ed747c682c5329347d5c815087fd0c59cfa93d05c2e05d8dd196d1345a05b2c4e787850513fc40e727e5a0a7ba61dd63ed0d63677d1
7
- data.tar.gz: b4023d485167c76211c06ba6765c550589fceaae77e1c6ed47d91a22d6d228a31b673168d06429a8fbc2b207e94f8845b06243b13386a71e3dc607403058f724
6
+ metadata.gz: 67a61910c8b7a2acd86ab0c14f097390f76af0d6bf1f6ec240df4bb75f49aa953e325852b0270356e35b2e7b521e28adeb41143586bce104dafbd0f7335cf3c7
7
+ data.tar.gz: 3a8ea11e7cb1683017b3f246378fa377029221a1b10385f518b0d9d65f41684dad5c7de48f6fd2ccf476fdb72990f88c37ebc34cc6086c571d04447bf6d0f132
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- sneakers-queue-migrator (0.1.0)
4
+ sneakers-queue-migrator (0.1.1)
5
5
  bunny (~> 2.0)
6
6
  json (~> 2.0)
7
7
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sneakers
4
4
  module Migrator
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.1"
6
6
  end
7
7
  end
@@ -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
- # Load all subscribers
28
+
29
+ # Load all subscribers (recursively)
29
30
  subscriber_paths.each do |path|
30
- Dir[File.join(path, "*.rb")].each { |f| require f }
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
- queue_info = all_queues.find { |info| info["name"] == q[:name] }
63
- tmp_queue_name = "#{q[:name]}_tmp"
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
- current_args = (queue_info["arguments"] || {}).transform_keys(&:to_s)
70
- desired_args = (q[:arguments] || {}).transform_keys(&:to_s)
71
- logger.puts "Migrator: Checking queue: #{q[:name]}"
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
- # --- Fetch and store bindings (exchange/routing_key pairs) ---
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
- ch.queue(tmp_queue_name, durable: queue_info["durable"], arguments: desired_args)
85
- Array(q[:routing_keys]).each do |rk|
86
- next if rk.nil? || rk.empty?
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
- # bind the tmp queue first so new messages go somewhere
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
- count = 0
125
- tmp_queue = ch.queue(tmp_queue_name, passive: true)
126
- loop do
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
- logger.puts "Migrator: Moving message back to #{q[:name]}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
131
- safe_basic_publish(ch, payload, q[:name], headers)
132
- count += 1
133
- end
134
- logger.puts "Migrator: Moved #{count} messages to #{q[:name]}"
135
- ch.queue(tmp_queue_name, passive: true).delete
136
- logger.puts "Migrator: Migration for #{q[:name]} complete."
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
- ch.close
139
- conn.close
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, queue_info)
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
@@ -0,0 +1,6 @@
1
+ {
2
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3
+ "extends": [
4
+ "config:recommended"
5
+ ]
6
+ }
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.0
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: []