ripe 0.2.0 → 0.2.1

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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +0 -5
  3. data/Guardfile +2 -4
  4. data/README.md +1 -0
  5. data/bin/ripe +1 -59
  6. data/lib/ripe.rb +8 -1
  7. data/lib/ripe/blocks.rb +13 -0
  8. data/lib/ripe/blocks/block.rb +142 -0
  9. data/lib/ripe/blocks/liquid_block.rb +48 -0
  10. data/lib/ripe/blocks/multi_block.rb +71 -0
  11. data/lib/ripe/blocks/parallel_block.rb +29 -0
  12. data/lib/ripe/blocks/serial_block.rb +61 -0
  13. data/lib/ripe/blocks/working_block.rb +101 -0
  14. data/lib/ripe/cli.rb +121 -0
  15. data/lib/ripe/cli/helper.rb +31 -0
  16. data/lib/ripe/db.rb +7 -0
  17. data/lib/ripe/db/task.rb +42 -0
  18. data/lib/ripe/db/task_migration.rb +33 -0
  19. data/lib/ripe/db/worker.rb +64 -0
  20. data/lib/ripe/db/worker_migration.rb +41 -0
  21. data/lib/ripe/dsl.rb +2 -4
  22. data/lib/ripe/dsl/task_dsl.rb +4 -2
  23. data/lib/ripe/dsl/workflow_dsl.rb +5 -0
  24. data/lib/ripe/library.rb +34 -45
  25. data/lib/ripe/repo.rb +24 -23
  26. data/lib/ripe/version.rb +1 -1
  27. data/lib/ripe/worker_controller.rb +72 -144
  28. data/lib/ripe/worker_controller/preparer.rb +172 -0
  29. data/lib/ripe/worker_controller/syncer.rb +118 -0
  30. data/spec/cli_spec.rb +14 -0
  31. data/spec/library_spec.rb +18 -18
  32. data/spec/spec_helper.rb +2 -0
  33. data/spec/testpack.rb +16 -5
  34. data/spec/testpack/.ripe/meta.db +0 -0
  35. data/spec/testpack/.ripe/tasks/bar.sh +3 -0
  36. data/spec/testpack/{ripe → .ripe}/tasks/foo.sh +0 -0
  37. data/spec/testpack/.ripe/workers/1/1.sh +16 -0
  38. data/spec/testpack/.ripe/workers/1/2.sh +16 -0
  39. data/spec/testpack/.ripe/workers/1/job.sh +54 -0
  40. data/spec/testpack/.ripe/workers/2/3.sh +16 -0
  41. data/spec/testpack/.ripe/workers/2/4.sh +16 -0
  42. data/spec/testpack/.ripe/workers/2/job.sh +54 -0
  43. data/spec/testpack/.ripe/workers/3/5.sh +16 -0
  44. data/spec/testpack/.ripe/workers/3/6.sh +16 -0
  45. data/spec/testpack/.ripe/workers/3/job.sh +54 -0
  46. data/spec/testpack/.ripe/workflows/foobar.rb +23 -0
  47. data/spec/testpack/{case/Sample1 → Sample1}/bar_output.txt +0 -0
  48. data/spec/testpack/{case/Sample1 → Sample1}/foo_input.txt +0 -0
  49. data/spec/testpack/{case/Sample1 → Sample1}/foo_output.txt +0 -0
  50. data/spec/testpack/{case/Sample2 → Sample2}/bar_output.txt +0 -0
  51. data/spec/testpack/{case/Sample2 → Sample2}/foo_input.txt +0 -0
  52. data/spec/testpack/{case/Sample2 → Sample2}/foo_output.txt +0 -0
  53. data/spec/testpack/{case/Sample3 → Sample3}/bar_output.txt +0 -0
  54. data/spec/testpack/{case/Sample3 → Sample3}/foo_input.txt +0 -0
  55. data/spec/testpack/{case/Sample3 → Sample3}/foo_output.txt +0 -0
  56. data/spec/worker_controller_spec.rb +143 -0
  57. metadata +66 -40
  58. data/lib/ripe/block.rb +0 -41
  59. data/lib/ripe/liquid_block.rb +0 -17
  60. data/lib/ripe/multi_block.rb +0 -35
  61. data/lib/ripe/parallel_block.rb +0 -13
  62. data/lib/ripe/serial_block.rb +0 -37
  63. data/lib/ripe/task.rb +0 -21
  64. data/lib/ripe/task_migration.rb +0 -18
  65. data/lib/ripe/worker.rb +0 -44
  66. data/lib/ripe/worker_migration.rb +0 -26
  67. data/lib/ripe/working_block.rb +0 -41
  68. data/spec/block_spec.rb +0 -7
  69. data/spec/ripe_spec.rb +0 -7
  70. data/spec/testpack/ripe/tasks/bar.sh +0 -3
  71. data/spec/testpack/ripe/workflows/foobar.rb +0 -23
