]> git.bundesbrandschatzamt.de Git - emacsconfig.git/commitdiff
update kubed and kubedoc
authorAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Fri, 27 Dec 2024 11:20:14 +0000 (12:20 +0100)
committerAndreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
Fri, 27 Dec 2024 11:20:14 +0000 (12:20 +0100)
Signed-off-by: Andreas Gerler <baronNOSPAMbundesbrandschatzamt.de>
17 files changed:
elpa/kubed-0.4.2.signed [new file with mode: 0644]
elpa/kubed-0.4.2/.elpaignore [new file with mode: 0644]
elpa/kubed-0.4.2/LICENSE [new file with mode: 0644]
elpa/kubed-0.4.2/NEWS.org [new file with mode: 0644]
elpa/kubed-0.4.2/cobra.el [new file with mode: 0644]
elpa/kubed-0.4.2/dir [new file with mode: 0644]
elpa/kubed-0.4.2/kubed-autoloads.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed-common.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed-pkg.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed-tramp.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed-transient.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed.el [new file with mode: 0644]
elpa/kubed-0.4.2/kubed.info [new file with mode: 0644]
elpa/kubed-0.4.2/kubed.texi [new file with mode: 0644]
elpa/kubedoc-20240108.1404/kubedoc-autoloads.el [new file with mode: 0644]
elpa/kubedoc-20240108.1404/kubedoc-pkg.el [new file with mode: 0644]
elpa/kubedoc-20240108.1404/kubedoc.el [new file with mode: 0644]

