foreman_remote_execution 14.0.0 → 14.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_remote_execution/locale/de/foreman_remote_execution.js +77 -77
  3. data/app/assets/javascripts/foreman_remote_execution/locale/en/foreman_remote_execution.js +2 -1667
  4. data/app/assets/javascripts/foreman_remote_execution/locale/en_GB/foreman_remote_execution.js +7 -7
  5. data/app/assets/javascripts/foreman_remote_execution/locale/es/foreman_remote_execution.js +33 -33
  6. data/app/assets/javascripts/foreman_remote_execution/locale/fr/foreman_remote_execution.js +131 -131
  7. data/app/assets/javascripts/foreman_remote_execution/locale/ja/foreman_remote_execution.js +131 -131
  8. data/app/assets/javascripts/foreman_remote_execution/locale/ka/foreman_remote_execution.js +38 -38
  9. data/app/assets/javascripts/foreman_remote_execution/locale/ko/foreman_remote_execution.js +16 -16
  10. data/app/assets/javascripts/foreman_remote_execution/locale/pt_BR/foreman_remote_execution.js +33 -33
  11. data/app/assets/javascripts/foreman_remote_execution/locale/ru/foreman_remote_execution.js +7 -7
  12. data/app/assets/javascripts/foreman_remote_execution/locale/zh_CN/foreman_remote_execution.js +134 -134
  13. data/app/assets/javascripts/foreman_remote_execution/locale/zh_TW/foreman_remote_execution.js +6 -6
  14. data/app/controllers/api/v2/job_invocations_controller.rb +34 -17
  15. data/app/helpers/job_invocations_helper.rb +1 -1
  16. data/app/helpers/remote_execution_helper.rb +2 -2
  17. data/app/lib/actions/remote_execution/proxy_action.rb +10 -5
  18. data/app/lib/actions/remote_execution/run_host_job.rb +1 -1
  19. data/app/lib/actions/remote_execution/template_invocation_progress_logging.rb +2 -3
  20. data/app/models/concerns/foreman_remote_execution/errors_flattener.rb +2 -2
  21. data/app/views/api/v2/job_invocations/hosts.json.rabl +15 -0
  22. data/app/views/template_invocations/show.html.erb +1 -1
  23. data/app/views/templates/script/package_action.erb +1 -1
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20240312133027_extend_template_invocation_events.rb +19 -0
  26. data/db/migrate/20240522093413_migrate_smart_proxy_ids_to_template_invocations.rb +3 -0
  27. data/lib/foreman_remote_execution/engine.rb +0 -4
  28. data/lib/foreman_remote_execution/version.rb +1 -1
  29. data/locale/Makefile +12 -2
  30. data/locale/de/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  31. data/locale/de/foreman_remote_execution.po +81 -80
  32. data/locale/en/foreman_remote_execution.po +0 -1675
  33. data/locale/en_GB/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  34. data/locale/en_GB/foreman_remote_execution.po +7 -7
  35. data/locale/es/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  36. data/locale/es/foreman_remote_execution.po +33 -33
  37. data/locale/fr/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  38. data/locale/fr/foreman_remote_execution.po +132 -132
  39. data/locale/ja/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  40. data/locale/ja/foreman_remote_execution.po +131 -131
  41. data/locale/ka/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  42. data/locale/ka/foreman_remote_execution.po +38 -38
  43. data/locale/ko/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  44. data/locale/ko/foreman_remote_execution.po +16 -16
  45. data/locale/pt_BR/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  46. data/locale/pt_BR/foreman_remote_execution.po +33 -33
  47. data/locale/ru/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  48. data/locale/ru/foreman_remote_execution.po +7 -7
  49. data/locale/zh_CN/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  50. data/locale/zh_CN/foreman_remote_execution.po +136 -134
  51. data/locale/zh_TW/LC_MESSAGES/foreman_remote_execution.mo +0 -0
  52. data/locale/zh_TW/foreman_remote_execution.po +6 -6
  53. data/webpack/JobInvocationDetail/JobInvocationActions.js +1 -1
  54. data/webpack/JobInvocationDetail/JobInvocationConstants.js +84 -0
  55. data/webpack/JobInvocationDetail/JobInvocationDetail.scss +0 -1
  56. data/webpack/JobInvocationDetail/JobInvocationHostTable.js +210 -0
  57. data/webpack/JobInvocationDetail/JobInvocationSelectors.js +2 -2
  58. data/webpack/JobInvocationDetail/__tests__/MainInformation.test.js +5 -1
  59. data/webpack/JobInvocationDetail/__tests__/fixtures.js +9 -0
  60. data/webpack/JobInvocationDetail/index.js +56 -34
  61. data/webpack/__mocks__/foremanReact/components/HostDetails/DetailsCard/DefaultLoaderEmptyState.js +1 -2
  62. data/webpack/react_app/components/RecentJobsCard/JobStatusIcon.js +38 -7
  63. data/webpack/react_app/components/RecentJobsCard/constants.js +4 -0
  64. data/webpack/react_app/components/TargetingHosts/__tests__/__snapshots__/HostStatus.test.js.snap +1 -1
  65. data/webpack/react_app/components/TargetingHosts/components/HostStatus.js +6 -6
  66. metadata +6 -95
  67. data/.babelrc.js +0 -3
  68. data/.eslintignore +0 -3
  69. data/.eslintrc +0 -13
  70. data/.github/workflows/js_ci.yml +0 -32
  71. data/.github/workflows/release.yml +0 -16
  72. data/.github/workflows/ruby_ci.yml +0 -19
  73. data/.gitignore +0 -18
  74. data/.packit.yaml +0 -45
  75. data/.prettierrc +0 -4
  76. data/.rubocop.yml +0 -105
  77. data/.rubocop_todo.yml +0 -516
  78. data/.tx/config +0 -10
  79. data/Gemfile +0 -5
  80. data/app/mailers/.gitkeep +0 -0
  81. data/app/views/dashboard/.gitkeep +0 -0
  82. data/foreman_remote_execution.gemspec +0 -33
  83. data/jsconfig.json +0 -8
  84. data/locale/action_names.rb +0 -2
  85. data/test/benchmark/run_hosts_job_benchmark.rb +0 -70
  86. data/test/benchmark/targeting_benchmark.rb +0 -31
  87. data/test/factories/foreman_remote_execution_factories.rb +0 -147
  88. data/test/functional/api/v2/foreign_input_sets_controller_test.rb +0 -58
  89. data/test/functional/api/v2/job_invocations_controller_test.rb +0 -446
  90. data/test/functional/api/v2/job_templates_controller_test.rb +0 -110
  91. data/test/functional/api/v2/registration_controller_test.rb +0 -73
  92. data/test/functional/api/v2/remote_execution_features_controller_test.rb +0 -34
  93. data/test/functional/api/v2/template_invocations_controller_test.rb +0 -33
  94. data/test/functional/cockpit_controller_test.rb +0 -16
  95. data/test/functional/job_invocations_controller_test.rb +0 -132
  96. data/test/functional/job_templates_controller_test.rb +0 -31
  97. data/test/functional/ui_job_wizard_controller_test.rb +0 -16
  98. data/test/graphql/mutations/job_invocations/create_test.rb +0 -58
  99. data/test/graphql/queries/job_invocation_query_test.rb +0 -31
  100. data/test/graphql/queries/job_invocations_query_test.rb +0 -35
  101. data/test/helpers/remote_execution_helper_test.rb +0 -46
  102. data/test/support/remote_execution_helper.rb +0 -5
  103. data/test/test_plugin_helper.rb +0 -9
  104. data/test/unit/actions/run_host_job_test.rb +0 -115
  105. data/test/unit/actions/run_hosts_job_test.rb +0 -214
  106. data/test/unit/api_params_test.rb +0 -25
  107. data/test/unit/concerns/foreman_tasks_cleaner_extensions_test.rb +0 -29
  108. data/test/unit/concerns/host_extensions_test.rb +0 -219
  109. data/test/unit/concerns/nic_extensions_test.rb +0 -9
  110. data/test/unit/execution_task_status_mapper_test.rb +0 -92
  111. data/test/unit/input_template_renderer_test.rb +0 -503
  112. data/test/unit/job_invocation_composer_test.rb +0 -974
  113. data/test/unit/job_invocation_report_template_test.rb +0 -60
  114. data/test/unit/job_invocation_test.rb +0 -232
  115. data/test/unit/job_template_effective_user_test.rb +0 -37
  116. data/test/unit/job_template_test.rb +0 -316
  117. data/test/unit/remote_execution_feature_test.rb +0 -86
  118. data/test/unit/remote_execution_provider_test.rb +0 -298
  119. data/test/unit/renderer_scope_input_test.rb +0 -49
  120. data/test/unit/targeting_test.rb +0 -206
  121. data/test/unit/template_invocation_input_value_test.rb +0 -38
