smart_proxy_dynflow 0.6.1 → 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.rb +7 -6
 - data/lib/smart_proxy_dynflow/action/batch_callback.rb +6 -1
 - data/lib/smart_proxy_dynflow/action/batch_runner.rb +3 -3
 - data/lib/smart_proxy_dynflow/action/external_polling.rb +16 -0
 - data/lib/smart_proxy_dynflow/action/output_collector.rb +1 -0
 - data/lib/smart_proxy_dynflow/action/runner.rb +10 -1
 - data/lib/smart_proxy_dynflow/action/single_runner_batch.rb +2 -14
 - 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 +7 -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/abstract.rb +10 -2
 - data/lib/smart_proxy_dynflow/task_launcher/batch.rb +12 -11
 - data/lib/smart_proxy_dynflow/task_launcher/group.rb +10 -4
 - data/lib/smart_proxy_dynflow/task_launcher/single.rb +3 -2
 - data/lib/smart_proxy_dynflow/version.rb +1 -1
 - data/settings.d/dynflow.yml.example +3 -3
 - metadata +7 -5
 - data/bundler.d/inspect_anything.rb +0 -1
 - data/bundler.d/x.local.rb +0 -1
 
    
        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
         
     | 
| 
         @@ -3,15 +3,16 @@ module Proxy::Dynflow::Action 
     | 
|
| 
       3 
3 
     | 
    
         
             
                include Dynflow::Action::WithSubPlans
         
     | 
| 
       4 
4 
     | 
    
         
             
                include Dynflow::Action::WithPollingSubPlans
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
     | 
    
         
            -
                # {  
     | 
| 
      
 6 
     | 
    
         
            +
                # { execution_plan_uuid => { :action_class => Klass, :input => input } }
         
     | 
| 
       7 
7 
     | 
    
         
             
                def plan(launcher, input_hash)
         
     | 
| 
       8 
     | 
    
         
            -
                   
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
                  plan_self :input_hash => input_hash,
         
     | 
| 
      
 9 
     | 
    
         
            +
                            :launcher => launcher.to_hash
         
     | 
| 
       10 
10 
     | 
    
         
             
                end
         
     | 
| 
       11 
11 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
                def  
     | 
| 
       13 
     | 
    
         
            -
                   
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
      
 12 
     | 
    
         
            +
                def create_sub_plans
         
     | 
| 
      
 13 
     | 
    
         
            +
                  Proxy::Dynflow::TaskLauncher::Abstract
         
     | 
| 
      
 14 
     | 
    
         
            +
                    .new_from_hash(world, input[:launcher])
         
     | 
| 
      
 15 
     | 
    
         
            +
                    .launch_children(self, input[:input_hash])
         
     | 
| 
       15 
16 
     | 
    
         
             
                end
         
     | 
| 
       16 
17 
     | 
    
         | 
| 
       17 
18 
     | 
    
         
             
                def rescue_strategy
         
     | 
| 
         @@ -1,7 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Proxy::Dynflow::Action
         
     | 
| 
       2 
2 
     | 
    
         
             
              class BatchCallback < ::Dynflow::Action
         
     | 
| 
       3 
3 
     | 
    
         
             
                def plan(input_hash, results)
         
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
      
 4 
     | 
    
         
            +
                  # In input_hash there are complete inputs for all the actions for which this is reporting
         
     | 
| 
      
 5 
     | 
    
         
            +
                  # Trim it down to only the bare minimum we actually need
         
     | 
| 
      
 6 
     | 
    
         
            +
                  callbacks = input_hash.reduce({}) do |acc, (key, value)|
         
     | 
| 
      
 7 
     | 
    
         
            +
                    acc.merge(key => value['action_input']['callback'])
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
                  plan_self :targets => callbacks, :results => results
         
     | 
| 
       5 
10 
     | 
    
         
             
                end
         
     | 
| 
       6 
11 
     | 
    
         | 
| 
       7 
12 
     | 
    
         
             
                def run
         
     | 
| 
         @@ -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,12 +74,16 @@ 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
         
     | 
| 
       75 
84 
     | 
    
         | 
| 
       76 
85 
     | 
    
         
             
                  def output_result
         
     | 
| 
       77 
     | 
    
         
            -
                    stored_output_chunks.map { |c| c[:chunk] }.reduce([], &:concat)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    (stored_output_chunks + (@pending_output_chunks || [])).map { |c| c[:chunk] }.reduce([], &:concat)
         
     | 
| 
       78 
87 
     | 
    
         
             
                  end
         
     | 
| 
       79 
88 
     | 
    
         
             
                end
         
     | 
