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)
#if MIN_VERSION_conduit(1,1,0)
import Control.Monad.Trans.Resource (runResourceT)
#endif
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 conduitManagerSettings
res <- http req mgr
responseBody 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 conduitManagerSettings
res <- http req mgr
mconcat <$> (responseBody 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"