2017-07-25 15:36:07 -05:00
|
|
|
;;; ein-jupyterhub.el --- Interface to Jupyterhub -*- lexical-binding: t -*-
|
2017-04-07 08:25:37 -05:00
|
|
|
|
|
|
|
;; Copyright (C) 2016 - John Miller
|
|
|
|
|
|
|
|
;; Authors: Takafumi Arakaki <aka.tkf at gmail.com>
|
|
|
|
;; John M. Miller <millejoh at mac.com>
|
|
|
|
|
|
|
|
;; This file is NOT part of GNU Emacs.
|
|
|
|
|
|
|
|
;; ein-jupyterhub.el 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.
|
|
|
|
|
|
|
|
;; ein-jupyterhub.el 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 ein-jupyter.el. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
;;;
|
|
|
|
;;; An interface to the Jupyterhub login and management API as described in
|
|
|
|
;;; http://jupyterhub.readthedocs.io/en/latest/api/index.html
|
|
|
|
;;;
|
|
|
|
|
|
|
|
;;
|
|
|
|
|
|
|
|
;;; Code:
|
2017-07-21 12:36:07 -05:00
|
|
|
(require 'ein-query)
|
2018-02-09 14:39:29 -07:00
|
|
|
(require 'ein-contents-api)
|
2017-07-21 12:36:07 -05:00
|
|
|
|
|
|
|
(defun ein:jupyterhub-api-url (url-or-port command &rest args)
|
|
|
|
(if args
|
2017-07-21 17:25:35 -05:00
|
|
|
(apply #'ein:url url-or-port "hub/api" command args)
|
2017-07-21 12:36:07 -05:00
|
|
|
(ein:url url-or-port "hub/api" command)))
|
|
|
|
|
2017-07-25 15:36:07 -05:00
|
|
|
(defun ein:jh-ask-url-or-port ()
|
|
|
|
(let* ((url-or-port-list (mapcar (lambda (x) (format "%s" x))
|
|
|
|
ein:url-or-port))
|
|
|
|
(default (format "%s" (ein:default-url-or-port)))
|
|
|
|
(url-or-port
|
|
|
|
(completing-read (format "URL or port number (default %s): " default)
|
|
|
|
url-or-port-list
|
|
|
|
nil nil nil nil
|
|
|
|
default)))
|
|
|
|
(if (string-match "^[0-9]+$" url-or-port)
|
|
|
|
(string-to-number url-or-port)
|
|
|
|
url-or-port)))
|
2017-07-21 17:25:35 -05:00
|
|
|
|
2017-07-21 17:39:01 -05:00
|
|
|
(defun ein:jupyterhub--do-connect (url-or-port user password)
|
2017-07-20 18:25:40 -05:00
|
|
|
(deferred:$
|
|
|
|
(ein:query-deferred
|
|
|
|
(ein:jupyterhub-api-url url-or-port "/")
|
|
|
|
:type "GET"
|
|
|
|
:parser #'ein:json-read)
|
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (response)
|
|
|
|
(when (and response (request-response-data response))
|
2017-07-21 12:36:07 -05:00
|
|
|
(let ((conn (make-ein:$jh-conn :url (or (ein:get-response-redirect response)
|
|
|
|
url-or-port)
|
|
|
|
:version (plist-get (request-response-data response) :version))))
|
|
|
|
conn))))
|
2017-07-25 15:36:07 -05:00
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (conn)
|
|
|
|
(ein:jupyterhub-login conn user password)))
|
2017-07-21 12:36:07 -05:00
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (conn)
|
|
|
|
(unless conn
|
|
|
|
(error "Connection to Jupyterhub server at %s failed! Maybe you used the wrong URL?" url-or-port))
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:jupyterhub-token-request conn)))
|
2017-07-21 12:36:07 -05:00
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (conn)
|
2017-07-21 17:25:35 -05:00
|
|
|
(setf (gethash (ein:$jh-conn-url conn) *ein:jupyterhub-servers*) conn)
|
|
|
|
(ein:jupyterhub-start-server conn user)))))
|
2017-07-20 18:25:40 -05:00
|
|
|
|
2017-07-25 15:36:07 -05:00
|
|
|
(defun ein:jupyterhub-login (conn username password)
|
2017-07-21 12:36:07 -05:00
|
|
|
(deferred:$
|
|
|
|
(ein:query-deferred
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:url (ein:$jh-conn-url conn) "hub/login")
|
2017-07-21 12:36:07 -05:00
|
|
|
:type "POST"
|
|
|
|
:parser #'ein:json-read
|
2017-07-25 15:36:07 -05:00
|
|
|
:data (format "username=%s&password=%s" username password) ;; (json-encode`((username . ,username)
|
|
|
|
;; (password . , password)))
|
|
|
|
)
|
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (response)
|
|
|
|
(ein:log 'info "Login for user %s with response %s." username (request-response-status-code response))
|
|
|
|
conn))))
|
|
|
|
|
|
|
|
(defun ein:jupyterhub-token-request (conn)
|
|
|
|
(deferred:$
|
|
|
|
(ein:query-deferred
|
|
|
|
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
|
|
|
|
"authorizations/token")
|
|
|
|
:type "POST"
|
|
|
|
:timeout ein:content-query-timeout
|
|
|
|
:parser #'ein:json-read)
|
2017-07-21 12:36:07 -05:00
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (response)
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:log 'info "response-data: %s, %s"
|
2017-07-21 12:36:07 -05:00
|
|
|
(request-response-data response)
|
|
|
|
(cadr (request-response-data response))) ;; FIXME: Why doesn't plist-get work?
|
2017-07-25 15:36:07 -05:00
|
|
|
(unless (eql (request-response-status-code response) 403)
|
|
|
|
(setf (ein:$jh-conn-token conn) (cadr (request-response-data response))))
|
2017-07-21 12:36:07 -05:00
|
|
|
conn))))
|
2017-04-07 08:25:37 -05:00
|
|
|
|
2017-07-21 17:25:35 -05:00
|
|
|
(defun ein:jupyterhub-get-user (conn username)
|
|
|
|
(deferred:$
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:query-deferred
|
|
|
|
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
|
|
|
|
"users"
|
|
|
|
username)
|
2017-04-07 08:25:37 -05:00
|
|
|
:type "GET"
|
2017-07-21 17:25:35 -05:00
|
|
|
:parser #'ein:json-read)
|
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (response)
|
2017-07-25 15:36:07 -05:00
|
|
|
(let* ((data (request-response-data response))
|
|
|
|
(user (make-ein:$jh-user :name (plist-get data :name)
|
|
|
|
:admin (plist-get data :admin)
|
|
|
|
:groups (plist-get data :groups)
|
|
|
|
:server (plist-get data :server)
|
|
|
|
:pending (plist-get data :pending)
|
|
|
|
:last-activity (plist-get data :last_activity))))
|
|
|
|
(ein:log 'info "Jupyterhub: Found user: %s" user)
|
|
|
|
user)))))
|
2017-07-21 17:25:35 -05:00
|
|
|
|
|
|
|
(defun ein:jupyterhub-start-server (conn username)
|
|
|
|
(deferred:$
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:query-deferred
|
|
|
|
(ein:jupyterhub-api-url (ein:$jh-conn-url conn)
|
|
|
|
"users"
|
|
|
|
username
|
|
|
|
"server")
|
2017-04-07 08:25:37 -05:00
|
|
|
:type "POST"
|
2017-07-21 17:25:35 -05:00
|
|
|
:parser #'ein:json-read)
|
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (response)
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:log 'info "Jupyterhub: Response status: %s" (request-response-status-code response))
|
|
|
|
(case (request-response-status-code response)
|
|
|
|
((201 400)
|
|
|
|
(ein:log 'info "Jupyterhub: Finding user: %s" username)
|
|
|
|
(ein:jupyterhub-get-user conn username)))))
|
2017-07-21 17:25:35 -05:00
|
|
|
(deferred:nextc it
|
|
|
|
(lambda (user)
|
2017-07-25 15:36:07 -05:00
|
|
|
(ein:log 'info "Jupyterhub: Found user? (%s)" user)
|
2017-07-21 17:25:35 -05:00
|
|
|
(when (ein:$jh-user-p user)
|
2017-07-25 15:36:07 -05:00
|
|
|
(setf (ein:$jh-conn-user conn) user)
|
|
|
|
(ein:log 'info "Jupyterhub: Opening notebook at %s: " (ein:$jh-conn-url conn))
|
2018-10-15 16:57:22 -04:00
|
|
|
(ein:notebooklist-open* (ein:$jh-conn-url conn) nil nil #'pop-to-buffer))))))
|
2017-04-07 08:25:37 -05:00
|
|
|
|
2017-07-25 15:36:07 -05:00
|
|
|
;;;###autoload
|
2017-07-21 17:39:01 -05:00
|
|
|
(defun ein:jupyterhub-connect (url user password)
|
2017-07-30 11:37:44 -05:00
|
|
|
"Log on to a jupyterhub server using PAM authentication. Requires jupyterhub version 0.8 or greater."
|
2017-07-25 15:36:07 -05:00
|
|
|
(interactive (list (ein:jh-ask-url-or-port)
|
2017-07-21 17:39:01 -05:00
|
|
|
(read-string "User: ")
|
|
|
|
(read-passwd "Password: ")))
|
|
|
|
(ein:jupyterhub--do-connect url user password))
|
2017-04-07 08:25:37 -05:00
|
|
|
|
|
|
|
(provide 'ein-jupyterhub)
|