git-annex/Remote.hs

234 lines
6.7 KiB
Haskell
Raw Normal View History

{- git-annex remotes
-
- Copyright 2011 Joey Hess <joey@kitenet.net>
-
- Licensed under the GNU GPL version 3 or higher.
-}
module Remote (
Remote,
uuid,
name,
storeKey,
retrieveKeyFile,
removeKey,
hasKey,
hasKeyCheap,
2011-03-29 18:55:59 +00:00
remoteTypes,
byName,
2011-07-06 20:06:10 +00:00
prettyPrintUUIDs,
remotesWithUUID,
2011-03-29 03:22:31 +00:00
remotesWithoutUUID,
2011-07-06 20:06:10 +00:00
keyPossibilities,
keyPossibilitiesTrusted,
nameToUUID,
showTriedRemotes,
showLocations,
2011-07-06 00:16:57 +00:00
forceTrust
) where
import Control.Monad.State (filterM)
import Data.List
2011-03-29 03:22:31 +00:00
import qualified Data.Map as M
import Data.String.Utils
import Data.Maybe
import Control.Applicative
import Text.JSON
import Text.JSON.Generic
import Types
import Types.Remote
import UUID
import qualified Annex
2011-03-29 22:28:37 +00:00
import Config
import Trust
import LocationLog
import Messages
2011-07-06 00:16:57 +00:00
import RemoteLog
2011-03-29 18:55:59 +00:00
import qualified Remote.Git
import qualified Remote.S3
import qualified Remote.Bup
2011-03-30 17:18:46 +00:00
import qualified Remote.Directory
import qualified Remote.Rsync
import qualified Remote.Web
import qualified Remote.Hook
2011-03-29 03:51:07 +00:00
remoteTypes :: [RemoteType Annex]
remoteTypes =
[ Remote.Git.remote
, Remote.S3.remote
, Remote.Bup.remote
2011-03-30 17:18:46 +00:00
, Remote.Directory.remote
, Remote.Rsync.remote
, Remote.Web.remote
, Remote.Hook.remote
2011-03-28 02:00:44 +00:00
]
{- Builds a list of all available Remotes.
2011-03-29 21:20:22 +00:00
- Since doing so can be expensive, the list is cached. -}
genList :: Annex [Remote Annex]
genList = do
rs <- Annex.getState Annex.remotes
if null rs
then do
2011-03-29 21:57:20 +00:00
m <- readRemoteLog
l <- mapM (process m) remoteTypes
let rs' = concat l
2011-03-29 20:21:21 +00:00
Annex.changeState $ \s -> s { Annex.remotes = rs' }
return rs'
else return rs
2011-03-29 21:57:20 +00:00
where
2011-05-15 19:27:49 +00:00
process m t =
enumerate t >>=
mapM (gen m t)
2011-03-29 21:57:20 +00:00
gen m t r = do
u <- getUUID r
2011-03-30 19:15:46 +00:00
generate t r u (M.lookup u m)
{- Looks up a remote by name. (Or by UUID.) Only finds currently configured
- git remotes. -}
byName :: String -> Annex (Remote Annex)
byName n = do
res <- byName' n
case res of
Left e -> error e
Right r -> return r
byName' :: String -> Annex (Either String (Remote Annex))
byName' "" = return $ Left "no remote specified"
byName' n = do
allremotes <- genList
let match = filter matching allremotes
if null match
then return $ Left $ "there is no git remote named \"" ++ n ++ "\""
else return $ Right $ head match
where
matching r = n == name r || n == uuid r
{- Looks up a remote by name (or by UUID, or even by description),
- and returns its UUID. Finds even remotes that are not configured in
- .git/config. -}
nameToUUID :: String -> Annex UUID
2011-05-15 19:27:49 +00:00
nameToUUID "." = getUUID =<< Annex.gitRepo -- special case for current repo
nameToUUID n = do
res <- byName' n
case res of
Left e -> fromMaybe (error e) <$> byDescription
Right r -> return $ uuid r
where
byDescription = do
m <- uuidMap
case M.lookup n $ transform swap m of
Just u -> return $ Just u
Nothing -> return $ M.lookup n $ transform double m
transform a = M.fromList . map a . M.toList
swap (a, b) = (b, a)
double (a, _) = (a, a)
{- Pretty-prints a list of UUIDs of remotes, for human display.
-
- Shows descriptions from the uuid log, falling back to remote names,
- as some remotes may not be in the uuid log.
-
- When JSON is enabled, also generates a machine-readable description
- of the UUIDs. -}
prettyPrintUUIDs :: String -> [UUID] -> Annex String
prettyPrintUUIDs desc uuids = do
here <- getUUID =<< Annex.gitRepo
m <- M.unionWith addname <$> uuidMap <*> remoteMap
maybeShowJSON [(desc, map (jsonify m here) uuids)]
return $ unwords $ map (\u -> "\t" ++ prettify m here u ++ "\n") uuids
where
addname d n
| d == n = d
| otherwise = n ++ " (" ++ d ++ ")"
remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> genList
findlog m u = M.findWithDefault "" u m
prettify m here u
| not (null d) = u ++ " -- " ++ d
| otherwise = u
where
ishere = here == u
n = findlog m u
d
| null n && ishere = "here"
| ishere = addname n "here"
| otherwise = n
jsonify m here u = toJSObject
[ ("uuid", toJSON u)
, ("description", toJSON $ findlog m u)
, ("here", toJSON $ here == u)
]
{- 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-06-23 17:39:04 +00:00
{- Cost ordered lists of remotes that the LocationLog indicate may have a key.
-}
keyPossibilities :: Key -> Annex [Remote Annex]
keyPossibilities key = fst <$> keyPossibilities' False key
2011-06-23 17:39:04 +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-06-23 17:39:04 +00:00
keyPossibilitiesTrusted :: Key -> Annex ([Remote Annex], [UUID])
2011-06-23 19:30:04 +00:00
keyPossibilitiesTrusted = keyPossibilities' True
keyPossibilities' :: Bool -> Key -> Annex ([Remote Annex], [UUID])
keyPossibilities' withtrusted key = do
g <- Annex.gitRepo
u <- getUUID g
2011-06-23 19:30:04 +00:00
trusted <- if withtrusted then trustGet Trusted else return []
-- get uuids of all remotes that are recorded to have the key
2011-06-22 20:13:43 +00:00
uuids <- keyLocations key
let validuuids = filter (/= u) uuids
-- note that validuuids is assumed to not have dups
2011-09-06 21:19:29 +00:00
let validtrusteduuids = validuuids `intersect` trusted
-- remotes that match uuids that have the key
allremotes <- filterM (repoNotIgnored . repo) =<< genList
let validremotes = remotesWithUUID allremotes validuuids
return (sort validremotes, validtrusteduuids)
{- Displays known locations of a key. -}
showLocations :: Key -> [UUID] -> Annex ()
showLocations key exclude = do
g <- Annex.gitRepo
u <- getUUID g
uuids <- keyLocations key
untrusteduuids <- trustGet UnTrusted
let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids)
let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)
ppuuidswanted <- Remote.prettyPrintUUIDs "wanted" uuidswanted
ppuuidsskipped <- Remote.prettyPrintUUIDs "skipped" uuidsskipped
showLongNote $ message ppuuidswanted ppuuidsskipped
where
filteruuids l x = filter (`notElem` x) l
message [] [] = "No other repository is known to contain the file."
message rs [] = "Try making some of these repositories available:\n" ++ rs
message [] us = "Also these untrusted repositories may contain the file:\n" ++ us
message rs us = message rs [] ++ message [] us
showTriedRemotes :: [Remote Annex] -> Annex ()
showTriedRemotes [] = return ()
showTriedRemotes remotes =
showLongNote $ "Unable to access these remotes: " ++
(join ", " $ map name remotes)
2011-06-02 06:33:31 +00:00
forceTrust :: TrustLevel -> String -> Annex ()
forceTrust level remotename = do
r <- nameToUUID remotename
2011-06-02 06:33:31 +00:00
Annex.changeState $ \s ->
s { Annex.forcetrust = (r, level):Annex.forcetrust s }