Merge pull request #105 from bermanjosh/remote-postgres

Remote postgres
This commit is contained in:
Christopher Reichert 2015-06-17 11:45:31 -05:00
commit 9781b49451
4 changed files with 84 additions and 24 deletions

View File

@ -8,7 +8,7 @@ module Keter.Plugin.Postgres
, load , load
) where ) where
import Control.Applicative ((<$>), (<*>)) import Control.Applicative ((<$>), (<*>), pure)
import Control.Concurrent (forkIO) import Control.Concurrent (forkIO)
import Control.Concurrent.Chan import Control.Concurrent.Chan
import Control.Concurrent.MVar import Control.Concurrent.MVar
@ -20,10 +20,12 @@ import qualified Data.Char as C
import Data.Default import Data.Default
import qualified Data.HashMap.Strict as HMap import qualified Data.HashMap.Strict as HMap
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe)
import Data.Monoid ((<>)) import Data.Monoid ((<>))
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Builder (fromText, toLazyText) import Data.Text.Lazy.Builder (fromText, toLazyText)
import qualified Data.Vector as V
import Data.Yaml import Data.Yaml
import Keter.Types import Keter.Types
import Prelude hiding (FilePath) import Prelude hiding (FilePath)
@ -48,29 +50,47 @@ instance Default Settings where
"';\nCREATE DATABASE " <> fromText dbiName <> "';\nCREATE DATABASE " <> fromText dbiName <>
" OWNER " <> fromText dbiUser <> " OWNER " <> fromText dbiUser <>
";" ";"
_ <- readProcess "sudo" ["-u", "postgres", "psql"] $ TL.unpack sql (cmd, args)
| ( dbServer dbiServer == "localhost"
|| dbServer dbiServer == "127.0.0.1") =
("sudo", ["-u", "postgres", "psql"])
| otherwise =
("psql",
[ "-h", (T.unpack $ dbServer dbiServer)
, "-p", (show $ dbPort dbiServer)
, "-U", "postgres"])
_ <- readProcess cmd args $ TL.unpack sql
return () return ()
} }
-- | Information on an individual PostgreSQL database. -- | Information on an individual PostgreSQL database.
data DBInfo = DBInfo data DBInfo = DBInfo
{ dbiName :: Text { dbiName :: Text
, dbiUser :: Text , dbiUser :: Text
, dbiPass :: Text , dbiPass :: Text
, dbiServer :: DBServerInfo
} }
deriving Show deriving Show
randomDBI :: R.StdGen -> (DBInfo, R.StdGen) data DBServerInfo = DBServerInfo
randomDBI = { dbServer :: Text
S.runState (DBInfo <$> rt <*> rt <*> rt) , dbPort :: Int
}
deriving Show
randomDBI :: DBServerInfo -> R.StdGen -> (DBInfo, R.StdGen)
randomDBI dbsi =
S.runState (DBInfo <$> rt <*> rt <*> rt <*> (pure dbsi))
where where
rt = T.pack <$> replicateM 10 (S.state $ R.randomR ('a', 'z')) rt = T.pack <$> replicateM 10 (S.state $ R.randomR ('a', 'z'))
instance ToJSON DBInfo where instance ToJSON DBInfo where
toJSON (DBInfo n u p) = object toJSON (DBInfo n u p (DBServerInfo server port)) = object
[ "name" .= n [ "name" .= n
, "user" .= u , "user" .= u
, "pass" .= p , "pass" .= p
, "server" .= server
, "port" .= port
] ]
instance FromJSON DBInfo where instance FromJSON DBInfo where
@ -78,9 +98,21 @@ instance FromJSON DBInfo where
<$> o .: "name" <$> o .: "name"
<*> o .: "user" <*> o .: "user"
<*> o .: "pass" <*> o .: "pass"
<*> (DBServerInfo
<$> o .:? "server" .!= "localhost"
<*> o .:? "port" .!= 5432)
parseJSON _ = mzero parseJSON _ = mzero
data Command = GetConfig Appname (Either SomeException DBInfo -> IO ()) instance FromJSON DBServerInfo where
parseJSON (Object o) = DBServerInfo
<$> o .: "server"
<*> o .: "port"
parseJSON _ = mzero
instance Default DBServerInfo where
def = DBServerInfo "localhost" 5432
data Command = GetConfig Appname DBServerInfo (Either SomeException DBInfo -> IO ())
-- | Load a set of existing connections from a config file. If the file does -- | Load a set of existing connections from a config file. If the file does
-- not exist, it will be created. Any newly created databases will -- not exist, it will be created. Any newly created databases will
@ -104,24 +136,29 @@ load Settings{..} fp = do
return Plugin return Plugin
{ pluginGetEnv = \appname o -> { pluginGetEnv = \appname o ->
case HMap.lookup "postgres" o of case HMap.lookup "postgres" o of
Just (Array v) -> do
let dbServer = fromMaybe def . parseMaybe parseJSON $ V.head v
doenv chan appname dbServer
Just (Bool True) -> do Just (Bool True) -> do
x <- newEmptyMVar doenv chan appname def
writeChan chan $ GetConfig appname $ putMVar x
edbi <- takeMVar x
edbiToEnv edbi
_ -> return [] _ -> return []
} }
where doenv chan appname dbs = do
x <- newEmptyMVar
writeChan chan $ GetConfig appname dbs $ putMVar x
edbi <- takeMVar x
edbiToEnv edbi
tmpfp = fp <.> "tmp" tmpfp = fp <.> "tmp"
loop chan = do loop chan = do
GetConfig appname f <- lift $ readChan chan GetConfig appname dbServer f <- lift $ readChan chan
(db, g) <- S.get (db, g) <- S.get
dbi <- dbi <-
case Map.lookup appname db of case Map.lookup appname db of
Just dbi -> return $ Right dbi Just dbi -> return $ Right dbi
Nothing -> do Nothing -> do
let (dbi', g') = randomDBI g let (dbi', g') = randomDBI dbServer g
let dbi = dbi' let dbi = dbi'
{ dbiName = sanitize appname <> dbiName dbi' { dbiName = sanitize appname <> dbiName dbi'
, dbiUser = sanitize appname <> dbiUser dbi' , dbiUser = sanitize appname <> dbiUser dbi'
@ -152,9 +189,10 @@ edbiToEnv :: Either SomeException DBInfo
-> IO [(Text, Text)] -> IO [(Text, Text)]
edbiToEnv (Left e) = throwIO e edbiToEnv (Left e) = throwIO e
edbiToEnv (Right dbi) = return edbiToEnv (Right dbi) = return
[ ("PGHOST", "localhost") [ ("PGHOST", dbServer $ dbiServer dbi)
, ("PGPORT", "5432") , ("PGPORT", T.pack . show . dbPort $ dbiServer dbi)
, ("PGUSER", dbiUser dbi) , ("PGUSER", dbiUser dbi)
, ("PGPASS", dbiPass dbi) , ("PGPASS", dbiPass dbi)
, ("PGDATABASE", dbiName dbi) , ("PGDATABASE", dbiName dbi)
] ]

20
README.md Normal file → Executable file
View File

@ -179,8 +179,26 @@ plugins:
postgres: true postgres: true
``` ```
* Keter can be configured to connect to a remote postgres server using the following syntax:
```yaml
plugins:
postgres:
- server: remoteServerNameOrIP
port: 1234
```
Different webapps can be configured to use different servers using the above syntax.
It should be noted that keter will prioritize it's own postgres.yaml record for an app.
So if moving an existing app from a local postgres server to a remote one (or
switching remote servers), the postgres.yaml file will need to be updated manually.
Keter will connect to the remote servers using the `postgres` account. This setup
assumes the remote server's `pg_hba.conf` file has been configured to allow connections
from the keter-server IP using the `trust` method.
(Note: The `plugins` configuration option was added in v1.0 of the (Note: The `plugins` configuration option was added in v1.0 of the
keter configuration syntax. If you are using v0.4 then use `postgres: true`.) keter configuration syntax. If you are using v0.4 then use `postgres: true`.
The remote-postgres server syntax was added in v1.4.2.)
* Modify your application to get its database connection settings from the following environment variables: * Modify your application to get its database connection settings from the following environment variables:
* `PGHOST` * `PGHOST`

View File

@ -67,3 +67,7 @@ stanzas:
plugins: plugins:
#postgres: true #postgres: true
# Syntax for remote-DB server:
# postgres:
# - server: remoteServerNameOrIP
# port: 1234

View File

@ -1,5 +1,5 @@
Name: keter Name: keter
Version: 1.4.1 Version: 1.4.2
Synopsis: Web application deployment manager, focusing on Haskell web frameworks Synopsis: Web application deployment manager, focusing on Haskell web frameworks
Description: Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/keter>. Description: Hackage documentation generation is not reliable. For up to date documentation, please see: <http://www.stackage.org/package/keter>.
Homepage: http://www.yesodweb.com/ Homepage: http://www.yesodweb.com/