Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions app/controllers/api/v2/job_invocations_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,16 @@ def index

api :GET, '/job_invocations/:id', N_('Show job invocation')
param :id, :identifier, :required => true
param :host_status, :bool, required: false, desc: N_('Show Job status for the hosts')
param :include_hosts, :bool, :required => false, :default_value => true, :desc => N_('Include hosts and template invocations in the response. Defaults to true for backwards compatibility. Pass false to skip serializing all hosts.')
param :host_status, :bool, :required => false, :default_value => false, :desc => N_('Show job status for each host, only applicable when include_hosts is true')
def show
set_hosts_and_template_invocations
@pattern_template_invocations = @job_invocation.pattern_template_invocations.includes(:input_values)
@job_organization = Taxonomy.find_by(id: @job_invocation.task.input[:current_organization_id])
@job_location = Taxonomy.find_by(id: @job_invocation.task.input[:current_location_id])
if params[:host_status] == 'true'
set_statuses_and_smart_proxies

if include_hosts?
set_hosts_and_template_invocations
set_statuses_and_smart_proxies if Foreman::Cast.to_bool(params[:host_status])
end
end

Expand Down Expand Up @@ -84,13 +87,14 @@ def show

api :POST, '/job_invocations/', N_('Create a job invocation')
param_group :job_invocation, :as => :create
param :include_hosts, :bool, :required => false, :default_value => true, :desc => N_('Include hosts and template invocations in the response. Defaults to true for backwards compatibility. Pass false to skip serializing all hosts.')
def create
composer = JobInvocationComposer.from_api_params(
job_invocation_params
)
composer.trigger!
@job_invocation = composer.job_invocation
@hosts = @job_invocation.targeting.hosts
set_hosts_and_template_invocations if include_hosts?
process_response @job_invocation
rescue JobInvocationComposer::JobTemplateNotFound, JobInvocationComposer::FeatureNotFound => e
not_found(error: { message: e.message })
Expand Down Expand Up @@ -298,7 +302,6 @@ def parent_scope
end

def set_hosts_and_template_invocations
@pattern_template_invocations = @job_invocation.pattern_template_invocations.includes(:input_values)
@hosts = @job_invocation.targeting.hosts.authorized(:view_hosts, Host)

if params[:search].present?
Expand All @@ -310,6 +313,10 @@ def set_hosts_and_template_invocations
.includes(:input_values)
end

def include_hosts?
params[:include_hosts].nil? || Foreman::Cast.to_bool(params[:include_hosts])
end

def set_statuses_and_smart_proxies
template_invocations = @template_invocations.where(host_id: @hosts.select(:id))
.includes(:run_host_job_task).to_a
Expand Down
26 changes: 15 additions & 11 deletions app/views/api/v2/job_invocations/main.json.rabl
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ child :targeting do
attributes :bookmark_id, :bookmark_name, :search_query, :targeting_type, :user_id, :status, :status_label,
:randomized_ordering

child @hosts => :hosts do
extends 'api/v2/hosts/base'
if @hosts
child @hosts => :hosts do
extends 'api/v2/hosts/base'

if params[:host_status] == 'true'
node :job_status do |host|
@host_statuses[host.id]
if @host_statuses
node :job_status do |host|
@host_statuses[host.id]
end
end
end
end
Expand All @@ -34,12 +36,14 @@ child :task do
attributes :id, :state, :started_at
end

child @template_invocations do
attributes :template_id, :template_name, :host_id
child :input_values do
attributes :template_input_name, :template_input_id
node :value do |iv|
iv.template_input.respond_to?(:hidden_value) && iv.template_input.hidden_value? ? '*' * 5 : iv.value
if @template_invocations
child @template_invocations => :template_invocations do
attributes :template_id, :template_name, :host_id
child :input_values do
attributes :template_input_name, :template_input_id
node :value do |iv|
iv.template_input.respond_to?(:hidden_value) && iv.template_input.hidden_value? ? '*' * 5 : iv.value
end
end
end
end
54 changes: 51 additions & 3 deletions test/functional/api/v2/job_invocations_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@ class JobInvocationsControllerTest < ActionController::TestCase
template = ActiveSupport::JSON.decode(@response.body)
assert_not_empty template
assert_equal template['job_category'], @invocation.job_category
assert_not_empty template['targeting']['hosts']
assert_not_nil template['targeting']
assert_not_empty template['pattern_template_invocations']
assert_not_nil template['targeting']['hosts']
assert_not_nil template['template_invocations']
end

