ripe 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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