@kui-shell/plugin-tekton
Version:
Visualizations for Tekton Pipelines
358 lines • 14.6 kB
JavaScript
;
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 core_1 = require("@kui-shell/core");
const success_1 = require("./success");
const resource_1 = require("../model/resource");
const debug = debug_1.default('plugins/tekton/lib/tekton2graph');
const defaultHeight = 13;
const defaultCharWidth = 3.25;
const makeSubGraph = (label = 'root', { visited, children, tooltip, tooltipColor, type, onclick } = { children: [] }) => {
return {
id: label,
label,
onclick,
children,
visited,
edges: [],
nParents: 0,
nChildren: 0,
type,
tooltip,
tooltipColor
};
};
const stepId = (taskRef, step) => `__step__${taskRef.name}__${step.name}`;
const getPipeline = (jsons) => {
const declaredPipeline = jsons.find(_ => _.kind === 'Pipeline');
if (resource_1.isPipeline(declaredPipeline)) {
return declaredPipeline;
}
else {
const tasks = jsons.filter(_ => _.kind === 'Task');
if (tasks.length === 0) {
throw new Error('No pipeline defined, and no Tasks defined');
}
else {
const pipeline = {
apiVersion: 'tekton.dev/v1alpha1',
kind: 'Pipeline',
metadata: {
name: 'pipeline'
},
originatingCommand: undefined,
spec: {
tasks: tasks.map(task => ({
name: task.metadata.name,
taskRef: {
name: task.metadata.name
}
}))
}
};
return pipeline;
}
}
};
function addEdge(graph, parent, child, { singletonSource, singletonTarget, hasRuns }) {
debug('addEdge', parent.id, child.id);
if (!parent.ports) {
parent.ports = [];
}
if (!child.ports) {
child.ports = [];
}
const targetPort = `${child.id}-` + (singletonTarget ? 'pTargetSingleton' : `p${child.ports.length}`);
if (!child.ports.find(_ => _.id === targetPort)) {
child.ports.push({ id: targetPort });
}
const sourcePort = `${parent.id}-` + (singletonSource ? 'pSourceSingleton' : `p${parent.ports.length}`);
if (!parent.ports.find(_ => _.id === sourcePort)) {
parent.ports.push({ id: sourcePort });
}
graph.edges.push({
id: `${parent.id}-${child.id}`,
source: parent.id,
sourcePort,
target: child.id,
targetPort,
visited: !hasRuns ? undefined : !!(parent.visited && child.visited)
});
child.nParents++;
parent.nChildren++;
}
function default_1(jsons, filepath, run) {
return __awaiter(this, void 0, void 0, function* () {
debug('jsons', jsons);
const pipeline = getPipeline(jsons);
debug('pipeline', pipeline);
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;
}, {});
const taskRefName2TaskRef = pipeline.spec.tasks.reduce((symtab, taskRef) => {
symtab[taskRef.name] = taskRef;
return symtab;
}, {});
const runs = run && run.status.taskRuns;
const startVisit = (run && [
{
start: new Date(run.status.startTime).getTime(),
duration: 0,
response: {
success: true
}
}
]) ||
[];
const endVisit = (run &&
run.status.completionTime && [
{
start: new Date(run.status.completionTime).getTime(),
duration: 0,
response: {
success: success_1.default(run.status.conditions)
}
}
]) ||
[];
const runInfo = runs &&
Object.keys(runs).reduce((M, _) => {
const taskRun = runs[_];
const taskRefName = taskRun.pipelineTaskName;
const task = taskRefName2Task[taskRefName];
if (task) {
const start = new Date(taskRun.status.startTime).getTime();
task.visitedIdx = M.length;
M.push({
start,
duration: taskRun.status.completionTime ? new Date(taskRun.status.completionTime).getTime() - start : 0,
response: {
success: success_1.default(taskRun.status.conditions)
}
});
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';
const step = task.spec.steps.find(_ => _.name === stepRun.name);
if (step) {
step.visitedIdx = M.length;
M.push({
start,
duration: end - start,
response: {
success
}
});
}
});
}
return M;
}, startVisit.concat(endVisit));
const graph = {
id: 'root',
label: 'root',
edges: [],
children: [],
nChildren: 0,
nParents: 0,
runs: runInfo,
properties: {
maxLabelLength: 24,
fontSize: '4px'
}
};
const start = {
id: 'Entry',
label: 'start',
type: 'Entry',
width: 18,
height: 18,
nChildren: 0,
nParents: 0,
visited: run && [0],
properties: {
title: 'The flow starts here',
fontSize: '4.5px'
}
};
const end = {
id: 'Exit',
label: 'end',
type: 'Exit',
width: 18,
height: 18,
nChildren: 0,
nParents: 0,
visited: run && [1],
properties: {
title: 'The flow ends here',
fontSize: '4.5px'
}
};
const symbolTable = pipeline.spec.tasks.reduce((symtab, taskRef) => {
const task = taskName2Task[taskRef.taskRef.name];
debug('TaskRef', taskRef.name, task);
const filearg = filepath ? `-f ${core_1.encodeComponent(filepath)}` : '';
let node;
if (task && task.spec.steps && task.spec.steps.length > 0) {
const resources = (task.spec.inputs && task.spec.inputs.resources) || [];
const resourceList = `${resources.map(_ => `<span class='color-base0A'>${_.type}</span>:${_.name}`).join(', ')}`;
const params = (task.spec.inputs && task.spec.inputs.params) || [];
const paramList = `(${params.map(_ => _.name).join(', ')})`;
const subgraph = makeSubGraph(taskRef.name, {
type: 'Tekton Task',
tooltip: `<table><tr><td><strong>Resources</strong></td><td>${resourceList}</td></tr><tr><td><strong>Params</strong></td><td>${paramList}</td></tr></table>`,
tooltipColor: '0C',
onclick: `tekton get task ${core_1.encodeComponent(pipeline.metadata.name)} ${core_1.encodeComponent(task.metadata.name)} ${filearg}`,
visited: task.visitedIdx !== undefined ? [task.visitedIdx] : undefined,
children: task.spec.steps.map(step => {
const stepNode = {
id: stepId(taskRef, step),
label: step.name,
width: step.name.length * defaultCharWidth,
height: defaultHeight,
nChildren: 0,
nParents: 0,
deployed: false,
visited: step.visitedIdx !== undefined ? [step.visitedIdx] : undefined,
type: 'Tekton Step',
tooltip: `<strong>Image</strong>: ${step.image}`,
tooltipColor: '0E',
onclick: `tekton get step ${core_1.encodeComponent(pipeline.metadata.name)} ${core_1.encodeComponent(task.metadata.name)} ${core_1.encodeComponent(step.name)} ${filearg}`
};
symtab[stepNode.id] = stepNode;
return stepNode;
})
});
subgraph.children.slice(1).reduce((cur, next) => {
addEdge(subgraph, cur, next, { hasRuns: runs !== undefined });
return next;
}, subgraph.children[0]);
node = subgraph;
}
else {
node = {
id: taskRef.name,
label: taskRef.name,
width: taskRef.name.length * defaultCharWidth,
height: defaultHeight,
nChildren: 0,
nParents: 0,
type: 'Tekton Task',
tooltip: 'test'
};
}
symtab[taskRef.name] = node;
graph.children.push(node);
return symtab;
}, {});
const lastStepOf = (node) => {
const taskRef = taskRefName2TaskRef[node.id];
const task = taskRefName2Task[node.id];
return task && symbolTable[stepId(taskRef, task.spec.steps[task.spec.steps.length - 1])];
};
const firstStepOf = (node) => {
const taskRef = taskRefName2TaskRef[node.id];
const task = taskRefName2Task[node.id];
return task && symbolTable[stepId(taskRef, task.spec.steps[0])];
};
const _addEdge = (parent, child, opts = { hasRuns: runs !== undefined }) => {
const lastStepOfParentTask = lastStepOf(parent);
const firstStepOfChildTask = firstStepOf(child);
if (lastStepOfParentTask && firstStepOfChildTask) {
addEdge(graph, lastStepOfParentTask, firstStepOfChildTask, {
singletonSource: true,
singletonTarget: true,
hasRuns: runs !== undefined
});
parent.nChildren++;
child.nParents++;
}
else if (!lastStepOfParentTask && firstStepOfChildTask) {
addEdge(graph, parent, firstStepOfChildTask, {
singletonSource: opts.singletonSource || false,
singletonTarget: true,
hasRuns: runs !== undefined
});
child.nParents++;
}
else if (lastStepOfParentTask && !firstStepOfChildTask) {
addEdge(graph, lastStepOfParentTask, child, {
singletonSource: true,
singletonTarget: opts.singletonTarget || false,
hasRuns: runs !== undefined
});
parent.nChildren++;
}
else {
addEdge(graph, parent, child, Object.assign({}, opts, { hasRuns: runs !== undefined }));
}
};
const wire = (parentTaskRefName, childTaskRef) => {
const parent = symbolTable[parentTaskRefName];
const child = symbolTable[childTaskRef.name];
if (parent) {
_addEdge(parent, child);
}
else {
console.error('parent not found', childTaskRef);
}
};
pipeline.spec.tasks.forEach((task) => {
if (task.runAfter) {
task.runAfter.forEach(parentTaskName => {
wire(parentTaskName, task);
});
}
if (task.resources) {
const wirePorts = (ports) => {
if (ports) {
ports.forEach(port => {
if (port.from) {
port.from.forEach(parentTaskName => {
wire(parentTaskName, task);
});
}
});
}
};
wirePorts(task.resources.inputs);
wirePorts(task.resources.outputs);
}
});
graph.children
.filter(child => child.nParents === 0)
.forEach(child => _addEdge(start, child, {
singletonSource: true,
hasRuns: graph.runs !== undefined
}));
graph.children
.filter(parent => parent.nChildren === 0)
.forEach(parent => _addEdge(parent, end, {
singletonTarget: true,
hasRuns: graph.runs !== undefined
}));
graph.children.push(start);
graph.children.push(end);
return graph;
});
}
exports.default = default_1;
//# sourceMappingURL=tekton2graph.js.map