branch: externals/kubed
commit 64be034b72f2b2d9c29d609dd0eceda080d9b754
Author: Eshel Yaron <m...@eshelyaron.com>
Commit: Eshel Yaron <m...@eshelyaron.com>

    Fix handling of context names with non-alphanumeric characters
    
    Hex-encode most non-alphanumeric characters in kubectl context names
    in Tramp file names.  This allows our Tramp method to work with
    context names that contain characters which are not allowed in the
    host name part of Tramp file names, such as ':'.
    
    This (hopefully) fixes the issue reported at
    https://lists.sr.ht/~eshel/kubed-devel/%3c87ldqz70a6....@gmail.com%3E
    
    * kubed-common.el (kubed-tramp-method): Bump to v2.
    * kubed-tramp.el (kubed-tramp--unhex)
    (kubed-tramp--decode-context-name)
    (kubed-tramp--v2-context): New functions.
    (kubed-tramp-context, kubed-tramp-enable): Adjust.
    * kubed.el (kubed--hex-encoding-table)
    (kubed--hex-allowed-chars-table): New constants.
    (kubed--encode-context-name): New function.
    (kubed-remote-file-name): Use it.
---
 kubed-common.el |   4 +--
 kubed-tramp.el  | 104 +++++++++++++++++++++++++++++++++++++++++---------------
 kubed.el        |  25 +++++++++++++-
 3 files changed, 102 insertions(+), 31 deletions(-)

diff --git a/kubed-common.el b/kubed-common.el
index 15e43090ca..f7cb240ed5 100644
--- a/kubed-common.el
+++ b/kubed-common.el
@@ -12,8 +12,8 @@
 
 ;;; Code:
 
