foreman_rh_cloud 5.0.30 → 5.0.33

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/rh_cloud/cloud_request_controller.rb +83 -0
  3. data/app/controllers/foreman_inventory_upload/uploads_controller.rb +7 -0
  4. data/app/helpers/foreman_inventory_upload_host_helper.rb +2 -1
  5. data/app/models/setting/rh_cloud.rb +2 -1
  6. data/app/services/foreman_rh_cloud/cloud_ping_service.rb +4 -1
  7. data/app/services/foreman_rh_cloud/cloud_presence.rb +124 -0
  8. data/app/services/foreman_rh_cloud/cloud_request.rb +8 -1
  9. data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +1 -1
  10. data/app/services/foreman_rh_cloud/hit_remediations_retriever.rb +67 -0
  11. data/app/services/foreman_rh_cloud/remediations_retriever.rb +16 -45
  12. data/app/services/foreman_rh_cloud/template_renderer_helper.rb +13 -1
  13. data/app/services/foreman_rh_cloud/url_remediations_retriever.rb +37 -0
  14. data/app/views/job_templates/rh_cloud_download_playbook.erb +26 -0
  15. data/config/routes.rb +2 -0
  16. data/lib/foreman_inventory_upload/generators/fact_helpers.rb +3 -1
  17. data/lib/foreman_rh_cloud/engine.rb +13 -2
  18. data/lib/foreman_rh_cloud/version.rb +1 -1
  19. data/lib/foreman_rh_cloud.rb +11 -1
  20. data/lib/insights_cloud/async/connector_playbook_execution_reporter_task.rb +193 -0
  21. data/lib/insights_cloud/generators/playbook_progress_generator.rb +49 -0
  22. data/lib/tasks/insights.rake +13 -0
  23. data/package.json +1 -1
  24. data/test/controllers/insights_cloud/api/cloud_request_controller_test.rb +78 -0
  25. data/test/controllers/inventory_upload/cloud_status_controller_test.rb +2 -0
  26. data/test/jobs/connector_playbook_execution_reporter_task_test.rb +207 -0
  27. data/test/unit/fact_helpers_test.rb +2 -2
  28. data/test/unit/foreman_rh_cloud_self_host_test.rb +7 -0
  29. data/test/unit/playbook_progress_generator_test.rb +75 -0
  30. data/test/unit/services/foreman_rh_cloud/cloud_request_forwarder_test.rb +19 -0
  31. data/test/unit/services/foreman_rh_cloud/cloud_status_service_test.rb +4 -0
  32. data/test/unit/services/foreman_rh_cloud/{remediations_retriever_test.rb → hit_remediations_retriever_test.rb} +3 -3
  33. data/test/unit/services/foreman_rh_cloud/url_remediations_retriever_test.rb +27 -0
  34. data/webpack/ForemanInventoryUpload/Components/PageHeader/components/CloudPingModal/index.js +1 -1
  35. data/webpack/InsightsHostDetailsTab/InsightsTotalRiskChart.js +1 -1
  36. metadata +19 -4
