From 50f882e54177645503bd9a9f7ffcfcd2dfae8ed8 Mon Sep 17 00:00:00 2001 From: Andreas Gerler Date: Fri, 27 Dec 2024 12:20:14 +0100 Subject: [PATCH] update kubed and kubedoc Signed-off-by: Andreas Gerler --- elpa/kubed-0.4.2.signed | 1 + elpa/kubed-0.4.2/.elpaignore | 3 + elpa/kubed-0.4.2/LICENSE | 201 + elpa/kubed-0.4.2/NEWS.org | 130 + elpa/kubed-0.4.2/cobra.el | 143 + elpa/kubed-0.4.2/dir | 18 + elpa/kubed-0.4.2/kubed-autoloads.el | 494 +++ elpa/kubed-0.4.2/kubed-common.el | 25 + elpa/kubed-0.4.2/kubed-pkg.el | 2 + elpa/kubed-0.4.2/kubed-tramp.el | 102 + elpa/kubed-0.4.2/kubed-transient.el | 585 +++ elpa/kubed-0.4.2/kubed.el | 3510 +++++++++++++++++ elpa/kubed-0.4.2/kubed.info | 754 ++++ elpa/kubed-0.4.2/kubed.texi | 789 ++++ .../kubedoc-autoloads.el | 34 + elpa/kubedoc-20240108.1404/kubedoc-pkg.el | 10 + elpa/kubedoc-20240108.1404/kubedoc.el | 363 ++ 17 files changed, 7164 insertions(+) create mode 100644 elpa/kubed-0.4.2.signed create mode 100644 elpa/kubed-0.4.2/.elpaignore create mode 100644 elpa/kubed-0.4.2/LICENSE create mode 100644 elpa/kubed-0.4.2/NEWS.org create mode 100644 elpa/kubed-0.4.2/cobra.el create mode 100644 elpa/kubed-0.4.2/dir create mode 100644 elpa/kubed-0.4.2/kubed-autoloads.el create mode 100644 elpa/kubed-0.4.2/kubed-common.el create mode 100644 elpa/kubed-0.4.2/kubed-pkg.el create mode 100644 elpa/kubed-0.4.2/kubed-tramp.el create mode 100644 elpa/kubed-0.4.2/kubed-transient.el create mode 100644 elpa/kubed-0.4.2/kubed.el create mode 100644 elpa/kubed-0.4.2/kubed.info create mode 100644 elpa/kubed-0.4.2/kubed.texi create mode 100644 elpa/kubedoc-20240108.1404/kubedoc-autoloads.el create mode 100644 elpa/kubedoc-20240108.1404/kubedoc-pkg.el create mode 100644 elpa/kubedoc-20240108.1404/kubedoc.el diff --git a/elpa/kubed-0.4.2.signed b/elpa/kubed-0.4.2.signed new file mode 100644 index 00000000..d8ef617c --- /dev/null +++ b/elpa/kubed-0.4.2.signed @@ -0,0 +1 @@ +Good signature from 645357D2883A0966 GNU ELPA Signing Agent (2023) (trust undefined) created at 2024-10-22T11:05:01+0200 using EDDSA \ No newline at end of file diff --git a/elpa/kubed-0.4.2/.elpaignore b/elpa/kubed-0.4.2/.elpaignore new file mode 100644 index 00000000..206be027 --- /dev/null +++ b/elpa/kubed-0.4.2/.elpaignore @@ -0,0 +1,3 @@ +.dir-locals.el +README.md +*.png diff --git a/elpa/kubed-0.4.2/LICENSE b/elpa/kubed-0.4.2/LICENSE new file mode 100644 index 00000000..77abeb3a --- /dev/null +++ b/elpa/kubed-0.4.2/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2024 Eshel Yaron + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/elpa/kubed-0.4.2/NEWS.org b/elpa/kubed-0.4.2/NEWS.org new file mode 100644 index 00000000..57ee664f --- /dev/null +++ b/elpa/kubed-0.4.2/NEWS.org @@ -0,0 +1,130 @@ +#+title: Kubed NEWS -- history of user-visible changes +#+author: Eshel Yaron +#+email: me@eshelyaron.com +#+language: en +#+options: ':t toc:nil num:nil ^:{} + +This file contains the release notes for Kubed, a rich Emacs interface +for Kubernetes. + +For further details, see the Kubed manual: +[[https://eshelyaron.com/sweep.html][https://eshelyaron.com/kubed.html]]. + +* Version 0.4.2 on 2024-10-22 + +** New user option 'kubed-list-mode-line-format' + +This user option lets you customize the indicator that Kubed shows in +the mode line of resource table buffers when update is in progress. + +** Fix Tramp method autoload issue + +This version arranges for the Kubed Tramp integration to be loaded +earlier, which circumvents the "Method ‘kubedv1’ is not known" error +that some users have experienced. + +* Version 0.4.1 on 2024-09-23 + +** New command and transient menu for scaling deployments + +You can now scale Kubernetes deployment with Kubed, directly with +~kubed-scale-deployment~, from the deployments table buffer, or via +the new transient menu ~kubed-transient-scale-deployment~. + +** New commands and transient menu for managing Kubernetes API proxies + +You can now use Kubed to create proxy servers that give access to the +Kubernetes API server. The commands ~kubed-proxy~ and +~kubed-stop-proxy~ start and stop proxy servers, respectively. The +new transient menu ~kubed-transient-proxy~ lets specify options when +starting a proxy, such as the local port to use. + +* Version 0.4.0 on 2024-08-23 + +** ~kubed-list-update~ is now bound to ~g~ in resource table buffers. + +Previously, this command was bound to the slightly less convenient +capital ~G~. This shadows the default binding of ~revert-buffer~, +which you can still invoke via ~C-x x g~. + +** Introduce resource type specific bindings in resource table help transients. + +The help transient menu that you get with ~?~ in resource table +buffers now includes type-specific bindings, such as the ~F~ key for +forwarding a local port to a pod in the Kubed pods buffer. + +** New transient menu ~kubed-transient-logs~ for showing container logs. + +** Improved Tramp integration. + +Kubed now uses a bespoke Tramp method to open Dired and Shell buffers +inside Kubernetes pods. The main advantage of this method is the +ability to work with multiple ~kubectl~ contexts simultaneously. This +method requires Tramp 2.7 or later, Kubed will complain if you try to +start remote Dired or Shell with an earlier Tramp version. + +* Version 0.3.2 on 2024-08-15 + +** Improved support for multiple contexts and namespaces + +Each buffer now has a local context and namespace, which acts as the +default/current context and namespace when you invoke Kubed commands +from that buffer. See new manual section "Context and Namespace" for +more details. + +** New filter operators ~<~ and ~>~ in resource tables. + +These operators let you filter line with columns that are less than or +greater than a given value. + +** New command for applying changes to displayed resource. + +You can now edit displayed resource YAML buffers after you select a +resource with ~RET~ in the table buffer, and apply your changes to the +live resource with ~C-c C-c~. + +* Version 0.3.1 on 2024-08-06 + +** New command for fitting column width to content in resource tables. + +** New transient menu showing help in resource table buffers. + +* Version 0.3.0 on 2024-08-05 + +** New command for copying name of resource at point to kill ring. + +** New commands for running ~kubectl~ command on resource at point. + +** Support bookmarking resource list and resource details buffers. + +** New command for jumping from resource details buffer to list buffer. + +** Fix installation on Doom Emacs. + +** New transient menu for displaying resources. + +** Support multiple resource list buffers with different namespaces or contexts. + +* Version 0.2.0 on 2024-08-02 + +** Ensure compatibility with Emacs 29. + +** Add right-click context menus in resource tables. + +** New menu-bar "Kubernetes" menu. + +** New commands for restarting and watching deployments. + +** Add filtering capability to all resource tables. + +** New cron job commands: suspend, resume, and start job now. + +** New commands for patching resources. + +** New useful columns in services table and deployments table. + +** Use multi-column layout in transient menus. + +* Version 0.1.0 on 2024-07-28 + +Initial release. diff --git a/elpa/kubed-0.4.2/cobra.el b/elpa/kubed-0.4.2/cobra.el new file mode 100644 index 00000000..91c975a6 --- /dev/null +++ b/elpa/kubed-0.4.2/cobra.el @@ -0,0 +1,143 @@ +;;; cobra.el --- Complete Cobra command lines -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Eshel Yaron +;; Keywords: tools + +;;; Commentary: + +;; Cobra is a popular Golang framework for CLI programs. This library +;; defines function `cobra-read-command-line', which helps you read a +;; command line for a program that uses Cobra, with completion. +;; Prominent examples of Cobra programs are `kubectl' and `docker'. + +;;; Code: + +(defvar cobra--cache nil) + +(defun cobra-completion-table (executable s p a) + "Completion table for command lines that invoke EXECUTABLE. + +Perform completion action A on string S with predicate P." + (let ((start 0)) + (while (string-match "[[:space:]=]" s start) + (setq start (match-end 0))) + (if (eq a 'metadata) + `(metadata + (category . cobra-command-line) + (affixation-function + . ,(lambda (cands) + (let ((max (seq-max + (cons 0 (mapcar #'string-width cands))))) + (mapcar + (lambda (cand) + (list + cand "" + (if-let + ((desc (get-text-property + 0 'cobra-argument-description + cand))) + (concat + (make-string (1+ (- max (string-width cand))) ?\s) + (propertize desc 'face 'completions-annotations)) + ""))) + cands))))) + (let* ((lines + (cdr + (if (string= s (car cobra--cache)) + ;; Cache hit. + cobra--cache + (setq + cobra--cache + (cons s + (apply #'process-lines-ignore-status + executable "__complete" + (let ((args (cdr (split-string-and-unquote s)))) + (if (string-suffix-p " " s) + ;; Restore omitted empty argument. + (nconc args '("")) + args)))))))) + (code nil) + (comps (seq-take-while + (lambda (line) + (not (and (string-match "^:\\([0-9]+\\)$" line) + (setq code (string-to-number + (match-string 1 line)))))) + lines))) + ;; `code' encodes "completion directives", as follows: + ;; #b000001: An error occurred, ignore completions. + ;; #b000010: Don't add space after completion. (TODO.) + ;; #b000100: Don't fall back to file completion. + ;; #b001000: Completions are really file extension filters. + ;; #b010000: Complete directory names. + ;; #b100000: Preserve completions order. + (when (and code (zerop (logand 1 code))) + ;; Error bit in unset, proceed. + (if (= #b100 (logand #b100 code)) + ;; No file name completion. + (if (eq (car-safe a) 'boundaries) + `(boundaries + ,start . ,(and (string-match "[[:space:]=]" (cdr a)) + (match-beginning 0))) + (let ((table + (mapcar + ;; Annotate completion candidates. + (lambda (comp) + (pcase (split-string comp "\t" t) + (`(,c ,d . ,_) + (propertize + c 'cobra-argument-description + ;; Only keep first sentence. + (car (split-string d "\\." t)))) + (`(,c . ,_) c))) + comps))) + (if a (complete-with-action a table (substring s start) p) + ;; `try-completion'. + (let ((comp (complete-with-action a table (substring s start) p))) + (if (stringp comp) (concat (substring s 0 start) comp) comp))))) + ;; File name completion. + (setq p + (cond + ((= #b1000 (logand #b1000 code)) + ;; `comps' are valid extensions. + (lambda (f) + (or (file-directory-p f) + (when (string-match "\\.[^.]*\\'" f) + (member (substring f (1+ (match-beginning 0))) + comps))))) + ((= #b10000 (logand #b10000 code)) + ;; Directory name completion. + #'file-directory-p))) + (if (eq (car-safe a) 'boundaries) + ;; Find nested boundaries. + (let* ((suf (cdr a)) + (bounds (completion-boundaries + (substring s start) #'completion-file-name-table p + (substring suf 0 (string-match "[[:space:]=]" suf))))) + `(boundaries ,(+ (car bounds) start) . ,(cdr bounds))) + (if a (complete-with-action a #'completion-file-name-table + (substring s start) p) + (let ((comp (complete-with-action a #'completion-file-name-table + (substring s start) p))) + (if (stringp comp) (concat (substring s 0 start) comp) comp)))))))))) + +;;;###autoload +(defun cobra-read-command-line (prompt initial &optional hist) + "Prompt with PROMPT for a command line starting with INITIAL. + +Optional argument HIST is the name of the history list variable to use, +if it is nil or omitted, it defaults to `shell-command-history'." + (let ((exec (car (split-string-and-unquote (car (ensure-list initial)))))) + (minibuffer-with-setup-hook + (:append (lambda () + (let ((map (make-sparse-keymap))) + ;; Ensure SPC is self-inserting. + (keymap-unset map "SPC") + (use-local-map + (make-composed-keymap map (current-local-map)))))) + (completing-read prompt (apply-partially #'cobra-completion-table exec) + nil nil initial (or hist 'shell-command-history))))) + +(provide 'cobra) +;;; cobra.el ends here diff --git a/elpa/kubed-0.4.2/dir b/elpa/kubed-0.4.2/dir new file mode 100644 index 00000000..c2a31044 --- /dev/null +++ b/elpa/kubed-0.4.2/dir @@ -0,0 +1,18 @@ +This is the file .../info/dir, which contains the +topmost node of the Info hierarchy, called (dir)Top. +The first time you invoke Info you start off looking at this node. + +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" visits the Emacs manual, etc. + + In Emacs, you can click mouse button 2 on a menu item or cross reference + to select it. + +* Menu: + +Emacs +* Kubed: (kubed). Kubernetes, Emacs, done! diff --git a/elpa/kubed-0.4.2/kubed-autoloads.el b/elpa/kubed-0.4.2/kubed-autoloads.el new file mode 100644 index 00000000..fb1201ab --- /dev/null +++ b/elpa/kubed-0.4.2/kubed-autoloads.el @@ -0,0 +1,494 @@ +;;; kubed-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; 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-")) + + +;;; 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-")) + + +;;; Generated autoloads from kubed-common.el + +(register-definition-prefixes "kubed-common" '("kubed-")) + + +;;; 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--")) + + +;;; 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-")) + +;;; End of scraped data + +(provide 'kubed-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; kubed-autoloads.el ends here diff --git a/elpa/kubed-0.4.2/kubed-common.el b/elpa/kubed-0.4.2/kubed-common.el new file mode 100644 index 00000000..a30777d3 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed-common.el @@ -0,0 +1,25 @@ +;;; kubed-common.el --- Common definitons for Kubed -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Eshel Yaron +;; Keywords: tools + +;;; Commentary: + +;; This library provides definitions that both `kubed' and `kubed-tramp' +;; require. + +;;; Code: + +(defvar kubed-tramp-method "kubedv1" ;Versioned, for compatibility. + ;; (find-file "/kubedv1:CONTEXT%NAMESPACE%POD%CONTAINER:/some/file") + "Name of the Kubed Tramp method.") + +(defcustom kubed-kubectl-program "kubectl" + "Name of `kubectl' executable to use for interacting with Kubernetes." + :type 'string + :group 'kubed) + +(provide 'kubed-common) +;;; kubed-tramp.el ends here diff --git a/elpa/kubed-0.4.2/kubed-pkg.el b/elpa/kubed-0.4.2/kubed-pkg.el new file mode 100644 index 00000000..d98849a6 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed-pkg.el @@ -0,0 +1,2 @@ +;; Generated package description from kubed.el -*- no-byte-compile: t -*- +(define-package "kubed" "0.4.2" "Kubernetes, Emacs, done!" '((emacs "29.1")) :commit "d0a0f2407575b321457f48582d04ef69471fe561" :authors '(("Eshel Yaron" . "me@eshelyaron.com")) :maintainer '("Eshel Yaron" . "~eshel/kubed-devel@lists.sr.ht") :keywords '("tools" "kubernetes" "containers") :url "https://eshelyaron.com/kubed.html") diff --git a/elpa/kubed-0.4.2/kubed-tramp.el b/elpa/kubed-0.4.2/kubed-tramp.el new file mode 100644 index 00000000..8e5ac7d5 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed-tramp.el @@ -0,0 +1,102 @@ +;;; kubed-tramp.el --- Kubed Tramp integration -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Eshel Yaron +;; Keywords: tools + +;;; Commentary: + +;; This library provides Tramp integration for Kubed. This is similar +;; to the built-in "kubernetes" Tramp method from tramp-container.el, +;; except that the Kubed method always requires the container name and +;; Kubernetes namespace to be specified, as well a `kubectl' context. +;; In other words, the filename syntax of this method is fully explicit. +;; An explicit syntax is useful when juggling between different contexts +;; and namespaces with Kubed; on the other hand, for finding a remote +;; file with C-x C-f, the concise syntax of the built-in "kubernetes" +;; method is probably more convenient. + +;;; Code: + +(require 'kubed-common) +(require 'tramp) + +(defun kubed-tramp--context (vec) + "Extract the context name from a kubernetes host name in VEC." + (or (when-let ((host (and vec (tramp-file-name-host vec)))) + (nth 0 (split-string host "%"))) + "")) + +(defun kubed-tramp--namespace (vec) + "Extract the namespace from a kubernetes host name in VEC." + (or (when-let ((host (and vec (tramp-file-name-host vec)))) + (nth 1 (split-string host "%"))) + "")) + +(defun kubed-tramp--pod (vec) + "Extract the pod name from a kubernetes host name in VEC." + (or (when-let ((host (and vec (tramp-file-name-host vec)))) + (nth 2 (split-string host "%"))) + "")) + +(defun kubed-tramp--container (vec) + "Extract the container name from a kubernetes host name in VEC." + (or (when-let ((host (and vec (tramp-file-name-host vec)))) + (nth 3 (split-string host "%"))) + "")) + +;;;###autoload +(defun kubed-tramp-context (file-name) + "Extract `kubectl' context from Kubed Tramp remote file name FILE-NAME." + (nth 0 (split-string + (tramp-file-name-host (tramp-dissect-file-name file-name)) "%"))) + +;;;###autoload +(defun kubed-tramp-namespace (file-name) + "Extract Kubernetes namespace from Kubed Tramp remote file name FILE-NAME." + (nth 1 (split-string + (tramp-file-name-host (tramp-dissect-file-name file-name)) "%"))) + +;;;###autoload +(defun kubed-tramp-assert-support () + "Check if Kubed Tramp support is available, throw `user-error' if not." + (unless (assoc kubed-tramp-method tramp-methods) + (user-error "Kubed Tramp support requires Tramp version 2.7 or later"))) + +;;;###autoload +(defun kubed-tramp-enable () + "Enable Kubed integration with Tramp." + (when (boundp 'tramp-extra-expand-args) ; Tramp 2.7+ + + (setf (alist-get kubed-tramp-method tramp-methods nil nil #'string=) + `((tramp-login-program ,kubed-kubectl-program) + (tramp-login-args (("exec") + ("--context" "%x") + ("--namespace" "%y") + ("-c" "%a") + ("%h") + ("-it") + ("--") + ("%l"))) + (tramp-direct-async (,tramp-default-remote-shell "-c")) + (tramp-remote-shell ,tramp-default-remote-shell) + (tramp-remote-shell-login ("-l")) + (tramp-remote-shell-args ("-i" "-c")))) + + (connection-local-set-profile-variables + 'kubed-tramp-connection-local-default-profile + '((tramp-extra-expand-args + ?a (kubed-tramp--container (car tramp-current-connection)) + ?h (kubed-tramp--pod (car tramp-current-connection)) + ?x (kubed-tramp--context (car tramp-current-connection)) + ?y (kubed-tramp--namespace (car tramp-current-connection))))) + + (connection-local-set-profiles + `(:application tramp :protocol ,kubed-tramp-method) + 'kubed-tramp-connection-local-default-profile))) + +;;;###autoload (with-eval-after-load 'tramp (kubed-tramp-enable)) + +(provide 'kubed-tramp) +;;; kubed-tramp.el ends here diff --git a/elpa/kubed-0.4.2/kubed-transient.el b/elpa/kubed-0.4.2/kubed-transient.el new file mode 100644 index 00000000..3d2838c5 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed-transient.el @@ -0,0 +1,585 @@ +;;; kubed-transient.el --- Kubernetes transient menus -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Eshel Yaron +;; Keywords: tools + +;;; Commentary: + +;; This library extends Kubed with transient menus for various +;; Kubernetes operations. + +;;; Code: + +(require 'kubed) +(require 'transient) + +(defun kubed-transient-read-date (prompt default _history) + "Prompt with PROMPT for a date, defaulting to DEFAULT. + +Return an RFC3339 string representation of the selected date." + (require 'org) + (when (fboundp 'org-read-date) + (format-time-string "%Y-%m-%dT%H:%M:%S%:z" + (org-read-date 'with-time t nil prompt default)))) + +(defun kubed-transient-read-context (prompt _initial-input _history) + "Prompt with PROMPT for Kubernetes context." + (kubed-read-context prompt (kubed-local-context))) + +(defun kubed-transient-read-namespace (prompt _initial-input _history) + "Prompt with PROMPT for Kubernetes namespace." + (let ((context (or (seq-some (lambda (s) + (and (cl-typep s 'transient-infix) + (equal (oref s argument) "--context=") + (oref s value))) + transient--suffixes) + (kubed-local-context)))) + (kubed-read-namespace prompt (kubed--namespace context) nil context))) + +(defun kubed-transient-read-ingressclass (prompt _initial-input _history) + "Prompt with PROMPT for Kubernetes ingress class." + (kubed-read-ingressclass prompt)) + +(defun kubed-transient-read-service-and-port (prompt _initial-input _history) + "Prompt with PROMPT for Kubernetes service and port number." + (let ((service (kubed-read-service prompt))) + (concat service ":" (number-to-string (read-number "Port number: "))))) + +(defun kubed-transient-read-resource-definition-file-name + (_prompt _initial-input _history) + "Read and return Kubernetes resource definition file name." + (kubed-read-resource-definition-file-name)) + +;;;###autoload (autoload 'kubed-transient "kubed-transient" nil t) +(transient-define-prefix kubed-transient () + "Perform Kubernetes operation." + ["Kubernetes" + [ :pad-keys t + ("RET" "Display" kubed-transient-display) + ("+" "Create" kubed-transient-create) + ("*" "Apply" kubed-transient-apply) + ("D" "Delete" kubed-transient-delete)] + [("e" "Edit" kubed-transient-edit) + ("r" "Run" kubed-transient-run) + ("a" "Attach" kubed-transient-attach) + ("X" "Exec" kubed-transient-exec)] + [("l" "Logs" kubed-transient-logs) + ("d" "Diff" kubed-transient-diff) + ("P" "Patch" kubed-transient-patch) + ("R" "Rollout" kubed-transient-rollout)] + [("$" "Scale" kubed-transient-scale-deployment) + (":" "Proxy" kubed-transient-proxy) + ("E" "Explain" kubed-explain) + ("!" "Command line" kubed-kubectl-command)]]) + +(defmacro kubed-transient-logs-for-resource (resource &optional plural) + "Define transient menu for showing logs for Kubernetes RESOURCE. + +Optional argument PLURAL is the plural form of RESOURCE. If nil, it +defaults to \"RESOURCEs\"." + (let ((name (intern (concat "kubed-transient-logs-for-" resource)))) + `(transient-define-prefix ,name (&optional value) + ,(format "Show logs for a Kubernetes %s." resource) + [,(format "Kubernetes Logs for %s\n" (capitalize resource)) + ["Actions" + ("l" "Show Logs" ,(intern (concat "kubed-logs-for-" resource))) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-b" "Limit bytes" "--limit-bytes=" + :prompt "Byte limit: " :reader transient-read-number-N+) + ("-t" "Limit lines" "--tail=" + :prompt "Byte limit: " :reader transient-read-number-N+) + ("-S" "Since time" "--since-time=" + :prompt "Since time: " :reader kubed-transient-read-date)] + ["Switches" + ("-A" "All containers" "--all-containers") + ("-f" "Stream logs" "--follow") + ("-P" "Add pod and container" "--prefix") + ("-T" "Add timestamps" "--timestamps")]] + (interactive + (list (kubed-transient-args 'kubed-transient-logs))) + (transient-setup ',name nil nil + :value value + :scope '("logs" ,(concat (or plural + (concat resource "s")) + "/")))))) + +;;;###autoload (autoload 'kubed-transient-logs-for-pod "kubed-transient" nil t) +(kubed-transient-logs-for-resource "pod") + +;;;###autoload (autoload 'kubed-transient-logs-for-deployment "kubed-transient" nil t) +(kubed-transient-logs-for-resource "deployment") + +;;;###autoload (autoload 'kubed-transient-logs-for-service "kubed-transient" nil t) +(kubed-transient-logs-for-resource "service") + +;;;###autoload (autoload 'kubed-transient-logs-for-job "kubed-transient" nil t) +(kubed-transient-logs-for-resource "job") + +;;;###autoload (autoload 'kubed-transient-logs-for-replicaset "kubed-transient" nil t) +(kubed-transient-logs-for-resource "replicaset") + +;;;###autoload (autoload 'kubed-transient-logs-for-statefulset "kubed-transient" nil t) +(kubed-transient-logs-for-resource "statefulset") + +;;;###autoload (autoload 'kubed-transient-logs "kubed-transient" nil t) +(transient-define-prefix kubed-transient-logs () + "Show logs for containers running in Kubernetes." + ["Kubernetes Logs\n" + ["Kinds" + ("p" "Pod" kubed-transient-logs-for-pod) + ("d" "Deployment" kubed-transient-logs-for-deployment) + ("j" "Job" kubed-transient-logs-for-job) + ("s" "Service" kubed-transient-logs-for-service) + ("l" "Any type" kubed-logs) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-b" "Limit bytes" "--limit-bytes=" + :prompt "Byte limit: " :reader transient-read-number-N+) + ("-t" "Limit lines" "--tail=" + :prompt "Byte limit: " :reader transient-read-number-N+) + ("-S" "Since time" "--since-time=" + :prompt "Since time: " :reader kubed-transient-read-date)] + ["Switches" + ("-A" "All containers" "--all-containers") + ("-f" "Stream logs" "--follow") + ("-P" "Add pod and container" "--prefix") + ("-T" "Add timestamps" "--timestamps")]] + (interactive) + (transient-setup 'kubed-transient-logs nil nil + :scope '("logs"))) + +;;;###autoload (autoload 'kubed-transient-scale-deployment "kubed-transient" nil t) +(transient-define-prefix kubed-transient-scale-deployment () + "Scale Kubernetes deployments." + ["Kubernetes Scale Deployment\n" + ["Action" + ("$" "Scale" kubed-scale-deployment) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-r" "Replicas" "--replicas=" + :prompt "Number of replicas: " :reader transient-read-number-N+)]] + (interactive) + (transient-setup 'kubed-transient-scale-deployment nil nil + :scope '("scale" "deployment"))) + +;;;###autoload (autoload 'kubed-transient-proxy "kubed-transient" nil t) +(transient-define-prefix kubed-transient-proxy () + "Create a proxy between localhost and the Kubernetes API server." + ["Kubernetes Proxy\n" + ["Action" + (":" "Start proxy" kubed-proxy) + ("|" "Stop proxy" kubed-stop-proxy) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-a" "Address" "--address=" + :prompt "Local proxy address: ") + ("-p" "Port" "--port=" + :prompt "Local proxy port: " :reader transient-read-number-N0) + ("-w" "WWW directory" "--www=" + :prompt "Local WWW directory: " :reader transient-read-existing-directory) + ("-P" "WWW prefix" "--www-prefix=" + :prompt "WWW prefix: ") + ("-A" "API prefix" "--api-prefix=" + :prompt "API prefix: ")]] + (interactive) + (transient-setup 'kubed-transient-proxy nil nil + :scope '("proxy"))) + +;;;###autoload (autoload 'kubed-transient-rollout "kubed-transient" nil t) +(transient-define-prefix kubed-transient-rollout () + "Manage Kubernetes deployments." + ["Kubernetes Rollout\n" + ["Actions" + ("W" "Watch" kubed-watch-deployment-status) + ("R" "Restart" kubed-restart-deployment) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-rollout nil nil + :scope '("rollout"))) + +;;;###autoload (autoload 'kubed-transient-attach "kubed-transient" nil t) +(transient-define-prefix kubed-transient-attach () + "Attach to running process in container in Kubernetes pod." + ["Kubernetes Attach\n" + ["Actions" + ("a" "Attach" kubed-attach) + ("!" "Command line" kubed-kubectl-command)] + ["Switches" + ("-i" "Open stdin" "--stdin") + ("-t" "Allocate TTY" "--tty")] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-attach nil nil + :value '("--stdin" "--tty") + :scope '("attach"))) + +;;;###autoload (autoload 'kubed-transient-diff "kubed-transient" nil t) +(transient-define-prefix kubed-transient-diff () + "Display difference between Kubernetes resource definition and current state." + ["Kubernetes Diff\n" + ["Actions" + ("d" "Diff" kubed-diff) + ("!" "Command line" kubed-kubectl-command)] + ["Switches" + ("-M" "Include managed fields" "--show-managed-fields")] + ["Options" + ("-f" "Definition file" "--filename=" + :reader kubed-transient-read-resource-definition-file-name) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-diff nil nil + :scope '("diff"))) + +;;;###autoload (autoload 'kubed-transient-exec "kubed-transient" nil t) +(transient-define-prefix kubed-transient-exec () + "Execute command in Kubernetes pod." + ["Kubernetes Exec\n" + ["Actions" + ("X" "Execute" kubed-exec) + ("!" "Command line" kubed-kubectl-command)] + ["Switches" + ("-i" "Open stdin" "--stdin") + ("-t" "Allocate TTY" "--tty")] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("--" "Command" "-- =" + :prompt "Command: ")]] + (interactive) + (transient-setup 'kubed-transient-exec nil nil + :value '("--stdin" "--tty") + :scope '("exec"))) + +;;;###autoload (autoload 'kubed-transient-run "kubed-transient" nil t) +(transient-define-prefix kubed-transient-run () + "Run container image in a Kubernetes pod." + ["Kubernetes Run\n" + ["Actions" + ("r" "Run" kubed-run) + ("!" "Command line" kubed-kubectl-command)] + ["Switches" + ("-A" "Attach" "--attach") + ("-i" "Open stdin" "--stdin") + ("-t" "Allocate TTY" "--tty") + ("-R" "Remove after exit" "--rm") + ("-c" "Override container command" "--command")] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-I" "Image" "--image=" + :prompt "Image to deploy: ") + ("-p" "Port" "--port=" + :prompt "Port to expose: " :reader transient-read-number-N+) + ("-E" "Env vars" "--env=" + :prompt "Set environment VAR=VAL: " + :multi-value repeat) + ("--" "Arguments" "-- =" + :prompt "Arguments for container command: ")]] + (interactive) + (transient-setup 'kubed-transient-run nil nil + :scope '("run"))) + +;;;###autoload (autoload 'kubed-transient-apply "kubed-transient" nil t) +(transient-define-prefix kubed-transient-apply () + "Apply configuration to Kubernetes resource." + ["Kubernetes Apply\n" + ["Actions" + ("*" "Apply" kubed-apply) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-f" "Definition file" "--filename=" + :reader kubed-transient-read-resource-definition-file-name) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-apply nil nil + :scope '("apply"))) + +;;;###autoload (autoload 'kubed-transient-patch "kubed-transient" nil t) +(transient-define-prefix kubed-transient-patch () + "Apply patch to Kubernetes resource." + ["Kubernetes Patch\n" + ["Actions" + ("P" "Patch" kubed-patch) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-t" "Patch type" "--type=" + :prompt "Patch type: " + :choices ("strategic" "merge" "json"))]] + (interactive) + (transient-setup 'kubed-transient-patch nil nil + :scope '("patch"))) + +;;;###autoload (autoload 'kubed-transient-create "kubed-transient" nil t) +(transient-define-prefix kubed-transient-create () + "Create Kubernetes resource." + ["Kubernetes Create\n" + ["Kinds" + ("p" "Pod" kubed-create-pod) + ("d" "Deployment" kubed-transient-create-deployment) + ("j" "Job" kubed-transient-create-job) + ("c" "CronJob" kubed-transient-create-cronjob) + ("s" "Service" kubed-create-service)] + ["More" + :pad-keys t + ("S" "Secret" kubed-create-secret) + ("N" "Namespace" kubed-create-namespace) + ("i" "Ingress" kubed-transient-create-ingress) + ("+" "Any type" kubed-create) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-f" "Definition file" "--filename=" + :reader kubed-transient-read-resource-definition-file-name) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-create nil nil + :scope '("create"))) + +;;;###autoload (autoload 'kubed-transient-display "kubed-transient" nil t) +(transient-define-prefix kubed-transient-display () + "Display Kubernetes resource." + ["Kubernetes Display\n" + ["Kinds" + ("p" "Pod" kubed-display-pod) + ("d" "Deployment" kubed-display-deployment) + ("j" "Job" kubed-display-job) + ("c" "CronJob" kubed-display-cronjob) + ("s" "Service" kubed-display-service)] + ["More" + :pad-keys t + ("S" "Secret" kubed-display-secret) + ("N" "Namespace" kubed-display-namespace) + ("i" "Ingress" kubed-display-ingress) + ("RET" "Any type" kubed-display-resource) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-display nil nil + :scope '("get"))) + +;;;###autoload (autoload 'kubed-transient-edit "kubed-transient" nil t) +(transient-define-prefix kubed-transient-edit () + "Edit Kubernetes resource." + ["Kubernetes Edit\n" + ["Kinds" + ("p" "Pod" kubed-edit-pod) + ("d" "Deployment" kubed-edit-deployment) + ("j" "Job" kubed-edit-job) + ("c" "CronJob" kubed-edit-cronjob) + ("s" "Service" kubed-edit-service)] + ["More" + :pad-keys t + ("S" "Secret" kubed-edit-secret) + ("N" "Namespace" kubed-edit-namespace) + ("i" "Ingress" kubed-edit-ingress) + ("RET" "Any type" kubed-edit-resource) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-edit nil nil + :scope '("edit"))) + +;;;###autoload (autoload 'kubed-transient-delete "kubed-transient" nil t) +(transient-define-prefix kubed-transient-delete () + "Delete Kubernetes resource." + ["Kubernetes Delete\n" + ["Kinds" + ("p" "Pod" kubed-delete-pods) + ("d" "Deployment" kubed-delete-deployments) + ("j" "Job" kubed-delete-jobs) + ("c" "CronJob" kubed-delete-cronjobs) + ("s" "Service" kubed-delete-services)] + ["More" + :pad-keys t + ("S" "Secret" kubed-delete-secrets) + ("N" "Namespace" kubed-delete-namespaces) + ("i" "Ingress" kubed-delete-ingresses) + ("D" "Any type" kubed-delete-resources) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context)]] + (interactive) + (transient-setup 'kubed-transient-delete nil nil + :scope '("delete"))) + +;;;###autoload (autoload 'kubed-transient-create-cronjob "kubed-transient" nil t) +(transient-define-prefix kubed-transient-create-cronjob () + "Create Kubernetes cronjob." + ["Kubernetes Create CronJob\n" + ["Actions" + ("+" "Create" kubed-create-cronjob) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-I" "Image" "--image=" + :prompt "Image to run: ") + ("-S" "Schedule" "--schedule=" + :prompt "Cron schedule: ") + ("--" "Command" "-- =" + :prompt "Command: ")]] + (interactive) + (transient-setup 'kubed-transient-create-cronjob nil nil + :scope '("create" "cronjob"))) + +;;;###autoload (autoload 'kubed-transient-create-ingress "kubed-transient" nil t) +(transient-define-prefix kubed-transient-create-ingress () + "Create Kubernetes ingress." + ["Kubernetes Create Ingress\n" + ["Actions" + ("+" "Create" kubed-create-ingress) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-c" "Class" "--class=" + :prompt "Class" :reader kubed-transient-read-ingressclass) + ("-d" "Default backend service" "--default-backend=" + :prompt "Default backend service" + :reader kubed-transient-read-service-and-port) + ("-a" "Annotation" "--annotation=" + :prompt "Ingress annotations: " + :multi-value repeat) + ("-r" "Rule" "--rule=" + :prompt "Ingress rule: ")]] + (interactive) + (transient-setup 'kubed-transient-create-ingress nil nil + :scope '("create" "ingress"))) + +;;;###autoload (autoload 'kubed-transient-create-deployment "kubed-transient" nil t) +(transient-define-prefix kubed-transient-create-deployment () + "Create Kubernetes deployment." + ["Kubernetes Create Deployment\n" + ["Actions" + ("+" "Create" kubed-create-deployment) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-r" "Replicas" "--replicas=" + :prompt "Number of replicas: " :reader transient-read-number-N+) + ("-I" "Image" "--image=" + :prompt "Images to deploy: " + :multi-value repeat) + ("-p" "Port" "--port=" + :prompt "Port to expose: " :reader transient-read-number-N+) + ("--" "Command" "-- =" + :prompt "Command: ")]] + (interactive) + (transient-setup 'kubed-transient-create-deployment nil nil + :scope '("create" "deployment"))) + +;;;###autoload (autoload 'kubed-transient-create-job "kubed-transient" nil t) +(transient-define-prefix kubed-transient-create-job () + "Create Kubernetes job." + ["Kubernetes Create Job\n" + ["Actions" + ("+" "Create" kubed-create-job) + ("c" "Create from cronjob" kubed-create-job-from-cronjob) + ("!" "Command line" kubed-kubectl-command)] + ["Options" + ("-n" "Namespace" "--namespace=" + :prompt "Namespace" :reader kubed-transient-read-namespace) + ("-C" "Context" "--context=" + :prompt "Context" :reader kubed-transient-read-context) + ("-I" "Image" "--image=" + :prompt "Image to run: ") + ("--" "Command" "-- =" + :prompt "Command: ")]] + (interactive) + (transient-setup 'kubed-transient-create-job nil nil + :scope '("create" "job"))) + +;;;###autoload (autoload 'kubed-list-transient "kubed-transient" nil t) +(transient-define-prefix kubed-list-transient () + "Help for Kubernetes resource list buffers." + ;; TODO: Add a type-specific group with `:setup-children'. + ["Kubernetes Resources:" + ["Select" + :pad-keys t + ("RET" "Select" kubed-list-select-resource) + ("C-o" "Display" kubed-list-display-resource :transient t) + ("e" "Edit" kubed-list-edit :transient t) + ("w" "Copy name" kubed-list-copy-as-kill :transient t) + ("!" "Command line" kubed-list-kubectl-command :transient t)] + ["Delete" + ("D" "Delete" kubed-list-delete :transient t) + ("d" "Mark" kubed-list-mark-for-deletion :transient t) + ("u" "Unmark" kubed-list-unmark :transient t) + ("x" "Delete marked" kubed-list-delete-marked :transient t) + ("+" "Create" kubed-list-create :transient t)] + ["Table" + ("|" "Fit column" kubed-list-fit-column-width-to-content :transient t) + ("}" "Widen column" tabulated-list-widen-current-column :transient t) + ("{" "Narrow column" tabulated-list-narrow-current-column :transient t) + ("S" "Sort" tabulated-list-sort :transient t) + ("/" "Filter" kubed-list-set-filter :transient t)] + ["Movement" + :pad-keys t + ("n" "Next line" next-line :transient t) + ("p" "Previous line" previous-line :transient t) + ("TAB" "Next column" kubed-list-next-column :transient t) + ("S-TAB" "Previous column" kubed-list-previous-column :transient t) + ("g" "Update" kubed-list-update :transient t)]] + ["Type Specific" + :class transient-columns + :setup-children + (lambda (_) + (transient-parse-suffixes 'kubed-list-transient + kubed-list-transient-extra-suffixes))]) + +(provide 'kubed-transient) +;;; kubed-transient.el ends here diff --git a/elpa/kubed-0.4.2/kubed.el b/elpa/kubed-0.4.2/kubed.el new file mode 100644 index 00000000..ee0c9187 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed.el @@ -0,0 +1,3510 @@ +;;; kubed.el --- Kubernetes, Emacs, done! -*- lexical-binding: t; -*- + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; Author: Eshel Yaron +;; Maintainer: Eshel Yaron <~eshel/kubed-devel@lists.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 + " " #'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 + "" #'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-cmd) + "" '("Create" . ,crt-name) + "" '("Edit" . ,edt-name) + "" '("Delete" . ,dlt-name) + "" '("Display" . ,dsp-name) + "" '("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 "") "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 "") "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 "") "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 "") "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 "") "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 + "" '("Namespaces..." . kubed-namespace-menu-map) + "" '("Pods..." . kubed-pod-menu-map) + "" '("Persistent Volumes..." . kubed-persistentvolume-menu-map) + "" '("Services..." . kubed-service-menu-map) + "" '("Secrets..." . kubed-secret-menu-map) + "" '("Jobs..." . kubed-job-menu-map) + "" '("Deployments..." . kubed-deployment-menu-map) + "" '("Replica Sets..." . kubed-replicaset-menu-map) + "" '("Stateful Sets..." . kubed-statefulset-menu-map) + "" '("Cron Jobs..." . kubed-cronjob-menu-map) + "" '("Ingress Classes..." . kubed-ingressclass-menu-map) + "" '("Ingresses..." . kubed-ingress-menu-map) + "" '("Proxy Kubernetes API" . kubed-proxy) + "" '("Patch Resource" . kubed-patch) + "" '("Diff Config with Live" . kubed-diff) + "" '("Invoke kubectl" . kubed-kubectl-command) + "" '("Explain Type or Field" . kubed-explain) + "" '("Set Current Context" . kubed-use-context) + "" '("Run Image" . kubed-run) + "" '("Apply Config" . kubed-apply) + "" '("Create Resource" . kubed-create) + "" '("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) + " " '("Kubernetes" . kubed-menu-map)) + (keymap-global-unset " "))) + +(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-devel@lists.sr.ht>" + (format "Kubed") + '(kubed-kubectl-program) + nil nil + (propertize " " + 'display + (propertize + "Insert your bug report below. +If possible, specify where you got Emacs, kubectl and Kubed, +and include a recipe for reproducing your issue. +[This line and the above text are not included in your report.]" + 'face 'italic))))) + +(provide 'kubed) +;;; kubed.el ends here diff --git a/elpa/kubed-0.4.2/kubed.info b/elpa/kubed-0.4.2/kubed.info new file mode 100644 index 00000000..5af1a7ef --- /dev/null +++ b/elpa/kubed-0.4.2/kubed.info @@ -0,0 +1,754 @@ +This is doc5HCYE0.info, produced by makeinfo version 6.8 from +kubed.texi. + +This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes. + + Copyright © 2024 Eshel Yaron. + + Permission is granted to copy, distribute and/or modify this + document under the terms of the GNU Free Documentation License, + Version 1.3 or any later version published by the Free Software + Foundation; with no Invariant Sections, no Front-Cover Texts, and + no Back-Cover Texts. +INFO-DIR-SECTION Emacs +START-INFO-DIR-ENTRY +* Kubed: (kubed). Kubernetes, Emacs, done! +END-INFO-DIR-ENTRY + + +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:: + + +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. + + +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: + + • + • + • + + 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. + + +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:: + + +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 ’. 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 ‘’. *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 +‘’ in ‘C-c k ’: ‘C-c k p ’ invokes ‘kubed-display-pod’, +‘C-c k d ’ 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. + + +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’). +‘’ + 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’). +‘’ + 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-’ + 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:: + + +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, ‘’ completion is +available for column names and values. To clear the filter, just exit +the minibuffer with an empty input. + + +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)’. + + +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::. + + +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::. + + +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. + + +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. + + +File: doc5HCYE0.info, Node: Indices, Prev: Contributing, Up: Top + +Indices +******* + +* Menu: + +* Function Index:: +* Variable Index:: +* Keystroke Index:: +* Concept Index:: + + +File: doc5HCYE0.info, Node: Function Index, Next: Variable Index, Up: Indices + +Function index +============== + +[index] +* 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) + + +File: doc5HCYE0.info, Node: Variable Index, Next: Keystroke Index, Prev: Function Index, Up: Indices + +Variable index +============== + +[index] +* 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) + + +File: doc5HCYE0.info, Node: Keystroke Index, Next: Concept Index, Prev: Variable Index, Up: Indices + +Keystroke index +=============== + +[index] +* 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) +* : Browse Resources. (line 42) +* S: Browse Resources. (line 86) +* S-: Browse Resources. (line 82) +* : Browse Resources. (line 78) +* u: Browse Resources. (line 69) +* w: Browse Resources. (line 71) +* x: Browse Resources. (line 67) + + +File: doc5HCYE0.info, Node: Concept Index, Prev: Keystroke Index, Up: Indices + +Concept index +============= + +[index] +* 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) + + + +Tag Table: +Node: Top596 +Node: Overview1140 +Node: Getting Started1908 +Node: Usage3005 +Ref: Definition of kubed-prefix-map3616 +Ref: Definition of kubed-menu-bar-mode4379 +Node: Display Resource5014 +Node: Browse Resources9544 +Node: List Filter14571 +Node: Context and Namespace17224 +Node: Transient Menus19614 +Node: Scale Deployment21189 +Node: Extending Kubed22486 +Node: Contributing24077 +Node: Indices24777 +Node: Function Index24951 +Node: Variable Index28346 +Node: Keystroke Index29009 +Node: Concept Index31069 + +End Tag Table + + +Local Variables: +coding: utf-8 +End: diff --git a/elpa/kubed-0.4.2/kubed.texi b/elpa/kubed-0.4.2/kubed.texi new file mode 100644 index 00000000..b9b5c164 --- /dev/null +++ b/elpa/kubed-0.4.2/kubed.texi @@ -0,0 +1,789 @@ +\input texinfo @c -*- texinfo -*- +@c %**start of header +@setfilename kubed.info +@settitle Kubed: Kubernetes, Emacs, done! +@documentencoding UTF-8 +@documentlanguage en +@set MAINTAINERSITE @uref{https://eshelyaron.com,maintainer webpage} +@set MAINTAINER Eshel Yaron +@set MAINTAINEREMAIL @email{me@eshelyaron.com} +@set MAINTAINERCONTACT @uref{mailto:me@eshelyaron.com,contact the maintainer} +@c %**end of header + +@copying +This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes. + +Copyright @copyright{} 2024 Eshel Yaron. + +@quotation +Permission is granted to copy, distribute and/or modify this document +under the terms of the GNU Free Documentation License, Version 1.3 or +any later version published by the Free Software Foundation; with no +Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. +@end quotation +@end copying + +@dircategory Emacs +@direntry +* Kubed: (kubed). Kubernetes, Emacs, done! +@end direntry + +@finalout +@titlepage +@title Kubed: Kubernetes, Emacs, done! +@author Eshel Yaron (@email{me@@eshelyaron.com}) +@end titlepage + +@contents + +@ifnottex +@node Top +@top Kubed: Kubernetes, Emacs, done! + +This manual is for Kubed 0.4.2, a rich Emacs interface for Kubernetes. + +@end ifnottex + +@menu +* Overview:: Introduction to Kubed +* Getting Started:: First steps with Kubed +* Usage:: Using Kubed to interact with Kubernetes +* Extending Kubed:: Adding support for your custom resources +* Contributing:: Get involved in Kubed development +* Indices:: +@end menu + +@node Overview +@chapter Overview + +@cindex Kubed +@cindex kubed +Kubed is a rich Kubernetes interface within Emacs. It helps you work +with your Kubernetes clusters and deployments with the full power of +@command{kubectl}, and with the comfort and confidence of an intuitive +interactive interface. + +@ifhtml +@float +@image{/assets/kubed-overview,,,Image: Four different kinds of Kubed buffers in Emacs,.png} +@caption{Pods, logs, shells, YAMLs, all in one integrated interface} +@end float +@end ifhtml + +You can use Kubed to: + +@itemize +@item +Browse and manage Kubernetes workloads +@item +Connect to pods and edit files or execute commands +@item +Create new resources, edit and delete them +@item +Work with multiple clusters +@item +Get help about various Kubernetes objects +@item +@dots{} +@end itemize + +These features and others are documented in the rest of this manual, +along with many options that Kubed provides for you to customize its +behavior. + +@node Getting Started +@chapter Getting Started + +@cindex installation + +Get Kubed from GNU ELPA with @kbd{M-x package-install}, or use your +favorite Emacs package manager to install Kubed from Git. You can +clone the Kubed Git repository from any of the following locations: + +@itemize +@item +@url{https://git.sr.ht/~eshel/kubed} +@item +@url{https://github.com/eshelyaron/kubed.git} +@item +@url{git://git.eshelyaron.com/kubed.git} +@end itemize + +@cindex requirements +To get started with Kubed, all you need is @command{kubectl} and Emacs. + +Kubed tries to find @command{kubectl} in the directories listed in the +Emacs variable @code{exec-path}. When Emacs is started from a shell, it +initializes @code{exec-path} from the shell's @env{PATH} environment +variable which normally includes the location of @command{kubectl} in +common @command{kubectl} installations. If Emacs doesn't find the +@command{kubectl} executable via @code{exec-path}, you can tell Kubed +where to find it by customizing @code{kubed-kubectl-program}. + +@defopt kubed-kubectl-program +Name of @command{kubectl} executable file to use. +@end defopt + +@node Usage +@chapter Usage + +Kubed comes with a large number of Emacs command for interacting with +Kubernetes. Some commands perform one action on one resource, while +others show you a menu with many resources and let you interact with any +number of them. Some commands are specific to one type of resources, +while others act on resources of any type. There are different ways to +use Kubed, depending on your use case. + +The two most important entry points for Kubed are +@code{kubed-prefix-map} and @code{kubed-transient}: + +@anchor{Definition of kubed-prefix-map} +@cindex global prefix keymap +@cindex prefix keymap, global commands +@cindex keymap, for global commands +@defvr Keymap kubed-prefix-map +Keymap with bindings for many useful Kubed commands. +@end defvr + +The prefix keymap @code{kubed-prefix-map} gives you quick access to the +main Kubed commands. You may want to bind it to a convenient key in +your global keymap, for example: + +@lisp +(keymap-global-set "C-c k" 'kubed-prefix-map) +@end lisp + +With this binding in place, you can create a new Kubernetes resource +with @kbd{C-c k +} and attach to a running pod with @kbd{C-c k p a}. +The @kbd{C-c k} binding is just a suggestion of course, feel free to +pick any key sequence you like. + +@ifhtml +@float + +@image{/assets/kubed-prefix-map,,,Image: Emacs which-key popup for kubed-prefix-map keymap,.png} + +@image{/assets/kubed-deployment-prefix-map,,,Image: Emacs which-key popup for kubed-deployment-prefix-map,.png} + +@caption{Use @code{kubed-prefix-map} for quick access to the main Kubed commands} +@end float +@end ifhtml + +You can also access all of the commands in @code{kubed-prefix-map} via +the menu-bar @samp{Kubernetes} menu---to add this menu to your +menu-bar, enable the global minor mode @code{kubed-menu-bar-mode}: + +@anchor{Definition of kubed-menu-bar-mode} +@deffn {Minor Mode} kubed-menu-bar-mode +Add @samp{Kubernetes} menu to your menu bar. +@end deffn + +The other important entry point that Kubed provides for exploring and +performing different Kubernetes operations is @code{kubed-transient}: + +@deffn Command kubed-transient +Open the main Kubed transient menu. +@end deffn + +This command pops up the Kubed transient menu, which gives you +interactive guidance for invoking Kubed commands. +@xref{Top,,,transient}. + +The following sections describe in detail the various Kubed commands. + +@menu +* Display Resource:: +* Browse Resources:: +* Context and Namespace:: +* Transient Menus:: +* Scale Deployment:: +@end menu + +@node Display Resource +@section Display Resource + +You can view the YAML description of any resource in your Kubernetes +cluster. In Kubed, this is referred to as @dfn{displaying} the given +resource. The following commands display a Kubernetes resource: + +@deffn Command kubed-display-resource +Display a Kubernetes resource. +@end deffn + +@deffn Command kubed-display-pod +@deffnx Command kubed-display-deployment +@deffnx Command kubed-display-namespace +@deffnx Command kubed-display-@var{foo} +Display Kubernetes resource of a given type. +@end deffn + +Command @code{kubed-display-resource} can display any Kubernetes +resource. It prompts you to select a resource type and a resource +name, and displays that resource in a dedicated buffer. By default, +this command assumes you want to display a resource that is in the +current namespace, but if you invoke @code{kubed-display-resource} +with a prefix argument (@kbd{C-u}) and choose a namespaced resource +type, then it also lets you select the namespace of the resource. +With a double prefix argument, it lets you select which +@command{kubectl} context to use too. @xref{Context and Namespace}. + +If you have @code{kubed-prefix-map} bound to @kbd{C-c k}, you can +invoke @code{kubed-display-resource} with @kbd{C-c k @key{RET}}. This +command is also available from the menu-bar ``Kubernetes'' menu +(@pxref{Definition of kubed-menu-bar-mode}), and from the transient +``Kubernetes Display'' menu (@pxref{Transient Menus}). + +You can also display the resource at point in resources list buffers +by simply typing @kbd{@key{RET}}. @xref{Browse Resources}. + +Kubed also provides a dedicated @code{kubed-display-@var{foo}} command +for each supported resource type @var{foo}. These commands are just +like @code{kubed-display-resource}, except that they do not prompt you +for a type, since they are type-specific. For example, +@code{kubed-display-pod} only prompts you for a pod name, and then +displays it like @code{kubed-display-resource} does. + +You can invoke these commands by adding a type-specific key before +@kbd{@key{RET}} in @kbd{C-c k @key{RET}}: @kbd{C-c k p @key{RET}} +invokes @code{kubed-display-pod}, @kbd{C-c k d @key{RET}} invokes +@code{kubed-display-deployment}, and so on. + +When Kubed prepares a buffer for displaying a resource, it runs the +hook @code{kubed-yaml-setup-hook} right after populating the buffer +with the YAML description of the resource: + +@defopt kubed-yaml-setup-hook +List of functions to call, in order, to prepare YAML buffers. The +first function on this hook is normally the major mode you want to use +for YAML buffers that display Kubernetes resources. By default, this +is the list @code{(yaml-ts-mode view-mode)}. +@end defopt + +In addition, Kubed enables a bespoke minor mode +@code{kubed-display-resource-mode} in buffers that display Kubernetes +resources: + +@deffn {Minor Mode} kubed-display-resource-mode +Minor mode for buffers that display a Kubernetes resource. +@end deffn + +@noindent +This minor mode does the following: + +@itemize +@item +It shows the word @samp{Kubed} in the mode line, so you know you're +seeing a rendered Kubernetes resource, rather than any old YAML file. +@item +It allows you to @emph{bookmark} the resource your viewing and your +position in its YAML description with the regular Emacs bookmarks +commands (@pxref{Bookmarks,,,emacs}), namely @kbd{C-x r m}. +@item +@kindex C-c C-j +@findex kubed-display-resource-jump-to-list +It binds the key sequence @kbd{C-c C-j} to command +@code{kubed-display-resource-jump-to-list}, which pops up the +resources list buffer for the type of the displayed resource, and +takes you to the line corresponding to it. +@item +@kindex C-c C-c +@findex kubed-display-resource-replace +@kindex C-c C-= +@findex kubed-display-resource-diff +@kindex C-x C-q +@findex read-only-mode +It binds @kbd{C-c C-c} to command +@code{kubed-display-resource-replace}, which lets you apply any +changes that you make in the YAML buffer back to the resource. When +you hit @kbd{C-c C-c}, Kubed asks you for confirmation before +proceeding; you can reply with @kbd{d} (@samp{diff}) to show a diff +between the current state of the resource and the current buffer +contents. You can also display this diff anytime with @kbd{C-c C-=}. +Note that by default Kubed displays resources in read-only buffers, so +you may need to hit @kbd{C-x C-q} (@code{read-only-mode}) before +making changes in the buffer. +@item +@kindex C-x x g +@findex revert-buffer +@findex revert-buffer-quick +It lets you repopulate the buffer with updated information about the +resource with @kbd{C-x x g} (@code{revert-buffer-quick}) and +@w{@kbd{M-x revert-buffer}}. This is also useful if you edit the +resource and want to restart from a clean slate. +@end itemize + +@ifhtml +@float +@image{/assets/kubed-replace-diff,,,Image: Displaying and editing YAML of Kubernetes pod with Kubed,.png} +@caption{Type @kbd{C-x C-q} in a resource YAML buffer to edit it, @kbd{C-c C-c} to apply} +@end float +@end ifhtml + +@node Browse Resources +@section Browse Resources + +Kubed gives you an interactive list of resources for each type, that +you access with the @code{kubed-list-@var{resource}s} commands, such +as @code{kubed-list-pods}. + +These commands are bound to @kbd{l} in Kubed's type-specific prefix +keymaps, most of which are accessible via the global +@code{kubed-prefix-map} by default (@pxref{Definition of +kubed-prefix-map}). So if you've bound @code{kubed-prefix-map} to +@kbd{C-c k}, you can pop up the pods list with @kbd{C-c k p l}, the +deployments list with @kbd{C-c k d l}, the jobs list with @w{@kbd{C-c +k j l}}, etc. Normally, Kubed shows the list of resources in the +current namespace, but if you invoke these commands with a prefix +argument (say, @kbd{C-u C-c k p l}), they first prompt you to select a +namespace, and then show resources in the namespace you selected. + +The list of @var{resource}s in shown a buffer called @w{@file{*Kubed +@var{resource}s@@@var{namespace}[@var{context}]*}}, where +@var{context} is a @command{kubectl} context, and @var{namespace} is a +namespace in @var{context}. For example, Kubed shows the list of pods +in namespace @samp{default} for context @samp{minikube} in buffer +@file{*Kubed pos@@default[minikube]}. For non-namespaced resources, +the @w{@samp{@@@var{namespace}}} part is omitted. + +Each resource type has its own major mode for list buffers with +type-specific commands and operations, but they are all also derived +from a shared parent mode. The pods list uses the +@code{kubed-pods-mode} (@samp{Kubernetes Pods}) major mode, the list +of Kubernetes deployments uses @code{kubed-deployments-mode}, and so +on. + +@ifhtml +@float +@image{/assets/kubed-list-transient,,,Image: Kubed pods list buffer with the help transient menu visible,.png} +@caption{Browse your pods, deployments, and other resources with Kubed list buffers} +@end float +@end ifhtml + +The common parent major mode is @code{kubed-list-mode}: + +@deffn {Major Mode} kubed-list-mode +Major mode for listing generic Kubernetes resources. +@end deffn + +This mode provides the following commands for all type-specific +derived modes: + +@table @kbd +@kindex @key{?} +@findex kubed-list-transient +@item @key{?} +Show help in transient menu (@code{kubed-list-transient}). +@kindex @key{RET} +@findex kubed-list-select-resource +@item @key{RET} +Display the resource at point in the current window +(@code{kubed-list-select-resource}). +@kindex o +@findex kubed-list-select-resource-other-window +@item o +Display the resource at point in another window and select that window +(@code{kubed-list-select-resource-other-window}). +@kindex C-o +@findex kubed-list-display-resource +@item C-o +Display the resource at point in another window without selecting it +(@code{kubed-list-display-resource}). +@kindex D +@findex kubed-list-delete +@item D +Delete the resource at point (@code{kubed-list-delete}). +@kindex e +@findex kubed-list-edit +@item e +Edit the resource at point (@code{kubed-list-edit}). +@kindex P +@findex kubed-list-patch +@item P +Patch the resource at point (@code{kubed-list-patch}). +@kindex g +@findex kubed-list-update +@item g +Update the resources list by fetching new information from Kubernetes +(@code{kubed-list-update}). +@kindex / +@findex kubed-list-set-filter +@item / +Filter the current list of resources (@code{kubed-list-set-filter}). +@xref{List Filter}. +@kindex d +@findex kubed-list-mark-for-deletion +@item d +Mark the resource at point for deletion +(@code{kubed-list-mark-for-deletion}). Resources marked for deletion +have a @samp{D} in the beginning of their line. +@kindex x +@findex kubed-list-delete-marked +@item x +Delete marked resources (@code{kubed-list-delete-marked}). +@kindex u +@findex kubed-list-unmark +@item u +Unmark from resource at point (@code{kubed-list-unmark}). +@kindex w +@findex kubed-list-copy-as-kill +@item w +Copy name of resource at point into the kill ring +(@code{kubed-list-copy-as-kill}). +@kindex n +@findex next-line +@item n +Move point to the next line (@code{next-line}). +@kindex p +@findex previous-line +@item p +Move point to the previous line (@code{previous-line}). +@kindex @key{TAB} +@findex kubed-list-next-column +@item @key{TAB} +Move point to the next column. If point is already at the last +column, move to the first column of the next line instead +(@code{kubed-list-next-column}). +@kindex S-@key{TAB} +@findex kubed-list-previous-column +@item S-@key{TAB} +Move point to the previous column. If point is already at the first +column, move to the last column of the previous line instead +(@code{kubed-list-previous-column}). +@kindex S +@findex tabulated-list-sort +@item S +Sort lines by the column at point (@code{tabulated-list-sort}). +@kindex | +@findex kubed-list-fit-column-width-to-content +@item | +Fit width of column at point to its content +(@code{kubed-list-fit-column-width-to-content}). With non-negative +prefix argument @var{n} (for example, @kbd{0 |}), fit @var{n}th +column. With negative prefix argument (@kbd{- |}), fit all columns. +@kindex @{ +@findex tabulated-list-narrow-current-column +@item @{ +Narrow the column at point +(@code{tabulated-list-narrow-current-column}). +@kindex @} +@findex tabulated-list-widen-current-column +@item @} +Widen the column at point +(@code{tabulated-list-widen-current-column}). +@end table + +In addition, you can bookmark any Kubed resources list buffer with the +usual @kbd{C-x r m}. @xref{Bookmarks,,,emacs}. + +Note that @code{kubed-list-mode} (and its type-specific derivatives) +bind the @kbd{g} key, which is commonly bound to @code{revert-buffer}, +to @code{kubed-list-update}. The difference between these commands is +that @code{revert-buffer} only refreshes the list display, without +fetching updated data from Kubernetes. You can still invoke +@code{revert-buffer} with @kbd{C-x x g} or @kbd{M-x}, if you need it. + +@vindex kubed-list-mode-line-format +Whenever Kubed is fetching data from Kubernetes to update a resources +list buffer, the mode line of that list buffer shows the @samp{[...]} +indicator. You can set user option @code{kubed-list-mode-line-format} +to customize this indicator. + +@menu +* List Filter:: +@end menu + +@node List Filter +@subsection Filtering Resource Lists + +In all Kubernetes resource list buffers, you can use @kbd{/} +(@code{kubed-list-set-filter}) to @dfn{filter} the list of resources. +This command sets the filter of the current buffer, which specifies +conditions on resources (lines). Resources that don't satisfy the +conditions are hidden, so you can focus on those that do. + +Filters can be simple (@dfn{atomic filters}) or complex (@dfn{composed +filters}). In the simple case, you enter a filter in the minibuffer +in the format @w{@samp{@var{op} @var{col} @var{val}}}, where @var{op} +is a comparison operator, one of @samp{=}, @samp{~}, @samp{<} and +@samp{>}; @var{col} is a column name; and @var{val} is a value to +compare to values of @var{col} with @var{op}. + +If @var{op} is @samp{=}, it says to keep only lines whose @var{col} +equals @var{val}. If @var{op} is @samp{~}, it says to keep lines +whose @var{col} matches the @var{val} as a regular expression. +@samp{<} and @samp{>} compare column values to numeric values. For +example, the filter @samp{= Name foobar} keeps only resources whose +name is @samp{foobar}. To include whitespace in @var{val}, wrap +@var{val} in double quotes: @samp{= Name "foo bar"}. + +The possible values of @var{op} and their meanings are defined in +@code{kubed-list-filter-operator-alist}, which is a user option you +can use to add or change the meaning of filter operators: + +@defopt kubed-list-filter-operator-alist +Association list of filter operators and corresponding Emacs Lisp +functions. See the documentation string of this option for specifics. +@end defopt + +You can use more refined filters by composing simple filters. To add +another filter that resources must satisfy, wrap your two filters in +parentheses and put them next to each other: + +@lisp +(= Name foobar) (~ Namespace kube) +@end lisp + +@noindent +This filter keeps only resources whose name is @samp{foobar} in +namespaces that include @samp{kube} as a substring, it expresses the +conjunction of the two simple filters. + +To negate a simple filter, put a single quote ahead of it, like so: + +@lisp +(= Name foobar) '(~ Namespace kube) +@end lisp + +@noindent +This matches resources named @samp{foobar} in namespaces that do +@emph{not} contain @samp{kube}. + +To specify a disjunction of simple filters (and negated simple +filters), add another level of nesting: + +@lisp +((= Name spam) (= Name foobar)) '(~ Namespace kube) +@end lisp + +@noindent +This filter matches resources named either @samp{spam} or +@samp{foobar}, in namespaces that do not contain @samp{kube}. + +While entering a filter in the minibuffer, @kbd{@key{TAB}} completion +is available for column names and values. To clear the filter, just +exit the minibuffer with an empty input. + +@node Context and Namespace +@section Context and Namespace + +Kubed uses @command{kubectl} to interact with Kubernetes clusters, and +you can use multiple @command{kubectl} @dfn{contexts} in Kubed to work +with different Kubernetes clusters in parallel. Within each context, +Kubed lets you work in multiple @dfn{namespaces} too. In general, +each buffer has its own @dfn{local context} and @dfn{local namespace}, +and Kubed commands use these local context and namespace by default. +This is similar to how different buffers have different values of +@code{default-directory} (@pxref{File Names,,,emacs}), and how that +affects commands that interact with the filesystem. + +For Kubed buffers that display a Kubernetes resource (@pxref{Display +Resource}) or a list of resources (@pxref{Browse Resources}), the +buffer-local context and namespace are the context and namespace of +the resources the buffer displays. For non-Kubed buffers, the user +option @code{kubed-default-context-and-namespace} determines the +default local context and namespace. + +@defopt kubed-default-context-and-namespace +Default @command{kubectl} context and Kubernetes namespace for Kubed +commands. The value of this option is either a cons cell +@w{@code{(@var{context} . @var{namespace})}} or @code{nil}. If it is +@code{nil}, Kubed initializes this option to a cons cell with the +current @command{kubectl} context and namespace for that context on +the first time Kubed consults this option. +@end defopt + +As mentioned above, most Kubed commands use the local context and +namespace by default---it is also conventional for Kubed commands to +let you choose a different namespace if you invoke them with a prefix +argument (@kbd{C-u}), and to also let you choose a different context +if you invoke them with a double prefix argument (@kbd{C-u C-u}). + +The following functions return the local context and namespace for +buffer in which you call them: + +@defun kubed-local-context +Return the local @command{kubectl} context for the current buffer. +@end defun + +@defun kubed-local-namespace +Return the local Kubernetes namespace for the current buffer. +@end defun + +@defun kubed-local-context-and-namespace +Return the local context and namespace for the current buffer as a +cons cell @w{@code{(@var{context} . @var{namespace})}}. +@end defun + +@node Transient Menus +@section Transient Menus + +Kubed provides a set of keyboard-driven @dfn{transient menus} +(@pxref{Top,,,transient}) that you can use to explore and invoke +various commands. Transient menus show you available commands along +with their key bindings, which helps you use many different commands +without the need to remember the key bindings for all them. +Additionally, the transient menus allow you to specify options and +switches for the commands you invoke, which may not be available when +invoking these commands directly. For example, while you can invoke +command @code{kubed-logs} directly to view logs from a container +running in Kubernetes, the ``Kubernetes Logs'' transient menu which +you open with @code{kubed-transient-logs} lets you set a maximum +number of logs to retrieve, among other knobs and handles. + +@ifhtml +@float + +@image{/assets/kubed-transient,,,Image: The Kubed main transient menu,.png} + +@image{/assets/kubed-transient-exec,,,Image: The Kubed Exec transient menu,.png} + +@caption{Explore Kubed commands with transient menus} +@end float +@end ifhtml + +Command @code{kubed-transient} opens the Kubed ``root'' transient +menu, which is a dispatch that lets you access different transient +menus, such as the aforementioned ``Kubernetes Logs'' transient menu. +It's a good idea to bind @code{kubed-transient} to a convenient key, +either in the global keymap or under @code{kubed-prefix-map}: + +@lisp +(keymap-global-set "s-k" #'kubed-transient) +(keymap-set kubed-prefix-map "k" #'kubed-transient) +@end lisp + +In Kubed resource list buffers, type @kbd{?} to pop up a transient +menu with commands that are specific to the type of resources the +buffer displays. @xref{Browse Resources}. + +@node Scale Deployment +@section Scale Deployment + +You can use Kubed to @dfn{scale} Kubernetes a deployment, which means +setting the size of the deployment in terms of the number of replicas +it governs. + +@deffn Command kubed-scale-deployment +Scale a Kubernetes deployment. +@end deffn + +@code{kubed-scale-deployment} is bound to @kbd{$} in the +@code{kubed-deployment-prefix-map}, so if you have +@code{kubed-prefix-map} bound to @kbd{C-c k} then you can invoke it +with @kbd{C-c k d $}. @xref{Definition of kubed-prefix-map}. This +command prompts you for a deployment and then lets you select a new +number of replicas for that deployment. By default you are prompted +for deployment in the local context and namespace (@pxref{Context and +Namespace}); with a prefix argument, you can specify a different +namespace; with a double prefix argument (@kbd{C-u C-u}), you can +specify a different context too. + +In the deployments list buffer, you can scale the deployment at point +by simply typing @kbd{$}. @xref{Browse Resources}. + +@ifhtml +@float +@image{/assets/kubed-scale-deployments,,,Image: Help transient menu in Kubed deployments list buffer,.png} +@caption{Scale deployments with @kbd{$} in the deployments list buffer} +@end float +@end ifhtml + +Lastly, you can scale deployments using the transient menu +@code{kubed-transient-scale-deployment}, which is available from the +main Kubed transient by pressing @kbd{$}. @xref{Transient Menus}. + +@node Extending Kubed +@chapter Extending Kubed + +Kubed has built-in support for many common types of Kubernetes +resources. ``Support'' for a resource type in Kubed just means +providing common commands for working with resources of that type, +such as a command for showing the interactive list of these resources, +like @code{kubed-list-pods} for Kubernetes pods. In Kubed, the macro +@code{kubed-define-resource} is used to define these common commands +and add support for more resource types: + +@deffn Macro kubed-define-resource resource &optional properties &rest [ keyword argument ]@dots{} commands +Define commands for @var{resource}. +@end deffn + +Say you want to add support for your Kubernetes service accounts. +With @code{kubed-define-resource}, all you need to do is to add the +following line to your Emacs initialization file: + +@lisp +(kubed-define-resource serviceaccount) +@end lisp + +This line defines the following commands for you: + +@itemize +@item +@code{kubed-display-serviceaccount} +@item +@code{kubed-list-serviceaccounts} +@item +@code{kubed-edit-serviceaccount} +@item +@code{kubed-delete-serviceaccount} +@item +@code{kubed-create-serviceaccount} +@item +@code{kubed-explain-serviceaccounts} +@end itemize + +See the documentation string of @code{kubed-define-resource} +(@w{@kbd{C-h f}}) for details about the optional arguments of this +macro: the @var{properties} argument which lets you specify what Kubed +should show in the resource list buffer and the @code{commands} argument +which lets you associate additional commands with your resource type. + +@node Contributing +@chapter Contributing + +We highly appreciate all contributions, including bug reports, +patches, improvement suggestions, and general feedback! + +The best way to get in touch with the Kubed maintainers is via +@uref{https://lists.sr.ht/~eshel/kubed-devel, the Kubed mailing list}. + +@deffn Command kubed-submit-bug-report +Report a bug in Kubed to the maintainers via mail. +@end deffn + +You can use the command @kbd{M-x kubed-submit-bug-report} to easily +contact the Kubed maintainers from within Emacs. This command opens a +new buffer with a message template ready to be sent to the development +mailing list. + +@node Indices +@unnumbered Indices + +@menu +* Function Index:: +* Variable Index:: +* Keystroke Index:: +* Concept Index:: +@end menu + +@node Function Index +@unnumberedsec Function index + +@printindex fn + +@node Variable Index +@unnumberedsec Variable index + +@printindex vr + +@node Keystroke Index +@unnumberedsec Keystroke index + +@printindex ky + +@node Concept Index +@unnumberedsec Concept index + +@printindex cp + +@bye diff --git a/elpa/kubedoc-20240108.1404/kubedoc-autoloads.el b/elpa/kubedoc-20240108.1404/kubedoc-autoloads.el new file mode 100644 index 00000000..b513ff4a --- /dev/null +++ b/elpa/kubedoc-20240108.1404/kubedoc-autoloads.el @@ -0,0 +1,34 @@ +;;; kubedoc-autoloads.el --- automatically extracted autoloads (do not edit) -*- lexical-binding: t -*- +;; Generated by the `loaddefs-generate' function. + +;; This file is part of GNU Emacs. + +;;; Code: + +(add-to-list 'load-path (or (and load-file-name (directory-file-name (file-name-directory load-file-name))) (car load-path))) + + + +;;; Generated autoloads from kubedoc.el + +(autoload 'kubedoc "kubedoc" "\ +Show Kubernetes API documentation. +Uses current context." t) +(autoload 'kubedoc-for-context "kubedoc" "\ +Show Kubernetes API documentation. +Prompts for changing current context." t) +(register-definition-prefixes "kubedoc" '("kubedoc-")) + +;;; End of scraped data + +(provide 'kubedoc-autoloads) + +;; Local Variables: +;; version-control: never +;; no-byte-compile: t +;; no-update-autoloads: t +;; no-native-compile: t +;; coding: utf-8-emacs-unix +;; End: + +;;; kubedoc-autoloads.el ends here diff --git a/elpa/kubedoc-20240108.1404/kubedoc-pkg.el b/elpa/kubedoc-20240108.1404/kubedoc-pkg.el new file mode 100644 index 00000000..5d3fd406 --- /dev/null +++ b/elpa/kubedoc-20240108.1404/kubedoc-pkg.el @@ -0,0 +1,10 @@ +(define-package "kubedoc" "20240108.1404" "Kubernetes API Documentation" + '((emacs "27.1")) + :commit "aac02b096c98b83b4eaf129e6d767cf7150a6d43" :authors + '(("Dean Lindqvist Todevski" . "https://github.com/r0bobo")) + :keywords + '("docs" "help" "k8s" "kubernetes" "tools") + :url "https://github.com/r0bobo/kubedoc.el/") +;; Local Variables: +;; no-byte-compile: t +;; End: diff --git a/elpa/kubedoc-20240108.1404/kubedoc.el b/elpa/kubedoc-20240108.1404/kubedoc.el new file mode 100644 index 00000000..6cd3c479 --- /dev/null +++ b/elpa/kubedoc-20240108.1404/kubedoc.el @@ -0,0 +1,363 @@ +;;; kubedoc.el --- Kubernetes API Documentation -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2021, 2024 Dean Lindqvist Todevski +;; +;; Author: Dean Lindqvist Todevski +;; Maintainer: Dean Lindqvist Todevski +;; Keywords: docs help k8s kubernetes tools +;; Version: 1.0 +;; Homepage: https://github.com/r0bobo/kubedoc.el/ +;; Package-Requires: ((emacs "27.1")) + +;; This file is NOT part of GNU Emacs. + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; kubedoc.el provides Kubernetes API documentation in Emacs. + +;;; Code: + +;;;; Requirements + +(require 'map) +(require 'seq) +(require 'subr-x) + +;;;; Completion style + +(add-to-list 'completion-category-overrides '(kubedoc (styles partial-completion))) + +;;;; Declarations + +(defgroup kubedoc nil + "Kubernetes API documentation." + :group 'tools) + +(defcustom kubedoc-excluded-resources '("\\.metrics\\.k8s\\.io") + "List of regexps for Kubernetes resources to ignore. +Some Aggregated APIs have no docs for example." + :type 'sexp) + +(defconst kubedoc--explain-field-regex + "^\\([ \t]+\\)\\(\\w+\\)[ \t]+\\(<.*>+\\)[ \t]*\\(-required-\\)?$") + +(defconst kubedoc-imenu-generic-expression + `((nil ,kubedoc--explain-field-regex 2 kubedoc--imenu-goto-field))) + +(defconst kubedoc-font-lock-defaults + `(("^GROUP\\|^KIND\\|^VERSION\\|^RESOURCE\\|^DESCRIPTION\\|^FIELDS?" . font-lock-keyword-face) + (,kubedoc--explain-field-regex 3 font-lock-comment-face) + ("-required-" . font-lock-function-name-face))) + +(defvar kubedoc-mode-map + (let ((map (make-sparse-keymap))) + (suppress-keymap map) + (set-keymap-parent map (make-composed-keymap button-buffer-map special-mode-map)) + map)) + +(defvar kubedoc--field-completion-table-cache (make-hash-table :test 'equal)) + +(defvar kubedoc--resource-completion-table-cache nil) + +(defvar kubedoc--current-context nil) + +(defvar-local kubedoc--buffer-path nil) + +(define-button-type 'kubedoc-field + 'follow-link t + 'help-echo "mouse-2, RET: display this section" + 'action #'kubedoc--field-button-function) + + +;;;; Commands + +;;; UI + +(defun kubedoc--imenu-goto-field (_name position) + "Jump to correctly indented field POSITION." + (goto-char (+ position 3))) + +(defun kubedoc--highlight-field-links () + "Create field link buttons in current buffer." + (while (re-search-forward kubedoc--explain-field-regex nil t) + (make-button + (match-beginning 2) + (match-end 2) + 'type 'kubedoc-field + 'kubectl-section (match-string 2)))) + +(defun kubedoc--field-button-function (button) + "Follow field link in BUTTON." + (with-current-buffer (current-buffer) + (let ((field (button-get button 'kubectl-section))) + (apply #'kubedoc--view-resource (append kubedoc--buffer-path (list field)))))) + +;;; Helpers + +(defun kubedoc--kubectl-command (&rest args) + "Run kubectl with ARGS and write output to current buffer." + (when kubedoc--current-context + (push (concat "--context=" kubedoc--current-context) args)) + (let ((stderr (make-temp-file "emacs-kubedoc-kubectl-stderr-")) + (kubectl (executable-find "kubectl"))) + (unwind-protect + (when-let* ((returncode (apply #'call-process kubectl nil `(t ,stderr) nil args)) + ((/= returncode 0)) + (errmsg (with-temp-buffer + (insert-file-contents stderr) + (buffer-string)))) + (user-error (format "kubedoc.el kubectl error: %s" errmsg))) + (delete-file stderr)))) + +;;; Parse functions + +(defun kubedoc--parse-api-resources () + "Read output of `kubectl api-resources' from buffer and return as list. +Excludes resouces that match regexps in `kubedoc-excluded-resources'." + (let ((excluded-resource-p (lambda (elt) + (seq-every-p + (lambda (re) + (not (string-match-p re elt))) kubedoc-excluded-resources)))) + (thread-last + (split-string (buffer-string) nil t) + (seq-filter excluded-resource-p) + (seq-map (apply-partially #'format "%s/"))))) + +(defun kubedoc--parse-kubectl-explain-fields () + "Parse resource document from current buffer. +Buffer should contain output from `kubectl explain --recursive'. +Result is list of full paths for current resource. +Supports both OpenAPI v2 and v3 schema." + (let ((result '()) + (path '()) + (base -1)) + (goto-char (point-min)) + (while (not (eobp)) + (let ((line (thing-at-point 'line))) + (when (string-match kubedoc--explain-field-regex line) + (let* ((depth (current-indentation)) + (field (match-string 2 line))) + ;; Set base indention length from first iteration + (when (= base -1) + (setq base depth)) + (let ((position (/ (- depth base) base))) + ;; Drop all previous path elements that are + ;; deeper than the current. + (setq path (reverse (seq-subseq (reverse path) 0 position))) + (push field path) + (push (string-join (reverse path) "/") result))))) + (forward-line 1)) + ;; Filter result so that every result + ;; ends with the left field of each hierarchy. + ;; If the result contains '("kind" "metadata" "metadata/labels") + ;; the result should be '("kind" "metadata/labels") + (let ((full-path-p (lambda (elt) + (thread-last + result + (seq-some (apply-partially #'string-prefix-p (concat elt "/"))) + (not))))) + (thread-last + result + (seq-filter full-path-p) + (reverse))))) + +(defun kubedoc--kubectl-contexts () + "List available kubectl contexts." + (with-temp-buffer + (kubedoc--kubectl-command "config" "get-contexts" "--output=name") + (seq-sort #'string< (split-string (buffer-string) nil t)))) + +;;; Completion + +(defun kubedoc--resource-completion-table () + "Completion candidate list for Kubernetes resources in the cluster." + (with-temp-buffer + (kubedoc--kubectl-command "api-resources" "--output" "name") + (kubedoc--parse-api-resources))) + +(defun kubedoc--resource-completion-table-cached () + "Cached completion candidate list for Kubernetes resources in the cluster." + (if-let ((cached kubedoc--resource-completion-table-cache)) + cached + (setq kubedoc--resource-completion-table-cache (kubedoc--resource-completion-table)))) + +(defun kubedoc--field-completion-table (resource) + "Completion candidate list for all fields of Kubernetes RESOURCE." + (with-temp-buffer + (kubedoc--kubectl-command "explain" "--recursive" resource) + (thread-last + (kubedoc--parse-kubectl-explain-fields) + (seq-map (apply-partially #'format "%s/%s" resource))))) + +(defun kubedoc--field-completion-table-cached (resource) + "Cached Completion candidate list for all fields of Kubernetes RESOURCE." + (if-let (cached (map-elt kubedoc--field-completion-table-cache resource)) + cached + (map-put! kubedoc--field-completion-table-cache resource (kubedoc--field-completion-table resource)))) + +(defun kubedoc--completion-table (path) + "Completion candidate list for given Kubernetes resource and field PATH. +PATH is a filesystem style path such as pods/spec/containers." + (let* ((string-parts (split-string path "/+")) + (resource (car string-parts))) + (if (= (length string-parts) 1) + (seq-filter + (lambda (e) + (string-prefix-p path e)) + (kubedoc--resource-completion-table-cached)) + (seq-uniq + (mapcar + (lambda (e) + (let* ((trimmed (string-remove-prefix (file-name-directory path) e)) + (end (or (string-match-p "/" trimmed) (- (length trimmed) 1)))) + (substring trimmed 0 (+ end 1)))) + (seq-filter + (lambda (e) + (string-prefix-p path e)) + (kubedoc--field-completion-table-cached resource))) + #'string-equal)))) + +(defun kubedoc--completion-sort (collection) + "Completion candidate list sorting of COLLECTION. +Sorts alphabetically with parent fields on top." + (seq-sort + (lambda (a b) + (let ((a-parent-p (string= (substring a -1) "/")) + (b-parent-p (string= (substring b -1) "/"))) + (if (equal a-parent-p b-parent-p) + (string< a b) + a-parent-p))) + collection)) + +(defun kubedoc--completion-function (string pred action) + "Completion for Kubernetes API resources. +See argument STRING PRED ACTION descriptions in command `try-completion'." + (cond + ((eq action 'metadata) + '(metadata (category . kubedoc) + (display-sort-function . kubedoc--completion-sort))) + ((eq (car-safe action) 'boundaries) + (let* ((start (length (file-name-directory string))) + (end (string-match-p "/" (cdr action)))) + `(boundaries ,start . ,end))) + (t + (let ((candidate + (if (string-equal string "") + string + (string-remove-prefix (or (file-name-directory string) "") string)))) + (complete-with-action action (kubedoc--completion-table string) candidate pred))))) + +(defun kubedoc--view-resource (resource &rest field) + "Display Kubernetes api documentation for RESOURCE and optionally FIELD." + (let* ((path (append (list resource) field)) + (prev-buffer (current-buffer)) + (buffer (concat "*Kubernetes Docs <" (string-join path "/") ">*"))) + (unless (get-buffer buffer) + (with-current-buffer (get-buffer-create buffer) + (kubedoc--kubectl-command "explain" (string-join path ".")) + (when field + (goto-char (point-max)) + (insert "\n") + (insert-text-button "Navigate up" 'action (lambda (_button) (kubedoc-up))) + (insert " ") + (insert-text-button "Navigate to top" 'action (lambda (_button) (kubedoc-top))) + (goto-char (point-min))) + (untabify (point-min) (point-max)) + (set-buffer-modified-p nil) + (kubedoc-mode) + (setq-local kubedoc--buffer-path path))) + (if (with-current-buffer prev-buffer (equal major-mode 'kubedoc-mode)) + (pop-to-buffer-same-window buffer) + (pop-to-buffer buffer)))) + +(defun kubedoc--resource-path-canonical (resource &rest field) + "Return canonical path for Kubernetes RESOURCE and optionally FIELD. +Paths are suffixed with a `/' if they contain any child fields." + (let* ((path (string-join (append (list resource) field) "/")) + (path-trailing-slash (concat path "/"))) + (if (kubedoc--completion-table path-trailing-slash) path-trailing-slash path))) + + +;;;; Interactive commands + +;;;###autoload +(defun kubedoc () + "Show Kubernetes API documentation. +Uses current context." + (interactive) + (let* ((current-path (if-let ((current kubedoc--buffer-path)) + (apply #'kubedoc--resource-path-canonical current) + "")) + (path (completing-read + "Kubernetes resource: " #'kubedoc--completion-function + nil nil current-path))) + (apply #'kubedoc--view-resource (split-string (string-trim-right path "/+") "/")))) + +;;;###autoload +(defun kubedoc-for-context () + "Show Kubernetes API documentation. +Prompts for changing current context." + (interactive) + (kubedoc-set-context) + (kubedoc)) + +(defun kubedoc-up () + "Navigate to parent field of current Kubernetes resource." + (interactive) + (let ((parts (buffer-local-value 'kubedoc--buffer-path (current-buffer)))) + (when (> (length parts) 1) + (apply #'kubedoc--view-resource (butlast kubedoc--buffer-path))))) + +(defun kubedoc-top () + "Navigate to top of current Kubernetes resource." + (interactive) + (let ((parts (buffer-local-value 'kubedoc--buffer-path (current-buffer)))) + (when (> (length parts) 1) + (apply #'kubedoc--view-resource (list (car kubedoc--buffer-path)))))) + +(defun kubedoc-set-context () + "Set current kubeconfig context." + (interactive) + (let* ((contexts (kubedoc--kubectl-contexts)) + (context (completing-read "Kubernetes context: " contexts))) + ;; Invalidate cache if context is changed + (unless (string= context kubedoc--current-context) + (kubedoc-invalidate-cache)) + (setq kubedoc--current-context context))) + +(defun kubedoc-invalidate-cache () + "Invalidate kubedoc completion cache." + (interactive) + (setq kubedoc--field-completion-table-cache (make-hash-table :test 'equal)) + (setq kubedoc--resource-completion-table-cache nil)) + + +;;;; Mode +(define-derived-mode kubedoc-mode special-mode "kubedoc" + "Major mode for displaying Kubernetes api documentation." + (setq buffer-auto-save-file-name nil + truncate-lines t + font-lock-defaults '((kubedoc-font-lock-defaults)) + imenu-generic-expression kubedoc-imenu-generic-expression) + (view-mode) + (auto-fill-mode -1) + (goto-char (point-min)) + (kubedoc--highlight-field-links) + (goto-char (point-min))) + + +(provide 'kubedoc) +;;; kubedoc.el ends here -- 2.47.3