Store an annex-uuid file in the bucket when setting up a new S3 remote.
This commit is contained in:
parent
bda4a6c56f
commit
883b17af01
2 changed files with 55 additions and 20 deletions
74
Remote/S3.hs
74
Remote/S3.hs
|
@ -8,7 +8,7 @@
|
||||||
module Remote.S3 (remote, iaHost, isIA, isIAHost, iaItemUrl) where
|
module Remote.S3 (remote, iaHost, isIA, isIAHost, iaItemUrl) where
|
||||||
|
|
||||||
import Network.AWS.AWSConnection
|
import Network.AWS.AWSConnection
|
||||||
import Network.AWS.S3Object
|
import Network.AWS.S3Object hiding (getStorageClass)
|
||||||
import Network.AWS.S3Bucket hiding (size)
|
import Network.AWS.S3Bucket hiding (size)
|
||||||
import Network.AWS.AWSResult
|
import Network.AWS.AWSResult
|
||||||
import qualified Data.Text as T
|
import qualified Data.Text as T
|
||||||
|
@ -96,7 +96,8 @@ s3Setup u c = if isIA c then archiveorg else defaulthost
|
||||||
archiveorg = do
|
archiveorg = do
|
||||||
showNote "Internet Archive mode"
|
showNote "Internet Archive mode"
|
||||||
maybe (error "specify bucket=") (const noop) $
|
maybe (error "specify bucket=") (const noop) $
|
||||||
M.lookup "bucket" archiveconfig
|
getBucket archiveconfig
|
||||||
|
writeUUIDFile archiveconfig u
|
||||||
use archiveconfig
|
use archiveconfig
|
||||||
where
|
where
|
||||||
archiveconfig =
|
archiveconfig =
|
||||||
|
@ -139,22 +140,15 @@ storeHelper (conn, bucket) r k p file = do
|
||||||
liftIO $ withMeteredFile file meterupdate $ \content -> do
|
liftIO $ withMeteredFile file meterupdate $ \content -> do
|
||||||
-- size is provided to S3 so the whole content
|
-- size is provided to S3 so the whole content
|
||||||
-- does not need to be buffered to calculate it
|
-- does not need to be buffered to calculate it
|
||||||
let object = setStorageClass storageclass $ S3Object
|
let object = S3Object
|
||||||
bucket (bucketFile r k) ""
|
bucket (bucketFile r k) ""
|
||||||
(("Content-Length", show size) : xheaders)
|
(("Content-Length", show size) : getXheaders (config r))
|
||||||
content
|
content
|
||||||
sendObject conn object
|
sendObject conn $
|
||||||
|
setStorageClass (getStorageClass $ config r) object
|
||||||
where
|
where
|
||||||
storageclass =
|
|
||||||
case fromJust $ M.lookup "storageclass" $ config r of
|
|
||||||
"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
|
|
||||||
_ -> STANDARD
|
|
||||||
|
|
||||||
getsize = liftIO $ fromIntegral . fileSize <$> getFileStatus file
|
getsize = liftIO $ fromIntegral . fileSize <$> getFileStatus file
|
||||||
|
|
||||||
xheaders = filter isxheader $ M.assocs $ config r
|
|
||||||
isxheader (h, _) = "x-amz-" `isPrefixOf` h
|
|
||||||
|
|
||||||
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool
|
retrieve :: Remote -> Key -> AssociatedFile -> FilePath -> MeterUpdate -> Annex Bool
|
||||||
retrieve r k _f d p = s3Action r False $ \(conn, bucket) ->
|
retrieve r k _f d p = s3Action r False $ \(conn, bucket) ->
|
||||||
metered (Just p) k $ \meterupdate -> do
|
metered (Just p) k $ \meterupdate -> do
|
||||||
|
@ -229,11 +223,13 @@ bucketFile :: Remote -> Key -> FilePath
|
||||||
bucketFile r = munge . key2file
|
bucketFile r = munge . key2file
|
||||||
where
|
where
|
||||||
munge s = case M.lookup "mungekeys" c of
|
munge s = case M.lookup "mungekeys" c of
|
||||||
Just "ia" -> iaMunge $ fileprefix ++ s
|
Just "ia" -> iaMunge $ filePrefix c ++ s
|
||||||
_ -> fileprefix ++ s
|
_ -> filePrefix c ++ s
|
||||||
fileprefix = M.findWithDefault "" "fileprefix" c
|
|
||||||
c = config r
|
c = config r
|
||||||
|
|
||||||
|
filePrefix :: RemoteConfig -> String
|
||||||
|
filePrefix = M.findWithDefault "" "fileprefix"
|
||||||
|
|
||||||
bucketKey :: Remote -> Bucket -> Key -> S3Object
|
bucketKey :: Remote -> Bucket -> Key -> S3Object
|
||||||
bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
|
bucketKey r bucket k = S3Object bucket (bucketFile r k) "" [] L.empty
|
||||||
|
|
||||||
|
@ -255,18 +251,43 @@ genBucket c u = do
|
||||||
showAction "checking bucket"
|
showAction "checking bucket"
|
||||||
loc <- liftIO $ getBucketLocation conn bucket
|
loc <- liftIO $ getBucketLocation conn bucket
|
||||||
case loc of
|
case loc of
|
||||||
Right _ -> noop
|
Right _ -> writeUUIDFile c u
|
||||||
Left err@(NetworkError _) -> s3Error err
|
Left err@(NetworkError _) -> s3Error err
|
||||||
Left (AWSError _ _) -> do
|
Left (AWSError _ _) -> do
|
||||||
showAction $ "creating bucket in " ++ datacenter
|
showAction $ "creating bucket in " ++ datacenter
|
||||||
res <- liftIO $ createBucketIn conn bucket datacenter
|
res <- liftIO $ createBucketIn conn bucket datacenter
|
||||||
case res of
|
case res of
|
||||||
Right _ -> noop
|
Right _ -> writeUUIDFile c u
|
||||||
Left err -> s3Error err
|
Left err -> s3Error err
|
||||||
where
|
where
|
||||||
bucket = fromJust $ M.lookup "bucket" c
|
bucket = fromJust $ getBucket c
|
||||||
datacenter = fromJust $ M.lookup "datacenter" c
|
datacenter = fromJust $ M.lookup "datacenter" c
|
||||||
|
|
||||||
|
{- Writes the UUID to an annex-uuid file within the bucket.
|
||||||
|
-
|
||||||
|
- If the file already exists in the bucket, it must match.
|
||||||
|
-
|
||||||
|
- Note that IA items do not get created by createBucketIn.
|
||||||
|
- Rather, they are created the first time a file is stored in them.
|
||||||
|
- So this also takes care of that.
|
||||||
|
-}
|
||||||
|
writeUUIDFile :: RemoteConfig -> UUID -> Annex ()
|
||||||
|
writeUUIDFile c u = do
|
||||||
|
conn <- s3ConnectionRequired c u
|
||||||
|
go conn =<< liftIO (tryNonAsync $ getObject conn $ mkobject L.empty)
|
||||||
|
where
|
||||||
|
go _conn (Right (Right o)) = unless (obj_data o == uuidb) $
|
||||||
|
error $ "This bucket is already in use by a different S3 special remote, with UUID: " ++ L.unpack (obj_data o)
|
||||||
|
go conn _ = do
|
||||||
|
let object = setStorageClass (getStorageClass c) (mkobject uuidb)
|
||||||
|
either s3Error return =<< liftIO (sendObject conn object)
|
||||||
|
|
||||||
|
file = filePrefix c ++ "annex-uuid"
|
||||||
|
uuidb = L.pack $ fromUUID u
|
||||||
|
bucket = fromJust $ getBucket c
|
||||||
|
|
||||||
|
mkobject = S3Object bucket file "" (getXheaders c)
|
||||||
|
|
||||||
s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection
|
s3ConnectionRequired :: RemoteConfig -> UUID -> Annex AWSConnection
|
||||||
s3ConnectionRequired c u =
|
s3ConnectionRequired c u =
|
||||||
maybe (error "Cannot connect to S3") return =<< s3Connection c u
|
maybe (error "Cannot connect to S3") return =<< s3Connection c u
|
||||||
|
@ -283,6 +304,19 @@ s3Connection c u = go =<< getRemoteCredPairFor "S3" c (AWS.creds u)
|
||||||
[(p, _)] -> p
|
[(p, _)] -> p
|
||||||
_ -> error $ "bad S3 port value: " ++ s
|
_ -> error $ "bad S3 port value: " ++ s
|
||||||
|
|
||||||
|
getBucket :: RemoteConfig -> Maybe Bucket
|
||||||
|
getBucket = M.lookup "bucket"
|
||||||
|
|
||||||
|
getStorageClass :: RemoteConfig -> StorageClass
|
||||||
|
getStorageClass c = case fromJust $ M.lookup "storageclass" c of
|
||||||
|
"REDUCED_REDUNDANCY" -> REDUCED_REDUNDANCY
|
||||||
|
_ -> STANDARD
|
||||||
|
|
||||||
|
getXheaders :: RemoteConfig -> [(String, String)]
|
||||||
|
getXheaders = filter isxheader . M.assocs
|
||||||
|
where
|
||||||
|
isxheader (h, _) = "x-amz-" `isPrefixOf` h
|
||||||
|
|
||||||
{- Hostname to use for archive.org S3. -}
|
{- Hostname to use for archive.org S3. -}
|
||||||
iaHost :: HostName
|
iaHost :: HostName
|
||||||
iaHost = "s3.us.archive.org"
|
iaHost = "s3.us.archive.org"
|
||||||
|
@ -299,4 +333,4 @@ iaItemUrl bucket = "http://archive.org/details/" ++ bucket
|
||||||
iaKeyUrl :: Remote -> Key -> URLString
|
iaKeyUrl :: Remote -> Key -> URLString
|
||||||
iaKeyUrl r k = "http://archive.org/download/" ++ bucket ++ "/" ++ bucketFile r k
|
iaKeyUrl r k = "http://archive.org/download/" ++ bucket ++ "/" ++ bucketFile r k
|
||||||
where
|
where
|
||||||
bucket = fromJust $ M.lookup "bucket" $ config r
|
bucket = fromMaybe "" $ getBucket $ config r
|
||||||
|
|
1
debian/changelog
vendored
1
debian/changelog
vendored
|
@ -46,6 +46,7 @@ git-annex (4.20130418) UNRELEASED; urgency=low
|
||||||
* webapp: Now automatically fills in any creds used by an existing remote
|
* webapp: Now automatically fills in any creds used by an existing remote
|
||||||
when creating a new remote of the same type. Done for Internet Archive,
|
when creating a new remote of the same type. Done for Internet Archive,
|
||||||
S3, Glacier, and Box.com remotes.
|
S3, Glacier, and Box.com remotes.
|
||||||
|
* Store an annex-uuid file in the bucket when setting up a new S3 remote.
|
||||||
|
|
||||||
-- Joey Hess <joeyh@debian.org> Thu, 18 Apr 2013 16:22:48 -0400
|
-- Joey Hess <joeyh@debian.org> Thu, 18 Apr 2013 16:22:48 -0400
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue