nutella_framework 0.4.5 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +14 -15
  3. data/VERSION +1 -1
  4. data/framework_components/beacon-cloud-bot/README.md +27 -0
  5. data/framework_components/beacon-cloud-bot/beacon_cloud_bot.rb +154 -0
  6. data/framework_components/beacon-cloud-bot/nutella.json +6 -0
  7. data/framework_components/beacon-cloud-bot/startup +4 -0
  8. data/framework_components/beacon-cloud-interface/LICENSE +21 -0
  9. data/framework_components/beacon-cloud-interface/Readme.md +0 -0
  10. data/framework_components/beacon-cloud-interface/bower.json +29 -0
  11. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/.bower.json +23 -0
  12. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/bower.json +14 -0
  13. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/mqttws31.js +2081 -0
  14. data/framework_components/beacon-cloud-interface/bower_components/bower-mqttws/readme.md +4 -0
  15. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/.bower.json +37 -0
  16. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/LICENSE +21 -0
  17. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/README.md +15 -0
  18. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/bower.json +28 -0
  19. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/browser/mqtt_client_hello_world.html +23 -0
  20. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/browser/nutella_hello_world.html +52 -0
  21. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/node/mqtt_client_hello_world.js +14 -0
  22. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/examples/node/nutella_hello_world.js +38 -0
  23. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/nutella_lib.js +789 -0
  24. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/package.json +30 -0
  25. data/framework_components/beacon-cloud-interface/bower_components/nutella_lib/simple-js-mqtt-client.js +428 -0
  26. data/framework_components/beacon-cloud-interface/css/animation.css +17 -0
  27. data/framework_components/beacon-cloud-interface/css/cursor.css +16 -0
  28. data/framework_components/beacon-cloud-interface/css/page_layout.css +73 -0
  29. data/framework_components/beacon-cloud-interface/index.html +157 -0
  30. data/framework_components/beacon-cloud-interface/js/lib/nutella_lib.js +4039 -0
  31. data/framework_components/beacon-cloud-interface/js/react/beacon-add.js +102 -0
  32. data/framework_components/beacon-cloud-interface/js/react/beacon-table.js +73 -0
  33. data/framework_components/beacon-cloud-interface/js/react/beacon.js +97 -0
  34. data/framework_components/beacon-cloud-interface/nutella.json +6 -0
  35. data/framework_components/example_framework_web_interface/index.html +11 -2
  36. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/.npmignore +10 -0
  37. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/.travis.yml +5 -0
  38. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/LICENSE +21 -0
  39. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/README.md +27 -0
  40. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/dist/nutella_lib.js +4039 -0
  41. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/dist/nutella_lib.js.map +1 -0
  42. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/examples/browser_hello_world.html +67 -0
  43. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/examples/node_hello_world.js +51 -0
  44. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/gulpfile.js +31 -0
  45. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/package.json +41 -0
  46. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_core.js +19 -0
  47. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_core_browser.js +17 -0
  48. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_log.js +50 -0
  49. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_net.js +279 -0
  50. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/app_persist.js +20 -0
  51. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_core_browser.js +17 -0
  52. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_log.js +50 -0
  53. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/fr_net.js +499 -0
  54. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_i.js +74 -0
  55. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_i_browser.js +130 -0
  56. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_lib.js +91 -0
  57. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/nutella_lib_browser.js +90 -0
  58. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_log.js +51 -0
  59. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_net.js +84 -0
  60. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/run_persist.js +20 -0
  61. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/src/util/net.js +327 -0
  62. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/test/nutella.test.js +16 -0
  63. data/framework_components/example_framework_web_interface/node_modules/nutella_lib/test/runner.html +22 -0
  64. data/framework_components/example_framework_web_interface/package.json +15 -0
  65. data/framework_components/{order.json.example → order.json} +0 -0
  66. data/framework_components/runs_list_bot/{app_runs_list_bot.rb → runs_list_bot.rb} +9 -3
  67. data/framework_components/runs_list_bot/startup +1 -1
  68. data/lib/commands/meta/run_command.rb +21 -36
  69. data/lib/commands/start.rb +9 -199
  70. data/lib/commands/util/components_list.rb +68 -0
  71. data/lib/commands/util/components_starter.rb +169 -0
  72. data/nutella_framework.gemspec +109 -47
  73. data/nutella_lib/framework_net.rb +17 -13
  74. metadata +84 -106
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <title>Nutella Tests</title>
6
+ <link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
7
+ </head>
8
+ <body>
9
+ <div id="mocha"></div>
10
+ <script src="../node_modules/mocha/mocha.js" type="text/javascript"></script>
11
+ <script src="../node_modules/chai/chai.js" type="text/javascript"></script>
12
+ <script>
13
+ mocha.setup('bdd');
14
+ mocha.setup({ignoreLeaks: true});
15
+ </script>
16
+ <script src="../dist/nutella_lib.js"></script>
17
+ <script src="nutella.test.js"></script>
18
+ <script>
19
+ mocha.run();
20
+ </script>
21
+ </body>
22
+ </html>
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "example_framework_web_interface",
3
+ "version": "0.1.0",
4
+ "description": "An example framework interface",
5
+ "main": "index.html",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "author": "Alessandro Gnoli",
10
+ "license": "MIT",
11
+ "private": true,
12
+ "dependencies": {
13
+ "nutella_lib": "*"
14
+ }
15
+ }
@@ -22,15 +22,21 @@ nutella.f.net.handle_requests_on_all_apps('app_runs_list', lambda do |req, app_i
22
22
  Nutella.runlist.runs_for_app app_id
23
23
  end)
