mail_room 0.8.1 → 0.9.0

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
  SHA1:
3
- metadata.gz: 6b5c7ee5327fe4bf1a8f3b5743ad5c39e274430a
4
- data.tar.gz: 0a4d4858b8c82da1fb27a3915906adc270bce87e
3
+ metadata.gz: ee519fad16a2cfffc77987222062d15f59da6081
4
+ data.tar.gz: e403cac3cc18fe5a258d892dc69481535e01c4b6
5
5
  SHA512:
6
- metadata.gz: 42c2777a6056a5a555a184eadcd4c603419a03f90eb2a5d3ebcd46e496fe395fcf29a238506f26f26271a1e079217aa398b561ce2019c1d95a6010a349141e6a
7
- data.tar.gz: 2cb1c45b90cbf2d1b35533db8bcfd153db3b0b019a25ce70df0b31f342f6e1326441c7904fba4f4fcc6521ebc2f01c8da85340685e992f372c8a363590e0a9db
6
+ metadata.gz: 3b001def60c902e4efab7b4708c655e6c07d2d1b4b94cd1d0a0703c91a987c05ba218834a9e711f377a2c38c524134de73a0b184d3e676b188cd077a1a3cf94c
7
+ data.tar.gz: 8df7ff1eb78ec3eb6f19d976c5b6fc3a884194daede0af990a4f2c74b2efcdd04b6c8dfd7f9dd2f8e7ee3fd156696ab5104062abfa6ea1f4d640d2cc2a9f7a53
@@ -0,0 +1,31 @@
1
+ # Cache gems in between builds
2
+
3
+ .test-template: &test
4
+ cache:
5
+ paths:
6
+ - vendor/ruby
7
+ script:
8
+ - bundle exec rspec spec
9
+ before_script:
10
+ - apt update && apt install -y libicu-dev
11
+ - ruby -v # Print out ruby version for debugging
12
+ # Uncomment next line if your rails app needs a JS runtime:
13
+ # - apt-get update -q && apt-get install nodejs -yqq
14
+ - gem install bundler --no-ri --no-rdoc # Bundler is not installed with the image
15
+ - bundle install -j $(nproc) --path vendor # Install dependencies into ./vendor/ruby
16
+
17
+ rspec-2.0:
18
+ image: "ruby:2.0"
19
+ <<: *test
20
+
21
+ rspec-2.1:
22
+ image: "ruby:2.1"
23
+ <<: *test
24
+
25
+ rspec-2.2:
26
+ image: "ruby:2.2"
27
+ <<: *test
28
+
29
+ rspec-2.3:
30
+ image: "ruby:2.3"
31
+ <<: *test
@@ -1,3 +1,9 @@
1
+ ## mail_room 0.9.0 ##
2
+
3
+ * Redis Sentinel configuration support - PR#79
4
+
5
+ *Gabriel Mazetto <@brodock>*
6
+
1
7
  ## mail_room 0.8.1 ##
2
8
 
3
9
  * Check watching thread exists before joining - PR#78
data/README.md CHANGED
@@ -44,7 +44,7 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
44
44
  :delivery_options:
45
45
  :delivery_url: "http://localhost:3000/inbox"
46
46
  :delivery_token: "abcdefg"
47
-
47
+
48
48
  -
49
49
  :email: "user2@gmail.com"
50
50
  :password: "password"
@@ -76,6 +76,20 @@ You will also need to install `faraday` or `letter_opener` if you use the `postb
76
76
  :delivery_options:
77
77
  :redis_url: redis://localhost:6379
78
78
  :worker: EmailReceiverWorker
79
+ -
80
+ :email: "user6@gmail.com"
81
+ :password: "password"
82
+ :name: "inbox"
83
+ :delivery_method: sidekiq
84
+ :delivery_options:
85
+ # When pointing to sentinel, follow this sintax for redis URLs:
86
+ # redis://:<password>@<master-name>/
87
+ :redis_url: redis://:password@my-redis-sentinel/
88
+ :sentinels:
89
+ -
90
+ :host: 127.0.0.1
91
+ :port: 26379
92
+ :worker: EmailReceiverWorker
79
93
  ```
80
94
 
81
95
  ## delivery_method ##
@@ -86,12 +100,12 @@ Requires `faraday` gem be installed.
86
100
 
