emacs.d/elpa/kubernetes-20200114.436/kubernetes-kubectl.el

525 lines
21 KiB
EmacsLisp
Raw Normal View History

2020-02-03 19:45:34 +01:00
;;; kubernetes-kubectl.el --- Low-level kubectl integration routines. -*- lexical-binding: t; -*-
;;; Commentary:
;;; Code:
(require 'dash)
(require 'kubernetes-process)
(require 'kubernetes-props)
(require 'kubernetes-state)
(require 'kubernetes-vars)
(autoload 'json-read-from-string "json")
(autoload 'kubernetes-utils-up-to-existing-dir "kubernetes-utils")
(defun kubernetes-kubectl--default-error-handler (props status)
(unless (kubernetes-props-overview-buffer-selected-p props)
(let* ((last-error (kubernetes-props-get-last-error props))
(last-error (concat
(or (when (listp last-error)
(alist-get 'command last-error))
"undefined command")
": "
(or (when (listp last-error)
(alist-get 'message last-error))
"undefined error")))
(process-killed-manually (string-match-p (rx bol (* space) "killed:" (* space) "9" (* space) eol) status)))
(unless process-killed-manually
(kubernetes-props-message props (string-trim-right last-error))))))
(defun kubernetes-kubectl--flags-from-state (state)
(append (when-let (ns (kubernetes-state-current-namespace state))
(list (format "--namespace=%s" ns)))
(kubernetes-state-kubectl-flags state)))
(defun kubernetes-kubectl (props state args on-success &optional on-error cleanup-cb)
"Run kubectl with ARGS.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the current application state, used to apply additional
global flags to kubectl.
ON-SUCCESS is a function of one argument, called with the process' buffer.
Optional ON-ERROR is a function of two arguments, called with the
process' stderr buffer. If omitted, it defaults to
`kubernetes-kubectl--default-error-handler', which logs an error
if the process exited unexpectedly.
Optional CLEANUP-CB is a function of no arguments that is always
called after the other callbacks. It can be used for releasing
resources.
After callbacks are executed, the process and its buffer will be killed.
Returns the process object for this execution of kubectl."
(let* ((buf (generate-new-buffer " kubectl"))
(err-buf (generate-new-buffer " kubectl-err"))
(command (append (list kubernetes-kubectl-executable) args (kubernetes-kubectl--flags-from-state state)))
;; `default-directory' must exist, otherwise `make-process' raises an
;; error.
(default-directory (kubernetes-utils-up-to-existing-dir default-directory))
(proc (make-process
:name "kubectl"
:buffer buf
:stderr err-buf
:command command
:noquery t
:sentinel
(lambda (proc status)
(unwind-protect
(let ((exit-code (process-exit-status proc)))
(cond
((zerop exit-code)
(funcall on-success buf))
(t
(let ((err-message (with-current-buffer err-buf (buffer-string))))
(unless (= 9 exit-code)
(kubernetes-props-update-last-error props err-message (string-join command " ") (current-time))))
(cond (on-error
(funcall on-error err-buf))
(t
(kubernetes-kubectl--default-error-handler props status))))))
(when cleanup-cb
(funcall cleanup-cb))
(kubernetes-process-kill-quietly proc))))))
;; Clean up stderr buffer when stdout buffer is killed.
(with-current-buffer buf
(add-hook 'kill-buffer-hook (lambda ()
(let ((kill-buffer-query-functions nil))
(ignore-errors (kill-buffer err-buf))))
nil t))
proc))
(defun kubernetes-kubectl-get-pods (props state cb &optional cleanup-cb)
"Get all pods and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "pods" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-nodes (props state cb &optional cleanup-cb)
"Get all nodes and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "nodes" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-configmaps (props state cb &optional cleanup-cb)
"Get all configmaps and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "configmaps" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-deployments (props state cb &optional cleanup-cb)
"Get all deployments and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "deployments" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-statefulsets (props state cb &optional cleanup-cb)
"Get all statefulsets and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "statefulsets" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-ingress (props state cb &optional cleanup-cb)
"Get all ingress and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "ingress" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-jobs (props state cb &optional cleanup-cb)
"Get all jobs and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "jobs" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-secrets (props state cb &optional cleanup-cb)
"Get all secrets and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "secrets" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-get-services (props state cb &optional cleanup-cb)
"Get all services and execute callback CB with the parsed JSON.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "services" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-config-view (props state cb &optional cleanup-cb)
"Get the current configuration and pass it to CB.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props
state
'("config" "view" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-config-use-context (props state context-name cb)
"Change the current kubernetes context to CONTEXT-NAME, a string.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CB is a function taking the name of the context that was switched to."
(kubernetes-kubectl props
state
(list "config" "use-context" context-name)
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "Switched to context \"" (group (+? nonl)) "\"." (* space) eol)
(buffer-string))
(funcall cb (match-string 1 (buffer-string)))))))
(defun kubernetes-kubectl-get-namespaces (props state cb &optional cleanup-cb)
"Get namespaces for the current cluster and pass the parsed response to CB.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
CLEANUP-CB is a function taking no arguments used to release any resources."
(kubernetes-kubectl props state '("get" "namespaces" "-o" "json")
(lambda (buf)
(let ((json (with-current-buffer buf
(json-read-from-string (buffer-string)))))
(funcall cb json)))
nil
cleanup-cb))
(defun kubernetes-kubectl-delete-pod (props state pod-name cb &optional error-cb)
"Delete pod with POD-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "pod" pod-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "pod/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-configmap (props state configmap-name cb &optional error-cb)
"Delete CONFIGMAP-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "configmap" configmap-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "configmap/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-ingress (props state ingress-name cb &optional error-cb)
"Delete INGRESS-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "ingress" ingress-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "ingress/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-secret (props state secret-name cb &optional error-cb)
"Delete SECRET-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "secret" secret-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "secret/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-describe-pod (props state pod-name cb)
"Describe pod with POD-NAME, then execute CB with the string response.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state."
(kubernetes-kubectl props state (list "describe" "pod" pod-name)
(lambda (buf)
(let ((s (with-current-buffer buf (buffer-string))))
(funcall cb s)))))
(defun kubernetes-kubectl-delete-service (props state service-name cb &optional error-cb)
"Delete SERVICE-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "service" service-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "service/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-deployment (props state deployment-name cb &optional error-cb)
"Delete DEPLOYMENT-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "deployment" deployment-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "deployment/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-statefulset (props state statefulset-name cb &optional error-cb)
"Delete STATEFULSET-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "statefulset" statefulset-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "statefulset/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-delete-job (props state job-name cb &optional error-cb)
"Delete JOB-NAME, then execute CB with the response buffer.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
ERROR-CB is called if an error occurred."
(kubernetes-kubectl props state (list "delete" "job" job-name "-o" "name")
(lambda (buf)
(with-current-buffer buf
(string-match (rx bol "job/" (group (+ nonl))) (buffer-string))
(funcall cb (match-string 1 (buffer-string)))))
error-cb))
(defun kubernetes-kubectl-await (command &rest callbacks)
"Apply COMMAND to list of CALLBACKS where first callback is assumed on-success.
If no callbacks called within `kubernetes-kubectl-timeout-seconds', give up,
possibly orphaning a process.
Return result of first callback if success, nil otherwise."
(let* (result
complete
sentinelized
(sentinel (lambda (&rest _args) (setq complete t))))
(mapc (lambda (f)
(when (functionp f)
(add-function :before (var f) sentinel))
(push f sentinelized))
callbacks)
(setf sentinelized (nreverse sentinelized))
(when (functionp (car sentinelized))
(add-function :around (car sentinelized)
(lambda (f &rest args)
(setq result (apply f args)))))
(cl-loop initially do (apply command sentinelized)
with ms = 500
with count = (max 1 (truncate
(/ (* 1000 kubernetes-kubectl-timeout-seconds)
ms)))
repeat count
until complete
do (sleep-for 0 ms)
finally return result)))
(defmacro kubernetes-kubectl-await-command (resource for-items)
(declare (indent defun))
"Await kubectl updating state's RESOURCE and return result of calling
FOR-ITEMS on updated RESOURCEs."
`(kubernetes-kubectl-await
(apply-partially #'kubernetes-kubectl
kubernetes-props
(kubernetes-state)
(split-string ,(format "get %s -o json" (symbol-name resource))))
(lambda (buf)
(with-current-buffer buf
(,(intern (concat "kubernetes-state-update-" (symbol-name resource)))
(json-read-from-string (buffer-string)))
(-let* (((&alist 'items)
(,(intern (concat "kubernetes-state-" (symbol-name resource)))
(kubernetes-state))))
(seq-map ,for-items items))))
nil
#'ignore))
(defun kubernetes-kubectl-await-on-async (props state fn)
"Turn an async function requiring a callback into a synchronous one.
PROPS is an alist of functions to inject. It should normally be passed
`kubernetes-props'.
STATE is the application state.
Transforms a function of type:
FN : (props, state, a -> b) -> process
to a function of the type:
FN' : () -> a"
(let* (complete result)
(funcall fn props state (lambda (response)
(setq complete t)
(setq result response)))
(while (not complete)
(sleep-for 0.001))
result))
(provide 'kubernetes-kubectl)
;;; kubernetes-kubectl.el ends here