-(defvar kubed-tramp-method "kubedv1"    ;Versioned, for compatibility.
-  ;; (find-file "/kubedv1:CONTEXT%NAMESPACE%POD%CONTAINER:/some/file")
+(defvar kubed-tramp-method "kubedv2"    ;Versioned, for compatibility.
+  ;; (find-file "/kubedv2:CONTEXT%NAMESPACE%POD%CONTAINER:/some/file")
   "Name of the Kubed Tramp method.")
 
 (defcustom kubed-kubectl-program "kubectl"
diff --git a/kubed-tramp.el b/kubed-tramp.el
index f355c49e31..9e6fbac4e4 100644
--- a/kubed-tramp.el
+++ b/kubed-tramp.el
@@ -22,6 +22,31 @@
 (require 'kubed-common)
 (require 'tramp)
 
+(defun kubed-tramp--unhex (x)
+  ;; Simplified version of `url-unhex'.
+  (if (> x ?9) (+ 10 (- x ?A)) (- x ?0)))
+
+(defun kubed-tramp--decode-context-name (str)
+  ;; Adopted from `url-unhex-string'.
+  (let ((tmp "") (case-fold-search nil))
+    (while (string-match "[.][0-9A-F][0-9A-F]" str)
+      (let* ((start (match-beginning 0))
+            (ch1 (kubed-tramp--unhex (elt str (+ start 1))))
+            (code (+ (* 16 ch1)
+                     (kubed-tramp--unhex (elt str (+ start 2))))))
+       (setq tmp (concat tmp (substring str 0 start) (byte-to-string code))
+             str (substring str (match-end 0)))))
+    (concat tmp str)))
+
+(defun kubed-tramp--v2-context (vec)
+  "Extract the context name from a kubernetes host name in VEC."
+  (or (when-let ((host (and vec (tramp-file-name-host vec))))
+        (shell-quote-argument
+         (decode-coding-string
+         (kubed-tramp--decode-context-name (nth 0 (split-string host "%")))
+         'utf-8)))
+      ""))
+
 (defun kubed-tramp--context (vec)
   "Extract the context name from a kubernetes host name in VEC."
   (or (when-let ((host (and vec (tramp-file-name-host vec))))
@@ -49,8 +74,14 @@
 ;;;###autoload
 (defun kubed-tramp-context (file-name)
   "Extract `kubectl' context from Kubed Tramp remote file name FILE-NAME."
-  (nth 0 (split-string
-          (tramp-file-name-host (tramp-dissect-file-name file-name)) "%")))
+  ;; TODO: Dispatch based on method version.  The following is intended
+  ;; for v2, although it also works for v1 in most cases.
+  (decode-coding-string
+   (kubed-tramp--decode-context-name
+    (nth 0 (split-string
+            (tramp-file-name-host (tramp-dissect-file-name file-name))
+            "%")))
+   'utf-8))
 
 ;;;###autoload
 (defun kubed-tramp-namespace (file-name)
@@ -69,32 +100,49 @@
   "Enable Kubed integration with Tramp."
   (when (boundp 'tramp-extra-expand-args) ; Tramp 2.7+
 
-    (setf (alist-get kubed-tramp-method tramp-methods nil nil #'string=)
-          `((tramp-login-program ,kubed-kubectl-program)
-            (tramp-login-args (("exec")
-                               ("--context" "%x")
-                               ("--namespace" "%y")
-                               ("-c" "%a")
-                               ("%h")
-                               ("-it")
-                               ("--")
-                              ("%l")))
-            (tramp-direct-async (,tramp-default-remote-shell "-c"))
-            (tramp-remote-shell ,tramp-default-remote-shell)
-            (tramp-remote-shell-login ("-l"))
-            (tramp-remote-shell-args ("-i" "-c"))))
-
-    (connection-local-set-profile-variables
-     'kubed-tramp-connection-local-default-profile
-     '((tramp-extra-expand-args
-        ?a (kubed-tramp--container (car tramp-current-connection))
-        ?h (kubed-tramp--pod       (car tramp-current-connection))
-        ?x (kubed-tramp--context   (car tramp-current-connection))
-        ?y (kubed-tramp--namespace (car tramp-current-connection)))))
-
-    (connection-local-set-profiles
-     `(:application tramp :protocol ,kubed-tramp-method)
-     'kubed-tramp-connection-local-default-profile)))
+    (let ((params `((tramp-login-program ,kubed-kubectl-program)
+                    (tramp-login-args (("exec")
+                                       ("--context" "%x")
+                                       ("--namespace" "%y")
+                                       ("-c" "%a")
+                                       ("%h")
+                                       ("-it")
+                                       ("--")
+                                      ("%l")))
+                    (tramp-direct-async (,tramp-default-remote-shell "-c"))
+                    (tramp-remote-shell ,tramp-default-remote-shell)
+                    (tramp-remote-shell-login ("-l"))
+                    (tramp-remote-shell-args ("-i" "-c")))))
+
+      ;; Old version.
+      (setf (alist-get "kubedv1" tramp-methods nil nil #'string=) params)
+
+      (connection-local-set-profile-variables
+       'kubed-tramp-connection-local-default-profile
+       '((tramp-extra-expand-args
+          ?a (kubed-tramp--container  (car tramp-current-connection))
+          ?h (kubed-tramp--pod        (car tramp-current-connection))
+          ?x (kubed-tramp--context    (car tramp-current-connection))
+          ?y (kubed-tramp--namespace  (car tramp-current-connection)))))
+
+      (connection-local-set-profiles
+       '(:application tramp :protocol "kubedv1")
+       'kubed-tramp-connection-local-default-profile)
+
+      ;; New version.
+      (setf (alist-get kubed-tramp-method tramp-methods nil nil #'string=) 
params)
+
+      (connection-local-set-profile-variables
+       'kubed-tramp-v2-connection-local-default-profile
+       '((tramp-extra-expand-args
+          ?a (kubed-tramp--container  (car tramp-current-connection))
+          ?h (kubed-tramp--pod        (car tramp-current-connection))
+          ?x (kubed-tramp--v2-context (car tramp-current-connection))
+          ?y (kubed-tramp--namespace  (car tramp-current-connection)))))
+
+      (connection-local-set-profiles
+       `(:application tramp :protocol ,kubed-tramp-method)
+       'kubed-tramp-v2-connection-local-default-profile))))
 
 ;;;###autoload (with-eval-after-load 'tramp (kubed-tramp-enable))
 
diff --git a/kubed.el b/kubed.el
index 9a9ec6a91c..2a32d36850 100644
--- a/kubed.el
+++ b/kubed.el
@@ -1673,10 +1673,33 @@ Interactively, use the current context.  With a prefix 
argument
                "\\)")
        1))
 
+(defconst kubed--hex-encoding-table
+  (let ((vec (make-vector 256 nil)))
+    (dotimes (byte 256) (aset vec byte (format ".%02X" byte))) vec))
+
+(defconst kubed--hex-allowed-chars-table
+  (let ((vec (make-vector 256 nil)))
+    (dolist (byte '( ?a ?b ?c ?d ?e ?f ?g ?h ?i ?j ?k ?l ?m ?n ?o ?p ?q ?r ?s 
?t ?u ?v ?w ?x ?y ?z
+                     ?A ?B ?C ?D ?E ?F ?G ?H ?I ?J ?K ?L ?M ?N ?O ?P ?Q ?R ?S 
?T ?U ?V ?W ?X ?Y ?Z
+                     ?0 ?1 ?2 ?3 ?4 ?5 ?6 ?7 ?8 ?9
+                     ?- ?_))
+      (ignore-errors (aset vec byte t)))
+    vec))
+
+(defun kubed--encode-context-name (str)
+  ;; Adopted from `url-hexify-string'.
+  (mapconcat (lambda (byte)
+              (if (aref kubed--hex-allowed-chars-table byte)
+                  (char-to-string byte)
+                (aref kubed--hex-encoding-table byte)))
+            (if (multibyte-string-p str)
+                (encode-coding-string str 'utf-8)
+              str)))
+
 (defun kubed-remote-file-name (context namespace pod &optional file-name)
   "Return remote FILE-NAME for POD in NAMESPACE and CONTEXT."
   (concat "/" kubed-tramp-method ":"
-          context "%" namespace "%" pod
+          (kubed--encode-context-name context) "%" namespace "%" pod
           "%" (kubed-read-container pod "Container" t context namespace)
           ":" file-name))
 

Reply via email to