From 5db20badf42797a7a52d3bbb88c0c7a3cc47c7e4 Mon Sep 17 00:00:00 2001 From: Eshel Yaron Date: Fri, 26 Jul 2024 23:18:55 +0200 Subject: [PATCH] kubed.el: Add optional per-column value-formatting function --- lisp/net/kubed.el | 159 ++++++++++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 53 deletions(-) diff --git a/lisp/net/kubed.el b/lisp/net/kubed.el index 838e9e84b58..538169da4c8 100644 --- a/lisp/net/kubed.el +++ b/lisp/net/kubed.el @@ -122,20 +122,21 @@ interacting with Kubernetes RESOURCEs: This macro also defines a prefix keymap, `kubed-RESOURCE-prefix-map', with bindings for the above commands. -PROPERTIES is a list of elements (PROPERTY JSON-PATH WIDTH SORT . ATTRS) -that specify properties of RESOURCEs. PROPERTY is the name of the -property, as a symbol; JSON-PATH is a JSONPath expression that evaluates -to the value of PROPERTY when applied to the full JSON representation of -a RESOURCE. WIDTH, SORT and ATTRS are optional and can be omitted. -WIDTH is used as the default width of the column corresponding to -PROPERTY in RESOURCEs list buffers; SORT is sort predicate, a function -that takes two values of PROPERTY as strings and return non-nil if the -first should sort before the second; ATTRS is a plist of additional -attributes of the PROPERTY column, see `tabulated-list-format' for -available attributes. For example, (phase \".status.phase\" 10) says -that RESOURCE has a `phase' property at JSONPath \".status.phase\" whose -values are typically 10 columns wide. The first property in PROPERTIES, -is used to annotate completion candidates when prompting for a RESOURCE. +PROPERTIES is a list of lists (PROP JSON-PATH WIDTH SORT FORMAT . ATTRS) +that specify properties of RESOURCEs. PROP is the name of the property, +as a symbol; JSON-PATH is a JSONPath expression that evaluates to the +value of PROP when applied to the JSON representation of a RESOURCE. +WIDTH, SORT, FORMAT and ATTRS are optional and can be omitted. WIDTH is +used as the default width of the column corresponding to PROP in +RESOURCEs list buffers; SORT is sort predicate, a function that takes +two values of PROP as strings and return non-nil if the first should +sort before the second; FORMAT is a function that takes a value of PROP +and formats it; and ATTRS is a plist of additional attributes of the +PROP column, see `tabulated-list-format' for available attributes. For +example, (phase \".status.phase\" 10) says that RESOURCE has a `phase' +property at JSONPath \".status.phase\" whose values are typically 10 +columns wide. The first property in PROPERTIES, is used to annotate +completion candidates when prompting for a RESOURCE. COMMANDS is a list of elements (COMMAND KEYS DOC-PREFIX . BODY) that define commands for RESOURCE list buffers. COMMAND is a symbol @@ -316,42 +317,60 @@ Other keyword arguments that go between PROPERTIES and COMMANDS are: (cadr p))) properties))) ","))) - :sentinel (lambda (_proc status) - (cond - ((string= status "finished\n") - (let (new offsets eol) - (with-current-buffer ,out-name - (goto-char (point-min)) - (setq eol (pos-eol)) - (while (re-search-forward "[^ ]+" eol t) - (push (1- (match-beginning 0)) offsets)) - (setq offsets (nreverse offsets)) - (forward-char 1) - (while (not (eobp)) - (let ((cols nil) - (beg (car offsets)) - (ends (cdr offsets))) - (dolist (end ends) - (push (string-trim (buffer-substring - (+ (point) beg) - (+ (point) end))) - cols) - (setq beg end)) - (push (string-trim (buffer-substring - (+ (point) beg) - (pos-eol))) - cols) - (push (nreverse cols) new)) - (forward-line 1))) - (setq ,list-var new - ,proc-var nil) - (run-hooks ',hook-var) - (message ,(format "Updated Kubernetes %Ss." resource)))) - ((string= status "exited abnormally with code 1\n") - (with-current-buffer ,err-name - (goto-char (point-max)) - (insert "\n" status)) - (display-buffer ,err-name)))))) + :sentinel + (lambda (_proc status) + (cond + ((string= status "finished\n") + (let (new offsets eol) + (with-current-buffer ,out-name + (goto-char (point-min)) + (setq eol (pos-eol)) + (while (re-search-forward "[^ ]+" eol t) + (push (1- (match-beginning 0)) offsets)) + (setq offsets (nreverse offsets)) + (forward-char 1) + (while (not (eobp)) + (let ((cols nil) + (beg (car offsets)) + (ends (append (cdr offsets) (list (- (pos-eol) (point)))))) + ,@(let ((read-col + (lambda (p) + ;; Fresh list to avoid circles. + (list `(push ,(if-let ((f (nth 4 p))) + `(funcall ,f (string-trim (buffer-substring + (+ (point) beg) + (+ (point) (car ends))))) + `(string-trim (buffer-substring + (+ (point) beg) + (+ (point) (car ends))))) + cols) + '(setq beg (pop ends)))))) + (if namespaced + ;; Resource is namespaced, generate + ;; code that is sensitive to + ;; `kubed-all-namespaces-mode'. + `((if kubed-all-namespaces-mode + (progn + ,@(mapcan + read-col + ;; Two nils, one for the + ;; name column, another + ;; for the namespace. + `(nil nil . ,properties))) + ,@(mapcan read-col `(nil . ,properties)))) + ;; Non-namespaced. + (mapcan read-col `(nil . ,properties)))) + (push (nreverse cols) new)) + (forward-line 1))) + (setq ,list-var new + ,proc-var nil) + (run-hooks ',hook-var) + (message ,(format "Updated Kubernetes %Ss." resource)))) + ((string= status "exited abnormally with code 1\n") + (with-current-buffer ,err-name + (goto-char (point-max)) + (insert "\n" status)) + (display-buffer ,err-name)))))) (minibuffer-message ,(format "Updating Kubernetes %Ss..." resource))) (defun ,affx-fun (,plrl-var) @@ -748,7 +767,7 @@ Optional argument DEFAULT is the minibuffer default argument." resource) (funcall ,sorter (aref (cadr l) c) (aref (cadr r) c))) `(funcall ,sorter (aref (cadr l) ,i) (aref (cadr r) ,i)))) t)) - (nthcdr 4 p)) + (nthcdr 5 p)) res)) (reverse res))) @@ -843,7 +862,30 @@ Optional argument DEFAULT is the minibuffer default argument." resource) ;;;###autoload (autoload 'kubed-create-pod "kubed" nil t) ;;;###autoload (autoload 'kubed-pod-prefix-map "kubed" nil t 'keymap) (kubed-define-resource pod - ((phase ".status.phase" 10) (starttime ".status.startTime" 20)) + ((phase ".status.phase" 10 + nil ; sorting function + (lambda (ph) + (if-let ((face (pcase ph + ;; TODO: Define/derive bespoke faces. + ("Pending" 'warning) + ("Running" 'success) + ("Succeeded" 'shadow) + ("Failed" 'error)))) + (propertize ph 'face face) + ph))) + (ready ".status.containerStatuses[?(.ready==true)].name" 6 + (lambda (l r) (< (string-to-number l) (string-to-number r))) + (lambda (cs) + (if (string= cs "") "0" + (number-to-string (1+ (seq-count (lambda (c) (= c ?,)) cs))))) + :right-align t) + (total ".status.containerStatuses[*].name" 6 + (lambda (l r) (< (string-to-number l) (string-to-number r))) + (lambda (cs) + (if (string= cs "") "0" + (number-to-string (1+ (seq-count (lambda (c) (= c ?,)) cs))))) + :right-align t) + (starttime ".status.startTime" 20)) :prefix ("L" #'kubed-logs "A" #'kubed-attach "X" #'kubed-exec @@ -880,7 +922,15 @@ Optional argument DEFAULT is the minibuffer default argument." resource) ;;;###autoload (autoload 'kubed-create-namespace "kubed" nil t) ;;;###autoload (autoload 'kubed-namespace-prefix-map "kubed" nil t 'keymap) (kubed-define-resource namespace - ((phase ".status.phase" 10) + ((phase ".status.phase" 10 + nil ; sorting function + (lambda (ph) + (if-let ((face (pcase ph + ;; TODO: Define/derive bespoke faces. + ("Active" 'success) + ("Terminating" 'shadow)))) + (propertize ph 'face face) + ph))) (creationtimestamp ".metadata.creationTimestamp" 20)) :namespaced nil :prefix ("S" #'kubed-set-namespace) @@ -948,6 +998,7 @@ Optional argument DEFAULT is the minibuffer default argument." resource) (kubed-define-resource deployment ((reps ".status.replicas" 4 (lambda (l r) (< (string-to-number l) (string-to-number r))) + nil ; formatting function :right-align t) (creationtimestamp ".metadata.creationTimestamp" 20)) :create @@ -1003,6 +1054,7 @@ optional command to run in the images." (kubed-define-resource replicaset ((reps ".status.replicas" 4 (lambda (l r) (< (string-to-number l) (string-to-number r))) + nil ; formatting function :right-align t) (ownerkind ".metadata.ownerReferences[0].kind" 12) (ownername ".metadata.ownerReferences[0].name" 16) @@ -1017,6 +1069,7 @@ optional command to run in the images." (kubed-define-resource statefulset ((reps ".status.replicas" 4 (lambda (l r) (< (string-to-number l) (string-to-number r))) + nil ; formatting function :right-align t) (ownerkind ".metadata.ownerReferences[0].kind" 12) (ownername ".metadata.ownerReferences[0].name" 16) -- 2.39.5