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

Watch tekton resources at the activation of extension and send data to telemetry if new resource are created. #569

Merged
merged 3 commits into from
May 11, 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
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