UNPKG

@kui-shell/plugin-tekton

Version:
257 lines 12.7 kB
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; import Debug from 'debug'; import * as prettyPrintDuration from 'pretty-ms'; import { empty, prettyPrintTime, i18n } from '@kui-shell/core'; import success from '../../lib/success'; import { getPipelineFromRef, getTasks } from '../fetch'; const strings = i18n('plugin-tekton', 'modes'); const debug = Debug('plugins/tekton/models/modes/trace'); export const render = (tab, activations, container, opts = {}) => { const { noCrop = false, showStart = false, showTimeline = true } = opts; debug('trace', activations); const legendHTMLtext = `<div class='legend-stripe'><div class='legend-entry' data-legend-type='queueing-delays' data-balloon='The time this activation waited for free execution resources' data-balloon-pos='left'>Queueing Delays<div class='legend-icon is-waitTime'></div></div><div class='legend-entry' data-legend-type='container-initialization' data-balloon='The "cold start time", i.e. time spent initializing a container' data-balloon-pos='left'>Container Initialization<div class='legend-icon is-initTime'></div></div><div class='legend-entry' data-legend-type='execution-time' data-balloon='The time this activation spent executing your code' data-balloon-pos='left'>Execution Time<div class='legend-icon is-runTime'></div></div><div class='legend-entry' data-legend-type='failures' data-balloon='The activation failed to complete' data-balloon-pos='left'>Failures<div class='legend-icon is-success-false'></div></div></div>`; const legend = document.createElement('div'); container.appendChild(legend); legend.className = 'legend-trace legend-list'; legend.innerHTML = legendHTMLtext; const logTable = document.createElement('table'); logTable.className = 'log-lines log-lines-loose'; container.appendChild(logTable); const first = 0; const start = activations[first].start; const maxEnd = activations.reduce((max, activation) => Math.max(max, activation.end || activation.start + 1), 0); const dur = Math.max(1, maxEnd - start, maxEnd - start); const tgap = 0; const gaps = new Array(activations.length).fill(0); const normalize = (value, idx) => { return (value - start - gaps[idx]) / (dur - tgap); }; activations.forEach((activation, idx) => { const isSuccess = !activation.end ? true : activation.statusCode !== undefined ? activation.statusCode === 0 : activation.response && activation.response.success; const line = logTable.insertRow(-1); line.className = 'log-line entity'; line.classList.add('activation'); line.setAttribute('data-name', activation.name); if (idx === 0) line.classList.add('log-line-root'); const nextCell = () => line.insertCell(-1); const id = nextCell(); const clicky = document.createElement('span'); clicky.className = 'clickable'; id.appendChild(clicky); id.className = 'log-field'; if (noCrop) id.classList.add('full-width'); clicky.innerText = activation.activationId; id.setAttribute('data-activation-id', id.innerText); const name = nextCell(); const nameClick = document.createElement('span'); name.className = 'slightly-deemphasize log-field entity-name'; nameClick.className = 'clickable'; nameClick.innerText = activation.name; name.appendChild(nameClick); const duration = nextCell(); duration.className = 'somewhat-smaller-text log-field log-field-right-align duration-field'; duration.classList.add(isSuccess ? 'green-text' : 'red-text'); if (activation.end) { duration.innerText = prettyPrintDuration(activation.end - activation.start); } else { duration.innerText = prettyPrintDuration(1); } const waitTime = 0; const initTime = 0; if (showTimeline) { const timeline = nextCell(); empty(timeline); const isRootBar = idx === 0; timeline.className = 'log-field log-line-bar-field'; const bar = document.createElement('div'); bar.style.position = 'absolute'; bar.classList.add('log-line-bar'); bar.classList.add(`is-success-${isSuccess}`); const left = normalize(activation.start + initTime, idx); const right = normalize(idx === 0 ? maxEnd : activation.end || activation.start + initTime + 1, idx); const width = right - left; const balloonPos = right > 0.9 ? 'left' : 'right'; bar.style.left = 100 * left + '%'; bar.style.width = 100 * width + '%'; bar.setAttribute('data-balloon', prettyPrintDuration(activation.end ? activation.end - activation.start - initTime : initTime)); bar.setAttribute('data-balloon-pos', balloonPos); bar.onmouseover = () => legend.setAttribute('data-hover-type', isSuccess ? 'execution-time' : 'failures'); bar.onmouseout = () => legend.removeAttribute('data-hover-type'); let initTimeBar; let waitTimeBar; if (initTime > 0 && !isRootBar) { initTimeBar = document.createElement('div'); const l = normalize(activation.start, idx); const w = normalize(activation.start + initTime, idx) - l; initTimeBar.style.left = 100 * l + '%'; initTimeBar.style.width = 100 * w + '%'; initTimeBar.style.position = 'absolute'; initTimeBar.classList.add('log-line-bar'); initTimeBar.classList.add('is-initTime'); initTimeBar.onmouseover = () => legend.setAttribute('data-hover-type', 'container-initialization'); initTimeBar.onmouseout = () => legend.removeAttribute('data-hover-type'); if (initTime === activation.duration) { initTimeBar.classList.add(`is-success-false`); } else { initTimeBar.classList.add(`is-success-true`); } initTimeBar.setAttribute('data-balloon', prettyPrintDuration(initTime)); initTimeBar.setAttribute('data-balloon-pos', balloonPos); } if (waitTime > 0 && !isRootBar) { waitTimeBar = document.createElement('div'); const l = normalize(activation.start - waitTime, idx); const w = normalize(activation.start, idx) - l; waitTimeBar.style.left = 100 * l + '%'; waitTimeBar.style.width = 100 * w + '%'; waitTimeBar.style.position = 'absolute'; waitTimeBar.classList.add('log-line-bar'); waitTimeBar.classList.add('is-waitTime'); waitTimeBar.setAttribute('data-balloon', prettyPrintDuration(waitTime)); waitTimeBar.setAttribute('data-balloon-pos', balloonPos); waitTimeBar.onmouseover = () => legend.setAttribute('data-hover-type', 'queueing-delays'); waitTimeBar.onmouseout = () => legend.removeAttribute('data-hover-type'); } if (balloonPos === 'right') { timeline.appendChild(bar); if (initTimeBar) timeline.appendChild(initTimeBar); if (waitTimeBar) timeline.appendChild(waitTimeBar); } else { if (waitTimeBar) timeline.appendChild(waitTimeBar); if (initTimeBar) timeline.appendChild(initTimeBar); timeline.appendChild(bar); } } if (showStart) { const start = nextCell(); const startInner = document.createElement('span'); const previous = activations[idx - 1]; const previousWaitTime = 0; const previousStart = previous && previous.start - previousWaitTime; const time = prettyPrintTime(activation.start - waitTime, 'short', previousStart); start.className = 'somewhat-smaller-text lighter-text log-field log-field-right-align start-time-field timestamp-like'; start.appendChild(startInner); if (typeof time === 'string') { startInner.innerText = time; } else { empty(startInner); startInner.appendChild(time); } } }); }; function makeRunActivationLike(run) { const start = run && run.status && run.status.startTime && new Date(run.status.startTime); const end = run && run.status && run.status.completionTime && new Date(run.status.completionTime); const duration = start && end && end.getTime() - start.getTime(); return { activationId: run.metadata.name, name: run.spec.pipelineRef.name, start: start && start.getTime(), end: end && end.getTime(), duration, response: { success: success(run.status.conditions) } }; } function makeSymbolTables(pipeline, jsons) { const taskName2Task = jsons .filter(_ => _.kind === 'Task') .reduce((symtab, task) => { symtab[task.metadata.name] = task; return symtab; }, {}); const taskRefName2Task = pipeline.spec.tasks.reduce((symtab, taskRef) => { symtab[taskRef.name] = taskName2Task[taskRef.taskRef.name]; return symtab; }, {}); return { taskRefName2Task }; } function makeTaskRunsActivationLike(run, pipeline, jsons) { const runs = run && run.status.taskRuns; const { taskRefName2Task } = makeSymbolTables(pipeline, jsons); const activations = Object.keys(runs || []).reduce((M, _) => { const taskRun = runs[_]; const taskRefName = taskRun.pipelineTaskName; const task = taskRefName2Task[taskRefName]; if (!task) { console.error('!! task not found', taskRefName, taskRefName2Task); } else { taskRun.status.steps.forEach(stepRun => { const start = new Date(stepRun.terminated.startedAt).getTime(); const end = new Date(stepRun.terminated.finishedAt).getTime(); const success = stepRun.terminated.reason !== 'Error'; M.push({ activationId: taskRun.pipelineTaskName, name: stepRun.name, start, end, duration: end - start, response: { success } }); }); } return M; }, []); activations.sort((a, b) => a.start - b.start); return activations; } export const traceView = (tab, run, pipeline, jsons) => { const content = document.createElement('div'); content.classList.add('padding-content', 'repl-result'); content.style.flex = '1'; content.style.display = 'flex'; content.style.flexDirection = 'column'; content.style.overflowX = 'hidden'; const runActivation = makeRunActivationLike(run); render(tab, [runActivation].concat(makeTaskRunsActivationLike(run, pipeline, jsons)), content); const badges = ['Tekton']; return { type: 'custom', isEntity: true, name: run.metadata.name, packageName: run.metadata.namespace, prettyType: 'PipelineRun', duration: runActivation.duration, badges, content }; }; const traceMode = { mode: 'trace', label: strings('trace'), content: (tab, resource) => __awaiter(void 0, void 0, void 0, function* () { const [pipeline, tasks] = yield Promise.all([getPipelineFromRef(tab, resource), getTasks(tab)]); return traceView(tab, resource, pipeline, tasks); }), defaultMode: true }; export default traceMode; //# sourceMappingURL=trace.js.map