├── doit2.sh ├── doit.sh ├── .gitignore ├── elnode-server.el ├── project.clj ├── README.md ├── src └── spawner │ └── core.clj └── LICENSE /doit2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | sleep 2 4 | echo "WAH!" 5 | exit 3 6 | -------------------------------------------------------------------------------- /doit.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | for i in $(seq 1 8) 4 | do 5 | date 6 | sleep 1 7 | echo "meh" 8 | done 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /classes 3 | /checkouts 4 | pom.xml 5 | pom.xml.asc 6 | *.jar 7 | *.class 8 | /.lein-* 9 | /.nrepl-port 10 | -------------------------------------------------------------------------------- /elnode-server.el: -------------------------------------------------------------------------------- 1 | 2 | (require 'elnode) 3 | 4 | (setq spawner/commands 5 | '((:start :doit2) 6 | (:status))) 7 | 8 | (setq spawner/commands 9 | '((:status))) 10 | 11 | (setq spawner/commands 12 | '((:exit))) 13 | 14 | (defun spawner-handler (httpcon) 15 | (message "data: %S" (elnode-http-params httpcon)) 16 | (elnode-http-start httpcon 200 '(content-type "text/lisp")) 17 | (elnode-http-return httpcon (format "%S" (pop spawner/commands)))) 18 | 19 | (elnode-start 'spawner-handler :port 6001) 20 | -------------------------------------------------------------------------------- /project.clj: -------------------------------------------------------------------------------- 1 | (defproject spawner "0.1.0-SNAPSHOT" 2 | :description "FIXME: write description" 3 | :url "http://example.com/FIXME" 4 | :license {:name "Eclipse Public License" 5 | :url "http://www.eclipse.org/legal/epl-v10.html"} 6 | :dependencies [[org.clojure/clojure "1.6.0"] 7 | [me.raynes/conch "0.8.0"] 8 | [org.clojure/core.async "0.1.256.0-1bf8cf-alpha"] 9 | [http-kit "2.1.6"]] 10 | :main ^:skip-aot spawner.core 11 | :target-path "target/%s" 12 | :profiles {:uberjar {:aot :all}}) 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spawner 2 | 3 | A process supervisor program in Clojure. 4 | 5 | ## Installation 6 | 7 | Install from github for now. 8 | 9 | ## Usage 10 | 11 | To run: 12 | 13 | $ java -jar spawner-0.1.0-standalone.jar [monitor-url] 14 | 15 | ## Options 16 | 17 | monitor-url is the HTTP address of a compliant monitoring daemon. 18 | 19 | 20 | ## Examples 21 | 22 | This whole thing is really an example. Since the config for the files 23 | is baked in it's not really portable. That's ok I think (maybe). The 24 | point is the ease of the pattern. 25 | 26 | ### Bugs 27 | 28 | There are no bugs with this software at all. 29 | 30 | 31 | ## License 32 | 33 | Copyright © 2014 N J Ferrier 34 | 35 | Distributed under the Eclipse Public License either version 1.0 or (at 36 | your option) any later version. 37 | -------------------------------------------------------------------------------- /src/spawner/core.clj: -------------------------------------------------------------------------------- 1 | (ns spawner.core 2 | "A spawning process controlller. 3 | 4 | Like a process supervisor. This uses core.async extensively." 5 | (:require 6 | [clojure.core.async 7 | :refer [>! !! !! from [:exit id (.waitFor proc)])) 25 | 26 | ;; Make a thread to read the inputstream ... 27 | (thread 28 | (binding [*in* (io/reader (.getInputStream proc))] 29 | (loop [line (read-line)] 30 | (when line 31 | (>!! from [:line id line]) 32 | (recur (read-line)))))) 33 | 34 | ;; ... and another to process incomming messages. 35 | (thread 36 | ;; This might have to be changed, what if a blocked out-stream 37 | ;; stopped us getting a kill signal from the channel? 38 | (loop [[message arg] (!! request-chan [:http-error error [status headers]]) 62 | (>!! request-chan (read-string body))))) 63 | (recur (alts!! [response-chan (timeout 500)]))))) 64 | 65 | (defn call-http-test 66 | "Wrap call-http with a test frame. 67 | 68 | Stops http calls actually happening but replaces them with similar 69 | async behaviour." [monitor-url request-chan response-chan] 70 | (let [command-chan (chan)] 71 | (go 72 | (>! command-chan [:start :doit2]) 73 | (>! command-chan [:noop]) 74 | (>! command-chan [:start :doit]) 75 | (>! command-chan [:start :nothere]) 76 | (>! command-chan [:status]) 77 | (>! command-chan [:exit])) 78 | (with-redefs 79 | [http-kit/post 80 | (fn [url options callback] 81 | (let [[command & args] (> the url is " url " options " options " and the command " command) 84 | (Thread/sleep (options :timeout)) 85 | (>!! request-chan (if args 86 | (apply conj [command] args) 87 | [command]))))] 88 | (call-http monitor-url request-chan response-chan)))) 89 | 90 | (defn -main 91 | "Main for the spawner." [& args] ; we could take a monitor url 92 | (let [input (chan) 93 | output (chan) 94 | started (ref {})] 95 | 96 | ;; read messages from the command input 97 | (go-loop 98 | [[message & args] (! ((@started args) :from) :kill))) 112 | :noop [:noop-received] 113 | :status [:status (map #(% " started " (scripts %)) (keys @started))] 114 | :http-error [:http-error "whoops! http error!"] 115 | :exit (do (println "dieing!") [:quit]) 116 | true [:http-error "whoops! that's not a signal we understand"])] 117 | (>! output (if response response []))) ; and send response so we recur http 118 | (recur (