@@ -1,147 +0,0 @@
1
- FactoryBot.define do
2
- factory :job_template do
3
- sequence(:name) { |n| "Job template #{n}" }
4
- sequence(:job_category) { |n| "Job name #{n}" }
5
- template { 'id' }
6
- provider_type { 'SSH' }
7
- organizations { [Organization.find_by(name: 'Organization 1')] }
8
- locations { [Location.find_by(name: 'Location 1')] }
9
-
10
- trait :with_input do
11
- after(:build) do |template, evaluator|
12
- template.template_inputs << FactoryBot.build(:template_input)
13
- end
14
- end
15
-
16
- trait :with_description_format do
17
- description_format { 'Factory-built %{job_category}' }
18
- end
19
-
20
- trait :with_feature do
21
- remote_execution_feature
22
- end
23
- end
24
-
25
- factory :foreign_input_set
26
-
27
- factory :targeting do
28
- search_query { 'name = foo' }
29
- targeting_type { 'static_query' }
30
- user
31
-
32
- trait :with_randomized_ordering do
33
- randomized_ordering { true }
34
- end
35
- end
36
-
37
- factory :job_invocation do |f|
38
- targeting
39
- f.sequence(:job_category) { |n| "Job name #{n}" }
40
- f.description_format { "%{job_category}" }
41
- trait :with_template do
42
- after(:build) do |invocation, evaluator|
43
- invocation.pattern_template_invocations << FactoryBot.build(:template_invocation)
44
- end
45
- end
46
-
47
- trait :with_failed_task do
48
- after(:build) do |invocation, _evaluator|
49
- invocation.template_invocations << FactoryBot.build(:template_invocation, :with_failed_task, :with_host)
50
- invocation.task = FactoryBot.build(:some_task)
51
- end
52
- end
53
-
54
- trait :with_task do
55
- after(:build) do |invocation, _evaluator|
56
- invocation.template_invocations << FactoryBot.build(:template_invocation, :with_task, :with_host)
57
- invocation.task = FactoryBot.build(:some_task)
58
- end
59
- end
60
-
61
- trait :with_unplanned_host do
62
- after(:build) do |invocation, _evaluator|
63
- invocation.targeting.hosts << FactoryBot.build(:host)
64
- end
65
- end
66
- end
67
-
68
- factory :remote_execution_provider do |f|
69
- f.sequence(:name) { |n| "Provider #{n}" }
70
- end
71
-
72
- factory :template_invocation do
73
- job_invocation
74
- association :template, :factory => :job_template
75
-
76
- trait :with_task do
77
- after(:build) do |template, _evaluator|
78
- template.run_host_job_task = FactoryBot.build(:some_task)
79
- end
80
- end
81
-
82
- trait :with_failed_task do
83
- after(:build) do |template, _evaluator|
84
- template.run_host_job_task = FactoryBot.build(:some_task, :result => 'error')
85
- end
86
- end
87
-
88
- trait :with_host do
89
- after(:build) do |template, _evaluator|
90
- template.host = FactoryBot.build(:host)
91
- end
92
- end
93
- end
94
-
95
- factory :template_invocation_input_value do |f|
96
- f.sequence(:value) { |n| "Input Value #{n}" }
97
- end
98
-
99
- factory :remote_execution_feature do |f|
100
- f.sequence(:label) { |n| "remote_execution_feature_#{n}" }
101
- f.sequence(:name) { |n| "Remote Execution Feature #{n}" }
102
- end
103
- end
104
-
105
- FactoryBot.modify do
106
- factory :feature do
107
- trait :ssh do
108
- name { 'SSH' }
109
- end
110
- end
111
-
112
- factory :smart_proxy do
113
- trait :ssh do
114
- features { [FactoryBot.create(:feature, :ssh)] }
115
- pubkey { 'ssh-rsa AAAAB3N...' }
116
- end
117
- end
118
-
119
- factory :subnet do
120
- trait :execution do
121
- remote_execution_proxies { [FactoryBot.build(:smart_proxy, :ssh)] }
122
- end
123
- end
124
-
125
- factory :host do
126
- trait :with_execution do
127
- managed
128
- domain
129
- subnet do
130
- overrides = {
131
- :remote_execution_proxies => [FactoryBot.create(:smart_proxy, :ssh)],
132
- }
133
-
134
- overrides[:locations] = [location] unless location.nil?
135
- overrides[:organizations] = [organization] unless organization.nil?
136
-
137
- FactoryBot.create(
138
- :subnet_ipv4,
139
- overrides
140
- )
141
- end
142
- interfaces do
143
- [FactoryBot.build(:nic_primary_and_provision, :ip => subnet.network.sub(/0\Z/, '1'), :execution => true)]
144
- end
145
- end
146
- end
147
- end
@@ -1,58 +0,0 @@
1
- require 'test_plugin_helper'
2
-
3
- module Api
4
- module V2
5
- class ForeignInputSetsControllerTest < ActionController::TestCase
6
- setup do
7
- @template = FactoryBot.create(:job_template)
8
- @foreign_template = FactoryBot.create(:job_template, :with_input)
9
- @new_foreign_template = FactoryBot.create(:job_template, :with_input)
10
- @input_set = @template.foreign_input_sets.create(:target_template_id => @foreign_template.id)
11
- end
12
-
13
- test 'should get index' do
14
- get :index, params: { :template_id => @template.id }
15
- input_sets = ActiveSupport::JSON.decode(@response.body)
16
- assert_not input_sets.empty?, 'Should respond with input sets'
17
- assert_response :success
18
- end
19
-
20
- test 'should get input set detail' do
21
- get :show, params: { :template_id => @template.to_param, :id => @input_set.to_param }
22
- assert_response :success
23
- input_set = ActiveSupport::JSON.decode(@response.body)
24
- assert_not input_set.empty?
25
- assert_equal input_set['target_template_name'], @foreign_template.name
26
- end
27
-
28
- test 'should create valid' do
29
- valid_attrs = { :target_template_id => @new_foreign_template.id }
30
- post :create, params: { :foreign_input_set => valid_attrs, :template_id => @template.to_param }
31
- input_set = ActiveSupport::JSON.decode(@response.body)
32
- assert_equal input_set['target_template_name'], @new_foreign_template.name
33
- assert_response :success
34
- end
35
-
36
- test 'should not create invalid' do
37
- post :create, params: { :template_id => @template.to_param }
38
- assert_response :unprocessable_entity
39
- end
40
-
41
- test 'should update valid' do
42
- put :update, params: { :template_id => @template.to_param, :id => @input_set.to_param, :foreign_input_set => { :include_all => false } }
43
- assert_response :ok
44
- end
45
-
46
- test 'should not update invalid' do
47
- put :update, params: { :template_id => @template.to_param, :id => @input_set.to_param, :foreign_input_set => { :target_template_id => '' } }
48
- assert_response :unprocessable_entity
49
- end
50
-
51
- test 'should destroy' do
52
- delete :destroy, params: { :template_id => @template.to_param, :id => @input_set.to_param }
53
- assert_response :ok
54
- assert_not ForeignInputSet.exists?(@input_set.id)
55
- end
56
- end
57
- end
58
- end
@@ -1,446 +0,0 @@
1
- require 'test_plugin_helper'
2
-
3
- module Api
4
- module V2
5
- class JobInvocationsControllerTest < ActionController::TestCase
6
- setup do
7
- @invocation = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
8
- @template = FactoryBot.create(:job_template, :with_input)
9
-
10
- # Without this the template in template_invocations and in pattern_template_invocations
11
- # would belong to different job_categories, causing trouble when trying to rerun
12
- @invocation.job_category = @invocation.pattern_template_invocations.first.template.job_category
13
- @invocation.save!
14
- end
15
-
16
- test 'should get index' do
17
- get :index
18
- invocations = ActiveSupport::JSON.decode(@response.body)
19
- assert_not_empty invocations, 'Should response with invocation'
20
- assert_response :success
21
- end
22
-
23
- describe 'show' do
24
- test 'should get invocation detail' do
25
- get :show, params: { :id => @invocation.id }
26
- assert_response :success
27
- template = ActiveSupport::JSON.decode(@response.body)
28
- assert_not_empty template
29
- assert_equal template['job_category'], @invocation.job_category
30
- assert_not_empty template['targeting']['hosts']
31
- end
32
-
33
- test 'should get invocation detail when taxonomies are set' do
34
- taxonomy_params = %w(organization location).reduce({}) { |acc, cur| acc.merge("#{cur}_id" => FactoryBot.create(cur)) }
35
- get :show, params: taxonomy_params.merge(:id => @invocation.id)
36
- assert_response :success
37
- end
38
-
39
- test 'should see only permitted hosts' do
40
- @user = FactoryBot.create(:user, admin: false)
41
- @invocation.task.update(user: @user)
42
- setup_user('view', 'job_invocations', nil, @user)
43
- setup_user('view', 'hosts', 'name ~ nope.example.com', @user)
44
-
45
- get :show, params: { :id => @invocation.id }, session: prepare_user(@user)
46
- assert_response :success
47
- response = ActiveSupport::JSON.decode(@response.body)
48
- assert_equal response['targeting']['hosts'], []
49
- end
50
- end
51
-
52
- context 'creation' do
53
- setup do
54
- @attrs = { :job_category => @template.job_category,
55
- :name => 'RandomName',
56
- :job_template_id => @template.id,
57
- :targeting_type => 'static_query',
58
- :search_query => 'foobar' }
59
- end
60
-
61
- test 'should create valid with job_template_id' do
62
- post :create, params: { job_invocation: @attrs }
63
-
64
- invocation = ActiveSupport::JSON.decode(@response.body)
65
- assert_equal @attrs[:job_category], invocation['job_category']
66
- assert_response :success
67
- end
68
-
69
- test 'should create with description format overridden' do
70
- @attrs[:description_format] = 'format'
71
- post :create, params: { job_invocation: @attrs }
72
-
73
- invocation = ActiveSupport::JSON.decode(@response.body)
74
- assert_equal @attrs[:description_format], invocation['description']
75
- end
76
-
77
- test 'should create with recurrence' do
78
- @attrs[:recurrence] = { cron_line: '5 * * * *' }
79
- post :create, params: { job_invocation: @attrs }
80
- invocation = ActiveSupport::JSON.decode(@response.body)
81
- assert_equal invocation['mode'], 'recurring'
82
- assert_response :success
83
- end
84
-
85
- test 'should propagate errors from triggering' do
86
- @attrs[:recurrence] = { cron_line: 'foo' }
87
- post :create, params: { job_invocation: @attrs }
88
- invocation = ActiveSupport::JSON.decode(@response.body)
89
- assert_match(/foo is not valid format of cron line/, invocation['error']['message'])
90
- assert_response 500
91
- end
92
-
93
- test 'should create with schedule' do
94
- @attrs[:scheduling] = { start_at: Time.now.to_s }
95
- post :create, params: { job_invocation: @attrs }
96
- invocation = ActiveSupport::JSON.decode(@response.body)
97
- assert_equal invocation['mode'], 'future'
98
- assert_response :success
99
- end
100
-
101
- test 'should create with a scheduled recurrence' do
102
- @attrs[:scheduling] = { start_at: (Time.now + 1.hour) }
103
- @attrs[:recurrence] = { cron_line: '5 * * * *' }
104
- post :create, params: { job_invocation: @attrs }
105
- invocation = ActiveSupport::JSON.decode(@response.body)
106
- assert_equal 'recurring', invocation['mode']
107
- assert invocation['start_at']
108
- assert_response :success
109
- end
110
-
111
- context 'with_feature' do
112
- setup do
113
- @feature = FactoryBot.create(:remote_execution_feature,
114
- :job_template => @template)
115
- @attrs = {
116
- feature: @feature.label,
117
- }
118
- end
119
-
120
- test 'host ids as array of FQDNs' do
121
- host = FactoryBot.create(:host)
122
- @attrs[:host_ids] = [host.fqdn]
123
- post :create, params: { job_invocation: @attrs }
124
- assert_response :success
125
- end
126
-
127
- test 'host ids as array of IDs' do
128
- host = FactoryBot.create(:host)
129
- host2 = FactoryBot.create(:host)
130
- @attrs[:host_ids] = [host.id, host2.id]
131
- post :create, params: { job_invocation: @attrs }
132
- assert_response :success
133
- end
134
-
135
- test 'host ids as search_query' do
136
- @attrs[:host_ids] = 'name = testfqdn'
137
- post :create, params: { job_invocation: @attrs }
138
- assert_response :success
139
- end
140
-
141
- test 'with search_query param' do
142
- @attrs[:targeting_type] = 'static_query'
143
- @attrs[:search_query] = 'name = testfqdn'
144
- post :create, params: { job_invocation: @attrs }
145
- assert_response :success
146
- end
147
-
148
- test 'with job_template_id param' do
149
- @attrs[:job_template_id] = 12_345
150
- post :create, params: { job_invocation: @attrs }
151
- assert_response :error
152
- end
153
- end
154
- end
155
-
156
- describe '#output' do
157
- let(:host) { @invocation.targeting.hosts.first }
158
-
159
- test 'should provide output for delayed task' do
160
- ForemanTasks::Task.any_instance.expects(:scheduled?).returns(true)
161
- get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
162
- result = ActiveSupport::JSON.decode(@response.body)
163
- assert_equal result['delayed'], true
164
- assert_equal result['refresh'], true
165
- assert_equal result['output'], []
166
- assert_response :success
167
- end
168
-
169
- test 'should provide empty output for host which does not have a task yet' do
170
- JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
171
- JobInvocation.any_instance.expects(:finished?).returns(false)
172
- get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
173
- result = ActiveSupport::JSON.decode(@response.body)
174
- assert_equal result['refresh'], true
175
- assert_equal result['output'], []
176
- assert_response :success
177
- end
178
-
179
- test 'should provide empty output marked as done for host which does not have a task when the job is finished' do
180
- JobInvocation.any_instance.expects(:sub_task_for_host).returns(nil)
181
- get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
182
- result = ActiveSupport::JSON.decode(@response.body)
183
- assert_equal result['refresh'], false
184
- assert_equal result['output'], []
185
- assert_response :success
186
- end
187
-
188
- test 'should fail with 404 for non-existing job invocation' do
189
- invocation_id = @invocation.id + 1
190
- assert_empty JobInvocation.where(:id => invocation_id)
191
- get :output, params: { :job_invocation_id => invocation_id, :host_id => 1234 }
192
- result = ActiveSupport::JSON.decode(@response.body)
193
- assert_equal result['message'], "Job invocation not found by id '#{invocation_id}'"
194
- assert_response :missing
195
- end
196
-
197
- test 'should get output only for host in job invocation' do
198
- get :output, params: { job_invocation_id: @invocation.id,
199
- host_id: FactoryBot.create(:host).id }
200
- assert_response :missing
201
- end
202
-
203
- test 'should not break when taxonomy parameters are provided' do
204
- get :output, params: { :job_invocation_id => @invocation.id, :host_id => host.id, :organization_id => host.organization_id, :location_id => host.location_id }
205
- assert_response :success
206
- end
207
- end
208
-
209
- describe '#outputs' do
210
- test 'should provide outputs for hosts in the job' do
211
- get :outputs, params: { :id => @invocation.id }
212
- result = ActiveSupport::JSON.decode(@response.body)
213
- host_output = result['outputs'].first
214
- assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
215
- assert_equal host_output['refresh'], false
216
- assert_equal host_output['output'], []
217
- assert_response :success
218
- end
219
-
220
- test 'should provide outputs for selected hosts in the job' do
221
- post :outputs, params: { :id => @invocation.id, :search_query => "id = #{@invocation.targeting.host_ids.first}" }, as: :json
222
- result = ActiveSupport::JSON.decode(@response.body)
223
- host_output = result['outputs'].first
224
- assert_equal host_output['host_id'], @invocation.targeting.host_ids.first
225
- assert_equal host_output['refresh'], false
226
- assert_equal host_output['output'], []
227
- assert_response :success
228
- end
229
-
230
- test 'should provide outputs for hosts in the job matching a search query' do
231
- get :outputs, params: { :id => @invocation.id, :search_query => "name = definitely_not_in_the_job" }
232
- result = ActiveSupport::JSON.decode(@response.body)
233
- assert_equal result['outputs'], []
234
- assert_response :success
235
- end
236
- end
237
-
238
- describe 'raw output' do
239
- let(:fake_output) do
240
- (1..5).map do |i|
241
- { 'timestamp' => (Time.now - (5 - i)).to_f, 'output' => "#{i}\n" }
242
- end
243
- end
244
- let(:fake_task) do
245
- OpenStruct.new :pending? => false, :main_action => OpenStruct.new(:live_output => fake_output)
246
- end
247
- let(:host) { @invocation.targeting.hosts.first }
248
-
249
- test 'should provide raw output for a host' do
250
- JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => false))
251
- JobInvocation.any_instance.expects(:sub_task_for_host).returns(fake_task)
252
- get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
253
- result = ActiveSupport::JSON.decode(@response.body)
254
- assert_equal result['complete'], true
255
- assert_equal result['output'], (1..5).map(&:to_s).join("\n") + "\n"
256
- assert_response :success
257
- end
258
-
259
- test 'should not break when taxonomy parameters are provided' do
260
- get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id, :organization_id => host.organization_id, :location_id => host.location_id }
261
- assert_response :success
262
- end
263
-
264
- test 'should provide raw output for delayed task' do
265
- start_time = Time.now
266
- JobInvocation.any_instance
267
- .expects(:task).twice
268
- .returns(OpenStruct.new(:scheduled? => true, :start_at => start_time, :pending? => true))
269
- JobInvocation.any_instance.expects(:sub_task_for_host).never
270
- get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
271
- result = ActiveSupport::JSON.decode(@response.body)
272
- assert_equal result['complete'], false
273
- assert_equal result['delayed'], true
274
- assert_nil result['output']
275
- assert_in_delta start_time.to_f, Time.parse(result['start_at']).to_f, 0.001
276
- assert_response :success
277
- end
278
-
279
- test 'should provide raw output for host without task' do
280
- JobInvocation.any_instance.expects(:task).times(3).returns(OpenStruct.new(:scheduled? => false, :pending? => true))
281
- JobInvocation.any_instance.expects(:sub_task_for_host)
282
- get :raw_output, params: { :job_invocation_id => @invocation.id, :host_id => host.id }
283
- result = ActiveSupport::JSON.decode(@response.body)
284
- assert_equal result['complete'], false
285
- assert_nil result['output']
286
- assert_response :success
287
- end
288
-
289
- test 'should get raw output only for host in job invocation' do
290
- get :raw_output, params: { job_invocation_id: @invocation.id,
291
- host_id: FactoryBot.create(:host).id }
292
- assert_response :missing
293
- end
294
- end
295
-
296
- test 'should cancel a job' do
297
- @invocation.task.expects(:cancellable?).returns(true)
298
- @invocation.task.expects(:cancel).returns(true)
299
- JobInvocation.expects(:from_param).with(@invocation.id.to_s).returns(@invocation)
300
- post :cancel, :params => { :id => @invocation.id }
301
- result = ActiveSupport::JSON.decode(@response.body)
302
- assert_equal result['cancelled'], true
303
- assert_equal result['id'], @invocation.id
304
-
305
- assert_response :success
306
- end
307
-
308
- test 'should abort a job' do
309
- @invocation.task.expects(:cancellable?).returns(true)
310
- @invocation.task.expects(:abort).returns(true)
311
- JobInvocation.expects(:from_param).with(@invocation.id.to_s).returns(@invocation)
312
- post :cancel, :params => { :id => @invocation.id, :force => true }
313
- result = ActiveSupport::JSON.decode(@response.body)
314
- assert_equal result['cancelled'], true
315
- assert_equal result['id'], @invocation.id
316
-
317
- assert_response :success
318
- end
319
-
320
- test 'should error when trying to cancel a stopped job' do
321
- @invocation.task.expects(:cancellable?).returns(false)
322
- JobInvocation.expects(:from_param).with(@invocation.id.to_s).returns(@invocation)
323
- post :cancel, :params => { :id => @invocation.id }
324
- assert_response 422
325
- end
326
-
327
- test 'should rerun' do
328
- JobInvocation.any_instance.expects(:generate_description)
329
- JobInvocationComposer.any_instance
330
- .expects(:validate_job_category)
331
- .with(@invocation.job_category)
332
- .returns(@invocation.job_category)
333
- post :rerun, params: { :id => @invocation.id }
334
- assert_response :success
335
- result = ActiveSupport::JSON.decode(@response.body)
336
- targeting = Targeting.find(result['targeting_id'])
337
- assert_equal users(:admin).id, targeting.user_id
338
- assert_equal @invocation.targeting.search_query, targeting.search_query
339
- end
340
-
341
- test 'should not raise an exception when reruning failed has no hosts' do
342
- @invocation.targeting.hosts.first.destroy
343
- JobInvocation.any_instance.expects(:generate_description)
344
- JobInvocationComposer.any_instance
345
- .expects(:validate_job_category)
346
- .with(@invocation.job_category)
347
- .returns(@invocation.job_category)
348
-
349
- post :rerun, params: { :id => @invocation.id, :failed_only => true }
350
- assert_response :success
351
- result = ActiveSupport::JSON.decode(@response.body)
352
- targeting = Targeting.find(result['targeting_id'])
353
- assert_equal users(:admin).id, targeting.user_id
354
- assert_equal 'name ^ ()', targeting.search_query
355
- end
356
-
357
- test 'should rerun failed only' do
358
- @invocation = FactoryBot.create(:job_invocation, :with_template, :with_failed_task)
359
- @invocation.job_category = @invocation.pattern_template_invocations.first.template.job_category
360
- @invocation.targeting.hosts = @invocation.template_invocations.map(&:host)
361
- @invocation.save!
362
- JobInvocation.any_instance.expects(:generate_description)
363
- JobInvocationComposer.any_instance
364
- .expects(:validate_job_category)
365
- .with(@invocation.job_category)
366
- .returns(@invocation.job_category)
367
- post :rerun, params: { :id => @invocation.id, :failed_only => true }
368
- assert_response :success
369
- result = ActiveSupport::JSON.decode(@response.body)
370
- targeting = Targeting.find(result['targeting_id'])
371
- hostnames = @invocation.template_invocations.map { |ti| ti.host.name }
372
- assert_equal users(:admin).id, targeting.user_id
373
- assert_equal "name ^ (#{hostnames.join(',')})", targeting.search_query
374
- end
375
-
376
- test 'should return 404 if template is not found' do
377
- @invocation.job_category = 'Missing category'
378
- @invocation.save!
379
- post :rerun, params: { :id => @invocation.id }
380
- assert_response 404
381
- end
382
-
383
- describe 'restricted access' do
384
- setup do
385
- @admin = FactoryBot.create(:user, mail: 'admin@test.foreman.com', admin: true)
386
- @user = FactoryBot.create(:user, mail: 'user@test.foreman.com', admin: false)
387
- @invocation = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
388
- @invocation2 = FactoryBot.create(:job_invocation, :with_template, :with_task, :with_unplanned_host)
389
-
390
- @invocation.task.update(user: @admin)
391
- @invocation2.task.update(user: @user)
392
-
393
- setup_user 'view', 'hosts', nil, @user
394
- setup_user 'view', 'job_invocations', 'user = current_user', @user
395
- setup_user 'create', 'job_invocations', 'user = current_user', @user
396
- setup_user 'cancel', 'job_invocations', 'user = current_user', @user
397
- end
398
-
399
- let(:host) { @invocation.targeting.hosts.first }
400
- let(:host2) { @invocation2.targeting.hosts.first }
401
-
402
- context 'without user filter' do
403
- test '#index' do
404
- get :index, session: prepare_user(@admin)
405
- assert_response :success
406
- assert JSON.parse(@response.body)['results'].size >= 2
407
- end
408
-
409
- test '#show' do
410
- get :show, params: { id: @invocation2.id }, session: prepare_user(@admin)
411
- assert_response :success
412
- end
413
-
414
- test '#output' do
415
- get :output, params: { job_invocation_id: @invocation2.id, host_id: host2.id }, session: prepare_user(@admin)
416
- assert_response :success
417
- end
418
- end
419
-
420
- context 'with user filter' do
421
- test '#index' do
422
- get :index, session: prepare_user(@user)
423
- assert_response :success
424
- assert_equal 1, JSON.parse(@response.body)['results'].size
425
- end
426
-
427
- test '#show' do
428
- get :show, params: { id: @invocation.id }, session: prepare_user(@user)
429
- assert_response :not_found
430
- end
431
-
432
- test '#output' do
433
- get :output, params: { job_invocation_id: @invocation.id, host_id: host.id }, session: prepare_user(@user)
434
- assert_response :not_found
435
- assert_includes @response.body, 'Job invocation not found'
436
- end
437
- end
438
- end
439
-
440
- def prepare_user(user)
441
- User.current = user
442
- set_session_user(user)
443
- end
444
- end
445
- end
446
- end