summaryrefslogtreecommitdiff
path: root/lisp/tfcloud/tfcloud.lisp
blob: 167abb6302fab1df9ac5c8754e13d04b25a38653 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(declaim (optimize (speed 0) (safety 3) (debug 3)))

(in-package :snow)

(defclass tfcloud ()
  ((token :initarg :token
          :initform (cdr (assoc :token (cdadr (car (cl-json:decode-json-from-string (uiop:read-file-string "~/.terraform.d/credentials.tfrc.json"))))))
          :accessor token)
   (baseurl :initarg :baseurl
            :initform "https://app.terraform.io/api/v2"
            :accessor baseurl)
   (proxy :initarg :proxy
          :initform (let ((proxy-string (uffi:getenv "http_proxy")))
                      (when (not (org-ckons-core::null-or-empty-p proxy-string))
                        (let ((proxy-list (cl-ppcre:split ":" (car (last (cl-ppcre:split "//" proxy-string))))))
                          (setf (elt proxy-list 1) (parse-integer (elt proxy-list 1)))
                          proxy-list)))
          :accessor proxy)
   (cookie-jar :initarg :cookie-jar
               :initform (make-instance 'drakma:cookie-jar)
               :accessor cookie-jar))
  (:documentation ""))

(defclass tfcloud-workspace (tfcloud)
  ((id :initarg :id
       :initform nil
       :accessor id)
   (name :initarg :name
         :initform nil
         :accessor name))
  (:documentation ""))

 (defmacro with-tfcloud ((instance-name) &body body)
  `(let ((,instance-name (make-instance 'tfcloud)))
     ,@body))

(defmethod sanitize-json ((tfcloud-workspace tfcloud-workspace))
  (make-instance 'tfcloud-workspace
                 :id (id tfcloud-workspace)
                 :name (name tfcloud-workspace)
                 :token nil
                 :baseurl nil
                 :proxy nil
                 :cookie-jar nil))

(defmacro define-tfcloud-api-call ((method-name) &body macro-body)
  (let ((endpoint (gensym)))
    `(progn
       (defgeneric ,method-name (tfcloud uri &key method macro-content-type params paginate-p))
       (defmethod ,method-name ((tfcloud tfcloud) uri &key method macro-content-type params paginate-p)
         (let ((,endpoint (format nil "~a/~a" (baseurl tfcloud) uri))
               (page "1")
               results)
           (loop until (null page) do
             (multiple-value-bind (body status-code headers uri stream must-close reason)
                 (apply #'org-ckons-http::drakma-request
                        `(,,endpoint
                          ,(cookie-jar tfcloud)
                          :method ,method
                          :content-type ,macro-content-type
                          :proxy ,(proxy tfcloud)
                          ,@(if (eq method :get)
                                `(:parameters ,(append params (when paginate-p `(("page[number]" . ,(format nil "~a" page))
                                                                                 ("page[size]" . "100")))))
                                `(:content ,params))
                          :additional-headers (("Authorization" . ,(format nil "Bearer ~a" (token tfcloud))))))
               (declare (ignore headers uri stream must-close reason))
               (let ((response (cl-json:decode-json-from-string (flexi-streams:octets-to-string body :external-format :utf-8))))
                 (cond ((< status-code 300)
                        (org-ckons-core::add-to-list results ,@macro-body)
                        (setf page (when paginate-p
                                     (cdr (assoc :next-page (cdr (assoc :pagination (cdr (assoc :meta response)))))))))
                       (t
                        (error (format nil "Error response from tfcloud.~%Method = [~a]~%Endpoint = [~a]~%Params = [~a]~%Page = [~a]~%Response = [~a]" method ,endpoint params page response)))))))
           results)))))

(define-tfcloud-api-call (tfcloud-default-impl)
  (cdr (assoc :data response)))

(defmethod get-tfcloud-workspaces ((tfcloud tfcloud) filter)
  (sort (loop for workspace in (tfcloud-default-impl tfcloud "organizations/olo/workspaces" :method :get :paginate-p t)
              when (org-ckons-core::match-it filter (cdr (assoc :name (cdr (assoc :attributes workspace)))))
                collect (make-instance 'tfcloud-workspace
                                       :id (cdr (assoc :id workspace))
                                       :name (cdr (assoc :name (cdr (assoc :attributes workspace))))))
        (lambda (x y) (string< (name x) (name y)))))

(defmethod bg-perform ((tfcloud-workspace tfcloud-workspace))
  (tfcloud-default-impl tfcloud-workspace
                                "runs"
                                :method :post
                                :macro-content-type "application/vnd.api+json"
                                :params (cl-json:encode-json-to-string `((:data . ((:attributes . ((:message . "Applied via Snow"))) (:type . "runs") (:relationships . ((:workspace . ((:data . ((:type . "workspaces") (:id . ,(id tfcloud-workspace))))))))))))))

(defmethod apply-tfcloud-workspace ((tfcloud-workspace tfcloud-workspace))
  (enqueue *queue-tfcloud* tfcloud-workspace))