@@ -0,0 +1,193 @@
1
+ module InsightsCloud
2
+ module Async
3
+ class ConnectorPlaybookExecutionReporterTask < ::Actions::EntryAction
4
+ include Dynflow::Action::Polling
5
+ include ForemanRhCloud::CloudAuth
6
+
7
+ def self.subscribe
8
+ Actions::RemoteExecution::RunHostsJob
9
+ end
10
+
11
+ def self.connector_feature_id
12
+ @connector_feature_id ||= RemoteExecutionFeature.feature!(:rh_cloud_connector_run_playbook).id
13
+ end
14
+
15
+ def plan(job_invocation)
16
+ return unless connector_playbook_job?(job_invocation)
17
+
18
+ @job_invocation = job_invocation
19
+
20
+ invocation_inputs = invocation_inputs(job_invocation)
21
+ report_url = invocation_inputs['report_url']
22
+ report_interval = [invocation_inputs['report_interval'].to_i, 5].max
23
+ correlation_id = invocation_inputs['correlation_id']
24
+
25
+ plan_self(
26
+ report_url: report_url,
27
+ report_interval: report_interval,
28
+ job_invocation_id: job_invocation.id,
29
+ correlation_id: correlation_id
30
+ )
31
+ end
32
+
33
+ def run(event = nil)
34
+ # Handle skip events
35
+ return if event == Dynflow::Action::Skip
36
+
37
+ super
38
+ end
39
+
40
+ def rescue_strategy_for_self
41
+ Dynflow::Action::Rescue::Skip
42
+ end
43
+
44
+ def done?(current_status = invocation_status)
45
+ job_invocation.finished? || current_status.map { |_host_id, task_status| task_status['report_done'] }.all?
46
+ end
47
+
48
+ # noop, we don't want to do anything when the polling task starts
49
+ def invoke_external_task
50
+ poll_external_task
51
+ end
52
+
53
+ def poll_external_task
54
+ current_status = invocation_status
55
+ report_job_progress(current_status)
56
+ # record the current state and increment the sequence for the next invocation
57
+ {
58
+ invocation_status: current_status,
59
+ }
60
+ end
61
+
62
+ def poll_intervals
63
+ [report_interval]
64
+ end
65
+
66
+ private
67
+
68
+ def connector_playbook_job?(job_invocation)
69
+ puts "Job invocation id: #{job_invocation&.remote_execution_feature_id}, feature id: #{connector_feature_id}"
70
+ job_invocation&.remote_execution_feature_id == connector_feature_id
71
+ end
72
+
73
+ def connector_feature_id
74
+ self.class.connector_feature_id
75
+ end
76
+
77
+ def invocation_inputs(job_invocation)
78
+ Hash[
79
+ job_invocation.pattern_template_invocations.first.input_values.map do |input_value|
80
+ [input_value.template_input.name, input_value.value]
81
+ end
82
+ ]
83
+ end
84
+
85
+ def job_invocation
86
+ @job_invocation ||= JobInvocation.find(input['job_invocation_id'])
87
+ end
88
+
89
+ def report_interval
90
+ @report_interval ||= input['report_interval']
91
+ end
92
+
93
+ def correlation_id
94
+ @correlation_id ||= input['correlation_id']
95
+ end
96
+
97
+ def host_status(host)
98
+ external_task&.dig('invocation_status', host)
99
+ end
100
+
101
+ def sequence(host)
102
+ host_status(host)&.fetch('sequence', 0).to_i
103
+ end
104
+
105
+ def host_done?(host)
106
+ ActiveModel::Type::Boolean.new.cast(host_status(host)&.fetch('report_done', nil))
107
+ end
108
+
109
+ def report_url
110
+ input['report_url']
111
+ end
112
+
113
+ def invocation_status
114
+ Hash[job_invocation.targeting.hosts.map do |host|
115
+ next unless host.insights&.uuid
116
+ [
117
+ host.insights.uuid,
118
+ task_status(job_invocation.sub_task_for_host(host), host.insights.uuid),
119
+ ]
120
+ end.compact]
121
+ end
122
+
123
+ def task_status(host_task, host_name)
124
+ unless host_task
125
+ return { 'state' => 'unknown' }
126
+ end
127
+
128
+ {
129
+ 'state' => host_task.state,
130
+ 'output' => host_task.main_action.live_output.map { |line| line['output'] }.join("\n"),
131
+ 'exit_status' => host_task.main_action.exit_status,
132
+ 'sequence' => sequence(host_name),
133
+ 'report_done' => host_done?(host_name),
134
+ }
135
+ end
136
+
137
+ def report_job_progress(invocation_status)
138
+ generator = InsightsCloud::Generators::PlaybookProgressGenerator.new(correlation_id)
139
+
140
+ invocation_status.each do |host_name, status|
141
+ # skip host if the host already reported that it's finished
142
+ next if status['report_done']
143
+
144
+ unless status['state'] == 'unknown'
145
+ sequence = status['sequence']
146
+ generator.host_progress_message(host_name, status['output'], sequence)
147
+ status['sequence'] = sequence + 1 # increase the sequence for each host
148
+ end
149
+
150
+ if status['state'] == 'stopped'
151
+ generator.host_finished_message(host_name, status['exit_status'])
152
+ status['report_done'] = true
153
+ end
154
+ end
155
+ generator.job_finished_message if done?(invocation_status)
156
+
157
+ send_report(generator.generate)
158
+ end
159
+
160
+ def send_report(report)
161
+ execute_cloud_request(
162
+ method: :post,
163
+ url: report_url,
164
+ content_type: 'application/vnd.redhat.playbook-sat.v3+jsonl',
165
+ payload: {
166
+ file: wrap_report(report),
167
+ multipart: true,
168
+ }
169
+ )
170
+ end
171
+
172
+ # RestClient has to accept an object that responds to :read, :path and :content_type methods
173
+ # to properly generate a multipart message.
174
+ # see: https://github.com/rest-client/rest-client/blob/2c72a2e77e2e87d25ff38feba0cf048d51bd5eca/lib/restclient/payload.rb#L161
175
+ def wrap_report(report)
176
+ obj = StringIO.new(report)
177
+ def obj.content_type
178
+ 'application/vnd.redhat.playbook-sat.v3+jsonl'
179
+ end
180
+
181
+ def obj.path
182
+ 'file'
183
+ end
184
+
185
+ obj
186
+ end
187
+
188
+ def logger
189
+ action_logger
190
+ end
191
+ end
192
+ end
193
+ end
@@ -0,0 +1,49 @@
1
+ module InsightsCloud
2
+ module Generators
3
+ class PlaybookProgressGenerator
4
+ attr_reader :correlation_id
5
+ def initialize(correlation_id)
6
+ @messages = []
7
+ @correlation_id = correlation_id
8
+ end
9
+
10
+ def host_progress_message(host_name, output, sequence)
11
+ @messages << {
12
+ "type": "playbook_run_update",
13
+ "version": 3,
14
+ "correlation_id": correlation_id,
15
+ "sequence": sequence,
16
+ "host": host_name,
17
+ "console": output,
18
+ }
19
+ end
20
+
21
+ def host_finished_message(host_name, exit_code)
22
+ @messages << {
23
+ "type": "playbook_run_finished",
24
+ "version": 3,
25
+ "correlation_id": correlation_id,
26
+ "host": host_name,
27
+ "status": exit_code == 0 ? 'success' : 'failure',
28
+ "connection_code": 0,
29
+ "execution_code": exit_code,
30
+ }
31
+ end
32
+
33
+ def job_finished_message
34
+ @messages << {
35
+ "type": "playbook_run_completed",
36
+ "version": 3,
37
+ "correlation_id": correlation_id,
38
+ "status": "success",
39
+ }
40
+ end
41
+
42
+ def generate
43
+ @messages.map do |message|
44
+ message.to_json
45
+ end.join("\n")
46
+ end
47
+ end
48
+ end
49
+ end
@@ -19,4 +19,17 @@ namespace :rh_cloud_insights do
19
19
 