24
24
 
25
+ # Listen for runs_list requests (done by framework interfaces when they connect)
26
+ nutella.f.net.handle_requests('runs_list', lambda do |req, from|
27
+ Nutella.runlist.all_runs
28
+ end)
29
+
25
30
 
26
- # Whenever the runs list is updated, fire an updated runlist to all the apps
31
+ # Whenever the runs list is updated, fire an updated runlist to all the apps and all framework components
27
32
  p = Nutella.runlist.all_runs
28
- while sleep .5
33
+ while sleep 0.25
29
34
  n = Nutella.runlist.all_runs
30
35
  if p!=n
31
- all_apps.each do |app_id, _|
36
+ Nutella.runlist.all_apps.each do |app_id, _|
32
37
  nutella.f.net.publish_to_app(app_id, 'app_runs_list', Nutella.runlist.runs_for_app(app_id))
33
38
  end
39
+ nutella.f.net.publish 'runs_list', Nutella.runlist.all_runs
34
40
  p = n
35
41
  end
36
42
  end
@@ -1,5 +1,5 @@
1
1
  #!/bin/sh
2
2
 
3
3
  BASEDIR=$(dirname $0)
4
- ruby $BASEDIR/app_runs_list_bot.rb > /dev/null 2>&1 &
4
+ ruby $BASEDIR/runs_list_bot.rb > /dev/null 2>&1 &
5
5
  echo $! > $BASEDIR/.pid
@@ -1,4 +1,5 @@
1
1
  require 'commands/meta/command'
2
+ require 'commands/util/components_list'
2
3
  require 'slop'
3
4
 
4
5
  module Nutella
@@ -50,40 +51,6 @@ module Nutella
50
51
  end
51
52
 
52
53
 
53
- # Returns all the components in a certain directory
54
- def components_in_dir( dir )
55
- Dir.entries(dir).select {|entry| File.directory?(File.join(dir, entry)) && !(entry =='.' || entry == '..') }
56
- end
57
-
58
-
59
- # Executes a code block for each component in a certain directory
60
- # @param [String] dir directory where we are iterating
61
- # @yield [component] Passes the component name to the block
62
- def for_each_component_in_dir( dir, &block )
63
- components_in_dir(dir).each do |component|
64
- block.call component
65
- end
66
- end
67
-
68
-
69
- # Runs a script for each bot in a certain directory.
70
- # Message is displayed in case something goes wrong
71
- def run_script_for_all_bots_in( dir, script, message )
72
- for_each_component_in_dir dir do |bot|
73
- # Skip bot if there is no script
74
- next unless File.exist? "#{dir}/bots/#{bot}/#{script}"
75
- # Output message
76
- console.info "#{message} bot #{bot}."
77
- # Execute 'script' script
78
- cur_dir = Dir.pwd
79
- Dir.chdir "#{dir}/bots/#{bot}"
80
- system "./#{script}"
81
- Dir.chdir cur_dir
82
- end
83
- true
84
- end
85
-
86
-
87
54
  # Prints a success message if the command completes correctly
88
55
  def print_success_message(app_id, run_id, action)
89
56
  if run_id == 'default'
