foreman-tasks 4.0.0 → 5.0.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/.github/workflows/ruby_tests.yml +4 -3
- data/app/controllers/concerns/foreman_tasks/find_tasks_common.rb +14 -0
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +4 -6
- data/app/controllers/foreman_tasks/tasks_controller.rb +3 -11
- data/app/graphql/types/recurring_logic.rb +18 -0
- data/app/graphql/types/task.rb +25 -0
- data/app/graphql/types/triggering.rb +16 -0
- data/app/lib/actions/helpers/with_continuous_output.rb +1 -1
- data/app/lib/actions/middleware/load_setting_values.rb +35 -0
- data/app/lib/actions/observable_action.rb +1 -1
- data/app/lib/actions/proxy_action.rb +2 -4
- data/app/models/foreman_tasks/recurring_logic.rb +2 -0
- data/app/models/foreman_tasks/task.rb +30 -2
- data/app/models/foreman_tasks/triggering.rb +2 -0
- data/db/migrate/20180927120509_add_user_id.foreman_tasks.rb +4 -2
- data/foreman-tasks.gemspec +0 -1
- data/lib/foreman_tasks.rb +2 -5
- data/lib/foreman_tasks/continuous_output.rb +50 -0
- data/lib/foreman_tasks/engine.rb +7 -15
- data/lib/foreman_tasks/version.rb +1 -1
- data/locale/action_names.rb +3 -2
- data/locale/en/foreman_tasks.po +60 -27
- data/locale/foreman_tasks.pot +180 -132
- data/locale/fr/foreman_tasks.po +61 -28
- data/locale/ja/foreman_tasks.po +61 -28
- data/locale/zh_CN/foreman_tasks.po +61 -28
- data/test/controllers/api/tasks_controller_test.rb +16 -1
- data/test/controllers/tasks_controller_test.rb +2 -2
- data/test/factories/task_factory.rb +31 -4
- data/test/graphql/queries/recurring_logic_test.rb +28 -0
- data/test/graphql/queries/recurring_logics_query_test.rb +30 -0
- data/test/graphql/queries/task_query_test.rb +33 -0
- data/test/graphql/queries/tasks_query_test.rb +31 -0
- data/test/unit/actions/proxy_action_test.rb +4 -1
- data/test/unit/task_test.rb +54 -23
- data/test/unit/triggering_test.rb +2 -2
- metadata +16 -26
- data/test/core/unit/dispatcher_test.rb +0 -43
- data/test/core/unit/runner_test.rb +0 -129
- data/test/core/unit/task_launcher_test.rb +0 -56
- data/test/foreman_tasks_core_test_helper.rb +0 -4
- data/test/unit/otp_manager_test.rb +0 -77
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e48966ae6049eb85b3051151b7d15d48beb91a55b3580525fe114a3e045ad0f
|
4
|
+
data.tar.gz: 93da446f348f1a27600a79223ba4daab0e5e83417db711919392cfab2eedf57a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 740ff700f75e43154068d3838611dc7c7306d7f5da91e3d2d41a6fce5ea0ccde84bd5f027545f24874b8fb112d9848505ae2a535386ee2f356852695dacd61a2
|
7
|
+
data.tar.gz: 8f61c3c868af4b7c3d3173b728858537c72ccf695e60b63971ec19f8a675a6ad9c19ecdd3dedb2549e2eb27b7cde62a2be67ead4d737bb43b774301abb303228
|
@@ -12,7 +12,7 @@ jobs:
|
|
12
12
|
- name: Setup Ruby
|
13
13
|
uses: ruby/setup-ruby@v1
|
14
14
|
with:
|
15
|
-
ruby-version: 2.
|
15
|
+
ruby-version: 2.7
|
16
16
|
- name: Setup
|
17
17
|
run: |
|
18
18
|
gem install bundler
|
@@ -31,7 +31,7 @@ jobs:
|
|
31
31
|
fail-fast: false
|
32
32
|
matrix:
|
33
33
|
foreman-core-branch: [develop]
|
34
|
-
ruby-version: [2.5, 2.6]
|
34
|
+
ruby-version: [2.5, 2.6, 2.7]
|
35
35
|
node-version: [12]
|
36
36
|
steps:
|
37
37
|
- run: sudo apt-get update
|
@@ -60,6 +60,7 @@ jobs:
|
|
60
60
|
- name: Setup Bundler
|
61
61
|
run: |
|
62
62
|
echo "gem 'foreman-tasks', path: './foreman-tasks'" > bundler.d/foreman-tasks.local.rb
|
63
|
+
echo "gem 'sqlite3'" >> bundler.d/foreman-tasks.local.rb
|
63
64
|
gem install bundler
|
64
65
|
bundle config set without journald development console libvirt
|
65
66
|
bundle config set path vendor/bundle
|
@@ -70,5 +71,5 @@ jobs:
|
|
70
71
|
bundle exec rake db:migrate
|
71
72
|
- name: Run plugin tests
|
72
73
|
run: |
|
73
|
-
bundle exec rake test:
|
74
|
+
bundle exec rake test:foreman_tasks
|
74
75
|
bundle exec rake test TEST="test/unit/foreman/access_permissions_test.rb"
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
module FindTasksCommon
|
3
|
+
def search_query
|
4
|
+
[current_taxonomy_search, params[:search]].select(&:present?).join(' AND ')
|
5
|
+
end
|
6
|
+
|
7
|
+
def current_taxonomy_search
|
8
|
+
conditions = []
|
9
|
+
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
10
|
+
conditions << "location_id = #{Location.current.id}" if Location.current
|
11
|
+
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
module ForemanTasks
|
2
2
|
module Api
|
3
3
|
class TasksController < ::Api::V2::BaseController
|
4
|
+
include ForemanTasks::FindTasksCommon
|
4
5
|
include ::Foreman::Controller::SmartProxyAuth
|
5
6
|
add_smart_proxy_filters :callback, :features => 'Dynflow'
|
6
7
|
|
@@ -51,7 +52,7 @@ module ForemanTasks
|
|
51
52
|
In case :type = 'resource', what resource id we're searching the tasks for
|
52
53
|
DESC
|
53
54
|
param :action_types, [String], :desc => <<-DESC
|
54
|
-
Return just tasks of given action type, e.g. ["Actions::Katello::Repository::Synchronize"]
|
55
|
+
Return just tasks of given action type, e.g. `["Actions::Katello::Repository::Synchronize"]`
|
55
56
|
DESC
|
56
57
|
param :active_only, :bool
|
57
58
|
param :page, String
|
@@ -195,7 +196,7 @@ module ForemanTasks
|
|
195
196
|
end
|
196
197
|
|
197
198
|
def search_options
|
198
|
-
[
|
199
|
+
[search_query, {}]
|
199
200
|
end
|
200
201
|
|
201
202
|
def_param_group :callback_target do
|
@@ -253,10 +254,7 @@ module ForemanTasks
|
|
253
254
|
if search_params[:user_id].blank?
|
254
255
|
raise BadRequest, _('User search_params requires user_id to be specified')
|
255
256
|
end
|
256
|
-
scope.
|
257
|
-
{ name: ::ForemanTasks::Lock::OWNER_LOCK_NAME,
|
258
|
-
resource_type: 'User',
|
259
|
-
resource_id: search_params[:user_id] })
|
257
|
+
scope.search_for("user_id = #{search_params[:user_id]}")
|
260
258
|
when 'resource'
|
261
259
|
if search_params[:resource_type].blank? || search_params[:resource_id].blank?
|
262
260
|
raise BadRequest,
|
@@ -2,6 +2,7 @@ module ForemanTasks
|
|
2
2
|
class TasksController < ::ApplicationController
|
3
3
|
include Foreman::Controller::AutoCompleteSearch
|
4
4
|
include Foreman::Controller::CsvResponder
|
5
|
+
include ForemanTasks::FindTasksCommon
|
5
6
|
|
6
7
|
def show
|
7
8
|
@task = resource_base.find(params[:id])
|
@@ -126,19 +127,10 @@ module ForemanTasks
|
|
126
127
|
end
|
127
128
|
|
128
129
|
def filter(scope, paginate: true)
|
129
|
-
search = current_taxonomy_search
|
130
|
-
search = [search, params[:search]].select(&:present?).join(' AND ')
|
131
130
|
scope = DashboardTableFilter.new(scope, params).scope
|
132
|
-
scope = scope.search_for(
|
133
|
-
scope = scope.paginate(:
|
131
|
+
scope = scope.search_for(search_query, order: params[:order])
|
132
|
+
scope = scope.paginate(page: params[:page], per_page: params[:per_page]) if paginate
|
134
133
|
scope.distinct
|
135
134
|
end
|
136
|
-
|
137
|
-
def current_taxonomy_search
|
138
|
-
conditions = []
|
139
|
-
conditions << "organization_id = #{Organization.current.id}" if Organization.current
|
140
|
-
conditions << "location_id = #{Location.current.id}" if Location.current
|
141
|
-
conditions.empty? ? '' : "(#{conditions.join(' AND ')})"
|
142
|
-
end
|
143
135
|
end
|
144
136
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Types
|
2
|
+
class RecurringLogic < Types::BaseObject
|
3
|
+
description 'A Recurring Logic'
|
4
|
+
model_class ::ForemanTasks::RecurringLogic
|
5
|
+
|
6
|
+
global_id_field :id
|
7
|
+
field :cron_line, String
|
8
|
+
field :end_time, GraphQL::Types::ISO8601DateTime
|
9
|
+
field :max_iteration, Integer
|
10
|
+
field :iteration, Integer
|
11
|
+
field :state, String
|
12
|
+
belongs_to :triggering, Types::Triggering
|
13
|
+
|
14
|
+
def self.graphql_definition
|
15
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::RecurringLogic') }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Types
|
2
|
+
class Task < Types::BaseObject
|
3
|
+
description 'A Task'
|
4
|
+
model_class ::ForemanTasks::Task
|
5
|
+
|
6
|
+
global_id_field :id
|
7
|
+
field :type, String
|
8
|
+
field :label, String
|
9
|
+
field :started_at, GraphQL::Types::ISO8601DateTime
|
10
|
+
field :ended_at, GraphQL::Types::ISO8601DateTime
|
11
|
+
field :state, String
|
12
|
+
field :result, String
|
13
|
+
field :external_id, String
|
14
|
+
field :parent_task_id, String
|
15
|
+
field :start_at, GraphQL::Types::ISO8601DateTime
|
16
|
+
field :start_before, GraphQL::Types::ISO8601DateTime
|
17
|
+
field :action, String
|
18
|
+
field :user_id, Integer
|
19
|
+
field :state_updated_at, GraphQL::Types::ISO8601DateTime
|
20
|
+
|
21
|
+
def self.graphql_definition
|
22
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Task') }
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Types
|
2
|
+
class Triggering < Types::BaseObject
|
3
|
+
description 'A Task Triggering'
|
4
|
+
model_class ::ForemanTasks::Triggering
|
5
|
+
|
6
|
+
global_id_field :id
|
7
|
+
field :mode, String
|
8
|
+
field :start_at, GraphQL::Types::ISO8601DateTime
|
9
|
+
field :start_before, GraphQL::Types::ISO8601DateTime
|
10
|
+
field :recurring_logic, Types::RecurringLogic
|
11
|
+
|
12
|
+
def self.graphql_definition
|
13
|
+
super.tap { |type| type.instance_variable_set(:@name, 'ForemanTasks::Triggering') }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -8,7 +8,7 @@ module Actions
|
|
8
8
|
end
|
9
9
|
|
10
10
|
def continuous_output
|
11
|
-
continuous_output = ::
|
11
|
+
continuous_output = ::ForemanTasks::ContinuousOutput.new
|
12
12
|
continuous_output_providers.each do |continous_output_provider|
|
13
13
|
continous_output_provider.fill_continuous_output(continuous_output)
|
14
14
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Actions
|
2
|
+
module Middleware
|
3
|
+
class LoadSettingValues < ::Dynflow::Middleware
|
4
|
+
# ::Actions::Middleware::LoadSettingValues
|
5
|
+
#
|
6
|
+
# A middleware to ensure we load current setting values
|
7
|
+
|
8
|
+
def delay(*args)
|
9
|
+
reload_setting_values
|
10
|
+
pass(*args)
|
11
|
+
end
|
12
|
+
|
13
|
+
def plan(*args)
|
14
|
+
reload_setting_values
|
15
|
+
pass(*args)
|
16
|
+
end
|
17
|
+
|
18
|
+
def run(*args)
|
19
|
+
reload_setting_values
|
20
|
+
pass(*args)
|
21
|
+
end
|
22
|
+
|
23
|
+
def finalize(*args)
|
24
|
+
reload_setting_values
|
25
|
+
pass(*args)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def reload_setting_values
|
31
|
+
::Foreman.settings.load_values
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -70,7 +70,7 @@ module Actions
|
|
70
70
|
apipie :class, "An common ancestor action for observable actions" do
|
71
71
|
name 'Actions::ObservableAction'
|
72
72
|
refs 'Actions::ObservableAction'
|
73
|
-
sections only: %w[webhooks]
|
73
|
+
sections only: %w[all webhooks]
|
74
74
|
property :task, object_of: 'Task', desc: 'Returns the task to which this action belongs'
|
75
75
|
end
|
76
76
|
class Jail < Safemode::Jail
|
@@ -218,10 +218,8 @@ module Actions
|
|
218
218
|
end
|
219
219
|
|
220
220
|
def get_proxy_data(response)
|
221
|
-
|
222
|
-
|
223
|
-
end
|
224
|
-
proxy_data.fetch('output', {})
|
221
|
+
response['actions'].detect { |action| action.fetch('input', {})['task_id'] == task.id }
|
222
|
+
.try(:fetch, 'output', {}) || {}
|
225
223
|
end
|
226
224
|
|
227
225
|
def proxy_version(proxy)
|
@@ -5,6 +5,8 @@ module ForemanTasks
|
|
5
5
|
include Authorizable
|
6
6
|
extend Search
|
7
7
|
|
8
|
+
graphql_type '::Types::Task'
|
9
|
+
|
8
10
|
def check_permissions_after_save
|
9
11
|
# there's no create_tasks permission, tasks are created as a result of internal actions, in such case we
|
10
12
|
# don't do authorization, that should have been performed on wrapping action level
|
@@ -89,7 +91,7 @@ module ForemanTasks
|
|
89
91
|
property :ended_at, ActiveSupport::TimeWithZone, desc: 'Returns date with time the task ended at'
|
90
92
|
end
|
91
93
|
class Jail < Safemode::Jail
|
92
|
-
allow :started_at, :ended_at, :result, :state, :label, :main_action, :
|
94
|
+
allow :started_at, :ended_at, :result, :state, :label, :main_action, :action_continuous_output
|
93
95
|
end
|
94
96
|
|
95
97
|
def input
|
@@ -249,12 +251,38 @@ module ForemanTasks
|
|
249
251
|
parts.join(' ').strip
|
250
252
|
end
|
251
253
|
|
252
|
-
def
|
254
|
+
def action_continuous_output
|
253
255
|
return unless main_action.is_a?(Actions::Helpers::WithContinuousOutput)
|
254
256
|
main_action.continuous_output.sort!
|
255
257
|
main_action.continuous_output.raw_outputs
|
256
258
|
end
|
257
259
|
|
260
|
+
def self.latest_tasks_by_resource_ids(label, resource_type, resource_ids)
|
261
|
+
tasks = arel_table
|
262
|
+
links = ForemanTasks::Link.arel_table
|
263
|
+
started_at = tasks[:started_at]
|
264
|
+
resource_id = links[:resource_id]
|
265
|
+
|
266
|
+
base_combined_table = tasks
|
267
|
+
.join(links).on(tasks[:id].eq(links[:task_id]))
|
268
|
+
.where(tasks[:label].eq(label)
|
269
|
+
.and(links[:resource_type].eq(resource_type))
|
270
|
+
.and(links[:resource_id].in(resource_ids)))
|
271
|
+
|
272
|
+
grouped = base_combined_table.project(
|
273
|
+
started_at.maximum.as('started_at_max'),
|
274
|
+
resource_id
|
275
|
+
).group(resource_id).order(resource_id).as('grouped')
|
276
|
+
|
277
|
+
max_per_resource_id = tasks
|
278
|
+
.join(links).on(tasks[:id].eq(links[:task_id]))
|
279
|
+
.join(grouped).on(grouped[:started_at_max].eq(started_at).and(grouped[:resource_id].eq(resource_id)))
|
280
|
+
.distinct
|
281
|
+
.project(tasks[Arel.star], grouped[:resource_id])
|
282
|
+
|
283
|
+
find_by_sql(max_per_resource_id.to_sql).index_by(&:resource_id)
|
284
|
+
end
|
285
|
+
|
258
286
|
protected
|
259
287
|
|
260
288
|
def generate_id
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class AddUserId < ActiveRecord::Migration[5.0]
|
2
|
+
OWNER_LOCK_NAME = :task_owner
|
3
|
+
|
2
4
|
def up
|
3
5
|
add_reference :foreman_tasks_tasks, :user, :foreign_key => true
|
4
6
|
|
@@ -29,7 +31,7 @@ class AddUserId < ActiveRecord::Migration[5.0]
|
|
29
31
|
private
|
30
32
|
|
31
33
|
def create_lock(user_id, task)
|
32
|
-
ForemanTasks::Lock.new(:name =>
|
34
|
+
ForemanTasks::Lock.new(:name => OWNER_LOCK_NAME,
|
33
35
|
:resource_type => User.name,
|
34
36
|
:resource_id => user_id,
|
35
37
|
:task_id => task.id,
|
@@ -37,6 +39,6 @@ class AddUserId < ActiveRecord::Migration[5.0]
|
|
37
39
|
end
|
38
40
|
|
39
41
|
def user_locks
|
40
|
-
ForemanTasks::Lock.where(:name =>
|
42
|
+
ForemanTasks::Lock.where(:name => OWNER_LOCK_NAME)
|
41
43
|
end
|
42
44
|
end
|
data/foreman-tasks.gemspec
CHANGED
@@ -29,7 +29,6 @@ same resource. It also optionally provides Dynflow infrastructure for using it f
|
|
29
29
|
s.extra_rdoc_files = Dir['README*', 'LICENSE']
|
30
30
|
|
31
31
|
s.add_dependency "dynflow", '>= 1.2.3'
|
32
|
-
s.add_dependency "foreman-tasks-core"
|
33
32
|
s.add_dependency "get_process_mem" # for memory polling
|
34
33
|
s.add_dependency "parse-cron", '~> 0.1.4'
|
35
34
|
s.add_dependency "sinatra" # for Dynflow web console
|
data/lib/foreman_tasks.rb
CHANGED
@@ -6,17 +6,14 @@ require 'foreman_tasks/dynflow/configuration'
|
|
6
6
|
require 'foreman_tasks/triggers'
|
7
7
|
require 'foreman_tasks/authorizer_ext'
|
8
8
|
require 'foreman_tasks/cleaner'
|
9
|
+
require 'foreman_tasks/continuous_output'
|
9
10
|
|
10
11
|
module ForemanTasks
|
11
12
|
extend Algebrick::TypeCheck
|
12
13
|
extend Algebrick::Matching
|
13
14
|
|
14
15
|
def self.dynflow
|
15
|
-
@dynflow ||=
|
16
|
-
world = ForemanTasks::Dynflow.new(nil, ForemanTasks::Dynflow::Configuration.new)
|
17
|
-
ForemanTasksCore.dynflow_setup(world) if defined?(ForemanTasksCore)
|
18
|
-
world
|
19
|
-
end
|
16
|
+
@dynflow ||= ForemanTasks::Dynflow.new(nil, ForemanTasks::Dynflow::Configuration.new)
|
20
17
|
end
|
21
18
|
|
22
19
|
def self.trigger(action, *args, &block)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module ForemanTasks
|
2
|
+
class ContinuousOutput
|
3
|
+
attr_accessor :raw_outputs
|
4
|
+
|
5
|
+
def initialize(raw_outputs = [])
|
6
|
+
@raw_outputs = []
|
7
|
+
raw_outputs.each { |raw_output| add_raw_output(raw_output) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_raw_output(raw_output)
|
11
|
+
missing_args = %w[output_type output timestamp] - raw_output.keys
|
12
|
+
unless missing_args.empty?
|
13
|
+
raise ArgumentError, "Missing args for raw output: #{missing_args.inspect}"
|
14
|
+
end
|
15
|
+
@raw_outputs << raw_output
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
@raw_outputs.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_timestamp
|
23
|
+
return if @raw_outputs.empty?
|
24
|
+
@raw_outputs.last.fetch('timestamp')
|
25
|
+
end
|
26
|
+
|
27
|
+
def sort!
|
28
|
+
@raw_outputs.sort_by! { |record| record['timestamp'].to_f }
|
29
|
+
end
|
30
|
+
|
31
|
+
def humanize
|
32
|
+
sort!
|
33
|
+
raw_outputs.map { |output| output['output'] }.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_exception(context, exception, timestamp = Time.now.getlocal)
|
37
|
+
add_output(context + ": #{exception.class} - #{exception.message}", 'debug', timestamp)
|
38
|
+
end
|
39
|
+
|
40
|
+
def add_output(*args)
|
41
|
+
add_raw_output(self.class.format_output(*args))
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.format_output(message, type = 'debug', timestamp = Time.now.getlocal)
|
45
|
+
{ 'output_type' => type,
|
46
|
+
'output' => message,
|
47
|
+
'timestamp' => timestamp.to_f }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|