{-# OPTIONS_GHC -fno-warn-duplicate-exports #-} -- | -- Module : Amazonka -- Copyright : (c) 2013-2021 Brendan Hay -- License : Mozilla Public License, v. 2.0. -- Maintainer : Brendan Hay <brendan.g.hay+amazonka@gmail.com> -- Stability : provisional -- Portability : non-portable (GHC extensions) -- -- This module provides simple 'Env' and 'IO'-based operations which -- can be performed against remote Amazon Web Services APIs, for use with the types -- supplied by the various @amazonka-*@ libraries. module Amazonka ( -- * Usage -- $usage -- * Authentication and Environment Env.Env' (..), Env.Env, Env.EnvNoAuth, Env.newEnv, Env.newEnvNoAuth, Env.newEnvWith, Env.envAuthMaybe, -- ** Service Configuration -- $service Env.authenticate, Env.override, Env.configure, Env.within, Env.once, Env.timeout, -- *** Lenses Env.envRegion, Env.envLogger, Env.envRetryCheck, Env.envOverride, Env.envManager, Env.envAuth, -- ** Running AWS Actions runResourceT, -- ** Credential Discovery AccessKey (..), SecretKey (..), SessionToken (..), Credentials (..), -- $discovery -- ** Supported Regions Region (..), -- ** Service Endpoints Endpoint (..), Endpoint.setEndpoint, -- * Sending Requests -- $sending send, sendEither, -- ** Pagination -- $pagination paginate, paginateEither, -- ** Waiters -- $waiters await, awaitEither, -- ** Streaming -- $streaming ToBody (..), RequestBody (..), ResponseBody (..), -- *** Hashed Request Bodies ToHashedBody (..), HashedBody (..), Body.hashedFile, Body.hashedFileRange, Body.hashedBody, -- *** Chunked Request Bodies ChunkedBody (..), ChunkSize (..), defaultChunkSize, Body.chunkedFile, Body.chunkedFileRange, Body.unsafeChunkedBody, -- *** Response Bodies Body.sinkBody, -- *** File Size and MD5/SHA256 Body.getFileSize, Crypto.sinkMD5, Crypto.sinkSHA256, -- * Presigning Requests -- $presigning presignURL, presign, -- * EC2 Instance Metadata -- $metadata EC2.Dynamic (..), dynamic, EC2.Metadata (..), metadata, userdata, -- * Running Asynchronous Actions -- $async -- * Handling Errors -- $errors AsError (..), AsAuthError (..), Lens.trying, Lens.catching, -- ** Building Error Prisms Error._MatchServiceError, Error.hasService, Error.hasStatus, Error.hasCode, -- * Logging -- $logging LogLevel (..), Logger, -- ** Constructing a Logger newLogger, -- * Re-exported Types module Amazonka.Core, ) where import Amazonka.Auth import Amazonka.Core import qualified Amazonka.Crypto as Crypto import qualified Amazonka.Data.Body as Body import qualified Amazonka.EC2.Metadata as EC2 import qualified Amazonka.Endpoint as Endpoint import Amazonka.Env (Env) import qualified Amazonka.Env as Env import qualified Amazonka.Error as Error import qualified Amazonka.HTTP as HTTP import qualified Amazonka.Lens as Lens import Amazonka.Logger import qualified Amazonka.Pager as Pager import Amazonka.Prelude import qualified Amazonka.Presign as Presign import Amazonka.Request (clientRequestURL) import qualified Amazonka.Waiter as Waiter import qualified Control.Exception as Exception import Control.Monad.Trans.Resource (runResourceT) import Data.Conduit (ConduitM) import qualified Data.Conduit as Conduit import Data.Monoid (Dual (..), Endo (..)) import qualified Network.HTTP.Client as Client -- $usage -- The key functions dealing with the request/response lifecycle are: -- -- * 'send', 'sendThrow' -- -- * 'paginate', 'paginateThrow' -- -- * 'await', 'awaitThrow' -- -- These functions have constraints that types from the @amazonka-*@ libraries -- satisfy. To utilise these, you will need to specify what 'Region' you wish to -- operate in and your Amazon credentials for AuthN/AuthZ purposes. -- -- 'Credentials' can be supplied in a number of ways. Either via explicit keys, -- via session profiles, or have Amazonka retrieve the credentials from an -- underlying IAM Role/Profile. -- -- As a basic example, you might wish to store an object in an S3 bucket using -- <http://hackage.haskell.org/package/amazonka-s3 amazonka-s3>: -- -- @ -- {-# LANGUAGE OverloadedStrings #-} -- -- import qualified Amazonka as AWS -- import qualified Amazonka.S3 as S3 -- import qualified System.IO as IO -- -- example :: IO S3.PutObjectResponse -- example = do -- -- A new 'Logger' to replace the default noop logger is created, with the logger -- -- set to print debug information and errors to stdout: -- logger <- AWS.newLogger AWS.Debug IO.stdout -- -- -- To specify configuration preferences, 'newEnv' is used to create a new -- -- configuration environment. The 'Credentials' parameter is used to specify -- -- mechanism for supplying or retrieving AuthN/AuthZ information. -- -- In this case 'Discover' will cause the library to try a number of options such -- -- as default environment variables, or an instance's IAM Profile and identity document: -- discover <- AWS.newEnv AWS.Discover -- -- let env = -- discover -- { AWS._envLogger = logger -- , AWS._envRegion = AWS.Frankfurt -- } -- -- -- The payload (and hash) for the S3 object is retrieved from a 'FilePath', -- -- either 'hashedFile' or 'chunkedFile' can be used, with the latter ensuring -- -- the contents of the file is enumerated exactly once, during send: -- body <- AWS.chunkedFile AWS.defaultChunkSize "local\/path\/to\/object-payload" -- -- -- We now run the 'AWS' computation with the overriden logger, performing the -- -- 'PutObject' request. '_envRegion' or 'within' can be used to set the -- -- remote AWS 'Region': -- AWS.runResourceT $ -- AWS.send env (S3.newPutObject "bucket-name" "object-key" body) -- @ -- $discovery -- AuthN/AuthZ information is handled similarly to other AWS SDKs. You can read -- some of the options available <http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs here>. -- -- When running on an EC2 instance and using 'FromProfile' or 'Discover', a thread -- is forked which transparently handles the expiry and subsequent refresh of IAM -- profile information. See 'Amazonka.Auth.fromProfileName' for more information. -- $sending -- To send a request you need to create a value of the desired operation type using -- the relevant constructor, as well as any further modifications of default/optional -- parameters using the appropriate lenses. This value can then be sent using 'send' -- or 'paginate' and the library will take care of serialisation/authentication and -- so forth. -- -- The default 'Service' configuration for a request contains retry configuration that is used to -- determine if a request can safely be retried and what kind of back off/on strategy -- should be used. (Usually exponential.) -- Typically services define retry strategies that handle throttling, general server -- errors and transport errors. Streaming requests are never retried. -- $pagination -- Some AWS operations return results that are incomplete and require subsequent -- requests in order to obtain the entire result set. The process of sending -- subsequent requests to continue where a previous request left off is called -- pagination. For example, the 'ListObjects' operation of Amazon S3 returns up to -- 1000 objects at a time, and you must send subsequent requests with the -- appropriate Marker in order to retrieve the next page of results. -- -- Operations that have an 'AWSPager' instance can transparently perform subsequent -- requests, correctly setting Markers and other request facets to iterate through -- the entire result set of a truncated API operation. Operations which support -- this have an additional note in the documentation. -- -- Many operations have the ability to filter results on the server side. See the -- individual operation parameters for details. -- $waiters -- Waiters poll by repeatedly sending a request until some remote success condition -- configured by the 'Wait' specification is fulfilled. The 'Wait' specification -- determines how many attempts should be made, in addition to delay and retry strategies. -- Error conditions that are not handled by the 'Wait' configuration will be thrown, -- or the first successful response that fulfills the success condition will be -- returned. -- -- 'Wait' specifications can be found under the @Amazonka.{ServiceName}.Waiters@ -- namespace for services which support 'await'. -- $service -- When a request is sent, various values such as the endpoint, -- retry strategy, timeout and error handlers are taken from the associated 'Service' -- for a request. For example, 'DynamoDB' will use the 'Amazonka.DynamoDB.defaultService' -- configuration when sending 'PutItem', 'Query' and all other operations. -- -- You can modify a specific 'Service''s default configuration by using -- 'configure' or 'reconfigure'. To modify all configurations simultaneously, see 'override'. -- -- An example of how you might alter default configuration using these mechanisms -- is demonstrated below. Firstly, the default 'dynamoDB' service is configured to -- use non-SSL localhost as the endpoint: -- -- -- > import qualified Amazonka as AWS -- > import qualified Amazonka.DynamoDB as Dynamo -- > -- > let dynamo :: AWS.Service -- > dynamo = AWS.setEndpoint False "localhost" 8000 DynamoDB.defaultService -- -- The updated configuration is then passed to the 'Env' during setup: -- -- > env <- AWS.configure dynamo <$> AWS.newEnv AWS.Discover -- > -- > AWS.runResourceT $ do -- > -- This S3 operation will communicate with remote AWS APIs. -- > x <- AWS.send env newListBuckets -- > -- > -- DynamoDB operations will communicate with localhost:8000. -- > y <- AWS.send env Dynamo.newListTables -- > -- > -- Any operations for services other than DynamoDB, are not affected. -- > ... -- -- You can also scope the service configuration modifications to specific actions: -- -- > env <- AWS.newEnv AWS.Discover -- > -- > AWS.runResourceT $ do -- > -- Service operations here will communicate with AWS, even remote DynamoDB. -- > x <- AWS.send env Dynamo.newListTables -- > -- > -- Here DynamoDB operations will communicate with localhost:8000. -- > y <- AWS.send (AWS.configure dynamo env) Dynamo.newListTables -- -- Functions such as 'within', 'once', and 'timeout' can also be used to modify -- service configuration for all (or specific) requests. -- $streaming -- Streaming comes in two flavours. 'HashedBody' represents a request -- that requires a precomputed 'SHA256' hash, or a 'ChunkedBody' type for those services -- that can perform incremental signing and do not require the entire payload to -- be hashed (such as S3). The type signatures for request smart constructors -- advertise which respective body type is required, denoting the underlying signing -- capabilities. -- -- 'ToHashedBody' and 'ToBody' typeclass instances are available to construct the -- streaming bodies, automatically calculating any hash or size as needed for types -- such as 'Text', 'ByteString', or Aeson's 'Value' type. To read files and other -- 'IO' primitives, functions such as 'hashedFile', 'chunkedFile', or 'hashedBody' -- should be used. -- -- For responses that contain streaming bodies (such as 'GetObject'), you can use -- 'sinkBody' to connect the response body to a -- <http://hackage.haskell.org/package/conduit conduit>-compatible sink. -- $presigning -- Presigning requires the 'Service' signer to be an instance of 'AWSPresigner'. -- Not all signing algorithms support this. -- $metadata -- Metadata can be retrieved from the underlying host assuming that you're running -- the code on an EC2 instance or have a compatible @instance-data@ endpoint available. -- $async -- Requests can be sent asynchronously, but due to guarantees about resource closure -- require the use of "UnliftIO.Async". -- -- The following example demonstrates retrieving two objects from S3 concurrently: -- -- @ -- {-# LANGUAGE OverloadedStrings #-} -- import qualified Amazonka as AWS -- import qualified Amazonka.S3 as S3 -- import qualified UnliftIO.Async as Async -- -- let requestA = S3.newGetObject "bucket" "prefix/object-a" -- let requestB = S3.newGetObject "bucket" "prefix/object-b" -- -- runResourceT $ -- Async.'UnliftIO.Async.withAsync' (send env requestA) $ \\asyncA -> -- Async.'UnliftIO.Async.withAsync' (send env requestB) $ \\asyncB -> do -- Async.'UnliftIO.Async.waitBoth' asyncA asyncB -- @ -- -- If you are running many async requests in parallel, using -- 'Control.Monad.Trans.Cont.ContT' can hide the giant callback pyramid: -- -- @ -- runResourceT . 'Control.Monad.Trans.Cont.evalContT' $ do -- asyncA <- ContT $ Async.'UnliftIO.Async.withAsync' (send env requestA) -- asyncB <- ContT $ Async.'UnliftIO.Async.withAsync' (send env requestB) -- Async.'UnliftIO.Async.waitBoth' asyncA asyncB -- @ -- $errors -- Errors are either returned or thrown by the library using 'IO'. Sub-errors of -- the canonical 'Error' type can be caught using 'trying' or 'catching' and the -- appropriate 'AsError' 'Prism' when using the non-'Either' send variants: -- -- @ -- trying '_Error' (send $ newListObjects "bucket-name") :: Either 'Error' ListObjectsResponse -- trying '_TransportError' (send $ newListObjects "bucket-name") :: Either 'HttpException' ListObjectsResponse -- trying '_SerializeError' (send $ newListObjects "bucket-name") :: Either 'SerializeError' ListObjectsResponse -- trying '_ServiceError' (send $ newListObjects "bucket-name") :: Either 'ServiceError' ListObjectsResponse -- @ -- -- Many of the individual @amazonka-*@ libraries export compatible 'Control.Lens.Getter's for -- matching service specific error codes and messages in the style above. -- See the @Error Matchers@ heading in each respective library for details. -- $logging -- The exposed logging interface is a primitive 'Logger' function which gets -- threaded through service calls and serialisation routines. This allows the -- library to output useful information and diagnostics. -- -- The 'newLogger' function can be used to construct a simple logger which writes -- output to a 'Handle', but in most production code you should probably consider -- using a more robust logging library such as -- <http://hackage.haskell.org/package/tinylog tinylog> or -- <http://hackage.haskell.org/package/fast-logger fast-logger>. -- | Send a request, returning the associated response if successful. -- -- See 'send'. sendEither :: ( MonadResource m, AWSRequest a ) => Env -> a -> m (Either Error (AWSResponse a)) sendEither :: Env -> a -> m (Either Error (AWSResponse a)) sendEither Env env = (Either Error (Response (AWSResponse a)) -> Either Error (AWSResponse a)) -> m (Either Error (Response (AWSResponse a))) -> m (Either Error (AWSResponse a)) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap ((Response (AWSResponse a) -> AWSResponse a) -> Either Error (Response (AWSResponse a)) -> Either Error (AWSResponse a) forall (p :: * -> * -> *) b c a. Bifunctor p => (b -> c) -> p a b -> p a c second Response (AWSResponse a) -> AWSResponse a forall body. Response body -> body Client.responseBody) (m (Either Error (Response (AWSResponse a))) -> m (Either Error (AWSResponse a))) -> (a -> m (Either Error (Response (AWSResponse a)))) -> a -> m (Either Error (AWSResponse a)) forall b c a. (b -> c) -> (a -> b) -> a -> c . Env -> a -> m (Either Error (Response (AWSResponse a))) forall (m :: * -> *) a (withAuth :: * -> *). (MonadResource m, AWSRequest a, Foldable withAuth) => Env' withAuth -> a -> m (Either Error (ClientResponse (AWSResponse a))) HTTP.retryRequest Env env -- | Send a request, returning the associated response if successful. -- -- Errors are thrown in 'IO'. -- -- See 'sendEither'. send :: ( MonadResource m, AWSRequest a ) => Env -> a -> m (AWSResponse a) send :: Env -> a -> m (AWSResponse a) send Env env = Env -> a -> m (Either Error (AWSResponse a)) forall (m :: * -> *) a. (MonadResource m, AWSRequest a) => Env -> a -> m (Either Error (AWSResponse a)) sendEither Env env (a -> m (Either Error (AWSResponse a))) -> (Either Error (AWSResponse a) -> m (AWSResponse a)) -> a -> m (AWSResponse a) forall (m :: * -> *) a b c. Monad m => (a -> m b) -> (b -> m c) -> a -> m c >=> Either Error (AWSResponse a) -> m (AWSResponse a) forall (m :: * -> *) a. MonadIO m => Either Error a -> m a hoistEither -- | Repeatedly send a request, automatically setting markers and performing pagination. -- -- Exits on the first encountered error. -- -- See 'paginate'. paginateEither :: ( MonadResource m, AWSPager a ) => Env -> a -> ConduitM () (AWSResponse a) m (Either Error ()) paginateEither :: Env -> a -> ConduitM () (AWSResponse a) m (Either Error ()) paginateEither Env env = a -> ConduitM () (AWSResponse a) m (Either Error ()) go where go :: a -> ConduitM () (AWSResponse a) m (Either Error ()) go a rq = m (Either Error (AWSResponse a)) -> ConduitT () (AWSResponse a) m (Either Error (AWSResponse a)) forall (t :: (* -> *) -> * -> *) (m :: * -> *) a. (MonadTrans t, Monad m) => m a -> t m a lift (Env -> a -> m (Either Error (AWSResponse a)) forall (m :: * -> *) a. (MonadResource m, AWSRequest a) => Env -> a -> m (Either Error (AWSResponse a)) sendEither Env env a rq) ConduitT () (AWSResponse a) m (Either Error (AWSResponse a)) -> (Either Error (AWSResponse a) -> ConduitM () (AWSResponse a) m (Either Error ())) -> ConduitM () (AWSResponse a) m (Either Error ()) forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b >>= \case Left Error err -> Either Error () -> ConduitM () (AWSResponse a) m (Either Error ()) forall (f :: * -> *) a. Applicative f => a -> f a pure (Error -> Either Error () forall a b. a -> Either a b Left Error err) Right AWSResponse a rs -> do AWSResponse a -> ConduitT () (AWSResponse a) m () forall (m :: * -> *) o i. Monad m => o -> ConduitT i o m () Conduit.yield AWSResponse a rs ConduitM () (AWSResponse a) m (Either Error ()) -> (a -> ConduitM () (AWSResponse a) m (Either Error ())) -> Maybe a -> ConduitM () (AWSResponse a) m (Either Error ()) forall b a. b -> (a -> b) -> Maybe a -> b maybe (Either Error () -> ConduitM () (AWSResponse a) m (Either Error ()) forall (f :: * -> *) a. Applicative f => a -> f a pure (() -> Either Error () forall a b. b -> Either a b Right ())) a -> ConduitM () (AWSResponse a) m (Either Error ()) go (a -> AWSResponse a -> Maybe a forall a. AWSPager a => a -> AWSResponse a -> Maybe a Pager.page a rq AWSResponse a rs) -- | Repeatedly send a request, automatically setting markers and performing pagination. -- Exits on the first encountered error. -- -- Errors are thrown in 'IO'. -- -- See 'paginateEither'. paginate :: ( MonadResource m, AWSPager a ) => Env -> a -> ConduitM () (AWSResponse a) m () paginate :: Env -> a -> ConduitM () (AWSResponse a) m () paginate Env env = Env -> a -> ConduitM () (AWSResponse a) m (Either Error ()) forall (m :: * -> *) a. (MonadResource m, AWSPager a) => Env -> a -> ConduitM () (AWSResponse a) m (Either Error ()) paginateEither Env env (a -> ConduitM () (AWSResponse a) m (Either Error ())) -> (Either Error () -> ConduitM () (AWSResponse a) m ()) -> a -> ConduitM () (AWSResponse a) m () forall (m :: * -> *) a b c. Monad m => (a -> m b) -> (b -> m c) -> a -> m c >=> Either Error () -> ConduitM () (AWSResponse a) m () forall (m :: * -> *) a. MonadIO m => Either Error a -> m a hoistEither -- | Poll the API with the supplied request until a specific 'Wait' condition -- is fulfilled. -- -- See 'await'. awaitEither :: ( MonadResource m, AWSRequest a ) => Env -> Waiter.Wait a -> a -> m (Either Error Waiter.Accept) awaitEither :: Env -> Wait a -> a -> m (Either Error Accept) awaitEither Env env Wait a wait = Env -> Wait a -> a -> m (Either Error Accept) forall (m :: * -> *) a (withAuth :: * -> *). (MonadResource m, AWSRequest a, Foldable withAuth) => Env' withAuth -> Wait a -> a -> m (Either Error Accept) HTTP.awaitRequest Env env Wait a wait -- | Poll the API with the supplied request until a specific 'Wait' condition -- is fulfilled. -- -- Errors are thrown in 'IO'. -- -- See 'awaitEither'. await :: ( MonadResource m, AWSRequest a ) => Env -> Waiter.Wait a -> a -> m Waiter.Accept await :: Env -> Wait a -> a -> m Accept await Env env Wait a wait = Env -> Wait a -> a -> m (Either Error Accept) forall (m :: * -> *) a. (MonadResource m, AWSRequest a) => Env -> Wait a -> a -> m (Either Error Accept) awaitEither Env env Wait a wait (a -> m (Either Error Accept)) -> (Either Error Accept -> m Accept) -> a -> m Accept forall (m :: * -> *) a b c. Monad m => (a -> m b) -> (b -> m c) -> a -> m c >=> Either Error Accept -> m Accept forall (m :: * -> *) a. MonadIO m => Either Error a -> m a hoistEither hoistEither :: MonadIO m => Either Error a -> m a hoistEither :: Either Error a -> m a hoistEither = (Error -> m a) -> (a -> m a) -> Either Error a -> m a forall a c b. (a -> c) -> (b -> c) -> Either a b -> c either (IO a -> m a forall (m :: * -> *) a. MonadIO m => IO a -> m a liftIO (IO a -> m a) -> (Error -> IO a) -> Error -> m a forall b c a. (b -> c) -> (a -> b) -> a -> c . Error -> IO a forall e a. Exception e => e -> IO a Exception.throwIO) a -> m a forall (f :: * -> *) a. Applicative f => a -> f a pure -- | Presign an URL that is valid from the specified time until the -- number of seconds expiry has elapsed. presignURL :: ( MonadIO m, AWSRequest a ) => Env -> -- | Signing time. UTCTime -> -- | Expiry time. Seconds -> -- | Request to presign. a -> m ByteString presignURL :: Env -> UTCTime -> Seconds -> a -> m ByteString presignURL Env env UTCTime time Seconds expires = (ClientRequest -> ByteString) -> m ClientRequest -> m ByteString forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b fmap ClientRequest -> ByteString clientRequestURL (m ClientRequest -> m ByteString) -> (a -> m ClientRequest) -> a -> m ByteString forall b c a. (b -> c) -> (a -> b) -> a -> c . Env -> UTCTime -> Seconds -> a -> m ClientRequest forall (m :: * -> *) a. (MonadIO m, AWSRequest a) => Env -> UTCTime -> Seconds -> a -> m ClientRequest presign Env env UTCTime time Seconds expires -- | Presign an HTTP request that is valid from the specified time until the -- number of seconds expiry has elapsed. presign :: ( MonadIO m, AWSRequest a ) => Env -> -- | Signing time. UTCTime -> -- | Expiry time. Seconds -> -- | Request to presign. a -> m ClientRequest presign :: Env -> UTCTime -> Seconds -> a -> m ClientRequest presign Env env UTCTime time Seconds expires a rq = (Service -> Service) -> Auth -> Region -> UTCTime -> Seconds -> a -> m ClientRequest forall (m :: * -> *) a. (MonadIO m, AWSRequest a) => (Service -> Service) -> Auth -> Region -> UTCTime -> Seconds -> a -> m ClientRequest Presign.presignWith (Endo Service -> Service -> Service forall a. Endo a -> a -> a appEndo (Dual (Endo Service) -> Endo Service forall a. Dual a -> a getDual (Env -> Dual (Endo Service) forall (withAuth :: * -> *). Env' withAuth -> Dual (Endo Service) Env._envOverride Env env))) (Identity Auth -> Auth forall a. Identity a -> a runIdentity (Identity Auth -> Auth) -> Identity Auth -> Auth forall a b. (a -> b) -> a -> b $ Env -> Identity Auth forall (withAuth :: * -> *). Env' withAuth -> withAuth Auth Env._envAuth Env env) (Env -> Region forall (withAuth :: * -> *). Env' withAuth -> Region Env._envRegion Env env) UTCTime time Seconds expires a rq -- | Retrieve the specified 'Dynamic' data. dynamic :: MonadIO m => Env -> EC2.Dynamic -> m ByteString dynamic :: Env -> Dynamic -> m ByteString dynamic Env env = Manager -> Dynamic -> m ByteString forall (m :: * -> *). MonadIO m => Manager -> Dynamic -> m ByteString EC2.dynamic (Env -> Manager forall (withAuth :: * -> *). Env' withAuth -> Manager Env._envManager Env env) -- | Retrieve the specified 'Metadata'. metadata :: MonadIO m => Env -> EC2.Metadata -> m ByteString metadata :: Env -> Metadata -> m ByteString metadata Env env = Manager -> Metadata -> m ByteString forall (m :: * -> *). MonadIO m => Manager -> Metadata -> m ByteString EC2.metadata (Env -> Manager forall (withAuth :: * -> *). Env' withAuth -> Manager Env._envManager Env env) -- | Retrieve the user data. Returns 'Nothing' if no user data is assigned -- to the instance. userdata :: MonadIO m => Env -> m (Maybe ByteString) userdata :: Env -> m (Maybe ByteString) userdata = Manager -> m (Maybe ByteString) forall (m :: * -> *). MonadIO m => Manager -> m (Maybe ByteString) EC2.userdata (Manager -> m (Maybe ByteString)) -> (Env -> Manager) -> Env -> m (Maybe ByteString) forall b c a. (b -> c) -> (a -> b) -> a -> c . Env -> Manager forall (withAuth :: * -> *). Env' withAuth -> Manager Env._envManager