Making new config more robust

This commit is contained in:
simon 2017-09-12 16:29:23 +01:00
parent a7aca5fab2
commit 009ae30a08
6 changed files with 119 additions and 42 deletions

View file

@ -22,20 +22,22 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;; config.edn: a simple configuration map for Smeagol; inspired by Cryogen.
;;; This is top-level configuration.
;;; This is top-level configuration. All values can be overridden with
;;; environment variables.
;; ; ; ; ; ; ; ; ; ;
{
:site-title "Smeagol" ;; overall title of the site, used in page headings
:content-dir "resources/public/content"
;; where content is served from.
:default-locale "en-GB" ;; default language used for messages
;; :content-dir "/home/simon/tmp/test-content"
;; where content is served from
:passwd "/home/simon/tmp/passwd"
;; where the password file is stored
:log-level :info ;; the minimum logging level; one of
;; TRACE DEBUG INFO WARN ERROR FATAL
:formatters {"vega" smeagol.formatting/process-vega
"vis" smeagol.formatting/process-vega
"mermaid" smeagol.formatting/process-mermaid
"backticks" smeagol.formatting/process-backticks}
:log-level :info ;; the minimum logging level; one of
;; :trace :debug :info :warn :error :fatal
:passwd "resources/passwd"
;; where the password file is stored
:site-title "Smeagol" ;; overall title of the site, used in
;; page headings
}

View file

@ -1 +1 @@
{:admin {:admin true, :email "info@weft.scot", :password "admin"}}
{:admin {:admin true, :email "info@weft.scot", :password "admin"}, :simon {:email "simon@journeyman.cc", :admin true, :password "$s0$f0801$sqhbxtzK6nx9RnVUhwtQlg==$dMIUbof8esjsGyiB+zb3gMH21L/WSCR+wD3vIag4EVc="}}

View file

