smart_proxy_dynflow 0.7.0 → 0.8.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/lib/smart_proxy_dynflow/action/batch_runner.rb +3 -3
- data/lib/smart_proxy_dynflow/action/external_polling.rb +1 -2
- data/lib/smart_proxy_dynflow/action/output_collector.rb +1 -0
- data/lib/smart_proxy_dynflow/action/runner.rb +9 -0
- data/lib/smart_proxy_dynflow/core.rb +3 -0
- data/lib/smart_proxy_dynflow/helpers.rb +14 -3
- data/lib/smart_proxy_dynflow/io_buffer.rb +115 -0
- data/lib/smart_proxy_dynflow/plugin.rb +1 -1
- data/lib/smart_proxy_dynflow/process_manager.rb +166 -0
- data/lib/smart_proxy_dynflow/runner/base.rb +8 -2
- data/lib/smart_proxy_dynflow/runner/dispatcher.rb +11 -0
- data/lib/smart_proxy_dynflow/runner/parent.rb +2 -2
- data/lib/smart_proxy_dynflow/runner/process_manager_command.rb +36 -0
- data/lib/smart_proxy_dynflow/task_launcher/group.rb +8 -2
- data/lib/smart_proxy_dynflow/version.rb +1 -1
- data/settings.d/dynflow.yml.example +3 -3
- metadata +6 -6
- data/bundler.d/inspect_anything.rb +0 -1
- data/bundler.d/x.local.rb +0 -1
- data/lib/smart_proxy_dynflow/process_manager.rbs +0 -46
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8852c64e45de97691310f1c9fd17bef228a9ea87db2085377cee1cd404546752
|
4
|
+
data.tar.gz: 54afa19849d245f44f8d5e03c69d82e30015ff95978b266cad0db3919eb82195
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a1799f87c74aa10807ea642d217686f208fe2de5444b4a87c540a8685e419d4c88b272e03faa83a9756fa7ef4e996f3cd8195f730ae43e97800e4666fecae59
|
7
|
+
data.tar.gz: ab1dd31d7555502d566803ba4e87965e2431bb6d32c67cc6e0e39f7ac1c6c5369e22b1b6c7f6e3abb8cfdcccf6b4432029ba10b277171931f6a5fa05ed792d8d
|
@@ -2,13 +2,13 @@ require 'smart_proxy_dynflow/action/runner'
|
|
2
2
|
|
3
3
|
module Proxy::Dynflow::Action
|
4
4
|
class BatchRunner < ::Proxy::Dynflow::Action::Runner
|
5
|
-
def plan(launcher, input)
|
6
|
-
plan_self :targets => launcher.runner_input(input), :operation => launcher.operation
|
5
|
+
def plan(launcher, input, runner_id)
|
6
|
+
plan_self :targets => launcher.runner_input(input), :operation => launcher.operation, :runner_id => runner_id
|
7
7
|
end
|
8
8
|
|
9
9
|
def initiate_runner
|
10
10
|
launcher = Proxy::Dynflow::TaskLauncherRegistry.fetch(input[:operation])
|
11
|
-
launcher.runner_class.new(input[:targets], suspended_action: suspended_action)
|
11
|
+
launcher.runner_class.new(input[:targets], suspended_action: suspended_action, id: input[:runner_id])
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -1,8 +1,10 @@
|
|
1
1
|
require 'smart_proxy_dynflow/action/shareable'
|
2
|
+
require 'smart_proxy_dynflow/action/external_polling'
|
2
3
|
module Proxy::Dynflow
|
3
4
|
module Action
|
4
5
|
class Runner < Shareable
|
5
6
|
include ::Dynflow::Action::Cancellable
|
7
|
+
include ::Proxy::Dynflow::Action::WithExternalPolling
|
6
8
|
|
7
9
|
def run(event = nil)
|
8
10
|
case event
|
@@ -14,6 +16,9 @@ module Proxy::Dynflow
|
|
14
16
|
process_external_event(event)
|
15
17
|
when ::Dynflow::Action::Cancellable::Cancel
|
16
18
|
kill_run
|
19
|
+
when ::Proxy::Dynflow::Action::WithExternalPolling::Poll
|
20
|
+
poll
|
21
|
+
suspend
|
17
22
|
else
|
18
23
|
raise "Unexpected event #{event.inspect}"
|
19
24
|
end
|
@@ -69,6 +74,10 @@ module Proxy::Dynflow
|
|
69
74
|
end
|
70
75
|
end
|
71
76
|
|
77
|
+
def poll
|
78
|
+
runner_dispatcher.refresh_output(output[:runner_id])
|
79
|
+
end
|
80
|
+
|
72
81
|
def failed_run?
|
73
82
|
output[:exit_status] != 0
|
74
83
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
1
3
|
module Proxy::Dynflow
|
2
4
|
class Core
|
3
5
|
attr_accessor :world, :accepted_cert_serial
|
@@ -29,6 +31,7 @@ module Proxy::Dynflow
|
|
29
31
|
Log.instance.warn "Could not open DB for dynflow at '#{db_file}', " \
|
30
32
|
"will keep data in memory. Restart will drop all dynflow data."
|
31
33
|
else
|
34
|
+
FileUtils.mkdir_p(File.dirname(db_file))
|
32
35
|
db_conn_string += "/#{db_file}"
|
33
36
|
end
|
34
37
|
|
@@ -36,9 +36,8 @@ module Proxy
|
|
36
36
|
def task_status(task_id)
|
37
37
|
ep = world.persistence.load_execution_plan(task_id)
|
38
38
|
actions = ep.actions.map do |action|
|
39
|
-
|
40
|
-
|
41
|
-
hash
|
39
|
+
refresh_output(ep, action)
|
40
|
+
expand_output(action)
|
42
41
|
end
|
43
42
|
ep.to_hash.merge(:actions => actions)
|
44
43
|
rescue KeyError => _e
|
@@ -58,6 +57,18 @@ module Proxy
|
|
58
57
|
params['step_id'].to_i,
|
59
58
|
::Proxy::Dynflow::Runner::ExternalEvent.new(params))
|
60
59
|
end
|
60
|
+
|
61
|
+
def refresh_output(execution_plan, action)
|
62
|
+
if action.is_a?(Proxy::Dynflow::Action::WithExternalPolling) && %i[running suspended].include?(action.run_step&.state)
|
63
|
+
world.event(execution_plan.id, action.run_step_id, Proxy::Dynflow::Action::WithExternalPolling::Poll)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def expand_output(action)
|
68
|
+
hash = action.to_hash
|
69
|
+
hash[:output][:result] = action.output_result if action.is_a?(Proxy::Dynflow::Action::Runner)
|
70
|
+
hash
|
71
|
+
end
|
61
72
|
end
|
62
73
|
end
|
63
74
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Proxy
|
2
|
+
module Dynflow
|
3
|
+
# A buffer around an IO object providing buffering and convenience methods
|
4
|
+
# for non-blocking reads and writes.
|
5
|
+
#
|
6
|
+
# @note Using a single IOBuffer with a single IO for both reads and writes might not be a good idea. If you need to use a single IO for both reads and writes, wrap it in two separate IOBuffers.
|
7
|
+
#
|
8
|
+
# @attr_accessor [IO] io The IO which the buffer wraps
|
9
|
+
# @attr_reader [String] buffer The buffer where the data read from the underlying IO is buffered
|
10
|
+
class IOBuffer
|
11
|
+
attr_accessor :io
|
12
|
+
attr_reader :buffer
|
13
|
+
|
14
|
+
# @param [IO] io The IO object to be buffered
|
15
|
+
def initialize(io)
|
16
|
+
@buffer = ''
|
17
|
+
@io = io
|
18
|
+
end
|
19
|
+
|
20
|
+
# Sets a callback to be executed each time data is read from the
|
21
|
+
# underlying IO.
|
22
|
+
#
|
23
|
+
# @note Note that if the callback is provided, the buffer will store the return value of the callback instead of the raw data.
|
24
|
+
#
|
25
|
+
# @yieldparam [String] data read from the underlying IO
|
26
|
+
# @yieldreturn [String] data to be buffered
|
27
|
+
# @return [void]
|
28
|
+
def on_data(&block)
|
29
|
+
@callback = block
|
30
|
+
end
|
31
|
+
|
32
|
+
# Exposes the underlying IO so that the buffer itself can be used in IO.select calls.
|
33
|
+
#
|
34
|
+
# @return [IO] the underlying IO
|
35
|
+
def to_io
|
36
|
+
@io
|
37
|
+
end
|
38
|
+
|
39
|
+
# Exposes the contents of the buffer as a String
|
40
|
+
#
|
41
|
+
# @return [String] the buffered data
|
42
|
+
def to_s
|
43
|
+
@buffer
|
44
|
+
end
|
45
|
+
|
46
|
+
# Checks whether the buffer is empty
|
47
|
+
#
|
48
|
+
# @return [true, false] whether the buffer is empty
|
49
|
+
def empty?
|
50
|
+
@buffer.empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
# Checks whether the underlying IO is empty
|
54
|
+
#
|
55
|
+
# @return [true, false] whether the underlying IO is empty
|
56
|
+
def closed?
|
57
|
+
@io.closed?
|
58
|
+
end
|
59
|
+
|
60
|
+
# Closes the underlying IO. Does nothing if the IO is already closed.
|
61
|
+
#
|
62
|
+
# @return [void]
|
63
|
+
def close
|
64
|
+
@io.close unless @io.closed?
|
65
|
+
end
|
66
|
+
|
67
|
+
# Reads all the data that is currently waiting in the IO and stores it. If
|
68
|
+
# EOFError is encountered during the read, the underlying IO is closed.
|
69
|
+
#
|
70
|
+
# @return [void]
|
71
|
+
def read_available!
|
72
|
+
data = ''
|
73
|
+
loop { data += @io.read_nonblock(4096) }
|
74
|
+
rescue IO::WaitReadable # rubocop:disable Lint/HandleExceptions
|
75
|
+
rescue EOFError
|
76
|
+
close
|
77
|
+
ensure
|
78
|
+
@buffer += with_callback(data) unless data.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Writes all the data into the IO that can be written without blocking. It
|
82
|
+
# is a no-op if there are no data to be written. If an EOFError is
|
83
|
+
# encountered during the write, the underlying IO is closed.
|
84
|
+
#
|
85
|
+
# @return [void]
|
86
|
+
def write_available!
|
87
|
+
until @buffer.empty?
|
88
|
+
n = @io.write_nonblock(@buffer)
|
89
|
+
@buffer = @buffer[n..-1]
|
90
|
+
end
|
91
|
+
rescue IO::WaitWritable # rubocop:disable Lint/HandleExceptions
|
92
|
+
rescue EOFError
|
93
|
+
close
|
94
|
+
end
|
95
|
+
|
96
|
+
# Adds data to the buffer. If the buffer is used for writing, then this
|
97
|
+
# should be the preferred method of queueing the data to be written.
|
98
|
+
#
|
99
|
+
# @return [void]
|
100
|
+
def add_data(data)
|
101
|
+
@buffer += data
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def with_callback(data)
|
107
|
+
if @callback
|
108
|
+
@callback.call(data)
|
109
|
+
else
|
110
|
+
data
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -11,7 +11,7 @@ module Proxy::Dynflow
|
|
11
11
|
settings_file "dynflow.yml"
|
12
12
|
requires :foreman_proxy, ">= 1.16.0"
|
13
13
|
default_settings :console_auth => true,
|
14
|
-
:execution_plan_cleaner_age => 60 *
|
14
|
+
:execution_plan_cleaner_age => 60 * 30
|
15
15
|
plugin :dynflow, Proxy::Dynflow::VERSION
|
16
16
|
|
17
17
|
capability(proc { self.available_operations })
|
@@ -0,0 +1,166 @@
|
|
1
|
+
require 'smart_proxy_dynflow/io_buffer'
|
2
|
+
|
3
|
+
module Proxy
|
4
|
+
module Dynflow
|
5
|
+
# An abstraction for managing local processes.
|
6
|
+
#
|
7
|
+
# It can be used to:
|
8
|
+
# - spawn a local process
|
9
|
+
# - track its lifecycle
|
10
|
+
# - communicate with it through its standard input, output and error
|
11
|
+
# - step through the execution one event at a time or start the child process and wait until it finishes
|
12
|
+
#
|
13
|
+
# @example Run date command and collect its output
|
14
|
+
# pm = ProcessManager.new('date')
|
15
|
+
# pm.run!
|
16
|
+
# pm.status #=> 0
|
17
|
+
# pm.stdout.to_s.chomp #=> "Thu Feb 3 04:27:42 PM CET 2022"
|
18
|
+
#
|
19
|
+
# @example Run a shell loop, outputting all the lines it generates
|
20
|
+
# pm = ProcessManager.new(['/bin/sh', '-c', 'for i in 1 2 3; do echo $i; sleep 1; done'])
|
21
|
+
# pm.on_stdout { |data| puts data; '' }
|
22
|
+
# pm.run!
|
23
|
+
# #=> 1
|
24
|
+
# #=> 2
|
25
|
+
# #=> 3
|
26
|
+
#
|
27
|
+
# @example Run bc (calculator) interactively and count down from 10 to 0
|
28
|
+
# pm = ProcessManager.new('bc')
|
29
|
+
# pm.on_stdout do |data|
|
30
|
+
# if data.match?(/^\d+/)
|
31
|
+
# n = data.to_i
|
32
|
+
# if n.zero?
|
33
|
+
# pm.stdin.to_io.close
|
34
|
+
# else
|
35
|
+
# pm.stdin.add_data("#{n} - 1\n")
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
# data
|
39
|
+
# end
|
40
|
+
# pm.stdin.add_data("10\n")
|
41
|
+
# pm.run!
|
42
|
+
# pm.stdout.to_s.lines #=. ["10\n", "9\n", "8\n", "7\n", "6\n", "5\n", "4\n", "3\n", "2\n", "1\n", "0\n"]
|
43
|
+
#
|
44
|
+
# @attr_reader [Proxy::Dynflow::IOBuffer] stdin IOBuffer buffering writes to child process' standard input
|
45
|
+
# @attr_reader [Proxy::Dynflow::IOBuffer] stdout IOBuffer buffering reads from child process' standard output
|
46
|
+
# @attr_reader [Proxy::Dynflow::IOBuffer] stderr IOBuffer buffering reads from child process' standard error
|
47
|
+
# @attr_reader [nil, Integer] pid Process id of the child process, nil if the process was not started yet, -1 if the process could not be started
|
48
|
+
# @attr_reader [nil, Integer] status Exit status of the child process. nil if the child process has not finished yet, 255 if the process could not be started
|
49
|
+
class ProcessManager
|
50
|
+
attr_reader :stdin, :stdout, :stderr, :pid, :status
|
51
|
+
|
52
|
+
# @param [String, [String], [Hash, String]] command A command to run in one of the forms accepted by Kernel.spawn
|
53
|
+
def initialize(command)
|
54
|
+
@command = command
|
55
|
+
@stdin = IOBuffer.new(nil)
|
56
|
+
@stdout = IOBuffer.new(nil)
|
57
|
+
@stderr = IOBuffer.new(nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Starts the process manager and runs it until it finishes
|
61
|
+
#
|
62
|
+
# @return [ProcessManager] the process manager itself to allow method chaining
|
63
|
+
def run!
|
64
|
+
start! unless started?
|
65
|
+
process until done?
|
66
|
+
self
|
67
|
+
end
|
68
|
+
|
69
|
+
# Starts the child process. It creates 3 pipes for communicating with the
|
70
|
+
# child process and the forks it. The process manager is considered done
|
71
|
+
# if the child process cannot be started.
|
72
|
+
#
|
73
|
+
# @return [void]
|
74
|
+
def start!
|
75
|
+
in_read, in_write = IO.pipe
|
76
|
+
out_read, out_write = IO.pipe
|
77
|
+
err_read, err_write = IO.pipe
|
78
|
+
|
79
|
+
@pid = spawn(*@command, :in => in_read, :out => out_write, :err => err_write)
|
80
|
+
[in_read, out_write, err_write].each(&:close)
|
81
|
+
|
82
|
+
@stdin.io = in_write
|
83
|
+
@stdout.io = out_read
|
84
|
+
@stderr.io = err_read
|
85
|
+
rescue Errno::ENOENT => e
|
86
|
+
[in_read, in_write, out_read, out_write, err_read, err_write].each(&:close)
|
87
|
+
@pid = -1
|
88
|
+
@status = 255
|
89
|
+
@stderr.add_data(e.message)
|
90
|
+
end
|
91
|
+
|
92
|
+
# Determines whether the process manager already forked off its child process
|
93
|
+
#
|
94
|
+
# @return [true, false] whether the process manager already forked off its child process
|
95
|
+
def started?
|
96
|
+
!pid.nil?
|
97
|
+
end
|
98
|
+
|
99
|
+
# Determines whether the child process of the process manager already finished
|
100
|
+
#
|
101
|
+
# @return [true, false] whether the child process of the process manager already finished
|
102
|
+
def done?
|
103
|
+
started? && !status.nil?
|
104
|
+
end
|
105
|
+
|
106
|
+
# Runs a single iteration of the manager's processing loop. It waits until either:
|
107
|
+
# - data is available in pipes connected to the child process' standard output or error
|
108
|
+
# - there is pending data to be written and the pipe connected to the child process' standard input is writable
|
109
|
+
# - a timeout is reached
|
110
|
+
#
|
111
|
+
# After the wait, all pending data is read and written.
|
112
|
+
#
|
113
|
+
# If all the pipes connected to the child process are closed, it marks the
|
114
|
+
# execution as complete and performs cleanup.
|
115
|
+
#
|
116
|
+
# @param timeout [nil, Numeric] controls how long this call should wait for data to become available. Waits indefinitely if nil.
|
117
|
+
# @return [void]
|
118
|
+
def process(timeout: nil)
|
119
|
+
raise 'Cannot process until the manager is started' unless started?
|
120
|
+
writers = [@stdin].reject { |buf| buf.empty? || buf.closed? }
|
121
|
+
readers = [@stdout, @stderr].reject(&:closed?)
|
122
|
+
|
123
|
+
if readers.empty? && writers.empty?
|
124
|
+
finish
|
125
|
+
return
|
126
|
+
end
|
127
|
+
|
128
|
+
ready_readers, ready_writers = IO.select(readers, writers, nil, timeout)
|
129
|
+
(ready_readers || []).each(&:read_available!)
|
130
|
+
(ready_writers || []).each(&:write_available!)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Sets block to be executed each time data is read from child process' standard output
|
134
|
+
#
|
135
|
+
# @return [void]
|
136
|
+
def on_stdout(&block)
|
137
|
+
@stdout.on_data(&block)
|
138
|
+
end
|
139
|
+
|
140
|
+
# Sets block to be executed each time data is read from child process' standard error
|
141
|
+
#
|
142
|
+
# @return [void]
|
143
|
+
def on_stderr(&block)
|
144
|
+
@stderr.on_data(&block)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Makes the process manager close all the pipes it may have opened to communicate with the child process
|
148
|
+
#
|
149
|
+
# @return [void]
|
150
|
+
def close
|
151
|
+
[@stdin, @stdout, @stderr].each(&:close)
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
# Makes the process manager finish its run, closing opened FDs and reaping the child process
|
157
|
+
#
|
158
|
+
# @return [void]
|
159
|
+
def finish
|
160
|
+
close
|
161
|
+
_pid, status = Process.wait2(@pid)
|
162
|
+
@status = status.exitstatus
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
@@ -6,9 +6,9 @@ module Proxy::Dynflow
|
|
6
6
|
attr_reader :id
|
7
7
|
attr_writer :logger
|
8
8
|
|
9
|
-
def initialize(*_args, suspended_action: nil)
|
9
|
+
def initialize(*_args, suspended_action: nil, id: nil)
|
10
10
|
@suspended_action = suspended_action
|
11
|
-
@id = SecureRandom.uuid
|
11
|
+
@id = id || SecureRandom.uuid
|
12
12
|
initialize_continuous_outputs
|
13
13
|
end
|
14
14
|
|
@@ -93,6 +93,12 @@ module Proxy::Dynflow
|
|
93
93
|
def initialize_continuous_outputs
|
94
94
|
@continuous_output = ::Proxy::Dynflow::ContinuousOutput.new
|
95
95
|
end
|
96
|
+
|
97
|
+
def run_refresh_output
|
98
|
+
logger.debug('refreshing runner on demand')
|
99
|
+
refresh
|
100
|
+
generate_updates
|
101
|
+
end
|
96
102
|
end
|
97
103
|
end
|
98
104
|
end
|
@@ -43,6 +43,11 @@ module Proxy::Dynflow
|
|
43
43
|
plan_next_refresh
|
44
44
|
end
|
45
45
|
|
46
|
+
def refresh_output
|
47
|
+
@logger.debug("refresh output #{@runner.id}")
|
48
|
+
dispatch_updates(@runner.run_refresh_output)
|
49
|
+
end
|
50
|
+
|
46
51
|
def dispatch_updates(updates)
|
47
52
|
updates.each { |receiver, update| (receiver || @suspended_action) << update }
|
48
53
|
|
@@ -157,6 +162,12 @@ module Proxy::Dynflow
|
|
157
162
|
end
|
158
163
|
end
|
159
164
|
|
165
|
+
def refresh_output(runner_id)
|
166
|
+
synchronize do
|
167
|
+
@runner_actors[runner_id]&.tell([:refresh_output])
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
160
171
|
def handle_command_exception(*args)
|
161
172
|
synchronize { _handle_command_exception(*args) }
|
162
173
|
end
|
@@ -3,10 +3,10 @@ module Proxy::Dynflow
|
|
3
3
|
class Parent < Base
|
4
4
|
# targets = { identifier => { :execution_plan_id => "...", :run_step_id => id,
|
5
5
|
# :input => { ... } }
|
6
|
-
def initialize(targets = {}, suspended_action: nil)
|
6
|
+
def initialize(targets = {}, suspended_action: nil, id: nil)
|
7
7
|
@targets = targets
|
8
8
|
@exit_statuses = {}
|
9
|
-
super suspended_action: suspended_action
|
9
|
+
super suspended_action: suspended_action, id: id
|
10
10
|
end
|
11
11
|
|
12
12
|
def generate_updates
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'smart_proxy_dynflow/process_manager'
|
2
|
+
|
3
|
+
module Proxy::Dynflow
|
4
|
+
module Runner
|
5
|
+
module ProcessManagerCommand
|
6
|
+
def initialize_command(*command)
|
7
|
+
@process_manager = ProcessManager.new(command)
|
8
|
+
set_process_manager_callbacks(@process_manager)
|
9
|
+
@process_manager.start!
|
10
|
+
if @process_manager.done? && @process_manager.status == 255
|
11
|
+
publish_exception("Error running command '#{command.join(' ')}'", @process_manager.stderr.to_s)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def set_process_manager_callbacks(pm)
|
16
|
+
pm.on_stdout do |data|
|
17
|
+
publish_data(data, 'stdout')
|
18
|
+
''
|
19
|
+
end
|
20
|
+
pm.on_stderr do |data|
|
21
|
+
publish_data(data, 'stderr')
|
22
|
+
''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def refresh
|
27
|
+
@process_manager.process(timeout: 0.1)
|
28
|
+
publish_exit_status(@process_manager.status) if @process_manager.done?
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
@process_manager&.close
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -3,6 +3,11 @@ require 'smart_proxy_dynflow/runner'
|
|
3
3
|
module Proxy::Dynflow
|
4
4
|
module TaskLauncher
|
5
5
|
class AbstractGroup < Batch
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@runner_id = SecureRandom.uuid
|
9
|
+
end
|
10
|
+
|
6
11
|
def self.runner_class
|
7
12
|
raise NotImplementedError
|
8
13
|
end
|
@@ -13,7 +18,7 @@ module Proxy::Dynflow
|
|
13
18
|
|
14
19
|
def launch_children(parent, input_hash)
|
15
20
|
super(parent, input_hash)
|
16
|
-
trigger(parent, Action::BatchRunner, self, input_hash)
|
21
|
+
trigger(parent, Action::BatchRunner, self, input_hash, @runner_id)
|
17
22
|
end
|
18
23
|
|
19
24
|
def operation
|
@@ -36,7 +41,8 @@ module Proxy::Dynflow
|
|
36
41
|
end
|
37
42
|
|
38
43
|
def transform_input(input)
|
39
|
-
wipe_callback(input)
|
44
|
+
tmp = wipe_callback(input)
|
45
|
+
input.merge('action_input' => tmp['action_input'].merge(:runner_id => @runner_id))
|
40
46
|
end
|
41
47
|
|
42
48
|
def wipe_callback(input)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
---
|
2
2
|
:enabled: true
|
3
|
-
:database:
|
3
|
+
:database:
|
4
4
|
|
5
5
|
# Require a valid cert to access Dynflow console
|
6
6
|
# :console_auth: true
|
7
7
|
|
8
8
|
# Maximum age of execution plans to keep before having them cleaned
|
9
|
-
# by the execution plan cleaner (in seconds), defaults to
|
10
|
-
# :execution_plan_cleaner_age:
|
9
|
+
# by the execution plan cleaner (in seconds), defaults to 30 minutes
|
10
|
+
# :execution_plan_cleaner_age: 1800
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: smart_proxy_dynflow
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nečas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dynflow
|
@@ -146,8 +146,6 @@ files:
|
|
146
146
|
- Gemfile
|
147
147
|
- LICENSE
|
148
148
|
- bundler.d/dynflow.rb
|
149
|
-
- bundler.d/inspect_anything.rb
|
150
|
-
- bundler.d/x.local.rb
|
151
149
|
- lib/smart_proxy_dynflow.rb
|
152
150
|
- lib/smart_proxy_dynflow/action.rb
|
153
151
|
- lib/smart_proxy_dynflow/action/batch.rb
|
@@ -164,11 +162,12 @@ files:
|
|
164
162
|
- lib/smart_proxy_dynflow/core.rb
|
165
163
|
- lib/smart_proxy_dynflow/helpers.rb
|
166
164
|
- lib/smart_proxy_dynflow/http_config.ru
|
165
|
+
- lib/smart_proxy_dynflow/io_buffer.rb
|
167
166
|
- lib/smart_proxy_dynflow/log.rb
|
168
167
|
- lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb
|
169
168
|
- lib/smart_proxy_dynflow/otp_manager.rb
|
170
169
|
- lib/smart_proxy_dynflow/plugin.rb
|
171
|
-
- lib/smart_proxy_dynflow/process_manager.
|
170
|
+
- lib/smart_proxy_dynflow/process_manager.rb
|
172
171
|
- lib/smart_proxy_dynflow/proxy_adapter.rb
|
173
172
|
- lib/smart_proxy_dynflow/runner.rb
|
174
173
|
- lib/smart_proxy_dynflow/runner/base.rb
|
@@ -176,6 +175,7 @@ files:
|
|
176
175
|
- lib/smart_proxy_dynflow/runner/command_runner.rb
|
177
176
|
- lib/smart_proxy_dynflow/runner/dispatcher.rb
|
178
177
|
- lib/smart_proxy_dynflow/runner/parent.rb
|
178
|
+
- lib/smart_proxy_dynflow/runner/process_manager_command.rb
|
179
179
|
- lib/smart_proxy_dynflow/runner/update.rb
|
180
180
|
- lib/smart_proxy_dynflow/settings.rb
|
181
181
|
- lib/smart_proxy_dynflow/settings_loader.rb
|
@@ -208,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
208
|
- !ruby/object:Gem::Version
|
209
209
|
version: '0'
|
210
210
|
requirements: []
|
211
|
-
rubygems_version: 3.
|
211
|
+
rubygems_version: 3.2.26
|
212
212
|
signing_key:
|
213
213
|
specification_version: 4
|
214
214
|
summary: Dynflow runtime for Foreman smart proxy
|
@@ -1 +0,0 @@
|
|
1
|
-
# gem 'inspect_anything'
|
data/bundler.d/x.local.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
# gem 'dynflow', :path => '../dynflow'
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# TypeProf 0.21.2
|
2
|
-
|
3
|
-
# Classes
|
4
|
-
module Proxy
|
5
|
-
module Dynflow
|
6
|
-
class IOBuffer
|
7
|
-
@callback: nil
|
8
|
-
|
9
|
-
attr_accessor io: nil
|
10
|
-
attr_reader buffer: String
|
11
|
-
def initialize: (nil io) -> void
|
12
|
-
def on_data: -> nil
|
13
|
-
def to_io: -> IO
|
14
|
-
def to_s: -> String
|
15
|
-
def empty?: -> bool
|
16
|
-
def closed?: -> untyped
|
17
|
-
def close: -> nil
|
18
|
-
def read_available!: -> nil
|
19
|
-
def write_available!: -> nil
|
20
|
-
def add_data: (untyped data) -> String
|
21
|
-
|
22
|
-
private
|
23
|
-
def with_callback: (String? data) -> String?
|
24
|
-
end
|
25
|
-
|
26
|
-
class ProcessManager
|
27
|
-
@command: untyped
|
28
|
-
|
29
|
-
attr_reader stdin: IOBuffer
|
30
|
-
attr_reader stdout: IOBuffer
|
31
|
-
attr_reader stderr: IOBuffer
|
32
|
-
attr_reader pid: Integer
|
33
|
-
attr_reader status: Integer?
|
34
|
-
def initialize: (untyped command) -> void
|
35
|
-
def run!: -> ProcessManager
|
36
|
-
def start!: -> String
|
37
|
-
def started?: -> bool
|
38
|
-
def done?: -> bool
|
39
|
-
def close: -> [IOBuffer, IOBuffer, IOBuffer]
|
40
|
-
def process: (?timeout: nil) -> Array[untyped]?
|
41
|
-
def finish: -> Integer?
|
42
|
-
def on_stdout: -> nil
|
43
|
-
def on_stderr: -> nil
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|