Extend Tramp kubernetes method

* doc/misc/tramp.texi (Inline methods): Adapt kubernetes method.

* etc/NEWS: Describe changes in Tramp kubernetes method.

* lisp/net/tramp-container.el (tramp-kubernetes-context)
(tramp-kubernetes-namespace): New defcustoms.
(tramp-kubernetes--completion-function): Extend for CONTAINER.POD
syntax.
(tramp-kubernetes--host-name-regexp): New defconst.
(tramp-kubernetes--container, tramp-kubernetes--pod)
(tramp-kubernetes--current-context): New defuns.
(tramp-kubernetes--current-context-data): Simplify.
(tramp-kubernetes--context-namespace): New defun.
(tramp-methods) <kubernetes>: Respect container, context and
namespace.  (Bug#59797)
(tramp-container-connection-local-default-kubernetes-variables):
New defconst.  Set respective connection-local variables.

* lisp/net/tramp-sh.el (tramp-config-check): New variable.
(tramp-open-connection-setup-interactive-shell): Use it.

* lisp/net/tramp.el (tramp-methods): Adapt docstring.
(tramp-extra-expand-args): New defvar.
(tramp-expand-args): Use it.
This commit is contained in:
Michael Albinus
2023-06-23 21:26:14 +02:00
parent 1c499c18af
commit 77c2f05d77
5 changed files with 161 additions and 42 deletions

View File

@@ -922,8 +922,15 @@ if desired.
@cindex @option{kubernetes} method
Integration for containers in Kubernetes pods. The host name is a pod
name returned by @samp{kubectl get pods}. The first container in a
pod is used.
name returned by @samp{kubectl get pods}, or
@samp{@var{container}.@var{pod}} if an explicit container name shall
be used. Otherwise, the first container in a pod is used.
@vindex tramp-kubernetes-context
@vindex tramp-kubernetes-namespace
If another Kubernetes context or namespace shall be used, configure
the user options @code{tramp-kubernetes-context} and
@code{tramp-kubernetes-namespace}.
This method does not support user names.

View File

@@ -224,6 +224,14 @@ point is not in a comment or a string. It is by default bound to
They allow accessing system containers provided by Toolbox or
sandboxes provided by Flatpak.
+++
*** Connection method "kubernetes" supports now optional container name.
The host name for Kubernetes connections can be of kind [CONTAINER.]POD,
in order to specify a dedicated container. If there is just the pod
name, the first container in the pod is taken. The new user options
'tramp-kubernetes-context' and 'tramp-kubernetes-namespace' allow to
access pods with different context or namespace but the default one.
+++
*** Rename 'tramp-use-ssh-controlmaster-options' to 'tramp-use-connection-share'.
The old name still exists as obsolete variable alias. This user

View File

@@ -37,19 +37,20 @@
;; C-x C-f /podman:USER@CONTAINER:/path/to/file
;;
;; Where:
;; USER is the user on the container to connect as (optional)
;; CONTAINER is the container to connect to
;; USER is the user on the container to connect as (optional).
;; CONTAINER is the container to connect to.
;;
;;
;;
;; Open file in a Kubernetes container:
;;
;; C-x C-f /kubernetes:POD:/path/to/file
;; C-x C-f /kubernetes:[CONTAINER.]POD:/path/to/file
;;
;; Where:
;; POD is the pod to connect to.
;; By default, the first container in that pod will be
;; used.
;; POD is the pod to connect to.
;; CONTAINER is the container to connect to (optional).
;; By default, the first container in that pod will
;; be used.
;;
;; Completion for POD and accessing it operate in the current
;; namespace, use this command to change it:
@@ -63,7 +64,7 @@
;; C-x C-f /toolbox:CONTAINER:/path/to/file
;;
;; Where:
;; CONTAINER is the container to connect to (optional)
;; CONTAINER is the container to connect to (optional).
;;
;; If the container is not running, it is started. If no container is
;; specified, the default Toolbox container is used.
@@ -106,6 +107,20 @@
:type '(choice (const "kubectl")
(string)))
(defcustom tramp-kubernetes-context nil
"Context of Kubernetes.
If it is nil, the default context will be used."
:group 'tramp
:version "30.1"
:type '(choice (const :tag "Use default" nil)
(string)))
(defcustom tramp-kubernetes-namespace "default"
"Namespace of Kubernetes."
:group 'tramp
:version "30.1"
:type 'string)
;;;###tramp-autoload
(defcustom tramp-toolbox-program "toolbox"
"Name of the Toolbox client program."
@@ -172,29 +187,83 @@ This function is used by `tramp-set-completion-function', please
see its function help for a description of the format."
(when-let ((default-directory tramp-compat-temporary-file-directory)
(raw-list (shell-command-to-string
(concat tramp-kubernetes-program
" get pods --no-headers "
"-o custom-columns=NAME:.metadata.name")))
(names (split-string raw-list "\n" 'omit)))
(mapcar (lambda (name) (list nil name)) (delq nil names))))
(concat
tramp-kubernetes-program " "
(tramp-kubernetes--context-namespace nil)
" get pods --no-headers"
;; We separate pods by "|". Inside a pod,
;; its name is separated from the containers
;; by ":". Containers are separated by ",".
" -o jsonpath='{range .items[*]}{\"|\"}{.metadata.name}"
"{\":\"}{range .spec.containers[*]}{.name}{\",\"}"
"{end}{end}'")))
(lines (split-string raw-list "|" 'omit)))
(let (names)
(dolist (line lines)
(setq line (split-string line ":" 'omit))
;; Pod name.
(push (car line) names)
;; Container names.
(dolist (elt (split-string (cadr line) "," 'omit))
(push (concat elt "." (car line)) names)))
(mapcar (lambda (name) (list nil name)) (delq nil names)))))
(defconst tramp-kubernetes--host-name-regexp
(rx (? (group (regexp tramp-host-regexp)) ".")
(group (regexp tramp-host-regexp)))
"The CONTAINER.POD syntax of kubernetes host names in Tramp.")
;;;###tramp-autoload
(defun tramp-kubernetes--container (vec)
"Extract the container name from a kubernetes host name in VEC."
(or (let ((host (tramp-file-name-host vec)))
(and (string-match tramp-kubernetes--host-name-regexp host)
(match-string 1 host)))
""))
;;;###tramp-autoload
(defun tramp-kubernetes--pod (vec)
"Extract the pod name from a kubernetes host name in VEC."
(or (let ((host (tramp-file-name-host vec)))
(and (string-match tramp-kubernetes--host-name-regexp host)
(match-string 2 host)))
""))
(defun tramp-kubernetes--current-context (vec)
"Return Kubernetes current context.
Obey `tramp-kubernetes-context'"
(or tramp-kubernetes-context
(with-tramp-connection-property nil "current-context"
(with-temp-buffer
(when (zerop
(tramp-call-process
vec tramp-kubernetes-program nil t nil
"config" "current-context"))
(goto-char (point-min))
(buffer-substring (point) (line-end-position)))))))
(defun tramp-kubernetes--current-context-data (vec)
"Return Kubernetes current context data as JSON string."
(with-temp-buffer
(when (zerop
(tramp-call-process
vec tramp-kubernetes-program nil t nil
"config" "current-context"))
(goto-char (point-min))
(let ((current-context (buffer-substring (point) (line-end-position))))
(erase-buffer)
(when (zerop
(tramp-call-process
vec tramp-kubernetes-program nil t nil
"config" "view" "-o"
(format
"jsonpath='{.contexts[?(@.name == \"%s\")]}'" current-context)))
(buffer-string))))))
(when-let ((current-context (tramp-kubernetes--current-context vec)))
(with-temp-buffer
(when (zerop
(tramp-call-process
vec tramp-kubernetes-program nil t nil
"config" "view" "-o"
(format
"jsonpath='{.contexts[?(@.name == \"%s\")]}'" current-context)))
(buffer-string)))))
;;;###tramp-autoload
(defun tramp-kubernetes--context-namespace (vec)
"The kubectl options for context and namespace."
(mapconcat
#'identity
`(,(when-let ((context (tramp-kubernetes--current-context vec)))
(format "--context=%s" context))
,(when tramp-kubernetes-namespace
(format "--namespace=%s" tramp-kubernetes-namespace)))
" "))
;;;###tramp-autoload
(defun tramp-toolbox--completion-function (&rest _args)
@@ -275,12 +344,13 @@ see its function help for a description of the format."
(add-to-list 'tramp-methods
`(,tramp-kubernetes-method
(tramp-login-program ,tramp-kubernetes-program)
(tramp-login-args (("exec")
(tramp-login-args (("%x") ; context and namespace.
("exec")
("-c" "%a") ; container.
("%h")
("-it")
("--")
("%l")))
(tramp-config-check tramp-kubernetes--current-context-data)
(tramp-direct-async (,tramp-default-remote-shell "-c"))
(tramp-remote-shell ,tramp-default-remote-shell)
(tramp-remote-shell-login ("-l"))
@@ -334,6 +404,23 @@ see its function help for a description of the format."
;; Default connection-local variables for Tramp.
(defconst tramp-container-connection-local-default-kubernetes-variables
'((tramp-config-check . tramp-kubernetes--current-context-data)
;; This variable will be eval'ed in `tramp-expand-args'.
(tramp-extra-expand-args
. (?a (tramp-kubernetes--container (car tramp-current-connection))
?h (tramp-kubernetes--pod (car tramp-current-connection))
?x (tramp-kubernetes--context-namespace (car tramp-current-connection)))))
"Default connection-local variables for remote kubernetes connections.")
(connection-local-set-profile-variables
'tramp-container-connection-local-default-kubernetes-profile
tramp-container-connection-local-default-kubernetes-variables)
(connection-local-set-profiles
`(:application tramp :protocol ,tramp-kubernetes-method)
'tramp-container-connection-local-default-kubernetes-profile)
(defconst tramp-container-connection-local-default-flatpak-variables
`((tramp-remote-path . ,(cons "/app/bin" tramp-remote-path)))
"Default connection-local variables for remote flatpak connections.")

View File

@@ -4324,6 +4324,14 @@ seconds. If not, it produces an error message with the given ERROR-ARGS."
(apply #'tramp-error-with-buffer
(tramp-get-connection-buffer vec) vec 'file-error error-args)))))
(defvar tramp-config-check nil
"A function to be called with one argument, VEC.
It should return a string which is used to check, whether the
configuration of the remote host has been changed (which would
require to flush the cache data). This string is kept as
connection property \"config-check-data\".
This variable is intended as connection-local variable.")
(defun tramp-open-connection-setup-interactive-shell (proc vec)
"Set up an interactive shell.
Mainly sets the prompt and the echo correctly. PROC is the shell
@@ -4370,7 +4378,7 @@ process to set up. VEC specifies the connection."
vec "uname"
(tramp-send-command-and-read vec "echo \\\"`uname -sr`\\\""))))
(config-check-function
(tramp-get-method-parameter vec 'tramp-config-check))
(buffer-local-value 'tramp-config-check (process-buffer proc)))
(old-config-check
(and config-check-function
(tramp-get-connection-property vec "config-check-data")))

View File

@@ -300,13 +300,6 @@ pair of the form (KEY VALUE). The following KEYs are defined:
and container methods do. If it is a list of strings, they
are used to construct the remote command.
* `tramp-config-check'
A function to be called with one argument, VEC. It should
return a string which is used to check, whether the
configuration of the remote host has been changed (which
would require to flush the cache data). This string is kept
as connection property \"config-check-data\".
* `tramp-copy-program'
This specifies the name of the program to use for remotely copying
the file; this might be the absolute filename of scp or the name of
@@ -4954,14 +4947,30 @@ Do not set it manually, it is used buffer-local in `tramp-get-lock-pid'.")
;; Result.
target-alist))
(defvar tramp-extra-expand-args nil
"Method specific arguments.")
(defun tramp-expand-args (vec parameter &rest spec-list)
"Expand login arguments as given by PARAMETER in `tramp-methods'.
PARAMETER is a symbol like `tramp-login-args', denoting a list of
list of strings from `tramp-methods', containing %-sequences for
substitution. SPEC-LIST is a list of char/value pairs used for
`format-spec-make'."
substitution.
SPEC-LIST is a list of char/value pairs used for
`format-spec-make'. It is appended by `tramp-extra-expand-args',
a connection-local variable."
(let ((args (tramp-get-method-parameter vec parameter))
(spec (apply 'format-spec-make spec-list)))
(extra-spec-list
(mapcar
#'eval
(buffer-local-value
'tramp-extra-expand-args (tramp-get-connection-buffer vec))))
spec)
;; Merge both spec lists. Remove duplicate entries.
(while spec-list
(unless (member (car spec-list) extra-spec-list)
(setq extra-spec-list (append (take 2 spec-list) extra-spec-list)))
(setq spec-list (cddr spec-list)))
(setq spec (apply #'format-spec-make extra-spec-list))
;; Expand format spec.
(flatten-tree
(mapcar