Skip to content

Commit

Permalink
Provide a way to get expose url from tree view (#447)
Browse files Browse the repository at this point in the history
* Provide a way to get expose URL from the tree view
  • Loading branch information
sudhirverma committed Nov 12, 2020
1 parent 0503ee2 commit 05b6213
Show file tree
Hide file tree
Showing 7 changed files with 265 additions and 3 deletions.
15 changes: 15 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,12 @@
"category": "Tekton",
"enablement": "tekton:tkn"
},
{
"command": "tekton.triggerTemplate.url",
"title": "Copy Expose URL",
"category": "Tekton",
"enablement": "tekton:tkn"
},
{
"command": "tekton.pipeline.list",
"title": "List Pipelines",
Expand Down Expand Up @@ -535,6 +541,10 @@
"command": "k8s.tekton.pipeline.start",
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/"
},
{
"command": "tekton.triggerTemplate.url",
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/"
},
{
"command": "tekton.openInEditor",
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/"
Expand Down Expand Up @@ -664,6 +674,11 @@
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == pipeline",
"group": "c1@2"
},
{
"command": "tekton.triggerTemplate.url",
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == triggertemplates",
"group": "c1@2"
},
{
"command": "tekton.addTrigger",
"when": "view =~ /^tekton(CustomTree|PipelineExplorer)View/ && viewItem == pipeline",
Expand Down
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { deleteFromExplorer, deleteFromCustom } from './commands/delete';
import { addTrigger } from './tekton/trigger';
import { triggerDetection } from './util/detection';
import { showDiagnosticData } from './tekton/diagnostic';
import { TriggerTemplate } from './tekton/triggertemplate';

export let contextGlobalState: vscode.ExtensionContext;
let k8sExplorer: k8s.ClusterExplorerV1 | undefined = undefined;
Expand Down Expand Up @@ -73,6 +74,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
vscode.commands.registerCommand('tekton.pipelinerun.followLogs.palette', (context) => execute(PipelineRun.followLogs, context)),
vscode.commands.registerCommand('tekton.pipelinerun.cancel', (context) => execute(PipelineRun.cancel, context)),
vscode.commands.registerCommand('tekton.pipelinerun.cancel.palette', (context) => execute(PipelineRun.cancel, context)),
vscode.commands.registerCommand('tekton.triggerTemplate.url', (context) => execute(TriggerTemplate.copyExposeUrl, context)),
vscode.commands.registerCommand('tekton.task.start', (context) => execute(Task.start, context)),
vscode.commands.registerCommand('tekton.task.start.palette', (context) => execute(Task.start, context)),
vscode.commands.registerCommand('tekton.task.list', (context) => execute(Task.list, context)),
Expand Down
6 changes: 5 additions & 1 deletion src/tekton/addtrigger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { Progress } from '../util/progress';
import { cli } from '../cli';
import { TknVersion, version } from '../util/tknversion';
import { NewPvc } from './createpvc';
import { getExposeURl } from '../util/exposeurl';

export const TriggerTemplateModel = {
apiGroup: 'triggers.tekton.dev',
Expand Down Expand Up @@ -100,7 +101,10 @@ export async function k8sCreate(trigger: TriggerTemplateKind | EventListenerKind
vscode.window.showErrorMessage(`Fail to deploy Resources: ${getStderrString(result.error)}`);
return false;
}
if (trigger.kind === RouteModel.kind && !result.error) vscode.window.showInformationMessage('Trigger successfully created.');
if (trigger.kind === RouteModel.kind && !result.error) {
const url = await getExposeURl(trigger.metadata.name);
vscode.window.showInformationMessage(`Trigger successfully created. Expose URL: ${url}`);
}
await fs.unlink(fsPath);
return true;
}
Expand Down
35 changes: 33 additions & 2 deletions src/tekton/triggertemplate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,45 @@
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/


import * as vscode from 'vscode';
import { TektonItem } from './tektonitem';
import { TektonNode, Command } from '../tkn';
import { TektonNode, Command, tkn } from '../tkn';
import { CliCommand } from '../cli';
import { getExposeURl } from '../util/exposeurl';

export class TriggerTemplate extends TektonItem {

static getDeleteCommand(item: TektonNode): CliCommand {
return Command.deleteTriggerTemplate(item.getName());
}

static async copyExposeUrl(trigger: TektonNode): Promise<string> {
if (!trigger) return null;
const result = await tkn.execute(Command.listEventListener());
const listEventListener = JSON.parse(result.stdout).items;
if (listEventListener.length === 0) {
vscode.window.showInformationMessage('Expose URl not available');
return null;
}
for (const eventListener of listEventListener) {
for (const triggers of eventListener.spec.triggers) {
if (triggers?.template?.name === trigger.getName()) {
const url = await getExposeURl(eventListener.status.configuration.generatedName);
vscode.env.clipboard.writeText(url);
vscode.window.showInformationMessage('Expose URl successfully copied');
return;
} else if (triggers?.triggerRef) {
const triggerData = await tkn.execute(Command.getTrigger(triggers.triggerRef));
const triggerName = JSON.parse(triggerData.stdout).spec.template.name;
if (triggerName === trigger.getName()) {
const url = await getExposeURl(eventListener.status.configuration.generatedName);
vscode.env.clipboard.writeText(url);
vscode.window.showInformationMessage('Expose URl successfully copied');
return;
}
}
}
}
vscode.window.showInformationMessage('Expose URl not available');
}
}
8 changes: 8 additions & 0 deletions src/tkn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,14 @@ export class Command {
return newK8sCommand('apply', '-f', file);
}

static getRoute(name: string): CliCommand {
return newK8sCommand('get', 'route', name, '-o', 'json');
}

static getTrigger(name: string): CliCommand {
return newK8sCommand('get', 'trigger', name, '-o', 'json');
}

}

