mail_room 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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