Initial
This commit is contained in:
@@ -17,11 +17,26 @@ function visibleJobClause() {
|
||||
function outputSelect() {
|
||||
return `
|
||||
SELECT jo.*, j.status AS job_status, j.runplan_id, j.script_id, j.triggered_by,
|
||||
r.name AS runplan_name, s.name AS script_name
|
||||
r.name AS runplan_name, s.name AS script_name,
|
||||
COALESCE(hs.host_total_count, 0) AS host_total_count,
|
||||
COALESCE(hs.host_success_count, 0) AS host_success_count,
|
||||
COALESCE(hs.host_failed_count, 0) AS host_failed_count,
|
||||
COALESCE(hs.host_skipped_count, 0) AS host_skipped_count,
|
||||
COALESCE(hs.host_canceled_count, 0) AS host_canceled_count
|
||||
FROM job_outputs jo
|
||||
JOIN jobs j ON j.id = jo.job_id
|
||||
LEFT JOIN runplans r ON r.id = j.runplan_id
|
||||
LEFT JOIN scripts s ON s.id = j.script_id
|
||||
LEFT JOIN (
|
||||
SELECT job_id,
|
||||
COUNT(*) AS host_total_count,
|
||||
SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) AS host_success_count,
|
||||
SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) AS host_failed_count,
|
||||
SUM(CASE WHEN status = 'skipped' THEN 1 ELSE 0 END) AS host_skipped_count,
|
||||
SUM(CASE WHEN status = 'canceled' THEN 1 ELSE 0 END) AS host_canceled_count
|
||||
FROM job_hosts
|
||||
GROUP BY job_id
|
||||
) hs ON hs.job_id = j.id
|
||||
`;
|
||||
}
|
||||
|
||||
|
||||
@@ -453,6 +453,19 @@ export function camelJobLog(row) {
|
||||
}
|
||||
|
||||
export function camelJobOutput(row) {
|
||||
const hostSuccessCount = row.host_success_count || 0;
|
||||
const hostFailedCount = row.host_failed_count || 0;
|
||||
const hostSkippedCount = row.host_skipped_count || 0;
|
||||
const hostCanceledCount = row.host_canceled_count || 0;
|
||||
const outputStatus = hostSuccessCount > 0
|
||||
? 'completed'
|
||||
: hostFailedCount > 0
|
||||
? 'failed'
|
||||
: hostCanceledCount > 0
|
||||
? 'canceled'
|
||||
: hostSkippedCount > 0
|
||||
? 'skipped'
|
||||
: row.job_status || row.status || '';
|
||||
return {
|
||||
id: row.id,
|
||||
jobId: row.job_id,
|
||||
@@ -467,6 +480,12 @@ export function camelJobOutput(row) {
|
||||
scriptId: row.script_id || null,
|
||||
scriptName: row.script_name || null,
|
||||
jobStatus: row.job_status || row.status || '',
|
||||
outputStatus,
|
||||
hostTotalCount: row.host_total_count || 0,
|
||||
hostSuccessCount,
|
||||
hostFailedCount,
|
||||
hostSkippedCount,
|
||||
hostCanceledCount,
|
||||
triggeredBy: row.triggered_by || null,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
|
||||
@@ -97,6 +97,32 @@ test('script test runs generate the same FileLocker job output artifacts', async
|
||||
updateSettings({ allow_script_execution: 'true' });
|
||||
});
|
||||
|
||||
test('job output status is successful when at least one host completed', async () => {
|
||||
const scriptId = 'scr_job_output_mixed_status_test';
|
||||
const jobId = 'job_output_mixed_status_test';
|
||||
db.prepare(`INSERT INTO scripts (id, name, content, visibility, owner_user_id, version, created_at, updated_at)
|
||||
VALUES (?, 'Mixed Host Status.ps1', 'Write-Output mixed', 'personal', ?, 1, ?, ?)`).run(scriptId, owner, ts, ts);
|
||||
const hostA = createHost({ name: 'Mixed Success Host', address: 'mixed-a.local', transport: 'local', osFamily: 'linux', visibility: 'personal' }, owner);
|
||||
const hostB = createHost({ name: 'Mixed Failed Host', address: 'mixed-b.local', transport: 'local', osFamily: 'linux', visibility: 'personal' }, owner);
|
||||
db.prepare(`INSERT INTO jobs (id, runplan_id, script_id, status, triggered_by, started_at, ended_at, created_at)
|
||||
VALUES (?, NULL, ?, 'failed', ?, ?, ?, ?)`).run(jobId, scriptId, owner, ts, ts, ts);
|
||||
db.prepare(`INSERT INTO job_hosts (id, job_id, host_id, status, exit_code, started_at, ended_at)
|
||||
VALUES (?, ?, ?, 'completed', 0, ?, ?)`).run('jhost_mixed_success', jobId, hostA.id, ts, ts);
|
||||
db.prepare(`INSERT INTO job_hosts (id, job_id, host_id, status, exit_code, started_at, ended_at)
|
||||
VALUES (?, ?, ?, 'failed', 1, ?, ?)`).run('jhost_mixed_failed', jobId, hostB.id, ts, ts);
|
||||
addLog(jobId, hostA.id, 'stdout', 'success payload', 1);
|
||||
addLog(jobId, hostB.id, 'stderr', 'failed payload', 2);
|
||||
|
||||
await generateJobOutputs(jobId);
|
||||
const output = listJobOutputsForJob(jobId, owner, false).find((row) => row.kind === 'stdout-pdf');
|
||||
|
||||
assert.equal(output.jobStatus, 'failed');
|
||||
assert.equal(output.outputStatus, 'completed');
|
||||
assert.equal(output.hostSuccessCount, 1);
|
||||
assert.equal(output.hostFailedCount, 1);
|
||||
assert.equal(output.hostTotalCount, 2);
|
||||
});
|
||||
|
||||
test('job output datatable uses one last-stdout row per host for unstructured output', async () => {
|
||||
const scriptId = 'scr_job_output_last_line_test';
|
||||
const jobId = 'job_output_last_line_test';
|
||||
|
||||
Reference in New Issue
Block a user