smart_proxy_dynflow 0.2.2 → 0.5.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.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +6 -21
  3. data/lib/smart_proxy_dynflow.rb +16 -1
  4. data/lib/smart_proxy_dynflow/action.rb +12 -0
  5. data/lib/smart_proxy_dynflow/action/batch.rb +21 -0
  6. data/lib/smart_proxy_dynflow/action/batch_callback.rb +20 -0
  7. data/lib/smart_proxy_dynflow/action/batch_runner.rb +14 -0
  8. data/lib/smart_proxy_dynflow/action/output_collector.rb +8 -0
  9. data/lib/smart_proxy_dynflow/action/runner.rb +76 -0
  10. data/lib/smart_proxy_dynflow/action/shareable.rb +25 -0
  11. data/lib/smart_proxy_dynflow/action/single_runner_batch.rb +39 -0
  12. data/lib/smart_proxy_dynflow/api.rb +63 -40
  13. data/lib/smart_proxy_dynflow/callback.rb +69 -25
  14. data/lib/smart_proxy_dynflow/continuous_output.rb +50 -0
  15. data/lib/smart_proxy_dynflow/core.rb +121 -0
  16. data/lib/smart_proxy_dynflow/helpers.rb +52 -6
  17. data/lib/smart_proxy_dynflow/http_config.ru +4 -0
  18. data/lib/smart_proxy_dynflow/log.rb +52 -0
  19. data/lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb +59 -0
  20. data/lib/smart_proxy_dynflow/otp_manager.rb +36 -0
  21. data/lib/smart_proxy_dynflow/plugin.rb +13 -19
  22. data/lib/smart_proxy_dynflow/proxy_adapter.rb +1 -1
  23. data/lib/smart_proxy_dynflow/runner.rb +10 -0
  24. data/lib/smart_proxy_dynflow/runner/base.rb +98 -0
  25. data/lib/smart_proxy_dynflow/runner/command.rb +40 -0
  26. data/lib/smart_proxy_dynflow/runner/command_runner.rb +11 -0
  27. data/lib/smart_proxy_dynflow/runner/dispatcher.rb +191 -0
  28. data/lib/smart_proxy_dynflow/runner/parent.rb +57 -0
  29. data/lib/smart_proxy_dynflow/runner/update.rb +28 -0
  30. data/lib/smart_proxy_dynflow/settings.rb +9 -0
  31. data/lib/smart_proxy_dynflow/settings_loader.rb +53 -0
  32. data/lib/smart_proxy_dynflow/task_launcher.rb +9 -0
  33. data/lib/smart_proxy_dynflow/task_launcher/abstract.rb +44 -0
  34. data/lib/smart_proxy_dynflow/task_launcher/batch.rb +37 -0
  35. data/lib/smart_proxy_dynflow/task_launcher/group.rb +48 -0
  36. data/lib/smart_proxy_dynflow/task_launcher/single.rb +17 -0
  37. data/lib/smart_proxy_dynflow/task_launcher_registry.rb +31 -0
  38. data/lib/smart_proxy_dynflow/testing.rb +24 -0
  39. data/lib/smart_proxy_dynflow/ticker.rb +47 -0
  40. data/lib/smart_proxy_dynflow/version.rb +2 -2
  41. data/settings.d/dynflow.yml.example +7 -1
  42. metadata +54 -12
  43. data/lib/smart_proxy_dynflow/http_config_with_executor.ru +0 -8
