From fdb883b6108dab2cc8ef54bfcd5042ed85c3f722 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Tue, 20 Jun 2023 16:37:18 +0300 Subject: [PATCH 1/9] fix: app_start/create_project event --- .../organisms/CreateProjectModal/CreateProjectModal.tsx | 1 + src/components/organisms/NewProject/NewProject.tsx | 1 + src/shared/models/telemetry.ts | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx b/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx index 9aec91d81b..e351a7742d 100644 --- a/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx +++ b/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx @@ -49,6 +49,7 @@ const CreateProjectModal: React.FC = () => { dispatch(setCreateProject({name, rootFolder})); onCloseModalHandler(); } else { + trackEvent('app_start/create_project', {from: 'folder'}); dispatch(setTemplateProjectCreate({name, rootFolder})); dispatch(openTemplateExplorer()); onCloseModalHandler(); diff --git a/src/components/organisms/NewProject/NewProject.tsx b/src/components/organisms/NewProject/NewProject.tsx index 214af4bf41..0952f757fe 100644 --- a/src/components/organisms/NewProject/NewProject.tsx +++ b/src/components/organisms/NewProject/NewProject.tsx @@ -51,6 +51,7 @@ const NewProject: React.FC = () => { 'Not sure where to start? Explore a sample project containing everything you need to get started with just one click.', itemAction: () => { dispatch(openGitCloneModal({fromSampleProject: true})); + trackEvent('app_start/create_project', {from: 'sample'}); }, }, { diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 712aeb7506..405f9199e4 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -46,7 +46,7 @@ export type EventMap = { numberOfValuesFiles: number; executionTime: number; }; - 'app_start/create_project': {from: 'scratch' | 'git' | 'template' | 'folder'; templateID?: string}; + 'app_start/create_project': {from: 'sample' | 'scratch' | 'git' | 'template' | 'folder'; templateID?: string}; 'app_start/select_page': {page: string}; 'app_start/select_project': undefined; 'project_list/open_project': undefined; From c87a541eaae00400b31b00c46572030bc6b4d882 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Tue, 20 Jun 2023 17:21:12 +0300 Subject: [PATCH 2/9] chore: resolve comments --- .../organisms/CreateProjectModal/CreateProjectModal.tsx | 1 - src/components/organisms/NewProject/NewProject.tsx | 1 + src/redux/thunks/project/createProject.ts | 2 ++ src/shared/models/telemetry.ts | 5 ++++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx b/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx index e351a7742d..9aec91d81b 100644 --- a/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx +++ b/src/components/organisms/CreateProjectModal/CreateProjectModal.tsx @@ -49,7 +49,6 @@ const CreateProjectModal: React.FC = () => { dispatch(setCreateProject({name, rootFolder})); onCloseModalHandler(); } else { - trackEvent('app_start/create_project', {from: 'folder'}); dispatch(setTemplateProjectCreate({name, rootFolder})); dispatch(openTemplateExplorer()); onCloseModalHandler(); diff --git a/src/components/organisms/NewProject/NewProject.tsx b/src/components/organisms/NewProject/NewProject.tsx index 0952f757fe..57f8e47e34 100644 --- a/src/components/organisms/NewProject/NewProject.tsx +++ b/src/components/organisms/NewProject/NewProject.tsx @@ -106,6 +106,7 @@ const NewProject: React.FC = () => { itemDescription: 'Create a new project from a Helm Chart in a Helm repository, and save it locally.', itemAction: () => { dispatch(openHelmRepoModal()); + trackEvent('app_start/create_project', {from: 'helm'}); }, }, ]; diff --git a/src/redux/thunks/project/createProject.ts b/src/redux/thunks/project/createProject.ts index c611649153..0f470db1d6 100644 --- a/src/redux/thunks/project/createProject.ts +++ b/src/redux/thunks/project/createProject.ts @@ -4,6 +4,7 @@ import {createProject} from '@redux/appConfig'; import {isFolderGitRepo} from '@redux/git/git.ipc'; import {Project} from '@shared/models/config'; +import {trackEvent} from '@shared/utils/telemetry'; import {setOpenProject} from './openProject'; @@ -18,4 +19,5 @@ export const setCreateProject = createAsyncThunk('config/setCreateProject', asyn thunkAPI.dispatch(createProject({...project, isGitRepo})); thunkAPI.dispatch(setOpenProject(project.rootFolder)); + trackEvent('app_start/create_project', {from: 'folder'}); }); diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 405f9199e4..92835d4fe2 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -46,7 +46,10 @@ export type EventMap = { numberOfValuesFiles: number; executionTime: number; }; - 'app_start/create_project': {from: 'sample' | 'scratch' | 'git' | 'template' | 'folder'; templateID?: string}; + 'app_start/create_project': { + from: 'sample' | 'scratch' | 'git' | 'template' | 'folder' | 'helm'; + templateID?: string; + }; 'app_start/select_page': {page: string}; 'app_start/select_project': undefined; 'project_list/open_project': undefined; From 29c40fd956add54349e3d3f97e6f50b411f4f344 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 12:09:40 +0300 Subject: [PATCH 3/9] fix: preview/helm_config/fail --- package-lock.json | 6 +++--- src/redux/thunks/runPreviewConfiguration.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package-lock.json b/package-lock.json index 62fef86155..c59e3ca17f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -22278,9 +22278,9 @@ } }, "node_modules/openai": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-3.3.0.tgz", - "integrity": "sha512-uqxI/Au+aPRnsaQRe8CojU0eCR7I0mBiKjD3sNMzY6DaC1ZVrc85u98mtJW6voDug8fgGN+DIZmTDxTthxb7dQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/openai/-/openai-3.2.1.tgz", + "integrity": "sha512-762C9BNlJPbjjlWZi4WYK9iM2tAVAv0uUp1UmI34vb0CN5T2mjB/qM6RYBmNKMh/dN9fC+bxqPwWJZUTWW052A==", "dependencies": { "axios": "^0.26.0", "form-data": "^4.0.0" diff --git a/src/redux/thunks/runPreviewConfiguration.ts b/src/redux/thunks/runPreviewConfiguration.ts index 7897831870..8c20dd6d5d 100644 --- a/src/redux/thunks/runPreviewConfiguration.ts +++ b/src/redux/thunks/runPreviewConfiguration.ts @@ -130,8 +130,8 @@ export const runPreviewConfiguration = createAsyncThunk< const result = await runCommandInMainThread(commandOptions); - if (result.error) { - trackEvent('preview/helm_config/fail', {reason: result.error}); + if (result.error || result.stderr) { + trackEvent('preview/helm_config/fail', {reason: result.error || result.stderr || 'unknown'}); return createRejectionWithAlert(thunkAPI, 'Helm Error', `${result.error} - ${result.stderr}`); } From 16ccf7a6b7e051937a264d5a47d048ca18289799 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 12:13:33 +0300 Subject: [PATCH 4/9] chore: track validation triggers --- src/redux/validation/validation.listeners.tsx | 4 ++++ src/shared/models/telemetry.ts | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/redux/validation/validation.listeners.tsx b/src/redux/validation/validation.listeners.tsx index 99416eeb5f..0a82be1981 100644 --- a/src/redux/validation/validation.listeners.tsx +++ b/src/redux/validation/validation.listeners.tsx @@ -47,6 +47,7 @@ import {doesSchemaExist} from '@utils/index'; import {ResourceIdentifier, ResourceStorage} from '@shared/models/k8sResource'; import {isDefined} from '@shared/utils/filter'; import {isEqual} from '@shared/utils/isEqual'; +import {trackEvent} from '@shared/utils/telemetry'; import {changeRuleLevel, setConfigK8sSchemaVersion, toggleRule, toggleValidation} from './validation.slice'; import {loadValidation, validateResources} from './validation.thunks'; @@ -73,6 +74,7 @@ const loadListener: AppListenerFn = listen => { changeRuleLevel ), async effect(_action, {dispatch, delay, signal, cancelActiveListeners}) { + trackEvent('validation/load_config', {actionType: _action.type}); if (isAnyOf(setIsInQuickClusterMode)(_action)) { if (!_action.payload) { return; @@ -114,6 +116,7 @@ const validateListener: AppListenerFn = listen => { ), async effect(_action, {dispatch, getState, cancelActiveListeners, signal, delay}) { cancelActiveListeners(); + trackEvent('validation/validate_all', {actionType: _action.type}); if (incrementalValidationStatus.isRunning) { incrementalValidationStatus.abortController?.abort(); @@ -178,6 +181,7 @@ const incrementalValidationListener: AppListenerFn = listen => { multiplePathsChanged.fulfilled ), async effect(_action, {dispatch, delay, signal}) { + trackEvent('validation/validate_incremental', {actionType: _action.type}); let resourceIdentifiers: ResourceIdentifier[] = []; if ( diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 92835d4fe2..3d6e9f661f 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -162,6 +162,9 @@ export type EventMap = { }; 'ai/generation/created-resources': {resourceKinds: string[]; resourcesCount: number}; 'logs/search': {resourceKind: string}; + 'validation/load_config': {actionType: string}; + 'validation/validate_all': {actionType: string}; + 'validation/validate_incremental': {actionType: string}; }; export const APP_INSTALLED = 'APP_INSTALLED'; From 3703d772386d576a56a0845fb586de658ad3cc8c Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 12:41:36 +0300 Subject: [PATCH 5/9] chore: better editor telemetry --- src/editor/editor.instance.ts | 8 +++++++- src/shared/models/telemetry.ts | 3 +++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/editor/editor.instance.ts b/src/editor/editor.instance.ts index 17bbf791b8..42bca4a420 100644 --- a/src/editor/editor.instance.ts +++ b/src/editor/editor.instance.ts @@ -116,7 +116,11 @@ export const addEditorCommand = (payload: EditorCommand['payload'], supportHtml? const {text, altText, handler, beforeText, afterText} = payload; const id = `cmd_${uuidv4()}`; - const disposable: monaco.IDisposable = monaco.editor.registerCommand(id, handler); + const wrappedHandler = () => { + trackEvent('editor/run_command'); + handler(); + }; + const disposable: monaco.IDisposable = monaco.editor.registerCommand(id, wrappedHandler); let markdownLink: monaco.IMarkdownString; @@ -168,6 +172,7 @@ export const clearEditorDecorations = () => { monaco.languages.registerHoverProvider('yaml', { provideHover: (model, position) => { + trackEvent('editor/hover'); const positionHovers = editorHovers.filter(hover => isPositionInRange(position, hover.range)); if (positionHovers.length === 0) { return null; @@ -191,6 +196,7 @@ monaco.languages.registerLinkProvider('yaml', { }; }, resolveLink: async link => { + trackEvent('editor/follow_link'); const linksToResolve = editorLinks.filter(({range}) => isRangeInRange(range, link.range)); const promises = linksToResolve.map(({handler}) => Promise.resolve(handler())); await Promise.all(promises); diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 3d6e9f661f..b4b0f74ff4 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -86,6 +86,9 @@ export type EventMap = { 'edit/side_by_side_editor': {resourceKind: string}; 'edit/select_hover_link': {type: 'resource' | 'image' | 'file'}; 'edit/graphview': {resourceKind?: string}; + 'editor/hover': undefined; + 'editor/follow_link': undefined; + 'editor/run_command': undefined; 'create/file': undefined; 'create/folder': undefined; 'create/resource': {resourceKind: string}; From d90e76d45e4e5aad88f8596267da9ebf8650d1d9 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 13:24:14 +0300 Subject: [PATCH 6/9] chore: improve cluster telemetry --- src/redux/services/clusterDashboard.ts | 24 ++++++++++++++++++++++++ src/shared/models/telemetry.ts | 1 + 2 files changed, 25 insertions(+) diff --git a/src/redux/services/clusterDashboard.ts b/src/redux/services/clusterDashboard.ts index 0c870f4b5f..38bda7b231 100644 --- a/src/redux/services/clusterDashboard.ts +++ b/src/redux/services/clusterDashboard.ts @@ -1,9 +1,16 @@ import * as k8s from '@kubernetes/client-node'; +import {uniq} from 'lodash'; + import {createKubeClientWithSetup} from '@redux/cluster/service/kube-client'; import {cpuParser, memoryParser} from '@utils/unit-converter'; +import {isDefined} from '@shared/utils/filter'; +import {trackEvent} from '@shared/utils/telemetry'; + +let lastContext: string | undefined; + export const getClusterUtilization = async (kubeconfig: string, context: string): Promise => { const kc = await createKubeClientWithSetup({context, kubeconfig, skipHealthCheck: true}); @@ -14,6 +21,23 @@ export const getClusterUtilization = async (kubeconfig: string, context: string) const nodeMetrics: k8s.NodeMetric[] = (await metricClient.getNodeMetrics()).items; const nodes = await k8s.topNodes(k8sApiClient); + if (lastContext !== context) { + const providers = uniq( + nodes + .map(node => { + const providerId = node.Node?.spec?.providerID; + // ID of the node assigned by the cloud provider in the format: :// + const providerParts = providerId?.split('://'); + if (providerParts?.length === 2) { + return providerParts[0]; + } + return undefined; + }) + .filter(isDefined) + ); + trackEvent('cluster/metrics', {providers}); + } + return nodeMetrics.map(m => ({ nodeName: m.metadata.name, cpuUsage: cpuParser(m.usage.cpu), diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index b4b0f74ff4..c9d77931dc 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -123,6 +123,7 @@ export type EventMap = { 'cluster/actions/scale': {replicasNumber: number}; 'cluster/actions/restart': undefined; 'cluster/actions/delete': {kind: string}; + 'cluster/metrics': {providers: string[]}; 'compare/opened': {from?: string}; 'compare/compared': {left?: string; right?: string; operation: string}; 'compare/inspected': {type?: string}; From 4a6a68bae3fde84126d2901903faa8a8b60b4b0f Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:06:07 +0300 Subject: [PATCH 7/9] chore: improve telemetry --- src/editor/editor.instance.ts | 17 ++++++++++++----- src/editor/editor.types.ts | 4 ++++ src/editor/enhancers/helm/templates.ts | 1 + src/editor/enhancers/helm/valuesFile.ts | 1 + src/editor/enhancers/k8sResource/refs.ts | 3 +++ src/editor/enhancers/k8sResource/symbols.ts | 5 +++++ src/redux/services/clusterDashboard.ts | 7 ++++++- src/shared/models/telemetry.ts | 10 +++++----- 8 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/editor/editor.instance.ts b/src/editor/editor.instance.ts index 42bca4a420..423aa390b3 100644 --- a/src/editor/editor.instance.ts +++ b/src/editor/editor.instance.ts @@ -19,6 +19,7 @@ let isRecreatingModel = false; let editorType: 'local' | 'cluster' | undefined; let hasTypedInEditor = false; let hasModelContentChanged = false; +let currentResourceKind: string | undefined; export const didEditorContentChange = () => hasModelContentChanged; @@ -36,7 +37,7 @@ export const mountEditor = (props: {element: HTMLElement; type: 'local' | 'clust if (hasTypedInEditor) { return; } - trackEvent('edit/code_changes', {from: getEditorType()}); + trackEvent('edit/code_changes', {from: getEditorType(), resourceKind: currentResourceKind}); hasTypedInEditor = true; }); EDITOR.onDidChangeModelContent(e => { @@ -81,6 +82,11 @@ export const setEditorNextSelection = (range: monaco.IRange) => { }; export function recreateEditorModel(editor: monaco.editor.ICodeEditor, text: string, language: string = 'yaml') { + const kindMatch = text.match(/kind:\s*(\w+)/); + if (kindMatch?.length === 1) { + currentResourceKind = kindMatch[1]; + } + isRecreatingModel = true; resetEditor(); editor.getModel()?.dispose(); @@ -113,11 +119,11 @@ export const clearEditorLinks = () => { }; export const addEditorCommand = (payload: EditorCommand['payload'], supportHtml?: boolean) => { - const {text, altText, handler, beforeText, afterText} = payload; + const {text, altText, handler, beforeText, afterText, type} = payload; const id = `cmd_${uuidv4()}`; const wrappedHandler = () => { - trackEvent('editor/run_command'); + trackEvent('editor/run_command', {type, resourceKind: currentResourceKind}); handler(); }; const disposable: monaco.IDisposable = monaco.editor.registerCommand(id, wrappedHandler); @@ -172,7 +178,8 @@ export const clearEditorDecorations = () => { monaco.languages.registerHoverProvider('yaml', { provideHover: (model, position) => { - trackEvent('editor/hover'); + // We're sending this event on any hover because this method might be triggered by a hover created by Monaco itself + trackEvent('editor/hover', {resourceKind: currentResourceKind}); const positionHovers = editorHovers.filter(hover => isPositionInRange(position, hover.range)); if (positionHovers.length === 0) { return null; @@ -196,7 +203,7 @@ monaco.languages.registerLinkProvider('yaml', { }; }, resolveLink: async link => { - trackEvent('editor/follow_link'); + trackEvent('editor/follow_link', {resourceKind: currentResourceKind}); const linksToResolve = editorLinks.filter(({range}) => isRangeInRange(range, link.range)); const promises = linksToResolve.map(({handler}) => Promise.resolve(handler())); await Promise.all(promises); diff --git a/src/editor/editor.types.ts b/src/editor/editor.types.ts index a3b1ff8b89..5c8c925879 100644 --- a/src/editor/editor.types.ts +++ b/src/editor/editor.types.ts @@ -13,6 +13,10 @@ export type EditorLink = { export type EditorCommand = { payload: { + /** + * The type of the command is only used for telemetry. + */ + type: string; text: string; altText: string; handler: monaco.editor.ICommandHandler; diff --git a/src/editor/enhancers/helm/templates.ts b/src/editor/enhancers/helm/templates.ts index 1dbbc2514c..e92d8ebe47 100644 --- a/src/editor/enhancers/helm/templates.ts +++ b/src/editor/enhancers/helm/templates.ts @@ -60,6 +60,7 @@ export const helmTemplateFileEnhancer = createEditorEnhancer(({state, resourceId typeof keyPathInFile.value === 'object' ? JSON.stringify(keyPathInFile.value, null, 4) : keyPathInFile.value; const newCommand = addEditorCommand({ + type: 'go_to_helm_values_file', text: `${keyPathInFile.filePath}`, altText: 'Select file', handler: () => { diff --git a/src/editor/enhancers/helm/valuesFile.ts b/src/editor/enhancers/helm/valuesFile.ts index 023b8aa861..351188244c 100644 --- a/src/editor/enhancers/helm/valuesFile.ts +++ b/src/editor/enhancers/helm/valuesFile.ts @@ -40,6 +40,7 @@ export const helmValuesFileEnhancer = createEditorEnhancer(({state, resourceIden decorations.push(createInlineDecoration(placeUsed.locationInValueFile, InlineDecorationTypes.SatisfiedRef)); placeUsed.uses.forEach(use => { const newCommand = addEditorCommand({ + type: 'go_to_helm_template_file', text: `${use.filePath}`, altText: 'Select file', beforeText: 'Found in: ', diff --git a/src/editor/enhancers/k8sResource/refs.ts b/src/editor/enhancers/k8sResource/refs.ts index 85b84b4c09..d06a0a895c 100644 --- a/src/editor/enhancers/k8sResource/refs.ts +++ b/src/editor/enhancers/k8sResource/refs.ts @@ -196,6 +196,7 @@ const addEditorCommandForRef = (args: {resourceMeta: ResourceMeta; ref: Resource if (ref.target.type === 'resource' && ref.target.resourceId) { command = addEditorCommand( { + type: 'go_to_resource', text: 'Open resource', altText: 'Open resource', handler: () => { @@ -215,6 +216,7 @@ const addEditorCommandForRef = (args: {resourceMeta: ResourceMeta; ref: Resource } else if (ref.target.type === 'file') { command = addEditorCommand( { + type: 'go_to_file', text: `Open file`, altText: 'Open file', handler: () => { @@ -229,6 +231,7 @@ const addEditorCommandForRef = (args: {resourceMeta: ResourceMeta; ref: Resource } else if (ref.target.type === 'image') { command = addEditorCommand( { + type: 'go_to_image', text: `Open image`, altText: 'Open image', handler: () => { diff --git a/src/editor/enhancers/k8sResource/symbols.ts b/src/editor/enhancers/k8sResource/symbols.ts index fb8ac80eec..464aa1c21f 100644 --- a/src/editor/enhancers/k8sResource/symbols.ts +++ b/src/editor/enhancers/k8sResource/symbols.ts @@ -43,6 +43,7 @@ function addNamespaceFilterLink( const namespace = getSymbolValue(lines, symbol); if (namespace) { const newCommand = addEditorCommand({ + type: 'filter_namespace', text: `Apply or remove`, altText: 'Add/remove namespace to/from current filter', handler: () => { @@ -88,6 +89,7 @@ function addKindFilterLink( const kind = getSymbolValue(lines, symbol); if (kind) { const newCommand = addEditorCommand({ + type: 'filter_kind', text: `Apply or remove`, altText: 'Add/remove kind to/from current filter', handler: () => { @@ -121,6 +123,7 @@ function addLabelFilterLink( const value = label.substring(symbol.name.length + 1).trim(); const newCommand = addEditorCommand({ + type: 'filter_label', text: `Apply or remove`, altText: 'Add/remove label to/from current filter', handler: () => { @@ -156,6 +159,7 @@ function addAnnotationFilterLink( const value = annotation.substring(symbol.name.length + 1).trim(); const newCommand = addEditorCommand({ + type: 'filter_annotation', text: `${annotation}`, altText: 'Add/remove annotation to/from current filter', handler: () => { @@ -190,6 +194,7 @@ function addDecodeSecretHover( const decoded = Buffer.from(value, 'base64').toString('utf-8'); const newCommand = addEditorCommand({ + type: 'secret_copy_to_clipboard', text: 'Copy to clipboard', altText: 'Copy decoded secret to clipboard', handler: () => { diff --git a/src/redux/services/clusterDashboard.ts b/src/redux/services/clusterDashboard.ts index 38bda7b231..1b3a1621d7 100644 --- a/src/redux/services/clusterDashboard.ts +++ b/src/redux/services/clusterDashboard.ts @@ -20,11 +20,16 @@ export const getClusterUtilization = async (kubeconfig: string, context: string) const nodeMetrics: k8s.NodeMetric[] = (await metricClient.getNodeMetrics()).items; const nodes = await k8s.topNodes(k8sApiClient); + let kubeletVersion: string | undefined; if (lastContext !== context) { const providers = uniq( nodes .map(node => { + if (!kubeletVersion) { + kubeletVersion = node.Node.status?.nodeInfo?.kubeletVersion; + } + const providerId = node.Node?.spec?.providerID; // ID of the node assigned by the cloud provider in the format: :// const providerParts = providerId?.split('://'); @@ -35,7 +40,7 @@ export const getClusterUtilization = async (kubeconfig: string, context: string) }) .filter(isDefined) ); - trackEvent('cluster/metrics', {providers}); + trackEvent('cluster/info', {providers, kubeletVersion}); } return nodeMetrics.map(m => ({ diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index c9d77931dc..883b33b98c 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -79,16 +79,16 @@ export type EventMap = { 'explore/quick_search': undefined; 'graph/select_resource': {kind: string}; 'graph/select_image': undefined; - 'edit/code_changes': {from?: 'local' | 'cluster'}; + 'edit/code_changes': {from?: 'local' | 'cluster'; resourceKind?: string}; 'edit/template_use': {templateID: string}; 'edit/form_editor': {resourceKind?: string}; 'edit/source': {resourceKind?: string}; 'edit/side_by_side_editor': {resourceKind: string}; 'edit/select_hover_link': {type: 'resource' | 'image' | 'file'}; 'edit/graphview': {resourceKind?: string}; - 'editor/hover': undefined; - 'editor/follow_link': undefined; - 'editor/run_command': undefined; + 'editor/hover': {resourceKind?: string}; + 'editor/follow_link': {resourceKind?: string}; + 'editor/run_command': {type: string; resourceKind?: string}; 'create/file': undefined; 'create/folder': undefined; 'create/resource': {resourceKind: string}; @@ -123,7 +123,7 @@ export type EventMap = { 'cluster/actions/scale': {replicasNumber: number}; 'cluster/actions/restart': undefined; 'cluster/actions/delete': {kind: string}; - 'cluster/metrics': {providers: string[]}; + 'cluster/info': {kubeletVersion?: string; providers: string[]}; 'compare/opened': {from?: string}; 'compare/compared': {left?: string; right?: string; operation: string}; 'compare/inspected': {type?: string}; From efc29ad2186cd6d223b72e41ed16a641bcd665c3 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:18:07 +0300 Subject: [PATCH 8/9] chore: improve editor telemetry --- src/editor/editor.instance.ts | 11 +++++++++-- src/editor/editor.types.ts | 5 ++--- src/editor/enhancers/helm/templates.ts | 3 +++ src/editor/enhancers/helm/valuesFile.ts | 1 + src/editor/enhancers/k8sResource/refs.ts | 4 +++- src/editor/enhancers/k8sResource/symbols.ts | 5 +++++ src/shared/models/telemetry.ts | 4 ++-- 7 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/editor/editor.instance.ts b/src/editor/editor.instance.ts index 423aa390b3..3208d7116a 100644 --- a/src/editor/editor.instance.ts +++ b/src/editor/editor.instance.ts @@ -179,14 +179,16 @@ export const clearEditorDecorations = () => { monaco.languages.registerHoverProvider('yaml', { provideHover: (model, position) => { // We're sending this event on any hover because this method might be triggered by a hover created by Monaco itself - trackEvent('editor/hover', {resourceKind: currentResourceKind}); const positionHovers = editorHovers.filter(hover => isPositionInRange(position, hover.range)); if (positionHovers.length === 0) { + trackEvent('editor/hover', {resourceKind: currentResourceKind}); return null; } if (positionHovers.length === 1) { + trackEvent('editor/hover', {resourceKind: currentResourceKind, types: [positionHovers[0].type]}); return positionHovers[0]; } + trackEvent('editor/hover', {resourceKind: currentResourceKind, types: positionHovers.map(hover => hover.type)}); return { contents: positionHovers.map(hover => hover.contents).flat(), }; @@ -203,8 +205,13 @@ monaco.languages.registerLinkProvider('yaml', { }; }, resolveLink: async link => { - trackEvent('editor/follow_link', {resourceKind: currentResourceKind}); const linksToResolve = editorLinks.filter(({range}) => isRangeInRange(range, link.range)); + if (linksToResolve.length > 0) { + trackEvent('editor/follow_link', { + resourceKind: currentResourceKind, + types: linksToResolve.map(l => l.type), + }); + } const promises = linksToResolve.map(({handler}) => Promise.resolve(handler())); await Promise.all(promises); return {range: link.range}; diff --git a/src/editor/editor.types.ts b/src/editor/editor.types.ts index 5c8c925879..7d14d942e1 100644 --- a/src/editor/editor.types.ts +++ b/src/editor/editor.types.ts @@ -1,11 +1,13 @@ import * as monaco from 'monaco-editor'; export type EditorHover = { + type: string; range: monaco.IRange; contents: monaco.IMarkdownString[]; }; export type EditorLink = { + type: string; range: monaco.IRange; tooltip?: string; handler: () => Promise | void; @@ -13,9 +15,6 @@ export type EditorLink = { export type EditorCommand = { payload: { - /** - * The type of the command is only used for telemetry. - */ type: string; text: string; altText: string; diff --git a/src/editor/enhancers/helm/templates.ts b/src/editor/enhancers/helm/templates.ts index e92d8ebe47..0c7dcf11b7 100644 --- a/src/editor/enhancers/helm/templates.ts +++ b/src/editor/enhancers/helm/templates.ts @@ -88,6 +88,7 @@ export const helmTemplateFileEnhancer = createEditorEnhancer(({state, resourceId const text = hasMultipleLinks ? `Found this value in ${keyPathsInFile.length} helm value files` : ``; if (!hasMultipleLinks) { addEditorLink({ + type: 'go_to_file', range: helmFileValue.range, tooltip: 'Open file', handler: () => { @@ -109,6 +110,7 @@ export const helmTemplateFileEnhancer = createEditorEnhancer(({state, resourceId if (commands.length) { addEditorHover({ + type: 'helm_template_values_found', range: helmFileValue.range, contents: [createMarkdownString(text), ...commands.map(c => c.markdownLink)], }); @@ -118,6 +120,7 @@ export const helmTemplateFileEnhancer = createEditorEnhancer(({state, resourceId } addEditorHover({ + type: 'helm_template_values_not_found', range: helmFileValue.range, contents: [createMarkdownString('This value was not found in any helm values file.')], }); diff --git a/src/editor/enhancers/helm/valuesFile.ts b/src/editor/enhancers/helm/valuesFile.ts index 351188244c..048416ab4f 100644 --- a/src/editor/enhancers/helm/valuesFile.ts +++ b/src/editor/enhancers/helm/valuesFile.ts @@ -64,6 +64,7 @@ export const helmValuesFileEnhancer = createEditorEnhancer(({state, resourceIden if (commands.length) { addEditorHover({ + type: 'helm_values_used_in_template', range: placeUsed.locationInValueFile, contents: commands.map(c => c.markdownLink), }); diff --git a/src/editor/enhancers/k8sResource/refs.ts b/src/editor/enhancers/k8sResource/refs.ts index d06a0a895c..157143fe3d 100644 --- a/src/editor/enhancers/k8sResource/refs.ts +++ b/src/editor/enhancers/k8sResource/refs.ts @@ -168,6 +168,7 @@ ${outgoingRefsMarkdownTableRows.join('\n')} ); } addEditorHover({ + type: 'list_outgoing_links', range, contents: hoverContents, }); @@ -175,11 +176,12 @@ ${outgoingRefsMarkdownTableRows.join('\n')} if (matchedRefs.length === 1 && getEditorType() !== 'cluster') { const ref = matchedRefs[0]; addEditorLink({ + type: 'go_to_outgoing_link', range, handler: () => onClickRefLink({resourceMeta, ref, dispatch}), }); } else { - addEditorLink({range, handler: () => {}}); + addEditorLink({type: 'go_to_outgoing_link', range, handler: () => {}}); } }); }); diff --git a/src/editor/enhancers/k8sResource/symbols.ts b/src/editor/enhancers/k8sResource/symbols.ts index 464aa1c21f..484b174029 100644 --- a/src/editor/enhancers/k8sResource/symbols.ts +++ b/src/editor/enhancers/k8sResource/symbols.ts @@ -58,6 +58,7 @@ function addNamespaceFilterLink( `); addEditorHover({ + type: 'list_namespace_filters', range: symbol.range, contents: [filterMarkdown], }); @@ -104,6 +105,7 @@ function addKindFilterLink( `); addEditorHover({ + type: 'list_kind_filters', range: symbol.range, contents: [filterMarkdown], }); @@ -140,6 +142,7 @@ function addLabelFilterLink( `); addEditorHover({ + type: 'list_label_filters', range: symbol.range, contents: [filterMarkdown], }); @@ -176,6 +179,7 @@ function addAnnotationFilterLink( `); addEditorHover({ + type: 'list_annotation_filters', range: symbol.range, contents: [filterMarkdown], }); @@ -209,6 +213,7 @@ function addDecodeSecretHover( `); addEditorHover({ + type: 'decoded_secret', range: symbol.range, contents: [secretMarkdown], }); diff --git a/src/shared/models/telemetry.ts b/src/shared/models/telemetry.ts index 883b33b98c..5254655f65 100644 --- a/src/shared/models/telemetry.ts +++ b/src/shared/models/telemetry.ts @@ -86,8 +86,8 @@ export type EventMap = { 'edit/side_by_side_editor': {resourceKind: string}; 'edit/select_hover_link': {type: 'resource' | 'image' | 'file'}; 'edit/graphview': {resourceKind?: string}; - 'editor/hover': {resourceKind?: string}; - 'editor/follow_link': {resourceKind?: string}; + 'editor/hover': {types?: string[]; resourceKind?: string}; + 'editor/follow_link': {types?: string[]; resourceKind?: string}; 'editor/run_command': {type: string; resourceKind?: string}; 'create/file': undefined; 'create/folder': undefined; From 011ebc82d0c0dd54c4ffe24c137806613e857632 Mon Sep 17 00:00:00 2001 From: Dev Catalin <20538711+devcatalin@users.noreply.github.com> Date: Wed, 21 Jun 2023 14:27:33 +0300 Subject: [PATCH 9/9] chore: quick fix --- src/editor/editor.instance.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/editor/editor.instance.ts b/src/editor/editor.instance.ts index 3208d7116a..f73ae2c834 100644 --- a/src/editor/editor.instance.ts +++ b/src/editor/editor.instance.ts @@ -85,6 +85,8 @@ export function recreateEditorModel(editor: monaco.editor.ICodeEditor, text: str const kindMatch = text.match(/kind:\s*(\w+)/); if (kindMatch?.length === 1) { currentResourceKind = kindMatch[1]; + } else { + currentResourceKind = undefined; } isRecreatingModel = true; @@ -178,7 +180,6 @@ export const clearEditorDecorations = () => { monaco.languages.registerHoverProvider('yaml', { provideHover: (model, position) => { - // We're sending this event on any hover because this method might be triggered by a hover created by Monaco itself const positionHovers = editorHovers.filter(hover => isPositionInRange(position, hover.range)); if (positionHovers.length === 0) { trackEvent('editor/hover', {resourceKind: currentResourceKind});