-
Notifications
You must be signed in to change notification settings - Fork 251
/
kubeconfig.ts
133 lines (113 loc) · 5.18 KB
/
kubeconfig.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import * as vscode from 'vscode';
import { fs } from '../../fs';
import * as path from 'path';
import * as yaml from 'js-yaml';
import * as shelljs from 'shelljs';
import { refreshExplorer } from '../clusterprovider/common/explorer';
import { getActiveKubeconfig, getUseWsl } from '../config/config';
import * as kubernetes from '@kubernetes/client-node';
import { mkdirpAsync } from '../../utils/mkdirp';
interface Named {
readonly name: string;
}
export async function loadKubeconfig(): Promise<kubernetes.KubeConfig> {
const kubeconfig = new kubernetes.KubeConfig();
const kubeconfigPath = getKubeconfigPath();
if (kubeconfigPath.pathType === 'host') {
kubeconfig.loadFromFile(kubeconfigPath.hostPath);
} else if (kubeconfigPath.pathType === 'wsl') {
const result = shelljs.exec(`wsl.exe sh -c "cat ${kubeconfigPath.wslPath}"`, { silent: true }) as shelljs.ExecOutputReturnValue;
if (!result) {
throw new Error(`Impossible to retrieve the kubeconfig content from WSL at path '${kubeconfigPath.wslPath}'. No result from the shelljs.exe call.`);
}
if (result.code !== 0) {
throw new Error(`Impossible to retrieve the kubeconfig content from WSL at path '${kubeconfigPath.wslPath}. Error code: ${result.code}. Error output: ${result.stderr.trim()}`);
}
kubeconfig.loadFromString(result.stdout.trim());
} else {
throw new Error(`Kubeconfig path type is not recognized.`);
}
return kubeconfig;
}
export function getKubeconfigPath(): { readonly pathType: 'host'; readonly hostPath: string; } | { readonly pathType: 'wsl'; readonly wslPath: string; } {
// If the user specified a kubeconfig path -WSL or not-, let's use it.
let kubeconfigPath: string | undefined = getActiveKubeconfig();
if (getUseWsl()) {
if (!kubeconfigPath) {
// User is using WSL: we want to use the same default that kubectl uses on Linux ($KUBECONFIG or home directory).
const result = shelljs.exec('wsl.exe sh -c "${KUBECONFIG:-$HOME/.kube/config}"', { silent: true }) as shelljs.ExecOutputReturnValue;
if (!result) {
throw new Error(`Impossible to retrieve the kubeconfig path from WSL. No result from the shelljs.exe call.`);
}
if (result.code !== 0) {
throw new Error(`Impossible to retrieve the kubeconfig path from WSL. Error code: ${result.code}. Error output: ${result.stderr.trim()}`);
}
kubeconfigPath = result.stdout.trim();
}
return {
pathType: 'wsl',
wslPath: kubeconfigPath
};
}
if (!kubeconfigPath) {
kubeconfigPath = process.env['KUBECONFIG'];
}
if (!kubeconfigPath) {
// Fall back on the default kubeconfig value.
kubeconfigPath = path.join((process.env['HOME'] || process.env['USERPROFILE'] || '.'), ".kube", "config");
}
return {
pathType: 'host',
hostPath: kubeconfigPath
};
}
export async function mergeToKubeconfig(newConfigText: string): Promise<void> {
const kubeconfigPath = getKubeconfigPath();
if (kubeconfigPath.pathType === 'wsl') {
vscode.window.showErrorMessage("You are on Windows, but are using WSL-based tools. We can't merge into your WSL kubeconfig. Consider running VS Code in WSL using the Remote Extensions.");
return;
}
const kcfile = kubeconfigPath.hostPath;
const kcfileExists = await fs.existsAsync(kcfile);
const kubeconfigText = kcfileExists ? await fs.readTextFile(kcfile) : '';
const kubeconfig = yaml.safeLoad(kubeconfigText) || {};
const newConfig = yaml.safeLoad(newConfigText);
for (const section of ['clusters', 'contexts', 'users']) {
const existing: Named[] | undefined = kubeconfig[section];
const toMerge: Named[] | undefined = newConfig[section];
if (!toMerge) {
continue;
}
if (!existing) {
kubeconfig[section] = toMerge;
continue;
}
await mergeInto(existing, toMerge);
}
if (!kcfileExists && newConfig.contexts && newConfig.contexts[0]) {
kubeconfig['current-context'] = newConfig.contexts[0].name;
}
const merged = yaml.safeDump(kubeconfig, { lineWidth: 1000000, noArrayIndent: true });
if (kcfileExists) {
const backupFile = kcfile + '.vscode-k8s-tools-backup';
if (await fs.existsAsync(backupFile)) {
await fs.unlinkAsync(backupFile);
}
await fs.renameAsync(kcfile, backupFile);
} else {
await mkdirpAsync(path.dirname(kcfile));
}
await fs.writeTextFile(kcfile, merged);
await refreshExplorer();
await vscode.window.showInformationMessage(`New configuration merged to ${kcfile}`);
}
async function mergeInto(existing: Named[], toMerge: Named[]): Promise<void> {
for (const toMergeEntry of toMerge) {
if (existing.some((e) => e.name === toMergeEntry.name)) {
// we have CONFLICT and CONFLICT BUILDS CHARACTER
await vscode.window.showWarningMessage(`${toMergeEntry.name} already exists - skipping`);
continue; // TODO: build character
}
existing.push(toMergeEntry);
}
}