const IMAGES = '../../images';
Expand Down
20 changes: 20 additions & 0 deletions src/util/exposeurl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*-----------------------------------------------------------------------------------------------
* 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 _ from 'lodash';
import { Command, tkn } from '../tkn';



export async function getExposeURl(name: string): Promise<string> {
const result = await tkn.execute(Command.getRoute(name));
const route = JSON.parse(result.stdout);
const scheme = _.get(route, 'spec.tls.termination') ? 'https' : 'http';
let url = `${scheme}://${route.spec.host}`;
if (route.spec?.path) {
url += route.spec.path;
}
return url;
}
182 changes: 182 additions & 0 deletions test/tekton/triggertemplate.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
/*-----------------------------------------------------------------------------------------------
* Copyright (c) Red Hat, Inc. All rights reserved.
* Licensed under the MIT License. See LICENSE file in the project root for license information.
*-----------------------------------------------------------------------------------------------*/


'use strict';

import * as chai from 'chai';
import * as vscode from 'vscode';
import * as sinon from 'sinon';
import * as sinonChai from 'sinon-chai';
import { EventListenerKind } from '../../src/tekton';
import { TriggerTemplate } from '../../src/tekton/triggertemplate';
import { ContextType, TknImpl } from '../../src/tkn';
import { TestItem } from './testTektonitem';

const expect = chai.expect;
chai.use(sinonChai);

