orchestrator 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.md +158 -0
  3. data/README.md +13 -0
  4. data/Rakefile +7 -0
  5. data/app/controllers/orchestrator/api/dependencies_controller.rb +109 -0
  6. data/app/controllers/orchestrator/api/modules_controller.rb +183 -0
  7. data/app/controllers/orchestrator/api/systems_controller.rb +294 -0
  8. data/app/controllers/orchestrator/api/zones_controller.rb +62 -0
  9. data/app/controllers/orchestrator/api_controller.rb +13 -0
  10. data/app/controllers/orchestrator/base.rb +59 -0
  11. data/app/controllers/orchestrator/persistence_controller.rb +29 -0
  12. data/app/models/orchestrator/access_log.rb +35 -0
  13. data/app/models/orchestrator/control_system.rb +160 -0
  14. data/app/models/orchestrator/dependency.rb +87 -0
  15. data/app/models/orchestrator/mod/by_dependency/map.js +6 -0
  16. data/app/models/orchestrator/mod/by_module_type/map.js +6 -0
  17. data/app/models/orchestrator/module.rb +127 -0
  18. data/app/models/orchestrator/sys/by_modules/map.js +9 -0
  19. data/app/models/orchestrator/sys/by_zones/map.js +9 -0
  20. data/app/models/orchestrator/zone.rb +47 -0
  21. data/app/models/orchestrator/zone/all/map.js +6 -0
  22. data/config/routes.rb +43 -0
  23. data/lib/generators/module/USAGE +8 -0
  24. data/lib/generators/module/module_generator.rb +52 -0
  25. data/lib/orchestrator.rb +52 -0
  26. data/lib/orchestrator/control.rb +303 -0
  27. data/lib/orchestrator/core/mixin.rb +123 -0
  28. data/lib/orchestrator/core/module_manager.rb +258 -0
  29. data/lib/orchestrator/core/request_proxy.rb +109 -0
  30. data/lib/orchestrator/core/requests_proxy.rb +47 -0
  31. data/lib/orchestrator/core/schedule_proxy.rb +49 -0
  32. data/lib/orchestrator/core/system_proxy.rb +153 -0
  33. data/lib/orchestrator/datagram_server.rb +114 -0
  34. data/lib/orchestrator/dependency_manager.rb +131 -0
  35. data/lib/orchestrator/device/command_queue.rb +213 -0
  36. data/lib/orchestrator/device/manager.rb +83 -0
  37. data/lib/orchestrator/device/mixin.rb +35 -0
  38. data/lib/orchestrator/device/processor.rb +441 -0
  39. data/lib/orchestrator/device/transport_makebreak.rb +221 -0
  40. data/lib/orchestrator/device/transport_tcp.rb +139 -0
  41. data/lib/orchestrator/device/transport_udp.rb +89 -0
  42. data/lib/orchestrator/engine.rb +70 -0
  43. data/lib/orchestrator/errors.rb +23 -0
  44. data/lib/orchestrator/logger.rb +115 -0
  45. data/lib/orchestrator/logic/manager.rb +18 -0
  46. data/lib/orchestrator/logic/mixin.rb +11 -0
  47. data/lib/orchestrator/service/manager.rb +63 -0
  48. data/lib/orchestrator/service/mixin.rb +56 -0
  49. data/lib/orchestrator/service/transport_http.rb +55 -0
  50. data/lib/orchestrator/status.rb +229 -0
  51. data/lib/orchestrator/system.rb +108 -0
  52. data/lib/orchestrator/utilities/constants.rb +41 -0
  53. data/lib/orchestrator/utilities/transcoder.rb +57 -0
  54. data/lib/orchestrator/version.rb +3 -0
  55. data/lib/orchestrator/websocket_manager.rb +425 -0
  56. data/orchestrator.gemspec +35 -0
  57. data/spec/orchestrator/queue_spec.rb +200 -0
  58. metadata +271 -0