87
101
  *NOTE:* If you're using Ruby `>= 2.0`, you'll need to use Faraday from `>= 0.8.9`. Versions before this seem to have some weird behavior with `mail_room`.
88
102
 
89
- The default delivery method, requires `delivery_url` and `delivery_token` in
103
+ The default delivery method, requires `delivery_url` and `delivery_token` in
90
104
  configuration.
91
105
 
92
- As the postback is essentially using your app as if it were an API endpoint,
93
- you may need to disable forgery protection as you would with a JSON API. In
94
- our case, the postback is plaintext, but the protection will still need to be
106
+ As the postback is essentially using your app as if it were an API endpoint,
107
+ you may need to disable forgery protection as you would with a JSON API. In
108
+ our case, the postback is plaintext, but the protection will still need to be
95
109
  disabled.
96
110
 
97
111
  ### sidekiq ###
@@ -105,6 +119,8 @@ Configured with `:delivery_method: sidekiq`.
105
119
  Delivery options:
106
120
  - **redis_url**: The Redis server to connect with. Use the same Redis URL that's used to configure Sidekiq.
107
121
  Required, defaults to `redis://localhost:6379`.
122
+ - **sentinels**: A list of sentinels servers used to provide HA to Redis. (see [Sentinel Support](#sentinel-support))
123
+ Optional.
108
124
  - **namespace**: The Redis namespace Sidekiq works under. Use the same Redis namespace that's used to configure Sidekiq.
109
125
  Optional.
110
126
  - **queue**: The Sidekiq queue the job is pushed onto. Make sure Sidekiq actually reads off this queue.
@@ -224,20 +240,57 @@ When running multiple instances of MailRoom against a single mailbox, to try to
224
240
  :delivery_options:
225
241
  :delivery_url: "http://localhost:3000/inbox"
226
242
  :delivery_token: "abcdefg"
227
-
243
+
228
244
  :arbitration_method: redis
229
245
  :arbitration_options:
230
246
  # The Redis server to connect with. Defaults to redis://localhost:6379.
231
247
  :redis_url: redis://redis.example.com:6379
232
- # The Redis namespace to house the Redis keys under. Optional.
248
+ # The Redis namespace to house the Redis keys under. Optional.
233
249
  :namespace: mail_room
250
+ -
251
+ :email: "user2@gmail.com"
252
+ :password: "password"
253
+ :name: "inbox"
254
+ :delivery_method: postback
255
+ :delivery_options:
256
+ :delivery_url: "http://localhost:3000/inbox"
257
+ :delivery_token: "abcdefg"
234
258
 
259
+ :arbitration_method: redis
260
+ :arbitration_options:
261
+ # When pointing to sentinel, follow this sintax for redis URLs:
262
+ # redis://:<password>@<master-name>/
263
+ :redis_url: redis://:password@my-redis-sentinel/
264
+ :sentinels:
265
+ -
266
+ :host: 127.0.0.1
267
+ :port: 26379
268
+ # The Redis namespace to house the Redis keys under. Optional.
269
+ :namespace: mail_room
235
270
  ```
236
271
 
237
272
  **Note:** This will likely never be a _perfect_ system for preventing multiple deliveries of the same message, so I would advise checking the unique `message_id` if you are running in this situation.
238
273
 
239
274
  **Note:** There are other scenarios for preventing duplication of messages at scale that _may_ be more appropriate in your particular setup. One such example is using multiple inboxes in reply-by-email situations. Another is to use labels and configure a different `SEARCH` command for each instance of MailRoom.
240
275
 
276
+ ## Sentinel Support
277
+
278
+ Redis Sentinel provides high availability for Redis. Please read their [documentation](http://redis.io/topics/sentinel)
279
+ first, before enabling it with mail_room.
280
+
281
+ To connect to a Sentinel, you need to setup authentication to both sentinels and redis daemons first, and make sure
282
+ both are binding to a reachable IP address.
283
+
284
+ In mail_room, when you are connecting to a Sentinel, you have to inform the `master-name` and the `password` through
285
+ `redis_url` param, following this syntax:
286
+
287
+ ```
288
+ redis://:<password>@<master-name>/
289
+ ```
290
+
291
+ You also have to inform at least one pair of `host` and `port` for a sentinel in your cluster.
292
+ To have a minimum reliable setup, you need at least `3` sentinel nodes and `3` redis servers (1 master, 2 slaves).
293
+
241
294
  ## Contributing ##
242
295
 
243
296
  1. Fork it
@@ -3,12 +3,13 @@ require "redis"
3
3
  module MailRoom
4
4
  module Arbitration
5
5
  class Redis
6
- Options = Struct.new(:redis_url, :namespace) do
6
+ Options = Struct.new(:redis_url, :namespace, :sentinels) do
7
7
  def initialize(mailbox)
8
8
  redis_url = mailbox.arbitration_options[:redis_url] || "redis://localhost:6379"
9
9
  namespace = mailbox.arbitration_options[:namespace]
10
+ sentinels = mailbox.arbitration_options[:sentinels]
10
11
 
11
- super(redis_url, namespace)
12
+ super(redis_url, namespace, sentinels)
12
13
  end
13
14
  end
14
15
 
@@ -25,12 +26,12 @@ module MailRoom
25
26
  key = "delivered:#{uid}"
26
27
 
27
28
  incr = nil
28
- redis.multi do |client|
29
- # At this point, `incr` is a future, which will get its value after
29
+ client.multi do |c|
30
+ # At this point, `incr` is a future, which will get its value after
30
31
  # the MULTI command returns.
31
- incr = client.incr(key)
32
+ incr = c.incr(key)
32
33
 
33
- client.expire(key, EXPIRATION)
34
+ c.expire(key, EXPIRATION)
34
35
  end
35
36
 
36
37
  # If INCR returns 1, that means the key didn't exist before, which means
@@ -42,9 +43,13 @@ module MailRoom
42
43
 
43
44
  private
44
45
 
45
- def redis
46
- @redis ||= begin
47
- redis = ::Redis.new(url: options.redis_url)
46
+ def client
47
+ @client ||= begin
48
+ sentinels = options.sentinels
49
+ redis_options = { url: options.redis_url }
50
+ redis_options[:sentinels] = sentinels if sentinels
51
+
52
+ redis = ::Redis.new(redis_options)
48
53
 
49
54
  namespace = options.namespace
50
55
  if namespace
@@ -8,14 +8,15 @@ module MailRoom
8
8
  # Sidekiq Delivery method
9
9
  # @author Douwe Maan
10
10
  class Sidekiq
11
- Options = Struct.new(:redis_url, :namespace, :queue, :worker) do
11
+ Options = Struct.new(:redis_url, :namespace, :sentinels, :queue, :worker) do
12
12
  def initialize(mailbox)
13
13
  redis_url = mailbox.delivery_options[:redis_url] || "redis://localhost:6379"
14
14
  namespace = mailbox.delivery_options[:namespace]
15
+ sentinels = mailbox.delivery_options[:sentinels]
15
16
  queue = mailbox.delivery_options[:queue] || "default"
16
17
  worker = mailbox.delivery_options[:worker]
17
18
 
18
- super(redis_url, namespace, queue, worker)
19
+ super(redis_url, namespace, sentinels, queue, worker)
19
20
  end
20
21
  end
21
22
 
@@ -40,14 +41,20 @@ module MailRoom
40
41
  private
41
42
 
42
43
  def client
43
- client = Redis.new(url: options.redis_url)
44
-
45
- namespace = options.namespace
46
- if namespace
47
- require 'redis/namespace'
48
- Redis::Namespace.new(namespace, redis: client)
49
- else
50
- client
44
+ @client ||= begin
45
+ sentinels = options.sentinels
46
+ redis_options = { url: options.redis_url }
47
+ redis_options[:sentinels] = sentinels if sentinels
48
+
49
+ redis = ::Redis.new(redis_options)
50
+
51
+ namespace = options.namespace
52
+ if namespace
53
+ require 'redis/namespace'
54
+ Redis::Namespace.new(namespace, redis: redis)
55
+ else
56
+ redis
57
+ end
51
58
  end
52
59
  end
53
60
 
@@ -1,4 +1,4 @@
1
1
  module MailRoom
2
2
  # Current version of MailRoom gem
3
- VERSION = "0.8.1"
3
+ VERSION = "0.9.0"
4
4
  end
@@ -2,18 +2,18 @@ require 'spec_helper'
2
2
  require 'mail_room/arbitration/redis'
3
3
 
4
4
  describe MailRoom::Arbitration::Redis do
5
- let(:mailbox) {
5
+ let(:mailbox) {
6
6
  MailRoom::Mailbox.new(
7
7
  arbitration_options: {
8
8
  namespace: "mail_room"
9
9
  }
10
- )
10
+ )
11
11
  }
12
12
  let(:options) { described_class::Options.new(mailbox) }
13
13
  subject { described_class.new(options) }
14
14
 
15
15
  # Private, but we don't care.
16
- let(:redis) { subject.send(:redis) }
16
+ let(:client) { subject.send(:client) }
17
17
 
18
18
  describe '#deliver?' do
19
19
  context "when called the first time" do
@@ -24,13 +24,13 @@ describe MailRoom::Arbitration::Redis do
24
24
  it "increments the delivered flag" do
25
25
  subject.deliver?(123)
26
26
 
27
- expect(redis.get("delivered:123")).to eq("1")
27
+ expect(client.get("delivered:123")).to eq("1")
28
28
  end
29
29
 
30
30
  it "sets an expiration on the delivered flag" do
31
31
  subject.deliver?(123)
32
32
 
33
- expect(redis.ttl("delivered:123")).to be > 0
33
+ expect(client.ttl("delivered:123")).to be > 0
34
34
  end
35
35
  end
36
36
 
@@ -46,7 +46,7 @@ describe MailRoom::Arbitration::Redis do
46
46
  it "increments the delivered flag" do
47
47
  subject.deliver?(123)
48
48
 
49
- expect(redis.get("delivered:123")).to eq("2")
49
+ expect(client.get("delivered:123")).to eq("2")
50
50
  end
51
51
  end
52
52
 
@@ -60,4 +60,68 @@ describe MailRoom::Arbitration::Redis do
60
60
  end
61
61
  end
62
62
  end
63
+
64
+ context 'redis client connection params' do
65
+ context 'when only url is present' do
66
+ let(:redis_url) { "redis://redis.example.com:8888" }
67
+ let(:mailbox) {
68
+ MailRoom::Mailbox.new(
69
+ arbitration_options: {
70
+ redis_url: redis_url
71
+ }
72
+ )
73
+ }
74
+
75
+ it 'client has same specified url' do
76
+ subject.deliver?(123)
77
+
78
+ expect(client.options[:url]).to eq redis_url
79
+ end
80
+
81
+ it 'client is a instance of Redis class' do
82
+ expect(client).to be_a Redis
83
+ end
84
+ end
85
+
86
+ context 'when namespace is present' do
87
+ let(:namespace) { 'mail_room' }
88
+ let(:mailbox) {
89
+ MailRoom::Mailbox.new(
90
+ arbitration_options: {
91
+ namespace: namespace
92
+ }
93
+ )
94
+ }
95
+
96
+ it 'client has same specified namespace' do
97
+ expect(client.namespace).to eq(namespace)
98
+ end
99
+
100
+ it 'client is a instance of RedisNamespace class' do
101
+ expect(client).to be_a ::Redis::Namespace
102
+ end
103
+ end
104
+
105
+ context 'when sentinel is present' do
106
+ let(:redis_url) { 'redis://:mypassword@sentinel-master:6379' }
107
+ let(:sentinels) { [{ host: '10.0.0.1', port: '26379' }] }
108
+ let(:mailbox) {
109
+ MailRoom::Mailbox.new(
110
+ arbitration_options: {
111
+ redis_url: redis_url,
112
+ sentinels: sentinels
113
+ }
114
+ )
115
+ }
116
+
117
+ before { ::Redis::Client::Connector::Sentinel.any_instance.stubs(:resolve).returns(sentinels) }
118
+
119
+ it 'client has same specified sentinel params' do
120
+ expect(client.client.instance_variable_get(:@connector)).to be_a Redis::Client::Connector::Sentinel
121
+ expect(client.client.options[:host]).to eq('sentinel-master')
122
+ expect(client.client.options[:password]).to eq('mypassword')
123
+ expect(client.client.options[:sentinels]).to eq(sentinels)
124
+ end
125
+ end
126
+ end
63
127
  end
@@ -0,0 +1,76 @@
1
+ require 'spec_helper'
2
+ require 'mail_room/delivery/sidekiq'
3
+
4
+ describe MailRoom::Delivery::Sidekiq do
5
+ subject { described_class.new(options) }
6
+ let(:redis) { subject.send(:client) }
7
+ let(:options) { MailRoom::Delivery::Sidekiq::Options.new(mailbox) }
8
+
9
+ describe '#options' do
10
+ let(:redis_url) { 'redis://redis.example.com' }
11
+
12
+ context 'when only redis_url is specified' do
13
+ let(:mailbox) {
14
+ MailRoom::Mailbox.new(
15
+ delivery_method: :sidekiq,
16
+ delivery_options: {
17
+ redis_url: redis_url
18
+ }
19
+ )
20
+ }
21
+
22
+ it 'client has same specified redis_url' do
23
+ expect(redis.options[:url]).to eq(redis_url)
24
+ end
25
+
26
+ it 'client is a instance of RedisNamespace class' do
27
+ expect(redis).to be_a ::Redis
28
+ end
29
+ end
30
+
31
+ context 'when namespace is specified' do
32
+ let(:namespace) { 'sidekiq_mailman' }
33
+ let(:mailbox) {
34
+ MailRoom::Mailbox.new(
35
+ delivery_method: :sidekiq,
36
+ delivery_options: {
37
+ redis_url: redis_url,
38
+ namespace: namespace
39
+ }
40
+ )
41
+ }
42
+
43
+ it 'client has same specified namespace' do
44
+ expect(redis.namespace).to eq(namespace)
45
+ end
46
+
47
+ it 'client is a instance of RedisNamespace class' do
48
+ expect(redis).to be_a ::Redis::Namespace
49
+ end
50
+ end
51
+
52
+ context 'when sentinel is specified' do
53
+ let(:redis_url) { 'redis://:mypassword@sentinel-master:6379' }
54
+ let(:sentinels) { [{ host: '10.0.0.1', port: '26379' }] }
55
+ let(:mailbox) {
56
+ MailRoom::Mailbox.new(
57
+ delivery_method: :sidekiq,
58
+ delivery_options: {
59
+ redis_url: redis_url,
60
+ sentinels: sentinels
61
+ }
62
+ )
63
+ }
64
+
65
+ before { ::Redis::Client::Connector::Sentinel.any_instance.stubs(:resolve).returns(sentinels) }
66
+
67
+ it 'client has same specified sentinel params' do
68
+ expect(redis.client.instance_variable_get(:@connector)).to be_a Redis::Client::Connector::Sentinel
69
+ expect(redis.client.options[:host]).to eq('sentinel-master')
70
+ expect(redis.client.options[:password]).to eq('mypassword')
71
+ expect(redis.client.options[:sentinels]).to eq(sentinels)
72
+ end
73
+ end
74
+
75
+ end
76
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mail_room
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tony Pitale
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-31 00:00:00.000000000 Z
11
+ date: 2016-10-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -187,6 +187,7 @@ extensions: []
187
187
  extra_rdoc_files: []
188
188
  files:
189
189
  - ".gitignore"
190
+ - ".gitlab-ci.yml"
190
191
  - ".ruby-version"
191
192
  - ".travis.yml"
192
193
  - CHANGELOG.md
@@ -226,6 +227,7 @@ files:
226
227
  - spec/lib/delivery/logger_spec.rb
227
228
  - spec/lib/delivery/postback_spec.rb
228
229
  - spec/lib/delivery/que_spec.rb
230
+ - spec/lib/delivery/sidekiq_spec.rb
229
231
  - spec/lib/mailbox_spec.rb
230
232
  - spec/lib/mailbox_watcher_spec.rb
231
233
  - spec/spec_helper.rb
@@ -264,6 +266,7 @@ test_files:
264
266
  - spec/lib/delivery/logger_spec.rb
265
267
  - spec/lib/delivery/postback_spec.rb
266
268
  - spec/lib/delivery/que_spec.rb
269
+ - spec/lib/delivery/sidekiq_spec.rb
267
270
  - spec/lib/mailbox_spec.rb
268
271
  - spec/lib/mailbox_watcher_spec.rb
269
272
  - spec/spec_helper.rb