don't send PREPARE before INITREMOTE

That complicated special remote programs, because they had to avoid making
PREPARE fail if some configuration is missing, because the remote might not
be initialized yet. Instead, complicate git-annex slightly by only sending
PREPARE immediately before some other request other than INITREMOTE (or
PREPARE of course).
This commit is contained in:
Joey Hess 2013-12-27 02:49:10 -04:00
parent 5b7c38c90a
commit 3289155e28
3 changed files with 31 additions and 14 deletions

View file

@ -169,6 +169,8 @@ handleRequest external req mp responsehandler =
handleRequest' :: ExternalLock -> External -> Request -> Maybe MeterUpdate -> (Response -> Maybe (Annex a)) -> Annex a handleRequest' :: ExternalLock -> External -> Request -> Maybe MeterUpdate -> (Response -> Maybe (Annex a)) -> Annex a
handleRequest' lck external req mp responsehandler = do handleRequest' lck external req mp responsehandler = do
when (needsPREPARE req) $
checkPrepared lck external
sendMessage lck external req sendMessage lck external req
loop loop
where where
@ -230,15 +232,13 @@ fromExternal lck external extractor a =
void $ liftIO $ atomically $ swapTMVar v st void $ liftIO $ atomically $ swapTMVar v st
{- Handle initial protocol startup; check the VERSION {- Handle initial protocol startup; check the VERSION
- the remote sends, and send it the PREPARE request. -} - the remote sends. -}
receiveMessage lck external receiveMessage lck external
(const Nothing) (const Nothing)
(checkVersion lck external) (checkVersion lck external)
(const Nothing) (const Nothing)
handleRequest' lck external PREPARE Nothing $ \resp ->
case resp of run st
PREPARE_SUCCESS -> Just $ run st
_ -> Nothing
run st = a $ extractor st run st = a $ extractor st
v = externalState external v = externalState external
@ -259,6 +259,7 @@ startExternal externaltype = liftIO $ do
{ externalSend = hin { externalSend = hin
, externalReceive = hout , externalReceive = hout
, externalPid = pid , externalPid = pid
, externalPrepared = False
} }
stopExternal :: External -> Annex () stopExternal :: External -> Annex ()
@ -282,6 +283,15 @@ checkVersion lck external (VERSION v) = Just $
else sendMessage lck external (ERROR "unsupported VERSION") else sendMessage lck external (ERROR "unsupported VERSION")
checkVersion _ _ _ = Nothing checkVersion _ _ _ = Nothing
checkPrepared :: ExternalLock -> External -> Annex ()
checkPrepared lck external =
fromExternal lck external externalPrepared $ \prepared ->
unless prepared $
handleRequest' lck external PREPARE Nothing $ \resp ->
case resp of
PREPARE_SUCCESS -> Just noop
_ -> Nothing
{- Caches the cost in the git config to avoid needing to start up an {- Caches the cost in the git config to avoid needing to start up an
- external special remote every time time just to ask it what its - external special remote every time time just to ask it what its
- cost is. -} - cost is. -}

View file

@ -18,6 +18,7 @@ module Remote.External.Types (
Sendable(..), Sendable(..),
Receivable(..), Receivable(..),
Request(..), Request(..),
needsPREPARE,
Response(..), Response(..),
RemoteRequest(..), RemoteRequest(..),
RemoteResponse(..), RemoteResponse(..),
@ -60,6 +61,7 @@ data ExternalState = ExternalState
{ externalSend :: Handle { externalSend :: Handle
, externalReceive :: Handle , externalReceive :: Handle
, externalPid :: ProcessHandle , externalPid :: ProcessHandle
, externalPrepared :: Bool
} }
-- Constructor is not exported, and only created by newExternal. -- Constructor is not exported, and only created by newExternal.
@ -98,6 +100,12 @@ data Request
| REMOVE Key | REMOVE Key
deriving (Show) deriving (Show)
-- Does PREPARE need to have been sent before this request?
needsPREPARE :: Request -> Bool
needsPREPARE PREPARE = False
needsPREPARE INITREMOTE = False
needsPREPARE _ = True
instance Sendable Request where instance Sendable Request where
formatMessage PREPARE = ["PREPARE"] formatMessage PREPARE = ["PREPARE"]
formatMessage INITREMOTE = ["INITREMOTE"] formatMessage INITREMOTE = ["INITREMOTE"]

View file

@ -36,8 +36,9 @@ the version of the protocol it is using.
VERSION 1 VERSION 1
Once it knows the version, git-annex will send a message telling the Once it knows the version, git-annex will generally
special remote to start up. send a message telling the special remote to start up.
(Or it might send a INITREMOTE, so don't hardcode this order.)
PREPARE PREPARE
@ -84,17 +85,15 @@ send one of the corresponding replies listed in the next section.
The following requests *must* all be supported by the special remote. The following requests *must* all be supported by the special remote.
* `PREPARE`
Tells the special remote it's time to prepare itself to be used.
Only run once, at startup, always immediately after the special remote
sends VERSION.
* `INITREMOTE` * `INITREMOTE`
Request that the remote initialized itself. This is where any one-time Request that the remote initialize itself. This is where any one-time
setup tasks can be done, for example creating an Amazon S3 bucket. setup tasks can be done, for example creating an Amazon S3 bucket.
(PREPARE is still sent before this.) Note: This may be run repeatedly over time, as a remote is initialized in
Note: This may be run repeatedly, as a remote is initialized in
different repositories, or as the configuration of a remote is changed. different repositories, or as the configuration of a remote is changed.
So any one-time setup tasks should be done idempotently. So any one-time setup tasks should be done idempotently.
* `PREPARE`
Tells the special remote it's time to prepare itself to be used.
Only INITREMOTE can come before this.
* `TRANSFER STORE|RETRIEVE Key File` * `TRANSFER STORE|RETRIEVE Key File`
Requests the transfer of a key. For Send, the File is the file to upload; Requests the transfer of a key. For Send, the File is the file to upload;
for Receive the File is where to store the download. for Receive the File is where to store the download.