abid 0.3.0.pre.alpha.1 → 0.3.0.pre.alpha.2
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.md +9 -29
- data/Rakefile +0 -22
- data/abid.gemspec +1 -1
- data/bin/setup +3 -0
- data/exe/abidsc +5 -0
- data/lib/abid.rb +4 -1
- data/lib/abid/application.rb +0 -10
- data/lib/abid/cli.rb +53 -0
- data/lib/abid/cli/assume.rb +32 -0
- data/lib/abid/cli/list.rb +48 -0
- data/lib/abid/cli/migrate.rb +25 -0
- data/lib/abid/cli/revoke.rb +46 -0
- data/lib/abid/cli/table_formatter.rb +42 -0
- data/lib/abid/config.rb +10 -8
- data/lib/abid/error.rb +2 -0
- data/lib/abid/job.rb +25 -0
- data/lib/abid/params_format.rb +61 -0
- data/lib/abid/state.rb +3 -3
- data/lib/abid/state_manager.rb +17 -0
- data/lib/abid/state_manager/database.rb +36 -0
- data/lib/abid/state_manager/state.rb +119 -0
- data/lib/abid/version.rb +1 -1
- metadata +19 -7
- data/lib/abid/tasks/core.rake +0 -98
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4721d7f5310427976f61dd1673b774c9881bfeb
|
4
|
+
data.tar.gz: aa2d329aae16145039c4bb28b57d50d3c98dd76b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 095800154b3abfccf282351e73b66e1d02e11bdb27e01d0fc54707aa533d4da0e8745c70ecf6f0275f338afa2e3846573cab8ba0c4e25547389888f6957e812b
|
7
|
+
data.tar.gz: fbd9d3d6b3e1975310a39e59d0b7a0b8a22974ec0eb74fe674f3b6702d1f62cd9537259cacb3714c5cd743830a595c4ce6f57a5c4f316068e8d06b0e22a1389e
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Abid
|
2
2
|
|
3
|
+
[](https://travis-ci.org/ojima-h/abid)
|
4
|
+
|
3
5
|
Abid is a simple Workflow Engine based on Rake.
|
4
6
|
|
5
7
|
## Installation
|
@@ -24,7 +26,7 @@ Abid is a simple Workflow Engine based on Rake.
|
|
24
26
|
|
25
27
|
2. Setup a database.
|
26
28
|
|
27
|
-
$ bundle exec
|
29
|
+
$ bundle exec abidsc migrate
|
28
30
|
|
29
31
|
## Usage
|
30
32
|
|
@@ -341,39 +343,17 @@ end
|
|
341
343
|
|
342
344
|
When play is defined, new subclass of Avid::Play is created and play body is evaluated in that new class context. So, any class goodies can be put in play's body, i.e. including modules, `attr_reader` / `attr_writer`, method definitions, etc..
|
343
345
|
|
344
|
-
##
|
345
|
-
|
346
|
-
### `state:list`
|
347
|
-
|
348
|
-
```
|
349
|
-
$ abid state:list started_after="2000-01-01 00:00:00" started_before="2000=01-02 00:00:00"
|
350
|
-
```
|
351
|
-
|
352
|
-
Display plays current states.
|
353
|
-
|
354
|
-
### `state:revoke`
|
355
|
-
|
356
|
-
```
|
357
|
-
$ abid state:revoke[id]
|
358
|
-
```
|
346
|
+
## `abidsc` command
|
359
347
|
|
360
|
-
|
348
|
+
You can manage plays states using `abidsc` command.
|
361
349
|
|
362
|
-
### `state:assume`
|
363
|
-
|
364
|
-
```
|
365
|
-
$ abid state:assume[task_name] date=2000-01-01
|
366
350
|
```
|
351
|
+
$ abidsc list --after='2000-01-01 00:00:00" --before="2000=01-02 00:00:00" # Display plays current states.
|
352
|
+
$ abidsc revoke STATE_ID # remove the job history # Remove the play recored from DB.
|
353
|
+
$ abidsc assume TASK_NAME date=2000-01-01 # Insert a record that the play successed into DB.
|
367
354
|
|
368
|
-
|
369
|
-
|
370
|
-
### `db:migrate`
|
371
|
-
|
355
|
+
$ abidsc migrate # Initialize or upgrade DB schema.
|
372
356
|
```
|
373
|
-
$ abid db:migrate
|
374
|
-
```
|
375
|
-
|
376
|
-
Initialize or update DB.
|
377
357
|
|
378
358
|
## Development
|
379
359
|
|
data/Rakefile
CHANGED
@@ -8,25 +8,3 @@ Rake::TestTask.new(:test) do |t|
|
|
8
8
|
end
|
9
9
|
|
10
10
|
task :default => :test
|
11
|
-
|
12
|
-
namespace :db do
|
13
|
-
desc 'Run migrations'
|
14
|
-
task :migrate, [:version] do |_t, args|
|
15
|
-
require 'sqlite3'
|
16
|
-
require 'sequel'
|
17
|
-
|
18
|
-
database_url = 'sqlite://' + File.expand_path('../tmp/abid.db', __FILE__)
|
19
|
-
migrations_path = File.expand_path('../migrations', __FILE__)
|
20
|
-
|
21
|
-
require 'sequel'
|
22
|
-
Sequel.extension :migration
|
23
|
-
db = Sequel.connect(database_url)
|
24
|
-
if args[:version]
|
25
|
-
puts "Migrating to version #{args[:version]}"
|
26
|
-
Sequel::Migrator.run(db, migrations_path, target: args[:version].to_i)
|
27
|
-
else
|
28
|
-
puts 'Migrating to latest'
|
29
|
-
Sequel::Migrator.run(db, migrations_path)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/abid.gemspec
CHANGED
@@ -26,8 +26,8 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_dependency 'rake', '~> 10.0'
|
28
28
|
spec.add_dependency 'concurrent-ruby-ext'
|
29
|
-
spec.add_dependency 'inifile'
|
30
29
|
spec.add_dependency 'sequel'
|
31
30
|
spec.add_dependency 'sqlite3'
|
32
31
|
spec.add_dependency 'rbtree'
|
32
|
+
spec.add_dependency 'thor'
|
33
33
|
end
|
data/bin/setup
CHANGED
data/exe/abidsc
ADDED
data/lib/abid.rb
CHANGED
@@ -7,13 +7,16 @@ require 'monitor'
|
|
7
7
|
require 'time'
|
8
8
|
require 'yaml'
|
9
9
|
require 'concurrent'
|
10
|
-
require 'inifile'
|
11
10
|
require 'rbtree'
|
12
11
|
require 'sqlite3'
|
13
12
|
require 'sequel'
|
14
13
|
|
15
14
|
require 'abid/config'
|
16
15
|
require 'abid/error'
|
16
|
+
require 'abid/params_format'
|
17
|
+
require 'abid/state_manager'
|
18
|
+
require 'abid/job'
|
19
|
+
|
17
20
|
require 'abid/rake_extensions'
|
18
21
|
require 'abid/version'
|
19
22
|
require 'abid/abid_module'
|
data/lib/abid/application.rb
CHANGED
@@ -111,15 +111,5 @@ module Abid
|
|
111
111
|
opts.environment('RAKEOPT')
|
112
112
|
end.parse!
|
113
113
|
end
|
114
|
-
|
115
|
-
def database
|
116
|
-
return @database if @database
|
117
|
-
|
118
|
-
# symbolize keys
|
119
|
-
params = {}
|
120
|
-
Abid.config.database.each { |k, v| params[k.to_sym] = v }
|
121
|
-
|
122
|
-
@database = Sequel.connect(**params)
|
123
|
-
end
|
124
114
|
end
|
125
115
|
end
|
data/lib/abid/cli.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
class CLI < Thor
|
5
|
+
class_option :config_file, aliases: '-C', desc: 'config file path'
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super(*args)
|
9
|
+
Abid.config.load(options[:config_file])
|
10
|
+
end
|
11
|
+
|
12
|
+
desc 'config', 'Show current config'
|
13
|
+
def config
|
14
|
+
puts Abid.config.to_yaml
|
15
|
+
end
|
16
|
+
|
17
|
+
desc 'migrate', 'Run database migration'
|
18
|
+
def migrate
|
19
|
+
require 'abid/cli/migrate'
|
20
|
+
Migrate.new(options).run
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'assume TASK [TASKS..] [PARAMS]', 'Assume the job to be SUCCESSED'
|
24
|
+
option :force, type: :boolean, aliases: '-f',
|
25
|
+
desc: 'set the state even if the job is running'
|
26
|
+
def assume(task, *rest_args)
|
27
|
+
require 'abid/cli/assume'
|
28
|
+
Assume.new(options, [task, *rest_args]).run
|
29
|
+
rescue AlreadyRunningError
|
30
|
+
exit 1
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'list [PREFIX]', 'List jobs'
|
34
|
+
option :after, type: :string, aliases: '-a', desc: 'start time fliter'
|
35
|
+
option :before, type: :string, aliases: '-b', desc: 'start time filter'
|
36
|
+
def list(prefix = nil)
|
37
|
+
require 'abid/cli/list'
|
38
|
+
List.new(options, prefix).run
|
39
|
+
end
|
40
|
+
map ls: :list
|
41
|
+
|
42
|
+
desc 'revoke JOB_ID...', 'Revoke jobs history'
|
43
|
+
option :force, type: :boolean, aliases: '-f',
|
44
|
+
desc: 'revoke the states even if the job is running'
|
45
|
+
option :quiet, type: :boolean, aliases: '-q',
|
46
|
+
desc: 'no prompt before removal'
|
47
|
+
def revoke(job_id, *rest_args)
|
48
|
+
require 'abid/cli/revoke'
|
49
|
+
Revoke.new(options, [job_id, *rest_args]).run
|
50
|
+
end
|
51
|
+
map rm: :revoke
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Abid
|
2
|
+
class CLI
|
3
|
+
class Assume
|
4
|
+
def initialize(options, args)
|
5
|
+
@options = options
|
6
|
+
@args = args
|
7
|
+
|
8
|
+
@force = @options[:force]
|
9
|
+
end
|
10
|
+
|
11
|
+
def run
|
12
|
+
tasks, params = ParamsFormat.parse_args(@args)
|
13
|
+
|
14
|
+
tasks.each { |task| assume(task, params) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def assume(task, params)
|
18
|
+
params_str = ParamsFormat.format(params)
|
19
|
+
|
20
|
+
job = Job.new(task, params)
|
21
|
+
state = StateManager::State.assume(job, force: @force)
|
22
|
+
|
23
|
+
puts "#{task} #{params_str} (id: #{state.id})" \
|
24
|
+
' is assumed to be SUCCESSED.'
|
25
|
+
rescue AlreadyRunningError
|
26
|
+
$stderr.puts "#{task} #{params_str} already running.\n" \
|
27
|
+
'Use -f option if you want to force assume.'
|
28
|
+
raise
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'abid/cli/table_formatter'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
class CLI
|
5
|
+
class List
|
6
|
+
def initialize(options, prefix)
|
7
|
+
@options = options
|
8
|
+
@prefix = prefix
|
9
|
+
|
10
|
+
@after = Time.parse(options[:after]) if options[:after]
|
11
|
+
@before = Time.parse(options[:before]) if options[:before]
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
puts build_table
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_table
|
19
|
+
states = StateManager::State
|
20
|
+
.filter_by_prefix(@prefix)
|
21
|
+
.filter_by_start_time(after: @after, before: @before)
|
22
|
+
table = states.map { |state| format_state(state) }
|
23
|
+
TableFormatter.new(table).format
|
24
|
+
end
|
25
|
+
|
26
|
+
def format_state(state)
|
27
|
+
t = state.start_time.strftime('%Y-%m-%d %H:%M:%S') if state.start_time
|
28
|
+
[
|
29
|
+
state.id,
|
30
|
+
t,
|
31
|
+
state.state_label,
|
32
|
+
format_exec_time(state.exec_time),
|
33
|
+
state.name + ' ' + ParamsFormat.format(YAML.load(state.params))
|
34
|
+
]
|
35
|
+
end
|
36
|
+
|
37
|
+
def format_exec_time(exec_time)
|
38
|
+
return '' unless exec_time
|
39
|
+
|
40
|
+
if exec_time >= 60 * 60 * 24
|
41
|
+
exec_time.div(60 * 60 * 24).to_s + ' days'
|
42
|
+
else
|
43
|
+
Time.at(exec_time).utc.strftime('%H:%M:%S').to_s
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'abid/state_manager'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
class CLI
|
5
|
+
class Migrate
|
6
|
+
def initialize(options)
|
7
|
+
@options = options
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
db = StateManager::Database.connect!
|
12
|
+
dir = StateManager::Database.migrations_path
|
13
|
+
|
14
|
+
if Sequel::Migrator.is_current?(db, dir)
|
15
|
+
puts 'Schema is latest.'
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
puts 'Start migration...'
|
20
|
+
Sequel::Migrator.run(db, dir)
|
21
|
+
puts 'Done'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'abid/cli/table_formatter'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
class CLI
|
5
|
+
class Revoke
|
6
|
+
def initialize(options, job_ids)
|
7
|
+
@options = options
|
8
|
+
@job_ids = job_ids.map(&:to_i)
|
9
|
+
|
10
|
+
@force = @options[:force]
|
11
|
+
@quiet = @options[:quiet]
|
12
|
+
end
|
13
|
+
|
14
|
+
def run
|
15
|
+
@job_ids.each do |job_id|
|
16
|
+
state = StateManager::State[job_id]
|
17
|
+
if state.nil?
|
18
|
+
$stderr.puts "state_id #{job_id} not found"
|
19
|
+
next
|
20
|
+
end
|
21
|
+
|
22
|
+
next if !@quiet && !ask(state)
|
23
|
+
|
24
|
+
revoke(state)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def revoke(state)
|
29
|
+
state.revoke(force: @force)
|
30
|
+
puts "revoked #{state.id}"
|
31
|
+
rescue AlreadyRunningError
|
32
|
+
params = ParamsFormat.format(YAML.load(state.params))
|
33
|
+
$stderr.puts "#{state.name} #{params} already running.\n" \
|
34
|
+
'Use -f option if you want to force assume.'
|
35
|
+
end
|
36
|
+
|
37
|
+
def ask(state)
|
38
|
+
params = ParamsFormat.format(YAML.load(state.params))
|
39
|
+
print "revoke task \`#{state.name} #{params}'? "
|
40
|
+
$stdout.flush
|
41
|
+
ret = $stdin.gets
|
42
|
+
ret.match(/y(es)?/i)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Abid
|
2
|
+
class CLI
|
3
|
+
class TableFormatter
|
4
|
+
def initialize(body, header = nil)
|
5
|
+
@body = body
|
6
|
+
@header = header
|
7
|
+
end
|
8
|
+
|
9
|
+
def format
|
10
|
+
result = ''
|
11
|
+
|
12
|
+
if @header
|
13
|
+
result << format_row(@header)
|
14
|
+
result << format_row(@header.length, '-')
|
15
|
+
end
|
16
|
+
|
17
|
+
@body.each do |row|
|
18
|
+
result << format_row(row)
|
19
|
+
end
|
20
|
+
|
21
|
+
result
|
22
|
+
end
|
23
|
+
|
24
|
+
def tab_width
|
25
|
+
return @tab_width if @tab_width
|
26
|
+
|
27
|
+
cols_num = (@header || @body.first).length
|
28
|
+
|
29
|
+
@tab_width = Array.new(cols_num) do |i|
|
30
|
+
w = @body.map { |row| row[i].to_s.length }.max || 0
|
31
|
+
@header ? [w, @header[i].length].max : w
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def format_row(row)
|
36
|
+
row.map.with_index do |v, i|
|
37
|
+
v.to_s.ljust(tab_width[i] + 2)
|
38
|
+
end.join('').rstrip + "\n"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/abid/config.rb
CHANGED
@@ -20,11 +20,8 @@ module Abid
|
|
20
20
|
end
|
21
21
|
|
22
22
|
# @return [Hash] database configuration
|
23
|
-
|
24
|
-
|
25
|
-
def initialize
|
26
|
-
super
|
27
|
-
@database = {}
|
23
|
+
def database
|
24
|
+
self['database']
|
28
25
|
end
|
29
26
|
|
30
27
|
# Load config file.
|
@@ -40,10 +37,15 @@ module Abid
|
|
40
37
|
# @return [Config] self
|
41
38
|
def load(config_file = nil)
|
42
39
|
replace(load_config_file(config_file))
|
43
|
-
|
40
|
+
assign_default
|
44
41
|
self
|
45
42
|
end
|
46
43
|
|
44
|
+
# @return [String] YAML string
|
45
|
+
def to_yaml
|
46
|
+
YAML.dump(to_h)
|
47
|
+
end
|
48
|
+
|
47
49
|
private
|
48
50
|
|
49
51
|
def load_config_file(file_path)
|
@@ -60,8 +62,8 @@ module Abid
|
|
60
62
|
YAML.load_file(file_path)
|
61
63
|
end
|
62
64
|
|
63
|
-
def
|
64
|
-
|
65
|
+
def assign_default
|
66
|
+
self['database'] ||= DEFAULT_DATABASE_CONFIG.dup
|
65
67
|
end
|
66
68
|
end
|
67
69
|
end
|
data/lib/abid/error.rb
CHANGED
data/lib/abid/job.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Abid
|
2
|
+
# Job instance that is consists of a task name and params.
|
3
|
+
class Job
|
4
|
+
attr_reader :name, :params
|
5
|
+
|
6
|
+
# @param name [String] task name
|
7
|
+
# @param params [Hash] task params
|
8
|
+
def initialize(name, params)
|
9
|
+
@name = name
|
10
|
+
@params = params.sort.to_h.freeze
|
11
|
+
end
|
12
|
+
|
13
|
+
def params_str
|
14
|
+
@params_str ||= YAML.dump(params)
|
15
|
+
end
|
16
|
+
|
17
|
+
def digest
|
18
|
+
@digest ||= Digest::MD5.hexdigest(name + "\n" + params_str)
|
19
|
+
end
|
20
|
+
|
21
|
+
def assume(force: false)
|
22
|
+
State.assume(self, force: force)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
module ParamsFormat
|
5
|
+
def self.format(params)
|
6
|
+
return '' unless params.is_a? Hash
|
7
|
+
params.map do |key, value|
|
8
|
+
val = Shellwords.escape(format_value(value))
|
9
|
+
"#{key}=#{val}"
|
10
|
+
end.join(' ')
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.format_value(value)
|
14
|
+
case value
|
15
|
+
when Numeric, TrueClass, FalseClass
|
16
|
+
value.to_s
|
17
|
+
when Date
|
18
|
+
value.strftime('%Y-%m-%d')
|
19
|
+
when Time, DateTime
|
20
|
+
value.strftime('%Y-%m-%d %H:%M:%S')
|
21
|
+
when String
|
22
|
+
value
|
23
|
+
else
|
24
|
+
raise Error, "#{value.class} class is not supported"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.parse_args(args)
|
29
|
+
tasks = []
|
30
|
+
params = {}
|
31
|
+
|
32
|
+
args.each do |arg|
|
33
|
+
m = arg.match(/^(\w+)=(.*)$/m)
|
34
|
+
if m
|
35
|
+
params.store(m[1].to_sym, parse_value(m[2]))
|
36
|
+
else
|
37
|
+
tasks << arg unless arg =~ /^-/
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
[tasks, params]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.parse_value(value)
|
45
|
+
case value
|
46
|
+
when 'true', 'false'
|
47
|
+
value == 'true'
|
48
|
+
when /\A\d+\z/
|
49
|
+
value.to_i
|
50
|
+
when /\A\d+\.\d+\z/
|
51
|
+
value.to_f
|
52
|
+
when /\A\d{4}-\d{2}-\d{2}\z/
|
53
|
+
Date.parse(value)
|
54
|
+
when /\A\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}( \d{4})?\z/
|
55
|
+
Time.parse(value)
|
56
|
+
else
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
data/lib/abid/state.rb
CHANGED
@@ -18,7 +18,7 @@ module Abid
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def list(pattern: nil, started_before: nil, started_after: nil)
|
21
|
-
dataset =
|
21
|
+
dataset = StateManager.database[:states]
|
22
22
|
|
23
23
|
dataset = dataset.where { start_time < started_before } if started_before
|
24
24
|
dataset = dataset.where { start_time > started_after } if started_after
|
@@ -38,7 +38,7 @@ module Abid
|
|
38
38
|
end
|
39
39
|
|
40
40
|
def revoke(id)
|
41
|
-
db =
|
41
|
+
db = StateManager.database
|
42
42
|
db.transaction do
|
43
43
|
running = db[:states].where(id: id, state: RUNNING).count > 0
|
44
44
|
fail 'task is now running' if running
|
@@ -65,7 +65,7 @@ module Abid
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def database
|
68
|
-
|
68
|
+
StateManager.database
|
69
69
|
end
|
70
70
|
|
71
71
|
def dataset
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'abid/state_manager/database'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
# StateManager manages jobs execution status and history.
|
5
|
+
#
|
6
|
+
# It ensures that same task is not executed simultaneously.
|
7
|
+
# Further more, it remembers all jobs history and prevents successed jobs to
|
8
|
+
# be executed again.
|
9
|
+
module StateManager
|
10
|
+
# @return [Sequel::Database] database object
|
11
|
+
def self.database
|
12
|
+
@database ||= Database.connect
|
13
|
+
end
|
14
|
+
|
15
|
+
autoload :State, 'abid/state_manager/state'
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'sequel/plugins/serialization'
|
2
|
+
|
3
|
+
module Abid
|
4
|
+
module StateManager
|
5
|
+
module Database
|
6
|
+
Sequel.extension :migration
|
7
|
+
|
8
|
+
# Create a new database object.
|
9
|
+
#
|
10
|
+
# Abid.config['database'] is used Sequel.connect params.
|
11
|
+
#
|
12
|
+
# @return [Sequel::Database] database object
|
13
|
+
def self.connect
|
14
|
+
db = connect!
|
15
|
+
Sequel::Migrator.check_current(db, migrations_path)
|
16
|
+
db
|
17
|
+
rescue Sequel::Migrator::NotCurrentError
|
18
|
+
raise Error, 'current schema is out of date'
|
19
|
+
end
|
20
|
+
|
21
|
+
# Connect to database without schema version check
|
22
|
+
#
|
23
|
+
# @return [Sequel::Database] database object
|
24
|
+
def self.connect!
|
25
|
+
# symbolize keys
|
26
|
+
params = {}
|
27
|
+
Abid.config.database.each { |k, v| params[k.to_sym] = v }
|
28
|
+
Sequel.connect(**params)
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.migrations_path
|
32
|
+
File.expand_path('../../../../migrations', __FILE__)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module Abid
|
2
|
+
module StateManager
|
3
|
+
# O/R Mapper for `states` table.
|
4
|
+
class State < Sequel::Model(StateManager.database)
|
5
|
+
RUNNING = 1
|
6
|
+
SUCCESSED = 2
|
7
|
+
FAILED = 3
|
8
|
+
|
9
|
+
# @!method self.filter_by_start_time(after: nil, before: nil)
|
10
|
+
# @param after [Time] lower bound of start_time
|
11
|
+
# @param before [Time] upper bound of start_time
|
12
|
+
# @return [Sequel::Dataset<State>] a set of states started between
|
13
|
+
# the given range.
|
14
|
+
#
|
15
|
+
# @!method self.filter_by_prefix(prefix)
|
16
|
+
# @param prefix [String] the prefix of task names
|
17
|
+
# @return [Sequel::Dataset<State>] a set of states which name starts
|
18
|
+
# with the given prefix.
|
19
|
+
dataset_module do
|
20
|
+
def filter_by_start_time(after: nil, before: nil)
|
21
|
+
dataset = self
|
22
|
+
dataset = dataset.where { start_time >= after } if after
|
23
|
+
dataset = dataset.where { start_time <= before } if before
|
24
|
+
dataset
|
25
|
+
end
|
26
|
+
|
27
|
+
def filter_by_prefix(prefix)
|
28
|
+
return self if prefix.nil?
|
29
|
+
where { Sequel.like(:name, prefix + '%') }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Find a state by the job.
|
34
|
+
#
|
35
|
+
# @param job [Job] job
|
36
|
+
# @return [State] state object
|
37
|
+
def self.find_by_job(job)
|
38
|
+
where(
|
39
|
+
name: job.name,
|
40
|
+
params: job.params_str,
|
41
|
+
digest: job.digest
|
42
|
+
).first
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.find_or_initialize_by_job(job)
|
46
|
+
find_by_job(job) || \
|
47
|
+
new(name: job.name, params: job.params_str, digest: job.digest)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Assume the job to be successed
|
51
|
+
#
|
52
|
+
# If the force option is true, update the state to SUCCESSED even if the
|
53
|
+
# task is running.
|
54
|
+
#
|
55
|
+
# @param job [Job] job
|
56
|
+
# @param force [Boolean] force update the state
|
57
|
+
# @return [State] state object
|
58
|
+
def self.assume(job, force: false)
|
59
|
+
StateManager.database.transaction do
|
60
|
+
state = find_or_initialize_by_job(job)
|
61
|
+
|
62
|
+
return state if state.successed?
|
63
|
+
state.check_running! unless force
|
64
|
+
|
65
|
+
state.state = SUCCESSED
|
66
|
+
state.start_time = Time.now
|
67
|
+
state.end_time = Time.now
|
68
|
+
state.save
|
69
|
+
state
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# Delete the state.
|
74
|
+
#
|
75
|
+
# @param force [Boolean] If true, delete the state even if running
|
76
|
+
# @return [void]
|
77
|
+
def revoke(force: false)
|
78
|
+
StateManager.database.transaction do
|
79
|
+
unless force
|
80
|
+
refresh
|
81
|
+
check_running!
|
82
|
+
end
|
83
|
+
delete
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# check if the state is running
|
88
|
+
def check_running!
|
89
|
+
raise AlreadyRunningError, 'job already running' if running?
|
90
|
+
end
|
91
|
+
|
92
|
+
def running?
|
93
|
+
state == RUNNING
|
94
|
+
end
|
95
|
+
|
96
|
+
def successed?
|
97
|
+
state == SUCCESSED
|
98
|
+
end
|
99
|
+
|
100
|
+
def failed?
|
101
|
+
state == FAILED
|
102
|
+
end
|
103
|
+
|
104
|
+
def state_label
|
105
|
+
case state
|
106
|
+
when 1 then 'RUNNING'
|
107
|
+
when 2 then 'SUCCESSED'
|
108
|
+
when 3 then 'FAILED'
|
109
|
+
else 'UNKNOWN'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def exec_time
|
114
|
+
return unless start_time && end_time
|
115
|
+
end_time - start_time
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
data/lib/abid/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abid
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.0.pre.alpha.
|
4
|
+
version: 0.3.0.pre.alpha.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Hikaru Ojima
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-12-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -95,7 +95,7 @@ dependencies:
|
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '0'
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
98
|
+
name: sequel
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - ">="
|
@@ -109,7 +109,7 @@ dependencies:
|
|
109
109
|
- !ruby/object:Gem::Version
|
110
110
|
version: '0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
112
|
+
name: sqlite3
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
115
|
- - ">="
|
@@ -123,7 +123,7 @@ dependencies:
|
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
|
-
name:
|
126
|
+
name: rbtree
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
129
|
- - ">="
|
@@ -137,7 +137,7 @@ dependencies:
|
|
137
137
|
- !ruby/object:Gem::Version
|
138
138
|
version: '0'
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
140
|
+
name: thor
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
@@ -155,6 +155,7 @@ email:
|
|
155
155
|
- amijo4rihaku@gmail.com
|
156
156
|
executables:
|
157
157
|
- abid
|
158
|
+
- abidsc
|
158
159
|
extensions: []
|
159
160
|
extra_rdoc_files: []
|
160
161
|
files:
|
@@ -170,14 +171,23 @@ files:
|
|
170
171
|
- bin/console
|
171
172
|
- bin/setup
|
172
173
|
- exe/abid
|
174
|
+
- exe/abidsc
|
173
175
|
- lib/Abidfile.rb
|
174
176
|
- lib/abid.rb
|
175
177
|
- lib/abid/abid_module.rb
|
176
178
|
- lib/abid/application.rb
|
179
|
+
- lib/abid/cli.rb
|
180
|
+
- lib/abid/cli/assume.rb
|
181
|
+
- lib/abid/cli/list.rb
|
182
|
+
- lib/abid/cli/migrate.rb
|
183
|
+
- lib/abid/cli/revoke.rb
|
184
|
+
- lib/abid/cli/table_formatter.rb
|
177
185
|
- lib/abid/config.rb
|
178
186
|
- lib/abid/dsl_definition.rb
|
179
187
|
- lib/abid/error.rb
|
188
|
+
- lib/abid/job.rb
|
180
189
|
- lib/abid/mixin_task.rb
|
190
|
+
- lib/abid/params_format.rb
|
181
191
|
- lib/abid/params_parser.rb
|
182
192
|
- lib/abid/play.rb
|
183
193
|
- lib/abid/play_core.rb
|
@@ -185,9 +195,11 @@ files:
|
|
185
195
|
- lib/abid/rake_extensions/task.rb
|
186
196
|
- lib/abid/session.rb
|
187
197
|
- lib/abid/state.rb
|
198
|
+
- lib/abid/state_manager.rb
|
199
|
+
- lib/abid/state_manager/database.rb
|
200
|
+
- lib/abid/state_manager/state.rb
|
188
201
|
- lib/abid/task.rb
|
189
202
|
- lib/abid/task_manager.rb
|
190
|
-
- lib/abid/tasks/core.rake
|
191
203
|
- lib/abid/version.rb
|
192
204
|
- lib/abid/waiter.rb
|
193
205
|
- lib/abid/worker.rb
|
data/lib/abid/tasks/core.rake
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
namespace :state do
|
2
|
-
task default: :list
|
3
|
-
|
4
|
-
desc 'Show play histories'
|
5
|
-
play :list, extends: Abid::Play do
|
6
|
-
set :volatile, true
|
7
|
-
|
8
|
-
param :started_after, type: :time, default: nil
|
9
|
-
param :started_before, type: :time, default: nil
|
10
|
-
|
11
|
-
def run
|
12
|
-
states = Abid::State.list(
|
13
|
-
started_after: started_after,
|
14
|
-
started_before: started_before
|
15
|
-
)
|
16
|
-
|
17
|
-
table = states.map do |state|
|
18
|
-
params = state[:params].map do |k, v|
|
19
|
-
v.to_s =~ /\s/ ? "#{k}='#{v}'" : "#{k}=#{v}"
|
20
|
-
end.join(' ')
|
21
|
-
|
22
|
-
if state[:start_time] && state[:end_time]
|
23
|
-
time_diff = (state[:end_time] - state[:start_time]).to_i
|
24
|
-
if time_diff >= 60*60*24
|
25
|
-
exec_time = time_diff.div(60*60*24).to_s + " days"
|
26
|
-
else
|
27
|
-
exec_time = Time.at(time_diff).utc.strftime('%H:%M:%S').to_s
|
28
|
-
end
|
29
|
-
else
|
30
|
-
exec_time = ''
|
31
|
-
end
|
32
|
-
|
33
|
-
[
|
34
|
-
state[:id].to_s,
|
35
|
-
state[:state].to_s,
|
36
|
-
state[:name],
|
37
|
-
params,
|
38
|
-
state[:start_time].to_s,
|
39
|
-
state[:end_time].to_s,
|
40
|
-
exec_time
|
41
|
-
]
|
42
|
-
end
|
43
|
-
|
44
|
-
header = %w(id state name params start_time end_time exec_time)
|
45
|
-
|
46
|
-
tab_width = header.each_with_index.map do |c, i|
|
47
|
-
[c.length, table.map { |row| row[i].length }.max || 0].max
|
48
|
-
end
|
49
|
-
|
50
|
-
header.each_with_index do |c, i|
|
51
|
-
print c.ljust(tab_width[i] + 2)
|
52
|
-
end
|
53
|
-
puts
|
54
|
-
|
55
|
-
header.each_with_index do |_, i|
|
56
|
-
print '-' * (tab_width[i] + 2)
|
57
|
-
end
|
58
|
-
puts
|
59
|
-
|
60
|
-
table.map do |row|
|
61
|
-
row.each_with_index do |v, i|
|
62
|
-
print v.ljust(tab_width[i] + 2)
|
63
|
-
end
|
64
|
-
puts
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
desc 'Insert play history'
|
70
|
-
task :assume, [:task] do |_t, args|
|
71
|
-
task = Rake.application[args[:task]]
|
72
|
-
state = Abid::State.find(task)
|
73
|
-
state.assume
|
74
|
-
end
|
75
|
-
|
76
|
-
desc 'Delete play history'
|
77
|
-
task :revoke do |_t, args|
|
78
|
-
args.extras.each { |id| Abid::State.revoke(id) }
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
namespace :db do
|
83
|
-
desc 'Run migrations'
|
84
|
-
task :migrate, [:version] do |_t, args|
|
85
|
-
migrations_path = File.expand_path('../../../../migrations', __FILE__)
|
86
|
-
|
87
|
-
require 'sequel'
|
88
|
-
Sequel.extension :migration
|
89
|
-
db = Rake.application.database
|
90
|
-
if args[:version]
|
91
|
-
puts "Migrating to version #{args[:version]}"
|
92
|
-
Sequel::Migrator.run(db, migrations_path, target: args[:version].to_i)
|
93
|
-
else
|
94
|
-
puts 'Migrating to latest'
|
95
|
-
Sequel::Migrator.run(db, migrations_path)
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|