kindly 0.0.6 → 0.1.0
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/.gitignore +1 -0
- data/Rakefile +31 -0
- data/kindly.gemspec +3 -1
- data/lib/kindly/db.rb +150 -0
- data/lib/kindly/jobs/do_nothing.rb +15 -0
- data/lib/kindly/jobs/test_job.rb +15 -0
- data/lib/kindly/jobs/test_job_with_input.rb +17 -0
- data/lib/kindly/jobs/test_job_with_output.rb +17 -0
- data/lib/kindly/queue.rb +56 -0
- data/lib/kindly/registry.rb +30 -0
- data/lib/kindly/requester.rb +17 -0
- data/lib/kindly/runner.rb +43 -16
- data/lib/kindly/version.rb +1 -1
- data/lib/kindly.rb +13 -28
- data/test/fixtures/jobs/fail.rb +15 -0
- data/test/kindly/migration_test.rb +114 -108
- data/test/kindly/registry_test.rb +24 -0
- data/test/kindly/runner_test.rb +68 -35
- data/test/kindly_test.rb +50 -17
- metadata +45 -16
- data/bin/kindly +0 -23
- data/lib/kindly/handlers/do_nothing.rb +0 -19
- data/lib/kindly/handlers.rb +0 -29
- data/lib/kindly/migration.rb +0 -37
- data/test/fixtures/pending/one.json +0 -3
- data/test/fixtures/pending/two.json +0 -3
- data/test/kindly/handlers_test.rb +0 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e6a32b5242cdaa718ac34d75b5f0d4c216faa09
|
4
|
+
data.tar.gz: 6b9b08dc1282bd73a313e2841028eecaacc19433
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 62eabc59ec779bed01bbb72a4501d5b1492d9968dcfa7a70cd473558ac75863fe2fdf6a72d143ead6a590eac236e667e5b06ec90358444c6570394f6e6d5a87a
|
7
|
+
data.tar.gz: a3447e56fd04edef3ec992f85d362ad651a970b08291907367f18ed49ac1272c9b827a82042a46fd8866228f6d840518362da04127d9b81c627558d391e72388
|
data/.gitignore
CHANGED
data/Rakefile
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'bundler/gem_tasks'
|
2
2
|
require 'rake/testtask'
|
3
|
+
require 'kindly'
|
3
4
|
|
4
5
|
Rake::TestTask.new do |test|
|
5
6
|
test.libs << 'lib' << 'test'
|
@@ -7,4 +8,34 @@ Rake::TestTask.new do |test|
|
|
7
8
|
test.verbose = true
|
8
9
|
end
|
9
10
|
|
11
|
+
desc 'Request test_job'
|
12
|
+
task :request_test_job do
|
13
|
+
Kindly.request :test_job
|
14
|
+
end
|
15
|
+
|
16
|
+
desc 'Request test_job_with_input'
|
17
|
+
task :request_test_job_with_input do
|
18
|
+
Kindly.request :test_job_with_input, 'hello'
|
19
|
+
end
|
20
|
+
|
21
|
+
desc 'Request test_job_with_output'
|
22
|
+
task :request_test_job_with_output do
|
23
|
+
Kindly.request :test_job_with_output
|
24
|
+
end
|
25
|
+
|
26
|
+
desc 'Run test_job'
|
27
|
+
task :run_test_job do
|
28
|
+
Kindly.run :test_job
|
29
|
+
end
|
30
|
+
|
31
|
+
desc 'Run test_job_with_input'
|
32
|
+
task :run_test_job_with_input do
|
33
|
+
Kindly.run :test_job_with_input
|
34
|
+
end
|
35
|
+
|
36
|
+
desc 'Run test_job_with_output'
|
37
|
+
task :run_test_job_with_output do
|
38
|
+
Kindly.run :test_job_with_output
|
39
|
+
end
|
40
|
+
|
10
41
|
task :default => :test
|
data/kindly.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.version = Kindly::VERSION
|
10
10
|
spec.authors = ['Greg Scott']
|
11
11
|
spec.email = ['i@gregoryjscott.com']
|
12
|
-
spec.summary = %q{Kindly run
|
12
|
+
spec.summary = %q{Kindly run jobs of any kind.}
|
13
13
|
spec.homepage = 'https://github.com/gregoryjscott/kindly'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
@@ -19,9 +19,11 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'mercenary', '~> 0.3'
|
22
|
+
spec.add_runtime_dependency 'aws-sdk', '~> 2'
|
22
23
|
|
23
24
|
spec.add_development_dependency 'bundler', '~> 1.7'
|
24
25
|
spec.add_development_dependency 'rake', '~> 10.0'
|
25
26
|
spec.add_development_dependency 'mocha', '~> 1.1'
|
27
|
+
spec.add_development_dependency 'minitest', '~> 5.8'
|
26
28
|
|
27
29
|
end
|
data/lib/kindly/db.rb
ADDED
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'kindly'
|
2
|
+
require 'aws-sdk'
|
3
|
+
|
4
|
+
module Kindly
|
5
|
+
class DB
|
6
|
+
|
7
|
+
DEFAULTS = {
|
8
|
+
:table_names => {
|
9
|
+
:data => 'job-data',
|
10
|
+
:pending => 'job-pending',
|
11
|
+
:running => 'job-running',
|
12
|
+
:completed => 'job-completed',
|
13
|
+
:failed => 'job-failed'
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
@config = DEFAULTS.merge(options)
|
19
|
+
@db = Aws::DynamoDB::Client.new(region: 'us-west-2')
|
20
|
+
end
|
21
|
+
|
22
|
+
def user
|
23
|
+
@user ||= Aws::IAM::CurrentUser.new(region: 'us-west-2')
|
24
|
+
end
|
25
|
+
|
26
|
+
def insert_job(job_name, input)
|
27
|
+
item = {
|
28
|
+
'JobId' => SecureRandom.uuid,
|
29
|
+
'JobName' => job_name,
|
30
|
+
'RequestedAt' => Time.now.to_s,
|
31
|
+
'RequestedBy' => user.user_name
|
32
|
+
}
|
33
|
+
|
34
|
+
unless input.empty?
|
35
|
+
data = insert_job_data(input)
|
36
|
+
item['InputDataId'] = data['JobDataId']
|
37
|
+
end
|
38
|
+
|
39
|
+
@db.put_item({ table_name: 'job-pending', item: item })
|
40
|
+
item
|
41
|
+
end
|
42
|
+
|
43
|
+
def insert_job_data(data)
|
44
|
+
item = {
|
45
|
+
'JobDataId' => SecureRandom.uuid,
|
46
|
+
'Data' => data,
|
47
|
+
'CreatedAt' => Time.now.to_s
|
48
|
+
}
|
49
|
+
@db.put_item({ table_name: 'job-data', item: item })
|
50
|
+
item
|
51
|
+
end
|
52
|
+
|
53
|
+
def fetch_job(job_name, job_id)
|
54
|
+
job = Registry.find(job_name)
|
55
|
+
fields = fetch_job_fields(job_id)
|
56
|
+
add_fields_to_job(job, fields)
|
57
|
+
|
58
|
+
if job.fields.has_key?('InputDataId')
|
59
|
+
input = fetch_job_data(job.fields['InputDataId'])
|
60
|
+
job.input = input
|
61
|
+
end
|
62
|
+
job
|
63
|
+
end
|
64
|
+
|
65
|
+
def update_job_status(job, job_status)
|
66
|
+
case job_status
|
67
|
+
when :running
|
68
|
+
delete_job_status(job, :pending)
|
69
|
+
when :completed, :failed
|
70
|
+
delete_job_status(job, :running)
|
71
|
+
else
|
72
|
+
raise "#{new_status} is invalid for job #{job.fields['JobId']}."
|
73
|
+
end
|
74
|
+
insert_job_status(job, job_status)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def fetch_job_fields(job_id)
|
80
|
+
response = @db.scan({
|
81
|
+
table_name: @config[:table_names][:pending],
|
82
|
+
select: 'ALL_ATTRIBUTES',
|
83
|
+
filter_expression: "JobId = :job_id",
|
84
|
+
expression_attribute_values: { ":job_id": job_id }
|
85
|
+
})
|
86
|
+
|
87
|
+
raise no_jobs(job_id) if response.items.length == 0
|
88
|
+
raise too_many_jobs(job_id) if response.items.length > 1
|
89
|
+
response.items[0]
|
90
|
+
end
|
91
|
+
|
92
|
+
def fetch_job_data(job_data_id)
|
93
|
+
response = @db.scan({
|
94
|
+
table_name: @config[:table_names][:data],
|
95
|
+
select: 'ALL_ATTRIBUTES',
|
96
|
+
filter_expression: "JobDataId = :job_data_id",
|
97
|
+
expression_attribute_values: { ":job_data_id": job_data_id }
|
98
|
+
})
|
99
|
+
|
100
|
+
raise no_job_data(job_data_id) if response.items.length == 0
|
101
|
+
raise too_many_job_data(job_data_id) if response.items.length > 1
|
102
|
+
response.items[0]['Data']
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_fields_to_job(job, fields)
|
106
|
+
job.instance_eval do
|
107
|
+
def fields
|
108
|
+
@fields
|
109
|
+
end
|
110
|
+
|
111
|
+
def fields=(val)
|
112
|
+
@fields = val
|
113
|
+
end
|
114
|
+
end
|
115
|
+
job.fields = fields
|
116
|
+
end
|
117
|
+
|
118
|
+
def insert_job_status(job, job_status)
|
119
|
+
job.fields['CreatedAt'] = Time.now.to_s
|
120
|
+
@db.put_item({
|
121
|
+
table_name: @config[:table_names][job_status],
|
122
|
+
item: job.fields
|
123
|
+
})
|
124
|
+
end
|
125
|
+
|
126
|
+
def delete_job_status(job, job_status)
|
127
|
+
@db.delete_item({
|
128
|
+
table_name: @config[:table_names][job_status],
|
129
|
+
key: { 'JobId': job.fields['JobId'] }
|
130
|
+
})
|
131
|
+
end
|
132
|
+
|
133
|
+
def no_jobs(job_id)
|
134
|
+
"No pending jobs found for #{job_id}."
|
135
|
+
end
|
136
|
+
|
137
|
+
def too_many_jobs(job_id)
|
138
|
+
"Found too many pending job records for #{job_id}."
|
139
|
+
end
|
140
|
+
|
141
|
+
def no_job_data(job_data_id)
|
142
|
+
"No job data found for #{job_data_id}."
|
143
|
+
end
|
144
|
+
|
145
|
+
def too_many_job_data(job_data_id)
|
146
|
+
"Found too many job data records for #{job_data_id}."
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
end
|
data/lib/kindly/queue.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'kindly'
|
2
|
+
require 'aws-sdk'
|
3
|
+
|
4
|
+
module Kindly
|
5
|
+
class Queue
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@sqs = Aws::SQS::Client.new(region: 'us-west-2')
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(job_name, job_id)
|
12
|
+
@sqs.send_message({
|
13
|
+
queue_url: queue_url(job_name),
|
14
|
+
message_body: "#{@job_name} has been requested.",
|
15
|
+
message_attributes: {
|
16
|
+
'JobId' => {
|
17
|
+
string_value: job_id,
|
18
|
+
data_type: "String",
|
19
|
+
},
|
20
|
+
},
|
21
|
+
})
|
22
|
+
end
|
23
|
+
|
24
|
+
def pop(job_name)
|
25
|
+
response = @sqs.receive_message({
|
26
|
+
queue_url: queue_url(job_name),
|
27
|
+
message_attribute_names: ['JobId'],
|
28
|
+
max_number_of_messages: 1
|
29
|
+
})
|
30
|
+
raise too_many_messages(job_name) if response.messages.length > 1
|
31
|
+
return nil if response.messages.length == 0
|
32
|
+
|
33
|
+
message = response.messages[0]
|
34
|
+
job_id = message.message_attributes['JobId'].string_value
|
35
|
+
|
36
|
+
@sqs.delete_message({
|
37
|
+
queue_url: queue_url(job_name),
|
38
|
+
receipt_handle: message.receipt_handle
|
39
|
+
})
|
40
|
+
|
41
|
+
job_id
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def queue_url(job_name)
|
47
|
+
queue = job_name.to_s.gsub('_', '-')
|
48
|
+
"https://sqs.us-west-2.amazonaws.com/529271381487/#{queue}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def too_many_messages(job_name)
|
52
|
+
"Found too many messages for #{job_name}."
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'kindly'
|
2
|
+
|
3
|
+
module Kindly
|
4
|
+
module Registry
|
5
|
+
|
6
|
+
def self.register(job_name, job)
|
7
|
+
@@jobs ||= {}
|
8
|
+
@@jobs[job_name.to_sym] = job
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.unregister(job_name)
|
12
|
+
@@jobs.delete(job_name.to_sym)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.find(job_name)
|
16
|
+
raise no_job(job_name) unless registered?(job_name)
|
17
|
+
@@jobs[job_name.to_sym]
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.no_job(job_name)
|
23
|
+
"No job registered with name #{job_name}."
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.registered?(job_name)
|
27
|
+
@@jobs.has_key?(job_name.to_sym)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'kindly'
|
2
|
+
|
3
|
+
module Kindly
|
4
|
+
class Requester
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@queue = Queue.new
|
8
|
+
@db = DB.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def request(job_name, input = {})
|
12
|
+
job = @db.insert_job(job_name, input)
|
13
|
+
@queue.add(job_name, job['JobId'])
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
data/lib/kindly/runner.rb
CHANGED
@@ -3,28 +3,60 @@ require 'kindly'
|
|
3
3
|
module Kindly
|
4
4
|
class Runner
|
5
5
|
|
6
|
-
def initialize
|
7
|
-
@
|
6
|
+
def initialize
|
7
|
+
@queue = Queue.new
|
8
|
+
@db = DB.new
|
8
9
|
end
|
9
10
|
|
10
|
-
def run(
|
11
|
-
|
11
|
+
def run(job_name)
|
12
|
+
job_id = @queue.pop(job_name)
|
13
|
+
if job_id.nil?
|
14
|
+
puts "No pending requests for #{job_name}."
|
15
|
+
return false
|
16
|
+
end
|
17
|
+
|
18
|
+
job = @db.fetch_job(job_name, job_id)
|
19
|
+
run_job(job)
|
20
|
+
|
21
|
+
job.respond_to?(:output) ? job.output : {}
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def run_job(job)
|
27
|
+
job.fields['RanBy'] = @db.user.user_name
|
28
|
+
job.fields['StartedAt'] = Time.now.to_s
|
29
|
+
@db.update_job_status(job, :running)
|
30
|
+
|
31
|
+
failed = false
|
32
|
+
log = capture_stdout do
|
33
|
+
puts "#{job.fields['JobId']} started at #{job.fields['StartedAt']}."
|
12
34
|
begin
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
rescue Exception
|
35
|
+
job.run
|
36
|
+
rescue
|
37
|
+
failed = true
|
17
38
|
puts $!, $@
|
18
|
-
|
39
|
+
end
|
40
|
+
job.fields['FinishedAt'] = Time.now.to_s
|
41
|
+
puts "#{job.fields['JobId']} finished at #{job.fields['FinishedAt']}."
|
42
|
+
|
43
|
+
if job.respond_to? :output
|
44
|
+
data = @db.insert_job_data(job.output)
|
45
|
+
job.fields['OutputDataId'] = data['JobDataId']
|
19
46
|
end
|
20
47
|
end
|
48
|
+
job.fields['Log'] = log.split("\n")
|
21
49
|
|
22
|
-
|
50
|
+
if failed
|
51
|
+
@db.update_job_status(job, :failed)
|
52
|
+
else
|
53
|
+
@db.update_job_status(job, :completed)
|
54
|
+
end
|
23
55
|
end
|
24
56
|
|
25
57
|
private
|
26
58
|
|
27
|
-
def
|
59
|
+
def capture_stdout
|
28
60
|
begin
|
29
61
|
old_stdout = $stdout
|
30
62
|
$stdout = StringIO.new('', 'w')
|
@@ -35,10 +67,5 @@ module Kindly
|
|
35
67
|
end
|
36
68
|
end
|
37
69
|
|
38
|
-
def write_log_file(migration, output)
|
39
|
-
filename = "#{migration.filename}.log"
|
40
|
-
File.open(filename, 'w') { |file| file.write(output) }
|
41
|
-
end
|
42
|
-
|
43
70
|
end
|
44
71
|
end
|
data/lib/kindly/version.rb
CHANGED
data/lib/kindly.rb
CHANGED
@@ -1,38 +1,23 @@
|
|
1
|
-
require 'kindly/
|
1
|
+
require 'kindly/db'
|
2
|
+
require 'kindly/queue'
|
3
|
+
require 'kindly/registry'
|
4
|
+
require 'kindly/requester'
|
2
5
|
require 'kindly/runner'
|
3
|
-
require 'kindly/handlers'
|
4
|
-
require 'kindly/handlers/do_nothing'
|
5
6
|
require 'kindly/version'
|
7
|
+
require 'kindly/jobs/do_nothing'
|
8
|
+
require 'kindly/jobs/test_job'
|
9
|
+
require 'kindly/jobs/test_job_with_input'
|
10
|
+
require 'kindly/jobs/test_job_with_output'
|
11
|
+
require 'aws-sdk'
|
6
12
|
|
7
13
|
module Kindly
|
8
14
|
|
9
|
-
def self.run(
|
10
|
-
|
11
|
-
puts "Kindly run #{handler_name} in #{source} directory."
|
12
|
-
|
13
|
-
handler = Handlers.find(handler_name)
|
14
|
-
runner = Runner.new(handler)
|
15
|
-
migrations = find_migrations(handler.ext)
|
16
|
-
|
17
|
-
puts "No migrations found for #{handler_name} handler." if migrations.empty?
|
18
|
-
migrations.each { |migration| runner.run(migration) }
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.source
|
22
|
-
@@source
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
def self.find_migrations(ext)
|
28
|
-
filenames = Dir[File.join(source, 'pending', "*.#{ext}")]
|
29
|
-
build_migrations(filenames)
|
15
|
+
def self.run(job_name)
|
16
|
+
Runner.new.run(job_name)
|
30
17
|
end
|
31
18
|
|
32
|
-
def self.
|
33
|
-
|
34
|
-
filenames.each { |filename| migrations << Migration.new(filename) }
|
35
|
-
migrations
|
19
|
+
def self.request(job_name, input = {})
|
20
|
+
Requester.new.request(job_name, input)
|
36
21
|
end
|
37
22
|
|
38
23
|
end
|
@@ -1,108 +1,114 @@
|
|
1
|
-
require 'kindly'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
require 'mocha/mini_test'
|
4
|
-
|
5
|
-
describe '
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
1
|
+
# require 'kindly'
|
2
|
+
# require 'minitest/autorun'
|
3
|
+
# require 'mocha/mini_test'
|
4
|
+
#
|
5
|
+
# describe 'Job' do
|
6
|
+
# let(:source) { File.join('test', 'fixtures', 'jobs', 'read_json') }
|
7
|
+
# let(:config) {
|
8
|
+
# {
|
9
|
+
# :source => source,
|
10
|
+
# :pending => File.join(source, 'pending'),
|
11
|
+
# :running => File.join(source, 'running'),
|
12
|
+
# :completed => File.join(source, 'completed'),
|
13
|
+
# :failed => File.join(source, 'failed')
|
14
|
+
# }
|
15
|
+
# }
|
16
|
+
# let(:filename) { File.join(config[:pending], 'one.json') }
|
17
|
+
# let(:job) { job = Kindly::Job.new(filename) }
|
18
|
+
#
|
19
|
+
# before(:each) do
|
20
|
+
# Kindly.stubs(:config).returns(config)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# describe 'logs' do
|
24
|
+
# before(:each) { job.stubs(:move) }
|
25
|
+
#
|
26
|
+
# it 'when running' do
|
27
|
+
# output = capture_output { job.running! }
|
28
|
+
# assert running?(output)
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# it 'when completed' do
|
32
|
+
# output = capture_output { job.completed! }
|
33
|
+
# assert completed?(output)
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# it 'when failed' do
|
37
|
+
# output = capture_output { job.failed! }
|
38
|
+
# assert failed?(output)
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# def running?(output)
|
42
|
+
# output.include?("#{filename} running")
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# def completed?(output)
|
46
|
+
# output.include?("#{filename} completed") && !failed?(output)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# def failed?(output)
|
50
|
+
# output.include?("#{filename} failed") && !completed?(output)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# describe 'moves files' do
|
56
|
+
#
|
57
|
+
# let(:tmp_dir) { File.join(source, 'tmp') }
|
58
|
+
#
|
59
|
+
# before(:each) do
|
60
|
+
# FileUtils.mkdir(tmp_dir) unless Dir.exist?(tmp_dir)
|
61
|
+
# Dir[File.join(source, 'pending', '*.json')].each do |file|
|
62
|
+
# FileUtils.cp(file, tmp_dir)
|
63
|
+
# end
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# after(:each) do
|
67
|
+
# restore_pending
|
68
|
+
# remove(config[:running])
|
69
|
+
# remove(config[:completed])
|
70
|
+
# remove(config[:failed])
|
71
|
+
# remove(tmp_dir)
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# it 'when running' do
|
75
|
+
# capture_output { job.running! }
|
76
|
+
# assert File.exist?(File.join(config[:running], File.basename(filename)))
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# it 'when completed' do
|
80
|
+
# capture_output { job.completed! }
|
81
|
+
# assert File.exist?(File.join(config[:completed], File.basename(filename)))
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# it 'when failed' do
|
85
|
+
# capture_output { job.failed! }
|
86
|
+
# assert File.exist?(File.join(config[:failed], File.basename(filename)))
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# def restore_pending
|
90
|
+
# FileUtils.rm_r(config[:pending])
|
91
|
+
# FileUtils.mkdir(config[:pending])
|
92
|
+
# Dir[File.join(tmp_dir, '*.json')].each do |file|
|
93
|
+
# FileUtils.cp(file, config[:pending])
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# def remove(dir)
|
98
|
+
# FileUtils.rm_r(dir) if Dir.exist?(dir)
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# def capture_output
|
104
|
+
# begin
|
105
|
+
# old_stdout = $stdout
|
106
|
+
# $stdout = StringIO.new('', 'w')
|
107
|
+
# yield
|
108
|
+
# $stdout.string
|
109
|
+
# ensure
|
110
|
+
# $stdout = old_stdout
|
111
|
+
# end
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'kindly'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
describe 'Kindly::Registry' do
|
5
|
+
|
6
|
+
let(:registry) { Kindly::Registry }
|
7
|
+
let(:do_nothing) { Kindly::Jobs::DoNothing.new }
|
8
|
+
|
9
|
+
it 'throws if job is not registered' do
|
10
|
+
assert_raises(RuntimeError) { registry.find(:missing) }
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'allows job to be registered' do
|
14
|
+
registry.register :do_nothing, do_nothing
|
15
|
+
assert registry.find(:do_nothing) == do_nothing
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'allows job to be unregistered' do
|
19
|
+
registry.register :do_nothing, do_nothing
|
20
|
+
registry.unregister :do_nothing
|
21
|
+
assert_raises(RuntimeError) { registry.find(:do_nothing) }
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
data/test/kindly/runner_test.rb
CHANGED
@@ -1,44 +1,77 @@
|
|
1
1
|
require 'kindly'
|
2
2
|
require 'minitest/autorun'
|
3
3
|
require 'mocha/mini_test'
|
4
|
-
require '
|
4
|
+
require 'fixtures/jobs/fail'
|
5
5
|
|
6
|
-
describe 'Runner' do
|
7
6
|
|
8
|
-
|
9
|
-
let(:migration) { migration = Kindly::Migration.new(filename) }
|
10
|
-
let(:runner) { Kindly::Runner.new(Kindly::Handlers::DoNothing.new) }
|
7
|
+
describe 'Kindly::Runner' do
|
11
8
|
|
12
|
-
|
13
|
-
|
14
|
-
|
9
|
+
# let(:job) { mock() }
|
10
|
+
#
|
11
|
+
# before(:each) do
|
12
|
+
# job.stubs(:fetch)
|
13
|
+
# job.stubs(:data)
|
14
|
+
# job.stubs(:running!)
|
15
|
+
# end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
migration.stubs(:load).raises
|
28
|
-
migration.expects(:running!).once
|
29
|
-
runner.run(migration)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'sets migration to completed if Runner succeeds' do
|
33
|
-
migration.expects(:completed!).once
|
34
|
-
migration.expects(:failed!).never
|
35
|
-
runner.run(migration)
|
36
|
-
end
|
37
|
-
|
38
|
-
it 'writes log file' do
|
39
|
-
runner.run(migration)
|
40
|
-
expected_log_file = "#{migration.filename}.log"
|
41
|
-
assert File.exist?(expected_log_file)
|
42
|
-
end
|
17
|
+
# it 'returns if the job is a success' do
|
18
|
+
# job.stubs(:completed!)
|
19
|
+
# result = Kindly::Runner.new(:do_nothing).run(job)
|
20
|
+
# assert result[:success]
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# it 'returns if the job is not a success' do
|
24
|
+
# job.stubs(:failed!)
|
25
|
+
# result = Kindly::Runner.new(:fail).run(job)
|
26
|
+
# refute result[:success]
|
27
|
+
# end
|
43
28
|
|
44
29
|
end
|
30
|
+
|
31
|
+
# require 'kindly'
|
32
|
+
# require 'minitest/autorun'
|
33
|
+
# require 'mocha/mini_test'
|
34
|
+
# require 'fileutils'
|
35
|
+
# require 'fixtures/handlers/fail'
|
36
|
+
#
|
37
|
+
# describe 'Runner' do
|
38
|
+
#
|
39
|
+
# let(:read_json) { File.join('test', 'fixtures', 'jobs', 'read_json') }
|
40
|
+
# let(:filename) { File.join(read_json, 'pending', 'one.json') }
|
41
|
+
# let(:job) { job = Kindly::Job.new(filename) }
|
42
|
+
# let(:runner) { Kindly::Runner.new(Kindly::Handlers::DoNothing.new) }
|
43
|
+
# let(:runner_that_fails) { Kindly::Runner.new(Fixtures::Handlers::Fail.new) }
|
44
|
+
#
|
45
|
+
# before(:each) do
|
46
|
+
# job.stubs(:move)
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# after(:each) do
|
50
|
+
# logfile = "#{filename}.log"
|
51
|
+
# FileUtils.rm(logfile) if File.exist?(logfile)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# it 'sets job to running' do
|
55
|
+
# job.expects(:running!).once
|
56
|
+
# runner.run(job)
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# it 'sets job to completed if job succeeds' do
|
60
|
+
# job.expects(:completed!).once
|
61
|
+
# job.expects(:failed!).never
|
62
|
+
# runner.run(job)
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# it 'sets job to failed if job fails' do
|
66
|
+
# job.expects(:completed!).never
|
67
|
+
# job.expects(:failed!).once
|
68
|
+
# runner_that_fails.run(job)
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
# it 'writes log file' do
|
72
|
+
# runner.run(job)
|
73
|
+
# expected_log_file = "#{job.filename}.log"
|
74
|
+
# assert File.exist?(expected_log_file)
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# end
|
data/test/kindly_test.rb
CHANGED
@@ -1,27 +1,60 @@
|
|
1
1
|
require 'kindly'
|
2
2
|
require 'minitest/autorun'
|
3
|
-
require 'mocha/mini_test'
|
3
|
+
# require 'mocha/mini_test'
|
4
4
|
|
5
5
|
describe 'Kindly' do
|
6
6
|
|
7
|
-
it '
|
8
|
-
|
9
|
-
|
10
|
-
capture_output { Kindly.run(:do_nothing) }
|
11
|
-
end
|
7
|
+
# it 'works' do
|
8
|
+
# Kindly.request :test_job
|
9
|
+
# end
|
12
10
|
|
13
|
-
it '
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
# it 'returns true if message was handled' do
|
12
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
13
|
+
# Kindly::Queue.any_instance.stubs(:pop).returns([1, { hello: 'world' }])
|
14
|
+
# Kindly::Queue.any_instance.stubs(:delete)
|
15
|
+
# assert Kindly.run(:do_nothing)
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# it 'returns false if message was not found' do
|
19
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
20
|
+
# Kindly::Queue.any_instance.stubs(:pop).returns([nil, nil])
|
21
|
+
# Kindly::Queue.any_instance.stubs(:delete)
|
22
|
+
# capture_output { refute Kindly.run(:do_nothing) }
|
23
|
+
# end
|
18
24
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
+
# let(:read_json) { File.join('test', 'fixtures', 'jobs', 'read_json') }
|
26
|
+
# let(:pending) { File.join(read_json, 'pending') }
|
27
|
+
#
|
28
|
+
# it 'runs given handler name' do
|
29
|
+
# Kindly.stubs(:config).returns(:source => read_json)
|
30
|
+
# Kindly::Runner.any_instance.expects(:run).twice
|
31
|
+
# capture_output { Kindly.run(:do_nothing) }
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# it 'defaults source to _jobs' do
|
35
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
36
|
+
# capture_output { Kindly.run(:do_nothing) }
|
37
|
+
# assert Kindly.config[:source] == '_jobs'
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# it 'allows source to be overridden' do
|
41
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
42
|
+
# capture_output { Kindly.run(:do_nothing, :source => read_json) }
|
43
|
+
# assert Kindly.config[:source] == read_json
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# it 'defaults pending to _jobs/pending' do
|
47
|
+
# expected = File.join('_jobs', 'pending')
|
48
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
49
|
+
# capture_output { Kindly.run(:do_nothing) }
|
50
|
+
# assert Kindly.config[:pending] == expected
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# it 'allows pending to be overridden' do
|
54
|
+
# Kindly::Runner.any_instance.stubs(:run)
|
55
|
+
# capture_output { Kindly.run(:do_nothing, :pending => pending) }
|
56
|
+
# assert_equal pending, Kindly.config[:pending]
|
57
|
+
# end
|
25
58
|
|
26
59
|
def capture_output
|
27
60
|
begin
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kindly
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Greg Scott
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mercenary
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: aws-sdk
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: bundler
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -66,11 +80,24 @@ dependencies:
|
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '1.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '5.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '5.8'
|
69
97
|
description:
|
70
98
|
email:
|
71
99
|
- i@gregoryjscott.com
|
72
|
-
executables:
|
73
|
-
- kindly
|
100
|
+
executables: []
|
74
101
|
extensions: []
|
75
102
|
extra_rdoc_files: []
|
76
103
|
files:
|
@@ -79,18 +106,21 @@ files:
|
|
79
106
|
- LICENSE
|
80
107
|
- README.md
|
81
108
|
- Rakefile
|
82
|
-
- bin/kindly
|
83
109
|
- kindly.gemspec
|
84
110
|
- lib/kindly.rb
|
85
|
-
- lib/kindly/
|
86
|
-
- lib/kindly/
|
87
|
-
- lib/kindly/
|
111
|
+
- lib/kindly/db.rb
|
112
|
+
- lib/kindly/jobs/do_nothing.rb
|
113
|
+
- lib/kindly/jobs/test_job.rb
|
114
|
+
- lib/kindly/jobs/test_job_with_input.rb
|
115
|
+
- lib/kindly/jobs/test_job_with_output.rb
|
116
|
+
- lib/kindly/queue.rb
|
117
|
+
- lib/kindly/registry.rb
|
118
|
+
- lib/kindly/requester.rb
|
88
119
|
- lib/kindly/runner.rb
|
89
120
|
- lib/kindly/version.rb
|
90
|
-
- test/fixtures/
|
91
|
-
- test/fixtures/pending/two.json
|
92
|
-
- test/kindly/handlers_test.rb
|
121
|
+
- test/fixtures/jobs/fail.rb
|
93
122
|
- test/kindly/migration_test.rb
|
123
|
+
- test/kindly/registry_test.rb
|
94
124
|
- test/kindly/runner_test.rb
|
95
125
|
- test/kindly_test.rb
|
96
126
|
homepage: https://github.com/gregoryjscott/kindly
|
@@ -113,14 +143,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
143
|
version: '0'
|
114
144
|
requirements: []
|
115
145
|
rubyforge_project:
|
116
|
-
rubygems_version: 2.
|
146
|
+
rubygems_version: 2.4.5
|
117
147
|
signing_key:
|
118
148
|
specification_version: 4
|
119
|
-
summary: Kindly run
|
149
|
+
summary: Kindly run jobs of any kind.
|
120
150
|
test_files:
|
121
|
-
- test/fixtures/
|
122
|
-
- test/fixtures/pending/two.json
|
123
|
-
- test/kindly/handlers_test.rb
|
151
|
+
- test/fixtures/jobs/fail.rb
|
124
152
|
- test/kindly/migration_test.rb
|
153
|
+
- test/kindly/registry_test.rb
|
125
154
|
- test/kindly/runner_test.rb
|
126
155
|
- test/kindly_test.rb
|
data/bin/kindly
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
|
4
|
-
|
5
|
-
require 'kindly'
|
6
|
-
require 'mercenary'
|
7
|
-
|
8
|
-
Mercenary.program(:kindly) do |p|
|
9
|
-
p.version Kindly::VERSION
|
10
|
-
p.description 'Kindly run migrations of any kind.'
|
11
|
-
p.syntax 'kindly <handler> [options]'
|
12
|
-
|
13
|
-
p.option 'source', '--source DIR', 'Host directory of the kindly directory structure.'
|
14
|
-
p.action do |args, options|
|
15
|
-
if args.empty?
|
16
|
-
puts p
|
17
|
-
elsif options['source']
|
18
|
-
Kindly.run(args.first.to_sym, options['source'])
|
19
|
-
else
|
20
|
-
Kindly.run(args.first.to_sym)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,19 +0,0 @@
|
|
1
|
-
require 'kindly'
|
2
|
-
|
3
|
-
module Kindly
|
4
|
-
module Handlers
|
5
|
-
class DoNothing
|
6
|
-
|
7
|
-
def ext
|
8
|
-
'*'
|
9
|
-
end
|
10
|
-
|
11
|
-
def run(migration)
|
12
|
-
puts "The handler for #{migration.filename} did nothing."
|
13
|
-
end
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
register(:do_nothing, DoNothing.new)
|
18
|
-
end
|
19
|
-
end
|
data/lib/kindly/handlers.rb
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
require 'kindly'
|
2
|
-
|
3
|
-
module Kindly
|
4
|
-
module Handlers
|
5
|
-
|
6
|
-
def self.register(name, handler)
|
7
|
-
@@handlers ||= {}
|
8
|
-
@@handlers[name.to_sym] = handler
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.unregister(name)
|
12
|
-
@@handlers.delete(name.to_sym)
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.find(name)
|
16
|
-
if not_registered(name)
|
17
|
-
raise "No handler registered with name #{name.to_sym}."
|
18
|
-
end
|
19
|
-
|
20
|
-
@@handlers[name.to_sym]
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def self.not_registered(name)
|
26
|
-
!@@handlers.has_key?(name.to_sym)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
data/lib/kindly/migration.rb
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'kindly'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
module Kindly
|
6
|
-
class Migration
|
7
|
-
|
8
|
-
attr_reader :filename
|
9
|
-
|
10
|
-
def initialize(filename)
|
11
|
-
@filename = filename
|
12
|
-
end
|
13
|
-
|
14
|
-
def move(destination)
|
15
|
-
path = File.join(Kindly.source, destination)
|
16
|
-
FileUtils.mkdir(path) unless Dir.exist?(path)
|
17
|
-
FileUtils.mv(@filename, path)
|
18
|
-
@filename = File.join(path, File.basename(@filename))
|
19
|
-
end
|
20
|
-
|
21
|
-
def running!
|
22
|
-
move('running')
|
23
|
-
puts "#{@filename} running."
|
24
|
-
end
|
25
|
-
|
26
|
-
def completed!
|
27
|
-
move('completed')
|
28
|
-
puts "#{@filename} completed."
|
29
|
-
end
|
30
|
-
|
31
|
-
def failed!
|
32
|
-
move('failed')
|
33
|
-
puts "#{@filename} failed."
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
require 'kindly'
|
2
|
-
require 'minitest/autorun'
|
3
|
-
|
4
|
-
describe 'Kindly::Handlers' do
|
5
|
-
|
6
|
-
let(:handlers) { Kindly::Handlers }
|
7
|
-
let(:handler) { Kindly::Handlers::DoNothing.new }
|
8
|
-
|
9
|
-
it 'throws if handler is not registered' do
|
10
|
-
assert_raises(RuntimeError) { handlers.find(:missing) }
|
11
|
-
end
|
12
|
-
|
13
|
-
it 'allows handlers to be registered' do
|
14
|
-
handlers.register :different, handler
|
15
|
-
assert handlers.find(:different) == handler
|
16
|
-
end
|
17
|
-
|
18
|
-
it 'allows handlers to be unregistered' do
|
19
|
-
handlers.register :different, handler
|
20
|
-
handlers.unregister :different
|
21
|
-
assert_raises(RuntimeError) { handlers.find(:different) }
|
22
|
-
end
|
23
|
-
|
24
|
-
end
|