rswim 2.1.0 → 2.3.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
  SHA256:
3
- metadata.gz: 2cdad1327bf5ba2e7fd3a2d975b7f0ad507d99952b9c2b2d01269f4f3b813f25
4
- data.tar.gz: 2050c00ddb9eec045103645d269683ad0454d664d92026f6849f80428e1483ff
3
+ metadata.gz: d0488c9886f92df9a49c83521f881fdfc691ca2acfdf11deaef591a040a5e082
4
+ data.tar.gz: 16930933f6e7fd5ee4f464d9693d437a7a493e37015b79049ad3c4b2647ccf60
5
5
  SHA512:
6
- metadata.gz: 17a26dc6da80bc10db656b7127f2e72c7bb77d962eec38fc63830aa5b5d4642c8e3650fb8c4d2b860efac2e1eaa12c590c98f31dc1e4092adec31018c8b558bf
7
- data.tar.gz: 9563514c494f896b54098b80b1636a20323a84d07b5f9b689577e592592edc6ae480449fcf7020ae1ad79b72084a7d087967983f9b2229a712de9d4712e95d8e
6
+ metadata.gz: 16e61b48d55d0f56d0803f2a86a48859c7896ad2fdc187fe474771a945549f60bac2b90793277f853ad510a5a22a6c3c92b1f341a8a905390196591786edebd0
7
+ data.tar.gz: 037e6ca91473d74c597107f2d711fceea3523beac4fab0060da984995d96bcee684f571bf578ea0934eb661792e28cb4ed735a07c32035c635f55e221a2a2cb7
data/CHANGELOG.md CHANGED
@@ -1,3 +1,5 @@
1
1
  # 1.0.0 Complete implementation for UDP plus simple, human readable serialisation of messages
2
2
  # 2.0.0 Piggyback custom state on the liveness propagation mechanism using `RSwim::Node#append_custom_state`
3
- # 2.1.0 Use non-blocking I/O by means of `Fiber.shedule` with the scheduler provided by the Async gem
3
+ # 2.1.0 Use non-blocking I/O by means of `Fiber.shedule` with the scheduler provided by the Async gem
4
+ # 2.2.0 Encrypted messages between peers. Run UDP Sender in fiber instead of thread to make whole node single threaded.
5
+ # 2.3.0 Runs on current Ruby. `require 'rswim'` failed outright on Ruby 3.4 and later, because base64 and logger left the standard library and were never declared as dependencies. `RSwim::VERSION` was unreachable once the gem was loaded, since the loader expected version.rb to define `RSwim::Version`. Requires Ruby 3.3 or newer and async 2.x. The published gem no longer carries the development scripts, the Rakefile or the Guardfile.
data/README.md CHANGED
@@ -4,11 +4,14 @@ RSwim is a Ruby implementation of the SWIM gossip protocol, a mechanism for disc
4
4
 
