google-cloud

Google Cloud Platform: Clojure + {GAE, GCE, anything else on Google Platform}
domparry 2017-06-25T21:01:44.797734Z

We use gitlab for our build process, which includes the container build and deploy to kube

domparry 2017-06-25T21:02:08.799053Z

we have a CI container that has all the deps already built into it that looks like this:

domparry 2017-06-25T21:02:39.800803Z

FROM clojure
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY project.clj /usr/src/app/
RUN lein deps

domparry 2017-06-25T21:05:04.808625Z

and then a Base container that contains any other things you might need to bundle in, starting with: FROM java:openjdk-8-jre This is then stored at: <http://registry.gitlab.com/projectname/appname:base-image|registry.gitlab.com/projectname/appname:base-image>

domparry 2017-06-25T21:06:13.812527Z

and then the actual Docker file that builds off those is very simple, like this:

domparry 2017-06-25T21:07:08.815498Z

FROM <http://registry.gitlab.com/projectname/appname:base-image|registry.gitlab.com/projectname/appname:base-image>

COPY target/standalone.jar standalone.jar

ENV PORT 8080

EXPOSE 8080

CMD ["java", "-jar", "standalone.jar"]

domparry 2017-06-25T21:08:48.820649Z

then build is literally just lein uberjar from inside the CI container.

domparry 2017-06-25T21:09:11.821862Z

you can similarly run lein test.

domparry 2017-06-25T21:09:43.823677Z

@mobileink ☝️ Sorry I didn’t see the question before…

2017-06-25T21:14:54.839843Z

yikes. i am so 2012 i have no docker clue. how does this all work in the dev cycle? can you do something like a repl?

domparry 2017-06-25T21:16:16.844342Z

So the dev cycle is all as normal. For us it’s still simple in that we dev locally, so the repl is local too.

domparry 2017-06-25T21:17:06.846878Z

basically, repl, code, test, then when you commit, it kicks this build off.

domparry 2017-06-25T21:17:32.848172Z

then we have a deploy step that builds the container and deployes it to app engine.

2017-06-25T21:17:49.849038Z

fwiw i'm the https://github.com/migae/boot-gae guy. the problem with gae is interactive dev. how do you solve that?

domparry 2017-06-25T21:18:41.851658Z

yeah, I was looking at that while doing this. I nearly went there, but went with this pattern that we knew.

domparry 2017-06-25T21:19:09.853268Z

so, because we’re just bundling an uberjar, I don’t even bother with app engine until deployment.

domparry 2017-06-25T21:19:23.854021Z

so it’s just like interactive dev cycles with any ring app

domparry 2017-06-25T21:19:39.854754Z

(running the server locally)

domparry 2017-06-25T21:20:14.856555Z

we use a model similar to component for hot reloading of backends, and figwheel for the cljs bits)

2017-06-25T21:26:49.877013Z

so you run your own server for dev, rather than the gae dev server? i'm confused.

2017-06-25T21:27:55.880464Z

you use gae services (e.g. datastore, memcache, etc.)?

2017-06-25T21:41:37.924304Z

@domparry can you post your code?

domparry 2017-06-25T21:52:22.957604Z

ah.

domparry 2017-06-25T21:52:49.959021Z

sure. For this service, it’s very simple… We’re kicking off Cloud dataflow jobs.

domparry 2017-06-25T21:53:56.962463Z

we have a simple handler like this:

domparry 2017-06-25T21:54:01.962757Z

(ns fulliautomatix.handler
  (:require [fulliautomatix
             [datastore-etl :as be]
             [callcentre-leads :as cl]]
            [compojure.core :as cc]
            [compojure.handler :as handler]
            [compojure.route :as route]
            [ring.middleware.keyword-params :as rparams]
            [clojure.tools.logging :as log]
            [ring.adapter.jetty :as jetty])
  (:gen-class))

(defn wrap-result [result]
  (str "Dataflow Job Submitted."))

(defn run-datastore-etl [args headers]
 (let [{:keys [namespace kind filterField filterValue]} args
       result (be/run-main namespace kind filterField filterValue)]
   (wrap-result result)))

(defn run-callcentre-leads [_ _]
  (wrap-result (cl/run-main nil)))

(defn secure-route [function params headers]
  (if (get headers "x-appengine-cron")
    (function params headers)
    (str "Request not from App Engine Cron service")))

(cc/defroutes app-routes
 (cc/GET "/" []
         (str "healthy"))
 (cc/GET "/jobs/etl" {params :params headers :headers}
      (secure-route run-datastore-etl params headers))
 (cc/GET "/_ah/warmup" []
         (str "healthy"))
 (cc/GET "/_ah/health" []
         (str "healthy"))
 (cc/GET "/jobs/callcentre-leads" {params :params headers :headers}
      (secure-route run-callcentre-leads params headers))
 (route/not-found "Not Found"))

(def app
  (-&gt;
   (handler/site app-routes)
   (rparams/wrap-keyword-params)))

(defn -main [&amp; [port]]
  (let [port (Integer. (or port
                           (System/getenv "PORT")
                           5000))]
    (jetty/run-jetty #'app {:port  port
                            :join? false})))

domparry 2017-06-25T21:57:34.973929Z

this then calls some code that set’s up the dataflow pipeline and runs it.

2017-06-25T22:14:32.029321Z

you're using flexible env, not standard env?

2017-06-25T22:15:57.033869Z

in std env,

(jetty/run-jetty #'app {:port port :join? false})))
makes no sense.

domparry 2017-06-25T22:17:28.038634Z

yes, we’re in flexible env.

domparry 2017-06-25T22:19:29.044611Z

we run all our other stuff in Container Engine, and build it the same way.

domparry 2017-06-25T22:20:24.047402Z

hence not really worth redoing the build pipeline just for the simple app to work in Standard Env