sneakers-queue-migrator 0.1.0 → 0.1.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: fc0b131523f71a4bd19d4d5d0275fbe7e47814e55ad51b0d927b2672ed7578cb
4
- data.tar.gz: 3ee9adb466b69696575011fd7a4a192cac88f9370226b33566cb07a084a637f8
3
+ metadata.gz: 3531ca5afff55adbc8e8da879f3d82d8aa7018197a3da1fcde32cc5adfa117c7
4
+ data.tar.gz: a437f2c1146e3f4945351d4bf713db726085ba92f34e614f0a93f04aa21dca64
5
5
  SHA512:
6
- metadata.gz: 663174974230df88b4c71ed747c682c5329347d5c815087fd0c59cfa93d05c2e05d8dd196d1345a05b2c4e787850513fc40e727e5a0a7ba61dd63ed0d63677d1
7
- data.tar.gz: b4023d485167c76211c06ba6765c550589fceaae77e1c6ed47d91a22d6d228a31b673168d06429a8fbc2b207e94f8845b06243b13386a71e3dc607403058f724
6
+ metadata.gz: 97a3d839285593a04c2f5fec1a45694f1fe740720dfb03bb3185c89558d196e398ea237e13d317d86a77ac7f1d240176aeccfbd4b9212dd3d1c7ff060e3b54c0
7
+ data.tar.gz: d1e9f3c98b5464cea668abff64c1f281f3132077288371b83550d816973c7ae7007a09f44ff4842d19374c53e8f426edbf8a72b63edc1191b3d623e63b6aa267
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.2)
5
5
  bunny (~> 2.0)
6
6
  json (~> 2.0)
7
7
 
data/README.md CHANGED
@@ -35,7 +35,7 @@ require 'sneakers/migrator'
35
35
  Sneakers::Migrator.migrate!(
36
36
  amqp_url: 'amqp://user:password@rabbitmq:5672/',
37
37
  amqp_api_url: 'http://user:password@rabbitmq:15672',
38
- subscriber_paths: ['app/workers/**/*.rb']
38
+ subscriber_paths: ['app/workers']
39
39
  )
