ruby_nos 0.0.1
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 +15 -0
- data/.gitignore +8 -0
- data/.travis.yml +10 -0
- data/Gemfile +13 -0
- data/Gemfile.lock +61 -0
- data/LICENSE +21 -0
- data/README.md +64 -0
- data/Rakefile +19 -0
- data/config/environment.rb +17 -0
- data/lib/initializable.rb +14 -0
- data/lib/ruby_nos.rb +64 -0
- data/lib/ruby_nos/agent.rb +127 -0
- data/lib/ruby_nos/aliasing.rb +15 -0
- data/lib/ruby_nos/cloud.rb +80 -0
- data/lib/ruby_nos/endpoint.rb +43 -0
- data/lib/ruby_nos/formatter.rb +25 -0
- data/lib/ruby_nos/list.rb +34 -0
- data/lib/ruby_nos/message.rb +47 -0
- data/lib/ruby_nos/processor.rb +133 -0
- data/lib/ruby_nos/remote_agent.rb +41 -0
- data/lib/ruby_nos/rest_api.rb +23 -0
- data/lib/ruby_nos/signature_generator.rb +22 -0
- data/lib/ruby_nos/udp_receptor.rb +47 -0
- data/lib/ruby_nos/udp_sender.rb +26 -0
- data/lib/ruby_nos/version.rb +3 -0
- data/ruby_nos.gemspec +22 -0
- data/spec/ruby_nos.spec.rb +7 -0
- data/spec/ruby_nos/agent_spec.rb +120 -0
- data/spec/ruby_nos/cloud_spec.rb +107 -0
- data/spec/ruby_nos/endpoint_spec.rb +38 -0
- data/spec/ruby_nos/formatter_spec.rb +34 -0
- data/spec/ruby_nos/list_spec.rb +54 -0
- data/spec/ruby_nos/message_spec.rb +47 -0
- data/spec/ruby_nos/processor_spec.rb +104 -0
- data/spec/ruby_nos/remote_agent_spec.rb +57 -0
- data/spec/ruby_nos/rest_api_spec.rb +23 -0
- data/spec/ruby_nos/signature_generator_spec.rb +28 -0
- data/spec/ruby_nos/udp_receptor_spec.rb +31 -0
- data/spec/ruby_nos/udp_sender_spec.rb +35 -0
- data/spec/spec_helper.rb +28 -0
- metadata +129 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
!binary "U0hBMQ==":
|
|
3
|
+
metadata.gz: !binary |-
|
|
4
|
+
YTgwMGY2Yjk5N2M4ODExNTM5ODVhNTdjNmI0MGQ4Njg1YjIzZWY2YQ==
|
|
5
|
+
data.tar.gz: !binary |-
|
|
6
|
+
M2VhNmQ3MjEzNjY5MjQ5Yzk2NWQ5M2YwNjE5N2FhMWFmNGU0MmM0Mg==
|
|
7
|
+
SHA512:
|
|
8
|
+
metadata.gz: !binary |-
|
|
9
|
+
NzA0ZGQyNTQwM2JmYTE0YTU1NmRkNmU5MzA1ZDY5OTY5YzFhZDM4NmU4NDFi
|
|
10
|
+
ZjkxZjE3YTI0ZDRmMDZhNmFlZWNlYWI0YTYyZmY2NTI2NzU1ZDE4MTRhYmVi
|
|
11
|
+
NGM2ZGM5ZDcwZDY5M2Y1NDdhYzk4ZGMzMGFmY2M4YjY5MjFmNzQ=
|
|
12
|
+
data.tar.gz: !binary |-
|
|
13
|
+
YTg2NzM3MzYwMWNhYzE4ODY0OTk0MzBhNjkzZDZjMGFmNzRiMmRhODA4YjM1
|
|
14
|
+
NGE4ODUwMTQ4NGYyOWI4ZTMyNzFiODQ2OTg5OTQzMzEyNmE0NWVhZGY0Nzc4
|
|
15
|
+
MGM0MzA3OTc5MDFlODNkMjM3YjU5Y2QzYjJhNThlNTU1ZWJjOWE=
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
ruby_nos (0.0.1)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
activesupport (4.1.5)
|
|
10
|
+
i18n (~> 0.6, >= 0.6.9)
|
|
11
|
+
json (~> 1.7, >= 1.7.7)
|
|
12
|
+
minitest (~> 5.1)
|
|
13
|
+
thread_safe (~> 0.1)
|
|
14
|
+
tzinfo (~> 1.1)
|
|
15
|
+
codeclimate-test-reporter (0.4.7)
|
|
16
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
|
17
|
+
coderay (1.1.0)
|
|
18
|
+
diff-lcs (1.2.5)
|
|
19
|
+
docile (1.1.5)
|
|
20
|
+
i18n (0.6.11)
|
|
21
|
+
json (1.8.1)
|
|
22
|
+
method_source (0.8.2)
|
|
23
|
+
minitest (5.4.1)
|
|
24
|
+
pry (0.10.1)
|
|
25
|
+
coderay (~> 1.1.0)
|
|
26
|
+
method_source (~> 0.8.1)
|
|
27
|
+
slop (~> 3.4)
|
|
28
|
+
rake (10.3.2)
|
|
29
|
+
rspec (3.1.0)
|
|
30
|
+
rspec-core (~> 3.1.0)
|
|
31
|
+
rspec-expectations (~> 3.1.0)
|
|
32
|
+
rspec-mocks (~> 3.1.0)
|
|
33
|
+
rspec-core (3.1.4)
|
|
34
|
+
rspec-support (~> 3.1.0)
|
|
35
|
+
rspec-expectations (3.1.1)
|
|
36
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
37
|
+
rspec-support (~> 3.1.0)
|
|
38
|
+
rspec-mocks (3.1.1)
|
|
39
|
+
rspec-support (~> 3.1.0)
|
|
40
|
+
rspec-support (3.1.0)
|
|
41
|
+
simplecov (0.10.0)
|
|
42
|
+
docile (~> 1.1.0)
|
|
43
|
+
json (~> 1.8)
|
|
44
|
+
simplecov-html (~> 0.10.0)
|
|
45
|
+
simplecov-html (0.10.0)
|
|
46
|
+
slop (3.6.0)
|
|
47
|
+
thread_safe (0.3.4)
|
|
48
|
+
tzinfo (1.2.2)
|
|
49
|
+
thread_safe (~> 0.1)
|
|
50
|
+
|
|
51
|
+
PLATFORMS
|
|
52
|
+
ruby
|
|
53
|
+
|
|
54
|
+
DEPENDENCIES
|
|
55
|
+
activesupport
|
|
56
|
+
bundler
|
|
57
|
+
codeclimate-test-reporter
|
|
58
|
+
pry
|
|
59
|
+
rake
|
|
60
|
+
rspec
|
|
61
|
+
ruby_nos!
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Workshare
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#RubyNos
|
|
2
|
+
[](https://travis-ci.org/mariaclrd/ruby-nos)
|
|
3
|
+
[](https://codeclimate.com/github/mariaclrd/ruby-nos)
|
|
4
|
+
|
|
5
|
+
A gem to provide microservices autodiscovery to Ruby microservices. This gem allows a microservice to publish its
|
|
6
|
+
existence on a cloud, store other microservices information and public its API.
|
|
7
|
+
|
|
8
|
+
#Configuration
|
|
9
|
+
|
|
10
|
+
You can configure the following characteristics of the gem:
|
|
11
|
+
|
|
12
|
+
###Agent characteristics
|
|
13
|
+
|
|
14
|
+
Every microservice will have an Agent object that will be the responsible of sending the messages to the cloud. You can
|
|
15
|
+
configure these characteristics:
|
|
16
|
+
|
|
17
|
+
* **Cloud_uuid:** The identifier of the cloud where all the microservices will be exchanging messages.
|
|
18
|
+
* **Port:** UDP port where the Agent will be listening to other microservices messages.
|
|
19
|
+
* **Group_address:** The IP group where all the microservices will be listening to the messages.
|
|
20
|
+
* **Time_between_messages:** Allows to specify how much time will pass between the keep alive messages.
|
|
21
|
+
* **Hops:** Number of hops for the messages.
|
|
22
|
+
|
|
23
|
+
An example of configuration would be:
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
config = load_file('ruby_nos')
|
|
27
|
+
RubyNos.configure do |c|
|
|
28
|
+
c.cloud_uuid = config['cloud']
|
|
29
|
+
c.port = config['port']
|
|
30
|
+
c.group_address = config['group_address']
|
|
31
|
+
c.time_between_messages = config['time_between_messages']
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To make the agent start sending and listening to messages you will have to add the following line to your code:
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
RubyNos::Agent.new.start!
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
###Publish an API
|
|
42
|
+
|
|
43
|
+
To publish the API of a microservice you will have to create a RestAPI agent and associate it to the Agent.
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
ruby_nos_api = RubyNos::RestApi.new
|
|
47
|
+
ruby_nos_agent.rest_api = ruby_nos_api
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
To publish an endpoint you have to add to the rest_api object.
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
ruby_nos_api.add_endpoint(path: <endpoint path> , type: <"PUB", "HCK", "INT">, port: <application port>)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
You will have to specify the type of the endpoint, there are three types supported right now:
|
|
58
|
+
|
|
59
|
+
* **PUBLIC(PUB):** If the endpoint belongs to a public API.
|
|
60
|
+
* **INTERNAL(INT):** If the endpoint belongs to an internal API.
|
|
61
|
+
* **HEALTHCHECK(HCK):** For a healthcheck endpoint.
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler'
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
Bundler.setup(:default, :development)
|
|
6
|
+
rescue Bundler::BundlerError => e
|
|
7
|
+
$stderr.puts e.message
|
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
9
|
+
exit e.status_code
|
|
10
|
+
end
|
|
11
|
+
require 'rake'
|
|
12
|
+
|
|
13
|
+
require 'rspec/core/rake_task'
|
|
14
|
+
task :default => :spec
|
|
15
|
+
task :test => :spec
|
|
16
|
+
desc "Run all specs"
|
|
17
|
+
RSpec::Core::RakeTask.new('spec') do |spec|
|
|
18
|
+
spec.rspec_opts = %w{}
|
|
19
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
root_path = File.join(File.dirname(__FILE__),'..')
|
|
2
|
+
lib_path = File.join(root_path,'lib')
|
|
3
|
+
app_name = 'ruby_nos'
|
|
4
|
+
# Add all files to Load path
|
|
5
|
+
$LOAD_PATH << root_path
|
|
6
|
+
$LOAD_PATH << lib_path
|
|
7
|
+
$LOAD_PATH << File.join(lib_path, app_name)
|
|
8
|
+
require 'rubygems'
|
|
9
|
+
require 'bundler'
|
|
10
|
+
begin
|
|
11
|
+
Bundler.setup(:default)
|
|
12
|
+
rescue Bundler::BundlerError => e
|
|
13
|
+
$stderr.puts e.message
|
|
14
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
|
15
|
+
exit e.status_code
|
|
16
|
+
end
|
|
17
|
+
require app_name
|
data/lib/ruby_nos.rb
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require "ruby_nos/version"
|
|
2
|
+
require "logger"
|
|
3
|
+
|
|
4
|
+
module RubyNos
|
|
5
|
+
autoload :Agent, "ruby_nos/agent"
|
|
6
|
+
autoload :Aliasing, "ruby_nos/aliasing"
|
|
7
|
+
autoload :Cloud, "ruby_nos/cloud"
|
|
8
|
+
autoload :Endpoint, "ruby_nos/endpoint"
|
|
9
|
+
autoload :Formatter, "ruby_nos/formatter"
|
|
10
|
+
autoload :Initializable, "initializable"
|
|
11
|
+
autoload :List, "ruby_nos/list"
|
|
12
|
+
autoload :Message, "ruby_nos/message"
|
|
13
|
+
autoload :Processor, "ruby_nos/processor"
|
|
14
|
+
autoload :RemoteAgent, "ruby_nos/remote_agent"
|
|
15
|
+
autoload :RestApi, "ruby_nos/rest_api"
|
|
16
|
+
autoload :SignatureGenerator, "ruby_nos/signature_generator"
|
|
17
|
+
autoload :UDPReceptor, "ruby_nos/udp_receptor"
|
|
18
|
+
autoload :UDPSender, "ruby_nos/udp_sender"
|
|
19
|
+
autoload :VERSION, "ruby_nos/version"
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
|
|
23
|
+
attr_accessor :signature_key, :logger, :port, :cloud_uuid, :group_address, :time_between_messages, :keep_alive_time, :hops
|
|
24
|
+
|
|
25
|
+
def configure
|
|
26
|
+
if block_given?
|
|
27
|
+
yield self
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def signature_key
|
|
33
|
+
@signature_key ||= "key"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def logger
|
|
37
|
+
@logger ||= Logger.new './log/application.log'
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def port
|
|
41
|
+
@port ||= 3784
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def cloud_uuid
|
|
45
|
+
@cloud_uuid ||= SecureRandom.uuid
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def group_address
|
|
49
|
+
@group_address ||= "230.31.32.33"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def time_between_messages
|
|
53
|
+
@time_between_message ||= 30
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def keep_alive_time
|
|
57
|
+
@keep_alive_time ||= 60*1000
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def hops
|
|
61
|
+
@hops ||= 10
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
module RubyNos
|
|
2
|
+
class Agent
|
|
3
|
+
include Initializable
|
|
4
|
+
attr_accessor :uuid, :cloud, :udp_tx, :udp_rx,:processor, :rest_api
|
|
5
|
+
|
|
6
|
+
def uuid
|
|
7
|
+
@uuid ||= SecureRandom.uuid
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def udp_tx
|
|
11
|
+
@udp_tx ||= UDPSender.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def udp_rx
|
|
15
|
+
@udp_rx ||= UDPReceptor.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cloud
|
|
19
|
+
@cloud ||= Cloud.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def processor
|
|
23
|
+
@processor ||= Processor.new(self)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rest_api
|
|
27
|
+
@rest_api ||= RestApi.new
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def start!
|
|
31
|
+
at_exit {
|
|
32
|
+
send_desconnection_message
|
|
33
|
+
}
|
|
34
|
+
listen
|
|
35
|
+
join_cloud
|
|
36
|
+
maintain_cloud
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def listen
|
|
40
|
+
udp_rx.listen(processor)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def send_message args={}
|
|
44
|
+
message = build_message(args)
|
|
45
|
+
udp_tx.send({host: args[:host], port: args[:port], message: message})
|
|
46
|
+
message
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def maintain_cloud
|
|
50
|
+
begin
|
|
51
|
+
thread = Thread.new do
|
|
52
|
+
i = 0
|
|
53
|
+
loop do
|
|
54
|
+
i = i+1
|
|
55
|
+
RubyNos.logger.send(:info, "Iteration number #{i}")
|
|
56
|
+
RubyNos.logger.send(:info, "Agents on the cloud #{cloud.list.list_of_keys.count}")
|
|
57
|
+
send_discovery_messages
|
|
58
|
+
send_connection_messages
|
|
59
|
+
sleep RubyNos.time_between_messages
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
thread
|
|
63
|
+
rescue StandardError => e
|
|
64
|
+
RubyNos.logger.send(:info, "Error executing the thread #{e.message}")
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def send_connection_messages
|
|
69
|
+
cloud.list.list_of_keys.each do |agent_uuid|
|
|
70
|
+
last_message_exists?(agent_uuid) ? send_message({to: "AGT:#{uuid_for_message(agent_uuid)}", type: "PIN"}) : cloud.list.eliminate(agent_uuid)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def send_discovery_messages
|
|
75
|
+
send_message({type: 'DSC'})
|
|
76
|
+
send_message({type: 'ENQ'})
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def join_cloud
|
|
80
|
+
send_message({type: 'PRS'})
|
|
81
|
+
send_message({type: 'QNE'}) unless rest_api.endpoints.empty?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def send_desconnection_message
|
|
85
|
+
send_message({type: "PRS", data: {present: 0}})
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
def last_message_exists?(agent_uuid)
|
|
91
|
+
remote_agent = cloud.list.info_for(agent_uuid)
|
|
92
|
+
(Formatter.timestamp - remote_agent.timestamp) < RubyNos.keep_alive_time
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def build_message args
|
|
96
|
+
if args[:data]
|
|
97
|
+
data = args[:data]
|
|
98
|
+
elsif args[:type] == "PRS"
|
|
99
|
+
data = receptor_info
|
|
100
|
+
elsif args[:type] == "QNE"
|
|
101
|
+
data = rest_api.to_hash if rest_api
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
message_hash = {from: "AGT:#{uuid_for_message(uuid)}", to: args[:to] || "CLD:#{uuid_for_message(cloud.uuid)}", type: args[:type]}
|
|
105
|
+
message_hash.merge!({data: data}) if data
|
|
106
|
+
|
|
107
|
+
Message.new(message_hash).serialize
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def receptor_info
|
|
112
|
+
{present: 1, endpoints: ["UDP,#{udp_rx.socket.connect_address.ip_port},#{udp_rx.socket.connect_address.ip_address}"]}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def formatter
|
|
116
|
+
@formatter ||= Formatter.new
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def uuid_for_message uuid
|
|
120
|
+
if formatter.uuid_format?(uuid)
|
|
121
|
+
formatter.uuid_to_string(uuid)
|
|
122
|
+
else
|
|
123
|
+
uuid
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|