git.fiddlerwoaroof.com
main.lisp
42dc5c76
 ;; MIT license Edward Langley (c) 2018
 
e4881c6a
 (defpackage :daydreamer.main
8b29101b
   (:use :cl :fw.lu :alexandria :daydreamer.aws-result)
cc02973e
   (:export main dump
            #:stack-parameters
            #:stack-outputs
            #:stack-for-name
5892c84f
            #:watch-stack
            #:stack-info))
42dc5c76
 
e4881c6a
 (in-package :daydreamer.main)
42dc5c76
 
 (define-condition invalid-result (error)
   ())
 
 (defun stack-for-name (name)
   (let* ((aws-result (car (extract-list (cdar (aws/cloudformation:describe-stacks :stack-name name)))))
          (the-stack (extract-stack aws-result)))
     the-stack))
 
9804b881
 (defun print-kvs (prefix formatter stream data)
   (let ((key-key (format nil "~aKey" prefix))
         (value-key (format nil "~aValue" prefix)))
     (mapcar (lambda (inp)
               (let ((k (cadr (assoc key-key inp :test #'equal)))
                     (v (cadr (assoc value-key inp :test #'equal))))
8e5bcff6
                 (format stream formatter k v)))
9804b881
             data)))
42dc5c76
 
 (defun stack-outputs (the-stack)
9804b881
   (print-kvs "Output"
              (tagged-kv-formatter "OUTPUT") t
42dc5c76
              (outputs the-stack)))
 
 (defun stack-parameters (the-stack)
9804b881
   (print-kvs "Parameter"
8b29101b
              (tagged-kv-formatter "PARAMETER") t
42dc5c76
              (parameters the-stack)))
 
cc02973e
 (defun lt-format (a b &key &allow-other-keys)
   (local-time:format-timestring a b))
42dc5c76
 
cc02973e
 (defgeneric duration-of (timeline)
   (:method ((timeline timeline))
     (local-time-duration:timestamp-difference (end-date-time timeline)
                                               (start-date-time timeline))))
42dc5c76
 
ea8450da
 (defun every-five-seconds (cb args &optional (delay-function #'sleep))
   (loop
8b29101b
     for (continue? . next-args) = (multiple-value-list (apply cb args)) then (multiple-value-list (apply cb next-args))
     while continue?
     do (funcall delay-function 5)))
ea8450da
 
8b29101b
 #+(or)
ea8450da
 (deftest every-five-seconds ()
   (let ((counter 0)
         delay)
     (flet ((fake-delay (num)
              (incf counter)
              (setf delay num))
            (work ()
              (< counter 10)))
       (every-five-seconds #'work () #'fake-delay)
       (should be = 10 counter)
dc602477
       (should be = 5 delay)))
 
   (let (delay
         (count 0)
         (counters ()))
     (flet ((fake-delay (num)
              (setf delay num))
            (work (counter)
              ;; temporary for initial red...
              (if (> count 10)
                  (throw 'quit nil)
                  (incf count))
 
              (push counter counters)
              (values (< counter 10)
                      (1+ counter))))
       (catch 'quit
         (every-five-seconds #'work '(0) #'fake-delay))
       (should be equal
               (alexandria:iota 11 :start 10 :step -1)
               counters)
ea8450da
       (should be = 5 delay))))
 
8b29101b
 (defun resource-block (the-stack)
   (format t "~& RESOURCES ~%============~%")
   (let ((resources (daydreamer.aws-result:extract-list
                     (serapeum:assocdr "StackResources"
                                       (aws/cloudformation:describe-stack-resources
                                        :stack-name (stack-name the-stack))
                                       :test 'equal)
                     'daydreamer.aws-result:extract-stack-resource)))
     (loop for resource in resources
           do (format t "RESOURCE ~a (~a) ~a ~a~:[~;~%~:*~4t~a~]~%"
                      (logical-resource-id resource)
                      (resource-type resource)
                      (resource-status resource)
                      (physical-resource-id resource)
                      (resource-status-reason resource))))
   (format t "~&============~2%"))
 
dc602477
 (defun parameter-block (the-stack)
   (format t "~& PARAMETERS ~%============~%")
   (stack-parameters the-stack)
   (format t "~&============~2%"))
 
 (defun output-block (the-stack)
   (format t "~2& OUTPUTS ~%=========~%")
   (stack-outputs the-stack)
   (format t "~&=========~%")
   (values))
 
9f1ff408
 (defclass stack-formatter ()
   ((%stack :initarg :stack :accessor stack)
    (%old-status :initarg :old-status :accessor old-status :initform nil)))
 
8b29101b
 (defmethod stack-name ((stack-formatter stack-formatter))
   (when (slot-boundp stack-formatter '%stack)
     (stack-status (stack stack-formatter))))
 
9f1ff408
 (defmethod stack-status ((stack-formatter stack-formatter))
   (when (slot-boundp stack-formatter '%stack)
     (stack-status (stack stack-formatter))))
 
 (defmethod parameters ((stack-formatter stack-formatter))
   (when (slot-boundp stack-formatter '%stack)
     (parameters (stack stack-formatter))))
 
 (defmethod outputs ((stack-formatter stack-formatter))
   (when (slot-boundp stack-formatter '%stack)
     (outputs (stack stack-formatter))))
 
 (defmethod (setf stack) :before (new-value (object stack-formatter))
   (setf (old-status object) (stack-status object)))
 
3c0af7c8
 (defgeneric refresh (stack-formatter)
e4881c6a
   (:method ((stack daydreamer.aws-result:stack))
8b29101b
     (stack-for-name (stack-name stack)))
9f1ff408
   (:method ((stack-formatter string))
     (make-instance 'stack-formatter :stack (stack-for-name stack-formatter)))
   (:method ((stack-formatter stack-formatter))
3c0af7c8
     (setf (stack stack-formatter) (refresh (stack stack-formatter)))
9f1ff408
     stack-formatter))
 
e4881c6a
 (defmethod old-status ((stack daydreamer.aws-result:stack))
5892c84f
   nil)
 
8b29101b
 (defun stack-info (the-stack status parameters outputs &optional (resources nil))
9f1ff408
   (with-accessors ((old-status old-status)) the-stack
     (let* ((current-status (stack-status the-stack)))
       (unless old-status
8b29101b
         (when parameters
           (parameter-block the-stack)))
9f1ff408
 
       (unless (equal old-status current-status)
8b29101b
         (when status
           (format t "~&STATUS ~a~%" current-status)))
 
       (when resources
         (terpri)
         (resource-block the-stack))
9f1ff408
 
       (if (ends-with-subseq "COMPLETE" (symbol-name current-status))
8b29101b
           (when outputs
             (output-block the-stack))
9f1ff408
           t))))
ea8450da
 
8b29101b
 (defmacro refreshing (cb &rest args)
3c0af7c8
   `(lambda (thing)
      (let ((refreshed-thing (refresh thing)))
8b29101b
        (values (,cb refreshed-thing ,@args)
3c0af7c8
                refreshed-thing))))
5892c84f
 
42dc5c76
 (defun watch-stack (name)
d59cfff7
   (format t "~&Watching ~s~2%" name)
8b29101b
   (every-five-seconds (refreshing stack-info t t t)
9f1ff408
                       (list name))
d59cfff7
   (fresh-line))