From 8a79bad483bf250ed183ea280e10b6a035339cfb Mon Sep 17 00:00:00 2001 From: Tatsinnit Date: Wed, 30 Nov 2022 09:35:45 +0530 Subject: [PATCH] Add aks category connectivity detector (#177) --- .gitignore | 1 + package.json | 11 ++- .../aksCategoryConnectivity.ts | 75 +++++++++++++++++++ src/commands/utils/webviews.ts | 11 ++- src/extension.ts | 2 + 5 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 src/commands/aksCategoryConnectivity/aksCategoryConnectivity.ts diff --git a/.gitignore b/.gitignore index 1a2aed5f..4282bfd5 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ node_modules vsix .vscode-test/ *.vsix +.DS_Store \ No newline at end of file diff --git a/package.json b/package.json index bc750eac..ebcc24c0 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,8 @@ "onCommand:aks.aksKubectlGetAPIResourcesCommands", "onCommand:aks.aksKubectlGetNodeCommands", "onCommand:aks.aksKubectlDescribeServicesCommands", - "onCommand:aks.aksKubectlGetEventsCommands" + "onCommand:aks.aksKubectlGetEventsCommands", + "onCommand:aks.aksCategoryConnectivity" ], "main": "./dist/extension", "contributes": { @@ -108,6 +109,10 @@ "command": "aks.aksCRUDDiagnostics", "title": "Create, Upgrade, Delete and Scale" }, + { + "command": "aks.aksCategoryConnectivity", + "title": "Network Connectivity Issues" + }, { "command": "aks.aksBestPracticesDiagnostics", "title": "Best Practices" @@ -245,6 +250,10 @@ { "command": "aks.aksKnownIssuesAvailabilityPerformanceDiagnostics", "group": "navigation" + }, + { + "command": "aks.aksCategoryConnectivity", + "group": "navigation" } ], "aks.ghWorkflowSubMenu": [ diff --git a/src/commands/aksCategoryConnectivity/aksCategoryConnectivity.ts b/src/commands/aksCategoryConnectivity/aksCategoryConnectivity.ts new file mode 100644 index 00000000..3f29e4ee --- /dev/null +++ b/src/commands/aksCategoryConnectivity/aksCategoryConnectivity.ts @@ -0,0 +1,75 @@ +import * as vscode from 'vscode'; +import * as k8s from 'vscode-kubernetes-tools-api'; +import { IActionContext } from "@microsoft/vscode-azext-utils"; +import { getAksClusterTreeItem } from '../utils/clusters'; +import { getExtensionPath, longRunning } from '../utils/host'; +import { AppLensARMResponse, getDetectorInfo, getDetectorListData, getPortalUrl } from '../utils/detectors'; +import { failed } from '../utils/errorable'; +import AksClusterTreeItem from '../../tree/aksClusterTreeItem'; +import { createWebView, getRenderedContent, getResourceUri } from '../utils/webviews'; + +export default async function aksCategoryConnectivity( + _context: IActionContext, + target: any +): Promise { + const cloudExplorer = await k8s.extension.cloudExplorer.v1; + + const cluster = getAksClusterTreeItem(target, cloudExplorer); + if (failed(cluster)) { + vscode.window.showErrorMessage(cluster.error); + return; + } + + const extensionPath = getExtensionPath(); + if (failed(extensionPath)) { + vscode.window.showErrorMessage(extensionPath.error); + return; + } + + await loadDetector(cluster.result, extensionPath.result); +} + +async function loadDetector( + cloudTarget: AksClusterTreeItem, + extensionPath: string) { + + const clustername = cloudTarget.name; + await longRunning(`Loading ${clustername} diagnostics.`, + async () => { + const detectorInfo = await getDetectorInfo(cloudTarget, "aks-category-connectivity"); + if (failed(detectorInfo)) { + vscode.window.showErrorMessage(detectorInfo.error); + return; + } + + const detectorMap = await getDetectorListData(cloudTarget, detectorInfo.result); + if (failed(detectorMap)) { + vscode.window.showErrorMessage(detectorMap.error); + return; + } + + const webview = createWebView('AKS Diagnostics', `AKS diagnostics view for: ${clustername}`).webview; + webview.html = getWebviewContent(detectorInfo.result, detectorMap.result, extensionPath, webview); + } + ); +} + +function getWebviewContent( + clusterdata: AppLensARMResponse, + detectorMap: Map, + vscodeExtensionPath: string, + webview: vscode.Webview + ): string { + const webviewClusterData = clusterdata?.properties; + const styleUri = getResourceUri(webview, vscodeExtensionPath, 'common', 'detector.css'); + const templateUri = getResourceUri(webview, vscodeExtensionPath, 'common', 'detector.html'); + const data = { + cssuri: styleUri, + name: webviewClusterData.metadata.name, + description: webviewClusterData.metadata.description, + portalUrl: getPortalUrl(clusterdata), + detectorData: detectorMap + }; + + return getRenderedContent(templateUri, data); +} diff --git a/src/commands/utils/webviews.ts b/src/commands/utils/webviews.ts index 01272edc..3cefdf23 100644 --- a/src/commands/utils/webviews.ts +++ b/src/commands/utils/webviews.ts @@ -29,7 +29,16 @@ htmlhandlers.registerHelper('markdownHelper', (htmltext: string) => { htmlhandlers.registerHelper('eachProperty', (context, options) => { let ret = ""; context.forEach((element: any) => { - ret = ret + options.fn({ property: element.properties.dataset[0].table.rows, value: element.properties.metadata.name }); + // Rather than using the first dataset, we use the first dataset that has 'type 7' in its rendering properties. + // This appears to correspond to the 'summary' dataset, which we previously thought was always the first one, + // but isn't always (as in snat-usage). + const summaryDatasets = element.properties.dataset.filter((d: any) => d.renderingProperties.type === 7); + if (summaryDatasets.length === 0) { + vscode.window.showErrorMessage(`No data set of type 7 for detector entity ${element.properties.metadata.name}`); + return; + } + + ret = ret + options.fn({ property: summaryDatasets[0].table.rows, value: element.properties.metadata.name }); }); return ret; }); diff --git a/src/extension.ts b/src/extension.ts index 118108ad..fbdab463 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -22,6 +22,7 @@ import aksClusterProperties from './commands/aksClusterProperties/aksClusterProp import aksCreateClusterNavToAzurePortal from './commands/aksCreateClusterNavToAzurePortal/aksCreateClusterNavToAzurePortal'; import { registerAzureUtilsExtensionVariables } from '@microsoft/vscode-azext-azureutils'; import { aksKubectlGetPodsCommands, aksKubectlGetClusterInfoCommands, aksKubectlGetAPIResourcesCommands, aksKubectlGetNodeCommands, aksKubectlDescribeServicesCommands, aksKubectlGetEventsCommands } from './commands/aksKubectlCommands/aksKubectlCommands'; +import aksCategoryConnectivity from './commands/aksCategoryConnectivity/aksCategoryConnectivity'; export async function activate(context: vscode.ExtensionContext) { const cloudExplorer = await k8s.extension.cloudExplorer.v1; @@ -62,6 +63,7 @@ export async function activate(context: vscode.ExtensionContext) { registerCommandWithTelemetry('aks.aksKubectlGetNodeCommands', aksKubectlGetNodeCommands); registerCommandWithTelemetry('aks.aksKubectlDescribeServicesCommands', aksKubectlDescribeServicesCommands); registerCommandWithTelemetry('aks.aksKubectlGetEventsCommands', aksKubectlGetEventsCommands); + registerCommandWithTelemetry('aks.aksCategoryConnectivity', aksCategoryConnectivity); await registerAzureServiceNodes(context);