foreman-tasks 0.16.0 → 0.16.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.babelrc +4 -1
- data/.eslintrc +4 -13
- data/app/assets/stylesheets/foreman_tasks/application.css.scss +0 -37
- data/app/controllers/foreman_tasks/api/tasks_controller.rb +9 -5
- data/app/controllers/foreman_tasks/tasks_controller.rb +23 -19
- data/app/helpers/foreman_tasks/foreman_tasks_helper.rb +28 -29
- data/app/lib/actions/bulk_action.rb +1 -1
- data/app/models/foreman_tasks/task.rb +1 -1
- data/app/models/foreman_tasks/task/dynflow_task.rb +30 -0
- data/app/services/foreman_tasks/troubleshooting_help_generator.rb +4 -0
- data/app/views/foreman_tasks/api/tasks/details.json.rabl +18 -0
- data/app/views/foreman_tasks/layouts/react.html.erb +1 -1
- data/app/views/foreman_tasks/tasks/index.html.erb +3 -0
- data/app/views/foreman_tasks/tasks/show.html.erb +10 -134
- data/config/routes.rb +3 -0
- data/lib/foreman_tasks/engine.rb +1 -1
- data/lib/foreman_tasks/tasks/export_tasks.rake +1 -2
- data/lib/foreman_tasks/version.rb +1 -1
- data/package.json +6 -20
- data/test/controllers/tasks_controller_test.rb +11 -0
- data/test/helpers/foreman_tasks/foreman_tasks_helper_test.rb +39 -0
- data/test/unit/actions/bulk_action_test.rb +2 -0
- data/test/unit/task_test.rb +4 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Errors.js +60 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Locks.js +46 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Raw.js +73 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/RunningSteps.js +55 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/Task.js +202 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskHelper.js +38 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/TaskInfo.js +238 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Errors.test.js +36 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Locks.test.js +28 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Raw.test.js +27 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/RunningSteps.test.js +29 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/Task.test.js +18 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskHelper.test.js +77 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/TaskInfo.test.js +60 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Errors.test.js.snap +77 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Locks.test.js.snap +108 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Raw.test.js.snap +174 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/RunningSteps.test.js.snap +62 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/Task.test.js.snap +282 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskHelper.test.js.snap +37 -0
- data/webpack/ForemanTasks/Components/TaskDetails/Components/__tests__/__snapshots__/TaskInfo.test.js.snap +568 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.js +87 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.scss +63 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetails.stories.js +5 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsActions.js +109 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsConstants.js +18 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsReducer.js +44 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TaskDetailsSelectors.js +83 -0
- data/webpack/ForemanTasks/Components/TaskDetails/TasksDetailsHelper.js +1 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetails.test.js +12 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsActions.test.js +20 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/TaskDetailsReducer.test.js +33 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetails.test.js.snap +97 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsActions.test.js.snap +43 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/TaskDetailsReducer.test.js.snap +26 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/__snapshots__/integration.test.js.snap +122 -0
- data/webpack/ForemanTasks/Components/TaskDetails/__tests__/integration.test.js +63 -0
- data/webpack/ForemanTasks/Components/TaskDetails/index.js +77 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/PausedTasksCard.js +6 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/PausedTasksCard/__snapshots__/PausedTasksCard.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/RunningTasksCard.js +6 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/RunningTasksCard/__snapshots__/RunningTasksCard.test.js.snap +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.js +3 -6
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/ScheduledTasksCard.scss +0 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/ScheduledTasksCard/__snapshots__/ScheduledTasksCard.test.js.snap +3 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.js +2 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCard.scss +0 -4
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/StoppedTasksCardHelper.js +3 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/StoppedTasksCard/__snapshots__/StoppedTasksCard.test.js.snap +495 -54
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.js +1 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutCard/TasksDonutCard.scss +6 -0
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksCardsGrid/Components/TasksDonutChart/TasksDonutChart.scss +0 -3
- data/webpack/ForemanTasks/Components/TasksDashboard/Components/TasksLabelsRow/TasksLabelsRow.scss +1 -1
- data/webpack/ForemanTasks/Components/TasksDashboard/TasksDashboard.scss +2 -2
- data/webpack/ForemanTasks/Components/common/ClickConfirmation/index.js +83 -0
- data/webpack/ForemanTasks/ForemanTasksReducers.js +2 -0
- data/webpack/__mocks__/foremanReact/common/I18n.js +2 -0
- data/webpack/__mocks__/foremanReact/components/Layout/LayoutActions.js +2 -0
- data/webpack/index.js +5 -0
- metadata +46 -9
- data/app/views/foreman_tasks/tasks/_details.html.erb +0 -195
- data/app/views/foreman_tasks/tasks/_errors.html.erb +0 -42
- data/app/views/foreman_tasks/tasks/_locks.html.erb +0 -19
- data/app/views/foreman_tasks/tasks/_raw.html.erb +0 -28
- data/app/views/foreman_tasks/tasks/_running_steps.html.erb +0 -40
data/config/routes.rb
CHANGED
data/lib/foreman_tasks/engine.rb
CHANGED
@@ -58,7 +58,7 @@ module ForemanTasks
|
|
58
58
|
security_block :foreman_tasks do |_map|
|
59
59
|
permission :view_foreman_tasks, { :'foreman_tasks/tasks' => [:auto_complete_search, :sub_tasks, :index, :summary, :show],
|
60
60
|
:'foreman_tasks/react' => [:index],
|
61
|
-
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary] }, :resource_type => ForemanTasks::Task.name
|
61
|
+
:'foreman_tasks/api/tasks' => [:bulk_search, :show, :index, :summary, :details] }, :resource_type => ForemanTasks::Task.name
|
62
62
|
permission :edit_foreman_tasks, { :'foreman_tasks/tasks' => [:resume, :unlock, :force_unlock, :cancel_step, :cancel, :abort],
|
63
63
|
:'foreman_tasks/api/tasks' => [:bulk_resume] }, :resource_type => ForemanTasks::Task.name
|
64
64
|
|
@@ -211,7 +211,6 @@ namespace :foreman_tasks do
|
|
211
211
|
'javascripts/application.js',
|
212
212
|
'vendor/bootstrap/css/bootstrap.css',
|
213
213
|
'stylesheets/application.css'].each do |file|
|
214
|
-
|
215
214
|
filename = File.join(Gem::Specification.find_by_name('dynflow').gem_dir, 'web', 'assets', file) # rubocop:disable Rails/DynamicFindBy
|
216
215
|
FileUtils.copy_file(filename, File.join(tmp_dir, File.basename(file)))
|
217
216
|
end
|
@@ -244,6 +243,7 @@ namespace :foreman_tasks do
|
|
244
243
|
|
245
244
|
tasks = ForemanTasks::Task.search_for(filter)
|
246
245
|
|
246
|
+
puts _("Exporting all tasks matching filter #{filter}")
|
247
247
|
puts _("Gathering #{tasks.count} tasks.")
|
248
248
|
if format == 'html'
|
249
249
|
Dir.mktmpdir('task-export') do |tmp_dir|
|
@@ -255,7 +255,6 @@ namespace :foreman_tasks do
|
|
255
255
|
tasks.each_with_index do |task, count|
|
256
256
|
File.open(File.join(tmp_dir, "#{task.id}.html"), 'w') { |file| file.write(PageHelper.pagify(renderer.render_task(task))) }
|
257
257
|
puts "#{count + 1}/#{total}"
|
258
|
-
count += 1
|
259
258
|
end
|
260
259
|
|
261
260
|
File.open(File.join(tmp_dir, 'index.html'), 'w') { |file| file.write(PageHelper.pagify(PageHelper.generate_index(tasks))) }
|
data/package.json
CHANGED
@@ -21,17 +21,22 @@
|
|
21
21
|
"bugs": {
|
22
22
|
"url": "http://projects.theforeman.org/projects/foreman-tasks/issues"
|
23
23
|
},
|
24
|
+
"dependencies": {
|
25
|
+
"@theforeman/vendor": "^0.1.1",
|
26
|
+
"humanize-duration": "^3.20.1",
|
27
|
+
"react-intl": "^2.8.0"
|
28
|
+
},
|
24
29
|
"devDependencies": {
|
25
30
|
"@storybook/addon-actions": "^5.0.1",
|
26
31
|
"@storybook/addon-knobs": "^5.0.1",
|
27
32
|
"@storybook/react": "^5.0.1",
|
33
|
+
"@theforeman/vendor-dev": "^0.1.1",
|
28
34
|
"babel-cli": "^6.10.1",
|
29
35
|
"babel-core": "^6.26.3",
|
30
36
|
"babel-eslint": "^8.2.3",
|
31
37
|
"babel-jest": "^23.6.0",
|
32
38
|
"babel-loader": "^7.1.1",
|
33
39
|
"babel-plugin-dynamic-import-node": "^2.0.0",
|
34
|
-
"babel-plugin-lodash": "^3.3.4",
|
35
40
|
"babel-plugin-module-resolver": "^3.2.0",
|
36
41
|
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
37
42
|
"babel-plugin-transform-class-properties": "^6.24.1",
|
@@ -61,25 +66,6 @@
|
|
61
66
|
"stylelint-config-standard": "^18.0.0",
|
62
67
|
"surge": "^0.20.3"
|
63
68
|
},
|
64
|
-
"dependencies": {
|
65
|
-
"babel-polyfill": "^6.26.0",
|
66
|
-
"classnames": "^2.2.5",
|
67
|
-
"lodash": "^4.17.11",
|
68
|
-
"patternfly-react": "^2.29.0",
|
69
|
-
"prop-types": "^15.6.0",
|
70
|
-
"react": "^16.8.1",
|
71
|
-
"react-dom": "^16.8.1",
|
72
|
-
"react-redux": "^5.0.6",
|
73
|
-
"react-router": "^4.3.1",
|
74
|
-
"react-router-bootstrap": "^0.24.4",
|
75
|
-
"react-router-dom": "^4.3.1",
|
76
|
-
"redux": "^3.6.0",
|
77
|
-
"redux-thunk": "^2.3.0",
|
78
|
-
"reselect": "^3.0.1",
|
79
|
-
"seamless-immutable": "^7.1.2",
|
80
|
-
"urijs": "^1.19.1",
|
81
|
-
"uuid": "^3.3.2"
|
82
|
-
},
|
83
69
|
"jest": {
|
84
70
|
"verbose": true,
|
85
71
|
"testMatch": [
|
@@ -67,6 +67,17 @@ module ForemanTasks
|
|
67
67
|
session: set_session_user(User.current)
|
68
68
|
assert_response :not_found
|
69
69
|
end
|
70
|
+
|
71
|
+
it 'supports csv export' do
|
72
|
+
parent = FactoryBot.create(:some_task, :action => 'Some action')
|
73
|
+
child = FactoryBot.create(:some_task, :action => 'Child action')
|
74
|
+
child.parent_task_id = parent.id
|
75
|
+
child.save!
|
76
|
+
get(:sub_tasks, params: { id: parent.id, format: :csv }, session: set_session_user)
|
77
|
+
assert_response :success
|
78
|
+
assert_equal 2, response.body.lines.size
|
79
|
+
assert_include response.body.lines[1], 'Child action'
|
80
|
+
end
|
70
81
|
end
|
71
82
|
|
72
83
|
describe 'taxonomy scoping' do
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'foreman_tasks_test_helper'
|
2
|
+
|
3
|
+
module ForemanTasks
|
4
|
+
class ForemanTasksHelperTest < ActionView::TestCase
|
5
|
+
describe 'breadcrumb items' do
|
6
|
+
before do
|
7
|
+
self.class.send(:include, ForemanTasks::TasksHelper)
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'prepares items for index correctly' do
|
11
|
+
stubs(:action_name).returns('index')
|
12
|
+
items = breadcrumb_items
|
13
|
+
items.count.must_equal 1
|
14
|
+
items.first[:caption].must_equal 'Tasks'
|
15
|
+
items.first[:url].must_be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'prepares items for show correctly' do
|
19
|
+
@task = FactoryBot.build(:dynflow_task, :user_create_task)
|
20
|
+
@task.action = 'A task'
|
21
|
+
stubs(:action_name).returns('show')
|
22
|
+
items = breadcrumb_items
|
23
|
+
items.map { |i| i[:caption] }.must_equal ['Tasks', 'A task']
|
24
|
+
items.last[:url].must_be_nil
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'prepares items for sub tasks correctly' do
|
28
|
+
@task = FactoryBot.build(:dynflow_task, :user_create_task)
|
29
|
+
child = FactoryBot.build(:dynflow_task, :user_create_task)
|
30
|
+
@task.sub_tasks = [child]
|
31
|
+
@task.action = 'A task'
|
32
|
+
stubs(:action_name).returns('sub_tasks')
|
33
|
+
items = breadcrumb_items
|
34
|
+
items.map { |i| i[:caption] }.must_equal ['Tasks', 'A task', 'Sub tasks']
|
35
|
+
items.last[:url].must_be_nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -28,6 +28,7 @@ module ForemanTasks
|
|
28
28
|
end
|
29
29
|
|
30
30
|
specify 'it plans a task for each target' do
|
31
|
+
Target.expects(:unscoped).returns(Target)
|
31
32
|
Target.expects(:where).with(:id => targets.map(&:id)).returns(targets)
|
32
33
|
|
33
34
|
task.sub_tasks.count.must_equal targets.count
|
@@ -37,6 +38,7 @@ module ForemanTasks
|
|
37
38
|
end
|
38
39
|
|
39
40
|
specify 'it plans a task for each target even if target cannot be found' do
|
41
|
+
Target.expects(:unscoped).returns(Target)
|
40
42
|
Target.expects(:where).with(:id => targets.map(&:id)).returns(targets.take(4))
|
41
43
|
|
42
44
|
task.sub_tasks.count.must_equal targets.count
|
data/test/unit/task_test.rb
CHANGED
@@ -43,6 +43,10 @@ class TasksTest < ActiveSupport::TestCase
|
|
43
43
|
test 'can search the tasks by array' do
|
44
44
|
assert_equal [@task_one], ForemanTasks::Task.search_for("user ^ (this_user, #{@user_one.login}, that_user)")
|
45
45
|
end
|
46
|
+
|
47
|
+
test 'properly returns username' do
|
48
|
+
assert_equal @task_one.username, @user_one.login
|
49
|
+
end
|
46
50
|
end
|
47
51
|
|
48
52
|
describe 'state_updated_at' do
|
@@ -0,0 +1,60 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Alert } from 'patternfly-react';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
|
6
|
+
const Errors = ({ ...props }) => {
|
7
|
+
const { failedSteps, executionPlan } = props;
|
8
|
+
if (!executionPlan)
|
9
|
+
return (
|
10
|
+
<Alert type="error">{__('Execution plan data not available ')}</Alert>
|
11
|
+
);
|
12
|
+
if (!failedSteps.length)
|
13
|
+
return <Alert type="success">{__('No errors')}</Alert>;
|
14
|
+
return (
|
15
|
+
<div>
|
16
|
+
{failedSteps.map((step, i) => (
|
17
|
+
<Alert type="error" key={i}>
|
18
|
+
<span>{__('Action')}:</span>
|
19
|
+
<span>
|
20
|
+
<pre>{step.action_class}</pre>
|
21
|
+
</span>
|
22
|
+
<span>{__('Input')}:</span>
|
23
|
+
<span>
|
24
|
+
<pre>{step.input}</pre>
|
25
|
+
</span>
|
26
|
+
<span>{__('Output')}:</span>
|
27
|
+
<span>
|
28
|
+
<pre>{step.output}</pre>
|
29
|
+
</span>
|
30
|
+
{step.error && (
|
31
|
+
<React.Fragment>
|
32
|
+
<span>{__('Exception')}:</span>
|
33
|
+
<span>
|
34
|
+
<pre>
|
35
|
+
{step.error.exception_class}: {step.error.message}
|
36
|
+
</pre>
|
37
|
+
</span>
|
38
|
+
<span>{__('Backtrace')}:</span>
|
39
|
+
<span>
|
40
|
+
<pre>{step.error.backtrace.join('\n')}</pre>
|
41
|
+
</span>
|
42
|
+
</React.Fragment>
|
43
|
+
)}
|
44
|
+
</Alert>
|
45
|
+
))}
|
46
|
+
</div>
|
47
|
+
);
|
48
|
+
};
|
49
|
+
|
50
|
+
Errors.propTypes = {
|
51
|
+
failedSteps: PropTypes.array,
|
52
|
+
executionPlan: PropTypes.shape({}),
|
53
|
+
};
|
54
|
+
|
55
|
+
Errors.defaultProps = {
|
56
|
+
failedSteps: [],
|
57
|
+
executionPlan: {},
|
58
|
+
};
|
59
|
+
|
60
|
+
export default Errors;
|
@@ -0,0 +1,46 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Alert, Card, Row, Col } from 'patternfly-react';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
|
6
|
+
const Locks = ({ locks }) => (
|
7
|
+
<div>
|
8
|
+
<Alert type="info">
|
9
|
+
{__(
|
10
|
+
'You can find resource locks on this page. Exclusive lock marked with locked icon means that no other task can use locked resource while this task is running. Non-exclusive lock marked with unlocked icon means other tasks can access the resource freely, it is only used to indicate the relation of this task with the resource'
|
11
|
+
)}
|
12
|
+
</Alert>
|
13
|
+
<Card.Grid>
|
14
|
+
<Row>
|
15
|
+
{locks.map((lock, key) => (
|
16
|
+
<Col xs={6} sm={4} md={4} key={key}>
|
17
|
+
<Card className="card-pf-aggregate-status" accented>
|
18
|
+
<Card.Title>
|
19
|
+
<span
|
20
|
+
className={`fa ${
|
21
|
+
lock.exclusive ? 'fa-lock' : 'fa-unlock-alt'
|
22
|
+
}`}
|
23
|
+
/>
|
24
|
+
{lock.name}
|
25
|
+
</Card.Title>
|
26
|
+
<Card.Body>
|
27
|
+
{`${lock.resource_type} id:${lock.resource_id}`}
|
28
|
+
<br />
|
29
|
+
</Card.Body>
|
30
|
+
</Card>
|
31
|
+
</Col>
|
32
|
+
))}
|
33
|
+
</Row>
|
34
|
+
</Card.Grid>
|
35
|
+
</div>
|
36
|
+
);
|
37
|
+
|
38
|
+
Locks.propTypes = {
|
39
|
+
locks: PropTypes.array,
|
40
|
+
};
|
41
|
+
|
42
|
+
Locks.defaultProps = {
|
43
|
+
locks: [],
|
44
|
+
};
|
45
|
+
|
46
|
+
export default Locks;
|
@@ -0,0 +1,73 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
4
|
+
|
5
|
+
import { durationInWords } from './TaskHelper';
|
6
|
+
|
7
|
+
const Raw = ({ id, label, startedAt, endedAt, input, output, externalId }) => (
|
8
|
+
<div>
|
9
|
+
<div>
|
10
|
+
<span className="param-name list-group-item-heading">{__('Id')}:</span>
|
11
|
+
<span className="param-value">{id}</span>
|
12
|
+
</div>
|
13
|
+
<div>
|
14
|
+
<span className="param-name list-group-item-heading">{__('Label')}:</span>
|
15
|
+
<span className="param-value">{label}</span>
|
16
|
+
</div>
|
17
|
+
<div>
|
18
|
+
<span className="param-name list-group-item-heading">
|
19
|
+
{__('Duration')}:
|
20
|
+
</span>
|
21
|
+
<span
|
22
|
+
className="param-value"
|
23
|
+
title={durationInWords(startedAt, endedAt || new Date()).tooltip}
|
24
|
+
>
|
25
|
+
{durationInWords(startedAt, endedAt || new Date()).text}
|
26
|
+
</span>
|
27
|
+
</div>
|
28
|
+
<div>
|
29
|
+
<span className="param-name list-group-item-heading">
|
30
|
+
{__('Raw input')}:
|
31
|
+
</span>
|
32
|
+
<span className="param-value">
|
33
|
+
<pre>{JSON.stringify(input, null, ' ')}</pre>
|
34
|
+
</span>
|
35
|
+
</div>
|
36
|
+
<div>
|
37
|
+
<span className="param-name list-group-item-heading">
|
38
|
+
{__('Raw output')}:
|
39
|
+
</span>
|
40
|
+
<span className="param-value">
|
41
|
+
<pre>{JSON.stringify(output, null, ' ')}</pre>
|
42
|
+
</span>
|
43
|
+
</div>
|
44
|
+
<div>
|
45
|
+
<span className="param-name list-group-item-heading">
|
46
|
+
{__('External Id')}:
|
47
|
+
</span>
|
48
|
+
<span className="param-value">{externalId}</span>
|
49
|
+
</div>
|
50
|
+
</div>
|
51
|
+
);
|
52
|
+
|
53
|
+
Raw.propTypes = {
|
54
|
+
id: PropTypes.string,
|
55
|
+
label: PropTypes.string,
|
56
|
+
startedAt: PropTypes.string,
|
57
|
+
endedAt: PropTypes.string,
|
58
|
+
input: PropTypes.oneOfType([PropTypes.array, PropTypes.shape({})]),
|
59
|
+
output: PropTypes.shape({}),
|
60
|
+
externalId: PropTypes.string,
|
61
|
+
};
|
62
|
+
|
63
|
+
Raw.defaultProps = {
|
64
|
+
id: '',
|
65
|
+
label: '',
|
66
|
+
startedAt: '',
|
67
|
+
endedAt: '',
|
68
|
+
input: [],
|
69
|
+
output: {},
|
70
|
+
externalId: '',
|
71
|
+
};
|
72
|
+
|
73
|
+
export default Raw;
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Alert, Button } from 'patternfly-react';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
|
6
|
+
const RunningSteps = ({ runningSteps }) => {
|
7
|
+
if (!runningSteps.length) return <span>{__('No running steps')}</span>;
|
8
|
+
return (
|
9
|
+
<div>
|
10
|
+
{runningSteps.map((step, i) => (
|
11
|
+
<Alert type="warning" key={i}>
|
12
|
+
{step.cancellable && (
|
13
|
+
<p>
|
14
|
+
<Button
|
15
|
+
bsSize="small"
|
16
|
+
data-method="post"
|
17
|
+
href={`/foreman_tasks/tasks/${step.id}/cancel_step`}
|
18
|
+
>
|
19
|
+
{__('Cancel')}
|
20
|
+
</Button>
|
21
|
+
</p>
|
22
|
+
)}
|
23
|
+
|
24
|
+
<p>
|
25
|
+
<span>{__('Action')}:</span>
|
26
|
+
<span />
|
27
|
+
</p>
|
28
|
+
<pre>{step.action_class}</pre>
|
29
|
+
<p>
|
30
|
+
<span>{__('State')}:</span>
|
31
|
+
<span>{step.state}</span>
|
32
|
+
</p>
|
33
|
+
<span>{__('Input')}:</span>
|
34
|
+
<span>
|
35
|
+
<pre>{step.input}</pre>
|
36
|
+
</span>
|
37
|
+
<span>{__('Output')}:</span>
|
38
|
+
<span>
|
39
|
+
<pre>{step.output}</pre>
|
40
|
+
</span>
|
41
|
+
</Alert>
|
42
|
+
))}
|
43
|
+
</div>
|
44
|
+
);
|
45
|
+
};
|
46
|
+
|
47
|
+
RunningSteps.propTypes = {
|
48
|
+
runningSteps: PropTypes.array,
|
49
|
+
};
|
50
|
+
|
51
|
+
RunningSteps.defaultProps = {
|
52
|
+
runningSteps: [],
|
53
|
+
};
|
54
|
+
|
55
|
+
export default RunningSteps;
|
@@ -0,0 +1,202 @@
|
|
1
|
+
import React, { Component } from 'react';
|
2
|
+
import PropTypes from 'prop-types';
|
3
|
+
import { Grid, Row, Col, Button } from 'patternfly-react';
|
4
|
+
import { translate as __ } from 'foremanReact/common/I18n';
|
5
|
+
import TaskInfo from './TaskInfo';
|
6
|
+
import { ClickConfirmation } from '../../common/ClickConfirmation';
|
7
|
+
|
8
|
+
class Task extends Component {
|
9
|
+
taskProgressToggle = () => {
|
10
|
+
const {
|
11
|
+
timeoutId,
|
12
|
+
refetchTaskDetails,
|
13
|
+
id,
|
14
|
+
loading,
|
15
|
+
taskReloadStop,
|
16
|
+
taskReloadStart,
|
17
|
+
} = this.props;
|
18
|
+
if (timeoutId) {
|
19
|
+
taskReloadStop(timeoutId);
|
20
|
+
} else {
|
21
|
+
taskReloadStart(timeoutId, refetchTaskDetails, id, loading);
|
22
|
+
}
|
23
|
+
};
|
24
|
+
|
25
|
+
render() {
|
26
|
+
const {
|
27
|
+
taskReload,
|
28
|
+
externalId,
|
29
|
+
id,
|
30
|
+
state,
|
31
|
+
allowDangerousActions,
|
32
|
+
resumable,
|
33
|
+
cancellable,
|
34
|
+
hasSubTasks,
|
35
|
+
parentTask,
|
36
|
+
showUnlockModal,
|
37
|
+
showForceUnlockModal,
|
38
|
+
toggleUnlockModal,
|
39
|
+
toggleForceUnlockModal,
|
40
|
+
} = this.props;
|
41
|
+
const modalUnlock = (
|
42
|
+
<ClickConfirmation
|
43
|
+
showModal={showUnlockModal}
|
44
|
+
title={__('Unlock')}
|
45
|
+
body={__(
|
46
|
+
"This will unlock the resources that the task is running against. Please note that this might lead to inconsistent state and should be used with caution, after making sure that the task can't be resumed."
|
47
|
+
)}
|
48
|
+
confirmationMessage={__(
|
49
|
+
'I understand that this may cause harm and have working database backups of all backend services.'
|
50
|
+
)}
|
51
|
+
confirmAction={__('Unlock')}
|
52
|
+
path={`/foreman_tasks/tasks/${id}/unlock`}
|
53
|
+
confirmType="warning"
|
54
|
+
closeModal={toggleUnlockModal}
|
55
|
+
/>
|
56
|
+
);
|
57
|
+
|
58
|
+
const modalForceUnlock = (
|
59
|
+
<ClickConfirmation
|
60
|
+
showModal={showForceUnlockModal}
|
61
|
+
title={__('Force Unlock')}
|
62
|
+
body={__(
|
63
|
+
'Resources will be unlocked and will not prevent other tasks from being run. As the task might be still running, it should be avoided to use this unless you are really sure the task got stuck'
|
64
|
+
)}
|
65
|
+
confirmationMessage={__(
|
66
|
+
'I understand that this may cause harm and have working database backups of all backend services.'
|
67
|
+
)}
|
68
|
+
confirmAction={__('Force Unlock')}
|
69
|
+
path={`/foreman_tasks/tasks/${id}/force_unlock`}
|
70
|
+
confirmType="danger"
|
71
|
+
closeModal={toggleForceUnlockModal}
|
72
|
+
/>
|
73
|
+
);
|
74
|
+
return (
|
75
|
+
<React.Fragment>
|
76
|
+
{modalUnlock}
|
77
|
+
{modalForceUnlock}
|
78
|
+
<Grid>
|
79
|
+
<Row>
|
80
|
+
<Col xs={12}>
|
81
|
+
<Button
|
82
|
+
hidden={!allowDangerousActions}
|
83
|
+
className="reload-button"
|
84
|
+
bsSize="small"
|
85
|
+
onClick={this.taskProgressToggle}
|
86
|
+
>
|
87
|
+
<span
|
88
|
+
className={`glyphicon glyphicon-refresh ${
|
89
|
+
taskReload ? 'spin' : ''
|
90
|
+
}`}
|
91
|
+
/>
|
92
|
+
{__(`${taskReload ? 'Stop' : 'Start'} auto-reloading`)}
|
93
|
+
</Button>
|
94
|
+
<Button
|
95
|
+
bsSize="small"
|
96
|
+
href={`/foreman_tasks/dynflow/${externalId}`}
|
97
|
+
>
|
98
|
+
{__('Dynflow console')}
|
99
|
+
</Button>
|
100
|
+
<Button
|
101
|
+
bsSize="small"
|
102
|
+
bsStyle="primary"
|
103
|
+
data-method="post"
|
104
|
+
href={`/foreman_tasks/tasks/${id}/resume`}
|
105
|
+
disabled={!resumable}
|
106
|
+
>
|
107
|
+
{__('Resume')}
|
108
|
+
</Button>
|
109
|
+
<Button
|
110
|
+
bsSize="small"
|
111
|
+
data-method="post"
|
112
|
+
href={`/foreman_tasks/tasks/${id}/cancel`}
|
113
|
+
disabled={!cancellable}
|
114
|
+
>
|
115
|
+
{__('Cancel')}
|
116
|
+
</Button>
|
117
|
+
|
118
|
+
{parentTask && (
|
119
|
+
<Button
|
120
|
+
bsSize="small"
|
121
|
+
href={`/foreman_tasks/tasks/${parentTask}`}
|
122
|
+
>
|
123
|
+
{__('Parent task')}
|
124
|
+
</Button>
|
125
|
+
)}
|
126
|
+
{hasSubTasks && (
|
127
|
+
<Button
|
128
|
+
bsSize="small"
|
129
|
+
href={`/foreman_tasks/tasks/${id}/sub_tasks`}
|
130
|
+
>
|
131
|
+
{__('Sub tasks')}
|
132
|
+
</Button>
|
133
|
+
)}
|
134
|
+
{allowDangerousActions && (
|
135
|
+
<Button
|
136
|
+
bsSize="small"
|
137
|
+
disabled={state !== 'paused'}
|
138
|
+
onClick={toggleUnlockModal}
|
139
|
+
>
|
140
|
+
{__('Unlock')}
|
141
|
+
</Button>
|
142
|
+
)}
|
143
|
+
{allowDangerousActions && (
|
144
|
+
<Button
|
145
|
+
bsSize="small"
|
146
|
+
disabled={state === 'stopped'}
|
147
|
+
onClick={toggleForceUnlockModal}
|
148
|
+
>
|
149
|
+
{__('Force Unlock')}
|
150
|
+
</Button>
|
151
|
+
)}
|
152
|
+
</Col>
|
153
|
+
</Row>
|
154
|
+
<TaskInfo {...this.props} />
|
155
|
+
</Grid>
|
156
|
+
</React.Fragment>
|
157
|
+
);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
Task.propTypes = {
|
162
|
+
...TaskInfo.PropTypes,
|
163
|
+
state: PropTypes.string,
|
164
|
+
allowDangerousActions: PropTypes.bool,
|
165
|
+
resumable: PropTypes.bool,
|
166
|
+
cancellable: PropTypes.bool,
|
167
|
+
refetchTaskDetails: PropTypes.func,
|
168
|
+
hasSubTasks: PropTypes.bool,
|
169
|
+
parentTask: PropTypes.string,
|
170
|
+
taskReload: PropTypes.bool,
|
171
|
+
taskReloadStop: PropTypes.func,
|
172
|
+
taskReloadStart: PropTypes.func,
|
173
|
+
timeoutId: PropTypes.number,
|
174
|
+
externalId: PropTypes.string,
|
175
|
+
id: PropTypes.string.isRequired,
|
176
|
+
showUnlockModal: PropTypes.bool,
|
177
|
+
showForceUnlockModal: PropTypes.bool,
|
178
|
+
toggleUnlockModal: PropTypes.func,
|
179
|
+
toggleForceUnlockModal: PropTypes.func,
|
180
|
+
};
|
181
|
+
|
182
|
+
Task.defaultProps = {
|
183
|
+
...TaskInfo.defaultProps,
|
184
|
+
state: '',
|
185
|
+
allowDangerousActions: false,
|
186
|
+
resumable: false,
|
187
|
+
cancellable: false,
|
188
|
+
refetchTaskDetails: () => null,
|
189
|
+
hasSubTasks: false,
|
190
|
+
parentTask: '',
|
191
|
+
taskReload: false,
|
192
|
+
taskReloadStop: () => null,
|
193
|
+
taskReloadStart: () => null,
|
194
|
+
timeoutId: null,
|
195
|
+
externalId: '',
|
196
|
+
showUnlockModal: false,
|
197
|
+
showForceUnlockModal: false,
|
198
|
+
toggleUnlockModal: () => null,
|
199
|
+
toggleForceUnlockModal: () => null,
|
200
|
+
};
|
201
|
+
|
202
|
+
export default Task;
|