@@ -0,0 +1,9 @@
1
+ # require 'ostruct'
2
+
3
+ module Proxy::Dynflow
4
+ class Settings
5
+ def self.instance
6
+ Proxy::Dynflow::Plugin.settings
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,53 @@
1
+ module Proxy::Dynflow
2
+ module SettingsLoader
3
+ def self.settings_registry
4
+ @settings_registry ||= {}
5
+ end
6
+
7
+ def self.name_to_settings
8
+ @name_to_settings ||= {}
9
+ end
10
+
11
+ def self.settings_keys
12
+ @settings_keys ||= []
13
+ end
14
+
15
+ def self.settings_registered?(name)
16
+ name_to_settings.key?(name)
17
+ end
18
+
19
+ def self.register_settings(names, object)
20
+ names = [names] unless names.is_a? Array
21
+ names.each do |name|
22
+ raise 'settings name has to be a symbol' unless name.is_a? Symbol
23
+ raise "settings #{name} already registered" if SettingsLoader.settings_registered?(name)
24
+ name_to_settings[name] = object
25
+ end
26
+ settings_registry[names] = object
27
+ end
28
+
29
+ def self.setup_settings(name, settings)
30
+ raise "Settings for #{name} were not registered" unless settings_registered?(name)
31
+ name_to_settings[name].initialize_settings(settings)
32
+ end
33
+
34
+ def register_settings(names, defaults = {})
35
+ SettingsLoader.register_settings(names, self)
36
+ @defaults = defaults
37
+ end
38
+
39
+ def initialize_settings(settings = {})
40
+ @settings = @defaults.merge(settings)
41
+ validate_settings!
42
+ end
43
+
44
+ def settings
45
+ raise "Settings for #{self} not initalized" unless @settings
46
+ @settings
47
+ end
48
+
49
+ def validate_settings!
50
+ raise 'Only symbols expected in keys' unless @settings.keys.all? { |key| key.is_a? Symbol }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,9 @@
1
+ module Proxy::Dynflow
2
+ module TaskLauncher
3
+ end
4
+ end
5
+
6
+ require 'smart_proxy_dynflow/task_launcher/abstract'
7
+ require 'smart_proxy_dynflow/task_launcher/single'
8
+ require 'smart_proxy_dynflow/task_launcher/batch'
9
+ require 'smart_proxy_dynflow/task_launcher/group'
@@ -0,0 +1,44 @@
1
+ module Proxy::Dynflow
2
+ module TaskLauncher
3
+ class Abstract
4
+ attr_reader :callback, :options, :results, :world
5
+ def initialize(world, callback, options = {})
6
+ @world = world
7
+ @callback = callback
8
+ @options = options
9
+ @results = {}
10
+ end
11
+
12
+ def launch!(_input)
13
+ raise NotImplementedError
14
+ end
15
+
16
+ def self.input_format; end
17
+
18
+ private
19
+
20
+ def format_result(result)
21
+ if result.triggered?
22
+ { :result => 'success', :task_id => result.execution_plan_id }
23
+ else
24
+ plan = world.persistence.load_execution_plan(result.id)
25
+ { :result => 'error', :errors => plan.errors }
26
+ end
27
+ end
28
+
29
+ def action_class(input)
30
+ options[:action_class_override] || ::Dynflow::Utils.constantize(input['action_class'])
31
+ end
32
+
33
+ def with_callback(input)
34
+ input.merge(:callback_host => callback)
35
+ end
36
+
37
+ def trigger(parent, klass, *input)
38
+ world.trigger do
39
+ world.plan_with_options(caller_action: parent, action_class: klass, args: input)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,37 @@
1
+ module Proxy::Dynflow
2
+ module TaskLauncher
3
+ class Batch < Abstract
4
+ def launch!(input)
5
+ trigger(nil, Proxy::Dynflow::Action::Batch, self, input)
6
+ end
7
+
8
+ def launch_children(parent, input_hash)
9
+ input_hash.each do |task_id, input|
10
+ launcher = child_launcher(parent)
11
+ launcher.launch!(transform_input(input))
12
+ results[task_id] = launcher.results
13
+ end
14
+ end
15
+
16
+ def prepare_batch(input_hash)
17
+ success_tasks = input_hash.select do |task_id, _input|
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
23
+ end
24
+
25
+ private
26
+
27
+ def child_launcher(parent)
28
+ Single.new(world, callback, :parent => parent)
29
+ end
30
+
31
+ # Identity by default
32
+ def transform_input(input)
33
+ input
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ require 'smart_proxy_dynflow/runner'
2
+
3
+ module Proxy::Dynflow
4
+ module TaskLauncher
5
+ class AbstractGroup < Batch
6
+ def self.runner_class
7
+ raise NotImplementedError
8
+ end
9
+
10
+ def launch!(input)
11
+ trigger(nil, Action::SingleRunnerBatch, self, input)
12
+ end
13
+
14
+ def launch_children(parent, input_hash)
15
+ super(parent, input_hash)
16
+ trigger(parent, Action::BatchRunner, self, input_hash)
17
+ end
18
+
19
+ def operation
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def runner_input(input)
24
+ input.reduce({}) do |acc, (id, input)|
25
+ input = { :execution_plan_id => results[id][:task_id],
26
+ :run_step_id => 2,
27
+ :input => input }
28
+ acc.merge(id => input)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def child_launcher(parent)
35
+ Single.new(world, callback, :parent => parent, :action_class_override => Action::OutputCollector)
36
+ end
37
+
38
+ def transform_input(input)
39
+ wipe_callback(input)
40
+ end
41
+
42
+ def wipe_callback(input)
43
+ callback = input['action_input']['callback']
44
+ input.merge('action_input' => input['action_input'].merge('callback' => nil, :task_id => callback['task_id']))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,17 @@
1
+ module Proxy::Dynflow
2
+ module TaskLauncher
3
+ class Single < Abstract
4
+ def self.input_format
5
+ { :action_class => "MyActionClass", :action_input => {} }
6
+ end
7
+
8
+ def launch!(input)
9
+ triggered = trigger(options[:parent],
10
+ action_class(input),
11
+ with_callback(input.fetch('action_input', {})))
12
+ @results = format_result(triggered)
13
+ triggered
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Proxy::Dynflow
2
+ class TaskLauncherRegistry
3
+ class << self
4
+ def register(name, launcher)
5
+ registry[name] = launcher
6
+ end
7
+
8
+ def fetch(name, default = nil)
9
+ if default.nil?
10
+ registry.fetch(name)
11
+ else
12
+ registry.fetch(name, default)
13
+ end
14
+ end
15
+
16
+ def key?(name)
17
+ registry.key?(name)
18
+ end
19
+
20
+ def operations
21
+ registry.keys
22
+ end
23
+
24
+ private
25
+
26
+ def registry
27
+ @registry ||= {}
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require 'dynflow/testing'
2
+
3
+ unless defined? DYNFLOW_TESTING_LOG_LEVEL
4
+ DYNFLOW_TESTING_LOG_LEVEL = 4
5
+ end
6
+
7
+ module Proxy::Dynflow
8
+ # Helper for usage in other dependent plugins that need Dynflow
9
+ # related things, such as testing instance of world etc.
10
+ module Testing
11
+ class << self
12
+ def create_world(&block)
13
+ Core.ensure_initialized
14
+ Core.instance.create_world do |config|
15
+ config.exit_on_terminate = false
16
+ config.auto_terminate = false
17
+ config.logger_adapter = ::Dynflow::LoggerAdapters::Simple.new $stderr, DYNFLOW_TESTING_LOG_LEVEL
18
+ config.execution_plan_cleaner = nil
19
+ yield(config) if block
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,47 @@
1
+ require 'dynflow'
2
+
3
+ module Proxy::Dynflow
4
+ class Ticker < ::Dynflow::Actor
5
+ attr_reader :clock
6
+
7
+ def initialize(clock, logger, refresh_interval)
8
+ @clock = clock
9
+ @logger = logger
10
+ @events = []
11
+ @refresh_interval = refresh_interval
12
+ plan_next_tick
13
+ end
14
+
15
+ def tick
16
+ @logger.debug("Ticker ticking for #{@events.size} events")
17
+ @events.each do |(target, args)|
18
+ pass_event(target, args)
19
+ end
20
+ @events = []
21
+ ensure
22
+ @planned = false
23
+ plan_next_tick
24
+ end
25
+
26
+ def add_event(target, args)
27
+ @events << [target, args]
28
+ plan_next_tick
29
+ end
30
+
31
+ private
32
+
33
+ def pass_event(target, args)
34
+ target.tell(args)
35
+ rescue => e
36
+ @logger.error("Failed passing event to #{target} with #{args}")
37
+ @logger.error(e)
38
+ end
39
+
40
+ def plan_next_tick
41
+ if !@planned && !@events.empty?
42
+ @clock.ping(reference, Time.now.getlocal + @refresh_interval, :tick)
43
+ @planned = true
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,5 +1,5 @@
1
1
  module Proxy