| 
       80 
89 
     | 
    
         
             
              end
         
     | 
| 
         @@ -1,20 +1,8 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module Proxy::Dynflow::Action
         
     | 
| 
       2 
2 
     | 
    
         
             
              class SingleRunnerBatch < Batch
         
     | 
| 
       3 
3 
     | 
    
         
             
                def plan(launcher, input_hash)
         
     | 
| 
       4 
     | 
    
         
            -
                   
     | 
| 
       5 
     | 
    
         
            -
                   
     | 
| 
       6 
     | 
    
         
            -
                    results = plan_self
         
     | 
| 
       7 
     | 
    
         
            -
                    plan_action BatchCallback, launcher.prepare_batch(input_hash), results.output[:results]
         
     | 
| 
       8 
     | 
    
         
            -
                  end
         
     | 
| 
       9 
     | 
    
         
            -
                end
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                def run(event = nil)
         
     | 
| 
       12 
     | 
    
         
            -
                  super unless event == Dynflow::Action::Skip
         
     | 
| 
       13 
     | 
    
         
            -
                end
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                def initiate
         
     | 
| 
       16 
     | 
    
         
            -
                  ping suspended_action
         
     | 
| 
       17 
     | 
    
         
            -
                  wait_for_sub_plans sub_plans
         
     | 
| 
      
 4 
     | 
    
         
            +
                  results = super
         
     | 
| 
      
 5 
     | 
    
         
            +
                  plan_action BatchCallback, input_hash, results.output[:results]
         
     | 
| 
       18 
6 
     | 
    
         
             
                end
         
     | 
| 
       19 
7 
     | 
    
         | 
| 
       20 
8 
     | 
    
         
             
                def check_for_errors!(optional = true)
         
     | 
| 
         @@ -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,9 +11,11 @@ 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 
     | 
    
         
            +
                capability(proc { self.available_operations })
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
       17 
19 
     | 
    
         
             
                after_activation do
         
     | 
| 
       18 
20 
     | 
    
         
             
                  require 'smart_proxy_dynflow/settings_loader'
         
     | 
| 
       19 
21 
     | 
    
         
             
                  require 'smart_proxy_dynflow/otp_manager'
         
     | 
| 
         @@ -25,5 +27,9 @@ module Proxy::Dynflow 
     | 
|
| 
       25 
27 
     | 
    
         | 
| 
       26 
28 
     | 
    
         
             
                  Proxy::Dynflow::Core.ensure_initialized
         
     | 
| 
       27 
29 
     | 
    
         
             
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                def self.available_operations
         
     | 
| 
      
 32 
     | 
    
         
            +
                  TaskLauncherRegistry.operations
         
     | 
| 
      
 33 
     | 
    
         
            +
                end
         
     | 
| 
       28 
34 
     | 
    
         
             
              end
         
     | 
| 
       29 
35 
     | 
    
         
             
            end
         
     | 
| 
         @@ -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
         
     | 
| 
         @@ -15,6 +15,14 @@ module Proxy::Dynflow 
     | 
|
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
                  def self.input_format; end
         
     | 
| 
       17 
17 
     | 
    
         | 
| 
      
 18 
     | 
    
         
            +
                  def to_hash
         
     | 
| 
      
 19 
     | 
    
         
            +
                    { :class => self.class.to_s, :callback => callback, :options => options }
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def self.new_from_hash(world, hash)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    ::Dynflow::Utils.constantize(hash[:class]).new(world, hash[:callback], hash[:options])
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
       18 
26 
     | 
    
         
             
                  private
         
     | 
| 
       19 
27 
     | 
    
         | 
| 
       20 
28 
     | 
    
         
             
                  def format_result(result)
         
     | 
| 
         @@ -34,9 +42,9 @@ module Proxy::Dynflow 
     | 
|
| 
       34 
42 
     | 
    
         
             
                    input.merge(:callback_host => callback)
         
     | 
| 
       35 
43 
     | 
    
         
             
                  end
         
     | 
| 
       36 
44 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
                  def trigger(parent, klass, *input)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  def trigger(parent, klass, *input, id: nil)
         
     | 
| 
       38 
46 
     | 
    
         
             
                    world.trigger do
         
     | 
| 
       39 
     | 
    
         
            -
                      world.plan_with_options(caller_action: parent, action_class: klass, args: input)
         
     | 
| 
      
 47 
     | 
    
         
            +
                      world.plan_with_options(caller_action: parent, action_class: klass, args: input, id: id)
         
     | 
| 
       40 
48 
     | 
    
         
             
                    end
         
     | 
| 
       41 
49 
     | 
    
         
             
                  end
         
     | 
