+++ /dev/null
-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
+++ /dev/null
-.dir-locals.el
-README.md
-*.png
+++ /dev/null
- 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.
+++ /dev/null
-#+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.
+++ /dev/null
-;;; 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
+++ /dev/null
-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!
+++ /dev/null
-;;; 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
+++ /dev/null
-;;; 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
+++ /dev/null
-;; 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")
+++ /dev/null
-;;; 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
+++ /dev/null
-;;; 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
+++ /dev/null
-;;; 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
+++ /dev/null
-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:
+++ /dev/null
-\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
--- /dev/null
+Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) <elpasignNOSPAMelpa.gnu.org> (trust undefined) created at 2025-01-21T23:05:01+0100 using EDDSA
\ No newline at end of file
--- /dev/null
+.dir-locals.el
+README.md
+*.png
--- /dev/null
+ 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-2025 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.
--- /dev/null
+#+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/kubed.html][https://eshelyaron.com/kubed.html]].
+
+* Version 0.4.3 on 2025-01-21
+
+This is a maintenance release, it includes minor improvements and one
+important bug fix:
+
+** Fix 'kubed-list-delete-marked' in non-default context
+
+Marking resources with ~d~ and deleting them with ~x~ now works also
+in resource tables of resources in non-default contexts or namespaces.
+
+* 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.
--- /dev/null
+;;; cobra.el --- Complete Cobra command lines -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2025 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
--- /dev/null
+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!
--- /dev/null
+;;; 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 returns non-nil if the first should
+sort before the second; FORMAT is a function that takes a value of PROP
+and formats it. If you provide a FORMAT function and a SORT function,
+then the SORT function gets the formatted values (the output of the
+FORMAT function), not the original values. If you want to sort based on
+the unformatted original values, you can store them as text properties
+in your FORMAT function and access them in your SORT function.
+
+ATTRS is a plist of additional attributes of the PROP column, see
+`tabulated-list-format' for available attributes.
+
+For example, if PROPERTIES is (phase \".status.phase\" 10) that means
+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
--- /dev/null
+;;; kubed-common.el --- Common definitons for Kubed -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2025 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
--- /dev/null
+;; Generated package description from kubed.el -*- no-byte-compile: t -*-
+(define-package "kubed" "0.4.3" "Kubernetes, Emacs, done!" '((emacs "29.1")) :commit "8f979a198537532ce7749951ee6a85072eeb0610" :authors '(("Eshel Yaron" . "meNOSPAMeshelyaron.com")) :maintainer '("Eshel Yaron" . "~eshel/kubed-develNOSPAMlists.sr.ht") :keywords '("tools" "kubernetes" "containers") :url "https://eshelyaron.com/kubed.html")
--- /dev/null
+;;; kubed-tramp.el --- Kubed Tramp integration -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2025 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
--- /dev/null
+;;; kubed-transient.el --- Kubernetes transient menus -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2025 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 "Line 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
--- /dev/null
+;;; kubed.el --- Kubernetes, Emacs, done! -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024-2025 Free Software Foundation, Inc.
+
+;; Author: Eshel Yaron <meNOSPAMeshelyaron.com>
+;; Maintainer: Eshel Yaron <~eshel/kubed-devel@lists.sr.ht>
+;; Keywords: tools kubernetes containers
+;; URL: https://eshelyaron.com/kubed.html
+;; Package-Version: 0.4.3
+;; 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 (cons kubed-kubectl-program
+ (append
+ (when kubed-list-context
+ (list "--context" kubed-list-context))
+ (when kubed-list-namespace
+ (list "--namespace" kubed-list-namespace))
+ (list "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 returns non-nil if the first should
+sort before the second; FORMAT is a function that takes a value of PROP
+and formats it. If you provide a FORMAT function and a SORT function,
+then the SORT function gets the formatted values (the output of the
+FORMAT function), not the original values. If you want to sort based on
+the unformatted original values, you can store them as text properties
+in your FORMAT function and access them in your SORT function.
+
+ATTRS is a plist of additional attributes of the PROP column, see
+`tabulated-list-format' for available attributes.
+
+For example, if PROPERTIES is (phase \".status.phase\" 10) that means
+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
--- /dev/null
+This is docHlL4Z1.info, produced by makeinfo version 6.8 from
+kubed.texi.
+
+This manual is for Kubed 0.4.3, a rich Emacs interface for Kubernetes.
+
+ Copyright © 2024-2025 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: docHlL4Z1.info, Node: Top, Next: Overview, Up: (dir)
+
+Kubed: Kubernetes, Emacs, done!
+*******************************
+
+This manual is for Kubed 0.4.3, 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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.info, Node: Indices, Prev: Contributing, Up: Top
+
+Indices
+*******
+
+* Menu:
+
+* Function Index::
+* Variable Index::
+* Keystroke Index::
+* Concept Index::
+
+\1f
+File: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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: docHlL4Z1.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\7f601
+Node: Overview\7f1145
+Node: Getting Started\7f1913
+Node: Usage\7f3010
+Ref: Definition of kubed-prefix-map\7f3621
+Ref: Definition of kubed-menu-bar-mode\7f4384
+Node: Display Resource\7f5019
+Node: Browse Resources\7f9549
+Node: List Filter\7f14576
+Node: Context and Namespace\7f17229
+Node: Transient Menus\7f19619
+Node: Scale Deployment\7f21194
+Node: Extending Kubed\7f22491
+Node: Contributing\7f24082
+Node: Indices\7f24782
+Node: Function Index\7f24956
+Node: Variable Index\7f28351
+Node: Keystroke Index\7f29014
+Node: Concept Index\7f31074
+\1f
+End Tag Table
+
+\1f
+Local Variables:
+coding: utf-8
+End:
--- /dev/null
+\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.3, a rich Emacs interface for Kubernetes.
+
+Copyright @copyright{} 2024-2025 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.3, 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