2
- class Dynflow
3
- VERSION = '0.2.2'.freeze
2
+ module Dynflow
3
+ VERSION = '0.5.0'.freeze
4
4
  end
5
5
  end
@@ -1,4 +1,10 @@
1
1
  ---
2
2
  :enabled: true
3
3
  :database: /var/lib/foreman-proxy/dynflow/dynflow.sqlite
4
- :core_url: 'http://127.0.0.1:8008'
4
+
5
+ # Require a valid cert to access Dynflow console
6
+ # :console_auth: true
7
+
8
+ # Maximum age of execution plans to keep before having them cleaned
9
+ # by the execution plan cleaner (in seconds), defaults to 24 hours
10
+ # :execution_plan_cleaner_age: 86400
metadata CHANGED
@@ -1,27 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smart_proxy_dynflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Nečas
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-01-08 00:00:00.000000000 Z
11
+ date: 2021-06-15 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logging
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
- - - "~>"
31
+ - - ">="
18
32
  - !ruby/object:Gem::Version
19
33
  version: '1.7'
20
34
  type: :development
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
- - - "~>"
38
+ - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '1.7'
27
41
  - !ruby/object:Gem::Dependency
@@ -105,37 +119,65 @@ files:
105
119
  - LICENSE
106
120
  - bundler.plugins.d/dynflow.rb