@@ -100,15 +67,33 @@ module Nutella
100
67
  console.warn 'The current directory is not a nutella application'
101
68
  return
102
69
  end
103
-
104
70
  # Run script
105
71
  return unless run_script_for_all_bots_in( Dir.pwd, script, in_progress_message )
106
-
107
72
  # Output success message
108
73
  console.success "All #{complete_message} for #{Nutella.current_app.config['name']}"
109
74
  end
110
75
 
111
76
 
77
+ private
78
+
79
+
80
+ # Runs a script for each bot in a certain directory.
81
+ # Message is displayed in case something goes wrong
82
+ def run_script_for_all_bots_in( dir, script, message )
83
+ ComponentsList.for_each_component_in_dir dir do |bot|
84
+ # Skip bot if there is no script
85
+ next unless File.exist? "#{dir}/bots/#{bot}/#{script}"
86
+ # Output message
87
+ console.info "#{message} bot #{bot}."
88
+ # Execute 'script' script
89
+ cur_dir = Dir.pwd
90
+ Dir.chdir "#{dir}/bots/#{bot}"
91
+ system "./#{script}"
92
+ Dir.chdir cur_dir
93
+ end
94
+ true
95
+ end
96
+
112
97
  end
113
98
 
114
99
  end
@@ -1,5 +1,5 @@
1
1
  require 'commands/meta/run_command'
2
- require 'tmux/tmux'
2
+ require 'commands/util/components_starter'
3
3
 
4
4
  module Nutella
5
5
  class Start < RunCommand
@@ -14,7 +14,7 @@ module Nutella
14
14
  end
15
15
 
16
16
  begin
17
- run_id, params = parse_cli args
17
+ run_id, params = parse_cli_arguments args
18
18
  rescue StandardError => e
19
19
  console.error e.message
20
20
  return
@@ -41,7 +41,7 @@ module Nutella
41
41
 
42
42
 
43
43
  # Parses command line arguments
44
- def parse_cli( args )
44
+ def parse_cli_arguments( args )
45
45
  # Parse run_id
46
46
  run_id = parse_run_id_from args
47
47
  # Extract parameters
@@ -65,54 +65,10 @@ module Nutella
65
65
  # Returns true if both the list of run level bots is empty and the app bots
66
66
  # have been started already
67
67
  def no_bot_to_start(app_id, app_path, params)
68
- return run_level_bots_list(app_path, params).empty? && app_bots_started?(app_id)
68
+ ComponentsList.run_level_bots_list(app_path, params).empty? && app_bots_started?(app_id)
69
69
  end
70
70
 
71
71
 
72
- # Returns the list of run-level bots for this run
73
- # Depending on the mode we are in, we want to start only some bots, exclude only some bots or start all bots
74
- def run_level_bots_list( app_path, params )
75
- # Fetch the list of all components in the bots dir
76
- all_bots = components_in_dir "#{app_path}/bots/"
77
- # Fetch the list of app bots
78
- app_bots = Nutella.current_app.config['app_bots']
79
- # Return correct list based on the mode we are in
80
- case start_mode(params)
81
- when :WITH
82
- return get_with_bots_list params[:with], app_bots
83
- when :WO
84
- return get_wo_bots_list all_bots, app_bots, params[:without]
85
- when :ALL
86
- return get_all_bots_list all_bots, app_bots
87
- else
88
- # If we get here it means we are both in with and without mode and something went very wrong...
89
- raise 'You are using simultaneously with and without modes. This should not happen. Please contact developers.'
90
- end
91
- end
92
-
93
- def start_mode(params)
94
- return :WITH unless params[:with].empty?
95
- return :WO unless params[:without].empty?
96
- :ALL if params[:with].empty? && params[:without].empty?
97
- end
98
-
99
- # If we are in "with mode", we want to run only the bots in the "with list" (minus the ones in app_bots_list)
100
- def get_with_bots_list( incl_bots, app_bots)
101
- return app_bots.nil? ? incl_bots : incl_bots - app_bots
102
- end
103
-
104
- # If we are in "without mode", we want to run all the bots in the bots_list minus the ones in the "without list" and in the "app bots list"
105
- def get_wo_bots_list( all_bots, app_bots, excl_bots )
106
- return app_bots.nil? ? all_bots - excl_bots : all_bots - excl_bots - app_bots
107
- end
108
-
109
- # If we are in "all mode", we want to run all the bots minus the ones in the "app bots list"
110
- def get_all_bots_list( all_bots, app_bots )
111
- return app_bots.nil? ? all_bots : all_bots - app_bots
112
- end
113
-
114
-
115
-
116
72
  # Returns true if the app bots have been started already