20
20
  puts "Deleted #{deleted_count} insights statuses"
21
21
  end
22
+
23
+ desc "Re-announce all organizations into Sources on RH cloud."
24
+ task announce_to_sources: [:environment] do
25
+ logger = Logging::Logger.new(STDOUT)
26
+ Organization.unscoped.each do |org|
27
+ presence = ForemanRhCloud::CloudPresence.new(org, logger)
28
+ presence.announce_to_sources
29
+ rescue StandardError => ex
30
+ logger.warn(ex)
31
+ end
32
+
33
+ logger.info('Reannounced all organizations')
34
+ end
22
35
  end
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "foreman_rh_cloud",
3
- "version": "5.0.30",
3
+ "version": "5.0.33",
4
4
  "description": "Inventory Upload =============",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -0,0 +1,78 @@
1
+ require 'json'
2
+ require 'test_plugin_helper'
3
+
4
+ module InsightsCloud::Api
5
+ class CloudRequestControllerTest < ActionController::TestCase
6
+ tests Api::V2::RhCloud::CloudRequestController
7
+
8
+ setup do
9
+ @test_org = FactoryBot.create(:organization)
10
+ end
11
+
12
+ test 'Fails for unknown directives' do
13
+ request_params = run_playbook_request
14
+ request_params['directive'] = 'not-valid'
15
+
16
+ post :update, params: request_params
17
+
18
+ assert_response :bad_request
19
+ end
20
+
21
+ test 'Starts playbook run for correct directive' do
22
+ Setting[:rh_cloud_token] = 'MOCK_TOKEN'
23
+ host1 = FactoryBot.create(:host, :with_insights_hits)
24
+ host1.insights.uuid = 'TEST_UUID1'
25
+ host1.insights.save!
26
+ host2 = FactoryBot.create(:host, :with_insights_hits)
27
+ host2.insights.uuid = 'TEST_UUID2'
28
+ host2.insights.save!
29
+
30
+ mock_composer = mock('composer')
31
+ ::JobInvocationComposer.expects(:for_feature).with do |feature, host_ids, params|
32
+ feature == :rh_cloud_connector_run_playbook &&
33
+ host_ids.first == host1.id &&
34
+ host_ids.last == host2.id
35
+ end.returns(mock_composer)
36
+ mock_composer.expects(:trigger!)
37
+ mock_composer.expects(:job_invocation)
38
+
39
+ post :update, params: run_playbook_request
40
+
41
+ assert_response :success
42
+ end
43
+
44
+ private
45
+
46
+ def run_playbook_request
47
+ request_json = <<-REQUEST
48
+ {
49
+ "type": "data",
50
+ "message_id": "a6a7d866-7de0-409a-84e0-3c56c4171bb7",
51
+ "version": 1,
52
+ "sent": "2021-01-12T15:30:08+00:00",
53
+ "directive": "playbook-sat",
54
+ "metadata": {
55
+ "operation": "run",
56
+ "return_url": "https://cloud.redhat.com/api/v1/ingres/upload",
57
+ "correlation_id": "6684b9dd-0d16-42c1-b13a-9f45be59e3b6",
58
+ "playbook_run_name": "Human-readable playbook run name",
59
+ "playbook_run_url": "https://console.redhat.com/insights/remediations/1234",
60
+ "sat_id": "aa3b1faa-56f3-4d14-8258-615d11e20060",
61
+ "sat_org_id": "#{FactoryBot.create(:organization).id}",
62
+ "initiator_user_id": "4efca34c6d9ae05ef7c3d7a7424e6370d198159a841ae005084888a9a4529e27",
63
+ "hosts": "TEST_UUID1,TEST_UUID2",
64
+ "response_interval": "30",
65
+ "response_full": "false"
66
+ },
67
+ "content": ""
68
+ }
69
+ REQUEST
70
+
71
+ request = JSON.parse(request_json)
72
+
73
+ request['content'] = "\"#{Base64.encode64('https://cloud.redhat.com/api/v1/remediations/1234/playbook')}\""
74
+
75
+ request
76
+ end
77
+ end
78
+ end
@@ -29,6 +29,8 @@ class CloudStatusControllerTest < ActionController::TestCase
29
29
  RestClient::Response.new('TEST RESPONSE ORG 1')
