@kui-shell/plugin-kubectl
Version:
Kubernetes visualization plugin for kubernetes
274 lines (273 loc) • 9.4 kB
JavaScript
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.NoPrepare = void 0;
exports.doExecWithPty = doExecWithPty;
exports.doExecWithRadioTable = doExecWithRadioTable;
exports.doExecWithStatus = void 0;
exports.doExecWithStdout = doExecWithStdout;
exports.doExecWithStdoutViaPty = doExecWithStdoutViaPty;
exports.doExecWithTable = doExecWithTable;
exports.doExecWithoutPty = doExecWithoutPty;
exports.exec = exec;
exports.reallyNeedsPty = reallyNeedsPty;
var _core = require("@kui-shell/core");
var _formatTable = require("../../lib/view/formatTable");
/*
* Copyright 2019 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 strings = (0, _core.i18n)('plugin-kubectl');
/** No-op argument preparation */
const NoPrepare = args => args.command;
/** Standard status preparation */
exports.NoPrepare = NoPrepare;
function DefaultPrepareForStatus(verb, args) {
return args.argvNoOptions.slice(args.argvNoOptions.indexOf(verb) + 1);
}
/**
* Execute the given command in the browser; this dispatches to
* _kubectl, which runs on the proxy (for electron and headless, these
* are the same machine).
*
*/
function doExecWithoutPty(args, prepare = NoPrepare, exec = 'kubectl') {
return __awaiter(this, void 0, void 0, function* () {
const raw = `_${exec}$1`;
const command = prepare(args).replace(new RegExp(`^${exec}(\\s)?`), raw).replace(/^k(\s)?/, raw);
const dbl = new RegExp(`_${exec}(\\s)?`);
const doubleCheck = dbl.test(command) ? command : `_${exec} ${command}`;
return args.REPL.qexec(doubleCheck, undefined, undefined, args.execOptions).catch(err => {
if (err.code === 500 || err.statusCode === 500) {
err.code = err.statusCode = 500;
}
throw err;
});
});
}
/**
* Behaves as does `exec`, except that it projects out just the
* `stdout` part -- thus ignoring the exit `code` and `stderr`.
*
*/
function doExecWithStdout(args, prepare = NoPrepare, exec) {
return doExecWithoutPty(args, prepare, exec).then(_ => _.content.stdout);
}
/**
* Do we see anything in the given command that indicates a PTY is
* required? That is, versus a plain nodejs spawn/exec.
*
*/
function reallyNeedsPty({
argvNoOptions,
parsedOptions
}) {
const test1 = _ => /(\$\(|`)/.test(_); // subprocess execution?
const test2 = _ => /^\s*(\|\||\||>|>>)\s*$/.test(_) || test1(_);
return !!argvNoOptions.find(test2) || !!Object.values(parsedOptions).find(test1);
}
/**
* Execute the given command using a pty
*
*/
function doExecWithPty(args, prepare = NoPrepare, exec) {
return __awaiter(this, void 0, void 0, function* () {
if (!reallyNeedsPty(args) && (_core.Capabilities.isHeadless() || !_core.Capabilities.inBrowser() && args.execOptions.raw)) {
return doExecWithStdout(args, prepare, exec);
} else {
//
// For commands `kubectl (--help/-h)` and `k (--help/-h)`, render usage model;
// Otherwise, execute the given command using a pty
//
const commandToPTY = args.command.replace(/^k(\s)/, 'kubectl$1');
if (args.execOptions.onInit) {
args.execOptions.quiet = true; // don't ever emit anything on your own
args.execOptions.replSilence = true; // repl: same thing
args.execOptions.echo = false; // do not even echo "ok"
}
// let pty handle redirection
args.execOptions.noCoreRedirect = true;
return args.REPL.qexec(`sendtopty ${commandToPTY}`, args.block, undefined, args.execOptions.onInit ? args.execOptions : Object.assign({}, args.execOptions, {
rawResponse: true,
quiet: args.execOptions.quiet === undefined ? args.execOptions.type === _core.ExecType.TopLevel ? false : undefined : args.execOptions.quiet
})).catch(err => {
if (err.code === 500 || err.statusCode === 500) {
err.code = err.statusCode = 500;
}
throw err;
});
}
});
}
/**
* Execute the given command using a pty, but return a string
*
*/
function doExecWithStdoutViaPty(args, prepare = NoPrepare) {
return __awaiter(this, void 0, void 0, function* () {
// eslint-disable-next-line no-async-promise-executor
return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
let stdout = '';
// a bit of plumbing: tell the PTY that we will be handling everything
const myExecOptions = Object.assign({}, args.execOptions, {
rethrowErrors: true,
quiet: true,
replSilence: true,
echo: false,
// the PTY will call this when the PTY process is ready; in
// return, we send it back a consumer of streaming output
onInit: () => {
return chunk => {
if (typeof chunk === 'string') {
stdout += chunk;
}
};
}
});
const myArgs = Object.assign({}, args, {
execOptions: myExecOptions
});
yield doExecWithPty(myArgs, prepare).catch(err => {
console.error(err);
reject(err);
});
resolve(stdout);
}));
});
}
/**
* Decide whether to use a pty or a raw exec.
*
*/
function exec(args, prepare = NoPrepare, exec = 'kubectl') {
return __awaiter(this, void 0, void 0, function* () {
if (reallyNeedsPty(args)) {
return Promise.resolve({
content: {
code: 0,
stdout: yield doExecWithPty(args, prepare),
stderr: undefined,
wasSentToPty: true
}
});
} else {
return doExecWithoutPty(args, prepare, exec);
}
});
}
/**
* Behaves as does `exec`, except that it projects out just the
* `stdout` part and parses it into a Table model.
*
*/
function doExecWithTable(args, prepare = NoPrepare, command = 'kubectl', {
usePty = false,
nameColumn,
verb,
entityType
} = {}) {
return __awaiter(this, void 0, void 0, function* () {
const response = usePty ? {
content: {
stdout: yield doExecWithStdoutViaPty(args, prepare),
stderr: undefined
}
} : yield doExecWithoutPty(args, prepare, command);
const table = yield (0, _formatTable.stringToTable)(response.content.stdout, response.content.stderr, args, command, verb, entityType, nameColumn);
if (typeof table === 'string') {
throw new Error(strings('Unable to parse table'));
} else {
return table;
}
});
}
/**
* Execute a command, and then execute the status command which will
* poll until the given FinalState is reached.
*
*/
const doExecWithStatus = (verb, finalState, command = 'kubectl', prepareForExec = NoPrepare, prepareForStatus = DefaultPrepareForStatus) => args => __awaiter(void 0, void 0, void 0, function* () {
const response = yield exec(args, prepareForExec, command);
if (response.content.code !== 0) {
const err = new Error(response.content.stderr);
err.code = response.content.code;
throw err;
} else if (_core.Capabilities.isHeadless()) {
return response.content.stdout;
} else {
const statusArgs = yield prepareForStatus(verb, args);
const initialResponse = response ? response.content.stdout : '';
const {
doStatus
} = yield Promise.resolve().then(() => require('./status'));
return doStatus(args, verb, command, initialResponse, finalState, statusArgs);
}
});
exports.doExecWithStatus = doExecWithStatus;
function doExecWithRadioTable(resources, defaultSelectedIdx, onSelect, {
title = resources.length === 0 ? undefined : resources[0].kind,
nameColumnTitle = 'NAME'
} = {}) {
return __awaiter(this, void 0, void 0, function* () {
if (resources.length > 0) {
return {
apiVersion: 'kui-shell/v1',
kind: 'RadioTable',
title,
defaultSelectedIdx,
header: {
cells: [nameColumnTitle]
},
body: resources.map(resource => {
const name = resource.metadata.name;
return {
nameIdx: 0,
cells: [name],
onSelect: onSelect(name, resource)
};
})
};
}
});
}
var _default = exec;
exports.default = _default;
;