Skip to content

Commit 95a46fe

Browse files
committed
Support KUBECONFIG environment variable
- Watch for changes in file(s) indicated by `KUBECONFIG` when it's set - Warn when multiple config files are specified in `KUBECONFIG`, since the Kubernetes client library we are using doesn't support this use case Closes #3382 Signed-off-by: David Thompson <[email protected]>
1 parent 1921c7a commit 95a46fe

File tree

2 files changed

+51
-19
lines changed

2 files changed

+51
-19
lines changed

src/explorer.ts

+49-17
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,34 @@ import { Progress } from './util/progress';
3030
import { FileContentChangeNotifier, WatchUtil } from './util/watch';
3131
import { vsCommand } from './vscommand';
3232

33-
const kubeConfigFolder: string = path.join(Platform.getUserHomePath(), '.kube');
33+
/**
34+
* Returns the list of kube config files:
35+
* - If KUBECONFIG is not set, just ~/.kube/config
36+
* - If KUBECONFIG is set, follows the semantics for specifying multiple config files described here:
37+
* https://kubernetes.io/docs/tasks/access-application-cluster/configure-access-multiple-clusters/#append-home-kube-config-to-your-kubeconfig-environment-variable
38+
* BUT: it shows an error if multiple configs are specified, since the Kubernetes client we are using doesn't support this use case.
39+
*/
40+
function getKubeConfigFiles(): string[] {
41+
if (process.env.KUBECONFIG) {
42+
let configuredFiles: string[] = [];
43+
if (process.platform === 'win32') {
44+
configuredFiles = process.env.KUBECONFIG.split(';');
45+
} else {
46+
configuredFiles = process.env.KUBECONFIG.split(':');
47+
}
48+
if (configuredFiles.length > 1) {
49+
void window.showWarningMessage('KUBECONFIG specifies multiple files. Unfortunately, the extension doesn\'t work properly with this setup. Expect things to break.');
50+
}
51+
const filesThatExist: string[] = [];
52+
for (const configFile of configuredFiles) {
53+
if (fs.existsSync(configFile)) {
54+
filesThatExist.push(configFile);
55+
}
56+
}
57+
return filesThatExist;
58+
}
59+
return [path.join(Platform.getUserHomePath(), '.kube', 'config')];
60+
}
3461

3562
type ExplorerItem = KubernetesObject | Helm.HelmRelease | Context | TreeItem;
3663

@@ -42,7 +69,7 @@ type PackageJSON = {
4269
const CREATE_OR_SET_PROJECT_ITEM = {
4370
label: 'Create new or set active Project',
4471
command: {
45-
title: 'Create new or ser active Project',
72+
title: 'Create new or set active Project',
4673
command: 'openshift.project.set'
4774
}
4875
};
@@ -52,7 +79,7 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
5279

5380
private treeView: TreeView<ExplorerItem>;
5481

55-
private fsw: FileContentChangeNotifier;
82+
private kubeConfigWatchers: FileContentChangeNotifier[];
5683
private kubeContext: Context;
5784
private kubeConfig: KubeConfigUtils;
5885

@@ -70,22 +97,25 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
7097
// ignore config loading error and let odo report it on first call
7198
}
7299
try {
73-
this.fsw = WatchUtil.watchFileForContextChange(kubeConfigFolder, 'config');
100+
const kubeconfigFiles = getKubeConfigFiles();
101+
this.kubeConfigWatchers = kubeconfigFiles.map(kubeconfigFile => WatchUtil.watchFileForContextChange(path.dirname(kubeconfigFile), path.basename(kubeconfigFile)));
74102
} catch (err) {
75103
void window.showWarningMessage('Couldn\'t install watcher for Kubernetes configuration file. OpenShift Application Explorer view won\'t be updated automatically.');
76104
}
77-
this.fsw?.emitter?.on('file-changed', () => {
78-
const ku2 = new KubeConfigUtils();
79-
const newCtx = ku2.getContextObject(ku2.currentContext);
80-
if (!this.kubeContext
81-
|| (this.kubeContext.cluster !== newCtx.cluster
82-
|| this.kubeContext.user !== newCtx.user
83-
|| this.kubeContext.namespace !== newCtx.namespace)) {
84-
this.refresh();
85-
}
86-
this.kubeContext = newCtx;
87-
this.kubeConfig = ku2;
88-
});
105+
for (const fsw of this.kubeConfigWatchers) {
106+
fsw.emitter?.on('file-changed', () => {
107+
const ku2 = new KubeConfigUtils();
108+
const newCtx = ku2.getContextObject(ku2.currentContext);
109+
if (Boolean(this.kubeContext) !== Boolean(newCtx)
110+
|| (this.kubeContext.cluster !== newCtx.cluster
111+
|| this.kubeContext.user !== newCtx.user
112+
|| this.kubeContext.namespace !== newCtx.namespace)) {
113+
this.refresh();
114+
}
115+
this.kubeContext = newCtx;
116+
this.kubeConfig = ku2;
117+
});
118+
}
89119
this.treeView = window.createTreeView<ExplorerItem>('openshiftProjectExplorer', {
90120
treeDataProvider: this,
91121
});
@@ -246,7 +276,9 @@ export class OpenShiftExplorer implements TreeDataProvider<ExplorerItem>, Dispos
246276
}
247277

248278
dispose(): void {
249-
this.fsw?.watcher?.close();
279+
for (const fsw of this.kubeConfigWatchers) {
280+
fsw?.watcher?.close();
281+
}
250282
this.treeView.dispose();
251283
}
252284

src/util/kubeUtils.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@
33
* Licensed under the MIT License. See LICENSE file in the project root for license information.
44
*-----------------------------------------------------------------------------------------------*/
55

6+
import { KubeConfig, findHomeDir, loadYaml } from '@kubernetes/client-node';
7+
import { Cluster, User } from '@kubernetes/client-node/dist/config_types';
68
import * as fs from 'fs';
79
import * as path from 'path';
810
import { QuickPickItem } from 'vscode';
9-
import { KubeConfig, findHomeDir, loadYaml } from '@kubernetes/client-node';
10-
import { User, Cluster } from '@kubernetes/client-node/dist/config_types';
1111

1212
function fileExists(file: string): boolean {
1313
try {

0 commit comments

Comments
 (0)