@ -1,3 +1,8 @@
## Choosing a deployment mechanism
There are currently three ways you can deploy Smeagol: as an executable Jar file, as a Docker image, and as a web-app in a [Servlet container](https://en.wikipedia.org/wiki/Web_container). Each method has advantages and disadvantages.
The Jar file is extremely easy to deploy and to configure, but cannot currently serve [HTTPS](https://en.wikipedia.org/wiki/HTTPS), which, on the modern web, is a significant disadvantage. The Docker image is just a wrapper around the Jar file; it's particularly suitable for automated deployment. The web-app solution offloads responsibility for things like HTTPS to the Servlet container, and consequently can be much more secure; but it can really only be configured at compile time.
## Deploying as a stand-alone application
To deploy Smeagol as a stand-alone application, either download the jar file for the release you want to deploy, or clone the source and compile it with:
@ -9,7 +14,7 @@ This will create a jar file in the `target` directory, named `smeagol-`*VERSION*
Smeagol cannot access either its configuration or its content from the jar file, as otherwise they would not be editable. There are three solutions to this:
### Custom configuration file
You can copy the standard [[Configuration]] file `resources/config.edn` to somewhere outside the jar file, edit it to suit your installation, and set up a single environment variable, `SMEAGOL_CONFIG`, whose value is the path to your new configuration file.
You can copy the standard configuration file `resources/config.edn` to somewhere outside the jar file, edit it to suit your installation, and set up a single environment variable, `SMEAGOL_CONFIG`, whose value is the path to your new configuration file.
### Environment variables
Alternatively, you can configure everything through [[Environment Variables]].
@ -18,13 +23,14 @@ Alternatively, you can configure everything through [[Environment Variables]].
You can have both a configuration file and environment variables. If you do this, the environment variables override the values in the configuration file.
### Necessary content
**NOTE** that `SMEAGOL_CONTENT_DIR` must contain at least the following files:
**NOTE** that the directory at `SMEAGOL_CONTENT_DIR` must contain at least the following files:
1. `_edit-side-bar.md` - the side-bar that should be displayed when editing pages;
2. `_header.md` - the header to be displayed on all pages;
3. `_side-bar.md` - the side-bar that should be displayed when not editing pages.
Standard versions of these files can be found in the [source repository](https://github.com/journeyman-cc/smeagol/tree/master/resources/public/content). All these files should be in markdown format - see [[Extensible Markup]].
Standard versions of these files can be found in the [source repository](https://github.com/journeyman-cc/smeagol/tree/master/resources/public/content).
You can run the jar file with:
@ -38,8 +44,31 @@ To deploy Smeagol within a servlet container, either download the jar file for t
This will create a war file in the `target` directory, named `smeagol-`*VERSION*`-standalone.war`. Deploy this to your servlet container in the normal way; details will depend on your container. Instructions for Tomcat are [here](https://tomcat.apache.org/tomcat-8.0-doc/deployer-howto.html).
The problem with this is that unless the environment variables (see above) were already set up in the environment of the servlet container at the time when the servlet container were launched, Smeagol will run with its built-in defaults. This will run perfectly satisfactorily provided your servlet container is configured to unpack war files, which most are.
The problem with this is that unless the environment variables (see above) were already set up in the environment of the servlet container at the time when the servlet container were launched, Smeagol will run with its built-in defaults. If you want to change the defaults, you would have to edit the `resources/config.edn` file and recompile the war file.
Smeagol will run as a web-app with the default configuration perfectly satisfactorily.
## Experimental Docker image
You can now run Smeagol as a [Docker](http://www.docker.com) image. Read more about using the [[Docker Image]].
You can now run Smeagol as a [Docker](http://www.docker.com) image. Read more about [[Using the Docker Image]].
To run my Docker image, use
docker run simonbrooke/smeagol
Smeagol will run, obviously, on the IP address of your Docker image, on port 8080. To find the IP address, start the image using the command above and then use
docker inspect --format '{{ .NetworkSettings.IPAddress }}' $(docker ps -q)
Suppose this prints '10.10.10.10', then the URL to browse to will be http://10.10.10.10:8080/smeagol/
This image is _experimental_, but it does seem to work fairly well. What it does **not** yet do, however, is push the git repository to a remote location, so when you tear the Docker image down your edits will be lost. My next objective for this image is for it to have a cammand line parameter being the git address of a repository from which it can initialise the Wiki content, and to which it will periodically push local changes to the Wiki content.
To build your own Docker image, run:
lein clean
lein bower install
lein ring uberwar
lein docker build
This will build a new Docker image locally; you can, obviously, push it to your own Docker repository if you wish.

View file

@ -1,3 +1,4 @@
## Smeagol-specific environment variables
Smeagol can be configured entirely with environment variables. The variables are:
1. `SMEAGOL_CONFIG` (optional but advised) should be the full or relative pathname of a Smeagol [[Configuration]] file;
@ -9,3 +10,14 @@ Smeagol can be configured entirely with environment variables. The variables are
7. `SMEAGOL_SITE_TITLE` which should be the title you want shown on the header of all pages.
You can have both a configuration file and environment variables; if you do, the values of the environment variables take precedence over the values in the config file.
## Other environment variables
If Smeagol is compiled as an executable jar file, the actual web server component is [Ring server](https://github.com/weavejester/ring-server). This recognises the `PORT` environment variable, and, if this is present and its value is a positive integer, will listen on the specified port (otherwise its default is 3000, which is... unusual).
Smeagol uses the [Timbre](https://github.com/ptaoussanis/timbre) logging library. This recognises the following environment variables:
1. `TIMBRE_DEFAULT_STACKTRACE_FONTS` Timbre by default colourises stacktrace dumps using ANSI terminal codes. This can be quite useful in a console, but is a real pain in a log file. To turn colourised stacktraces off, set the value of this to an empty string;
2. `TIMBRE_LEVEL` Sets the minimum logging level; but there are two problems with this. The first is that the environment variable is only read at compile time not at run time, and the second is that the syntax is a bit odd, which is why I've implemented `SMEAGOL_LOG_LEVEL` (above);
3. `TIMBRE_NS_WHITELIST` Sets a list of [Clojure namespaces](https://clojure.org/reference/namespaces) from which messages should be logged; however this is only read at compile time so isn't much use in practice;
4. `TIMBRE_NS_BLACKLIST` As above, but sets a list of namespaces from which messages should **not** be logged.

View file

@ -1,7 +1,9 @@
(ns ^{:doc "Read and make available configuration."
:author "Simon Brooke"}
smeagol.configuration
(:require [environ.core :refer [env]]
(:require [clojure.pprint :refer [pprint]]
[clojure.string :as s]
[environ.core :refer [env]]
[noir.io :as io]
[taoensso.timbre :as timbre]))
@ -54,6 +56,16 @@
vars))
(defn to-keyword
"Convert this argument into an idiomatic clojure keyword."
[arg]
(if (and arg (not (keyword? arg)))
(keyword
(s/lower-case
(s/replace (str arg) #"[^A-Za-z0-9]+" "-")))
arg))
(defn transform-map
"transform this map `m` by applying these `transforms`. Each transforms
is expected to comprise a map with the keys :from and :to, whose values
@ -61,14 +73,21 @@
and optionally a key :transform, whose value is a function of one
argument to be used to transform the value of that key."
[m tuples]
(timbre/debug
"transform-map:\n"
(with-out-str (clojure.pprint/pprint m)))
(reduce
(fn [m tuple]
(if
(and (map? tuple) (map? m) (m (:from tuple)))
(let [old-val (m (:from tuple))
t (:transform tuple)
new-val (if t (apply t (list old-val)) old-val)]
(assoc (dissoc m (:from tuple)) (:to tuple) new-val))
t (:transform tuple)]
(assoc
(dissoc m (:from tuple))
(:to tuple)
(if-not
(nil? t)
(eval (list t old-val)) old-val)))
m))
m
tuples))
@ -78,27 +97,41 @@
"Transforms to use with `transform-map` to convert environment
variable names (which need to be specific) into the shorter names
used internally"
'( {:from :smeagol-site-title :to :site-title}
'( {:from :smeagol-content-dir :to :content-dir}
{:from :smeagol-default-locale :to :default-locale}
{:from :smeagol-formatters :to :formatters :transform read-string}
{:from :smeagol-content-dir :to :content-dir}
{:from :smeagol-log-level :to :log-level :transform to-keyword}
{:from :smeagol-passwd :to :passwd}
{:from :smeagol-log-level :to :log-level :transform (fn [s] (keyword (lower-case s)))}))
{:from :smeagol-site-title :to :site-title}))
(def config
(defn build-config
[]
"The actual configuration, as a map. The idea here is that the config
file is read (if it is specified and present), but that individual
values "
values can be overridden by environment variables."
(try
(let [file-contents (try
(read-string (slurp config-file-path))
(catch Exception _ {}))]
(merge
(catch Exception _ {}))
config (merge
file-contents
(transform-map
(from-env-vars :smeagol-site-title :smeagol-default-locale)
config-env-transforms)))
(from-env-vars
:smeagol-content-dir
:smeagol-default-locale
:smeagol-formatters
:smeagol-log-level
:smeagol-passwd
:smeagol-site-title)
config-env-transforms))]
(if (env :dev)
(timbre/debug
"Loaded configuration\n"
(with-out-str (clojure.pprint/pprint config))))
config)
(catch Exception any
(timbre/error any "Could not load configuration")
{})))
(def config (build-config))

View file

@ -1,4 +1,5 @@
(ns ^{:doc "Functions related to sanity checks and error reporting in conditions where the environment may not be sane."
(ns ^{:doc "Functions related to sanity checks and error reporting in conditions
where the environment may not be sane."
:author "Simon Brooke"}
smeagol.sanity
(:import (java.util Locale))
@ -412,7 +413,7 @@
(timbre/warn "Sanity check completed; " (count (keys result)) " problem(s) found")
(sanity-check-report result))
(do
(timbre/info "Sanity check completed; no problem(s) found")
(timbre/info "Sanity check completed; no problems found")
nil))))