aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Albinus2023-09-16 20:35:35 +0200
committerMichael Albinus2023-09-16 20:35:35 +0200
commitf0f4cbfe8760bb04f9676c31020642acdcb03b16 (patch)
treee51c53fd779d3c06798bb024c5388c7b9f2e3445
parentbe70f4ab0e2dabf0ae7519bae8197b1e5f315f54 (diff)
downloademacs-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.el170
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.
163BODY 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.
163METHOD is the Tramp method to be used for \"ps\", either 183METHOD 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
166This function is used by `tramp-set-completion-function', please 186This function is used by `tramp-set-completion-function', please
167see its function help for a description of the format." 187see 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
200This function is used by `tramp-set-completion-function', please 208This function is used by `tramp-set-completion-function', please
201see its function help for a description of the format." 209see 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.
259BODY 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.
248Obey `tramp-kubernetes-context'" 277Obey `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