2011-03-27 19:56:43 +00:00
|
|
|
{- git-annex remotes
|
|
|
|
-
|
|
|
|
- Copyright 2011 Joey Hess <joey@kitenet.net>
|
|
|
|
-
|
|
|
|
- Licensed under the GNU GPL version 3 or higher.
|
|
|
|
-}
|
|
|
|
|
|
|
|
module Remote (
|
2011-03-27 21:12:32 +00:00
|
|
|
Remote,
|
|
|
|
uuid,
|
|
|
|
name,
|
|
|
|
storeKey,
|
|
|
|
retrieveKeyFile,
|
|
|
|
removeKey,
|
|
|
|
hasKey,
|
|
|
|
hasKeyCheap,
|
|
|
|
|
2011-03-27 20:55:43 +00:00
|
|
|
byName,
|
|
|
|
nameToUUID,
|
2011-03-27 19:56:43 +00:00
|
|
|
keyPossibilities,
|
|
|
|
remotesWithUUID,
|
2011-03-29 03:22:31 +00:00
|
|
|
remotesWithoutUUID,
|
|
|
|
|
|
|
|
configGet,
|
|
|
|
configSet,
|
|
|
|
keyValToMap
|
2011-03-27 19:56:43 +00:00
|
|
|
) where
|
|
|
|
|
|
|
|
import Control.Monad.State (liftIO)
|
2011-03-27 20:55:43 +00:00
|
|
|
import Control.Monad (when, liftM)
|
2011-03-27 19:56:43 +00:00
|
|
|
import Data.List
|
2011-03-28 01:43:25 +00:00
|
|
|
import Data.String.Utils
|
2011-03-29 03:22:31 +00:00
|
|
|
import qualified Data.Map as M
|
|
|
|
import Data.Maybe
|
2011-03-27 19:56:43 +00:00
|
|
|
|
|
|
|
import RemoteClass
|
2011-03-27 23:23:00 +00:00
|
|
|
import qualified Remote.Git
|
2011-03-28 02:00:44 +00:00
|
|
|
import qualified Remote.S3
|
2011-03-27 19:56:43 +00:00
|
|
|
import Types
|
|
|
|
import UUID
|
|
|
|
import qualified Annex
|
|
|
|
import Trust
|
|
|
|
import LocationLog
|
2011-03-29 03:22:31 +00:00
|
|
|
import Locations
|
2011-03-28 01:43:25 +00:00
|
|
|
import Messages
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-03-29 03:51:07 +00:00
|
|
|
remoteTypes :: [RemoteType Annex]
|
|
|
|
remoteTypes =
|
|
|
|
[ Remote.Git.remote
|
|
|
|
, Remote.S3.remote
|
2011-03-28 02:00:44 +00:00
|
|
|
]
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-03-29 03:51:07 +00:00
|
|
|
{- Runs the generators of each type of Remote -}
|
|
|
|
runGenerators :: Annex [Remote Annex]
|
|
|
|
runGenerators = do
|
|
|
|
(actions, expensive) <- collect ([], []) $ map generator remoteTypes
|
2011-03-28 01:43:25 +00:00
|
|
|
when (not $ null expensive) $
|
|
|
|
showNote $ "getting UUID for " ++ join ", " expensive
|
|
|
|
sequence actions
|
|
|
|
where
|
|
|
|
collect v [] = return v
|
|
|
|
collect (actions, expensive) (x:xs) = do
|
|
|
|
(a, e) <- x
|
|
|
|
collect (a++actions, e++expensive) xs
|
|
|
|
|
2011-03-27 20:24:46 +00:00
|
|
|
{- Builds a list of all available Remotes.
|
|
|
|
- Since doing so can be expensive, the list is cached in the Annex. -}
|
|
|
|
genList :: Annex [Remote Annex]
|
|
|
|
genList = do
|
|
|
|
rs <- Annex.getState Annex.remotes
|
|
|
|
if null rs
|
|
|
|
then do
|
2011-03-29 03:51:07 +00:00
|
|
|
rs' <- runGenerators
|
2011-03-27 20:24:46 +00:00
|
|
|
Annex.changeState $ \s -> s { Annex.remotes = rs' }
|
|
|
|
return rs'
|
|
|
|
else return rs
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-03-27 20:55:43 +00:00
|
|
|
{- Looks up a remote by name. (Or by UUID.) -}
|
|
|
|
byName :: String -> Annex (Remote Annex)
|
|
|
|
byName "" = error "no remote specified"
|
|
|
|
byName n = do
|
|
|
|
allremotes <- genList
|
|
|
|
let match = filter matching allremotes
|
|
|
|
when (null match) $ error $
|
|
|
|
"there is no git remote named \"" ++ n ++ "\""
|
|
|
|
return $ head match
|
|
|
|
where
|
|
|
|
matching r = n == name r || n == uuid r
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-03-27 20:55:43 +00:00
|
|
|
{- Looks up a remote by name (or by UUID), and returns its UUID. -}
|
|
|
|
nameToUUID :: String -> Annex UUID
|
|
|
|
nameToUUID "." = do -- special case for current repo
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
getUUID g
|
|
|
|
nameToUUID n = liftM uuid (byName n)
|
2011-03-27 19:56:43 +00:00
|
|
|
|
|
|
|
{- Cost ordered lists of remotes that the LocationLog indicate may have a key.
|
|
|
|
-
|
|
|
|
- Also returns a list of UUIDs that are trusted to have the key
|
|
|
|
- (some may not have configured remotes).
|
|
|
|
-}
|
2011-03-27 20:17:56 +00:00
|
|
|
keyPossibilities :: Key -> Annex ([Remote Annex], [UUID])
|
2011-03-27 19:56:43 +00:00
|
|
|
keyPossibilities key = do
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
u <- getUUID g
|
|
|
|
trusted <- trustGet Trusted
|
|
|
|
|
|
|
|
-- get uuids of all remotes that are recorded to have the key
|
|
|
|
uuids <- liftIO $ keyLocations g key
|
|
|
|
let validuuids = filter (/= u) uuids
|
|
|
|
|
|
|
|
-- note that validuuids is assumed to not have dups
|
|
|
|
let validtrusteduuids = intersect validuuids trusted
|
|
|
|
|
|
|
|
-- remotes that match uuids that have the key
|
2011-03-27 20:24:46 +00:00
|
|
|
allremotes <- genList
|
2011-03-27 19:56:43 +00:00
|
|
|
let validremotes = remotesWithUUID allremotes validuuids
|
|
|
|
|
|
|
|
return (sort validremotes, validtrusteduuids)
|
2011-03-27 20:55:43 +00:00
|
|
|
|
|
|
|
{- Filters a list of remotes to ones that have the listed uuids. -}
|
|
|
|
remotesWithUUID :: [Remote Annex] -> [UUID] -> [Remote Annex]
|
|
|
|
remotesWithUUID rs us = filter (\r -> uuid r `elem` us) rs
|
|
|
|
|
|
|
|
{- Filters a list of remotes to ones that do not have the listed uuids. -}
|
|
|
|
remotesWithoutUUID :: [Remote Annex] -> [UUID] -> [Remote Annex]
|
|
|
|
remotesWithoutUUID rs us = filter (\r -> uuid r `notElem` us) rs
|
|
|
|
|
2011-03-29 03:22:31 +00:00
|
|
|
{- Filename of remote.log. -}
|
|
|
|
remoteLog :: Annex FilePath
|
|
|
|
remoteLog = do
|
|
|
|
g <- Annex.gitRepo
|
|
|
|
return $ gitStateDir g ++ "remote.log"
|
|
|
|
|
|
|
|
{- Reads the uuid and config of the specified remote from the remoteLog. -}
|
|
|
|
configGet :: String -> Annex (Maybe (UUID, M.Map String String))
|
|
|
|
configGet n = do
|
|
|
|
rs <- readRemoteLog
|
|
|
|
let matches = filter (matchName n) rs
|
|
|
|
case matches of
|
|
|
|
[] -> return Nothing
|
|
|
|
((u, _, c):_) -> return $ Just (u, c)
|
|
|
|
|
|
|
|
{- Changes or adds a remote's config in the remoteLog. -}
|
|
|
|
configSet :: String -> UUID -> M.Map String String -> Annex ()
|
|
|
|
configSet n u c = do
|
|
|
|
rs <- readRemoteLog
|
|
|
|
let others = filter (not . matchName n) rs
|
|
|
|
writeRemoteLog $ (u, n, c):others
|
|
|
|
|
|
|
|
matchName :: String -> (UUID, String, M.Map String String) -> Bool
|
|
|
|
matchName n (_, n', _) = n == n'
|
|
|
|
|
|
|
|
readRemoteLog :: Annex [(UUID, String, M.Map String String)]
|
|
|
|
readRemoteLog = do
|
|
|
|
l <- remoteLog
|
|
|
|
s <- liftIO $ catch (readFile l) ignoreerror
|
|
|
|
return $ remoteLogParse s
|
|
|
|
where
|
|
|
|
ignoreerror _ = return []
|
|
|
|
|
|
|
|
writeRemoteLog :: [(UUID, String, M.Map String String)] -> Annex ()
|
|
|
|
writeRemoteLog rs = do
|
|
|
|
l <- remoteLog
|
|
|
|
liftIO $ writeFile l $ unlines $ map toline rs
|
|
|
|
where
|
|
|
|
toline (u, n, c) = u ++ " " ++ n ++ (unwords $ mapToKeyVal c)
|
|
|
|
|
|
|
|
remoteLogParse :: String -> [(UUID, String, M.Map String String)]
|
|
|
|
remoteLogParse s = catMaybes $ map parseline $ filter (not . null) $ lines s
|
|
|
|
where
|
|
|
|
parseline l
|
|
|
|
| length w > 2 = Just (u, n, c)
|
|
|
|
| otherwise = Nothing
|
|
|
|
where
|
|
|
|
w = words l
|
|
|
|
u = w !! 0
|
|
|
|
n = w !! 1
|
|
|
|
c = keyValToMap $ drop 2 w
|
|
|
|
|
|
|
|
{- Given Strings like "key=value", generates a Map. -}
|
|
|
|
keyValToMap :: [String] -> M.Map String String
|
|
|
|
keyValToMap ws = M.fromList $ map (/=/) ws
|
|
|
|
where
|
|
|
|
(/=/) s = (k, v)
|
|
|
|
where
|
|
|
|
k = takeWhile (/= '=') s
|
|
|
|
v = drop (1 + length k) s
|
|
|
|
|
|
|
|
mapToKeyVal :: M.Map String String -> [String]
|
|
|
|
mapToKeyVal m = map toword $ M.toList m
|
|
|
|
where
|
|
|
|
toword (k, v) = k ++ "=" ++ v
|