| 
       42 
50 
     | 
    
         
             
                end
         
     | 
| 
         @@ -2,36 +2,37 @@ module Proxy::Dynflow 
     | 
|
| 
       2 
2 
     | 
    
         
             
              module TaskLauncher
         
     | 
| 
       3 
3 
     | 
    
         
             
                class Batch < Abstract
         
     | 
| 
       4 
4 
     | 
    
         
             
                  def launch!(input)
         
     | 
| 
       5 
     | 
    
         
            -
                    trigger(nil,  
     | 
| 
      
 5 
     | 
    
         
            +
                    plan = trigger(nil, action_class, self, input)
         
     | 
| 
      
 6 
     | 
    
         
            +
                    results[:parent] = format_result(plan)
         
     | 
| 
       6 
7 
     | 
    
         
             
                  end
         
     | 
| 
       7 
8 
     | 
    
         | 
| 
       8 
9 
     | 
    
         
             
                  def launch_children(parent, input_hash)
         
     | 
| 
       9 
     | 
    
         
            -
                    input_hash. 
     | 
| 
      
 10 
     | 
    
         
            +
                    input_hash.map do |task_id, input|
         
     | 
| 
       10 
11 
     | 
    
         
             
                      launcher = child_launcher(parent)
         
     | 
| 
       11 
     | 
    
         
            -
                      launcher.launch!(transform_input(input))
         
     | 
| 
      
 12 
     | 
    
         
            +
                      triggered = launcher.launch!(transform_input(input), id: task_id)
         
     | 
| 
       12 
13 
     | 
    
         
             
                      results[task_id] = launcher.results
         
     | 
| 
      
 14 
     | 
    
         
            +
                      triggered
         
     | 
| 
       13 
15 
     | 
    
         
             
                    end
         
     | 
| 
       14 
16 
     | 
    
         
             
                  end
         
     | 
| 
       15 
17 
     | 
    
         | 
| 
       16 
18 
     | 
    
         
             
                  def prepare_batch(input_hash)
         
     | 
| 
       17 
     | 
    
         
            -
                     
     | 
| 
       18 
     | 
    
         
            -
                      results[task_id][:result] == 'success'
         
     | 
| 
       19 
     | 
    
         
            -
                    end
         
     | 
| 
       20 
     | 
    
         
            -
                    success_tasks.reduce({}) do |acc, (key, value)|
         
     | 
| 
       21 
     | 
    
         
            -
                      acc.merge(results[key][:task_id] => value['action_input']['callback'])
         
     | 
| 
       22 
     | 
    
         
            -
                    end
         
     | 
| 
      
 19 
     | 
    
         
            +
                    input_hash
         
     | 
| 
       23 
20 
     | 
    
         
             
                  end
         
     | 
| 
       24 
21 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
                  private
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
22 
     | 
    
         
             
                  def child_launcher(parent)
         
     | 
| 
       28 
23 
     | 
    
         
             
                    Single.new(world, callback, :parent => parent)
         
     | 
| 
       29 
24 
     | 
    
         
             
                  end
         
     | 
| 
       30 
25 
     | 
    
         | 
| 
      
 26 
     | 
    
         
            +
                  private
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
       31 
28 
     | 
    
         
             
                  # Identity by default
         
     | 
| 
       32 
29 
     | 
    
         
             
                  def transform_input(input)
         
     | 
| 
       33 
30 
     | 
    
         
             
                    input
         
     | 
| 
       34 
31 
     | 
    
         
             
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def action_class
         
     | 
| 
      
 34 
     | 
    
         
            +
                    Proxy::Dynflow::Action::Batch
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
       35 
36 
     | 
    
         
             
                end
         
     | 
| 
       36 
37 
     | 
    
         
             
              end
         
     | 
| 
       37 
38 
     | 
    
         
             
            end
         
     | 
| 
         @@ -3,17 +3,22 @@ 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
         
     | 
| 
       9 
14 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                  def  
     | 
| 
       11 
     | 
    
         
            -
                     
     | 
| 
      
 15 
     | 
    
         
            +
                  def action_class
         
     | 
| 
      
 16 
     | 
    
         
            +
                    Action::SingleRunnerBatch
         
     | 
| 
       12 
17 
     | 
    
         
             
                  end
         
     | 
| 
       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)
         
     | 
| 
         @@ -5,10 +5,11 @@ module Proxy::Dynflow 
     | 
|
| 
       5 
5 
     | 
    
         
             
                    { :action_class => "MyActionClass", :action_input => {} }
         
     | 
| 
       6 