@@ -0,0 +1,101 @@
1
+ module Ripe
2
+
3
+ module Blocks
4
+
5
+ ##
6
+ # This class represents a {Ripe::CLI::TaskCLI} that has been parametrized.
7
+ # In the block arborescence, {WorkingBlock}s are always leaf nodes.
8
+ #
9
+ # @see Ripe::CLI::TaskCLI
10
+ # @see Ripe::WorkerController::Preparer#prepare
11
+
12
+ class WorkingBlock < Block
13
+
14
+ ##
15
+ # @param filename [String] filename of the template file
16
+ # @param vars [Hash<Symbol, String>] key-value pairs
17
+
18
+ def initialize(filename, vars = {})
19
+ @filename = filename
20
+ super(File.basename(@filename), [], vars)
21
+ end
22
+
23
+ ##
24
+ # Return working block +parameters+ as a sequence of bash variable
25
+ # assignments.
26
+ #
27
+ # @return [String] sequence of bash variable assignments
28
+
29
+ def declarations
30
+ vars.map do |key, value|
31
+ lh = key.upcase
32
+ rh = value.is_a?(Array) ? "(\"#{value.join("\" \"")}\")" : "\"#{value}\""
33
+ "#{lh}=#{rh}"
34
+ end
35
+ end
36
+
37
+ ##
38
+ # (see Block#command)
39
+ #
40
+ # The resulting string contains the result of the application of
41
+ # parameters to the +task+ from which the {WorkingBlock} was defined.
42
+ #
43
+ # @see Ripe::DB::Task
44
+ # @see Ripe::DSL::TaskDSL
45
+
46
+ def command
47
+ <<-EOF.gsub(/^[ ]+/, '')
48
+
49
+ # <#{id}>
50
+
51
+ #{declarations.join("\n")}
52
+
53
+ exec 1>"$LOG" 2>&1
54
+
55
+ #{File.new(@filename).read}
56
+ echo "##.DONE.##"
57
+
58
+ # </#{id}>
59
+ EOF
60
+ end
61
+
62
+ ##
63
+ # (see Block#prune)
64
+ #
65
+ # A {WorkingBlock} will be pruned if its targets exists, unless the
66
+ # +protect+ parameter is set to +true+.
67
+
68
+ def prune(protect, depend)
69
+ targets_exist? && !protect ? nil : self
70
+ end
71
+
72
+ ##
73
+ # (see Block#targets_exist?)
74
+ #
75
+ # For {WorkingBlock}s, if there is so much as a single target -- a block
76
+ # variable starting with +output_+) that does not exist, return +false+.
77
+ # Otherwise, return +true+.
78
+
79
+ def targets_exist?
80
+ statuses = @vars.select { |key, _| !key[/^output_/].nil? }.values.flatten
81
+ targets_exist = statuses.map { |target| File.exists? target }.inject(:&)
82
+
83
+ # If there are no targets at all, then assume that all targets exist
84
+ targets_exist == nil ? true : targets_exist
85
+ end
86
+
87
+ ##
88
+ # (see Block#topology)
89
+ #
90
+ # Since a {WorkingBlock} is always a leaf node in the tree, the subtree
91
+ # starting at the leaf node only contains the {WorkingBlock}.
92
+
93
+ def topology
94
+ [@id]
95
+ end
96
+
97
+ end
98
+
99
+ end
100
+
101
+ end
data/lib/ripe/cli.rb ADDED
@@ -0,0 +1,121 @@
1
+ require 'ripl' # REPL
2
+ require 'hirb' # Pretty output for +ActiveRecord+ objects
3
+ require 'thor'
4
+
5
+ require_relative '../ripe'
6
+
7
+ include Ripe
8
+ include Ripe::DB
9
+ include Ripe::DSL
10
+
11
+ module Ripe
12
+
13
+ ##
14
+ # This class represents the CLI interface to ripe. The methods defined in
15
+ # this class are wrappers for the methods defined in +Ripe::Repo+ and
16
+ # +Ripe::WorkerController+.
17
+ #
18
+ # @see Ripe::Repo
19
+ # @see Ripe::WorkerController
20
+
21
+ class CLI < Thor
22
+
23
+ desc 'init', 'Initialize ripe repository'
24
+
25
+ ##
26
+ # Initialize ripe repository.
27
+ #
28
+ # @see Ripe::Repo#attach_or_create
29
+
30
+ def init
31
+ puts "Initialized ripe repository in #{Dir.pwd}"
32
+ repo = Repo.new
33
+ repo.attach_or_create
34
+ end
35
+
36
+
37
+
38
+
39
+
40
+ desc 'console', 'Launch ripe console'
41
+
42
+ ##
43
+ # Launch ripe console. It is a REPL bound to the context of a
44
+ # +Ripe::WorkerController+ initialized in the working directory.
45
+
46
+ def console
47
+ repo = Repo.new
48
+ repo.attach
49
+
50
+ unless repo.has_repository?
51
+ abort "Cannot launch console: ripe repo not initialized"
52
+ end
53
+
54
+ # Do not send arguments to the REPL
55
+ ARGV.clear
56
+
57
+ Ripl.config[:prompt] = proc do
58
+ # This is the only place I could think of placing +Hirb#enable+.
59
+ Hirb.enable unless Hirb::View.enabled?
60
+ 'ripe> '
61
+ end
62
+
63
+ # Launch the REPL session in the context of +WorkerController+.
64
+ Ripl.start :binding => repo.controller.instance_eval { binding }
65
+ end
66
+
67
+
68
+
69
+
70
+
71
+ desc 'prepare SAMPLES', 'Prepare jobs from template workflow'
72
+ option :workflow, :aliases => '-w', :type => :string, :required => true,
73
+ :desc => 'Workflow to be applied'
74
+ option :options, :aliases => '-o', :type => :string, :required => false,
75
+ :desc => 'Options', :default => ''
76
+ option :start, :aliases => '-s', :type => :boolean, :required => false,
77
+ :desc => 'Automatically submit the prepared jobs onto the compute cluster', :default => false
78
+
79
+ ##
80
+ # Prepare samples.
81
+ #
82
+ # @see Ripe::Repo#controller
83
+ # @see Ripe::WorkerController#prepare
84
+
85
+ def prepare(*samples)
86
+ repo = Repo.new
87
+ repo.attach
88
+
89
+ unless repo.has_repository?
90
+ abort 'Cannot launch console: ripe repo not initialized'
91
+ end
92
+
93
+ abort 'No samples specified.' if samples.length == 0
94
+
95
+ workers = repo.controller.prepare(options[:workflow], samples,
96
+ Helper.parse_cli_opts(options[:options]))
97
+
98
+ repo.controller.start(workers) if options[:start]
99
+ end
100
+
101
+
102
+
103
+
104
+
105
+ ##
106
+ # Retrieve ripe version.
107
+
108
+ desc 'version', 'Retrieve ripe version'
109
+ def version
110
+ puts "ripe version #{Ripe::VERSION}"
111
+ end
112
+
113
+
114
+
115
+
116
+
117
+ end
118
+
119
+ end
120
+
121
+ require_relative 'cli/helper.rb'
@@ -0,0 +1,31 @@
1
+ module Ripe
2
+
3
+ class CLI
4
+
5
+ ##
6
+ # This class defines helper methods for +Ripe::CLI+.
7
+ #
8
+ # @see Ripe::CLI
9
+
10
+ class Helper
11
+
12
+ ##
13
+ # Parses a string representing a hash in the format of +a=1,b=2,c=3+ into a
14
+ # hash in the format of +{a: 1, b: 2, c: 3}+.
15
+ #
16
+ # @param options [String] a hash in the format of +a=1,b=2,c=3+
17
+ # @return [Hash] a hash in the format of +{a: 1, b: 2, c: 3}+
18
+
19
+ def self.parse_cli_opts(options)
20
+ params = options.split(/,/).map do |pair|
21
+ key, value = pair.split(/=/)
22
+ { key.to_sym => value }
23
+ end
24
+ params.inject(&:merge) || {}
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
data/lib/ripe/db.rb ADDED
@@ -0,0 +1,7 @@
1
+ require 'active_record'
2
+ require 'fileutils'
3
+
4
+ require_relative 'db/task'
5
+ require_relative 'db/task_migration'
6
+ require_relative 'db/worker'
7
+ require_relative 'db/worker_migration'
@@ -0,0 +1,42 @@
1
+ module Ripe
2
+
3
+ module DB
4
+
5
+ ##
6
+ # This class represents a +Task+ object in ripe's internal database. Its
7
+ # fields are defined by +Ripe::DB::TaskMigration+.
8
+ #
9
+ # @see Ripe::WorkerController
10
+
11
+ class Task < ActiveRecord::Base
12
+ belongs_to :worker
13
+
14
+ ##
15
+ # Return path to task directory, which is the same as worker directory.
16
+
17
+ def dir
18
+ "#{self.worker.dir}"
19
+ end
20
+
21
+ ##
22
+ # Return path to task-level combined +stdout+ and +stderr+ log.
23
+
24
+ def log
25
+ "#{self.dir}/#{self.id}.log"
26
+ end
27
+
28
+ ##
29
+ # Return path to task-level job script, which only includes the task at
30
+ # hand. This script is never actually executed by ripe.
31
+ #
32
+ # @see Ripe::DB::Worker#sh
33
+
34
+ def sh
35
+ "#{self.dir}/#{self.id}.sh"
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,33 @@
1
+ module Ripe
2
+
3
+ module DB
4
+
5
+ ##
6
+ # This class creates and destroys the +Task+ model in the internal
7
+ # database.
8
+
9
+ class TaskMigration < ActiveRecord::Migration
10
+
11
+ ##
12
+ # Create model in database.
13
+
14
+ def self.up
15
+ create_table :tasks do |t|
16
+ t.belongs_to :worker
17
+ t.string :sample
18
+ t.string :block
19
+ end
20
+ end
21
+
22
+ ##
23
+ # Destroy model in database.
24
+
25
+ def self.down
26
+ drop_table :tasks
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,64 @@
1
+ module Ripe
2
+
3
+ module DB
4
+
5
+ ##
6
+ # This class represents a +Worker+ object in ripe's internal database. Its
7
+ # fields are defined by +Ripe::DB::WorkerMigration+.
8
+ #
9
+ # @see Ripe::WorkerController
10
+
11
+ class Worker < ActiveRecord::Base
12
+ has_many :tasks, dependent: :destroy
13
+
14
+ ##
15
+ # Return path to worker directory
16
+
17
+ def dir
18
+ "#{Repo::REPOSITORY_PATH}/workers/#{self.id}"
19
+ end
20
+
21
+ ##
22
+ # Return path to worker/job script, which includes all tasks defined in
23
+ # the worker. This is the script that is actually executed when the
24
+ # worker is run.
25
+ #
26
+ # @see Ripe::DB::Task#sh
27
+
28
+ def sh
29
+ "#{self.dir}/job.sh"
30
+ end
31
+
32
+ ##
33
+ # Return path to the +stdout+ output of the job, which only exists after
34
+ # the job has been completed.
35
+
36
+ def stdout
37
+ "#{self.dir}/job.stdout"
38
+ end
39
+
40
+ ##
41
+ # Return path to the +stderr+ output of the job, which only exists after
42
+ # the job has been completed.
43
+
44
+ def stderr
45
+ "#{self.dir}/job.stderr"
46
+ end
47
+
48
+ # Automatically create worker directory upon object instantiation
49
+
50
+ after_create do
51
+ FileUtils.mkdir_p dir if !Dir.exists? dir
52
+ end
53
+
54
+ # Automatically remove worker directory upon object destruction
55
+
56
+ before_destroy do
57
+ FileUtils.rm_r dir if Dir.exists? dir
58
+ end
59
+
60
+ end
61
+
62
+ end
63
+
64
+ end
@@ -0,0 +1,41 @@
1
+ module Ripe
2
+
3
+ module DB
4
+
5
+ ##
6
+ # This class creates and destroys the +Worker+ model in the internal
7
+ # database.
8
+
9
+ class WorkerMigration < ActiveRecord::Migration
10
+
11
+ ##
12
+ # Create model in database.
13
+
14
+ def self.up
15
+ create_table :workers do |t|
16
+ t.string :cpu_used
17
+ t.string :exit_code
18
+ t.string :handle
19
+ t.string :host
20
+ t.string :moab_id
21
+ t.string :memory_used
22
+ t.integer :ppn
23
+ t.string :queue
24
+ t.string :time
25
+ t.string :status, default: :unprepared
26
+ t.string :walltime
27
+ end
28
+ end
29
+
30
+ ##
31
+ # Destroy model in database.
32
+
33
+ def self.down
34
+ drop_table :workers
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end