diff --git a/clojure_frontend/package-lock.json b/clojure_frontend/package-lock.json new file mode 100644 index 0000000..0eafedf --- /dev/null +++ b/clojure_frontend/package-lock.json @@ -0,0 +1,35 @@ +{ + "name": "toaster", + "version": "0.1.0-SNAPSHOT", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "dockerlint": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/dockerlint/-/dockerlint-0.3.9.tgz", + "integrity": "sha512-gps1IlRWx0hqhG7qZNYoF/Ae8wpnnPDGV0eYC60FdH2UscS4hZ+NFYX3Pusj/GImjLD/Pxkp/wib7CBb63yzZw==", + "requires": { + "sty": "0.6.1", + "subarg": "1.0.0" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "sty": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/sty/-/sty-0.6.1.tgz", + "integrity": "sha1-3j+5rlcLxgp0RyRfDewxIT6i1ag=" + }, + "subarg": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/subarg/-/subarg-1.0.0.tgz", + "integrity": "sha1-9izxdYHplrSPyWVpn1TAauJouNI=", + "requires": { + "minimist": "1.2.0" + } + } + } +} diff --git a/clojure_frontend/project.clj b/clojure_frontend/project.clj index 6a4da44..a0372bc 100644 --- a/clojure_frontend/project.clj +++ b/clojure_frontend/project.clj @@ -35,10 +35,12 @@ :uberwar {:init toaster.ring/init :handler toaster.handler/app} :mail toaster.handler + :npm {:dependencies [[dockerlint "0.3.9"]]} :profiles { :dev {:dependencies [[javax.servlet/servlet-api "2.5"] [ring/ring-mock "0.3.2"] [midje "1.9.2"]] - :plugins [[lein-midje "3.1.3"]] + :plugins [[lein-midje "3.1.3"] + [lein-npm "0.6.2"]] :aot :all :main toaster.handler} :uberjar {:aot :all diff --git a/clojure_frontend/src/toaster/handler.clj b/clojure_frontend/src/toaster/handler.clj index b9d32b9..4bdce62 100644 --- a/clojure_frontend/src/toaster/handler.clj +++ b/clojure_frontend/src/toaster/handler.clj @@ -51,18 +51,18 @@ (GET "/" request (web/render "Hello World!"));; web/readme)) ;; NEW ROUTES HERE - (GET "/dockerfile" request - (web/render views/dockerfile-upload-form)) - ;; (->> views/dockerfile-get - ;; (s/check request))) + (GET "/upload" request + (->> (fn [req conf acct] + (web/render acct views/dockerfile-upload-form)) + (s/check request))) (POST "/dockerfile" request - (views/dockerfile-upload-post request nil nil)) + (->> views/dockerfile-upload-post + (s/check request))) (GET "/list" request - (web/render [:div {:class "container-fluid"} - [:h1 "List all created jobs"] - (job/listall config)])) + (->> views/list-jobs + (s/check request))) ;; JUST-AUTH ROUTES (GET "/login" request @@ -85,11 +85,12 @@ (let [session {:session {:config config :auth logged}}] (conj session - (web/render - logged - [:div - [:h1 "Logged in: " username] - (web/render-yaml session)]))) + (views/list-jobs request config logged))) + ;; (web/render + ;; logged + ;; [:div + ;; [:h1 "Logged in: " username] + ;; views/welcome-menu]))) (f/when-failed [e] (web/render-error-page (str "Login failed: " (f/message e)))))) diff --git a/clojure_frontend/src/toaster/jobs.clj b/clojure_frontend/src/toaster/jobs.clj index 4d08c19..139a768 100644 --- a/clojure_frontend/src/toaster/jobs.clj +++ b/clojure_frontend/src/toaster/jobs.clj @@ -2,6 +2,8 @@ (:require [clojure.string :as str] [clojure.java.io :as io] + [clj-time.core :as time] + [clj-time.coerce :as tc] [failjure.core :as f] [taoensso.timbre :as log :refer [debug]] [me.raynes.conch :as sh :refer [with-programs]] @@ -9,25 +11,27 @@ [hiccup.form :as hf])) (defn add [path config account] - (with-programs [ssh scp] - (let [jobname "test@dyne.org-vm_amd64-12345678" ;; TODO: + (with-programs [ssh scp node] + (let [tstamp (tc/to-long (time/now)) + jobname (str (:email account) "-vm_amd64-" tstamp) jobdir (str "/srv/toaster/" jobname)] (f/attempt-all - [r_mkdir (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "mkdir" "-p" jobdir ) + [r_lint (node "node_modules/dockerlint/bin/dockerlint.js" path) + r_mkdir (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "mkdir" "-p" jobdir ) r_scp (scp "-i" "../id_ed25519" path (str "jenkins@sdk.bridge:" jobdir)) - r_ssh (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "sync_jobs.py" "-a" jobname {:verbose true})] + r_job (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "sync_jobs.py" "-a" jobname)] + {:lint r_lint + :job r_job} (f/when-failed [e] (web/render-error-page (str "Job add failure: " (f/message e)))))))) -(defn listall [config] +(defn listall [config account] (with-programs [ssh] (f/attempt-all - [r_ssh (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "sync_jobs.py" "-l" "all" {:verbose true})] - ;; (let [jobs (str/split (:stdout r_ssh) #" ")] - ;; jobs) - (:stdout r_ssh) + [r_ssh (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "sync_jobs.py" "-l" (:email account))] + (str/split r_ssh #"\n") (f/when-failed [e] (web/render-error-page (str "Job list failure: " (f/message e))))))) diff --git a/clojure_frontend/src/toaster/views.clj b/clojure_frontend/src/toaster/views.clj index 356ed4b..60fec00 100644 --- a/clojure_frontend/src/toaster/views.clj +++ b/clojure_frontend/src/toaster/views.clj @@ -1,6 +1,7 @@ (ns toaster.views (:require [clojure.java.io :as io] + [clojure.string :as str] ;; [clojure.data.json :as json :refer [read-str]] [toaster.webpage :as web] [toaster.session :as s] @@ -11,6 +12,9 @@ [toaster.config :as conf] [taoensso.timbre :as log :refer [debug]] [me.raynes.conch :as sh :refer [with-programs]] + [clj-time.core :as time] + [clj-time.coerce :as tc] + [clj-time.local :as tl] [hiccup.form :as hf])) (def dockerfile-upload-form @@ -29,9 +33,15 @@ proceed to validation."] :id "field-submit" :type "submit" :name "submit" :value "submit"}]]]]) -(defn dockerfile-upload-post - [request config account] +(def welcome-menu + [:div {:class "container-fluid"} + [:div {:class "row-fluid"} + [:div {:class "row"} [:a {:href "/list"} "List all your toaster jobs"]] + [:div {:class "row"} [:a {:href "/upload"} "Upload a new toaster job"]]]]) + +(defn dockerfile-upload-post [request config account] (web/render + account (let [tempfile (get-in request [:params :file :tempfile]) filename (get-in request [:params :file :filename]) @@ -50,15 +60,26 @@ proceed to validation."] (log/spy :error [:h1 (str "Uploaded file not found: " filename)])) ;; file is now in 'tmp' var - (with-programs [ssh scp] - ;; (require '[clj-time.core :as time] - ;; '[clj-time.coerce :as tc]) - ;; timestamp: (tc/to-long (time/now)) - (let [jobname "test@dyne.org-vm_amd64-12345678" ;; TODO: - jobdir (str "/srv/toaster/" jobname)] - (log/spy (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "mkdir" "-p" jobdir {:throw false})) - (log/spy (scp "-i" "../id_ed25519" path (str "jenkins@sdk.bridge:" jobdir) {:throw false})) - (log/spy (ssh "-i" "../id_ed25519" "jenkins@sdk.bridge" "sync_jobs.py" "-a" jobname {:verbose true :throw false})))) - - ;; TODO: launch scripts - )))))) + [:div {:class "container-fluid"} + [:h1 "Job uploaded and added"] + [:p "Log messages:"] + (web/render-yaml (job/add path config account))])))))) + +(defn list-jobs [request config account] + (web/render + account + [:div {:class "container-fluid"} + [:h1 (str "List all toaster jobs for " (:name account))] + [:table {:class "sortable table"} + [:thead nil + [:tr nil [:th nil "Date"] [:th nil "Type"] [:th nil "Actions"]]] + [:tbody nil + (for [j (job/listall config account)] + (let [type (-> j (str/split #"-") second) + tstamp (-> j (str/split #"-") last)] + [:tr nil + [:td {:class "date"} (-> tstamp Long/valueOf tc/from-long tl/to-local-date-time)] + [:td {:class "job"} [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" j)} type]] + [:td {:class "start-job"} (web/button "/start" "Start" (hf/hidden-field "job" j)) + (web/button "/remove" "Remove" (hf/hidden-field "job" j))]] + ))]]])) diff --git a/clojure_frontend/src/toaster/webpage.clj b/clojure_frontend/src/toaster/webpage.clj index 6597763..ae56089 100644 --- a/clojure_frontend/src/toaster/webpage.clj +++ b/clojure_frontend/src/toaster/webpage.clj @@ -150,24 +150,25 @@ (page/include-css "/static/css/fontawesome.min.css") (page/include-css "/static/css/toaster.css")])) +(def navbar-brand + [:div {:class "navbar-header"} + [:button {:class "navbar-toggle" :type "button" + :data-toggle "collapse" + :data-target "#navbarResponsive" + :aria-controls "navbarResponsive" + :aria-expanded "false" + :aria-label "Toggle navigation"} + [:span {:class "sr-only"} "Toggle navigation"] + [:span {:class "icon-bar"}] + [:span {:class "icon-bar"}] + [:span {:class "icon-bar"}]] + [:a {:class "navbar-item " :href "/"} + [:img {:src "/static/img/whale_toast.jpg"}]]]) + (def navbar-guest [:nav {:class "navbar navbar-default navbar-fixed-top navbar-expand-md navbar-expand-lg"} - [:div {:class "navbar-header"} - [:button {:class "navbar-toggle" :type "button" - :data-toggle "collapse" - :data-target "#navbarResponsive" - :aria-controls "navbarResponsive" - :aria-expanded "false" - :aria-label "Toggle navigation"} - [:span {:class "sr-only"} "Toggle navigation"] - [:span {:class "icon-bar"}] - [:span {:class "icon-bar"}] - [:span {:class "icon-bar"}]] - [:a {:class "navbar-item " :href "/"} - [:img {:src "/static/img/whale_toast.jpg"}] - ]] - + navbar-brand [:div {:class "collapse navbar-collapse" :id "navbarResponsive"} [:ul {:class "nav navbar-nav hidden-sm md-auto ml-auto"} ;; -- @@ -178,41 +179,30 @@ ]]]) (def navbar-account - [:nav {:class "navbar navbar-default navbar-fixed-top navbar-expand-lg"} - - [:div {:class "navbar-header"} - [:button {:class "navbar-toggle" :type "button" - :data-toggle "collapse" :data-target "#navbarResponsive" - :aria-controls "navbarResponsive" :aria-expanded "false" - :aria-label "Toggle navigation"} - [:span {:class "sr-only"} "Toggle navigation"] - [:span {:class "icon-bar"}] - [:span {:class "icon-bar"}] - [:span {:class "icon-bar"}]] - [:a {:class "navbar-brand far fa-handshake" :href "/"} "toaster"]] - + [:nav {:class "navbar navbar-default navbar-fixed-top navbar-expand-md navbar-expand-lg"} + navbar-brand [:div {:class "collapse navbar-collapse" :id "navbarResponsive"} [:ul {:class "nav navbar-nav hidden-sm ml-auto"} ;; -- [:li {:class "divider" :role "separator"}] ;; LIST OF RELEVANT LINKS AFTER LOGIN - ;; [:li {:class "nav-item"} - ;; [:a {:class "nav-link far fa-address-card" - ;; :href "/persons/list"} " Persons"]] + [:li {:class "nav-item"} + [:a {:class "nav-link far fa-address-card" + :href "/list"} " List"]] ;; [:li {:class "nav-item"} ;; [:a {:class "nav-link far fa-paper-plane" ;; :href "/projects/list"} " Projects"]] - ;; [:li {:class "nav-item"} - ;; [:a {:class "nav-link far fa-plus-square" - ;; :href "/timesheets"} " Upload"]] + [:li {:class "nav-item"} + [:a {:class "nav-link far fa-plus-square" + :href "/upload"} " Add"]] ;; [:li {:class "nav-item"} ;; [:a {:class "nav-link far fa-save" ;; :href "/reload"} " Reload"]] ;; -- - [:li {:role "separator" :class "divider"} ] - [:li {:class "nav-item"} - [:a {:class "nav-link far fa-file-code" - :href "/config"} " Configuration"]] + ;; [:li {:role "separator" :class "divider"} ] + ;; [:li {:class "nav-item"} + ;; [:a {:class "nav-link far fa-file-code" + ;; :href "/config"} " Configuration"]] ]]]) (defn render-footer []