Fix stalls in S3 when transferring encrypted data.
Stalls were caused by code that did approximatly: content' <- liftIO $ withEncryptedContent cipher content return store content' The return evaluated without actually reading content from S3, and so the cleanup code began waiting on gpg to exit before gpg could send all its data. Fixing it involved moving the `store` type action into the IO monad: liftIO $ withEncryptedContent cipher content store Which was a bit of a pain to do, thank you type system, but avoids the problem as now the whole content is consumed, and stored, before cleanup.
This commit is contained in:
parent
1687fecd33
commit
a441e08da1
4 changed files with 50 additions and 34 deletions
|
@ -199,7 +199,10 @@ gpgPipeStrict params input = do
|
||||||
return output
|
return output
|
||||||
|
|
||||||
{- Runs gpg with a cipher and some parameters, feeding it an input,
|
{- Runs gpg with a cipher and some parameters, feeding it an input,
|
||||||
- and passing a handle to its output to an action. -}
|
- and passing a handle to its output to an action.
|
||||||
|
-
|
||||||
|
- Note that to avoid deadlock with the cleanup stage,
|
||||||
|
- the action must fully consume gpg's input before returning. -}
|
||||||
gpgCipherHandle :: [CommandParam] -> Cipher -> L.ByteString -> (Handle -> IO a) -> IO a
|
gpgCipherHandle :: [CommandParam] -> Cipher -> L.ByteString -> (Handle -> IO a) -> IO a
|
||||||
gpgCipherHandle params c input a = do
|
gpgCipherHandle params c input a = do
|
||||||
-- pipe the passphrase into gpg on a fd
|
-- pipe the passphrase into gpg on a fd
|
||||||
|
|
|
@ -100,13 +100,13 @@ s3Setup u c = do
|
||||||
loc <- liftIO $ getBucketLocation conn bucket
|
loc <- liftIO $ getBucketLocation conn bucket
|
||||||
case loc of
|
case loc of
|
||||||
Right _ -> return ()
|
Right _ -> return ()
|
||||||
Left err@(NetworkError _) -> error $ prettyReqError err
|
Left err@(NetworkError _) -> s3Error err
|
||||||
Left (AWSError _ _) -> do
|
Left (AWSError _ _) -> do
|
||||||
showNote $ "creating bucket in " ++ datacenter
|
showNote $ "creating bucket in " ++ datacenter
|
||||||
res <- liftIO $ createBucketIn conn bucket datacenter
|
res <- liftIO $ createBucketIn conn bucket datacenter
|
||||||
case res of
|
case res of
|
||||||
Right _ -> return ()
|
Right _ -> return ()
|
||||||
Left err -> error $ prettyReqError err
|
Left err -> s3Error err
|
||||||
|
|
||||||
gitConfigSpecialRemote u fullconfig "s3" "true"
|
gitConfigSpecialRemote u fullconfig "s3" "true"
|
||||||
return fullconfig
|
return fullconfig
|
||||||
|
@ -141,33 +141,32 @@ checkPresent r k = s3Action r noconn $ \(conn, bucket) -> do
|
||||||
case res of
|
case res of
|
||||||
Right _ -> return $ Right True
|
Right _ -> return $ Right True
|
||||||
Left (AWSError _ _) -> return $ Right False
|
Left (AWSError _ _) -> return $ Right False
|
||||||
Left e -> return $ Left (error $ prettyReqError e)
|
Left e -> return $ Left (s3Error e)
|
||||||
where
|
where
|
||||||
noconn = Left $ error "S3 not configured"
|
noconn = Left $ error "S3 not configured"
|
||||||
|
|
||||||
store :: Remote Annex -> Key -> Annex Bool
|
store :: Remote Annex -> Key -> Annex Bool
|
||||||
store r k = storeHelper r k =<< lazyKeyContent k
|
store r k = s3Action r False $ \(conn, bucket) -> do
|
||||||
|
content <- lazyKeyContent k
|
||||||
|
res <- liftIO $ storeHelper (conn, bucket) r k content
|
||||||
|
s3Bool res
|
||||||
|
|
||||||
storeEncrypted :: Remote Annex -> (Cipher, Key) -> Key -> Annex Bool
|
storeEncrypted :: Remote Annex -> (Cipher, Key) -> Key -> Annex Bool
|
||||||
storeEncrypted r (cipher, enck) k = do
|
storeEncrypted r (cipher, enck) k = s3Action r False $ \(conn, bucket) -> do
|
||||||
content <- lazyKeyContent k
|
content <- lazyKeyContent k
|
||||||
content' <- liftIO $ withEncryptedContent cipher content return
|
res <- liftIO $ withEncryptedContent cipher content $ \s -> do
|
||||||
storeHelper r enck content'
|
storeHelper (conn, bucket) r enck s
|
||||||
|
s3Bool res
|
||||||
|
|
||||||
lazyKeyContent :: Key -> Annex L.ByteString
|
lazyKeyContent :: Key -> Annex L.ByteString
|
||||||
lazyKeyContent k = do
|
lazyKeyContent k = do
|
||||||
g <- Annex.gitRepo
|
g <- Annex.gitRepo
|
||||||
liftIO $ L.readFile $ gitAnnexLocation g k
|
liftIO $ L.readFile $ gitAnnexLocation g k
|
||||||
|
|
||||||
storeHelper :: Remote Annex -> Key -> L.ByteString -> Annex Bool
|
storeHelper :: (AWSConnection, String) -> Remote Annex -> Key -> L.ByteString -> IO (AWSResult ())
|
||||||
storeHelper r k content = s3Action r False $ \(conn, bucket) -> do
|
storeHelper (conn, bucket) r k content = do
|
||||||
let object = setStorageClass storageclass $ bucketKey bucket k content
|
let object = setStorageClass storageclass $ bucketKey bucket k content
|
||||||
res <- liftIO $ sendObject conn object
|
sendObject conn object
|
||||||
case res of
|
|
||||||
Right _ -> return True
|
|
||||||
Left e -> do
|
|
||||||
warning $ prettyReqError e
|
|
||||||
return False
|
|
||||||
where
|
where
|
||||||
storageclass =
|
storageclass =
|
||||||
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
|
case fromJust $ M.lookup "storageclass" $ fromJust $ config r of
|
||||||
|
@ -175,30 +174,41 @@ storeHelper r k content = s3Action r False $ \(conn, bucket) -> do
|
||||||
_ -> STANDARD
|
_ -> STANDARD
|
||||||
|
|
||||||
retrieve :: Remote Annex -> Key -> FilePath -> Annex Bool
|
retrieve :: Remote Annex -> Key -> FilePath -> Annex Bool
|
||||||
retrieve = retrieveHelper (return . obj_data)
|
retrieve r k f = s3Action r False $ \(conn, bucket) -> do
|
||||||
|
|
||||||
retrieveEncrypted :: Remote Annex -> (Cipher, Key) -> FilePath -> Annex Bool
|
|
||||||
retrieveEncrypted r (cipher, enck) f = retrieveHelper decrypt r enck f
|
|
||||||
where
|
|
||||||
decrypt o = withDecryptedContent cipher (obj_data o) return
|
|
||||||
|
|
||||||
retrieveHelper :: (S3Object -> IO L.ByteString) -> Remote Annex -> Key -> FilePath -> Annex Bool
|
|
||||||
retrieveHelper a r k f = s3Action r False $ \(conn, bucket) -> do
|
|
||||||
res <- liftIO $ getObject conn $ bucketKey bucket k L.empty
|
res <- liftIO $ getObject conn $ bucketKey bucket k L.empty
|
||||||
case res of
|
case res of
|
||||||
Right o -> do
|
Right o -> do
|
||||||
content <- liftIO $ a o
|
liftIO $ L.writeFile f $ obj_data o
|
||||||
liftIO $ L.writeFile f content
|
|
||||||
return True
|
return True
|
||||||
Left e -> do
|
Left e -> s3Warning e
|
||||||
warning $ prettyReqError e
|
|
||||||
return False
|
retrieveEncrypted :: Remote Annex -> (Cipher, Key) -> FilePath -> Annex Bool
|
||||||
|
retrieveEncrypted r (cipher, enck) f = s3Action r False $ \(conn, bucket) -> do
|
||||||
|
res <- liftIO $ getObject conn $ bucketKey bucket enck L.empty
|
||||||
|
case res of
|
||||||
|
Right o -> liftIO $
|
||||||
|
withDecryptedContent cipher (obj_data o) $ \content -> do
|
||||||
|
L.writeFile f content
|
||||||
|
return True
|
||||||
|
Left e -> s3Warning e
|
||||||
|
|
||||||
remove :: Remote Annex -> Key -> Annex Bool
|
remove :: Remote Annex -> Key -> Annex Bool
|
||||||
remove r k = s3Action r False $ \(conn, bucket) -> do
|
remove r k = s3Action r False $ \(conn, bucket) -> do
|
||||||
res <- liftIO $ deleteObject conn $ bucketKey bucket k L.empty
|
res <- liftIO $ deleteObject conn $ bucketKey bucket k L.empty
|
||||||
case res of
|
case res of
|
||||||
Right _ -> return True
|
Right _ -> return True
|
||||||
Left e -> do
|
Left e -> s3Warning e
|
||||||
|
|
||||||
|
s3Warning :: ReqError -> Annex Bool
|
||||||
|
s3Warning e = do
|
||||||
warning $ prettyReqError e
|
warning $ prettyReqError e
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
s3Error :: ReqError -> a
|
||||||
|
s3Error e = error $ prettyReqError e
|
||||||
|
|
||||||
|
s3Bool :: AWSResult () -> Annex Bool
|
||||||
|
s3Bool res = do
|
||||||
|
case res of
|
||||||
|
Right _ -> return True
|
||||||
|
Left e -> s3Warning e
|
||||||
|
|
1
debian/changelog
vendored
1
debian/changelog
vendored
|
@ -3,6 +3,7 @@ git-annex (0.20110418) UNRELEASED; urgency=low
|
||||||
* Don't run gpg in batch mode, so it can prompt for passphrase when
|
* Don't run gpg in batch mode, so it can prompt for passphrase when
|
||||||
there is no agent.
|
there is no agent.
|
||||||
* Add missing build dep on dataenc.
|
* Add missing build dep on dataenc.
|
||||||
|
* Fix stalls in S3 when transferring encrypted data.
|
||||||
|
|
||||||
-- Joey Hess <joeyh@debian.org> Sun, 17 Apr 2011 14:29:49 -0400
|
-- Joey Hess <joeyh@debian.org> Sun, 17 Apr 2011 14:29:49 -0400
|
||||||
|
|
||||||
|
|
|
@ -5,3 +5,5 @@ dialup.
|
||||||
There was a similar issue with bup, which I fixed by forking a process
|
There was a similar issue with bup, which I fixed by forking a process
|
||||||
rather than using a thread to do some IO. Probably need the same here.
|
rather than using a thread to do some IO. Probably need the same here.
|
||||||
--[[Joey]]
|
--[[Joey]]
|
||||||
|
|
||||||
|
[[done]] --[[Joey]]
|
||||||
|
|
Loading…
Reference in a new issue