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-07-05 22:31:46 +00:00
|
|
|
|
2011-03-29 18:55:59 +00:00
|
|
|
remoteTypes,
|
2011-11-18 17:22:48 +00:00
|
|
|
remoteMap,
|
2011-03-27 20:55:43 +00:00
|
|
|
byName,
|
2011-07-06 20:06:10 +00:00
|
|
|
prettyPrintUUIDs,
|
2011-03-27 19:56:43 +00:00
|
|
|
remotesWithUUID,
|
2011-03-29 03:22:31 +00:00
|
|
|
remotesWithoutUUID,
|
2011-07-06 20:06:10 +00:00
|
|
|
keyPossibilities,
|
|
|
|
keyPossibilitiesTrusted,
|
|
|
|
nameToUUID,
|
2011-07-05 22:31:46 +00:00
|
|
|
showTriedRemotes,
|
|
|
|
showLocations,
|
2011-10-28 21:26:38 +00:00
|
|
|
forceTrust,
|
2011-11-09 22:33:15 +00:00
|
|
|
logStatus
|
2011-03-27 19:56:43 +00:00
|
|
|
) where
|
|
|
|
|
2011-03-29 03:22:31 +00:00
|
|
|
import qualified Data.Map as M
|
2011-09-01 20:02:01 +00:00
|
|
|
import Text.JSON
|
|
|
|
import Text.JSON.Generic
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-10-05 20:02:51 +00:00
|
|
|
import Common.Annex
|
2011-06-02 01:56:04 +00:00
|
|
|
import Types.Remote
|
2011-03-27 19:56:43 +00:00
|
|
|
import qualified Annex
|
2011-03-29 22:28:37 +00:00
|
|
|
import Config
|
2011-10-15 21:47:03 +00:00
|
|
|
import Annex.UUID
|
2011-10-15 20:21:08 +00:00
|
|
|
import Logs.UUID
|
|
|
|
import Logs.Trust
|
|
|
|
import Logs.Location
|
|
|
|
import Logs.Remote
|
2011-03-29 18:55:59 +00:00
|
|
|
|
|
|
|
import qualified Remote.Git
|
|
|
|
import qualified Remote.S3
|
2011-04-08 20:44:43 +00:00
|
|
|
import qualified Remote.Bup
|
2011-03-30 17:18:46 +00:00
|
|
|
import qualified Remote.Directory
|
2011-04-28 00:06:07 +00:00
|
|
|
import qualified Remote.Rsync
|
2011-07-01 19:24:07 +00:00
|
|
|
import qualified Remote.Web
|
2011-04-28 21:21:45 +00:00
|
|
|
import qualified Remote.Hook
|
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-04-08 20:44:43 +00:00
|
|
|
, Remote.Bup.remote
|
2011-03-30 17:18:46 +00:00
|
|
|
, Remote.Directory.remote
|
2011-04-28 00:06:07 +00:00
|
|
|
, Remote.Rsync.remote
|
2011-07-01 19:24:07 +00:00
|
|
|
, Remote.Web.remote
|
2011-04-28 21:21:45 +00:00
|
|
|
, Remote.Hook.remote
|
2011-03-28 02:00:44 +00:00
|
|
|
]
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-03-27 20:24:46 +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. -}
|
2011-03-27 20:24:46 +00:00
|
|
|
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' }
|
2011-03-27 20:24:46 +00:00
|
|
|
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
|
2011-10-11 18:43:45 +00:00
|
|
|
u <- getRepoUUID r
|
2011-03-30 19:15:46 +00:00
|
|
|
generate t r u (M.lookup u m)
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-11-18 17:22:48 +00:00
|
|
|
{- Map of UUIDs of Remotes and their names. -}
|
|
|
|
remoteMap :: Annex (M.Map UUID String)
|
|
|
|
remoteMap = M.fromList . map (\r -> (uuid r, name r)) <$> genList
|
|
|
|
|
2011-09-30 06:23:24 +00:00
|
|
|
{- Looks up a remote by name. (Or by UUID.) Only finds currently configured
|
|
|
|
- git remotes. -}
|
2011-03-27 20:55:43 +00:00
|
|
|
byName :: String -> Annex (Remote Annex)
|
|
|
|
byName n = do
|
2011-06-14 02:19:44 +00:00
|
|
|
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
|
2011-03-27 20:55:43 +00:00
|
|
|
allremotes <- genList
|
|
|
|
let match = filter matching allremotes
|
2011-07-15 07:12:05 +00:00
|
|
|
if null match
|
2011-06-14 02:19:44 +00:00
|
|
|
then return $ Left $ "there is no git remote named \"" ++ n ++ "\""
|
|
|
|
else return $ Right $ head match
|
2011-03-27 20:55:43 +00:00
|
|
|
where
|
2011-11-08 03:21:22 +00:00
|
|
|
matching r = n == name r || toUUID n == uuid r
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-06-14 02:19:44 +00:00
|
|
|
{- Looks up a remote by name (or by UUID, or even by description),
|
2011-09-30 06:23:24 +00:00
|
|
|
- and returns its UUID. Finds even remotes that are not configured in
|
|
|
|
- .git/config. -}
|
2011-03-27 20:55:43 +00:00
|
|
|
nameToUUID :: String -> Annex UUID
|
2011-10-11 18:43:45 +00:00
|
|
|
nameToUUID "." = getUUID -- special case for current repo
|
2011-11-07 20:34:12 +00:00
|
|
|
nameToUUID n = byName' n >>= go
|
2011-06-14 02:19:44 +00:00
|
|
|
where
|
2011-11-07 20:34:12 +00:00
|
|
|
go (Right r) = return $ uuid r
|
|
|
|
go (Left e) = fromMaybe (error e) <$> bydescription
|
|
|
|
bydescription = do
|
2011-09-30 06:23:24 +00:00
|
|
|
m <- uuidMap
|
2011-11-07 20:34:12 +00:00
|
|
|
case M.lookup n $ transform swap m of
|
2011-09-30 06:23:24 +00:00
|
|
|
Just u -> return $ Just u
|
2011-11-07 20:34:12 +00:00
|
|
|
Nothing -> return $ byuuid m
|
2011-11-08 03:21:22 +00:00
|
|
|
byuuid m = M.lookup (toUUID n) $ transform double m
|
2011-09-30 06:51:05 +00:00
|
|
|
transform a = M.fromList . map a . M.toList
|
2011-06-14 02:19:44 +00:00
|
|
|
swap (a, b) = (b, a)
|
2011-11-07 20:34:12 +00:00
|
|
|
double (a, _) = (a, a)
|
2011-03-27 19:56:43 +00:00
|
|
|
|
2011-09-01 20:02:01 +00:00
|
|
|
{- 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
|
2011-11-08 19:34:10 +00:00
|
|
|
hereu <- getUUID
|
2011-09-30 19:02:08 +00:00
|
|
|
m <- M.unionWith addname <$> uuidMap <*> remoteMap
|
2011-11-08 19:34:10 +00:00
|
|
|
maybeShowJSON [(desc, map (jsonify m hereu) uuids)]
|
|
|
|
return $ unwords $ map (\u -> "\t" ++ prettify m hereu u ++ "\n") uuids
|
2011-07-01 19:24:07 +00:00
|
|
|
where
|
2011-09-30 19:02:08 +00:00
|
|
|
addname d n
|
|
|
|
| d == n = d
|
2011-10-27 16:58:01 +00:00
|
|
|
| null d = n
|
2011-09-30 19:02:08 +00:00
|
|
|
| otherwise = n ++ " (" ++ d ++ ")"
|
2011-09-01 20:02:01 +00:00
|
|
|
findlog m u = M.findWithDefault "" u m
|
2011-11-08 19:34:10 +00:00
|
|
|
prettify m hereu u
|
2011-11-08 03:21:22 +00:00
|
|
|
| not (null d) = fromUUID u ++ " -- " ++ d
|
|
|
|
| otherwise = fromUUID u
|
2011-07-01 19:24:07 +00:00
|
|
|
where
|
2011-11-08 19:34:10 +00:00
|
|
|
ishere = hereu == u
|
2011-09-30 19:02:08 +00:00
|
|
|
n = findlog m u
|
|
|
|
d
|
|
|
|
| null n && ishere = "here"
|
|
|
|
| ishere = addname n "here"
|
|
|
|
| otherwise = n
|
2011-11-08 19:34:10 +00:00
|
|
|
jsonify m hereu u = toJSObject
|
2011-11-08 03:21:22 +00:00
|
|
|
[ ("uuid", toJSON $ fromUUID u)
|
2011-09-01 20:02:01 +00:00
|
|
|
, ("description", toJSON $ findlog m u)
|
2011-11-08 19:34:10 +00:00
|
|
|
, ("here", toJSON $ hereu == u)
|
2011-09-01 20:02:01 +00:00
|
|
|
]
|
2011-07-01 19:24:07 +00:00
|
|
|
|
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-10-15 20:21:08 +00:00
|
|
|
{- Cost ordered lists of remotes that the Logs.Location indicate may have a key.
|
2011-06-23 17:39:04 +00:00
|
|
|
-}
|
|
|
|
keyPossibilities :: Key -> Annex [Remote Annex]
|
2011-08-25 04:28:55 +00:00
|
|
|
keyPossibilities key = fst <$> keyPossibilities' False key
|
2011-06-23 17:39:04 +00:00
|
|
|
|
2011-10-15 20:21:08 +00:00
|
|
|
{- Cost ordered lists of remotes that the Logs.Location indicate may have a key.
|
2011-06-01 23:10:38 +00:00
|
|
|
-
|
|
|
|
- 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
|
2011-10-11 18:43:45 +00:00
|
|
|
u <- getUUID
|
2011-06-23 19:30:04 +00:00
|
|
|
trusted <- if withtrusted then trustGet Trusted else return []
|
2011-06-01 23:10:38 +00:00
|
|
|
|
|
|
|
-- get uuids of all remotes that are recorded to have the key
|
2011-06-22 20:13:43 +00:00
|
|
|
uuids <- keyLocations key
|
2011-06-01 23:10:38 +00:00
|
|
|
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
|
2011-06-01 23:10:38 +00:00
|
|
|
|
|
|
|
-- remotes that match uuids that have the key
|
2011-09-19 00:11:39 +00:00
|
|
|
allremotes <- filterM (repoNotIgnored . repo) =<< genList
|
2011-06-01 23:10:38 +00:00
|
|
|
let validremotes = remotesWithUUID allremotes validuuids
|
|
|
|
|
|
|
|
return (sort validremotes, validtrusteduuids)
|
|
|
|
|
2011-07-05 22:31:46 +00:00
|
|
|
{- Displays known locations of a key. -}
|
|
|
|
showLocations :: Key -> [UUID] -> Annex ()
|
|
|
|
showLocations key exclude = do
|
2011-10-11 18:43:45 +00:00
|
|
|
u <- getUUID
|
2011-07-05 22:31:46 +00:00
|
|
|
uuids <- keyLocations key
|
|
|
|
untrusteduuids <- trustGet UnTrusted
|
|
|
|
let uuidswanted = filteruuids uuids (u:exclude++untrusteduuids)
|
|
|
|
let uuidsskipped = filteruuids uuids (u:exclude++uuidswanted)
|
2011-09-01 20:02:01 +00:00
|
|
|
ppuuidswanted <- Remote.prettyPrintUUIDs "wanted" uuidswanted
|
|
|
|
ppuuidsskipped <- Remote.prettyPrintUUIDs "skipped" uuidsskipped
|
2011-07-05 22:31:46 +00:00
|
|
|
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
|
2011-07-05 22:31:46 +00:00
|
|
|
r <- nameToUUID remotename
|
2011-06-02 06:33:31 +00:00
|
|
|
Annex.changeState $ \s ->
|
|
|
|
s { Annex.forcetrust = (r, level):Annex.forcetrust s }
|
2011-10-28 21:26:38 +00:00
|
|
|
|
|
|
|
{- Used to log a change in a remote's having a key. The change is logged
|
|
|
|
- in the local repo, not on the remote. The process of transferring the
|
|
|
|
- key to the remote, or removing the key from it *may* log the change
|
|
|
|
- on the remote, but this cannot always be relied on. -}
|
2011-11-09 22:33:15 +00:00
|
|
|
logStatus :: Remote Annex -> Key -> Bool -> Annex ()
|
|
|
|
logStatus remote key present = logChange key (uuid remote) status
|
2011-10-28 21:26:38 +00:00
|
|
|
where
|
|
|
|
status = if present then InfoPresent else InfoMissing
|