Skip to content

Commit

Permalink
Refactoring: encapsulate configuration access (vscode-kubernetes-tool…
Browse files Browse the repository at this point in the history
  • Loading branch information
itowlson authored Oct 3, 2018
1 parent e06ec72 commit 8f70633
Show file tree
Hide file tree
Showing 14 changed files with 96 additions and 51 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ Minikube tools to be installed and available on your PATH.
* `vs-kubernetes.kubectl-path` - File path to the kubectl binary. Note this is the binary file itself, not just the directory containing the file. On Windows, this must contain the `.exe` extension.
* `vs-kubernetes.helm-path` - File path to the helm binary. Note this is the binary file itself, not just the directory containing the file. On Windows, this must contain the `.exe` extension.
* `vs-kubernetes.draft-path` - File path to the draft binary. Note this is the binary file itself, not just the directory containing the file. On Windows, this must contain the `.exe` extension (note current versions of Draft are not supported on Windows).
* `vs-kubernetes.minikube-path` - File path to the minikube binary. Note this is the binary file itself, not just the directory containing the file. On Windows, this must contain the `.exe` extension.
* `vs-kubernetes.kubeconfig` - File path to the kubeconfig file you want to use. This overrides both the default kubeconfig and the KUBECONFIG environment variable.
* `vs-kubernetes.knownKubeconfigs` - An array of file paths of kubeconfig files that you want to be able to quickly switch between using the Set Kubeconfig command.
* `vs-kubernetes.autoCleanupOnDebugTerminate` - The flag to control whether to auto cleanup the created deployment and associated pod by the command "Kubernetes: Debug (Launch)". The cleanup action occurs when it failed to start debug session or debug session terminated. If not specified, the extension will prompt for whether to clean up or not. You might choose not to clean up if you wanted to view pod logs, etc.
Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,10 @@
"type": "string",
"description": "File path to a draft binary."
},
"vs-kubernetes.minikube-path": {
"type": "string",
"description": "File path to a minikube binary."
},
"vs-kubernetes.kubeconfig": {
"type": "string",
"description": "File path to the kubeconfig file."
Expand All @@ -134,6 +138,7 @@
"vs-kubernetes.kubectl-path": "",
"vs-kubernetes.helm-path": "",
"vs-kubernetes.draft-path": "",
"vs-kubernetes.minikube-path": "",
"vs-kubernetes.outputFormat": "yaml",
"vs-kubernetes.kubeconfig": "",
"vs-kubernetes.knownKubeconfigs": [],
Expand Down
7 changes: 4 additions & 3 deletions src/components/clusterprovider/minikube/minikube.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { FS } from '../../../fs';
import * as binutil from '../../../binutil';
import { Errorable } from '../../../errorable';
import { fromShellExitCodeOnly, Diagnostic } from '../../../wizard';
import { getToolPath } from '../../config/config';

export class MinikubeInfo {
readonly running: boolean;
Expand Down Expand Up @@ -182,9 +183,9 @@ async function checkPresent(context: Context, mode: CheckPresentMode): Promise<b

async function checkForMinikubeInternal(context: Context, mode: CheckPresentMode): Promise<boolean> {
const binName = 'minikube';
const bin = context.host.getConfiguration('vs-kubernetes')[`vs-kubernetes.${binName}-path`];
const bin = getToolPath(context.host, binName);

const inferFailedMessage = 'Could not find "minikube" binary.';
const configuredFileMissingMessage = bin + ' does not exist!';
const inferFailedMessage = `Could not find "${binName}" binary.`;
const configuredFileMissingMessage = `${bin} does not exist!`;
return binutil.checkForBinary(context, bin, binName, inferFailedMessage, configuredFileMissingMessage, mode === CheckPresentMode.Alert);
}
62 changes: 53 additions & 9 deletions src/components/config/config.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import * as vscode from 'vscode';
import { Host } from '../../host';

const EXTENSION_CONFIG_KEY = "vs-kubernetes";
const KUBECONFIG_PATH_KEY = "vs-kubernetes.kubeconfig";
const KNOWN_KUBECONFIGS_KEY = "vs-kubernetes.knownKubeconfigs";

export async function addPathToConfig(configKey: string, value: string): Promise<void> {
const config = vscode.workspace.getConfiguration().inspect(EXTENSION_CONFIG_KEY);
await addPathToConfigAtScope(configKey, value, vscode.ConfigurationTarget.Global, config.globalValue, true);
await addPathToConfigAtScope(configKey, value, vscode.ConfigurationTarget.Workspace, config.workspaceValue, false);
await addPathToConfigAtScope(configKey, value, vscode.ConfigurationTarget.WorkspaceFolder, config.workspaceFolderValue, false);
await setConfigValue(configKey, value);
}

async function setConfigValue(configKey: string, value: any): Promise<void> {
await atAllConfigScopes(addValueToConfigAtScope, configKey, value);
}

async function addPathToConfigAtScope(configKey: string, value: string, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean): Promise<void> {
async function addValueToConfigAtScope(configKey: string, value: any, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean): Promise<void> {
if (!createIfNotExist) {
if (!valueAtScope || !(valueAtScope[configKey])) {
return;
Expand All @@ -27,10 +29,7 @@ async function addPathToConfigAtScope(configKey: string, value: string, scope: v
}

async function addValueToConfigArray(configKey: string, value: string): Promise<void> {
const config = vscode.workspace.getConfiguration().inspect(EXTENSION_CONFIG_KEY);
await addValueToConfigArrayAtScope(configKey, value, vscode.ConfigurationTarget.Global, config.globalValue, true);
await addValueToConfigArrayAtScope(configKey, value, vscode.ConfigurationTarget.Workspace, config.workspaceValue, false);
await addValueToConfigArrayAtScope(configKey, value, vscode.ConfigurationTarget.WorkspaceFolder, config.workspaceFolderValue, false);
await atAllConfigScopes(addValueToConfigArrayAtScope, configKey, value);
}

async function addValueToConfigArrayAtScope(configKey: string, value: string, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean): Promise<void> {
Expand All @@ -50,6 +49,15 @@ async function addValueToConfigArrayAtScope(configKey: string, value: string, sc
await vscode.workspace.getConfiguration().update(EXTENSION_CONFIG_KEY, newValue, scope);
}

type ConfigUpdater<T> = (configKey: string, value: T, scope: vscode.ConfigurationTarget, valueAtScope: any, createIfNotExist: boolean) => Promise<void>;

async function atAllConfigScopes<T>(fn: ConfigUpdater<T>, configKey: string, value: T): Promise<void> {
const config = vscode.workspace.getConfiguration().inspect(EXTENSION_CONFIG_KEY);
await fn(configKey, value, vscode.ConfigurationTarget.Global, config.globalValue, true);
await fn(configKey, value, vscode.ConfigurationTarget.Workspace, config.workspaceValue, false);
await fn(configKey, value, vscode.ConfigurationTarget.WorkspaceFolder, config.workspaceFolderValue, false);
}

// Functions for working with the list of known kubeconfigs

export function getKnownKubeconfigs(): string[] {
Expand All @@ -73,3 +81,39 @@ export async function setActiveKubeconfig(kubeconfig: string): Promise<void> {
export function getActiveKubeconfig(): string {
return vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)[KUBECONFIG_PATH_KEY];
}

// Functions for working with tool paths

export function getToolPath(host: Host, tool: string): string | undefined {
return host.getConfiguration(EXTENSION_CONFIG_KEY)[toolPathKey(tool)];
}

export function toolPathKey(tool: string) {
return `vs-kubernetes.${tool}-path`;
}

// Auto cleanup on debug terminate

const AUTO_CLEANUP_DEBUG_KEY = "vs-kubernetes.autoCleanupOnDebugTerminate";

export function getAutoCompleteOnDebugTerminate(): boolean {
return vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)[AUTO_CLEANUP_DEBUG_KEY];
}

export async function setAlwaysCleanUp(): Promise<void> {
await setConfigValue(AUTO_CLEANUP_DEBUG_KEY, true);
}

// Other bits and bobs

export function getOutputFormat(): string {
return vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)['vs-kubernetes.outputFormat'];
}

export function getConfiguredNamespace(): string | undefined {
return vscode.workspace.getConfiguration(EXTENSION_CONFIG_KEY)['vs-kubernetes.namespace'];
}

export function affectsUs(change: vscode.ConfigurationChangeEvent) {
return change.affectsConfiguration(EXTENSION_CONFIG_KEY);
}
8 changes: 4 additions & 4 deletions src/components/installer/installer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as tar from 'tar';
import * as vscode from 'vscode';
import { Shell, Platform } from '../../shell';
import { Errorable, failed, succeeded } from '../../errorable';
import { addPathToConfig } from '../config/config';
import { addPathToConfig, toolPathKey } from '../config/config';

export async function installKubectl(shell: Shell): Promise<Errorable<void>> {
const tool = 'kubectl';
Expand All @@ -34,7 +34,7 @@ export async function installKubectl(shell: Shell): Promise<Errorable<void>> {
fs.chmodSync(downloadFile, '0777');
}

await addPathToConfig(`vs-kubernetes.${tool}-path`, downloadFile);
await addPathToConfig(toolPathKey(tool), downloadFile);
return { succeeded: true, result: null };
}

Expand Down Expand Up @@ -79,7 +79,7 @@ export async function installMinikube(shell: Shell): Promise<Errorable<void>> {
if (shell.isUnix()) {
await shell.exec(`chmod +x ${executableFullPath}`);
}
const configKey = `vs-kubernetes.${tool}-path`;
const configKey = toolPathKey(tool);
await addPathToConfig(configKey, executableFullPath);

return { succeeded: true, result: null };
Expand All @@ -93,7 +93,7 @@ async function installToolFromTar(tool: string, urlTemplate: string, shell: Shel
const installFolder = getInstallFolder(shell, tool);
const executable = formatBin(tool, shell.platform());
const url = urlTemplate.replace('{os_placeholder}', os);
const configKey = `vs-kubernetes.${tool}-path`;
const configKey = toolPathKey(tool);
return installFromTar(url, installFolder, executable, configKey);
}

Expand Down
20 changes: 3 additions & 17 deletions src/debug/debugSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { shell } from "../shell";
import { DockerfileParser } from "../docker/dockerfileParser";
import * as dockerUtils from "../docker/dockerUtils";
import { isPod, Pod, KubernetesCollection, Container } from "../kuberesources.objectmodel";
import * as config from "../components/config/config";

const debugCommandDocumentationUrl = "https://github.com/Azure/vscode-kubernetes-tools/blob/master/debug-on-kubernetes.md";

Expand Down Expand Up @@ -289,30 +290,15 @@ export class DebugSession implements IDebugSession {
}

private async promptForCleanup(resourceId: string): Promise<void> {
const autoCleanupFlag = vscode.workspace.getConfiguration("vs-kubernetes")["vs-kubernetes.autoCleanupOnDebugTerminate"];
const autoCleanupFlag = config.getAutoCompleteOnDebugTerminate();
if (autoCleanupFlag) {
return await this.cleanupResource(resourceId);
}
const answer = await vscode.window.showWarningMessage(`Resource ${resourceId} will continue running on the cluster.`, "Clean Up", "Always Clean Up");
if (answer === "Clean Up") {
return await this.cleanupResource(resourceId);
} else if (answer === "Always Clean Up") {
// The object returned by VS Code API is readonly, clone it first.
const oldConfig = vscode.workspace.getConfiguration().inspect("vs-kubernetes");
// Always update global config.
const globalConfig = <any> Object.assign({}, oldConfig.globalValue);
globalConfig["vs-kubernetes.autoCleanupOnDebugTerminate"] = true;
await vscode.workspace.getConfiguration().update("vs-kubernetes", globalConfig, vscode.ConfigurationTarget.Global);
// If workspace folder value exists, update it.
if (oldConfig.workspaceFolderValue && oldConfig.workspaceFolderValue["vs-kubernetes.autoCleanupOnDebugTerminate"] === false) {
const workspaceFolderConfig = <any> Object.assign({}, oldConfig.workspaceFolderValue);
workspaceFolderConfig["vs-kubernetes.autoCleanupOnDebugTerminate"] = true;
await vscode.workspace.getConfiguration().update("vs-kubernetes", workspaceFolderConfig, vscode.ConfigurationTarget.WorkspaceFolder);
} else if (oldConfig.workspaceValue && oldConfig.workspaceValue["vs-kubernetes.autoCleanupOnDebugTerminate"] === false) { // if workspace value exists, update it.
const workspaceConfig = <any> Object.assign({}, oldConfig.workspaceValue);
workspaceConfig["vs-kubernetes.autoCleanupOnDebugTerminate"] = true;
await vscode.workspace.getConfiguration().update("vs-kubernetes", workspaceConfig, vscode.ConfigurationTarget.Workspace);
}
await config.setAlwaysCleanUp();
return await this.cleanupResource(resourceId);
}
}
Expand Down
7 changes: 4 additions & 3 deletions src/draft/draft.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { FS } from '../fs';
import * as syspath from 'path';
import * as binutil from '../binutil';
import { Errorable } from '../errorable';
import { getToolPath } from '../components/config/config';

export interface Draft {
checkPresent(mode: CheckPresentMode): Promise<boolean>;
Expand Down Expand Up @@ -144,10 +145,10 @@ async function version(context: Context): Promise<Errorable<string>> {

async function checkForDraftInternal(context: Context, mode: CheckPresentMode): Promise<boolean> {
const binName = 'draft';
const bin = context.host.getConfiguration('vs-kubernetes')[`vs-kubernetes.${binName}-path`];
const bin = getToolPath(context.host, binName);

const inferFailedMessage = 'Could not find "draft" binary.';
const configuredFileMissingMessage = bin + ' does not exist!';
const inferFailedMessage = `Could not find "${binName}" binary.`;
const configuredFileMissingMessage = `${bin} does not exist!`;
return binutil.checkForBinary(context, bin, binName, inferFailedMessage, configuredFileMissingMessage, mode === CheckPresentMode.Alert);
}

Expand Down
3 changes: 2 additions & 1 deletion src/explorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as kuberesources from './kuberesources';
import { failed } from './errorable';
import * as helmexec from './helm.exec';
import { K8S_RESOURCE_SCHEME, KUBECTL_RESOURCE_AUTHORITY, kubefsUri } from './kuberesources.virtualfs';
import { affectsUs } from './components/config/config';

const KUBERNETES_CLUSTER = "vsKubernetes.cluster";
const MINIKUBE_CLUSTER = "vsKubernetes.minikubeCluster";
Expand Down Expand Up @@ -64,7 +65,7 @@ export class KubernetesExplorer implements vscode.TreeDataProvider<KubernetesObj

constructor(private readonly kubectl: Kubectl, private readonly host: Host) {
host.onDidChangeConfiguration((change) => {
if (change.affectsConfiguration('vs-kubernetes')) {
if (affectsUs(change)) {
this.refresh();
}
});
Expand Down
4 changes: 2 additions & 2 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,7 @@ function maybeRunKubernetesCommandForActiveWindow(command: string, progressMessa
return false; // No open text editor
}

const namespace = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.namespace'];
const namespace = config.getConfiguredNamespace();
if (namespace) {
command = `${command} --namespace ${namespace} `;
}
Expand Down Expand Up @@ -677,7 +677,7 @@ function loadKubernetes(explorerNode?: explorer.ResourceNode) {
}

function loadKubernetesCore(namespace: string | null, value: string) {
const outputFormat = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.outputFormat'];
const outputFormat = config.getOutputFormat();
const uri = kubefsUri(namespace, value, outputFormat);
vscode.workspace.openTextDocument(uri).then((doc) => {
if (doc) {
Expand Down
10 changes: 6 additions & 4 deletions src/helm.exec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import { parseLineOutput } from './outputUtils';
import { sleep } from './sleep';
import { currentNamespace } from './kubectlUtils';
import { Kubectl } from './kubectl';
import { getToolPath } from './components/config/config';
import { host } from './host';

export interface PickChartUIOptions {
readonly warnIfNoCharts: boolean;
Expand Down Expand Up @@ -472,7 +474,7 @@ export function helmExec(args: string, fn) {
if (!ensureHelm(EnsureMode.Alert)) {
return;
}
const configuredBin: string | undefined = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.helm-path'];
const configuredBin: string | undefined = getToolPath(host, 'helm');
const bin = configuredBin ? `"${configuredBin}"` : "helm";
const cmd = `${bin} ${args}`;
shell.exec(cmd, fn);
Expand All @@ -483,7 +485,7 @@ export async function helmExecAsync(args: string): Promise<ShellResult> {
if (!ensureHelm(EnsureMode.Alert)) {
return { code: -1, stdout: "", stderr: "" };
}
const configuredBin: string | undefined = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.helm-path'];
const configuredBin: string | undefined = getToolPath(host, 'helm');
const bin = configuredBin ? `"${configuredBin}"` : "helm";
const cmd = `${bin} ${args}`;
return await sh.exec(cmd);
Expand Down Expand Up @@ -529,7 +531,7 @@ export async function helmListAll(namespace?: string): Promise<Errorable<{ [key:
}

export function ensureHelm(mode: EnsureMode) {
const configuredBin: string | undefined = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.helm-path'];
const configuredBin: string | undefined = getToolPath(host, 'helm');
if (configuredBin) {
if (fs.existsSync(configuredBin)) {
return true;
Expand Down Expand Up @@ -628,7 +630,7 @@ export function helmHome(): string {
}

export async function helmServe(): Promise<vscode.Disposable> {
const configuredBin: string | undefined = vscode.workspace.getConfiguration('vs-kubernetes')['vs-kubernetes.helm-path'];
const configuredBin: string | undefined = getToolPath(host, 'helm');
const bin = sh.unquotedPath(configuredBin ? `"${configuredBin}"` : "helm");
const process = spawn(bin, [ "serve" ]);
let ready = false;
Expand Down
3 changes: 2 additions & 1 deletion src/helm.repoExplorer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import * as helm from './helm.exec';
import { HELM_OUTPUT_COLUMN_SEPARATOR } from './helm';
import { Errorable, failed } from './errorable';
import { parseLineOutput } from './outputUtils';
import { affectsUs } from './components/config/config';

export function create(host: Host): HelmRepoExplorer {
return new HelmRepoExplorer(host);
Expand Down Expand Up @@ -56,7 +57,7 @@ export class HelmRepoExplorer implements vscode.TreeDataProvider<HelmObject> {

constructor(private readonly host: Host) {
host.onDidChangeConfiguration((change) => {
if (change.affectsConfiguration('vs-kubernetes')) {
if (affectsUs(change)) {
this.refresh();
}
});
Expand Down
Loading

0 comments on commit 8f70633

Please sign in to comment.