[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 77c2f05d773 2/2: Extend Tramp kubernetes method
From: |
Michael Albinus |
Subject: |
master 77c2f05d773 2/2: Extend Tramp kubernetes method |
Date: |
Fri, 23 Jun 2023 15:49:30 -0400 (EDT) |
branch: master
commit 77c2f05d773271cb59ebfd994b06a4075cacbfa8
Author: Michael Albinus <michael.albinus@gmx.de>
Commit: Michael Albinus <michael.albinus@gmx.de>
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.
---
doc/misc/tramp.texi | 11 +++-
etc/NEWS | 8 +++
lisp/net/tramp-container.el | 145 +++++++++++++++++++++++++++++++++++---------
lisp/net/tramp-sh.el | 10 ++-
lisp/net/tramp.el | 29 ++++++---
5 files changed, 161 insertions(+), 42 deletions(-)
diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi
index eb5c418728e..01f46865a39 100644
--- a/doc/misc/tramp.texi
+++ b/doc/misc/tramp.texi
@@ -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.
diff --git a/etc/NEWS b/etc/NEWS
index 88d432960f3..467ac3ee587 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -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
diff --git a/lisp/net/tramp-container.el b/lisp/net/tramp-container.el
index 473cb1c54b8..6e8d28a3016 100644
--- a/lisp/net/tramp-container.el
+++ b/lisp/net/tramp-container.el
@@ -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.")
diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el
index da34f31fea6..d8231bd5bd2 100644
--- a/lisp/net/tramp-sh.el
+++ b/lisp/net/tramp-sh.el
@@ -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")))
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index e7928e4e1f4..cbd4e1611eb 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -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