40
40
  ```
41
41
 
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Sneakers
4
4
  module Migrator
5
- VERSION = "0.1.0"
5
+ VERSION = "0.1.2"
6
6
  end
7
7
  end
@@ -25,16 +25,31 @@ 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
+ begin
34
+ require f
35
+ rescue LoadError => e
36
+ logger.puts "Migrator: Failed to load subscriber file #{f}: #{e.message}"
37
+ rescue StandardError => e
38
+ logger.puts "Migrator: Error loading subscriber file #{f}: #{e.class}: #{e.message}"
39
+ end
31
40
  end
41
+
32
42
  # Find all worker classes for the loaded framework
33
43
  subscribers = ObjectSpace.each_object(Class).select do |klass|
34
44
  klass.included_modules.include?(::Sneakers::Worker)
35
45
  rescue StandardError
36
46
  false
37
47
  end
48
+
49
+ # output found subscribers
50
+ logger.puts "Migrator: Found #{subscribers.size} subscribers:"
51
+ subscribers.each { |s| logger.puts " - #{s.name}" }
52
+
38
53
  queues = subscribers.map do |klass|
39
54
  opts = klass.instance_variable_get(:@queue_opts) || {}
40
55
  name = klass.instance_variable_get(:@queue_name) ||
@@ -59,90 +74,98 @@ module Sneakers
59
74
  all_queues = fetch_all_queues(amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass)
60
75
 
61
76
  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?
77
+ existing_queue_info = all_queues.find { |info| info["name"] == q[:name] }
78
+ if existing_queue_info.nil?
65
79
  logger.puts "Migrator: Queue #{q[:name]} does not exist, creating..."
66
80
  ch.queue(q[:name], durable: q[:durable], arguments: q[:arguments])
67
81
  next
68
82
  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..."
83
+ migrate_queue!(q, ch, amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, logger, existing_queue_info)
84
+ end
85
+ end
79
86
 
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}"
87
+ private
83
88
 
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?
89
+ def migrate_queue!(q, ch, amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, logger, existing_queue_info)
90
+ tmp_queue_name = "#{q[:name]}_tmp"
91
+ current_args = (existing_queue_info["arguments"] || {}).transform_keys(&:to_s)
92
+ desired_args = (q[:arguments] || {}).transform_keys(&:to_s)
93
+ logger.puts "Migrator: Checking queue: #{q[:name]}"
94
+ if queue_args_match?({ arguments: current_args }, desired_args)
95
+ logger.puts "Migrator: Queue #{q[:name]} arguments match, nothing to do."
96
+ return
97
+ end
98
+ logger.puts "Migrator: Current args: #{current_args.inspect}"
99
+ logger.puts "Migrator: Desired args: #{desired_args.inspect}"
87
100
 
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
101
+ vhost = existing_queue_info["vhost"] || "/"
123
102
 
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
103
+ # --- Fetch and store bindings (exchange/routing_key pairs) ---
104
+ bindings = fetch_queue_bindings(amqp_api_url, rabbitmq_api_user, rabbitmq_api_pass, vhost, existing_queue_info["name"])
105
+ logger.puts "Migrator: Found bindings: #{bindings.inspect}"
129
106
 
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."
107
+ ch.queue(tmp_queue_name, durable: existing_queue_info["durable"], arguments: desired_args)
108
+ Array(q[:routing_keys]).each do |rk|
109
+ next if rk.nil? || rk.empty?
110
+
111
+ # bind the tmp queue first so new messages go somewhere
112
+ ch.queue(tmp_queue_name).bind(q[:exchange], routing_key: rk)
113
+ # unbind the original queue from the exchange
114
+ logger.puts "Migrator: Unbinding queue #{q[:name]} from exchange #{q[:exchange]} with routing key #{rk}"
115
+ ch.queue(q[:name], passive: true).unbind(q[:exchange], routing_key: rk)
116
+ end
117
+ count = 0
118
+ queue = ch.queue(q[:name], passive: true)
119
+ loop do
120
+ delivery_info, headers, payload = queue.pop
121
+ break unless payload
122
+
123
+ logger.puts "Migrator: Moving message to #{tmp_queue_name}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
124
+ safe_basic_publish(ch, payload, tmp_queue_name, headers)
125
+ count += 1
126
+ end
127
+ logger.puts "Migrator: Moved #{count} messages to #{tmp_queue_name}"
128
+ ch.queue(q[:name], passive: true).delete
129
+
130
+ # create new queue with desired arguments
131
+ logger.puts "Migrator: Recreating queue #{q[:name]} with new arguments: #{q[:arguments].inspect}"
132
+ ch.queue(q[:name], durable: q[:durable], arguments: q[:arguments])
133
+
134
+ # --- Restore bindings ---
135
+ bindings.each do |binding|
136
+ # Only restore if it's a binding from an exchange (not from a queue)
137
+ next unless binding["source"] && !binding["source"].empty?
138
+
139
+ ch.queue(q[:name]).bind(binding["source"], routing_key: binding["routing_key"])
140
+ logger.puts "Migrator: Restored binding: exchange=#{binding["source"]}, routing_key=#{binding["routing_key"]}"
141
+ # now drop the tmp queue binding
142
+ ch.queue(tmp_queue_name).unbind(binding["source"], routing_key: binding["routing_key"])
143
+ rescue StandardError => e
144
+ logger.puts "Migrator: Failed to restore binding: #{e.class}: #{e.message}"
137
145
  end
138
- ch.close
139
- conn.close
146
+
147
+ count = 0
148
+ tmp_queue = ch.queue(tmp_queue_name, passive: true)
149
+ loop do
150
+ delivery_info, headers, payload = tmp_queue.pop
151
+ break unless payload
152
+
153
+ logger.puts "Migrator: Moving message back to #{q[:name]}: payload=#{payload.inspect}, headers=#{headers.inspect}, delivery_info=#{delivery_info.inspect}"
154
+ safe_basic_publish(ch, payload, q[:name], headers)
155
+ count += 1
156
+ end
157
+ logger.puts "Migrator: Moved #{count} messages to #{q[:name]}"
158
+ ch.queue(tmp_queue_name, passive: true).delete
159
+ logger.puts "Migrator: Migration for #{q[:name]} complete."
160
+ rescue Bunny::NotFound => e
161
+ logger.puts "Migrator: Queue #{q[:name]} not found, skipping migration. Error: #{e.message}"
162
+ rescue StandardError => e
163
+ logger.puts "Migrator: Error migrating queue #{q[:name]}: #{e.class}: #{e.message}"
164
+ raise
140
165
  end
141
166
 
142
167
  # 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"]
168
+ def fetch_queue_bindings(api_url, user, pass, vhost, queue_name)
146
169
  uri = URI.parse(api_url)
147
170
  scheme = uri.scheme || "http"
148
171
  host = uri.host
@@ -160,8 +183,6 @@ module Sneakers
160
183
  JSON.parse(res.body)
161
184
  end
162
185
 
163
- private
164
-
165
186
  def fetch_all_queues(api_url, user, pass)
166
187
  uri = URI.parse(api_url)
167
188
  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.2
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: []