test 'should get invocation detail when taxonomies are set' do
Expand All @@ -36,9 +39,17 @@ class JobInvocationsControllerTest < ActionController::TestCase
assert_response :success
end

test 'should exclude hosts when include_hosts=false' do
get :show, params: { :id => @invocation.id, :include_hosts => false }
assert_response :success
result = ActiveSupport::JSON.decode(@response.body)
assert_nil result['targeting']['hosts']
assert_nil result['template_invocations']
end

test 'should see only permitted hosts' do
@user = FactoryBot.create(:user, admin: false)
@invocation.task.update(user: @user)
@user = FactoryBot.create(:user, :admin => false)
@invocation.task.update(:user => @user)
setup_user('view', 'job_invocations', nil, @user)
setup_user('view', 'hosts', 'name ~ nope.example.com', @user)

Expand All @@ -47,6 +58,29 @@ class JobInvocationsControllerTest < ActionController::TestCase
response = ActiveSupport::JSON.decode(@response.body)
assert_equal response['targeting']['hosts'], []
end

test 'should ignore host_status when include_hosts=false' do
get :show, params: { :id => @invocation.id, :include_hosts => false, :host_status => true }
assert_response :success
result = ActiveSupport::JSON.decode(@response.body)
assert_nil result['targeting']['hosts']
assert_nil result['template_invocations']
end

test 'should include job_status per host when host_status=true' do
invocation = FactoryBot.create(:job_invocation, :with_template, :with_task)
invocation.template_invocations << FactoryBot.create(:template_invocation, :with_task, :with_host, :job_invocation => invocation)
invocation.job_category = invocation.pattern_template_invocations.first.template.job_category
invocation.targeting.hosts = invocation.template_invocations.map(&:host)
invocation.save!

get :show, params: { :id => invocation.id, :include_hosts => true, :host_status => true }
assert_response :success
result = ActiveSupport::JSON.decode(@response.body)
hosts = result['targeting']['hosts']
assert_not_empty hosts
assert hosts.all? { |h| h.key?('job_status') }, 'Expected job_status to be present on each host'
end
end

context 'creation' do
Expand All @@ -66,6 +100,20 @@ class JobInvocationsControllerTest < ActionController::TestCase
assert_response :success
end

test 'should include hosts in create response by default' do
post :create, params: { job_invocation: @attrs }
invocation = ActiveSupport::JSON.decode(@response.body)
assert_not_nil invocation['targeting']['hosts']
assert_response :success
end

test 'should exclude hosts from create response when include_hosts=false' do
post :create, params: { job_invocation: @attrs, include_hosts: false }
invocation = ActiveSupport::JSON.decode(@response.body)
assert_nil invocation['targeting']['hosts']
assert_response :success
end

test 'should create with description format overridden' do
@attrs[:description_format] = 'format'
post :create, params: { job_invocation: @attrs }
Expand Down
3 changes: 2 additions & 1 deletion webpack/JobInvocationDetail/JobInvocationActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const getJobInvocation = url => dispatch => {
const fetchData = withInterval(
get({
key: JOB_INVOCATION_KEY,
params: { include_permissions: true },
params: { include_permissions: true, include_hosts: false },
url,
handleError: () => {
dispatch(stopInterval(JOB_INVOCATION_KEY));
Expand All @@ -44,6 +44,7 @@ export const updateJob = jobId => dispatch => {
APIActions.get({
url,
key: UPDATE_JOB,
params: { include_hosts: false },
})
);
};
Expand Down
Loading