5
5
  It is an implementation inspired by the original [SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol](https://www.cs.cornell.edu/projects/Quicksilver/public_pdfs/SWIM.pdf) paper by Abhinandan Das, Indranil Gupta, Ashish Motivala.
6
6
 
7
- The implementation is kept intentionally simple and limited to the features described in the paper, except for the addition in version 2.0.0 of the ability to piggyback custom state on the liveness propagation mechanism, see `RSwim::Node#append_custom_state`
7
+ The implementation is kept intentionally simple and includes only the features described in the paper along with a few additions after version 2.0.0:
8
+
9
+ - The ability to piggyback custom state on the liveness propagation mechanism was added in version 2.0.0, see `RSwim::Node#append_custom_state`
10
+ - Encryption of messsages between peers based on a shared secret was introduced in version 2.2.0, see module `RSwim::Serialization::Encrypted`
8
11
 
9
12
  No attempts have been made to address known security issues such as Byzantine attacks.
10
13
 
11
- Currently RSwim runs on UDP with a custom, human readable serialization format.
14
+ Currently RSwim runs on UDP. In the unencrypted mode it uses a custom, human readable serialization format. Peers in unencrypted mode cannot communicate with peers in encrypted mode.
12
15
 
13
16
 
14
17
  ## Installation
@@ -34,6 +37,9 @@ Example:
34
37
  ```ruby
35
38
  require 'rswim'
36
39
 
40
+ RSwim.encrypted = true
41
+ RSwim.shared_secret = 'santa 2000'
42
+
37
43
  port = 4545
38
44
 
39
45
  # known, running nodes to connect with initially.
@@ -0,0 +1,43 @@
1
+ module RSwim
2
+ module Encryption
3
+ class << self
4
+ def encrypt(message)
5
+ message = message.dup.force_encoding('UTF-8')
6
+ salt = cipher.random_iv
7
+ cipher_text = cipher.update(message) + cipher.final
8
+ [cipher_text, salt]
9
+ rescue StandardError => e
10
+ raise Error, "Failed to encrypt: #{e.message}"
11
+ end
12
+
13
+ def decrypt(cipher_text, salt)
14
+ decipher.iv = salt
15
+ message = decipher.update(cipher_text) + decipher.final
16
+ message.force_encoding('UTF-8')
17
+ rescue StandardError => e
18
+ raise Error, "Failed to decrypt: #{e.message}"
19
+ end
20
+
21
+ private
22
+
23
+ def cipher
24
+ @_cipher ||= begin
25
+ cipher = OpenSSL::Cipher::AES256.new :CBC
26
+ cipher.encrypt
27
+ cipher.key = Digest::SHA256.digest(RSwim.shared_secret)
28
+ cipher
29
+ end
30
+ end
31
+
32
+ def decipher
33
+ @_decipher ||= begin
34
+ cipher = OpenSSL::Cipher::AES256.new :CBC
35
+ cipher.decrypt
36
+ cipher.key = Digest::SHA256.digest(RSwim.shared_secret)
37
+ cipher
38
+ end
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -3,36 +3,6 @@
3
3
  module RSwim
4
4
  module Integration
5
5
  module UDP
6
- class Sender
7
- def initialize(port, out_q)
8
- @out_q = out_q
9
- @port = port
10
- @out_s = UDPSocket.new
11
- end
12
-
13
- def run
14
- Async do
15
- loop do
16
- wire_messages = @out_q.pop
17
- wire_messages.each do |(host, wire_message)|
18
- logger.debug "about to send message to #{host} on port #{@port}"
19
- Fiber.schedule do
20
- @out_s.send(wire_message, 0, host, @port)
21
- rescue StandardError => e
22
- logger.debug("Error while sending: #{e}")
23
- end
24
- end
25
- end
26
- end
27
- end
28
-
29
- private
30
-
31
- def logger
32
- @_logger ||= RSwim::Logger.new(self.class, $stderr)
33
- end
34
- end
35
-
36
6
  class IOLoop < RSwim::IOLoop
37
7
  def initialize(agent, serializer, deserializer, directory, sleep_time_seconds, my_host, port)
38
8
  super(agent, serializer, deserializer, directory, sleep_time_seconds)
@@ -46,7 +16,7 @@ module RSwim
46
16
  @in_s = UDPSocket.new
47
17
  @in_s.bind(@my_host, @port)
48
18
  @out_q = Queue.new
49
- Thread.new { Sender.new(@port, @out_q).run }.abort_on_exception = true
19
+ Fiber.schedule { Sender.new(@port, @out_q).run }
50
20
  end
51
21
 
52
22
  def read
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Integration
5
+ module UDP
6
+ class Sender
7
+ def initialize(port, out_q)
8
+ @out_q = out_q
9
+ @port = port
10
+ @out_s = UDPSocket.new
11
+ end
12
+
13
+ def run
14
+ Async do
15
+ loop do
16
+ wire_messages = @out_q.pop
17
+ wire_messages.each do |(host, wire_message)|
18
+ logger.debug "about to send message to #{host} on port #{@port}"
19
+ Fiber.schedule do
20
+ @out_s.send(wire_message, 0, host, @port)
21
+ rescue StandardError => e
22
+ logger.debug("Error while sending: #{e}")
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def logger
32
+ @_logger ||= RSwim::Logger.new(self.class, $stderr)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
data/lib/rswim/io_loop.rb CHANGED
@@ -12,8 +12,8 @@ module RSwim
12
12
  end
13
13
 
14
14
  def run
15
- before_run
16
15
  Async do
16
+ before_run
17
17
  start_producer
18
18
  loop do
19
19
  in_messages = consume_read_buffer
data/lib/rswim/node.rb CHANGED
@@ -7,11 +7,13 @@ module RSwim
7
7
  end
8
8
 
9
9
  def initialize(my_host, seed_hosts, t_ms, r_ms)
10
+ RSwim.validate_config!
10
11
  @my_host = my_host
11
12
  @directory = Directory.new
12
13
  @my_id = @directory.id(@my_host)
13
- @deserializer = Integration::Deserializer.new(@directory, @my_id)
14
- @serializer = Integration::Serializer.new(@directory)
14
+ serialization = RSwim.encrypted ? Serialization::Encrypted : Serialization::Simple
15
+ @deserializer = serialization::Deserializer.new(@directory, @my_id)
16
+ @serializer = serialization::Serializer.new(@directory)
15
17
  @seed_ids = seed_hosts.map { |host| @directory.id(host) }
16
18
  @t_ms = t_ms
17
19
  @r_ms = r_ms
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Serialization::Encrypted
5
+ class Deserializer
6
+ def initialize(directory, my_id)
7
+ @directory = directory
8
+ @my_id = my_id
9
+ end
10
+
11
+ def deserialize(sender_host, wire_message)
12
+ outer = JSON.parse(wire_message)
13
+ cipher_text, salt = outer.values_at('message', 'salt').map { |s| Base64.decode64(s) }
14
+ inner = JSON.parse(Encryption.decrypt(cipher_text, salt), symbolize_names: true)
15
+
16
+ from = @directory.id(sender_host)
17
+ payload = {}
18
+ payload[:target_id] = @directory.id(inner[:target]) unless inner[:target].nil?
19
+ payload[:updates] = inner[:updates].to_a.map do |u|
20
+ UpdateEntry.new(
21
+ @directory.id(u[:host]),
22
+ u[:status].to_sym,
23
+ u[:incarnation_number].to_i,
24
+ u[:custom_state]
25
+ )
26
+ end
27
+ Message.new(@my_id, from, inner[:type].to_sym, payload)
28
+ rescue StandardError => e
29
+ logger.debug("Failed to parse wire message")
30
+ nil
31
+ end
32
+
33
+ protected
34
+
35
+ def logger
36
+ @_logger ||= RSwim::Logger.new(self.class, $stderr)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSwim
4
+ module Serialization::Encrypted
5
+ class Serializer
6
+ def initialize(directory)
7
+ @directory = directory
8
+ end
9
+
10
+ def serialize(message)
11
+ unencrypted = serialize_unencrypted(message)
12
+ cipher_text, salt = Encryption.encrypt(unencrypted).map { |s| Base64.encode64(s) }
13
+ { message: cipher_text, salt: salt }.to_json
14
+ end
15
+
16
+ protected
17
+
18
+ def logger
19
+ @_logger ||= RSwim::Logger.new(self.class, STDERR)
20
+ end
21
+
22
+ private
23
+
24
+ def serialize_unencrypted(message)
25
+ out = {}
26
+ out[:type] = message.type
27
+ out[:target] = @directory.host(message.payload[:target_id]) if message.type == :ping_req
28
+ out[:updates] = message.payload[:updates].to_a.map do |update|
29
+ {
30
+ host: @directory.host(update.member_id),
31
+ status: update.status,
32
+ incarnation_number: update.incarnation_number,
33
+ custom_state: update.custom_state
34
+ }
35
+ end
36
+
37
+ out.to_json
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSwim
4
- module Integration
4
+ module Serialization::Simple
5
5
  class Deserializer
6
6
  def initialize(directory, my_id)
7
7
  @directory = directory
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSwim
4
- module Integration
4
+ module Serialization::Simple
5
5
  class Serializer
6
6
  def initialize(directory)
7
7
  @directory = directory
data/lib/rswim/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RSwim
4
- VERSION = '2.1.0'
4
+ VERSION = '2.3.0'
5
5
  end
data/lib/rswim.rb CHANGED
@@ -4,8 +4,10 @@ require 'logger'
4
4
  require 'socket'
5
5
  require 'zeitwerk'
6
6
  require 'async'
7
+ require 'openssl'
8
+ require 'base64'
7
9
 
8
- # frozen_string_literal: true
10
+ require_relative 'rswim/version'
9
11
 
10
12
  class MyInflector < Zeitwerk::Inflector
11
13
  def camelize(basename, _abspath)
@@ -20,6 +22,9 @@ end
20
22
 
21
23
  loader = Zeitwerk::Loader.for_gem
22
24
  loader.inflector = MyInflector.new
25
+ # version.rb defines RSwim::VERSION, not the RSwim::Version the loader would
26
+ # infer from the filename, so it is required above and left out of the loader.
27
+ loader.ignore("#{__dir__}/rswim/version.rb")
23
28
  loader.setup
24
29
 
25
30
  module RSwim
@@ -31,5 +36,19 @@ module RSwim
31
36
  # Roundtrip time, millis
32
37
  R_MS = 10_000
33
38
 
39
+ class << self
40
+ attr_accessor :encrypted, :shared_secret
41
+
42
+ def validate_config!
43
+ validate_shared_secret! if @encrypted
44
+ true
45
+ end
46
+
47
+ def validate_shared_secret!
48
+ raise Error, 'Encrypted mode was set, but no shared secret configured' if @shared_secret.nil?
49
+ raise Error, 'Shared secret too short' if @shared_secret.length < 8
50
+ end
51
+ end
52
+
34
53
  class Error < StandardError; end
35
54
  end
metadata CHANGED
@@ -1,15 +1,42 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rswim
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Madsen
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2021-10-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: base64
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '0.2'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '0.2'
26
+ - !ruby/object:Gem::Dependency
27
+ name: logger
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '1.6'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.6'
13
40
  - !ruby/object:Gem::Dependency
14
41
  name: zeitwerk
15
42
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +71,14 @@ dependencies:
44
71
  requirements:
45
72
  - - "~>"
46
73
  - !ruby/object:Gem::Version
47
- version: '1.30'
74
+ version: '2.0'
48
75
  type: :runtime
49
76
  prerelease: false
50
77
  version_requirements: !ruby/object:Gem::Requirement
51
78
  requirements:
52
79
  - - "~>"
53
80
  - !ruby/object:Gem::Version
54
- version: '1.30'
81
+ version: '2.0'
55
82
  - !ruby/object:Gem::Dependency
56
83
  name: bundler
57
84
  requirement: !ruby/object:Gem::Requirement
@@ -72,14 +99,14 @@ dependencies:
72
99
  requirements:
73
100
  - - "~>"
74
101
  - !ruby/object:Gem::Version
75
- version: '12.0'
102
+ version: '13.0'
76
103
  type: :development
77
104
  prerelease: false
78
105
  version_requirements: !ruby/object:Gem::Requirement
79
106
  requirements:
80
107
  - - "~>"
81
108
  - !ruby/object:Gem::Version
82
- version: '12.0'
109
+ version: '13.0'
83
110
  - !ruby/object:Gem::Dependency
84
111
  name: rspec
85
112
  requirement: !ruby/object:Gem::Requirement
@@ -145,28 +172,16 @@ executables: []
145
172
  extensions: []
146
173
  extra_rdoc_files: []
147
174
  files:
148
- - ".gitignore"
149
- - ".rspec"
150
- - ".ruby-version"
151
- - ".travis.yml"
152
175
  - CHANGELOG.md
153
- - Gemfile
154
- - Gemfile.lock
155
- - Guardfile
156
176
  - LICENSE.txt
157
177
  - README.md
158
- - Rakefile
159
- - bin/async_loop
160
- - bin/console
161
- - bin/run_node
162
- - bin/setup
163
178
  - lib/rswim.rb
164
179
  - lib/rswim/agent.rb
165
180
  - lib/rswim/directory.rb
166
- - lib/rswim/integration/deserializer.rb
167
- - lib/rswim/integration/serializer.rb
181
+ - lib/rswim/encryption.rb
168
182
  - lib/rswim/integration/udp/io_loop.rb
169
183
  - lib/rswim/integration/udp/node.rb
184
+ - lib/rswim/integration/udp/sender.rb
170
185
  - lib/rswim/io_loop.rb
171
186
  - lib/rswim/logger.rb
172
187
  - lib/rswim/member/ack_responder.rb
@@ -191,12 +206,13 @@ files:
191
206
  - lib/rswim/node.rb
192
207
  - lib/rswim/pipe.rb
193
208
  - lib/rswim/protocol_state.rb
209
+ - lib/rswim/serialization/encrypted/deserializer.rb
210
+ - lib/rswim/serialization/encrypted/serializer.rb
211
+ - lib/rswim/serialization/simple/deserializer.rb
212
+ - lib/rswim/serialization/simple/serializer.rb
194
213
  - lib/rswim/status_report.rb
195
214
  - lib/rswim/update_entry.rb
196
215
  - lib/rswim/version.rb
197
- - log/.keep
198
- - rswim.gemspec
199
- - tmp/.keep
200
216
  homepage: https://github.com/beatmadsen/rswim
201
217
  licenses:
202
218
  - MIT
@@ -204,7 +220,9 @@ metadata:
204
220
  homepage_uri: https://github.com/beatmadsen/rswim
205
221
  source_code_uri: https://github.com/beatmadsen/rswim
206
222
  changelog_uri: https://github.com/beatmadsen/rswim/blob/master/CHANGELOG.md
207
- post_install_message:
223
+ bug_tracker_uri: https://github.com/beatmadsen/rswim/issues
224
+ documentation_uri: https://github.com/beatmadsen/rswim#readme
225
+ rubygems_mfa_required: 'true'
208
226
  rdoc_options: []
209
227
  require_paths:
210
228
  - lib
@@ -212,15 +230,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
212
230
  requirements:
213
231
  - - ">="
214
232
  - !ruby/object:Gem::Version
215
- version: 3.0.0
233
+ version: '3.3'
216
234
  required_rubygems_version: !ruby/object:Gem::Requirement
217
235
  requirements:
218
236
  - - ">="
219
237
  - !ruby/object:Gem::Version
220
238
  version: '0'
221
239
  requirements: []
222
- rubygems_version: 3.2.22
223
- signing_key:
240
+ rubygems_version: 4.0.9
224
241
  specification_version: 4
225
242
  summary: Ruby implementation of the SWIM gossip protocol
226
243
  test_files: []
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /_yardoc/
4
- /coverage/
5
- /doc/
6
- /pkg/
7
- /spec/reports/
8
-
9
- # Ignore all logfiles and tempfiles.
10
- /log/*
11
- /tmp/*
12
- !/log/.keep
13
- !/tmp/.keep
14
-
15
- # rspec failure tracking
16
- .rspec_status
17
- .byebug_history
18
- .DS_Store
data/.rspec DELETED
@@ -1,3 +0,0 @@
1
- --format Fuubar
2
- --color
3
- --require spec_helper
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 3.0.2
data/.travis.yml DELETED
@@ -1,6 +0,0 @@
1
- ---
2
- language: ruby
3
- cache: bundler
4
- rvm:
5
- - 2.7.1
6
- before_install: gem install bundler -v 2.1.4
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in rswim.gemspec
4
- gemspec
data/Gemfile.lock DELETED
@@ -1,91 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- rswim (2.1.0)
5
- async (~> 1.30)
6
- slop (~> 4.9)
7
- zeitwerk (~> 2.2)
8
-
9
- GEM
10
- remote: https://rubygems.org/
11
- specs:
12
- async (1.30.1)
13
- console (~> 1.10)
14
- nio4r (~> 2.3)
15
- timers (~> 4.1)
16
- byebug (11.1.3)
17
- coderay (1.1.3)
18
- console (1.13.1)
19
- fiber-local
20
- diff-lcs (1.4.4)
21
- ffi (1.15.4)
22
- fiber-local (1.0.0)
23
- formatador (0.3.0)
24
- fuubar (2.5.1)
25
- rspec-core (~> 3.0)
26
- ruby-progressbar (~> 1.4)
27
- guard (2.18.0)
28
- formatador (>= 0.2.4)
29
- listen (>= 2.7, < 4.0)
30
- lumberjack (>= 1.0.12, < 2.0)
31
- nenv (~> 0.1)
32
- notiffany (~> 0.0)
33
- pry (>= 0.13.0)
34
- shellany (~> 0.0)
35
- thor (>= 0.18.1)
36
- guard-compat (1.2.1)
37
- guard-rspec (4.7.3)
38
- guard (~> 2.1)
39
- guard-compat (~> 1.1)
40
- rspec (>= 2.99.0, < 4.0)
41
- listen (3.7.0)
42
- rb-fsevent (~> 0.10, >= 0.10.3)
43
- rb-inotify (~> 0.9, >= 0.9.10)
44
- lumberjack (1.2.8)
45
- method_source (1.0.0)
46
- nenv (0.3.0)
47
- nio4r (2.5.8)
48
- notiffany (0.1.3)
49
- nenv (~> 0.1)
50
- shellany (~> 0.0)
51
- pry (0.14.1)
52
- coderay (~> 1.1)
53
- method_source (~> 1.0)
54
- rake (12.3.3)
55
- rb-fsevent (0.11.0)
56
- rb-inotify (0.10.1)
57
- ffi (~> 1.0)
58
- rspec (3.10.0)
59
- rspec-core (~> 3.10.0)
60
- rspec-expectations (~> 3.10.0)
61
- rspec-mocks (~> 3.10.0)
62
- rspec-core (3.10.1)
63
- rspec-support (~> 3.10.0)
64
- rspec-expectations (3.10.1)
65
- diff-lcs (>= 1.2.0, < 2.0)
66
- rspec-support (~> 3.10.0)
67
- rspec-mocks (3.10.2)
68
- diff-lcs (>= 1.2.0, < 2.0)
69
- rspec-support (~> 3.10.0)
70
- rspec-support (3.10.2)
71
- ruby-progressbar (1.11.0)
72
- shellany (0.0.1)
73
- slop (4.9.1)
74
- thor (1.1.0)
75
- timers (4.3.3)
76
- zeitwerk (2.4.2)
77
-
78
- PLATFORMS
79
- ruby
80
-
81
- DEPENDENCIES
82
- bundler (>= 2.2.10)
83
- byebug
84
- fuubar (~> 2.5)
85
- guard-rspec (~> 4.7)
86
- rake (~> 12.0)
87
- rspec (~> 3.0)
88
- rswim!
89
-
90
- BUNDLED WITH
91
- 2.2.22
data/Guardfile DELETED
@@ -1,42 +0,0 @@
1
- # A sample Guardfile
2
- # More info at https://github.com/guard/guard#readme
3
-
4
- ## Uncomment and set this to only include directories you want to watch
5
- # directories %w(app lib config test spec features) \
6
- # .select{|d| Dir.exist?(d) ? d : UI.warning("Directory #{d} does not exist")}
7
-
8
- ## Note: if you are using the `directories` clause above and you are not
9
- ## watching the project directory ('.'), then you will want to move
10
- ## the Guardfile to a watched dir and symlink it back, e.g.
11
- #
12
- # $ mkdir config
13
- # $ mv Guardfile config/
14
- # $ ln -s config/Guardfile .
15
- #
16
- # and, you'll have to watch "config/Guardfile" instead of "Guardfile"
17
-
18
- # Note: The cmd option is now required due to the increasing number of ways
19
- # rspec may be run, below are examples of the most common uses.
20
- # * bundler: 'bundle exec rspec'
21
- # * bundler binstubs: 'bin/rspec'
22
- # * spring: 'bin/rspec' (This will use spring if running and you have
23
- # installed the spring binstubs per the docs)
24
- # * zeus: 'zeus rspec' (requires the server to be started separately)
25
- # * 'just' rspec: 'rspec'
26
-
27
- guard :rspec, cmd: "bundle exec rspec" do
28
- require "guard/rspec/dsl"
29
- dsl = Guard::RSpec::Dsl.new(self)
30
-
31
- # Feel free to open issues for suggestions and improvements
32
-
33
- # RSpec files
34
- rspec = dsl.rspec
35
- watch(rspec.spec_helper) { rspec.spec_dir }
36
- watch(rspec.spec_support) { rspec.spec_dir }
37
- watch(rspec.spec_files)
38
-
39
- # Ruby files
40
- ruby = dsl.ruby
41
- dsl.watch_spec_files_for(ruby.lib_files)
42
- end
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/bin/async_loop DELETED
@@ -1,37 +0,0 @@
1
- #!/usr/bin/env ruby --jit
2
- require 'bundler/setup'
3
- require 'async'
4
- require 'rswim'
5
-
6
- PORT = 4545
7
-
8
- @stuff = []
9
-
10
- def read
11
- if @stuff.empty?
12
- sleep 2
13
- end
14
- Array.new(@stuff.size) { @stuff.pop }
15
- end
16
-
17
- puts "Ready\n"
18
- begin
19
- # Run node (blocking)
20
- Async do
21
- Fiber.schedule do
22
- in_s = UDPSocket.new
23
- my_host = "localhost" # Socket.ip_address_list.find(&:ipv4_private?).ip_address
24
-
25
- in_s.bind(my_host, PORT)
26
- loop do
27
- text, sender = in_s.recvfrom(10_000)
28
- puts "received #{text} from network"
29
- @stuff << text
30
- end
31
- end
32
- loop { puts "Read: #{read }" }
33
- end
34
- rescue Interrupt
35
- puts "\nShutting down gracefully"
36
- end
37
- puts "\nDone"
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "rswim"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start(__FILE__)
data/bin/run_node DELETED
@@ -1,59 +0,0 @@
1
- #!/usr/bin/env ruby --jit
2
- require 'bundler/setup'
3
- require 'slop'
4
- require 'rswim'
5
-
6
- PORT = 4545
7
-
8
- opts = Slop.parse do |o|
9
- o.array '-s', '--seeds', 'a comma separated list of seed nodes'
10
- o.bool '-d', '--debug', 'turn on debug logging'
11
- o.on '--help' do
12
- puts o
13
- exit
14
- end
15
- o.on '-v', '--version' do
16
- puts RSwim::VERSION
17
- exit
18
- end
19
- end
20
-
21
- puts "Ruby version: #{RUBY_VERSION}"
22
-
23
- RSwim::Logger.level = ::Logger::DEBUG if opts.debug?
24
- seed_hosts = opts[:seeds]
25
- abort 'EOF' if seed_hosts.nil?
26
-
27
- if seed_hosts.empty?
28
- puts 'Operating with no seed nodes'
29
- else
30
- seed_hosts.each { |h| puts "Seed node: #{h}" }
31
- end
32
-
33
- puts 'Starting node'
34
-
35
- # Instantiate node, setting my_host to nil to auto detect host IP.
36
- node = RSwim::Node.udp(nil, seed_hosts, PORT, 3_500, 1_000)
37
-
38
- # Subscribe to updates
39
- node.subscribe do |host, status, custom_state|
40
- puts "Update: #{host} entered liveness state #{status} with custom state #{custom_state}"
41
- end
42
-
43
- Thread.new do
44
- uptime = 0
45
- loop do
46
- sleep(5)
47
- uptime += 5
48
- node.append_custom_state(:uptime_seconds, uptime)
49
- end
50
- end.abort_on_exception = true
51
-
52
- puts "Ready\n"
53
- begin
54
- # Run node (blocking)
55
- node.start
56
- rescue Interrupt
57
- puts "\nShutting down gracefully"
58
- end
59
- puts "\nDone"
data/bin/setup DELETED
@@ -1,8 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
7
-
8
- # Do any other automated setup that you need to do here
data/log/.keep DELETED
File without changes
data/rswim.gemspec DELETED
@@ -1,41 +0,0 @@
1
- lib = File.expand_path("lib", __dir__)
2
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require "rswim/version"
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "rswim"
7
- spec.version = RSwim::VERSION
8
- spec.authors = ["Erik Madsen"]
9
- spec.email = ["beatmadsen@gmail.com"]
10
-
11
- spec.summary = %q{Ruby implementation of the SWIM gossip protocol}
12
- spec.description = %q{RSwim is a Ruby implementation of the SWIM gossip protocol, a mechanism for discovering new peers and getting updates about liveness of existing peers in a network.}
13
- spec.homepage = "https://github.com/beatmadsen/rswim"
14
- spec.license = "MIT"
15
-
16
- spec.metadata["homepage_uri"] = spec.homepage
17
- spec.metadata["source_code_uri"] = "https://github.com/beatmadsen/rswim"
18
- spec.metadata["changelog_uri"] = "https://github.com/beatmadsen/rswim/blob/master/CHANGELOG.md"
19
-
20
- # Specify which files should be added to the gem when it is released.
21
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
22
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
23
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
24
- end
25
- spec.bindir = "exe"
26
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
- spec.require_paths = ["lib"]
28
-
29
- spec.required_ruby_version = '>= 3.0.0'
30
-
31
- spec.add_dependency 'zeitwerk', '~> 2.2'
32
- spec.add_dependency 'slop', '~> 4.9'
33
- spec.add_dependency 'async', '~> 1.30'
34
-
35
- spec.add_development_dependency "bundler", ">= 2.2.10"
36
- spec.add_development_dependency "rake", "~> 12.0"
37
- spec.add_development_dependency "rspec", "~> 3.0"
38
- spec.add_development_dependency 'guard-rspec', '~> 4.7'
39
- spec.add_development_dependency 'fuubar', '~> 2.5'
40
- spec.add_development_dependency 'byebug'
41
- end
data/tmp/.keep DELETED
File without changes