webapp automatic grouping

webapp: Adds newly created repositories to one of these groups:
clients, drives, servers

This is heuristic, but it's a pretty good heuristic, and can always be
configured.
This commit is contained in:
Joey Hess 2012-10-09 14:24:17 -04:00
parent 8eb1ba4cfe
commit a5781fd9ba
7 changed files with 73 additions and 43 deletions

View file

@ -28,11 +28,12 @@ import qualified Data.Map as M
import Data.Char import Data.Char
{- Sets up and begins syncing with a new ssh or rsync remote. -} {- Sets up and begins syncing with a new ssh or rsync remote. -}
makeSshRemote :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> Bool -> SshData -> IO () makeSshRemote :: ThreadState -> DaemonStatusHandle -> ScanRemoteMap -> Bool -> SshData -> IO Remote
makeSshRemote st dstatus scanremotes forcersync sshdata = do makeSshRemote st dstatus scanremotes forcersync sshdata = do
r <- runThreadState st $ r <- runThreadState st $
addRemote $ maker (sshRepoName sshdata) sshurl addRemote $ maker (sshRepoName sshdata) sshurl
syncNewRemote st dstatus scanremotes r syncNewRemote st dstatus scanremotes r
return r
where where
rsync = forcersync || rsyncOnly sshdata rsync = forcersync || rsyncOnly sshdata
maker maker

View file

