From fe944519ac63d75cd645768bccc44c329fdb6413 Mon Sep 17 00:00:00 2001 From: Jaromil Date: Mon, 8 Oct 2018 16:41:27 +0200 Subject: [PATCH] completed basic gui functionalities linking to run and start --- .../resources/public/static/css/toaster.css | 1 + clojure_frontend/src/toaster/handler.clj | 36 ++- clojure_frontend/src/toaster/jobs.clj | 39 +-- clojure_frontend/src/toaster/views.clj | 231 ++++++++++-------- clojure_frontend/src/toaster/webpage.clj | 8 + 5 files changed, 197 insertions(+), 118 deletions(-) diff --git a/clojure_frontend/resources/public/static/css/toaster.css b/clojure_frontend/resources/public/static/css/toaster.css index 580af1a..61efcf9 100644 --- a/clojure_frontend/resources/public/static/css/toaster.css +++ b/clojure_frontend/resources/public/static/css/toaster.css @@ -56,6 +56,7 @@ table.sortable thead { table.sortable td { font-size: .8em; font-family: "arial" + vertical-align: middle; } .input-form { diff --git a/clojure_frontend/src/toaster/handler.clj b/clojure_frontend/src/toaster/handler.clj index a46d34a..6c2bd8d 100644 --- a/clojure_frontend/src/toaster/handler.clj +++ b/clojure_frontend/src/toaster/handler.clj @@ -48,16 +48,19 @@ (defroutes app-routes - (GET "/" request (web/render "Hello World!")) ;; web/readme)) + (GET "/" request (web/render "Hello there!")) ;; web/readme)) ;; NEW ROUTES HERE (GET "/upload" request (->> (fn [req conf acct] - (web/render acct views/dockerfile-upload-form)) + (web/render acct [:div + views/dockerfile-upload-form + (views/list-jobs acct)])) (s/check request))) (POST "/dockerfile" request - (->> views/dockerfile-upload-post + (->> (fn [req conf acct] + (web/render acct (views/dockerfile-upload-post req conf acct))) (s/check request))) (GET "/edit" request @@ -66,7 +69,30 @@ (s/check request))) (GET "/list" request - (->> views/list-jobs + (->> (fn [req conf acct] + (web/render acct (views/list-jobs acct))) + (s/check request))) + + (POST "/remove" request + (->> (fn [req conf acct] + (web/render acct (views/remove-job req conf acct))) + (s/check request))) + + (POST "/start" request + (->> (fn [req conf acct] + (web/render acct (views/start-job req conf acct))) + (s/check request))) + + (POST "/view" request + (->> (fn [req conf acct] + (web/render acct (views/view-job req conf acct))) + (s/check request))) + + (GET "/error" request + (->> (fn [req conf acct] + (web/render acct [:div + (web/render-error "Generic Error Page") + (views/list-jobs acct)])) (s/check request))) ;; JUST-AUTH ROUTES @@ -90,7 +116,7 @@ (let [session {:session {:config config :auth logged}}] (conj session - (views/list-jobs request config logged))) + (web/render logged (views/list-jobs logged)))) ;; (web/render ;; logged ;; [:div diff --git a/clojure_frontend/src/toaster/jobs.clj b/clojure_frontend/src/toaster/jobs.clj index e685f19..2d2ffd3 100644 --- a/clojure_frontend/src/toaster/jobs.clj +++ b/clojure_frontend/src/toaster/jobs.clj @@ -46,26 +46,35 @@ r_mkdir (ssh "-i" (q config [:jenkins :key]) (ssh-host config) "mkdir" "-p" jobdir) r_scp (scp "-i" (q config [:jenkins :key]) - path (str (ssh-host config) ":" jobdir)) - r_job (log/spy (sync_jobs config "-a" jobname)) - r_store (log/spy (db/store! - @jobs :jobid - (log/spy {:jobid jobname - :email (:email account) - :account (dissoc account :password :activation-link) - :lint (if (.contains r_lint "is OK") true false) - :timestamp tstamp - :type "vm_amd64"})))] + path (str (ssh-host config) ":" jobdir "/Dockerfile")) + r_job (sync_jobs config "-a" jobname) + r_store (db/store! @jobs :jobid + {:jobid jobname + :email (:email account) + :account (dissoc account :password :activation-link) + :lint (if (.contains r_lint "is OK") true false) + :timestamp tstamp + :type "vm_amd64" + :dockerfile (slurp path)})] {:lint r_lint :job r_job} (f/when-failed [e] - (web/render-error-page + (web/render-error (str "Job add failure: " (f/message e)))))))) +(defn trash [jobid config] + (f/attempt-all [r_sync (sync_jobs config "-d" jobid)] + jobid + (f/when-failed [e] + (web/render-error + (str "Job remove failure: " (f/message e)))))) + +(defn start [jobid config] + (f/attempt-all [r_sync (sync_jobs config "-r" jobid)] + jobid + (f/when-failed [e] + (web/render-error + (str "Job start failure: " (f/message e)))))) -(defn listall [config account] - (sync_jobs config "-l" (:email account)) - ;;(db/query @jobs {:email (:email account)}) - ) diff --git a/clojure_frontend/src/toaster/views.clj b/clojure_frontend/src/toaster/views.clj index f063984..2964266 100644 --- a/clojure_frontend/src/toaster/views.clj +++ b/clojure_frontend/src/toaster/views.clj @@ -1,113 +1,148 @@ (ns toaster.views - (:require - [clojure.java.io :as io] - [clojure.string :as str] - [clojure.contrib.humanize :as humanize :refer [datetime]] - ;; [clojure.data.json :as json :refer [read-str]] - [toaster.webpage :as web] - [toaster.session :as s] - [toaster.ring :as ring] - [toaster.jobs :as job] - [failjure.core :as f] - [auxiliary.string :refer [strcasecmp]] - [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])) + (:require + [clojure.java.io :as io] + [clojure.string :as str] + [clojure.contrib.humanize :as humanize :refer [datetime]] + ;; [clojure.data.json :as json :refer [read-str]] + [toaster.webpage :as web] + [toaster.session :as s] + [toaster.ring :as ring] + [toaster.jobs :as job] + [failjure.core :as f] + [auxiliary.string :refer [strcasecmp]] + [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] + [clj-storage.core :as db] + [hiccup.form :as hf])) (def dockerfile-upload-form - [:div {:class "container-fluid"} - [:h1 "Upload a Dockerfile to toast"] - [:p " Choose the file in your computer and click 'Submit' to + [:div {:class "container-fluid"} + [:h1 "Upload a Dockerfile to toast"] + [:p " Choose the file in your computer and click 'Submit' to proceed to validation."] - [:div {:class "form-group"} - [:form {:action "dockerfile" :method "post" - :class "form-shell" - :enctype "multipart/form-data"} - [:fieldset {:class "fieldset btn btn-default btn-file btn-lg"} - [:input {:name "file" :type "file"}]] - ;; [:fieldset {:class "fieldset-submit"} - [:input {:class "btn btn-primary btn-lg" - :id "field-submit" :type "submit" - :name "submit" :value "submit"}]]]]) + [:div {:class "form-group"} + [:form {:action "dockerfile" :method "post" + :class "form-shell" + :enctype "multipart/form-data"} + [:fieldset {:class "fieldset btn btn-default btn-file btn-lg"} + [:input {:name "file" :type "file"}]] + ;; [:fieldset {:class "fieldset-submit"} + [:input {:class "btn btn-primary btn-lg" + :id "field-submit" :type "submit" + :name "submit" :value "submit"}]]]]) (def dockerfile-edit-form - [:div {:class "container-fluid"} - [:h1 "Edit your Dockerfile to toast"] - [:div {:class "form-group"} - [:form {:action "dockerfile" :method "post" - :class "form-shell" - :enctype "multipart/form-data"} - [:fieldset {:class "fieldset btn btn-default btn-file btn-lg"} - [:textarea {:name "editor" :id "editor" - :rows 30 :cols 72 } "FROM: dyne/devuan:ascii"] - [:input {:class "btn btn-primary btn-lg" - :id "field-submit" :type "submit" - :name "submit" :value "submit"}]]]] - [:script "var editor = ace.edit(\"editor\"); - editor.setTheme(\"ace/theme/monokai\"); - editor.session.setMode(\"ace/mode/dockerfile\");"]]) + [:div {:class "container-fluid"} + [:h1 "Edit your Dockerfile to toast"] + [:div {:class "form-group"} + [:form {:action "dockerfile" :method "post" + :class "form-shell" + :enctype "multipart/form-data"} + [:fieldset {:class "fieldset btn btn-default btn-file btn-lg"} + [:textarea {:name "editor" :id "editor" + :rows 30 :cols 72} "FROM: dyne/devuan:ascii"] + [:input {:class "btn btn-primary btn-lg" + :id "field-submit" :type "submit" + :name "submit" :value "submit"}]]]] + [:script "var editor = ace.edit(\"editor\"); + editor.setTheme(\"ace/theme/monokai\"); + editor.session.setMode(\"ace/mode/dockerfile\");"]]) (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"]]]]) + [: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]) - params (:params request)] - (cond - (> (get-in params [:file :size]) 64000) - ;; max upload size in bytes - ;; TODO: put in config - (web/render-error-page params "File too big in upload (64KB limit).") - :else - (let [file (io/copy tempfile (io/file "/tmp" filename)) - path (str "/tmp/" filename)] - (io/delete-file tempfile) - (if (not (.exists (io/file path))) - (web/render-error-page - (log/spy :error - [:h1 (str "Uploaded file not found: " filename)])) - ;; file is now in 'tmp' var - [:div {:class "container-fluid"} - [:h1 "Job uploaded and added"] - [:p "Log messages:"] - (web/render-yaml (job/add path config account))])))))) + (let + [tempfile (get-in request [:params :file :tempfile]) + filename (get-in request [:params :file :filename]) + params (:params request)] + (cond + (> (get-in params [:file :size]) 64000) + ;; max upload size in bytes + ;; TODO: put in config + (web/render-error params "File too big in upload (64KB limit).") + :else + (let [file (io/copy tempfile (io/file "/tmp" filename)) + path (str "/tmp/" filename)] + (io/delete-file tempfile) + (if (not (.exists (io/file path))) + (web/render-error + (log/spy :error + [:h1 (str "Uploaded file not found: " filename)])) + ;; file is now in 'tmp' var + [: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] +(defn list-jobs [account] + (f/attempt-all + [joblist (db/query @ring/jobs {:email (:email 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 "Status"] [:th nil "Actions"]]] + [:tbody nil + (for [j joblist] + (let [type (:type j) ;; (-> j (str/split #"-") second) + tstamp (:timestamp j) ;; (-> j (str/split #"-") last) + jobid (:jobid j) + joburl (str/replace jobid #"@" "AT")] + [:tr nil + [:td {:class "date"} (-> tstamp Long/valueOf tc/from-long humanize/datetime)] + [:td {:class "job"} [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" joburl)} type]] + [:td {:class "status"} [:a {:href (str "https://sdk.dyne.org:4443/job/" joburl "/lastBuild/console")} + [:img {:src (str "https://sdk.dyne.org:4443/job/" joburl "/badge/icon")}]]] + [:td {:class "actions"} + [:div {:class "btn-toolbar" :role "toolbar" :arial-label "Actions"} + (web/button "/view" "\uD83D\uDC41" (hf/hidden-field "jobid" jobid)) + (web/button "/start" "▶" (hf/hidden-field "jobid" jobid)) + (web/button "/remove" "\uD83D\uDDD1" (hf/hidden-field "jobid" jobid))]]] + ))]]] + (f/when-failed [e] + (web/render-error + (str "Job list failure: " (f/message e)))))) + + +(defn remove-job [request config account] (f/attempt-all - [joblist (job/listall 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 joblist] - (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 - humanize/datetime)] - [:td {:class "job"} [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" - (str/replace j #"@" "AT"))} type]] - [:td {:class "start-job"} (web/button "/start" "Start" (hf/hidden-field "job" j)) - (web/button "/remove" "Remove" (hf/hidden-field "job" j))]] - ))]]]) + [jobid (s/param request :jobid) + jobfound (db/query @ring/jobs {:jobid jobid}) + r_rmjob (db/delete! @ring/jobs jobid) + r_sync (job/trash jobid config)] + [:h1 (str "Job removed: " jobid)] (f/when-failed [e] - (web/render-error-page - (str "Job list failure: " (f/message e)))) - )) + (web/render-error (str "Failure removing job: " (f/message e)))))) + +(defn start-job [request config account] + (f/attempt-all + [jobid (s/param request :jobid) + jobfound (db/query @ring/jobs {:jobid jobid}) + r_sync (job/start jobid config)] + [:div {:class "container-fluid"} + [:h1 (str "Job started: " jobid)] + [:p [:a {:href (str "https://sdk.dyne.org:4443/view/web-sdk-builds/job/" + (str/replace jobid #"@" "AT"))}]]] + (f/when-failed [e] + (web/render-error (str "Failure starting job: " (f/message e)))))) + +(defn view-job [request config account] + (f/attempt-all + [jobid (s/param request :jobid) + jobfound (db/fetch @ring/jobs jobid) + dockerfile (log/spy (-> jobfound :dockerfile))] + (web/render account [:div + [:h1 (str "Viewing job: " jobid)] + [:pre dockerfile]]) + (f/when-failed [e] + (web/render-error (str "Failure viewing job: " (f/message e)))))) diff --git a/clojure_frontend/src/toaster/webpage.clj b/clojure_frontend/src/toaster/webpage.clj index ae56089..1d19498 100644 --- a/clojure_frontend/src/toaster/webpage.clj +++ b/clojure_frontend/src/toaster/webpage.clj @@ -98,6 +98,14 @@ [:div {:class "container-fluid"} body] (render-footer)])})) +(defn render-success + "render a successful message without ending the page" + [succ] + [:div {:class "alert alert-success" :role "alert"} + [:span {:class "far fa-check-circle" + :aria-hidden "true" :style "padding: .5em"}] + [:span {:class "sr-only"} "Success:" ] + succ]) (defn render-error "render an error message without ending the page"