30
30
  )
31
31
 
32
+ Katello::UpstreamConnectionChecker.any_instance.expects(:assert_connection).twice.returns(true)
33
+
32
34
  get :index, session: set_session_user
33
35
 
34
36
  assert_response :success
@@ -0,0 +1,207 @@
1
+ require 'json'
2
+ require 'test_plugin_helper'
3
+ require 'foreman_tasks/test_helpers'
4
+ require "#{ForemanTasks::Engine.root}/test/support/dummy_dynflow_action"
5
+
6
+ class ConnectorPlaybookExecutionReporterTaskTest < ActiveSupport::TestCase
7
+ include ForemanTasks::TestHelpers::WithInThreadExecutor
8
+
9
+ # override default send behavior for the test
10
+ class TestConnectorPlaybookExecutionReporterTask < InsightsCloud::Async::ConnectorPlaybookExecutionReporterTask
11
+ def send_report(report)
12
+ output[:saved_reports] = (output[:saved_reports] || []) << report
13
+ end
14
+ end
15
+
16
+ setup do
17
+ feature = RemoteExecutionFeature.register(
18
+ :rh_cloud_connector_run_playbook,
19
+ N_('Run RH Cloud playbook'),
20
+ description: N_('Run playbook genrated by Red Hat remediations app'),
21
+ host_action_button: false,
22
+ provided_inputs: ['playbook_url', 'report_url', 'correlation_id', 'report_interval']
23
+ )
24
+
25
+ puts "REX Register: #{feature.id}"
26
+
27
+ @job_invocation = generate_job_invocation
28
+
29
+ # reset connector feature ID cache
30
+ TestConnectorPlaybookExecutionReporterTask.instance_variable_set(:@connector_feature_id, nil)
31
+
32
+ puts RemoteExecutionFeature.all.to_a
33
+ end
34
+
35
+ test 'It reports finish playbook messages' do
36
+ TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(true)
37
+
38
+ actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
39
+
40
+ actual_report = actual.output[:saved_reports].first.to_s
41
+
42
+ assert_equal 1, actual.output[:saved_reports].size
43
+ assert_not_nil actual_report
44
+ actual_jsonl = read_jsonl(actual_report)
45
+
46
+ assert_not_nil actual_report_finished = actual_jsonl.find { |l| l['type'] == 'playbook_run_completed' }
47
+ assert_equal 'TEST_CORRELATION', actual_report_finished['correlation_id']
48
+ assert_equal 'success', actual_report_finished['status']
49
+
50
+ assert_not_nil actual_host_finished = actual_jsonl.find { |l| l['type'] == 'playbook_run_finished' && l['host'] == @host1.insights.uuid }
51
+ assert_equal 'TEST_CORRELATION', actual_host_finished['correlation_id']
52
+ assert_equal 'success', actual_host_finished['status']
53
+ end
54
+
55
+ test 'It reports single progress message for done host' do
56
+ TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, true)
57
+
58
+ actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
59
+
60
+ actual_report = actual.output[:saved_reports].first.to_s
61
+
62
+ assert_equal 1, actual.output[:saved_reports].size
63
+ assert_not_nil actual_report
64
+ actual_jsonl = read_jsonl(actual_report)
65
+
66
+ actual_host_updates = actual_jsonl
67
+ .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
68
+ assert_equal 1, actual_host_updates.size
69
+ assert_equal 0, actual_host_updates.first['sequence']
70
+ end
71
+
72
+ test 'It reports two progress messages for in progress host' do
73
+ TestConnectorPlaybookExecutionReporterTask.any_instance.stubs(:done?).returns(false, false, true)
74
+
75
+ host1_task = @job_invocation.template_invocations.joins(:host).where(hosts: {name: @host1.name}).first.run_host_job_task
76
+ host1_task.state = 'running'
77
+ host1_task.save!
78
+
79
+ actual = ForemanTasks.sync_task(TestConnectorPlaybookExecutionReporterTask, @job_invocation)
80
+
81
+ assert_equal 2, actual.output[:saved_reports].size
82
+
83
+ first_report = actual.output[:saved_reports].first.to_s
84
+ actual_jsonl = read_jsonl(first_report)
85
+
86
+ actual_host_updates = actual_jsonl
87
+ .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
88
+ assert_equal 1, actual_host_updates.size
89
+ assert_equal 0, actual_host_updates.first['sequence']
90
+
91
+ actual_host_updates = actual_jsonl
92
+ .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid }
93
+ assert_equal 1, actual_host_updates.size
94
+ assert_equal 0, actual_host_updates.first['sequence']
95
+
96
+ second_report = actual.output[:saved_reports].last.to_s
97
+ actual_jsonl = read_jsonl(second_report)
98
+
99
+ actual_host_updates = actual_jsonl
100
+ .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host1.insights.uuid }
101
+ assert_equal 1, actual_host_updates.size
102
+ assert_equal 1, actual_host_updates.first['sequence']
103
+
104
+ actual_host_updates = actual_jsonl
105
+ .select { |l| l['type'] == 'playbook_run_update' && l['host'] == @host2.insights.uuid }
106
+ assert_equal 0, actual_host_updates.size
107
+ end
108
+
109
+ private
110
+
111
+ def generate_job_invocation
112
+ job_template = FactoryBot.build(
113
+ :job_template,
114
+ :template => 'BLEH'
115
+ )
116
+ feature = RemoteExecutionFeature.feature!(:rh_cloud_connector_run_playbook).id
117
+
118
+ puts "Generated feature: #{feature}"
119
+
120
+ job_invocation = FactoryBot.create(
121
+ :job_invocation,
122
+ remote_execution_feature_id: feature,
123
+ task_id: FactoryBot.create(:dynflow_task).id
124
+ )
125
+
126
+ job_template.template_inputs << playbook_url_input = FactoryBot.build(:template_input,
127
+ :name => 'playbook_url',
128
+ :input_type => 'user',
129
+ :required => true)
130
+ job_template.template_inputs << report_url_input = FactoryBot.build(:template_input,
131
+ :name => 'report_url',
132
+ :input_type => 'user',
133
+ :required => true)
134
+ job_template.template_inputs << correlation_id_input = FactoryBot.build(:template_input,
135
+ :name => 'correlation_id',
136
+ :input_type => 'user',
137
+ :required => true)
138
+ job_template.template_inputs << report_interval_input = FactoryBot.build(:template_input,
139
+ :name => 'report_interval',
140
+ :input_type => 'user',
141
+ :required => true)
142
+
143
+ template_invocation = FactoryBot.build(:template_invocation,
144
+ :template => job_template,
145
+ :job_invocation => job_invocation
146
+ )
147
+
148
+ template_invocation.input_values << FactoryBot.create(
149
+ :template_invocation_input_value,
150
+ :template_invocation => template_invocation,
151
+ :template_input => playbook_url_input,
152
+ :value => 'http://example.com/TEST_PLAYBOOK'
153
+ )
154
+ template_invocation.input_values << FactoryBot.create(
155
+ :template_invocation_input_value,
156
+ :template_invocation => template_invocation,
157
+ :template_input => report_url_input,
158
+ :value => 'http://example.com/TEST_REPORT'
159
+ )
160
+ template_invocation.input_values << FactoryBot.create(
161
+ :template_invocation_input_value,
162
+ :template_invocation => template_invocation,
163
+ :template_input => correlation_id_input,
164
+ :value => 'TEST_CORRELATION'
165
+ )
166
+ template_invocation.input_values << FactoryBot.create(
167
+ :template_invocation_input_value,
168
+ :template_invocation => template_invocation,
169
+ :template_input => report_interval_input,
170
+ :value => '1'
171
+ )
172
+
173
+ @host1 = FactoryBot.create(:host, :with_insights_hits, name: 'host1')
174
+ @host1.insights.uuid = 'TEST_UUID1'
175
+ @host1.insights.save!
176
+ @host2 = FactoryBot.create(:host, :with_insights_hits, name: 'host2')
177
+ @host2.insights.uuid = 'TEST_UUID2'
178
+ @host2.insights.save!
179
+
180
+ targeting = FactoryBot.create(:targeting, hosts: [@host1, @host2])
181
+ job_invocation.targeting = targeting
182
+ job_invocation.save!
183
+
184
+ job_invocation.template_invocations << FactoryBot.create(
185
+ :template_invocation,
186
+ run_host_job_task: FactoryBot.create(:dynflow_task),
187
+ host_id: @host1.id
188
+ )
189
+ job_invocation.template_invocations << FactoryBot.create(
190
+ :template_invocation,
191
+ run_host_job_task: FactoryBot.create(:dynflow_task),
192
+ host_id: @host2.id
193
+ )
194
+
195
+ fake_output = (1..5).map do |i|
196
+ { 'timestamp' => (Time.now - (5 - i)).to_f, 'output' => "#{i}\n" }
197
+ end
198
+ Support::DummyDynflowAction.any_instance.stubs(:live_output).returns(fake_output)
199
+ Support::DummyDynflowAction.any_instance.stubs(:exit_status).returns(0)
200
+
201
+ job_invocation
202
+ end
203
+
204
+ def read_jsonl(jsonl)
205
+ jsonl.lines.map { |l| JSON.parse(l) }
206
+ end
207
+ end
@@ -30,13 +30,13 @@ class FactHelpersTest < ActiveSupport::TestCase
30
30
  test 'obfuscates ips with insights-client data' do
