module AWS.EC2.Metadata
( latestVersion
, amiId
, amiLaunchIndex
, amiManifestPath
, blockDeviceMapping
, hostname
, instanceAction
, instanceId
, instanceType
, kernelId
, localHostname
, localIpv4
, mac
, metrics
, interfaces
, availabilityZone
, profile
, publicKeys
, reservationId
, securityGroups
, userdata
, idPkcs7
, idSignature
, idDocument
, query
) where
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BC
import Data.Text (Text)
import Data.Conduit
import Network.HTTP.Conduit hiding (path)
import Control.Monad.IO.Class (liftIO)
import Data.Monoid
import Control.Applicative
import Control.Exception
import qualified Data.Conduit.Binary as CB
import qualified Data.Conduit.List as CL
import AWS.Util
base :: String
base = "http://169.254.169.254"
version :: String
version = "/latest"
query :: String -> IO [ByteString]
query path = runResourceT $ do
req <- liftIO $ parseUrl (base <> path)
mgr <- liftIO $ newManager def
Response _ _ _ res <- http req mgr
res $$+- CB.lines =$ CL.consume
latestVersion :: IO Text
latestVersion = bsToText . last . init <$> query ""
metadata :: String -> IO [ByteString]
metadata path = query $ version <> "/meta-data/" <> path
h :: Functor f => f [ByteString] -> f Text
h = fmap (bsToText . head)
t :: [ByteString] -> [Text]
t = map bsToText
ignore :: IO (Maybe a) -> IO (Maybe a)
ignore io = handle ignore' io
where
ignore' :: HttpException -> IO (Maybe a)
ignore' _ = return Nothing
amiId :: IO Text
amiId = h $ metadata "ami-id"
amiLaunchIndex :: IO Text
amiLaunchIndex = h $ metadata "ami-launch-index"
amiManifestPath :: IO Text
amiManifestPath = h $ metadata "ami-manifest-path"
blockDeviceMapping :: IO [(Text, Text)]
blockDeviceMapping = do
bs <- metadata bdm
ds <- mapM (\b -> head <$> (metadata $ bdm <> BC.unpack b)) bs
return $ zip (t bs) (t ds)
where
bdm = "block-device-mapping/"
hostname :: IO Text
hostname = h $ metadata "hostname"
instanceAction :: IO Text
instanceAction = h $ metadata "instance-action"
instanceId :: IO Text
instanceId = h $ metadata "instance-id"
instanceType :: IO Text
instanceType = h $ metadata "instance-type"
kernelId :: IO Text
kernelId = h $ metadata "kernel-id"
localHostname :: IO Text
localHostname = h $ metadata "local-hostname"
localIpv4 :: IO Text
localIpv4 = h $ metadata "local-ipv4"
mac :: IO Text
mac = h $ metadata "mac"
metrics :: IO Text
metrics = h $ metadata "metrics/vhostmd"
interfaces :: IO [(Text, [(Text, Text)])]
interfaces = do
ms <- metadata macs
vs <- mapM (\b -> (val b)) ms
ts <- mapM (uncurry f) (zip ms vs)
return $ zip (map (bsToText . BC.init) ms) ts
where
macs = "network/interfaces/macs/"
f m ks = zip (t ks) <$> mapM (kv m) ks
kv m k = bsToText . head <$> val (m <> k)
val k = metadata $ macs <> BC.unpack k
availabilityZone :: IO Text
availabilityZone = h $ metadata "placement/availability-zone"
profile :: IO Text
profile = h $ metadata "profile"
publicKeys :: IO [Text]
publicKeys = t <$> metadata "public-keys"
reservationId :: IO Text
reservationId = h $ metadata "reservation-id"
securityGroups :: IO [Text]
securityGroups = t <$> metadata "security-groups"
userdata :: IO (Maybe Text)
userdata = ignore $
Just <$> (h <$> query $ version <> "/user-data")
queryRaw :: String -> IO ByteString
queryRaw path = runResourceT $ do
req <- liftIO $ parseUrl (base <> path)
mgr <- liftIO $ newManager def
Response _ _ _ res <- http req mgr
mconcat <$> (res $$+- CL.consume)
identity :: String -> IO Text
identity name =
bsToText <$> (queryRaw $ is <> name)
where
is = version <> "/dynamic/instance-identity/"
idPkcs7 :: IO Text
idPkcs7 = identity "pkcs7"
idSignature :: IO Text
idSignature = identity "signature"
idDocument :: IO Text
idDocument = identity "document"