ductwork 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 +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +168 -0
- data/README.md +154 -0
- data/Rakefile +10 -0
- data/app/models/ductwork/availability.rb +9 -0
- data/app/models/ductwork/execution.rb +13 -0
- data/app/models/ductwork/job.rb +181 -0
- data/app/models/ductwork/pipeline.rb +195 -0
- data/app/models/ductwork/process.rb +19 -0
- data/app/models/ductwork/record.rb +15 -0
- data/app/models/ductwork/result.rb +13 -0
- data/app/models/ductwork/run.rb +9 -0
- data/app/models/ductwork/step.rb +27 -0
- data/lib/ductwork/cli.rb +48 -0
- data/lib/ductwork/configuration.rb +145 -0
- data/lib/ductwork/dsl/branch_builder.rb +102 -0
- data/lib/ductwork/dsl/definition_builder.rb +153 -0
- data/lib/ductwork/engine.rb +14 -0
- data/lib/ductwork/machine_identifier.rb +11 -0
- data/lib/ductwork/processes/job_worker.rb +71 -0
- data/lib/ductwork/processes/job_worker_runner.rb +164 -0
- data/lib/ductwork/processes/pipeline_advancer.rb +91 -0
- data/lib/ductwork/processes/pipeline_advancer_runner.rb +169 -0
- data/lib/ductwork/processes/supervisor.rb +160 -0
- data/lib/ductwork/processes/supervisor_runner.rb +35 -0
- data/lib/ductwork/running_context.rb +22 -0
- data/lib/ductwork/testing/helpers.rb +18 -0
- data/lib/ductwork/testing/minitest.rb +8 -0
- data/lib/ductwork/testing/rspec.rb +63 -0
- data/lib/ductwork/testing.rb +15 -0
- data/lib/ductwork/version.rb +5 -0
- data/lib/ductwork.rb +77 -0
- data/lib/generators/ductwork/install/USAGE +11 -0
- data/lib/generators/ductwork/install/install_generator.rb +36 -0
- data/lib/generators/ductwork/install/templates/bin/ductwork +8 -0
- data/lib/generators/ductwork/install/templates/config/ductwork.yml +25 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_availabilities.rb +16 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_executions.rb +14 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_jobs.rb +17 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_pipelines.rb +19 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_processes.rb +14 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_results.rb +14 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_runs.rb +12 -0
- data/lib/generators/ductwork/install/templates/db/create_ductwork_steps.rb +17 -0
- metadata +165 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ductwork
|
|
4
|
+
module Processes
|
|
5
|
+
class Supervisor
|
|
6
|
+
attr_reader :workers
|
|
7
|
+
|
|
8
|
+
def initialize
|
|
9
|
+
@running = true
|
|
10
|
+
@workers = []
|
|
11
|
+
|
|
12
|
+
run_hooks_for(:start)
|
|
13
|
+
|
|
14
|
+
Signal.trap(:INT) { @running = false }
|
|
15
|
+
Signal.trap(:TERM) { @running = false }
|
|
16
|
+
Signal.trap(:TTIN) { puts "No threads to dump" }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def add_worker(metadata: {}, &block)
|
|
20
|
+
pid = fork do
|
|
21
|
+
block.call(metadata)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
workers << { metadata:, pid:, block: }
|
|
25
|
+
logger.debug(
|
|
26
|
+
msg: "Started child process (#{pid}) with metadata #{metadata}",
|
|
27
|
+
pid: pid
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def run
|
|
32
|
+
logger.debug(msg: "Entering main work loop", role: :supervisor, pid: ::Process.pid)
|
|
33
|
+
|
|
34
|
+
while running
|
|
35
|
+
sleep(Ductwork.configuration.supervisor_polling_timeout)
|
|
36
|
+
check_workers
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
shutdown
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def shutdown
|
|
43
|
+
@running = false
|
|
44
|
+
|
|
45
|
+
logger.debug(msg: "Beginning shutdown", role: :supervisor)
|
|
46
|
+
terminate_gracefully
|
|
47
|
+
wait_for_workers_to_exit
|
|
48
|
+
terminate_immediately
|
|
49
|
+
run_hooks_for(:stop)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
attr_reader :running
|
|
55
|
+
|
|
56
|
+
def check_workers
|
|
57
|
+
logger.debug(msg: "Checking workers are alive", role: :supervisor)
|
|
58
|
+
|
|
59
|
+
workers.each do |worker|
|
|
60
|
+
if process_dead?(worker[:pid])
|
|
61
|
+
old_pid = worker[:pid]
|
|
62
|
+
new_pid = fork do
|
|
63
|
+
worker[:block].call(worker[:metadata])
|
|
64
|
+
end
|
|
65
|
+
worker[:pid] = new_pid
|
|
66
|
+
logger.debug(
|
|
67
|
+
msg: "Restarted process (#{old_pid}) as (#{new_pid})",
|
|
68
|
+
role: :supervisor,
|
|
69
|
+
old_pid: old_pid,
|
|
70
|
+
new_pid: new_pid
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
logger.debug(msg: "All workers are alive or revived", role: :supervisor)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def terminate_gracefully
|
|
79
|
+
workers.each do |worker|
|
|
80
|
+
logger.debug(
|
|
81
|
+
msg: "Sending TERM signal to process (#{worker[:pid]})",
|
|
82
|
+
role: :supervisor,
|
|
83
|
+
pid: worker[:pid],
|
|
84
|
+
signal: :TERM
|
|
85
|
+
)
|
|
86
|
+
::Process.kill(:TERM, worker[:pid])
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def wait_for_workers_to_exit
|
|
91
|
+
deadline = now + Ductwork.configuration.supervisor_shutdown_timeout
|
|
92
|
+
|
|
93
|
+
while workers.any? && now < deadline
|
|
94
|
+
sleep(0.1)
|
|
95
|
+
workers.each_with_index do |worker, index|
|
|
96
|
+
if ::Process.wait(worker[:pid], ::Process::WNOHANG)
|
|
97
|
+
workers[index] = nil
|
|
98
|
+
logger.debug(
|
|
99
|
+
msg: "Child process (#{worker[:pid]}) stopped successfully",
|
|
100
|
+
role: :supervisor,
|
|
101
|
+
pid: worker[:pid]
|
|
102
|
+
)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
@workers = workers.compact
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def terminate_immediately
|
|
110
|
+
workers.each_with_index do |worker, index|
|
|
111
|
+
logger.debug(
|
|
112
|
+
msg: "Sending KILL signal to process (#{worker[:pid]})",
|
|
113
|
+
role: :supervisor,
|
|
114
|
+
pid: worker[:pid],
|
|
115
|
+
signal: :KILL
|
|
116
|
+
)
|
|
117
|
+
::Process.kill(:KILL, worker[:pid])
|
|
118
|
+
::Process.wait(worker[:pid])
|
|
119
|
+
workers[index] = nil
|
|
120
|
+
logger.debug(
|
|
121
|
+
msg: "Child process (#{worker[:pid]}) killed after timeout",
|
|
122
|
+
role: :supervisor,
|
|
123
|
+
pid: worker[:pid]
|
|
124
|
+
)
|
|
125
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
126
|
+
# no-op because process is already dead
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
@workers = workers.compact
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def process_dead?(pid)
|
|
133
|
+
machine_identifier = Ductwork::MachineIdentifier.fetch
|
|
134
|
+
|
|
135
|
+
Ductwork.wrap_with_app_executor do
|
|
136
|
+
Ductwork::Process
|
|
137
|
+
.where(pid:, machine_identifier:)
|
|
138
|
+
.where("last_heartbeat_at < ?", 5.minutes.ago)
|
|
139
|
+
.exists?
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def run_hooks_for(event)
|
|
144
|
+
Ductwork.hooks[:supervisor].fetch(event, []).each do |block|
|
|
145
|
+
Ductwork.wrap_with_app_executor do
|
|
146
|
+
block.call(self)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def now
|
|
152
|
+
::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def logger
|
|
156
|
+
Ductwork.configuration.logger
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ductwork
|
|
4
|
+
module Processes
|
|
5
|
+
class SupervisorRunner
|
|
6
|
+
def self.start!
|
|
7
|
+
supervisor = Ductwork::Processes::Supervisor.new
|
|
8
|
+
pipelines_to_advance = Ductwork.configuration.pipelines
|
|
9
|
+
logger = Ductwork.configuration.logger
|
|
10
|
+
|
|
11
|
+
supervisor.add_worker(metadata: { pipelines: pipelines_to_advance }) do
|
|
12
|
+
logger.debug(
|
|
13
|
+
msg: "Starting Pipeline Advancer process",
|
|
14
|
+
role: :supervisor_runner
|
|
15
|
+
)
|
|
16
|
+
Ductwork::Processes::PipelineAdvancerRunner
|
|
17
|
+
.new(*pipelines_to_advance).run
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
pipelines_to_advance.each do |pipeline|
|
|
21
|
+
supervisor.add_worker(metadata: { pipeline: }) do
|
|
22
|
+
logger.debug(
|
|
23
|
+
msg: "Starting Job Worker Runner process",
|
|
24
|
+
role: :supervisor_runner,
|
|
25
|
+
pipeline: pipeline
|
|
26
|
+
)
|
|
27
|
+
Ductwork::Processes::JobWorkerRunner.new(pipeline).run
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
supervisor.run
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ductwork
|
|
4
|
+
class RunningContext
|
|
5
|
+
def initialize
|
|
6
|
+
@mutex = Mutex.new
|
|
7
|
+
@running = true
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def running?
|
|
11
|
+
mutex.synchronize { running }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def shutdown!
|
|
15
|
+
@running = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
attr_reader :mutex, :running
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ductwork
|
|
4
|
+
module Testing
|
|
5
|
+
module Helpers
|
|
6
|
+
def pipelines_created_around(&block)
|
|
7
|
+
before_ids = Ductwork::Pipeline.ids
|
|
8
|
+
|
|
9
|
+
block.call
|
|
10
|
+
|
|
11
|
+
after_ids = Ductwork::Pipeline.ids
|
|
12
|
+
ids_delta = after_ids - before_ids
|
|
13
|
+
|
|
14
|
+
Ductwork::Pipeline.where(id: ids_delta)
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
RSpec::Matchers.define(:have_triggered_pipeline) do |expected|
|
|
4
|
+
include Ductwork::Testing::Helpers
|
|
5
|
+
|
|
6
|
+
supports_block_expectations
|
|
7
|
+
|
|
8
|
+
match do |block|
|
|
9
|
+
pipelines = pipelines_created_around(&block)
|
|
10
|
+
delta = pipelines.count
|
|
11
|
+
expected_count = count || 1
|
|
12
|
+
|
|
13
|
+
if delta == expected_count
|
|
14
|
+
pipelines.pluck(:klass).uniq.sort == Array(expected).map(&:name).sort
|
|
15
|
+
else
|
|
16
|
+
@failure_result = if delta.zero?
|
|
17
|
+
:none
|
|
18
|
+
elsif delta > 1
|
|
19
|
+
:too_many
|
|
20
|
+
else
|
|
21
|
+
:other
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
false
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
chain :exactly, :count
|
|
29
|
+
|
|
30
|
+
chain :times do # rubocop:disable Lint/EmptyBlock
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
failure_message do |actual|
|
|
34
|
+
case @failure_result
|
|
35
|
+
when :none
|
|
36
|
+
"expected to trigger pipeline #{expected} but triggered none"
|
|
37
|
+
when :too_many
|
|
38
|
+
"expected to trigger pipeline #{expected} but triggered more than one"
|
|
39
|
+
when :other
|
|
40
|
+
"expected to trigger pipeline #{expected} but triggered #{actual}"
|
|
41
|
+
else
|
|
42
|
+
"expected to trigger pipeline #{expected} but did not"
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
RSpec::Matchers.define(:have_triggered_pipelines) do |*expected|
|
|
48
|
+
include Ductwork::Testing::Helpers
|
|
49
|
+
|
|
50
|
+
supports_block_expectations
|
|
51
|
+
|
|
52
|
+
match do |block|
|
|
53
|
+
pipelines = pipelines_created_around(&block)
|
|
54
|
+
|
|
55
|
+
pipelines.map(&:klass).sort == expected.map(&:name).sort
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
failure_message do |_actual|
|
|
59
|
+
pipeline_names = expected.map(&:name).join(", ")
|
|
60
|
+
|
|
61
|
+
"expected to trigger pipelines: #{pipeline_names} but did not"
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ductwork/testing/helpers"
|
|
4
|
+
|
|
5
|
+
module Ductwork
|
|
6
|
+
module Testing
|
|
7
|
+
if defined?(RSpec)
|
|
8
|
+
require "ductwork/testing/rspec"
|
|
9
|
+
elsif defined?(Minitest)
|
|
10
|
+
require "ductwork/testing/minitest"
|
|
11
|
+
else
|
|
12
|
+
raise "Testing framework is not supported"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
data/lib/ductwork.rb
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_record"
|
|
4
|
+
require "active_support"
|
|
5
|
+
require "logger"
|
|
6
|
+
require "rails/engine"
|
|
7
|
+
require "securerandom"
|
|
8
|
+
require "zeitwerk"
|
|
9
|
+
|
|
10
|
+
require "ductwork/engine"
|
|
11
|
+
|
|
12
|
+
loader = Zeitwerk::Loader.for_gem
|
|
13
|
+
loader.inflector.inflect("cli" => "CLI")
|
|
14
|
+
loader.inflector.inflect("dsl" => "DSL")
|
|
15
|
+
loader.ignore("#{__dir__}/generators")
|
|
16
|
+
loader.ignore("#{__dir__}/ductwork/testing")
|
|
17
|
+
loader.ignore("#{__dir__}/ductwork/testing.rb")
|
|
18
|
+
loader.setup
|
|
19
|
+
|
|
20
|
+
module Ductwork
|
|
21
|
+
class << self
|
|
22
|
+
attr_accessor :app_executor, :configuration
|
|
23
|
+
attr_writer :defined_pipelines, :hooks
|
|
24
|
+
|
|
25
|
+
def wrap_with_app_executor(&block)
|
|
26
|
+
if app_executor.present?
|
|
27
|
+
app_executor.wrap(&block)
|
|
28
|
+
else
|
|
29
|
+
yield
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def hooks
|
|
34
|
+
@hooks ||= {
|
|
35
|
+
supervisor: { start: [], stop: [] },
|
|
36
|
+
advancer: { start: [], stop: [] },
|
|
37
|
+
worker: { start: [], stop: [] },
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def on_supervisor_start(&block)
|
|
42
|
+
add_lifecycle_hook(:supervisor, :start, block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def on_supervisor_stop(&block)
|
|
46
|
+
add_lifecycle_hook(:supervisor, :stop, block)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def on_advancer_start(&block)
|
|
50
|
+
add_lifecycle_hook(:advancer, :start, block)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def on_advancer_stop(&block)
|
|
54
|
+
add_lifecycle_hook(:advancer, :stop, block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def on_worker_start(&block)
|
|
58
|
+
add_lifecycle_hook(:worker, :start, block)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def on_worker_stop(&block)
|
|
62
|
+
add_lifecycle_hook(:worker, :stop, block)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def defined_pipelines
|
|
66
|
+
@defined_pipelines ||= []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def add_lifecycle_hook(target, event, block)
|
|
72
|
+
hooks[target] ||= {}
|
|
73
|
+
hooks[target][event] ||= []
|
|
74
|
+
hooks[target][event].push(block)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/migration"
|
|
4
|
+
require "rails/generators/active_record/migration"
|
|
5
|
+
|
|
6
|
+
module Ductwork
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
include ActiveRecord::Generators::Migration
|
|
9
|
+
|
|
10
|
+
source_root File.expand_path("templates", __dir__)
|
|
11
|
+
|
|
12
|
+
def create_files
|
|
13
|
+
template "config/ductwork.yml"
|
|
14
|
+
template "bin/ductwork"
|
|
15
|
+
|
|
16
|
+
migration_template "db/create_ductwork_pipelines.rb",
|
|
17
|
+
"db/migrate/create_ductwork_pipelines.rb"
|
|
18
|
+
migration_template "db/create_ductwork_steps.rb",
|
|
19
|
+
"db/migrate/create_ductwork_steps.rb"
|
|
20
|
+
migration_template "db/create_ductwork_jobs.rb",
|
|
21
|
+
"db/migrate/create_ductwork_jobs.rb"
|
|
22
|
+
migration_template "db/create_ductwork_executions.rb",
|
|
23
|
+
"db/migrate/create_ductwork_executions.rb"
|
|
24
|
+
migration_template "db/create_ductwork_availabilities.rb",
|
|
25
|
+
"db/migrate/create_ductwork_availabilities.rb"
|
|
26
|
+
migration_template "db/create_ductwork_runs.rb",
|
|
27
|
+
"db/migrate/create_ductwork_runs.rb"
|
|
28
|
+
migration_template "db/create_ductwork_results.rb",
|
|
29
|
+
"db/migrate/create_ductwork_results.rb"
|
|
30
|
+
migration_template "db/create_ductwork_processes.rb",
|
|
31
|
+
"db/migrate/create_ductwork_processes.rb"
|
|
32
|
+
|
|
33
|
+
chmod "bin/ductwork", 0o755 & ~File.umask, verbose: false
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
default: &default
|
|
2
|
+
pipelines: "*"
|
|
3
|
+
logger:
|
|
4
|
+
level: 1
|
|
5
|
+
source: default
|
|
6
|
+
job_worker:
|
|
7
|
+
count: 5
|
|
8
|
+
max_retry: 3
|
|
9
|
+
polling_timeout: 1
|
|
10
|
+
shutdown_timeout: 20
|
|
11
|
+
pipeline_advancer:
|
|
12
|
+
polling_timeout: 1
|
|
13
|
+
shutdown_timeout: 20
|
|
14
|
+
supervisor:
|
|
15
|
+
polling_timeout: 1
|
|
16
|
+
shutdown_timeout: 30
|
|
17
|
+
|
|
18
|
+
development:
|
|
19
|
+
<<: *default
|
|
20
|
+
|
|
21
|
+
test:
|
|
22
|
+
<<: *default
|
|
23
|
+
|
|
24
|
+
production:
|
|
25
|
+
<<: *default
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkAvailabilities < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_availabilities do |table|
|
|
6
|
+
table.belongs_to :execution, index: false, null: false, foreign_key: { to_table: :ductwork_executions }
|
|
7
|
+
table.timestamp :started_at, null: false
|
|
8
|
+
table.timestamp :completed_at
|
|
9
|
+
table.integer :process_id
|
|
10
|
+
table.timestamps null: false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :ductwork_availabilities, :execution_id, unique: true
|
|
14
|
+
add_index :ductwork_availabilities, %i[id process_id]
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkExecutions < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_executions do |table|
|
|
6
|
+
table.belongs_to :job, index: true, null: false, foreign_key: { to_table: :ductwork_jobs }
|
|
7
|
+
table.timestamp :started_at, null: false
|
|
8
|
+
table.timestamp :completed_at
|
|
9
|
+
table.integer :retry_count, null: false
|
|
10
|
+
table.integer :process_id
|
|
11
|
+
table.timestamps null: false
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkJobs < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_jobs do |table|
|
|
6
|
+
table.belongs_to :step, index: false, null: false, foreign_key: { to_table: :ductwork_steps }
|
|
7
|
+
table.string :klass, null: false
|
|
8
|
+
table.timestamp :started_at, null: false
|
|
9
|
+
table.timestamp :completed_at
|
|
10
|
+
table.string :input_args, null: false
|
|
11
|
+
table.string :output_payload
|
|
12
|
+
table.timestamps null: false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :ductwork_jobs, :step_id, unique: true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkPipelines < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_pipelines do |table|
|
|
6
|
+
table.string :klass, null: false
|
|
7
|
+
table.string :definition, null: false
|
|
8
|
+
table.string :definition_sha1, null: false
|
|
9
|
+
table.timestamp :triggered_at, null: false
|
|
10
|
+
table.timestamp :completed_at
|
|
11
|
+
table.timestamp :claimed_for_advancing_at
|
|
12
|
+
table.timestamp :last_advanced_at, null: false
|
|
13
|
+
table.string :status, null: false
|
|
14
|
+
table.timestamps null: false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
add_index :ductwork_pipelines, :klass
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkProcesses < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_processes do |table|
|
|
6
|
+
table.integer :pid, null: false
|
|
7
|
+
table.string :machine_identifier, null: false
|
|
8
|
+
table.timestamp :last_heartbeat_at, null: false
|
|
9
|
+
table.timestamps null: false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_index :ductwork_processes, %i[pid machine_identifier], unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkResults < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_results do |table|
|
|
6
|
+
table.belongs_to :execution, index: false, null: false, foreign_key: { to_table: :ductwork_executions }
|
|
7
|
+
table.string :result_type, null: false
|
|
8
|
+
table.string :error_klass
|
|
9
|
+
table.string :error_message
|
|
10
|
+
table.text :error_backtrace
|
|
11
|
+
table.timestamps null: false
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkRuns < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_runs do |table|
|
|
6
|
+
table.belongs_to :execution, index: false, null: false, foreign_key: { to_table: :ductwork_executions }
|
|
7
|
+
table.timestamp :started_at, null: false
|
|
8
|
+
table.timestamp :completed_at
|
|
9
|
+
table.timestamps null: false
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateDuctworkSteps < ActiveRecord::Migration[<%= Rails::VERSION::MAJOR %>.<%= Rails::VERSION::MINOR %>]
|
|
4
|
+
def change
|
|
5
|
+
create_table :ductwork_steps do |table|
|
|
6
|
+
table.belongs_to :pipeline, index: true, null: false, foreign_key: { to_table: :ductwork_pipelines }
|
|
7
|
+
table.string :klass, null: false
|
|
8
|
+
table.string :step_type, null: false
|
|
9
|
+
table.timestamp :started_at
|
|
10
|
+
table.timestamp :completed_at
|
|
11
|
+
table.string :status, null: false
|
|
12
|
+
table.timestamps null: false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :ductwork_steps, %i[pipeline_id status]
|
|
16
|
+
end
|
|
17
|
+
end
|