117
73
  def app_bots_started?( app_id )
118
74
  Tmux.session_exist? Tmux.app_bot_session_name app_id
@@ -137,162 +93,17 @@ module Nutella
137
93
  # Starts all the components at all levels for this run
138
94
  def start_all_components( app_id, app_path, run_id, params )
139
95
  # Start the internal broker
140
- return false unless start_internal_broker
96
+ return false unless ComponentsStarter.start_internal_broker
141
97
  # Start all framework-level components (if needed)
142
- return false unless start_framework_components
98
+ return false unless ComponentsStarter.start_framework_components
143
99
  # Start all app-level bots (if any, if needed)
144
- return false unless start_app_bots(app_id, app_path)
100
+ return false unless ComponentsStarter.start_app_bots(app_id, app_path)
145
101
  # Start all run-level bots
146
- false unless start_run_bots( app_path, app_id, run_id, params )
147
- true
148
- end
149
-
150
-
151
- def start_internal_broker
152
- pid_file_path = "#{Nutella.config['broker_dir']}bin/.pid"
153
- # Check if the process with pid indicated in the pidfile is alive
154
- return true if sanitize_pid_file pid_file_path
155
- # Check that broker is not running 'unsupervised' (i.e. check port 1883), if it is, return
156
- return true unless broker_port_free?
157
- # Broker is not running and there is no pid file so we try to start
158
- # the internal broker and create a new pid file. Note that the pid file is created by
159
- # the `startup` script, not here.
160
- pid = fork
161
- exec("#{Nutella.config['broker_dir']}/startup") if pid.nil?
162
- # Wait a bit to give the chance to the broker to actually start up
163
- sleep 1
164
- # All went well so we return true
165
- true
166
- end
167
-
168
-
169
- # Cleans the pid file of a given process
170
- # @param [String] pid_file_path the file storing the pid file of the process
171
- # @return [Boolean] true if the pid file exists AND the process with that pid is still alive
172
- def sanitize_pid_file( pid_file_path )
173
- # Does the pid file exist?
174
- # If it does we try to see if the process with that pid is still alive
175
- if File.exist? pid_file_path
176
- pid_file = File.open(pid_file_path, 'rb')
177
- pid = pid_file.read.to_i
178
- pid_file.close
179
- begin
180
- # If this statement doesn't throw an exception then a process with
181
- # this pid is still alive so we do nothing and just return true
182
- Process.getpgid pid
183
- return true
184
- rescue
185
- # If there is an exception, there is no process with this pid
186
- # so we have a stale pid file that we need to remove
187
- File.delete pid_file_path
188
- return false
189
- end
190
- end
191
- # If there is no pid file, there is no process running
192
- false
193
- end
194
-
195
-
196
- # Checks if port 1883 (MQTT broker port) is free
197
- # or some other service is already listening on it
198
- def broker_port_free?
199
- begin
200
- s = TCPServer.new('0.0.0.0', 1883)
201
- s.close
202
- rescue
203
- return false
204
- end
102
+ false unless ComponentsStarter.start_run_bots(ComponentsList.run_level_bots_list(app_path, params), app_path, app_id, run_id)
205
103
  true
206
104
  end
207
105
 
208
106
 
