move supportedBackends list into annex monad

This was necessary so the File backend could import Backend w/o a cycle.

Moved code that checks whether enough backends have a file into File
backend.
This commit is contained in:
Joey Hess 2010-10-17 11:47:36 -04:00
parent 6bfa534aa4
commit b471822cfe
7 changed files with 105 additions and 95 deletions

View file

@ -7,6 +7,8 @@ module Annex (
gitRepoChange, gitRepoChange,
backends, backends,
backendsChange, backendsChange,
supportedBackends,
supportedBackendsChange,
flagIsSet, flagIsSet,
flagChange, flagChange,
Flag(..) Flag(..)
@ -20,20 +22,21 @@ import qualified BackendTypes as Backend
{- Create and returns an Annex state object for the specified git repo. {- Create and returns an Annex state object for the specified git repo.
-} -}
new :: Git.Repo -> IO AnnexState new :: Git.Repo -> [Backend] -> IO AnnexState
new g = do new gitrepo allbackends = do
let s = Backend.AnnexState { let s = Backend.AnnexState {
Backend.repo = g, Backend.repo = gitrepo,
Backend.backends = [], Backend.backends = [],
Backend.supportedBackends = allbackends,
Backend.flags = [] Backend.flags = []
} }
(_,s') <- Annex.run s (prep g) (_,s') <- Annex.run s (prep gitrepo)
return s' return s'
where where
prep g = do prep gitrepo = do
-- read git config and update state -- read git config and update state
g' <- liftIO $ Git.configRead g gitrepo' <- liftIO $ Git.configRead gitrepo
Annex.gitRepoChange g' Annex.gitRepoChange gitrepo'
-- performs an action in the Annex monad -- performs an action in the Annex monad
run state action = runStateT (action) state run state action = runStateT (action) state
@ -57,6 +60,15 @@ backendsChange b = do
state <- get state <- get
put state { Backend.backends = b } put state { Backend.backends = b }
return () return ()
supportedBackends :: Annex [Backend]
supportedBackends = do
state <- get
return (Backend.supportedBackends state)
supportedBackendsChange :: [Backend] -> Annex ()
supportedBackendsChange b = do
state <- get
put state { Backend.supportedBackends = b }
return ()
flagIsSet :: Flag -> Annex Bool flagIsSet :: Flag -> Annex Bool
flagIsSet flag = do flagIsSet flag = do
state <- get state <- get

View file

@ -28,14 +28,12 @@ import System.FilePath
import Data.String.Utils import Data.String.Utils
import System.Posix.Files import System.Posix.Files
import BackendList
import Locations import Locations
import qualified GitRepo as Git import qualified GitRepo as Git
import qualified Annex import qualified Annex
import Utility import Utility
import Types import Types
import qualified BackendTypes as B import qualified BackendTypes as B
import BackendList
{- List of backends in the order to try them when storing a new key. -} {- List of backends in the order to try them when storing a new key. -}
backendList :: Annex [Backend] backendList :: Annex [Backend]
@ -44,10 +42,24 @@ backendList = do
if (0 < length l) if (0 < length l)
then return l then return l
else do else do
all <- Annex.supportedBackends
g <- Annex.gitRepo g <- Annex.gitRepo
let l = parseBackendList $ Git.configGet g "annex.backends" "" let l = parseBackendList all $ Git.configGet g "annex.backends" ""
Annex.backendsChange l Annex.backendsChange l
return l return l
where
parseBackendList all s =
if (length s == 0)
then all
else map (lookupBackendName all) $ words s
{- Looks up a backend in the list of supportedBackends -}
lookupBackendName :: [Backend] -> String -> Backend
lookupBackendName all s =
if ((length matches) /= 1)
then error $ "unknown backend " ++ s
else matches !! 0
where matches = filter (\b -> s == B.name b) all
{- Attempts to store a file in one of the backends. -} {- Attempts to store a file in one of the backends. -}
storeFileKey :: FilePath -> Annex (Maybe (Key, Backend)) storeFileKey :: FilePath -> Annex (Maybe (Key, Backend))
@ -81,21 +93,24 @@ removeKey backend key = (B.removeKey backend) key
{- Checks if a backend has its key. -} {- Checks if a backend has its key. -}
hasKey :: Key -> Annex Bool hasKey :: Key -> Annex Bool
hasKey key = (B.hasKey (lookupBackendName $ backendName key)) key hasKey key = do
all <- Annex.supportedBackends
(B.hasKey (lookupBackendName all $ backendName key)) key
{- Looks up the key and backend corresponding to an annexed file, {- Looks up the key and backend corresponding to an annexed file,
- by examining what the file symlinks to. -} - by examining what the file symlinks to. -}
lookupFile :: FilePath -> IO (Maybe (Key, Backend)) lookupFile :: FilePath -> Annex (Maybe (Key, Backend))
lookupFile file = do lookupFile file = do
result <- try (lookup)::IO (Either SomeException (Maybe (Key, Backend))) all <- Annex.supportedBackends
result <- liftIO $ (try (lookup all)::IO (Either SomeException (Maybe (Key, Backend))))
case (result) of case (result) of
Left err -> return Nothing Left err -> return Nothing
Right succ -> return succ Right succ -> return succ
where where
lookup = do lookup all = do
l <- readSymbolicLink file l <- readSymbolicLink file
return $ Just $ pair $ takeFileName l return $ Just $ pair all $ takeFileName l
pair file = (k, b) pair all file = (k, b)
where where
k = fileKey file k = fileKey file
b = lookupBackendName $ backendName k b = lookupBackendName all $ backendName k

View file

@ -25,13 +25,14 @@ import Utility
import Core import Core
import qualified Annex import qualified Annex
import UUID import UUID
import qualified Backend
backend = Backend { backend = Backend {
name = mustProvide, name = mustProvide,
getKey = mustProvide, getKey = mustProvide,
storeFileKey = dummyStore, storeFileKey = dummyStore,
retrieveKeyFile = copyKeyFile, retrieveKeyFile = copyKeyFile,
removeKey = dummyRemove, removeKey = checkRemoveKey,
hasKey = checkKeyFile hasKey = checkKeyFile
} }
@ -41,10 +42,6 @@ mustProvide = error "must provide this field"
dummyStore :: FilePath -> Key -> Annex (Bool) dummyStore :: FilePath -> Key -> Annex (Bool)
dummyStore file key = return True dummyStore file key = return True
{- Allow keys to be removed. -}
dummyRemove :: Key -> Annex Bool
dummyRemove url = return True
{- Just check if the .git/annex/ file for the key exists. -} {- Just check if the .git/annex/ file for the key exists. -}
checkKeyFile :: Key -> Annex Bool checkKeyFile :: Key -> Annex Bool
checkKeyFile k = inAnnex k checkKeyFile k = inAnnex k
@ -102,3 +99,56 @@ copyFromRemote r key file = do
else error "cp failed" else error "cp failed"
getremote = error "get via network not yet implemented!" getremote = error "get via network not yet implemented!"
location = annexLocation r key location = annexLocation r key
{- Checks remotes to verify that enough copies of a key exist to allow
- for a key to be safely removed (with no data loss), and fails with an
- error if not. -}
checkRemoveKey :: Key -> Annex (Bool)
checkRemoveKey key = do
force <- Annex.flagIsSet Force
if (force)
then return True
else do
g <- Annex.gitRepo
let numcopies = read $ Git.configGet g config "1"
remotes <- Remotes.withKey key
if (numcopies > length remotes)
then retNotEnoughCopiesKnown remotes numcopies
else findcopies numcopies remotes []
where
failMsg w = do
liftIO $ hPutStrLn stderr $ "git-annex: " ++ w
return False -- failure, not enough copies found
findcopies 0 _ _ = return True -- success, enough copies found
findcopies _ [] bad = notEnoughCopiesSeen bad
findcopies n (r:rs) bad = do
all <- Annex.supportedBackends
result <- liftIO $ ((try $ remoteHasKey r all)::IO (Either SomeException Bool))
case (result) of
Right True -> findcopies (n-1) rs bad
Right False -> findcopies n rs bad
Left _ -> findcopies n rs (r:bad)
remoteHasKey r all = do
-- To check if a remote has a key, construct a new
-- Annex monad and query its backend.
a <- Annex.new r all
(result, _) <- Annex.run a (Backend.hasKey key)
return result
notEnoughCopiesSeen bad = failMsg $
"I failed to find enough other copies of: " ++
(keyFile key) ++
(if (0 /= length bad) then listbad bad else "")
++ unsafe
listbad bad = "\nI was unable to access these remotes: " ++
(Remotes.list bad)
retNotEnoughCopiesKnown remotes numcopies = failMsg $
"I only know about " ++ (show $ length remotes) ++
" out of " ++ (show numcopies) ++
" necessary copies of: " ++ (keyFile key) ++
unsafe
unsafe = "\n" ++
" -- According to the " ++ config ++
" setting, it is not safe to remove it!\n" ++
" (Use --force to override.)"
config = "annex.numcopies"

View file

@ -1,11 +1,7 @@
{- git-annex backend list {- git-annex backend list
- -} - -}
module BackendList ( module BackendList (allBackends) where
supportedBackends,
parseBackendList,
lookupBackendName
) where
import BackendTypes import BackendTypes
@ -13,25 +9,8 @@ import BackendTypes
import qualified Backend.WORM import qualified Backend.WORM
import qualified Backend.SHA1 import qualified Backend.SHA1
import qualified Backend.URL import qualified Backend.URL
supportedBackends = allBackends =
[ Backend.WORM.backend [ Backend.WORM.backend
, Backend.SHA1.backend , Backend.SHA1.backend
, Backend.URL.backend , Backend.URL.backend
] ]
{- Parses a string with a list of backend names into
- a list of Backend objects. If the list is empty,
- defaults to supportedBackends. -}
parseBackendList :: String -> [Backend]
parseBackendList s =
if (length s == 0)
then supportedBackends
else map (lookupBackendName) $ words s
{- Looks up a supported backend by name. -}
lookupBackendName :: String -> Backend
lookupBackendName s =
if ((length matches) /= 1)
then error $ "unknown backend " ++ s
else matches !! 0
where matches = filter (\b -> s == name b) supportedBackends

View file

@ -19,6 +19,7 @@ data Flag = Force | NoCommit | NeedCommit
data AnnexState = AnnexState { data AnnexState = AnnexState {
repo :: Git.Repo, repo :: Git.Repo,
backends :: [Backend], backends :: [Backend],
supportedBackends :: [Backend],
flags :: [Flag] flags :: [Flag]
} deriving (Show) } deriving (Show)

View file

@ -16,7 +16,6 @@ import qualified Annex
import Utility import Utility
import Locations import Locations
import qualified Backend import qualified Backend
import BackendList
import UUID import UUID
import LocationLog import LocationLog
import Types import Types
@ -169,10 +168,6 @@ dropCmd file = notinBackend file err $ \(key, backend) -> do
if (not inbackend) if (not inbackend)
then return () -- no-op then return () -- no-op
else do else do
force <- Annex.flagIsSet Force
if (not force)
then requireEnoughCopies key
else return ()
success <- Backend.removeKey backend key success <- Backend.removeKey backend key
if (success) if (success)
then cleanup key then cleanup key
@ -235,51 +230,8 @@ logStatus key status = do
gitAdd f Nothing -- all logs are committed at end gitAdd f Nothing -- all logs are committed at end
inBackend file yes no = do inBackend file yes no = do
r <- liftIO $ Backend.lookupFile file r <- Backend.lookupFile file
case (r) of case (r) of
Just v -> yes v Just v -> yes v
Nothing -> no Nothing -> no
notinBackend file yes no = inBackend file no yes notinBackend file yes no = inBackend file no yes
{- Checks remotes to verify that enough copies of a key exist to allow
- for a key to be safely removed (with no data loss), and fails with an
- error if not. -}
requireEnoughCopies :: Key -> Annex ()
requireEnoughCopies key = do
g <- Annex.gitRepo
let numcopies = read $ Git.configGet g config "1"
remotes <- Remotes.withKey key
if (numcopies > length remotes)
then error $ "I only know about " ++ (show $ length remotes) ++
" out of " ++ (show numcopies) ++
" necessary copies of: " ++ (keyFile key) ++
unsafe
else findcopies numcopies remotes []
where
findcopies 0 _ _ = return () -- success, enough copies found
findcopies _ [] bad = die bad
findcopies n (r:rs) bad = do
result <- liftIO $ try $ haskey r
case (result) of
Right True -> findcopies (n-1) rs bad
Right False -> findcopies n rs bad
Left _ -> findcopies n rs (r:bad)
haskey r = do
-- To check if a remote has a key, construct a new
-- Annex monad and query its backend.
a <- Annex.new r
(result, _) <- Annex.run a (Backend.hasKey key)
return result
die bad =
error $ "I failed to find enough other copies of: " ++
(keyFile key) ++
(if (0 /= length bad) then listbad bad else "")
++ unsafe
listbad bad = "\nI was unable to access these remotes: " ++
(Remotes.list bad)
unsafe = "\n" ++
" -- According to the " ++ config ++
" setting, it is not safe to remove it!\n" ++
" (Use --force to override.)"
config = "annex.numcopies"

View file

@ -9,11 +9,12 @@ import Types
import Core import Core
import Commands import Commands
import qualified GitRepo as Git import qualified GitRepo as Git
import BackendList
main = do main = do
args <- getArgs args <- getArgs
gitrepo <- Git.repoFromCwd gitrepo <- Git.repoFromCwd
state <- Annex.new gitrepo state <- Annex.new gitrepo allBackends
(flags, actions) <- parseCmd args state (flags, actions) <- parseCmd args state
tryRun state $ [startup flags] ++ actions ++ [shutdown] tryRun state $ [startup flags] ++ actions ++ [shutdown]