@kui-shell/plugin-kubectl
Version:
Kubernetes visualization plugin for kubernetes
229 lines (227 loc) • 7.68 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getProxyState;
var _debug = _interopRequireDefault(require("debug"));
var _core = require("@kui-shell/core");
var _config = require("../../kubectl/config");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/*
* Copyright 2020 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var __awaiter = void 0 && (void 0).__awaiter || function (thisArg, _arguments, P, generator) {
function adopt(value) {
return value instanceof P ? value : new P(function (resolve) {
resolve(value);
});
}
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
}
function rejected(value) {
try {
step(generator["throw"](value));
} catch (e) {
reject(e);
}
}
function step(result) {
result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected);
}
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const debug = (0, _debug.default)('plugin-kubectl/client/proxy');
/** Maximum number of times we try start the kubectl proxy */
const maxRetries = 1000;
/** State of current kubectl proxy */
const currentProxyState = {
oc: undefined,
kubectl: undefined
};
/**
* Unregister onQuit handlers
*
*/
function unregisterOnQuit(onQuitHandler) {
try {
if (typeof onQuitHandler === 'function') {
(0, _core.offQuit)(onQuitHandler);
}
} catch (err) {
console.error('Error unregistering kubectl proxy onQuit', err);
}
}
/**
* Stop kubectl proxy for the given kubectl proxy state
*
*/
function stopProxy() {
try {
// kill the proxy process
if (this.process) {
debug('killing kubectl proxy', this.port);
this.process.kill();
}
// unregister event handlers
unregisterOnQuit(this.onQuitHandler);
if (typeof this.onConfigChangeHandler === 'function') {
(0, _config.offKubectlConfigChangeEvents)(this.onConfigChangeHandler);
}
} catch (err) {
console.error('Error stopping kubectl proxy', err);
}
}
/**
* Register onQuit handlers, to make sure that we kill any extant
* kubectl proxy processes.
*
*/
function registerOnQuit(state) {
try {
const onQuitHandler = stopProxy.bind(state);
(0, _core.onQuit)(onQuitHandler);
// kill our instance if there is a change to our context
const onConfigChangeHandler = (eventType, _2, newContext) => {
if (eventType === 'LoginToContext' && state.context === newContext) {
onQuitHandler();
}
};
(0, _config.onKubectlConfigChangeEvents)(onConfigChangeHandler);
return Object.assign(state, {
onQuitHandler,
onConfigChangeHandler
});
} catch (err) {
console.error('Error registering kubectl proxy onQuit', err);
}
}
/**
* Launch kubectl proxy for the current context.
*
* @return the State of the kubectl proxy
*
*/
function startProxy(command, context) {
return __awaiter(this, void 0, void 0, function* () {
const {
spawn
} = yield Promise.resolve().then(() => require('child_process'));
return new Promise((resolve, reject) => {
const iter = (port = 8001, retryCount = 0) => {
try {
debug(`attempting to spawn kubectl proxy on port=${port} context=${context || 'default'}`);
const args = ['proxy', '--keepalive=120s', '--port', port.toString()];
if (context) {
args.push('--context');
args.push(context);
}
const process = spawn(command, args, {
windowsHide: true
});
let myState;
// to make sure we don't smash the global variable on exit
let iGotRetried = false;
process.on('error', err => {
console.error('Error spawning kubectl proxy', err);
reject(err);
});
process.stdout.on('data', data => {
const msg = data.toString();
debug('stdout', msg);
if (/Starting to serve/.test(msg)) {
// success!
debug('succeessfully spawned kubectl proxy on port', port);
myState = registerOnQuit({
process,
port,
context
});
resolve(myState);
}
});
let stderr = '';
process.stderr.on('data', data => {
const msg = data.toString();
if (/address already in use/.test(msg) && retryCount < maxRetries) {
iGotRetried = true; // so we don't smash the global on exit
iter(port + 1, retryCount + 1);
} else {
debug('stderr', msg);
stderr += msg;
}
});
process.on('exit', (code, signal) => {
debug('kubectl proxy has exited with code', code || signal);
if (currentProxyState[command] !== undefined && retryCount >= maxRetries) {
// then we are still trying to initialize, and haven't
// exceeded our port retry loop count
console.error(`kubectl proxy exited unexpectedly with exitCode=${code || signal}`);
reject(new Error(stderr));
} else if (currentProxyState[command]) {
// then we thought we had a stable kubectl proxy process, but it went and died on its own
debug('marking proxy as terminated');
if (myState) {
myState.process = undefined;
}
if (!iGotRetried) {
currentProxyState[command] = undefined;
}
}
});
} catch (err) {
console.error('Error establishing kubectl proxy', err);
reject(err);
// proxyForContext = undefined
}
};
iter();
});
});
}
/** Wrapper around `startProxy` that deals with the currentProxyState variable */
function initProxyState(command, context) {
if (!currentProxyState[command]) {
const myProxyState = startProxy(command, context);
currentProxyState[command] = {
[context]: myProxyState
};
} else if (!currentProxyState[command][context]) {
const myProxyState = startProxy(command, context);
currentProxyState[command][context] = myProxyState;
}
return currentProxyState[command][context];
}
/** Is the current kubectl proxy viable? */
function isProxyActive(command, context) {
return currentProxyState[command] !== undefined && currentProxyState[command][context] !== undefined;
}
/** @return information about the current kubectl proxy */
function getProxyState(command, context) {
return __awaiter(this, void 0, void 0, function* () {
if (!isProxyActive(command, context)) {
initProxyState(command, context);
}
return {
baseUrl: !isProxyActive(command, context) ? undefined : `http://localhost:${(yield currentProxyState[command][context]).port}`
};
});
}
;