assistant restart on upgrade

This commit is contained in:
Joey Hess 2013-11-22 23:12:06 -04:00
parent ad653e49cf
commit b9cdb55e0c
9 changed files with 127 additions and 25 deletions

View file

@ -153,7 +153,7 @@ startDaemon assistant foreground startdelay cannotrun listenhost startbrowser =
#endif #endif
, assist $ netWatcherThread , assist $ netWatcherThread
, assist $ upgraderThread urlrenderer , assist $ upgraderThread urlrenderer
, assist $ upgradWatcherThread , assist $ upgradWatcherThread urlrenderer
, assist $ netWatcherFallbackThread , assist $ netWatcherFallbackThread
, assist $ transferScannerThread urlrenderer , assist $ transferScannerThread urlrenderer
, assist $ cronnerThread urlrenderer , assist $ cronnerThread urlrenderer

View file

@ -217,11 +217,10 @@ notFsckedAlert mr button = Alert
canUpgradeAlert :: AlertPriority -> AlertButton -> Alert canUpgradeAlert :: AlertPriority -> AlertButton -> Alert
canUpgradeAlert priority button = Alert canUpgradeAlert priority button = Alert
{ alertHeader = Just $ fromString $ concat { alertHeader = Just $ fromString $
[ if priority >= High if priority >= High
then "An important upgrade of git-annex is available!" then "An important upgrade of git-annex is available!"
else "An upgrade of git-annex is available." else "An upgrade of git-annex is available."
]
, alertIcon = Just UpgradeIcon , alertIcon = Just UpgradeIcon
, alertPriority = priority , alertPriority = priority
, alertButton = Just button , alertButton = Just button
@ -230,11 +229,31 @@ canUpgradeAlert priority button = Alert
, alertMessageRender = renderData , alertMessageRender = renderData
, alertCounter = 0 , alertCounter = 0
, alertBlockDisplay = True , alertBlockDisplay = True
, alertName = Just UpgradeAlert , alertName = Just CanUpgradeAlert
, alertCombiner = Just $ dataCombiner $ \_old new -> new , alertCombiner = Just $ dataCombiner $ \_old new -> new
, alertData = [] , alertData = []
} }
upgradeReadyAlert :: AlertButton -> Alert
upgradeReadyAlert button = Alert
{ alertHeader = Just $ fromString
"A new version of git-annex has been installed."
, alertIcon = Just UpgradeIcon
, alertPriority = High
, alertButton = Just button
, alertClosable = True
, alertClass = Message
, alertMessageRender = renderData
, alertCounter = 0
, alertBlockDisplay = True
, alertName = Just UpgradeReadyAlert
, alertCombiner = Just $ dataCombiner $ \_old new -> new
, alertData = []
}
upgradingAlert :: Alert
upgradingAlert = activityAlert Nothing [fromString "Upgrading git-annex"]
brokenRepositoryAlert :: AlertButton -> Alert brokenRepositoryAlert :: AlertButton -> Alert
brokenRepositoryAlert = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!" brokenRepositoryAlert = errorAlert "Serious problems have been detected with your repository. This needs your immediate attention!"

View file