@ -46,7 +46,7 @@ finishedPairing st dstatus scanremotes msg keypair = do
, "git-annex-shell -c configlist " ++ T.unpack (sshDirectory sshdata) , "git-annex-shell -c configlist " ++ T.unpack (sshDirectory sshdata)
] ]
"" ""
makeSshRemote st dstatus scanremotes False sshdata void $ makeSshRemote st dstatus scanremotes False sshdata
{- Mostly a straightforward conversion. Except: {- Mostly a straightforward conversion. Except:
- * Determine the best hostname to use to contact the host. - * Determine the best hostname to use to contact the host.

View file

@ -28,10 +28,13 @@ import Utility.DiskFree
import Utility.DataUnits import Utility.DataUnits
import Utility.Network import Utility.Network
import Remote (prettyListUUIDs) import Remote (prettyListUUIDs)
import Logs.Group
import Annex.UUID
import Yesod import Yesod
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Set as S
import Data.Char import Data.Char
import System.Posix.Directory import System.Posix.Directory
import qualified Control.Exception as E import qualified Control.Exception as E
@ -142,10 +145,10 @@ getNewRepositoryR = bootstrap (Just Config) $ do
case res of case res of
FormSuccess (RepositoryPath p) -> lift $ do FormSuccess (RepositoryPath p) -> lift $ do
let path = T.unpack p let path = T.unpack p
liftIO $ do liftIO $ makeRepo path False
makeRepo path False u <- liftIO $ initRepo path Nothing
initRepo path Nothing runAnnex () $ groupSet u (S.singleton "clients")
addAutoStart path liftIO $ addAutoStart path
redirect $ SwitchToRepositoryR path redirect $ SwitchToRepositoryR path
_ -> $(widgetFile "configurators/newrepository") _ -> $(widgetFile "configurators/newrepository")
@ -191,8 +194,9 @@ getAddDriveR = bootstrap (Just Config) $ do
where where
go mountpoint = do go mountpoint = do
liftIO $ makerepo dir liftIO $ makerepo dir
liftIO $ initRepo dir $ Just remotename u <- liftIO $ initRepo dir $ Just remotename
r <- addremote dir remotename r <- addremote dir remotename
runAnnex () $ groupSet u (S.singleton "drives")
syncRemote r syncRemote r
where where
dir = mountpoint </> gitAnnexAssistantDefaultDir dir = mountpoint </> gitAnnexAssistantDefaultDir
@ -260,9 +264,10 @@ driveList = mapM (gen . mnt_dir) =<< filter sane <$> getMounts
startFullAssistant :: FilePath -> Handler () startFullAssistant :: FilePath -> Handler ()
startFullAssistant path = do startFullAssistant path = do
webapp <- getYesod webapp <- getYesod
liftIO $ makeRepo path False
u <- liftIO $ initRepo path Nothing
runAnnex () $ groupSet u (S.singleton "clients")
url <- liftIO $ do url <- liftIO $ do
makeRepo path False
initRepo path Nothing
addAutoStart path addAutoStart path
changeWorkingDirectory path changeWorkingDirectory path
fromJust $ postFirstRun webapp fromJust $ postFirstRun webapp
@ -286,10 +291,11 @@ inDir dir a = do
Annex.eval state a Annex.eval state a
{- Initializes a git-annex repository in a directory with a description. -} {- Initializes a git-annex repository in a directory with a description. -}
initRepo :: FilePath -> Maybe String -> IO () initRepo :: FilePath -> Maybe String -> IO UUID
initRepo dir desc = inDir dir $ initRepo dir desc = inDir dir $ do
unlessM isInitialized $ unlessM isInitialized $
initialize desc initialize desc
getUUID
{- Adds a directory to the autostart file. -} {- Adds a directory to the autostart file. -}
addAutoStart :: FilePath -> IO () addAutoStart :: FilePath -> IO ()

View file

@ -19,12 +19,14 @@ import Assistant.ThreadedMonad
import Utility.Yesod import Utility.Yesod
import qualified Remote.S3 as S3 import qualified Remote.S3 as S3
import Logs.Remote import Logs.Remote
import Remote (prettyListUUIDs) import qualified Remote
import Types.Remote (RemoteConfig) import Types.Remote (RemoteConfig)
import Logs.Group
import Yesod import Yesod
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Set as S
import qualified Data.Map as M import qualified Data.Map as M
s3Configurator :: Widget -> Handler RepHtml s3Configurator :: Widget -> Handler RepHtml
@ -80,7 +82,7 @@ getAddS3R = s3Configurator $ do
case result of case result of
FormSuccess s3input -> lift $ do FormSuccess s3input -> lift $ do
let name = T.unpack $ repoName s3input let name = T.unpack $ repoName s3input
makeS3Remote (extractCreds s3input) name $ M.fromList makeS3Remote (extractCreds s3input) name setgroup $ M.fromList
[ ("encryption", "shared") [ ("encryption", "shared")
, ("type", "S3") , ("type", "S3")
, ("datacenter", T.unpack $ datacenter s3input) , ("datacenter", T.unpack $ datacenter s3input)
@ -91,6 +93,8 @@ getAddS3R = s3Configurator $ do
showform form enctype = do showform form enctype = do
let authtoken = webAppFormAuthToken let authtoken = webAppFormAuthToken
$(widgetFile "configurators/adds3") $(widgetFile "configurators/adds3")
setgroup r = runAnnex () $
groupSet (Remote.uuid r) (S.singleton "servers")
getEnableS3R :: UUID -> Handler RepHtml getEnableS3R :: UUID -> Handler RepHtml
getEnableS3R uuid = s3Configurator $ do getEnableS3R uuid = s3Configurator $ do
@ -101,24 +105,24 @@ getEnableS3R uuid = s3Configurator $ do
m <- runAnnex M.empty readRemoteLog m <- runAnnex M.empty readRemoteLog
let name = fromJust $ M.lookup "name" $ let name = fromJust $ M.lookup "name" $
fromJust $ M.lookup uuid m fromJust $ M.lookup uuid m
makeS3Remote s3creds name M.empty makeS3Remote s3creds name (const noop) M.empty
_ -> showform form enctype _ -> showform form enctype
where where
showform form enctype = do showform form enctype = do
let authtoken = webAppFormAuthToken let authtoken = webAppFormAuthToken
description <- lift $ runAnnex "" $ description <- lift $ runAnnex "" $
T.pack . concat <$> prettyListUUIDs [uuid] T.pack . concat <$> Remote.prettyListUUIDs [uuid]
$(widgetFile "configurators/enables3") $(widgetFile "configurators/enables3")
makeS3Remote :: S3Creds -> String -> RemoteConfig -> Handler () makeS3Remote :: S3Creds -> String -> (Remote -> Handler ()) -> RemoteConfig -> Handler ()
makeS3Remote (S3Creds ak sk) name config = do makeS3Remote (S3Creds ak sk) name setup config = do
webapp <- getYesod webapp <- getYesod
let st = fromJust $ threadState webapp let st = fromJust $ threadState webapp
remotename <- runAnnex name $ fromRepo $ uniqueRemoteName name 0 remotename <- runAnnex name $ fromRepo $ uniqueRemoteName name 0
liftIO $ do liftIO $ S3.s3SetCredsEnv ( T.unpack ak, T.unpack sk)
S3.s3SetCredsEnv ( T.unpack ak, T.unpack sk) r <- liftIO $ runThreadState st $ addRemote $ do
r <- runThreadState st $ addRemote $ do makeSpecialRemote name S3.remote config
makeSpecialRemote name S3.remote config return remotename
return remotename setup r
syncNewRemote st (daemonStatus webapp) (scanRemotes webapp) r liftIO $ syncNewRemote st (daemonStatus webapp) (scanRemotes webapp) r
redirect RepositoriesR redirect RepositoriesR

View file

@ -19,11 +19,13 @@ import Utility.Yesod
import Utility.Rsync (rsyncUrlIsShell) import Utility.Rsync (rsyncUrlIsShell)
import Logs.Remote import Logs.Remote
import Remote import Remote
import Logs.Group
import Yesod import Yesod
import Data.Text (Text) import Data.Text (Text)
import qualified Data.Text as T import qualified Data.Text as T
import qualified Data.Map as M import qualified Data.Map as M
import qualified Data.Set as S
import Network.Socket import Network.Socket
import System.Posix.User import System.Posix.User
@ -130,7 +132,7 @@ getEnableRsyncR u = do
case result of case result of
FormSuccess sshinput' FormSuccess sshinput'
| isRsyncNet (hostname sshinput') -> | isRsyncNet (hostname sshinput') ->
void $ lift $ makeRsyncNet sshinput' void $ lift $ makeRsyncNet sshinput' (const noop)
| otherwise -> do | otherwise -> do
s <- liftIO $ testServer sshinput' s <- liftIO $ testServer sshinput'
case s of case s of
@ -250,23 +252,23 @@ getConfirmSshR sshdata = sshConfigurator $ do
$(widgetFile "configurators/ssh/confirm") $(widgetFile "configurators/ssh/confirm")
getMakeSshGitR :: SshData -> Handler RepHtml getMakeSshGitR :: SshData -> Handler RepHtml
getMakeSshGitR = makeSsh False getMakeSshGitR = makeSsh False setupGroup
getMakeSshRsyncR :: SshData -> Handler RepHtml getMakeSshRsyncR :: SshData -> Handler RepHtml
getMakeSshRsyncR = makeSsh True getMakeSshRsyncR = makeSsh True setupGroup
makeSsh :: Bool -> SshData -> Handler RepHtml makeSsh :: Bool -> (Remote -> Handler ()) -> SshData -> Handler RepHtml
makeSsh rsync sshdata makeSsh rsync setup sshdata
| needsPubKey sshdata = do | needsPubKey sshdata = do
keypair <- liftIO genSshKeyPair keypair <- liftIO genSshKeyPair
sshdata' <- liftIO $ setupSshKeyPair keypair sshdata sshdata' <- liftIO $ setupSshKeyPair keypair sshdata
makeSsh' rsync sshdata' (Just keypair) makeSsh' rsync setup sshdata' (Just keypair)
| otherwise = makeSsh' rsync sshdata Nothing | otherwise = makeSsh' rsync setup sshdata Nothing
makeSsh' :: Bool -> SshData -> Maybe SshKeyPair -> Handler RepHtml makeSsh' :: Bool -> (Remote -> Handler ()) -> SshData -> Maybe SshKeyPair -> Handler RepHtml
makeSsh' rsync sshdata keypair = makeSsh' rsync setup sshdata keypair =
sshSetup [sshhost, remoteCommand] "" $ sshSetup [sshhost, remoteCommand] "" $
makeSshRepo rsync sshdata makeSshRepo rsync setup sshdata
where where
sshhost = genSshHost (sshHostName sshdata) (sshUserName sshdata) sshhost = genSshHost (sshHostName sshdata) (sshUserName sshdata)
remotedir = T.unpack $ sshDirectory sshdata remotedir = T.unpack $ sshDirectory sshdata
@ -280,14 +282,15 @@ makeSsh' rsync sshdata keypair =
else Nothing else Nothing
] ]
makeSshRepo :: Bool -> SshData -> Handler RepHtml makeSshRepo :: Bool -> (Remote -> Handler ()) -> SshData -> Handler RepHtml
makeSshRepo forcersync sshdata = do makeSshRepo forcersync setup sshdata = do
webapp <- getYesod webapp <- getYesod
liftIO $ makeSshRemote r <- liftIO $ makeSshRemote
(fromJust $ threadState webapp) (fromJust $ threadState webapp)
(daemonStatus webapp) (daemonStatus webapp)
(scanRemotes webapp) (scanRemotes webapp)
forcersync sshdata forcersync sshdata
setup r
redirect RepositoriesR redirect RepositoriesR
getAddRsyncNetR :: Handler RepHtml getAddRsyncNetR :: Handler RepHtml
@ -303,14 +306,14 @@ getAddRsyncNetR = do
case result of case result of
FormSuccess sshinput FormSuccess sshinput
| isRsyncNet (hostname sshinput) -> | isRsyncNet (hostname sshinput) ->
makeRsyncNet sshinput makeRsyncNet sshinput setupGroup
| otherwise -> | otherwise ->
showform $ UnusableServer showform $ UnusableServer
"That is not a rsync.net host name." "That is not a rsync.net host name."
_ -> showform UntestedServer _ -> showform UntestedServer
makeRsyncNet :: SshInput -> Handler RepHtml makeRsyncNet :: SshInput -> (Remote -> Handler ()) -> Handler RepHtml
makeRsyncNet sshinput = do makeRsyncNet sshinput setup = do
knownhost <- liftIO $ maybe (return False) knownHost (hostname sshinput) knownhost <- liftIO $ maybe (return False) knownHost (hostname sshinput)
keypair <- liftIO $ genSshKeyPair keypair <- liftIO $ genSshKeyPair
sshdata <- liftIO $ setupSshKeyPair keypair $ sshdata <- liftIO $ setupSshKeyPair keypair $
@ -338,8 +341,11 @@ makeRsyncNet sshinput = do
, remotecommand , remotecommand
] ]
sshSetup sshopts (sshPubKey keypair) $ sshSetup sshopts (sshPubKey keypair) $
makeSshRepo True sshdata makeSshRepo True setup sshdata
isRsyncNet :: Maybe Text -> Bool isRsyncNet :: Maybe Text -> Bool
isRsyncNet Nothing = False isRsyncNet Nothing = False
isRsyncNet (Just host) = ".rsync.net" `T.isSuffixOf` T.toLower host isRsyncNet (Just host) = ".rsync.net" `T.isSuffixOf` T.toLower host
setupGroup :: Remote -> Handler ()
setupGroup r = runAnnex () $ groupSet (Remote.uuid r) (S.singleton "server")

