~eshel/kubed

2e45b42397a454b09bc3168b2fd003d73ef7f336 — Eshel Yaron 2 months ago 5f23c41
New commands for managing Kubernetes API proxies

* kubed.el (kubed--proxy-alist): New variable.
(kubed-stop-proxy, kubed-proxy): New commands, use it.
(kubed-prefix-map, kubed-menu-map): Bind 'kubed-proxy'.
* kubed-transient.el (kubed-transient-proxy): New transient.
(kubed-transient): Bind it.
* NEWS.org: Announce new commands.
3 files changed, 121 insertions(+), 1 deletions(-)

M NEWS.org
M kubed-transient.el
M kubed.el
M NEWS.org => NEWS.org +9 -1
@@ 10,7 10,7 @@ for Kubernetes.
For further details, see the Kubed manual:
[[https://eshelyaron.com/sweep.html][https://eshelyaron.com/kubed.html]].

* Version 0.4.1 on in development
* Version 0.4.1 in development

** New command and transient menu for scaling deployments



@@ 18,6 18,14 @@ You can now scale Kubernetes deployment with Kubed, directly with
~kubed-scale-deployment~, from the deployments table buffer, or via
the new transient menu ~kubed-transient-scale-deployment~.

** New commands and transient menu for managing Kubernetes API proxies

You can now use Kubed to create proxy servers that give access to the
Kubernetes API server.  The commands ~kubed-proxy~ and
~kubed-stop-proxy~ start and stop proxy servers, respectively.  The
new transient menu ~kubed-transient-proxy~ lets specify options when
starting a proxy, such as the local port to use.

* Version 0.4.0 on 2024-08-23

** ~kubed-list-update~ is now bound to ~g~ in resource table buffers.

M kubed-transient.el => kubed-transient.el +26 -0
@@ 70,6 70,7 @@ Return an RFC3339 string representation of the selected date."
    ("P" "Patch"   kubed-transient-patch)
    ("R" "Rollout" kubed-transient-rollout)]
   [("$" "Scale" kubed-transient-scale-deployment)
    (":" "Proxy" kubed-transient-proxy)
    ("E" "Explain" kubed-explain)
    ("!" "Command line" kubed-kubectl-command)]])