31
31
  host = mock('host')
32
32
  @instance.expects(:fact_value).with(host, 'insights_client::ips').returns(
33
- '[{"obfuscated": "10.230.230.1", "original": "224.0.0.1"}, {"obfuscated": "10.230.230.2", "original": "224.0.0.251"}]'
33
+ '[{"obfuscated": "10.230.230.1", "original": "224.0.0.1"}, {"obfuscated": "10.230.230.255", "original": "224.0.0.251"}]'
34
34
  )
35
35
 
36
36
  actual = @instance.obfuscated_ips(host)
37
37
 
38
38
  assert_equal '10.230.230.1', actual['224.0.0.1']
39
- assert_equal '10.230.230.3', actual['224.0.0.2']
39
+ assert_equal '10.230.231.0', actual['224.0.0.2']
40
40
  end
41
41
 
42
42
  test 'obfuscates ips without insights-client data' do
@@ -25,4 +25,11 @@ class ForemanRhCloudSelfHostTest < ActiveSupport::TestCase
25
25
 
26
26
  assert_not_nil actual
27
27
  end
28
+
29
+ test 'finds host by infrastructure facet' do
30
+ @host = FactoryBot.create(:host, :managed, :with_infrastructure_facet)
31
+ actual = ForemanRhCloud.foreman_host
32
+
33
+ assert_equal @host, actual
34
+ end
28
35
  end
