Datomic provides tooling to deploy your code to the cloud services. It's quick and avoids downtime.
There are a few steps in the process:
- Push: Prepares and uploads a release of your application
- Deploy: Updates an application with zero downtime
The process is best done after committing your code so that the git rev can be used as a unique identifier for the release.
During development it may be useful to quickly release unstable code to a development environment. We can support that by adding some helper functions to our user
namespace.
Note: We're not considering multiple deployments here.
Firstly, let's provide a release helper function in our user
namespace. We can use this to release from the REPL during development.
This code does a push, then a deploy and finally polls until the deploy reports it's finished running. It should provide what we need during development to do a one-step deployment.
Note: the extra code to support :creds-profile & :region might be excessive but gives us full access to the ion-dev API.
Note: We're hardcoding the compute group name for now - seems like something which might be resolved from system name via AWS CLI.
(ns user
(:require [datomic.ion.dev :as ion-dev]))
(def group "og-condense-sandbox-Compute-XXXZZZYYY")
(defn release
"Do push and deploy of app. Supports stable and unstable releases. Returns when deploy finishes running."
[args]
(try
(let [push-data (ion-dev/push args)
deploy-args (merge (select-keys args [:creds-profile :region :uname])
(select-keys push-data [:rev])
{:group group})]
(let [deploy-data (ion-dev/deploy deploy-args)
deploy-status-args (merge (select-keys args [:creds-profile :region])
(select-keys deploy-data [:execution-arn]))]
(loop []
(let [status-data (ion-dev/deploy-status deploy-status-args)]
(if (= "RUNNING" (:code-deploy-status status-data))
(do (Thread/sleep 5000) (recur))
status-data)))))
(catch Exception e
{:deploy-status "ERROR"
:message (.getMessage e)})))
Now let's create a simple script for cases where we're not at a REPL. The only sophistication here is ensuring the script completes cleanly and returns a valid status code.
Note: This doesn't provide a way to pass args into the release process. Needs thought.
(require 'user)
(let [ret (user/release {})]
(println ret)
(shutdown-agents)
(if (= (:deploy-status ret) "SUCCEEDED")
(System/exit 0)
(System/exit 1)))
Finally, a simple makefile target to ensure releases are run consistently.
release:
clojure -A:dev scripts/release.clj
There's a problem with the interaction between the script and the release fn. The release fn returns when :code-deploy-status has finished running, but the script checks the result of :deploy-status. The :deploy-status may still be running (and does in my case) for some time after the :code-deploy-status has completed. Mine takes another second or two. Therefore, the script returns "1", which the Makefile recognizes as failed. I updated my version of the release fn to loop on :deploy-status instead of :code-deploy-status.
Thanks for this code! It's made a real difference for me.