abid 0.3.0.pre.alpha.1 → 0.3.0.pre.alpha.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/ojima-h/abid.svg?branch=master)](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
|