@@ -0,0 +1,75 @@
1
+ require 'test_plugin_helper'
2
+
3
+ class PlaybookProgressGeneratorTest < ActiveSupport::TestCase
4
+ setup do
5
+ @correlation_id = 'CORRELATION_ID'
6
+ @generator = InsightsCloud::Generators::PlaybookProgressGenerator.new(@correlation_id)
7
+ end
8
+
9
+ test 'Outputs host progress message' do
10
+ @generator.host_progress_message('test_host', 'test_output', 0)
11
+
12
+ actual = @generator.generate
13
+ actual_message = JSON.parse(actual)
14
+
15
+ assert_equal "playbook_run_update", actual_message["type"]
16
+ assert_equal 3, actual_message["version"]
17
+ assert_equal @correlation_id, actual_message["correlation_id"]
18
+ assert_equal 0, actual_message["sequence"]
19
+ assert_equal 'test_host', actual_message["host"]
20
+ assert_equal 'test_output', actual_message["console"]
21
+ end
22
+
23
+ test 'Outputs host finished with error message' do
24
+ @generator.host_finished_message('test_host', 100)
25
+
26
+ actual = @generator.generate
27
+ actual_message = JSON.parse(actual)
28
+
29
+ assert_equal "playbook_run_finished", actual_message["type"]
30
+ assert_equal 3, actual_message["version"]
31
+ assert_equal @correlation_id, actual_message["correlation_id"]
32
+ assert_equal 'test_host', actual_message["host"]
33
+ assert_equal 'failure', actual_message["status"]
34
+ assert_equal 0, actual_message["connection_code"]
35
+ assert_equal 100, actual_message["execution_code"]
36
+ end
37
+
38
+ test 'Outputs host finished successfully message' do
39
+ @generator.host_finished_message('test_host', 0)
40
+
41
+ actual = @generator.generate
42
+ actual_message = JSON.parse(actual)
43
+
44
+ assert_equal "playbook_run_finished", actual_message["type"]
45
+ assert_equal 3, actual_message["version"]
46
+ assert_equal @correlation_id, actual_message["correlation_id"]
47
+ assert_equal 'test_host', actual_message["host"]
48
+ assert_equal 'success', actual_message["status"]
49
+ assert_equal 0, actual_message["connection_code"]
50
+ assert_equal 0, actual_message["execution_code"]
51
+ end
52
+
53
+ test 'Outputs job finished message' do
54
+ @generator.job_finished_message
55
+
56
+ actual = @generator.generate
57
+ actual_message = JSON.parse(actual)
58
+
59
+ assert_equal "playbook_run_completed", actual_message["type"]
60
+ assert_equal 3, actual_message["version"]
61
+ assert_equal @correlation_id, actual_message["correlation_id"]
62
+ assert_equal "success", actual_message["status"]
63
+ end
64
+
65
+ test 'Outputs a valid JSONL format' do
66
+ @generator.host_finished_message('test_host1', 0)
67
+ @generator.host_finished_message('test_host2', 0)
68
+
69
+ actual = @generator.generate.lines
70
+ actual_message1 = JSON.parse(actual[0])
71
+ assert_equal 'test_host1', actual_message1['host']
72
+ actual_message2 = JSON.parse(actual[1])
73
+ assert_equal 'test_host2', actual_message2['host']
74
+ end
75
+ end