neptune 0.2.2 → 0.2.3
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.
- 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
|