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 +4 -4
- data/CHANGELOG.md +3 -1
- data/README.md +8 -2
- data/lib/rswim/encryption.rb +43 -0
- data/lib/rswim/integration/udp/io_loop.rb +1 -31
- data/lib/rswim/integration/udp/sender.rb +37 -0
- data/lib/rswim/io_loop.rb +1 -1
- data/lib/rswim/node.rb +4 -2
- data/lib/rswim/serialization/encrypted/deserializer.rb +40 -0
- data/lib/rswim/serialization/encrypted/serializer.rb +41 -0
- data/lib/rswim/{integration → serialization/simple}/deserializer.rb +1 -1
- data/lib/rswim/{integration → serialization/simple}/serializer.rb +1 -1
- data/lib/rswim/version.rb +1 -1
- data/lib/rswim.rb +20 -1
- metadata +45 -28
- data/.gitignore +0 -18
- data/.rspec +0 -3
- data/.ruby-version +0 -1
- data/.travis.yml +0 -6
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -91
- data/Guardfile +0 -42
- data/Rakefile +0 -6
- data/bin/async_loop +0 -37
- data/bin/console +0 -14
- data/bin/run_node +0 -59
- data/bin/setup +0 -8
- data/log/.keep +0 -0
- data/rswim.gemspec +0 -41
- data/tmp/.keep +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d0488c9886f92df9a49c83521f881fdfc691ca2acfdf11deaef591a040a5e082
|
|
4
|
+
data.tar.gz: 16930933f6e7fd5ee4f464d9693d437a7a493e37015b79049ad3c4b2647ccf60
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
|
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
|
|
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
|
-
|
|
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
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
|
-
|
|
14
|
-
@
|
|
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
|
data/lib/rswim/version.rb
CHANGED
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
|
-
|
|
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.
|
|
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:
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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/
|
|
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
|
-
|
|
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.
|
|
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:
|
|
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
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.0.2
|
data/.travis.yml
DELETED
data/Gemfile
DELETED
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
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
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
|