@ -5,6 +5,8 @@
- Licensed under the GNU GPL version 3 or higher. - Licensed under the GNU GPL version 3 or higher.
-} -}
{-# LANGUAGE CPP #-}
module Assistant.Threads.UpgradeWatcher ( module Assistant.Threads.UpgradeWatcher (
upgradWatcherThread upgradWatcherThread
) where ) where
@ -13,19 +15,29 @@ import Assistant.Common
import Utility.DirWatcher import Utility.DirWatcher
import Utility.DirWatcher.Types import Utility.DirWatcher.Types
import Config.Files import Config.Files
import qualified Utility.Lsof as Lsof
import Utility.ThreadScheduler
import Assistant.Types.UrlRenderer
import Assistant.Alert
import Assistant.DaemonStatus
#ifdef WITH_WEBAPP
import Assistant.WebApp.Types
#endif
import Control.Concurrent.MVar import Control.Concurrent.MVar
import Data.Tuple.Utils
import qualified Data.Text as T
data WatcherState = InStartupScan | Started | Upgrading data WatcherState = InStartupScan | Started | Upgrading
deriving (Eq) deriving (Eq)
upgradWatcherThread :: NamedThread upgradWatcherThread :: UrlRenderer -> NamedThread
upgradWatcherThread = namedThread "UpgradeWatcher" $ go =<< liftIO programPath upgradWatcherThread urlrenderer = namedThread "UpgradeWatcher" $ go =<< liftIO programPath
where where
go Nothing = debug [ "cannot determine program path" ] go Nothing = debug [ "cannot determine program path" ]
go (Just program) = do go (Just program) = do
mvar <- liftIO $ newMVar InStartupScan mvar <- liftIO $ newMVar InStartupScan
changed <- Just <$> asIO2 (changedFile mvar program) changed <- Just <$> asIO2 (changedFile urlrenderer mvar program)
let hooks = mkWatchHooks let hooks = mkWatchHooks
{ addHook = changed { addHook = changed
, addSymlinkHook = changed , addSymlinkHook = changed
@ -42,10 +54,44 @@ upgradWatcherThread = namedThread "UpgradeWatcher" $ go =<< liftIO programPath
void $ swapMVar mvar Started void $ swapMVar mvar Started
return r return r
changedFile :: MVar WatcherState -> FilePath -> FilePath -> Maybe FileStatus -> Assistant () changedFile :: UrlRenderer -> MVar WatcherState -> FilePath -> FilePath -> Maybe FileStatus -> Assistant ()
changedFile mvar program file _status changedFile urlrenderer mvar program file _status
| program == file = do | program /= file = noop
| otherwise = do
state <- liftIO $ readMVar mvar state <- liftIO $ readMVar mvar
when (state == Started) $ when (state == Started) $ do
debug [ "saw change to", file ] setstate Upgrading
| otherwise = noop ifM (sanityCheck program)
( handleUpgrade urlrenderer
, do
debug ["new version of", program, "failed sanity check; not using"]
setstate Started
)
where
setstate = void . liftIO . swapMVar mvar
{- The program's file has been changed. Before restarting,
- it needs to not be open for write by anything, and should run
- successfully when run with the parameter "version".
-}
sanityCheck :: FilePath -> Assistant Bool
sanityCheck program = do
whileM (liftIO haswriter) $ do
debug [program, "is still being written; waiting"]
liftIO $ threadDelaySeconds (Seconds 60)
debug [program, "has changed, and seems to be ready to run"]
liftIO $ boolSystem program [Param "version"]
where
haswriter = not . null
. filter (`elem` [Lsof.OpenReadWrite, Lsof.OpenWriteOnly])
. map snd3
<$> Lsof.query [program]
handleUpgrade :: UrlRenderer -> Assistant ()
handleUpgrade urlrenderer = do
#ifdef WITH_WEBAPP
button <- mkAlertButton True (T.pack "Finish Upgrade") urlrenderer ConfigFinishUpgradeR
void $ addAlert (upgradeReadyAlert button)
#else
noop
#endif

View file

@ -73,7 +73,7 @@ checkUpgrade urlrenderer = do
canUpgrade :: AlertPriority -> UrlRenderer -> GitAnnexDistribution -> Assistant () canUpgrade :: AlertPriority -> UrlRenderer -> GitAnnexDistribution -> Assistant ()
canUpgrade urgency urlrenderer d = do canUpgrade urgency urlrenderer d = do
#ifdef WITH_WEBAPP #ifdef WITH_WEBAPP
button <- mkAlertButton False (T.pack "Upgrade") urlrenderer (ConfigUpgradeR d) button <- mkAlertButton True (T.pack "Upgrade") urlrenderer (ConfigStartUpgradeR d)
void $ addAlert (canUpgradeAlert urgency button) void $ addAlert (canUpgradeAlert urgency button)
#else #else
noop noop

View file

@ -31,7 +31,8 @@ data AlertName
| CloudRepoNeededAlert | CloudRepoNeededAlert
| SyncAlert | SyncAlert
| NotFsckedAlert | NotFsckedAlert
| UpgradeAlert | CanUpgradeAlert
| UpgradeReadyAlert
deriving (Eq) deriving (Eq)
{- The first alert is the new alert, the second is an old alert. {- The first alert is the new alert, the second is an old alert.

View file

@ -10,15 +10,47 @@
module Assistant.WebApp.Configurators.Upgrade where module Assistant.WebApp.Configurators.Upgrade where
import Assistant.WebApp.Common import Assistant.WebApp.Common
import qualified Annex
import Types.Distribution import Types.Distribution
import Assistant.WebApp.OtherRepos
import Assistant.Threads.Watcher
import Assistant.Alert
import Assistant.DaemonStatus
import Assistant.NamedThread
import Utility.ThreadScheduler
import Utility.HumanTime import Utility.HumanTime
import Git
import Data.Time.Clock import Data.Time.Clock
import Control.Concurrent
getConfigUpgradeR :: GitAnnexDistribution -> Handler Html getConfigStartUpgradeR :: GitAnnexDistribution -> Handler Html
getConfigUpgradeR d = page "Upgrade git-annex" (Just Configuration) $ do getConfigStartUpgradeR d = page "Upgrade git-annex" (Just Configuration) $ do
now <- liftIO getCurrentTime now <- liftIO getCurrentTime
let upgradeage = Duration $ floor $ let upgradeage = Duration $ floor $
now `diffUTCTime` distributionReleasedate d now `diffUTCTime` distributionReleasedate d
$(widgetFile "configurators/upgrade") $(widgetFile "configurators/upgrade/start")
{- Finish upgrade by starting the new assistant in the same repository this
- one is running in, and redirecting to it.
-
- Before the new assistant can be started, have to remove our
- gitAnnexUrlFile and our gitAnnexPidFile. Pausing the watcher is also
- a good idea, to avoid fighting when two assistants are running in the
- same repo.
-}
getConfigFinishUpgradeR :: Handler Html
getConfigFinishUpgradeR = do
liftAssistant $ void $ addAlert upgradingAlert
liftIO . maybe noop (`throwTo` PauseWatcher) =<< liftAssistant (namedThreadId watchThread)
liftIO . nukeFile =<< liftAnnex (fromRepo gitAnnexUrlFile)
liftIO . nukeFile =<< liftAnnex (fromRepo gitAnnexPidFile)
ret <- switchToAssistant =<< liftAnnex (repoLocation <$> Annex.gitRepo)
void . liftIO . forkIO =<< liftAssistant (asIO reaper)
return ret
where
-- Wait for the redirect to be served to the browser
-- before terminating this process.
reaper = do
liftIO $ threadDelaySeconds (Seconds 120)
liftIO $ exitSuccess

View file

@ -35,14 +35,17 @@ listOtherRepos = do
names <- mapM relHome gooddirs names <- mapM relHome gooddirs
return $ sort $ zip names gooddirs return $ sort $ zip names gooddirs
{- Starts up the assistant in the repository, and waits for it to create
- a gitAnnexUrlFile. Waits for the assistant to be up and listening for
- connections by testing the url. Once it's running, redirect to it.
-}
getSwitchToRepositoryR :: FilePath -> Handler Html getSwitchToRepositoryR :: FilePath -> Handler Html
getSwitchToRepositoryR repo = do getSwitchToRepositoryR repo = do
liftIO $ startAssistant repo
liftIO $ addAutoStartFile repo -- make this the new default repo liftIO $ addAutoStartFile repo -- make this the new default repo
switchToAssistant repo
{- Starts up the assistant in the repository, and waits for it to create
- a gitAnnexUrlFile. Waits for the assistant to be up and listening for
- connections by testing the url. Once it's running, redirect to it. -}
switchToAssistant :: FilePath -> Handler Html
switchToAssistant repo = do
liftIO $ startAssistant repo
redirect =<< liftIO geturl redirect =<< liftIO geturl
where where
geturl = do geturl = do

View file

@ -21,7 +21,8 @@
/config/xmpp/needcloudrepo/#UUID NeedCloudRepoR GET /config/xmpp/needcloudrepo/#UUID NeedCloudRepoR GET
/config/fsck ConfigFsckR GET POST /config/fsck ConfigFsckR GET POST
/config/fsck/preferences ConfigFsckPreferencesR POST /config/fsck/preferences ConfigFsckPreferencesR POST
/config/upgrade/#GitAnnexDistribution ConfigUpgradeR GET /config/upgrade/start/#GitAnnexDistribution ConfigStartUpgradeR GET
/config/upgrade/finish ConfigFinishUpgradeR GET
/config/addrepository AddRepositoryR GET /config/addrepository AddRepositoryR GET
/config/repository/new NewRepositoryR GET POST /config/repository/new NewRepositoryR GET POST