UNPKG

kui-shell

Version:

This is the monorepo for Kui, the hybrid command-line/GUI electron-based Kubernetes tool

236 lines 10.2 kB
"use strict"; 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