diff --git a/README.md b/README.md index 5989a76..9071861 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,128 @@ A NPM package containing utility functions for parsing output from the Kubernetes `kubectl` command line tool. +# Reference + +## Table format functions + +These functions are for use with `kubectl` commands that return data in tabular format - +that is, of the form: + +``` +TITLE1 TITLE2 TITLE3 +value11 value12 value13 +value21 value22 value23 +``` + +where no column header or value contains a space, and columns are separated by one or more spaces. +Examples include the default behaviour of `kubectl get`, for example `kubectl get pods` or +`kubectl get pods -o wide` to get a list of pods. + +In all cases: + +* The function takes a `KubectlOutput`: that is, either a `ShellResult` (an object with numeric `code`, +`stdout` string and `stderr` string properties), or `undefined`. `code` and `stderr` are used +for error handling; on the happy path, the function will operate on the `stdout` string. +* The function returns an `Errorable`: that is, an object with a boolean `succeeded` property. If +`succeeded` is true, the object also has a `result` property containing the result of the function; +if `succeeded` is false, the object has `reason` and `error` properties describing the failure. +* If the input is `undefined`, or has a non-zero `code`, the function returns a _failed_ Errorable. +* The function does not check the format of the provided `stdout`. You should use it **only** on the output +of `kubectl` commands that return tabular data without spaces or missing values. + +### parseTabular + +**JavaScript:** `parseTabular(output)` + +**TypeScript:** `parseTabular(output: KubectlOutput): Errorable[]>` + +Parses tabular `kubectl` output into an array of key-value objects, one per line. + +The result is an array of the form: + +```javascript +[ + { + title1: "value11", + title2: "value12", + title3: "value13" + }, + { + title1: "value21", + title2: "value22", + title3: "value23" + } +] +``` + +Each non-header row is parsed as an object within the array. Each object's keys are the lowercased +column headers, and the value of each key is the string under that header in the object's row. + +If `output.stdout` is empty then the function returns success with an empty array. + +**TypeScript:** `Dictionary` is an alias for `{ [key: string]: T }`. + +### asTableLines + +**JavaScript:** `asTableLines(output)` + +**TypeScript:** `asTableLines(output: KubectlOutput): Errorable` + +Splits tabular `kubectl` output into a header line and an array of body lines. The result is +an object of the form: + +```javascript +{ + header: "TITLE1 TITLE2 TITLE3", + body: [ + "value11 value12 value13". + "value21 value22 value23" + ] +} +``` + +If `output.stdout` is empty then the function returns success with an empty string `header` and +an empty `body` array. + +## JSON format functions + +These functions are for use with `kubectl` commands that return data in JSON format. +This is typically triggered by the `-o json` option and may be used for lists or for single +resources, e.g. `kubectl get pods -o json` or `kubectl get deploy/nginx -o json`. + +In all cases: + +* The function takes a `KubectlOutput`: that is, either a `ShellResult` (an object with numeric `code`, +`stdout` string and `stderr` string properties), or `undefined`. `code` and `stderr` are used +for error handling; on the happy path, the function will operate on the `stdout` string. +* The function returns an `Errorable`: that is, an object with a boolean `succeeded` property. If +`succeeded` is true, the object also has a `result` property containing the result of the function; +if `succeeded` is false, the object has `reason` and `error` properties describing the failure. +* If the input is `undefined`, or has a non-zero `code`, or if `stdout` is empty, the function +returns a _failed_ Errorable. +* The function does not check the format of the provided `stdout`. You should use it **only** on the output +of `kubectl` commands that return JSON data. + +### parseJSON + +**JavaScript:** `parseJSON(output)` + +**TypeScript:** `parseJSON(output: KubectlOutput): Errorable` + +Checks for `kubectl` failure and then returns the deserialised object corresponding to the +`stdout` JSON. + +### parseJSONCollection + +**TypeScript:** `parseJSONCollection(output: KubectlOutput): Errorable>` + +Checks for `kubectl` failure and then returns the deserialised object corresponding to the +`stdout` JSON, where this is a Kubernetes item list object. + +This is equivalent to writing `parseJSON>`; it is provided for TypeScript +users to reduce generics clutter in their code. (In untyped JavaScript, there's no difference +between this and the `parseJSON` function.) + # Contributing This project welcomes contributions and suggestions. Most contributions require you to agree to a diff --git a/ts/src/dictionary.ts b/ts/src/dictionary.ts index ca38d7e..2d2712a 100644 --- a/ts/src/dictionary.ts +++ b/ts/src/dictionary.ts @@ -1,8 +1,16 @@ +/** + * Represents objects which can have arbitrary property names but whose + * properties must all be of the specified type. This is equivalent + * to the TypeScript `{ [key: string]: T }` type. + */ export type Dictionary = { [key: string]: T }; export module Dictionary { + /** + * Returns a new, empty Dictionary. + */ export function of(): Dictionary { return {}; } diff --git a/ts/src/errorable.ts b/ts/src/errorable.ts index 7a8ba72..9988cc3 100644 --- a/ts/src/errorable.ts +++ b/ts/src/errorable.ts @@ -1,20 +1,64 @@ +/** + * Represents a successful result of a function on kubectl output - that is, the function + * successfully computed a value. + */ export interface Succeeded { + /** + * Identifies this as a successful result. + */ readonly succeeded: true; + /** + * The result value of the function. + */ readonly result: T; } +/** + * Represents a failed result of a function on kubectl output - that is, the function was + * unable to compute a value, and this object describes why. + */ export interface Failed { + /** + * Identifies this as a failed result. + */ readonly succeeded: false; + /** + * The reason for the failure. This is a programmatic identifier which you + * can use to create a meaningful error message. Values are: + * + * 'failed-to-run': failed because the kubectl process was not created + * 'kubectl-error': failed because kubectl encountered an error (returned a non-zero exit code) + * 'failed-to-parse': failed because it could not parse kubectl's standard output + */ readonly reason: 'failed-to-run' | 'kubectl-error' | 'failed-to-parse'; + /** + * If reason is 'kubectl-error', contains the stderr from kubectl. Otherwise, + * contains a default message for the reason. + */ readonly error: string; } +/** + * Represents the result of trying to parse kubectl output - success or failure. + */ export type Errorable = Succeeded | Failed; +/** + * Checks if an Errorable represents a successful parse. In TypeScript, this is + * a type guard, and you may access the .result property after calling this. + * @param e The Errorable to be checked. + * @returns Whether the Errorable represents a successful parse. + */ export function succeeded(e: Errorable): e is Succeeded { return e.succeeded; } +/** + * Checks if an Errorable represents a fauked parse. In TypeScript, this is + * a type guard, and you may access the .reason and .error properties after calling this. + * @param e The Errorable to be checked. + * @returns Whether the Errorable represents a failed parse. + */ export function failed(e: Errorable): e is Failed { return !e.succeeded; } diff --git a/ts/src/index.ts b/ts/src/index.ts index 4e58659..37aaab9 100644 --- a/ts/src/index.ts +++ b/ts/src/index.ts @@ -1,12 +1,19 @@ +/** + * The result of invoking an external program via the shell. + */ export interface ShellResult { readonly code: number; readonly stdout: string; readonly stderr: string; } +/** + * The result of invoking the kubectl program via the shell. + * This is undefined if creating the kubectl process failed. + */ export type KubectlOutput = ShellResult | undefined; export * from './dictionary'; export * from './errorable'; -export { asTableLines, parseTabular, parseTableLines, TableLines } from './table'; +export { asTableLines, parseTabular, TableLines } from './table'; export { parseJSON, parseJSONCollection, KubernetesList } from './json'; diff --git a/ts/src/json.ts b/ts/src/json.ts index ded3763..cccc061 100644 --- a/ts/src/json.ts +++ b/ts/src/json.ts @@ -1,13 +1,35 @@ import { KubectlOutput, Errorable } from "."; import { Dictionary } from "./dictionary"; +/** + * Represents how kubectl -o json formats lists of resources. + */ export interface KubernetesList { + /** + * The Kubernetes API version. + */ readonly apiVersion: string; + /** + * The contents of the list. + */ readonly items: T[]; + /** + * Identifies this object to the Kubernetes API as a list. + */ readonly kind: "List"; + /** + * Contains additional data about the list. + */ readonly metadata: Dictionary; } +/** + * Parses JSON kubectl output into an object. + * @param output The result of invoking kubectl via the shell. + * @returns If kubectl ran successfully and produced JSON output, a success value + * containing the deserialised object. If kubectl did not run + * successfully, a failure value. + */ export function parseJSON(output: KubectlOutput): Errorable { if (!output) { return { succeeded: false, reason: 'failed-to-run', error: 'Unable to run kubectl' }; @@ -24,6 +46,14 @@ export function parseJSON(output: KubectlOutput): Errorable { return { succeeded: false, reason: 'kubectl-error', error: output.stderr }; } +/** + * Parses JSON kubectl output into a Kubernetes list object. You may use this if your + * kubectl command requested a list of resources rather than a single resource. + * @param output The result of invoking kubectl via the shell. + * @returns If kubectl ran successfully and produced JSON output, a success value + * containing the deserialised object. If kubectl did not run + * successfully, a failure value. + */ export function parseJSONCollection(output: KubectlOutput): Errorable> { return parseJSON>(output); } diff --git a/ts/src/table.ts b/ts/src/table.ts index 01ed916..7189661 100644 --- a/ts/src/table.ts +++ b/ts/src/table.ts @@ -4,11 +4,23 @@ import { Errorable, failed } from "./errorable"; const KUBECTL_OUTPUT_COLUMN_SEPARATOR = /\s+/g; +/** + * Provides a line-oriented view of tabular kubectl output. + */ export interface TableLines { readonly header: string; readonly body: string[]; } +/** + * Parses tabular kubectl output into an array of objects. Each non-header row + * is mapped to an object in the output, and each object has a property per column, + * named as the lower-cased column header. + * @param output The result of invoking kubectl via the shell. + * @returns If kubectl ran successfully and produced tabular output, a success value + * containing an array of objects for the non-header rows. If kubectl did not run + * successfully, a failure value. + */ export function parseTabular(output: KubectlOutput): Errorable[]> { const table = asTableLines(output); if (failed(table)) { @@ -19,6 +31,15 @@ export function parseTabular(output: KubectlOutput): Errorable { if (!output) { return { succeeded: false, reason: 'failed-to-run', error: 'Unable to run kubectl' }; @@ -37,7 +58,7 @@ export function asTableLines(output: KubectlOutput): Errorable { return { succeeded: false, reason: 'kubectl-error', error: output.stderr }; } -export function parseTableLines(table: TableLines, columnSeparator: RegExp): Dictionary[] { +function parseTableLines(table: TableLines, columnSeparator: RegExp): Dictionary[] { if (table.header.length === 0 || table.body.length === 0) { return []; }