Skip to content

Commit 07cbe75

Browse files
authored
Merge pull request #304 from rancher-sandbox/253c-handle-delays-reading-config
Be more careful when processing the config file:
2 parents 3c7caa0 + b8442b1 commit 07cbe75

File tree

3 files changed

+73
-7
lines changed

3 files changed

+73
-7
lines changed

src/k8s-engine/k3sHelper.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,23 @@ export default class K3sHelper extends events.EventEmitter implements VersionLis
453453
await workFile.close();
454454
}
455455

456+
// On Windows repeat until the kubeconfig file is readable
457+
let delay = 0; // msec
458+
const delayIncrement = 200;
459+
const maxDelay = 10_000;
460+
461+
while (delay < maxDelay) {
462+
try {
463+
await fs.promises.readFile(workPath, { encoding: 'utf-8' });
464+
break;
465+
} catch (err) {
466+
console.log(`Error reading ${ workPath }: ${ err }`);
467+
console.log(`Waiting for ${ delay / 1000.0 } sec`);
468+
delay += delayIncrement;
469+
await util.promisify(setTimeout)(delay);
470+
}
471+
}
472+
456473
// For some reason, using KubeConfig.loadFromFile presents permissions
457474
// errors; doing the same ourselves seems to work better. Since the file
458475
// comes from the WSL container, it must not contain any paths, so there

src/k8s-engine/wsl.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
242242
}
243243
});
244244

245-
// Wait for k3s server; note that we're delibrately sending a HTTP request
245+
// Wait for k3s server; note that we're deliberately sending an HTTP request
246246
// to an HTTPS server, and expecting an error response back.
247247
while (true) {
248248
try {
@@ -259,8 +259,13 @@ export default class WSLBackend extends events.EventEmitter implements K8s.Kuber
259259
await util.promisify(setTimeout)(500);
260260
}
261261

262-
await this.k3sHelper.updateKubeconfig(
263-
'wsl.exe', '--distribution', 'k3s', '--exec', '/usr/local/bin/kubeconfig');
262+
try {
263+
await this.k3sHelper.updateKubeconfig(
264+
'wsl.exe', '--distribution', 'k3s', '--exec', '/usr/local/bin/kubeconfig');
265+
} catch (err) {
266+
console.log(`k3sHelper.updateKubeconfig failed: ${ err }. Will retry...`);
267+
throw err;
268+
}
264269

265270
this.client = new K8s.Client();
266271
await this.client.waitForServiceWatcher();

src/menu/tray.js

Lines changed: 48 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const { EventEmitter } = require('events');
77
const fs = require('fs');
88
const pth = require('path');
99
const electron = require('electron');
10+
const yaml = require('yaml');
1011
const k8s = require('@kubernetes/client-node');
1112
const kubectl = require('../k8s-engine/kubectl.js');
1213
const kubeconfig = require('../config/kubeconfig.js');
@@ -83,17 +84,60 @@ export class Tray extends EventEmitter {
8384
if (!kubeconfigPath) {
8485
throw new Error('No kubeconfig path found');
8586
}
86-
fs.watch(kubeconfigPath, () => {
87-
this.updateContexts();
88-
const contextMenu = electron.Menu.buildFromTemplate(this.#contextMenuItems);
87+
this.buildFromConfig(kubeconfigPath);
88+
const watcher = fs.watch(kubeconfigPath);
8989

90-
this.#trayMenu.setContextMenu(contextMenu);
90+
watcher.on('error', (code, signal) => {
91+
console.log(`Failed to fs.watch ${ kubeconfigPath }: code: ${ code }, signal: ${ signal }`);
92+
});
93+
watcher.on('change', (eventType, _) => {
94+
if (eventType === 'rename' && !kubeconfig.hasAccess(kubeconfigPath)) {
95+
// File doesn't exist. Wait for it to be recreated
96+
return;
97+
}
98+
this.buildFromConfig(kubeconfigPath);
9199
});
92100

93101
this.on('k8s-check-state', this.k8sStateChanged.bind(this));
94102
this.on('settings-update', this.settingsChanged.bind(this));
95103
}
96104

105+
buildFromConfig(configPath) {
106+
if (!kubeconfig.hasAccess(configPath)) {
107+
return;
108+
}
109+
110+
try {
111+
let parsedConfig;
112+
const contents = fs.readFileSync(configPath).toString();
113+
114+
if (contents.length === 0) {
115+
console.log('Config file is empty, will try to process it later');
116+
117+
return;
118+
}
119+
120+
try {
121+
parsedConfig = yaml.parse(contents);
122+
} catch (err) {
123+
console.log(`yaml parse failure: ${ err } on kubeconfig: contents ${ contents }., will retry later.`);
124+
parsedConfig = null;
125+
}
126+
127+
if ((parsedConfig?.clusters || []).length === 0) {
128+
console.log('Config file has no clusters, will retry later');
129+
130+
return;
131+
}
132+
this.updateContexts();
133+
const contextMenu = electron.Menu.buildFromTemplate(this.#contextMenuItems);
134+
135+
this.#trayMenu.setContextMenu(contextMenu);
136+
} catch (err) {
137+
console.log(`Error trying to update context menu: ${ err }`);
138+
}
139+
}
140+
97141
/**
98142
* Called when the Kubernetes cluster state has changed.
99143
* @param {State} state The new cluster state.

0 commit comments

Comments
 (0)