Skip to content

Commit

Permalink
Watch tekton resources at the activation of extension and send data t…
Browse files Browse the repository at this point in the history
…o telemetry if new resource are created. (#569)

* Watch tekton resources at the activation of extension and send data to telemetry if new resource are created.
  • Loading branch information
sudhirverma authored May 11, 2021
1 parent 7128182 commit 1c4a727
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 63 deletions.
3 changes: 3 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -122,6 +123,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
detectTknCli().then(() => {
triggerDetection();
});
checkClusterStatus(true); // watch Tekton resources when all required dependency are installed
setCommandContext(CommandContext.TreeZenMode, false);
setCommandContext(CommandContext.PipelinePreview, false);

Expand Down Expand Up @@ -149,6 +151,7 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
if (configurationApi.available) {
const confApi = configurationApi.api;
confApi.onDidChangeContext(() => {
checkClusterStatus(true);
pipelineExplorer.refresh();
});
}
Expand Down
65 changes: 5 additions & 60 deletions src/tkn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -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<TektonNode[]> {
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();
}

Expand Down
83 changes: 83 additions & 0 deletions src/util/check-cluster-status.ts
Original file line number Diff line number Diff line change
@@ -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<TektonNode[]> {
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;
}
29 changes: 29 additions & 0 deletions src/util/telemetry-watch-tekton-resource.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
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);
}
}

10 changes: 7 additions & 3 deletions src/util/watchResources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, boolean>();
Expand Down Expand Up @@ -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);
}
}
}
Expand Down

0 comments on commit 1c4a727

Please sign in to comment.