diff options
| author | Bret Koppel <bret@olo.com> | 2025-10-07 09:00:37 -0400 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-10-07 09:00:37 -0400 |
| commit | 910d7fd656b9e957181732b36761449de5a970d1 (patch) | |
| tree | 0f8ee46af3e93aa489785d64d54c5e155ef0057b /lisp/aws | |
| parent | 9adba239b937df2830a5110adaa1dae17b7dd7d7 (diff) | |
| parent | 0ad86ba37be273ee1fe4d9bdd5f7bb15c5701abf (diff) | |
Merge pull request #1 from ololabs/initial-commit
initial commit
Diffstat (limited to 'lisp/aws')
| -rw-r--r-- | lisp/aws/aws.lisp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/lisp/aws/aws.lisp b/lisp/aws/aws.lisp new file mode 100644 index 0000000..f5d6b93 --- /dev/null +++ b/lisp/aws/aws.lisp @@ -0,0 +1,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)) |
