kui-shell
Version:
This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool
236 lines • 10.2 kB
JavaScript
;
var __awaiter = (this && this.__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());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const debug_1 = require("debug");
const path_1 = require("path");
const tables_1 = require("@kui-shell/core/api/tables");
const util_1 = require("@kui-shell/core/api/util");
const retry_1 = require("../util/retry");
const util_2 = require("../util/util");
const states_1 = require("../model/states");
const formatEntity_1 = require("../view/formatEntity");
const debug = debug_1.default('k8s/controller/status');
const usage = (command) => ({
command,
strict: command,
docs: 'Check the deployment status of a set of resources',
onlyEnforceOptions: true,
optional: [
{
name: 'file|kind',
file: true,
positional: true,
docs: 'A kubernetes resource file or kind'
},
{
name: 'resourceName',
positional: true,
docs: 'The name of a kubernetes resource of the given kind'
},
{ name: '--final-state', hidden: true },
{ name: '--namespace', alias: '-n', docs: 'Inspect a specified namespace' },
{ name: '--all', alias: '-a', docs: 'Show status across all namespaces' },
{
name: '--multi',
alias: '-m',
docs: 'Display multi-cluster views as a multiple tables'
},
{
name: '--watch',
alias: '-w',
docs: 'After listing/getting the requested object, watch for changes'
}
],
example: `kubectl ${command} @seed/cloud-functions/function/echo.yaml`
});
const headerRow = (kind) => {
debug('headerRow', kind);
const kindAttr = [{ value: 'KIND', outerCSS: 'header-cell not-too-wide entity-kind' }];
const namespaceAttr = kind && kind.match(/(ns|Namespace)/i)
? []
: [
{
value: 'NAMESPACE',
outerCSS: 'header-cell pretty-narrow hide-with-sidecar'
}
];
const statusAttr = [
{ value: 'STATUS', outerCSS: 'header-cell badge-width' },
{
value: 'MESSAGE',
outerCSS: 'header-cell not-too-wide hide-with-sidecar min-width-date-like'
}
];
const attributes = kindAttr
.concat(namespaceAttr)
.concat(statusAttr);
return {
type: 'status',
name: 'NAME',
outerCSS: 'header-cell',
attributes
};
};
const errorEntity = (execOptions, base, backupNamespace) => (err) => {
debug('creating error entity', err.code, base, backupNamespace, err);
if (!base) {
base = {
apiVersion: undefined,
kind: undefined,
metadata: { name: undefined, namespace: backupNamespace }
};
}
else if (!base.metadata) {
base.metadata = { name: undefined, namespace: backupNamespace };
}
else if (!base.metadata.namespace) {
base.metadata.namespace = backupNamespace;
}
if (err.code === 404) {
return Object.assign({}, base, {
status: {
state: states_1.States.Offline,
message: 'resource has been deleted'
}
});
}
else {
if (execOptions.raw) {
throw err;
}
else {
return Object.assign({}, base, {
status: {
state: states_1.States.Failed,
message: 'error fetching resource'
}
});
}
}
};
const getDirectReferences = (command) => ({ execOptions, argvNoOptions, parsedOptions, REPL }) => __awaiter(void 0, void 0, void 0, function* () {
const raw = Object.assign({}, execOptions, { raw: true });
const idx = argvNoOptions.indexOf(command) + 1;
const file = argvNoOptions[idx];
const name = argvNoOptions[idx + 1];
const namespace = parsedOptions.namespace || parsedOptions.n || 'default';
const finalState = parsedOptions['final-state'] || states_1.FinalState.NotPendingLike;
const ns = ({ metadata = {} } = {}) => {
debug('ns', metadata['namespace'], namespace);
return metadata['namespace']
? `-n "${metadata['namespace']}"`
: parsedOptions.namespace || parsedOptions.n
? `-n ${namespace}`
: '';
};
const { safeLoadAll: parseYAML } = yield Promise.resolve().then(() => require('js-yaml'));
if (file.charAt(0) === '!') {
const resources = parseYAML(execOptions.parameters[file.slice(1)]);
debug('status by programmatic parameter', resources);
return {
kind: 'file',
resource: Promise.all(resources.map((_) => __awaiter(void 0, void 0, void 0, function* () {
return REPL.qexec(`kubectl get "${_.kind}" "${_.metadata.name}" ${ns(_)} -o json`, undefined, undefined, raw);
})))
};
}
else if (name) {
const kind = file;
const command = `kubectl get "${kind}" "${name || ''}" ${ns()} -o json`;
debug('status by kind and name', command);
const getter = () => __awaiter(void 0, void 0, void 0, function* () {
return REPL.qexec(command, undefined, undefined, raw);
});
const kubeEntity = !finalState || finalState === states_1.FinalState.OfflineLike ? getter() : retry_1.withRetryOn404(getter, command);
return { kind, resource: kubeEntity };
}
else {
const filepath = util_1.default.findFile(file);
const isURL = file.match(/^http[s]?:\/\//);
const isDir = isURL ? false : yield util_2.isDirectory(filepath);
debug('status by filepath', file, filepath, isURL, isDir);
if (isDir) {
debug('status of directory');
const { readdir } = yield Promise.resolve().then(() => require('fs-extra'));
const files = yield readdir(filepath);
const yamls = files.filter(_ => _.match(/^[^\\.#].*\.yaml$/)).map(file => path_1.join(filepath, file));
if (files.find(file => file === 'seeds')) {
const seedsDir = path_1.join(filepath, 'seeds');
if (yield util_2.isDirectory(seedsDir)) {
const seeds = (yield readdir(seedsDir)).filter(_ => _.match(/\.yaml$/));
seeds.forEach(file => yamls.push(path_1.join(filepath, 'seeds', file)));
}
}
const main = yamls.find(_ => _.match(/main.yaml$/));
const yamlsWithMainFirst = (main ? [main] : []).concat(yamls.filter(_ => !_.match(/main.yaml$/)));
return {
kind: 'dir',
resource: Promise.all(yamlsWithMainFirst.map((filepath) => __awaiter(void 0, void 0, void 0, function* () {
return REPL.qexec(`k status "${filepath}" --final-state ${finalState}`, undefined, undefined, execOptions);
})))
};
}
else if (isDir === undefined) {
debug('status by resource kind', file, name);
const kubeEntities = REPL.qexec(`kubectl get "${file}" "${name || ''}" ${ns()} -o json`, undefined, undefined, raw).catch(err => {
if (err.code === 404) {
throw err;
}
else {
return errorEntity(execOptions, undefined, namespace)(err);
}
});
return { kind: file, resource: kubeEntities };
}
else {
debug('status by file', file);
const passedAsParameter = !isURL && filepath.match(/\/(!.*$)/);
const { fetchFileString } = yield Promise.resolve().then(() => require('../util/fetch-file'));
const specs = (passedAsParameter
? parseYAML(execOptions.parameters[passedAsParameter[1].slice(1)])
: util_1.default.flatten((yield fetchFileString(file)).map(_ => parseYAML(_)))).filter(_ => _);
const kubeEntities = Promise.all(specs.map((spec) => __awaiter(void 0, void 0, void 0, function* () {
return REPL.qexec(`kubectl get "${spec.kind}" "${spec.metadata.name}" ${ns(spec)} -o json`, undefined, undefined, raw).catch(errorEntity(execOptions, spec, namespace));
})));
return { kind: undefined, resource: kubeEntities };
}
}
});
exports.status = (command) => (args) => __awaiter(void 0, void 0, void 0, function* () {
const doWatch = args.parsedOptions.watch || args.parsedOptions.w;
const refreshCommand = args.command.replace('--watch', '').replace('-w', '');
const { kind, resource } = yield getDirectReferences(command)(args);
const direct = yield resource;
if (args.execOptions.raw && !Array.isArray(direct)) {
return direct;
}
const body = Array.isArray(direct)
? yield Promise.all(direct.map(formatEntity_1.formatEntity(args.parsedOptions)))
: [yield formatEntity_1.formatEntity(args.parsedOptions)(direct)];
const table = new tables_1.default.Table({
body,
header: headerRow(kind),
noSort: true
});
return !doWatch
? table
: tables_1.default.formatWatchableTable(table, {
refreshCommand,
watchByDefault: true
});
});
exports.default = (commandTree) => {
commandTree.listen('/status', exports.status('status'), {
usage: usage('status'),
inBrowserOk: true
});
};
//# sourceMappingURL=status.js.map