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
+ module Aliasing
2
+
3
+ module ClassMethods
4
+ def attr_alias(new_attr, original)
5
+ alias_method(new_attr, original) if method_defined? original
6
+ new_writer = "#{new_attr}="
7
+ original_writer = "#{original}="
8
+ alias_method(new_writer, original_writer) if method_defined? original_writer
9
+ end
10
+ end
11
+
12
+ def self.included klass
13
+ klass.extend ClassMethods
14
+ end
15
+ end
@@ -0,0 +1,80 @@
1
+ module RubyNos
2
+ class Cloud
3
+ include Initializable
4
+ attr_accessor :uuid, :current_agent, :list
5
+
6
+ def uuid
7
+ @uuid ||= RubyNos.cloud_uuid
8
+ end
9
+
10
+ def list
11
+ @list ||= List.new
12
+ end
13
+
14
+ def update agent_info
15
+ if agent_info.is_a?(Hash)
16
+ self.current_agent = build_remote_agent(agent_info)
17
+ else
18
+ self.current_agent = agent_info
19
+ end
20
+
21
+ if list.is_on_the_list?(self.current_agent.uuid)
22
+ update_actual_info
23
+ else
24
+ RubyNos.logger.send(:info, "Added agent #{self.current_agent.uuid}")
25
+ list.add(self.current_agent)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def build_remote_agent agent_info
32
+ agent = RemoteAgent.new(uuid: agent_info[:agent_uuid], timestamp: (agent_info[:timestamp] || timestamp_for_list))
33
+ info = agent_info[:info]
34
+ agent.endpoints = process_endpoints(info[:endpoints]) if (info && info[:endpoints])
35
+ agent
36
+ end
37
+
38
+ def timestamp_for_list
39
+ Formatter.timestamp
40
+ end
41
+
42
+ def update_actual_info
43
+ if correct_timestamp? && !same_info?
44
+ prepare_agent
45
+ list.update(self.current_agent.uuid, self.current_agent)
46
+ end
47
+ end
48
+
49
+ def remote_agent_on_the_list
50
+ list.info_for(self.current_agent.uuid)
51
+ end
52
+
53
+ def prepare_agent
54
+ if self.current_agent.endpoints == []
55
+ self.current_agent.endpoints = remote_agent_on_the_list.endpoints if remote_agent_on_the_list.endpoints
56
+ end
57
+ if self.current_agent.rest_api == nil
58
+ self.current_agent.rest_api = remote_agent_on_the_list.rest_api if remote_agent_on_the_list.rest_api
59
+ end
60
+ end
61
+
62
+ def correct_timestamp?
63
+ timestamp = current_agent.timestamp
64
+ ((timestamp_for_list - RubyNos.keep_alive_time) < timestamp) && (timestamp <= timestamp_for_list)
65
+ end
66
+
67
+ def same_info?
68
+ remote_agent_on_the_list.same_endpoints?(self.current_agent) && remote_agent_on_the_list.same_api?(self.current_agent) && remote_agent_on_the_list.same_timestamp?(self.current_agent)
69
+ end
70
+
71
+ def process_endpoints endpoints
72
+ [].tap do |endpoints_info|
73
+ endpoints.each do |endpoint|
74
+ e_info = endpoint.split(",")
75
+ endpoints_info << Endpoint.new({type: e_info[0], port: e_info[1], host: e_info[2]})
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,43 @@
1
+ module RubyNos
2
+ class Endpoint
3
+ include Initializable
4
+ include Aliasing
5
+
6
+ attr_accessor :path, :port, :sticky, :type, :priority, :host
7
+ ALLOWED_TYPES = ["PUBLIC", "HEALTHCHECK", "INTERNAL", "MSNOS_HTTP", "UDP", "HTTP", "PUB", "HCK", "INT", "MHT"]
8
+
9
+ attr_alias :pa, :path
10
+ attr_alias :po, :port
11
+ attr_alias :st, :sticky
12
+ attr_alias :ty, :type
13
+ attr_alias :xp, :priority
14
+ attr_alias :ho, :host
15
+
16
+ def type= type
17
+ if ALLOWED_TYPES.include?(type)
18
+ @type = type
19
+ else
20
+ raise ArgumentError
21
+ end
22
+ end
23
+
24
+ def priority
25
+ @priority ||= 0
26
+ end
27
+
28
+ def sticky
29
+ @sticky ||= 0
30
+ end
31
+
32
+ def to_hash
33
+ {
34
+ pa: self.path,
35
+ po: self.port,
36
+ st: self.sticky,
37
+ ty: self.type,
38
+ xp: self.priority,
39
+ ho: self.host
40
+ }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,25 @@
1
+ require "json"
2
+
3
+ module RubyNos
4
+ class Formatter
5
+ def convert_to_uuid string_uuid
6
+ string_uuid.match(/(\h{8})(\h{4})(\h{4})(\h{4})(\h{12})/).captures.join("-")
7
+ end
8
+
9
+ def uuid_format? uuid
10
+ !!uuid.match(/\h{8}-\h{4}-\h{4}-\h{4}-\h{12}/)
11
+ end
12
+
13
+ def uuid_to_string uuid
14
+ uuid.gsub("-", "")
15
+ end
16
+
17
+ def parse_message message
18
+ JSON.parse(message, {symbolize_names: true})
19
+ end
20
+
21
+ def self.timestamp
22
+ (Time.now.to_f*1000).to_i
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,34 @@
1
+ module RubyNos
2
+ class List
3
+ include Initializable
4
+ attr_accessor :list
5
+
6
+ def list
7
+ @list ||= []
8
+ end
9
+
10
+ def add element
11
+ list << {element.uuid => element}
12
+ end
13
+
14
+ def update uuid, new_element
15
+ list.select{|e| e[uuid]}.first[uuid] = new_element
16
+ end
17
+
18
+ def eliminate uuid
19
+ list.delete_if{|e| e.keys.first == uuid}
20
+ end
21
+
22
+ def info_for uuid
23
+ list.select{|e| e[uuid]}.first[uuid]
24
+ end
25
+
26
+ def list_of_keys
27
+ list.map{|e| e.keys}.flatten
28
+ end
29
+
30
+ def is_on_the_list? uuid
31
+ list_of_keys.include?(uuid)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ require "securerandom"
2
+ require "initializable"
3
+
4
+ module RubyNos
5
+ class Message
6
+ include Aliasing
7
+
8
+ include Initializable
9
+ attr_accessor :version, :from, :type, :to, :hops, :reliable, :data, :id, :signature, :timestamp
10
+ attr_alias :v, :version
11
+ attr_alias :fr, :from
12
+ attr_alias :ty, :type
13
+ attr_alias :hp, :hops
14
+ attr_alias :rx, :reliable
15
+ attr_alias :dt, :data
16
+ attr_alias :sg, :signature
17
+ attr_alias :ts, :timestamp
18
+
19
+ def to_hash
20
+ {
21
+ v: self.version || "1.0",
22
+ ty: self.type,
23
+ fr: self.from,
24
+ to: self.to,
25
+ hp: self.hops || RubyNos.hops,
26
+ ts: self.timestamp || generate_miliseconds_timestamp,
27
+ rx: self.reliable || 0,
28
+ dt: self.data
29
+ }.delete_if{|key, value| value==nil || value == {}}
30
+ end
31
+
32
+ def serialize
33
+ message = to_hash
34
+ message.merge!({sg: signature_generator.generate_signature(message.to_s)})
35
+ end
36
+
37
+ private
38
+
39
+ def generate_miliseconds_timestamp
40
+ Formatter.timestamp
41
+ end
42
+
43
+ def signature_generator
44
+ @signature_generator ||= SignatureGenerator.new
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,133 @@
1
+ module RubyNos
2
+ class Processor
3
+
4
+ attr_accessor :agent, :current_message
5
+
6
+ def initialize agent
7
+ @agent = agent
8
+ end
9
+
10
+ def formatter
11
+ @formatter ||= Formatter.new
12
+ end
13
+
14
+ def signature_generator
15
+ @signature_generator ||= SignatureGenerator.new
16
+ end
17
+
18
+ def process_message received_message
19
+ formatted_message = formatter.parse_message(received_message)
20
+ self.current_message = Message.new(formatted_message)
21
+
22
+ unless sender_uuid == agent.uuid || !correct_signature?(formatted_message)
23
+ RubyNos.logger.send(:info, "#{self.current_message.type} arrives")
24
+ if agent_receptor? || cloud_receptor?
25
+ processor = message_processor.fetch(current_message.type, nil)
26
+ processor.call if processor
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def message_processor
34
+ {
35
+ "PIN" => lambda {process_pin_message},
36
+ "PON" => lambda {process_pon_message},
37
+ "PRS" => lambda {process_presence_message},
38
+ "DSC" => lambda {process_discovery_message},
39
+ "ENQ" => lambda {process_enquiry_message},
40
+ "QNE" => lambda {process_enquiry_answer_message}
41
+ }
42
+ end
43
+
44
+ def sender_uuid
45
+ get_uuid(self.current_message.from)
46
+ end
47
+
48
+ def receptor_uuid
49
+ get_uuid(self.current_message.to)
50
+ end
51
+
52
+ def received_api
53
+ RestApi.new({name: self.current_message.data[:name]}).tap do |api|
54
+ if self.current_message.data[:apis]
55
+ self.current_message.data[:apis].each do |endpoint|
56
+ api.add_endpoint(endpoint)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def correct_signature? received_message
63
+ if received_message[:sg]
64
+ signature = received_message.delete(:sg)
65
+ signature_generator.valid_signature?(received_message.to_s, signature)
66
+ else
67
+ true
68
+ end
69
+ end
70
+
71
+ def process_pin_message
72
+ update_cloud
73
+ send_response "PON"
74
+ end
75
+
76
+ def process_pon_message
77
+ update_cloud
78
+ end
79
+
80
+ def process_presence_message
81
+ if self.current_message.data[:present] == 0
82
+ agent.cloud.list.eliminate(sender_uuid)
83
+ else
84
+ update_cloud
85
+ end
86
+ end
87
+
88
+ def process_discovery_message
89
+ unless agent.cloud.list.is_on_the_list?(sender_uuid)
90
+ update_cloud
91
+ end
92
+ send_response "PRS"
93
+ end
94
+
95
+ def process_enquiry_message
96
+ update_cloud
97
+ send_response "QNE"
98
+ end
99
+
100
+ def process_enquiry_answer_message
101
+ agent.cloud.update(RemoteAgent.new({uuid: sender_uuid, timestamp: self.current_message.timestamp, rest_api: received_api}))
102
+ end
103
+
104
+ def update_cloud
105
+ agent.cloud.update({agent_uuid: sender_uuid, info: self.current_message.data, timestamp: self.current_message.timestamp}) unless agent.cloud.uuid == sender_uuid
106
+ end
107
+
108
+ def agent_receptor?
109
+ agent.uuid == receptor_uuid
110
+ end
111
+
112
+ def cloud_receptor?
113
+ agent.cloud.uuid == receptor_uuid
114
+ end
115
+
116
+ def get_uuid uuid_param
117
+ if uuid_param.start_with?("AGT:", "CLD:")
118
+ uuid = formatter.convert_to_uuid(uuid_param[4..-1])
119
+ if formatter.uuid_format?(uuid)
120
+ uuid
121
+ else
122
+ raise(ArgumentError, 'Argument format is incorrect')
123
+ end
124
+ else
125
+ raise(ArgumentError, 'Argument format is incorrect')
126
+ end
127
+ end
128
+
129
+ def send_response type
130
+ agent.send_message({type: type})
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,41 @@
1
+ module RubyNos
2
+ class RemoteAgent
3
+ include Initializable
4
+ attr_accessor :uuid, :rest_api, :endpoints, :timestamp
5
+
6
+ def endpoints
7
+ @endpoints ||= []
8
+ end
9
+
10
+ def add_endpoint *args
11
+ endpoints << Endpoint.new(*args)
12
+ end
13
+
14
+ def endpoints_collection
15
+ endpoints.map{|e| e.to_hash}
16
+ end
17
+
18
+ def same_endpoints? another_agent
19
+ endpoints_collection == another_agent.endpoints_collection
20
+ end
21
+
22
+ def same_api? another_agent
23
+ if rest_api && another_agent.rest_api
24
+ rest_api.to_hash == another_agent.rest_api.to_hash
25
+ else
26
+ false
27
+ end
28
+ end
29
+
30
+ def same_timestamp? another_agent
31
+ timestamp == another_agent.timestamp
32
+ end
33
+
34
+ def to_hash
35
+ {
36
+ uuid: uuid,
37
+ timestamp: Time.at(timestamp/1000)
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,23 @@
1
+ module RubyNos
2
+ class RestApi
3
+ include Initializable
4
+ attr_accessor :name, :endpoints, :port, :host
5
+
6
+ def endpoints
7
+ @endpoints ||= []
8
+ end
9
+
10
+ def add_endpoint args
11
+ args.merge!({port: port}) unless (args[:port] || args[:po] )
12
+ args.merge!({host: host}) unless (args[:host] || args[:ho] )
13
+ endpoints << Endpoint.new(args)
14
+ end
15
+
16
+ def to_hash
17
+ {
18
+ name: self.name,
19
+ apis: endpoints.map{|e| e.to_hash}
20
+ }
21
+ end
22
+ end
23
+ end