diff --git a/elpa/kubed-0.4.2.signed b/elpa/kubed-0.4.2.signed
new file mode 100644 (file)
index 0000000..d8ef617
--- /dev/null
@@ -0,0 +1 @@
+Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasignNOSPAMelpa.gnu.org> (trust undefined) created at 2024-10-22T11:05:01+0200 using EDDSA
\ No newline at end of file
diff --git a/elpa/kubed-0.4.2/.elpaignore b/elpa/kubed-0.4.2/.elpaignore
new file mode 100644 (file)
index 0000000..206be02
--- /dev/null
@@ -0,0 +1,3 @@
+.dir-locals.el
+README.md
+*.png
diff --git a/elpa/kubed-0.4.2/LICENSE b/elpa/kubed-0.4.2/LICENSE
new file mode 100644 (file)
index 0000000..77abeb3
--- /dev/null
@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright 2024 Eshel Yaron
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/elpa/kubed-0.4.2/NEWS.org b/elpa/kubed-0.4.2/NEWS.org
new file mode 100644 (file)
index 0000000..57ee664
--- /dev/null
@@ -0,0 +1,130 @@
+#+title:                 Kubed NEWS -- history of user-visible changes
+#+author:                Eshel Yaron
+#+email:                 meNOSPAMeshelyaron.com
+#+language:              en
+#+options:               ':t toc:nil num:nil ^:{}
+
+This file contains the release notes for Kubed, a rich Emacs interface
+for Kubernetes.
+
+For further details, see the Kubed manual:
+[[https://eshelyaron.com/sweep.html][https://eshelyaron.com/kubed.html]].
+
+* Version 0.4.2 on 2024-10-22
+
+** New user option 'kubed-list-mode-line-format'
+
+This user option lets you customize the indicator that Kubed shows in
+the mode line of resource table buffers when update is in progress.
+
+** Fix Tramp method autoload issue
+
+This version arranges for the Kubed Tramp integration to be loaded
+earlier, which circumvents the "Method ‘kubedv1’ is not known" error
+that some users have experienced.
+
+* Version 0.4.1 on 2024-09-23
+
+** New command and transient menu for scaling deployments
+
+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.
+
+Previously, this command was bound to the slightly less convenient
+capital ~G~.  This shadows the default binding of ~revert-buffer~,
+which you can still invoke via ~C-x x g~.
+
+** Introduce resource type specific bindings in resource table help transients.
+
+The help transient menu that you get with ~?~ in resource table
+buffers now includes type-specific bindings, such as the ~F~ key for
+forwarding a local port to a pod in the Kubed pods buffer.
+
+** New transient menu ~kubed-transient-logs~ for showing container logs.
+
+** Improved Tramp integration.
+
+Kubed now uses a bespoke Tramp method to open Dired and Shell buffers
+inside Kubernetes pods.  The main advantage of this method is the
+ability to work with multiple ~kubectl~ contexts simultaneously.  This
+method requires Tramp 2.7 or later, Kubed will complain if you try to
+start remote Dired or Shell with an earlier Tramp version.
+
+* Version 0.3.2 on 2024-08-15
+
+** Improved support for multiple contexts and namespaces
+
+Each buffer now has a local context and namespace, which acts as the
+default/current context and namespace when you invoke Kubed commands
+from that buffer.  See new manual section "Context and Namespace" for
+more details.
+
+** New filter operators ~<~ and ~>~ in resource tables.
+
+These operators let you filter line with columns that are less than or
+greater than a given value.
+
+** New command for applying changes to displayed resource.
+
+You can now edit displayed resource YAML buffers after you select a
+resource with ~RET~ in the table buffer, and apply your changes to the
+live resource with ~C-c C-c~.
+
+* Version 0.3.1 on 2024-08-06
+
+** New command for fitting column width to content in resource tables.
+
+** New transient menu showing help in resource table buffers.
+
+* Version 0.3.0 on 2024-08-05
+
+** New command for copying name of resource at point to kill ring.
+
+** New commands for running ~kubectl~ command on resource at point.
+
+** Support bookmarking resource list and resource details buffers.
+
+** New command for jumping from resource details buffer to list buffer.
+
+** Fix installation on Doom Emacs.
+
+** New transient menu for displaying resources.
+
+** Support multiple resource list buffers with different namespaces or contexts.
+
+* Version 0.2.0 on 2024-08-02
+
+** Ensure compatibility with Emacs 29.
+
+** Add right-click context menus in resource tables.
+
+** New menu-bar "Kubernetes" menu.
+
+** New commands for restarting and watching deployments.
+
+** Add filtering capability to all resource tables.
+
+** New cron job commands: suspend, resume, and start job now.
+
+** New commands for patching resources.
+
+** New useful columns in services table and deployments table.
+
+** Use multi-column layout in transient menus.
+
+* Version 0.1.0 on 2024-07-28
+
+Initial release.
diff --git a/elpa/kubed-0.4.2/cobra.el b/elpa/kubed-0.4.2/cobra.el
new file mode 100644 (file)
index 0000000..91c975a
--- /dev/null
@@ -0,0 +1,143 @@
+;;; cobra.el --- Complete Cobra command lines   -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Keywords: tools
+
+;;; Commentary:
+
+;; Cobra is a popular Golang framework for CLI programs.  This library
+;; defines function `cobra-read-command-line', which helps you read a
+;; command line for a program that uses Cobra, with completion.
+;; Prominent examples of Cobra programs are `kubectl' and `docker'.
+
+;;; Code:
+
+(defvar cobra--cache nil)
+
+(defun cobra-completion-table (executable s p a)
+  "Completion table for command lines that invoke EXECUTABLE.
+
+Perform completion action A on string S with predicate P."
+  (let ((start 0))
+    (while (string-match "[[:space:]=]" s start)
+      (setq start (match-end 0)))
+    (if (eq a 'metadata)
+        `(metadata
+          (category . cobra-command-line)
+          (affixation-function
+           . ,(lambda (cands)
+                (let ((max (seq-max
+                            (cons 0 (mapcar #'string-width cands)))))
+                  (mapcar
+                   (lambda (cand)
+                     (list
+                      cand ""
+                      (if-let
+                          ((desc (get-text-property
+                                  0 'cobra-argument-description
+                                  cand)))
+                          (concat
+                           (make-string (1+ (- max (string-width cand))) ?\s)
+                           (propertize desc 'face 'completions-annotations))
+                        "")))
+                   cands)))))
+      (let* ((lines
+              (cdr
+               (if (string= s (car cobra--cache))
+                   ;; Cache hit.
+                   cobra--cache
+                 (setq
+                  cobra--cache
+                  (cons s
+                        (apply #'process-lines-ignore-status
+                               executable "__complete"
+                               (let ((args (cdr (split-string-and-unquote s))))
+                                 (if (string-suffix-p " " s)
+                                     ;; Restore omitted empty argument.
+                                     (nconc args '(""))
+                                   args))))))))
+             (code nil)
+             (comps (seq-take-while
+                     (lambda (line)
+                       (not (and (string-match "^:\\([0-9]+\\)$" line)
+                                 (setq code (string-to-number
+                                             (match-string 1 line))))))
+                     lines)))
+        ;; `code' encodes "completion directives", as follows:
+        ;; #b000001: An error occurred, ignore completions.
+        ;; #b000010: Don't add space after completion.  (TODO.)
+        ;; #b000100: Don't fall back to file completion.
+        ;; #b001000: Completions are really file extension filters.
+        ;; #b010000: Complete directory names.
+        ;; #b100000: Preserve completions order.
+        (when (and code (zerop (logand 1 code)))
+          ;; Error bit in unset, proceed.
+          (if (= #b100 (logand #b100 code))
+              ;; No file name completion.
+              (if (eq (car-safe a) 'boundaries)
+                  `(boundaries
+                    ,start . ,(and (string-match "[[:space:]=]" (cdr a))
+                                   (match-beginning 0)))
+                (let ((table
+                       (mapcar
+                        ;; Annotate completion candidates.
+                        (lambda (comp)
+                          (pcase (split-string comp "\t" t)
+                            (`(,c ,d . ,_)
+                             (propertize
+                              c 'cobra-argument-description
+                              ;; Only keep first sentence.
+                              (car (split-string d "\\." t))))
+                            (`(,c . ,_)  c)))
+                        comps)))
+                  (if a (complete-with-action a table (substring s start) p)
+                    ;; `try-completion'.
+                    (let ((comp (complete-with-action a table (substring s start) p)))
+                      (if (stringp comp) (concat (substring s 0 start) comp) comp)))))
+            ;; File name completion.
+            (setq p
+                  (cond
+                   ((= #b1000 (logand #b1000 code))
+                    ;; `comps' are valid extensions.
+                    (lambda (f)
+                      (or (file-directory-p f)
+                          (when (string-match "\\.[^.]*\\'" f)
+                            (member (substring f (1+ (match-beginning 0)))
+                                    comps)))))
+                   ((= #b10000 (logand #b10000 code))
+                    ;; Directory name completion.
+                    #'file-directory-p)))
+            (if (eq (car-safe a) 'boundaries)
+                ;; Find nested boundaries.
+                (let* ((suf (cdr a))
+                       (bounds (completion-boundaries
+                                (substring s start) #'completion-file-name-table p
+                                (substring suf 0 (string-match "[[:space:]=]" suf)))))
+                  `(boundaries ,(+ (car bounds) start) . ,(cdr bounds)))
+              (if a (complete-with-action a #'completion-file-name-table
+                                          (substring s start) p)
+                (let ((comp (complete-with-action a #'completion-file-name-table
+                                                  (substring s start) p)))
+                  (if (stringp comp) (concat (substring s 0 start) comp) comp))))))))))
+
+;;;###autoload
+(defun cobra-read-command-line (prompt initial &optional hist)
+  "Prompt with PROMPT for a command line starting with INITIAL.
+
+Optional argument HIST is the name of the history list variable to use,
+if it is nil or omitted, it defaults to `shell-command-history'."
+  (let ((exec (car (split-string-and-unquote (car (ensure-list initial))))))
+    (minibuffer-with-setup-hook
+        (:append (lambda ()
+                   (let ((map (make-sparse-keymap)))
+                     ;; Ensure SPC is self-inserting.
+                     (keymap-unset map "SPC")
+                     (use-local-map
+                      (make-composed-keymap map (current-local-map))))))
+      (completing-read prompt (apply-partially #'cobra-completion-table exec)
+                       nil nil initial (or hist 'shell-command-history)))))
+
+(provide 'cobra)
+;;; cobra.el ends here
diff --git a/elpa/kubed-0.4.2/dir b/elpa/kubed-0.4.2/dir
new file mode 100644 (file)
index 0000000..c2a3104
--- /dev/null
@@ -0,0 +1,18 @@
+This is the file .../info/dir, which contains the
+topmost node of the Info hierarchy, called (dir)Top.
+The first time you invoke Info you start off looking at this node.
+\1f
+File: dir,     Node: Top       This is the top of the INFO tree
+
+  This (the Directory node) gives a menu of major topics.
+  Typing "q" exits, "H" lists all Info commands, "d" returns here,
+  "h" gives a primer for first-timers,
+  "mEmacs<Return>" visits the Emacs manual, etc.
+
+  In Emacs, you can click mouse button 2 on a menu item or cross reference
+  to select it.
+
+* Menu:
+
+Emacs
+* Kubed: (kubed).               Kubernetes, Emacs, done!
diff --git a/elpa/kubed-0.4.2/kubed-autoloads.el b/elpa/kubed-0.4.2/kubed-autoloads.el
new file mode 100644 (file)
index 0000000..fb1201a
--- /dev/null
@@ -0,0 +1,494 @@
+;;; kubed-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+\f
+;;; Generated autoloads from cobra.el
+
+(autoload 'cobra-read-command-line "cobra" "\
+Prompt with PROMPT for a command line starting with INITIAL.
+
+Optional argument HIST is the name of the history list variable to use,
+if it is nil or omitted, it defaults to `shell-command-history'.
+
+(fn PROMPT INITIAL &optional HIST)")
+(register-definition-prefixes "cobra" '("cobra-"))
+
+\f
+;;; Generated autoloads from kubed.el
+
+(autoload 'kubed-display-resource "kubed" "\
+Display Kubernetes RESOURCE of type TYPE in CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCE.  With a prefix argument \\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well.
+
+(fn TYPE RESOURCE CONTEXT &optional NAMESPACE)" t)
+(autoload 'kubed-display-resource-handle-bookmark "kubed" "\
+Display Kubernetes resource according to BOOKMARK.
+
+(fn BOOKMARK)")
+(autoload 'kubed-list-handle-bookmark "kubed" "\
+Display Kubernetes resource according to BOOKMARK.
+
+(fn BOOKMARK)")
+(autoload 'kubed-delete-resources "kubed" "\
+Delete Kubernetes RESOURCES of type TYPE in context CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCES.  With a prefix argument \\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well.
+
+(fn TYPE RESOURCES CONTEXT &optional NAMESPACE)" t)
+(autoload 'kubed-edit-resource "kubed" "\
+Edit Kubernetes RESOURCE of type TYPE in context CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCES.  With a prefix argument \\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well.
+
+(fn TYPE RESOURCE CONTEXT &optional NAMESPACE)" t)
+(autoload 'kubed-define-resource "kubed" "\
+Define Kubernetes RESOURCE with associated PROPERTIES and COMMANDS.
+
+RESOURCE is a symbol corresponding to a Kubernetes resource type, such
+as `pod' or `service'.  This macro defines the following commands for
+interacting with Kubernetes RESOURCEs:
+
+- `kubed-display-RESOURCE': prompt for a RESOURCE and display its
+  description in YAML format.  See also `kubed-yaml-setup-hook'.
+- `kubed-edit-RESOURCE': prompt for a RESOURCE and edit it.
+- `kubed-delete-RESOURCE': prompt for a RESOURCE and delete it.
+- `kubed-list-RESOURCEs': display a buffer listing all RESOURCEs in the
+  current namespace.  The RESOURCEs list buffer uses a dedicated major
+  mode, `kubed-RESOURCEs-mode', which is also defined by this macro.
+- `kubed-create-RESOURCE': create a RESOURCE from a YAML or a JSON file.
+- `kubed-explain-RESOURCEs': show buffer with help about RESOURCEs.
+
+This macro also defines a prefix keymap, `kubed-RESOURCE-prefix-map',
+with bindings for the above commands.
+
+PROPERTIES is a list of lists (PROP JSON-PATH WIDTH SORT FORMAT . ATTRS)
+that specify properties of RESOURCEs.  PROP is the name of the property,
+as a symbol; JSON-PATH is a JSONPath expression that evaluates to the
+value of PROP when applied to the JSON representation of a RESOURCE.
+WIDTH, SORT, FORMAT and ATTRS are optional and can be omitted.  WIDTH is
+used as the default width of the column corresponding to PROP in
+RESOURCEs list buffers; SORT is sort predicate, a function that takes
+two values of PROP as strings and return non-nil if the first should
+sort before the second; FORMAT is a function that takes a value of PROP
+and formats it; and ATTRS is a plist of additional attributes of the
+PROP column, see `tabulated-list-format' for available attributes.  For
+example, (phase \".status.phase\" 10) says that RESOURCE has a `phase'
+property at JSONPath \".status.phase\" whose values are typically 10
+columns wide.  The first property in PROPERTIES, is used to annotate
+completion candidates when prompting for a RESOURCE.
+
+COMMANDS is a list of elements (COMMAND KEYS DOC-PREFIX . BODY) that
+define commands for RESOURCE list buffers.  COMMAND is a symbol
+specifying the suffix of the command name, the full name of the command
+is `kubed-RESOURCEs-COMMAND' (for example, `kubed-pods-shell'); KEYS is
+either a string that specifies a key sequence to bind to the command in
+`kubed-RESOURCEs-mode-map', or nil if the command should not be bound;
+DOC-PREFIX is a string used to construct the docstring of the command,
+this macro appends the string \" Kubernetes RESOURCE at point.\" to it
+to obtain the final docstring; lastly, BODY is the body the command.
+Within BODY, the variable RESOURCE is let-bound to the name of the
+RESOURCE at point.  For example, if RESOURCE is `pod', the following
+COMMANDS element defines a command `kubed-pods-frob' and binds it to the
+key \"f\" in `kubed-pods-mode-map':
+
+  (frob \"f\" \"Frobnicate\"
+        (message \"Preparing...\")
+        (frobnicate-pod pod)
+        (message \"Done.\"))
+
+By default, this macro assumes that RESOURCE is namespaced.  To define a
+namespaceless resource type, put `:namespaced nil' before COMMANDS:
+
+  (kubed-define-resource global-thingy (PROP1 PROP2 ...) :namespaced nil
+    CMD1
+    CMD2
+    ...)
+
+Other keyword arguments that go between PROPERTIES and COMMANDS are:
+
+- `:create (ARGLIST DOCSTRING INTERACTIVE BODY...)': specialize the
+  resource creation command, `kubed-create-RESOURCE'.  ARGLIST,
+  DOCSTRING, INTERACTIVE and BODY have the same meaning as in `defun'.
+- `:prefix ((KEY LABEL DEFINITION)...)': additional keybinding for the
+  prefix keymap `kubed-RESOURCE-prefix-map' and the
+  `kubed-RESOURCE-menu-map' menu.  Each element (KEY LABEL DEFINITION)
+  says to bind KEY to DEFINITION in `kubed-RESOURCE-menu-map', and to
+  add DEFINITION to `kubed-RESOURCE-menu-map' with the label LABEL.
+- `:plural PLURAL': specify plural form of RESOURCE, as a symbol.  If
+  you omit this keyword argument, the plural form defaults to RESOURCE
+  followed by \"s\".
+- `:logs t': generate `kubed-logs-for-RESOURCE' command.
+- `:suffixes (SUFFIX...)': add type-specific transient suffixes to
+  `kubed-list-transient'.
+
+(fn RESOURCE &optional PROPERTIES &rest COMMANDS)" nil t)
+(function-put 'kubed-define-resource 'lisp-indent-function 2)
+ (autoload 'kubed-display-pod "kubed" nil t)
+ (autoload 'kubed-edit-pod "kubed" nil t)
+ (autoload 'kubed-delete-pods "kubed" nil t)
+ (autoload 'kubed-list-pods "kubed" nil t)
+ (autoload 'kubed-create-pod "kubed" nil t)
+ (autoload 'kubed-logs-for-pod "kubed" nil t)
+ (autoload 'kubed-pod-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-namespace "kubed" nil t)
+ (autoload 'kubed-edit-namespace "kubed" nil t)
+ (autoload 'kubed-delete-namespaces "kubed" nil t)
+ (autoload 'kubed-list-namespaces "kubed" nil t)
+ (autoload 'kubed-create-namespace "kubed" nil t)
+ (autoload 'kubed-namespace-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-persistentvolume "kubed" nil t)
+ (autoload 'kubed-edit-persistentvolume "kubed" nil t)
+ (autoload 'kubed-delete-persistentvolumes "kubed" nil t)
+ (autoload 'kubed-list-persistentvolumes "kubed" nil t)
+ (autoload 'kubed-create-persistentvolume "kubed" nil t)
+ (autoload 'kubed-persistentvolume-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-service "kubed" nil t)
+ (autoload 'kubed-edit-service "kubed" nil t)
+ (autoload 'kubed-delete-services "kubed" nil t)
+ (autoload 'kubed-list-services "kubed" nil t)
+ (autoload 'kubed-create-service "kubed" nil t)
+ (autoload 'kubed-logs-for-service "kubed" nil t)
+ (autoload 'kubed-service-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-secret "kubed" nil t)
+ (autoload 'kubed-edit-secret "kubed" nil t)
+ (autoload 'kubed-delete-secrets "kubed" nil t)
+ (autoload 'kubed-list-secrets "kubed" nil t)
+ (autoload 'kubed-create-secret "kubed" nil t)
+ (autoload 'kubed-secret-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-job "kubed" nil t)
+ (autoload 'kubed-edit-job "kubed" nil t)
+ (autoload 'kubed-delete-jobs "kubed" nil t)
+ (autoload 'kubed-list-jobs "kubed" nil t)
+ (autoload 'kubed-create-job "kubed" nil t)
+ (autoload 'kubed-logs-for-job "kubed" nil t)
+ (autoload 'kubed-job-prefix-map "kubed" nil t 'keymap)
+(autoload 'kubed-watch-deployment-status "kubed" "\
+Show and update status of Kubernetes deployment DEP in a dedicate buffer.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.  CB is an optional callback function to call with
+no arguments when the deployment ends.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+
+(fn DEP &optional CONTEXT NAMESPACE CB)" t)
+(autoload 'kubed-scale-deployment "kubed" "\
+Scale deployment DEP in namespace NAMESPACE via CONTEXT to REPS replicas.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+
+(fn DEP REPS &optional CONTEXT NAMESPACE)" t)
+(autoload 'kubed-restart-deployment "kubed" "\
+Restart Kubernetes deployment DEP in namespace NAMESPACE via CONTEXT.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.  CB is an optional callback function to call with
+no arguments when the deployment ends.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+
+(fn DEP &optional CONTEXT NAMESPACE)" t)
+ (autoload 'kubed-display-deployment "kubed" nil t)
+ (autoload 'kubed-edit-deployment "kubed" nil t)
+ (autoload 'kubed-delete-deployments "kubed" nil t)
+ (autoload 'kubed-list-deployments "kubed" nil t)
+ (autoload 'kubed-create-deployment "kubed" nil t)
+ (autoload 'kubed-logs-for-deployment "kubed" nil t)
+ (autoload 'kubed-deployment-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-replicaset "kubed" nil t)
+ (autoload 'kubed-edit-replicaset "kubed" nil t)
+ (autoload 'kubed-delete-replicasets "kubed" nil t)
+ (autoload 'kubed-list-replicasets "kubed" nil t)
+ (autoload 'kubed-create-replicaset "kubed" nil t)
+ (autoload 'kubed-logs-for-replicaset "kubed" nil t)
+ (autoload 'kubed-replicaset-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-statefulset "kubed" nil t)
+ (autoload 'kubed-edit-statefulset "kubed" nil t)
+ (autoload 'kubed-delete-statefulsets "kubed" nil t)
+ (autoload 'kubed-list-statefulsets "kubed" nil t)
+ (autoload 'kubed-create-statefulset "kubed" nil t)
+ (autoload 'kubed-logs-for-statefulset "kubed" nil t)
+ (autoload 'kubed-statefulset-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-cronjob "kubed" nil t)
+ (autoload 'kubed-edit-cronjob "kubed" nil t)
+ (autoload 'kubed-delete-cronjobs "kubed" nil t)
+ (autoload 'kubed-list-cronjobs "kubed" nil t)
+ (autoload 'kubed-create-cronjob "kubed" nil t)
+ (autoload 'kubed-cronjob-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-ingressclass "kubed" nil t)
+ (autoload 'kubed-edit-ingressclass "kubed" nil t)
+ (autoload 'kubed-delete-ingressclasses "kubed" nil t)
+ (autoload 'kubed-list-ingressclasss "kubed" nil t)
+ (autoload 'kubed-create-ingressclass "kubed" nil t)
+ (autoload 'kubed-ingressclass-prefix-map "kubed" nil t 'keymap)
+ (autoload 'kubed-display-ingress "kubed" nil t)
+ (autoload 'kubed-edit-ingress "kubed" nil t)
+ (autoload 'kubed-delete-ingresses "kubed" nil t)
+ (autoload 'kubed-list-ingresss "kubed" nil t)
+ (autoload 'kubed-create-ingress "kubed" nil t)
+ (autoload 'kubed-ingress-prefix-map "kubed" nil t 'keymap)
+(autoload 'kubed-use-context "kubed" "\
+Set current Kubernetes context to CONTEXT.
+
+(fn CONTEXT)" t)
+(autoload 'kubed-rename-context "kubed" "\
+Rename Kubernetes context OLD to NEW.
+
+(fn OLD NEW)" t)
+(autoload 'kubed-display-config "kubed" "\
+Display current Kubernetes client settings in a YAML buffer." t)
+(autoload 'kubed-set-namespace "kubed" "\
+Set default Kubernetes namespace in CONTEXT to NAMESPACE.
+
+Interactively, prompt for NAMESPACE and use the current context.  With a
+prefix argument, prompt for CONTEXT as well.
+
+(fn NAMESPACE &optional CONTEXT)" t)
+(autoload 'kubed-apply "kubed" "\
+Apply CONFIG to Kubernetes resource of kind KIND via CONTEXT.
+
+(fn CONFIG &optional KIND CONTEXT)" t)
+(autoload 'kubed-create "kubed" "\
+Create resource of kind KIND with definition DEFINITION via CONTEXT.
+
+(fn DEFINITION &optional KIND CONTEXT)" t)
+(autoload 'kubed-run "kubed" "\
+Run container IMAGE in a new Kubernetes POD.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context.  NAMESPACE is the namespace in which to create POD,
+defaulting to the current namespace.  PORT is the port to expose,
+defaulting to none.  If ATTACH is non-nil, then attach to the created
+image with a `comint-mode' buffer, and pop to that buffer.  Non-nil
+STDIN says to keep the standard input of the container open; non-nil TTY
+says to allocate a TTY for the container; and non-nil RM says to remove
+the container after it exits.  ENVS is a list of strings \"VAR=VAL\"
+which specify environment variables VAR and values VAL to give them in
+the created container.  ARGS are command line arguments for the
+container command.  If COMMAND is non-nil, ARGS consist of a complete
+command line, that overrides the container command instead of just
+providing it with arguments.
+
+Interactively, prompt for POD and IMAGE.  With a prefix argument, prompt
+for NAMESPACE; with a double prefix argument, also prompt for CONTEXT.
+
+(fn POD IMAGE &optional CONTEXT NAMESPACE PORT ATTACH STDIN TTY RM ENVS COMMAND ARGS)" t)
+(autoload 'kubed-logs "kubed" "\
+Show logs for container CONTAINER in Kubernetes RESOURCE of type TYPE.
+
+Optional argument CONTEXT is the `kubectl' context to use; NAMESPACE is
+the namespace for RESOURCE; CONTAINER is the name of the container in
+RESOURCE to show logs for, or t which specifies all relevant containers.
+
+With a prefix argument, prompt for NAMESPACE; with a double prefix
+argument, also prompt for CONTEXT.
+
+(fn TYPE RESOURCE &optional CONTEXT NAMESPACE CONTAINER FOLLOW LIMIT PREFIX SINCE TAIL TIMESTAMPS)" t)
+(autoload 'kubed-forward-port-to-pod "kubed" "\
+Forward LOCAL-PORT to REMOTE-PORT of Kubernetes pod POD.
+
+(fn POD LOCAL-PORT REMOTE-PORT CONTEXT NAMESPACE)" t)
+(autoload 'kubed-attach "kubed" "\
+Attach to running process in CONTAINER in Kubernetes POD.
+
+Optional argument NAMESPACE is the namespace in which to look for POD.
+Non-nil STDIN says to connect local standard input to remote process.
+Non-nil TTY says to use a TTY for standard input.
+
+Interactively, prompt for POD; if there are multiple pod containers,
+prompt for CONTAINER as well.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+STDIN and TTY are t interactively.
+
+(fn POD &optional CONTAINER CONTEXT NAMESPACE STDIN TTY)" t)
+(autoload 'kubed-diff "kubed" "\
+Display difference between Kubernetes resource DEFINITION and current state.
+
+DEFINITION is either a file name or a buffer.  Interactively, prompt for
+a YAML or JSON file name to use as DEFINITION.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; non-nil INCLUDE-MANAGED (interactively, the prefix
+argument) says to include managed fields in the comparison.
+
+(fn DEFINITION &optional INCLUDE-MANAGED CONTEXT)" t)
+(autoload 'kubed-exec "kubed" "\
+Execute COMMAND with ARGS in CONTAINER in Kubernetes POD.
+
+Optional argument NAMESPACE is the namespace in which to look for POD.
+Non-nil STDIN says to connect local standard input to remote process.
+Non-nil TTY says to use a TTY for standard input.
+
+Interactively, prompt for POD; if there are multiple pod containers,
+prompt for CONTAINER as well.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+STDIN and TTY are t interactively.
+
+(fn POD COMMAND &optional CONTAINER CONTEXT NAMESPACE STDIN TTY ARGS)" t)
+(autoload 'kubed-patch "kubed" "\
+Patch Kubernetes resource NAME of TYPE with patch PATCH.
+
+Optional argument NAMESPACE is the namespace in which to look for NAME.
+STRATEGY is the patch type to use, one of \"json\", \"merge\" and
+\"strategic\", defaulting to \"strategic\".
+
+Interactively, prompt for TYPE, NAME and PATCH.  With a prefix argument,
+prompt for NAMESPACE too.  With a double prefix argument, also prompt
+for CONTEXT.
+
+(fn TYPE NAME PATCH &optional CONTEXT NAMESPACE STRATEGY)" t)
+(autoload 'kubed-proxy "kubed" "\
+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.
+
+(fn &optional ADDR PORT API-PREFIX WWW-DIR WWW-PREFIX CONTEXT)" t)
+(autoload 'kubed-stop-proxy "kubed" "\
+Stop local proxy for context CONTEXT, defaulting to the local context.
+
+(fn &optional CONTEXT)" t)
+(autoload 'kubed-explain "kubed" "\
+Show help buffer with explanation about Kubernetes resource FIELD.
+
+(fn FIELD)" t)
+(autoload 'kubed-kubectl-command "kubed" "\
+Execute `kubectl' COMMAND.
+
+This function calls `shell-command' (which see) to do the work.
+
+Interactively, prompt for COMMAND with completion for `kubectl' arguments.
+
+(fn COMMAND)" t)
+ (autoload 'kubed-prefix-map "kubed" nil t 'keymap)
+(defvar kubed-menu-bar-mode nil "\
+Non-nil if Kubed-Menu-Bar mode is enabled.
+See the `kubed-menu-bar-mode' command
+for a description of this minor mode.
+Setting this variable directly does not take effect;
+either customize it (see the info node `Easy Customization')
+or call the function `kubed-menu-bar-mode'.")
+(custom-autoload 'kubed-menu-bar-mode "kubed" nil)
+(autoload 'kubed-menu-bar-mode "kubed" "\
+Add \"Kubernetes\" menu to your menu bar.
+
+This is a global minor mode.  If called interactively, toggle the
+`Kubed-Menu-Bar mode' mode.  If the prefix argument is positive, enable
+the mode, and if it is zero or negative, disable the mode.
+
+If called from Lisp, toggle the mode if ARG is `toggle'.  Enable the
+mode if ARG is nil, omitted, or is a positive number.  Disable the mode
+if ARG is a negative number.
+
+To check whether the minor mode is enabled in the current buffer,
+evaluate `(default-value \\='kubed-menu-bar-mode)'.
+
+The mode's hook is called both when the mode is enabled and when it is
+disabled.
+
+(fn &optional ARG)" t)
+(register-definition-prefixes "kubed" '("kubed-"))
+
+\f
+;;; Generated autoloads from kubed-common.el
+
+(register-definition-prefixes "kubed-common" '("kubed-"))
+
+\f
+;;; Generated autoloads from kubed-tramp.el
+
+(autoload 'kubed-tramp-context "kubed-tramp" "\
+Extract `kubectl' context from Kubed Tramp remote file name FILE-NAME.
+
+(fn FILE-NAME)")
+(autoload 'kubed-tramp-namespace "kubed-tramp" "\
+Extract Kubernetes namespace from Kubed Tramp remote file name FILE-NAME.
+
+(fn FILE-NAME)")
+(autoload 'kubed-tramp-assert-support "kubed-tramp" "\
+Check if Kubed Tramp support is available, throw `user-error' if not.")
+(autoload 'kubed-tramp-enable "kubed-tramp" "\
+Enable Kubed integration with Tramp.")
+ (with-eval-after-load 'tramp (kubed-tramp-enable))
+(register-definition-prefixes "kubed-tramp" '("kubed-tramp--"))
+
+\f
+;;; Generated autoloads from kubed-transient.el
+
+ (autoload 'kubed-transient "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-pod "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-deployment "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-service "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-job "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-replicaset "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs-for-statefulset "kubed-transient" nil t)
+ (autoload 'kubed-transient-logs "kubed-transient" nil t)
+ (autoload 'kubed-transient-scale-deployment "kubed-transient" nil t)
+ (autoload 'kubed-transient-proxy "kubed-transient" nil t)
+ (autoload 'kubed-transient-rollout "kubed-transient" nil t)
+ (autoload 'kubed-transient-attach "kubed-transient" nil t)
+ (autoload 'kubed-transient-diff "kubed-transient" nil t)
+ (autoload 'kubed-transient-exec "kubed-transient" nil t)
+ (autoload 'kubed-transient-run "kubed-transient" nil t)
+ (autoload 'kubed-transient-apply "kubed-transient" nil t)
+ (autoload 'kubed-transient-patch "kubed-transient" nil t)
+ (autoload 'kubed-transient-create "kubed-transient" nil t)
+ (autoload 'kubed-transient-display "kubed-transient" nil t)
+ (autoload 'kubed-transient-edit "kubed-transient" nil t)
+ (autoload 'kubed-transient-delete "kubed-transient" nil t)
+ (autoload 'kubed-transient-create-cronjob "kubed-transient" nil t)
+ (autoload 'kubed-transient-create-ingress "kubed-transient" nil t)
+ (autoload 'kubed-transient-create-deployment "kubed-transient" nil t)
+ (autoload 'kubed-transient-create-job "kubed-transient" nil t)
+ (autoload 'kubed-list-transient "kubed-transient" nil t)
+(register-definition-prefixes "kubed-transient" '("kubed-transient-"))
+\f
+;;; End of scraped data
+
+(provide 'kubed-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; kubed-autoloads.el ends here
diff --git a/elpa/kubed-0.4.2/kubed-common.el b/elpa/kubed-0.4.2/kubed-common.el
new file mode 100644 (file)
index 0000000..a30777d
--- /dev/null
@@ -0,0 +1,25 @@
+;;; kubed-common.el --- Common definitons for Kubed   -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Keywords: tools
+
+;;; Commentary:
+
+;; This library provides definitions that both `kubed' and `kubed-tramp'
+;; require.
+
+;;; Code:
+
+(defvar kubed-tramp-method "kubedv1"    ;Versioned, for compatibility.
+  ;; (find-file "/kubedv1:CONTEXT%NAMESPACE%POD%CONTAINER:/some/file")
+  "Name of the Kubed Tramp method.")
+
+(defcustom kubed-kubectl-program "kubectl"
+  "Name of `kubectl' executable to use for interacting with Kubernetes."
+  :type 'string
+  :group 'kubed)
+
+(provide 'kubed-common)
+;;; kubed-tramp.el ends here
diff --git a/elpa/kubed-0.4.2/kubed-pkg.el b/elpa/kubed-0.4.2/kubed-pkg.el
new file mode 100644 (file)
index 0000000..d98849a
--- /dev/null
@@ -0,0 +1,2 @@
+;; Generated package description from kubed.el  -*- no-byte-compile: t -*-
+(define-package "kubed" "0.4.2" "Kubernetes, Emacs, done!" '((emacs "29.1")) :commit "d0a0f2407575b321457f48582d04ef69471fe561" :authors '(("Eshel Yaron" . "meNOSPAMeshelyaron.com")) :maintainer '("Eshel Yaron" . "~eshel/kubed-develNOSPAMlists.sr.ht") :keywords '("tools" "kubernetes" "containers") :url "https://eshelyaron.com/kubed.html")
diff --git a/elpa/kubed-0.4.2/kubed-tramp.el b/elpa/kubed-0.4.2/kubed-tramp.el
new file mode 100644 (file)
index 0000000..8e5ac7d
--- /dev/null
@@ -0,0 +1,102 @@
+;;; kubed-tramp.el --- Kubed Tramp integration   -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Keywords: tools
+
+;;; Commentary:
+
+;; This library provides Tramp integration for Kubed.  This is similar
+;; to the built-in "kubernetes" Tramp method from tramp-container.el,
+;; except that the Kubed method always requires the container name and
+;; Kubernetes namespace to be specified, as well a `kubectl' context.
+;; In other words, the filename syntax of this method is fully explicit.
+;; An explicit syntax is useful when juggling between different contexts
+;; and namespaces with Kubed; on the other hand, for finding a remote
+;; file with C-x C-f, the concise syntax of the built-in "kubernetes"
+;; method is probably more convenient.
+
+;;; Code:
+
+(require 'kubed-common)
+(require 'tramp)
+
+(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))))
+        (nth 0 (split-string host "%")))
+      ""))
+
+(defun kubed-tramp--namespace (vec)
+  "Extract the namespace from a kubernetes host name in VEC."
+  (or (when-let ((host (and vec (tramp-file-name-host vec))))
+        (nth 1 (split-string host "%")))
+      ""))
+
+(defun kubed-tramp--pod (vec)
+  "Extract the pod name from a kubernetes host name in VEC."
+  (or (when-let ((host (and vec (tramp-file-name-host vec))))
+        (nth 2 (split-string host "%")))
+      ""))
+
+(defun kubed-tramp--container (vec)
+  "Extract the container name from a kubernetes host name in VEC."
+  (or (when-let ((host (and vec (tramp-file-name-host vec))))
+        (nth 3 (split-string host "%")))
+      ""))
+
+;;;###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)) "%")))
+
+;;;###autoload
+(defun kubed-tramp-namespace (file-name)
+  "Extract Kubernetes namespace from Kubed Tramp remote file name FILE-NAME."
+  (nth 1 (split-string
+          (tramp-file-name-host (tramp-dissect-file-name file-name)) "%")))
+
+;;;###autoload
+(defun kubed-tramp-assert-support ()
+  "Check if Kubed Tramp support is available, throw `user-error' if not."
+  (unless (assoc kubed-tramp-method tramp-methods)
+    (user-error "Kubed Tramp support requires Tramp version 2.7 or later")))
+
+;;;###autoload
+(defun kubed-tramp-enable ()
+  "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)))
+
+;;;###autoload (with-eval-after-load 'tramp (kubed-tramp-enable))
+
+(provide 'kubed-tramp)
+;;; kubed-tramp.el ends here
diff --git a/elpa/kubed-0.4.2/kubed-transient.el b/elpa/kubed-0.4.2/kubed-transient.el
new file mode 100644 (file)
index 0000000..3d2838c
--- /dev/null
@@ -0,0 +1,585 @@
+;;; kubed-transient.el --- Kubernetes transient menus   -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Keywords: tools
+
+;;; Commentary:
+
+;; This library extends Kubed with transient menus for various
+;; Kubernetes operations.
+
+;;; Code:
+
+(require 'kubed)
+(require 'transient)
+
+(defun kubed-transient-read-date (prompt default _history)
+  "Prompt with PROMPT for a date, defaulting to DEFAULT.
+
+Return an RFC3339 string representation of the selected date."
+  (require 'org)
+  (when (fboundp 'org-read-date)
+    (format-time-string "%Y-%m-%dT%H:%M:%S%:z"
+                        (org-read-date 'with-time t nil prompt default))))
+
+(defun kubed-transient-read-context (prompt _initial-input _history)
+  "Prompt with PROMPT for Kubernetes context."
+  (kubed-read-context prompt (kubed-local-context)))
+
+(defun kubed-transient-read-namespace (prompt _initial-input _history)
+  "Prompt with PROMPT for Kubernetes namespace."
+  (let ((context (or (seq-some (lambda (s)
+                                 (and (cl-typep s 'transient-infix)
+                                      (equal (oref s argument) "--context=")
+                                      (oref s value)))
+                               transient--suffixes)
+                     (kubed-local-context))))
+    (kubed-read-namespace prompt (kubed--namespace context) nil context)))
+
+(defun kubed-transient-read-ingressclass (prompt _initial-input _history)
+  "Prompt with PROMPT for Kubernetes ingress class."
+  (kubed-read-ingressclass prompt))
+
+(defun kubed-transient-read-service-and-port (prompt _initial-input _history)
+  "Prompt with PROMPT for Kubernetes service and port number."
+  (let ((service (kubed-read-service prompt)))
+    (concat service ":" (number-to-string (read-number "Port number: ")))))
+
+(defun kubed-transient-read-resource-definition-file-name
+    (_prompt _initial-input _history)
+  "Read and return Kubernetes resource definition file name."
+  (kubed-read-resource-definition-file-name))
+
+;;;###autoload (autoload 'kubed-transient "kubed-transient" nil t)
+(transient-define-prefix kubed-transient ()
+  "Perform Kubernetes operation."
+  ["Kubernetes"
+   [ :pad-keys t
+     ("RET" "Display" kubed-transient-display)
+     ("+" "Create"  kubed-transient-create)
+     ("*" "Apply"   kubed-transient-apply)
+     ("D" "Delete"  kubed-transient-delete)]
+   [("e" "Edit"    kubed-transient-edit)
+    ("r" "Run"     kubed-transient-run)
+    ("a" "Attach"  kubed-transient-attach)
+    ("X" "Exec"    kubed-transient-exec)]
+   [("l" "Logs"    kubed-transient-logs)
+    ("d" "Diff"    kubed-transient-diff)
+    ("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)]])
+
+(defmacro kubed-transient-logs-for-resource (resource &optional plural)
+  "Define transient menu for showing logs for Kubernetes RESOURCE.
+
+Optional argument PLURAL is the plural form of RESOURCE.  If nil, it
+defaults to \"RESOURCEs\"."
+  (let ((name (intern (concat "kubed-transient-logs-for-" resource))))
+    `(transient-define-prefix ,name (&optional value)
+       ,(format "Show logs for a Kubernetes %s." resource)
+       [,(format "Kubernetes Logs for %s\n" (capitalize resource))
+        ["Actions"
+         ("l" "Show Logs" ,(intern (concat "kubed-logs-for-" resource)))
+         ("!" "Command line" kubed-kubectl-command)]
+        ["Options"
+         ("-n" "Namespace" "--namespace="
+          :prompt "Namespace" :reader kubed-transient-read-namespace)
+         ("-C" "Context" "--context="
+          :prompt "Context" :reader kubed-transient-read-context)
+         ("-b" "Limit bytes" "--limit-bytes="
+          :prompt "Byte limit: " :reader transient-read-number-N+)
+         ("-t" "Limit lines" "--tail="
+          :prompt "Byte limit: " :reader transient-read-number-N+)
+         ("-S" "Since time" "--since-time="
+          :prompt "Since time: " :reader kubed-transient-read-date)]
+        ["Switches"
+         ("-A" "All containers" "--all-containers")
+         ("-f" "Stream logs" "--follow")
+         ("-P" "Add pod and container" "--prefix")
+         ("-T" "Add timestamps" "--timestamps")]]
+       (interactive
+        (list (kubed-transient-args 'kubed-transient-logs)))
+       (transient-setup ',name nil nil
+                        :value value
+                        :scope '("logs" ,(concat (or plural
+                                                     (concat resource "s"))
+                                                 "/"))))))
+
+;;;###autoload (autoload 'kubed-transient-logs-for-pod "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "pod")
+
+;;;###autoload (autoload 'kubed-transient-logs-for-deployment "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "deployment")
+
+;;;###autoload (autoload 'kubed-transient-logs-for-service "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "service")
+
+;;;###autoload (autoload 'kubed-transient-logs-for-job "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "job")
+
+;;;###autoload (autoload 'kubed-transient-logs-for-replicaset "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "replicaset")
+
+;;;###autoload (autoload 'kubed-transient-logs-for-statefulset "kubed-transient" nil t)
+(kubed-transient-logs-for-resource "statefulset")
+
+;;;###autoload (autoload 'kubed-transient-logs "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-logs ()
+  "Show logs for containers running in Kubernetes."
+  ["Kubernetes Logs\n"
+   ["Kinds"
+    ("p" "Pod" kubed-transient-logs-for-pod)
+    ("d" "Deployment" kubed-transient-logs-for-deployment)
+    ("j" "Job" kubed-transient-logs-for-job)
+    ("s" "Service" kubed-transient-logs-for-service)
+    ("l" "Any type" kubed-logs)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-b" "Limit bytes" "--limit-bytes="
+     :prompt "Byte limit: " :reader transient-read-number-N+)
+    ("-t" "Limit lines" "--tail="
+     :prompt "Byte limit: " :reader transient-read-number-N+)
+    ("-S" "Since time" "--since-time="
+     :prompt "Since time: " :reader kubed-transient-read-date)]
+   ["Switches"
+    ("-A" "All containers" "--all-containers")
+    ("-f" "Stream logs" "--follow")
+    ("-P" "Add pod and container" "--prefix")
+    ("-T" "Add timestamps" "--timestamps")]]
+  (interactive)
+  (transient-setup 'kubed-transient-logs nil nil
+                   :scope '("logs")))
+
+;;;###autoload (autoload 'kubed-transient-scale-deployment "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-scale-deployment ()
+  "Scale Kubernetes deployments."
+  ["Kubernetes Scale Deployment\n"
+   ["Action"
+    ("$" "Scale" kubed-scale-deployment)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-r" "Replicas" "--replicas="
+     :prompt "Number of replicas: " :reader transient-read-number-N+)]]
+  (interactive)
+  (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."
+  ["Kubernetes Rollout\n"
+   ["Actions"
+    ("W" "Watch"   kubed-watch-deployment-status)
+    ("R" "Restart" kubed-restart-deployment)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-rollout nil nil
+                   :scope '("rollout")))
+
+;;;###autoload (autoload 'kubed-transient-attach "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-attach ()
+  "Attach to running process in container in Kubernetes pod."
+  ["Kubernetes Attach\n"
+   ["Actions"
+    ("a" "Attach" kubed-attach)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Switches"
+    ("-i" "Open stdin" "--stdin")
+    ("-t" "Allocate TTY" "--tty")]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-attach nil nil
+                   :value '("--stdin" "--tty")
+                   :scope '("attach")))
+
+;;;###autoload (autoload 'kubed-transient-diff "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-diff ()
+  "Display difference between Kubernetes resource definition and current state."
+  ["Kubernetes Diff\n"
+   ["Actions"
+    ("d" "Diff" kubed-diff)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Switches"
+    ("-M" "Include managed fields" "--show-managed-fields")]
+   ["Options"
+    ("-f" "Definition file" "--filename="
+     :reader kubed-transient-read-resource-definition-file-name)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-diff nil nil
+                   :scope '("diff")))
+
+;;;###autoload (autoload 'kubed-transient-exec "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-exec ()
+  "Execute command in Kubernetes pod."
+  ["Kubernetes Exec\n"
+   ["Actions"
+    ("X" "Execute" kubed-exec)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Switches"
+    ("-i" "Open stdin" "--stdin")
+    ("-t" "Allocate TTY" "--tty")]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("--" "Command" "-- ="
+     :prompt "Command: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-exec nil nil
+                   :value '("--stdin" "--tty")
+                   :scope '("exec")))
+
+;;;###autoload (autoload 'kubed-transient-run "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-run ()
+  "Run container image in a Kubernetes pod."
+  ["Kubernetes Run\n"
+   ["Actions"
+    ("r" "Run" kubed-run)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Switches"
+    ("-A" "Attach" "--attach")
+    ("-i" "Open stdin" "--stdin")
+    ("-t" "Allocate TTY" "--tty")
+    ("-R" "Remove after exit" "--rm")
+    ("-c" "Override container command" "--command")]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-I" "Image" "--image="
+     :prompt "Image to deploy: ")
+    ("-p" "Port" "--port="
+     :prompt "Port to expose: " :reader transient-read-number-N+)
+    ("-E" "Env vars" "--env="
+     :prompt "Set environment VAR=VAL: "
+     :multi-value repeat)
+    ("--" "Arguments" "-- ="
+     :prompt "Arguments for container command: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-run nil nil
+                   :scope '("run")))
+
+;;;###autoload (autoload 'kubed-transient-apply "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-apply ()
+  "Apply configuration to Kubernetes resource."
+  ["Kubernetes Apply\n"
+   ["Actions"
+    ("*" "Apply" kubed-apply)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-f" "Definition file" "--filename="
+     :reader kubed-transient-read-resource-definition-file-name)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-apply nil nil
+                   :scope '("apply")))
+
+;;;###autoload (autoload 'kubed-transient-patch "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-patch ()
+  "Apply patch to Kubernetes resource."
+  ["Kubernetes Patch\n"
+   ["Actions"
+    ("P" "Patch" kubed-patch)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-t" "Patch type" "--type="
+     :prompt "Patch type: "
+     :choices ("strategic" "merge" "json"))]]
+  (interactive)
+  (transient-setup 'kubed-transient-patch nil nil
+                   :scope '("patch")))
+
+;;;###autoload (autoload 'kubed-transient-create "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-create ()
+  "Create Kubernetes resource."
+  ["Kubernetes Create\n"
+   ["Kinds"
+    ("p" "Pod" kubed-create-pod)
+    ("d" "Deployment" kubed-transient-create-deployment)
+    ("j" "Job" kubed-transient-create-job)
+    ("c" "CronJob" kubed-transient-create-cronjob)
+    ("s" "Service" kubed-create-service)]
+   ["More"
+    :pad-keys t
+    ("S" "Secret" kubed-create-secret)
+    ("N" "Namespace" kubed-create-namespace)
+    ("i" "Ingress" kubed-transient-create-ingress)
+    ("+" "Any type" kubed-create)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-f" "Definition file" "--filename="
+     :reader kubed-transient-read-resource-definition-file-name)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-create nil nil
+                   :scope '("create")))
+
+;;;###autoload (autoload 'kubed-transient-display "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-display ()
+  "Display Kubernetes resource."
+  ["Kubernetes Display\n"
+   ["Kinds"
+    ("p" "Pod" kubed-display-pod)
+    ("d" "Deployment" kubed-display-deployment)
+    ("j" "Job" kubed-display-job)
+    ("c" "CronJob" kubed-display-cronjob)
+    ("s" "Service" kubed-display-service)]
+   ["More"
+    :pad-keys t
+    ("S" "Secret" kubed-display-secret)
+    ("N" "Namespace" kubed-display-namespace)
+    ("i" "Ingress" kubed-display-ingress)
+    ("RET" "Any type" kubed-display-resource)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-display nil nil
+                   :scope '("get")))
+
+;;;###autoload (autoload 'kubed-transient-edit "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-edit ()
+  "Edit Kubernetes resource."
+  ["Kubernetes Edit\n"
+   ["Kinds"
+    ("p" "Pod" kubed-edit-pod)
+    ("d" "Deployment" kubed-edit-deployment)
+    ("j" "Job" kubed-edit-job)
+    ("c" "CronJob" kubed-edit-cronjob)
+    ("s" "Service" kubed-edit-service)]
+   ["More"
+    :pad-keys t
+    ("S" "Secret" kubed-edit-secret)
+    ("N" "Namespace" kubed-edit-namespace)
+    ("i" "Ingress" kubed-edit-ingress)
+    ("RET" "Any type" kubed-edit-resource)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-edit nil nil
+                   :scope '("edit")))
+
+;;;###autoload (autoload 'kubed-transient-delete "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-delete ()
+  "Delete Kubernetes resource."
+  ["Kubernetes Delete\n"
+   ["Kinds"
+    ("p" "Pod" kubed-delete-pods)
+    ("d" "Deployment" kubed-delete-deployments)
+    ("j" "Job" kubed-delete-jobs)
+    ("c" "CronJob" kubed-delete-cronjobs)
+    ("s" "Service" kubed-delete-services)]
+   ["More"
+    :pad-keys t
+    ("S" "Secret" kubed-delete-secrets)
+    ("N" "Namespace" kubed-delete-namespaces)
+    ("i" "Ingress" kubed-delete-ingresses)
+    ("D" "Any type" kubed-delete-resources)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)]]
+  (interactive)
+  (transient-setup 'kubed-transient-delete nil nil
+                   :scope '("delete")))
+
+;;;###autoload (autoload 'kubed-transient-create-cronjob "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-create-cronjob ()
+  "Create Kubernetes cronjob."
+  ["Kubernetes Create CronJob\n"
+   ["Actions"
+    ("+" "Create" kubed-create-cronjob)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-I" "Image" "--image="
+     :prompt "Image to run: ")
+    ("-S" "Schedule" "--schedule="
+     :prompt "Cron schedule: ")
+    ("--" "Command" "-- ="
+     :prompt "Command: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-create-cronjob nil nil
+                   :scope '("create" "cronjob")))
+
+;;;###autoload (autoload 'kubed-transient-create-ingress "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-create-ingress ()
+  "Create Kubernetes ingress."
+  ["Kubernetes Create Ingress\n"
+   ["Actions"
+    ("+" "Create" kubed-create-ingress)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-c" "Class" "--class="
+     :prompt "Class" :reader kubed-transient-read-ingressclass)
+    ("-d" "Default backend service" "--default-backend="
+     :prompt "Default backend service"
+     :reader kubed-transient-read-service-and-port)
+    ("-a" "Annotation" "--annotation="
+     :prompt "Ingress annotations: "
+     :multi-value repeat)
+    ("-r" "Rule" "--rule="
+     :prompt "Ingress rule: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-create-ingress nil nil
+                   :scope '("create" "ingress")))
+
+;;;###autoload (autoload 'kubed-transient-create-deployment "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-create-deployment ()
+  "Create Kubernetes deployment."
+  ["Kubernetes Create Deployment\n"
+   ["Actions"
+    ("+" "Create" kubed-create-deployment)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-r" "Replicas" "--replicas="
+     :prompt "Number of replicas: " :reader transient-read-number-N+)
+    ("-I" "Image" "--image="
+     :prompt "Images to deploy: "
+     :multi-value repeat)
+    ("-p" "Port" "--port="
+     :prompt "Port to expose: " :reader transient-read-number-N+)
+    ("--" "Command" "-- ="
+     :prompt "Command: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-create-deployment nil nil
+                   :scope '("create" "deployment")))
+
+;;;###autoload (autoload 'kubed-transient-create-job "kubed-transient" nil t)
+(transient-define-prefix kubed-transient-create-job ()
+  "Create Kubernetes job."
+  ["Kubernetes Create Job\n"
+   ["Actions"
+    ("+" "Create" kubed-create-job)
+    ("c" "Create from cronjob" kubed-create-job-from-cronjob)
+    ("!" "Command line" kubed-kubectl-command)]
+   ["Options"
+    ("-n" "Namespace" "--namespace="
+     :prompt "Namespace" :reader kubed-transient-read-namespace)
+    ("-C" "Context" "--context="
+     :prompt "Context" :reader kubed-transient-read-context)
+    ("-I" "Image" "--image="
+     :prompt "Image to run: ")
+    ("--" "Command" "-- ="
+     :prompt "Command: ")]]
+  (interactive)
+  (transient-setup 'kubed-transient-create-job nil nil
+                   :scope '("create" "job")))
+
+;;;###autoload (autoload 'kubed-list-transient "kubed-transient" nil t)
+(transient-define-prefix kubed-list-transient ()
+  "Help for Kubernetes resource list buffers."
+  ;; TODO: Add a type-specific group with `:setup-children'.
+  ["Kubernetes Resources:"
+   ["Select"
+    :pad-keys t
+    ("RET" "Select" kubed-list-select-resource)
+    ("C-o" "Display" kubed-list-display-resource :transient t)
+    ("e" "Edit" kubed-list-edit :transient t)
+    ("w" "Copy name" kubed-list-copy-as-kill :transient t)
+    ("!" "Command line" kubed-list-kubectl-command :transient t)]
+   ["Delete"
+    ("D" "Delete" kubed-list-delete :transient t)
+    ("d" "Mark" kubed-list-mark-for-deletion :transient t)
+    ("u" "Unmark" kubed-list-unmark :transient t)
+    ("x" "Delete marked" kubed-list-delete-marked :transient t)
+    ("+" "Create" kubed-list-create :transient t)]
+   ["Table"
+    ("|" "Fit column" kubed-list-fit-column-width-to-content :transient t)
+    ("}" "Widen column" tabulated-list-widen-current-column :transient t)
+    ("{" "Narrow column" tabulated-list-narrow-current-column :transient t)
+    ("S" "Sort" tabulated-list-sort :transient t)
+    ("/" "Filter" kubed-list-set-filter :transient t)]
+   ["Movement"
+    :pad-keys t
+    ("n" "Next line" next-line :transient t)
+    ("p" "Previous line" previous-line :transient t)
+    ("TAB" "Next column" kubed-list-next-column :transient t)
+    ("S-TAB" "Previous column" kubed-list-previous-column :transient t)
+    ("g" "Update" kubed-list-update :transient t)]]
+  ["Type Specific"
+   :class transient-columns
+   :setup-children
+   (lambda (_)
+     (transient-parse-suffixes 'kubed-list-transient
+                               kubed-list-transient-extra-suffixes))])
+
+(provide 'kubed-transient)
+;;; kubed-transient.el ends here
diff --git a/elpa/kubed-0.4.2/kubed.el b/elpa/kubed-0.4.2/kubed.el
new file mode 100644 (file)
index 0000000..ee0c918
--- /dev/null
@@ -0,0 +1,3510 @@
+;;; kubed.el --- Kubernetes, Emacs, done!   -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Maintainer: Eshel Yaron <~eshel/kubed-develNOSPAMlists.sr.ht>
+;; Keywords: tools kubernetes containers
+;; URL: https://eshelyaron.com/kubed.html
+;; Package-Version: 0.4.2
+;; Package-Requires: ((emacs "29.1"))
+
+;;; Commentary:
+
+;; This library defines commands for interacting with Kubernetes
+;; resources, such as Kubernetes pods, services, deployments, and more.
+;;
+;; Use `kubed-display-pod' to display a Kubernetes pod,
+;; `kubed-edit-pod' to edit it, `kubed-delete-pods' to delete it, and
+;; `kubed-list-pods' to see a menu of all pods.  You can create new pods
+;; from YAML or JSON files with `kubed-create-pod'.
+;;
+;; Similar commands are defined for other types of resources as well.
+;;
+;; This library interacts with Kubernetes via `kubectl', and uses the
+;; current `kubectl' context and namespace by default.  To change your
+;; current context or namespace, use commands `kubed-use-context' and
+;; `kubed-set-namespace' respectively; you can also interact with
+;; resources in other namespaces without changing your default
+;; namespace, for example you can call `kubed-list-pods' with a prefix
+;; argument to choose another namespace for listing pods.  The prefix
+;; keymap `kubed-prefix-map' gives you quick access to these and other
+;; useful commands, you may want to bind it globally to a convenient key
+;; with `keymap-global-set':
+;;
+;;   (keymap-global-set "C-c k" 'kubed-prefix-map)
+;;
+;; In addition, the command `kubed-transient' lets you explore various
+;; Kubernetes operations with a transient menu interface.  You may also
+;; want to enable `kubed-menu-bar-mode', which add a "Kubernetes" menu
+;; to your menu-bar with many useful entries.
+;;
+;; If you want to work with more or different types of Kubernetes
+;; resources, use the macro `kubed-define-resource'.  This macro defines
+;; some common functions and commands that'll get you started with ease.
+;;
+;; For more information, see the Kubed manual at (info "(kubed)Top"), or
+;; online at https://eshelyaron.com/kubed.html
+
+;;; Code:
+
+(require 'kubed-common)
+
+(defgroup kubed nil
+  "Kubernetes interface."
+  :group 'tools)
+
+(defcustom kubed-default-context-and-namespace nil
+  "Default `kubectl' context and Kubernetes namespace.
+
+This is either a cons cell (CONTEXT . NAMESPACE), or nil.  If nil, Kubed
+initializes this option to a non-nil value next time Kubed consults it."
+  :type '(choice (const :tag "Initialize from `kubectl' on first use" nil)
+                 (cons :tag "Context and namespace"
+                       (string :tag "Context")
+                       (string :tag "Namespace"))))
+
+(defcustom kubed-yaml-setup-hook '(yaml-ts-mode view-mode)
+  "List of functions to call in Kubernetes resource description YAML buffers.
+
+The first function in the list should normally be the major mode to use,
+by default it is `yaml-ts-mode'."
+  :type 'hook)
+
+(defcustom kubed-logs-setup-hook '(view-mode)
+  "List of functions to call when setting up Kubernetes pod logs buffers."
+  :type 'hook)
+
+(defcustom kubed-name-column '("Name" 48 t)
+  "Specification of resource name column in Kubernetes resource list buffers."
+  :type '(list string natnum boolean))
+
+(defvar kubed--data nil)
+
+(defun kubed--alist (type context namespace)
+  "Return information about resources in CONTEXT of type TYPE in NAMESPACE."
+  (alist-get namespace
+             (alist-get type
+                        (alist-get context
+                                   kubed--data nil nil #'string=)
+                        nil nil #'string=)
+             nil nil #'equal))
+
+(gv-define-setter kubed--alist (value type context namespace)
+  `(setf (alist-get ,namespace
+                    (alist-get ,type
+                               (alist-get ,context
+                                          kubed--data nil nil #'string=)
+                               nil nil #'string=)
+                    nil nil #'equal)
+         ,value))
+
+(defvar kubed--columns nil)
+
+(defun kubed-update (type context &optional namespace)
+  "Update list of resources of type TYPE in CONTEXT and NAMESPACE."
+  (when (process-live-p (alist-get 'process (kubed--alist type context namespace)))
+    (user-error "Update in progress"))
+  (let* ((out (get-buffer-create (format " *kubed-get-%s*"        type)))
+         (err (get-buffer-create (format " *kubed-get-%s-stderr*" type)))
+         (columns (alist-get type kubed--columns nil nil #'string=)))
+    (with-current-buffer out (erase-buffer))
+    (setf (alist-get 'process (kubed--alist type context namespace))
+          (make-process
+           :name (format "*kubed-get-%s*" type)
+           :buffer out
+           :stderr err
+           :command (append
+                     (list kubed-kubectl-program "get" type
+                           "--context" context
+                           (format "--output=custom-columns=%s"
+                                   (mapconcat #'car columns ",")))
+                     (when namespace (list "--namespace" namespace)))
+           :sentinel
+           (lambda (_proc status)
+             (cond
+              ((string= status "finished\n")
+               (let (new offsets eol)
+                 (with-current-buffer out
+                   (goto-char (point-min))
+                   (setq eol (pos-eol))
+                   (while (re-search-forward "[^ ]+" eol t)
+                     (push (1- (match-beginning 0)) offsets))
+                   (setq offsets (nreverse offsets))
+                   (forward-char 1)
+                   (while (not (eobp))
+                     (let ((cols nil)
+                           (beg (car offsets))
+                           (ends (append (cdr offsets)
+                                         (list (- (pos-eol) (point))))))
+                       (dolist (column columns)
+                         (let ((str (string-trim (buffer-substring
+                                                  (+ (point) beg)
+                                                  (+ (point) (car ends))))))
+                           (push (if-let ((f (cdr column))) (funcall f str) str)
+                                 cols)
+                           (setq beg (pop ends))))
+                       (push (nreverse cols) new))
+                     (forward-line 1)))
+                 (setf (kubed--alist type context namespace)
+                       (list (cons 'resources
+                                   (mapcar (lambda (c) (list (car c) (apply #'vector c)))
+                                           new))))
+                 (let ((bufs nil))
+                   (dolist (buf (buffer-list))
+                     (and (equal (buffer-local-value 'kubed-list-type buf) type)
+                          (equal (buffer-local-value 'kubed-list-context buf) context)
+                          (equal (buffer-local-value 'kubed-list-namespace buf) namespace)
+                          (with-current-buffer buf
+                            (when (derived-mode-p 'kubed-list-mode)
+                              (revert-buffer)
+                              (when-let ((win (get-buffer-window)))
+                                (set-window-point win (point))
+                                (push buf bufs))))))
+                   (walk-windows
+                    (lambda (win)
+                      (let ((buf (window-buffer win)))
+                        (when (memq buf bufs)
+                          (set-window-point
+                           win (with-current-buffer buf (point))))))))
+                 (message (format "Updated Kubernetes %S." type))))
+              ((string= status "exited abnormally with code 1\n")
+               (with-current-buffer err
+                 (goto-char (point-max))
+                 (insert "\n" status))
+               (display-buffer err))))))))
+
+(defvar-local kubed-display-resource-info nil
+  "Information about Kubernetes resource that current buffer displays.
+
+The value is a list (TYPE NAME CONTEXT NAMESPACE), where TYPE is the
+type of the resource, NAME is the name of the resource, CONTEXT is the
+`kubectl' context to use for accessing the resource, and NAMESPACE is
+the namespace of the resource, or nil if TYPE is not namespaced.")
+
+(put 'kubed-display-resource-info 'permanent-local t)
+
+(defun kubed-display-resource-revert (&optional _ _)
+  "Clear and populate current Kubernetes resource buffer."
+  (seq-let (type name context namespace)
+      kubed-display-resource-info
+    (let ((inhibit-read-only t)
+          (target (current-buffer)))
+      (buffer-disable-undo)
+      (with-temp-buffer
+        (unless (zerop
+                 (apply
+                  #'call-process
+                  kubed-kubectl-program nil t nil "get"
+                  type "--output=yaml" name
+                  (append (when namespace (list "-n" namespace))
+                          (when context (list "--context" context)))))
+          (error "Failed to display Kubernetes resource `%s'" name))
+        (let ((source (current-buffer)))
+          (with-current-buffer target
+            (replace-buffer-contents source)
+            (set-buffer-modified-p nil)
+            (buffer-enable-undo)))))))
+
+(defun kubed-display-resource-in-buffer
+    (buffer type resource &optional context namespace)
+  "Display Kubernetes RESOURCE of type TYPE in BUFFER."
+  (let ((info (list type resource context namespace)))
+    (with-current-buffer (get-buffer-create buffer)
+      (setq-local kubed-display-resource-info info)
+      (kubed-display-resource-revert)
+      (goto-char (point-min))
+      (run-hooks 'kubed-yaml-setup-hook)
+      (kubed-display-resource-mode)
+      (current-buffer))))
+
+(defun kubed-display-resource-short-description
+    (type resource context namespace)
+  "Return short string to use as a label for RESOURCE of type TYPE."
+  (concat type "/" resource
+          (when namespace (concat "@" namespace))
+          (when context   (concat "[" context "]"))))
+
+(defun kubed-namespaced-p (type &optional context)
+  "Return non-nil if TYPE is a namespaced resource type in context CONTEXT."
+  (member type (kubed-api-resources context t)))
+
+(defun kubed--namespace (context &optional prompt-p)
+  "Return Kubernetes namespace for CONTEXT.
+
+Non-nil optional argument PROMPT-P says to prompt for a namespace."
+  (let ((ns (if (equal context (kubed-local-context))
+                (kubed-local-namespace)
+              (kubed-current-namespace context))))
+    ;; If CONTEXT is another context that has no configured namespace,
+    ;; then NS is nil and we must prompt.
+    (unless ns (setq prompt-p t))
+    (if prompt-p
+        (kubed-read-namespace "Namespace" ns nil context)
+      ns)))
+
+;;;###autoload
+(defun kubed-display-resource
+    (type resource context &optional namespace)
+  "Display Kubernetes RESOURCE of type TYPE in CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCE.  With a prefix argument \
+\\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \
+\\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well."
+  (interactive
+   (let ((type nil) (context nil) (namespace nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-display))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless type
+       (setq type (kubed-read-resource-type "Type of resource to display"
+                                            nil context)))
+     (when (and (null namespace) (kubed-namespaced-p type context))
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list type (kubed-read-resource-name type "Display" nil nil
+                                          context namespace)
+           context namespace)))
+  (display-buffer
+   (kubed-display-resource-in-buffer
+    (concat "*Kubed "
+            (kubed-display-resource-short-description
+             type resource context namespace)
+            "*")
+    type resource context namespace)))
+
+(declare-function bookmark-prop-get                 "bookmark")
+(declare-function bookmark-get-front-context-string "bookmark")
+(declare-function bookmark-get-rear-context-string  "bookmark")
+(declare-function bookmark-make-record-default      "bookmark")
+
+;;;###autoload
+(defun kubed-display-resource-handle-bookmark (bookmark)
+  "Display Kubernetes resource according to BOOKMARK."
+  (require 'bookmark)
+  (seq-let (type name context namespace)
+      (bookmark-prop-get bookmark 'resource)
+    (set-buffer
+     (kubed-display-resource-in-buffer
+      (concat "*Kubed "
+              (kubed-display-resource-short-description
+               type name context namespace)
+              "*")
+      type name context namespace))
+    (when-let ((str (bookmark-get-front-context-string bookmark))
+               ((search-forward str (point-max) t)))
+      (goto-char (match-beginning 0)))
+    (when-let ((str (bookmark-get-rear-context-string bookmark))
+               ((search-backward str (point-min) t)))
+      (goto-char (match-end 0)))))
+
+(put 'kubed-display-resource-handle-bookmark 'bookmark-handler-type "KubedResource")
+
+(defun kubed-display-resource-make-bookmark ()
+  "Return bookmark pointing to currently displayed Kubernetes resource."
+  (require 'bookmark)
+  (seq-let (type name context namespace) kubed-display-resource-info
+    (cons
+     (kubed-display-resource-short-description type name context namespace)
+     (append
+      (list
+       (cons 'handler #'kubed-display-resource-handle-bookmark)
+       (cons 'resource kubed-display-resource-info))
+      (bookmark-make-record-default t)))))
+
+(defun kubed-display-resource-jump-to-list ()
+  "Jump to line in resources list that corresponds to the displayed resource."
+  (interactive)
+  (seq-let (type name context namespace) kubed-display-resource-info
+    (apply (intern (concat "kubed-list-" type)) context
+           (when namespace (list namespace)))
+    (kubed-list-go-to-line name)))
+
+(defun kubed-display-resource-diff ()
+  "Show diff of current buffer with current state of the displayed resource."
+  (interactive)
+  (kubed-diff (current-buffer) nil (nth 2 kubed-display-resource-info)))
+
+(defun kubed-display-resource-replace ()
+  "Replace displayed Kubernetes resource with current buffer contents."
+  (interactive)
+  (let (choice)
+    (while (= ?d (setq choice
+                       (car (read-multiple-choice
+                             "Replace resource with changes in current buffer?"
+                             '((?y "yes") (?n "no") (?d "diff"))))))
+      (kubed-display-resource-diff))
+    (when (= ?y choice)
+      (let ((err-buf (get-buffer-create " *kubed-replace*")))
+        (with-current-buffer err-buf (erase-buffer))
+        (unless (zerop (call-process-region nil nil kubed-kubectl-program
+                                            nil err-buf nil "replace" "-f" "-"))
+          (display-buffer err-buf)
+          (user-error "`kubectl replace' failed!")))
+      (message "Replaced Kubernetes resource with changes in current buffer.")
+      (revert-buffer))))
+
+(defun kubed-display-resource-p (_symbol buffer)
+  "Return non-nil if `kubed-display-resource-mode' is enabled in BUFFER.
+
+The first argument, SYMBOL, is ignored.  You can use this function as
+the `completion-predicate' property of commands that you define that
+should only be available in buffers that display Kubernetes resources."
+  (buffer-local-value 'kubed-display-resource-mode buffer))
+
+(dolist (cmd '(kubed-display-resource-jump-to-list
+               kubed-display-resource-diff
+               kubed-display-resource-replace))
+  (put cmd 'completion-predicate #'kubed-display-resource-p))
+
+(defvar-keymap kubed-display-resource-mode-map
+  :doc "Keymap buffers that display Kubernetes resource."
+  "C-c C-j" #'kubed-display-resource-jump-to-list
+  "C-c C-=" #'kubed-display-resource-diff
+  "C-c C-c" #'kubed-display-resource-replace)
+
+(define-minor-mode kubed-display-resource-mode
+  "Minor mode for buffers that display a Kubernetes resource."
+  :interactive nil
+  :lighter " Kubed"
+  (when kubed-display-resource-mode
+    (setq-local
+     revert-buffer-function        #'kubed-display-resource-revert
+     bookmark-make-record-function #'kubed-display-resource-make-bookmark)))
+
+(defun kubed-list-filter-lt-operator (v s)
+  "Return non-nil if S is less than V as a number or as a string."
+  (let ((l (string-to-number s)) (r (string-to-number v)))
+    (if (= l r) (string< s v) (< l r))))
+
+(defun kubed-list-filter-gt-operator (v s)
+  "Return non-nil if S is greater than V as a number or as a string."
+  (let ((l (string-to-number s)) (r (string-to-number v)))
+    (if (= l r) (string> s v) (> l r))))
+
+(defcustom kubed-list-filter-operator-alist
+  '((= . string=)
+    (~ . string-match-p)
+    (< . kubed-list-filter-lt-operator)
+    (> . kubed-list-filter-gt-operator))
+  "Association list of filter operators and functions that implement them.
+
+Elements of the list are cons cells (OP . FN), where OP is a symbol that
+is used as a filter operator and FN is a function that implements OP.
+FN takes two arguments, a string STR and a parameter VAL.  FN should
+return non-nil if STR and VAL are related according to OP: to determine
+if a line in which column COL is STR satisfies the filter (OP COL VAL),
+Kubed checks if the form (FN VAL STR) evaluates to non-nil."
+  :type '(alist :key-type (symbol :tag "Operator") :value-type function))
+
+(defun kubed-list-interpret-atomic-filter (atom)
+  "Return function that implements atomic filter ATOM."
+  (if (eq (car-safe atom) 'quote)
+      (let ((p (kubed-list-interpret-atomic-filter (cadr atom))))
+        (lambda (x) (not (funcall p x))))
+    (let* ((column-number (tabulated-list--column-number (symbol-name (nth 1 atom))))
+           (value (nth 2 atom))
+           (value (if (stringp value) value (prin1-to-string value)))
+           (op (alist-get (car atom) kubed-list-filter-operator-alist)))
+      (unless op (user-error "Unknown filter operator `%S'" (car atom)))
+      (lambda (x) (funcall op value (aref (cadr x) column-number))))))
+
+(defvar-local kubed-list-filter nil "Filter in effect in the current buffer.")
+
+(defun kubed-list-interpret-filter (&optional filter)
+  "Return function that implements FILTER.
+
+If FILTER is omitted or nil, it defaults to `kubed-list-filter'."
+  (let ((conjunction (or filter kubed-list-filter)))
+    (if (listp (car conjunction))
+        (let ((conjuncts
+               (mapcar (lambda (disjunction)
+                         (if (listp (car disjunction))
+                             (let ((disjuncts
+                                    (mapcar #'kubed-list-interpret-atomic-filter
+                                            disjunction)))
+                               (lambda (entry)
+                                 (catch 'keep-it
+                                   (dolist (pred disjuncts)
+                                     (when (funcall pred entry)
+                                       (throw 'keep-it t)))
+                                   nil)))
+                           ;; Single atomic disjunct.
+                           (kubed-list-interpret-atomic-filter disjunction)))
+                       (or conjunction kubed-list-filter))))
+          (lambda (entry)
+            (catch 'keep-it
+              (dolist (pred conjuncts)
+                (unless (funcall pred entry)
+                  (throw 'keep-it nil)))
+              t)))
+      ;; Single atomic conjunct.
+      (kubed-list-interpret-atomic-filter conjunction))))
+
+(defun kubed-list-validate-atomic-filter (atom)
+  "Return string explaining why ATOM is invalid, or nil if it is valid."
+  (unless (consp atom)
+    (throw 'validation-error
+           (format (substitute-quotes
+                    "Invalid atomic filter `%S', must be a list")
+                   atom)))
+  (if (eq (car-safe atom) 'quote)
+      (kubed-list-validate-atomic-filter (cadr atom))
+    (unless (length= atom 3)
+      (throw 'validation-error
+             (format (substitute-quotes
+                      "Invalid atomic filter `%S', must have three elements")
+                     atom)))
+    (unless (assq (car atom) kubed-list-filter-operator-alist)
+      (throw 'validation-error
+             (format (substitute-quotes
+                      "No operator `%S' in `kubed-list-filter-operator-alist'")
+                     (car atom))))
+    (unless (ignore-errors
+              (tabulated-list--column-number (symbol-name (nth 1 atom))))
+      (throw 'validation-error (format (substitute-quotes
+                                        "Invalid column name `%S'")
+                                       (nth 1 atom))))))
+
+(defun kubed-list-validate-filter (filter)
+  "Return string explaining why FILTER is invalid, or nil if it is valid."
+  (catch 'validation-error
+    (if (listp (car-safe filter))
+        (dolist (disjunction filter)
+          (if (and (consp disjunction) (listp (car disjunction)))
+              (dolist (disjunct disjunction)
+                (kubed-list-validate-atomic-filter disjunct))
+            (kubed-list-validate-atomic-filter disjunction)))
+      (kubed-list-validate-atomic-filter filter))))
+
+(defvar-local kubed-list-filter-history-variable nil
+  "History list variable to use for filter history in the current buffer.")
+
+(defvar-local kubed--list-read-filter-target-buffer nil
+  "Resource list buffer for which this minibuffer is reading a filter.")
+
+(defvar-local kubed-list-type nil)
+(defvar-local kubed-list-context nil)
+(defvar-local kubed-list-namespace nil)
+
+(defvar-local kubed-list-transient-extra-suffixes nil
+  "List of transient suffixes for the type of resources in current buffer.")
+
+(defun kubed-list-go-to-line (id)
+  "Go to beginning of table line with ID."
+  (let ((pos nil))
+    (save-excursion
+      ;; Wait for refresh to finish, if currently underway.
+      (while (process-live-p
+              (alist-get 'process (kubed--alist kubed-list-type
+                                                kubed-list-context
+                                                kubed-list-namespace)))
+        (accept-process-output
+         (alist-get 'process (kubed--alist kubed-list-type
+                                           kubed-list-context
+                                           kubed-list-namespace))
+         1))
+      (goto-char (point-min))
+      (while (not (or pos (eobp)))
+        (if (equal id (tabulated-list-get-id))
+            (setq pos (point))
+          (forward-line))))
+    (goto-char pos)))
+
+(defun kubed-list-try-read-filter ()
+  "Try to read a resource list filter in the minibuffer.
+
+Exit the minibuffer if successful, else report the error and move point
+to the location of the error.  If point is not already at the location
+of the error, push a mark before moving point."
+  (interactive "" minibuffer-mode)
+  (let* ((prompt-end (minibuffer-prompt-end))
+         (contents (minibuffer-contents))
+         (error-point nil) (error-message nil) (form nil) (inval nil))
+    (with-temp-buffer
+      (condition-case err
+          (progn
+            ;; FIXME: There is a small edge case here that could get
+            ;; better treatment: when `contents' ends with " ?", it
+            ;; espaces the terminating closing parenthesis and leads us
+            ;; to incorrectly report the input as incomplete.
+            (insert "(" contents ")")
+            (goto-char (point-min))
+            (setq form (read (current-buffer))))
+        (error (setq error-point (+ prompt-end (1- (point)))
+                     error-message err))))
+    (cond
+     (error-point
+      (unless (= (point) error-point) (push-mark))
+      (goto-char error-point)
+      (minibuffer-message (error-message-string error-message)))
+     ((setq inval (with-current-buffer kubed--list-read-filter-target-buffer
+                    (kubed-list-validate-filter form)))
+      (minibuffer-message inval))
+     (t (exit-minibuffer)))))
+
+(defvar-keymap kubed-list-read-filter-map
+  :parent minibuffer-local-map
+  "TAB" #'completion-at-point
+  "M-?" #'completion-help-at-point
+  "<remap> <exit-minibuffer>" #'kubed-list-try-read-filter)
+
+(defun kubed-list-read-filter (prompt)
+  "Prompt with PROMPT for a filter for the current buffer."
+  (let* ((buf (current-buffer))
+         (cols (seq-map #'car tabulated-list-format))
+         (vals (let ((tmp nil))
+                 (dolist (ent (alist-get 'resources
+                                         (kubed--alist kubed-list-type
+                                                       kubed-list-context
+                                                       kubed-list-namespace)))
+                   (let ((i 0))
+                     (dolist (col cols)
+                       (push (aref (cadr ent) i)
+                             (alist-get col tmp nil nil #'string=))
+                       (setq i (1+ i)))))
+                 (mapcar #'delete-dups tmp)))
+         (filter
+          (minibuffer-with-setup-hook
+              (lambda ()
+                (set-syntax-table emacs-lisp-mode-syntax-table)
+                (setq-local kubed--list-read-filter-target-buffer buf)
+                (add-hook
+                 'completion-at-point-functions
+                 (lambda ()
+                   (let ((cont (buffer-substring
+                                (minibuffer-prompt-end) (point)))
+                         (bounds (or (bounds-of-thing-at-point 'symbol)
+                                     (cons (point) (point)))))
+                     (with-temp-buffer
+                       (set-syntax-table emacs-lisp-mode-syntax-table)
+                       (insert "(" cont)
+                       (when-let ((fn-argi (elisp--fnsym-in-current-sexp))
+                                  (argi (cadr fn-argi)))
+                         (if (= argi 0)
+                             ;; Complete operators.
+                             (list
+                              (car bounds) (cdr bounds)
+                              (mapcar #'car kubed-list-filter-operator-alist))
+                           (when (car fn-argi)
+                             (cond
+                              ((= argi 1)
+                               ;; Complete column names.
+                               (list (car bounds) (cdr bounds) cols))
+                              ((= argi 2)
+                               ;; Complete column values.
+                               (when-let ((beg (nth 1 (syntax-ppss)))
+                                          ;; Grab preceding symbol.
+                                          (col (save-excursion
+                                                 (goto-char beg)
+                                                 (forward-char 1)
+                                                 (forward-sexp 2)
+                                                 (thing-at-point 'symbol))))
+                                 (list (car bounds) (cdr bounds)
+                                       (alist-get col vals
+                                                  nil nil #'string=)))))))))))
+                 nil t))
+            (read-from-minibuffer
+             (format-prompt prompt "disable")
+             (mapconcat #'prin1-to-string kubed-list-filter " ")
+             kubed-list-read-filter-map nil
+             kubed-list-filter-history-variable ""))))
+    (car (ignore-errors (read-from-string (format "(%s)" filter))))))
+
+(defun kubed-list-set-filter (filter)
+  "Set the filter of the current buffer to FILTER.
+
+FILTER determines which resources to keep.  FILTER can be an atomic
+filter, which is a list (OP COL VAL), where OP is an operator defined in
+`kubed-list-filter-operator-alist' (which see), COL is a symbol whose
+name is a column name, and VAL is a string or an object whose printed
+representation is compared to the value of the column COL according to
+OP.  For example, the atomic filter (= Name foobar) keeps only resources
+whose name is \"foobar\".  (= Name \"foobar\") does exactly the same.
+To negate an atomic filter, quote it.  E.g. use \\='(~ Namespace kube)
+to hide all resources in namespaces whose name contains \"kube\".
+
+FILTER can also be a list of sub-filters (SUB1 SUB2 ...) where each
+sub-filter is either an atomic filter or a list of atomic filters.  If a
+sub-filter is a list of atomic filters, then that sub-filter denotes the
+disjunction of those atomic filters.  FILTER denotes the conjunction of
+the sub-filters.  In particular, FILTER nil denotes the empty
+conjunction which is always true (keeps all resources).
+
+More examples:
+
+- `((= Name foobar) (~ Namespace kube))': keep only resources named
+  \"foobar\" in namespaces that contain \"kube\".
+- `(((= Name foobar) (~ Namespace kube)))': keep resources that are
+  either named \"foobar\" or in a namespace that contains \"kube\".
+- `(((= Name foobar) (~ Namespace kube)) \\='(~ Starttime 2024-07))':
+  keep only resources that are either named \"foobar\" or in a namespace
+  that contains \"kube\", and were not started during July 2024.
+
+Interactively, prompt for FILTER sans the outermost set of parenthesis.
+For example, enter \"= Name foobar\" in the minibuffer to specify the
+atomic FILTER (= Name foobar).
+
+See also Info node \"(kubed) List Filter\"."
+  (interactive (list (kubed-list-read-filter "Set filter")) kubed-list-mode)
+  (when-let ((validation-error (kubed-list-validate-filter filter)))
+    (user-error validation-error))
+  (setq-local kubed-list-filter filter)
+  (revert-buffer))
+
+(defun kubed-list-mark-for-deletion ()
+  "Mark Kubernetes resource at point for deletion."
+  (interactive "" kubed-list-mode)
+  (tabulated-list-put-tag (propertize "D" 'help-echo "Marked for deletion") t))
+
+(defun kubed-list-unmark ()
+  "Remove mark from Kubernetes resource at point."
+  (interactive "" kubed-list-mode)
+  (tabulated-list-put-tag " " t))
+
+(defun kubed-list-previous-column (&optional n)
+  "Move backward N columns.
+
+Interactively, N is the numeric value of the prefix argument, defaulting
+to 1."
+  (interactive "p" kubed-list-mode)
+  (kubed-list-next-column (- n)))
+
+(defun kubed-list-next-column (&optional n)
+  "Move forward N columns.
+
+Interactively, N is the numeric value of the prefix argument, defaulting
+to 1."
+  (interactive "p" kubed-list-mode)
+  (let ((next (point))
+        (times (abs (or n 1)))
+        (dir-fn (if (< 0 n)
+                    #'next-single-property-change
+                  #'previous-single-property-change)))
+    (dotimes (_ times)
+      (setq next (funcall dir-fn next 'tabulated-list-column-name))
+      (when (= (char-after next) ?\n)
+        ;; At line boundary, go to first/last column of next line.
+        (setq next (funcall dir-fn next 'tabulated-list-column-name)))
+      (unless next (user-error "End of table")))
+    (goto-char next)))
+
+(defun kubed-list-copy-as-kill (click)
+  "Copy name of Kubernetes resource at CLICK into the kill ring."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((ent (tabulated-list-get-entry (mouse-set-point click)))
+           (new (aref ent 0)))
+      (progn
+        (kill-new new)
+        (message "Copied resource name `%s'" new))
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-context-menu (menu click)
+  "Extend MENU with common actions on Kubernetes resource at CLICK."
+  (when (tabulated-list-get-entry (posn-point (event-start click)))
+    (define-key menu [kubed-list-select-resource]
+                '(menu-item "Select" kubed-list-select-resource))
+    (define-key menu [kubed-list-display-resource]
+                '(menu-item "Display" kubed-list-display-resource))
+    (define-key menu [kubed-list-delete]
+                '(menu-item "Delete" kubed-list-delete))
+    (define-key menu [kubed-list-patch]
+                '(menu-item "Patch" kubed-list-patch))
+    (define-key menu [kubed-list-edit]
+                '(menu-item "Edit" kubed-list-edit))
+    (define-key menu [kubed-list-kubectl-command]
+                '(menu-item "Execute `kubectl' command" kubed-list-kubectl-command))
+    (define-key menu [kubed-list-copy-as-kill]
+                '(menu-item "Copy name" kubed-list-copy-as-kill)))
+  menu)
+
+(defun kubed-list-update (&optional quiet)
+  "Update list of Kubernetes resources.
+
+If optional argument QUIET is non-nil, do not emit a message when
+starting to update.  Display a message when the update is done
+regardless of QUIET."
+  (interactive "" kubed-list-mode)
+  (kubed-update kubed-list-type kubed-list-context kubed-list-namespace)
+  (force-mode-line-update)
+  (unless quiet (minibuffer-message (format "Updating Kubernetes %S..." kubed-list-type))))
+
+(defun kubed-list-delete-marked ()
+  "Delete marked Kubernetes resources."
+  (interactive "" kubed-list-mode)
+  (let (delete-list)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (when (eq (char-after) ?D)
+          (push (tabulated-list-get-id) delete-list))
+        (forward-line)))
+    (if delete-list
+        (when (y-or-n-p (format "Delete %d marked Kubernetes resources?"
+                                (length delete-list)))
+          (save-excursion
+            (goto-char (point-min))
+            (while (not (eobp))
+              (when (member (tabulated-list-get-id) delete-list)
+                (tabulated-list-put-tag
+                 (propertize "K" 'help-echo "Deletion in progress")))
+              (forward-line)))
+          (let ((errb (generate-new-buffer " *kubed-list-delete-marked-stderr*"))
+                (buf (current-buffer)))
+            (make-process
+             :name "*kubed-list-delete-marked*"
+             :stderr errb
+             :command (append
+                       (list kubed-kubectl-program "delete" kubed-list-type)
+                       delete-list)
+             :sentinel (lambda (_proc status)
+                         (cond
+                          ((string= status "finished\n")
+                           (message (format "Deleted %d marked Kubernetes resources."
+                                            (length delete-list)))
+                           (when (buffer-live-p buf)
+                             (with-current-buffer buf
+                               (kubed-list-update t))))
+                          ((string= status "exited abnormally with code 1\n")
+                           (with-current-buffer errb
+                             (goto-char (point-max))
+                             (insert "\n" status))
+                           (display-buffer errb)))))))
+      (user-error "No Kubernetes resources marked for deletion"))))
+
+(defun kubed-list-display-resource (click)
+  "Display Kubernetes resource at CLICK in another window."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (kubed-display-resource
+       kubed-list-type resource kubed-list-context kubed-list-namespace)
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-select-resource (click)
+  "Display Kubernetes resource at CLICK in current window."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (switch-to-buffer
+       (kubed-display-resource-in-buffer
+        (concat "*Kubed "
+                (kubed-display-resource-short-description
+                 kubed-list-type resource kubed-list-context kubed-list-namespace)
+                "*")
+        kubed-list-type resource kubed-list-context kubed-list-namespace))
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-select-resource-other-window (click)
+  "Display Kubernetes resource at CLICK in other window and select that window."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (switch-to-buffer-other-window
+       (kubed-display-resource-in-buffer
+        (concat "*Kubed "
+                (kubed-display-resource-short-description
+                 kubed-list-type resource kubed-list-context kubed-list-namespace)
+                "*")
+        kubed-list-type resource kubed-list-context kubed-list-namespace))
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-delete (click)
+  "Delete Kubernetes resource at CLICK."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (when (y-or-n-p (format "Delete `%s'?" resource))
+        (kubed-delete-resources kubed-list-type (list resource)
+                                kubed-list-context kubed-list-namespace))
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-patch (click)
+  "Patch Kubernetes resource at CLICK."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (kubed-patch kubed-list-type resource
+                   (kubed-read-patch) kubed-list-context kubed-list-namespace)
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-edit (click)
+  "Edit Kubernetes resource at CLICK."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (kubed-edit-resource kubed-list-type resource
+                           kubed-list-context kubed-list-namespace)
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-kubectl-command (click)
+  "Use Kubernetes resource at CLICK as argument for `kubectl' command."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (kubed-kubectl-command
+       (kubed-read-kubectl-command
+        "Execute command: "
+        (cons (concat
+               " "
+               (when kubed-list-namespace (concat "-n " kubed-list-namespace " "))
+               (when kubed-list-context (concat "--context " kubed-list-context " "))
+               kubed-list-type " " resource)
+              ;; Put point after "kubectl ".
+              0)))
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-logs (click)
+  "Show logs for Kubernetes resource at CLICK."
+  (interactive (list last-nonmenu-event) kubed-list-mode)
+  (if-let ((resource (tabulated-list-get-id (mouse-set-point click))))
+      (kubed-logs kubed-list-type resource kubed-list-context kubed-list-namespace
+                  t t nil t)
+    (user-error "No Kubernetes resource at point")))
+
+(defun kubed-list-create (definition &optional kind)
+  "Create Kubernetes resource of kind KIND from definition file DEFINITION."
+  (interactive (list (kubed-read-resource-definition-file-name)) kubed-list-mode)
+  (kubed-create definition kind kubed-list-context)
+  (kubed-list-update t))
+
+(defun kubed-list-column-number-at-point ()
+  "Return table column number at point."
+  (let ((start (current-column))
+        (nb-cols (1- (length tabulated-list-format)))
+        (col-nb 0)
+        (total-width tabulated-list-padding)
+        (found nil))
+    (while (and (not found) (< col-nb nb-cols))
+      (if (>= start
+              (setq total-width
+                    (+ total-width
+                       (cadr (aref tabulated-list-format col-nb))
+                       (or (plist-get (nthcdr 3 (aref tabulated-list-format
+                                                      col-nb))
+                                      :pad-right)
+                           1))))
+          (setq col-nb (1+ col-nb))
+        (setq found t)))
+    col-nb))
+
+(defun kubed-list-fit-column-width-to-content (n)
+  "Fit width of Nth table column to its content.
+
+If N is negative, fit all columns.  Interactively, N is the column
+number at point, or the numeric prefix argument if you provide one."
+  (interactive
+   (list (if current-prefix-arg
+             (prefix-numeric-value current-prefix-arg)
+           (kubed-list-column-number-at-point)))
+   kubed-list-mode)
+  (if (< n 0)
+      ;; Fit all columns.
+      (let* ((num-cols (length tabulated-list-format))
+             (widths (apply #'vector (seq-map
+                                      ;; +2 to make room for sorting icon.
+                                      (lambda (col) (+ 2 (length (car col))))
+                                      tabulated-list-format))))
+        (save-excursion
+          (goto-char (point-min))
+          (while-let ((entry (tabulated-list-get-entry)))
+            (dotimes (i num-cols)
+              (aset widths i (max (aref widths i) (length (aref entry i)))))
+            (forward-line)))
+        (setq tabulated-list-format (copy-tree tabulated-list-format t))
+        (dotimes (i num-cols)
+          (setf (cadr (aref tabulated-list-format i))
+                (1+ (aref widths i)))))
+    ;; Fit Nth column.
+    (let* ((width (+ 2 (length (car (aref tabulated-list-format n))))))
+      (save-excursion
+        (goto-char (point-min))
+        (while-let ((entry (tabulated-list-get-entry)))
+          (setq width (max width (length (aref entry n))))
+          (forward-line)))
+      (setq tabulated-list-format (copy-tree tabulated-list-format t))
+      (setf (cadr (aref tabulated-list-format n)) (1+ width))))
+  (tabulated-list-print t)
+  (tabulated-list-init-header))
+
+(declare-function kubed-list-transient                 "kubed-transient" ())
+(declare-function kubed-transient-logs-for-pod         "kubed-transient" (val))
+(declare-function kubed-transient-logs-for-deployment  "kubed-transient" (val))
+(declare-function kubed-transient-logs-for-statefulset "kubed-transient" (val))
+(declare-function kubed-transient-logs-for-replicaset  "kubed-transient" (val))
+(declare-function kubed-transient-logs-for-job         "kubed-transient" (val))
+(declare-function kubed-transient-logs-for-service     "kubed-transient" (val))
+
+(defvar-keymap kubed-list-mode-map
+  :doc "Common keymap for Kubernetes resource list buffers."
+  "RET" #'kubed-list-select-resource
+  "o" #'kubed-list-select-resource-other-window
+  "C-o" #'kubed-list-display-resource
+  "D" #'kubed-list-delete
+  "P" #'kubed-list-patch
+  "x" #'kubed-list-delete-marked
+  "e" #'kubed-list-edit
+  "!" #'kubed-list-kubectl-command
+  "g" #'kubed-list-update
+  "/" #'kubed-list-set-filter
+  "|" #'kubed-list-fit-column-width-to-content
+  "d" #'kubed-list-mark-for-deletion
+  "u" #'kubed-list-unmark
+  "w" #'kubed-list-copy-as-kill
+  "C-i" #'kubed-list-next-column
+  "TAB" #'kubed-list-next-column
+  "C-S-i" #'kubed-list-previous-column
+  "S-TAB" #'kubed-list-previous-column
+  "<backtab>" #'kubed-list-previous-column
+  "+" #'kubed-list-create
+  "?" #'kubed-list-transient)
+
+(defun kubed-list-entries ()
+  "`tabulated-list-entries' function for `kubed-list-mode'."
+  (let ((pred (kubed-list-interpret-filter))
+        (ents nil))
+    (dolist (ent (alist-get 'resources (kubed--alist kubed-list-type
+                                                     kubed-list-context
+                                                     kubed-list-namespace)))
+      (when (funcall pred ent) (push ent ents)))
+    (reverse ents)))
+
+;;;###autoload
+(defun kubed-list-handle-bookmark (bookmark)
+  "Display Kubernetes resource according to BOOKMARK."
+  (require 'bookmark)
+  (let* ((type (bookmark-prop-get bookmark 'type))
+         (context (bookmark-prop-get bookmark 'context))
+         (namespace (bookmark-prop-get bookmark 'namespace))
+         (current (bookmark-prop-get bookmark 'current))
+         (buff-fn (intern (format "kubed-%s-buffer" type)))
+         (buff (apply buff-fn context (when namespace (list namespace)))))
+    (set-buffer buff)
+    (when current (kubed-list-go-to-line current))))
+
+(put 'kubed-list-handle-bookmark 'bookmark-handler-type "KubedList")
+
+(defun kubed-list-make-bookmark ()
+  "Return bookmark pointing to currently displayed Kubernetes resource."
+  (require 'bookmark)
+  (cons
+   (concat kubed-list-type
+           (when kubed-list-namespace (concat "@" kubed-list-namespace))
+           (when kubed-list-context   (concat "[" kubed-list-context "]")))
+   (append
+    (list
+     (cons 'handler #'kubed-list-handle-bookmark)
+     (cons 'filter kubed-list-filter)
+     (cons 'type kubed-list-type)
+     (cons 'context kubed-list-context)
+     (cons 'current (tabulated-list-get-id))
+     (cons 'namespace kubed-list-namespace)))))
+
+(defun kubed-list-revert (&rest _)
+  "Revert the current Kubernetes resources list buffer.
+
+This is the `revert-buffer-function' for `kubed-list-mode' and its
+derivatives.  This function does not fetch new data from Kubernetes, it
+only (re)displays the existing data."
+  (let (marks)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (unless (eq (char-after) ?\s)
+          (push (cons (tabulated-list-get-id)
+                      ;; Preserve mark text properties.
+                      (buffer-substring (point) (1+ (point))))
+                marks))
+        (forward-line)))
+    (tabulated-list-revert)
+    (save-excursion
+      (goto-char (point-min))
+      (while (not (eobp))
+        (let ((id (tabulated-list-get-id)))
+          (when-let ((mark (alist-get id marks nil nil #'equal)))
+            (tabulated-list-put-tag mark)))
+        (forward-line)))))
+
+(define-derived-mode kubed-list-mode tabulated-list-mode "Kubernetes Resources"
+  "Major mode for listing Kubernetes resources.
+
+Modes for specific resource types, such as `kubed-pods-mode', use this
+mode as their parent."
+  :interactive nil
+  (setq-local truncate-string-ellipsis (propertize ">" 'face 'shadow)
+              tabulated-list-entries #'kubed-list-entries
+              bookmark-make-record-function #'kubed-list-make-bookmark
+              revert-buffer-function #'kubed-list-revert)
+  (add-hook 'context-menu-functions #'kubed-list-context-menu nil t))
+
+;;;###autoload
+(defun kubed-delete-resources (type resources context &optional namespace)
+  "Delete Kubernetes RESOURCES of type TYPE in context CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCES.  With a prefix argument \
+\\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \
+\\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well."
+  (interactive
+   (let ((type nil) (context nil) (namespace nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-delete))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless type
+       (setq type (kubed-read-resource-type "Type of resource to delete"
+                                            nil context)))
+     (when (and (null namespace) (kubed-namespaced-p type context))
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list type (kubed-read-resource-name type "Delete" nil t
+                                          context namespace)
+           context namespace)))
+  (unless resources (user-error "You didn't specify %s to delete" type))
+  (message (format "Deleting Kubernetes %s `%s'..."
+                   type (string-join resources "', `")))
+  (if (zerop (apply #'call-process
+                    kubed-kubectl-program nil nil nil
+                    "delete" type
+                    (append (when namespace (list "-n" namespace))
+                            (when context (list "--context" context))
+                            resources)))
+      (message (format "Deleting Kubernetes %s `%s'...  Done."
+                       type (string-join resources "', `")))
+    (error (format "Failed to delete Kubernetes %s `%s'"
+                   type (string-join resources "', `")))))
+
+;;;###autoload
+(defun kubed-edit-resource (type resource context &optional namespace)
+  "Edit Kubernetes RESOURCE of type TYPE in context CONTEXT.
+
+For namespaced resource types, NAMESPACE is the namespace of RESOURCE.
+
+Interactively, use the current context and namespace by default, and
+prompt for TYPE and RESOURCES.  With a prefix argument \
+\\[universal-argument],
+prompt for NAMESPACE.  With a double prefix argument \
+\\[universal-argument] \\[universal-argument],
+prompt for CONTEXT as well."
+  (interactive
+   (let ((type nil) (context nil) (namespace nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-edit))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless type
+       (setq type (kubed-read-resource-type "Type of resource to edit"
+                                            nil context)))
+     (when (and (null namespace) (kubed-namespaced-p type context))
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list type (kubed-read-resource-name type "Edit" nil nil
+                                          context namespace)
+           context namespace)))
+  (unless (bound-and-true-p server-process) (server-start))
+  (let ((process-environment
+         (cons (if (boundp 'emacsclient-program-name)
+                   (concat "KUBE_EDITOR=" emacsclient-program-name)
+                 "KUBE_EDITOR=emacsclient")
+               process-environment)))
+    (apply #'start-process
+           (format "*kubed-%S-edit*" type) nil
+           kubed-kubectl-program "edit" type resource
+           (append
+            (when namespace (list "-n" namespace))
+            (when context (list "--context" context))))))
+
+(defcustom kubed-list-mode-line-format
+  '(:eval (cond
+           ((process-live-p
+             (alist-get 'process
+                        (kubed--alist kubed-list-type
+                                      kubed-list-context
+                                      kubed-list-namespace)))
+            (propertize "[...]" 'help-echo "Updating..."))
+           (kubed-list-filter
+            (propertize
+             (concat "[" (mapconcat #'prin1-to-string kubed-list-filter " ") "]")
+             'help-echo "Current filter"))))
+  "Mode line construct for indicating status of Kubernetes resources list."
+  :type 'sexp
+  :risky t)
+
+;;;###autoload
+(defmacro kubed-define-resource (resource &optional properties &rest commands)
+  "Define Kubernetes RESOURCE with associated PROPERTIES and COMMANDS.
+
+RESOURCE is a symbol corresponding to a Kubernetes resource type, such
+as `pod' or `service'.  This macro defines the following commands for
+interacting with Kubernetes RESOURCEs:
+
+- `kubed-display-RESOURCE': prompt for a RESOURCE and display its
+  description in YAML format.  See also `kubed-yaml-setup-hook'.
+- `kubed-edit-RESOURCE': prompt for a RESOURCE and edit it.
+- `kubed-delete-RESOURCE': prompt for a RESOURCE and delete it.
+- `kubed-list-RESOURCEs': display a buffer listing all RESOURCEs in the
+  current namespace.  The RESOURCEs list buffer uses a dedicated major
+  mode, `kubed-RESOURCEs-mode', which is also defined by this macro.
+- `kubed-create-RESOURCE': create a RESOURCE from a YAML or a JSON file.
+- `kubed-explain-RESOURCEs': show buffer with help about RESOURCEs.
+
+This macro also defines a prefix keymap, `kubed-RESOURCE-prefix-map',
+with bindings for the above commands.
+
+PROPERTIES is a list of lists (PROP JSON-PATH WIDTH SORT FORMAT . ATTRS)
+that specify properties of RESOURCEs.  PROP is the name of the property,
+as a symbol; JSON-PATH is a JSONPath expression that evaluates to the
+value of PROP when applied to the JSON representation of a RESOURCE.
+WIDTH, SORT, FORMAT and ATTRS are optional and can be omitted.  WIDTH is
+used as the default width of the column corresponding to PROP in
+RESOURCEs list buffers; SORT is sort predicate, a function that takes
+two values of PROP as strings and return non-nil if the first should
+sort before the second; FORMAT is a function that takes a value of PROP
+and formats it; and ATTRS is a plist of additional attributes of the
+PROP column, see `tabulated-list-format' for available attributes.  For
+example, (phase \".status.phase\" 10) says that RESOURCE has a `phase'
+property at JSONPath \".status.phase\" whose values are typically 10
+columns wide.  The first property in PROPERTIES, is used to annotate
+completion candidates when prompting for a RESOURCE.
+
+COMMANDS is a list of elements (COMMAND KEYS DOC-PREFIX . BODY) that
+define commands for RESOURCE list buffers.  COMMAND is a symbol
+specifying the suffix of the command name, the full name of the command
+is `kubed-RESOURCEs-COMMAND' (for example, `kubed-pods-shell'); KEYS is
+either a string that specifies a key sequence to bind to the command in
+`kubed-RESOURCEs-mode-map', or nil if the command should not be bound;
+DOC-PREFIX is a string used to construct the docstring of the command,
+this macro appends the string \" Kubernetes RESOURCE at point.\" to it
+to obtain the final docstring; lastly, BODY is the body the command.
+Within BODY, the variable RESOURCE is let-bound to the name of the
+RESOURCE at point.  For example, if RESOURCE is `pod', the following
+COMMANDS element defines a command `kubed-pods-frob' and binds it to the
+key \"f\" in `kubed-pods-mode-map':
+
+  (frob \"f\" \"Frobnicate\"
+        (message \"Preparing...\")
+        (frobnicate-pod pod)
+        (message \"Done.\"))
+
+By default, this macro assumes that RESOURCE is namespaced.  To define a
+namespaceless resource type, put `:namespaced nil' before COMMANDS:
+
+  (kubed-define-resource global-thingy (PROP1 PROP2 ...) :namespaced nil
+    CMD1
+    CMD2
+    ...)
+
+Other keyword arguments that go between PROPERTIES and COMMANDS are:
+
+- `:create (ARGLIST DOCSTRING INTERACTIVE BODY...)': specialize the
+  resource creation command, `kubed-create-RESOURCE'.  ARGLIST,
+  DOCSTRING, INTERACTIVE and BODY have the same meaning as in `defun'.
+- `:prefix ((KEY LABEL DEFINITION)...)': additional keybinding for the
+  prefix keymap `kubed-RESOURCE-prefix-map' and the
+  `kubed-RESOURCE-menu-map' menu.  Each element (KEY LABEL DEFINITION)
+  says to bind KEY to DEFINITION in `kubed-RESOURCE-menu-map', and to
+  add DEFINITION to `kubed-RESOURCE-menu-map' with the label LABEL.
+- `:plural PLURAL': specify plural form of RESOURCE, as a symbol.  If
+  you omit this keyword argument, the plural form defaults to RESOURCE
+  followed by \"s\".
+- `:logs t': generate `kubed-logs-for-RESOURCE' command.
+- `:suffixes (SUFFIX...)': add type-specific transient suffixes to
+  `kubed-list-transient'."
+  (declare (indent 2))
+  (let ((plrl-var (intern (format "%Ss"                         resource)))
+        (read-fun (intern (format "kubed-read-%S"               resource)))
+        (dsp-name (intern (format "kubed-display-%S"            resource)))
+        (edt-name (intern (format "kubed-edit-%S"               resource)))
+        (crt-name (intern (format "kubed-create-%S"             resource)))
+        (map-name (intern (format "kubed-%S-prefix-map"         resource)))
+        (menu-map (intern (format "kubed-%S-menu-map"           resource)))
+        (logs-cmd (intern (format "kubed-logs-for-%S"           resource)))
+        (logs-trs (intern (format "kubed-transient-logs-for-%S" resource)))
+        (namespaced t) (logs nil)
+        (keyword nil)
+        frmt-var buff-fun list-cmd expl-cmd dlt-name mod-name
+        ctxt-fun crt-spec prf-keys hist-var trs-cols)
+
+    ;; Process keyword arguments.
+    (while (keywordp (car commands))
+      (setq keyword (pop commands))
+      (cond
+       ((eq keyword :namespaced) (setq namespaced (pop commands)))
+       ((eq keyword :logs)       (setq logs       (pop commands)))
+       ((eq keyword :create)     (setq crt-spec   (pop commands)))
+       ((eq keyword :prefix)     (setq prf-keys   (pop commands)))
+       ((eq keyword :plural)     (setq plrl-var   (pop commands)))
+       ((eq keyword :suffixes)   (setq trs-cols   (pop commands)))
+       ;; FIXME: Add error for unknown keywords.
+       (t (pop commands))))
+
+    (setq frmt-var (intern (format "kubed-%S-columns"       plrl-var))
+          hist-var (intern (format "kubed-%S-history"       plrl-var))
+          buff-fun (intern (format "kubed-%S-buffer"        plrl-var))
+          list-cmd (intern (format "kubed-list-%S"          plrl-var))
+          expl-cmd (intern (format "kubed-explain-%S"       plrl-var))
+          dlt-name (intern (format "kubed-delete-%S"        plrl-var))
+          mod-name (intern (format "kubed-%S-mode"          plrl-var))
+          ctxt-fun (intern (format "kubed-%S-context-menu"  plrl-var)))
+
+    (when logs (push `("L" "Show Logs" ,logs-cmd) prf-keys))
+
+    ;; Generate code.
+    `(progn
+       (setf (alist-get ,(symbol-name plrl-var) kubed--columns nil nil #'string=)
+             (list
+              '("NAME:.metadata.name")
+              ,@(mapcar (lambda (p)
+                          `(cons ,(concat (upcase (symbol-name (car p)))
+                                          ":"
+                                          (cadr p))
+                                 ,(nth 4 p)))
+                        properties)))
+
+       (defvar ,hist-var nil
+         ,(format "History list for `%S'." read-fun))
+
+       (defun ,read-fun (prompt &optional default multi context
+                                . ,(when namespaced '(namespace)))
+         ,(format "Prompt with PROMPT for a Kubernetes %S name.
+
+Optional argument DEFAULT is the minibuffer default argument.
+
+Non-nil optional argument MULTI says to read and return a list
+of %S, instead of just one." resource plrl-var)
+         (kubed-read-resource-name ,(symbol-name plrl-var) prompt default multi context . ,(when namespaced '(namespace))))
+
+       (defun ,dsp-name (,resource &optional context . ,(when namespaced '(namespace)))
+         ,(if namespaced
+              (format "Display Kubernetes %S %s in CONTEXT and NAMESPACE.
+
+Interactively, use the current context and namespace by default.  With a
+prefix argument \\[universal-argument], prompt for NAMESPACE.  With a
+double prefix argument \\[universal-argument] \\[universal-argument], \
+prompt for CONTEXT as well." resource (upcase (symbol-name resource)))
+            (format "Display Kubernetes %S %s in context CONTEXT.
+
+Interactively, use the current context.  With a prefix argument
+\\[universal-argument], prompt for CONTEXT." resource (upcase (symbol-name resource))))
+         (interactive
+          ,(if namespaced
+               `(let ((context nil) (namespace nil))
+                  (dolist (arg (kubed-transient-args 'kubed-transient-display))
+                    (cond
+                     ((string-match "--namespace=\\(.+\\)" arg)
+                      (setq namespace (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))))
+                  (unless namespace
+                    (setq namespace (kubed--namespace context current-prefix-arg)))
+                  (list (,read-fun "Display" nil nil context namespace) context namespace))
+             `(let ((context nil))
+                (dolist (arg (kubed-transient-args 'kubed-transient-display))
+                  (cond
+                   ((string-match "--context=\\(.+\\)" arg)
+                    (setq context (match-string 1 arg)))))
+                (unless context
+                  (setq context
+                        (let ((cxt (kubed-local-context)))
+                          (if current-prefix-arg
+                              (kubed-read-context "Context" cxt)
+                            cxt))))
+                (list (,read-fun "Display" nil nil context) context))))
+         (let ((context (or context (kubed-local-context))))
+           (kubed-display-resource
+            ,(symbol-name plrl-var) ,resource context
+            . ,(when namespaced '((or namespace (kubed--namespace context)))))))
+
+       (defun ,edt-name (,resource &optional context . ,(when namespaced '(namespace)))
+         ,(if namespaced
+              (format "Edit Kubernetes %S %s in CONTEXT and NAMESPACE.
+
+Interactively, use the current context and namespace by default.  With a
+prefix argument \\[universal-argument], prompt for NAMESPACE.  With a
+double prefix argument \\[universal-argument] \\[universal-argument], \
+prompt for CONTEXT as well." resource (upcase (symbol-name resource)))
+            (format "Edit Kubernetes %S %s in context CONTEXT.
+
+Interactively, use the current context.  With a prefix argument
+\\[universal-argument], prompt for CONTEXT." resource (upcase (symbol-name resource))))
+         (interactive
+          ,(if namespaced
+               `(let ((context nil) (namespace nil))
+                  (dolist (arg (kubed-transient-args 'kubed-transient-edit))
+                    (cond
+                     ((string-match "--namespace=\\(.+\\)" arg)
+                      (setq namespace (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))))
+                  (unless namespace
+                    (setq namespace (kubed--namespace context current-prefix-arg)))
+                  (list (,read-fun "Edit" nil nil context namespace) context namespace))
+             `(let ((context nil))
+                (dolist (arg (kubed-transient-args 'kubed-transient-edit))
+                  (cond
+                   ((string-match "--context=\\(.+\\)" arg)
+                    (setq context (match-string 1 arg)))))
+                (unless context
+                  (setq context
+                        (let ((cxt (kubed-local-context)))
+                          (if current-prefix-arg
+                              (kubed-read-context "Context" cxt)
+                            cxt))))
+                (list (,read-fun "Edit" nil nil context) context))))
+         (let ((context (or context (kubed-local-context))))
+           (kubed-edit-resource
+            ,(symbol-name plrl-var) ,resource context
+            . ,(when namespaced '((or namespace (kubed--namespace context)))))))
+
+       (defun ,dlt-name (,plrl-var &optional context
+                                   . ,(when namespaced '(namespace)))
+         ,(if namespaced
+              (format "Delete Kubernetes %S %s in CONTEXT and NAMESPACE.
+
+Interactively, use the current context and namespace by default.  With a
+prefix argument \\[universal-argument], prompt for NAMESPACE.  With a
+double prefix argument \\[universal-argument] \\[universal-argument], \
+prompt for CONTEXT as well." plrl-var (upcase (symbol-name plrl-var)))
+            (format "Delete Kubernetes %S %s in context CONTEXT.
+
+Interactively, use the current context.  With a prefix argument
+\\[universal-argument], prompt for CONTEXT." plrl-var (upcase (symbol-name plrl-var))))
+         (interactive
+          ,(if namespaced
+               `(let ((context nil) (namespace nil))
+                  (dolist (arg (kubed-transient-args 'kubed-transient-delete))
+                    (cond
+                     ((string-match "--namespace=\\(.+\\)" arg)
+                      (setq namespace (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))))
+                  (unless namespace
+                    (setq namespace (kubed--namespace context current-prefix-arg)))
+                  (list (,read-fun "Delete" nil t context namespace) context namespace))
+             `(let ((context nil))
+                (dolist (arg (kubed-transient-args 'kubed-transient-delete))
+                  (cond
+                   ((string-match "--context=\\(.+\\)" arg)
+                    (setq context (match-string 1 arg)))))
+                (unless context
+                  (setq context
+                        (let ((cxt (kubed-local-context)))
+                          (if current-prefix-arg
+                              (kubed-read-context "Context" cxt)
+                            cxt))))
+                (list (,read-fun "Delete" nil t context) context))))
+         (unless ,plrl-var
+           (user-error ,(format "You didn't specify %S to delete" plrl-var)))
+         (let ((context (or context (kubed-local-context))))
+           (kubed-delete-resources
+            ,(symbol-name plrl-var) ,plrl-var context
+            . ,(when namespaced '((or namespace (kubed--namespace context)))))))
+
+       ,(if crt-spec `(defun ,crt-name . ,crt-spec)
+          `(defun ,crt-name (definition &optional context)
+             ,(format "Create Kubernetes %s from file DEFINITION in CONTEXT.
+
+Interactively, prompt for DEFINITION and use the current context.  With
+a prefix argument \\[universal-argument], prompt for CONTEXT too."
+                      (symbol-name resource))
+             (interactive
+              (let ((context nil))
+                (dolist (arg (kubed-transient-args 'kubed-transient-create))
+                  (cond
+                   ((string-match "--context=\\(.+\\)" arg)
+                    (setq context (match-string 1 arg)))))
+                (unless context
+                  (setq context
+                        (let ((cxt (kubed-local-context)))
+                          (if current-prefix-arg
+                              (kubed-read-context "Context" cxt)
+                            cxt))))
+                (list (kubed-read-resource-definition-file-name ,(symbol-name resource))
+                      context)))
+             (kubed-create definition ,(symbol-name resource) context)
+             (kubed-update ,(symbol-name plrl-var) context)))
+
+       ,@(when logs
+           `((defun ,logs-cmd
+                 ( ,resource &optional context namespace
+                   container follow limit prefix since tail timestamps)
+               ,(format "Show logs for Kubernetes %S %s."
+                        resource (upcase (symbol-name resource)))
+               (interactive
+                (let ( ,resource context namespace
+                       container follow limit prefix since tail timestamps)
+                  (dolist (arg (kubed-transient-args
+                                ',logs-trs))
+                    (cond
+                     ((string-match "--namespace=\\(.+\\)" arg)
+                      (setq namespace (match-string 1 arg)))
+                     ((string-match "--context=\\(.+\\)" arg)
+                      (setq context (match-string 1 arg)))
+                     ((string-match "--limit-bytes=\\(.+\\)" arg)
+                      (setq limit (string-to-number (match-string 1 arg))))
+                     ((string-match "--tail=\\(.+\\)" arg)
+                      (setq tail (string-to-number (match-string 1 arg))))
+                     ((string-match "--since-time=\\(.+\\)" arg)
+                      (setq since (match-string 1 arg)))
+                     ((equal "--all-containers" arg) (setq container t))
+                     ((equal "--follow" arg) (setq follow t))
+                     ((equal "--prefix" arg) (setq prefix t))
+                     ((equal "--timestamps" arg) (setq timestamps t))))
+                  (unless context
+                    (setq context
+                          (let ((cxt (kubed-local-context)))
+                            (if (equal current-prefix-arg '(16))
+                                (kubed-read-context "Context" cxt)
+                              cxt))))
+                  (unless namespace
+                    (setq namespace (kubed--namespace context current-prefix-arg)))
+                  (setq ,resource (,read-fun "Show logs for"
+                                             (and (equal context kubed-list-context)
+                                                  (equal namespace kubed-list-namespace)
+                                                  (equal ,(symbol-name plrl-var) kubed-list-type)
+                                                  (tabulated-list-get-id (mouse-set-point last-nonmenu-event)))
+                                             nil context namespace))
+                  (list ,resource context namespace
+                        ,(if (eq resource 'pod)
+                             '(or container
+                                  (kubed-read-container pod "Container" t
+                                                        context namespace))
+                           'container)
+                        follow limit prefix since tail timestamps)))
+               (kubed-logs ,(symbol-name plrl-var) ,resource context namespace container
+                           follow limit prefix since tail timestamps))))
+
+       ,@(let ((click-var (gensym "click")))
+           (mapcar
+            (pcase-lambda (`(,suffix ,_key ,desc . ,body))
+              `(defun ,(intern (format "kubed-%S-%S" plrl-var suffix)) (,click-var)
+                 ,(format "%s Kubernetes %S at point." desc resource)
+                 (interactive (list last-nonmenu-event) ,mod-name)
+                 (if-let ((,resource (tabulated-list-get-id (mouse-set-point ,click-var))))
+                     (progn ,@body)
+                   (user-error ,(format "No Kubernetes %S at point" resource)))))
+            commands))
+
+       (defvar-keymap ,(intern (format "kubed-%S-mode-map" plrl-var))
+         :doc ,(format "Keymap for `%S" mod-name)
+         "+" #',crt-name
+         ,@(when logs
+             `("l" #'kubed-list-logs
+               "L" #',logs-trs))
+         ,@(mapcan
+            (pcase-lambda (`(,suffix ,key ,_desc . ,_body))
+              (when key
+                (list key `#',(intern (format "kubed-%S-%S" plrl-var suffix)))))
+            commands))
+
+       (defvar ,frmt-var
+         ',(let ((i 0)
+                 (res nil))
+             (dolist (p properties)
+               (setq i (1+ i))
+               (push
+                (append
+                 (list (capitalize (symbol-name (car p)))
+                       (caddr p)
+                       (if-let ((sorter (cadddr p)))
+                           `(lambda (l r)
+                              (funcall ,sorter (aref (cadr l) ,i) (aref (cadr r) ,i)))
+                         t))
+                 (nthcdr 5 p))
+                res))
+             (reverse res)))
+
+       (defun ,ctxt-fun (menu . ,(if commands '(click) '(_click)))
+         ,@(when commands
+             `((when (tabulated-list-get-entry (posn-point (event-start click)))
+                 ,@(mapcar
+                    (pcase-lambda (`(,suffix ,_key ,desc . ,_body))
+                      `(define-key
+                        menu [,(intern (format "kubed-%S-%S" plrl-var suffix))]
+                        (list 'menu-item ,(format "%s this %S" desc resource)
+                              #',(intern (format "kubed-%S-%S" plrl-var suffix)))))
+                    (reverse commands)))))
+         menu)
+
+       (define-derived-mode ,mod-name kubed-list-mode
+         '(,(format "Kubernetes %s" (capitalize (symbol-name plrl-var)))
+           kubed-list-mode-line-format)
+         ,(format "Major mode for listing Kubernetes %S." plrl-var)
+         :interactive nil
+         (setq kubed-list-filter-history-variable
+               ',(intern (format "kubed-%S-filter-history" plrl-var))
+               kubed-list-type ,(symbol-name plrl-var)
+               ,@(when trs-cols
+                   `(kubed-list-transient-extra-suffixes ',trs-cols))
+               tabulated-list-padding 2
+               tabulated-list-format (apply #'vector (cons kubed-name-column ,frmt-var)))
+         (add-hook 'context-menu-functions #',ctxt-fun nil t)
+         (tabulated-list-init-header))
+
+       (defun ,buff-fun (context . ,(when namespaced '(namespace)))
+         (let ((buf-name (format ,(format "*Kubed %S%%s*" plrl-var)
+                                 ,(if namespaced
+                                      `(concat "@" namespace "[" context "]")
+                                    `(concat "[" context "]")))))
+           (if-let ((buf (get-buffer buf-name))) buf
+             (with-current-buffer (get-buffer-create buf-name)
+               (,mod-name)
+               (setq kubed-list-context context
+                     ,@(when namespaced
+                         '(kubed-list-namespace namespace)))
+               (kubed-list-update)
+               (tabulated-list-print)
+               (current-buffer)))))
+
+       (defun ,list-cmd (context . ,(when namespaced '(namespace)))
+         ,(if namespaced
+              (format "List Kubernetes %S in context CONTEXT and namespace NAMESPACE.
+
+Interactively, use the current context and namespace by default.  With a
+prefix argument \\[universal-argument], prompt for NAMESPACE.  With a
+double prefix argument \\[universal-argument] \\[universal-argument], \
+prompt for CONTEXT as well." plrl-var)
+            (format "List Kubernetes %S in context CONTEXT.
+
+Interactively, use the current context.  With a prefix argument
+\\[universal-argument], prompt for CONTEXT." plrl-var))
+         (interactive
+          (let* ((context (kubed-local-context))
+                 (context (if (equal current-prefix-arg
+                                     ,(if namespaced ''(16) ''(4)))
+                              (kubed-read-context "Context" context)
+                            context)))
+            (list context
+                  . ,(when namespaced '((kubed--namespace context current-prefix-arg))))))
+         (pop-to-buffer (,buff-fun context . ,(when namespaced '(namespace)))))
+
+       (defun ,expl-cmd ()
+         ,(format "Show help buffer with explanation about Kubernetes %S." plrl-var)
+         (interactive)
+         (kubed-explain ,(symbol-name plrl-var)))
+
+       (defvar-keymap ,map-name
+         :doc ,(format "Prefix keymap for Kubed %S commands." resource)
+         :prefix ',map-name
+         "l" #',list-cmd
+         "+" #',crt-name
+         "e" #',edt-name
+         "d" #',dlt-name
+         "RET" #',dsp-name
+         "E" #',expl-cmd
+         ,@(mapcan
+            (pcase-lambda (`(,key ,_label ,cmd))
+              (list key `#',cmd))
+            prf-keys))
+
+       (defvar-keymap ,menu-map
+         :doc ,(format "Keymap with Kubernetes %S related menu entries." resource)
+         :prefix ',menu-map
+         "<list>"    '("List"           . ,list-cmd)
+         "<create>"  '("Create"         . ,crt-name)
+         "<edit>"    '("Edit"           . ,edt-name)
+         "<delete>"  '("Delete"         . ,dlt-name)
+         "<display>" '("Display"        . ,dsp-name)
+         "<explain>" '("Explain Fields" . ,expl-cmd)
+         ,@(mapcan
+            (pcase-lambda (`(,key ,label ,cmd))
+              (list key `'(,label . ,cmd)))
+            prf-keys)))))
+
+(setf
+ ;; Teach Imenu about `kubed-define-resource'.
+ (alist-get "KubedResource" lisp-imenu-generic-expression nil nil #'equal)
+ (list (concat "^\\s-*(kubed-define-resource\\s-+\\("
+               lisp-mode-symbol-regexp
+               "\\)")
+       1))
+
+(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-read-container pod "Container" t context namespace)
+          ":" file-name))
+
+(declare-function kubed-tramp-context          "kubed-tramp" (file-name))
+(declare-function kubed-tramp-namespace        "kubed-tramp" (file-name))
+(declare-function kubed-tramp-assert-support   "kubed-tramp" ())
+
+;;;###autoload (autoload 'kubed-display-pod "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-pod "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-pods "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-pods "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-pod "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-pod "kubed" nil t)
+;;;###autoload (autoload 'kubed-pod-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource pod
+    ((phase ".status.phase" 10
+            nil                         ; sorting function
+            (lambda (ph)
+              (if-let ((face (pcase ph
+                               ;; TODO: Define/derive bespoke faces.
+                               ("Pending"   'warning)
+                               ("Running"   'success)
+                               ("Succeeded" 'shadow)
+                               ("Failed"    'error))))
+                  (propertize ph 'face face)
+                ph)))
+     (ready ".status.containerStatuses[?(.ready==true)].name" 6
+            (lambda (l r) (< (string-to-number l) (string-to-number r)))
+            (lambda (cs)
+              (if (string= cs "<none>") "0"
+                (number-to-string (1+ (seq-count (lambda (c) (= c ?,)) cs)))))
+            :right-align t)
+     (total ".status.containerStatuses[*].name" 6
+            (lambda (l r) (< (string-to-number l) (string-to-number r)))
+            (lambda (cs)
+              (if (string= cs "<none>") "0"
+                (number-to-string (1+ (seq-count (lambda (c) (= c ?,)) cs)))))
+            :right-align t)
+     (starttime ".status.startTime" 20))
+  :logs t
+  :prefix (("A" "Attach"       kubed-attach)
+           ("X" "Execute"      kubed-exec)
+           ("F" "Forward Port" kubed-forward-port-to-pod))
+  :suffixes ([ ("L" "Logs" kubed-transient-logs-for-pod)
+               ("X" "Exec" kubed-pods-exec)
+               ("a" "Attach" kubed-pods-attach)]
+             [ :pad-keys t
+               ("C-d" "Dired" kubed-pods-dired)
+               ("s" "Shell" kubed-pods-shell)
+               ("F" "Forward port" kubed-pods-forward-port)])
+  (dired "C-d" "Start Dired in"
+         (kubed-tramp-assert-support)
+         (dired (kubed-remote-file-name
+                 kubed-list-context kubed-list-namespace pod)))
+  (shell "s" "Start shell in"
+         (kubed-tramp-assert-support)
+         (let* ((default-directory (kubed-remote-file-name
+                                    kubed-list-context kubed-list-namespace pod)))
+           (shell
+            (concat "*Kubed Shell "
+                    (kubed-display-resource-short-description
+                     "pods" pod kubed-list-context kubed-list-namespace)
+                    "*"))))
+  (attach "a" "Attach to remote process running on"
+          (kubed-attach pod (kubed-read-container pod "Container" t
+                                                  kubed-list-context
+                                                  kubed-list-namespace)
+                        kubed-list-context kubed-list-namespace t t))
+  (exec "X" "Execute command in"
+        (let ((container (kubed-read-container pod "Container" t
+                                               kubed-list-context
+                                               kubed-list-namespace))
+              (cmd-args (split-string-and-unquote
+                         (read-string "Execute command: "))))
+          (kubed-exec pod (car cmd-args) container kubed-list-context
+                      kubed-list-namespace t t (cdr cmd-args))))
+  (forward-port "F" "Forward local network port to remote port of"
+                (let ((local-port (read-number "Forward local port: ")))
+                  (kubed-forward-port-to-pod
+                   pod local-port
+                   (read-number (format "Forward local port %d to remote port: "
+                                        local-port))
+                   kubed-list-context
+                   kubed-list-namespace))))
+
+;;;###autoload (autoload 'kubed-display-namespace "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-namespace "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-namespaces "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-namespaces "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-namespace "kubed" nil t)
+;;;###autoload (autoload 'kubed-namespace-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource namespace
+    ((phase ".status.phase" 10
+            nil                         ; sorting function
+            (lambda (ph)
+              (if-let ((face (pcase ph
+                               ;; TODO: Define/derive bespoke faces.
+                               ("Active"      'success)
+                               ("Terminating" 'shadow))))
+                  (propertize ph 'face face)
+                ph)))
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :namespaced nil
+  :prefix (("S" "Set" kubed-set-namespace))
+  :create
+  ((name &optional context)
+   "Create Kubernetes namespace NAME in CONTEXT.
+
+Interactively, prompt for NAME and use the current context by default.
+With a prefix argument, prompt for CONTEXT instead."
+   (interactive
+    (let ((context nil))
+      (dolist (arg (kubed-transient-args 'kubed-transient-create))
+        (cond
+         ((string-match "--context=\\(.+\\)" arg)
+          (setq context (match-string 1 arg)))))
+      (unless context
+        (setq context
+              (let ((cxt (kubed-local-context)))
+                (if current-prefix-arg
+                    (kubed-read-context "Context" cxt)
+                  cxt))))
+      (list (read-string "Create namespace with name: ") context)))
+   (unless (zerop
+            (apply #'call-process
+                   kubed-kubectl-program nil nil nil
+                   "create" "namespace" name
+                   (when context (list "--context" context))))
+     (user-error "Failed to create Kubernetes namespace with name `%s'" name))
+   (kubed-update "namespaces" (or context (kubed-local-context)))
+   (message "Created Kubernetes namespace with name `%s'." name))
+  (set "s" "Set current namespace to"
+       (save-excursion
+         (goto-char (point-min))
+         (while (not (eobp))
+           (when (eq (char-after) ?*)
+             (tabulated-list-put-tag " "))
+           (forward-line)))
+       (kubed-set-namespace namespace kubed-list-context)
+       (tabulated-list-put-tag
+        (propertize "*" 'help-echo "Current namespace"))))
+
+;;;###autoload (autoload 'kubed-display-persistentvolume "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-persistentvolume "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-persistentvolumes "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-persistentvolumes "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-persistentvolume "kubed" nil t)
+;;;###autoload (autoload 'kubed-persistentvolume-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource persistentvolume () :namespaced nil)
+
+;;;###autoload (autoload 'kubed-display-service "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-service "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-services "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-services "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-service "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-service "kubed" nil t)
+;;;###autoload (autoload 'kubed-service-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource service
+    ((type ".spec.type" 12)
+     (clusterip ".spec.clusterIP" 16)
+     (externalip ".spec.externalIPs[*]" 16)
+     (ports ".spec.ports[*].port" 6)
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :logs t)
+
+;;;###autoload (autoload 'kubed-display-secret "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-secret "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-secrets "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-secrets "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-secret "kubed" nil t)
+;;;###autoload (autoload 'kubed-secret-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource secret
+    ((type ".type" 32) (creationtimestamp ".metadata.creationTimestamp" 20)))
+
+(defun kubed-create-job-from-cronjob (name cronjob &optional context namespace)
+  "Create Kubernetes job with name NAME from cronjob CRONJOB.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace to use for the job,
+defaulting to the current namespace."
+  (interactive
+   (let ((name (read-string "Create job with name: "))
+         (context nil) (namespace nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-create-job))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list name
+           (kubed-read-cronjob
+            (format "Create job `%s' from cronjob" name) nil nil context namespace)
+           context namespace)))
+  (let* ((context (or context (kubed-local-context)))
+         (namespace (or namespace (kubed--namespace context))))
+    (unless (zerop
+             (call-process
+              kubed-kubectl-program nil nil nil
+              "create" "job" name "--from" (concat "cronjob/" cronjob)
+              "-n" namespace "--context" context))
+      (user-error "Failed to create Kubernetes job `%s'" name))
+    (kubed-update "jobs" context namespace)
+    (message "Created Kubernetes job `%s'." name)))
+
+;;;###autoload (autoload 'kubed-display-job "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-job "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-jobs "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-jobs "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-job "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-job "kubed" nil t)
+;;;###autoload (autoload 'kubed-job-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource job
+    ((status ".status.conditions[0].type" 10) (starttime ".status.startTime" 20))
+  :prefix (("c" "Create from Cron" kubed-create-job-from-cronjob))
+  :logs t
+  :create
+  ((name image &optional context namespace command)
+   "Create Kubernetes job with name NAME executing COMMAND in IMAGE.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace to use for the job,
+defaulting to the current namespace."
+   (interactive
+    (let ((name (read-string "Create job with name: "))
+          (image nil) (context nil) (namespace nil) (command nil))
+      (dolist (arg (kubed-transient-args 'kubed-transient-create-job))
+        (cond
+         ((string-match "--image=\\(.+\\)" arg)
+          (setq image (match-string 1 arg)))
+         ((string-match "--context=\\(.+\\)" arg)
+          (setq context (match-string 1 arg)))
+         ((string-match "--namespace=\\(.+\\)" arg)
+          (setq namespace (match-string 1 arg)))
+         ((string-match "-- =\\(.+\\)" arg)
+          (setq command (split-string-and-unquote (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))))
+      (unless namespace
+        (setq namespace (kubed--namespace context current-prefix-arg)))
+      (unless image
+        (setq image (kubed-read-container-image "Image to run in job")))
+      (list name image context namespace command)))
+   (unless (zerop
+            (apply #'call-process
+                   kubed-kubectl-program nil nil nil
+                   "create" "job" name "--image" image
+                   (append
+                    (when namespace (list "-n" namespace))
+                    (when context (list "--context" context))
+                    (when command (cons "--" command)))))
+     (user-error "Failed to create Kubernetes job `%s'" name))
+   (message "Created Kubernetes job `%s'." name)))
+
+;;;###autoload
+(defun kubed-watch-deployment-status (dep &optional context namespace cb)
+  "Show and update status of Kubernetes deployment DEP in a dedicate buffer.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.  CB is an optional callback function to call with
+no arguments when the deployment ends.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT."
+  (interactive
+   (let (context namespace)
+     (dolist (arg (kubed-transient-args 'kubed-transient-rollout))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list (kubed-read-deployment "Watch deployment status" nil nil
+                                  context namespace)
+           context namespace)))
+  (let* ((buf (get-buffer-create "*kubed-deployment-status*"))
+         (context (or context (kubed-local-context)))
+         (namespace (or namespace (kubed--namespace context))))
+    (with-current-buffer buf (erase-buffer))
+    (make-process
+     :name "*kubed-watch-deployment-status*"
+     :buffer buf
+     :command (list kubed-kubectl-program "rollout" "status"
+                    "deployment" dep "-n" namespace "--context" context)
+     :sentinel (when cb
+                 (lambda (_proc status)
+                   (when (member status '("finished\n"
+                                          "exited abnormally with code 1\n"))
+                     (funcall cb)))))
+    (display-buffer buf)))
+
+(defcustom kubed-restart-deployment-watch-status t
+  "Whether to pop up a progress buffer when restarting Kubernetes deployments."
+  :type 'boolean)
+
+;;;###autoload
+(defun kubed-scale-deployment (dep reps &optional context namespace)
+  "Scale deployment DEP in namespace NAMESPACE via CONTEXT to REPS replicas.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT."
+  (interactive
+   (let (reps context namespace)
+     (dolist (arg (kubed-transient-args 'kubed-transient-rollout))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--replicas=\\(.+\\)" arg)
+         (setq reps (string-to-number (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list (kubed-read-deployment "Scale deployment" nil nil context namespace)
+           (or reps (read-number "Number of replicas: ")) context namespace)))
+  (let* ((context (or context (kubed-local-context)))
+         (namespace (or namespace (kubed--namespace context))))
+    (unless (zerop
+             (apply #'call-process
+                    kubed-kubectl-program nil nil nil
+                    "scale" "deployment" dep
+                    "--replicas" (number-to-string reps)
+                    (append
+                     (when namespace (list "-n" namespace))
+                     (when context (list "--context" context)))))
+      (user-error "Failed to scale Kubernetes deployment `%s'" dep))
+    (message "Scaled Kubernetes deployment `%s' to %d replicas." dep reps)))
+
+;;;###autoload
+(defun kubed-restart-deployment (dep &optional context namespace)
+  "Restart Kubernetes deployment DEP in namespace NAMESPACE via CONTEXT.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; NAMESPACE is the namespace of DEP, defaulting to
+the current namespace.  CB is an optional callback function to call with
+no arguments when the deployment ends.
+
+Interactively, prompt for DEP.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT."
+  (interactive
+   (let (context namespace)
+     (dolist (arg (kubed-transient-args 'kubed-transient-rollout))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list (kubed-read-deployment "Restart deployment" nil nil
+                                  context namespace)
+           context namespace)))
+  (let* ((context (or context (kubed-local-context)))
+         (namespace (or namespace (kubed--namespace context))))
+    (unless (zerop
+             (apply #'call-process
+                    kubed-kubectl-program nil nil nil
+                    "rollout" "restart" "deployment" dep
+                    (append
+                     (when namespace (list "-n" namespace))
+                     (when context (list "--context" context)))))
+      (user-error "Failed to restart Kubernetes deployment `%s'" dep))
+    (message "Restarting Kubernetes deployment `%s'." dep)
+    (when kubed-restart-deployment-watch-status
+      (kubed-watch-deployment-status
+       dep context namespace
+       (lambda ()
+         (kubed-update "deployments" context namespace))))))
+
+;;;###autoload (autoload 'kubed-display-deployment "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-deployment "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-deployments "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-deployments "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-deployment "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-deployment "kubed" nil t)
+;;;###autoload (autoload 'kubed-deployment-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource deployment
+    (( ready ".status.readyReplicas" 6
+       (lambda (l r) (< (string-to-number l) (string-to-number r)))
+       (lambda (s) (if (string= s "<none>") "0" s))
+       :right-align t)
+     ( updated ".status.updatedReplicas" 8
+       (lambda (l r) (< (string-to-number l) (string-to-number r)))
+       (lambda (s) (if (string= s "<none>") "0" s))
+       :right-align t)
+     ( available ".status.availableReplicas" 10
+       (lambda (l r) (< (string-to-number l) (string-to-number r)))
+       (lambda (s) (if (string= s "<none>") "0" s))
+       :right-align t)
+     ( reps ".status.replicas" 4
+       (lambda (l r) (< (string-to-number l) (string-to-number r)))
+       nil                              ; formatting function
+       :right-align t)
+     ( creationtimestamp ".metadata.creationTimestamp" 20))
+  :prefix (("R" "Restart" kubed-restart-deployment)
+           ("W" "Watch"   kubed-watch-deployment-status)
+           ("$" "Scale"   kubed-scale-deployment))
+  :logs t
+  :suffixes ([("L" "Logs" kubed-transient-logs-for-deployment)
+              ("W" "Watch" kubed-deployments-watch)
+              ("R" "Restart" kubed-deployments-restart)
+              ("$" "Scale" kubed-deployments-scale)])
+  :create
+  ((name images &optional context namespace replicas port command)
+   "Deploy IMAGES to Kubernetes in deployment with name NAME.
+
+Optional argument NAMESPACE is the namespace to use for the deployment,
+defaulting to the current namespace, REPLICAS in the number of replicas
+to create for each image, PORT is the port to expose, and COMMAND is an
+optional command to run in the images."
+   (interactive
+    (let ((name (read-string "Create deployment with name: "))
+          (images nil) (replicas nil) (port nil) (command nil)
+          (context nil) (namespace nil))
+      (dolist (arg (kubed-transient-args 'kubed-transient-create-deployment))
+        (cond
+         ((string-match "--replicas=\\(.+\\)" arg)
+          (setq replicas (string-to-number (match-string 1 arg))))
+         ((string-match "--image=\\(.+\\)" arg)
+          (push (match-string 1 arg) images))
+         ((string-match "--port=\\(.+\\)" arg)
+          (setq port (string-to-number (match-string 1 arg))))
+         ((string-match "--namespace=\\(.+\\)" arg)
+          (setq namespace (match-string 1 arg)))
+         ((string-match "--context=\\(.+\\)" arg)
+          (setq context (match-string 1 arg)))
+         ((string-match "-- =\\(.+\\)" arg)
+          (setq command (split-string-and-unquote (match-string 1 arg))))))
+      (unless images
+        (setq images (kubed-read-container-image "Images to deploy" nil t)))
+      (unless context
+        (setq context
+              (let ((cxt (kubed-local-context)))
+                (if (equal current-prefix-arg '(16))
+                    (kubed-read-context "Context" cxt)
+                  cxt))))
+      (unless namespace
+        (setq namespace (kubed--namespace context current-prefix-arg)))
+      (list name images context namespace replicas port command)))
+   (let* ((context (or context (kubed-local-context)))
+          (namespace (or namespace (kubed--namespace context))))
+     (unless (zerop
+              (apply #'call-process
+                     kubed-kubectl-program nil nil nil
+                     "create" "deployment" name
+                     (append
+                      (mapcar (lambda (image) (concat "--image=" image)) images)
+                      (when namespace (list (concat "--namespace=" namespace)))
+                      (when context (list (concat "--context=" context)))
+                      (when replicas (list (format "--replicas=%d" replicas)))
+                      (when port (list (format "--port=%d" port)))
+                      (when command (cons "--" command)))))
+       (user-error "Failed to create Kubernetes deployment `%s'" name))
+     (message "Created Kubernetes deployment `%s'." name)
+     (kubed-update "deployments" context namespace)))
+  (restart "R" "Restart"
+           (kubed-restart-deployment deployment kubed-list-context kubed-list-namespace)
+           (unless kubed-restart-deployment-watch-status
+             (message "Deployment restarting")
+             (kubed-list-update t)))
+  (watch "W" "Watch" (kubed-watch-deployment-status
+                      deployment kubed-list-context kubed-list-namespace))
+  (scale "$" "Scale" (kubed-scale-deployment
+                      deployment (if current-prefix-arg
+                                     (prefix-numeric-value current-prefix-arg)
+                                   (read-number "Number of replicas: "))
+                      kubed-list-context kubed-list-namespace)))
+
+;;;###autoload (autoload 'kubed-display-replicaset "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-replicaset "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-replicasets "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-replicasets "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-replicaset "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-replicaset "kubed" nil t)
+;;;###autoload (autoload 'kubed-replicaset-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource replicaset
+    ((reps ".status.replicas" 4
+           (lambda (l r) (< (string-to-number l) (string-to-number r)))
+           nil                          ; formatting function
+           :right-align t)
+     (ownerkind ".metadata.ownerReferences[0].kind" 12)
+     (ownername ".metadata.ownerReferences[0].name" 16)
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :logs t)
+
+;;;###autoload (autoload 'kubed-display-statefulset "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-statefulset "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-statefulsets "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-statefulsets "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-statefulset "kubed" nil t)
+;;;###autoload (autoload 'kubed-logs-for-statefulset "kubed" nil t)
+;;;###autoload (autoload 'kubed-statefulset-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource statefulset
+    ((reps ".status.replicas" 4
+           (lambda (l r) (< (string-to-number l) (string-to-number r)))
+           nil                          ; formatting function
+           :right-align t)
+     (ownerkind ".metadata.ownerReferences[0].kind" 12)
+     (ownername ".metadata.ownerReferences[0].name" 16)
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :logs t)
+
+(defun kubed-cronjob-suspended-p (cj &optional context ns)
+  "Return non-nil if cronjob CJ in CONTEXT and namespace NS is suspended."
+  (equal (car (apply #'process-lines
+                     kubed-kubectl-program
+                     "get" "cronjobs" cj
+                     "-o" "custom-columns=SUSPENDED:.spec.suspend" "--no-headers"
+                     (append
+                      (when ns (list "-n" ns))
+                      (when context (list "--context" context)))))
+         "true"))
+
+;;;###autoload (autoload 'kubed-display-cronjob "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-cronjob "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-cronjobs "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-cronjobs "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-cronjob "kubed" nil t)
+;;;###autoload (autoload 'kubed-cronjob-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource cronjob
+    ((schedule ".spec.schedule" 20)
+     (suspend ".spec.suspend" 20)
+     (lastschedule ".status.lastScheduleTime" 20)
+     (lastsuccess ".status.lastSuccessfulTime" 20)
+     (activejob ".status.active[0].name" 36))
+  :create
+  ((name image schedule &optional context namespace command)
+   "Schedule IMAGE to run in a cronjob with name NAME according to SCHEDULE.
+
+Optional argument NAMESPACE is the namespace to use for the cronjob,
+defaulting to the current namespace.  COMMAND is a list of strings that
+represent a program followed by its arguments, if it non-nil then it
+overrides the default command IMAGE runs."
+   (interactive
+    (let ((name (read-string "Create cronjob with name: "))
+          (image nil)
+          (schedule nil)
+          (command nil)
+          (context nil)
+          (namespace nil))
+      (dolist (arg (kubed-transient-args 'kubed-transient-create-cronjob))
+        (cond
+         ((string-match "--image=\\(.+\\)" arg)
+          (setq image (match-string 1 arg)))
+         ((string-match "--schedule=\\(.+\\)" arg)
+          (setq schedule (match-string 1 arg)))
+         ((string-match "--namespace=\\(.+\\)" arg)
+          (setq namespace (match-string 1 arg)))
+         ((string-match "--context=\\(.+\\)" arg)
+          (setq context (match-string 1 arg)))
+         ((string-match "-- =\\(.+\\)" arg)
+          (setq command (split-string-and-unquote (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))))
+      (unless namespace
+        (setq namespace (kubed--namespace context current-prefix-arg)))
+      (unless image
+        (setq image (kubed-read-container-image "Image to run")))
+      (unless schedule
+        (setq schedule (read-string "Cron schedule: " "* * * * *")))
+      (list name image schedule context namespace command)))
+   (unless (zerop
+            (apply #'call-process
+                   kubed-kubectl-program nil nil nil
+                   "create" "cronjob" name
+                   "--image" image "--schedule" schedule
+                   (append
+                    (when context (list "--context" context))
+                    (when namespace (list "--namespace" namespace))
+                    (when command (cons "--" command)))))
+     (user-error "Failed to create Kubernetes cronjob `%s'" name))
+   (message "Created Kubernetes cronjob `%s'." name))
+  ;; Commands in *kubed-cronjobs* buffer.
+  ( toggle-suspension "T" "Toggle suspension of"
+    (kubed-patch "cronjobs" cronjob
+                 (format
+                  "{\"spec\": {\"suspend\": %s}}"
+                  (if (kubed-cronjob-suspended-p
+                       cronjob kubed-list-context kubed-list-namespace)
+                      "false" "true"))
+                 kubed-list-context kubed-list-namespace))
+  ( create-job "j" "Create job from"
+    (kubed-create-job-from-cronjob
+     (read-string "Create job with name: ") cronjob
+     kubed-list-context kubed-list-namespace)))
+
+;;;###autoload (autoload 'kubed-display-ingressclass "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-ingressclass "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-ingressclasses "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-ingressclasss "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-ingressclass "kubed" nil t)
+;;;###autoload (autoload 'kubed-ingressclass-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource ingressclass
+    ((controller ".spec.controller" 32)
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :namespaced nil
+  :plural ingressclasses)
+
+;;;###autoload (autoload 'kubed-display-ingress "kubed" nil t)
+;;;###autoload (autoload 'kubed-edit-ingress "kubed" nil t)
+;;;###autoload (autoload 'kubed-delete-ingresses "kubed" nil t)
+;;;###autoload (autoload 'kubed-list-ingresss "kubed" nil t)
+;;;###autoload (autoload 'kubed-create-ingress "kubed" nil t)
+;;;###autoload (autoload 'kubed-ingress-prefix-map "kubed" nil t 'keymap)
+(kubed-define-resource ingress
+    ((class ".spec.ingressClassName" 8)
+     (creationtimestamp ".metadata.creationTimestamp" 20))
+  :plural ingresses
+  :create
+  ((name rules &optional context namespace class default-backend annotations)
+   "Create Kubernetes ingress with name NAME and rules RULES.
+
+Optional argument NAMESPACE is the namespace to use for the ingress,
+defaulting to the current namespace.  CLASS is the ingress class,
+ANNOTATIONS are a list of annotations for the created ingress, and
+DEFAULT-BACKEND is the service to use as a backend for unhandled URLs."
+   (interactive
+    (let ((name (read-string "Create ingress with name: "))
+          (rules nil)
+          (context nil)
+          (namespace nil)
+          (class nil)
+          (annotations nil)
+          (default-backend nil))
+      (dolist (arg (kubed-transient-args 'kubed-transient-create-ingress))
+        (cond
+         ((string-match "--rule=\\(.+\\)" arg)
+          (push (match-string 1 arg) rules))
+         ((string-match "--context=\\(.+\\)" arg)
+          (setq context (match-string 1 arg)))
+         ((string-match "--namespace=\\(.+\\)" arg)
+          (setq namespace (match-string 1 arg)))
+         ((string-match "--class=\\(.+\\)" arg)
+          (setq class (match-string 1 arg)))
+         ((string-match "--default-backend=\\(.+\\)" arg)
+          (setq default-backend (match-string 1 arg)))
+         ((string-match "--annotation=\\(.+\\)" arg)
+          (push (match-string 1 arg) annotations))))
+      (unless context
+        (setq context
+              (let ((cxt (kubed-local-context)))
+                (if (equal current-prefix-arg '(16))
+                    (kubed-read-context "Context" cxt)
+                  cxt))))
+      (unless namespace
+        (setq namespace (kubed--namespace context current-prefix-arg)))
+      (unless rules (setq rules (kubed-read-ingress-rules)))
+      (list name rules context namespace class default-backend annotations)))
+   (unless (zerop
+            (apply #'call-process
+                   kubed-kubectl-program nil nil nil
+                   "create" "ingress" name
+                   (append
+                    (mapcan (lambda (rule) (list "--rule" rule)) rules)
+                    (when namespace (list "--namespace" namespace))
+                    (when context (list "--context" context))
+                    (when class (list "--class" class))
+                    (when default-backend
+                      (list "--default-backend" default-backend))
+                    (mapcan (lambda (ann) (list "--annotation" ann))
+                            annotations))))
+     (user-error "Failed to create Kubernetes ingress `%s'" name))
+   (message "Created Kubernetes ingress `%s'." name)))
+
+;; TODO: Events may be numerous.  Need to only get a few.
+;; ;;;###autoload (autoload 'kubed-list-events "kubed" nil t)
+;; ;;;###autoload (autoload 'kubed-event-prefix-map "kubed" nil t 'keymap)
+;; (kubed-define-resource event
+;;     ((last ".lastTimestamp" 20)
+;;      (type ".type" 10)
+;;      (reason ".reason" 20)
+;;      (objectkind ".involvedObject.kind" 12)
+;;      (objectname ".involvedObject.name" 16)
+;;      (message ".message" 36)))
+
+(defun kubed-contexts ()
+  "Return list of Kubernetes contexts."
+  (process-lines kubed-kubectl-program "config" "get-contexts" "-o" "name"))
+
+(defun kubed-current-context ()
+  "Return current Kubernetes context."
+  (car (process-lines kubed-kubectl-program "config" "current-context")))
+
+(defun kubed-read-default-namespace-non-empty (context)
+  "Prompt for a default namespace for CONTEXT, refusing empty input."
+  (let ((ns (kubed-read-namespace
+             (format "Default namespace for context `%s'" context)
+             nil nil context)))
+    (when (string-empty-p ns)
+      (user-error "You didn't specify a default namespace"))
+    ns))
+
+(defun kubed-default-context-and-namespace ()
+  "Return default context and namespace as a cons cell (CONTEXT . NAMESPACE)."
+  (or kubed-default-context-and-namespace
+      (setq kubed-default-context-and-namespace
+            (let ((context (kubed-current-context)))
+              (cons context
+                    (or (kubed-current-namespace context)
+                        (kubed-read-default-namespace-non-empty context)))))))
+
+(defun kubed-default-context ()
+  "Return default `kubectl' context."
+  (car (kubed-default-context-and-namespace)))
+
+(defun kubed-default-namespace ()
+  "Return default Kubernetes namespace in the default context."
+  (cdr (kubed-default-context-and-namespace)))
+
+(defun kubed-remote-file-name-p (file-name)
+  "Check whether FILE-NAME is a Kubed Tramp remote file name."
+  ;; We use this function to heuristically check file names without
+  ;; loading `kubed-tramp' (which in turn loads `tramp').
+  (string-match-p "[/|]kubedv[0-9]+:" file-name))
+
+(defun kubed-local-context ()
+  "Return Kubernetes context local to the current buffer."
+  (or kubed-list-context
+      (nth 2 kubed-display-resource-info)
+      (and (kubed-remote-file-name-p default-directory)
+           (kubed-tramp-context default-directory))
+      (kubed-default-context)))
+
+(defvar kubed-context-history nil
+  "History list for `kubed-read-context'.")
+
+(defun kubed-read-context (prompt &optional default)
+  "Prompt with PROMPT for a Kubernetes context.
+
+Optional argument DEFAULT is the minibuffer default argument."
+  (completing-read (format-prompt prompt default)
+                   (kubed-contexts)
+                   nil 'confirm nil 'kubed-context-history default))
+
+;;;###autoload
+(defun kubed-use-context (context)
+  "Set current Kubernetes context to CONTEXT."
+  (interactive
+   (list (kubed-read-context "Use context" (kubed-local-context))))
+  (unless (zerop
+           (call-process
+            kubed-kubectl-program nil nil nil
+            "config" "use-context" context))
+    (user-error "Failed to use Kubernetes context `%s'" context))
+  (message "Now using Kubernetes context `%s'." context))
+
+;;;###autoload
+(defun kubed-rename-context (old new)
+  "Rename Kubernetes context OLD to NEW."
+  (interactive
+   (let ((old (kubed-read-context "Rename context" (kubed-local-context))))
+     (list old (read-string (format-prompt "Rename context to" old)
+                            nil 'kubed-context-history old))))
+  (unless (zerop
+           (call-process
+            kubed-kubectl-program nil nil nil
+            "config" "rename-context" old new))
+    (user-error "Failed to rename Kubernetes context `%s' to `%s'" old new))
+  (message "Renamed Kubernetes context `%s' to `%s'." old new))
+
+;;;###autoload
+(defun kubed-display-config ()
+  "Display current Kubernetes client settings in a YAML buffer."
+  (interactive)
+  (let* ((buf (get-buffer-create "*kubed-config*"))
+         (fun (lambda (&optional _ _)
+                (let ((inhibit-read-only t)
+                      (target (current-buffer)))
+                  (buffer-disable-undo)
+                  (with-temp-buffer
+                    (unless (zerop
+                             (call-process
+                              kubed-kubectl-program nil t nil "config" "view"))
+                      (error "`kubectl config view' failed"))
+                    (let ((source (current-buffer)))
+                      (with-current-buffer target
+                        (replace-buffer-contents source)
+                        (set-buffer-modified-p nil)
+                        (buffer-enable-undo))))))))
+    (with-current-buffer buf
+      (funcall fun)
+      (goto-char (point-min))
+      (run-hooks 'kubed-yaml-setup-hook)
+      (setq-local revert-buffer-function fun))
+    (display-buffer buf)))
+
+(defun kubed-current-namespace (&optional context)
+  "Return current Kubernetes namespace for context CONTEXT.
+
+If no namespace is configured for CONTEXT, return nil."
+  (or (car (process-lines
+            kubed-kubectl-program
+            "config" "view" "-o"
+            (format "jsonpath={.contexts[?(.name==\"%s\")].context.namespace}"
+                    (or context (kubed-current-context)))))))
+
+(defun kubed-local-namespace ()
+  "Return Kubernetes namespace in CONTEXT local to the current buffer."
+  (or kubed-list-namespace
+      (nth 3 kubed-display-resource-info)
+      (and (kubed-remote-file-name-p default-directory)
+           (kubed-tramp-namespace default-directory))
+      (kubed-default-namespace)))
+
+(defun kubed-local-context-and-namespace ()
+  "Return (CONTEXT . NAMESPACE) pair local to the current buffer."
+  (or (when-let ((context kubed-list-context))
+        (cons context
+              (or kubed-list-namespace
+                  (kubed-current-namespace context))))
+      (when-let ((context (nth 2 kubed-display-resource-info)))
+        (cons context
+              (or (nth 3 kubed-display-resource-info)
+                  (kubed-current-namespace context))))
+      (when-let ((context (and (kubed-remote-file-name-p default-directory)
+                               (kubed-tramp-context default-directory))))
+        (cons context (kubed-tramp-namespace default-directory)))
+      (kubed-default-context-and-namespace)))
+
+;;;###autoload
+(defun kubed-set-namespace (namespace &optional context)
+  "Set default Kubernetes namespace in CONTEXT to NAMESPACE.
+
+Interactively, prompt for NAMESPACE and use the current context.  With a
+prefix argument, prompt for CONTEXT as well."
+  (interactive
+   (let* ((context (kubed-local-context))
+          (context (if current-prefix-arg
+                       (kubed-read-context "Context" context)
+                     context)))
+     (list (kubed-read-namespace "Set namespace" (kubed--namespace context)
+                                 nil context)
+           context)))
+  (unless (zerop
+           (call-process
+            kubed-kubectl-program nil nil nil
+            "config" "set-context" (or context "--current")
+            "--namespace" namespace))
+    (user-error "Failed to set Kubernetes namespace to `%s'" namespace))
+  (message "Kubernetes namespace is now `%s'." namespace))
+
+(defcustom kubed-read-resource-definition-filter-files-by-kind t
+  "Whether to filter file completion candidates by their Kubernetes \"kind\".
+
+If this is non-nil, `kubed-read-resource-definition-file-name' only
+suggests files with the right \"kind\" as completion candidates when you
+call it with non-nil KIND argument.  This is useful because you get more
+relevant completions, but it may become slow in directories with many
+large JSON and YAML files, in which case you can set this option to nil."
+  :type 'boolean)
+
+(defun kubed-read-resource-definition-file-name (&optional kind)
+  "Prompt for Kubernetes resource definition file name.
+
+Optional argument KIND is the kind of resource the file should define.
+If `kubed-read-resource-definition-filter-files-by-kind' is non-nil,
+this function suggests only files that define resources of kind KIND as
+completion candidates."
+  (read-file-name
+   (format "%s definition file: " (or kind "Resource")) nil nil t nil
+   (if (and kind kubed-read-resource-definition-filter-files-by-kind
+            (executable-find "grep"))
+       (let ((cache (make-hash-table :test 'equal)))
+         (lambda (f)
+           (or (file-directory-p f)
+               (when-let ((ext (and (string-match "\\.[^.]*\\'" f)
+                                    (substring f (1+ (match-beginning 0))))))
+                 (or (and (member ext '("yaml" "yml"))
+                          (pcase (gethash (expand-file-name f) cache 'noval)
+                            ('noval
+                             (puthash (expand-file-name f)
+                                      (zerop (call-process
+                                              "grep" f nil nil "-i"
+                                              (format "^kind: %s$" kind)))
+                                      cache))
+                            (val val)))
+                     (and (equal ext "json")
+                          (pcase (gethash (expand-file-name f) cache 'noval)
+                            ('noval
+                             (puthash (expand-file-name f)
+                                      (zerop (call-process
+                                              "grep" f nil nil "-i"
+                                              (format "^kind: %s$" kind)))
+                                      cache))
+                            (val val))))))))
+     (lambda (f)
+       (or (file-directory-p f)
+           (when (string-match "\\.[^.]*\\'" f)
+             (member (substring f (1+ (match-beginning 0)))
+                     '("yaml" "yml" "json"))))))))
+
+;;;###autoload
+(defun kubed-apply (config &optional kind context)
+  "Apply CONFIG to Kubernetes resource of kind KIND via CONTEXT."
+  (interactive
+   (let ((config nil) (context nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-apply))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--filename=\\(.+\\)" arg)
+         (setq config (match-string 1 arg)))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if current-prefix-arg
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (unless config
+       (setq config (kubed-read-resource-definition-file-name)))
+     (list config nil context)))
+  (let ((kind (or kind "resource"))
+        (context (or context (kubed-local-context))))
+    (message "Applying Kubernetes %s configuration `%s'..." kind config)
+    (call-process kubed-kubectl-program nil nil nil
+                  "apply" "-f" (expand-file-name config) "--context" context)
+    (message "Applying Kubernetes %s configuration `%s'... Done." kind config)))
+
+;;;###autoload
+(defun kubed-create (definition &optional kind context)
+  "Create resource of kind KIND with definition DEFINITION via CONTEXT."
+  (interactive
+   (let ((definition nil) (context nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-create))
+       (cond
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--filename=\\(.+\\)" arg)
+         (setq definition (match-string 1 arg)))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if current-prefix-arg
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (unless definition
+       (setq definition (kubed-read-resource-definition-file-name)))
+     (list definition nil context)))
+  (let ((kind (or kind "resource")))
+    (message "Creating Kubernetes %s with definition `%s'..." kind definition)
+    (message "Creating Kubernetes %s with definition `%s'... Done.  New %s name is `%s'."
+             kind definition kind
+             (car (apply #'process-lines kubed-kubectl-program
+                         "create" "-f" (expand-file-name definition)
+                         "-o" "jsonpath={.metadata.name}"
+                         (when context (list "--context" context)))))))
+
+;;;###autoload
+(defun kubed-run
+    ( pod image
+      &optional context namespace port attach stdin tty rm envs command args)
+  "Run container IMAGE in a new Kubernetes POD.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context.  NAMESPACE is the namespace in which to create POD,
+defaulting to the current namespace.  PORT is the port to expose,
+defaulting to none.  If ATTACH is non-nil, then attach to the created
+image with a `comint-mode' buffer, and pop to that buffer.  Non-nil
+STDIN says to keep the standard input of the container open; non-nil TTY
+says to allocate a TTY for the container; and non-nil RM says to remove
+the container after it exits.  ENVS is a list of strings \"VAR=VAL\"
+which specify environment variables VAR and values VAL to give them in
+the created container.  ARGS are command line arguments for the
+container command.  If COMMAND is non-nil, ARGS consist of a complete
+command line, that overrides the container command instead of just
+providing it with arguments.
+
+Interactively, prompt for POD and IMAGE.  With a prefix argument, prompt
+for NAMESPACE; with a double prefix argument, also prompt for CONTEXT."
+  (interactive
+   (let ((pod nil) (image nil) (port nil) (context nil) (namespace nil)
+         (attach nil) (stdin nil) (tty nil) (rm nil) (envs nil)
+         (command nil) (args nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-run))
+       (cond
+        ((string-match "--image=\\(.+\\)" arg)
+         (setq image (match-string 1 arg)))
+        ((string-match "--port=\\(.+\\)" arg)
+         (setq port (string-to-number (match-string 1 arg))))
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((equal "--attach" arg) (setq attach t))
+        ((equal "--stdin" arg) (setq stdin t))
+        ((equal "--tty" arg) (setq tty t))
+        ((equal "--rm" arg) (setq rm t))
+        ((equal "--command" arg) (setq command t))
+        ((string-match "--env=\\(.+\\)" arg)
+         (push (match-string 1 arg) envs))
+        ((string-match "-- =\\(.+\\)" arg)
+         (setq args (split-string-and-unquote (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (setq pod (read-string "Run image in pod with name: "))
+     (unless image
+       (setq image (read-string "Image to run: " nil 'kubed-container-image-history)))
+     (list pod image context namespace port attach stdin tty rm envs command args)))
+  (if attach
+      (pop-to-buffer
+       (apply #'make-comint "kubed-run" kubed-kubectl-program nil
+              "run" pod (concat "--image=" image) "--attach"
+              (append
+               (mapcar (lambda (env) (concat "--env=" env))
+                       (cons "TERM=dumb" envs))
+               (when namespace (list (concat "--namespace=" namespace)))
+               (when stdin '("-i"))
+               (when tty '("-t"))
+               (when rm '("--rm"))
+               (when port (list (format "--port=%d" port)))
+               (when command '("--command"))
+               (when args (cons "--" args)))))
+    (unless (zerop
+             (apply #'call-process
+                    kubed-kubectl-program nil nil nil
+                    "run" pod (concat "--image=" image)
+                    (append
+                     (mapcar (lambda (env) (concat "--env=" env)) envs)
+                     (when namespace (list (concat "--namespace=" namespace)))
+                     (when context (list (concat "--context=" context)))
+                     (when stdin '("-i"))
+                     (cond
+                      (attach '("--attach"))
+                      (stdin  '("--attach=false")))
+                     (when tty '("-t"))
+                     (when rm '("--rm"))
+                     (when port (list (format "--port=%d" port)))
+                     (when command '("--command"))
+                     (when args (cons "--" args)))))
+      (user-error "Failed to run image `%s'" image))
+    (message "Image `%s' is now running in pod `%s'." image pod)))
+
+(defun kubed-pod-containers (pod &optional context namespace)
+  "Return list of containers in Kubernetes pod POD in NAMESPACE in CONTEXT."
+  (string-split
+   (car (apply #'process-lines
+               kubed-kubectl-program "get"
+               "pod" pod "-o" "jsonpath={.spec.containers[*].name}"
+               (append
+                (when namespace (list "--namespace" namespace))
+                (when context (list "--context" context)))))
+   " "))
+
+(defun kubed-pod-default-container (pod &optional context namespace)
+  "Return default container of Kubernetes pod POD in NAMESPACE in CONTEXT."
+  (car (apply #'process-lines
+              kubed-kubectl-program
+              "get" "pod" pod "-o"
+              "jsonpath={.metadata.annotations.kubectl\\.kubernetes\\.io/default-container}"
+              (append
+               (when namespace (list "--namespace" namespace))
+               (when context (list "--context" context))))))
+
+(defun kubed-read-container
+    (pod prompt &optional guess context namespace)
+  "Prompt with PROMPT for a container in POD and return its name.
+
+Non-nil optional argument GUESS says to try and guess which container to
+use without prompting: if the pod has a
+\"kubectl.kubernetes.id/default-container\" annotation, use the
+container that this annotation specifes; if there's just one container,
+use it; otherwise, fall back to prompting."
+  (let ((default (kubed-pod-default-container pod context namespace))
+        (all 'unset))
+    (or
+     ;; There's a default container, so that's our guess.
+     (and guess default)
+     ;; No default, but we're allowed to guess, so check if there's just
+     ;; one container, and if so that's our guess.
+     (and guess (setq all (kubed-pod-containers pod context namespace))
+          (null (cdr all))
+          (car all))
+     ;; No guessing, prompt.
+     (completing-read (format-prompt prompt default)
+                      (completion-table-dynamic
+                       (lambda (_)
+                         (if (eq all 'unset)
+                             (setq all (kubed-pod-containers pod context namespace))
+                           all)))
+                      nil t nil nil default))))
+
+;;;###autoload
+(defun kubed-logs
+    ( type resource &optional context namespace
+      container follow limit prefix since tail timestamps)
+  "Show logs for container CONTAINER in Kubernetes RESOURCE of type TYPE.
+
+Optional argument CONTEXT is the `kubectl' context to use; NAMESPACE is
+the namespace for RESOURCE; CONTAINER is the name of the container in
+RESOURCE to show logs for, or t which specifies all relevant containers.
+
+With a prefix argument, prompt for NAMESPACE; with a double prefix
+argument, also prompt for CONTEXT."
+  (interactive
+   (let ( type resource context namespace
+          container follow limit prefix since tail timestamps)
+     (dolist (arg (kubed-transient-args 'kubed-transient-logs))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--limit-bytes=\\(.+\\)" arg)
+         (setq limit (string-to-number (match-string 1 arg))))
+        ((string-match "--tail=\\(.+\\)" arg)
+         (setq tail (string-to-number (match-string 1 arg))))
+        ((string-match "--since-time=\\(.+\\)" arg)
+         (setq since (match-string 1 arg)))
+        ((equal "--all-containers" arg) (setq container t))
+        ((equal "--follow" arg) (setq follow t))
+        ((equal "--prefix" arg) (setq prefix t))
+        ((equal "--timestamps" arg) (setq timestamps t))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if (equal current-prefix-arg '(16))
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (setq type (kubed-read-resource-type "Type of resource to display"
+                                          nil context))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (setq resource (kubed-read-resource-name type "Show logs for" nil nil
+                                              context namespace))
+     (list type resource context namespace
+           (or container
+               (and (string= type "pods")
+                    (kubed-read-container resource "Container" t
+                                          context namespace)))
+           follow limit prefix since tail timestamps)))
+  (let* ((context (or context (kubed-local-context)))
+         (namespace (or namespace (kubed--namespace context)))
+         (buf (generate-new-buffer
+               (format "*kubed-logs %s/%s%s in %s[%s]*"
+                       type resource
+                       (cond
+                        ((stringp container) (concat "[" container "]"))
+                        (container "[all containers]")
+                        (t ""))
+                       namespace context))))
+    (with-current-buffer buf (run-hooks 'kubed-logs-setup-hook))
+    (apply #'start-process "*kubed-logs*" buf
+           kubed-kubectl-program "logs" (concat type "/" resource)
+           "--context" context "-n" namespace
+           (append
+            (cond
+             ((stringp container) (list "-c" container))
+             (container '("--all-containers")))
+            (when follow '("--follow"))
+            (when prefix '("--prefix"))
+            (when timestamps '("--timestamps"))
+            (when since (list "--since-time" since))
+            (when tail (list "--tail" (number-to-string tail)))
+            (when limit (list "--limit-bytes" (number-to-string limit)))))
+    (display-buffer buf)))
+
+(defvar kubed-port-forward-process-alist nil
+  "Alist of current port-forwarding descriptors and corresponding processes.")
+
+(defun kubed-port-forward-process-alist (&optional _ignored)
+  "Update and return value of variable `kubed-port-forward-process-alist'."
+  (setq kubed-port-forward-process-alist
+        (seq-filter (lambda (pair)
+                      (process-live-p (cdr pair)))
+                    kubed-port-forward-process-alist)))
+
+;;;###autoload
+(defun kubed-forward-port-to-pod
+    (pod local-port remote-port context namespace)
+  "Forward LOCAL-PORT to REMOTE-PORT of Kubernetes pod POD."
+  (interactive
+   (let* ((c (kubed-local-context))
+          (c (if (equal current-prefix-arg '(16))
+                 (kubed-read-context "Context" c)
+               c))
+          (n (kubed--namespace c current-prefix-arg))
+          (n (if current-prefix-arg
+                 (kubed-read-namespace "Namespace" n nil c)
+               n))
+          (p (kubed-read-pod "Forward port to pod" nil nil c n))
+          (l (read-number "Local port: "))
+          (r (read-number "Remote port: ")))
+     (list p l r c n)))
+  (message "Forwarding local port %d to remote port %d of pod `%s' in namespace `%s'..."
+           local-port remote-port pod namespace)
+  (push
+   (cons
+    (format "%s %d:%d in %s[%s]" pod local-port remote-port namespace context)
+    (apply #'start-process
+           "*kubed-port-forward*" nil
+           kubed-kubectl-program "port-forward"
+           pod (format "%d:%d" local-port remote-port)
+           (append
+            (when namespace (list "-n" namespace))
+            (when context (list "--context" context)))))
+   kubed-port-forward-process-alist))
+
+(defun kubed-stop-port-forward (descriptor)
+  "Stop Kubernetes port-forwarding with descriptor DESCRIPTOR.
+
+DESCRIPTOR is a string that says which port-forwarding process to stop,
+it has the format \"POD LOCAL-PORT:REMOTE-PORT in NAMESPACE[CONTEXT]\",
+where POD is the name of the pod that is the target of the
+port-forwarding, LOCAL-PORT is the local port that is being forwarded,
+REMOTE-PORT is the correspoding remote port of POD, NAMESPACE is the
+namespace of POD and CONTEXT is the `kubectl' context.
+
+Interactively, prompt for DESCRIPTOR with completion.  If there is only
+one port-forwarding process, stop that process without prompting."
+  (interactive
+   (list
+    (cond
+     ((cdr (kubed-port-forward-process-alist))
+      (completing-read "Stop port-forwarding: "
+                       (completion-table-dynamic
+                        #'kubed-port-forward-process-alist)
+                       nil t))
+     ((caar kubed-port-forward-process-alist))
+     (t (user-error "No port-forwarding to Kubernetes in progress")))))
+  (if-let ((pair (assoc descriptor kubed-port-forward-process-alist)))
+      (delete-process (cdr pair))
+    (error "No port-forwarding for %s" descriptor))
+  (message "Stopped port-forwarding for %s" descriptor))
+
+(defvar kubed-container-image-history nil
+  "Minibuffer history for `kubed-read-container-image'.")
+
+(defun kubed-read-container-image (prompt &optional default multi)
+  "Prompt with PROMPT for names of container images.
+
+Optional argument DEFAULT is the minibuffer default argument.  Non-nil
+optional argument MULTI says to read multiple image names and return
+them as list."
+  (funcall (if multi #'completing-read-multiple #'completing-read)
+           (format-prompt prompt default) nil nil nil nil
+           'kubed-container-images-history default))
+
+(defvar kubed-ingress-rule-history nil
+  "Minibuffer history for `kubed-read-ingress-rules'.")
+
+(defun kubed-read-ingress-rules ()
+  "Prompt with PROMPT for Kubernetes ingress rules."
+  (let ((rules (list (read-string "Ingress rule: "
+                                  nil 'kubed-ingress-rule-history))))
+    (while (not
+            (string-empty-p
+             (car (push (read-string (format-prompt "Additional rules" "done")
+                                     nil 'kubed-ingress-rule-history)
+                        rules)))))
+    (nreverse (cdr rules))))
+
+(defvar transient-current-command)
+
+(defun kubed-transient-args (&optional prefix)
+  "Return current arguments from transient PREFIX.
+
+If PREFIX nil, it defaults to the value of `transient-current-command'."
+  (when-let ((prefix (or prefix (bound-and-true-p transient-current-command))))
+    (and (featurep 'kubed-transient)
+         (fboundp 'transient-args)
+         (transient-args prefix))))
+
+;;;###autoload
+(defun kubed-attach (pod &optional container context namespace stdin tty)
+  "Attach to running process in CONTAINER in Kubernetes POD.
+
+Optional argument NAMESPACE is the namespace in which to look for POD.
+Non-nil STDIN says to connect local standard input to remote process.
+Non-nil TTY says to use a TTY for standard input.
+
+Interactively, prompt for POD; if there are multiple pod containers,
+prompt for CONTAINER as well.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+STDIN and TTY are t interactively."
+  (interactive
+   (let ((context nil) (namespace nil) (stdin t) (tty t))
+     (dolist (arg (kubed-transient-args 'kubed-transient-attach))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((equal "--stdin" arg) (setq stdin t))
+        ((equal "--tty" arg) (setq tty t))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if (equal current-prefix-arg '(16))
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (let* ((p (kubed-read-pod "Exec on pod" nil nil context namespace))
+            (c (kubed-read-container p "Container" t context namespace)))
+       (list p c context namespace stdin tty))))
+  (pop-to-buffer
+   (apply #'make-comint "kubed-attach" kubed-kubectl-program nil
+          "attach" pod
+          (append
+           (when namespace (list "-n" namespace))
+           (when context (list "--context" context))
+           (when container (list "-c" container))
+           (when stdin '("-i"))
+           (when tty   '("-t"))))))
+
+;;;###autoload
+(defun kubed-diff (definition &optional include-managed context)
+  "Display difference between Kubernetes resource DEFINITION and current state.
+
+DEFINITION is either a file name or a buffer.  Interactively, prompt for
+a YAML or JSON file name to use as DEFINITION.
+
+Optional argument CONTEXT is the `kubectl' context to use, defaulting to
+the current context; non-nil INCLUDE-MANAGED (interactively, the prefix
+argument) says to include managed fields in the comparison."
+  (interactive
+   (let ((definition nil) (context nil) (include-managed nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-diff))
+       (cond
+        ((string-match "--filename=\\(.+\\)" arg)
+         (setq definition (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((equal "--show-managed-fields" arg) (setq include-managed t))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if (equal current-prefix-arg '(16))
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (list (or definition (kubed-read-resource-definition-file-name))
+           (or include-managed current-prefix-arg)
+           context)))
+  (let ((buf (get-buffer-create "*kubed-diff*"))
+        (args (cons "diff"
+                    (append
+                     (when context (list "--context" context))
+                     (when include-managed
+                       (list "--show-managed-fields" "true"))
+                     (list "-f" (if (bufferp definition) "-"
+                                  (expand-file-name definition)))))))
+    (with-current-buffer buf
+      (setq buffer-read-only nil)
+      (delete-region (point-min) (point-max))
+      (fundamental-mode)
+      (if (bufferp definition)
+          (with-current-buffer definition
+            (apply #'call-process-region nil nil kubed-kubectl-program
+                   nil buf nil args))
+        (apply #'call-process kubed-kubectl-program
+               nil t nil args))
+      (setq buffer-read-only t)
+      (diff-mode)
+      (goto-char (point-min)))
+    (display-buffer buf)))
+
+;;;###autoload
+(defun kubed-exec
+    (pod command &optional container context namespace stdin tty args)
+  "Execute COMMAND with ARGS in CONTAINER in Kubernetes POD.
+
+Optional argument NAMESPACE is the namespace in which to look for POD.
+Non-nil STDIN says to connect local standard input to remote process.
+Non-nil TTY says to use a TTY for standard input.
+
+Interactively, prompt for POD; if there are multiple pod containers,
+prompt for CONTAINER as well.  With a prefix argument, prompt for
+NAMESPACE too.  With a double prefix argument, also prompt for CONTEXT.
+STDIN and TTY are t interactively."
+  (interactive
+   (let ((context nil) (namespace nil) (stdin t) (tty t) (command nil) (args nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-exec))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((equal "--stdin" arg) (setq stdin t))
+        ((equal "--tty" arg) (setq tty t))
+        ((string-match "-- =\\(.+\\)" arg)
+         (setq args    (split-string-and-unquote (match-string 1 arg))
+               command (car args)
+               args    (cdr args)))))
+     (unless context
+       (setq context
+             (let ((cxt (kubed-local-context)))
+               (if (equal current-prefix-arg '(16))
+                   (kubed-read-context "Context" cxt)
+                 cxt))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (unless command
+       (setq args    (split-string-and-unquote
+                      (read-string "Execute command: "))
+             command (car args)
+             args    (cdr args)))
+     (let* ((p (kubed-read-pod "Exec on pod" nil nil context namespace))
+            (c (kubed-read-container p "Container" t context namespace)))
+       (list p command c context namespace stdin tty args))))
+  (pop-to-buffer
+   (apply #'make-comint "kubed-exec" kubed-kubectl-program nil
+          "exec" pod
+          (append
+           (when namespace (list "-n" namespace))
+           (when context (list "--context" context))
+           (when container (list "-c" container))
+           (when stdin '("-i"))
+           (when tty '("-t"))
+           (list "--" command)
+           args))))
+
+(defvar kubed-patch-history nil
+  "Minibuffer history for `kubed-read-patch'.")
+
+(defun kubed-read-patch ()
+  "Prompt for a Kubernetes resource patch in JSON or YAML format."
+  (read-string "Patch (JSON or YAML): " nil 'kubed-patch-history))
+
+;;;###autoload
+(defun kubed-patch (type name patch &optional context namespace strategy)
+  "Patch Kubernetes resource NAME of TYPE with patch PATCH.
+
+Optional argument NAMESPACE is the namespace in which to look for NAME.
+STRATEGY is the patch type to use, one of \"json\", \"merge\" and
+\"strategic\", defaulting to \"strategic\".
+
+Interactively, prompt for TYPE, NAME and PATCH.  With a prefix argument,
+prompt for NAMESPACE too.  With a double prefix argument, also prompt
+for CONTEXT."
+  (interactive
+   (let ((type nil) (context nil) (namespace nil) (strategy nil))
+     (dolist (arg (kubed-transient-args 'kubed-transient-patch))
+       (cond
+        ((string-match "--namespace=\\(.+\\)" arg)
+         (setq namespace (match-string 1 arg)))
+        ((string-match "--context=\\(.+\\)" arg)
+         (setq context (match-string 1 arg)))
+        ((string-match "--type=\\(.+\\)" arg)
+         (setq strategy (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))))
+     (unless namespace
+       (setq namespace (kubed--namespace context current-prefix-arg)))
+     (list (kubed-read-resource-type "Resource type to patch" nil context)
+           (kubed-read-resource-name type "Resource to patch" nil nil context namespace)
+           (kubed-read-patch) context namespace strategy)))
+  (message "Applying patch to `%s'..." name)
+  (unless (zerop
+           (apply #'call-process
+                  kubed-kubectl-program nil nil nil
+                  "patch" type name "-p" patch
+                  (append
+                   (when namespace (list "-n" namespace))
+                   (when context (list "--context" context))
+                   (when strategy (list "--type" strategy)))))
+    (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'.
+  (define-button-type 'kubed-explain
+    :supertype 'help-xref
+    'help-function 'kubed-explain
+    'help-echo "mouse-2, RET: explain"))
+
+(defvar kubed-resource-field-history nil
+  "Minibuffer history for `kubed-read-resource-field'.")
+
+(defun kubed-api-resources (&optional context only-namespaced)
+  "Return list of resource types in the context CONTEXT.
+
+Non-nil optional argument ONLY-NAMESPACED says to return only namespaced
+resource types."
+  (mapcar
+   (lambda (line)
+     (car (split-string line)))
+   (apply #'process-lines
+          kubed-kubectl-program
+          "api-resources" "--no-headers"
+          (append
+           (when only-namespaced '("--namespaced=true"))
+           (when context (list (concat "--context=" context)))))))
+
+(defun kubed-resource-names (type context &optional namespace)
+  "Return list of Kubernetes resources of type TYPE in NAMESPACE via CONTEXT."
+  (unless (kubed--alist type context namespace)
+    (let ((proc (kubed-update type context namespace)))
+      (while (process-live-p proc)
+        (accept-process-output proc 1))))
+  (mapcar #'car (alist-get 'resources (kubed--alist type context namespace))))
+
+(defun kubed-read-resource-name (type prompt &optional default multi context namespace)
+  "Prompt with PROMPT for Kubernetes resource name of type TYPE.
+
+Optional argument DEFAULT is the minibuffer default argument; non-nil
+MULTI says to read multiple names and return them as a list; CONTEXT is
+the `kubectl' context to use, defaulting to the local context; and
+NAMESPACE is the namespace to use, or nil if TYPE is non-namespaced."
+  (funcall
+   (if multi #'completing-read-multiple #'completing-read)
+   (format-prompt prompt default)
+   (let ((table 'unset))
+     (lambda (s p a)
+       (if (eq a 'metadata)
+           `(metadata (category . ,(intern (concat "kubernetes-" type))))
+         (when (eq table 'unset)
+           (setq table (kubed-resource-names
+                        type (or context (kubed-local-context)) namespace)))
+         (complete-with-action a table s p))))
+   nil 'confirm nil (intern (concat "kubed-" type "-history")) default))
+
+(defun kubed-read-resource-type (prompt &optional default context)
+  "Prompt with PROMPT for Kubernetes resource type in context CONTEXT.
+
+Optional argument DEFAULT is the minibuffer default argument."
+  (completing-read
+   (format-prompt prompt default)
+   (kubed-api-resources context)
+   nil 'confirm nil nil default))
+
+(defun kubed-read-resource-field (prompt &optional default context)
+  "Prompt with PROMPT for Kubernetes resource type or field name.
+
+Optional argument DEFAULT is the minibuffer default argument, and
+CONTEXT is the `kubectl' context to use."
+  (completing-read
+   (format-prompt prompt default)
+   (lambda (s p a)
+     (unless (eq a 'metadata)
+       (let ((start 0))
+         (while (string-match "\\." s start)
+           (setq start (match-end 0)))
+         (if (eq (car-safe a) 'boundaries)
+             `(boundaries ,start . ,(and (string-match "\\." (cdr a))
+                                         (match-beginning 0)))
+           (let ((table
+                  (if (zerop start)
+                      ;; Complete resource type.
+                      (kubed-api-resources context)
+                    ;; Complete (sub-)field name.
+                    (with-temp-buffer
+                      (call-process
+                       kubed-kubectl-program nil t nil
+                       "explain" (substring s 0 start))
+                      (goto-char (point-min))
+                      (let ((res nil))
+                        (while (re-search-forward
+                                (rx line-start (+ " ") (group-n 1 (* alnum)) "\t")
+                                nil t)
+                          (push (match-string 1) res))
+                        res)))))
+             (if a (complete-with-action a table (substring s start) p)
+               ;; `try-completion'.
+               (let ((comp (complete-with-action a table (substring s start) p)))
+                 (if (stringp comp) (concat (substring s 0 start) comp) comp))))))))
+   nil 'confirm nil 'kubed-resource-field-history default))
+
+;;;###autoload
+(defun kubed-explain (field)
+  "Show help buffer with explanation about Kubernetes resource FIELD."
+  (interactive
+   (list (kubed-read-resource-field "Explain type or field")))
+  (let ((help-buffer-under-preparation t))
+    (help-setup-xref (list #'kubed-explain field)
+                     (called-interactively-p 'interactive))
+    (with-help-window (help-buffer)
+      (with-current-buffer (help-buffer)
+        (insert (substitute-quotes
+                 (concat "`kubed explain " field "' says:\n\n")))
+        (save-excursion
+          ;; Add button that goes to parent.
+          (goto-char (point-min))
+          (when (re-search-forward (rx " "
+                                       (group-n 1 (* graph))
+                                       "."
+                                       (+ (not (any ?.)))
+                                       line-end)
+                                   nil t)
+            (help-xref-button 1 'kubed-explain (match-string 1))))
+
+        (call-process kubed-kubectl-program nil t nil "explain" field)
+        ;; Buttonize references to other fields.
+        (goto-char (point-min))
+        (while (re-search-forward (rx line-start
+                                      (+ " ")
+                                      (group-n 1 (* alnum))
+                                      "\t")
+                                  nil t)
+          (help-xref-button 1 'kubed-explain
+                            (concat field "." (match-string 1))))))))
+
+(defvar kubed-kubectl-command-history nil
+  "Minibuffer history for `kubed-kubectl-command'.")
+
+(declare-function cobra-read-command-line "cobra"
+                  (prompt initial &optional hist))
+
+(defun kubed-read-kubectl-command (prompt &optional initial)
+  "Prompt with PROMPT for a `kubectl' command line.
+
+Optional argument INITIAL added to the initial minibuffer input
+following the value of `kubed-kubectl-program' and a space character."
+  (let ((init (if (consp initial)
+                  (cons    (concat kubed-kubectl-program " " (car initial))
+                        (+ (length kubed-kubectl-program) 1  (cdr initial)))
+                (concat kubed-kubectl-program " " initial))))
+    (cobra-read-command-line prompt init 'kubed-kubectl-command-history)))
+
+;;;###autoload
+(defun kubed-kubectl-command (command)
+  "Execute `kubectl' COMMAND.
+
+This function calls `shell-command' (which see) to do the work.
+
+Interactively, prompt for COMMAND with completion for `kubectl' arguments."
+  (interactive
+   (list (kubed-read-kubectl-command
+          "Command: "
+          (let* ((args (kubed-transient-args))
+                 (prefix (and (fboundp 'transient-prefix-object)
+                              (transient-prefix-object)))
+                 (scope (and prefix (fboundp 'eieio-oref)
+                             (eieio-oref prefix 'scope))))
+            (when (or args scope)
+              (concat (string-join (append scope args) " ") " "))))))
+  (shell-command command))
+
+;;;###autoload (autoload 'kubed-prefix-map "kubed" nil t 'keymap)
+(defvar-keymap kubed-prefix-map
+  :doc "Prefix keymap for Kubed commands."
+  :prefix 'kubed-prefix-map
+  "p" 'kubed-pod-prefix-map
+  "N" 'kubed-namespace-prefix-map
+  "s" 'kubed-service-prefix-map
+  "S" 'kubed-secret-prefix-map
+  "j" 'kubed-job-prefix-map
+  "d" 'kubed-deployment-prefix-map
+  "i" 'kubed-ingress-prefix-map
+  "c" 'kubed-cronjob-prefix-map
+  "C" #'kubed-use-context
+  "+" #'kubed-create
+  "*" #'kubed-apply
+  "R" #'kubed-run
+  "=" #'kubed-diff
+  "E" #'kubed-explain
+  "P" #'kubed-patch
+  ":" #'kubed-proxy
+  "RET" #'kubed-display-resource
+  "!" #'kubed-kubectl-command)
+
+(defvar-keymap kubed-menu-map
+  :doc "Keymap with Kubed menu entries."
+  :prefix 'kubed-menu-map
+  "<namespace>"        '("Namespaces..."         . kubed-namespace-menu-map)
+  "<pod>"              '("Pods..."               . kubed-pod-menu-map)
+  "<persistentvolume>" '("Persistent Volumes..." . kubed-persistentvolume-menu-map)
+  "<service>"          '("Services..."           . kubed-service-menu-map)
+  "<secret>"           '("Secrets..."            . kubed-secret-menu-map)
+  "<job>"              '("Jobs..."               . kubed-job-menu-map)
+  "<deployment>"       '("Deployments..."        . kubed-deployment-menu-map)
+  "<replicaset>"       '("Replica Sets..."       . kubed-replicaset-menu-map)
+  "<statefulset>"      '("Stateful Sets..."      . kubed-statefulset-menu-map)
+  "<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)
+  "<explain>"          '("Explain Type or Field" . kubed-explain)
+  "<use-context>"      '("Set Current Context"   . kubed-use-context)
+  "<run>"              '("Run Image"             . kubed-run)
+  "<apply>"            '("Apply Config"          . kubed-apply)
+  "<create>"           '("Create Resource"       . kubed-create)
+  "<display>"          '("Display Resource"      . kubed-display-resource))
+
+;;;###autoload
+(define-minor-mode kubed-menu-bar-mode
+  "Add \"Kubernetes\" menu to your menu bar."
+  :global t
+  (if kubed-menu-bar-mode
+      (keymap-set-after (current-global-map)
+        "<menu-bar> <kubed>" '("Kubernetes" . kubed-menu-map))
+    (keymap-global-unset "<menu-bar> <kubed>")))
+
+(defvar reporter-prompt-for-summary-p)
+
+(defun kubed-submit-bug-report ()
+  "Report a Kubed issue to the maintainers via email."
+  (interactive)
+  (require 'reporter)
+  (let ((reporter-prompt-for-summary-p t))
+    (reporter-submit-bug-report
+     "Kubed Development <~eshel/kubed-develNOSPAMlists.sr.ht>"
+     (format "Kubed")
+     '(kubed-kubectl-program)
+     nil nil
+     (propertize " "
+                 'display
+                 (propertize
+                  "Insert your bug report below.
+If possible, specify where you got Emacs, kubectl and Kubed,
+and include a recipe for reproducing your issue.
+[This line and the above text are not included in your report.]"
+                  'face 'italic)))))
+
+(provide 'kubed)
+;;; kubed.el ends here
diff --git a/elpa/kubed-0.4.2/kubed.info b/elpa/kubed-0.4.2/kubed.info
new file mode 100644 (file)
index 0000000..5af1a7e
--- /dev/null
@@ -0,0 +1,754 @@
+This is doc5HCYE0.info, produced by makeinfo version 6.8 from
+kubed.texi.
+
+This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes.
+
+   Copyright © 2024 Eshel Yaron.
+
+     Permission is granted to copy, distribute and/or modify this
+     document under the terms of the GNU Free Documentation License,
+     Version 1.3 or any later version published by the Free Software
+     Foundation; with no Invariant Sections, no Front-Cover Texts, and
+     no Back-Cover Texts.
+INFO-DIR-SECTION Emacs
+START-INFO-DIR-ENTRY
+* Kubed: (kubed).       Kubernetes, Emacs, done!
+END-INFO-DIR-ENTRY
+
+\1f
+File: doc5HCYE0.info,  Node: Top,  Next: Overview,  Up: (dir)
+
+Kubed: Kubernetes, Emacs, done!
+*******************************
+
+This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes.
+
+* Menu:
+
+* Overview::                    Introduction to Kubed
+* Getting Started::             First steps with Kubed
+* Usage::                       Using Kubed to interact with Kubernetes
+* Extending Kubed::             Adding support for your custom resources
+* Contributing::                Get involved in Kubed development
+* Indices::
+
+\1f
+File: doc5HCYE0.info,  Node: Overview,  Next: Getting Started,  Prev: Top,  Up: Top
+
+1 Overview
+**********
+
+Kubed is a rich Kubernetes interface within Emacs.  It helps you work
+with your Kubernetes clusters and deployments with the full power of
+‘kubectl’, and with the comfort and confidence of an intuitive
+interactive interface.
+
+   You can use Kubed to:
+
+   • Browse and manage Kubernetes workloads
+   • Connect to pods and edit files or execute commands
+   • Create new resources, edit and delete them
+   • Work with multiple clusters
+   • Get help about various Kubernetes objects
+   • ...
+
+   These features and others are documented in the rest of this manual,
+along with many options that Kubed provides for you to customize its
+behavior.
+
+\1f
+File: doc5HCYE0.info,  Node: Getting Started,  Next: Usage,  Prev: Overview,  Up: Top
+
+2 Getting Started
+*****************
+
+Get Kubed from GNU ELPA with ‘M-x package-install’, or use your favorite
+Emacs package manager to install Kubed from Git.  You can clone the
+Kubed Git repository from any of the following locations:
+
+   • <https://git.sr.ht/~eshel/kubed>
+   • <https://github.com/eshelyaron/kubed.git>
+   • <git://git.eshelyaron.com/kubed.git>
+
+   To get started with Kubed, all you need is ‘kubectl’ and Emacs.
+
+   Kubed tries to find ‘kubectl’ in the directories listed in the Emacs
+variable ‘exec-path’.  When Emacs is started from a shell, it
+initializes ‘exec-path’ from the shell’s ‘PATH’ environment variable
+which normally includes the location of ‘kubectl’ in common ‘kubectl’
+installations.  If Emacs doesn’t find the ‘kubectl’ executable via
+‘exec-path’, you can tell Kubed where to find it by customizing
+‘kubed-kubectl-program’.
+
+ -- User Option: kubed-kubectl-program
+     Name of ‘kubectl’ executable file to use.
+
+\1f
+File: doc5HCYE0.info,  Node: Usage,  Next: Extending Kubed,  Prev: Getting Started,  Up: Top
+
+3 Usage
+*******
+
+Kubed comes with a large number of Emacs command for interacting with
+Kubernetes.  Some commands perform one action on one resource, while
+others show you a menu with many resources and let you interact with any
+number of them.  Some commands are specific to one type of resources,
+while others act on resources of any type.  There are different ways to
+use Kubed, depending on your use case.
+
+   The two most important entry points for Kubed are ‘kubed-prefix-map’
+and ‘kubed-transient’:
+
+ -- Keymap: kubed-prefix-map
+     Keymap with bindings for many useful Kubed commands.
+
+   The prefix keymap ‘kubed-prefix-map’ gives you quick access to the
+main Kubed commands.  You may want to bind it to a convenient key in
+your global keymap, for example:
+
+     (keymap-global-set "C-c k" 'kubed-prefix-map)
+
+   With this binding in place, you can create a new Kubernetes resource
+with ‘C-c k +’ and attach to a running pod with ‘C-c k p a’.  The ‘C-c
+k’ binding is just a suggestion of course, feel free to pick any key
+sequence you like.
+
+   You can also access all of the commands in ‘kubed-prefix-map’ via the
+menu-bar ‘Kubernetes’ menu—to add this menu to your menu-bar, enable the
+global minor mode ‘kubed-menu-bar-mode’:
+
+ -- Minor Mode: kubed-menu-bar-mode
+     Add ‘Kubernetes’ menu to your menu bar.
+
+   The other important entry point that Kubed provides for exploring and
+performing different Kubernetes operations is ‘kubed-transient’:
+
+ -- Command: kubed-transient
+     Open the main Kubed transient menu.
+
+   This command pops up the Kubed transient menu, which gives you
+interactive guidance for invoking Kubed commands.  *Note
+(transient)Top::.
+
+   The following sections describe in detail the various Kubed commands.
+
+* Menu:
+
+* Display Resource::
+* Browse Resources::
+* Context and Namespace::
+* Transient Menus::
+* Scale Deployment::
+
+\1f
+File: doc5HCYE0.info,  Node: Display Resource,  Next: Browse Resources,  Up: Usage
+
+3.1 Display Resource
+====================
+
+You can view the YAML description of any resource in your Kubernetes
+cluster.  In Kubed, this is referred to as “displaying” the given
+resource.  The following commands display a Kubernetes resource:
+
+ -- Command: kubed-display-resource
+     Display a Kubernetes resource.
+
+ -- Command: kubed-display-pod
+ -- Command: kubed-display-deployment
+ -- Command: kubed-display-namespace
+ -- Command: kubed-display-FOO
+     Display Kubernetes resource of a given type.
+
+   Command ‘kubed-display-resource’ can display any Kubernetes resource.
+It prompts you to select a resource type and a resource name, and
+displays that resource in a dedicated buffer.  By default, this command
+assumes you want to display a resource that is in the current namespace,
+but if you invoke ‘kubed-display-resource’ with a prefix argument
+(‘C-u’) and choose a namespaced resource type, then it also lets you
+select the namespace of the resource.  With a double prefix argument, it
+lets you select which ‘kubectl’ context to use too.  *Note Context and
+Namespace::.
+
+   If you have ‘kubed-prefix-map’ bound to ‘C-c k’, you can invoke
+‘kubed-display-resource’ with ‘C-c k <RET>’.  This command is also
+available from the menu-bar “Kubernetes” menu (*note Definition of
+kubed-menu-bar-mode::), and from the transient “Kubernetes Display” menu
+(*note Transient Menus::).
+
+   You can also display the resource at point in resources list buffers
+by simply typing ‘<RET>’.  *Note Browse Resources::.
+
+   Kubed also provides a dedicated ‘kubed-display-FOO’ command for each
+supported resource type FOO.  These commands are just like
+‘kubed-display-resource’, except that they do not prompt you for a type,
+since they are type-specific.  For example, ‘kubed-display-pod’ only
+prompts you for a pod name, and then displays it like
+‘kubed-display-resource’ does.
+
+   You can invoke these commands by adding a type-specific key before
+‘<RET>’ in ‘C-c k <RET>’: ‘C-c k p <RET>’ invokes ‘kubed-display-pod’,
+‘C-c k d <RET>’ invokes ‘kubed-display-deployment’, and so on.
+
+   When Kubed prepares a buffer for displaying a resource, it runs the
+hook ‘kubed-yaml-setup-hook’ right after populating the buffer with the
+YAML description of the resource:
+
+ -- User Option: kubed-yaml-setup-hook
+     List of functions to call, in order, to prepare YAML buffers.  The
+     first function on this hook is normally the major mode you want to
+     use for YAML buffers that display Kubernetes resources.  By
+     default, this is the list ‘(yaml-ts-mode view-mode)’.
+
+   In addition, Kubed enables a bespoke minor mode
+‘kubed-display-resource-mode’ in buffers that display Kubernetes
+resources:
+
+ -- Minor Mode: kubed-display-resource-mode
+     Minor mode for buffers that display a Kubernetes resource.
+
+This minor mode does the following:
+
+   • It shows the word ‘Kubed’ in the mode line, so you know you’re
+     seeing a rendered Kubernetes resource, rather than any old YAML
+     file.
+   • It allows you to _bookmark_ the resource your viewing and your
+     position in its YAML description with the regular Emacs bookmarks
+     commands (*note (emacs)Bookmarks::), namely ‘C-x r m’.
+   • It binds the key sequence ‘C-c C-j’ to command
+     ‘kubed-display-resource-jump-to-list’, which pops up the resources
+     list buffer for the type of the displayed resource, and takes you
+     to the line corresponding to it.
+   • It binds ‘C-c C-c’ to command ‘kubed-display-resource-replace’,
+     which lets you apply any changes that you make in the YAML buffer
+     back to the resource.  When you hit ‘C-c C-c’, Kubed asks you for
+     confirmation before proceeding; you can reply with ‘d’ (‘diff’) to
+     show a diff between the current state of the resource and the
+     current buffer contents.  You can also display this diff anytime
+     with ‘C-c C-=’.  Note that by default Kubed displays resources in
+     read-only buffers, so you may need to hit ‘C-x C-q’
+     (‘read-only-mode’) before making changes in the buffer.
+   • It lets you repopulate the buffer with updated information about
+     the resource with ‘C-x x g’ (‘revert-buffer-quick’) and
+     ‘M-x revert-buffer’.  This is also useful if you edit the resource
+     and want to restart from a clean slate.
+
+\1f
+File: doc5HCYE0.info,  Node: Browse Resources,  Next: Context and Namespace,  Prev: Display Resource,  Up: Usage
+
+3.2 Browse Resources
+====================
+
+Kubed gives you an interactive list of resources for each type, that you
+access with the ‘kubed-list-RESOURCEs’ commands, such as
+‘kubed-list-pods’.
+
+   These commands are bound to ‘l’ in Kubed’s type-specific prefix
+keymaps, most of which are accessible via the global ‘kubed-prefix-map’
+by default (*note Definition of kubed-prefix-map::).  So if you’ve bound
+‘kubed-prefix-map’ to ‘C-c k’, you can pop up the pods list with ‘C-c k
+p l’, the deployments list with ‘C-c k d l’, the jobs list with
+‘C-c k j l’, etc.  Normally, Kubed shows the list of resources in the
+current namespace, but if you invoke these commands with a prefix
+argument (say, ‘C-u C-c k p l’), they first prompt you to select a
+namespace, and then show resources in the namespace you selected.
+
+   The list of RESOURCEs in shown a buffer called
+‘*Kubed RESOURCEs@NAMESPACE[CONTEXT]*’, where CONTEXT is a ‘kubectl’
+context, and NAMESPACE is a namespace in CONTEXT.  For example, Kubed
+shows the list of pods in namespace ‘default’ for context ‘minikube’ in
+buffer ‘*Kubed pos@default[minikube]’.  For non-namespaced resources,
+the ‘@NAMESPACE’ part is omitted.
+
+   Each resource type has its own major mode for list buffers with
+type-specific commands and operations, but they are all also derived
+from a shared parent mode.  The pods list uses the ‘kubed-pods-mode’
+(‘Kubernetes Pods’) major mode, the list of Kubernetes deployments uses
+‘kubed-deployments-mode’, and so on.
+
+   The common parent major mode is ‘kubed-list-mode’:
+
+ -- Major Mode: kubed-list-mode
+     Major mode for listing generic Kubernetes resources.
+
+   This mode provides the following commands for all type-specific
+derived modes:
+
+‘<?>’
+     Show help in transient menu (‘kubed-list-transient’).
+‘<RET>’
+     Display the resource at point in the current window
+     (‘kubed-list-select-resource’).
+‘o’
+     Display the resource at point in another window and select that
+     window (‘kubed-list-select-resource-other-window’).
+‘C-o’
+     Display the resource at point in another window without selecting
+     it (‘kubed-list-display-resource’).
+‘D’
+     Delete the resource at point (‘kubed-list-delete’).
+‘e’
+     Edit the resource at point (‘kubed-list-edit’).
+‘P’
+     Patch the resource at point (‘kubed-list-patch’).
+‘g’
+     Update the resources list by fetching new information from
+     Kubernetes (‘kubed-list-update’).
+‘/’
+     Filter the current list of resources (‘kubed-list-set-filter’).
+     *Note List Filter::.
+‘d’
+     Mark the resource at point for deletion
+     (‘kubed-list-mark-for-deletion’).  Resources marked for deletion
+     have a ‘D’ in the beginning of their line.
+‘x’
+     Delete marked resources (‘kubed-list-delete-marked’).
+‘u’
+     Unmark from resource at point (‘kubed-list-unmark’).
+‘w’
+     Copy name of resource at point into the kill ring
+     (‘kubed-list-copy-as-kill’).
+‘n’
+     Move point to the next line (‘next-line’).
+‘p’
+     Move point to the previous line (‘previous-line’).
+‘<TAB>’
+     Move point to the next column.  If point is already at the last
+     column, move to the first column of the next line instead
+     (‘kubed-list-next-column’).
+‘S-<TAB>’
+     Move point to the previous column.  If point is already at the
+     first column, move to the last column of the previous line instead
+     (‘kubed-list-previous-column’).
+‘S’
+     Sort lines by the column at point (‘tabulated-list-sort’).
+‘|’
+     Fit width of column at point to its content
+     (‘kubed-list-fit-column-width-to-content’).  With non-negative
+     prefix argument N (for example, ‘0 |’), fit Nth column.  With
+     negative prefix argument (‘- |’), fit all columns.
+‘{’
+     Narrow the column at point
+     (‘tabulated-list-narrow-current-column’).
+‘}’
+     Widen the column at point (‘tabulated-list-widen-current-column’).
+
+   In addition, you can bookmark any Kubed resources list buffer with
+the usual ‘C-x r m’.  *Note (emacs)Bookmarks::.
+
+   Note that ‘kubed-list-mode’ (and its type-specific derivatives) bind
+the ‘g’ key, which is commonly bound to ‘revert-buffer’, to
+‘kubed-list-update’.  The difference between these commands is that
+‘revert-buffer’ only refreshes the list display, without fetching
+updated data from Kubernetes.  You can still invoke ‘revert-buffer’ with
+‘C-x x g’ or ‘M-x’, if you need it.
+
+   Whenever Kubed is fetching data from Kubernetes to update a resources
+list buffer, the mode line of that list buffer shows the ‘[...]’
+indicator.  You can set user option ‘kubed-list-mode-line-format’ to
+customize this indicator.
+
+* Menu:
+
+* List Filter::
+
+\1f
+File: doc5HCYE0.info,  Node: List Filter,  Up: Browse Resources
+
+3.2.1 Filtering Resource Lists
+------------------------------
+
+In all Kubernetes resource list buffers, you can use ‘/’
+(‘kubed-list-set-filter’) to “filter” the list of resources.  This
+command sets the filter of the current buffer, which specifies
+conditions on resources (lines).  Resources that don’t satisfy the
+conditions are hidden, so you can focus on those that do.
+
+   Filters can be simple (“atomic filters”) or complex (“composed
+filters”).  In the simple case, you enter a filter in the minibuffer in
+the format ‘OP COL VAL’, where OP is a comparison operator, one of ‘=’,
+‘~’, ‘<’ and ‘>’; COL is a column name; and VAL is a value to compare to
+values of COL with OP.
+
+   If OP is ‘=’, it says to keep only lines whose COL equals VAL.  If OP
+is ‘~’, it says to keep lines whose COL matches the VAL as a regular
+expression.  ‘<’ and ‘>’ compare column values to numeric values.  For
+example, the filter ‘= Name foobar’ keeps only resources whose name is
+‘foobar’.  To include whitespace in VAL, wrap VAL in double quotes: ‘=
+Name "foo bar"’.
+
+   The possible values of OP and their meanings are defined in
+‘kubed-list-filter-operator-alist’, which is a user option you can use
+to add or change the meaning of filter operators:
+
+ -- User Option: kubed-list-filter-operator-alist
+     Association list of filter operators and corresponding Emacs Lisp
+     functions.  See the documentation string of this option for
+     specifics.
+
+   You can use more refined filters by composing simple filters.  To add
+another filter that resources must satisfy, wrap your two filters in
+parentheses and put them next to each other:
+
+     (= Name foobar) (~ Namespace kube)
+
+This filter keeps only resources whose name is ‘foobar’ in namespaces
+that include ‘kube’ as a substring, it expresses the conjunction of the
+two simple filters.
+
+   To negate a simple filter, put a single quote ahead of it, like so:
+
+     (= Name foobar) '(~ Namespace kube)
+
+This matches resources named ‘foobar’ in namespaces that do _not_
+contain ‘kube’.
+
+   To specify a disjunction of simple filters (and negated simple
+filters), add another level of nesting:
+
+     ((= Name spam) (= Name foobar)) '(~ Namespace kube)
+
+This filter matches resources named either ‘spam’ or ‘foobar’, in
+namespaces that do not contain ‘kube’.
+
+   While entering a filter in the minibuffer, ‘<TAB>’ completion is
+available for column names and values.  To clear the filter, just exit
+the minibuffer with an empty input.
+
+\1f
+File: doc5HCYE0.info,  Node: Context and Namespace,  Next: Transient Menus,  Prev: Browse Resources,  Up: Usage
+
+3.3 Context and Namespace
+=========================
+
+Kubed uses ‘kubectl’ to interact with Kubernetes clusters, and you can
+use multiple ‘kubectl’ “contexts” in Kubed to work with different
+Kubernetes clusters in parallel.  Within each context, Kubed lets you
+work in multiple “namespaces” too.  In general, each buffer has its own
+“local context” and “local namespace”, and Kubed commands use these
+local context and namespace by default.  This is similar to how
+different buffers have different values of ‘default-directory’ (*note
+(emacs)File Names::), and how that affects commands that interact with
+the filesystem.
+
+   For Kubed buffers that display a Kubernetes resource (*note Display
+Resource::) or a list of resources (*note Browse Resources::), the
+buffer-local context and namespace are the context and namespace of the
+resources the buffer displays.  For non-Kubed buffers, the user option
+‘kubed-default-context-and-namespace’ determines the default local
+context and namespace.
+
+ -- User Option: kubed-default-context-and-namespace
+     Default ‘kubectl’ context and Kubernetes namespace for Kubed
+     commands.  The value of this option is either a cons cell
+     ‘(CONTEXT . NAMESPACE)’ or ‘nil’.  If it is ‘nil’, Kubed
+     initializes this option to a cons cell with the current ‘kubectl’
+     context and namespace for that context on the first time Kubed
+     consults this option.
+
+   As mentioned above, most Kubed commands use the local context and
+namespace by default—it is also conventional for Kubed commands to let
+you choose a different namespace if you invoke them with a prefix
+argument (‘C-u’), and to also let you choose a different context if you
+invoke them with a double prefix argument (‘C-u C-u’).
+
+   The following functions return the local context and namespace for
+buffer in which you call them:
+
+ -- Function: kubed-local-context
+     Return the local ‘kubectl’ context for the current buffer.
+
+ -- Function: kubed-local-namespace
+     Return the local Kubernetes namespace for the current buffer.
+
+ -- Function: kubed-local-context-and-namespace
+     Return the local context and namespace for the current buffer as a
+     cons cell ‘(CONTEXT . NAMESPACE)’.
+
+\1f
+File: doc5HCYE0.info,  Node: Transient Menus,  Next: Scale Deployment,  Prev: Context and Namespace,  Up: Usage
+
+3.4 Transient Menus
+===================
+
+Kubed provides a set of keyboard-driven “transient menus” (*note
+(transient)Top::) that you can use to explore and invoke various
+commands.  Transient menus show you available commands along with their
+key bindings, which helps you use many different commands without the
+need to remember the key bindings for all them.  Additionally, the
+transient menus allow you to specify options and switches for the
+commands you invoke, which may not be available when invoking these
+commands directly.  For example, while you can invoke command
+‘kubed-logs’ directly to view logs from a container running in
+Kubernetes, the “Kubernetes Logs” transient menu which you open with
+‘kubed-transient-logs’ lets you set a maximum number of logs to
+retrieve, among other knobs and handles.
+
+   Command ‘kubed-transient’ opens the Kubed “root” transient menu,
+which is a dispatch that lets you access different transient menus, such
+as the aforementioned “Kubernetes Logs” transient menu.  It’s a good
+idea to bind ‘kubed-transient’ to a convenient key, either in the global
+keymap or under ‘kubed-prefix-map’:
+
+     (keymap-global-set "s-k" #'kubed-transient)
+     (keymap-set kubed-prefix-map "k" #'kubed-transient)
+
+   In Kubed resource list buffers, type ‘?’ to pop up a transient menu
+with commands that are specific to the type of resources the buffer
+displays.  *Note Browse Resources::.
+
+\1f
+File: doc5HCYE0.info,  Node: Scale Deployment,  Prev: Transient Menus,  Up: Usage
+
+3.5 Scale Deployment
+====================
+
+You can use Kubed to “scale” Kubernetes a deployment, which means
+setting the size of the deployment in terms of the number of replicas it
+governs.
+
+ -- Command: kubed-scale-deployment
+     Scale a Kubernetes deployment.
+
+   ‘kubed-scale-deployment’ is bound to ‘$’ in the
+‘kubed-deployment-prefix-map’, so if you have ‘kubed-prefix-map’ bound
+to ‘C-c k’ then you can invoke it with ‘C-c k d $’.  *Note Definition of
+kubed-prefix-map::.  This command prompts you for a deployment and then
+lets you select a new number of replicas for that deployment.  By
+default you are prompted for deployment in the local context and
+namespace (*note Context and Namespace::); with a prefix argument, you
+can specify a different namespace; with a double prefix argument (‘C-u
+C-u’), you can specify a different context too.
+
+   In the deployments list buffer, you can scale the deployment at point
+by simply typing ‘$’.  *Note Browse Resources::.
+
+   Lastly, you can scale deployments using the transient menu
+‘kubed-transient-scale-deployment’, which is available from the main
+Kubed transient by pressing ‘$’.  *Note Transient Menus::.
+
+\1f
+File: doc5HCYE0.info,  Node: Extending Kubed,  Next: Contributing,  Prev: Usage,  Up: Top
+
+4 Extending Kubed
+*****************
+
+Kubed has built-in support for many common types of Kubernetes
+resources.  “Support” for a resource type in Kubed just means providing
+common commands for working with resources of that type, such as a
+command for showing the interactive list of these resources, like
+‘kubed-list-pods’ for Kubernetes pods.  In Kubed, the macro
+‘kubed-define-resource’ is used to define these common commands and add
+support for more resource types:
+
+ -- Macro: kubed-define-resource resource &optional properties &rest [
+          keyword argument ]... commands
+     Define commands for RESOURCE.
+
+   Say you want to add support for your Kubernetes service accounts.
+With ‘kubed-define-resource’, all you need to do is to add the following
+line to your Emacs initialization file:
+
+     (kubed-define-resource serviceaccount)
+
+   This line defines the following commands for you:
+
+   • ‘kubed-display-serviceaccount’
+   • ‘kubed-list-serviceaccounts’
+   • ‘kubed-edit-serviceaccount’
+   • ‘kubed-delete-serviceaccount’
+   • ‘kubed-create-serviceaccount’
+   • ‘kubed-explain-serviceaccounts’
+
+   See the documentation string of ‘kubed-define-resource’ (‘C-h f’) for
+details about the optional arguments of this macro: the PROPERTIES
+argument which lets you specify what Kubed should show in the resource
+list buffer and the ‘commands’ argument which lets you associate
+additional commands with your resource type.
+
+\1f
+File: doc5HCYE0.info,  Node: Contributing,  Next: Indices,  Prev: Extending Kubed,  Up: Top
+
+5 Contributing
+**************
+
+We highly appreciate all contributions, including bug reports, patches,
+improvement suggestions, and general feedback!
+
+   The best way to get in touch with the Kubed maintainers is via the
+Kubed mailing list (https://lists.sr.ht/~eshel/kubed-devel).
+
+ -- Command: kubed-submit-bug-report
+     Report a bug in Kubed to the maintainers via mail.
+
+   You can use the command ‘M-x kubed-submit-bug-report’ to easily
+contact the Kubed maintainers from within Emacs.  This command opens a
+new buffer with a message template ready to be sent to the development
+mailing list.
+
+\1f
+File: doc5HCYE0.info,  Node: Indices,  Prev: Contributing,  Up: Top
+
+Indices
+*******
+
+* Menu:
+
+* Function Index::
+* Variable Index::
+* Keystroke Index::
+* Concept Index::
+
+\1f
+File: doc5HCYE0.info,  Node: Function Index,  Next: Variable Index,  Up: Indices
+
+Function index
+==============
+
+\0\b[index\0\b]
+* Menu:
+
+* kubed-define-resource:                 Extending Kubed.      (line 14)
+* kubed-display-deployment:              Display Resource.     (line 14)
+* kubed-display-FOO:                     Display Resource.     (line 16)
+* kubed-display-namespace:               Display Resource.     (line 15)
+* kubed-display-pod:                     Display Resource.     (line 13)
+* kubed-display-resource:                Display Resource.     (line 10)
+* kubed-display-resource-diff:           Display Resource.     (line 78)
+* kubed-display-resource-jump-to-list:   Display Resource.     (line 74)
+* kubed-display-resource-mode:           Display Resource.     (line 63)
+* kubed-display-resource-replace:        Display Resource.     (line 78)
+* kubed-list-copy-as-kill:               Browse Resources.     (line 71)
+* kubed-list-delete:                     Browse Resources.     (line 51)
+* kubed-list-delete-marked:              Browse Resources.     (line 67)
+* kubed-list-display-resource:           Browse Resources.     (line 48)
+* kubed-list-edit:                       Browse Resources.     (line 53)
+* kubed-list-fit-column-width-to-content: Browse Resources.    (line 88)
+* kubed-list-mark-for-deletion:          Browse Resources.     (line 63)
+* kubed-list-mode:                       Browse Resources.     (line 35)
+* kubed-list-next-column:                Browse Resources.     (line 78)
+* kubed-list-patch:                      Browse Resources.     (line 55)
+* kubed-list-previous-column:            Browse Resources.     (line 82)
+* kubed-list-select-resource:            Browse Resources.     (line 42)
+* kubed-list-select-resource-other-window: Browse Resources.   (line 45)
+* kubed-list-set-filter:                 Browse Resources.     (line 60)
+* kubed-list-transient:                  Browse Resources.     (line 41)
+* kubed-list-unmark:                     Browse Resources.     (line 69)
+* kubed-list-update:                     Browse Resources.     (line 57)
+* kubed-local-context:                   Context and Namespace.
+                                                               (line 40)
+* kubed-local-context-and-namespace:     Context and Namespace.
+                                                               (line 46)
+* kubed-local-namespace:                 Context and Namespace.
+                                                               (line 43)
+* kubed-menu-bar-mode:                   Usage.                (line 34)
+* kubed-scale-deployment:                Scale Deployment.     (line 10)
+* kubed-submit-bug-report:               Contributing.         (line 12)
+* kubed-transient:                       Usage.                (line 40)
+* next-line:                             Browse Resources.     (line 74)
+* previous-line:                         Browse Resources.     (line 76)
+* read-only-mode:                        Display Resource.     (line 78)
+* revert-buffer:                         Display Resource.     (line 87)
+* revert-buffer-quick:                   Display Resource.     (line 87)
+* tabulated-list-narrow-current-column:  Browse Resources.     (line 93)
+* tabulated-list-sort:                   Browse Resources.     (line 86)
+* tabulated-list-widen-current-column:   Browse Resources.     (line 96)
+
+\1f
+File: doc5HCYE0.info,  Node: Variable Index,  Next: Keystroke Index,  Prev: Function Index,  Up: Indices
+
+Variable index
+==============
+
+\0\b[index\0\b]
+* Menu:
+
+* kubed-default-context-and-namespace:   Context and Namespace.
+                                                              (line  23)
+* kubed-kubectl-program:                 Getting Started.     (line  24)
+* kubed-list-filter-operator-alist:      List Filter.         (line  29)
+* kubed-list-mode-line-format:           Browse Resources.    (line 110)
+* kubed-prefix-map:                      Usage.               (line  16)
+* kubed-yaml-setup-hook:                 Display Resource.    (line  53)
+
+\1f
+File: doc5HCYE0.info,  Node: Keystroke Index,  Next: Concept Index,  Prev: Variable Index,  Up: Indices
+
+Keystroke index
+===============
+
+\0\b[index\0\b]
+* Menu:
+
+* /:                                     Browse Resources.     (line 60)
+* <?>:                                   Browse Resources.     (line 41)
+* {:                                     Browse Resources.     (line 93)
+* |:                                     Browse Resources.     (line 88)
+* }:                                     Browse Resources.     (line 96)
+* C-c C-=:                               Display Resource.     (line 78)
+* C-c C-c:                               Display Resource.     (line 78)
+* C-c C-j:                               Display Resource.     (line 74)
+* C-o:                                   Browse Resources.     (line 48)
+* C-x C-q:                               Display Resource.     (line 78)
+* C-x x g:                               Display Resource.     (line 87)
+* D:                                     Browse Resources.     (line 51)
+* d:                                     Browse Resources.     (line 63)
+* e:                                     Browse Resources.     (line 53)
+* g:                                     Browse Resources.     (line 57)
+* n:                                     Browse Resources.     (line 74)
+* o:                                     Browse Resources.     (line 45)
+* P:                                     Browse Resources.     (line 55)
+* p:                                     Browse Resources.     (line 76)
+* <RET>:                                 Browse Resources.     (line 42)
+* S:                                     Browse Resources.     (line 86)
+* S-<TAB>:                               Browse Resources.     (line 82)
+* <TAB>:                                 Browse Resources.     (line 78)
+* u:                                     Browse Resources.     (line 69)
+* w:                                     Browse Resources.     (line 71)
+* x:                                     Browse Resources.     (line 67)
+
+\1f
+File: doc5HCYE0.info,  Node: Concept Index,  Prev: Keystroke Index,  Up: Indices
+
+Concept index
+=============
+
+\0\b[index\0\b]
+* Menu:
+
+* global prefix keymap:                  Usage.                (line 16)
+* installation:                          Getting Started.      (line  6)
+* keymap, for global commands:           Usage.                (line 16)
+* Kubed:                                 Overview.             (line  6)
+* kubed:                                 Overview.             (line  6)
+* prefix keymap, global commands:        Usage.                (line 16)
+* requirements:                          Getting Started.      (line 14)
+
+
+\1f
+Tag Table:
+Node: Top\7f596
+Node: Overview\7f1140
+Node: Getting Started\7f1908
+Node: Usage\7f3005
+Ref: Definition of kubed-prefix-map\7f3616
+Ref: Definition of kubed-menu-bar-mode\7f4379
+Node: Display Resource\7f5014
+Node: Browse Resources\7f9544
+Node: List Filter\7f14571
+Node: Context and Namespace\7f17224
+Node: Transient Menus\7f19614
+Node: Scale Deployment\7f21189
+Node: Extending Kubed\7f22486
+Node: Contributing\7f24077
+Node: Indices\7f24777
+Node: Function Index\7f24951
+Node: Variable Index\7f28346
+Node: Keystroke Index\7f29009
+Node: Concept Index\7f31069
+\1f
+End Tag Table
+
+\1f
+Local Variables:
+coding: utf-8
+End:
diff --git a/elpa/kubed-0.4.2/kubed.texi b/elpa/kubed-0.4.2/kubed.texi
new file mode 100644 (file)
index 0000000..b9b5c16
--- /dev/null
@@ -0,0 +1,789 @@
+\input texinfo    @c -*- texinfo -*-
+@c %**start of header
+@setfilename kubed.info
+@settitle Kubed: Kubernetes, Emacs, done!
+@documentencoding UTF-8
+@documentlanguage en
+@set MAINTAINERSITE @uref{https://eshelyaron.com,maintainer webpage}
+@set MAINTAINER Eshel Yaron
+@set MAINTAINEREMAIL @email{meNOSPAMeshelyaron.com}
+@set MAINTAINERCONTACT @uref{mailto:meNOSPAMeshelyaron.com,contact the maintainer}
+@c %**end of header
+
+@copying
+This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes.
+
+Copyright @copyright{} 2024 Eshel Yaron.
+
+@quotation
+Permission is granted to copy, distribute and/or modify this document
+under the terms of the GNU Free Documentation License, Version 1.3 or
+any later version published by the Free Software Foundation; with no
+Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts.
+@end quotation
+@end copying
+
+@dircategory Emacs
+@direntry
+* Kubed: (kubed).       Kubernetes, Emacs, done!
+@end direntry
+
+@finalout
+@titlepage
+@title Kubed: Kubernetes, Emacs, done!
+@author Eshel Yaron (@email{me@@eshelyaron.com})
+@end titlepage
+
+@contents
+
+@ifnottex
+@node Top
+@top Kubed: Kubernetes, Emacs, done!
+
+This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes.
+
+@end ifnottex
+
+@menu
+* Overview::                    Introduction to Kubed
+* Getting Started::             First steps with Kubed
+* Usage::                       Using Kubed to interact with Kubernetes
+* Extending Kubed::             Adding support for your custom resources
+* Contributing::                Get involved in Kubed development
+* Indices::
+@end menu
+
+@node Overview
+@chapter Overview
+
+@cindex Kubed
+@cindex kubed
+Kubed is a rich Kubernetes interface within Emacs.  It helps you work
+with your Kubernetes clusters and deployments with the full power of
+@command{kubectl}, and with the comfort and confidence of an intuitive
+interactive interface.
+
+@ifhtml
+@float
+@image{/assets/kubed-overview,,,Image: Four different kinds of Kubed buffers in Emacs,.png}
+@caption{Pods, logs, shells, YAMLs, all in one integrated interface}
+@end float
+@end ifhtml
+
+You can use Kubed to:
+
+@itemize
+@item
+Browse and manage Kubernetes workloads
+@item
+Connect to pods and edit files or execute commands
+@item
+Create new resources, edit and delete them
+@item
+Work with multiple clusters
+@item
+Get help about various Kubernetes objects
+@item
+@dots{}
+@end itemize
+
+These features and others are documented in the rest of this manual,
+along with many options that Kubed provides for you to customize its
+behavior.
+
+@node Getting Started
+@chapter Getting Started
+
+@cindex installation
+
+Get Kubed from GNU ELPA with @kbd{M-x package-install}, or use your
+favorite Emacs package manager to install Kubed from Git.  You can
+clone the Kubed Git repository from any of the following locations:
+
+@itemize
+@item
+@url{https://git.sr.ht/~eshel/kubed}
+@item
+@url{https://github.com/eshelyaron/kubed.git}
+@item
+@url{git://git.eshelyaron.com/kubed.git}
+@end itemize
+
+@cindex requirements
+To get started with Kubed, all you need is @command{kubectl} and Emacs.
+
+Kubed tries to find @command{kubectl} in the directories listed in the
+Emacs variable @code{exec-path}.  When Emacs is started from a shell, it
+initializes @code{exec-path} from the shell's @env{PATH} environment
+variable which normally includes the location of @command{kubectl} in
+common @command{kubectl} installations.  If Emacs doesn't find the
+@command{kubectl} executable via @code{exec-path}, you can tell Kubed
+where to find it by customizing @code{kubed-kubectl-program}.
+
+@defopt kubed-kubectl-program
+Name of @command{kubectl} executable file to use.
+@end defopt
+
+@node Usage
+@chapter Usage
+
+Kubed comes with a large number of Emacs command for interacting with
+Kubernetes.  Some commands perform one action on one resource, while
+others show you a menu with many resources and let you interact with any
+number of them.  Some commands are specific to one type of resources,
+while others act on resources of any type.  There are different ways to
+use Kubed, depending on your use case.
+
+The two most important entry points for Kubed are
+@code{kubed-prefix-map} and @code{kubed-transient}:
+
+@anchor{Definition of kubed-prefix-map}
+@cindex global prefix keymap
+@cindex prefix keymap, global commands
+@cindex keymap, for global commands
+@defvr Keymap kubed-prefix-map
+Keymap with bindings for many useful Kubed commands.
+@end defvr
+
+The prefix keymap @code{kubed-prefix-map} gives you quick access to the
+main Kubed commands.  You may want to bind it to a convenient key in
+your global keymap, for example:
+
+@lisp
+(keymap-global-set "C-c k" 'kubed-prefix-map)
+@end lisp
+
+With this binding in place, you can create a new Kubernetes resource
+with @kbd{C-c k +} and attach to a running pod with @kbd{C-c k p a}.
+The @kbd{C-c k} binding is just a suggestion of course, feel free to
+pick any key sequence you like.
+
+@ifhtml
+@float
+
+@image{/assets/kubed-prefix-map,,,Image: Emacs which-key popup for kubed-prefix-map keymap,.png}
+
+@image{/assets/kubed-deployment-prefix-map,,,Image: Emacs which-key popup for kubed-deployment-prefix-map,.png}
+
+@caption{Use @code{kubed-prefix-map} for quick access to the main Kubed commands}
+@end float
+@end ifhtml
+
+You can also access all of the commands in @code{kubed-prefix-map} via
+the menu-bar @samp{Kubernetes} menu---to add this menu to your
+menu-bar, enable the global minor mode @code{kubed-menu-bar-mode}:
+
+@anchor{Definition of kubed-menu-bar-mode}
+@deffn {Minor Mode} kubed-menu-bar-mode
+Add @samp{Kubernetes} menu to your menu bar.
+@end deffn
+
+The other important entry point that Kubed provides for exploring and
+performing different Kubernetes operations is @code{kubed-transient}:
+
+@deffn Command kubed-transient
+Open the main Kubed transient menu.
+@end deffn
+
+This command pops up the Kubed transient menu, which gives you
+interactive guidance for invoking Kubed commands.
+@xref{Top,,,transient}.
+
+The following sections describe in detail the various Kubed commands.
+
+@menu
+* Display Resource::
+* Browse Resources::
+* Context and Namespace::
+* Transient Menus::
+* Scale Deployment::
+@end menu
+
+@node Display Resource
+@section Display Resource
+
+You can view the YAML description of any resource in your Kubernetes
+cluster.  In Kubed, this is referred to as @dfn{displaying} the given
+resource.  The following commands display a Kubernetes resource:
+
+@deffn Command kubed-display-resource
+Display a Kubernetes resource.
+@end deffn
+
+@deffn Command kubed-display-pod
+@deffnx Command kubed-display-deployment
+@deffnx Command kubed-display-namespace
+@deffnx Command kubed-display-@var{foo}
+Display Kubernetes resource of a given type.
+@end deffn
+
+Command @code{kubed-display-resource} can display any Kubernetes
+resource.  It prompts you to select a resource type and a resource
+name, and displays that resource in a dedicated buffer.  By default,
+this command assumes you want to display a resource that is in the
+current namespace, but if you invoke @code{kubed-display-resource}
+with a prefix argument (@kbd{C-u}) and choose a namespaced resource
+type, then it also lets you select the namespace of the resource.
+With a double prefix argument, it lets you select which
+@command{kubectl} context to use too.  @xref{Context and Namespace}.
+
+If you have @code{kubed-prefix-map} bound to @kbd{C-c k}, you can
+invoke @code{kubed-display-resource} with @kbd{C-c k @key{RET}}.  This
+command is also available from the menu-bar ``Kubernetes'' menu
+(@pxref{Definition of kubed-menu-bar-mode}), and from the transient
+``Kubernetes Display'' menu (@pxref{Transient Menus}).
+
+You can also display the resource at point in resources list buffers
+by simply typing @kbd{@key{RET}}.  @xref{Browse Resources}.
+
+Kubed also provides a dedicated @code{kubed-display-@var{foo}} command
+for each supported resource type @var{foo}.  These commands are just
+like @code{kubed-display-resource}, except that they do not prompt you
+for a type, since they are type-specific.  For example,
+@code{kubed-display-pod} only prompts you for a pod name, and then
+displays it like @code{kubed-display-resource} does.
+
+You can invoke these commands by adding a type-specific key before
+@kbd{@key{RET}} in @kbd{C-c k @key{RET}}: @kbd{C-c k p @key{RET}}
+invokes @code{kubed-display-pod}, @kbd{C-c k d @key{RET}} invokes
+@code{kubed-display-deployment}, and so on.
+
+When Kubed prepares a buffer for displaying a resource, it runs the
+hook @code{kubed-yaml-setup-hook} right after populating the buffer
+with the YAML description of the resource:
+
+@defopt kubed-yaml-setup-hook
+List of functions to call, in order, to prepare YAML buffers.  The
+first function on this hook is normally the major mode you want to use
+for YAML buffers that display Kubernetes resources.  By default, this
+is the list @code{(yaml-ts-mode view-mode)}.
+@end defopt
+
+In addition, Kubed enables a bespoke minor mode
+@code{kubed-display-resource-mode} in buffers that display Kubernetes
+resources:
+
+@deffn {Minor Mode} kubed-display-resource-mode
+Minor mode for buffers that display a Kubernetes resource.
+@end deffn
+
+@noindent
+This minor mode does the following:
+
+@itemize
+@item
+It shows the word @samp{Kubed} in the mode line, so you know you're
+seeing a rendered Kubernetes resource, rather than any old YAML file.
+@item
+It allows you to @emph{bookmark} the resource your viewing and your
+position in its YAML description with the regular Emacs bookmarks
+commands (@pxref{Bookmarks,,,emacs}), namely @kbd{C-x r m}.
+@item
+@kindex C-c C-j
+@findex kubed-display-resource-jump-to-list
+It binds the key sequence @kbd{C-c C-j} to command
+@code{kubed-display-resource-jump-to-list}, which pops up the
+resources list buffer for the type of the displayed resource, and
+takes you to the line corresponding to it.
+@item
+@kindex C-c C-c
+@findex kubed-display-resource-replace
+@kindex C-c C-=
+@findex kubed-display-resource-diff
+@kindex C-x C-q
+@findex read-only-mode
+It binds @kbd{C-c C-c} to command
+@code{kubed-display-resource-replace}, which lets you apply any
+changes that you make in the YAML buffer back to the resource.  When
+you hit @kbd{C-c C-c}, Kubed asks you for confirmation before
+proceeding; you can reply with @kbd{d} (@samp{diff}) to show a diff
+between the current state of the resource and the current buffer
+contents.  You can also display this diff anytime with @kbd{C-c C-=}.
+Note that by default Kubed displays resources in read-only buffers, so
+you may need to hit @kbd{C-x C-q} (@code{read-only-mode}) before
+making changes in the buffer.
+@item
+@kindex C-x x g
+@findex revert-buffer
+@findex revert-buffer-quick
+It lets you repopulate the buffer with updated information about the
+resource with @kbd{C-x x g} (@code{revert-buffer-quick}) and
+@w{@kbd{M-x revert-buffer}}.  This is also useful if you edit the
+resource and want to restart from a clean slate.
+@end itemize
+
+@ifhtml
+@float
+@image{/assets/kubed-replace-diff,,,Image: Displaying and editing YAML of Kubernetes pod with Kubed,.png}
+@caption{Type @kbd{C-x C-q} in a resource YAML buffer to edit it, @kbd{C-c C-c} to apply}
+@end float
+@end ifhtml
+
+@node Browse Resources
+@section Browse Resources
+
+Kubed gives you an interactive list of resources for each type, that
+you access with the @code{kubed-list-@var{resource}s} commands, such
+as @code{kubed-list-pods}.
+
+These commands are bound to @kbd{l} in Kubed's type-specific prefix
+keymaps, most of which are accessible via the global
+@code{kubed-prefix-map} by default (@pxref{Definition of
+kubed-prefix-map}).  So if you've bound @code{kubed-prefix-map} to
+@kbd{C-c k}, you can pop up the pods list with @kbd{C-c k p l}, the
+deployments list with @kbd{C-c k d l}, the jobs list with @w{@kbd{C-c
+k j l}}, etc.  Normally, Kubed shows the list of resources in the
+current namespace, but if you invoke these commands with a prefix
+argument (say, @kbd{C-u C-c k p l}), they first prompt you to select a
+namespace, and then show resources in the namespace you selected.
+
+The list of @var{resource}s in shown a buffer called @w{@file{*Kubed
+@var{resource}s@@@var{namespace}[@var{context}]*}}, where
+@var{context} is a @command{kubectl} context, and @var{namespace} is a
+namespace in @var{context}.  For example, Kubed shows the list of pods
+in namespace @samp{default} for context @samp{minikube} in buffer
+@file{*Kubed pos@@default[minikube]}.  For non-namespaced resources,
+the @w{@samp{@@@var{namespace}}} part is omitted.
+
+Each resource type has its own major mode for list buffers with
+type-specific commands and operations, but they are all also derived
+from a shared parent mode.  The pods list uses the
+@code{kubed-pods-mode} (@samp{Kubernetes Pods}) major mode, the list
+of Kubernetes deployments uses @code{kubed-deployments-mode}, and so
+on.
+
+@ifhtml
+@float
+@image{/assets/kubed-list-transient,,,Image: Kubed pods list buffer with the help transient menu visible,.png}
+@caption{Browse your pods, deployments, and other resources with Kubed list buffers}
+@end float
+@end ifhtml
+
+The common parent major mode is @code{kubed-list-mode}:
+
+@deffn {Major Mode} kubed-list-mode
+Major mode for listing generic Kubernetes resources.
+@end deffn
+
+This mode provides the following commands for all type-specific
+derived modes:
+
+@table @kbd
+@kindex @key{?}
+@findex kubed-list-transient
+@item @key{?}
+Show help in transient menu (@code{kubed-list-transient}).
+@kindex @key{RET}
+@findex kubed-list-select-resource
+@item @key{RET}
+Display the resource at point in the current window
+(@code{kubed-list-select-resource}).
+@kindex o
+@findex kubed-list-select-resource-other-window
+@item o
+Display the resource at point in another window and select that window
+(@code{kubed-list-select-resource-other-window}).
+@kindex C-o
+@findex kubed-list-display-resource
+@item C-o
+Display the resource at point in another window without selecting it
+(@code{kubed-list-display-resource}).
+@kindex D
+@findex kubed-list-delete
+@item D
+Delete the resource at point (@code{kubed-list-delete}).
+@kindex e
+@findex kubed-list-edit
+@item e
+Edit the resource at point (@code{kubed-list-edit}).
+@kindex P
+@findex kubed-list-patch
+@item P
+Patch the resource at point (@code{kubed-list-patch}).
+@kindex g
+@findex kubed-list-update
+@item g
+Update the resources list by fetching new information from Kubernetes
+(@code{kubed-list-update}).
+@kindex /
+@findex kubed-list-set-filter
+@item /
+Filter the current list of resources (@code{kubed-list-set-filter}).
+@xref{List Filter}.
+@kindex d
+@findex kubed-list-mark-for-deletion
+@item d
+Mark the resource at point for deletion
+(@code{kubed-list-mark-for-deletion}).  Resources marked for deletion
+have a @samp{D} in the beginning of their line.
+@kindex x
+@findex kubed-list-delete-marked
+@item x
+Delete marked resources (@code{kubed-list-delete-marked}).
+@kindex u
+@findex kubed-list-unmark
+@item u
+Unmark from resource at point (@code{kubed-list-unmark}).
+@kindex w
+@findex kubed-list-copy-as-kill
+@item w
+Copy name of resource at point into the kill ring
+(@code{kubed-list-copy-as-kill}).
+@kindex n
+@findex next-line
+@item n
+Move point to the next line (@code{next-line}).
+@kindex p
+@findex previous-line
+@item p
+Move point to the previous line (@code{previous-line}).
+@kindex @key{TAB}
+@findex kubed-list-next-column
+@item @key{TAB}
+Move point to the next column.  If point is already at the last
+column, move to the first column of the next line instead
+(@code{kubed-list-next-column}).
+@kindex S-@key{TAB}
+@findex kubed-list-previous-column
+@item S-@key{TAB}
+Move point to the previous column.  If point is already at the first
+column, move to the last column of the previous line instead
+(@code{kubed-list-previous-column}).
+@kindex S
+@findex tabulated-list-sort
+@item S
+Sort lines by the column at point (@code{tabulated-list-sort}).
+@kindex |
+@findex kubed-list-fit-column-width-to-content
+@item |
+Fit width of column at point to its content
+(@code{kubed-list-fit-column-width-to-content}).  With non-negative
+prefix argument @var{n} (for example, @kbd{0 |}), fit @var{n}th
+column.  With negative prefix argument (@kbd{- |}), fit all columns.
+@kindex @{
+@findex tabulated-list-narrow-current-column
+@item @{
+Narrow the column at point
+(@code{tabulated-list-narrow-current-column}).
+@kindex @}
+@findex tabulated-list-widen-current-column
+@item @}
+Widen the column at point
+(@code{tabulated-list-widen-current-column}).
+@end table
+
+In addition, you can bookmark any Kubed resources list buffer with the
+usual @kbd{C-x r m}.  @xref{Bookmarks,,,emacs}.
+
+Note that @code{kubed-list-mode} (and its type-specific derivatives)
+bind the @kbd{g} key, which is commonly bound to @code{revert-buffer},
+to @code{kubed-list-update}.  The difference between these commands is
+that @code{revert-buffer} only refreshes the list display, without
+fetching updated data from Kubernetes.  You can still invoke
+@code{revert-buffer} with @kbd{C-x x g} or @kbd{M-x}, if you need it.
+
+@vindex kubed-list-mode-line-format
+Whenever Kubed is fetching data from Kubernetes to update a resources
+list buffer, the mode line of that list buffer shows the @samp{[...]}
+indicator.  You can set user option @code{kubed-list-mode-line-format}
+to customize this indicator.
+
+@menu
+* List Filter::
+@end menu
+
+@node List Filter
+@subsection Filtering Resource Lists
+
+In all Kubernetes resource list buffers, you can use @kbd{/}
+(@code{kubed-list-set-filter}) to @dfn{filter} the list of resources.
+This command sets the filter of the current buffer, which specifies
+conditions on resources (lines).  Resources that don't satisfy the
+conditions are hidden, so you can focus on those that do.
+
+Filters can be simple (@dfn{atomic filters}) or complex (@dfn{composed
+filters}).  In the simple case, you enter a filter in the minibuffer
+in the format @w{@samp{@var{op} @var{col} @var{val}}}, where @var{op}
+is a comparison operator, one of @samp{=}, @samp{~}, @samp{<} and
+@samp{>}; @var{col} is a column name; and @var{val} is a value to
+compare to values of @var{col} with @var{op}.
+
+If @var{op} is @samp{=}, it says to keep only lines whose @var{col}
+equals @var{val}.  If @var{op} is @samp{~}, it says to keep lines
+whose @var{col} matches the @var{val} as a regular expression.
+@samp{<} and @samp{>} compare column values to numeric values.  For
+example, the filter @samp{= Name foobar} keeps only resources whose
+name is @samp{foobar}.  To include whitespace in @var{val}, wrap
+@var{val} in double quotes: @samp{= Name "foo bar"}.
+
+The possible values of @var{op} and their meanings are defined in
+@code{kubed-list-filter-operator-alist}, which is a user option you
+can use to add or change the meaning of filter operators:
+
+@defopt kubed-list-filter-operator-alist
+Association list of filter operators and corresponding Emacs Lisp
+functions.  See the documentation string of this option for specifics.
+@end defopt
+
+You can use more refined filters by composing simple filters.  To add
+another filter that resources must satisfy, wrap your two filters in
+parentheses and put them next to each other:
+
+@lisp
+(= Name foobar) (~ Namespace kube)
+@end lisp
+
+@noindent
+This filter keeps only resources whose name is @samp{foobar} in
+namespaces that include @samp{kube} as a substring, it expresses the
+conjunction of the two simple filters.
+
+To negate a simple filter, put a single quote ahead of it, like so:
+
+@lisp
+(= Name foobar) '(~ Namespace kube)
+@end lisp
+
+@noindent
+This matches resources named @samp{foobar} in namespaces that do
+@emph{not} contain @samp{kube}.
+
+To specify a disjunction of simple filters (and negated simple
+filters), add another level of nesting:
+
+@lisp
+((= Name spam) (= Name foobar)) '(~ Namespace kube)
+@end lisp
+
+@noindent
+This filter matches resources named either @samp{spam} or
+@samp{foobar}, in namespaces that do not contain @samp{kube}.
+
+While entering a filter in the minibuffer, @kbd{@key{TAB}} completion
+is available for column names and values.  To clear the filter, just
+exit the minibuffer with an empty input.
+
+@node Context and Namespace
+@section Context and Namespace
+
+Kubed uses @command{kubectl} to interact with Kubernetes clusters, and
+you can use multiple @command{kubectl} @dfn{contexts} in Kubed to work
+with different Kubernetes clusters in parallel.  Within each context,
+Kubed lets you work in multiple @dfn{namespaces} too.  In general,
+each buffer has its own @dfn{local context} and @dfn{local namespace},
+and Kubed commands use these local context and namespace by default.
+This is similar to how different buffers have different values of
+@code{default-directory} (@pxref{File Names,,,emacs}), and how that
+affects commands that interact with the filesystem.
+
+For Kubed buffers that display a Kubernetes resource (@pxref{Display
+Resource}) or a list of resources (@pxref{Browse Resources}), the
+buffer-local context and namespace are the context and namespace of
+the resources the buffer displays.  For non-Kubed buffers, the user
+option @code{kubed-default-context-and-namespace} determines the
+default local context and namespace.
+
+@defopt kubed-default-context-and-namespace
+Default @command{kubectl} context and Kubernetes namespace for Kubed
+commands.  The value of this option is either a cons cell
+@w{@code{(@var{context} . @var{namespace})}} or @code{nil}.  If it is
+@code{nil}, Kubed initializes this option to a cons cell with the
+current @command{kubectl} context and namespace for that context on
+the first time Kubed consults this option.
+@end defopt
+
+As mentioned above, most Kubed commands use the local context and
+namespace by default---it is also conventional for Kubed commands to
+let you choose a different namespace if you invoke them with a prefix
+argument (@kbd{C-u}), and to also let you choose a different context
+if you invoke them with a double prefix argument (@kbd{C-u C-u}).
+
+The following functions return the local context and namespace for
+buffer in which you call them:
+
+@defun kubed-local-context
+Return the local @command{kubectl} context for the current buffer.
+@end defun
+
+@defun kubed-local-namespace
+Return the local Kubernetes namespace for the current buffer.
+@end defun
+
+@defun kubed-local-context-and-namespace
+Return the local context and namespace for the current buffer as a
+cons cell @w{@code{(@var{context} . @var{namespace})}}.
+@end defun
+
+@node Transient Menus
+@section Transient Menus
+
+Kubed provides a set of keyboard-driven @dfn{transient menus}
+(@pxref{Top,,,transient}) that you can use to explore and invoke
+various commands.  Transient menus show you available commands along
+with their key bindings, which helps you use many different commands
+without the need to remember the key bindings for all them.
+Additionally, the transient menus allow you to specify options and
+switches for the commands you invoke, which may not be available when
+invoking these commands directly.  For example, while you can invoke
+command @code{kubed-logs} directly to view logs from a container
+running in Kubernetes, the ``Kubernetes Logs'' transient menu which
+you open with @code{kubed-transient-logs} lets you set a maximum
+number of logs to retrieve, among other knobs and handles.
+
+@ifhtml
+@float
+
+@image{/assets/kubed-transient,,,Image: The Kubed main transient menu,.png}
+
+@image{/assets/kubed-transient-exec,,,Image: The Kubed Exec transient menu,.png}
+
+@caption{Explore Kubed commands with transient menus}
+@end float
+@end ifhtml
+
+Command @code{kubed-transient} opens the Kubed ``root'' transient
+menu, which is a dispatch that lets you access different transient
+menus, such as the aforementioned ``Kubernetes Logs'' transient menu.
+It's a good idea to bind @code{kubed-transient} to a convenient key,
+either in the global keymap or under @code{kubed-prefix-map}:
+
+@lisp
+(keymap-global-set "s-k" #'kubed-transient)
+(keymap-set kubed-prefix-map "k" #'kubed-transient)
+@end lisp
+
+In Kubed resource list buffers, type @kbd{?} to pop up a transient
+menu with commands that are specific to the type of resources the
+buffer displays.  @xref{Browse Resources}.
+
+@node Scale Deployment
+@section Scale Deployment
+
+You can use Kubed to @dfn{scale} Kubernetes a deployment, which means
+setting the size of the deployment in terms of the number of replicas
+it governs.
+
+@deffn Command kubed-scale-deployment
+Scale a Kubernetes deployment.
+@end deffn
+
+@code{kubed-scale-deployment} is bound to @kbd{$} in the
+@code{kubed-deployment-prefix-map}, so if you have
+@code{kubed-prefix-map} bound to @kbd{C-c k} then you can invoke it
+with @kbd{C-c k d $}.  @xref{Definition of kubed-prefix-map}.  This
+command prompts you for a deployment and then lets you select a new
+number of replicas for that deployment.  By default you are prompted
+for deployment in the local context and namespace (@pxref{Context and
+Namespace}); with a prefix argument, you can specify a different
+namespace; with a double prefix argument (@kbd{C-u C-u}), you can
+specify a different context too.
+
+In the deployments list buffer, you can scale the deployment at point
+by simply typing @kbd{$}.  @xref{Browse Resources}.
+
+@ifhtml
+@float
+@image{/assets/kubed-scale-deployments,,,Image: Help transient menu in Kubed deployments list buffer,.png}
+@caption{Scale deployments with @kbd{$} in the deployments list buffer}
+@end float
+@end ifhtml
+
+Lastly, you can scale deployments using the transient menu
+@code{kubed-transient-scale-deployment}, which is available from the
+main Kubed transient by pressing @kbd{$}.  @xref{Transient Menus}.
+
+@node Extending Kubed
+@chapter Extending Kubed
+
+Kubed has built-in support for many common types of Kubernetes
+resources.  ``Support'' for a resource type in Kubed just means
+providing common commands for working with resources of that type,
+such as a command for showing the interactive list of these resources,
+like @code{kubed-list-pods} for Kubernetes pods.  In Kubed, the macro
+@code{kubed-define-resource} is used to define these common commands
+and add support for more resource types:
+
+@deffn Macro kubed-define-resource resource &optional properties &rest [ keyword argument ]@dots{} commands
+Define commands for @var{resource}.
+@end deffn
+
+Say you want to add support for your Kubernetes service accounts.
+With @code{kubed-define-resource}, all you need to do is to add the
+following line to your Emacs initialization file:
+
+@lisp
+(kubed-define-resource serviceaccount)
+@end lisp
+
+This line defines the following commands for you:
+
+@itemize
+@item
+@code{kubed-display-serviceaccount}
+@item
+@code{kubed-list-serviceaccounts}
+@item
+@code{kubed-edit-serviceaccount}
+@item
+@code{kubed-delete-serviceaccount}
+@item
+@code{kubed-create-serviceaccount}
+@item
+@code{kubed-explain-serviceaccounts}
+@end itemize
+
+See the documentation string of @code{kubed-define-resource}
+(@w{@kbd{C-h f}}) for details about the optional arguments of this
+macro: the @var{properties} argument which lets you specify what Kubed
+should show in the resource list buffer and the @code{commands} argument
+which lets you associate additional commands with your resource type.
+
+@node Contributing
+@chapter Contributing
+
+We highly appreciate all contributions, including bug reports,
+patches, improvement suggestions, and general feedback!
+
+The best way to get in touch with the Kubed maintainers is via
+@uref{https://lists.sr.ht/~eshel/kubed-devel, the Kubed mailing list}.
+
+@deffn Command kubed-submit-bug-report
+Report a bug in Kubed to the maintainers via mail.
+@end deffn
+
+You can use the command @kbd{M-x kubed-submit-bug-report} to easily
+contact the Kubed maintainers from within Emacs.  This command opens a
+new buffer with a message template ready to be sent to the development
+mailing list.
+
+@node Indices
+@unnumbered Indices
+
+@menu
+* Function Index::
+* Variable Index::
+* Keystroke Index::
+* Concept Index::
+@end menu
+
+@node Function Index
+@unnumberedsec Function index
+
+@printindex fn
+
+@node Variable Index
+@unnumberedsec Variable index
+
+@printindex vr
+
+@node Keystroke Index
+@unnumberedsec Keystroke index
+
+@printindex ky
+
+@node Concept Index
+@unnumberedsec Concept index
+
+@printindex cp
+
+@bye
diff --git a/elpa/kubedoc-20240108.1404/kubedoc-autoloads.el b/elpa/kubedoc-20240108.1404/kubedoc-autoloads.el
new file mode 100644 (file)
index 0000000..b513ff4
--- /dev/null
@@ -0,0 +1,34 @@
+;;; kubedoc-autoloads.el --- automatically extracted autoloads (do not edit)   -*- lexical-binding: t -*-
+;; Generated by the `loaddefs-generate' function.
+
+;; This file is part of GNU Emacs.
+
+;;; Code:
+
+(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path)))
+
+
+\f
+;;; Generated autoloads from kubedoc.el
+
+(autoload 'kubedoc "kubedoc" "\
+Show Kubernetes API documentation.
+Uses current context." t)
+(autoload 'kubedoc-for-context "kubedoc" "\
+Show Kubernetes API documentation.
+Prompts for changing current context." t)
+(register-definition-prefixes "kubedoc" '("kubedoc-"))
+\f
+;;; End of scraped data
+
+(provide 'kubedoc-autoloads)
+
+;; Local Variables:
+;; version-control: never
+;; no-byte-compile: t
+;; no-update-autoloads: t
+;; no-native-compile: t
+;; coding: utf-8-emacs-unix
+;; End:
+
+;;; kubedoc-autoloads.el ends here
diff --git a/elpa/kubedoc-20240108.1404/kubedoc-pkg.el b/elpa/kubedoc-20240108.1404/kubedoc-pkg.el
new file mode 100644 (file)
index 0000000..5d3fd40
--- /dev/null
@@ -0,0 +1,10 @@
+(define-package "kubedoc" "20240108.1404" "Kubernetes API Documentation"
+  '((emacs "27.1"))
+  :commit "aac02b096c98b83b4eaf129e6d767cf7150a6d43" :authors
+  '(("Dean Lindqvist Todevski" . "https://github.com/r0bobo"))
+  :keywords
+  '("docs" "help" "k8s" "kubernetes" "tools")
+  :url "https://github.com/r0bobo/kubedoc.el/")
+;; Local Variables:
+;; no-byte-compile: t
+;; End:
diff --git a/elpa/kubedoc-20240108.1404/kubedoc.el b/elpa/kubedoc-20240108.1404/kubedoc.el
new file mode 100644 (file)
index 0000000..6cd3c47
--- /dev/null
@@ -0,0 +1,363 @@
+;;; kubedoc.el --- Kubernetes API Documentation -*- lexical-binding: t; -*-
+;;
+;; Copyright (C) 2021, 2024 Dean Lindqvist Todevski
+;;
+;; Author: Dean Lindqvist Todevski <https://github.com/r0bobo>
+;; Maintainer: Dean Lindqvist Todevski
+;; Keywords: docs help k8s kubernetes tools
+;; Version: 1.0
+;; Homepage: https://github.com/r0bobo/kubedoc.el/
+;; Package-Requires: ((emacs "27.1"))
+
+;; This file is NOT part of GNU Emacs.
+
+;; This program is free software: you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; kubedoc.el provides Kubernetes API documentation in Emacs.
+
+;;; Code:
+
+;;;; Requirements
+
+(require 'map)
+(require 'seq)
+(require 'subr-x)
+
+;;;; Completion style
+
+(add-to-list 'completion-category-overrides '(kubedoc (styles partial-completion)))
+
+;;;; Declarations
+
+(defgroup kubedoc nil
+  "Kubernetes API documentation."
+  :group 'tools)
+
+(defcustom kubedoc-excluded-resources '("\\.metrics\\.k8s\\.io")
+  "List of regexps for Kubernetes resources to ignore.
+Some Aggregated APIs have no docs for example."
+  :type 'sexp)
+
+(defconst kubedoc--explain-field-regex
+  "^\\([ \t]+\\)\\(\\w+\\)[ \t]+\\(<.*>+\\)[ \t]*\\(-required-\\)?$")
+
+(defconst kubedoc-imenu-generic-expression
+  `((nil ,kubedoc--explain-field-regex 2 kubedoc--imenu-goto-field)))
+
+(defconst kubedoc-font-lock-defaults
+  `(("^GROUP\\|^KIND\\|^VERSION\\|^RESOURCE\\|^DESCRIPTION\\|^FIELDS?" . font-lock-keyword-face)
+    (,kubedoc--explain-field-regex 3 font-lock-comment-face)
+    ("-required-" . font-lock-function-name-face)))
+
+(defvar kubedoc-mode-map
+  (let ((map (make-sparse-keymap)))
+    (suppress-keymap map)
+    (set-keymap-parent map (make-composed-keymap button-buffer-map special-mode-map))
+    map))
+
+(defvar kubedoc--field-completion-table-cache (make-hash-table :test 'equal))
+
+(defvar kubedoc--resource-completion-table-cache nil)
+
+(defvar kubedoc--current-context nil)
+
+(defvar-local kubedoc--buffer-path nil)
+
+(define-button-type 'kubedoc-field
+  'follow-link t
+  'help-echo "mouse-2, RET: display this section"
+  'action #'kubedoc--field-button-function)
+
+
+;;;; Commands
+
+;;; UI
+
+(defun kubedoc--imenu-goto-field (_name position)
+  "Jump to correctly indented field POSITION."
+  (goto-char (+ position 3)))
+
+(defun kubedoc--highlight-field-links ()
+  "Create field link buttons in current buffer."
+  (while (re-search-forward kubedoc--explain-field-regex nil t)
+    (make-button
+     (match-beginning 2)
+     (match-end 2)
+     'type 'kubedoc-field
+     'kubectl-section (match-string 2))))
+
+(defun kubedoc--field-button-function (button)
+  "Follow field link in BUTTON."
+  (with-current-buffer (current-buffer)
+    (let ((field (button-get button 'kubectl-section)))
+      (apply #'kubedoc--view-resource (append kubedoc--buffer-path (list field))))))
+
+;;; Helpers
+
+(defun kubedoc--kubectl-command (&rest args)
+  "Run kubectl with ARGS and write output to current buffer."
+  (when kubedoc--current-context
+    (push (concat "--context=" kubedoc--current-context) args))
+  (let ((stderr (make-temp-file "emacs-kubedoc-kubectl-stderr-"))
+        (kubectl (executable-find "kubectl")))
+    (unwind-protect
+        (when-let* ((returncode (apply #'call-process kubectl nil `(t ,stderr) nil args))
+                    ((/= returncode 0))
+                    (errmsg (with-temp-buffer
+                              (insert-file-contents stderr)
+                              (buffer-string))))
+          (user-error (format "kubedoc.el kubectl error: %s" errmsg)))
+      (delete-file stderr))))
+
+;;; Parse functions
+
+(defun kubedoc--parse-api-resources ()
+  "Read output of `kubectl api-resources' from buffer and return as list.
+Excludes resouces that match regexps in `kubedoc-excluded-resources'."
+  (let ((excluded-resource-p (lambda (elt)
+                               (seq-every-p
+                                (lambda (re)
+                                  (not (string-match-p re elt))) kubedoc-excluded-resources))))
+    (thread-last
+      (split-string (buffer-string) nil t)
+      (seq-filter excluded-resource-p)
+      (seq-map (apply-partially #'format "%s/")))))
+
+(defun kubedoc--parse-kubectl-explain-fields ()
+  "Parse resource document from current buffer.
+Buffer should contain output from `kubectl explain --recursive'.
+Result is list of full paths for current resource.
+Supports both OpenAPI v2 and v3 schema."
+  (let ((result '())
+        (path '())
+        (base -1))
+    (goto-char (point-min))
+    (while (not (eobp))
+      (let ((line (thing-at-point 'line)))
+        (when (string-match kubedoc--explain-field-regex line)
+          (let* ((depth (current-indentation))
+                 (field (match-string 2 line)))
+            ;; Set base indention length from first iteration
+            (when (= base -1)
+              (setq base depth))
+            (let ((position (/ (- depth base) base)))
+              ;; Drop all previous path elements that are
+              ;; deeper than the current.
+              (setq path (reverse (seq-subseq (reverse path) 0 position)))
+              (push field path)
+              (push (string-join (reverse path) "/") result)))))
+      (forward-line 1))
+    ;; Filter result so that every result
+    ;; ends with the left field of each hierarchy.
+    ;; If the result contains '("kind" "metadata" "metadata/labels")
+    ;; the result should be '("kind" "metadata/labels")
+    (let ((full-path-p (lambda (elt)
+                         (thread-last
+                           result
+                           (seq-some (apply-partially #'string-prefix-p (concat elt "/")))
+                           (not)))))
+      (thread-last
+        result
+        (seq-filter full-path-p)
+        (reverse)))))
+
+(defun kubedoc--kubectl-contexts ()
+  "List available kubectl contexts."
+  (with-temp-buffer
+    (kubedoc--kubectl-command "config" "get-contexts" "--output=name")
+    (seq-sort #'string< (split-string (buffer-string) nil t))))
+
+;;; Completion
+
+(defun kubedoc--resource-completion-table ()
+  "Completion candidate list for Kubernetes resources in the cluster."
+  (with-temp-buffer
+    (kubedoc--kubectl-command "api-resources" "--output" "name")
+    (kubedoc--parse-api-resources)))
+
+(defun kubedoc--resource-completion-table-cached ()
+  "Cached completion candidate list for Kubernetes resources in the cluster."
+  (if-let ((cached kubedoc--resource-completion-table-cache))
+      cached
+    (setq kubedoc--resource-completion-table-cache (kubedoc--resource-completion-table))))
+
+(defun kubedoc--field-completion-table (resource)
+  "Completion candidate list for all fields of Kubernetes RESOURCE."
+  (with-temp-buffer
+    (kubedoc--kubectl-command "explain" "--recursive" resource)
+    (thread-last
+      (kubedoc--parse-kubectl-explain-fields)
+      (seq-map (apply-partially #'format "%s/%s" resource)))))
+
+(defun kubedoc--field-completion-table-cached (resource)
+  "Cached Completion candidate list for all fields of Kubernetes RESOURCE."
+  (if-let (cached (map-elt kubedoc--field-completion-table-cache resource))
+      cached
+    (map-put! kubedoc--field-completion-table-cache resource (kubedoc--field-completion-table resource))))
+
+(defun kubedoc--completion-table (path)
+  "Completion candidate list for given Kubernetes resource and field PATH.
+PATH is a filesystem style path such as pods/spec/containers."
+  (let* ((string-parts (split-string path "/+"))
+         (resource (car string-parts)))
+    (if (= (length string-parts) 1)
+        (seq-filter
+         (lambda (e)
+           (string-prefix-p path e))
+         (kubedoc--resource-completion-table-cached))
+      (seq-uniq
+       (mapcar
+        (lambda (e)
+          (let* ((trimmed (string-remove-prefix (file-name-directory path) e))
+                 (end (or (string-match-p "/" trimmed) (- (length trimmed) 1))))
+            (substring trimmed 0 (+ end 1))))
+        (seq-filter
+         (lambda (e)
+           (string-prefix-p path e))
+         (kubedoc--field-completion-table-cached resource)))
+       #'string-equal))))
+
+(defun kubedoc--completion-sort (collection)
+  "Completion candidate list sorting of COLLECTION.
+Sorts alphabetically with parent fields on top."
+  (seq-sort
+   (lambda (a b)
+     (let ((a-parent-p (string= (substring a -1) "/"))
+           (b-parent-p (string= (substring b -1) "/")))
+       (if (equal a-parent-p b-parent-p)
+           (string< a b)
+         a-parent-p)))
+   collection))
+
+(defun kubedoc--completion-function (string pred action)
+  "Completion for Kubernetes API resources.
+See argument STRING PRED ACTION descriptions in command `try-completion'."
+  (cond
+   ((eq action 'metadata)
+    '(metadata (category . kubedoc)
+      (display-sort-function . kubedoc--completion-sort)))
+   ((eq (car-safe action) 'boundaries)
+    (let* ((start (length (file-name-directory string)))
+           (end (string-match-p "/" (cdr action))))
+      `(boundaries ,start . ,end)))
+   (t
+    (let ((candidate
+           (if (string-equal string "")
+               string
+             (string-remove-prefix (or (file-name-directory string) "") string))))
+      (complete-with-action action (kubedoc--completion-table string) candidate pred)))))
+
+(defun kubedoc--view-resource (resource &rest field)
+  "Display Kubernetes api documentation for RESOURCE and optionally FIELD."
+  (let* ((path (append (list resource) field))
+         (prev-buffer (current-buffer))
+         (buffer (concat "*Kubernetes Docs <" (string-join path "/") ">*")))
+    (unless (get-buffer buffer)
+      (with-current-buffer (get-buffer-create buffer)
+        (kubedoc--kubectl-command "explain" (string-join path "."))
+        (when field
+          (goto-char (point-max))
+          (insert "\n")
+          (insert-text-button "Navigate up" 'action (lambda (_button) (kubedoc-up)))
+          (insert "  ")
+          (insert-text-button "Navigate to top" 'action (lambda (_button) (kubedoc-top)))
+          (goto-char (point-min)))
+        (untabify (point-min) (point-max))
+        (set-buffer-modified-p nil)
+        (kubedoc-mode)
+        (setq-local kubedoc--buffer-path path)))
+    (if (with-current-buffer prev-buffer (equal major-mode 'kubedoc-mode))
+        (pop-to-buffer-same-window buffer)
+      (pop-to-buffer buffer))))
+
+(defun kubedoc--resource-path-canonical (resource &rest field)
+  "Return canonical path for Kubernetes RESOURCE and optionally FIELD.
+Paths are suffixed with a `/' if they contain any child fields."
+  (let* ((path (string-join (append (list resource) field) "/"))
+         (path-trailing-slash (concat path "/")))
+    (if (kubedoc--completion-table path-trailing-slash) path-trailing-slash path)))
+
+
+;;;; Interactive commands
+
+;;;###autoload
+(defun kubedoc ()
+  "Show Kubernetes API documentation.
+Uses current context."
+  (interactive)
+  (let* ((current-path (if-let ((current kubedoc--buffer-path))
+                           (apply #'kubedoc--resource-path-canonical current)
+                         ""))
+         (path (completing-read
+                "Kubernetes resource: " #'kubedoc--completion-function
+                nil nil current-path)))
+    (apply #'kubedoc--view-resource (split-string (string-trim-right path "/+") "/"))))
+
+;;;###autoload
+(defun kubedoc-for-context ()
+  "Show Kubernetes API documentation.
+Prompts for changing current context."
+  (interactive)
+  (kubedoc-set-context)
+  (kubedoc))
+
+(defun kubedoc-up ()
+  "Navigate to parent field of current Kubernetes resource."
+  (interactive)
+  (let ((parts (buffer-local-value 'kubedoc--buffer-path (current-buffer))))
+    (when (> (length parts) 1)
+      (apply #'kubedoc--view-resource (butlast kubedoc--buffer-path)))))
+
+(defun kubedoc-top ()
+  "Navigate to top of current Kubernetes resource."
+  (interactive)
+  (let ((parts (buffer-local-value 'kubedoc--buffer-path (current-buffer))))
+    (when (> (length parts) 1)
+      (apply #'kubedoc--view-resource (list (car kubedoc--buffer-path))))))
+
+(defun kubedoc-set-context ()
+  "Set current kubeconfig context."
+  (interactive)
+  (let* ((contexts (kubedoc--kubectl-contexts))
+         (context (completing-read "Kubernetes context: " contexts)))
+    ;; Invalidate cache if context is changed
+    (unless (string= context kubedoc--current-context)
+      (kubedoc-invalidate-cache))
+    (setq kubedoc--current-context context)))
+
+(defun kubedoc-invalidate-cache ()
+  "Invalidate kubedoc completion cache."
+  (interactive)
+  (setq kubedoc--field-completion-table-cache (make-hash-table :test 'equal))
+  (setq kubedoc--resource-completion-table-cache nil))
+
+
+;;;; Mode
+(define-derived-mode kubedoc-mode special-mode "kubedoc"
+  "Major mode for displaying Kubernetes api documentation."
+  (setq buffer-auto-save-file-name nil
+        truncate-lines t
+        font-lock-defaults '((kubedoc-font-lock-defaults))
+        imenu-generic-expression kubedoc-imenu-generic-expression)
+  (view-mode)
+  (auto-fill-mode -1)
+  (goto-char (point-min))
+  (kubedoc--highlight-field-links)
+  (goto-char (point-min)))
+
+
+(provide 'kubedoc)
+;;; kubedoc.el ends here