Skip to content

Instantly share code, notes, and snippets.

@olivergeorge
Last active August 13, 2021 02:06
Show Gist options
  • Save olivergeorge/691230277059c644a259109aa5d11d3a to your computer and use it in GitHub Desktop.
Save olivergeorge/691230277059c644a259109aa5d11d3a to your computer and use it in GitHub Desktop.
Devops tools for simple Datomic Ions app
{:paths ["resources" "dev"]
:deps {com.datomic/ion {:mvn/version "0.9.28"}}
:mvn/repos {"datomic-cloud" {:url "s3://datomic-releases-1fc2183a/maven/releases"}}
:aliases {:dev {:extra-paths ["dev"]
:extra-deps {com.datomic/client-cloud {:mvn/version "0.8.71"}
com.datomic/ion-dev {:mvn/version "0.9.186"}
com.cognitect.aws/api {:mvn/version "0.8.171"}
com.cognitect.aws/endpoints {:mvn/version "1.1.11.479"}
com.cognitect.aws/cloudformation {:mvn/version "684.2.377.0"}
com.cognitect.aws/autoscaling {:mvn/version "668.2.364.0"}
com.cognitect.aws/ec2 {:mvn/version "693.2.386.0"}}}}}
(ns devops
(:require [datomic.ion.dev :as ion-dev]
[cognitect.aws.client.api :as aws]
[clojure.edn :as edn]
[clojure.java.io :as io]
[clojure.main :as cljmain]
[clojure.data.json :as json]))
(def ion-config (edn/read-string (slurp (io/resource "datomic/ion-config.edn"))))
(defn find-where
[ms kvs]
(first (filter #(= kvs (select-keys % (keys kvs))) ms)))
(defn push
[args]
(let [push-args (select-keys args [:creds-profile :region :uname])]
(ion-dev/push push-args)))
(defn deploy
[args]
(let [deploy-args (select-keys args [:creds-profile :region :uname :group])]
(ion-dev/deploy deploy-args)))
(defn deploy-status [args]
(let [deploy-status-args (select-keys args [:execution-arn])]
(ion-dev/deploy-status deploy-status-args)))
(defn release
"Do push and deploy of app. Supports stable and unstable releases. Returns when deploy finishes running."
[req args]
(let [push-data (push args)
primary-group (-> (aws/client {:api :cloudformation})
(aws/invoke {:op :DescribeStacks :request req})
(get-in [:Stacks 0 :Outputs])
(find-where {:OutputKey "CodeDeployDeploymentGroup"})
:OutputValue)
deploy-args (merge {:group primary-group} args (select-keys push-data [:rev]))
deploy-data (deploy deploy-args)]
(loop []
(let [status-data (deploy-status deploy-data)]
(case (:code-deploy-status status-data)
"RUNNING" (do (Thread/sleep 5000) (recur))
"SUCCEEDED" status-data
(throw (ex-info "ion-release did not succeed" status-data)))))))
(defn autoscaling-groups [req]
(let [ComputeStackName (-> (aws/client {:api :cloudformation})
(aws/invoke {:op :DescribeStacks :request req})
(get-in [:Stacks 0 :Outputs])
(find-where {:OutputKey "CodeDeployDeploymentGroup"})
:OutputValue)
ComputeStackResources (-> (aws/client {:api :cloudformation})
(aws/invoke {:op :DescribeStackResources :request {:StackName ComputeStackName}})
:StackResources)
TxAutoScalingGroup (:PhysicalResourceId (find-where ComputeStackResources {:LogicalResourceId "TxAutoScalingGroup"}))
BastionAutoScalingGroup (:PhysicalResourceId (find-where ComputeStackResources {:LogicalResourceId "BastionAutoScalingGroup"}))]
{:TxAutoScalingGroup TxAutoScalingGroup
:BastionAutoScalingGroup BastionAutoScalingGroup}))
(defn cloud-off [req]
(let [{:keys [TxAutoScalingGroup BastionAutoScalingGroup]} (autoscaling-groups req)
autoscaling (aws/client {:api :autoscaling})]
(aws/invoke autoscaling {:op :UpdateAutoScalingGroup :request {:AutoScalingGroupName TxAutoScalingGroup :MinSize 0}})
(aws/invoke autoscaling {:op :SetDesiredCapacity :request {:AutoScalingGroupName TxAutoScalingGroup :DesiredCapacity 0}})
(aws/invoke autoscaling {:op :SetDesiredCapacity :request {:AutoScalingGroupName BastionAutoScalingGroup :DesiredCapacity 0}})))
(defn cloud-on [req]
(let [{:keys [TxAutoScalingGroup BastionAutoScalingGroup]} (autoscaling-groups req)
autoscaling (aws/client {:api :autoscaling})]
(aws/invoke autoscaling {:op :UpdateAutoScalingGroup :request {:AutoScalingGroupName TxAutoScalingGroup :MinSize 1}})
(aws/invoke autoscaling {:op :SetDesiredCapacity :request {:AutoScalingGroupName TxAutoScalingGroup :DesiredCapacity 1}})
(aws/invoke autoscaling {:op :SetDesiredCapacity :request {:AutoScalingGroupName BastionAutoScalingGroup :DesiredCapacity 1}})))
(def datomic-solo-template "https://awsmp-fulfillment-cf-templates-prod.s3-external-1.amazonaws.com/5e99c028-1707-4840-8e41-f70361e29600/decd6065-c6d8-4250-8fab-a8f26f87afa2/d16575983c03499488729bcd677300ec.template")
(defn provision
[{:keys [KeyPair Stack]}]
(aws/invoke (aws/client {:api :ec2}) {:op :CreateKeyPair :request {:KeyName (:KeyName KeyPair)}})
(aws/invoke (aws/client {:api :cloudformation})
{:op :CreateStack
:request {:StackName (:StackName Stack)
:TemplateBody (:TemplateBody Stack)
:Parameters (conj (:Parameters Stack) {:ParameterKey "KeyName" :ParameterValue (:KeyName KeyPair)})
:Capabilities ["CAPABILITY_NAMED_IAM"]}}))
(defn teardown
[{:keys [StackName]}]
(aws/invoke (aws/client {:api :cloudformation}) {:op :DeleteStack :request {:StackName StackName}}))
(defn -main [& args]
(let [cmd (first args)
edn (edn/read-string (second args))]
(try
(println
(case cmd
"provision" (provision {:KeyPair {:KeyName (:app-name ion-config)}
:Stack {:StackName (:app-name ion-config)
:TemplateBody datomic-solo-template
:Parameters [{:ParameterKey "EnvironmentMap" :ParameterValue "{:env :dev}"}]}})
"teardown" (teardown {:StackName (:app-name ion-config)})
"release" (release {:StackName (:app-name ion-config)} edn)
"cloud-on" (cloud-on {:StackName (:app-name ion-config)})
"cloud-off" (cloud-off {:StackName (:app-name ion-config)})))
(flush)
(System/exit 0)
(catch Exception e
(println (-> e Throwable->map cljmain/ex-triage cljmain/ex-str))
(System/exit 1))
(finally
(shutdown-agents)))))
{:allow []
:lambdas {}
:app-name "XXX"}
provision:
clojure -A:dev -m devops provision
teardown:
clojure -A:dev -m devops teardown
cloud-on:
clojure -A:dev -m devops cloud-on
cloud-off:
clojure -A:dev -m devops cloud-off
release-rev:
clojure -A:dev -m devops release '{}'
release-dev:
clojure -A:dev -m devops release '{:uname "dev"}'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment