summaryrefslogtreecommitdiff
path: root/lisp/aws/aws.lisp
blob: f5d6b93d059c9b8deb14d70ca057244b8f3e3830 (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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
;;; -*- Mode: Lisp; Syntax: ANSI-Common-Lisp; Base: 10 -*-
(declaim (optimize (speed 0) (safety 3) (debug 3)))

(in-package :snow)

(defclass aws ()
  ((profile :initarg :profile
            :initform nil
            :accessor profile)
   (region :initarg :region
           :initform (region *webapp*)
           :accessor region))
  (:documentation ""))

(defclass aws-tag ()
  ((key :initarg :key
        :initform nil
        :accessor key)
   (value :initarg :value
          :initform nil
          :accessor value))
  (:documentation ""))

(defclass aws-asg (aws)
  ((name :initarg :name
         :initform nil
         :accessor name)
   (arn :initarg :arn
        :initform nil
        :accessor arn)
   (count-min :initarg :count-min
              :initform nil
              :accessor count-min)
   (count-max :initarg :count-max
              :initform nil
              :accessor count-max)
   (count-desired :initarg :count-desired
                  :initform nil
                  :accessor count-desired)
   (instances :initarg :instances
              :initform nil
              :accessor instances)
   (howmany :initarg :howmany
            :initform nil
            :accessor howmany))
  (:documentation ""))

(defclass aws-certificate (aws)
  ((arn :initarg :arn
        :initform nil
        :accessor arn)
   (domain-name :initarg :domain-name
                :initform nil
                :accessor domain-name)
   (status :initarg :status
           :initform nil
           :accessor status)
   (renewal-eligibility :initarg :renewal-eligibility
                        :initform nil
                        :accessor renewal-eligibility)
   (not-before :initarg :not-before
               :initform nil
               :accessor not-before)
   (not-after :initarg :not-after
              :initform nil
              :accessor not-after)
   (tags :initarg :tags
         :initform nil
         :accessor tags)
   (cert :initarg :cert
         :initform nil
         :accessor cert)
   (chain :initarg :chain
          :initform nil
          :accessor chain)
   (key :initarg :key
        :initform nil
        :accessor key)
   (passphrase :initarg :passphrase
               :initform nil
               :accessor passphrase))
  (:documentation ""))

(defclass aws-ssm-parameter (aws)
  ((name :initarg :name
         :initform nil
         :accessor name)
   (value :initarg :value
          :initform nil
          :accessor value))
  (:documentation ""))

(defmethod sanitize-json ((aws-asg aws-asg))
  (make-instance 'aws-asg
                 :profile nil
                 :region nil
                 :name (name aws-asg)
                 :count-min (count-min aws-asg)
                 :count-max (count-max aws-asg)
                 :count-desired (count-desired aws-asg)
                 :instances (instances aws-asg)
                 :howmany nil))

(defmethod run-aws-cli ((aws aws) command)
  "Runs an awscli command and returns the results as CL-JSON object."
  (let ((output (make-array '(0) :element-type 'character :fill-pointer 0 :adjustable t)))
    (with-output-to-string (stream output)
      (uffi:run-shell-command (format nil
                                      "~a --profile ~a --region ~a"
                                      command
                                      (profile aws)
                                      (region aws))
                              :output stream))
    (if (org-ckons-core::null-or-empty-p output)
        output
        (cl-json:decode-json-from-string output))))

(defmethod get-asgs ((aws aws) filter)
  (let ((command "aws autoscaling describe-auto-scaling-groups"))
    (loop for asg in (cdar (run-aws-cli aws command))
          when (org-ckons-core::match-it filter (cdr (assoc :*auto-scaling-group-name asg)))
            collect (make-instance 'aws-asg
                                   :profile (profile aws)
                                   :region (region aws)
                                   :name (cdr (assoc :*auto-scaling-group-name asg))
                                   :arn (cdr (assoc :*auto-scaling-group-+arn+ asg))
                                   :count-min (cdr (assoc :*min-size asg))
                                   :count-max (cdr (assoc :*max-size asg))
                                   :count-desired (cdr (assoc :*desired-capacity asg))
                                   :instances (loop for instance in (cdr (assoc :*instances asg))
                                                    collect (cdr (assoc :*instance-id instance)))))))

(defmethod get-certificates ((aws aws) &optional filter)
  (sort (loop for cert in (cdr (assoc :*certificate-summary-list (run-aws-cli aws "aws acm list-certificates")))
              when (or (and (null filter)
                            (string= (cdr (assoc :*status cert)) "ISSUED"))
                       (string= filter (cdr (assoc :*domain-name cert))))
                collect (make-instance 'aws-certificate
                                       :profile (profile aws)
                                       :region (region aws)
                                       :arn (cdr (assoc :*certificate-arn cert))
                                       :domain-name (cdr (assoc :*domain-name cert))
                                       :status (cdr (assoc :*status cert))
                                       :renewal-eligibility (cdr (assoc :*renewal-eligibility cert))
                                       :not-before (cdr (assoc :*not-before cert))
                                       :not-after (cdr (assoc :*not-after cert))))
        (lambda (x y) (string< (domain-name x) (domain-name y)))))

(defmethod get-certificate-tags ((aws-certificate aws-certificate))
  (setf (tags aws-certificate) (sort (loop for tag in (cdr (assoc :*tags (run-aws-cli aws-certificate (format nil "aws acm list-tags-for-certificate --certificate-arn ~a" (arn aws-certificate)))))
                                           collect (make-instance 'aws-tag
                                                                  :key (cdr (assoc :*key tag))
                                                                  :value (cdr (assoc :*value tag))))
                                     (lambda (x y) (string< (key x) (key y))))))

(defun tag-exists-p (cert key value)
  (find-if (lambda (x)
             (and (string= (key x) key)
                  (string= (value x) value)))
           (tags cert)))

(defun get-certificates-to-renew (certs environment)
  (loop for cert in certs
        do (get-certificate-tags cert))
  (loop for cert in certs
        when (and (or (tag-exists-p cert "Purpose" "KafkaAuth")
                      (tag-exists-p cert "Purpose" "OloAuthSigningKey"))
                  (tag-exists-p cert "Environment" environment))
          collect cert))

(defmethod bg-perform ((aws-asg aws-asg))
  (labels ((do-scale (count-desired)
             (let ((command (format nil
                                    "aws autoscaling set-desired-capacity --auto-scaling-group-name ~a --desired-capacity ~a"
                                    (name aws-asg)
                                    count-desired)))
               (run-aws-cli aws-asg command)))
           (instances-ready-p ()
             (let ((updated-asg (car (get-asgs aws-asg (name aws-asg))))
                   (in-service-p t))
               (loop for id in (instances updated-asg)
                     do (let* ((command (format nil "aws autoscaling describe-auto-scaling-instances --instance-id ~a" id))
                               (instance (cadar (run-aws-cli updated-asg command))))
                          (when (not (string= (cdr (assoc :*lifecycle-state instance)) "InService"))
                            (setf in-service-p nil))))
               in-service-p)))
    (let ((count (cond ((string= (howmany aws-asg) "all") (count-desired aws-asg))
                       ((string= (howmany aws-asg) "one") 1)
                       (t 0))))
      (loop for index from 1 to count
            do (progn
                 (do-scale (+ (count-desired aws-asg) 1))
                 (sleep 15)
                 (do-scale (count-desired aws-asg))
                 (sleep 15)
                 (loop while (not (instances-ready-p))
                       do (sleep 30)))))))

(defmethod recycle-asg ((aws-asg aws-asg))
  (enqueue *queue-aws* aws-asg))

(defmethod bg-perform ((aws-certificate aws-certificate))
  )

(defmethod renew-certificates ((aws-certificate aws-certificate) environment filter)
  (loop for cert in (get-certificates-to-renew (get-certificates aws-certificate filter) environment)
        do (let* ((passphrase (cl-base64:string-to-base64-string (org-ckons-session::generate-sessionid)))
                  (exported-cert (run-aws-cli aws-certificate (format nil "aws acm export-certificate --certificate-arn ~a --passphrase ~a --output json" (arn cert) passphrase))))
             (setf (cert cert) (cdr (assoc :*certificate exported-cert)))
             (setf (chain cert) (cdr (assoc :*certificate-chain exported-cert)))
             (setf (key cert) (cdr (assoc :*private-key exported-cert)))
             (setf (passphrase cert) passphrase))))
        ;;do (enqueue *queue-aws* cert)))

(defun get-aws-ssm-parameter (profile key)
  (let* ((aws-ssm-parameter (make-instance 'aws-ssm-parameter :profile profile :region "us-east-1"))
         (command (format nil "aws ssm get-parameter --with-decryption --name \"~a\"" key))
         (results (run-aws-cli aws-ssm-parameter command)))
    (setf (name aws-ssm-parameter) (cdr (assoc :*name (cdar results))))
    (setf (value aws-ssm-parameter) (cdr (assoc :*value (cdar results))))
    aws-ssm-parameter))