import Assistant.Alert.Utility
import Utility.Tmp
import Utility.NotificationBroadcaster
+import Types.Availability
import Types.Transfer
import Logs.Transfer
import Logs.Trust
let (exportremotes, nonexportremotes) = partition (exportTree . Remote.config) contentremotes
let isimport r = importTree (Remote.config r) || Remote.thirdPartyPopulated (Remote.remotetype r)
let dataremotes = filter (not . isimport) nonexportremotes
+ tocloud <- anyM iscloud contentremotes
return $ \dstatus -> dstatus
{ syncRemotes = syncable
, syncDataRemotes = dataremotes
, exportRemotes = exportremotes
, downloadRemotes = contentremotes
- , syncingToCloudRemote = any iscloud contentremotes
+ , syncingToCloudRemote = tocloud
}
where
- iscloud r = not (Remote.readonly r) && Remote.availability r == Remote.GloballyAvailable
+ iscloud r
+ | Remote.readonly r = pure False
+ | otherwise = tryNonAsync (Remote.availability r) >>= return . \case
+ Right GloballyAvailable -> True
+ _ -> False
{- Updates the syncRemotes list from the list of all remotes in Annex state. -}
updateSyncRemotes :: Assistant ()
directory does not exist. An empty tree was imported, rather than the
import failing.
* Stop bundling curl in the OSX dmg and linux standalone image.
+ * sync, assist, push, pull: Skip more types of remotes when they
+ are not present due to eg being on a drive that is offline.
+ * Added AVAILABILITY UNAVAILABLE and the UNAVAILABLERESPONSE extension
+ to the external special remote protocol.
+ * The remote.name.annex-availability git config is no longer used.
-- Joey Hess <id@joeyh.name> Mon, 07 Aug 2023 13:04:13 -0400
import Annex.CheckIgnore
import Types.FileMatcher
import Types.GitConfig
+import Types.Availability
import qualified Database.Export as Export
import Utility.Bloom
import Utility.OptParse
listed = concat <$> mapM Remote.byNameOrGroup ps
- good r
- | Remote.gitSyncableRemoteType (Remote.remotetype r) =
- Remote.Git.repoAvail =<< Remote.getRepo r
- | otherwise = return True
+ good r = tryNonAsync (Remote.availability r) >>= return . \case
+ Right Unavailable -> False
+ _ -> True
fastest = fromMaybe [] . headMaybe . Remote.byCost
, gitconfig = gc
, localpath = Nothing
, remotetype = remote
- , availability = LocallyAvailable
+ , availability = pure LocallyAvailable
, readonly = False
, appendonly = False
, untrustworthy = False
, readonly = True
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = return []
, gitconfig = gc
, localpath = borgRepoLocalPath borgrepo
, remotetype = remote
- , availability = if borgLocal borgrepo then LocallyAvailable else GloballyAvailable
+ , availability = pure $
+ if borgLocal borgrepo then LocallyAvailable else GloballyAvailable
, readonly = False
, appendonly = False
-- When the user sets the appendonly field, they are
then Just buprepo
else Nothing
, remotetype = remote
- , availability = if bupLocal buprepo then LocallyAvailable else GloballyAvailable
+ , availability = pure $
+ if bupLocal buprepo then LocallyAvailable else GloballyAvailable
, readonly = False
, appendonly = False
, untrustworthy = False
then Just $ ddarRepoLocation ddarrepo
else Nothing
, remotetype = remote
- , availability = if ddarLocal ddarrepo then LocallyAvailable else GloballyAvailable
+ , availability = pure $
+ if ddarLocal ddarrepo then LocallyAvailable else GloballyAvailable
, readonly = False
, appendonly = False
, untrustworthy = False
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = LocallyAvailable
+ , availability = pure LocallyAvailable
, remotetype = remote
, mkUnavailable = gen r u rc
(gc { remoteAnnexDirectory = Just "/dev/null" }) rs
| externaltype == "readonly" = do
c <- parsedRemoteConfig remote rc
cst <- remoteCost gc c expensiveRemoteCost
- let rmt = mk c cst GloballyAvailable
+ let rmt = mk c cst (pure GloballyAvailable)
Nothing
(externalInfo externaltype)
Nothing
(Git.remoteName r) (Just rs)
Annex.addCleanupAction (RemoteCleanup u) $ stopExternal external
cst <- getCost external r gc c
- avail <- getAvailability external r gc
exportsupported <- if exportTree c
then checkExportSupported' external
else return False
let cheapexportsupported = if exportsupported
then exportIsSupported
else exportUnsupported
- let rmt = mk c cst avail
+ let rmt = mk c cst (getAvailability external)
(Just (whereisKeyM external))
(getInfoM external)
(Just (claimUrlM external))
return c
defcst = expensiveRemoteCost
-{- Caches the availability in the git config to avoid needing to start up an
- - external special remote every time time just to ask it what its
- - availability is.
- -
- - Most remotes do not bother to implement a reply to this request;
+{- Most remotes do not bother to implement a reply to this request;
- globally available is the default.
-}
-getAvailability :: External -> Git.Repo -> RemoteGitConfig -> Annex Availability
-getAvailability external r gc =
- maybe (catchNonAsync query (const (pure defavail))) return
- (remoteAnnexAvailability gc)
+getAvailability :: External -> Annex Availability
+getAvailability external = catchNonAsync query (const (pure defavail))
where
- query = do
- avail <- handleRequest external GETAVAILABILITY Nothing $ \req -> case req of
- AVAILABILITY avail -> result avail
- UNSUPPORTED_REQUEST -> result defavail
- _ -> Nothing
- setRemoteAvailability r avail
- return avail
+ query = handleRequest external GETAVAILABILITY Nothing $ \req -> case req of
+ AVAILABILITY avail -> result avail
+ UNSUPPORTED_REQUEST -> result defavail
+ _ -> Nothing
defavail = GloballyAvailable
claimUrlM :: External -> URLString -> Annex Bool
supportedExtensionList = ExtensionList
[ "INFO"
, "GETGITREMOTENAME"
+ , "UNAVAILABLERESPONSE"
, asyncExtension
]
instance Proto.Serializable Availability where
serialize GloballyAvailable = "GLOBAL"
serialize LocallyAvailable = "LOCAL"
+ serialize Unavailable = "UNAVAILABLE"
deserialize "GLOBAL" = Just GloballyAvailable
deserialize "LOCAL" = Just LocallyAvailable
+ deserialize "UNAVAILABLE" = Just Unavailable
deserialize _ = Nothing
instance Proto.Serializable [(URLString, Size, FilePath)] where
, readonly = Git.repoIsHttp r
, appendonly = False
, untrustworthy = False
- , availability = availabilityCalc r
+ , availability = pure (availabilityCalc r)
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = gitRepoInfo this
{- Standard git remotes.
-
- - Copyright 2011-2021 Joey Hess <id@joeyh.name>
+ - Copyright 2011-2023 Joey Hess <id@joeyh.name>
-
- Licensed under the GNU AGPL version 3 or higher.
-}
module Remote.Git (
remote,
configRead,
- repoAvail,
onLocalRepo,
) where
, readonly = Git.repoIsHttp r
, appendonly = False
, untrustworthy = False
- , availability = availabilityCalc r
+ , availability = repoAvail r
, remotetype = remote
, mkUnavailable = unavailable r u rc gc rs
, getInfo = gitRepoInfo new
_ -> r -- already unavailable
{- Checks relatively inexpensively if a repository is available for use. -}
-repoAvail :: Git.Repo -> Annex Bool
+repoAvail :: Git.Repo -> Annex Availability
repoAvail r
- | Git.repoIsHttp r = return True
+ | Git.repoIsHttp r = return GloballyAvailable
| Git.GCrypt.isEncrypted r = do
g <- gitRepo
liftIO $ do
er <- Git.GCrypt.encryptedRemote g r
if Git.repoIsLocal er || Git.repoIsLocalUnknown er
- then catchBoolIO $
- void (Git.Config.read er) >> return True
- else return True
- | Git.repoIsUrl r = return True
- | Git.repoIsLocalUnknown r = return False
- | otherwise = liftIO $ isJust <$> catchMaybeIO (Git.Config.read r)
+ then checklocal er
+ else return GloballyAvailable
+ | Git.repoIsUrl r = return GloballyAvailable
+ | Git.repoIsLocalUnknown r = return Unavailable
+ | otherwise = checklocal r
+ where
+ checklocal r' = ifM (liftIO $ isJust <$> catchMaybeIO (Git.Config.read r'))
+ ( return LocallyAvailable
+ , return Unavailable
+ )
{- Tries to read the config for a specified remote, updates state, and
- returns the updated repo. -}
, gitconfig = gc
, localpath = Nothing
, remotetype = remote
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, readonly = False
-- content cannot be removed from a git-lfs repo
, appendonly = True
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = includeCredsInfo c (AWS.creds u) $
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = gen r u rc
(gc { remoteAnnexHookType = Just "!dne!" })
, readonly = True
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = return []
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = gitRepoInfo this
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = if islocal then LocallyAvailable else GloballyAvailable
+ , availability = pure $
+ if islocal then LocallyAvailable else GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = return [("url", url)]
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = gen r u (M.insert hostField (Proposed "!dne!") rc) gc rs
, getInfo = includeCredsInfo c (AWS.creds u) (s3Info c info)
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = return []
, readonly = True
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = return Nothing
, getInfo = return []
, readonly = False
, appendonly = False
, untrustworthy = False
- , availability = GloballyAvailable
+ , availability = pure GloballyAvailable
, remotetype = remote
, mkUnavailable = gen r u (M.insert urlField (Proposed "http://!dne!/") rc) gc rs
, getInfo = includeCredsInfo c (davCreds u) $
module Types.Availability where
-data Availability = GloballyAvailable | LocallyAvailable
- deriving (Eq, Show, Read)
+data Availability = GloballyAvailable | LocallyAvailable | Unavailable
+ deriving (Eq, Show)
import Config.Cost
import Types.UUID
import Types.Distribution
-import Types.Availability
import Types.Concurrency
import Types.NumCopies
import Types.Difference
, remoteAnnexTrustLevel :: Maybe String
, remoteAnnexStartCommand :: Maybe String
, remoteAnnexStopCommand :: Maybe String
- , remoteAnnexAvailability :: Maybe Availability
, remoteAnnexSpeculatePresent :: Bool
, remoteAnnexBare :: Maybe Bool
, remoteAnnexRetry :: Maybe Integer
, remoteAnnexTrustLevel = notempty $ getmaybe "trustlevel"
, remoteAnnexStartCommand = notempty $ getmaybe "start-command"
, remoteAnnexStopCommand = notempty $ getmaybe "stop-command"
- , remoteAnnexAvailability = getmayberead "availability"
, remoteAnnexSpeculatePresent = getbool "speculate-present" False
, remoteAnnexBare = getmaybebool "bare"
, remoteAnnexRetry = getmayberead "retry"
-- decide.
, untrustworthy :: Bool
-- a Remote can be globally available. (Ie, "in the cloud".)
- , availability :: Availability
+ -- Some Remotes can mark themselves unavailable.
+ , availability :: a Availability
-- the type of the remote
, remotetype :: RemoteTypeA a
-- For testing, makes a version of this remote that is not
protocol extensions that it supports. Older versions of
git-annex do not send this message.
- EXTENSIONS INFO ASYNC GETGITREMOTENAME
+ EXTENSIONS INFO ASYNC GETGITREMOTENAME UNAVAILABLERESPONSE
The special remote can respond to that with its own EXTENSIONS message, listing
any extensions it wants to use.
(Ie stored in the cloud vs on a local disk.)
If the remote replies with `UNSUPPORTED-REQUEST`, its availability
is assumed to be global. So, only remotes that are only reachable
- locally need to worry about implementing this.
+ locally need to worry about implementing this.
+ This is queried at remote startup, so should avoid doing anything that
+ can take long to run or is expensive. Checking if a directory where the
+ remote stores files is currently mounted is the kind of thing it makes
+ sense to do here.
* `AVAILABILITY GLOBAL|LOCAL`
Indicates if the remote is globally or only locally available.
+ * `AVAILABILITY UNAVAILABLE`
+ Indicates that the remote is not currently available.
+ This will prevent some git-annex commands like `git-annex sync` from
+ trying to use the remote.
+ Older versions of git-annex do not support this response, so avoid
+ sending it unless the `UNAVAILABLERESPONSE` extension is enabled.
* `CLAIMURL Url`
Asks the remote if it wishes to claim responsibility for downloading
an url.
These protocol extensions are currently supported.
* `INFO`
- This makes the `INFO` message available to use.
+ This allows using the `INFO` message.
* `ASYNC`
This lets multiple actions be performed at the same time by
a single external special remote program, rather than starting multiple
programs. See the [[async_appendix]] for details.
* `GETGITREMOTENAME`
- This makes the `GETGITREMOTENAME` message available to use.
+ This allows using the `GETGITREMOTENAME` message.
+* `UNAVAILABLERESPONSE`
+ This allows the `AVAILABILITY UNAVAILABLE` response to be used
+ in reply to `GETAVAILABILITY`.
## signals
* `remote.<name>.annex-availability`
- Can be used to tell git-annex whether a remote is LocallyAvailable
- or GloballyAvailable. Normally, git-annex determines this automatically.
+ This configuration setting is no longer used.
* `remote.<name>.annex-speculate-present`