UNPKG

kui-shell

Version:

This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool

260 lines 12.9 kB
"use strict"; 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()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); const debug_1 = require("debug"); const prettyPrintDuration = require("pretty-ms"); const UI = require("@kui-shell/core/api/ui-lite"); const pretty_print_1 = require("@kui-shell/core/api/pretty-print"); const success_1 = require("../../lib/success"); const fetch_1 = require("../fetch"); const debug = debug_1.default('plugins/tekton/models/modes/trace'); exports.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(); UI.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 = pretty_print_1.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 { UI.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_1.default(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; } exports.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); exports.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', direct: (tab, _) => __awaiter(void 0, void 0, void 0, function* () { const resource = _.resource; const [pipeline, tasks] = yield Promise.all([fetch_1.getPipelineFromRef(tab, resource), fetch_1.getTasks(tab)]); return exports.traceView(tab, resource, pipeline, tasks); }), defaultMode: true, leaveBottomStripeAlone: true }; exports.default = traceMode; //# sourceMappingURL=trace.js.map