6 
     | 
    
         
             
                  end
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                  def launch!(input)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  def launch!(input, id: nil)
         
     | 
| 
       9 
9 
     | 
    
         
             
                    triggered = trigger(options[:parent],
         
     | 
| 
       10 
10 
     | 
    
         
             
                                        action_class(input),
         
     | 
| 
       11 
     | 
    
         
            -
                                        with_callback(input.fetch('action_input', {})) 
     | 
| 
      
 11 
     | 
    
         
            +
                                        with_callback(input.fetch('action_input', {})),
         
     | 
| 
      
 12 
     | 
    
         
            +
                                        id: id)
         
     | 
| 
       12 
13 
     | 
    
         
             
                    @results = format_result(triggered)
         
     | 
| 
       13 
14 
     | 
    
         
             
                    triggered
         
     | 
| 
       14 
15 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -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,13 +146,12 @@ 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
         
     | 
| 
       154 
152 
     | 
    
         
             
            - lib/smart_proxy_dynflow/action/batch_callback.rb
         
     | 
| 
       155 
153 
     | 
    
         
             
            - lib/smart_proxy_dynflow/action/batch_runner.rb
         
     | 
| 
      
 154 
     | 
    
         
            +
            - lib/smart_proxy_dynflow/action/external_polling.rb
         
     | 
| 
       156 
155 
     | 
    
         
             
            - lib/smart_proxy_dynflow/action/output_collector.rb
         
     | 
| 
       157 
156 
     | 
    
         
             
            - lib/smart_proxy_dynflow/action/runner.rb
         
     | 
| 
       158 
157 
     | 
    
         
             
            - lib/smart_proxy_dynflow/action/shareable.rb
         
     | 
| 
         @@ -163,10 +162,12 @@ files: 
     | 
|
| 
       163 
162 
     | 
    
         
             
            - lib/smart_proxy_dynflow/core.rb
         
     | 
| 
       164 
163 
     | 
    
         
             
            - lib/smart_proxy_dynflow/helpers.rb
         
     | 
| 
       165 
164 
     | 
    
         
             
            - lib/smart_proxy_dynflow/http_config.ru
         
     | 
| 
      
 165 
     | 
    
         
            +
            - lib/smart_proxy_dynflow/io_buffer.rb
         
     | 
| 
       166 
166 
     | 
    
         
             
            - lib/smart_proxy_dynflow/log.rb
         
     | 
| 
       167 
167 
     | 
    
         
             
            - lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb
         
     | 
| 
       168 
168 
     | 
    
         
             
            - lib/smart_proxy_dynflow/otp_manager.rb
         
     | 
| 
       169 
169 
     | 
    
         
             
            - lib/smart_proxy_dynflow/plugin.rb
         
     | 
| 
      
 170 
     | 
    
         
            +
            - lib/smart_proxy_dynflow/process_manager.rb
         
     | 
| 
       170 
171 
     | 
    
         
             
            - lib/smart_proxy_dynflow/proxy_adapter.rb
         
     | 
| 
       171 
172 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner.rb
         
     | 
| 
       172 
173 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner/base.rb
         
     | 
| 
         @@ -174,6 +175,7 @@ files: 
     | 
|
| 
       174 
175 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner/command_runner.rb
         
     | 
| 
       175 
176 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner/dispatcher.rb
         
     | 
| 
       176 
177 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner/parent.rb
         
     | 
| 
      
 178 
     | 
    
         
            +
            - lib/smart_proxy_dynflow/runner/process_manager_command.rb
         
     | 
| 
       177 
179 
     | 
    
         
             
            - lib/smart_proxy_dynflow/runner/update.rb
         
     | 
| 
       178 
180 
     | 
    
         
             
            - lib/smart_proxy_dynflow/settings.rb
         
     | 
| 
       179 
181 
     | 
    
         
             
            - lib/smart_proxy_dynflow/settings_loader.rb
         
     | 
| 
         @@ -206,7 +208,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement 
     | 
|
| 
       206 
208 
     | 
    
         
             
                - !ruby/object:Gem::Version
         
     | 
| 
       207 
209 
     | 
    
         
             
                  version: '0'
         
     | 
| 
       208 
210 
     | 
    
         
             
            requirements: []
         
     | 
| 
       209 
     | 
    
         
            -
            rubygems_version: 3. 
     | 
| 
      
 211 
     | 
    
         
            +
            rubygems_version: 3.2.26
         
     | 
| 
       210 
212 
     | 
    
         
             
            signing_key:
         
     | 
| 
       211 
213 
     | 
    
         
             
            specification_version: 4
         
     | 
| 
       212 
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'
         
     |