|
| 1 | +# Expose `get` output from the server |
| 2 | + |
| 3 | +Today, all clients must reproduce the tabular and describe output implemented in `kubectl` to perform simple lists |
| 4 | +of objects. This logic in many cases is non-trivial and condenses multiple fields into succinct output. It also requires |
| 5 | +that every client provide rendering logic for every possible type, including those provided by API aggregation or third |
| 6 | +party resources which may not be known at compile time. |
| 7 | + |
| 8 | +This proposal covers moving `get` and `describe` to the server to reduce total work for all clients and centralize these |
| 9 | +core display options for better reuse and extension. |
| 10 | + |
| 11 | + |
| 12 | +## Background |
| 13 | + |
| 14 | +`kubectl get` is a simple tabular representation of one or more instances of a particular resource type. It is the primary |
| 15 | +listing mechanism and so must be implemented for each type. Today, we have a single generic implementation for unrecognized |
| 16 | +types that attempts to load information from the `metadata` field (assuming the object follows the metav1 Kubernetes API |
| 17 | +schema). `get` supports a `wide` mode that includes additional columns. Users can add additional columns for labels via a |
| 18 | +flag. Headers corresponding to the columns are optionally displayed. |
| 19 | + |
| 20 | +`kubectl describe` shows a textual representation of individual objects that describes individual fields as subsequent |
| 21 | +lines and uses indendation and nested tables to convey deeper structure on the resource (such as events for a pod or |
| 22 | +each container). It sometimes retrieves related objects like events, pods for a replication controller, or autoscalers |
| 23 | +for a deployment. It supports no significant flags. |
| 24 | + |
| 25 | +The implementation of both is modeled as a registered function that takes an object or list of objects and outputs |
| 26 | +semi-structured text. |
| 27 | + |
| 28 | +## Goals |
| 29 | + |
| 30 | +* Make it easy for a simple client to get a list of resources for a web UI or CLI output |
| 31 | +* Support all existing options, leave open the door for future extension and experimentation |
| 32 | +* Allow new API extensions and third party resources to be implemented server side, removing the need to version |
| 33 | + schemes for retrieving data from the server |
| 34 | +* Keep implementation of `get` and `describe` simple |
| 35 | +* Ease internationalization of `get` and `describe` output for all clients |
| 36 | + |
| 37 | +## Non-Goals |
| 38 | + |
| 39 | +* Deep customization of the returned output by the client |
| 40 | + |
| 41 | + |
| 42 | +## Specification of server-side `get` |
| 43 | + |
| 44 | +The server would return a `Table` object (working-name) that contains metadata for columns and one or more |
| 45 | +rows composed of cells for each column. Some additional data may be relevant for each row and returned by the |
| 46 | +server. Since every object should have a `Table` representation, treat this as part of content negotiation |
| 47 | +as described in [the alternative representations of objects proposal](alternate-api-representations.md). |
| 48 | + |
| 49 | +Example request: |
| 50 | + |
| 51 | +``` |
| 52 | +$ curl https://localhost:8443/api/v1/pods -H "Accept: application/json+vnd.kubernetes.as+meta.k8s.io+v1alpha1+Table" |
| 53 | +{ |
| 54 | + "kind": "Table", |
| 55 | + "apiVersion": "meta.k8s.io/v1alpha1", |
| 56 | + "headers": [ |
| 57 | + {"name": "Name", "type": "string", "description": "The name of the pod, must be unique ..."}, |
| 58 | + {"name": "Status", "type": "string", "description": "Describes the current state of the pod"}, |
| 59 | + ... |
| 60 | + ], |
| 61 | + "items": [ |
| 62 | + {"cells": ["pod1", "Failed - unable to start", ...]}, |
| 63 | + {"cells": ["pod2", "Init 0/2", ...]}, |
| 64 | + ... |
| 65 | + ] |
| 66 | +} |
| 67 | +``` |
| 68 | + |
| 69 | +This representation is also possible to return from a watch. The watch can omit headers on subsequent queries. |
| 70 | + |
| 71 | +``` |
| 72 | +$ curl https://localhost:8443/api/v1/pods?watch=1 -H "Accept: application/json+vnd.kubernetes.as+meta.k8s.io+v1alpha1+Table" |
| 73 | +{ |
| 74 | + "kind": "Table", |
| 75 | + "apiVersion": "meta.k8s.io/v1alpha1", |
| 76 | + // headers are printed first, in case the watch holds |
| 77 | + "headers": [ |
| 78 | + {"name": "Name", "type": "string", "description": "The name of the pod, must be unique ..."}, |
| 79 | + {"name": "Status", "type": "string", "description": "Describes the current state of the pod"}, |
| 80 | + ... |
| 81 | + ] |
| 82 | +} |
| 83 | +{ |
| 84 | + "kind": "Table", |
| 85 | + "apiVersion": "meta.k8s.io/v1alpha1", |
| 86 | + // headers are not returned here |
| 87 | + "items": [ |
| 88 | + {"cells": ["pod1", "Failed - unable to start", ...]}, |
| 89 | + ... |
| 90 | + ] |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +It can also be returned in CSV form: |
| 95 | + |
| 96 | +``` |
| 97 | +$ curl https://localhost:8443/api/v1/pods -H "Accept: text/csv+vnd.kubernetes.as+meta.k8s.io+v1alpha1+Table" |
| 98 | +Name,Status,... |
| 99 | +pod1,"Failed - unable to start",... |
| 100 | +pod2,"Init 0/2",... |
| 101 | +... |
| 102 | +``` |
| 103 | + |
| 104 | +To support "wide" format, columns may be marked with an optional priority field of increasing integers (default |
| 105 | +priority 0): |
| 106 | + |
| 107 | +``` |
| 108 | +{ |
| 109 | + "kind": "Table", |
| 110 | + "apiVersion": "meta.k8s.io/v1alpha1", |
| 111 | + "headers": [ |
| 112 | + ... |
| 113 | + {"name": "Node Name", "type": "string", "description": "The node the pod is scheduled on, empty if the pod is not yet scheduled", "priority": 1}, |
| 114 | + ... |
| 115 | + ], |
| 116 | + ... |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +To allow label columns, and to enable integrators to build effective UIs, each row may contain an `object` field that |
| 121 | +is either `PartialObjectMetadata` (a standard object containing only ObjectMeta) or the object itself. Clients may request |
| 122 | +this field be set by specifying `?includeObject=None|Metadata|Self` on the query parameter. |
| 123 | + |
| 124 | +``` |
| 125 | +GET ...?includeObject=Metadata |
| 126 | +{ |
| 127 | + "kind": "Table", |
| 128 | + "apiVersion": "meta.k8s.io/v1alpha1", |
| 129 | + "items": [ |
| 130 | + ... |
| 131 | + {"cells": [...], "object": {"kind": "PartialObjectMetadata", "apiVersion":"meta.k8s.io/v1alpha1", "metadata": {"name": "pod1", "namespace": "pod2", "labels": {"a": "1"}, ...}}, |
| 132 | + ... |
| 133 | + ] |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +The `Metadata` value would be the default. Clients that wish to print in an advanced manner may use `Self` to get the full |
| 138 | +object and perform arbitrary transformations. |
| 139 | + |
| 140 | +All fields on the server side are candidates for translation and localization changes can be delivered more |
| 141 | +quickly and to all clients. |
| 142 | + |
| 143 | +Third-party resources can more easily implement `get` in this fashion - instead of web dashboards and |
| 144 | +`kubectl` both implementing their own logic to parse a particular version of Swagger or OpenAPI, the server |
| 145 | +component performs the transformation. The server encapsulates the details of printing. Aggregated resources |
| 146 | +automatically provide this behavior when possible. |
| 147 | + |
| 148 | + |
| 149 | +### Specific features in `kubectl get` |
| 150 | + |
| 151 | +Feature | Implementation |
| 152 | +--- | --- |
| 153 | +sort-by | Continue to implement client-side (no server side sort planned) |
| 154 | +custom-column (jsonpath) | Implement client-side by requesting object `?includeObject=Self` and parsing |
| 155 | +custom-column (label) | Implement client-side by getting labels from metadata returned with each row |
| 156 | +show-kind | Implement client-side by using the discovery info associated with the object (rather than being returned by server) |
| 157 | +template | Implement client-side, bypass receiving table output and get raw objects |
| 158 | +watch | Request Table output via the watch endpoint |
| 159 | +export | Implement client-side, bypass receiving table output and get exported object |
| 160 | +wide | Server should indicate which columns are "additional" via a field on on the header column - client then shows those columns if it wants to |
| 161 | +color (proposed) | Rows which should be highlighted should have a semantic field on the row - e.g. `alert: [{type: Warning, message: "This pod has been deleted"}]`. Cells could be selected by adding an additional field `alert: [{type: Warning, ..., cells: [0, 1]}]`. |
| 162 | + |
| 163 | + |
| 164 | +## Future considerations |
| 165 | + |
| 166 | +* When we introduce server side paging, Table would be paged similar to how PodList or other types are paged. https://issues.k8s.io/2349 |
| 167 | +* More advanced output could in the future be provided by an external call-out or an aggregation API on the server side. |
| 168 | +* `describe` could be managed on the server as well, with a similar generic format, and external outbound links used to reference other objects. |
| 169 | + |
| 170 | + |
| 171 | +## Migration |
| 172 | + |
| 173 | +Old clients will continue retrieving the primary representation. Clients can begin using the optional `Accept` |
| 174 | +header to indicate they want the simpler version, and if they receive a Table perform the new path, otherwise |
| 175 | +fall back to client side functions. |
| 176 | + |
| 177 | +Server side code would reuse the existing display functions but replace TabWriter with either a structured writer |
| 178 | +or the tabular form. |
| 179 | + |
| 180 | + |
| 181 | +<!-- BEGIN MUNGE: GENERATED_ANALYTICS --> |
| 182 | +[]() |
| 183 | +<!-- END MUNGE: GENERATED_ANALYTICS --> |
0 commit comments