Read settings from config file (#714)

This commit is contained in:
Joe Nelson
2016-10-20 08:34:57 -07:00
committed by GitHub
parent 74e68408e6
commit 9233d90075
6 changed files with 121 additions and 46 deletions
+1
View File
@@ -32,6 +32,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
- HTTP 401 rather than 400 for expired JWT - @begriffs
- Remove default JWT secret - @begriffs
- Use GUC request.jwt.claim.foo rather than postgrest.claims.foo - @begriffs
- Use config file rather than command line arguments - @begriffs
## [0.3.2.0] - 2016-06-10
+4 -13
View File
@@ -1,11 +1,6 @@
FROM debian:jessie
ENV POSTGREST_VERSION 0.3.2.0
ENV POSTGREST_SCHEMA public
ENV POSTGREST_ANONYMOUS postgres
ENV POSTGREST_JWT_SECRET thisisnotarealsecret
ENV POSTGREST_MAX_ROWS 1000000
ENV POSTGREST_POOL 200
ENV POSTGREST_VERSION 0.4.0.0
RUN apt-get update && \
apt-get install -y tar xz-utils wget libpq-dev && \
@@ -16,12 +11,8 @@ RUN wget http://github.com/begriffs/postgrest/releases/download/v${POSTGREST_VER
mv postgrest /usr/local/bin/postgrest && \
rm postgrest-${POSTGREST_VERSION}-ubuntu.tar.xz
CMD exec postgrest postgres://${PG_ENV_POSTGRES_USER}:${PG_ENV_POSTGRES_PASSWORD}@${PG_PORT_5432_TCP_ADDR}:${PG_PORT_5432_TCP_PORT}/${PG_ENV_POSTGRES_DB} \
--port 3000 \
--schema ${POSTGREST_SCHEMA} \
--anonymous ${POSTGREST_ANONYMOUS} \
--pool ${POSTGREST_POOL} \
--jwt-secret ${POSTGREST_JWT_SECRET} \
--max-rows ${POSTGREST_MAX_ROWS}
# PostgREST reads /etc/postgrest.conf so map the configuration
# file in when you run this container
CMD exec postgrest
EXPOSE 3000
+6
View File
@@ -8,12 +8,14 @@ import PostgREST.Config (AppConfig (..),
minimumPgVersion,
prettyVersion,
readOptions)
import PostgREST.Error (prettyUsageError)
import PostgREST.OpenAPI (isMalformedProxyUri)
import PostgREST.DbStructure
import Control.AutoUpdate
import Data.String (IsString (..))
import Data.Text (stripPrefix)
import Data.Text.IO (hPutStrLn)
import Data.Function (id)
import Data.Time.Clock.POSIX (getPOSIXTime)
import qualified Hasql.Query as H
@@ -68,6 +70,10 @@ main = do
<> show minimumPgVersion)
getDbStructure (toS $ configSchema conf)
forM_ (lefts [result]) $ \e -> do
hPutStrLn stderr (prettyUsageError e)
exitFailure
refDbStructure <- newIORef $ either (panic . show) id result
#ifndef mingw32_HOST_OS
+2
View File
@@ -51,11 +51,13 @@ library
, bytestring
, case-insensitive
, cassava
, configurator
, containers
, contravariant
, hasql
, hasql-pool == 0.4.1
, hasql-transaction == 0.4.5.1
, heredoc
, HTTP
, http-types
, insert-ordered-containers
+102 -32
View File
@@ -2,12 +2,13 @@
Module : PostgREST.Config
Description : Manages PostgREST configuration options.
This module provides a helper function to read the command line arguments using the optparse-applicative
and the AppConfig type to store them.
It also can be used to define other middleware configuration that may be delegated to some sort of
external configuration.
This module provides a helper function to read the command line
arguments using the optparse-applicative and the AppConfig type to store
them. It also can be used to define other middleware configuration that
may be delegated to some sort of external configuration.
It currently includes a hardcoded CORS policy but this could easly be turned in configurable behaviour if needed.
It currently includes a hardcoded CORS policy but this could easly be
turned in configurable behaviour if needed.
Other hardcoded options such as the minimum version number also belong here.
-}
@@ -19,20 +20,26 @@ module PostgREST.Config ( prettyVersion
)
where
import System.IO.Error (IOError)
import Control.Applicative
import qualified Data.ByteString.Char8 as BS
import qualified Data.CaseInsensitive as CI
import qualified Data.Configurator as C
import qualified Data.Configurator.Types as C
import Data.List (lookup)
import Data.Text (strip, intercalate)
import Data.Text.IO (hPutStrLn)
import Data.Version (versionBranch)
import Network.Wai
import Network.Wai.Middleware.Cors (CorsResourcePolicy (..))
import Options.Applicative
import Options.Applicative hiding (str)
import Paths_postgrest (version)
import Text.Heredoc
import Protolude hiding (intercalate
, (<>))
import Safe (readMay)
-- | Data type to store all command line options
-- | Config file settings for the server
data AppConfig = AppConfig {
configDatabase :: Text
, configAnonRole :: Text
@@ -47,20 +54,6 @@ data AppConfig = AppConfig {
, configQuiet :: Bool
}
argParser :: Parser AppConfig
argParser = AppConfig
<$> (toS <$> argument str (help "(REQUIRED) database connection string, e.g. postgres://user:pass@host:port/db" <> metavar "DB_URL"))
<*> (toS <$> strOption (long "anonymous" <> short 'a' <> help "(REQUIRED) postgres role to use for non-authenticated requests" <> metavar "ROLE"))
<*> (optional . map toS . strOption) (long "proxy-uri" <> short 'x' <> help "proxy uri of the HTTP server" <> metavar "PROXY")
<*> (toS <$> strOption (long "schema" <> short 's' <> help "schema to use for API routes" <> metavar "NAME" <> value "public" <> showDefault))
<*> (toS <$> strOption (long "host" <> short 'l' <> help "hostname or ip on which to run HTTP server" <> metavar "HOST" <> value "*4" <> showDefault))
<*> option auto (long "port" <> short 'p' <> help "port number on which to run HTTP server" <> metavar "PORT" <> value 3000 <> showDefault)
<*> (optional . map toS <$> strOption) (long "jwt-secret" <> short 'j' <> help "secret used to encrypt and decrypt JWT tokens" <> metavar "SECRET")
<*> option auto (long "pool" <> short 'o' <> help "max connections in database pool" <> metavar "COUNT" <> value 10 <> showDefault)
<*> (readMay <$> strOption (long "max-rows" <> short 'm' <> help "max rows in response" <> metavar "COUNT" <> value "infinity" <> showDefault))
<*> (optional . map toS . strOption) (long "pre-request" <> help "schema-qualified name of proc to call to validate requests" <> metavar "FUNCTION")
<*> pure False
defaultCorsPolicy :: CorsResourcePolicy
defaultCorsPolicy = CorsResourcePolicy Nothing
["GET", "POST", "PATCH", "DELETE", "OPTIONS"] ["Authorization"] Nothing
@@ -90,16 +83,93 @@ prettyVersion = intercalate "." $ map show $ versionBranch version
-- | Function to read and parse options from the command line
readOptions :: IO AppConfig
readOptions = customExecParser parserPrefs opts
where
opts = info (helper <*> argParser) $
fullDesc
<> progDesc (
"PostgREST "
<> toS prettyVersion
<> " / create a REST API to an existing Postgres database"
)
parserPrefs = prefs showHelpOnError
readOptions = do
args <- customExecParser parserPrefs opts
when (caExample args) $ do
putStrLn (
[str|db-uri = "postgres://user:pass@localhost:5432/dbname"
|db-schema = "public"
|db-anon-role = "postgres"
|db-pool = 10
|
|server-host = "*4"
|server-port = 3000
|
|## base url for swagger output
|# server-proxy-uri = ""
|
|## choose a secret to enable JWT auth
|## (use "@filename" to load from separate file)
|# jwt-secret = "foo"
|
|## limit rows in response
|# max-rows = 1000
|
|## stored proc to exec immediately after auth
|# pre-request = "stored_proc_name"
|]::Text)
exitSuccess
conf <- catch
(C.load [C.Required $ caConfig args])
configNotfoundHint
handle missingKeyHint $ do
-- db ----------------
cDbUri <- C.require conf "db-uri"
cDbSchema <- C.require conf "db-schema"
cDbAnon <- C.require conf "db-anon-role"
cPool <- C.lookupDefault 10 conf "db-pool"
-- server ------------
cHost <- C.lookupDefault "*4" conf "server-host"
cPort <- C.lookupDefault 3000 conf "server-port"
cProxy <- C.lookup conf "server-proxy-uri"
-- jwt ---------------
cJwtSec <- C.lookup conf "jwt-secret"
-- safety ------------
cMaxRows <- C.lookup conf "max-rows"
cReqCheck <- C.lookup conf "pre-request"
return $ AppConfig cDbUri cDbAnon cProxy cDbSchema cHost cPort
cJwtSec cPool cMaxRows cReqCheck False
where
opts = info (helper <*> argParser) $
fullDesc
<> progDesc (
"PostgREST "
<> toS prettyVersion
<> " / create a REST API to an existing Postgres database"
)
parserPrefs = prefs showHelpOnError
configNotfoundHint :: IOError -> IO a
configNotfoundHint e = do
hPutStrLn stderr $ intercalate "\n" [
"Cannot open config file:",
"\t" <> show e,
"\nUse the --help flag to learn how to fix this."]
exitFailure
missingKeyHint :: C.KeyError -> IO a
missingKeyHint (C.KeyError n) = do
hPutStrLn stderr $
"Required config parameter \"" <> n <> "\" is missing or of wrong type.\n" <>
"Try the --example-config option to see how to configure PostgREST."
exitFailure
data CmdArgs = CmdArgs {
caConfig :: FilePath
, caExample :: Bool
}
argParser :: Parser CmdArgs
argParser = CmdArgs <$>
(toS <$> strOption
(short 'c' <> metavar "filename" <>
help "Path to configuration file")) <*>
switch (long "example-config" <> help "output an example config file")
-- | Tells the minimum PostgreSQL version required by this version of PostgREST
minimumPgVersion :: Integer
+6 -1
View File
@@ -2,7 +2,7 @@
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeSynonymInstances #-}
module PostgREST.Error (pgErrResponse, errResponse) where
module PostgREST.Error (pgErrResponse, errResponse, prettyUsageError) where
import Protolude
import Data.Aeson ((.=))
@@ -29,6 +29,11 @@ pgErrResponse authed e =
else [jsonType] in
responseLBS status hdrs (JSON.encode e)
prettyUsageError :: P.UsageError -> Text
prettyUsageError (P.ConnectionError e) =
"Database connection error:\n" <> toS (fromMaybe "" e)
prettyUsageError e = show $ JSON.encode e
instance JSON.ToJSON P.UsageError where
toJSON (P.ConnectionError e) = JSON.object [
"code" .= ("" :: Text),