diff options
| author | Michael Albinus | 2023-09-16 20:35:35 +0200 |
|---|---|---|
| committer | Michael Albinus | 2023-09-16 20:35:35 +0200 |
| commit | f0f4cbfe8760bb04f9676c31020642acdcb03b16 (patch) | |
| tree | e51c53fd779d3c06798bb024c5388c7b9f2e3445 | |
| parent | be70f4ab0e2dabf0ae7519bae8197b1e5f315f54 (diff) | |
| download | emacs-f0f4cbfe8760bb04f9676c31020642acdcb03b16.tar.gz emacs-f0f4cbfe8760bb04f9676c31020642acdcb03b16.zip | |
Make "kubernetes" multi-hop completion capable in Tramp
* lisp/net/tramp-container.el (tramp-skeleton-completion-function):
New defmacro.
(tramp-container--completion-function): Use it.
(tramp-kubernetes--completion-function): Use METHOD as argument.
Use `tramp-skeleton-completion-function'.
(tramp-skeleton-kubernetes-vector): New defmacro.
(tramp-kubernetes--current-context)
(tramp-kubernetes--current-context-data): Use it.
(tramp-completion-multi-hop-methods): Add "kubernetes".
| -rw-r--r-- | lisp/net/tramp-container.el | 170 |
1 files changed, 102 insertions, 68 deletions
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el index 2e9ddc64bd3..bef3b04b371 100644 --- a/lisp/net/tramp-container.el +++ b/lisp/net/tramp-container.el | |||
| @@ -158,6 +158,26 @@ If it is nil, the default context will be used." | |||
| 158 | "Tramp method name to use to connect to Flatpak sandboxes.") | 158 | "Tramp method name to use to connect to Flatpak sandboxes.") |
| 159 | 159 | ||
| 160 | ;;;###tramp-autoload | 160 | ;;;###tramp-autoload |
| 161 | (defmacro tramp-skeleton-completion-function (method &rest body) | ||
| 162 | "Skeleton for `tramp-*-completion-function' with multi-hop support. | ||
| 163 | BODY is the backend specific code." | ||
| 164 | (declare (indent 1) (debug t)) | ||
| 165 | `(let* ((default-directory | ||
| 166 | (or (and (member ,method tramp-completion-multi-hop-methods) | ||
| 167 | tramp--last-hop-directory) | ||
| 168 | tramp-compat-temporary-file-directory)) | ||
| 169 | (program (tramp-get-method-parameter | ||
| 170 | (make-tramp-file-name :method ,method) 'tramp-login-program)) | ||
| 171 | (vec (when (tramp-tramp-file-p default-directory) | ||
| 172 | (tramp-dissect-file-name default-directory))) | ||
| 173 | non-essential) | ||
| 174 | ;; We don't use connection properties, because this information | ||
| 175 | ;; shouldn't be kept persistently. | ||
| 176 | (with-tramp-file-property | ||
| 177 | vec (concat "/" ,method ":") "user-host-completions" | ||
| 178 | ,@body))) | ||
| 179 | |||
| 180 | ;;;###tramp-autoload | ||
| 161 | (defun tramp-container--completion-function (method) | 181 | (defun tramp-container--completion-function (method) |
| 162 | "List running containers available for connection. | 182 | "List running containers available for connection. |
| 163 | METHOD is the Tramp method to be used for \"ps\", either | 183 | METHOD is the Tramp method to be used for \"ps\", either |
| @@ -165,62 +185,51 @@ METHOD is the Tramp method to be used for \"ps\", either | |||
| 165 | 185 | ||
| 166 | This function is used by `tramp-set-completion-function', please | 186 | This function is used by `tramp-set-completion-function', please |
| 167 | see its function help for a description of the format." | 187 | see its function help for a description of the format." |
| 168 | (let ((default-directory | 188 | (tramp-skeleton-completion-function method |
| 169 | (or (and (member method tramp-completion-multi-hop-methods) | 189 | (when-let ((raw-list |
| 170 | tramp--last-hop-directory) | 190 | (shell-command-to-string |
| 171 | tramp-compat-temporary-file-directory)) | 191 | (concat program " ps --format '{{.ID}}\t{{.Names}}'"))) |
| 172 | (program (tramp-get-method-parameter | 192 | (lines (split-string raw-list "\n" 'omit)) |
| 173 | (make-tramp-file-name :method method) 'tramp-login-program)) | 193 | (names |
| 174 | non-essential) | 194 | (mapcar |
| 175 | ;; We don't use connection properties, because this information | 195 | (lambda (line) |
| 176 | ;; shouldn't be kept persistently. | 196 | (when (string-match |
| 177 | (with-tramp-file-property | 197 | (rx bol (group (1+ nonl)) |
| 178 | (when (tramp-tramp-file-p default-directory) | 198 | "\t" (? (group (1+ nonl))) eol) |
| 179 | (tramp-dissect-file-name default-directory)) | 199 | line) |
| 180 | (concat "/" method ":") "user-host-completions" | 200 | (or (match-string 2 line) (match-string 1 line)))) |
| 181 | (when-let ((raw-list | 201 | lines))) |
| 182 | (shell-command-to-string | 202 | (mapcar (lambda (name) (list nil name)) (delq nil names))))) |
| 183 | (concat program " ps --format '{{.ID}}\t{{.Names}}'"))) | ||
| 184 | (lines (split-string raw-list "\n" 'omit)) | ||
| 185 | (names | ||
| 186 | (mapcar | ||
| 187 | (lambda (line) | ||
| 188 | (when (string-match | ||
| 189 | (rx bol (group (1+ nonl)) | ||
| 190 | "\t" (? (group (1+ nonl))) eol) | ||
| 191 | line) | ||
| 192 | (or (match-string 2 line) (match-string 1 line)))) | ||
| 193 | lines))) | ||
| 194 | (mapcar (lambda (name) (list nil name)) (delq nil names)))))) | ||
| 195 | 203 | ||
| 196 | ;;;###tramp-autoload | 204 | ;;;###tramp-autoload |
| 197 | (defun tramp-kubernetes--completion-function (&rest _args) | 205 | (defun tramp-kubernetes--completion-function (method) |
| 198 | "List Kubernetes pods available for connection. | 206 | "List Kubernetes pods available for connection. |
| 199 | 207 | ||
| 200 | This function is used by `tramp-set-completion-function', please | 208 | This function is used by `tramp-set-completion-function', please |
| 201 | see its function help for a description of the format." | 209 | see its function help for a description of the format." |
| 202 | (when-let ((default-directory tramp-compat-temporary-file-directory) | 210 | (tramp-skeleton-completion-function method |
| 203 | (raw-list (shell-command-to-string | 211 | (when-let ((raw-list |
| 204 | (concat | 212 | (shell-command-to-string |
| 205 | tramp-kubernetes-program " " | 213 | (concat |
| 206 | (tramp-kubernetes--context-namespace nil) | 214 | program " " |
| 207 | " get pods --no-headers" | 215 | (tramp-kubernetes--context-namespace vec) |
| 208 | ;; We separate pods by "|". Inside a pod, | 216 | " get pods --no-headers" |
| 209 | ;; its name is separated from the containers | 217 | ;; We separate pods by "|". Inside a pod, its name |
| 210 | ;; by ":". Containers are separated by ",". | 218 | ;; is separated from the containers by ":". |
| 211 | " -o jsonpath='{range .items[*]}{\"|\"}{.metadata.name}" | 219 | ;; Containers are separated by ",". |
| 212 | "{\":\"}{range .spec.containers[*]}{.name}{\",\"}" | 220 | " -o jsonpath='{range .items[*]}{\"|\"}{.metadata.name}" |
| 213 | "{end}{end}'"))) | 221 | "{\":\"}{range .spec.containers[*]}{.name}{\",\"}" |
| 214 | (lines (split-string raw-list "|" 'omit))) | 222 | "{end}{end}'"))) |
| 215 | (let (names) | 223 | (lines (split-string raw-list "|" 'omit))) |
| 216 | (dolist (line lines) | 224 | (let (names) |
| 217 | (setq line (split-string line ":" 'omit)) | 225 | (dolist (line lines) |
| 218 | ;; Pod name. | 226 | (setq line (split-string line ":" 'omit)) |
| 219 | (push (car line) names) | 227 | ;; Pod name. |
| 220 | ;; Container names. | 228 | (push (car line) names) |
| 221 | (dolist (elt (split-string (cadr line) "," 'omit)) | 229 | ;; Container names. |
| 222 | (push (concat elt "." (car line)) names))) | 230 | (dolist (elt (split-string (cadr line) "," 'omit)) |
| 223 | (mapcar (lambda (name) (list nil name)) (delq nil names))))) | 231 | (push (concat elt "." (car line)) names))) |
| 232 | (mapcar (lambda (name) (list nil name)) (delq nil names)))))) | ||
| 224 | 233 | ||
| 225 | (defconst tramp-kubernetes--host-name-regexp | 234 | (defconst tramp-kubernetes--host-name-regexp |
| 226 | (rx (? (group (regexp tramp-host-regexp)) ".") | 235 | (rx (? (group (regexp tramp-host-regexp)) ".") |
| @@ -243,30 +252,53 @@ see its function help for a description of the format." | |||
| 243 | (match-string 2 host))) | 252 | (match-string 2 host))) |
| 244 | "")) | 253 | "")) |
| 245 | 254 | ||
| 255 | ;; We must change `vec' and `default-directory' to the previous hop, | ||
| 256 | ;; in order to run `process-file' in a proper environment. | ||
| 257 | (defmacro tramp-skeleton-kubernetes-vector (vec &rest body) | ||
| 258 | "Skeleton for `tramp-kubernetes--current-context*' with multi-hop support. | ||
| 259 | BODY is the backend specific code." | ||
| 260 | (declare (indent 1) (debug t)) | ||
| 261 | `(let* ((vec | ||
| 262 | (cond | ||
| 263 | ((null ,vec) tramp-null-hop) | ||
| 264 | ((equal (tramp-file-name-method ,vec) tramp-kubernetes-method) | ||
| 265 | (if (tramp-file-name-hop ,vec) | ||
| 266 | (tramp-dissect-hop-name (tramp-file-name-hop ,vec)) | ||
| 267 | tramp-null-hop)) | ||
| 268 | (t ,vec))) | ||
| 269 | (default-directory | ||
| 270 | (if (equal vec tramp-null-hop) | ||
| 271 | tramp-compat-temporary-file-directory | ||
| 272 | (tramp-make-tramp-file-name vec "/")))) | ||
| 273 | ,@body)) | ||
| 274 | |||
| 246 | (defun tramp-kubernetes--current-context (vec) | 275 | (defun tramp-kubernetes--current-context (vec) |
| 247 | "Return Kubernetes current context. | 276 | "Return Kubernetes current context. |
| 248 | Obey `tramp-kubernetes-context'" | 277 | Obey `tramp-kubernetes-context'" |
| 249 | (or tramp-kubernetes-context | 278 | (or tramp-kubernetes-context |
| 250 | (with-tramp-connection-property nil "current-context" | 279 | (tramp-skeleton-kubernetes-vector vec |
| 251 | (with-temp-buffer | 280 | (with-tramp-file-property |
| 252 | (when (zerop | 281 | vec (concat "/" tramp-kubernetes-method ":") "current-context" |
| 253 | (tramp-call-process | 282 | (with-temp-buffer |
| 254 | vec tramp-kubernetes-program nil t nil | 283 | (when (zerop |
| 255 | "config" "current-context")) | 284 | (process-file |
| 256 | (goto-char (point-min)) | 285 | tramp-kubernetes-program nil t nil |
| 257 | (buffer-substring (point) (line-end-position))))))) | 286 | "config" "current-context")) |
| 287 | (goto-char (point-min)) | ||
| 288 | (buffer-substring (point) (line-end-position)))))))) | ||
| 258 | 289 | ||
| 259 | (defun tramp-kubernetes--current-context-data (vec) | 290 | (defun tramp-kubernetes--current-context-data (vec) |
| 260 | "Return Kubernetes current context data as JSON string." | 291 | "Return Kubernetes current context data as JSON string." |
| 261 | (when-let ((current-context (tramp-kubernetes--current-context vec))) | 292 | (when-let ((current-context (tramp-kubernetes--current-context vec))) |
| 262 | (with-temp-buffer | 293 | (tramp-skeleton-kubernetes-vector vec |
| 263 | (when (zerop | 294 | (with-temp-buffer |
| 264 | (tramp-call-process | 295 | (when (zerop |
| 265 | vec tramp-kubernetes-program nil t nil | 296 | (process-file |
| 266 | "config" "view" "-o" | 297 | tramp-kubernetes-program nil t nil |
| 267 | (format | 298 | "config" "view" "-o" |
| 268 | "jsonpath='{.contexts[?(@.name == \"%s\")]}'" current-context))) | 299 | (format |
| 269 | (buffer-string))))) | 300 | "jsonpath='{.contexts[?(@.name == \"%s\")]}'" current-context))) |
| 301 | (buffer-string)))))) | ||
| 270 | 302 | ||
| 271 | ;;;###tramp-autoload | 303 | ;;;###tramp-autoload |
| 272 | (defun tramp-kubernetes--context-namespace (vec) | 304 | (defun tramp-kubernetes--context-namespace (vec) |
| @@ -404,7 +436,7 @@ see its function help for a description of the format." | |||
| 404 | 436 | ||
| 405 | (tramp-set-completion-function | 437 | (tramp-set-completion-function |
| 406 | tramp-kubernetes-method | 438 | tramp-kubernetes-method |
| 407 | '((tramp-kubernetes--completion-function ""))) | 439 | `((tramp-kubernetes--completion-function ,tramp-kubernetes-method))) |
| 408 | 440 | ||
| 409 | (tramp-set-completion-function | 441 | (tramp-set-completion-function |
| 410 | tramp-toolbox-method | 442 | tramp-toolbox-method |
| @@ -416,6 +448,7 @@ see its function help for a description of the format." | |||
| 416 | 448 | ||
| 417 | (add-to-list 'tramp-completion-multi-hop-methods tramp-docker-method) | 449 | (add-to-list 'tramp-completion-multi-hop-methods tramp-docker-method) |
| 418 | (add-to-list 'tramp-completion-multi-hop-methods tramp-podman-method) | 450 | (add-to-list 'tramp-completion-multi-hop-methods tramp-podman-method) |
| 451 | (add-to-list 'tramp-completion-multi-hop-methods tramp-kubernetes-method) | ||
| 419 | 452 | ||
| 420 | ;; Default connection-local variables for Tramp. | 453 | ;; Default connection-local variables for Tramp. |
| 421 | 454 | ||
| @@ -425,7 +458,8 @@ see its function help for a description of the format." | |||
| 425 | (tramp-extra-expand-args | 458 | (tramp-extra-expand-args |
| 426 | . (?a (tramp-kubernetes--container (car tramp-current-connection)) | 459 | . (?a (tramp-kubernetes--container (car tramp-current-connection)) |
| 427 | ?h (tramp-kubernetes--pod (car tramp-current-connection)) | 460 | ?h (tramp-kubernetes--pod (car tramp-current-connection)) |
| 428 | ?x (tramp-kubernetes--context-namespace (car tramp-current-connection))))) | 461 | ?x (tramp-kubernetes--context-namespace |
| 462 | (car tramp-current-connection))))) | ||
| 429 | "Default connection-local variables for remote kubernetes connections.") | 463 | "Default connection-local variables for remote kubernetes connections.") |
| 430 | 464 | ||
| 431 | (connection-local-set-profile-variables | 465 | (connection-local-set-profile-variables |