@@ 176,6 177,31 @@ defaults to \"RESOURCEs\"."
  (transient-setup 'kubed-transient-scale-deployment nil nil
                   :scope '("scale" "deployment")))

;;;###autoload (autoload 'kubed-transient-proxy "kubed-transient" nil t)
(transient-define-prefix kubed-transient-proxy ()
  "Create a proxy between localhost and the Kubernetes API server."
  ["Kubernetes Proxy\n"
   ["Action"
    (":" "Start proxy" kubed-proxy)
    ("|" "Stop proxy" kubed-stop-proxy)
    ("!" "Command line" kubed-kubectl-command)]
   ["Options"
    ("-C" "Context" "--context="
     :prompt "Context" :reader kubed-transient-read-context)
    ("-a" "Address" "--address="
     :prompt "Local proxy address: ")
    ("-p" "Port" "--port="
     :prompt "Local proxy port: " :reader transient-read-number-N0)
    ("-w" "WWW directory" "--www="
     :prompt "Local WWW directory: " :reader transient-read-existing-directory)
    ("-P" "WWW prefix" "--www-prefix="
     :prompt "WWW prefix: ")
    ("-A" "API prefix" "--api-prefix="
     :prompt "API prefix: ")]]
  (interactive)
  (transient-setup 'kubed-transient-proxy nil nil
                   :scope '("proxy")))

;;;###autoload (autoload 'kubed-transient-rollout "kubed-transient" nil t)
(transient-define-prefix kubed-transient-rollout ()
  "Manage Kubernetes deployments."

M kubed.el => kubed.el +86 -0
@@ 3158,6 3158,90 @@ for CONTEXT."
    (user-error "Patching `%s' failed" name))
  (message "Applying patch to `%s'...  Done." name))

(defvar kubed--proxy-alist nil
  "Alist associating `kubectl' context names and their proxy processes.")

;;;###autoload
(defun kubed-proxy
    (&optional addr port api-prefix www-dir www-prefix context)
  "Start a web server on localhost proxying the Kubernetes API server.

ADDR and PORT are the local address and port to bind the proxy server
to.  They default to 127.0.0.1 and 8001, respectively.  API-PREFIX is a
prefix under which to proxy the Kubernetes API.  WWW-DIR is a local
directory to serve under WWW-PREFIX, which defaults to \"/static/\".
Lastly, CONTEXT is the the `kubectl' context to use, defaulting to the
local context."
  (interactive
   (let ((addr nil) (port nil)
         (api-prefix nil) (www-dir nil) (www-prefix nil)
         (context nil))
     (dolist (arg (kubed-transient-args 'kubed-transient-proxy))
       (cond
        ((string-match "--address=\\(.+\\)" arg)
         (setq addr (match-string 1 arg)))
        ((string-match "--port=\\(.+\\)" arg)
         (setq port (string-to-number (match-string 1 arg))))
        ((string-match "--api-prefix=\\(.+\\)" arg)
         (setq api-prefix (match-string 1 arg)))
        ((string-match "--www-dir=\\(.+\\)" arg)
         (setq www-dir (match-string 1 arg)))
        ((string-match "--www-prefix=\\(.+\\)" arg)
         (setq www-prefix (match-string 1 arg)))
        ((string-match "--context=\\(.+\\)" arg)
         (setq context (match-string 1 arg)))))
     (unless context
       (setq context
             (let ((cxt (kubed-local-context)))
               (if (equal current-prefix-arg '(16))
                   (kubed-read-context "Context" cxt)
                 cxt))))
     (list addr port api-prefix www-dir www-prefix context)))
  (let ((context (or context (kubed-local-context))))
    (when-let ((proc (alist-get context kubed--proxy-alist
                                nil nil #'string=))
               ((process-live-p proc)))
      (if (y-or-n-p (concat "Proxy already running for context `" context
                            "'.  Stop it and start new proxy?"))
          (kill-process proc)
        (user-error "Proxy already running for context `%s'" context))
      (message "Stopped proxy for context `%s'." context))
    (setf (alist-get context kubed--proxy-alist nil nil #'string=)
          (apply #'start-process (format "*kubed-proxy[%s]*" context) nil
                 kubed-kubectl-program "proxy"
                 "--context" context
                 (append
                  (when addr (list "--address" addr))
                  (when port (list "--port" (number-to-string port)))
                  (when api-prefix (list "--api-prefix" api-prefix))
                  (when www-dir (list "--www-dir" www-dir))
                  (when www-prefix (list "--www-prefix" www-prefix)))))
    (message "Started proxy for context `%s'." context)))

;;;###autoload
(defun kubed-stop-proxy (&optional context)
  "Stop local proxy for context CONTEXT, defaulting to the local context."
  (interactive
   (let ((context nil))
     (dolist (arg (kubed-transient-args 'kubed-transient-proxy))
       (cond
        ((string-match "--context=\\(.+\\)" arg)
         (setq context (match-string 1 arg)))))
     (unless context
       (setq context
             (let ((cxt (kubed-local-context)))
               (if (equal current-prefix-arg '(16))
                   (kubed-read-context "Context" cxt)
                 cxt))))
     (list context)))
  (let ((context (or context (kubed-local-context))))
    (if-let ((proc (alist-get context kubed--proxy-alist
                              nil nil #'string=))
             ((process-live-p proc)))
        (kill-process proc)
      (user-error "No proxy running for context `%s'" context))
    (message "Stopped proxy for context `%s'." context)))

(with-eval-after-load 'help-mode
  ;; Wait for `help-mode' to define `help-xref'.  It's always loaded by
  ;; the time we actually need it in `kubed-explain'.


@@ 3347,6 3431,7 @@ Interactively, prompt for COMMAND with completion for `kubectl' arguments."
  "=" #'kubed-diff
  "E" #'kubed-explain
  "P" #'kubed-patch
  ":" #'kubed-proxy
  "RET" #'kubed-display-resource
  "!" #'kubed-kubectl-command)



@@ 3365,6 3450,7 @@ Interactively, prompt for COMMAND with completion for `kubectl' arguments."
  "<cronjob>"          '("Cron Jobs..."          . kubed-cronjob-menu-map)
  "<ingressclass>"     '("Ingress Classes..."    . kubed-ingressclass-menu-map)
  "<ingress>"          '("Ingresses..."          . kubed-ingress-menu-map)
  "<proxy>"            '("Proxy Kubernetes API"  . kubed-proxy)
  "<patch>"            '("Patch Resource"        . kubed-patch)
  "<diff>"             '("Diff Config with Live" . kubed-diff)
  "<kubectl-command>"  '("Invoke kubectl"        . kubed-kubectl-command)