From aa90aa3e9ecc9e5a78862412a4050bfd23bf04e0 Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Wed, 25 Jul 2018 19:30:09 +0530 Subject: [PATCH 1/3] check for updates every 24 hrs in background (fix #204) --- server/graphql-engine.cabal | 1 + server/src-exec/Main.hs | 12 ++++- server/src-lib/Hasura/Server/App.hs | 5 +- server/src-lib/Hasura/Server/Auth.hs | 3 +- server/src-lib/Hasura/Server/CheckUpdates.hs | 57 ++++++++++++++++++++ server/src-lib/Hasura/Server/Version.hs | 12 ++--- 6 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 server/src-lib/Hasura/Server/CheckUpdates.hs diff --git a/server/graphql-engine.cabal b/server/graphql-engine.cabal index ed20640f5b10e..015f9671b110a 100644 --- a/server/graphql-engine.cabal +++ b/server/graphql-engine.cabal @@ -126,6 +126,7 @@ library , Hasura.Server.Query , Hasura.Server.Utils , Hasura.Server.Version + , Hasura.Server.CheckUpdates , Hasura.RQL.Types , Hasura.RQL.Instances , Hasura.RQL.Types.SchemaCache diff --git a/server/src-exec/Main.hs b/server/src-exec/Main.hs index 4b5124fd5e2fe..f62cfb497562d 100644 --- a/server/src-exec/Main.hs +++ b/server/src-exec/Main.hs @@ -6,23 +6,25 @@ module Main where import Ops import Data.Time.Clock (getCurrentTime) -import qualified Network.Wai.Handler.Warp as Warp import Options.Applicative import System.Environment (lookupEnv) import System.Exit (exitFailure) +import qualified Control.Concurrent as C import qualified Data.Aeson as A import qualified Data.ByteString.Char8 as BC import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.Text as T import qualified Data.Yaml as Y +import qualified Network.Wai.Handler.Warp as Warp -import Hasura.Logging (mkLoggerCtx, defaultLoggerSettings) +import Hasura.Logging (defaultLoggerSettings, mkLoggerCtx) import Hasura.Prelude import Hasura.RQL.DDL.Metadata (fetchMetadata) import Hasura.Server.App (mkWaiApp) import Hasura.Server.Auth (AuthMode (..)) +import Hasura.Server.CheckUpdates (checkForUpdates) import Hasura.Server.Init import qualified Database.PG.Query as Q @@ -109,6 +111,7 @@ main = do loggerCtx <- mkLoggerCtx defaultLoggerSettings case ravenMode of ROServe (ServeOptions port cp isoL mRootDir mAccessKey corsCfg mWebHook enableConsole) -> do + mFinalAccessKey <- considerEnv "HASURA_GRAPHQL_ACCESS_KEY" mAccessKey mFinalWebHook <- considerEnv "HASURA_GRAPHQL_AUTH_HOOK" mWebHook am <- either ((>> exitFailure) . putStrLn) return $ @@ -123,7 +126,12 @@ main = do app <- mkWaiApp isoL mRootDir loggerCtx pool am finalCorsCfg enableConsole let warpSettings = Warp.setPort port Warp.defaultSettings -- Warp.setHost "*" Warp.defaultSettings + + -- start a background thread to check for updates + void $ C.forkIO checkForUpdates + Warp.runSettings warpSettings app + ROExport -> do res <- runTx ci fetchMetadata either ((>> exitFailure) . printJSON) printJSON res diff --git a/server/src-lib/Hasura/Server/App.hs b/server/src-lib/Hasura/Server/App.hs index bc0eff007f24d..b321b5f670edd 100644 --- a/server/src-lib/Hasura/Server/App.hs +++ b/server/src-lib/Hasura/Server/App.hs @@ -54,6 +54,7 @@ import Hasura.SQL.Types import qualified Hasura.Logging as L import Hasura.Server.Auth (AuthMode, getUserInfo) + consoleTmplt :: M.Template consoleTmplt = $(M.embedSingleTemplate "src-rsr/console.html") @@ -311,7 +312,9 @@ httpApp mRootDir corsCfg serverCtx enableConsole = do serveApiConsole consoleHTML else maybe (return ()) (middleware . MS.staticPolicy . MS.addBase) mRootDir - get "v1/version" getVersion + get "v1/version" $ do + uncurry setHeader jsonHeader + lazyBytes $ encode $ object [ "version" .= currentVersion ] get ("v1/template" var) tmpltGetOrDeleteH post ("v1/template" var) tmpltPutOrPostH diff --git a/server/src-lib/Hasura/Server/Auth.hs b/server/src-lib/Hasura/Server/Auth.hs index f6568e4899b28..a83fde0fbec80 100644 --- a/server/src-lib/Hasura/Server/Auth.hs +++ b/server/src-lib/Hasura/Server/Auth.hs @@ -76,8 +76,7 @@ userInfoFromWebhook manager urlT reqHeaders = do where filteredHeaders = flip filter reqHeaders $ \(n, _) -> - n /= "Content-Length" && n /= "User-Agent" && n /= "Host" - && n /= "Origin" && n /= "Referer" + n `notElem` ["Content-Length", "User-Agent", "Host", "Origin", "Referer"] validateStatus statusCode | statusCode == N.status200 = return () diff --git a/server/src-lib/Hasura/Server/CheckUpdates.hs b/server/src-lib/Hasura/Server/CheckUpdates.hs new file mode 100644 index 0000000000000..898d2bc26500a --- /dev/null +++ b/server/src-lib/Hasura/Server/CheckUpdates.hs @@ -0,0 +1,57 @@ +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TemplateHaskell #-} + +module Hasura.Server.CheckUpdates + ( checkForUpdates + ) where + +import Control.Exception (try) +import Control.Monad (forever) + +import qualified Control.Concurrent as C +import qualified Control.Lens as L +import qualified Data.Aeson as A +import qualified Data.Aeson.Casing as A +import qualified Data.Aeson.TH as A +import qualified Data.Text as T +import qualified Network.HTTP.Client as H +import qualified Network.Wreq as Wreq +import qualified System.Log.FastLogger as FL + +import Hasura.Prelude +import Hasura.Server.Version (currentVersion) + + +newtype UpdateInfo + = UpdateInfo + { uiLatest :: T.Text + } deriving (Show, Eq) + +$(A.deriveJSON (A.aesonDrop 2 A.snakeCase) ''UpdateInfo) + +checkForUpdates :: IO () +checkForUpdates = do + loggerSet <- FL.newStdoutLoggerSet FL.defaultBufSize + forever $ do + resp <- try $ Wreq.get $ T.unpack url + case resp of + Left ex -> ignoreHttpErr ex + Right bs -> do + UpdateInfo latestVersion <- decodeResp $ bs L.^. Wreq.responseBody + when (latestVersion /= currentVersion) $ + FL.pushLogStrLn loggerSet $ FL.toLogStr ("Update: A new version is available: " <> latestVersion) + + C.threadDelay aDay + + where + url = "https://releases.hasura.io/graphql-engine?agent=server&version=" + <> currentVersion + aDay = 86400 * 1000 * 1000 + + -- ignoring if there is any error in response and returning the current version + decodeResp bs = case A.eitherDecode bs of + Left _ -> return $ UpdateInfo currentVersion + Right a -> return a + + ignoreHttpErr :: H.HttpException -> IO () + ignoreHttpErr _ = return () diff --git a/server/src-lib/Hasura/Server/Version.hs b/server/src-lib/Hasura/Server/Version.hs index 2104e911fb321..b9df75b9f79f0 100644 --- a/server/src-lib/Hasura/Server/Version.hs +++ b/server/src-lib/Hasura/Server/Version.hs @@ -2,20 +2,18 @@ {-# LANGUAGE TemplateHaskell #-} module Hasura.Server.Version - ( getVersion + ( currentVersion , consoleVersion ) where import Control.Lens ((^.)) -import Data.Aeson -import Web.Spock.Core import qualified Data.SemVer as V import qualified Data.Text as T import Hasura.Prelude -import Hasura.Server.Utils (jsonHeader, runScript) +import Hasura.Server.Utils (runScript) version :: T.Text version = T.dropWhileEnd (== '\n') $ $(runScript "../scripts/get-version.sh") @@ -31,7 +29,5 @@ mkVersion ver = T.pack $ "v" ++ show major ++ "." ++ show minor major = ver ^. V.major minor = ver ^. V.minor -getVersion :: (MonadIO m) => ActionT m () -getVersion = do - uncurry setHeader jsonHeader - lazyBytes $ encode $ object [ "version" .= version ] +currentVersion :: T.Text +currentVersion = version From 2f0a802f9712b84f92db0f1f60f213f89907c787 Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Thu, 26 Jul 2018 13:11:34 +0530 Subject: [PATCH 2/3] server: use the common logger set for update checker --- server/src-exec/Main.hs | 2 +- server/src-lib/Hasura/Logging.hs | 2 +- server/src-lib/Hasura/Server/CheckUpdates.hs | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/server/src-exec/Main.hs b/server/src-exec/Main.hs index f62cfb497562d..3f6078e031d5d 100644 --- a/server/src-exec/Main.hs +++ b/server/src-exec/Main.hs @@ -128,7 +128,7 @@ main = do -- Warp.setHost "*" Warp.defaultSettings -- start a background thread to check for updates - void $ C.forkIO checkForUpdates + void $ C.forkIO $ checkForUpdates loggerCtx Warp.runSettings warpSettings app diff --git a/server/src-lib/Hasura/Logging.hs b/server/src-lib/Hasura/Logging.hs index 376db192b0c5e..64f1c55dbf36c 100644 --- a/server/src-lib/Hasura/Logging.hs +++ b/server/src-lib/Hasura/Logging.hs @@ -16,7 +16,7 @@ module Hasura.Logging , Logger , LogLevel(..) , mkLogger - , LoggerCtx + , LoggerCtx(..) , mkLoggerCtx , cleanLoggerCtx ) where diff --git a/server/src-lib/Hasura/Server/CheckUpdates.hs b/server/src-lib/Hasura/Server/CheckUpdates.hs index 898d2bc26500a..023fb6370d403 100644 --- a/server/src-lib/Hasura/Server/CheckUpdates.hs +++ b/server/src-lib/Hasura/Server/CheckUpdates.hs @@ -18,6 +18,7 @@ import qualified Network.HTTP.Client as H import qualified Network.Wreq as Wreq import qualified System.Log.FastLogger as FL +import Hasura.Logging (LoggerCtx (..)) import Hasura.Prelude import Hasura.Server.Version (currentVersion) @@ -29,9 +30,8 @@ newtype UpdateInfo $(A.deriveJSON (A.aesonDrop 2 A.snakeCase) ''UpdateInfo) -checkForUpdates :: IO () -checkForUpdates = do - loggerSet <- FL.newStdoutLoggerSet FL.defaultBufSize +checkForUpdates :: LoggerCtx -> IO () +checkForUpdates (LoggerCtx loggerSet _ _) = forever $ do resp <- try $ Wreq.get $ T.unpack url case resp of @@ -39,11 +39,12 @@ checkForUpdates = do Right bs -> do UpdateInfo latestVersion <- decodeResp $ bs L.^. Wreq.responseBody when (latestVersion /= currentVersion) $ - FL.pushLogStrLn loggerSet $ FL.toLogStr ("Update: A new version is available: " <> latestVersion) + FL.pushLogStrLn loggerSet $ FL.toLogStr $ updateMsg latestVersion C.threadDelay aDay where + updateMsg v = "Update: A new version is available: " <> v url = "https://releases.hasura.io/graphql-engine?agent=server&version=" <> currentVersion aDay = 86400 * 1000 * 1000 From abd9f045aee086d1314a251f7a58c08b5f8f7475 Mon Sep 17 00:00:00 2001 From: Anon Ray Date: Fri, 27 Jul 2018 14:19:30 +0530 Subject: [PATCH 3/3] use http manager for http requests in check updates --- server/graphql-engine.cabal | 5 +++++ server/src-exec/Main.hs | 7 +++++-- server/src-lib/Hasura/Server/App.hs | 8 +++----- server/src-lib/Hasura/Server/CheckUpdates.hs | 14 +++++++++----- server/test/Main.hs | 5 ++++- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/server/graphql-engine.cabal b/server/graphql-engine.cabal index 9e1cf3224fc2e..32cd866d8ae4b 100644 --- a/server/graphql-engine.cabal +++ b/server/graphql-engine.cabal @@ -213,6 +213,9 @@ executable graphql-engine , lens , unordered-containers >= 0.2 , pg-client + , http-client + , http-client-tls + other-modules: Ops TH @@ -242,5 +245,7 @@ test-suite graphql-engine-test , pg-client , time , yaml + , http-client + , http-client-tls other-modules: Spec \ No newline at end of file diff --git a/server/src-exec/Main.hs b/server/src-exec/Main.hs index 3f6078e031d5d..aab9e8169cc5c 100644 --- a/server/src-exec/Main.hs +++ b/server/src-exec/Main.hs @@ -17,6 +17,8 @@ import qualified Data.ByteString.Lazy as BL import qualified Data.ByteString.Lazy.Char8 as BLC import qualified Data.Text as T import qualified Data.Yaml as Y +import qualified Network.HTTP.Client as HTTP +import qualified Network.HTTP.Client.TLS as HTTP import qualified Network.Wai.Handler.Warp as Warp import Hasura.Logging (defaultLoggerSettings, mkLoggerCtx) @@ -109,6 +111,7 @@ main = do return $ mkConnInfo mEnvDbUrl rci printConnInfo ci loggerCtx <- mkLoggerCtx defaultLoggerSettings + httpManager <- HTTP.newManager HTTP.tlsManagerSettings case ravenMode of ROServe (ServeOptions port cp isoL mRootDir mAccessKey corsCfg mWebHook enableConsole) -> do @@ -123,12 +126,12 @@ main = do migrate ci pool <- Q.initPGPool ci cp putStrLn $ "server: running on port " ++ show port - app <- mkWaiApp isoL mRootDir loggerCtx pool am finalCorsCfg enableConsole + app <- mkWaiApp isoL mRootDir loggerCtx pool httpManager am finalCorsCfg enableConsole let warpSettings = Warp.setPort port Warp.defaultSettings -- Warp.setHost "*" Warp.defaultSettings -- start a background thread to check for updates - void $ C.forkIO $ checkForUpdates loggerCtx + void $ C.forkIO $ checkForUpdates loggerCtx httpManager Warp.runSettings warpSettings app diff --git a/server/src-lib/Hasura/Server/App.hs b/server/src-lib/Hasura/Server/App.hs index b321b5f670edd..330ba74b1f4d8 100644 --- a/server/src-lib/Hasura/Server/App.hs +++ b/server/src-lib/Hasura/Server/App.hs @@ -25,7 +25,6 @@ import qualified Text.Mustache.Compile as M import Web.Spock.Core import qualified Network.HTTP.Client as HTTP -import qualified Network.HTTP.Client.TLS as HTTP import qualified Network.Wai.Middleware.Static as MS import qualified Database.PG.Query as Q @@ -39,7 +38,7 @@ import qualified Network.WebSockets as WS import Hasura.Prelude hiding (get, put) import Hasura.RQL.DDL.Schema.Table -import Hasura.RQL.DML.Explain +--import Hasura.RQL.DML.Explain import Hasura.RQL.DML.QueryTemplate import Hasura.RQL.Types import Hasura.Server.Init @@ -269,11 +268,12 @@ mkWaiApp -> Maybe String -> L.LoggerCtx -> Q.PGPool + -> HTTP.Manager -> AuthMode -> CorsConfig -> Bool -> IO Wai.Application -mkWaiApp isoLevel mRootDir loggerCtx pool mode corsCfg enableConsole = do +mkWaiApp isoLevel mRootDir loggerCtx pool httpManager mode corsCfg enableConsole = do cacheRef <- do pgResp <- liftIO $ runExceptT $ Q.runTx pool (Q.Serializable, Nothing) $ do Q.catchE defaultTxErrorHandler initStateTx @@ -281,8 +281,6 @@ mkWaiApp isoLevel mRootDir loggerCtx pool mode corsCfg enableConsole = do (,) sc <$> GS.mkGCtxMap (scTables sc) either initErrExit return pgResp >>= newIORef - httpManager <- HTTP.newManager HTTP.tlsManagerSettings - cacheLock <- newMVar () let serverCtx = diff --git a/server/src-lib/Hasura/Server/CheckUpdates.hs b/server/src-lib/Hasura/Server/CheckUpdates.hs index 023fb6370d403..f4611fd49bf33 100644 --- a/server/src-lib/Hasura/Server/CheckUpdates.hs +++ b/server/src-lib/Hasura/Server/CheckUpdates.hs @@ -6,10 +6,10 @@ module Hasura.Server.CheckUpdates ) where import Control.Exception (try) +import Control.Lens import Control.Monad (forever) import qualified Control.Concurrent as C -import qualified Control.Lens as L import qualified Data.Aeson as A import qualified Data.Aeson.Casing as A import qualified Data.Aeson.TH as A @@ -30,14 +30,18 @@ newtype UpdateInfo $(A.deriveJSON (A.aesonDrop 2 A.snakeCase) ''UpdateInfo) -checkForUpdates :: LoggerCtx -> IO () -checkForUpdates (LoggerCtx loggerSet _ _) = +checkForUpdates :: LoggerCtx -> H.Manager -> IO () +checkForUpdates (LoggerCtx loggerSet _ _) manager = do + let options = Wreq.defaults + & Wreq.checkResponse ?~ (\_ _ -> return ()) + & Wreq.manager .~ Right manager + forever $ do - resp <- try $ Wreq.get $ T.unpack url + resp <- try $ Wreq.getWith options $ T.unpack url case resp of Left ex -> ignoreHttpErr ex Right bs -> do - UpdateInfo latestVersion <- decodeResp $ bs L.^. Wreq.responseBody + UpdateInfo latestVersion <- decodeResp $ bs ^. Wreq.responseBody when (latestVersion /= currentVersion) $ FL.pushLogStrLn loggerSet $ FL.toLogStr $ updateMsg latestVersion diff --git a/server/test/Main.hs b/server/test/Main.hs index 32bc313a289b1..4562ff87e4ba5 100644 --- a/server/test/Main.hs +++ b/server/test/Main.hs @@ -22,6 +22,8 @@ import Hasura.Server.Auth (AuthMode (..)) import qualified Database.PG.Query as PGQ +import qualified Network.HTTP.Client as HTTP +import qualified Network.HTTP.Client.TLS as HTTP import Hasura.Server.Init import Ops (initCatalogSafe) @@ -42,8 +44,9 @@ resetStateTx = do ravenApp :: L.LoggerCtx -> PGQ.PGPool -> IO Application ravenApp loggerCtx pool = do let corsCfg = CorsConfigG "*" False -- cors is enabled + httpManager <- HTTP.newManager HTTP.tlsManagerSettings -- spockAsApp $ spockT id $ app Q.Serializable Nothing rlogger pool AMNoAuth corsCfg True -- no access key and no webhook - mkWaiApp Q.Serializable Nothing loggerCtx pool AMNoAuth corsCfg True -- no access key and no webhook + mkWaiApp Q.Serializable Nothing loggerCtx pool httpManager AMNoAuth corsCfg True -- no access key and no webhook main :: IO () main = do