(transient-define-prefix kubed-transient ()
"Perform Kubernetes operation."
["Kubernetes"
- ;; First column.
[ :pad-keys t
("RET" "Display" kubed-transient-display)
("+" "Create" kubed-transient-create)
("*" "Apply" kubed-transient-apply)
- ("e" "Edit" kubed-transient-edit)
("D" "Delete" kubed-transient-delete)]
- ;; Second column.
- [("r" "Run" kubed-transient-run)
+ [("e" "Edit" kubed-transient-edit)
+ ("r" "Run" kubed-transient-run)
("a" "Attach" kubed-transient-attach)
- ("X" "Exec" kubed-transient-exec)
- ("R" "Rollout" kubed-transient-rollout)]
- ;; Third column.
- [("d" "Diff" kubed-transient-diff)
+ ("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)
- ("E" "Explain" kubed-explain)
+ ("R" "Rollout" kubed-transient-rollout)]
+ [("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 Kuberenetes 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 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 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-rollout "kubed-transient" nil t)
(transient-define-prefix kubed-transient-rollout ()
"Manage Kubernetes deployments."
;;;###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
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)))
+ (interactive (list (kubed-read-resource-definition-file-name)) kubed-list-mode)
(kubed-create definition kind kubed-list-context)
(kubed-list-update t))
(while (not (eobp))
(unless (eq (char-after) ?\s)
(push (cons (tabulated-list-get-id)
- ;; Preserve mark properties.
+ ;; Preserve mark text properties.
(buffer-substring (point) (1+ (point))))
marks))
(forward-line)))
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\"."
+ followed by \"s\".
+- `:logs t': generate `kubed-logs-for-RESOURCE' command."
(declare (indent 2))
(let ((plrl-var (intern (format "%Ss" resource)))
(read-fun (intern (format "kubed-read-%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)))
- (namespaced t)
+ (logs-cmd (intern (format "kubed-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)
(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)))
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=)
(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 Kuberenetes %S %s."
+ resource (upcase (symbol-name resource)))
+ (interactive
+ (let ( ,resource context namespace
+ container follow limit prefix since tail timestamps)
+ (dolist (arg (kubed-transient-args
+ ',(intern (format "kubed-transient-logs-for-%S" resource))))
+ (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))
(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" #',(intern (format "kubed-transient-logs-for-%S" resource))))
,@(mapcan
(pcase-lambda (`(,suffix ,key ,_desc . ,_body))
(when key
(number-to-string (1+ (seq-count (lambda (c) (= c ?,)) cs)))))
:right-align t)
(starttime ".status.startTime" 20))
- :prefix (("L" "Show Logs" kubed-logs)
- ("A" "Attach" kubed-attach)
+ :logs t
+ :prefix (("A" "Attach" kubed-attach)
("X" "Execute" kubed-exec)
("F" "Forward Port" kubed-forward-port-to-pod))
(dired "C-d" "Start Dired in"
(read-string "Execute command: "))))
(kubed-exec pod (car cmd-args) container kubed-list-context
kubed-list-namespace t t (cdr cmd-args))))
- (logs "l" "Show logs for a container of"
- (kubed-logs pod (kubed-read-container pod "Container" t
- kubed-list-context kubed-list-namespace)
- kubed-list-context kubed-list-namespace))
(forward-port "F" "Forward local network port to remote port of"
(let ((local-port (read-number "Forward local port: ")))
(kubed-forward-port-to-pod
(clusterip ".spec.clusterIP" 16)
(externalip ".spec.externalIPs[*]" 16)
(ports ".spec.ports[*].port" 6)
- (creationtimestamp ".metadata.creationTimestamp" 20)))
+ (creationtimestamp ".metadata.creationTimestamp" 20))
+ :logs t)
;;;###autoload (autoload 'kubed-display-secret "kubed" nil t)
;;;###autoload (autoload 'kubed-edit-secret "kubed" nil t)
(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.
( creationtimestamp ".metadata.creationTimestamp" 20))
:prefix (("R" "Restart" kubed-restart-deployment)
("W" "Watch" kubed-watch-deployment-status))
+ :logs t
:create
((name images &optional context namespace replicas port command)
"Deploy IMAGES to Kubernetes in deployment with name NAME.
:right-align t)
(ownerkind ".metadata.ownerReferences[0].kind" 12)
(ownername ".metadata.ownerReferences[0].name" 16)
- (creationtimestamp ".metadata.creationTimestamp" 20)))
+ (creationtimestamp ".metadata.creationTimestamp" 20))
+ :logs t)
;;;###autoload (autoload 'kubed-display-statefulset "kubed" nil t)
;;;###autoload (autoload 'kubed-edit-statefulset "kubed" nil t)
:right-align t)
(ownerkind ".metadata.ownerReferences[0].kind" 12)
(ownername ".metadata.ownerReferences[0].name" 16)
- (creationtimestamp ".metadata.creationTimestamp" 20)))
+ (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."
(defun kubed-current-namespace (&optional context)
"Return current Kubernetes namespace for context CONTEXT."
+ ;; FIXME: If no namespace is configured as the default for CONTEXT,
+ ;; suggest setting one.
(car (process-lines
kubed-kubectl-program
"config" "view" "-o"
nil t nil nil default))))
;;;###autoload
-(defun kubed-logs (pod container context namespace)
- "Show logs for container CONTAINER in Kubernetes pod POD."
+(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* ((context (kubed-local-context))
- (context (if (equal current-prefix-arg '(16))
- (kubed-read-context "Context" context)
- context))
- (n (kubed--namespace context current-prefix-arg))
- (p (kubed-read-pod "Show logs for pod" nil nil context n))
- (c (kubed-read-container p "Container" nil context n)))
- (list p c context n)))
+ (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] in %s[%s]*"
- pod container namespace context))))
+ (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))
- (message "Getting logs for container `%s' in pod `%s' in namespace `%s'..."
- container pod namespace)
- (start-process "*kubed-logs*" buf
- kubed-kubectl-program "logs"
- "-f" "-c" container pod
- "-n" namespace "--context" context)
+ (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" 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