suite('Tekton/Pipeline', () => {
const sandbox = sinon.createSandbox();
let exeStub: sinon.SinonStub;
const pipelineNode = new TestItem(TknImpl.ROOT, 'test-pipeline', ContextType.PIPELINENODE, null);
const triggerTemplateItem = new TestItem(pipelineNode, 'trigger-template-sample-pipeline-cluster-task-4-awhmgc', ContextType.TRIGGERTEMPLATES, null);

setup(() => {
exeStub = sandbox.stub(TknImpl.prototype, 'execute').resolves({
error: '',
stdout: ''
});
});

teardown(() => {
sandbox.restore();
});

const eventListener: EventListenerKind[] = [{
apiVersion:'triggers.tekton.dev/v1alpha1',
kind:'EventListener',
metadata: {
name:'event-listener-jwwe6j',
namespace:'pipelines-tutorial'
},
spec: {
serviceAccountName:'pipeline',
triggers: [{
bindings: [{
kind:'TriggerBinding',
name:'vote-app'
}],
template: {
name:'trigger-template-sample-pipeline-cluster-task-4-awhmgc'
}
}]
},
status: {
configuration: {
generatedName:'el-event-listener-jwwe6j'
}
}
}];

const eventListenerTriggerRef: EventListenerKind[] = [{
apiVersion:'triggers.tekton.dev/v1alpha1',
kind:'EventListener',
metadata: {
name:'event-listener-jwwe6j',
namespace:'pipelines-tutorial'
},
spec: {
serviceAccountName:'pipeline',
triggers: [{
triggerRef: 'test'
}]
},
status: {
configuration: {
generatedName:'el-event-listener-jwwe6j'
}
}
}];

const triggerData = {
spec: {
template: {
name: 'trigger-template-sample-pipeline-cluster-task-4-awhmgc'
}
}
}

const route = {
spec: {
host: 'test.openshift.com'
}
}

suite('Add trigger', () => {
test('copy expose URL', async () => {
exeStub.onFirstCall().resolves({
error: '',
stdout: JSON.stringify({items: eventListener})
});
exeStub.onSecondCall().resolves({
error: '',
stdout: JSON.stringify(route)
});
const writeTextStub = sandbox.stub(vscode.env.clipboard, 'writeText').resolves('http://test.openshift.com');
const infoMsg = sandbox.stub(vscode.window, 'showInformationMessage').resolves('Expose URl successfully copied');
await TriggerTemplate.copyExposeUrl(triggerTemplateItem);
expect(exeStub).called;
expect(infoMsg).is.calledOnce;
expect(writeTextStub).called;
});

test('copy expose URL for triggerRef', async () => {
exeStub.onFirstCall().resolves({
error: '',
stdout: JSON.stringify({items: eventListenerTriggerRef})
});
exeStub.onSecondCall().resolves({
error: '',
stdout: JSON.stringify(triggerData)
});
exeStub.onThirdCall().resolves({
error: '',
stdout: JSON.stringify(route)
});
const writeTextStub = sandbox.stub(vscode.env.clipboard, 'writeText').resolves('http://test.openshift.com');
const infoMsg = sandbox.stub(vscode.window, 'showInformationMessage').resolves('Expose URl successfully copied');
await TriggerTemplate.copyExposeUrl(triggerTemplateItem);
expect(exeStub).called;
expect(infoMsg).is.calledOnce;
expect(writeTextStub).called;
});

test('return null if no EventListener found', async () => {
exeStub.onFirstCall().resolves({
error: '',
stdout: JSON.stringify({items: []})
});
const infoMsg = sandbox.stub(vscode.window, 'showInformationMessage').resolves('Expose URl not available');
const result = await TriggerTemplate.copyExposeUrl(triggerTemplateItem);
expect(result).equals(null);
expect(infoMsg).is.calledOnce;
});

test('expose URL not found', async () => {
exeStub.onFirstCall().resolves({
error: '',
stdout: JSON.stringify({items: [{
apiVersion:'triggers.tekton.dev/v1alpha1',
kind:'EventListener',
metadata: {
name:'event-listener-jwwe6j',
namespace:'pipelines-tutorial'
},
spec: {
serviceAccountName:'pipeline',
triggers: [{
bindings: [{
kind:'TriggerBinding',
name:'vote-app'
}],
template: {
name:'test'
}
}]
},
status: {
configuration: {
generatedName:'el-event-listener-jwwe6j'
}
}
}]})
});
const infoMsg = sandbox.stub(vscode.window, 'showInformationMessage').resolves('Expose URl not available');
await TriggerTemplate.copyExposeUrl(triggerTemplateItem);
expect(infoMsg).is.calledOnce;
});
});
});

0 comments on commit 05b6213

Please sign in to comment.