diff --git a/.vscode/settings.json b/.vscode/settings.json index 5adc2423..129a253b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -11,5 +11,9 @@ }, // Turn off tsc task auto detection since we have the necessary tasks as npm scripts "typescript.tsc.autoDetect": "off", - "debug.node.autoAttach": "on" + "debug.node.autoAttach": "on", + "editor.codeActionsOnSave": { + "source.fixAll": true, + "source.organizeImports": true + }, } \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index c19145fb..288b176e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-gitops-tools", - "version": "0.25.1-edge.5", + "version": "0.25.1-edge.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-gitops-tools", - "version": "0.25.1-edge.5", + "version": "0.25.1-edge.6", "license": "MPL-2.0", "dependencies": { "@kubernetes/client-node": "^0.18.1", diff --git a/package.json b/package.json index 27110951..345fa073 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-gitops-tools", "displayName": "GitOps Tools for Flux", "description": "GitOps automation tools for continuous delivery of Kubernetes and Cloud Native applications", - "version": "0.25.1-edge.5", + "version": "0.25.1-edge.6", "author": "Kingdon Barrett ", "contributors": [ "Kingdon Barrett ", diff --git a/src/cli/flux/fluxTools.ts b/src/cli/flux/fluxTools.ts index ae86514f..94860a1b 100644 --- a/src/cli/flux/fluxTools.ts +++ b/src/cli/flux/fluxTools.ts @@ -143,7 +143,7 @@ class FluxTools { errorData += `Command '${cmd}' timed out`; } // + (treeShellResult.code === null ? 'Command timed out' : ''; - window.showErrorMessage(`Failed to get resources created by the kustomization ${name}. ERROR: ${errorData}`); + window.showWarningMessage(`Failed to get resources created by the kustomization ${name}. ERROR: ${errorData}`); return; } diff --git a/src/cli/kubernetes/apiResources.ts b/src/cli/kubernetes/apiResources.ts index 89772452..5f844fd6 100644 --- a/src/cli/kubernetes/apiResources.ts +++ b/src/cli/kubernetes/apiResources.ts @@ -1,12 +1,12 @@ +import { redrawhResourcesTreeViews, refreshResourcesTreeViews } from 'commands/refreshTreeViews'; +import { currentContextData } from 'data/contextData'; import { setVSCodeContext, telemetry } from 'extension'; -import { TelemetryError } from 'types/telemetryEventNames'; -import { invokeKubectlCommand } from './kubernetesToolsKubectl'; -import { Kind } from 'types/kubernetes/kubernetesTypes'; -import { createK8sClients } from 'k8s/client'; import { ContextId } from 'types/extensionIds'; -import { refreshAllTreeViews, refreshResourcesTreeViews } from 'commands/refreshTreeViews'; -import { restartKubeProxy } from './kubectlProxy'; +import { Kind } from 'types/kubernetes/kubernetesTypes'; +import { TelemetryError } from 'types/telemetryEventNames'; import { clusterDataProvider } from 'ui/treeviews/treeViews'; +import { restartKubeProxy } from './kubectlProxy'; +import { invokeKubectlCommand } from './kubernetesToolsKubectl'; export enum ApiState { Loading, @@ -14,26 +14,22 @@ export enum ApiState { ClusterUnreachable, } -type KindApiParams = { +export type KindApiParams = { plural: string; // configmaps, deployments, gitrepositories, ... group: string; // '', apps, source.toolkit.fluxcd.io, ... version: string; // v1, v1beta2, ... }; -export let apiState: ApiState = ApiState.Loading; -/* - * Current cluster supported kubernetes resource kinds. - */ -let apiResources: Map | undefined; /** * Return all available kubernetes resource kinds */ export function getAvailableResourcePlurals(): string[] | undefined { + const context = currentContextData(); const plurals: string[] = []; - if(apiResources) { - apiResources.forEach((value, key) => { + if(context.apiResources) { + context.apiResources.forEach((value, key) => { plurals.push(value.plural); }); @@ -43,24 +39,30 @@ export function getAvailableResourcePlurals(): string[] | undefined { export function getAPIParams(kind: Kind): KindApiParams | undefined { - if(apiResources) { - return apiResources.get(kind); + const context = currentContextData(); + + if(context.apiResources) { + return context.apiResources.get(kind); } } export async function loadAvailableResourceKinds() { - apiResources = undefined; - apiState = ApiState.Loading; + const context = currentContextData(); + context.apiResources = undefined; + context.apiState = ApiState.Loading; + // will set their content to Loading API... + redrawhResourcesTreeViews(); const kindsShellResult = await invokeKubectlCommand('api-resources --verbs=list -o wide'); if (kindsShellResult?.code !== 0) { telemetry.sendError(TelemetryError.FAILED_TO_GET_AVAILABLE_RESOURCE_KINDS); console.warn(`Failed to get resource kinds: ${kindsShellResult?.stderr}`); - apiState = ApiState.ClusterUnreachable; + context.apiState = ApiState.ClusterUnreachable; setVSCodeContext(ContextId.ClusterUnreachable, true); clusterDataProvider.updateCurrentContextChildNodes(); refreshResourcesTreeViews(); + redrawhResourcesTreeViews(); return; } @@ -68,7 +70,7 @@ export async function loadAvailableResourceKinds() { .split('\n') .filter(line => line.length).slice(1); - apiResources = new Map(); + context.apiResources = new Map(); lines.map(line => { let cols = line.split(/\s+/); @@ -86,16 +88,15 @@ export async function loadAvailableResourceKinds() { version = group; group = ''; } - apiResources?.set(kind, { plural, group, version }); + context.apiResources?.set(kind, { plural, group, version }); }); console.log('apiResources loaded'); - apiState = ApiState.Loaded; + context.apiState = ApiState.Loaded; setVSCodeContext(ContextId.ClusterUnreachable, false); clusterDataProvider.updateCurrentContextChildNodes(); - createK8sClients(); await restartKubeProxy(); // give proxy init callbacks time to fire diff --git a/src/cli/kubernetes/kubectlGet.ts b/src/cli/kubernetes/kubectlGet.ts index 11e81f6e..15758741 100644 --- a/src/cli/kubernetes/kubectlGet.ts +++ b/src/cli/kubernetes/kubectlGet.ts @@ -1,6 +1,6 @@ import safesh from 'shell-escape-tag'; -import { setVSCodeContext, telemetry } from 'extension'; +import { telemetry } from 'extension'; import { k8sList } from 'k8s/list'; import { Bucket } from 'types/flux/bucket'; import { GitOpsTemplate } from 'types/flux/gitOpsTemplate'; @@ -12,10 +12,9 @@ import { OCIRepository } from 'types/flux/ociRepository'; import { Deployment, Kind, KubernetesObject, Pod, qualifyToolkitKind } from 'types/kubernetes/kubernetesTypes'; import { TelemetryError } from 'types/telemetryEventNames'; import { parseJson, parseJsonItems } from 'utils/jsonUtils'; -import { invokeKubectlCommand } from './kubernetesToolsKubectl'; -import { getAvailableResourcePlurals } from './apiResources'; import { window } from 'vscode'; -import { ContextId } from 'types/extensionIds'; +import { getAvailableResourcePlurals } from './apiResources'; +import { invokeKubectlCommand } from './kubernetesToolsKubectl'; /** * RegExp for the Error that should not be sent in telemetry. * Server doesn't have a resource type = when GitOps not enabled @@ -41,11 +40,8 @@ export async function getResource(name: string, namespace: string, kind: string) } export async function getResourcesAllNamespaces(kind: Kind, telemetryError: TelemetryError): Promise { - const t1 = Date.now(); - const list = await k8sList(kind); if(list !== undefined) { - console.log(`k8sList ${kind}`, list.length, ' ∆', Date.now() - t1); return list as T[]; } @@ -61,7 +57,6 @@ export async function getResourcesAllNamespaces(kind } const items = parseJsonItems(shellResult.stdout); - console.log(`kubectl get ${kind}`, items.length, ' ∆', Date.now() - t1); return items as T[]; } diff --git a/src/cli/kubernetes/kubectlProxy.ts b/src/cli/kubernetes/kubectlProxy.ts index b1d25cf5..0efaa648 100644 --- a/src/cli/kubernetes/kubectlProxy.ts +++ b/src/cli/kubernetes/kubectlProxy.ts @@ -1,16 +1,15 @@ import { KubeConfig } from '@kubernetes/client-node'; import { ChildProcess } from 'child_process'; import * as shell from 'cli/shell/exec'; -import { destroyK8sClients } from 'k8s/client'; +import { createK8sClients, destroyK8sClients } from 'k8s/client'; import { createProxyConfig } from 'k8s/createKubeProxyConfig'; -// let isConnecting = false; export let proxyProc: ChildProcess | undefined; export let kubeProxyConfig: KubeConfig | undefined; // tries to keep alive the `kubectl proxy` process // if process dies or errors out it will be stopped -// and restarted by the 1000ms interval +// and restarted by the 2000ms interval // if context changes proxy is stopped and restarted immediately export function kubeProxyKeepAlive() { // keep alive @@ -28,35 +27,31 @@ async function startKubeProxy() { } proxyProc = shell.execProc('kubectl proxy -p 0'); - console.log(`~proxy started ${proxyProc.pid}`); procListen(proxyProc); } function procListen(p: ChildProcess) { p.on('exit', async code => { - console.log('~proxy exit', p.pid, code); if(proxyProc?.pid === p.pid) { stopKubeProxy(); } }); p.on('error', err => { - console.log('~proxy error', p.pid, err); p.kill(); }); p.stdout?.on('data', (data: string) => { - console.log(`~proxy ${p.pid} STDOUT: ${data}`); if(data.includes('Starting to serve on')) { const port = parseInt(data.split(':')[1].trim()); kubeProxyConfig = createProxyConfig(port); - console.log('~kubeproxy config ready'); + createK8sClients(); } }); p.stderr?.on('data', (data: string) => { - console.log(`~proxy ${p.pid} STDERR: ${data}`); + console.warn(`kubectl proxy ${p.pid} STDERR: ${data}`); p.kill(); }); } @@ -65,17 +60,13 @@ function procListen(p: ChildProcess) { export async function stopKubeProxy() { if(proxyProc) { if(!proxyProc.killed) { - console.log(`~proxy.kill() ${proxyProc.pid}`); proxyProc.kill(); } proxyProc = undefined; kubeProxyConfig = undefined; destroyK8sClients(); - // isConnecting = false; - console.log('~stopped kube proxy'); } - } export async function restartKubeProxy() { diff --git a/src/cli/kubernetes/kubernetesConfig.ts b/src/cli/kubernetes/kubernetesConfig.ts index 2ca7f764..38f9db5d 100644 --- a/src/cli/kubernetes/kubernetesConfig.ts +++ b/src/cli/kubernetes/kubernetesConfig.ts @@ -4,14 +4,12 @@ import { window } from 'vscode'; import * as k8s from '@kubernetes/client-node'; import { ActionOnInvalid } from '@kubernetes/client-node/dist/config_types'; import { shellCodeError } from 'cli/shell/exec'; -import { refreshAllTreeViews } from 'commands/refreshTreeViews'; import { setVSCodeContext, telemetry } from 'extension'; import { ContextId } from 'types/extensionIds'; import { TelemetryError } from 'types/telemetryEventNames'; -import { refreshClustersTreeView } from 'ui/treeviews/treeViews'; +import { reloadClustersTreeView } from 'ui/treeviews/treeViews'; import { kcContextsListChanged, kcCurrentContextChanged, kcTextChanged } from 'utils/kubeConfigCompare'; import { loadAvailableResourceKinds as loadApiResources } from './apiResources'; -import { restartKubeProxy } from './kubectlProxy'; import { loadKubeConfigPath } from './kubernetesConfigWatcher'; import { invokeKubectlCommand } from './kubernetesToolsKubectl'; @@ -31,7 +29,6 @@ export const kubeConfig: k8s.KubeConfig = new k8s.KubeConfig(); // reload the kubeconfig via kubernetes-tools. fire events if things have changed export async function syncKubeConfig(forceReloadResourceKinds = false) { - console.log('syncKubeConfig'); kubeConfigState = KubeConfigState.Loading; const configShellResult = await invokeKubectlCommand('config view'); @@ -65,23 +62,21 @@ async function kubeconfigChanged(newKubeConfig: k8s.KubeConfig, forceReloadResou // load the changed kubeconfig globally so that the following code use the new config kubeConfig.loadFromString(newKubeConfig.exportConfig(), {onInvalidEntry: ActionOnInvalid.FILTER}); - console.log(kubeConfig.currentContext); if(!currentContextExists()) { kubeConfigState = KubeConfigState.NoContextSelected; } if (contextChanged) { - console.log('currentContext changed', kubeConfig.getCurrentContext()); setVSCodeContext(ContextId.CurrentClusterGitOpsNotEnabled, false); setVSCodeContext(ContextId.ClusterUnreachable, false); } if (contextChanged || forceReloadResourceKinds) { - refreshClustersTreeView(); + reloadClustersTreeView(); loadApiResources(); } else if (contextsListChanged) { - refreshClustersTreeView(); + reloadClustersTreeView(); } } diff --git a/src/cli/kubernetes/kubernetesToolsKubectl.ts b/src/cli/kubernetes/kubernetesToolsKubectl.ts index bbef8619..9dd41fd5 100644 --- a/src/cli/kubernetes/kubernetesToolsKubectl.ts +++ b/src/cli/kubernetes/kubernetesToolsKubectl.ts @@ -47,11 +47,7 @@ export async function invokeKubectlCommand(command: string, printOutput = true): let kubectlShellResult; const commandWithArgs = `kubectl ${command} --request-timeout ${getRequestTimeout()}`; - const t1 = Date.now(); kubectlShellResult = await shell.exec(commandWithArgs); - const t2 = Date.now(); - console.log(`exec ${command} ∆`, t2 - t1); - if(printOutput) { output.send(`> kubectl ${command}`, { diff --git a/src/commands/createFromTemplate.ts b/src/commands/createFromTemplate.ts index 03c2d049..91d9ae63 100644 --- a/src/commands/createFromTemplate.ts +++ b/src/commands/createFromTemplate.ts @@ -1,4 +1,3 @@ -import * as vscode from 'vscode'; import { GitOpsTemplateNode } from 'ui/treeviews/nodes/gitOpsTemplateNode'; import { openCreateFromTemplatePanel } from 'ui/webviews/createFromTemplate/openWebview'; diff --git a/src/commands/deleteSource.ts b/src/commands/deleteSource.ts index 934da810..dd35af83 100644 --- a/src/commands/deleteSource.ts +++ b/src/commands/deleteSource.ts @@ -2,6 +2,7 @@ import { window } from 'vscode'; import { AzureClusterProvider, azureTools } from 'cli/azure/azureTools'; import { fluxTools } from 'cli/flux/fluxTools'; +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; import { telemetry } from 'extension'; import { failed } from 'types/errorable'; import { FluxSource } from 'types/fluxCliTypes'; @@ -11,8 +12,7 @@ import { BucketNode } from 'ui/treeviews/nodes/source/bucketNode'; import { GitRepositoryNode } from 'ui/treeviews/nodes/source/gitRepositoryNode'; import { HelmRepositoryNode } from 'ui/treeviews/nodes/source/helmRepositoryNode'; import { OCIRepositoryNode } from 'ui/treeviews/nodes/source/ociRepositoryNode'; -import { getCurrentClusterInfo, refreshSourcesTreeView, refreshWorkloadsTreeView } from 'ui/treeviews/treeViews'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { getCurrentClusterInfo, reloadSourcesTreeView, reloadWorkloadsTreeView } from 'ui/treeviews/treeViews'; /** * Delete a source @@ -54,10 +54,10 @@ export async function deleteSource(sourceNode: GitRepositoryNode | OCIRepository if (currentClusterInfo.result.isAzure) { await azureTools.deleteSource(sourceName, contextName, currentClusterInfo.result.clusterProvider as AzureClusterProvider); - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } else { await fluxTools.delete(sourceType, sourceName, sourceNamespace); } - refreshSourcesTreeView(); + reloadSourcesTreeView(); } diff --git a/src/commands/deleteWorkload.ts b/src/commands/deleteWorkload.ts index 9b9f0133..c908404d 100644 --- a/src/commands/deleteWorkload.ts +++ b/src/commands/deleteWorkload.ts @@ -2,6 +2,7 @@ import { window } from 'vscode'; import { AzureClusterProvider, azureTools } from 'cli/azure/azureTools'; import { fluxTools } from 'cli/flux/fluxTools'; +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; import { telemetry } from 'extension'; import { failed } from 'types/errorable'; import { FluxWorkload } from 'types/fluxCliTypes'; @@ -9,8 +10,7 @@ import { Kind } from 'types/kubernetes/kubernetesTypes'; import { TelemetryEvent } from 'types/telemetryEventNames'; import { HelmReleaseNode } from 'ui/treeviews/nodes/workload/helmReleaseNode'; import { KustomizationNode } from 'ui/treeviews/nodes/workload/kustomizationNode'; -import { getCurrentClusterInfo, refreshWorkloadsTreeView } from 'ui/treeviews/treeViews'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { getCurrentClusterInfo, reloadWorkloadsTreeView } from 'ui/treeviews/treeViews'; /** @@ -73,5 +73,5 @@ export async function deleteWorkload(workloadNode: KustomizationNode | HelmRelea await fluxTools.delete(workloadType, workloadName, workloadNamespace); } - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } diff --git a/src/commands/enableDisableGitOps.ts b/src/commands/enableDisableGitOps.ts index be847ad5..e3746b51 100644 --- a/src/commands/enableDisableGitOps.ts +++ b/src/commands/enableDisableGitOps.ts @@ -3,14 +3,12 @@ import { window } from 'vscode'; import { azureTools, isAzureProvider } from 'cli/azure/azureTools'; import { fluxTools } from 'cli/flux/fluxTools'; import { detectClusterProvider } from 'cli/kubernetes/clusterProvider'; +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { refreshAllTreeViewsCommand } from 'commands/refreshTreeViews'; import { disableConfirmations, telemetry } from 'extension'; -import { failed } from 'types/errorable'; import { ClusterProvider } from 'types/kubernetes/clusterProvider'; import { TelemetryEvent } from 'types/telemetryEventNames'; import { ClusterNode } from 'ui/treeviews/nodes/cluster/clusterNode'; -import { getCurrentClusterInfo } from 'ui/treeviews/treeViews'; -import { refreshAllTreeViewsCommand } from 'commands/refreshTreeViews'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; diff --git a/src/commands/expandAll.ts b/src/commands/expandAll.ts index 41361ba2..87a5e096 100644 --- a/src/commands/expandAll.ts +++ b/src/commands/expandAll.ts @@ -1,11 +1,9 @@ -import { TreeNode } from 'ui/treeviews/nodes/treeNode'; -import { sourceDataProvider, sourceTreeView, workloadDataProvider } from 'ui/treeviews/treeViews'; -import { TreeItemCollapsibleState } from 'vscode'; +import { sourceDataProvider, workloadDataProvider } from 'ui/treeviews/treeViews'; export async function expandAllSources() { sourceDataProvider.expandAll(); } export async function expandAllWorkloads() { - sourceDataProvider.expandAll(); + workloadDataProvider.expandAll(); } diff --git a/src/commands/fluxCheck.ts b/src/commands/fluxCheck.ts index 1351710b..55c2ec20 100644 --- a/src/commands/fluxCheck.ts +++ b/src/commands/fluxCheck.ts @@ -2,8 +2,6 @@ import safesh from 'shell-escape-tag'; import * as shell from 'cli/shell/exec'; import { ClusterNode } from 'ui/treeviews/nodes/cluster/clusterNode'; -import { enabledFluxChecks, suppressDebugMessages } from 'extension'; -import { window } from 'vscode'; /** * Runs `flux check` command for selected cluster in the output view. diff --git a/src/commands/fluxCheckPrerequisites.ts b/src/commands/fluxCheckPrerequisites.ts index e21ca642..a05b2add 100644 --- a/src/commands/fluxCheckPrerequisites.ts +++ b/src/commands/fluxCheckPrerequisites.ts @@ -1,5 +1,4 @@ import * as shell from 'cli/shell/exec'; -import { enabledFluxChecks } from 'extension'; /** * Runs `flux check --pre` command in the output view. diff --git a/src/commands/openResource.ts b/src/commands/openResource.ts index 12d754a2..7a3aa461 100644 --- a/src/commands/openResource.ts +++ b/src/commands/openResource.ts @@ -1,9 +1,8 @@ import { Uri, window, workspace } from 'vscode'; +import { kubeConfigPath } from 'cli/kubernetes/kubernetesConfigWatcher'; import { telemetry } from 'extension'; import { TelemetryError } from 'types/telemetryEventNames'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; -import { kubeConfigPath } from 'cli/kubernetes/kubernetesConfigWatcher'; /** * Open resource in the editor diff --git a/src/commands/refreshTreeViews.ts b/src/commands/refreshTreeViews.ts index 53f6e25b..6fd2b562 100644 --- a/src/commands/refreshTreeViews.ts +++ b/src/commands/refreshTreeViews.ts @@ -1,5 +1,5 @@ import { syncKubeConfig } from 'cli/kubernetes/kubernetesConfig'; -import { refreshClustersTreeView, refreshSourcesTreeView, refreshTemplatesTreeView, refreshWorkloadsTreeView } from '../ui/treeviews/treeViews'; +import { reloadClustersTreeView, reloadSourcesTreeView, reloadTemplatesTreeView, reloadWorkloadsTreeView, sourceDataProvider, templateDateProvider, workloadDataProvider } from '../ui/treeviews/treeViews'; /** * Clicked button on the cluster tree view @@ -11,9 +11,8 @@ export async function refreshAllTreeViewsCommand() { } export async function refreshAllTreeViews() { - console.log('refreshAllTreeViews'); - refreshClustersTreeView(); + reloadClustersTreeView(); refreshResourcesTreeViews(); } @@ -26,7 +25,13 @@ export function refreshResourcesTreeViewsCommand() { } export function refreshResourcesTreeViews() { - refreshSourcesTreeView(); - refreshWorkloadsTreeView(); - refreshTemplatesTreeView(); + reloadSourcesTreeView(); + reloadWorkloadsTreeView(); + reloadTemplatesTreeView(); +} + +export function redrawhResourcesTreeViews() { + sourceDataProvider.redraw(); + workloadDataProvider.redraw(); + templateDateProvider.redraw(); } diff --git a/src/commands/resume.ts b/src/commands/resume.ts index 371e91f5..023c82d4 100644 --- a/src/commands/resume.ts +++ b/src/commands/resume.ts @@ -10,7 +10,7 @@ import { HelmRepositoryNode } from 'ui/treeviews/nodes/source/helmRepositoryNode import { OCIRepositoryNode } from 'ui/treeviews/nodes/source/ociRepositoryNode'; import { HelmReleaseNode } from 'ui/treeviews/nodes/workload/helmReleaseNode'; import { KustomizationNode } from 'ui/treeviews/nodes/workload/kustomizationNode'; -import { getCurrentClusterInfo, refreshSourcesTreeView, refreshWorkloadsTreeView } from 'ui/treeviews/treeViews'; +import { getCurrentClusterInfo, reloadSourcesTreeView, reloadWorkloadsTreeView } from 'ui/treeviews/treeViews'; /** * Resume source or workload reconciliation and refresh its Tree View. @@ -47,11 +47,11 @@ export async function resume(node: GitRepositoryNode | HelmReleaseNode | HelmRep } if (node instanceof GitRepositoryNode || node instanceof OCIRepositoryNode || node instanceof HelmRepositoryNode) { - refreshSourcesTreeView(); + reloadSourcesTreeView(); if (currentClusterInfo.result.isAzure) { - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } } else { - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } } diff --git a/src/commands/setClusterProvider.ts b/src/commands/setClusterProvider.ts index 448c47da..75a12ecb 100644 --- a/src/commands/setClusterProvider.ts +++ b/src/commands/setClusterProvider.ts @@ -5,7 +5,7 @@ import { ClusterMetadata } from 'data/globalState'; import { globalState } from 'extension'; import { KnownClusterProviders, knownClusterProviders } from 'types/kubernetes/clusterProvider'; import { ClusterNode } from 'ui/treeviews/nodes/cluster/clusterNode'; -import { refreshClustersTreeView } from 'ui/treeviews/treeViews'; +import { reloadClustersTreeView } from 'ui/treeviews/treeViews'; import { refreshAllTreeViews } from './refreshTreeViews'; export async function setClusterProvider(clusterNode: ClusterNode) { @@ -37,7 +37,7 @@ export async function setClusterProvider(clusterNode: ClusterNode) { if(clusterNode.context.name === kubeConfig.getCurrentContext()) { refreshAllTreeViews(); } else { - refreshClustersTreeView(); + reloadClustersTreeView(); } } } diff --git a/src/commands/suspend.ts b/src/commands/suspend.ts index 13bf797c..26d65cf1 100644 --- a/src/commands/suspend.ts +++ b/src/commands/suspend.ts @@ -1,16 +1,16 @@ import { window } from 'vscode'; -import { AzureClusterProvider, azureTools, isAzureProvider } from 'cli/azure/azureTools'; -import { failed } from 'types/errorable'; +import { AzureClusterProvider, azureTools } from 'cli/azure/azureTools'; import { fluxTools } from 'cli/flux/fluxTools'; +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { failed } from 'types/errorable'; import { FluxSource, FluxWorkload } from 'types/fluxCliTypes'; import { GitRepositoryNode } from 'ui/treeviews/nodes/source/gitRepositoryNode'; -import { HelmReleaseNode } from 'ui/treeviews/nodes/workload/helmReleaseNode'; import { HelmRepositoryNode } from 'ui/treeviews/nodes/source/helmRepositoryNode'; -import { KustomizationNode } from 'ui/treeviews/nodes/workload/kustomizationNode'; import { OCIRepositoryNode } from 'ui/treeviews/nodes/source/ociRepositoryNode'; -import { getCurrentClusterInfo, refreshSourcesTreeView, refreshWorkloadsTreeView } from 'ui/treeviews/treeViews'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { HelmReleaseNode } from 'ui/treeviews/nodes/workload/helmReleaseNode'; +import { KustomizationNode } from 'ui/treeviews/nodes/workload/kustomizationNode'; +import { getCurrentClusterInfo, reloadSourcesTreeView, reloadWorkloadsTreeView } from 'ui/treeviews/treeViews'; /** * Suspend source or workload reconciliation and refresh its Tree View. @@ -49,11 +49,11 @@ export async function suspend(node: GitRepositoryNode | HelmReleaseNode | Kustom } if (node instanceof GitRepositoryNode || node instanceof OCIRepositoryNode || node instanceof HelmRepositoryNode) { - refreshSourcesTreeView(); + reloadSourcesTreeView(); if (currentClusterInfo.result.isAzure) { - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } } else { - refreshWorkloadsTreeView(); + reloadWorkloadsTreeView(); } } diff --git a/src/data/contextData.ts b/src/data/contextData.ts new file mode 100644 index 00000000..a987a52b --- /dev/null +++ b/src/data/contextData.ts @@ -0,0 +1,64 @@ +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { Kind } from 'types/kubernetes/kubernetesTypes'; +import { TreeNode } from 'ui/treeviews/nodes/treeNode'; +import { TreeItemCollapsibleState } from 'vscode'; +import { ApiState, KindApiParams } from '../cli/kubernetes/apiResources'; + + +export class ContextData { + public viewData: { [key: string]: ViewData; }; + public contextName = ''; + public apiState = ApiState.Loading; + // Current cluster supported kubernetes resource kinds. + public apiResources: Map | undefined; + + constructor(contextName: string) { + this.contextName = contextName; + this.viewData = { + 'source': new ViewData(), + 'workload': new ViewData(), + 'template': new ViewData(), + }; + } + +} + +const contextDatas = new Map(); + +export function currentContextData() { + let currentData = contextDatas.get(kubeConfig.currentContext); + if (!currentData) { + currentData = new ContextData(kubeConfig.currentContext); + contextDatas.set(kubeConfig.currentContext, currentData); + } + return currentData; +} + +export class ViewData { + public nodes: TreeNode[] = []; + public collapsibleStates = new Map(); + public loading = false; + + saveCollapsibleStates() { + this.collapsibleStates.clear(); + + for (const node of this.nodes) { + const name = node.resource?.metadata?.name; + if (name) { + this.collapsibleStates.set(name, node.collapsibleState || TreeItemCollapsibleState.Collapsed); + } + } + } + + loadCollapsibleStates() { + for (const node of this.nodes) { + const name = node.resource?.metadata?.name; + if (name) { + const state = this.collapsibleStates.get(name); + if (state) { + node.collapsibleState = state; + } + } + } + } +} diff --git a/src/extension.ts b/src/extension.ts index ddd7cc04..223de489 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,7 +10,6 @@ import { getExtensionVersion } from './commands/showInstalledVersions'; import { showNewUserGuide } from './commands/showNewUserGuide'; import { GlobalState, GlobalStateKey } from './data/globalState'; import { Telemetry } from './data/telemetry'; -import { succeeded } from './types/errorable'; import { CommandId, ContextId, GitOpsExtensionConstants } from './types/extensionIds'; import { TelemetryEvent } from './types/telemetryEventNames'; import { checkInstalledFluxVersion } from './ui/promptToInstallFlux'; @@ -105,7 +104,6 @@ function listenExtensionConfigChanged() { } const selected = await window.showInformationMessage('Configuration changed. Reload VS Code to apply?', 'Reload'); - console.log(e); if(selected === 'Reload') { await commands.executeCommand(CommandId.VSCodeReload); } diff --git a/src/k8s/informers.ts b/src/k8s/informers.ts index 3a43eba1..51dd31ab 100644 --- a/src/k8s/informers.ts +++ b/src/k8s/informers.ts @@ -19,7 +19,6 @@ export function createInformers(kc: k8s.KubeConfig) { createInformer(kc, workloadDataProvider, kind); }); - console.log('*- informers started'); } export function destroyInformers() { @@ -73,7 +72,6 @@ function registerInformerEvents(informer: k8s.Informer, receiv }); informer?.on('error', (err: Error) => { - console.log('*- informer Error', err); destroyInformers(); }); } diff --git a/src/k8s/list.ts b/src/k8s/list.ts index 8922fa84..7464e5ac 100644 --- a/src/k8s/list.ts +++ b/src/k8s/list.ts @@ -6,12 +6,10 @@ import { k8sCoreApi, k8sCustomApi } from './client'; export async function k8sList(kind: Kind): Promise { const api = getAPIParams(kind); if(!api) { - console.log('k8sList no apiParams'); return; } if(!k8sCustomApi) { - console.log('k8sList no k8sCustomApi'); return; } @@ -26,14 +24,11 @@ export async function k8sList(kind: Kind): Promise { if(!k8sCoreApi) { - console.log('k8sList no k8sCoreApi'); return; } try { - const t1 = Date.now(); const result = await k8sCoreApi.listNamespace(); - console.log('k8sList Namespace ∆', Date.now() - t1); const kbody = result.body as KubernetesListObject; return kbody.items.map(ns => { diff --git a/src/types/flux/helmRelease.ts b/src/types/flux/helmRelease.ts index 2a9b11f6..e4837fe5 100644 --- a/src/types/flux/helmRelease.ts +++ b/src/types/flux/helmRelease.ts @@ -1,5 +1,5 @@ import { Condition, Kind, KubernetesJSON, KubernetesObject } from 'types/kubernetes/kubernetesTypes'; -import { DependsOn, KustomizationKubeConfig, Kustomization, NamespacedObjectKindReference } from './kustomization'; +import { DependsOn, Kustomization, KustomizationKubeConfig, NamespacedObjectKindReference } from './kustomization'; /** * Helm release info object. diff --git a/src/ui/treeviews/dataProviders/asyncDataProvider.ts b/src/ui/treeviews/dataProviders/asyncDataProvider.ts new file mode 100644 index 00000000..aae63317 --- /dev/null +++ b/src/ui/treeviews/dataProviders/asyncDataProvider.ts @@ -0,0 +1,83 @@ +import { ApiState } from 'cli/kubernetes/apiResources'; +import { KubeConfigState, kubeConfigState } from 'cli/kubernetes/kubernetesConfig'; +import { ContextData, ViewData, currentContextData } from 'data/contextData'; +import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; +import { TreeNode } from '../nodes/treeNode'; +import { clusterDataProvider } from '../treeViews'; +import { SimpleDataProvider } from './simpleDataProvider'; + +/**` + * Defines tree view data provider base class for all GitOps tree views. + */ +export class AsyncDataProvider extends SimpleDataProvider{ + get nodes() { + return this.viewData(currentContextData()).nodes; + } + + // child views override this to provide their own view data + protected viewData(contextData: ContextData) { + return new ViewData(); + } + + public currentViewData() { + return this.viewData(currentContextData()); + } + + + // give nodes for vscode to render based on async data loading state + protected async getRootNodes(): Promise { + const context = currentContextData(); + + if(context.apiState === ApiState.Loading) { + return infoNodes(InfoNode.LoadingApi); + } + + if(context.apiState === ApiState.ClusterUnreachable) { + return infoNodes(InfoNode.ClusterUnreachable); + } + + // return empty array so that vscode welcome view with embedded link "Enable Gitops ..." is shown + if(clusterDataProvider.currentContextIsGitOpsNotEnabled()) { + return []; + } + + if (this.currentViewData().loading || kubeConfigState === KubeConfigState.Loading) { + return infoNodes(InfoNode.Loading); + } + + if(this.currentViewData().nodes.length === 0) { + return infoNodes(InfoNode.NoResources); + } + + return this.currentViewData().nodes; + } + + + public async reload() { + const context = currentContextData(); + const viewData = this.viewData(context); + + + if(viewData.loading) { + return; + } + + viewData.loading = true; + viewData.saveCollapsibleStates(); + if(viewData.nodes.length === 0) { + // show Loading... if no nodes yet + this.redraw(); + } + viewData.nodes = []; // clear them first for good luck + viewData.nodes = await this.loadRootNodes(); + viewData.loadCollapsibleStates(); + viewData.loading = false; + console.log(`finish loading ${context.contextName} ${this.constructor.name}`); + + this.redraw(); + } + + +} + + diff --git a/src/ui/treeviews/dataProviders/clusterDataProvider.ts b/src/ui/treeviews/dataProviders/clusterDataProvider.ts index 59c1bc9c..96cf6e00 100644 --- a/src/ui/treeviews/dataProviders/clusterDataProvider.ts +++ b/src/ui/treeviews/dataProviders/clusterDataProvider.ts @@ -1,21 +1,17 @@ import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; -import { setVSCodeContext } from 'extension'; -import { ContextId } from 'types/extensionIds'; import { statusBar } from 'ui/statusBar'; -import { TreeItem } from 'vscode'; import { ClusterNode } from '../nodes/cluster/clusterNode'; -import { TreeNode } from '../nodes/treeNode'; -import { DataProvider } from './dataProvider'; +import { SimpleDataProvider } from './simpleDataProvider'; /** * Defines Clusters data provider for loading configured kubernetes clusters * and contexts in GitOps Clusters tree view. */ -export class ClusterDataProvider extends DataProvider { - protected nodes: ClusterNode[] = []; +export class ClusterDataProvider extends SimpleDataProvider { public getCurrentClusterNode(): ClusterNode | undefined { - return this.nodes.find(c => c.context.name === kubeConfig?.getCurrentContext()); + const nodes = this.nodes as ClusterNode[]; + return nodes.find(c => c.context.name === kubeConfig?.getCurrentContext()); } public redrawCurrentNode() { @@ -23,50 +19,18 @@ export class ClusterDataProvider extends DataProvider { } - public async getChildren(element?: TreeItem): Promise { - if(!element) { - return this.getRootNodes(); - } else if (element instanceof TreeNode) { - return element.children; - } - - return []; - } - - - /** - * Check if the cluster node exists or not. - */ - public includesTreeNode(treeItem: TreeItem, clusterNodes: TreeNode[] = this.nodes) { - for (const clusterNode of clusterNodes) { - if (treeItem === clusterNode) { - return true; - } - const includesInNested = this.includesTreeNode(treeItem, clusterNode.children); - if (includesInNested) { - return true; - } - } - return false; - } - - /** * Creates Clusters tree view items from local kubernetes config. */ async loadRootNodes() { - console.log('+ started loadClusterNodes'); - - const t1 = Date.now(); - - statusBar.startLoadingTree(); + const clusterNodes: ClusterNode[] = []; let currentContextTreeItem: ClusterNode | undefined; if (kubeConfig.getContexts().length === 0) { statusBar.stopLoadingTree(); - return; + return []; } for (const context of kubeConfig.getContexts()) { @@ -75,13 +39,12 @@ export class ClusterDataProvider extends DataProvider { currentContextTreeItem = clusterNode; clusterNode.makeCollapsible(); } - this.nodes.push(clusterNode); + clusterNodes.push(clusterNode); } statusBar.stopLoadingTree(); - const t2 = Date.now(); - console.log('+ loadClusterNodes ∆', t2 - t1); + return clusterNodes; } public updateCurrentContextChildNodes() { diff --git a/src/ui/treeviews/dataProviders/documentationDataProvider.ts b/src/ui/treeviews/dataProviders/documentationDataProvider.ts index 272127d7..7eefb437 100644 --- a/src/ui/treeviews/dataProviders/documentationDataProvider.ts +++ b/src/ui/treeviews/dataProviders/documentationDataProvider.ts @@ -1,13 +1,15 @@ -import { ContextId } from 'types/extensionIds'; import { documentationLinks } from '../documentationConfig'; import { DocumentationNode } from '../nodes/documentationNode'; -import { DataProvider } from './dataProvider'; -import { setVSCodeContext } from 'extension'; +import { SimpleDataProvider } from './simpleDataProvider'; /** * Defines data provider for Documentation tree view. */ -export class DocumentationDataProvider extends DataProvider { +export class DocumentationDataProvider extends SimpleDataProvider { + + protected async getRootNodes() { + return this.nodes; + } /** * Creates documentation tree view from documenation links config. @@ -25,6 +27,6 @@ export class DocumentationDataProvider extends DataProvider { treeNodes.push(treeNode); } - this.nodes = treeNodes; + return treeNodes; } } diff --git a/src/ui/treeviews/dataProviders/kubernetesObjectDataProvider.ts b/src/ui/treeviews/dataProviders/kubernetesObjectDataProvider.ts index 7aad3c5e..36a3ddd9 100644 --- a/src/ui/treeviews/dataProviders/kubernetesObjectDataProvider.ts +++ b/src/ui/treeviews/dataProviders/kubernetesObjectDataProvider.ts @@ -1,37 +1,17 @@ import { getNamespace } from 'cli/kubernetes/kubectlGetNamespace'; +import { currentContextData } from 'data/contextData'; import { GitRepository } from 'types/flux/gitRepository'; import { KubernetesObject } from 'types/kubernetes/kubernetesTypes'; import { groupNodesByNamespace, sortNodes } from 'utils/treeNodeUtils'; import { NamespaceNode } from '../nodes/namespaceNode'; import { GitRepositoryNode } from '../nodes/source/gitRepositoryNode'; import { TreeNode } from '../nodes/treeNode'; -import { DataProvider } from './dataProvider'; -import { ApiState, apiState } from 'cli/kubernetes/apiResources'; -import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; -import { clusterDataProvider } from '../treeViews'; +import { AsyncDataProvider } from './asyncDataProvider'; /** * Superclass for data providers that group objects by namespace: Source and Workload data providers */ -export abstract class KubernetesObjectDataProvider extends DataProvider { - - protected async getRootNodes(): Promise { - if(apiState === ApiState.Loading) { - return infoNodes(InfoNode.LoadingApi); - } - - if(apiState === ApiState.ClusterUnreachable) { - return infoNodes(InfoNode.ClusterUnreachable); - } - - // return empty array so that vscode welcome view with embedded link "Enable Gitops ..." is shown - if(clusterDataProvider.currentContextIsGitOpsNotEnabled()) { - return []; - } - - return super.getRootNodes(); - } - +export abstract class KubernetesObjectDataProvider extends AsyncDataProvider { public namespaceNodeTreeItems(): NamespaceNode[] { return (this.nodes?.filter(node => node instanceof NamespaceNode) as NamespaceNode[] || []); } @@ -49,8 +29,6 @@ export abstract class KubernetesObjectDataProvider extends DataProvider { } public async add(object: KubernetesObject) { - console.log('add', object); - if(!object.metadata?.namespace) { return; } @@ -100,8 +78,6 @@ export abstract class KubernetesObjectDataProvider extends DataProvider { } public delete(object: KubernetesObject) { - console.log('delete', object); - const namespaceNode = this.findParentNamespaceNode(object); if(!namespaceNode) { return; @@ -133,7 +109,8 @@ export abstract class KubernetesObjectDataProvider extends DataProvider { }); // rebuild top level nodes or the tree will not redraw - [this.nodes] = await groupNodesByNamespace(resourceNodes, true, true); + const viewData = this.viewData(currentContextData()); + [viewData.nodes] = await groupNodesByNamespace(resourceNodes, true, true); this.redraw(); } diff --git a/src/ui/treeviews/dataProviders/dataProvider.ts b/src/ui/treeviews/dataProviders/simpleDataProvider.ts similarity index 54% rename from src/ui/treeviews/dataProviders/dataProvider.ts rename to src/ui/treeviews/dataProviders/simpleDataProvider.ts index 690baf3c..8a567086 100644 --- a/src/ui/treeviews/dataProviders/dataProvider.ts +++ b/src/ui/treeviews/dataProviders/simpleDataProvider.ts @@ -1,35 +1,28 @@ -import { ApiState, apiState } from 'cli/kubernetes/apiResources'; +import { KubeConfigState, kubeConfigState } from 'cli/kubernetes/kubernetesConfig'; import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; -import { Event, EventEmitter, TreeDataProvider, TreeItem, TreeItemCollapsibleState } from 'vscode'; +import { Event, EventEmitter, TreeDataProvider, TreeItem } from 'vscode'; import { TreeNode } from '../nodes/treeNode'; -import { KubeConfigState, kubeConfigState } from 'cli/kubernetes/kubernetesConfig'; -/** +/**` * Defines tree view data provider base class for all GitOps tree views. */ -export class DataProvider implements TreeDataProvider { - protected nodes: TreeNode[] = []; - protected collapsibleStates = new Map(); +export class SimpleDataProvider implements TreeDataProvider { + private _nodes: TreeNode[] = []; + + get nodes() { + return this._nodes; + } + protected loading = false; protected _onDidChangeTreeData: EventEmitter = new EventEmitter(); readonly onDidChangeTreeData: Event = this._onDidChangeTreeData.event; - /* if treeItem is undefined, refresh all tree items */ - public async refresh(treeItem?: TreeItem) { - console.log(`## ${this.constructor.name} refresh`, treeItem ? treeItem : 'ALL'); - - if (!treeItem) { - this.reloadData(); - } - this.redraw(treeItem); - } /* if treeItem is undefined, redraw all tree items */ public redraw(treeItem?: TreeItem) { - this._onDidChangeTreeData.fire(treeItem); } @@ -54,6 +47,7 @@ export class DataProvider implements TreeDataProvider { return null; } + // this is called by vscode treeview redraw to get the nodes to display public async getChildren(element?: TreeItem): Promise { if(!element) { return this.getRootNodes(); @@ -64,7 +58,7 @@ export class DataProvider implements TreeDataProvider { return []; } - + // give nodes for vscode to render based on async data loading state protected async getRootNodes(): Promise { if (this.loading || kubeConfigState === KubeConfigState.Loading) { return infoNodes(InfoNode.Loading); @@ -76,55 +70,24 @@ export class DataProvider implements TreeDataProvider { return this.nodes; } - async reloadData() { - const t1 = Date.now(); - console.log(`# started ${this.constructor.name} reloadData`); + public async reload() { if(this.loading) { return; } this.loading = true; - this.saveCollapsibleStates(); - this.nodes = []; - await this.loadRootNodes(); - this.loadCollapsibleStates(); + this._nodes = []; + this._nodes = await this.loadRootNodes(); this.loading = false; - this.redraw(); - const t2 = Date.now(); - console.log(`# finished ${this.constructor.name} reloadData ∆`, t2 - t1); - } - - async loadRootNodes() { - this.nodes = []; - } - - saveCollapsibleStates() { - this.collapsibleStates.clear(); - - for(const node of this.nodes) { - const name = node.resource?.metadata?.name; - if(name) { - this.collapsibleStates.set(name, node.collapsibleState || TreeItemCollapsibleState.Collapsed); - } - } + this.redraw(); } - loadCollapsibleStates() { - for(const node of this.nodes) { - const name = node.resource?.metadata?.name; - if(name) { - const state = this.collapsibleStates.get(name); - if(state) { - node.collapsibleState = state; - } - } - } + async loadRootNodes(): Promise { + return []; } - - } diff --git a/src/ui/treeviews/dataProviders/sourceDataProvider.ts b/src/ui/treeviews/dataProviders/sourceDataProvider.ts index 8cfbf49a..54e8600c 100644 --- a/src/ui/treeviews/dataProviders/sourceDataProvider.ts +++ b/src/ui/treeviews/dataProviders/sourceDataProvider.ts @@ -1,7 +1,6 @@ import { getBuckets, getGitRepositories, getHelmRepositories, getOciRepositories } from 'cli/kubernetes/kubectlGet'; import { getNamespaces } from 'cli/kubernetes/kubectlGetNamespace'; -import { setVSCodeContext } from 'extension'; -import { ContextId } from 'types/extensionIds'; +import { ContextData } from 'data/contextData'; import { statusBar } from 'ui/statusBar'; import { sortByMetadataName } from 'utils/sortByMetadataName'; import { groupNodesByNamespace } from 'utils/treeNodeUtils'; @@ -18,6 +17,10 @@ import { KubernetesObjectDataProvider } from './kubernetesObjectDataProvider'; */ export class SourceDataProvider extends KubernetesObjectDataProvider { + protected viewData(contextData: ContextData) { + return contextData.viewData.source; + } + /** * Creates Source tree view items for the currently selected kubernetes cluster. */ @@ -56,6 +59,7 @@ export class SourceDataProvider extends KubernetesObjectDataProvider { statusBar.stopLoadingTree(); - [this.nodes] = await groupNodesByNamespace(sourceNodes, false, true); + const [groupedNodes] = await groupNodesByNamespace(sourceNodes, false, true); + return groupedNodes; } } diff --git a/src/ui/treeviews/dataProviders/templateDataProvider.ts b/src/ui/treeviews/dataProviders/templateDataProvider.ts index e704745c..37579b1f 100644 --- a/src/ui/treeviews/dataProviders/templateDataProvider.ts +++ b/src/ui/treeviews/dataProviders/templateDataProvider.ts @@ -1,9 +1,13 @@ import { getGitOpsTemplates } from 'cli/kubernetes/kubectlGet'; +import { ContextData } from 'data/contextData'; import { sortByMetadataName } from 'utils/sortByMetadataName'; import { GitOpsTemplateNode } from '../nodes/gitOpsTemplateNode'; -import { DataProvider } from './dataProvider'; +import { AsyncDataProvider } from './asyncDataProvider'; -export class TemplateDataProvider extends DataProvider { +export class TemplateDataProvider extends AsyncDataProvider { + protected viewData(contextData: ContextData) { + return contextData.viewData.template; + } async loadRootNodes() { const nodes = []; @@ -14,6 +18,6 @@ export class TemplateDataProvider extends DataProvider { nodes.push(new GitOpsTemplateNode(template)); } - this.nodes = nodes; + return nodes; } } diff --git a/src/ui/treeviews/dataProviders/workloadDataProvider.ts b/src/ui/treeviews/dataProviders/workloadDataProvider.ts index b59e08ab..b6c6bbba 100644 --- a/src/ui/treeviews/dataProviders/workloadDataProvider.ts +++ b/src/ui/treeviews/dataProviders/workloadDataProvider.ts @@ -1,24 +1,27 @@ import { fluxTools } from 'cli/flux/fluxTools'; import { getChildrenOfWorkload, getHelmReleases, getKustomizations } from 'cli/kubernetes/kubectlGet'; import { getNamespaces } from 'cli/kubernetes/kubectlGetNamespace'; -import { setVSCodeContext } from 'extension'; -import { ContextId } from 'types/extensionIds'; +import { ContextData } from 'data/contextData'; import { statusBar } from 'ui/statusBar'; +import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; import { sortByMetadataName } from 'utils/sortByMetadataName'; import { addFluxTreeToNode, groupNodesByNamespace } from 'utils/treeNodeUtils'; import { AnyResourceNode } from '../nodes/anyResourceNode'; -import { TreeNode, TreeNodeIcon } from '../nodes/treeNode'; +import { TreeNode } from '../nodes/treeNode'; import { HelmReleaseNode } from '../nodes/workload/helmReleaseNode'; import { KustomizationNode } from '../nodes/workload/kustomizationNode'; import { WorkloadNode } from '../nodes/workload/workloadNode'; import { KubernetesObjectDataProvider } from './kubernetesObjectDataProvider'; -import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; /**- * Defines data provider for loading Kustomizations * and Helm Releases in Workloads Tree View. */ export class WorkloadDataProvider extends KubernetesObjectDataProvider { + protected viewData(contextData: ContextData) { + return contextData.viewData.workload; + } + /** * Creates Workload tree nodes for the currently selected kubernetes cluster. */ @@ -49,7 +52,8 @@ export class WorkloadDataProvider extends KubernetesObjectDataProvider { statusBar.stopLoadingTree(); - [this.nodes] = await groupNodesByNamespace(workloadNodes, false, true); + const [groupedNodes] = await groupNodesByNamespace(workloadNodes, false, true); + return groupedNodes; } /** diff --git a/src/ui/treeviews/nodes/cluster/clusterNode.ts b/src/ui/treeviews/nodes/cluster/clusterNode.ts index e0a5cba1..19dd6f8e 100644 --- a/src/ui/treeviews/nodes/cluster/clusterNode.ts +++ b/src/ui/treeviews/nodes/cluster/clusterNode.ts @@ -4,19 +4,20 @@ import { ExtensionMode, MarkdownString } from 'vscode'; import { fluxVersion } from 'cli/checkVersions'; import { fluxTools } from 'cli/flux/fluxTools'; +import { ApiState } from 'cli/kubernetes/apiResources'; import { detectClusterProvider } from 'cli/kubernetes/clusterProvider'; import { getFluxControllers } from 'cli/kubernetes/kubectlGet'; import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { currentContextData } from 'data/contextData'; import { extensionContext, globalState, setVSCodeContext } from 'extension'; import { CommandId, ContextId } from 'types/extensionIds'; import { ClusterProvider } from 'types/kubernetes/clusterProvider'; import { NodeContext } from 'types/nodeContext'; import { clusterDataProvider, revealClusterNode } from 'ui/treeviews/treeViews'; +import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; import { createContextMarkdownTable, createMarkdownHr } from 'utils/markdownUtils'; -import { TreeNode, TreeNodeIcon } from '../treeNode'; +import { TreeNode } from '../treeNode'; import { ClusterDeploymentNode } from './clusterDeploymentNode'; -import { ApiState, apiState } from 'cli/kubernetes/apiResources'; -import { InfoNode, infoNodes } from 'utils/makeTreeviewInfoNode'; /** * Defines Cluster context tree view item for displaying @@ -98,11 +99,16 @@ export class ClusterNode extends TreeNode { } private async updateControllersNodes() { - if(apiState === ApiState.ClusterUnreachable) { + const contextData = currentContextData(); + if(contextData.contextName !== this.context.name) { + return; + } + + if(contextData.apiState === ApiState.ClusterUnreachable) { this.children = infoNodes(InfoNode.ClusterUnreachable); return; } - if(apiState === ApiState.Loading) { + if(contextData.apiState === ApiState.Loading) { this.children = infoNodes(InfoNode.LoadingApi); return; } @@ -132,7 +138,12 @@ export class ClusterNode extends TreeNode { * Get status from running flux commands instead of kubectl. */ private async updateControllersStatus() { - if (this.children.length === 0 || apiState === ApiState.ClusterUnreachable) { + const contextData = currentContextData(); + if(contextData.contextName !== this.context.name) { + return; + } + + if (this.children.length === 0 || contextData.apiState === ApiState.ClusterUnreachable) { return; } const fluxCheckResult = await fluxTools.check(this.context.name); diff --git a/src/ui/treeviews/nodes/namespaceNode.ts b/src/ui/treeviews/nodes/namespaceNode.ts index a2fbf1eb..e17701b2 100644 --- a/src/ui/treeviews/nodes/namespaceNode.ts +++ b/src/ui/treeviews/nodes/namespaceNode.ts @@ -1,9 +1,8 @@ import { Kind, Namespace } from 'types/kubernetes/kubernetesTypes'; -import { TreeNode, TreeNodeIcon } from './treeNode'; import { TreeItemCollapsibleState } from 'vscode'; import { SourceNode } from './source/sourceNode'; +import { TreeNode, TreeNodeIcon } from './treeNode'; import { WorkloadNode } from './workload/workloadNode'; -import { read } from 'fs'; /** * Defines any kubernetes resourse. diff --git a/src/ui/treeviews/treeViews.ts b/src/ui/treeviews/treeViews.ts index 9fd8fd07..7374cb12 100644 --- a/src/ui/treeviews/treeViews.ts +++ b/src/ui/treeviews/treeViews.ts @@ -1,15 +1,14 @@ -import { TreeItem, TreeItemCollapsibleState, TreeView, commands, window } from 'vscode'; +import { TreeItem, TreeItemCollapsibleState, TreeView, window } from 'vscode'; import { isAzureProvider } from 'cli/azure/azureTools'; -import { globalState, setVSCodeContext } from 'extension'; +import { globalState } from 'extension'; import { Errorable } from 'types/errorable'; -import { CommandId, ContextId, TreeViewId } from 'types/extensionIds'; +import { TreeViewId } from 'types/extensionIds'; import { ClusterDataProvider } from './dataProviders/clusterDataProvider'; import { DocumentationDataProvider } from './dataProviders/documentationDataProvider'; import { SourceDataProvider } from './dataProviders/sourceDataProvider'; import { WorkloadDataProvider } from './dataProviders/workloadDataProvider'; import { ClusterNode } from './nodes/cluster/clusterNode'; -import { TreeNode } from './nodes/treeNode'; import { detectClusterProvider } from 'cli/kubernetes/clusterProvider'; import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; @@ -64,7 +63,7 @@ export function createTreeViews() { showCollapseAll: true, }); - documentationDataProvider.refresh(); + documentationDataProvider.reload(); } function listenCollapsableState() { @@ -72,7 +71,7 @@ function listenCollapsableState() { if (e.element instanceof NamespaceNode) { e.element.collapsibleState = TreeItemCollapsibleState.Collapsed; e.element.updateLabel(); - sourceDataProvider.refresh(e.element); + sourceDataProvider.redraw(e.element); } }); @@ -80,7 +79,7 @@ function listenCollapsableState() { if (e.element instanceof NamespaceNode) { e.element.collapsibleState = TreeItemCollapsibleState.Expanded; e.element.updateLabel(); - sourceDataProvider.refresh(e.element); + sourceDataProvider.redraw(e.element); } }); @@ -89,7 +88,7 @@ function listenCollapsableState() { if (e.element instanceof NamespaceNode) { e.element.collapsibleState = TreeItemCollapsibleState.Collapsed; e.element.updateLabel(); - workloadDataProvider.refresh(e.element); + workloadDataProvider.redraw(e.element); } }); @@ -97,7 +96,7 @@ function listenCollapsableState() { if (e.element instanceof NamespaceNode) { e.element.collapsibleState = TreeItemCollapsibleState.Expanded; e.element.updateLabel(); - workloadDataProvider.refresh(e.element); + workloadDataProvider.redraw(e.element); } }); } @@ -107,36 +106,29 @@ function listenCollapsableState() { * When an argument is passed - only that tree item * and its children are updated. */ -export function refreshClustersTreeView(node?: TreeNode) { - if (node && !clusterDataProvider.includesTreeNode(node)) { - // Trying to refresh old (non-existent) cluster context node - return; - } - clusterDataProvider.refresh(node); +export function reloadClustersTreeView() { + clusterDataProvider.reload(); } /** * Reloads sources tree view for the selected cluster. */ -export function refreshSourcesTreeView(node?: TreeNode) { - // const g = await commands.executeCommand('getContext', ContextId.CurrentClusterGitOpsNotEnabled); - // const u = await commands.executeCommand('getContext', ContextId.ClusterUnreachable); - - sourceDataProvider.refresh(node); +export function reloadSourcesTreeView() { + sourceDataProvider.reload(); } /** * Reloads workloads tree view for the selected cluster. */ -export function refreshWorkloadsTreeView(node?: TreeNode) { - workloadDataProvider.refresh(node); +export function reloadWorkloadsTreeView() { + workloadDataProvider.reload(); } /** * Reloads workloads tree view for the selected cluster. */ -export function refreshTemplatesTreeView(node?: TreeNode) { - templateDateProvider.refresh(node); +export function reloadTemplatesTreeView() { + templateDateProvider.reload(); } /** diff --git a/src/ui/webviews/configureGitOps/lib/createAzure.ts b/src/ui/webviews/configureGitOps/lib/createAzure.ts index 5ac9e233..35bd8405 100644 --- a/src/ui/webviews/configureGitOps/lib/createAzure.ts +++ b/src/ui/webviews/configureGitOps/lib/createAzure.ts @@ -1,13 +1,13 @@ -import { AzureClusterProvider, azureTools, CreateSourceBucketAzureArgs, CreateSourceGitAzureArgs } from 'cli/azure/azureTools'; +import { AzureClusterProvider, azureTools, CreateSourceGitAzureArgs } from 'cli/azure/azureTools'; +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; import { showDeployKeyNotificationIfNeeded } from 'commands/createSource'; import { telemetry } from 'extension'; +import { ClusterInfo } from 'types/kubernetes/clusterProvider'; import { Kind } from 'types/kubernetes/kubernetesTypes'; import { TelemetryEvent } from 'types/telemetryEventNames'; -import { ParamsDictionary } from 'utils/typeUtils'; -import { refreshSourcesTreeView, refreshWorkloadsTreeView } from 'ui/treeviews/treeViews'; -import { ClusterInfo } from 'types/kubernetes/clusterProvider'; -import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; +import { reloadSourcesTreeView, reloadWorkloadsTreeView } from 'ui/treeviews/treeViews'; import { splitNamespacedFluxObject } from 'utils/namespacedFluxObject'; +import { ParamsDictionary } from 'utils/typeUtils'; export async function createConfigurationAzure(data: ParamsDictionary) { const clusterInfo = data.clusterInfo as ClusterInfo; @@ -51,8 +51,8 @@ async function createGitSourceAzure(source: ParamsDictionary, kustomization: Par setTimeout(() => { // Wait a bit for the repository to have a failed state in case of SSH url - refreshSourcesTreeView(); - refreshWorkloadsTreeView(); + reloadSourcesTreeView(); + reloadWorkloadsTreeView(); }, 1000); showDeployKeyNotificationIfNeeded(args.url, deployKey); @@ -83,8 +83,8 @@ async function createBucketSourceAzure(source: ParamsDictionary, kustomization: await azureTools.createSourceBucket(args); setTimeout(() => { - refreshSourcesTreeView(); - refreshWorkloadsTreeView(); + reloadSourcesTreeView(); + reloadWorkloadsTreeView(); }, 1000); } diff --git a/src/ui/webviews/configureGitOps/lib/createGeneric.ts b/src/ui/webviews/configureGitOps/lib/createGeneric.ts index 7d219f7b..85a06e39 100644 --- a/src/ui/webviews/configureGitOps/lib/createGeneric.ts +++ b/src/ui/webviews/configureGitOps/lib/createGeneric.ts @@ -1,10 +1,10 @@ +import { fluxTools } from 'cli/flux/fluxTools'; import { showDeployKeyNotificationIfNeeded } from 'commands/createSource'; +import { refreshAllTreeViewsCommand } from 'commands/refreshTreeViews'; import { telemetry } from 'extension'; -import { fluxTools } from 'cli/flux/fluxTools'; import { TelemetryEvent } from 'types/telemetryEventNames'; +import { reloadSourcesTreeView } from 'ui/treeviews/treeViews'; import { ParamsDictionary } from 'utils/typeUtils'; -import { refreshSourcesTreeView } from 'ui/treeviews/treeViews'; -import { refreshAllTreeViewsCommand } from 'commands/refreshTreeViews'; export async function createConfigurationGeneric(data: ParamsDictionary) { telemetry.send(TelemetryEvent.CreateSource, { @@ -17,7 +17,7 @@ export async function createConfigurationGeneric(data: ParamsDictionary) { showDeployKeyNotificationIfNeeded(data.source.url, deployKey); setTimeout(() => { // Wait a bit for the repository to have a failed state in case of SSH url - refreshSourcesTreeView(); + reloadSourcesTreeView(); }, 1000); } diff --git a/src/ui/webviews/createFromTemplate/receiveMessage.ts b/src/ui/webviews/createFromTemplate/receiveMessage.ts index 94d4ae7b..cca11a9b 100644 --- a/src/ui/webviews/createFromTemplate/receiveMessage.ts +++ b/src/ui/webviews/createFromTemplate/receiveMessage.ts @@ -5,8 +5,6 @@ import { Uri, WebviewPanel, workspace } from 'vscode'; export async function receiveMessage(message: any, panel: WebviewPanel) { switch (message.action) { case 'show-yaml': - // actionYAML(message.data); - console.log(message.data); const data = message.data; renderTemplates(data.template, data.values); diff --git a/src/utils/kubeConfigCompare.ts b/src/utils/kubeConfigCompare.ts index ddfc1d9b..67b8e84d 100644 --- a/src/utils/kubeConfigCompare.ts +++ b/src/utils/kubeConfigCompare.ts @@ -1,5 +1,5 @@ -import deepEqual from 'lite-deep-equal'; import * as k8s from '@kubernetes/client-node'; +import deepEqual from 'lite-deep-equal'; export function kcTextChanged(kc1: k8s.KubeConfig, kc2: k8s.KubeConfig): boolean { // exportConfig() will omit tokens and certs diff --git a/src/utils/makeTreeviewInfoNode.ts b/src/utils/makeTreeviewInfoNode.ts index ad802f7e..1e71a9b7 100644 --- a/src/utils/makeTreeviewInfoNode.ts +++ b/src/utils/makeTreeviewInfoNode.ts @@ -1,3 +1,4 @@ +import { kubeConfig } from 'cli/kubernetes/kubernetesConfig'; import { TreeNode, TreeNodeIcon } from '../ui/treeviews/nodes/treeNode'; @@ -28,7 +29,8 @@ export function infoNode(type: InfoNode) { case InfoNode.LoadingApi: return new TreeNode('Loading API...'); case InfoNode.ClusterUnreachable: - node = new TreeNode('Cluster unreachable'); + const name = kubeConfig.currentContext; + node = new TreeNode(`Cluster ${name} unreachable`); node.setIcon(TreeNodeIcon.Disconnected); return node; } diff --git a/tslint-imports.json b/tslint-imports.json new file mode 100644 index 00000000..7929f6ec --- /dev/null +++ b/tslint-imports.json @@ -0,0 +1,11 @@ +{ + "extends": [ + "tslint-etc" + ], + "rules": { + "no-unused-declaration": true + } +} +// https://wesleygrimes.com/angular/2019/02/14/how-to-use-tslint-to-autoremove-all-unused-imports-in-a-typescript-project +// npm install -g typescript tslint tslint-etc +// tslint --config tslint-imports.json --fix --project . \ No newline at end of file