neptune 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- data/doc/ExodusHelper.html +64 -19
- data/doc/NeptuneHelper.html +12 -11
- data/doc/Object.html +1 -1
- data/doc/created.rid +9 -9
- data/doc/table_of_contents.html +6 -6
- data/lib/exodus.rb +51 -3
- data/lib/neptune.rb +2 -1
- data/test/test_exodus.rb +74 -17
- metadata +4 -4
data/doc/ExodusHelper.html
CHANGED
@@ -170,6 +170,18 @@ cluttering up <a href="Object.html">Object</a> or Kernel’s namespace.</p>
|
|
170
170
|
<dd class="description">
|
171
171
|
|
172
172
|
|
173
|
+
<dt id="GET_CPU_INFO">GET_CPU_INFO
|
174
|
+
|
175
|
+
<dd class="description"><p>The command that we can run to get information about the number and speed
|
176
|
+
of CPUs on this machine.</p>
|
177
|
+
|
178
|
+
|
179
|
+
<dt id="NEPTUNE_DATA_DIR">NEPTUNE_DATA_DIR
|
180
|
+
|
181
|
+
<dd class="description"><p>The location on this machine where we can read and write profiling
|
182
|
+
information about jobs.</p>
|
183
|
+
|
184
|
+
|
173
185
|
<dt id="OPTIMIZE_FOR_CHOICES">OPTIMIZE_FOR_CHOICES
|
174
186
|
|
175
187
|
<dd class="description">
|
@@ -208,7 +220,7 @@ cluttering up <a href="Object.html">Object</a> or Kernel’s namespace.</p>
|
|
208
220
|
|
209
221
|
|
210
222
|
<div class="method-source-code" id="average-source">
|
211
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
223
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 316</span>
|
212
224
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">average</span>(<span class="ruby-identifier">vals</span>)
|
213
225
|
<span class="ruby-identifier">sum</span> = <span class="ruby-identifier">vals</span>.<span class="ruby-identifier">reduce</span>(<span class="ruby-value">0.0</span>) { <span class="ruby-operator">|</span><span class="ruby-identifier">running_total</span>, <span class="ruby-identifier">val</span><span class="ruby-operator">|</span>
|
214
226
|
<span class="ruby-identifier">running_total</span> <span class="ruby-operator">+</span> <span class="ruby-identifier">val</span>
|
@@ -246,7 +258,7 @@ href="BadConfigurationException.html">BadConfigurationException</a> if
|
|
246
258
|
|
247
259
|
|
248
260
|
<div class="method-source-code" id="convert_clouds_to_use_to_array-source">
|
249
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
261
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 138</span>
|
250
262
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">convert_clouds_to_use_to_array</span>(<span class="ruby-identifier">job</span>)
|
251
263
|
<span class="ruby-identifier">clouds_class</span> = <span class="ruby-identifier">job</span>[<span class="ruby-value">:clouds_to_use</span>].<span class="ruby-identifier">class</span>
|
252
264
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">clouds_class</span> <span class="ruby-operator">==</span> <span class="ruby-constant">Symbol</span>
|
@@ -290,7 +302,7 @@ standard format for Neptune jobs.</p>
|
|
290
302
|
|
291
303
|
|
292
304
|
<div class="method-source-code" id="ensure_all_jobs_are_hashes-source">
|
293
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
305
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 110</span>
|
294
306
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">ensure_all_jobs_are_hashes</span>(<span class="ruby-identifier">jobs</span>)
|
295
307
|
<span class="ruby-identifier">jobs</span>.<span class="ruby-identifier">each</span> { <span class="ruby-operator">|</span><span class="ruby-identifier">job</span><span class="ruby-operator">|</span>
|
296
308
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">job</span>.<span class="ruby-identifier">class</span> <span class="ruby-operator">!=</span> <span class="ruby-constant">Hash</span>
|
@@ -327,7 +339,7 @@ missing params.</p>
|
|
327
339
|
|
328
340
|
|
329
341
|
<div class="method-source-code" id="ensure_all_params_are_present-source">
|
330
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
342
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 122</span>
|
331
343
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">ensure_all_params_are_present</span>(<span class="ruby-identifier">job</span>)
|
332
344
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">job</span>[<span class="ruby-value">:clouds_to_use</span>].<span class="ruby-identifier">nil?</span>
|
333
345
|
<span class="ruby-identifier">raise</span> <span class="ruby-constant">BadConfigurationException</span>.<span class="ruby-identifier">new</span>(<span class="ruby-string">":clouds_to_use was not specified"</span>)
|
@@ -364,7 +376,7 @@ missing params.</p>
|
|
364
376
|
|
365
377
|
|
366
378
|
<div class="method-source-code" id="ensure_credentials_are_in_correct_format-source">
|
367
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
379
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 180</span>
|
368
380
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">ensure_credentials_are_in_correct_format</span>(<span class="ruby-identifier">job</span>)
|
369
381
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">job</span>[<span class="ruby-value">:credentials</span>].<span class="ruby-identifier">nil?</span>
|
370
382
|
<span class="ruby-identifier">raise</span> <span class="ruby-constant">BadConfigurationException</span>.<span class="ruby-identifier">new</span>(<span class="ruby-string">"No credentials were specified."</span>)
|
@@ -401,7 +413,7 @@ missing params.</p>
|
|
401
413
|
|
402
414
|
|
403
415
|
<div class="method-source-code" id="find_optimal_cloud_for_task-source">
|
404
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
416
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 325</span>
|
405
417
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">find_optimal_cloud_for_task</span>(<span class="ruby-identifier">job</span>, <span class="ruby-identifier">profiling_info</span>)
|
406
418
|
<span class="ruby-identifier">raise</span> <span class="ruby-constant">NotImplementedError</span>
|
407
419
|
<span class="ruby-keyword">end</span></pre>
|
@@ -431,7 +443,7 @@ missing params.</p>
|
|
431
443
|
|
432
444
|
|
433
445
|
<div class="method-source-code" id="generate_babel_tasks-source">
|
434
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
446
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 330</span>
|
435
447
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">generate_babel_tasks</span>(<span class="ruby-identifier">job</span>, <span class="ruby-identifier">clouds_to_run_task_on</span>)
|
436
448
|
<span class="ruby-identifier">tasks</span> = []
|
437
449
|
|
@@ -480,7 +492,7 @@ missing params.</p>
|
|
480
492
|
|
481
493
|
|
482
494
|
<div class="method-source-code" id="get_clouds_to_run_task_on-source">
|
483
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
495
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 275</span>
|
484
496
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_clouds_to_run_task_on</span>(<span class="ruby-identifier">job</span>, <span class="ruby-identifier">profiling_info</span>)
|
485
497
|
<span class="ruby-identifier">optimize_for</span> = <span class="ruby-identifier">job</span>[<span class="ruby-value">:optimize_for</span>]
|
486
498
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">optimize_for</span> <span class="ruby-operator">==</span> <span class="ruby-value">:performance</span> <span class="ruby-keyword">or</span> <span class="ruby-identifier">optimize_for</span> <span class="ruby-operator">==</span> <span class="ruby-value">:cost</span>
|
@@ -515,9 +527,9 @@ missing params.</p>
|
|
515
527
|
|
516
528
|
|
517
529
|
<div class="method-source-code" id="get_key_from_job_data-source">
|
518
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
530
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 270</span>
|
519
531
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_key_from_job_data</span>(<span class="ruby-identifier">job</span>)
|
520
|
-
<span class="ruby-keyword">return</span> <span class="ruby-identifier">job</span>[<span class="ruby-value">:code</span>]
|
532
|
+
<span class="ruby-keyword">return</span> <span class="ruby-identifier">job</span>[<span class="ruby-value">:code</span>].<span class="ruby-identifier">gsub</span>(<span class="ruby-regexp">%r[\/\.]/</span>, <span class="ruby-string">""</span>)
|
521
533
|
<span class="ruby-keyword">end</span></pre>
|
522
534
|
</div><!-- get_key_from_job_data-source -->
|
523
535
|
|
@@ -545,7 +557,7 @@ missing params.</p>
|
|
545
557
|
|
546
558
|
|
547
559
|
<div class="method-source-code" id="get_minimum_val_in_data-source">
|
548
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
560
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 285</span>
|
549
561
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_minimum_val_in_data</span>(<span class="ruby-identifier">job</span>, <span class="ruby-identifier">profiling_info</span>)
|
550
562
|
<span class="ruby-identifier">min_cloud</span> = <span class="ruby-keyword">nil</span>
|
551
563
|
<span class="ruby-identifier">min_val</span> = <span class="ruby-value">1_000_000</span> <span class="ruby-comment"># infinity</span>
|
@@ -600,11 +612,44 @@ missing params.</p>
|
|
600
612
|
|
601
613
|
|
602
614
|
<div class="method-source-code" id="get_profiling_info-source">
|
603
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
615
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 229</span>
|
604
616
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_profiling_info</span>(<span class="ruby-identifier">job</span>)
|
605
617
|
<span class="ruby-identifier">key</span> = <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_key_from_job_data</span>(<span class="ruby-identifier">job</span>)
|
606
|
-
|
607
|
-
<span class="ruby-keyword">
|
618
|
+
|
619
|
+
<span class="ruby-keyword">if</span> <span class="ruby-operator">!</span><span class="ruby-constant">File</span>.<span class="ruby-identifier">exists?</span>(<span class="ruby-constant">NEPTUNE_DATA_DIR</span>)
|
620
|
+
<span class="ruby-constant">FileUtils</span>.<span class="ruby-identifier">mkdir</span>(<span class="ruby-constant">NEPTUNE_DATA_DIR</span>)
|
621
|
+
<span class="ruby-keyword">end</span>
|
622
|
+
|
623
|
+
<span class="ruby-identifier">profiling_info_file</span> = <span class="ruby-node">"#{NEPTUNE_DATA_DIR}/#{key}.json"</span>
|
624
|
+
<span class="ruby-keyword">if</span> <span class="ruby-constant">File</span>.<span class="ruby-identifier">exists?</span>(<span class="ruby-identifier">profiling_info_file</span>)
|
625
|
+
<span class="ruby-identifier">contents</span> = <span class="ruby-constant">File</span>.<span class="ruby-identifier">open</span>(<span class="ruby-identifier">profiling_info_file</span>) { <span class="ruby-operator">|</span><span class="ruby-identifier">f</span><span class="ruby-operator">|</span> <span class="ruby-identifier">f</span>.<span class="ruby-identifier">read</span>() }
|
626
|
+
<span class="ruby-keyword">return</span> <span class="ruby-constant">JSON</span>.<span class="ruby-identifier">load</span>(<span class="ruby-identifier">contents</span>)
|
627
|
+
<span class="ruby-keyword">end</span>
|
628
|
+
|
629
|
+
<span class="ruby-comment"># If we don't have any profiling info on this job, run it locally and</span>
|
630
|
+
<span class="ruby-comment"># gather the data ourselves.</span>
|
631
|
+
|
632
|
+
<span class="ruby-identifier">start_time</span> = <span class="ruby-constant">Time</span>.<span class="ruby-identifier">now</span>
|
633
|
+
<span class="ruby-comment"># TODO(cgb): exec the user's code</span>
|
634
|
+
<span class="ruby-identifier">end_time</span> = <span class="ruby-constant">Time</span>.<span class="ruby-identifier">now</span>
|
635
|
+
|
636
|
+
<span class="ruby-comment"># To find out how fast this computer is, just check the file that has</span>
|
637
|
+
<span class="ruby-comment"># this info on it and take the first processor. This should be fine</span>
|
638
|
+
<span class="ruby-comment"># since we assume the user's code is not-multi-core aware and that</span>
|
639
|
+
<span class="ruby-comment"># all processors on this box are the same speed.</span>
|
640
|
+
<span class="ruby-identifier">cpu_speed</span> = <span class="ruby-constant">Float</span>(<span class="ruby-constant">CommonFunctions</span>.<span class="ruby-identifier">shell</span>(<span class="ruby-constant">GET_CPU_INFO</span>).
|
641
|
+
<span class="ruby-identifier">scan</span>(<span class="ruby-regexp">%rcpu MHz\s*:\s*(\d+\.\d+)/</span>).<span class="ruby-identifier">flatten</span>[<span class="ruby-value">0</span>])
|
642
|
+
|
643
|
+
<span class="ruby-identifier">json_info</span> = {
|
644
|
+
<span class="ruby-string">"total_execution_time"</span> =<span class="ruby-operator">></span> <span class="ruby-identifier">end_time</span> <span class="ruby-operator">-</span> <span class="ruby-identifier">start_time</span>,
|
645
|
+
<span class="ruby-string">"cpu_speed"</span> =<span class="ruby-operator">></span> <span class="ruby-identifier">cpu_speed</span>
|
646
|
+
}
|
647
|
+
|
648
|
+
<span class="ruby-constant">File</span>.<span class="ruby-identifier">open</span>(<span class="ruby-identifier">profiling_info_file</span>, <span class="ruby-string">"w+"</span>) { <span class="ruby-operator">|</span><span class="ruby-identifier">file</span><span class="ruby-operator">|</span>
|
649
|
+
<span class="ruby-identifier">file</span>.<span class="ruby-identifier">write</span>(<span class="ruby-constant">JSON</span>.<span class="ruby-identifier">dump</span>(<span class="ruby-identifier">json_info</span>))
|
650
|
+
}
|
651
|
+
|
652
|
+
<span class="ruby-keyword">return</span> <span class="ruby-identifier">json_info</span>
|
608
653
|
<span class="ruby-keyword">end</span></pre>
|
609
654
|
</div><!-- get_profiling_info-source -->
|
610
655
|
|
@@ -634,7 +679,7 @@ the job does not specify it.</p>
|
|
634
679
|
|
635
680
|
|
636
681
|
<div class="method-source-code" id="propogate_credentials_from_environment-source">
|
637
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
682
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 195</span>
|
638
683
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">propogate_credentials_from_environment</span>(<span class="ruby-identifier">job</span>)
|
639
684
|
<span class="ruby-constant">CLOUD_CREDENTIALS</span>.<span class="ruby-identifier">each</span> { <span class="ruby-operator">|</span><span class="ruby-identifier">cloud_name</span>, <span class="ruby-identifier">credential_list</span><span class="ruby-operator">|</span>
|
640
685
|
<span class="ruby-identifier">credential_list</span>.<span class="ruby-identifier">each</span> { <span class="ruby-operator">|</span><span class="ruby-identifier">cred</span><span class="ruby-operator">|</span>
|
@@ -670,7 +715,7 @@ the job does not specify it.</p>
|
|
670
715
|
|
671
716
|
|
672
717
|
<div class="method-source-code" id="run_job-source">
|
673
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
718
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 354</span>
|
674
719
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">run_job</span>(<span class="ruby-identifier">tasks_to_run</span>)
|
675
720
|
<span class="ruby-keyword">return</span> <span class="ruby-identifier">babel</span>(<span class="ruby-identifier">tasks_to_run</span>)
|
676
721
|
<span class="ruby-keyword">end</span></pre>
|
@@ -702,7 +747,7 @@ credentials needed to use that cloud.</p>
|
|
702
747
|
|
703
748
|
|
704
749
|
<div class="method-source-code" id="validate_clouds_to_use-source">
|
705
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
750
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 159</span>
|
706
751
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">validate_clouds_to_use</span>(<span class="ruby-identifier">job</span>)
|
707
752
|
<span class="ruby-keyword">self</span>.<span class="ruby-identifier">ensure_credentials_are_in_correct_format</span>(<span class="ruby-identifier">job</span>)
|
708
753
|
<span class="ruby-keyword">self</span>.<span class="ruby-identifier">propogate_credentials_from_environment</span>(<span class="ruby-identifier">job</span>)
|
@@ -748,7 +793,7 @@ credentials needed to use that cloud.</p>
|
|
748
793
|
|
749
794
|
|
750
795
|
<div class="method-source-code" id="validate_files_argv_executable-source">
|
751
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
796
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 220</span>
|
752
797
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">validate_files_argv_executable</span>(<span class="ruby-identifier">job</span>)
|
753
798
|
[<span class="ruby-value">:code</span>, <span class="ruby-value">:argv</span>, <span class="ruby-value">:executable</span>].<span class="ruby-identifier">each</span> { <span class="ruby-operator">|</span><span class="ruby-identifier">param</span><span class="ruby-operator">|</span>
|
754
799
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">job</span>[<span class="ruby-identifier">param</span>].<span class="ruby-identifier">nil?</span>
|
@@ -782,7 +827,7 @@ credentials needed to use that cloud.</p>
|
|
782
827
|
|
783
828
|
|
784
829
|
<div class="method-source-code" id="validate_optimize_for_param-source">
|
785
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
830
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 206</span>
|
786
831
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">validate_optimize_for_param</span>(<span class="ruby-identifier">job</span>)
|
787
832
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">job</span>[<span class="ruby-value">:optimize_for</span>].<span class="ruby-identifier">nil?</span>
|
788
833
|
<span class="ruby-identifier">raise</span> <span class="ruby-constant">BadConfigurationException</span>.<span class="ruby-identifier">new</span>(<span class="ruby-string">":optimize_for needs to be "</span> <span class="ruby-operator">+</span>
|
data/doc/NeptuneHelper.html
CHANGED
@@ -186,7 +186,7 @@ indicates whether or not the compilation was successful.</p>
|
|
186
186
|
|
187
187
|
|
188
188
|
<div class="method-source-code" id="compile_code-source">
|
189
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
189
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 459</span>
|
190
190
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">compile_code</span>(<span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">ssh_args</span>, <span class="ruby-identifier">shadow_ip</span>)
|
191
191
|
<span class="ruby-identifier">compiled_location</span> = <span class="ruby-identifier">controller</span>.<span class="ruby-identifier">compile_code</span>(<span class="ruby-identifier">job_data</span>)
|
192
192
|
<span class="ruby-identifier">copy_to</span> = <span class="ruby-identifier">job_data</span>[<span class="ruby-string">"@copy_to"</span>]
|
@@ -278,7 +278,7 @@ job succeeded and if it failed, the reason for it.</p>
|
|
278
278
|
|
279
279
|
|
280
280
|
<div class="method-source-code" id="get_input-source">
|
281
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
281
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 407</span>
|
282
282
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_input</span>(<span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">ssh_args</span>, <span class="ruby-identifier">shadow_ip</span>, <span class="ruby-identifier">controller</span>)
|
283
283
|
<span class="ruby-identifier">result</span> = {<span class="ruby-value">:result</span> =<span class="ruby-operator">></span> <span class="ruby-value">:success</span>}
|
284
284
|
|
@@ -335,7 +335,7 @@ NeptuneManager.</p>
|
|
335
335
|
|
336
336
|
|
337
337
|
<div class="method-source-code" id="get_job_data-source">
|
338
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
338
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 314</span>
|
339
339
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_job_data</span>(<span class="ruby-identifier">params</span>)
|
340
340
|
<span class="ruby-identifier">job_data</span> = {}
|
341
341
|
<span class="ruby-identifier">params</span>.<span class="ruby-identifier">each</span> { <span class="ruby-operator">|</span><span class="ruby-identifier">k</span>, <span class="ruby-identifier">v</span><span class="ruby-operator">|</span>
|
@@ -411,7 +411,7 @@ not the job completed successfully (success = no errors).</p>
|
|
411
411
|
|
412
412
|
|
413
413
|
<div class="method-source-code" id="get_std_out_and_err-source">
|
414
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
414
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 487</span>
|
415
415
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">get_std_out_and_err</span>(<span class="ruby-identifier">location</span>)
|
416
416
|
<span class="ruby-identifier">result</span> = {}
|
417
417
|
|
@@ -458,7 +458,7 @@ engines can be found by contacting an AppScale node.</p>
|
|
458
458
|
|
459
459
|
|
460
460
|
<div class="method-source-code" id="preprocess_babel-source">
|
461
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
461
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 262</span>
|
462
462
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">preprocess_babel</span>(<span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">controller</span>)
|
463
463
|
<span class="ruby-keyword">self</span>.<span class="ruby-identifier">require_param</span>(<span class="ruby-string">"@code"</span>, <span class="ruby-identifier">job_data</span>)
|
464
464
|
<span class="ruby-keyword">self</span>.<span class="ruby-identifier">require_param</span>(<span class="ruby-string">"@engine"</span>, <span class="ruby-identifier">job_data</span>)
|
@@ -731,7 +731,8 @@ it does not, throws an exception.</p>
|
|
731
731
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">controller</span>.<span class="ruby-identifier">does_file_exist?</span>(<span class="ruby-identifier">file</span>, <span class="ruby-identifier">job_data</span>)
|
732
732
|
<span class="ruby-keyword">return</span>
|
733
733
|
<span class="ruby-keyword">else</span>
|
734
|
-
<span class="ruby-identifier">raise</span> <span class="ruby-constant">FileNotFoundException</span>
|
734
|
+
<span class="ruby-identifier">raise</span> <span class="ruby-constant">FileNotFoundException</span>.<span class="ruby-identifier">new</span>(<span class="ruby-node">"Expecting file #{file} to exist "</span> <span class="ruby-operator">+</span>
|
735
|
+
<span class="ruby-string">"in the remote datastore, which did not exist."</span>)
|
735
736
|
<span class="ruby-keyword">end</span>
|
736
737
|
<span class="ruby-keyword">end</span></pre>
|
737
738
|
</div><!-- require_file_to_exist-source -->
|
@@ -762,7 +763,7 @@ raising an exception if the named file does exist.</p>
|
|
762
763
|
|
763
764
|
|
764
765
|
<div class="method-source-code" id="require_file_to_not_exist-source">
|
765
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
766
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 247</span>
|
766
767
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">require_file_to_not_exist</span>(<span class="ruby-identifier">file</span>, <span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">controller</span>)
|
767
768
|
<span class="ruby-keyword">begin</span>
|
768
769
|
<span class="ruby-keyword">self</span>.<span class="ruby-identifier">require_file_to_exist</span>(<span class="ruby-identifier">file</span>, <span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">controller</span>)
|
@@ -832,7 +833,7 @@ as well as information about the node to send the request to.</p>
|
|
832
833
|
|
833
834
|
|
834
835
|
<div class="method-source-code" id="run_job-source">
|
835
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
836
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 537</span>
|
836
837
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">run_job</span>(<span class="ruby-identifier">job_data</span>, <span class="ruby-identifier">ssh_args</span>, <span class="ruby-identifier">shadow_ip</span>, <span class="ruby-identifier">secret</span>)
|
837
838
|
<span class="ruby-identifier">controller</span> = <span class="ruby-constant">NeptuneManagerClient</span>.<span class="ruby-identifier">new</span>(<span class="ruby-identifier">shadow_ip</span>, <span class="ruby-identifier">secret</span>)
|
838
839
|
|
@@ -893,7 +894,7 @@ with Cicero jobs. It requires the AppScale tools to be installed.</p>
|
|
893
894
|
|
894
895
|
|
895
896
|
<div class="method-source-code" id="upload_app_for_cicero-source">
|
896
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
897
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 508</span>
|
897
898
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">upload_app_for_cicero</span>(<span class="ruby-identifier">job_data</span>)
|
898
899
|
<span class="ruby-keyword">if</span> <span class="ruby-operator">!</span><span class="ruby-identifier">job_data</span>[<span class="ruby-string">"@app"</span>]
|
899
900
|
<span class="ruby-comment"># Kernel.puts "No app specified, not uploading..." </span>
|
@@ -948,7 +949,7 @@ parameter is missing.</p>
|
|
948
949
|
|
949
950
|
|
950
951
|
<div class="method-source-code" id="validate_storage_params-source">
|
951
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
952
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 367</span>
|
952
953
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">validate_storage_params</span>(<span class="ruby-identifier">job_data</span>)
|
953
954
|
<span class="ruby-identifier">job_data</span>[<span class="ruby-string">"@storage"</span>] <span class="ruby-operator">||=</span> <span class="ruby-string">"appdb"</span>
|
954
955
|
|
@@ -1012,7 +1013,7 @@ location.</p>
|
|
1012
1013
|
|
1013
1014
|
|
1014
1015
|
<div class="method-source-code" id="wait_for_compilation_to_finish-source">
|
1015
|
-
<pre><span class="ruby-comment"># File lib/neptune.rb, line
|
1016
|
+
<pre><span class="ruby-comment"># File lib/neptune.rb, line 438</span>
|
1016
1017
|
<span class="ruby-keyword">def</span> <span class="ruby-keyword">self</span>.<span class="ruby-identifier">wait_for_compilation_to_finish</span>(<span class="ruby-identifier">ssh_args</span>, <span class="ruby-identifier">shadow_ip</span>, <span class="ruby-identifier">compiled_location</span>)
|
1017
1018
|
<span class="ruby-identifier">loop</span> {
|
1018
1019
|
<span class="ruby-identifier">ssh_command</span> = <span class="ruby-node">"ssh #{ssh_args} root@#{shadow_ip} 'ls #{compiled_location}' 2>&1"</span>
|
data/doc/Object.html
CHANGED
@@ -329,7 +329,7 @@ select the best cloud for their job and run it there.</p>
|
|
329
329
|
|
330
330
|
|
331
331
|
<div class="method-source-code" id="exodus-source">
|
332
|
-
<pre><span class="ruby-comment"># File lib/exodus.rb, line
|
332
|
+
<pre><span class="ruby-comment"># File lib/exodus.rb, line 20</span>
|
333
333
|
<span class="ruby-keyword">def</span> <span class="ruby-identifier">exodus</span>(<span class="ruby-identifier">jobs</span>)
|
334
334
|
<span class="ruby-keyword">if</span> <span class="ruby-identifier">jobs</span>.<span class="ruby-identifier">class</span> <span class="ruby-operator">==</span> <span class="ruby-constant">Hash</span>
|
335
335
|
<span class="ruby-identifier">job_given_as_hash</span> = <span class="ruby-keyword">true</span>
|
data/doc/created.rid
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
lib/exodus.rb
|
4
|
-
lib/task_info.rb Mon, 28 May 2012 16:24:14 -0700
|
5
|
-
lib/babel.rb Mon, 30 Jul 2012 17:10:06 -0700
|
6
|
-
lib/neptune_manager_client.rb Mon, 04 Jun 2012 13:53:07 -0700
|
7
|
-
lib/common_functions.rb Sun, 12 Feb 2012 16:18:14 -0800
|
8
|
-
lib/neptune.rb Mon, 30 Jul 2012 17:09:09 -0700
|
1
|
+
Fri, 28 Sep 2012 13:17:21 -0700
|
2
|
+
lib/babel.rb Sat, 01 Sep 2012 13:14:23 -0700
|
3
|
+
lib/exodus.rb Tue, 25 Sep 2012 12:38:28 -0700
|
9
4
|
lib/custom_exceptions.rb Mon, 28 May 2012 16:23:52 -0700
|
10
|
-
lib/
|
5
|
+
lib/neptune_manager_client.rb Sat, 01 Sep 2012 13:14:23 -0700
|
6
|
+
lib/exodus_task_info.rb Mon, 30 Jul 2012 17:40:12 -0700
|
7
|
+
lib/common_functions.rb Sat, 01 Sep 2012 13:14:23 -0700
|
8
|
+
lib/neptune.rb Fri, 28 Sep 2012 13:12:58 -0700
|
9
|
+
lib/task_info.rb Mon, 28 May 2012 16:24:14 -0700
|
10
|
+
bin/neptune Sat, 01 Sep 2012 13:14:23 -0700
|
data/doc/table_of_contents.html
CHANGED
@@ -119,10 +119,10 @@
|
|
119
119
|
|
120
120
|
<li class="method"><a href="ExodusTaskInfo.html#method-c-new">::new — ExodusTaskInfo</a>
|
121
121
|
|
122
|
-
<li class="method"><a href="NeptuneManagerClient.html#method-c-new">::new — NeptuneManagerClient</a>
|
123
|
-
|
124
122
|
<li class="method"><a href="TaskInfo.html#method-c-new">::new — TaskInfo</a>
|
125
123
|
|
124
|
+
<li class="method"><a href="NeptuneManagerClient.html#method-c-new">::new — NeptuneManagerClient</a>
|
125
|
+
|
126
126
|
<li class="method"><a href="NeptuneHelper.html#method-c-preprocess_babel">::preprocess_babel — NeptuneHelper</a>
|
127
127
|
|
128
128
|
<li class="method"><a href="NeptuneHelper.html#method-c-preprocess_compile">::preprocess_compile — NeptuneHelper</a>
|
@@ -147,12 +147,12 @@
|
|
147
147
|
|
148
148
|
<li class="method"><a href="NeptuneHelper.html#method-c-require_param">::require_param — NeptuneHelper</a>
|
149
149
|
|
150
|
+
<li class="method"><a href="ExodusHelper.html#method-c-run_job">::run_job — ExodusHelper</a>
|
151
|
+
|
150
152
|
<li class="method"><a href="BabelHelper.html#method-c-run_job">::run_job — BabelHelper</a>
|
151
153
|
|
152
154
|
<li class="method"><a href="NeptuneHelper.html#method-c-run_job">::run_job — NeptuneHelper</a>
|
153
155
|
|
154
|
-
<li class="method"><a href="ExodusHelper.html#method-c-run_job">::run_job — ExodusHelper</a>
|
155
|
-
|
156
156
|
<li class="method"><a href="CommonFunctions.html#method-c-scp_file">::scp_file — CommonFunctions</a>
|
157
157
|
|
158
158
|
<li class="method"><a href="CommonFunctions.html#method-c-scp_to_shadow">::scp_to_shadow — CommonFunctions</a>
|
@@ -211,10 +211,10 @@
|
|
211
211
|
|
212
212
|
<li class="method"><a href="TaskInfo.html#method-i-to_json">#to_json — TaskInfo</a>
|
213
213
|
|
214
|
-
<li class="method"><a href="ExodusTaskInfo.html#method-i-to_s">#to_s — ExodusTaskInfo</a>
|
215
|
-
|
216
214
|
<li class="method"><a href="TaskInfo.html#method-i-to_s">#to_s — TaskInfo</a>
|
217
215
|
|
216
|
+
<li class="method"><a href="ExodusTaskInfo.html#method-i-to_s">#to_s — ExodusTaskInfo</a>
|
217
|
+
|
218
218
|
</ul>
|
219
219
|
|
220
220
|
|
data/lib/exodus.rb
CHANGED
@@ -2,7 +2,12 @@
|
|
2
2
|
# Programmer: Chris Bunch
|
3
3
|
|
4
4
|
|
5
|
+
require 'rubygems'
|
6
|
+
require 'json'
|
7
|
+
|
8
|
+
|
5
9
|
require 'babel'
|
10
|
+
require 'common_functions'
|
6
11
|
require 'custom_exceptions'
|
7
12
|
require 'exodus_task_info'
|
8
13
|
|
@@ -87,6 +92,16 @@ module ExodusHelper
|
|
87
92
|
}
|
88
93
|
|
89
94
|
|
95
|
+
# The location on this machine where we can read and write profiling
|
96
|
+
# information about jobs.
|
97
|
+
NEPTUNE_DATA_DIR = File.expand_path("~/.neptune")
|
98
|
+
|
99
|
+
|
100
|
+
# The command that we can run to get information about the number and speed
|
101
|
+
# of CPUs on this machine.
|
102
|
+
GET_CPU_INFO = "cat /proc/cpuinfo"
|
103
|
+
|
104
|
+
|
90
105
|
OPTIMIZE_FOR_CHOICES = [:performance, :cost, :auto]
|
91
106
|
|
92
107
|
|
@@ -213,14 +228,47 @@ module ExodusHelper
|
|
213
228
|
|
214
229
|
def self.get_profiling_info(job)
|
215
230
|
key = self.get_key_from_job_data(job)
|
216
|
-
|
217
|
-
|
231
|
+
|
232
|
+
if !File.exists?(NEPTUNE_DATA_DIR)
|
233
|
+
FileUtils.mkdir(NEPTUNE_DATA_DIR)
|
234
|
+
end
|
235
|
+
|
236
|
+
profiling_info_file = "#{NEPTUNE_DATA_DIR}/#{key}.json"
|
237
|
+
if File.exists?(profiling_info_file)
|
238
|
+
contents = File.open(profiling_info_file) { |f| f.read() }
|
239
|
+
return JSON.load(contents)
|
240
|
+
end
|
241
|
+
|
242
|
+
# If we don't have any profiling info on this job, run it locally and
|
243
|
+
# gather the data ourselves.
|
244
|
+
|
245
|
+
start_time = Time.now
|
246
|
+
# TODO(cgb): exec the user's code
|
247
|
+
end_time = Time.now
|
248
|
+
|
249
|
+
# To find out how fast this computer is, just check the file that has
|
250
|
+
# this info on it and take the first processor. This should be fine
|
251
|
+
# since we assume the user's code is not-multi-core aware and that
|
252
|
+
# all processors on this box are the same speed.
|
253
|
+
cpu_speed = Float(CommonFunctions.shell(GET_CPU_INFO).
|
254
|
+
scan(/cpu MHz\s*:\s*(\d+\.\d+)/).flatten[0])
|
255
|
+
|
256
|
+
json_info = {
|
257
|
+
"total_execution_time" => end_time - start_time,
|
258
|
+
"cpu_speed" => cpu_speed
|
259
|
+
}
|
260
|
+
|
261
|
+
File.open(profiling_info_file, "w+") { |file|
|
262
|
+
file.write(JSON.dump(json_info))
|
263
|
+
}
|
264
|
+
|
265
|
+
return json_info
|
218
266
|
end
|
219
267
|
|
220
268
|
|
221
269
|
# TODO(cgb): what is a job's key?
|
222
270
|
def self.get_key_from_job_data(job)
|
223
|
-
return job[:code]
|
271
|
+
return job[:code].gsub(/[\/\.]/, "")
|
224
272
|
end
|
225
273
|
|
226
274
|
|
data/lib/neptune.rb
CHANGED
@@ -236,7 +236,8 @@ module NeptuneHelper
|
|
236
236
|
if controller.does_file_exist?(file, job_data)
|
237
237
|
return
|
238
238
|
else
|
239
|
-
raise FileNotFoundException
|
239
|
+
raise FileNotFoundException.new("Expecting file #{file} to exist " +
|
240
|
+
"in the remote datastore, which did not exist.")
|
240
241
|
end
|
241
242
|
end
|
242
243
|
|
data/test/test_exodus.rb
CHANGED
@@ -36,6 +36,8 @@ class TestExodus < Test::Unit::TestCase
|
|
36
36
|
:S3_URL => "http://s3.url",
|
37
37
|
:S3_bucket_name => "bazbucket"
|
38
38
|
}
|
39
|
+
|
40
|
+
flexmock(FileUtils).should_receive(:mkdir).and_return()
|
39
41
|
end
|
40
42
|
|
41
43
|
|
@@ -223,16 +225,38 @@ class TestExodus < Test::Unit::TestCase
|
|
223
225
|
:executable => "ruby"
|
224
226
|
}
|
225
227
|
|
226
|
-
#
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
228
|
+
# let's say they've never run an exodus job before, so they have
|
229
|
+
# no profiling data stored locally
|
230
|
+
flexmock(File).should_receive(:exists?).
|
231
|
+
with(ExodusHelper::NEPTUNE_DATA_DIR).and_return(false)
|
232
|
+
flexmock(FileUtils).should_receive(:mkdir).
|
233
|
+
with(ExodusHelper::NEPTUNE_DATA_DIR).and_return()
|
234
|
+
|
235
|
+
# mock out the gathering of profiling info
|
236
|
+
profiling_filename = "#{ExodusHelper::NEPTUNE_DATA_DIR}/#{ExodusHelper.get_key_from_job_data(job)}.json"
|
237
|
+
flexmock(File).should_receive(:exists?).
|
238
|
+
with(profiling_filename).and_return(false)
|
239
|
+
|
240
|
+
# mock out timing the user's code
|
241
|
+
flexmock(Time).should_receive(:now).and_return(1.0, 2.0)
|
233
242
|
|
234
|
-
|
235
|
-
|
243
|
+
# then mock out exec'ing the user's code
|
244
|
+
# TODO(cgb): do this
|
245
|
+
|
246
|
+
# mock out getting cpuinfo
|
247
|
+
flexmock(CommonFunctions).should_receive(:shell).
|
248
|
+
with(ExodusHelper::GET_CPU_INFO).and_return("cpu MHz: 666.66")
|
249
|
+
|
250
|
+
# finally, mock out writing the profiling information
|
251
|
+
flexmock(File).should_receive(:open).with(profiling_filename, "w+", Proc).
|
252
|
+
and_return()
|
253
|
+
|
254
|
+
expected = {
|
255
|
+
"total_execution_time" => 1.0,
|
256
|
+
"cpu_speed" => 666.66
|
257
|
+
}
|
258
|
+
actual = ExodusHelper.get_profiling_info(job)
|
259
|
+
assert_equal(expected, actual)
|
236
260
|
end
|
237
261
|
|
238
262
|
|
@@ -526,10 +550,6 @@ class TestExodus < Test::Unit::TestCase
|
|
526
550
|
|
527
551
|
# mock out calls to the NeptuneManager
|
528
552
|
flexmock(NeptuneManagerClient).new_instances { |instance|
|
529
|
-
# for this test, let's say there's no data on this task right now
|
530
|
-
instance.should_receive(:get_profiling_info).with(String).
|
531
|
-
and_return({})
|
532
|
-
|
533
553
|
# let's say that all checks to see if temp files exist tell us that
|
534
554
|
# the files don't exist
|
535
555
|
instance.should_receive(:does_file_exist?).
|
@@ -562,6 +582,31 @@ class TestExodus < Test::Unit::TestCase
|
|
562
582
|
# we'll say that our code does exist
|
563
583
|
flexmock(File).should_receive(:exists?).with("/foo").and_return(true)
|
564
584
|
|
585
|
+
# let's say that we have a neptune profiling directory
|
586
|
+
flexmock(File).should_receive(:exists?).
|
587
|
+
with(ExodusHelper::NEPTUNE_DATA_DIR).and_return(true)
|
588
|
+
|
589
|
+
# and let's say that we've never run the job before
|
590
|
+
# start by mocking out its filesystem reads
|
591
|
+
key = ExodusHelper.get_key_from_job_data(job)
|
592
|
+
profiling_key = "#{ExodusHelper::NEPTUNE_DATA_DIR}/#{key}.json"
|
593
|
+
flexmock(File).should_receive(:exists?).
|
594
|
+
with(profiling_key).and_return(false)
|
595
|
+
|
596
|
+
# mock out timing the user's code
|
597
|
+
flexmock(Time).should_receive(:now).and_return(1.0, 2.0)
|
598
|
+
|
599
|
+
# then mock out exec'ing the user's code
|
600
|
+
# TODO(cgb): do this
|
601
|
+
|
602
|
+
# mock out getting cpuinfo
|
603
|
+
flexmock(CommonFunctions).should_receive(:shell).
|
604
|
+
with(ExodusHelper::GET_CPU_INFO).and_return("cpu MHz: 666.66")
|
605
|
+
|
606
|
+
# finally, mock out writing the profiling information
|
607
|
+
flexmock(File).should_receive(:open).with(profiling_key, "w+", Proc).
|
608
|
+
and_return()
|
609
|
+
|
565
610
|
# mock out scp calls - assume they go through with no problems
|
566
611
|
flexmock(CommonFunctions).should_receive(:shell).with(/\Ascp/).
|
567
612
|
and_return()
|
@@ -605,12 +650,24 @@ class TestExodus < Test::Unit::TestCase
|
|
605
650
|
|
606
651
|
job2 = job.dup
|
607
652
|
|
653
|
+
# let's say that we have a neptune profiling directory
|
654
|
+
flexmock(File).should_receive(:exists?).
|
655
|
+
with(ExodusHelper::NEPTUNE_DATA_DIR).and_return(true)
|
656
|
+
|
657
|
+
# and let's say that we've run the job before
|
658
|
+
key = ExodusHelper.get_key_from_job_data(job)
|
659
|
+
profiling_filename = "#{ExodusHelper::NEPTUNE_DATA_DIR}/#{key}.json"
|
660
|
+
dumped_data = JSON.dump({
|
661
|
+
"total_execution_time" => 60.00,
|
662
|
+
"cpu_speed" => 666.66
|
663
|
+
})
|
664
|
+
flexmock(File).should_receive(:exists?).
|
665
|
+
with(profiling_filename).and_return(true)
|
666
|
+
flexmock(File).should_receive(:open).
|
667
|
+
with(profiling_filename, Proc).and_return(dumped_data)
|
668
|
+
|
608
669
|
# mock out calls to the NeptuneManager
|
609
670
|
flexmock(NeptuneManagerClient).new_instances { |instance|
|
610
|
-
# for this test, let's say there's no data on this task right now
|
611
|
-
instance.should_receive(:get_profiling_info).with(String).
|
612
|
-
and_return({})
|
613
|
-
|
614
671
|
# let's say that all checks to see if temp files exist tell us that
|
615
672
|
# the files don't exist
|
616
673
|
instance.should_receive(:does_file_exist?).
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neptune
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 17
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 2
|
9
|
-
-
|
10
|
-
version: 0.2.
|
9
|
+
- 3
|
10
|
+
version: 0.2.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Chris Bunch
|
@@ -15,7 +15,7 @@ autorequire: neptune
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-09-28 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: promise
|