testremote: Support testing readonly remotes with the --test-readonly option
This commit was sponsored by Ilya Shlyakhter on Patreon.
This commit is contained in:
parent
8230b62e06
commit
8555169e71
4 changed files with 89 additions and 38 deletions
|
@ -29,6 +29,7 @@ git-annex (7.20181212) UNRELEASED; urgency=medium
|
||||||
allowing for future expansion.
|
allowing for future expansion.
|
||||||
* addunused, merge, assistant: Avoid creating work tree files in
|
* addunused, merge, assistant: Avoid creating work tree files in
|
||||||
subdirectories in an edge case where the key contains "/".
|
subdirectories in an edge case where the key contains "/".
|
||||||
|
* testremote: Support testing readonly remotes with the --test-readonly option.
|
||||||
|
|
||||||
-- Joey Hess <id@joeyh.name> Tue, 18 Dec 2018 12:24:52 -0400
|
-- Joey Hess <id@joeyh.name> Tue, 18 Dec 2018 12:24:52 -0400
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{- git-annex command
|
{- git-annex command
|
||||||
-
|
-
|
||||||
- Copyright 2014 Joey Hess <id@joeyh.name>
|
- Copyright 2014-2019 Joey Hess <id@joeyh.name>
|
||||||
-
|
-
|
||||||
- Licensed under the GNU GPL version 3 or higher.
|
- Licensed under the GNU GPL version 3 or higher.
|
||||||
-}
|
-}
|
||||||
|
@ -14,7 +14,9 @@ import qualified Types.Remote as Remote
|
||||||
import qualified Types.Backend as Backend
|
import qualified Types.Backend as Backend
|
||||||
import Types.KeySource
|
import Types.KeySource
|
||||||
import Annex.Content
|
import Annex.Content
|
||||||
|
import Annex.WorkTree
|
||||||
import Backend
|
import Backend
|
||||||
|
import Logs.Location
|
||||||
import qualified Backend.Hash
|
import qualified Backend.Hash
|
||||||
import Utility.Tmp
|
import Utility.Tmp
|
||||||
import Utility.Metered
|
import Utility.Metered
|
||||||
|
@ -42,6 +44,7 @@ cmd = command "testremote" SectionTesting
|
||||||
data TestRemoteOptions = TestRemoteOptions
|
data TestRemoteOptions = TestRemoteOptions
|
||||||
{ testRemote :: RemoteName
|
{ testRemote :: RemoteName
|
||||||
, sizeOption :: ByteSize
|
, sizeOption :: ByteSize
|
||||||
|
, testReadonlyFile :: [FilePath]
|
||||||
}
|
}
|
||||||
|
|
||||||
optParser :: CmdParamsDesc -> Parser TestRemoteOptions
|
optParser :: CmdParamsDesc -> Parser TestRemoteOptions
|
||||||
|
@ -52,22 +55,43 @@ optParser desc = TestRemoteOptions
|
||||||
<> value (1024 * 1024)
|
<> value (1024 * 1024)
|
||||||
<> help "base key size (default 1MiB)"
|
<> help "base key size (default 1MiB)"
|
||||||
)
|
)
|
||||||
|
<*> many testreadonly
|
||||||
|
where
|
||||||
|
testreadonly = option str
|
||||||
|
( long "test-readonly" <> metavar paramFile
|
||||||
|
<> help "readonly test object"
|
||||||
|
)
|
||||||
|
|
||||||
seek :: TestRemoteOptions -> CommandSeek
|
seek :: TestRemoteOptions -> CommandSeek
|
||||||
seek o = commandAction $ start (fromInteger $ sizeOption o) (testRemote o)
|
seek = commandAction . start
|
||||||
|
|
||||||
start :: Int -> RemoteName -> CommandStart
|
start :: TestRemoteOptions -> CommandStart
|
||||||
start basesz name = do
|
start o = do
|
||||||
showStart' "testremote" (Just name)
|
showStart' "testremote" (Just (testRemote o))
|
||||||
fast <- Annex.getState Annex.fast
|
fast <- Annex.getState Annex.fast
|
||||||
r <- either giveup disableExportTree =<< Remote.byName' name
|
r <- either giveup disableExportTree =<< Remote.byName' (testRemote o)
|
||||||
rs <- catMaybes <$> mapM (adjustChunkSize r) (chunkSizes basesz fast)
|
ks <- case testReadonlyFile o of
|
||||||
rs' <- concat <$> mapM encryptionVariants rs
|
[] -> if Remote.readonly r
|
||||||
unavailrs <- catMaybes <$> mapM Remote.mkUnavailable [r]
|
then giveup "This remote is readonly, so you need to use the --test-readonly option."
|
||||||
exportr <- exportTreeVariant r
|
else do
|
||||||
showAction "generating test keys"
|
showAction "generating test keys"
|
||||||
ks <- mapM randKey (keySizes basesz fast)
|
mapM randKey (keySizes basesz fast)
|
||||||
next $ perform rs' unavailrs exportr ks
|
fs -> mapM (getReadonlyKey r) fs
|
||||||
|
let r' = if null (testReadonlyFile o)
|
||||||
|
then r
|
||||||
|
else r { Remote.readonly = True }
|
||||||
|
rs <- if Remote.readonly r'
|
||||||
|
then return [r']
|
||||||
|
else do
|
||||||
|
rs <- catMaybes <$> mapM (adjustChunkSize r') (chunkSizes basesz fast)
|
||||||
|
concat <$> mapM encryptionVariants rs
|
||||||
|
unavailrs <- catMaybes <$> mapM Remote.mkUnavailable [r']
|
||||||
|
exportr <- if Remote.readonly r'
|
||||||
|
then return Nothing
|
||||||
|
else exportTreeVariant r'
|
||||||
|
next $ perform rs unavailrs exportr ks
|
||||||
|
where
|
||||||
|
basesz = fromInteger $ sizeOption o
|
||||||
|
|
||||||
perform :: [Remote] -> [Remote] -> Maybe Remote -> [Key] -> CommandPerform
|
perform :: [Remote] -> [Remote] -> Maybe Remote -> [Key] -> CommandPerform
|
||||||
perform rs unavailrs exportr ks = do
|
perform rs unavailrs exportr ks = do
|
||||||
|
@ -132,18 +156,18 @@ adjustRemoteConfig r adjustconfig = do
|
||||||
(Remote.gitconfig r)
|
(Remote.gitconfig r)
|
||||||
|
|
||||||
test :: Annex.AnnexState -> Remote -> Key -> [TestTree]
|
test :: Annex.AnnexState -> Remote -> Key -> [TestTree]
|
||||||
test st r k =
|
test st r k = catMaybes
|
||||||
[ check "removeKey when not present" remove
|
[ whenwritable $ check "removeKey when not present" remove
|
||||||
, present False
|
, whenwritable $ present False
|
||||||
, check "storeKey" store
|
, whenwritable $ check "storeKey" store
|
||||||
, present True
|
, whenwritable $ present True
|
||||||
, check "storeKey when already present" store
|
, whenwritable $ check "storeKey when already present" store
|
||||||
, present True
|
, whenwritable $ present True
|
||||||
, check "retrieveKeyFile" $ do
|
, Just $ check "retrieveKeyFile" $ do
|
||||||
lockContentForRemoval k removeAnnex
|
lockContentForRemoval k removeAnnex
|
||||||
get
|
get
|
||||||
, check "fsck downloaded object" fsck
|
, Just $ check "fsck downloaded object" fsck
|
||||||
, check "retrieveKeyFile resume from 33%" $ do
|
, Just $ check "retrieveKeyFile resume from 33%" $ do
|
||||||
loc <- Annex.calcRepo (gitAnnexLocation k)
|
loc <- Annex.calcRepo (gitAnnexLocation k)
|
||||||
tmp <- prepTmp k
|
tmp <- prepTmp k
|
||||||
partial <- liftIO $ bracket (openBinaryFile loc ReadMode) hClose $ \h -> do
|
partial <- liftIO $ bracket (openBinaryFile loc ReadMode) hClose $ \h -> do
|
||||||
|
@ -152,24 +176,25 @@ test st r k =
|
||||||
liftIO $ L.writeFile tmp partial
|
liftIO $ L.writeFile tmp partial
|
||||||
lockContentForRemoval k removeAnnex
|
lockContentForRemoval k removeAnnex
|
||||||
get
|
get
|
||||||
, check "fsck downloaded object" fsck
|
, Just $ check "fsck downloaded object" fsck
|
||||||
, check "retrieveKeyFile resume from 0" $ do
|
, Just $ check "retrieveKeyFile resume from 0" $ do
|
||||||
tmp <- prepTmp k
|
tmp <- prepTmp k
|
||||||
liftIO $ writeFile tmp ""
|
liftIO $ writeFile tmp ""
|
||||||
lockContentForRemoval k removeAnnex
|
lockContentForRemoval k removeAnnex
|
||||||
get
|
get
|
||||||
, check "fsck downloaded object" fsck
|
, Just $ check "fsck downloaded object" fsck
|
||||||
, check "retrieveKeyFile resume from end" $ do
|
, Just $ check "retrieveKeyFile resume from end" $ do
|
||||||
loc <- Annex.calcRepo (gitAnnexLocation k)
|
loc <- Annex.calcRepo (gitAnnexLocation k)
|
||||||
tmp <- prepTmp k
|
tmp <- prepTmp k
|
||||||
void $ liftIO $ copyFileExternal CopyAllMetaData loc tmp
|
void $ liftIO $ copyFileExternal CopyAllMetaData loc tmp
|
||||||
lockContentForRemoval k removeAnnex
|
lockContentForRemoval k removeAnnex
|
||||||
get
|
get
|
||||||
, check "fsck downloaded object" fsck
|
, Just $ check "fsck downloaded object" fsck
|
||||||
, check "removeKey when present" remove
|
, whenwritable $ check "removeKey when present" remove
|
||||||
, present False
|
, whenwritable $ present False
|
||||||
]
|
]
|
||||||
where
|
where
|
||||||
|
whenwritable a = if Remote.readonly r then Nothing else Just a
|
||||||
check desc a = testCase desc $
|
check desc a = testCase desc $
|
||||||
Annex.eval st (Annex.setOutput QuietOutput >> a) @? "failed"
|
Annex.eval st (Annex.setOutput QuietOutput >> a) @? "failed"
|
||||||
present b = check ("present " ++ show b) $
|
present b = check ("present " ++ show b) $
|
||||||
|
@ -252,7 +277,9 @@ testUnavailable st r k =
|
||||||
checkval v @? ("(got: " ++ show v ++ ")")
|
checkval v @? ("(got: " ++ show v ++ ")")
|
||||||
|
|
||||||
cleanup :: [Remote] -> [Key] -> Bool -> CommandCleanup
|
cleanup :: [Remote] -> [Key] -> Bool -> CommandCleanup
|
||||||
cleanup rs ks ok = do
|
cleanup rs ks ok
|
||||||
|
| all Remote.readonly rs = return ok
|
||||||
|
| otherwise = do
|
||||||
forM_ rs $ \r -> forM_ ks (Remote.removeKey r)
|
forM_ rs $ \r -> forM_ ks (Remote.removeKey r)
|
||||||
forM_ ks $ \k -> lockContentForRemoval k removeAnnex
|
forM_ ks $ \k -> lockContentForRemoval k removeAnnex
|
||||||
return ok
|
return ok
|
||||||
|
@ -297,3 +324,13 @@ randKey sz = withTmpFile "randkey" $ \f h -> do
|
||||||
<$> Backend.getKey Backend.Hash.testKeyBackend ks
|
<$> Backend.getKey Backend.Hash.testKeyBackend ks
|
||||||
_ <- moveAnnex k f
|
_ <- moveAnnex k f
|
||||||
return k
|
return k
|
||||||
|
|
||||||
|
getReadonlyKey :: Remote -> FilePath -> Annex Key
|
||||||
|
getReadonlyKey r f = lookupFile f >>= \case
|
||||||
|
Nothing -> giveup $ f ++ " is not an annexed file"
|
||||||
|
Just k -> do
|
||||||
|
unlessM (inAnnex k) $
|
||||||
|
giveup $ f ++ " does not have its content locally present, cannot test it"
|
||||||
|
unlessM ((Remote.uuid r `elem`) <$> loggedLocations k) $
|
||||||
|
giveup $ f ++ " is not stored in the remote being tested, cannot test it"
|
||||||
|
return k
|
||||||
|
|
|
@ -8,8 +8,8 @@ git annex testremote `remote`
|
||||||
|
|
||||||
# DESCRIPTION
|
# DESCRIPTION
|
||||||
|
|
||||||
This tests a remote by generating some random objects and sending them to
|
This tests a remote by sending objects to it, downloading objects from it,
|
||||||
the remote, then redownloading them, removing them from the remote, etc.
|
etc.
|
||||||
|
|
||||||
It's safe to run in an existing repository (the repository contents are
|
It's safe to run in an existing repository (the repository contents are
|
||||||
not altered), although it may perform expensive data transfers.
|
not altered), although it may perform expensive data transfers.
|
||||||
|
@ -20,7 +20,8 @@ the cleanup might fail, leaving test data in the remote.
|
||||||
|
|
||||||
Testing will use the remote's configuration, automatically varying
|
Testing will use the remote's configuration, automatically varying
|
||||||
the chunk sizes, and with simple shared encryption disabled and enabled,
|
the chunk sizes, and with simple shared encryption disabled and enabled,
|
||||||
and exporttree disabled and enabled.
|
and exporttree disabled and enabled. If the remote is readonly, testing
|
||||||
|
is limited to checking various properties of downloading from it.
|
||||||
|
|
||||||
# OPTIONS
|
# OPTIONS
|
||||||
|
|
||||||
|
@ -28,9 +29,19 @@ and exporttree disabled and enabled.
|
||||||
|
|
||||||
Perform a smaller set of tests.
|
Perform a smaller set of tests.
|
||||||
|
|
||||||
|
* `--test-readonly=file`
|
||||||
|
|
||||||
|
Normally, random objects are generated for the test and are sent to the
|
||||||
|
remote. When a readonly remote is being tested, that cannot be done,
|
||||||
|
and so you need to specify some annexed files to use in the testing,
|
||||||
|
using this option. Their content needs to be present in the readonly remote
|
||||||
|
being tested, and in the local repository.
|
||||||
|
|
||||||
|
This option can be repeated.
|
||||||
|
|
||||||
* `--size=NUnits`
|
* `--size=NUnits`
|
||||||
|
|
||||||
Tune the base size of the generated objects. The default is 1MiB.
|
Tune the base size of generated objects. The default is 1MiB.
|
||||||
|
|
||||||
# SEE ALSO
|
# SEE ALSO
|
||||||
|
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
It's not uncommon to create external special remotes for which, like for the built-in web remote, only download operations are defined. It would be good if git-annex-testremote had the option of testing such remotes, using as test data the keys and URLs already registered as present in the remote. This could also be used to test addurl-related functionality for fully implemented remotes; currently this part of a remote's implementation isn't tested.
|
It's not uncommon to create external special remotes for which, like for the built-in web remote, only download operations are defined. It would be good if git-annex-testremote had the option of testing such remotes, using as test data the keys and URLs already registered as present in the remote. This could also be used to test addurl-related functionality for fully implemented remotes; currently this part of a remote's implementation isn't tested.
|
||||||
|
|
||||||
|
> Good idea, [[done]] using the --test-readonly option. --[[Joey]]
|
||||||
|
|
Loading…
Reference in a new issue