4
debian/changelog vendored
View file

@ -1,10 +1,12 @@
git-annex (3.20121002) UNRELEASED; urgency=low git-annex (3.20121002) UNRELEASED; urgency=low
* group, ungroup: New commands to indicate groups of repositories.
* watch, assistant: It's now safe to git annex unlock files while * watch, assistant: It's now safe to git annex unlock files while
the watcher is running, as well as modify files checked into git the watcher is running, as well as modify files checked into git
as normal files. Additionally, .gitignore settings are now honored. as normal files. Additionally, .gitignore settings are now honored.
Closes: #689979 Closes: #689979
* group, ungroup: New commands to indicate groups of repositories.
* webapp: Adds newly created repositories to one of these groups:
clients, drives, servers
* vicfg: New command, allows editing (or simply viewing) most * vicfg: New command, allows editing (or simply viewing) most
of the repository configuration settings stored in the git-annex branch. of the repository configuration settings stored in the git-annex branch.
* Added preferred content expressions, configurable using vicfg. * Added preferred content expressions, configurable using vicfg.

View file

@ -6,8 +6,19 @@ it doesn't currently have, is covered by the [[partial_content]] page.
But often the remote is just a removable drive or a cloud remote, But often the remote is just a removable drive or a cloud remote,
that has a limited size. This page is about making the assistant do that has a limited size. This page is about making the assistant do
something smart with such remotes. (Which it now does.. **done** except for something smart with such remotes.
an easy way to configure this.)
## TODO
* easy configuration of preferred content
* Drop no longer preferred content.
- When a file is renamed, it might stop being preferred, so
could be checked and dropped. (If there's multiple links to
the same content, this gets tricky.)
- When a file is sent or received, the sender's preferred content
settings may change, causing it to be dropped from the sender.
- May also want to check for things to drop, from both local and remotes,
when doing the expensive trasfer scan.
## specifying what data a remote prefers to contain **done** ## specifying what data a remote prefers to contain **done**