UNPKG

@kui-shell/plugin-kubectl

Version:

Kubernetes visualization plugin for kubernetes

229 lines (227 loc) 7.68 kB
"use strict"; 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}` }; }); }