107
121
  - lib/smart_proxy_dynflow.rb
122
+ - lib/smart_proxy_dynflow/action.rb
123
+ - lib/smart_proxy_dynflow/action/batch.rb
124
+ - lib/smart_proxy_dynflow/action/batch_callback.rb
125
+ - lib/smart_proxy_dynflow/action/batch_runner.rb
126
+ - lib/smart_proxy_dynflow/action/output_collector.rb
127
+ - lib/smart_proxy_dynflow/action/runner.rb
128
+ - lib/smart_proxy_dynflow/action/shareable.rb
129
+ - lib/smart_proxy_dynflow/action/single_runner_batch.rb
108
130
  - lib/smart_proxy_dynflow/api.rb
109
131
  - lib/smart_proxy_dynflow/callback.rb
132
+ - lib/smart_proxy_dynflow/continuous_output.rb
133
+ - lib/smart_proxy_dynflow/core.rb
110
134
  - lib/smart_proxy_dynflow/helpers.rb
111
135
  - lib/smart_proxy_dynflow/http_config.ru
112
- - lib/smart_proxy_dynflow/http_config_with_executor.ru
136
+ - lib/smart_proxy_dynflow/log.rb
137
+ - lib/smart_proxy_dynflow/middleware/keep_current_request_id.rb
138
+ - lib/smart_proxy_dynflow/otp_manager.rb
113
139
  - lib/smart_proxy_dynflow/plugin.rb
114
140
  - lib/smart_proxy_dynflow/proxy_adapter.rb
141
+ - lib/smart_proxy_dynflow/runner.rb
142
+ - lib/smart_proxy_dynflow/runner/base.rb
143
+ - lib/smart_proxy_dynflow/runner/command.rb
144
+ - lib/smart_proxy_dynflow/runner/command_runner.rb
145
+ - lib/smart_proxy_dynflow/runner/dispatcher.rb
146
+ - lib/smart_proxy_dynflow/runner/parent.rb
147
+ - lib/smart_proxy_dynflow/runner/update.rb
148
+ - lib/smart_proxy_dynflow/settings.rb
149
+ - lib/smart_proxy_dynflow/settings_loader.rb
150
+ - lib/smart_proxy_dynflow/task_launcher.rb
151
+ - lib/smart_proxy_dynflow/task_launcher/abstract.rb
152
+ - lib/smart_proxy_dynflow/task_launcher/batch.rb
153
+ - lib/smart_proxy_dynflow/task_launcher/group.rb
154
+ - lib/smart_proxy_dynflow/task_launcher/single.rb
155
+ - lib/smart_proxy_dynflow/task_launcher_registry.rb
156
+ - lib/smart_proxy_dynflow/testing.rb
157
+ - lib/smart_proxy_dynflow/ticker.rb
115
158
  - lib/smart_proxy_dynflow/version.rb
116
159
  - settings.d/dynflow.yml.example
117
160
  homepage: https://github.com/theforeman/smart_proxy_dynflow
118
161
  licenses:
119
162
  - GPL-3.0
120
163
  metadata: {}
121
- post_install_message:
164
+ post_install_message:
122
165
  rdoc_options: []
123
166
  require_paths:
124
167
  - lib
125
168
  required_ruby_version: !ruby/object:Gem::Requirement
126
169
  requirements:
127
- - - ">="
170
+ - - "~>"
128
171
  - !ruby/object:Gem::Version
129
- version: '0'
172
+ version: '2.5'
130
173
  required_rubygems_version: !ruby/object:Gem::Requirement
131
174
  requirements:
132
175
  - - ">="
133
176
  - !ruby/object:Gem::Version
134
177
  version: '0'
135
178
  requirements: []
136
- rubyforge_project:
137
- rubygems_version: 2.6.12
138
- signing_key:
179
+ rubygems_version: 3.1.2
180
+ signing_key:
139
181
  specification_version: 4
140
182
  summary: Dynflow runtime for Foreman smart proxy
141
183
  test_files: []