1 ; Copyright 2022 Frank Duncan (frank@consxy.com) under AGPL3. See distributed LICENSE.txt.
2 (in-package #:candle-aws)
4 (defvar *aws-mutex* (sb-thread:make-mutex))
5 (defvar *aws-waitq* (sb-thread:make-waitqueue))
6 (defvar *aws-state* :initial)
8 (defvar *aws-instance-id*)
9 (defvar *aws-username*)
10 (defvar *aws-keyfile*)
13 (defvar *remote-work-dir*)
14 (defvar *remote-candle-location*)
16 (defmethod candle:process-job-in-system ((job-system (eql :aws)) job)
17 (sb-thread:with-mutex (*aws-mutex*)
18 ; Don't start it up until we process the first job
19 (when (eql :initial *aws-state*)
20 (setf *aws-state* :down)
21 (start-shutdown-thread))
22 (when (eql :down *aws-state*) (start-aws-box))
24 ((retn (multiple-value-list (run-job job))))
25 (setf *aws-state* :up)
26 (sb-thread:condition-broadcast *aws-waitq*)
29 (defun start-shutdown-thread ()
30 (log:info "Starting AWS shutdown thread")
31 (sb-thread:make-thread
34 (sb-thread:with-mutex (*aws-mutex*)
35 (when (eql :down *aws-state*)
36 (sb-thread:condition-wait *aws-waitq* *aws-mutex*))
37 (when (eql :shutting-down-soon *aws-state*)
39 (setf *aws-state* :down))
40 (when (eql :up *aws-state*)
41 (setf *aws-state* :shutting-down-soon)))
43 :name "AWS Shutdown Thread"))
45 (defmethod candle:shutdown-system ((job-system (eql :aws)))
46 (log:info "Shutting down AWS box for exit")
47 ; If there's a job going, we need to wait for it to finish
48 (sb-thread:with-mutex (*aws-mutex*))
51 (defun aws-command (cmd &rest args)
52 (with-output-to-string (out)
59 :error *error-output*)))
61 (defun describe-property (property)
68 (format nil "Reservations[0].Instances[0].~A" property))))
70 (defun get-remote-state ()
71 (intern (string-upcase (describe-property "State.Name")) :keyword))
73 (defun start-aws-box ()
74 (aws-command "start-instances" "--instance-ids" *aws-instance-id*)
77 :until (eql :running (get-remote-state))
79 ; Make sure ssh and services are started up
81 (when (not (eql :running (get-remote-state)))
82 (error "Waited two minutes and still not running...?")))
84 (defun stop-aws-box ()
85 (aws-command "stop-instances" "--instance-ids" *aws-instance-id*)
88 :until (eql :stopped (get-remote-state))
90 (when (not (eql :stopped (get-remote-state)))
91 (error "Waited two minutes and still not stopped...?")))
100 (format nil "ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~A" *aws-keyfile*)
103 (candle:project-dir (candle:job-project job))
104 (format nil "~A@~A:~A/~A" *aws-username*
105 (describe-property "PublicIpAddress")
107 (candle:project-name (candle:job-project job)))))
112 (with-output-to-string (out-str)
114 (sb-ext:process-exit-code
119 "StrictHostKeyChecking=no"
121 "UserKnownHostsFile=/dev/null"
124 (describe-property "PublicIpAddress")
125 (format nil "cd ~A/~A ; ~A run --env aws"
127 (candle:project-name (candle:job-project job))
128 *remote-candle-location*))
132 (values (zerop code) out)))