diff --git a/onboarding.clj b/onboarding.clj new file mode 100644 index 0000000..8bfa3bd --- /dev/null +++ b/onboarding.clj @@ -0,0 +1,170 @@ +#!/usr/bin bb +(ns + (:require [babashka.curl :as curl]) + (:import (java.nio.file Files + LinkOption) + (java.nio.file.attribute PosixFileAttributeView + FileAttribute) + (java.io BufferedReader File) + (java.util.regex Pattern))) + +(defn sh + "Launches a process with optional args, returning the exit code. + Prints stdout & stderr." + [bin & args] + (let [arg-array ^"[Ljava.lang.String;]" (into-array String (cons bin args)) + process (-> (ProcessBuilder. arg-array) + (.redirectErrorStream true) ;; TODO stream stderr to stderr + (.start))] + (with-open [out (io/reader (.getInputStream process))] + (loop [] + (when-let [line (.readLine ^BufferedReader out)] + (println line) + (recur)))) + (.waitFor process))) + +;; ## Deployment directory structure +(defn set-owner [path owner] + (-> + (Files/getFileAttributeView path PosixFileAttributeView (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])) + (.setOwner owner))) + +(defn set-group [path group] + (-> + (Files/getFileAttributeView path PosixFileAttributeView (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])) + (.setGroup group))) + +(defn make-dirs! [{:keys [company-name]}] + (printf "Creating deploy directories for %s." company-name) + (let [attrs (Files/getFileAttributeView (.toPath (io/file "/srv/http/www.sompani.com")) + PosixFileAttributeView + (into-array LinkOption [LinkOption/NOFOLLOW_LINKS])) + owner (.getOwner attrs) + fattr (into-array FileAttribute [])] + (doseq [dir-str ["/srv/http/staging.%s.talent.careers-cache" + "/srv/http/staging.%s.talent.careers-logs" + "/srv/http/staging.%s.talent.careers-sessions" + "/srv/http/staging.%s.talent.careers-uploads" + "/srv/http/staging.%s.talent.careers.1234" + "/srv/http/%s.talent.careers-cache" + "/srv/http/%s.talent.careers-logs" + "/srv/http/%s.talent.careers-sessions" + "/srv/http/%s.talent.careers-uploads" + "/srv/http/%s.talent.careers.1234"] + :let [dir (.toPath (io/file (format dir-str company-name)))]] + (printf ".") + (and + (Files/createDirectory dir fattr) + (set-owner dir owner))) + (printf ".") + (let [link (.toPath (io/file (format "/srv/http/staging.%s.talent.careers" company-name)))] + (and + (Files/createSymbolicLink link + (.toPath (io/file (format "/srv/http/staging.%s.talent.careers.1234" company-name))) + fattr) + (set-owner link owner))) + + (printf ".") + (let [link (.toPath (io/file (format "/srv/http/%s.talent.careers" company-name)))] + (and + (Files/createSymbolicLink link + (.toPath (io/file (format "/srv/http/%s.talent.careers.1234" company-name))) + fattr) + (set-owner link owner))) + (printf " ok.\n"))) + +;; ## nginx configuration +(defn render-replace [input datamap] + (reduce + #(str/replace %1 (Pattern/compile (str "\{\{" (name (key %2)) "\}\}")) (val %2)) + input + datamap)) + +(defn create-nginx-server! [{:keys [company-name] :as datamap}] + (printf "Creating nginx entries for %n." company-name) + (doseq [file ["skel.talent.careers" + "staging.skel.talent.careers"] + :let [in-file (str "resources/nginx/" file) + out-file (format "/etc/nginx/servers-available/%s" (str/replace file #"skel" company-name))]] + (printf ".") + (spit out-file + (render-replace (slurp in-file) datamap))) + (printf " ok.\n")) + +;; ## Certificates +(defn generate-certs! [{:keys [company-name company-domain] :as datamap}] + (println "Generating certificates...") + (let [command (map #(render-replace % datamap) + ["certbot" "certonly" "--nginx" + "-d" "staging.{{company-name}}.talent.careers" + "-d" "{{company-name}}.talent.careers" + "-d" "talent.{{company-domain}}" + "-d" "www.talent.{{company-domain}}" + "--cert-name" "{{company-name}}"])] + (println "Executing " (pr-str command)) + (apply sh command)) + (println "ok.")) + +;; # DNS Configuration + +;; ## Basic cloudflare entries + +(defn basic-dns-entries [company-name] + [{:type "A" :name company-name :content "185.163.117.139"} + {:type "A" :name (format "staging.%s" company-name) :content "185.163.117.139"} + {:type "AAAA" :name company-name :content "2a03:4000:3b:2bb:18ea:e0ff:fe8c:aa9a"} + {:type "AAAA" :name (format "staging.%s" company-name) :content "2a03:4000:3b:2bb:18ea:e0ff:fe8c:aa9a"} + {:type "MX" :name company-name :content "mx.yandex.net."} + {:type "MX" :name (format "m.%s.talent.careers" company-name) :content "feedback-smtp.eu-west-1.amazonses.com"} + {:type "TXT" :name (format "m.%s.talent.careers" company-name) :content "v=spf1 include:amazonses.com ~all"} + {:type "TXT" :name (format "%s.talent.careers" company-name) :content "v=spf1 redirect=_spf.yandex.net"} + {:type "TXT" :name (format "_dmarc.%s" company-name) :content "v=DMARC1; p=none; rua=sompani-d@dmarc.report-uri.com"}]) + +(defn make-dns-entries! [entries] + (let [uri "https://api.cloudflare.com/client/v4/zones/5cd643e7432d7cf69f44e268e32b5452/dns_records" + headers {"Authorization" (str "Bearer " (System/getenv "CLOUDFLARE_API"))}] + (doseq [entry entries] + (printf ".") + (curl/post uri + {:headers headers + :content-type :json + :form-params entry})))) + +(defn make-basic-dns-entries [{:keys [company-name]}] + (printf "Creating basic DNS entries for %s." company-name) + (make-dns-entries! (basic-dns-entries company-name)) + (println " ok.")) + +;; # Main initialization +(def cli-options + [["-d" "--domain VCDOMAIN" "The domain of the VC to onboard" + :default nil + :id :vc-domain] + ["-n" "--name VCNAME" "The name of the VC to onboard" + :default nil + :id :vc-name] + [nil "--deploy-directories" "Create deploy directories"] + [nil "--base-dns" "Create basic DNS entries. Make sure to have the CLOUDFLARE_API env var set."] + [nil "--nginx" "Create nginx entries"] + [nil "--certify" "Generate certificates"] + ["-h" "--help"]]) + +(let [{:keys [vc-domain + vc-name + deploy-directories + base-dns + aws-domain + nginx + certify + help] + :as options} + (:options (tools.cli/parse-opts args cli-options)) + datamap {:company-name vc-name :company-domain vc-domain}] + (if help + (println (:summary (tools.cli/parse-opts args cli-options))) + (do + (and deploy-directories (make-dirs! datamap)) + (and base-dns (make-basic-dns-entries! datamap)) + (and nginx (create-nginx-server! datamap)) + (and certify (generate-certs! datamap)) + (println "Done."))))