emacs-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]