@@ -0,0 +1,221 @@
1
+
2
+ module Orchestrator
3
+ module Device
4
+ class MakebreakConnection < ::UV::OutboundConnection
5
+ def post_init(manager, processor, tls)
6
+ @manager = manager
7
+ @processor = processor
8
+ @config = @processor.config
9
+ @tls = tls
10
+
11
+ @connected = false
12
+ @changing_state = true
13
+ @disconnecting = false
14
+ @last_retry = 0
15
+
16
+
17
+ @activity = nil # Activity timer
18
+ @connecting = nil # Connection timer
19
+ @retries = 2 # Connection retries
20
+ @write_queue = []
21
+
22
+ @timeout = method(:timeout)
23
+ @reset_timeout = method(:reset_timeout)
24
+ end
25
+
26
+ def transmit(cmd)
27
+ return if @terminated
28
+
29
+ data = cmd[:data]
30
+
31
+ if @connected
32
+ promise = write(data)
33
+ reset_timeout
34
+ if cmd[:wait]
35
+ promise.catch do |err|
36
+ if @processor.queue.waiting == cmd
37
+ # Fail fast
38
+ @processor.thread.next_tick do
39
+ @processor.__send__(:resp_failure, err)
40
+ end
41
+ else
42
+ cmd[:defer].reject(err)
43
+ end
44
+ end
45
+ end
46
+ elsif @retries < 2
47
+ @write_queue << cmd
48
+ reconnect unless @disconnecting
49
+ else
50
+ cmd[:defer].reject(Error::CommandFailure.new "transmit aborted as disconnected")
51
+ end
52
+ # discards data when officially disconnected
53
+ end
54
+
55
+ def on_connect(transport)
56
+ @connected = true
57
+ @changing_state = false
58
+
59
+ if @connecting
60
+ @connecting.cancel
61
+ @connecting = nil
62
+ end
63
+
64
+ if @terminated
65
+ close_connection(:after_writing)
66
+ else
67
+ begin
68
+ use_tls(@config) if @tls
69
+ rescue => e
70
+ @manager.logger.print_error(e, 'error starting tls')
71
+ end
72
+
73
+ if @config[:wait_ready]
74
+ @delaying = ''
75
+ else
76
+ init_connection
77
+ end
78
+ end
79
+ end
80
+
81
+ def on_close
82
+ @delaying = false
83
+ @connected = false
84
+ @changing_state = false
85
+ @disconnecting = false
86
+
87
+
88
+ if @connecting
89
+ @connecting.cancel
90
+ @connecting = nil
91
+ end
92
+
93
+ # Prevent re-connect if terminated
94
+ unless @terminated
95
+ @retries += 1
96
+ the_time = @processor.thread.now
97
+ boundry = @last_retry + @config[:thrashing_threshold]
98
+
99
+ # ensure we are not thrashing (rapid connect then disconnect)
100
+ # This equals a disconnect and requires a warning
101
+ if @retries == 1 && boundry >= the_time
102
+ @retries += 1
103
+ @manager.logger.warn('possible connection thrashing. Disconnecting')
104
+ end
105
+
106
+ @activity.cancel if @activity
107
+ @activity = nil
108
+
109
+ if @retries == 1
110
+ if @write_queue.length > 0
111
+ # We reconnect here as there are pending writes
112
+ @last_retry = the_time
113
+ reconnect
114
+ end
115
+ else # retries > 1
116
+ @write_queue.clear
117
+
118
+ variation = 1 + rand(2000)
119
+ @connecting = @manager.get_scheduler.in(3000 + variation) do
120
+ @connecting = nil
121
+ reconnect
122
+ end
123
+
124
+ # we mark the queue as offline if more than 1 reconnect fails
125
+ # or if the first connect fails
126
+ if @retries == 2 || (@retries == 3 && @last_retry == 0)
127
+ @processor.disconnected
128
+ @processor.queue.offline(@config[:clear_queue_on_disconnect])
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ def on_read(data, *args)
135
+ if @delaying
136
+ @delaying << data
137
+ result = @delaying.split(@config[:wait_ready], 2)
138
+ if result.length > 1
139
+ @delaying = false
140
+ init_connection
141
+ rem = result[-1]
142
+ @processor.buffer(rem) unless rem.empty?
143
+ end
144
+ else
145
+ @processor.buffer(data)
146
+ end
147
+ end
148
+
149
+ def terminate
150
+ @terminated = true
151
+ @connecting.cancel if @connecting
152
+ @activity.cancel if @activity
153
+ close_connection(:after_writing) if @transport.connected
154
+ end
155
+
156
+ def disconnect
157
+ @connected = false
158
+ @disconnecting = true
159
+ close_connection(:after_writing)
160
+ end
161
+
162
+
163
+ protected
164
+
165
+
166
+ def timeout(*args)
167
+ @activity = nil
168
+ disconnect
169
+ end
170
+
171
+ def reset_timeout
172
+ return if @terminated
173
+
174
+ if @activity
175
+ @activity.cancel
176
+ @activity = nil
177
+ end
178
+
179
+ timeout = @config[:inactivity_timeout] || 0
180
+ if timeout > 0
181
+ @activity = @manager.get_scheduler.in(timeout, @timeout)
182
+ else # Wait for until queue complete
183
+ waiting = @processor.queue.waiting
184
+ if waiting
185
+ if waiting[:makebreak_set].nil?
186
+ waiting[:defer].promise.finally @reset_timeout
187
+ waiting[:makebreak_set] = true
188
+ end
189
+ elsif @processor.queue.length == 0
190
+ disconnect
191
+ end
192
+ end
193
+ end
194
+
195
+ def reconnect
196
+ return if @changing_state || @connected
197
+ @changing_state = true
198
+ super
199
+ end
200
+
201
+ def init_connection
202
+ # Write pending directly
203
+ queue = @write_queue
204
+ @write_queue = []
205
+ while queue.length > 0
206
+ transmit(queue.shift)
207
+ end
208
+
209
+ # Notify module
210
+ if @retries > 1
211
+ @processor.queue.online
212
+ @processor.connected
213
+ end
214
+ @retries = 0
215
+
216
+ # Start inactivity timeout
217
+ reset_timeout
218
+ end
219
+ end
220
+ end
221
+ end
@@ -0,0 +1,139 @@
1
+
2
+ module Orchestrator
3
+ module Device
4
+ class TcpConnection < ::UV::OutboundConnection
5
+ def post_init(manager, processor, tls)
6
+ @manager = manager
7
+ @processor = processor
8
+ @config = @processor.config
9
+ @tls = tls
10
+
11
+ # Delay retry by default if connection fails on load
12
+ @retries = 1 # Connection retries
13
+ @connecting = nil # Connection timer
14
+
15
+ # Last retry shouldn't break any thresholds
16
+ @last_retry = 0
17
+ end
18
+
19
+ def transmit(cmd)
20
+ return if @terminated
21
+ promise = write(cmd[:data])
22
+ if cmd[:wait]
23
+ promise.catch do |err|
24
+ if @processor.queue.waiting == cmd
25
+ # Fail fast
26
+ @processor.thread.next_tick do
27
+ @processor.__send__(:resp_failure, err)
28
+ end
29
+ else
30
+ cmd[:defer].reject(err)
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ def on_connect(transport)
37
+ if @terminated
38
+ close_connection(:after_writing)
39
+ else
40
+ begin
41
+ use_tls(@config) if @tls
42
+ rescue => e
43
+ @manager.logger.print_error(e, 'error starting tls')
44
+ end
45
+
46
+ if @config[:wait_ready]
47
+ @delaying = ''
48
+ else
49
+ init_connection
50
+ end
51
+ end
52
+ end
53
+
54
+ def on_close
55
+ unless @terminated
56
+ # Clear the connection delay if in use
57
+ @delaying = false if @delaying
58
+ @retries += 1
59
+ the_time = @processor.thread.now
60
+ boundry = @last_retry + @config[:thrashing_threshold]
61
+
62
+ # ensure we are not thrashing (rapid connect then disconnect)
63
+ # This equals a disconnect and requires a warning
64
+ if @retries == 1 && boundry >= the_time
65
+ @retries += 1
66
+ @processor.disconnected
67
+ @manager.logger.warn('possible connection thrashing. Disconnecting')
68
+ end
69
+
70
+ if @retries == 1
71
+ @last_retry = the_time
72
+ @processor.disconnected
73
+ reconnect
74
+ else
75
+ variation = 1 + rand(2000)
76
+ @connecting = @manager.get_scheduler.in(3000 + variation) do
77
+ @connecting = nil
78
+ reconnect
79
+ end
80
+
81
+ if @retries == 2
82
+ # NOTE:: edge case if disconnected on first connect
83
+ @processor.disconnected if @last_retry == 0
84
+
85
+ # we mark the queue as offline if more than 1 reconnect fails
86
+ @processor.queue.offline(@config[:clear_queue_on_disconnect])
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ def on_read(data, *args)
93
+ if @delaying
94
+ # Update last retry so we don't trigger multiple
95
+ # calls to disconnected as connection is working
96
+ @last_retry += 1
97
+
98
+ @delaying << data
99
+ result = @delaying.split(@config[:wait_ready], 2)
100
+ if result.length > 1
101
+ @delaying = false
102
+ init_connection
103
+ rem = result[-1]
104
+ @processor.buffer(rem) unless rem.empty?
105
+ end
106
+ else
107
+ @processor.buffer(data)
108
+ end
109
+ end
110
+
111
+ def terminate
112
+ @terminated = true
113
+ @connecting.cancel if @connecting
114
+ close_connection(:after_writing) if @transport.connected
115
+ end
116
+
117
+ def disconnect
118
+ # Shutdown quickly
119
+ close_connection
120
+ end
121
+
122
+
123
+ protected
124
+
125
+
126
+ def init_connection
127
+ # Enable keep alive every 30 seconds
128
+ keepalive(30)
129
+
130
+ # We only have to mark a queue online if more than 1 retry was required
131
+ if @retries > 1
132
+ @processor.queue.online
133
+ end
134
+ @retries = 0
135
+ @processor.connected
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,89 @@
1
+ module Orchestrator
2
+ module Device
3
+ class UdpConnection
4
+ def initialize(manager, processor)
5
+ @manager = manager
6
+ @loop = manager.thread
7
+ @processor = processor
8
+
9
+ settings = manager.settings
10
+ @ip = settings.ip
11
+ @port = settings.port
12
+ @on_read = method(:on_read)
13
+
14
+ # One per loop unless port specified
15
+ @udp_server = @loop.udp_service
16
+
17
+ if IPAddress.valid? @ip
18
+ @attached_ip = @ip
19
+ @udp_server.attach(@attached_ip, @port, @on_read)
20
+ @loop.next_tick do
21
+ # Call connected (we only need to do this once)
22
+ @processor.connected
23
+ end
24
+ else
25
+ variation = 1 + rand(60000 * 5) # 5min
26
+ @checker = @manager.get_scheduler.in(60000 * 5 + variation) do
27
+ find_ip(@ip)
28
+ end
29
+ find_ip(@ip)
30
+ end
31
+ end
32
+
33
+ def transmit(cmd)
34
+ return if @terminated
35
+ @udp_server.send(@attached_ip, @port, cmd[:data])
36
+ end
37
+
38
+ def on_read(data)
39
+ # We schedule as UDP server may be on a different thread
40
+ @loop.schedule do
41
+ @processor.buffer(data)
42
+ end
43
+ end
44
+
45
+ def terminate
46
+ #@processor.disconnected # Disconnect should never be called
47
+ @terminated = true
48
+ if @searching
49
+ @searching.cancel
50
+ @searching = nil
51
+ else
52
+ @udp_server.detach(@attached_ip, @port)
53
+ end
54
+
55
+ @checker.cancel if @checker
56
+ end
57
+
58
+ def disconnect; end
59
+
60
+
61
+ protected
62
+
63
+
64
+ def find_ip(hostname)
65
+ @loop.lookup(hostname).then(proc{ |result|
66
+ update_ip(result[0][0])
67
+ }, proc { |failure|
68
+ variation = 1 + rand(8000)
69
+ @searching = @manager.get_scheduler.in(8000 + variation) do
70
+ @searching = nil
71
+ find_ip(hostname)
72
+ end
73
+ })
74
+ end
75
+
76
+ def update_ip(ip)
77
+ if ip != @attached_ip
78
+ if @attached_ip
79
+ @udp_server.detach(@attached_ip, @port)
80
+ else
81
+ @processor.connected
82
+ end
83
+ @attached_ip = ip
84
+ @udp_server.attach(@attached_ip, @port, @on_read)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end