#!/bin/sh #_( DEPS=' {:deps {org.clojure/clojure {:mvn/version "1.10.1"} org.clojure/tools.cli {:mvn/version "0.4.2"} cheshire/cheshire {:mvn/version "5.10.0"} clj-http/clj-http {:mvn/version "3.10.0"} org.seleniumhq.selenium/selenium-java {:mvn/version "4.0.0-alpha-4"} org.postgresql/postgresql {:mvn/version "42.2.9"} com.layerware/hugsql-core {:mvn/version "0.5.1"} com.layerware/hugsql-adapter-next-jdbc {:mvn/version "0.5.1"} digest/digest {:mvn/version "1.4.9"} cljstache/cljstache {:mvn/version "2.0.4"}} :paths ["src" "resources"] :mvn/repos {"central" {:url "https://repo1.maven.org/maven2/"} "clojars" {:url "https://repo.clojars.org/"}} :aliases {:outdated {:extra-deps {olical/depot {:mvn/version "1.8.4"}} :main-opts ["-m" "depot.outdated.main" "-a" "outdated"]}}} ' OPTS='' echo "Calling onboarding script with $@" exec clojure $OPTS -Sdeps "$DEPS" "$0" "$@" ) (require '[clojure.java.io :as io] '[clojure.string :as str] '[clojure.tools.cli :refer [parse-opts]] '[clj-http.client :as http] '[cljstache.core :as cljstache]) (import (java.nio.file Files LinkOption) (java.nio.file.attribute PosixFileAttributeView FileAttribute) (java.io BufferedReader #_File) (org.openqa.selenium.firefox FirefoxDriver) (org.openqa.selenium By)) ;; # Helper functions (defn sh "Launches a process with optional args, returning 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))) (def driver (atom nil)) ;; # AWS Domain (declare make-dns-entries!) (defn aws-login [& {:keys [aws-username aws-password]}] (.get driver "https://eu-west-1.console.aws.amazon.com/ses/home?region=eu-west-1") (.sendKeys (.findElement driver (By/id "resolving_input")) (into-array String [(or aws-username (System/getenv "AWS_USERNAME"))])) (.click (.findElement driver (By/id "next_button"))) (Thread/sleep 15000) (.sendKeys (.findElement driver (By/id "password")) (into-array String [(or aws-password (System/getenv "AWS_PASSWORD"))])) (.click (.findElement driver (By/id "signin_button")))) (defn aws-verify-domain-dns [{:keys [company-name]}] (.click (.findElement driver (By/linkText "Domains"))) (.click (.findElement driver (By/id "gwt-debug-verifyNewDomain"))) (.sendKeys (.findElement driver (By/cssSelector "td > .gwt-TextBox")) (into-array String [(str company-name ".talent.careers")])) (.click (.findElement driver (By/cssSelector ".gwt-CheckBox input"))) (.click (.findElement driver (By/xpath "//span[contains(text(),\"Verify This Domain\")]"))) (Thread/sleep 2000) (let [rows (.findElements driver (By/cssSelector ".dialogMiddle table[style^=\"table-layout\"] tr[__gwt_subrow=\"0\"]")) entries (->> (map #(identity {:name (.getText (.findElement % (By/cssSelector "td:nth-child(1)"))) :type (.getText (.findElement % (By/cssSelector "td:nth-child(2)"))) :content (.getText (.findElement % (By/cssSelector "td:nth-child(3)")))}) rows) (remove #(or (= "" (:name %)) (= "MX" (:type %)))))] (.click (.findElement driver (By/xpath "//span[contains(text(), \"Close\")]"))) (make-dns-entries! entries))) (defn aws-verify-domain-mailfrom [{:keys [company-name]}] (.click (.findElement driver (By/linkText "Domains"))) (.click (.findElement driver (By/linkText (str company-name ".talent.careers")))) (.click (.findElement driver (By/xpath "//span[contains(text(),\"MAIL FROM Domain\")]"))) (.click (.findElement driver (By/xpath "//button[contains(text(),\"Set MAIL FROM Domain\")]"))) (.sendKeys (.findElement driver (By/cssSelector "td > input")) (into-array String ["m"])) (.click (.findElement driver (By/xpath "//span[contains(text(),\"Set MAIL FROM Domain\")]"))) (Thread/sleep 2000) (let [rows (.findElements driver (By/cssSelector ".dialogMiddle table[style^=\"table-layout\"] tr[__gwt_subrow=\"0\"]")) entries (->> (map #(identity {:name (.getText (.findElement % (By/cssSelector "td:nth-child(1)"))) :type (.getText (.findElement % (By/cssSelector "td:nth-child(2)"))) :content (.getText (.findElement % (By/cssSelector "td:nth-child(3)")))}) rows))] (.click (.findElement driver (By/xpath "//span[contains(text(),\"Close\")]"))) (make-dns-entries! entries))) ;; # AWS Mail (defn aws-verify-mail-address [{:keys [company-name]}] (.click (.findElement driver (By/linkText "Email Addresses"))) (.click (.findElement driver (By/xpath "//button[contains(text(),\"Verify a New Email Address\")]"))) (.sendKeys (.findElement driver (By/id "gwt-debug-verifySenderEmailBox")) (into-array String [(str "info@" company-name ".talent.careers")])) (.click (.findElement driver (By/xpath "//span[contains(text(),\"Verify This Email Address\")]")))) ;; ## 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 create-nginx-server! [datamap] (printf "Creating nginx entries for %n." (:company-name datamap)) (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 datamap)))]] (printf ".") (spit out-file (cljstache/render (slurp in-file) datamap))) (printf " ok.\n")) ;; ## Certificates (defn generate-certs! [{:keys [_company-name _company-domain] :as datamap}] (println "Generating certificates...") (let [command (map #(cljstache/render % 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." :priority 0} {:type "MX" :name (format "m.%s.talent.careers" company-name) :content "feedback-smtp.eu-west-1.amazonses.com" :priority 0} {: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 ".") (http/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. Example: connectventures.co" :default nil :id :vc-domain] ["-n" "--name VCNAME" "The name of the VC to onboard. Example: connectventures" :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 "--aws-domain" "Create AWS Domain DNS entries and verify new domain. Make sure to have the AWS_USERNAME and AWS_PASSWORD env vars set."] [nil "--nginx" "Create nginx entries"] [nil "--certify" "Generate certificates"] ["-h" "--help"]]) (defn -main [& args] (let [{:keys [vc-domain vc-name deploy-directories base-dns aws-domain nginx certify help] :as _options} (:options (parse-opts args cli-options)) datamap {:company-name vc-name :company-domain vc-domain}] (if help (println (:summary (parse-opts args cli-options))) (do (and deploy-directories (make-dirs! datamap)) (and base-dns (make-basic-dns-entries! datamap)) (and aws-domain (do (reset! driver (FirefoxDriver.)) (aws-login) (Thread/sleep 5000) (aws-verify-domain-dns datamap) (aws-verify-domain-mailfrom datamap))) (and nginx (create-nginx-server! datamap)) (and certify (generate-certs! datamap)) (println "Done."))))) (apply -main *command-line-args*)