209
- def start_framework_components
210
- nutella_components_dir = "#{Nutella::NUTELLA_HOME}framework_components"
211
- if File.exist? "#{nutella_components_dir}/order.json"
212
- components_list = JSON.parse IO.read "#{nutella_components_dir}/order.json"
213
- else
214
- components_list = components_in_dir(nutella_components_dir)
215
- end
216
- components_list.each do |component|
217
- if File.exist? "#{nutella_components_dir}/#{component}/startup"
218
- unless start_framework_component "#{nutella_components_dir}/#{component}"
219
- return false
220
- end
221
- end
222
- end
223
- true
224
- end
225
-
226
-
227
- def start_framework_component( component_dir )
228
- pid_file_path = "#{component_dir}/.pid"
229
- return true if sanitize_pid_file pid_file_path
230
- # Component is not running and there is no pid file so we try to start it
231
- # and create a new pid file. Note that the pid file is created by
232
- # the startup script!
233
- nutella_config_file = "#{Nutella.config['config_dir']}config.json"
234
- runs_list_file = "#{Nutella.config['config_dir']}runlist.json"
235
- if nutella_config_file==nil || runs_list_file==nil
236
- return false
237
- end
238
- # We are passing the configuration file and the run list files paths to the framework components
239
- command = "#{component_dir}/startup #{nutella_config_file} #{runs_list_file}"
240
- pid = fork
241
- exec(command) if pid.nil?
242
- # Give it a second so they can start properly
243
- sleep 1
244
- # All went well so we return true
245
- true
246
- end
247
-
248
-
249
- def start_app_bots( app_id, app_path )
250
- app_bots_list = Nutella.current_app.config['app_bots']
251
- bots_dir = "#{app_path}/bots/"
252
- # If app bots have been started already, then do nothing
253
- unless Tmux.session_exist? Tmux.app_bot_session_name app_id
254
- # Start all app bots in the list into a new tmux session
255
- tmux = Tmux.new app_id, nil
256
- for_each_component_in_dir bots_dir do |bot|
257
- unless app_bots_list.nil? || !app_bots_list.include?( bot )
258
- # If there is no 'startup' script output a warning (because
259
- # startup is mandatory) and skip the bot
260
- unless File.exist?("#{bots_dir}#{bot}/startup")
261
- console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
262
- next
263
- end
264
- # Create a new window in the session for this run
265
- tmux.new_app_bot_window bot
266
- end
267
- end
268
- end
269
- true
270
- end
271
-
272
-
273
- def start_run_bots( app_path, app_id, run_id, params )
274
- # Create a new tmux instance for this run
275
- tmux = Tmux.new app_id, run_id
276
- # Fetch bots dir
277
- bots_dir = "#{app_path}/bots/"
278
- # Start the appropriate bots
279
- run_level_bots_list( app_path, params ).each { |bot| start_run_level_bot(bots_dir, bot, tmux) }
280
- true
281
- end
282
-
283
- # Starts a run level bot
284
- def start_run_level_bot( bots_dir, bot, tmux )
285
- # If there is no 'startup' script output a warning (because
286
- # startup is mandatory) and skip the bot
287
- unless File.exist?("#{bots_dir}#{bot}/startup")
288
- console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
289
- return
290
- end
291
- # Create a new window in the session for this run
292
- tmux.new_bot_window bot
293
- end
294
-
295
-
296
107
  def print_confirmation( run_id, params, app_id, app_path )
297
108
  # If there are no run-level bots to start, do not create the run and error out
298
109
  if run_level_bots_list(app_path, params).empty?
@@ -316,8 +127,7 @@ module Nutella
316
127
  console.success "Do `tmux attach-session -t #{Tmux.session_name(app_id,run_id)}` to monitor your bots."
317
128
  console.success "Go to http://localhost:#{Nutella.config['main_interface_port']}/#{app_id}/#{run_id} to access your interfaces"
318
129
  end
319
-
320
-
130
+
321
131
  end
322
132
 
323
133
  end
