Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add task step in to pipeline diagram #560

Merged
merged 4 commits into from
Apr 30, 2021
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
Empty file removed .vscode/test.json.code-snippets
Empty file.
38 changes: 38 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
},
"bugs": "https://github.com/redhat-developer/vscode-tekton/issues",
"engines": {
"vscode": "^1.50.0"
"vscode": "^1.50.0",
"npm": ">=7.0.0"
},
"categories": [
"Snippets",
Expand Down Expand Up @@ -930,6 +931,7 @@
},
"devDependencies": {
"@openapitools/openapi-generator-cli": "^2.1.18",
"@popperjs/core": "^2.9.2",
"@types/byline": "^4.2.31",
"@types/chai": "^4.1.7",
"@types/collections": "^5.0.0",
Expand Down Expand Up @@ -960,6 +962,7 @@
"css-loader": "^3.6.0",
"cytoscape": "^3.14.0",
"cytoscape-dagre": "^2.2.2",
"cytoscape-popper": "^2.0.0",
"decache": "^4.5.1",
"eslint": "^6.7.2",
"eslint-plugin-header": "^3.0.0",
Expand Down
5 changes: 5 additions & 0 deletions src/cli-command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export function newK8sCommand(...k8sArguments: string[]): CliCommand {
return createCliCommand('kubectl', ...k8sArguments);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function verbose(_target: unknown, key: string, descriptor: any): void {
let fnKey: string | undefined;
let fn: Function;
Expand Down Expand Up @@ -258,6 +259,10 @@ export class Command {
return newK8sCommand('get', 'taskrun', '-l', `tekton.dev/pipelineRun=${pipelineRunName}`);
}

static getTask(name: string, type: 'clustertask' | 'task.tekton'): CliCommand {
return newK8sCommand('get', type, name, '-o', 'json');
}

@verbose
static deleteTask(name: string): CliCommand {
return newTknCommand('task', 'delete', name, '-f');
Expand Down
27 changes: 27 additions & 0 deletions src/humanizer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/
import * as humanize from 'humanize-duration';

export const humanizer = humanize.humanizer(createConfig());
function createConfig(): humanize.HumanizerOptions {
return {
language: 'shortEn',
languages: {
shortEn: {
y: () => 'y',
mo: () => 'mo',
w: () => 'w',
d: () => 'd',
h: () => 'h',
m: () => 'm',
s: () => 's',
ms: () => 'ms',
}
},
round: true,
largest: 2,
conjunction: ' '
};
}
59 changes: 51 additions & 8 deletions src/pipeline/pipeline-graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,38 +6,48 @@
import * as vscode from 'vscode';
import { tektonYaml, pipelineYaml, pipelineRunYaml, TektonYamlType, DeclaredTask, PipelineRunTask } from '../yaml-support/tkn-yaml';
import { YamlDocument, VirtualDocument } from '../yaml-support/yaml-locator';
import { humanizer, getPipelineRunTaskState } from '../tkn';
import { NodeOrEdge, NodeData, EdgeData } from '../webview/pipeline-preview/model';
import { getPipelineRunTaskState, tkn } from '../tkn';
import { NodeOrEdge, NodeData, EdgeData, StepData } from '../webview/pipeline-preview/model';
import { PipelineRunData, TaskRuns, TaskRun, PipelineRunConditionCheckStatus } from '../tekton';
import { tektonFSUri, tektonVfsProvider } from '../util/tekton-vfs';
import { ContextType } from '../context-type';
import { humanizer } from '../humanizer';

export interface GraphProvider {
(document: vscode.TextDocument | VirtualDocument, pipelineRun?: PipelineRunData): Promise<NodeOrEdge[]>;
getGraph(document: vscode.TextDocument | VirtualDocument, pipelineRun?: PipelineRunData): Promise<NodeOrEdge[]>;
getElementBySelection?(document: vscode.TextDocument, selection: vscode.Selection): string | undefined;
getTaskSteps(document: vscode.TextDocument | VirtualDocument, task: NodeData): Promise<StepData[]>;
}
export const pipelineGraph: GraphProvider = {
getGraph: calculatePipelineGraph,
getElementBySelection,
getTaskSteps: getPipelineTaskSteps
}

export const pipelineRunGraph: GraphProvider = {
getGraph: calculatePipelineRunGraph,
getTaskSteps: getPipelineRunTaskSteps
}
export async function calculatePipelineGraph(document: vscode.TextDocument): Promise<NodeOrEdge[]> {

const doc: YamlDocument = await getPipelineDocument(document, TektonYamlType.Pipeline);
if (!doc) {
return []; // TODO: throw error there
return [];
}

const tasks = pipelineYaml.getPipelineTasks(doc);

return convertTasksToNode(tasks);
}

calculatePipelineGraph.getElementBySelection = function (document: vscode.TextDocument, selection: vscode.Selection): string | undefined {

export function getElementBySelection(document: vscode.TextDocument, selection: vscode.Selection): string | undefined {
return pipelineYaml.findTask(document, selection.start);
}

export async function calculatePipelineRunGraph(document: VirtualDocument, pipelineRun?: PipelineRunData): Promise<NodeOrEdge[]> {
const doc: YamlDocument = await getPipelineDocument(document, TektonYamlType.PipelineRun);
if (!doc) {
return []; // TODO: throw error there
return [];
}
let tasks: DeclaredTask[];
const refOrSpec = pipelineRunYaml.getTektonPipelineRefOrSpec(doc);
Expand Down Expand Up @@ -110,7 +120,7 @@ function convertTasksToNode(tasks: PipelineRunTask[], includePositions = true):
tasks.forEach((task: DeclaredTask) => tasksMap.set( task.id, task));

for (const task of tasks) {
result.push({ data: { id: task.id, label: getLabel(task), type: task.kind, taskRef: task.taskRef, state: task.state, yamlPosition: includePositions ? task.position : undefined, final: task.final } as NodeData });
result.push({ data: { id: task.id, label: getLabel(task), type: task.kind, taskRef: task.taskRef, state: task.state, yamlPosition: includePositions ? task.position : undefined, final: task.final, steps: task.steps ?? undefined } as NodeData });
for (const after of task.runAfter ?? []) {
if (tasksMap.has(after)) {
result.push({ data: { source: after, target: task.id, id: `${after}-${ task.id}`, state: tasksMap.get(after).state } as EdgeData });
Expand Down Expand Up @@ -161,6 +171,7 @@ function updatePipelineRunTasks(pipelineRun: PipelineRunData, tasks: DeclaredTas
const steps = (taskRun as TaskRun).status?.steps;
if (steps) {
runTask.stepsCount = steps.length;
runTask.steps = steps;
let finishedSteps = 0;
for (const step of steps) {
const terminated = step.terminated;
Expand Down Expand Up @@ -201,3 +212,35 @@ function findConditionInTaskRun(name: string, taskRuns: TaskRuns): PipelineRunCo
}
}
}

async function getPipelineTaskSteps(document: vscode.TextDocument | VirtualDocument, task: NodeData): Promise<StepData[] | undefined> {
if (task.type === 'Task' || task.type === 'ClusterTask') {
try {
const rawTask = await tkn.getRawTask(task.taskRef, task.type);
return rawTask.spec?.steps.map(it => { return {name: it.name}});
} catch (err) {
console.error(err);
return undefined;
}
}
if (task.type === 'TaskSpec') {
// task steps should be provided by initial parsing if present
return;
}

return undefined;
}

async function getPipelineRunTaskSteps(document: vscode.TextDocument | VirtualDocument, task: NodeData): Promise<StepData[]| undefined> {

if (task.type === 'Task' || task.type === 'ClusterTask') {
try {
const rawTask = await tkn.getRawTask(task.taskRef, task.type);
return rawTask.spec?.steps.map(it => { return {name: it.name}});
} catch (err) {
console.error(err);
return undefined;
}
}
return undefined;
}
8 changes: 4 additions & 4 deletions src/pipeline/pipeline-preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as vscode from 'vscode';
import { TektonYamlType, tektonYaml, pipelineRunYaml } from '../yaml-support/tkn-yaml';
import { previewManager, PreviewSettings } from './preview-manager';
import { CommandContext, setCommandContext } from '../commands';
import { calculatePipelineGraph, calculatePipelineRunGraph, askToSelectPipeline } from './pipeline-graph';
import { askToSelectPipeline, pipelineGraph, pipelineRunGraph } from './pipeline-graph';
import { tektonFSUri, tektonVfsProvider } from '../util/tekton-vfs';
import { telemetryLog } from '../telemetry';
import { ContextType } from '../context-type';
Expand All @@ -20,7 +20,7 @@ export async function showPipelinePreview(commandId?: string): Promise<void> {
const resourceColumn = (vscode.window.activeTextEditor && vscode.window.activeTextEditor.viewColumn) || vscode.ViewColumn.One;
const pipelines = tektonYaml.getTektonDocuments(document, TektonYamlType.Pipeline)
if (pipelines?.length > 0) {
previewManager.showPipelinePreview(document, { resourceColumn, previewColumn: resourceColumn + 1, graphProvider: calculatePipelineGraph });
previewManager.showPipelinePreview(document, { resourceColumn, previewColumn: resourceColumn + 1, graphProvider: pipelineGraph });
return;
}

Expand All @@ -37,7 +37,7 @@ export async function showPipelinePreview(commandId?: string): Promise<void> {
previewManager.showPipelinePreview(document, {
resourceColumn,
previewColumn: resourceColumn + 1,
graphProvider: calculatePipelineRunGraph,
graphProvider: pipelineRunGraph,
pipelineRunName: pipelineRunYaml.getPipelineRunName(pipelineRunDoc),
pipelineRunStatus: pipelineRunYaml.getPipelineRunStatus(pipelineRunDoc)
} as PreviewSettings);
Expand All @@ -57,7 +57,7 @@ export async function showPipelineRunPreview(name: string, uid?: string): Promis
previewManager.createPipelinePreview(pipelineRunDoc, {
resourceColumn: vscode.ViewColumn.Active,
previewColumn: vscode.ViewColumn.Active,
graphProvider: calculatePipelineRunGraph,
graphProvider: pipelineRunGraph,
pipelineRunName: name,
pipelineRunStatus: pipelineRunYaml.getPipelineRunStatus(pipelineRun[0])
} as PreviewSettings);
Expand Down
30 changes: 19 additions & 11 deletions src/pipeline/preview.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { kubectl } from '../kubectl';
import { PipelineRunData } from '../tekton';
import { NodeData } from '../webview/pipeline-preview/model';
import { VirtualDocument } from '../yaml-support/yaml-locator';
import { telemetryLogError } from '../telemetry';

export interface PipelinePreviewInput {
readonly document: vscode.TextDocument | VirtualDocument;
Expand Down Expand Up @@ -73,6 +74,11 @@ export class PipelinePreview extends Disposable {
case 'onDidClick':
this.onDidClick(e.body);
break;
case 'getSteps':
this.handleGetSteps(e.body);
break;
default:
console.error(`Cannot handle message: ${e.type}`);
}
}));

Expand Down Expand Up @@ -154,6 +160,17 @@ export class PipelinePreview extends Disposable {

}

private async handleGetSteps(node: NodeData): Promise<void> {
try {
const steps = await this.graphProvider.getTaskSteps(this.document, node);
this.postMessage({type: 'showSteps', data: steps});
} catch (err) {
console.error(err);
telemetryLogError('Pipeline Diagram', err);
}

}

private isPreviewOf(resource: vscode.Uri): boolean {
return this.document.uri.fsPath === resource.fsPath;
}
Expand All @@ -166,7 +183,7 @@ export class PipelinePreview extends Disposable {
this.setContent(html);

try {
const graph = await this.graphProvider(this.document);
const graph = await this.graphProvider.getGraph(this.document);
this.postMessage({ type: 'showData', data: graph });
} catch (err) {
console.error(err);
Expand All @@ -175,7 +192,7 @@ export class PipelinePreview extends Disposable {

private async updatePipelineRun(run: PipelineRunData): Promise<void> {
try {
const graph = await this.graphProvider(this.document, run);
const graph = await this.graphProvider.getGraph(this.document, run);
this.postMessage({ type: 'showData', data: graph });
} catch (err) {
console.error(err);
Expand All @@ -191,15 +208,13 @@ export class PipelinePreview extends Disposable {
private setContent(html: string): void {
const fileName = path.basename(this.document.uri.fsPath);
this.editor.title = `Preview ${fileName}`;
// this.editor.iconPath = this.iconPath; //TODO: implement
this.editor.webview.options = getWebviewOptions();
this.editor.webview.html = html;
}

private getHmlContent(): string {
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
const rule = this.editor.webview.cspSource;
//<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' AllowScriptsAndAllContent https: data: http://localhost:* http://127.0.0.1:*; media-src 'self' AllowScriptsAndAllContent https: data: http://localhost:* http://127.0.0.1:*; script-src 'unsafe-eval' 'nonce-${nonce}'; style-src 'self' AllowScriptsAndAllContent 'unsafe-inline' https: data: http://localhost:* http://127.0.0.1:*; font-src 'self' AllowScriptsAndAllContent https: data: http://localhost:* http://127.0.0.1:*;">
return `<!DOCTYPE html>
<html>
<head>
Expand Down Expand Up @@ -242,13 +257,6 @@ export class PipelinePreview extends Disposable {
return out.join('\n');
}

private getImagesUri(): { [key: string]: string } {
const result: { [key: string]: string } = Object.create(null);
result['task'] = this.editor.webview.asWebviewUri(vscode.Uri.file(path.join(contextGlobalState.extensionPath, 'images', 'T.svg'))).toString();
result['clustertask'] = this.editor.webview.asWebviewUri(vscode.Uri.file(path.join(contextGlobalState.extensionPath, 'images', 'CT.svg'))).toString();
return result;
}

}

function escapeAttribute(value: string | vscode.Uri): string {
Expand Down
Loading