ripe 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +0 -5
- data/Guardfile +2 -4
- data/README.md +1 -0
- data/bin/ripe +1 -59
- data/lib/ripe.rb +8 -1
- data/lib/ripe/blocks.rb +13 -0
- data/lib/ripe/blocks/block.rb +142 -0
- data/lib/ripe/blocks/liquid_block.rb +48 -0
- data/lib/ripe/blocks/multi_block.rb +71 -0
- data/lib/ripe/blocks/parallel_block.rb +29 -0
- data/lib/ripe/blocks/serial_block.rb +61 -0
- data/lib/ripe/blocks/working_block.rb +101 -0
- data/lib/ripe/cli.rb +121 -0
- data/lib/ripe/cli/helper.rb +31 -0
- data/lib/ripe/db.rb +7 -0
- data/lib/ripe/db/task.rb +42 -0
- data/lib/ripe/db/task_migration.rb +33 -0
- data/lib/ripe/db/worker.rb +64 -0
- data/lib/ripe/db/worker_migration.rb +41 -0
- data/lib/ripe/dsl.rb +2 -4
- data/lib/ripe/dsl/task_dsl.rb +4 -2
- data/lib/ripe/dsl/workflow_dsl.rb +5 -0
- data/lib/ripe/library.rb +34 -45
- data/lib/ripe/repo.rb +24 -23
- data/lib/ripe/version.rb +1 -1
- data/lib/ripe/worker_controller.rb +72 -144
- data/lib/ripe/worker_controller/preparer.rb +172 -0
- data/lib/ripe/worker_controller/syncer.rb +118 -0
- data/spec/cli_spec.rb +14 -0
- data/spec/library_spec.rb +18 -18
- data/spec/spec_helper.rb +2 -0
- data/spec/testpack.rb +16 -5
- data/spec/testpack/.ripe/meta.db +0 -0
- data/spec/testpack/.ripe/tasks/bar.sh +3 -0
- data/spec/testpack/{ripe → .ripe}/tasks/foo.sh +0 -0
- data/spec/testpack/.ripe/workers/1/1.sh +16 -0
- data/spec/testpack/.ripe/workers/1/2.sh +16 -0
- data/spec/testpack/.ripe/workers/1/job.sh +54 -0
- data/spec/testpack/.ripe/workers/2/3.sh +16 -0
- data/spec/testpack/.ripe/workers/2/4.sh +16 -0
- data/spec/testpack/.ripe/workers/2/job.sh +54 -0
- data/spec/testpack/.ripe/workers/3/5.sh +16 -0
- data/spec/testpack/.ripe/workers/3/6.sh +16 -0
- data/spec/testpack/.ripe/workers/3/job.sh +54 -0
- data/spec/testpack/.ripe/workflows/foobar.rb +23 -0
- data/spec/testpack/{case/Sample1 → Sample1}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample1 → Sample1}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample1 → Sample1}/foo_output.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample2 → Sample2}/foo_output.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/bar_output.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/foo_input.txt +0 -0
- data/spec/testpack/{case/Sample3 → Sample3}/foo_output.txt +0 -0
- data/spec/worker_controller_spec.rb +143 -0
- metadata +66 -40
- data/lib/ripe/block.rb +0 -41
- data/lib/ripe/liquid_block.rb +0 -17
- data/lib/ripe/multi_block.rb +0 -35
- data/lib/ripe/parallel_block.rb +0 -13
- data/lib/ripe/serial_block.rb +0 -37
- data/lib/ripe/task.rb +0 -21
- data/lib/ripe/task_migration.rb +0 -18
- data/lib/ripe/worker.rb +0 -44
- data/lib/ripe/worker_migration.rb +0 -26
- data/lib/ripe/working_block.rb +0 -41
- data/spec/block_spec.rb +0 -7
- data/spec/ripe_spec.rb +0 -7
- data/spec/testpack/ripe/tasks/bar.sh +0 -3
- 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
data/lib/ripe/db/task.rb
ADDED
@@ -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
|