;;; kubernetes-commands.el --- Interactive commands for Kubernetes modes. -*- lexical-binding: t; -*- ;;; Commentary: ;;; Code: (require 'kubernetes-ast) (require 'kubernetes-modes) (require 'kubernetes-popups) (require 'kubernetes-props) (require 'kubernetes-state) (require 'kubernetes-utils) (autoload 'kubernetes-configmaps-delete-marked "kubernetes-configmaps") (autoload 'kubernetes-deployments-delete-marked "kubernetes-deployments") (autoload 'kubernetes-statefulsets-delete-marked "kubernetes-statefulsets") (autoload 'kubernetes-ingress-delete-marked "kubernetes-ingress") (autoload 'kubernetes-display-config "kubernetes-contexts") (autoload 'kubernetes-display-configmap "kubernetes-configmaps") (autoload 'kubernetes-display-deployment "kubernetes-deployments") (autoload 'kubernetes-display-statefulset "kubernetes-statefulsets") (autoload 'kubernetes-display-ingress "kubernetes-ingress") (autoload 'kubernetes-display-node "kubernetes-nodes") (autoload 'kubernetes-display-job "kubernetes-jobs") (autoload 'kubernetes-display-namespace "kubernetes-namespaces") (autoload 'kubernetes-display-pod "kubernetes-pods") (autoload 'kubernetes-display-secret "kubernetes-secrets") (autoload 'kubernetes-display-service "kubernetes-services") (autoload 'kubernetes-jobs-delete-marked "kubernetes-jobs") (autoload 'kubernetes-pods-delete-marked "kubernetes-pods") (autoload 'kubernetes-secrets-delete-marked "kubernetes-secrets") (autoload 'kubernetes-services-delete-marked "kubernetes-services") (autoload 'kubernetes-show-pods-for-label "kubernetes-labels") ;; Mark management ;;;###autoload (defun kubernetes-mark-for-delete (point) "Mark the thing at POINT for deletion, then advance to the next line." (interactive "d") (pcase (get-text-property point 'kubernetes-nav) (`(:pod-name ,name) (kubernetes-state-mark-pod name)) (`(:configmap-name ,name) (kubernetes-state-mark-configmap name)) (`(:ingress-name ,name) (kubernetes-state-mark-ingress name)) (`(:job-name ,name) (kubernetes-state-mark-job name)) (`(:secret-name ,name) (kubernetes-state-mark-secret name)) (`(:service-name ,name) (kubernetes-state-mark-service name)) (`(:deployment-name ,name) (kubernetes-state-mark-deployment name)) (`(:statefulset-name ,name) (kubernetes-state-mark-statefulset name)) (_ (user-error "Nothing here can be marked"))) (let ((inhibit-read-only t)) (kubernetes-ast-put-delete-mark-on-line-at-pt point)) (magit-section-forward)) ;;;###autoload (defun kubernetes-unmark (point) "Unmark the thing at POINT, then advance to the next line." (interactive "d") (pcase (get-text-property point 'kubernetes-nav) (`(:pod-name ,name) (kubernetes-state-unmark-pod name)) (`(:configmap-name ,name) (kubernetes-state-unmark-configmap name)) (`(:ingress-name ,name) (kubernetes-state-unmark-ingress name)) (`(:job-name ,name) (kubernetes-state-unmark-job name)) (`(:secret-name ,name) (kubernetes-state-unmark-secret name)) (`(:service-name ,name) (kubernetes-state-unmark-service name)) (`(:deployment-name ,name) (kubernetes-state-unmark-deployment name)) (`(:statefulset-name ,name) (kubernetes-state-unmark-statefulset name))) (kubernetes-state-trigger-redraw) (goto-char point) (magit-section-forward)) ;;;###autoload (defun kubernetes-unmark-all () "Unmark everything in the buffer." (interactive) (kubernetes-state-unmark-all) (let ((pt (point))) (kubernetes-state-trigger-redraw) (goto-char pt))) ;;;###autoload (defun kubernetes-execute-marks () "Action all marked items in the buffer." (interactive) (let ((state (kubernetes-state))) (let ((n (length (kubernetes-state-marked-pods state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s pod%s? " n (if (equal 1 n) "" "s")))) (kubernetes-pods-delete-marked state))) (let ((n (length (kubernetes-state-marked-configmaps state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s configmap%s? " n (if (equal 1 n) "" "s")))) (kubernetes-configmaps-delete-marked state))) (let ((n (length (kubernetes-state-marked-ingress state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s ingress%s? " n (if (equal 1 n) "" "s")))) (kubernetes-ingress-delete-marked state))) (let ((n (length (kubernetes-state-marked-secrets state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s secret%s? " n (if (equal 1 n) "" "s")))) (kubernetes-secrets-delete-marked state))) (let ((n (length (kubernetes-state-marked-deployments state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s deployment%s? " n (if (equal 1 n) "" "s")))) (kubernetes-deployments-delete-marked state))) (let ((n (length (kubernetes-state-marked-statefulsets state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s statefulsets%s? " n (if (equal 1 n) "" "s")))) (kubernetes-statefulsets-delete-marked state))) (let ((n (length (kubernetes-state-marked-jobs state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s job%s? " n (if (equal 1 n) "" "s")))) (kubernetes-jobs-delete-marked state))) (let ((n (length (kubernetes-state-marked-services state)))) (when (and (not (zerop n)) (y-or-n-p (format "Delete %s service%s? " n (if (equal 1 n) "" "s")))) (kubernetes-services-delete-marked state)))) (kubernetes-unmark-all)) ;; Misc commands ;;;###autoload (defun kubernetes-copy-thing-at-point (point) "Perform a context-sensitive copy action. Inspects the `kubernetes-copy' text property at POINT to determine what to copy." (interactive "d") (when-let (s (get-text-property point 'kubernetes-copy)) (kill-new s) ;; Print a user-friendly message for feedback. (let ((n-lines 1) (first-line nil)) (with-temp-buffer (insert s) (goto-char (point-min)) (setq first-line (buffer-substring (line-beginning-position) (line-end-position))) (while (search-forward "\n" nil t) (setq n-lines (1+ n-lines)))) (let ((ellipsized (kubernetes-utils-ellipsize first-line 70))) (if (< 1 n-lines) (message "Copied %s lines, starting with: %s" n-lines ellipsized) (message "Copied: %s" ellipsized)))))) ;;;###autoload (defun kubernetes-refresh (&optional verbose) "Force Kubernetes buffers to redraw. With optional argument VERBOSE, log status changes." (interactive "p") (run-hook-with-args 'kubernetes-poll-hook verbose) (kubernetes-state-trigger-redraw)) ;;;###autoload (defun kubernetes-navigate (point state) "Perform a context-sensitive navigation action. STATE is the current application state. Inspecs the `kubernetes-nav' text property at POINT to determine how to navigate. If that property is not found, attempt to toggle the magit section at point." (interactive (list (point) (kubernetes-state))) (pcase (get-text-property point 'kubernetes-nav) (:display-config (kubernetes-display-config (alist-get 'config state))) (`(:configmap-name ,configmap-name) (kubernetes-display-configmap configmap-name state)) (`(:service-name ,service-name) (kubernetes-display-service service-name state)) (`(:deployment-name ,deployment-name) (kubernetes-display-deployment deployment-name state)) (`(:statefulset-name ,statefulset-name) (kubernetes-display-statefulset statefulset-name state)) (`(:ingress-name ,ingress-name) (kubernetes-display-ingress ingress-name state)) (`(:node-name ,node-name) (kubernetes-display-node node-name state)) (`(:job-name ,job-name) (kubernetes-display-job job-name state)) (`(:secret-name ,secret-name) (kubernetes-display-secret secret-name state)) (`(:namespace-name ,namespace-name) (kubernetes-display-namespace namespace-name state)) (`(:pod-name ,pod-name) (kubernetes-display-pod pod-name state)) (`(:selector ,selector) (kubernetes-show-pods-for-label selector)) (_ (when-let (section (get-text-property (point) 'magit-section)) (magit-section-toggle section))))) (defun kubernetes--describable-thing-at-pt () (save-excursion (back-to-indentation) (get-text-property (point) 'kubernetes-nav))) ;;;###autoload (defun kubernetes-describe-dwim (thing) "Describe the thing at point. THING must be a valid target for `kubectl describe'." (interactive (list (kubernetes--describable-thing-at-pt))) (pcase thing (`(:pod-name ,pod-name) (kubernetes-describe-pod pod-name)) (_ (user-error "Nothing at point to describe")))) ;;;###autoload (defun kubernetes-describe-pod (pod-name) "Display a buffer for describing a pod. POD-NAME is the name of the pod to describe." (interactive (list (or (kubernetes-utils-maybe-pod-name-at-point) (kubernetes-utils-read-pod-name (kubernetes-state))))) (let ((buf (get-buffer-create kubernetes-pod-buffer-name)) (marker (make-marker))) (with-current-buffer buf (kubernetes-display-thing-mode) (let ((inhibit-read-only t)) (erase-buffer) (set-marker marker (point)) (insert (propertize "Loading..." 'face 'magit-dimmed)))) (let* ((populate-buffer (lambda (s) (with-current-buffer (marker-buffer marker) (setq-local tab-width 8) (let ((inhibit-read-only t) (inhibit-redisplay t)) (erase-buffer) (insert "---\n") (insert s) (untabify (point-min) (point-max)) (goto-char (point-min)))))) (proc (kubernetes-kubectl-describe-pod kubernetes-props (kubernetes-state) pod-name populate-buffer))) (with-current-buffer buf (add-hook 'kill-buffer-hook (lambda () (kubernetes-process-kill-quietly proc)) nil t))) (select-window (display-buffer buf)) buf)) ;;;###autoload (defun kubernetes-exec-into (pod-name args exec-command state) "Open a terminal for execting into a pod. POD-NAME is the name of the pod to exec into. ARGS are additional args to pass to kubectl. EXEC-COMMAND is the command to run in the container. STATE is the current application state. Should be invoked via command `kubernetes-logs-popup'." (interactive (let* ((state (kubernetes-state)) (pod-name (or (kubernetes-utils-maybe-pod-name-at-point) (kubernetes-utils-read-pod-name state))) (command (let ((cmd (string-trim (read-string (format "Command (default: %s): " kubernetes-default-exec-command) nil 'kubernetes-exec-history)))) (if (string-empty-p cmd) kubernetes-default-exec-command cmd)))) (list pod-name (kubernetes-exec-arguments) command state))) (let* ((command-args (append (list "exec") (kubernetes-kubectl--flags-from-state (kubernetes-state)) args (when-let (ns (kubernetes-state-current-namespace state)) (list (format "--namespace=%s" ns))) (list pod-name exec-command))) (interactive-tty (member "-t" args)) (buf (if interactive-tty (kubernetes-utils-term-buffer-start kubernetes-exec-buffer-name kubernetes-kubectl-executable command-args) (kubernetes-utils-process-buffer-start kubernetes-exec-buffer-name #'kubernetes-mode kubernetes-kubectl-executable command-args)))) (when (and interactive-tty kubernetes-clean-up-interactive-exec-buffers) (set-process-sentinel (get-buffer-process buf) #'kubernetes-process-kill-quietly)) (select-window (display-buffer buf)))) ;; View management (defun kubernetes-commands-display-buffer-fullframe (buffer) (let ((display-fn (lambda (buffer alist) (when-let (window (or (display-buffer-reuse-window buffer alist) (display-buffer-same-window buffer alist) (display-buffer-pop-up-window buffer alist) (display-buffer-use-some-window buffer alist))) (delete-other-windows window) window)))) (display-buffer buffer (list display-fn)))) (defun kubernetes-commands-display-buffer (buffer) (let ((window (funcall kubernetes-commands-display-buffer-function buffer))) (when kubernetes-commands-display-buffer-select (select-frame-set-input-focus (window-frame (select-window window)))))) ;; Config management ;;;###autoload (defun kubernetes-set-namespace (ns state) "Set the namespace to query to NS. Overrides the namespace settings for the current context. STATE is the current application state." (interactive (let ((state (kubernetes-state))) (list (completing-read "Use namespace: " (kubernetes--namespace-names state) nil t) state))) (kubernetes-process-kill-polling-processes) (kubernetes-state-clear) (goto-char (point-min)) ;; State for the context and view should be preserved. (kubernetes-state-update-config (kubernetes-state-config state)) (kubernetes-state-update-current-namespace ns) (kubernetes-state-update-overview-sections (kubernetes-state-overview-sections state)) (kubernetes-state-trigger-redraw)) (defun kubernetes--namespace-names (state) (-let* ((config (or (kubernetes-state-namespaces state) (kubernetes-kubectl-await-on-async kubernetes-props state #'kubernetes-kubectl-get-namespaces))) ((&alist 'items items) config)) (-map (-lambda ((&alist 'metadata (&alist 'name name))) name) items))) (defun kubernetes-use-context (context) "Switch Kubernetes context refresh the pods buffer. CONTEXT is the name of a context as a string." (interactive (list (completing-read "Context: " (kubernetes--context-names (kubernetes-state)) nil t))) (kubernetes-process-kill-polling-processes) (let ((state (kubernetes-state))) (kubernetes-state-clear) (kubernetes-state-update-overview-sections (kubernetes-state-overview-sections state))) (kubernetes-state-trigger-redraw) (when-let (buf (get-buffer kubernetes-overview-buffer-name)) (with-current-buffer buf (goto-char (point-min)))) (let ((state (kubernetes-state))) (kubernetes-kubectl-config-use-context kubernetes-props state context (lambda (_) (when kubernetes-default-overview-namespace (kubernetes-set-namespace kubernetes-default-overview-namespace state)) (kubernetes-state-trigger-redraw))))) (defun kubernetes--context-names (state) (-let* ((config (or (kubernetes-state-config state) (kubernetes-kubectl-await-on-async kubernetes-props state #'kubernetes-kubectl-config-view))) ((&alist 'contexts contexts) config)) (--map (alist-get 'name it) contexts))) (provide 'kubernetes-commands) ;;; kubernetes-commands.el ends here