From 4313cdb328917ce3307d8ce9d26653a7d8cc97af Mon Sep 17 00:00:00 2001 From: Yevhen Vydolob Date: Wed, 27 May 2020 09:36:43 +0300 Subject: [PATCH] Add command to open task definition for taskrun item (#302) * #252 add command to open task definition for taskrun item Signed-off-by: Yevhen Vydolob * add support for ClusterTask taskrun Signed-off-by: Yevhen Vydolob * check if taskRun is not undefined Signed-off-by: Yevhen Vydolob --- .vscode/settings.json | 2 +- images/dark/go-to-task.svg | 4 ++++ images/light/go-to-task.svg | 4 ++++ package.json | 20 ++++++++++++++++- src/extension.ts | 1 + src/tekton/taskrun.ts | 37 ++++++++++++++++++++++++++++--- src/tkn.ts | 11 ++++++++-- test/tekton/taskrun.test.ts | 43 +++++++++++++++++++++++++++++++++++++ 8 files changed, 115 insertions(+), 7 deletions(-) create mode 100644 images/dark/go-to-task.svg create mode 100644 images/light/go-to-task.svg diff --git a/.vscode/settings.json b/.vscode/settings.json index 7d7ece5c..cb63b4a3 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,5 @@ // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off", "debug.node.autoAttach": "off", - "svg.preview.background": "transparent" + "svg.preview.background": "custom" } diff --git a/images/dark/go-to-task.svg b/images/dark/go-to-task.svg new file mode 100644 index 00000000..8873c580 --- /dev/null +++ b/images/dark/go-to-task.svg @@ -0,0 +1,4 @@ + + + + diff --git a/images/light/go-to-task.svg b/images/light/go-to-task.svg new file mode 100644 index 00000000..a98e32aa --- /dev/null +++ b/images/light/go-to-task.svg @@ -0,0 +1,4 @@ + + + + diff --git a/package.json b/package.json index b9028746..34f725dd 100644 --- a/package.json +++ b/package.json @@ -161,6 +161,14 @@ "dark": "images/dark/go-to-file.svg" } }, + { + "command": "tekton.open.task", + "title": "Open Task Definition", + "icon": { + "light": "images/light/go-to-task.svg", + "dark": "images/dark/go-to-task.svg" + } + }, { "command": "tekton.pipeline.preview", "title": "Open Pipeline preview to the Side", @@ -728,10 +736,20 @@ "when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == taskrun", "group": "c2" }, + { + "command": "tekton.open.task", + "when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == taskrun", + "group": "inline@1" + }, { "command": "tekton.edit", "when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem =~ /^(pipeline|pipelinerun|clustertask|pipelineresource|taskrun|triggertemplates|triggerbinding|eventlistener|conditions|task|clustertriggerbinding)$/", - "group": "inline" + "group": "inline@2" + }, + { + "command": "tekton.open.task", + "when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == taskrun", + "group": "c0@2" }, { "command": "tekton.openInEditor", diff --git a/src/extension.ts b/src/extension.ts index b63583e0..fbc0006a 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -88,6 +88,7 @@ export async function activate(context: vscode.ExtensionContext): Promise vscode.commands.registerCommand('tekton.custom.explorer.removeItem', removeItemFromCustomTree), vscode.commands.registerCommand('k8s.tekton.run.logs', k8sCommands.showLogs), vscode.commands.registerCommand('k8s.tekton.run.followLogs', k8sCommands.followLogs), + vscode.commands.registerCommand('tekton.open.task', (context)=> execute(TaskRun.openDefinition, context)), pipelineExplorer, // Temporarily loaded resource providers vscode.workspace.registerFileSystemProvider(TKN_RESOURCE_SCHEME, tektonVfsProvider, { isCaseSensitive: true, }), diff --git a/src/tekton/taskrun.ts b/src/tekton/taskrun.ts index 33a45eb7..bd95b665 100644 --- a/src/tekton/taskrun.ts +++ b/src/tekton/taskrun.ts @@ -4,15 +4,15 @@ *-----------------------------------------------------------------------------------------------*/ import { TektonItem } from './tektonitem'; -import { TektonNode, Command } from '../tkn'; +import { TektonNode, Command, PipelineTaskRunData } from '../tkn'; import { window } from 'vscode'; import { Progress } from '../util/progress'; export class TaskRun extends TektonItem { static async listFromPipelineRun(pipelineRun: TektonNode): Promise { - if (pipelineRun) { - TaskRun.tkn.executeInTerminal(Command.listTaskRunsForPipelineRunInTerminal(pipelineRun.getName())); + if (pipelineRun) { + TaskRun.tkn.executeInTerminal(Command.listTaskRunsForPipelineRunInTerminal(pipelineRun.getName())); } } @@ -40,4 +40,35 @@ export class TaskRun extends TektonItem { } return null; } + + static async openDefinition(taskRun: TektonNode): Promise { + if (!taskRun) { + return; + } + const taskName = await TaskRun.getTaskNameByTaskRun(taskRun.getName()); + if (taskName) { + TektonItem.loadTektonResource(taskName[0], taskName[1]); + } + } + + private static async getTaskNameByTaskRun(taskRunName: string): Promise<[string, string] | undefined> { + const result = await TaskRun.tkn.execute(Command.getTaskRun(taskRunName), undefined, false); + if (result.error) { + window.showErrorMessage(`TaskRun may not have started yet, try again when it starts running. "${result.error}"`) + return; + } + let data: PipelineTaskRunData; + try { + data = JSON.parse(result.stdout); + // eslint-disable-next-line no-empty + } catch (ignore) { + } + + if (data.metadata.labels['tekton.dev/conditionCheck']) { + window.showErrorMessage(`Cannot find Condition definition for: ${taskRunName}. Please look in Pipeline definition`); + return; + } + + return [data.spec.taskRef?.kind ?? 'task', data.metadata.labels['tekton.dev/task']]; + } } diff --git a/src/tkn.ts b/src/tkn.ts index aad78bc4..e2b13c1c 100644 --- a/src/tkn.ts +++ b/src/tkn.ts @@ -133,6 +133,10 @@ export class Command { return newK8sCommand('get', 'taskrun', '-l', `tekton.dev/task=${task}`, '-o', 'json'); } + static getTaskRun(taskRunName: string): CliCommand { + return newK8sCommand('get', 'taskrun', taskRunName, '-o', 'json'); + } + @verbose static listTaskRunsForTasksInTerminal(task: string): CliCommand { return newTknCommand('taskrun', 'list', task); @@ -364,7 +368,7 @@ export class Command { return newK8sCommand('create', '--save-config', '-f', file); } static apply(file: string): CliCommand { - return newK8sCommand('apply','-f', file); + return newK8sCommand('apply', '-f', file); } } @@ -553,13 +557,15 @@ export class TektonNodeImpl implements TektonNode { } -type PipelineTaskRunData = { +export type PipelineTaskRunData = { metadata?: { creationTimestamp: string; name: string; labels: { 'tekton.dev/pipelineTask': string; 'tekton.dev/pipelineRun': string; + 'tekton.dev/task': string; + 'tekton.dev/conditionCheck'?: string; }; }; status?: { @@ -571,6 +577,7 @@ type PipelineTaskRunData = { spec: { taskRef: { name: string; + kind: string; }; }; }; diff --git a/test/tekton/taskrun.test.ts b/test/tekton/taskrun.test.ts index e055910c..87133a7f 100644 --- a/test/tekton/taskrun.test.ts +++ b/test/tekton/taskrun.test.ts @@ -14,6 +14,7 @@ import { TaskRun } from '../../src/tekton/taskrun'; import { TknImpl, Command, ContextType } from '../../src/tkn'; import { TektonItem } from '../../src/tekton/tektonitem'; import { pipelineExplorer } from '../../src/pipeline/pipelineExplorer'; +import { CliExitData } from '../../src/cli'; const expect = chai.expect; chai.use(sinonChai); @@ -179,4 +180,46 @@ suite('Tekton/TaskRun', () => { }); }); }); + + suite('Open Task Definition', () => { + + let loadTektonResourceStub: sinon.SinonStub; + let showErrorStub: sinon.SinonStub; + setup(() => { + loadTektonResourceStub = sandbox.stub(TektonItem, 'loadTektonResource'); + showErrorStub = sandbox.stub(vscode.window, 'showErrorMessage'); + }); + + test('openDefinition should ask taskrun definition', async () => { + execStub.resolves({ stdout: '{"metadata":{"labels": {"tekton.dev/task": "FooTaskName"}}, "spec":{"taskRef":{"kind": "FooKind"}}}' } as CliExitData); + loadTektonResourceStub.returns(undefined); + + await TaskRun.openDefinition(taskrunItem); + + expect(execStub).calledOnceWith(Command.getTaskRun(taskrunItem.getName())); + expect(loadTektonResourceStub).calledOnceWith('FooKind', 'FooTaskName'); + }); + + test('openDefinition should check errors on fetching taskrun definition', async () => { + execStub.resolves({ error: 'Foo error' } as CliExitData); + loadTektonResourceStub.returns(undefined); + + await TaskRun.openDefinition(taskrunItem); + + expect(execStub).calledOnceWith(Command.getTaskRun(taskrunItem.getName())); + expect(loadTektonResourceStub).not.called; + expect(showErrorStub).calledOnceWith('TaskRun may not have started yet, try again when it starts running. "Foo error"'); + }); + + test('openDefinition should show error if trying to open condition taskrun', async () => { + execStub.resolves({ stdout: '{"metadata":{"labels": {"tekton.dev/task": "FooTaskName","tekton.dev/conditionCheck": "bar-condition-run"}}, "spec":{"taskRef":{"kind": "FooKind"}}}' } as CliExitData); + loadTektonResourceStub.returns(undefined); + + await TaskRun.openDefinition(taskrunItem); + + expect(execStub).calledOnceWith(Command.getTaskRun(taskrunItem.getName())); + expect(loadTektonResourceStub).not.called; + expect(showErrorStub).calledOnceWith('Cannot find Condition definition for: taskrun. Please look in Pipeline definition'); + }); + }); });