From eb92a47634d103c8d24310b03988d8939e430178 Mon Sep 17 00:00:00 2001 From: Sudhir Verma Date: Fri, 7 May 2021 00:39:38 +0530 Subject: [PATCH 1/3] Watch tekton resources at the activation of extension and send data to telemetry if new resource are created. --- src/extension.ts | 5 +- src/tkn.ts | 65 ++-------------- src/util/check-cluster-status.ts | 83 +++++++++++++++++++++ src/util/telemetry-watch-tekton-resource.ts | 29 +++++++ 4 files changed, 121 insertions(+), 61 deletions(-) create mode 100644 src/util/check-cluster-status.ts create mode 100644 src/util/telemetry-watch-tekton-resource.ts diff --git a/src/extension.ts b/src/extension.ts index c82a0642..9319713d 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -37,6 +37,7 @@ import sendTelemetry, { telemetryLogError, TelemetryProperties } from './telemet import { cli, createCliCommand } from './cli'; import { getVersion, tektonVersionType } from './util/tknversion'; import { TektonNode } from './tree-view/tekton-node'; +import { checkClusterStatus } from './util/check-cluster-status'; export let contextGlobalState: vscode.ExtensionContext; let k8sExplorer: k8s.ClusterExplorerV1 | undefined = undefined; @@ -122,6 +123,7 @@ export async function activate(context: vscode.ExtensionContext): Promise detectTknCli().then(() => { triggerDetection(); }); + checkClusterStatus(true); // watch Tekton resources when all required dependency are installed setCommandContext(CommandContext.TreeZenMode, false); setCommandContext(CommandContext.PipelinePreview, false); @@ -148,7 +150,8 @@ export async function activate(context: vscode.ExtensionContext): Promise const configurationApi = await k8s.extension.configuration.v1_1; if (configurationApi.available) { const confApi = configurationApi.api; - confApi.onDidChangeContext(() => { + confApi.onDidChangeContext(async () => { + checkClusterStatus(true); pipelineExplorer.refresh(); }); } diff --git a/src/tkn.ts b/src/tkn.ts index 8b1d50cb..3efeea5e 100644 --- a/src/tkn.ts +++ b/src/tkn.ts @@ -4,7 +4,7 @@ *-----------------------------------------------------------------------------------------------*/ import { CliCommand, CliExitData, cli, cliCommandToString, WatchProcess } from './cli'; -import { TreeItemCollapsibleState, Terminal, workspace, commands } from 'vscode'; +import { TreeItemCollapsibleState, Terminal, workspace } from 'vscode'; import { WindowUtil } from './util/windowUtils'; import * as path from 'path'; import { ToolsConfig } from './tools'; @@ -13,8 +13,6 @@ import { kubectl } from './kubectl'; import { pipelineExplorer } from './pipeline/pipelineExplorer'; import { StartObject } from './tekton/pipelinecontent'; import { RunState } from './yaml-support/tkn-yaml'; -import { version } from './util/tknversion'; -import { pipelineTriggerStatus, watchResources } from './util/watchResources'; import { getStderrString } from './util/stderrstring'; import { ContextType } from './context-type'; import { TektonNode, TektonNodeImpl } from './tree-view/tekton-node'; @@ -24,9 +22,10 @@ import { MoreNode } from './tree-view/expand-node'; import { Command } from './cli-command'; import { getPipelineList } from './util/list-tekton-resource'; import { telemetryLog, telemetryLogError } from './telemetry'; +import { checkClusterStatus } from './util/check-cluster-status'; -let tektonResourceCount = {}; +const tektonResourceCount = {}; interface NameId { name: string; @@ -112,62 +111,8 @@ export class TknImpl implements Tkn { defaultPageSize: number = workspace.getConfiguration('vs-tekton').has('treePaginationLimit') ? workspace.getConfiguration('vs-tekton').get('treePaginationLimit') : 5; async getPipelineNodes(): Promise { - const result: CliExitData = await this.execute( - Command.checkTekton(), process.cwd(), false - ); - const kubectlCheck = RegExp('kubectl:\\s*command not found'); - if (kubectlCheck.test(getStderrString(result.error))) { - const tknMessage = 'Please install kubectl.'; - telemetryLog('kubeclt_not_found', tknMessage); - watchResources.disableWatch(); - return [new TektonNodeImpl(null, tknMessage, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)] - } - if (result.stdout.trim() === 'no') { - const tknPrivilegeMsg = 'The current user doesn\'t have the privileges to interact with tekton resources.'; - telemetryLog('tekton_resource_privileges', tknPrivilegeMsg); - watchResources.disableWatch(); - return [new TektonNodeImpl(null, tknPrivilegeMsg, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)]; - } - if (result.error && getStderrString(result.error).indexOf('You must be logged in to the server (Unauthorized)') > -1) { - const tknMessage = 'Please login to the server.'; - telemetryLog('login', tknMessage); - watchResources.disableWatch(); - return [new TektonNodeImpl(null, tknMessage, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)] - } - const serverCheck = RegExp('Unable to connect to the server'); - if (serverCheck.test(getStderrString(result.error))) { - const loginError = 'Unable to connect to OpenShift cluster, is it down?'; - telemetryLogError('problem_connect_cluster', loginError); - watchResources.disableWatch(); - commands.executeCommand('setContext', 'tekton.pipeline', false); - commands.executeCommand('setContext', 'tekton.cluster', true); - return []; - } - if (result.error && getStderrString(result.error).indexOf('the server doesn\'t have a resource type \'pipeline\'') > -1) { - const message = 'Please install the Pipelines Operator.'; - telemetryLog('install_pipeline_operator', message); - watchResources.disableWatch(); - commands.executeCommand('setContext', 'tekton.cluster', false); - commands.executeCommand('setContext', 'tekton.pipeline', true); - return []; - } - - const tknVersion = await version(); - const resourceUidAtStart = {}; - if (tknVersion && (tknVersion?.trigger === undefined || tknVersion.trigger.trim() === 'unknown')) { - pipelineTriggerStatus.set('trigger', false); - } - if (!pipelineTriggerStatus.get('pipeline')) { - tektonResourceCount = {}; - const resourceList = ['pipeline', 'pipelinerun', 'taskrun', 'task', 'clustertask', 'pipelineresources', 'condition']; - watchResources.watchCommand(resourceList, resourceUidAtStart); - pipelineTriggerStatus.set('pipeline', true); - } - if (!pipelineTriggerStatus.get('trigger') && tknVersion && tknVersion?.trigger !== undefined && tknVersion.trigger.trim() !== 'unknown') { - const resourceList = ['tt', 'tb', 'el', 'ctb']; - watchResources.watchCommand(resourceList, resourceUidAtStart); - pipelineTriggerStatus.set('trigger', true); - } + const clusterInfo = await checkClusterStatus(); + if (clusterInfo !== null) return clusterInfo; return this._getPipelineNodes(); } diff --git a/src/util/check-cluster-status.ts b/src/util/check-cluster-status.ts new file mode 100644 index 00000000..e727b616 --- /dev/null +++ b/src/util/check-cluster-status.ts @@ -0,0 +1,83 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ + +import { commands, TreeItemCollapsibleState } from 'vscode'; +import { CliExitData } from '../cli'; +import { Command } from '../cli-command'; +import { ContextType } from '../context-type'; +import { telemetryLog, telemetryLogError } from '../telemetry'; +import { tkn } from '../tkn'; +import { TektonNode, TektonNodeImpl } from '../tree-view/tekton-node'; +import { getStderrString } from './stderrstring'; +import { watchTektonResources } from './telemetry-watch-tekton-resource'; +import { watchResources } from './watchResources'; + +export async function checkClusterStatus(extensionStartUpCheck?: boolean): Promise { + const result: CliExitData = await tkn.execute( + Command.checkTekton(), process.cwd(), false + ); + const kubectlCheck = RegExp('kubectl:\\s*command not found'); + if (kubectlCheck.test(getStderrString(result.error))) { + const tknMessage = 'Please install kubectl.'; + watchResources.disableWatch(); + if (extensionStartUpCheck) { + telemetryLog('kubeclt_not_found', tknMessage); + return; + } + telemetryLog('kubeclt_not_found', tknMessage); + return [new TektonNodeImpl(null, tknMessage, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)] + } + if (result.stdout.trim() === 'no') { + const tknPrivilegeMsg = 'The current user doesn\'t have the privileges to interact with tekton resources.'; + const identifier = 'tekton_resource_privileges'; + watchResources.disableWatch(); + if (extensionStartUpCheck) { + telemetryLog(`${identifier}_startUp`, tknPrivilegeMsg); + return; + } + telemetryLog(identifier, tknPrivilegeMsg); + return [new TektonNodeImpl(null, tknPrivilegeMsg, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)]; + } + if (result.error && getStderrString(result.error).indexOf('You must be logged in to the server (Unauthorized)') > -1) { + const tknMessage = 'Please login to the server.'; + const identifier = 'tekton_login'; + watchResources.disableWatch(); + if (extensionStartUpCheck) { + telemetryLog(`${identifier}_startUp`, tknMessage); + return; + } + telemetryLog(identifier, tknMessage); + return [new TektonNodeImpl(null, tknMessage, ContextType.TKN_DOWN, this, TreeItemCollapsibleState.None)] + } + const serverCheck = RegExp('Unable to connect to the server'); + if (serverCheck.test(getStderrString(result.error))) { + const loginError = 'Unable to connect to OpenShift cluster, is it down?'; + const identifier = 'problem_connect_cluster'; + watchResources.disableWatch(); + if (extensionStartUpCheck) { + telemetryLogError(`${identifier}_startUp`, loginError); + return; + } + telemetryLogError(identifier, loginError); + commands.executeCommand('setContext', 'tekton.pipeline', false); + commands.executeCommand('setContext', 'tekton.cluster', true); + return []; + } + if (result.error && getStderrString(result.error).indexOf('the server doesn\'t have a resource type \'pipeline\'') > -1) { + const message = 'Please install the Pipelines Operator.'; + const identifier = 'install_pipeline_operator'; + watchResources.disableWatch(); + if (extensionStartUpCheck) { + telemetryLog(`${identifier}_startUp`, message); + return; + } + telemetryLog(identifier, message); + commands.executeCommand('setContext', 'tekton.cluster', false); + commands.executeCommand('setContext', 'tekton.pipeline', true); + return []; + } + await watchTektonResources(extensionStartUpCheck); + return null; +} diff --git a/src/util/telemetry-watch-tekton-resource.ts b/src/util/telemetry-watch-tekton-resource.ts new file mode 100644 index 00000000..177c9ee3 --- /dev/null +++ b/src/util/telemetry-watch-tekton-resource.ts @@ -0,0 +1,29 @@ +/*----------------------------------------------------------------------------------------------- + * Copyright (c) Red Hat, Inc. All rights reserved. + * Licensed under the MIT License. See LICENSE file in the project root for license information. + *-----------------------------------------------------------------------------------------------*/ + +import { telemetryLog } from '../telemetry'; +import { version } from './tknversion'; +import { pipelineTriggerStatus, watchResources } from './watchResources'; + +export async function watchTektonResources(extensionStartUpCheck?: boolean): Promise { + const tknVersion = await version(); + const resourceUidAtStart = {}; + if (tknVersion && (tknVersion?.trigger === undefined || tknVersion.trigger.trim() === 'unknown')) { + pipelineTriggerStatus.set('trigger', false); + } + if (!pipelineTriggerStatus.get('pipeline')) { + const resourceList = ['pipeline', 'pipelinerun', 'taskrun', 'task', 'clustertask', 'pipelineresources', 'condition']; + if (extensionStartUpCheck) telemetryLog('startUp_watch_tekton_Pipeline_resource', 'startUp Pipeline watch'); + watchResources.watchCommand(resourceList, resourceUidAtStart); + pipelineTriggerStatus.set('pipeline', true); + } + if (!pipelineTriggerStatus.get('trigger') && tknVersion && tknVersion?.trigger !== undefined && tknVersion.trigger.trim() !== 'unknown') { + const resourceList = ['tt', 'tb', 'el', 'ctb']; + if (extensionStartUpCheck) telemetryLog('startUp_watch_tekton_Trigger_resource', 'startUp trigger watch'); + watchResources.watchCommand(resourceList, resourceUidAtStart); + pipelineTriggerStatus.set('trigger', true); + } +} + From e3957ffbc1f45d1d896a5906ec2470f1f49d8e37 Mon Sep 17 00:00:00 2001 From: Sudhir Verma Date: Fri, 7 May 2021 00:48:18 +0530 Subject: [PATCH 2/3] fix minor changes --- src/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/extension.ts b/src/extension.ts index 9319713d..8c0a6bbb 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -150,7 +150,7 @@ export async function activate(context: vscode.ExtensionContext): Promise const configurationApi = await k8s.extension.configuration.v1_1; if (configurationApi.available) { const confApi = configurationApi.api; - confApi.onDidChangeContext(async () => { + confApi.onDidChangeContext(() => { checkClusterStatus(true); pipelineExplorer.refresh(); }); From 26e8447c95a6c95d700dab2b6c41907da7fbb046 Mon Sep 17 00:00:00 2001 From: Sudhir Verma Date: Tue, 11 May 2021 16:05:47 +0530 Subject: [PATCH 3/3] watch pipelineRun pass or fails status --- src/util/watchResources.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/util/watchResources.ts b/src/util/watchResources.ts index aa7cd0c5..dab9dca9 100644 --- a/src/util/watchResources.ts +++ b/src/util/watchResources.ts @@ -10,7 +10,7 @@ import { pipelineExplorer } from '../pipeline/pipelineExplorer'; import { FileContentChangeNotifier, WatchUtil } from './watch'; import { window, workspace } from 'vscode'; import { getResourceList } from './list-tekton-resource'; -import { telemetryLog } from '../telemetry'; +import { telemetryLog, telemetryLogError } from '../telemetry'; import { humanizer } from '../humanizer'; export const pipelineTriggerStatus = new Map(); @@ -71,9 +71,13 @@ export class WatchResources { if (checkPipelineRunNotifications()) { if (run.kind === ResourceType.pipelineRun) { if (run.status.conditions[0].status === 'True') { - window.showInformationMessage(`PipelineRun: ${run.metadata.name} is successfully completed. Duration to complete the execution 'Time: ${humanizer(Date.parse(run.status.completionTime) - Date.parse(run.status.startTime))}'`); + const successfulPipelineRun = `PipelineRun: ${run.metadata.name} is successfully completed. Duration to complete the execution 'Time: ${humanizer(Date.parse(run.status.completionTime) - Date.parse(run.status.startTime))}'`; + telemetryLog('watch.pipelineRun.complete.status', successfulPipelineRun); + window.showInformationMessage(successfulPipelineRun); } else if (run.status.conditions[0].status === 'False') { - window.showErrorMessage(`PipelineRun: ${run.metadata.name} fails. Reason: ${run.status.conditions?.[0].reason} and Message: ${run.status.conditions?.[0].message}`); + const failPipelineRun = `PipelineRun: ${run.metadata.name} fails. Reason: ${run.status.conditions?.[0].reason} and Message: ${run.status.conditions?.[0].message}`; + telemetryLogError('watch.pipelineRun.error', failPipelineRun) + window.showErrorMessage(failPipelineRun); } } }