nutella_framework 0.4.5 → 0.4.8

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.
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