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.
@@ -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=
@@ -0,0 +1,8 @@
1
+ # Files to ignore
2
+
3
+ *.DS_Store
4
+ log/*
5
+ Guardfile
6
+ *~
7
+ .idea/*
8
+ projectFilesBackup/*
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.6
6
+ - 2.2.2
7
+ cache: bundler
8
+ addons:
9
+ code_climate:
10
+ repo_token: cfa24207fbf4926805264ce2b1d04ea090543297f74ad06648346567ead2c839
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test, :development do
6
+ gem 'pry', require: false
7
+ gem 'activesupport', require: false
8
+ end
9
+
10
+ group :test do
11
+ gem "codeclimate-test-reporter"
12
+ gem 'rspec', require: false
13
+ end
@@ -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.
@@ -0,0 +1,64 @@
1
+ #RubyNos
2
+ [![Build Status](https://travis-ci.org/mariaclrd/ruby-nos.svg?branch=master)](https://travis-ci.org/mariaclrd/ruby-nos)
3
+ [![Code Climate](https://codeclimate.com/github/mariaclrd/ruby-nos/badges/gpa.svg)](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
+
@@ -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
@@ -0,0 +1,14 @@
1
+ module Initializable
2
+
3
+ def initialize args = {}
4
+ set_attributes(args)
5
+ end
6
+
7
+ private
8
+
9
+ def set_attributes args
10
+ args.each do |k,v|
11
+ send("#{k}=",v)
12
+ end
13
+ end
14
+ end
@@ -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