@@ -0,0 +1,68 @@
1
+ # Utility methods to list components
2
+ class ComponentsList
3
+
4
+ # Returns all the components in a certain directory
5
+ def self.components_in_dir( dir )
6
+ Dir.entries(dir).select {|entry| File.directory?(File.join(dir, entry)) && !(entry =='.' || entry == '..') }
7
+ end
8
+
9
+
10
+ # Executes a code block for each component in a certain directory
11
+ # @param [String] dir directory where we are iterating
12
+ # @yield [component] Passes the component name to the block
13
+ def self.for_each_component_in_dir( dir, &block )
14
+ components_in_dir(dir).each { |component| block.call component }
15
+ end
16
+
17
+
18
+ # Returns the list of run-level bots for this run
19
+ # Depending on the mode we are in, we want to start only some bots, exclude only some bots or start all bots
20
+ def self.run_level_bots_list( app_path, params )
21
+ # Fetch the list of all components in the bots dir
22
+ all_bots = components_in_dir "#{app_path}/bots/"
23
+ # Fetch the list of app bots
24
+ app_bots = Nutella.current_app.config['app_bots']
25
+ # Return correct list based on the mode we are in
26
+ case start_mode(params)
27
+ when :WITH
28
+ return get_with_bots_list params[:with], app_bots
29
+ when :WO
30
+ return get_wo_bots_list all_bots, app_bots, params[:without]
31
+ when :ALL
32
+ return get_all_bots_list all_bots, app_bots
33
+ else
34
+ # If we get here it means we are both in with and without mode and something went very wrong...
35
+ raise 'You are using simultaneously with and without modes. This should not happen. Please contact developers.'
36
+ end
37
+ end
38
+
39
+
40
+ #--- Private class methods --------------
41
+
42
+
43
+ def self.start_mode(params)
44
+ return :WITH unless params[:with].empty?
45
+ return :WO unless params[:without].empty?
46
+ :ALL if params[:with].empty? && params[:without].empty?
47
+ end
48
+ private_class_method :start_mode
49
+
50
+ # If we are in "with mode", we want to run only the bots in the "with list" (minus the ones in app_bots_list)
51
+ def self.get_with_bots_list( incl_bots, app_bots)
52
+ return app_bots.nil? ? incl_bots : incl_bots - app_bots
53
+ end
54
+ private_class_method :get_with_bots_list
55
+
56
+ # If we are in "without mode", we want to run all the bots in the bots_list minus the ones in the "without list" and in the "app bots list"
57
+ def self.get_wo_bots_list( all_bots, app_bots, excl_bots )
58
+ return app_bots.nil? ? all_bots - excl_bots : all_bots - excl_bots - app_bots
59
+ end
60
+ private_class_method :get_wo_bots_list
61
+
62
+ # If we are in "all mode", we want to run all the bots minus the ones in the "app bots list"
63
+ def self.get_all_bots_list( all_bots, app_bots )
64
+ return app_bots.nil? ? all_bots : all_bots - app_bots
65
+ end
66
+ private_class_method :get_all_bots_list
67
+
68
+ end
@@ -0,0 +1,169 @@
1
+ require 'commands/util/components_list'
2
+ require 'tmux/tmux'
3
+
4
+ # Utility functions to start components
5
+ class ComponentsStarter
6
+
7
+ # Starts the internal broker if it's not started already
8
+ # @return [boolean] true if the broker is correctly started, false otherwise
9
+ def self.start_internal_broker
10
+ pid_file_path = "#{Nutella.config['broker_dir']}bin/.pid"
11
+ # Check if the process with pid indicated in the pidfile is alive
12
+ return true if sanitize_pid_file pid_file_path
13
+ # Check that broker is not running 'unsupervised' (i.e. check port 1883), if it is, return
14
+ return true unless broker_port_free?
15
+ # Broker is not running and there is no pid file so we try to start
16
+ # the internal broker and create a new pid file. Note that the pid file is created by
17
+ # the `startup` script, not here.
18
+ pid = fork
19
+ exec("#{Nutella.config['broker_dir']}/startup") if pid.nil?
20
+ # Wait a bit to give the chance to the broker to actually start up
21
+ sleep 1
22
+ # All went well so we return true
23
+ true
24
+ end
25
+
26
+
27
+ # Starts all framework components. If order.json is present, components are started
28
+ # in that order.
29
+ # @return [boolean] true if all components are started correctly, false otherwise
30
+ def self.start_framework_components
31
+ nutella_components_dir = "#{Nutella::NUTELLA_HOME}framework_components"
32
+ if File.exist? "#{nutella_components_dir}/order.json"
33
+ components_list = JSON.parse IO.read "#{nutella_components_dir}/order.json"
34
+ else
35
+ components_list = ComponentsList.components_in_dir nutella_components_dir
36
+ end
37
+ components_list.each do |component|
38
+ if File.exist? "#{nutella_components_dir}/#{component}/startup"
39
+ unless start_framework_component "#{nutella_components_dir}/#{component}"
40
+ return false
41
+ end
42
+ end
43
+ end
44
+ true
45
+ end
46
+
47
+
48
+ # Starts the application level bots
49
+ # @return [boolean] true if all bots are started correctly, false otherwise
50
+ def self.start_app_bots( app_id, app_path )
51
+ app_bots_list = Nutella.current_app.config['app_bots']
52
+ bots_dir = "#{app_path}/bots/"
53
+ # If app bots have been started already, then do nothing
54
+ unless Tmux.session_exist? Tmux.app_bot_session_name app_id
55
+ # Start all app bots in the list into a new tmux session
56
+ tmux = Tmux.new app_id, nil
57
+ ComponentsList.for_each_component_in_dir bots_dir do |bot|
58
+ unless app_bots_list.nil? || !app_bots_list.include?( bot )
59
+ # If there is no 'startup' script output a warning (because
60
+ # startup is mandatory) and skip the bot
61
+ unless File.exist?("#{bots_dir}#{bot}/startup")
62
+ console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
63
+ next
64
+ end
65
+ # Create a new window in the session for this run
66
+ tmux.new_app_bot_window bot
67
+ end
68
+ end
69
+ end
70
+ true
71
+ end
72
+
73
+
74
+ def self.start_run_bots( bots_list, app_path, app_id, run_id )
75
+ # Create a new tmux instance for this run
76
+ tmux = Tmux.new app_id, run_id
77
+ # Fetch bots dir
78
+ bots_dir = "#{app_path}/bots/"
79
+ # Start the appropriate bots
80
+ bots_list.each { |bot| start_run_level_bot(bots_dir, bot, tmux) }
81
+ true
82
+ end
83
+
84
+
85
+ #--- Private class methods --------------
86
+
87
+
88
+ # Cleans the pid file of a given process
89
+ # @param [String] pid_file_path the file storing the pid file of the process
90
+ # @return [Boolean] true if the pid file exists AND the process with that pid is still alive
91
+ def self.sanitize_pid_file( pid_file_path )
92
+ # Does the pid file exist?
93
+ # If it does we try to see if the process with that pid is still alive
94
+ if File.exist? pid_file_path
95
+ pid_file = File.open(pid_file_path, 'rb')
96
+ pid = pid_file.read.to_i
97
+ pid_file.close
98
+ begin
99
+ # If this statement doesn't throw an exception then a process with
100
+ # this pid is still alive so we do nothing and just return true
101
+ Process.getpgid pid
102
+ return true
103
+ rescue
104
+ # If there is an exception, there is no process with this pid
105
+ # so we have a stale pid file that we need to remove
106
+ File.delete pid_file_path
107
+ return false
108
+ end
109
+ end
110
+ # If there is no pid file, there is no process running
111
+ false
112
+ end
113
+ private_class_method :sanitize_pid_file
114
+
115
+
116
+ # Checks if port 1883 (MQTT broker port) is free
117
+ # or some other service is already listening on it
118
+ # @return [boolean] true if there is no broker listening on port 1883, false otherwise
119
+ def self.broker_port_free?
120
+ begin
121
+ s = TCPServer.new('0.0.0.0', 1883)
122
+ s.close
123
+ rescue
124
+ return false
125
+ end
126
+ true
127
+ end
128
+ private_class_method :broker_port_free?
129
+
130
+
131
+ # Starts a single framework component
132
+ # @return [boolean] true if the component has been started successfully, false otherwise
133
+ def self.start_framework_component( component_dir )
134
+ pid_file_path = "#{component_dir}/.pid"
135
+ return true if sanitize_pid_file pid_file_path
136
+ # Component is not running and there is no pid file so we try to start it
137
+ # and create a new pid file. Note that the pid file is created by
138
+ # the startup script!
139
+ nutella_config_file = "#{Nutella.config['config_dir']}config.json"
140
+ runs_list_file = "#{Nutella.config['config_dir']}runlist.json"
141
+ if nutella_config_file==nil || runs_list_file==nil
142
+ return false
143
+ end
144
+ # We are passing the configuration file and the run list files paths to the framework components
145
+ command = "#{component_dir}/startup #{nutella_config_file} #{runs_list_file}"
146
+ pid = fork
147
+ exec(command) if pid.nil?
148
+ # Give it a second so they can start properly
149
+ sleep 1
150
+ # All went well so we return true
151
+ true
152
+ end
153
+ private_class_method :start_framework_component
154
+
155
+
156
+ # Starts a run level bot
157
+ def self.start_run_level_bot( bots_dir, bot, tmux )
158
+ # If there is no 'startup' script output a warning (because
159
+ # startup is mandatory) and skip the bot
160
+ unless File.exist?("#{bots_dir}#{bot}/startup")
161
+ console.warn "Impossible to start bot #{bot}. Couldn't locate 'startup' script."
162
+ return
163
+ end
164
+ # Create a new window in the session for this run
165
+ tmux.new_bot_window bot
166
+ end
167
+ private_class_method :start_run_level_bot
168
+
169
+ end