packages feed

amazonka 0.3.6 → 1.0.0

raw patch · 18 files changed

+2118/−1104 lines, 18 filesdep +amazonkadep +directorydep +freedep −cryptohashdep −cryptohash-conduitdep ~amazonka-coredep ~basedep ~conduit-extraPVP ok

version bump matches the API change (PVP)

Dependencies added: amazonka, directory, free, http-client, ini, tasty, tasty-hunit

Dependencies removed: cryptohash, cryptohash-conduit

Dependency ranges changed: amazonka-core, base, conduit-extra, mmorph, monad-control, retry, transformers-base

API changes (from Hackage documentation)

- Control.Monad.Trans.AWS: accessKey :: Text
- Control.Monad.Trans.AWS: awaitCatch :: (MonadCatch m, MonadResource m, MonadReader Env m, AWSRequest a) => Wait a -> a -> m (Response a)
- Control.Monad.Trans.AWS: build :: ToBuilder a => a -> Builder
- Control.Monad.Trans.AWS: class ToBuilder a
- Control.Monad.Trans.AWS: debug :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()
- Control.Monad.Trans.AWS: fromKeys :: AccessKey -> SecretKey -> Auth
- Control.Monad.Trans.AWS: fromSession :: AccessKey -> SecretKey -> SecurityToken -> Auth
- Control.Monad.Trans.AWS: getAuth :: (Functor m, MonadIO m) => Manager -> Credentials -> ExceptT String m Auth
- Control.Monad.Trans.AWS: getEnv :: Region -> Credentials -> IO Env
- Control.Monad.Trans.AWS: hoistEither :: (MonadError Error m, AWSError e) => Either e a -> m a
- Control.Monad.Trans.AWS: info :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()
- Control.Monad.Trans.AWS: instance (Applicative m, MonadIO m, MonadBase IO m, MonadThrow m) => MonadResource (AWST m)
- Control.Monad.Trans.AWS: instance (Monad m, Functor m) => Alternative (AWST m)
- Control.Monad.Trans.AWS: instance (Monad m, Functor m) => Applicative (AWST m)
- Control.Monad.Trans.AWS: instance Functor m => Functor (AWST m)
- Control.Monad.Trans.AWS: instance MMonad AWST
- Control.Monad.Trans.AWS: instance Monad m => MonadError Error (AWST m)
- Control.Monad.Trans.AWS: instance Monad m => MonadPlus (AWST m)
- Control.Monad.Trans.AWS: instance MonadTransControl AWST
- Control.Monad.Trans.AWS: paginateCatch :: (MonadCatch m, MonadResource m, MonadReader Env m, AWSPager a) => a -> Source m (Response a)
- Control.Monad.Trans.AWS: secretKey :: Text
- Control.Monad.Trans.AWS: sendCatch :: (MonadCatch m, MonadResource m, MonadReader Env m, AWSRequest a) => a -> m (Response a)
- Control.Monad.Trans.AWS: send_ :: (MonadCatch m, MonadResource m, MonadReader Env m, MonadError Error m, AWSRequest a) => a -> m ()
- Control.Monad.Trans.AWS: throwAWSError :: (MonadError Error m, AWSError e) => e -> m a
- Control.Monad.Trans.AWS: trace :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()
- Control.Monad.Trans.AWS: type AWS = AWST IO
- Control.Monad.Trans.AWS: type Error = ServiceError String
- Control.Monad.Trans.AWS: type MonadAWS m = (MonadBaseControl IO m, MonadCatch m, MonadResource m, MonadError Error m, MonadReader Env m)
- Control.Monad.Trans.AWS: verify :: (AWSError e, MonadError Error m) => Prism' e a -> e -> m ()
- Control.Monad.Trans.AWS: verifyWith :: (AWSError e, MonadError Error m) => Prism' e a -> (a -> Bool) -> e -> m ()
- Network.AWS: accessKey :: Text
- Network.AWS: fromKeys :: AccessKey -> SecretKey -> Auth
- Network.AWS: fromSession :: AccessKey -> SecretKey -> SecurityToken -> Auth
- Network.AWS: getAuth :: (Functor m, MonadIO m) => Manager -> Credentials -> ExceptT String m Auth
- Network.AWS: getEnv :: Region -> Credentials -> IO Env
- Network.AWS: presign :: (MonadIO m, AWSRequest a, AWSPresigner (Sg (Sv a))) => Env -> a -> UTCTime -> Integer -> m Request
- Network.AWS: secretKey :: Text
- Network.AWS.EC2.Metadata: Info :: Info
- Network.AWS.EC2.Metadata: instance ToPath Dynamic
- Network.AWS.EC2.Metadata: instance ToPath Info
- Network.AWS.EC2.Metadata: instance ToPath Interface
- Network.AWS.EC2.Metadata: instance ToPath Mapping
- Network.AWS.EC2.Metadata: instance ToPath Metadata
+ Control.Monad.Trans.AWS: AMIId :: Metadata
+ Control.Monad.Trans.AWS: AMILaunchIndex :: Metadata
+ Control.Monad.Trans.AWS: AMIManifestPath :: Metadata
+ Control.Monad.Trans.AWS: AncestorAMIIds :: Metadata
+ Control.Monad.Trans.AWS: AvailabilityZone :: Metadata
+ Control.Monad.Trans.AWS: BlockDevice :: !Mapping -> Metadata
+ Control.Monad.Trans.AWS: Debug :: LogLevel
+ Control.Monad.Trans.AWS: Document :: Dynamic
+ Control.Monad.Trans.AWS: Error :: LogLevel
+ Control.Monad.Trans.AWS: FWS :: Dynamic
+ Control.Monad.Trans.AWS: FromFile :: Text -> FilePath -> Credentials
+ Control.Monad.Trans.AWS: Hostname :: Metadata
+ Control.Monad.Trans.AWS: IAM :: !Info -> Metadata
+ Control.Monad.Trans.AWS: Info :: LogLevel
+ Control.Monad.Trans.AWS: InstanceAction :: Metadata
+ Control.Monad.Trans.AWS: InstanceId :: Metadata
+ Control.Monad.Trans.AWS: InstanceType :: Metadata
+ Control.Monad.Trans.AWS: KernelId :: Metadata
+ Control.Monad.Trans.AWS: LocalHostname :: Metadata
+ Control.Monad.Trans.AWS: LocalIPV4 :: Metadata
+ Control.Monad.Trans.AWS: MAC :: Metadata
+ Control.Monad.Trans.AWS: Network :: !Text -> !Interface -> Metadata
+ Control.Monad.Trans.AWS: OpenSSHKey :: Metadata
+ Control.Monad.Trans.AWS: PKCS7 :: Dynamic
+ Control.Monad.Trans.AWS: ProductCodes :: Metadata
+ Control.Monad.Trans.AWS: PublicHostname :: Metadata
+ Control.Monad.Trans.AWS: PublicIPV4 :: Metadata
+ Control.Monad.Trans.AWS: RAMDiskId :: Metadata
+ Control.Monad.Trans.AWS: ReservationId :: Metadata
+ Control.Monad.Trans.AWS: SecurityGroups :: Metadata
+ Control.Monad.Trans.AWS: Signature :: Dynamic
+ Control.Monad.Trans.AWS: Trace :: LogLevel
+ Control.Monad.Trans.AWS: _AuthError :: AsAuthError a => Prism' a AuthError
+ Control.Monad.Trans.AWS: _Error :: AsError a => Prism' a Error
+ Control.Monad.Trans.AWS: _InvalidFileError :: AsAuthError a => Prism' a Text
+ Control.Monad.Trans.AWS: _InvalidIAMError :: AsAuthError a => Prism' a Text
+ Control.Monad.Trans.AWS: _MissingEnvError :: AsAuthError a => Prism' a Text
+ Control.Monad.Trans.AWS: _MissingFileError :: AsAuthError a => Prism' a FilePath
+ Control.Monad.Trans.AWS: _RetrievalError :: AsAuthError a => Prism' a HttpException
+ Control.Monad.Trans.AWS: _SerializeError :: AsError a => Prism' a SerializeError
+ Control.Monad.Trans.AWS: _ServiceError :: AsError a => Prism' a ServiceError
+ Control.Monad.Trans.AWS: _TransportError :: AsError a => Prism' a HttpException
+ Control.Monad.Trans.AWS: awaitWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> Wait a -> a -> m (Rs a)
+ Control.Monad.Trans.AWS: catching :: MonadCatch m => Getting (First a) SomeException a -> m r -> (a -> m r) -> m r
+ Control.Monad.Trans.AWS: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
+ Control.Monad.Trans.AWS: class AsError a
+ Control.Monad.Trans.AWS: class HasEnv a where envRegion = environment . lens _envRegion (\ s a -> s {_envRegion = a}) envLogger = environment . lens _envLogger (\ s a -> s {_envLogger = a}) envRetryCheck = environment . lens _envRetryCheck (\ s a -> s {_envRetryCheck = a}) envRetryPolicy = environment . lens _envRetryPolicy (\ s a -> s {_envRetryPolicy = a}) envTimeout = environment . lens _envTimeout (\ s a -> s {_envTimeout = a}) envManager = environment . lens _envManager (\ s a -> s {_envManager = a}) envAuth = environment . lens _envAuth (\ s a -> s {_envAuth = a}) envEC2 = environment . to _envEC2
+ Control.Monad.Trans.AWS: class ToBody a
+ Control.Monad.Trans.AWS: data Dynamic
+ Control.Monad.Trans.AWS: data LogLevel :: *
+ Control.Monad.Trans.AWS: data Metadata
+ Control.Monad.Trans.AWS: data RqBody :: *
+ Control.Monad.Trans.AWS: data RsBody :: *
+ Control.Monad.Trans.AWS: dynamic :: MonadFree Command m => Dynamic -> m ByteString
+ Control.Monad.Trans.AWS: envEC2 :: HasEnv a => Getter a (IORef (Maybe Bool))
+ Control.Monad.Trans.AWS: envTimeout :: HasEnv a => Lens' a (Maybe Seconds)
+ Control.Monad.Trans.AWS: environment :: HasEnv a => Lens' a Env
+ Control.Monad.Trans.AWS: execAWST :: (MonadCatch m, MonadResource m, HasEnv r) => (forall a. Either Error a -> m a) -> r -> AWST m b -> m b
+ Control.Monad.Trans.AWS: getFileSize :: MonadIO m => FilePath -> m Int64
+ Control.Monad.Trans.AWS: instance Monad m => Applicative (AWST m)
+ Control.Monad.Trans.AWS: instance Monad m => Functor (AWST m)
+ Control.Monad.Trans.AWS: instance Monad m => MonadFree Command (AWST m)
+ Control.Monad.Trans.AWS: instance MonadError e m => MonadError e (AWST m)
+ Control.Monad.Trans.AWS: instance MonadPlus m => Alternative (AWST m)
+ Control.Monad.Trans.AWS: instance MonadPlus m => MonadPlus (AWST m)
+ Control.Monad.Trans.AWS: instance MonadResource m => MonadResource (AWST m)
+ Control.Monad.Trans.AWS: instance MonadState s m => MonadState s (AWST m)
+ Control.Monad.Trans.AWS: instance MonadWriter w m => MonadWriter w (AWST m)
+ Control.Monad.Trans.AWS: isEC2 :: MonadFree Command m => m Bool
+ Control.Monad.Trans.AWS: metadata :: MonadFree Command m => Metadata -> m ByteString
+ Control.Monad.Trans.AWS: paginateWith :: (MonadFree Command m, AWSSigner (Sg s), AWSPager a) => (Service (Sv a) -> Service s) -> a -> Source m (Rs a)
+ Control.Monad.Trans.AWS: presignWith :: (MonadFree Command m, AWSPresigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> UTCTime -> Seconds -> a -> m ClientRequest
+ Control.Monad.Trans.AWS: runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a
+ Control.Monad.Trans.AWS: sendWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> a -> m (Rs a)
+ Control.Monad.Trans.AWS: sinkBody :: MonadResource m => RsBody -> Sink ByteString m a -> m a
+ Control.Monad.Trans.AWS: sinkMD5 :: Monad m => Consumer ByteString m (Digest MD5)
+ Control.Monad.Trans.AWS: sinkSHA256 :: Monad m => Consumer ByteString m (Digest SHA256)
+ Control.Monad.Trans.AWS: timeout :: (MonadReader r m, HasEnv r) => Seconds -> m a -> m a
+ Control.Monad.Trans.AWS: toBody :: ToBody a => a -> RqBody
+ Control.Monad.Trans.AWS: trying :: MonadCatch m => Getting (First a) SomeException a -> m r -> m (Either a r)
+ Control.Monad.Trans.AWS: type Logger = LogLevel -> Builder -> IO ()
+ Control.Monad.Trans.AWS: userdata :: MonadFree Command m => m (Maybe ByteString)
+ Network.AWS: AMIId :: Metadata
+ Network.AWS: AMILaunchIndex :: Metadata
+ Network.AWS: AMIManifestPath :: Metadata
+ Network.AWS: AncestorAMIIds :: Metadata
+ Network.AWS: AvailabilityZone :: Metadata
+ Network.AWS: Beijing :: Region
+ Network.AWS: BlockDevice :: !Mapping -> Metadata
+ Network.AWS: Debug :: LogLevel
+ Network.AWS: Document :: Dynamic
+ Network.AWS: Error :: LogLevel
+ Network.AWS: FWS :: Dynamic
+ Network.AWS: Frankfurt :: Region
+ Network.AWS: FromFile :: Text -> FilePath -> Credentials
+ Network.AWS: GovCloud :: Region
+ Network.AWS: GovCloudFIPS :: Region
+ Network.AWS: Hostname :: Metadata
+ Network.AWS: IAM :: !Info -> Metadata
+ Network.AWS: Info :: LogLevel
+ Network.AWS: InstanceAction :: Metadata
+ Network.AWS: InstanceId :: Metadata
+ Network.AWS: InstanceType :: Metadata
+ Network.AWS: Ireland :: Region
+ Network.AWS: KernelId :: Metadata
+ Network.AWS: LocalHostname :: Metadata
+ Network.AWS: LocalIPV4 :: Metadata
+ Network.AWS: MAC :: Metadata
+ Network.AWS: Network :: !Text -> !Interface -> Metadata
+ Network.AWS: NorthCalifornia :: Region
+ Network.AWS: NorthVirginia :: Region
+ Network.AWS: OpenSSHKey :: Metadata
+ Network.AWS: Oregon :: Region
+ Network.AWS: PKCS7 :: Dynamic
+ Network.AWS: ProductCodes :: Metadata
+ Network.AWS: PublicHostname :: Metadata
+ Network.AWS: PublicIPV4 :: Metadata
+ Network.AWS: RAMDiskId :: Metadata
+ Network.AWS: ReservationId :: Metadata
+ Network.AWS: SaoPaulo :: Region
+ Network.AWS: SecurityGroups :: Metadata
+ Network.AWS: Signature :: Dynamic
+ Network.AWS: Singapore :: Region
+ Network.AWS: Sydney :: Region
+ Network.AWS: Tokyo :: Region
+ Network.AWS: Trace :: LogLevel
+ Network.AWS: _AuthError :: AsAuthError a => Prism' a AuthError
+ Network.AWS: _Error :: AsError a => Prism' a Error
+ Network.AWS: _InvalidFileError :: AsAuthError a => Prism' a Text
+ Network.AWS: _InvalidIAMError :: AsAuthError a => Prism' a Text
+ Network.AWS: _MissingEnvError :: AsAuthError a => Prism' a Text
+ Network.AWS: _MissingFileError :: AsAuthError a => Prism' a FilePath
+ Network.AWS: _RetrievalError :: AsAuthError a => Prism' a HttpException
+ Network.AWS: _SerializeError :: AsError a => Prism' a SerializeError
+ Network.AWS: _ServiceError :: AsError a => Prism' a ServiceError
+ Network.AWS: _TransportError :: AsError a => Prism' a HttpException
+ Network.AWS: catching :: MonadCatch m => Getting (First a) SomeException a -> m r -> (a -> m r) -> m r
+ Network.AWS: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
+ Network.AWS: class AsError a
+ Network.AWS: class HasEnv a where envRegion = environment . lens _envRegion (\ s a -> s {_envRegion = a}) envLogger = environment . lens _envLogger (\ s a -> s {_envLogger = a}) envRetryCheck = environment . lens _envRetryCheck (\ s a -> s {_envRetryCheck = a}) envRetryPolicy = environment . lens _envRetryPolicy (\ s a -> s {_envRetryPolicy = a}) envTimeout = environment . lens _envTimeout (\ s a -> s {_envTimeout = a}) envManager = environment . lens _envManager (\ s a -> s {_envManager = a}) envAuth = environment . lens _envAuth (\ s a -> s {_envAuth = a}) envEC2 = environment . to _envEC2
+ Network.AWS: class (Functor m, Applicative m, Monad m) => MonadAWS m
+ Network.AWS: class ToBody a
+ Network.AWS: data Dynamic
+ Network.AWS: data LogLevel :: *
+ Network.AWS: data Metadata
+ Network.AWS: data Region :: *
+ Network.AWS: data RqBody :: *
+ Network.AWS: data RsBody :: *
+ Network.AWS: dynamic :: MonadAWS m => Dynamic -> m ByteString
+ Network.AWS: envEC2 :: HasEnv a => Getter a (IORef (Maybe Bool))
+ Network.AWS: envTimeout :: HasEnv a => Lens' a (Maybe Seconds)
+ Network.AWS: environment :: HasEnv a => Lens' a Env
+ Network.AWS: getFileSize :: MonadIO m => FilePath -> m Int64
+ Network.AWS: instance (Functor f, MonadAWS m) => MonadAWS (FreeT f m)
+ Network.AWS: instance (Monoid w, MonadAWS m) => MonadAWS (RWST r w s m)
+ Network.AWS: instance (Monoid w, MonadAWS m) => MonadAWS (WriterT w m)
+ Network.AWS: instance MonadAWS AWS
+ Network.AWS: instance MonadAWS m => MonadAWS (ContT r m)
+ Network.AWS: instance MonadAWS m => MonadAWS (ExceptT e m)
+ Network.AWS: instance MonadAWS m => MonadAWS (FT f m)
+ Network.AWS: instance MonadAWS m => MonadAWS (IdentityT m)
+ Network.AWS: instance MonadAWS m => MonadAWS (IterT m)
+ Network.AWS: instance MonadAWS m => MonadAWS (ListT m)
+ Network.AWS: instance MonadAWS m => MonadAWS (MaybeT m)
+ Network.AWS: instance MonadAWS m => MonadAWS (ReaderT r m)
+ Network.AWS: instance MonadAWS m => MonadAWS (StateT s m)
+ Network.AWS: isEC2 :: MonadAWS m => m Bool
+ Network.AWS: liftAWS :: MonadAWS m => AWS a -> m a
+ Network.AWS: metadata :: MonadAWS m => Metadata -> m ByteString
+ Network.AWS: once :: MonadAWS m => AWS a -> m a
+ Network.AWS: runAWS :: (MonadCatch m, MonadResource m, HasEnv r) => r -> AWS a -> m a
+ Network.AWS: runResourceT :: MonadBaseControl IO m => ResourceT m a -> m a
+ Network.AWS: sinkBody :: MonadResource m => RsBody -> Sink ByteString m a -> m a
+ Network.AWS: sinkMD5 :: Monad m => Consumer ByteString m (Digest MD5)
+ Network.AWS: sinkSHA256 :: Monad m => Consumer ByteString m (Digest SHA256)
+ Network.AWS: timeout :: MonadAWS m => Seconds -> AWS a -> m a
+ Network.AWS: toBody :: ToBody a => a -> RqBody
+ Network.AWS: trying :: MonadCatch m => Getting (First a) SomeException a -> m r -> m (Either a r)
+ Network.AWS: type AWS = AWST IO
+ Network.AWS: type Logger = LogLevel -> Builder -> IO ()
+ Network.AWS: userdata :: MonadAWS m => m (Maybe ByteString)
+ Network.AWS: within :: MonadAWS m => Region -> AWS a -> m a
+ Network.AWS.Auth: AccessKey :: ByteString -> AccessKey
+ Network.AWS.Auth: Discover :: Credentials
+ Network.AWS.Auth: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
+ Network.AWS.Auth: FromFile :: Text -> FilePath -> Credentials
+ Network.AWS.Auth: FromKeys :: AccessKey -> SecretKey -> Credentials
+ Network.AWS.Auth: FromProfile :: Text -> Credentials
+ Network.AWS.Auth: FromSession :: AccessKey -> SecretKey -> SessionToken -> Credentials
+ Network.AWS.Auth: InvalidFileError :: Text -> AuthError
+ Network.AWS.Auth: InvalidIAMError :: Text -> AuthError
+ Network.AWS.Auth: MissingEnvError :: Text -> AuthError
+ Network.AWS.Auth: MissingFileError :: FilePath -> AuthError
+ Network.AWS.Auth: RetrievalError :: HttpException -> AuthError
+ Network.AWS.Auth: SecretKey :: ByteString -> SecretKey
+ Network.AWS.Auth: SessionToken :: ByteString -> SessionToken
+ Network.AWS.Auth: _AuthError :: AsAuthError a => Prism' a AuthError
+ Network.AWS.Auth: _InvalidFileError :: AsAuthError a => Prism' a Text
+ Network.AWS.Auth: _InvalidIAMError :: AsAuthError a => Prism' a Text
+ Network.AWS.Auth: _MissingEnvError :: AsAuthError a => Prism' a Text
+ Network.AWS.Auth: _MissingFileError :: AsAuthError a => Prism' a FilePath
+ Network.AWS.Auth: _RetrievalError :: AsAuthError a => Prism' a HttpException
+ Network.AWS.Auth: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
+ Network.AWS.Auth: credAccessKey :: Text
+ Network.AWS.Auth: credFile :: MonadIO m => m FilePath
+ Network.AWS.Auth: credProfile :: Text
+ Network.AWS.Auth: credSecretKey :: Text
+ Network.AWS.Auth: credSessionToken :: Text
+ Network.AWS.Auth: data Auth :: *
+ Network.AWS.Auth: data AuthError
+ Network.AWS.Auth: data Credentials
+ Network.AWS.Auth: envAccessKey :: Text
+ Network.AWS.Auth: envSecretKey :: Text
+ Network.AWS.Auth: envSessionToken :: Text
+ Network.AWS.Auth: fromEnv :: (Applicative m, MonadIO m, MonadThrow m) => m Auth
+ Network.AWS.Auth: fromEnvKeys :: (Applicative m, MonadIO m, MonadThrow m) => Text -> Text -> Maybe Text -> m Auth
+ Network.AWS.Auth: fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m Auth
+ Network.AWS.Auth: fromFilePath :: (Applicative m, MonadIO m, MonadCatch m) => Text -> FilePath -> m Auth
+ Network.AWS.Auth: fromKeys :: AccessKey -> SecretKey -> Auth
+ Network.AWS.Auth: fromProfile :: (MonadIO m, MonadCatch m) => Manager -> m Auth
+ Network.AWS.Auth: fromProfileName :: (MonadIO m, MonadCatch m) => Manager -> Text -> m Auth
+ Network.AWS.Auth: fromSession :: AccessKey -> SecretKey -> SessionToken -> Auth
+ Network.AWS.Auth: getAuth :: (Applicative m, MonadIO m, MonadCatch m) => Manager -> Credentials -> m Auth
+ Network.AWS.Auth: instance AsAuthError AuthError
+ Network.AWS.Auth: instance AsAuthError SomeException
+ Network.AWS.Auth: instance Eq Credentials
+ Network.AWS.Auth: instance Exception AuthError
+ Network.AWS.Auth: instance Show AuthError
+ Network.AWS.Auth: instance Show Credentials
+ Network.AWS.Auth: instance ToLog AuthError
+ Network.AWS.Auth: instance ToLog Credentials
+ Network.AWS.Auth: instance Typeable AuthError
+ Network.AWS.Auth: newtype AccessKey :: *
+ Network.AWS.Auth: newtype SecretKey :: *
+ Network.AWS.Auth: newtype SessionToken :: *
+ Network.AWS.Data: build :: ToLog a => a -> Builder
+ Network.AWS.Data: class FromText a
+ Network.AWS.Data: class ToByteString a
+ Network.AWS.Data: class ToLog a
+ Network.AWS.Data: class ToText a
+ Network.AWS.Data: fromText :: FromText a => Text -> Either String a
+ Network.AWS.Data: parser :: FromText a => Parser a
+ Network.AWS.Data: toBS :: ToByteString a => a -> ByteString
+ Network.AWS.Data: toText :: ToText a => a -> Text
+ Network.AWS.EC2.Metadata: Info' :: Info
+ Network.AWS.EC2.Metadata: instance Eq Dynamic
+ Network.AWS.EC2.Metadata: instance Ord Dynamic
+ Network.AWS.EC2.Metadata: instance Show Dynamic
+ Network.AWS.EC2.Metadata: instance ToText Dynamic
+ Network.AWS.EC2.Metadata: instance ToText Info
+ Network.AWS.EC2.Metadata: instance ToText Interface
+ Network.AWS.EC2.Metadata: instance ToText Mapping
+ Network.AWS.EC2.Metadata: instance ToText Metadata
+ Network.AWS.EC2.Metadata: instance Typeable Dynamic
+ Network.AWS.EC2.Metadata: instance Typeable Info
+ Network.AWS.EC2.Metadata: instance Typeable Interface
+ Network.AWS.EC2.Metadata: instance Typeable Mapping
+ Network.AWS.EC2.Metadata: instance Typeable Metadata
+ Network.AWS.Env: Env :: !Region -> !Logger -> !(Int -> HttpException -> IO Bool) -> !(Maybe RetryPolicy) -> !(Maybe Seconds) -> !Manager -> !(IORef (Maybe Bool)) -> !Auth -> Env
+ Network.AWS.Env: _envAuth :: Env -> !Auth
+ Network.AWS.Env: _envEC2 :: Env -> !(IORef (Maybe Bool))
+ Network.AWS.Env: _envLogger :: Env -> !Logger
+ Network.AWS.Env: _envManager :: Env -> !Manager
+ Network.AWS.Env: _envRegion :: Env -> !Region
+ Network.AWS.Env: _envRetryCheck :: Env -> !(Int -> HttpException -> IO Bool)
+ Network.AWS.Env: _envRetryPolicy :: Env -> !(Maybe RetryPolicy)
+ Network.AWS.Env: _envTimeout :: Env -> !(Maybe Seconds)
+ Network.AWS.Env: class HasEnv a where envRegion = environment . lens _envRegion (\ s a -> s {_envRegion = a}) envLogger = environment . lens _envLogger (\ s a -> s {_envLogger = a}) envRetryCheck = environment . lens _envRetryCheck (\ s a -> s {_envRetryCheck = a}) envRetryPolicy = environment . lens _envRetryPolicy (\ s a -> s {_envRetryPolicy = a}) envTimeout = environment . lens _envTimeout (\ s a -> s {_envTimeout = a}) envManager = environment . lens _envManager (\ s a -> s {_envManager = a}) envAuth = environment . lens _envAuth (\ s a -> s {_envAuth = a}) envEC2 = environment . to _envEC2
+ Network.AWS.Env: data Env
+ Network.AWS.Env: envAuth :: HasEnv a => Lens' a Auth
+ Network.AWS.Env: envEC2 :: HasEnv a => Getter a (IORef (Maybe Bool))
+ Network.AWS.Env: envLogger :: HasEnv a => Lens' a Logger
+ Network.AWS.Env: envManager :: HasEnv a => Lens' a Manager
+ Network.AWS.Env: envRegion :: HasEnv a => Lens' a Region
+ Network.AWS.Env: envRetryCheck :: HasEnv a => Lens' a (Int -> HttpException -> IO Bool)
+ Network.AWS.Env: envRetryPolicy :: HasEnv a => Lens' a (Maybe RetryPolicy)
+ Network.AWS.Env: envTimeout :: HasEnv a => Lens' a (Maybe Seconds)
+ Network.AWS.Env: environment :: HasEnv a => Lens' a Env
+ Network.AWS.Env: instance HasEnv Env
+ Network.AWS.Env: instance ToLog Env
+ Network.AWS.Env: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
+ Network.AWS.Env: newEnvWith :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> Maybe Bool -> Manager -> m Env
+ Network.AWS.Env: once :: (MonadReader r m, HasEnv r) => m a -> m a
+ Network.AWS.Env: timeout :: (MonadReader r m, HasEnv r) => Seconds -> m a -> m a
+ Network.AWS.Env: within :: (MonadReader r m, HasEnv r) => Region -> m a -> m a
+ Network.AWS.Free: AwaitF :: Service s -> Wait a -> a -> (Rs a -> r) -> Command r
+ Network.AWS.Free: CheckF :: (Bool -> r) -> Command r
+ Network.AWS.Free: DynF :: Dynamic -> (ByteString -> r) -> Command r
+ Network.AWS.Free: MetaF :: Metadata -> (ByteString -> r) -> Command r
+ Network.AWS.Free: SendF :: Service s -> a -> (Rs a -> r) -> Command r
+ Network.AWS.Free: SignF :: Service s -> UTCTime -> Seconds -> a -> (ClientRequest -> r) -> Command r
+ Network.AWS.Free: UserF :: (Maybe ByteString -> r) -> Command r
+ Network.AWS.Free: await :: (MonadFree Command m, AWSRequest a) => Wait a -> a -> m (Rs a)
+ Network.AWS.Free: awaitWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> Wait a -> a -> m (Rs a)
+ Network.AWS.Free: data Command r
+ Network.AWS.Free: dynamic :: MonadFree Command m => Dynamic -> m ByteString
+ Network.AWS.Free: instance Functor Command
+ Network.AWS.Free: isEC2 :: MonadFree Command m => m Bool
+ Network.AWS.Free: metadata :: MonadFree Command m => Metadata -> m ByteString
+ Network.AWS.Free: paginate :: (MonadFree Command m, AWSPager a) => a -> Source m (Rs a)
+ Network.AWS.Free: paginateWith :: (MonadFree Command m, AWSSigner (Sg s), AWSPager a) => (Service (Sv a) -> Service s) -> a -> Source m (Rs a)
+ Network.AWS.Free: presign :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a) => UTCTime -> Seconds -> a -> m ClientRequest
+ Network.AWS.Free: presignURL :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a) => UTCTime -> Seconds -> a -> m ByteString
+ Network.AWS.Free: presignWith :: (MonadFree Command m, AWSPresigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> UTCTime -> Seconds -> a -> m ClientRequest
+ Network.AWS.Free: send :: (MonadFree Command m, AWSRequest a) => a -> m (Rs a)
+ Network.AWS.Free: sendWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> a -> m (Rs a)
+ Network.AWS.Free: userdata :: MonadFree Command m => m (Maybe ByteString)
+ Network.AWS.Presign: presign :: (MonadIO m, AWSPresigner (Sg (Sv a)), AWSRequest a) => Auth -> Region -> UTCTime -> Seconds -> a -> m ClientRequest
+ Network.AWS.Presign: presignURL :: (MonadIO m, AWSPresigner (Sg (Sv a)), AWSRequest a) => Auth -> Region -> UTCTime -> Seconds -> a -> m ByteString
+ Network.AWS.Presign: presignWith :: (MonadIO m, AWSPresigner (Sg s), AWSRequest a) => (Service (Sv a) -> Service s) -> Auth -> Region -> UTCTime -> Seconds -> a -> m ClientRequest
- Control.Monad.Trans.AWS: FromEnv :: Text -> Text -> Credentials
+ Control.Monad.Trans.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
- Control.Monad.Trans.AWS: FromSession :: AccessKey -> SecretKey -> SecurityToken -> Credentials
+ Control.Monad.Trans.AWS: FromSession :: AccessKey -> SecretKey -> SessionToken -> Credentials
- Control.Monad.Trans.AWS: await :: (MonadCatch m, MonadResource m, MonadReader Env m, MonadError Error m, AWSRequest a) => Wait a -> a -> m (Rs a)
+ Control.Monad.Trans.AWS: await :: (MonadFree Command m, AWSRequest a) => Wait a -> a -> m (Rs a)
- Control.Monad.Trans.AWS: envAuth :: Lens' Env Auth
+ Control.Monad.Trans.AWS: envAuth :: HasEnv a => Lens' a Auth
- Control.Monad.Trans.AWS: envLogger :: Lens' Env Logger
+ Control.Monad.Trans.AWS: envLogger :: HasEnv a => Lens' a Logger
- Control.Monad.Trans.AWS: envManager :: Lens' Env Manager
+ Control.Monad.Trans.AWS: envManager :: HasEnv a => Lens' a Manager
- Control.Monad.Trans.AWS: envRegion :: Lens' Env Region
+ Control.Monad.Trans.AWS: envRegion :: HasEnv a => Lens' a Region
- Control.Monad.Trans.AWS: envRetryCheck :: Lens' Env (Int -> HttpException -> IO Bool)
+ Control.Monad.Trans.AWS: envRetryCheck :: HasEnv a => Lens' a (Int -> HttpException -> IO Bool)
- Control.Monad.Trans.AWS: envRetryPolicy :: Lens' Env (Maybe RetryPolicy)
+ Control.Monad.Trans.AWS: envRetryPolicy :: HasEnv a => Lens' a (Maybe RetryPolicy)
- Control.Monad.Trans.AWS: newEnv :: (Functor m, MonadIO m) => Region -> Credentials -> Manager -> ExceptT String m Env
+ Control.Monad.Trans.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
- Control.Monad.Trans.AWS: once :: MonadReader Env m => m a -> m a
+ Control.Monad.Trans.AWS: once :: (MonadReader r m, HasEnv r) => m a -> m a
- Control.Monad.Trans.AWS: paginate :: (MonadCatch m, MonadResource m, MonadReader Env m, MonadError Error m, AWSPager a) => a -> Source m (Rs a)
+ Control.Monad.Trans.AWS: paginate :: (MonadFree Command m, AWSPager a) => a -> Source m (Rs a)
- Control.Monad.Trans.AWS: presign :: (MonadIO m, MonadReader Env m, AWSRequest a, AWSPresigner (Sg (Sv a))) => a -> UTCTime -> Integer -> m Request
+ Control.Monad.Trans.AWS: presign :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a) => UTCTime -> Seconds -> a -> m ClientRequest
- Control.Monad.Trans.AWS: presignURL :: (MonadIO m, MonadReader Env m, AWSRequest a, AWSPresigner (Sg (Sv a))) => a -> UTCTime -> Integer -> m ByteString
+ Control.Monad.Trans.AWS: presignURL :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a) => UTCTime -> Seconds -> a -> m ByteString
- Control.Monad.Trans.AWS: runAWST :: MonadBaseControl IO m => Env -> AWST m a -> m (Either Error a)
+ Control.Monad.Trans.AWS: runAWST :: (MonadCatch m, MonadResource m, HasEnv r) => r -> AWST m a -> m a
- Control.Monad.Trans.AWS: send :: (MonadCatch m, MonadResource m, MonadReader Env m, MonadError Error m, AWSRequest a) => a -> m (Rs a)
+ Control.Monad.Trans.AWS: send :: (MonadFree Command m, AWSRequest a) => a -> m (Rs a)
- Control.Monad.Trans.AWS: within :: MonadReader Env m => Region -> m a -> m a
+ Control.Monad.Trans.AWS: within :: (MonadReader r m, HasEnv r) => Region -> m a -> m a
- Network.AWS: FromEnv :: Text -> Text -> Credentials
+ Network.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
- Network.AWS: FromSession :: AccessKey -> SecretKey -> SecurityToken -> Credentials
+ Network.AWS: FromSession :: AccessKey -> SecretKey -> SessionToken -> Credentials
- Network.AWS: await :: (MonadCatch m, MonadResource m, AWSRequest a) => Env -> Wait a -> a -> m (Response a)
+ Network.AWS: await :: (MonadAWS m, AWSRequest a) => Wait a -> a -> m (Rs a)
- Network.AWS: envAuth :: Lens' Env Auth
+ Network.AWS: envAuth :: HasEnv a => Lens' a Auth
- Network.AWS: envLogger :: Lens' Env Logger
+ Network.AWS: envLogger :: HasEnv a => Lens' a Logger
- Network.AWS: envManager :: Lens' Env Manager
+ Network.AWS: envManager :: HasEnv a => Lens' a Manager
- Network.AWS: envRegion :: Lens' Env Region
+ Network.AWS: envRegion :: HasEnv a => Lens' a Region
- Network.AWS: envRetryCheck :: Lens' Env (Int -> HttpException -> IO Bool)
+ Network.AWS: envRetryCheck :: HasEnv a => Lens' a (Int -> HttpException -> IO Bool)
- Network.AWS: envRetryPolicy :: Lens' Env (Maybe RetryPolicy)
+ Network.AWS: envRetryPolicy :: HasEnv a => Lens' a (Maybe RetryPolicy)
- Network.AWS: newEnv :: (Functor m, MonadIO m) => Region -> Credentials -> Manager -> ExceptT String m Env
+ Network.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
- Network.AWS: paginate :: (MonadCatch m, MonadResource m, AWSPager a) => Env -> a -> Source m (Response a)
+ Network.AWS: paginate :: (MonadAWS m, AWSPager a) => a -> Source m (Rs a)
- Network.AWS: presignURL :: (MonadIO m, AWSRequest a, AWSPresigner (Sg (Sv a))) => Env -> a -> UTCTime -> Integer -> m ByteString
+ Network.AWS: presignURL :: (MonadAWS m, AWSPresigner (Sg (Sv a)), AWSRequest a) => UTCTime -> Seconds -> a -> m ByteString
- Network.AWS: send :: (MonadCatch m, MonadResource m, AWSRequest a) => Env -> a -> m (Response a)
+ Network.AWS: send :: (MonadAWS m, AWSRequest a) => a -> m (Rs a)
- Network.AWS.EC2.Metadata: dynamic :: MonadIO m => Manager -> Dynamic -> ExceptT HttpException m ByteString
+ Network.AWS.EC2.Metadata: dynamic :: (MonadIO m, MonadThrow m) => Manager -> Dynamic -> m ByteString
- Network.AWS.EC2.Metadata: isEC2 :: Manager -> IO Bool
+ Network.AWS.EC2.Metadata: isEC2 :: MonadIO m => Manager -> m Bool
- Network.AWS.EC2.Metadata: metadata :: MonadIO m => Manager -> Metadata -> ExceptT HttpException m ByteString
+ Network.AWS.EC2.Metadata: metadata :: (MonadIO m, MonadThrow m) => Manager -> Metadata -> m ByteString
- Network.AWS.EC2.Metadata: userdata :: MonadIO m => Manager -> ExceptT HttpException m (Maybe ByteString)
+ Network.AWS.EC2.Metadata: userdata :: (MonadIO m, MonadCatch m) => Manager -> m (Maybe ByteString)

Files

README.md view
@@ -6,8 +6,9 @@  ## Description -Client library containing request and response primitives to be used alongside-the types from all `amazonka-*` libraries.+Client library containing request and response logic to communicate+with Amazon Web Service compatible APIs. Intended to be used alongside+the types supplied by the various `amazonka-*` service libraries.   ## Contribute
amazonka.cabal view
@@ -1,5 +1,5 @@ name:                  amazonka-version:               0.3.6+version:               1.0.0 synopsis:              Comprehensive Amazon Web Services SDK homepage:              https://github.com/brendanhay/amazonka license:               OtherLicense@@ -13,11 +13,28 @@ cabal-version:         >= 1.10  description:-    Client library containing request and response primitives to be used-    alongside the types from all amazonka related service libraries.+    This client library contains request and response logic to communicate+    with Amazon Web Service compatible APIs using the types supplied by the+    various @amazonka-*@ service libraries. See the <http://hackage.haskell.org/packages/#cat:AWS AWS>+    category on Hackage for supported services.     .-    /Warning:/ This is an experimental preview release which is still under-    heavy development and not intended for public consumption, caveat emptor!+    To get started, import the desired @amazonka-*@ library (such as+    <http://hackage.haskell.org/package/amazonka-ml/docs/Network-AWS-MachineLearning.html Network.AWS.MachineLearning>)+    and one of the following:+    .+    * "Control.Monad.Trans.AWS": The 'Control.Monad.Trans.AWS.AWST' transformer+    and generalised operations.+    .+    * "Network.AWS": The 'Network.AWS.AWS' monad and 'Network.AWS.MonadAWS' type+    class for automatically lifting operations when embedded as a layer in a transformer stack.+    This is built upon "Control.Monad.Trans.AWS".+    .+    Both 'Control.Monad.Trans.AWS.AWST' (and 'Network.AWS.AWS') and the provided+    functions are built upon the 'Control.Monad.Trans.Free' DSL defined in+    "Network.AWS.Free". This allows writing a custom interpreter (say, for+    mocking purposes) and defining your own AWS logic if desired.+    .+    GHC 7.8.4 and higher is officially supported.  source-repository head     type:     git@@ -32,33 +49,54 @@     exposed-modules:           Control.Monad.Trans.AWS         , Network.AWS+        , Network.AWS.Auth+        , Network.AWS.Data         , Network.AWS.EC2.Metadata+        , Network.AWS.Env+        , Network.AWS.Free+        , Network.AWS.Presign      other-modules:-          Network.AWS.Internal.Auth-        , Network.AWS.Internal.Body-        , Network.AWS.Internal.Env-        , Network.AWS.Internal.Log-        , Network.AWS.Internal.Retry+          Network.AWS.Internal.Body+        , Network.AWS.Internal.HTTP+        , Network.AWS.Internal.Logger      build-depends:-          amazonka-core       == 0.3.6.*+          amazonka-core       == 1.0.0.*         , base                >= 4.7     && < 5         , bytestring          >= 0.9         , conduit             >= 1.1-        , conduit-extra       == 1.1.*-        , cryptohash          == 0.11.*-        , cryptohash-conduit  >= 0.1.1+        , conduit-extra       >= 1.1+        , directory           >= 1.2         , exceptions          >= 0.6+        , free                >= 4.5+        , http-client         >= 0.4.9         , http-conduit        >= 2.1.4+        , ini                 >= 0.2         , lens                >= 4.4-        , mmorph              >= 1       && < 2-        , monad-control       >= 1       && < 2+        , mmorph              >= 1+        , monad-control       >= 1         , mtl                 >= 2.1.3.1         , resourcet           >= 1.1         , retry               >= 0.5         , text                >= 1.1         , time                >= 1.2         , transformers        >= 0.2-        , transformers-base   >= 0.4.2+        , transformers-base   >= 0.4         , transformers-compat >= 0.3++test-suite tests+    type:              exitcode-stdio-1.0+    default-language:  Haskell2010+    hs-source-dirs:    test+    main-is:           Main.hs++    ghc-options:       -Wall -threaded++    other-modules:++    build-depends:+          amazonka+        , base+        , tasty+        , tasty-hunit
src/Control/Monad/Trans/AWS.hs view
@@ -1,446 +1,436 @@-{-# LANGUAGE ConstraintKinds            #-}+{-# LANGUAGE BangPatterns               #-} {-# LANGUAGE FlexibleContexts           #-} {-# LANGUAGE FlexibleInstances          #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE LambdaCase                 #-} {-# LANGUAGE MultiParamTypeClasses      #-} {-# LANGUAGE RankNTypes                 #-}-{-# LANGUAGE TupleSections              #-} {-# LANGUAGE TypeFamilies               #-}---- This is required due to the MonadBaseControl instance. {-# LANGUAGE UndecidableInstances       #-}+{-# LANGUAGE ViewPatterns               #-} +{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}++-- | -- Module      : Control.Monad.Trans.AWS--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0. -- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental+-- Stability   : provisional -- Portability : non-portable (GHC extensions)---- | A monad transformer built on top of functions from "Network.AWS" which--- encapsulates various common parameters, errors, and usage patterns.+--+-- The 'AWST' transformer provides the environment required to perform AWS+-- operations and constructs a 'Command' AST using 'FreeT' which can then be+-- interpreted using 'runAWST'. The transformer is intended to be used directly+-- or embedded as a layer within a transformer stack.+--+-- "Network.AWS" contains a 'IO' specialised version of 'AWST' with a typeclass+-- to assist in automatically lifting operations. module Control.Monad.Trans.AWS     (-    -- * Requests-    -- ** Synchronous-      send-    , send_-    , sendCatch-    -- ** Paginated+    -- * Running AWS Actions+      AWST+    , runAWST+    , execAWST++    -- * Authentication and Environment+    , newEnv+    , Env+    , HasEnv       (..)++    -- ** Credential Discovery+    , Credentials  (..)+    -- $discovery++    -- ** Supported Regions+    , Region       (..)++    -- * Sending Requests+    -- $sending++    , send++    -- ** Pagination+    -- $pagination+     , paginate-    , paginateCatch-    -- ** Eventual consistency-    , await-    , awaitCatch-    -- ** Pre-signing URLs-    , presign-    , presignURL -    -- * Transformer-    , AWS-    , AWST-    , MonadAWS+    -- ** Waiters+    -- $waiters -    -- * Running-    , runAWST+    , await -    -- * Regionalisation-    , Region      (..)-    , within+    -- ** Overriding Service Configuration+    -- $service -    -- * Retries+    -- *** Scoped Actions+    , within     , once--    -- * Environment-    , Env-    -- ** Lenses-    , envRegion-    , envLogger-    , envRetryCheck-    , envRetryPolicy-    , envManager-    , envAuth-    -- ** Creating the environment-    , AWS.newEnv-    , AWS.getEnv-    -- ** Specifying credentials-    , Credentials (..)-    , fromKeys-    , fromSession-    , getAuth-    , accessKey-    , secretKey+    , timeout -    -- * Logging-    , newLogger-    , info-    , debug-    , trace+    -- *** Per Request+    , sendWith+    , paginateWith+    , awaitWith+    , presignWith -    -- * Errors-    , Error-    , hoistEither-    , throwAWSError-    , verify-    , verifyWith+    -- ** Streaming+    -- $streaming -    -- ** Streaming body helpers+    -- *** Request Bodies+    , ToBody       (..)     , sourceBody     , sourceHandle     , sourceFile     , sourceFileIO -    -- * Types-    , ToBuilder   (..)+    -- *** Response Bodies+    , sinkBody++    -- *** File Size and MD5/SHA256+    , getFileSize+    , sinkMD5+    , sinkSHA256++    -- * Presigning Requests+    -- $presigning++    , presignURL+    , presign++    -- * EC2 Instance Metadata+    -- $metadata++    , isEC2+    , dynamic+    , metadata+    , userdata++    , EC2.Dynamic  (..)+    , EC2.Metadata (..)++    -- * Running Asynchronous Actions+    -- $async++    -- * Handling Errors+    -- $errors++    , AsError      (..)+    , AsAuthError  (..)++    , trying+    , catching++    -- * Logging+    -- $logging++    , Logger+    , LogLevel     (..)++    -- ** Constructing a Logger+    , newLogger++    -- * Re-exported Types+    , RqBody+    , RsBody     , module Network.AWS.Types-    , module Network.AWS.Error+    , module Network.AWS.Waiter+    , module Network.AWS.Pager++    -- * runResourceT+    , runResourceT     ) where  import           Control.Applicative-import           Control.Arrow                (first)-import           Control.Lens+import           Control.Exception.Lens import           Control.Monad.Base import           Control.Monad.Catch-import           Control.Monad.Error          (MonadError, throwError)+import           Control.Monad.Error.Class       (MonadError (..)) import           Control.Monad.Morph import           Control.Monad.Reader+import           Control.Monad.State.Class import           Control.Monad.Trans.Control-import           Control.Monad.Trans.Except+import           Control.Monad.Trans.Free+import qualified Control.Monad.Trans.Free.Church as Free import           Control.Monad.Trans.Resource-import           Control.Retry                (limitRetries)-import           Data.ByteString              (ByteString)-import           Data.Conduit                 hiding (await)-import           Data.Time                    (UTCTime)-import qualified Network.AWS                  as AWS-import           Network.AWS.Data             (ToBuilder (..))-import           Network.AWS.Error-import           Network.AWS.Internal.Auth+import           Control.Monad.Writer.Class+import           Data.IORef+import           Network.AWS.Auth+import qualified Network.AWS.EC2.Metadata        as EC2+import           Network.AWS.Env+import           Network.AWS.Free import           Network.AWS.Internal.Body-import           Network.AWS.Internal.Env-import           Network.AWS.Internal.Log     hiding (debug, info, trace)-import qualified Network.AWS.Internal.Log     as Log-import           Network.AWS.Types-import           Network.AWS.Waiters-import qualified Network.HTTP.Conduit         as Client---- | The top-level error type.-type Error = ServiceError String---- | A convenient alias for 'AWST' 'IO'.-type AWS = AWST IO---- | Provides an alias for shortening type signatures if preferred.------ /Note:/ requires the @ConstraintKinds@ extension.-type MonadAWS m =-    ( MonadBaseControl IO m-    , MonadCatch m-    , MonadResource m-    , MonadError Error m-    , MonadReader Env m-    )+import           Network.AWS.Internal.HTTP+import           Network.AWS.Internal.Logger+import           Network.AWS.Pager               (AWSPager)+import           Network.AWS.Prelude             as AWS+import qualified Network.AWS.Presign             as Sign+import           Network.AWS.Types               hiding (LogLevel (..))+import           Network.AWS.Waiter              (Wait) --- | The transformer. This satisfies all of the constraints that the functions--- in this module require, such as providing 'MonadResource' instances,--- and keeping track of the 'Env' environment.------ The 'MonadError' instance for this transformer internally uses 'ExceptT'--- to handle actions that result in an 'Error'. For more information see--- 'sendCatch' and 'paginateCatch'.-newtype AWST m a = AWST-    { unAWST :: ReaderT (Env, InternalState) (ExceptT Error m) a-    } deriving+-- | The 'AWST' transformer.+newtype AWST m a = AWST { unAWST :: FreeT Command (ReaderT Env m) a }+    deriving         ( Functor         , Applicative         , Alternative         , Monad-        , MonadIO         , MonadPlus-        , MonadThrow-        , MonadCatch-        , MonadError Error+        , MonadIO+        , MonadFree Command+        , MonadReader Env         ) -instance Monad m => MonadReader Env (AWST m) where-    ask = AWST (fst `liftM` ask)-    {-# INLINE ask #-}--    local f = AWST . local (first f) . unAWST-    {-# INLINE local #-}+instance MonadThrow m => MonadThrow (AWST m) where+    throwM = lift . throwM -instance MonadTrans AWST where-    lift = AWST . lift . lift-    {-# INLINE lift #-}+instance MonadCatch m => MonadCatch (AWST m) where+    catch (AWST m) f = AWST (m `catch` \e -> unAWST (f e))  instance MonadBase b m => MonadBase b (AWST m) where     liftBase = liftBaseDefault-    {-# INLINE liftBase #-} -instance MonadTransControl AWST where-    type StT AWST a =-        StT (ExceptT Error) (StT (ReaderT (Env, InternalState)) a)+instance MFunctor AWST where+    hoist nat = AWST . hoistFreeT (hoist nat) . unAWST -    liftWith f = AWST $-        liftWith $ \g ->-            liftWith $ \h ->-                f (h . g . unAWST)-    {-# INLINE liftWith #-}+instance MonadBaseControl b m => MonadBaseControl b (AWST m) where+    type StM (AWST m) a =+         StM m (FreeF Command a (FreeT Command (ReaderT Env m) a)) -    restoreT = AWST . restoreT . restoreT-    {-# INLINE restoreT #-}+    liftBaseWith f = AWST . FreeT . liftM Pure $+        liftBaseWith $ \runInBase ->+            f $ \k ->+                runInBase (runFreeT (unAWST k)) -instance MonadBaseControl b m => MonadBaseControl b (AWST m) where-    type StM (AWST m) a = ComposeSt AWST m a+    restoreM = AWST . FreeT . restoreM -    liftBaseWith = defaultLiftBaseWith-    {-# INLINE liftBaseWith #-}+instance MonadTrans AWST where+    lift = AWST . lift . lift -    restoreM = defaultRestoreM-    {-# INLINE restoreM #-}+instance MonadResource m => MonadResource (AWST m) where+    liftResourceT = lift . liftResourceT -instance MFunctor AWST where-    hoist nat m = AWST (ReaderT (ExceptT . nat . runAWST' m))-    {-# INLINE hoist #-}+instance MonadError e m => MonadError e (AWST m) where+    throwError     = lift . throwError+    catchError m f = AWST (unAWST m `catchError` (unAWST . f)) -instance MMonad AWST where-    embed f m = liftM2 (,) ask resources-            >>= f . runAWST' m-            >>= either throwError return-    {-# INLINE embed #-}+instance MonadState s m => MonadState s (AWST m) where+    get = lift get+    put = lift . put -instance (Applicative m, MonadIO m, MonadBase IO m, MonadThrow m)-    => MonadResource (AWST m) where-        liftResourceT f = resources >>= liftIO . runInternalState f-        {-# INLINE liftResourceT #-}+instance MonadWriter w m => MonadWriter w (AWST m) where+    writer = lift . writer+    tell   = lift . tell+    listen = AWST . listen . unAWST+    pass   = AWST . pass   . unAWST --- | Unwrap an 'AWST' transformer, calling all of the registered 'ResourceT'--- release actions.-runAWST :: MonadBaseControl IO m => Env -> AWST m a -> m (Either Error a)-runAWST e m = runResourceT . withInternalState $ runAWST' f . (e,)+-- | Run an 'AWST' action with the specified 'HasEnv' environment.+-- Any outstanding HTTP responses' 'ResumableSource' will+-- be closed when the 'ResourceT' computation is unwrapped with 'runResourceT'.+--+-- Throws 'Error' during interpretation of the underlying 'FreeT' 'Command' AST.+--+-- /See:/ 'runResourceT'.+runAWST :: (MonadCatch m, MonadResource m, HasEnv r) => r -> AWST m a -> m a+runAWST = execAWST hoistError++-- | Run an 'AWST' action with configurable 'Error' handling.+--+-- Does not explictly throw 'Error's and instead uses the supplied lift function.+execAWST :: (MonadCatch m, MonadResource m, HasEnv r)+         => (forall a. Either Error a -> m a)+            -- ^ Lift an 'Error' into the base Monad.+         -> r+         -> AWST m b+         -> m b+execAWST f = innerAWST go   where-    f = liftBase (_envLogger e Debug (build e)) >> m+    go (CheckF k) = do+        io <- view envEC2+        mp <- liftIO (readIORef io)+        case mp of+            Just p  -> k p+            Nothing -> do+                m  <- view envManager+                !r <- lift . f =<< tryT (EC2.isEC2 m)+                liftIO (atomicWriteIORef io (Just r))+                k r -runAWST' :: AWST m a -> (Env, InternalState) -> m (Either Error a)-runAWST' (AWST k) = runExceptT . runReaderT k+    go (DynF x k) = do+        m <- view envManager+        r <- lift . f =<< tryT (EC2.dynamic m x)+        k r -resources :: Monad m => AWST m InternalState-resources = AWST (ReaderT (return . snd))+    go (MetaF x k) = do+        m <- view envManager+        r <- lift . f =<< tryT (EC2.metadata m x)+        k r --- | Hoist an 'Either' throwing the 'Left' case, and returning the 'Right'.-hoistEither :: (MonadError Error m, AWSError e) => Either e a -> m a-hoistEither = either throwAWSError return+    go (UserF k) = do+        m <- view envManager+        r <- lift . f =<< tryT (EC2.userdata m)+        k r --- | Throw any 'AWSError' using 'throwError'.-throwAWSError :: (MonadError Error m, AWSError e) => e -> m a-throwAWSError = throwError . awsError+    go (SignF s ts ex x k) = do+        a <- view envAuth+        g <- view envRegion+        r <- Sign.presignWith (const s) a g ts ex x+        k r --- | Verify that an 'AWSError' matches the given 'Prism', otherwise throw the--- error using 'throwAWSError'.-verify :: (AWSError e, MonadError Error m)-       => Prism' e a-       -> e-       -> m ()-verify p e-    | isn't p e = throwAWSError e-    | otherwise = return ()+    go (SendF s (request -> x) k) = do+        e <- view environment+        r <- lift . f =<< retrier e s x (perform e s x)+        k (snd r) --- | Verify that an 'AWSError' matches the given 'Prism', with an additional--- guard on the result of the 'Prism'.------ /See:/ 'verify'-verifyWith :: (AWSError e, MonadError Error m)-           => Prism' e a-           -> (a -> Bool)-           -> e-           -> m ()-verifyWith p f e = either (const err) g (matching p e)-  where-    g x | f x       = return ()-        | otherwise = err+    go (AwaitF s w (request -> x) k) = do+        e <- view environment+        r <- lift . f =<< waiter e w x (perform e s x)+        k (snd r) -    err = throwAWSError e+    tryT m = either (Left . TransportError) Right <$> try m --- | Pass the current environment to a function.-scoped :: MonadReader Env m => (Env -> m a) -> m a-scoped f = ask >>= f+innerAWST :: (Monad m, HasEnv r)+          => (Command (ReaderT Env m a) -> ReaderT Env m a)+          -> r+          -> AWST m a+          -> m a+innerAWST f e (AWST m) =+    runReaderT (f `Free.iterT` Free.toFT m) (e ^. environment) --- | Use the supplied logger from 'envLogger' to log info messages.------ /Note:/ By default, the library does not output 'Info' level messages.--- Exclusive output is guaranteed via use of this function.-info :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()-info x = view envLogger >>= (`Log.info` x)+hoistError :: MonadThrow m => Either Error a -> m a+hoistError = either (throwingM _Error) return --- | Use the supplied logger from 'envLogger' to log debug messages.-debug :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()-debug x = view envLogger >>= (`Log.debug` x)+{- $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>. --- | Use the supplied logger from 'envLogger' to log trace messages.-trace :: (MonadIO m, MonadReader Env m, ToBuilder a) => a -> m ()-trace x = view envLogger >>= (`Log.trace` x)+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 'Network.AWS.Auth.fromProfileName' for more information.+-} --- | Scope a monadic action within the specific 'Region'.-within :: MonadReader Env m => Region -> m a -> m a-within r = local (envRegion .~ r)+{- $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. --- | Scope a monadic action such that any retry logic for the 'Service' is--- ignored and any requests will at most be sent once.-once :: MonadReader Env m => m a -> m a-once = local $ \e ->-    e & envRetryPolicy ?~ limitRetries 0-      & envRetryCheck  .~ (\_ _ -> return False)+The default 'Service' configuration for a request (or the supplied 'Service' configuration+when using the @*With@ variants) 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.+-} --- | Send a data type which is an instance of 'AWSRequest', returning it's--- associated 'Rs' response type.------ This will throw any 'HTTPException' or 'AWSServiceError' returned by the--- service using the 'MonadError' instance. In the case of 'AWST' this will--- cause the internal 'ExceptT' to short-circuit and return an 'Error' in--- the 'Left' case as the result of the computation.------ /See:/ 'sendCatch'-send :: ( MonadCatch m-        , MonadResource m-        , MonadReader Env m-        , MonadError Error m-        , AWSRequest a-        )-     => a-     -> m (Rs a)-send = sendCatch >=> hoistEither+{- $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. --- | A variant of 'send' which discards any successful response.------ /See:/ 'send'-send_ :: ( MonadCatch m-         , MonadResource m-         , MonadReader Env m-         , MonadError Error m-         , AWSRequest a-         )-      => a-      -> m ()-send_ = void . send+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. --- | Send a data type which is an instance of 'AWSRequest', returning either the--- associated 'Rs' response type in the success case, or the related service's--- 'Er' type in the error case.------ This includes 'HTTPExceptions', serialisation errors, and any service--- errors returned as part of the 'Response'.------ /Note:/ Requests will be retried depending upon each service's respective--- strategy. This can be overriden using 'once' or 'envRetry'.--- Requests which contain streaming request bodies (such as S3's 'PutObject') are--- never considered for retries.-sendCatch :: ( MonadCatch m-             , MonadResource m-             , MonadReader Env m-             , AWSRequest a-             )-          => a-          -> m (Response a)-sendCatch x = scoped (`AWS.send` x)+Many operations have the ability to filter results on the server side. See the+individual operation parameters for details.+-} --- | Send a data type which is an instance of 'AWSPager' and paginate while--- there are more results as defined by the related service operation.------ Errors will be handle identically to 'send'.------ /Note:/ The 'ResumableSource' will close when there are no more results or the--- 'ResourceT' computation is unwrapped. See: 'runResourceT' for more information.------ /See:/ 'paginateCatch'-paginate :: ( MonadCatch m-            , MonadResource m-            , MonadReader Env m-            , MonadError Error m-            , AWSPager a-            )-         => a-         -> Source m (Rs a)-paginate x = paginateCatch x $= awaitForever (hoistEither >=> yield)+{- $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. --- | Send a data type which is an instance of 'AWSPager' and paginate over--- the associated 'Rs' response type in the success case, or the related service's--- 'Er' type in the error case.------ /Note:/ The 'ResumableSource' will close when there are no more results or the--- 'ResourceT' computation is unwrapped. See: 'runResourceT' for more information.-paginateCatch :: ( MonadCatch m-                 , MonadResource m-                 , MonadReader Env m-                 , AWSPager a-                 )-              => a-              -> Source m (Response a)-paginateCatch x = scoped (`AWS.paginate` x)+'Wait' specifications can be found under the @Network.AWS.{ServiceName}.Waiters@+namespace for services which support 'await'.+-} --- | Poll the API until a predfined condition is fulfilled using the--- supplied 'Wait' specification from the respective service.------ Any errors which are unhandled by the 'Wait' specification during retries--- will be thrown in the same manner as 'send'.------ /See:/ 'awaitCatch'-await :: ( MonadCatch m-         , MonadResource m-         , MonadReader Env m-         , MonadError Error m-         , AWSRequest a-         )-      => Wait a-      -> a-      -> m (Rs a)-await w = awaitCatch w >=> hoistEither+{- $service+When a request is sent, various configuration values such as the endpoint,+retry strategy, timeout and error handlers are taken from the associated 'Service'+configuration. --- | Poll the API until a predfined condition is fulfilled using the--- supplied 'Wait' specification from the respective service.------ The response will be either the first error returned that is not handled--- by the specification, or the successful response from the await request.------ /Note:/ You can find any available 'Wait' specifications under the--- namespace @Network.AWS.<ServiceName>.Waiters@ for supported services.-awaitCatch :: ( MonadCatch m-              , MonadResource m-              , MonadReader Env m-              , AWSRequest a-              )-           => Wait a-           -> a-           -> m (Response a)-awaitCatch w x = scoped (\e -> AWS.await e w x)+You can override the default configuration for a series of one or more actions+by using 'within', 'once' and 'timeout', or by using the @*With@ suffixed+functions on an individual request basis below.+-} --- | Presign an HTTP request that expires at the specified amount of time--- in the future.------ /Note:/ Requires the service's signer to be an instance of 'AWSPresigner'.--- Not all signing process support this.-presign :: ( MonadIO m-           , MonadReader Env m-           , AWSRequest a-           , AWSPresigner (Sg (Sv a))-           )-        => a       -- ^ Request to presign.-        -> UTCTime -- ^ Signing time.-        -> Integer -- ^ Expiry time in seconds.-        -> m Client.Request-presign x t ex = scoped (\e -> AWS.presign e x t ex)+{- $streaming+Streaming request bodies (such as 'PutObject') require a precomputed+'SHA256' for signing purposes.+The 'ToBody' typeclass has instances available to construct a 'RqBody',+automatically calculating the hash as needed for types such as 'Text' and 'ByteString'. --- | Presign a URL that expires at the specified amount of time in the future.------ /See:/ 'presign'-presignURL :: ( MonadIO m-             , MonadReader Env m-             , AWSRequest a-             , AWSPresigner (Sg (Sv a))-             )-           => a       -- ^ Request to presign.-           -> UTCTime -- ^ Signing time.-           -> Integer -- ^ Expiry time in seconds.-           -> m ByteString-presignURL x t ex = scoped (\e -> AWS.presignURL e x t ex)+For reading files and handles, functions such 'sourceFileIO' or 'sourceHandle'+can 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 <http://hackage.haskell.org/package/lifted-async lifted-async>.++The following example demonstrates retrieving two objects from S3 concurrently:++> import Control.Concurrent.Async.Lifted+> import Control.Lens+> import Control.Monad.Trans.AWS+> import Network.AWS.S3+>+> do x   <- async . send $ getObject "bucket" "prefix/object-foo"+>    y   <- async . send $ getObject "bucket" "prefix/object-bar"+>    foo <- wait x+>    bar <- wait y+>    ...++/See:/ <http://hackage.haskell.org/package/lifted-async Control.Concurrent.Async.Lifted>+-}++{- $errors+Errors are thrown by the library using 'MonadThrow' (unless "Control.Monad.Error.AWS" is used).+Sub-errors of the canonical 'Error' type can be caught using 'trying' or+'catching' and the appropriate 'AsError' 'Prism':++@+trying '_Error'          (send $ ListObjects "bucket-name") :: Either 'Error'          ListObjectsResponse+trying '_TransportError' (send $ ListObjects "bucket-name") :: Either 'HttpException'  ListObjectsResponse+trying '_SerializeError' (send $ ListObjects "bucket-name") :: Either 'SerializeError' ListObjectsResponse+trying '_ServiceError'   (send $ ListObjects "bucket-name") :: Either 'ServiceError'   ListObjectsResponse+@++Many of the individual @amazonka-*@ libraries export compatible '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/tiny-log tiny-log> or+<http://hackage.haskell.org/package/fast-logger fast-logger>.+-}
src/Network/AWS.hs view
@@ -1,223 +1,459 @@-{-# LANGUAGE ConstraintKinds   #-} {-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards   #-}-{-# LANGUAGE TupleSections     #-}-{-# LANGUAGE TypeFamilies      #-}-{-# LANGUAGE ViewPatterns      #-}+{-# LANGUAGE FlexibleInstances #-} +{-# OPTIONS_GHC -fno-warn-duplicate-exports #-}++-- | -- Module      : Network.AWS--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0. -- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental+-- Stability   : provisional -- Portability : non-portable (GHC extensions)---- | The core module for making requests to the various AWS services.+--+-- This module provides a simple 'AWS' monad and a set of operations which+-- can be performed against remote Amazon Web Services APIs, for use with the types+-- supplied by the various @amazonka-*@ libraries.+--+-- A 'MonadAWS' typeclass is used as a function constraint to provide automatic+-- lifting of functions when embedding 'AWS' as a layer inside your own+-- application stack.+--+-- "Control.Monad.Trans.AWS" contains the underlying 'AWST' transformer. module Network.AWS     (-    -- * Requests-    -- ** Synchronous-      send-    -- ** Paginated+    -- * Usage+    -- $usage++    -- * Running AWS Actions+      AWS+    , MonadAWS    (..)+    , runAWS++    -- * Authentication and Environment+    , newEnv+    , Env+    , HasEnv       (..)++    -- ** Credential Discovery+    , Credentials  (..)+    -- $discovery++    -- ** Supported Regions+    , Region       (..)++    -- * Sending Requests+    -- $sending++    , send++    -- ** Pagination+    -- $pagination+     , paginate-    -- ** Eventual consistency++    -- ** Waiters+    -- $waiters+     , await-    -- ** Pre-signing URLs-    , presign-    , presignURL -    -- * Environment-    , Env-    -- ** Lenses-    , envRegion-    , envLogger-    , envRetryCheck-    , envRetryPolicy-    , envManager-    , envAuth-    -- ** Creating the environment-    , newEnv-    , getEnv-    -- ** Specifying credentials-    , Credentials (..)-    , fromKeys-    , fromSession-    , getAuth-    , accessKey-    , secretKey+    -- ** Overriding Service Configuration+    -- $service -    -- * Logging-    , newLogger+    -- *** Scoped Actions+    , within+    , once+    , timeout -    -- ** Streaming body helpers+    -- ** Streaming+    -- $streaming++    -- *** Request Bodies+    , ToBody       (..)     , sourceBody     , sourceHandle     , sourceFile     , sourceFileIO -    -- * Types+    -- *** Response Bodies+    , sinkBody++    -- *** File Size and MD5/SHA256+    , getFileSize+    , sinkMD5+    , sinkSHA256++    -- * Presigning Requests+    -- $presigning++    , presignURL++    -- * EC2 Instance Metadata+    -- $metadata++    , isEC2+    , dynamic+    , metadata+    , userdata++    , EC2.Dynamic  (..)+    , EC2.Metadata (..)++    -- * Running Asynchronous Actions+    -- $async++    -- * Handling Errors+    -- $errors++    , AsError      (..)+    , AsAuthError  (..)++    , AWST.trying+    , AWST.catching++    -- * Logging+    -- $logging++    , Logger+    , LogLevel     (..)++    -- ** Constructing a Logger+    , newLogger++    -- * Re-exported Types+    , RqBody+    , RsBody     , module Network.AWS.Types-    , module Network.AWS.Error+    , module Network.AWS.Waiter+    , module Network.AWS.Pager++    -- * runResourceT+    , runResourceT     ) where  import           Control.Applicative-import           Control.Monad-import           Control.Monad.Catch-import           Control.Monad.IO.Class-import           Control.Monad.Trans.Class-import           Control.Monad.Trans.Except+import           Control.Monad.Catch             (MonadCatch)+import           Control.Monad.Morph             (hoist)+import qualified Control.Monad.RWS.Lazy          as LRW+import qualified Control.Monad.RWS.Strict        as RW+import qualified Control.Monad.State.Lazy        as LS+import qualified Control.Monad.State.Strict      as S+import           Control.Monad.Trans.AWS         (AWST)+import qualified Control.Monad.Trans.AWS         as AWST+import           Control.Monad.Trans.Class       (lift)+import           Control.Monad.Trans.Cont        (ContT)+import           Control.Monad.Trans.Except      (ExceptT)+import           Control.Monad.Trans.Free        (FreeT)+import           Control.Monad.Trans.Free.Church (FT)+import           Control.Monad.Trans.Identity    (IdentityT)+import           Control.Monad.Trans.Iter        (IterT)+import           Control.Monad.Trans.List        (ListT)+import           Control.Monad.Trans.Maybe       (MaybeT)+import           Control.Monad.Trans.Reader      (ReaderT) import           Control.Monad.Trans.Resource-import           Data.ByteString              (ByteString)-import           Data.Conduit                 hiding (await)+import qualified Control.Monad.Writer.Lazy       as LW+import qualified Control.Monad.Writer.Strict     as W+import           Data.Conduit                    (Source) import           Data.Monoid-import           Data.Time                    (getCurrentTime)-import           Network.AWS.Data-import           Network.AWS.Error-import           Network.AWS.Internal.Auth+import           Network.AWS.Auth+import qualified Network.AWS.EC2.Metadata        as EC2+import           Network.AWS.Env                 (Env, HasEnv (..), newEnv) import           Network.AWS.Internal.Body-import           Network.AWS.Internal.Env-import           Network.AWS.Internal.Log-import           Network.AWS.Internal.Retry-import qualified Network.AWS.Signing          as Sign-import           Network.AWS.Types-import           Network.AWS.Waiters-import           Network.HTTP.Conduit         hiding (Request, Response)-import qualified Network.HTTP.Conduit         as Client+import           Network.AWS.Internal.Logger+import           Network.AWS.Pager               (AWSPager)+import           Network.AWS.Prelude+import           Network.AWS.Types               hiding (LogLevel (..))+import           Network.AWS.Waiter              (Wait) --- | This creates a new environment without debug logging and uses 'getAuth'--- to expand/discover the supplied 'Credentials'.------ Lenses such as 'envLogger' can be used to modify the 'Env' with a debug logger.-newEnv :: (Functor m, MonadIO m)-       => Region-       -> Credentials-       -> Manager-       -> ExceptT String m Env-newEnv r c m = Env r logger check Nothing m `liftM` getAuth m c-  where-    logger _ _ = return ()-    check  _ _ = return True+import           Prelude --- | Create a new environment in the specified 'Region' with silent log output--- and a new 'Manager'.------ Any errors are thrown using 'error'.------ /See:/ 'newEnv' for safe 'Env' instantiation.-getEnv :: Region -> Credentials -> IO Env-getEnv r c = do-    m <- newManager conduitManagerSettings-    runExceptT (newEnv r c m) >>= either error return+-- | A specialisation of the 'AWST' transformer.+type AWS = AWST IO --- | Send a data type which is an instance of 'AWSRequest', returning either the--- associated 'Rs' response type in the success case, or the related service's--- 'Er' type in the error case.+-- | Monads in which 'AWS' actions may be embedded.+class (Functor m, Applicative m, Monad m) => MonadAWS m where+    -- | Lift a computation to the 'AWS' monad.+    liftAWS :: AWS a -> m a++instance MonadAWS AWS where+    liftAWS = id++instance MonadAWS m => MonadAWS (IdentityT   m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (ListT       m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (MaybeT      m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (ExceptT   e m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (ContT     r m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (ReaderT   r m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (S.StateT  s m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (LS.StateT s m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (IterT       m) where liftAWS = lift . liftAWS+instance MonadAWS m => MonadAWS (FT        f m) where liftAWS = lift . liftAWS++instance (Monoid w, MonadAWS m) => MonadAWS (W.WriterT w m) where+    liftAWS = lift . liftAWS++instance (Monoid w, MonadAWS m) => MonadAWS (LW.WriterT w m) where+    liftAWS = lift . liftAWS++instance (Monoid w, MonadAWS m) => MonadAWS (RW.RWST r w s m) where+    liftAWS = lift . liftAWS++instance (Monoid w, MonadAWS m) => MonadAWS (LRW.RWST r w s m) where+    liftAWS = lift . liftAWS++instance (Functor f, MonadAWS m) => MonadAWS (FreeT f m) where+    liftAWS = lift . liftAWS++-- FIXME: verify the use of withInternalState to create a ResourceT here++-- | Run the 'AWS' monad. Any outstanding HTTP responses' 'ResumableSource' will+-- be closed when the 'ResourceT' computation is unwrapped with 'runResourceT'. ----- This includes 'HTTPExceptions', serialisation errors, and any service--- errors returned as part of the 'Response'.+-- Throws 'Error', which will include 'HTTPExceptions', serialisation errors,+-- or any particular errors returned by the respective AWS service. ----- /Note:/ Requests will be retried depending upon each service's respective--- strategy. This can be overriden using 'envRetry'. Requests which contain--- streaming request bodies (such as S3's 'PutObject') are never considered for retries.-send :: (MonadCatch m, MonadResource m, AWSRequest a)-     => Env-     -> a-     -> m (Response a)-send e@Env{..} (request -> rq) = fmap snd <$> retrier e rq (raw e rq)+-- /See:/ 'runAWST', 'runResourceT'.+runAWS :: (MonadCatch m, MonadResource m, HasEnv r) => r -> AWS a -> m a+runAWS e = liftResourceT . AWST.runAWST e . hoist (withInternalState . const) --- | Poll the API until a predefined condition is fulfilled using the--- supplied 'Wait' specification from the respective service.------ The response will be either the first error returned that is not handled--- by the specification, or the successful response from the await request.+-- | Scope an action within the specific 'Region'.+within :: MonadAWS m => Region -> AWS a -> m a+within r = liftAWS . AWST.within r++-- | Scope an action such that any retry logic for the 'Service' is+-- ignored and any requests will at most be sent once.+once :: MonadAWS m => AWS a -> m a+once = liftAWS . AWST.once++-- | Scope an action such that any HTTP response will use this timeout value.+timeout :: MonadAWS m => Seconds -> AWS a -> m a+timeout s = liftAWS . AWST.timeout s++-- | Send a request, returning the associated response if successful. ----- /Note:/ You can find any available 'Wait' specifications under then--- @Network.AWS.<ServiceName>.Waiters@ namespace for supported services.-await :: (MonadCatch m, MonadResource m, AWSRequest a)-      => Env-      -> Wait a-      -> a-      -> m (Response a)-await e w (request -> rq) = fmap snd <$> waiter e w rq (raw e rq)+-- /See:/ 'AWST.sendWith'+send :: (MonadAWS m, AWSRequest a) => a -> m (Rs a)+send = liftAWS . AWST.send --- | Send a data type which is an instance of 'AWSPager' and paginate over--- the associated 'Rs' response type in the success case, or the related service's--- 'Er' type in the error case.+-- | Repeatedly send a request, automatically setting markers and+-- paginating over multiple responses while available. ----- /Note:/ The 'ResumableSource' will close when there are no more results or the--- 'ResourceT' computation is unwrapped. See: 'runResourceT' for more information.-paginate :: (MonadCatch m, MonadResource m, AWSPager a)-         => Env-         -> a-         -> Source m (Response a)-paginate e = go-  where-    go x = do-        y <- lift (send e x)-        yield y-        either (const (return ()))-               (maybe (return ()) go . page x)-               y+-- /See:/ 'AWST.paginateWith'+paginate :: (MonadAWS m, AWSPager a) => a -> Source m (Rs a)+paginate = hoist liftAWS . AWST.paginate --- | Presign an HTTP request that expires at the specified amount of time--- in the future.+-- | Poll the API with the supplied request until a specific 'Wait' condition+-- is fulfilled. ----- /Note:/ Requires the service's signer to be an instance of 'AWSPresigner'.--- Not all signing process support this.-presign :: (MonadIO m, AWSRequest a, AWSPresigner (Sg (Sv a)))-        => Env-        -> a       -- ^ Request to presign.-        -> UTCTime -- ^ Signing time.-        -> Integer -- ^ Expiry time in seconds.-        -> m Client.Request-presign Env{..} (request -> rq) t ex =-    _sgRequest `liftM` Sign.presign _envAuth _envRegion rq t ex+-- /See:/ 'AWST.awaitWith'+await :: (MonadAWS m, AWSRequest a) => Wait a -> a -> m (Rs a)+await w = liftAWS . AWST.await w --- | Presign a URL that expires at the specified amount of time in the future.+-- | Presign an URL that is valid from the specified time until the+-- number of seconds expiry has elapsed. ----- /See:/ 'presign'-presignURL :: (MonadIO m, AWSRequest a, AWSPresigner (Sg (Sv a)))-           => Env-           -> a       -- ^ Request to presign.-           -> UTCTime -- ^ Signing time.-           -> Integer -- ^ Expiry time in seconds.+-- /See:/ 'AWST.presign', 'AWST.presignWith'+presignURL :: (MonadAWS m, AWSPresigner (Sg (Sv a)), AWSRequest a)+           => UTCTime     -- ^ Signing time.+           -> Seconds     -- ^ Expiry time.+           -> a           -- ^ Request to presign.            -> m ByteString-presignURL e x t ex = (toBS . uri) `liftM` presign e x t ex-  where-    uri rq =-           scheme (secure rq)-        <> build  (host rq)-        <> port'  (port rq)-        <> build  (path rq)-        <> build  (queryString rq)+presignURL t ex = liftAWS . AWST.presignURL t ex -    scheme True = "https://"-    scheme _    = "http://"+-- | Test whether the underlying host is running on EC2.+-- This is memoised and an HTTP request is made to the host's metadata+-- endpoint for the first call only.+isEC2 :: MonadAWS m => m Bool+isEC2 = liftAWS AWST.isEC2 -    port' = \case-        80  -> ""-        443 -> ""-        n   -> build ':' <> build n+-- | Retrieve the specified 'Dynamic' data.+dynamic :: MonadAWS m => EC2.Dynamic -> m ByteString+dynamic = liftAWS . AWST.dynamic -raw :: (MonadCatch m, MonadResource m, AWSRequest a)-    => Env-    -> Request a-    -> m (Response' a)-raw Env{..} rq = catch go err >>= response _envLogger rq-  where-    go = do-        trace _envLogger (build rq)-        t  <- liftIO getCurrentTime-        Signed m s <- Sign.sign _envAuth _envRegion rq t-        debug _envLogger (build s)-        trace _envLogger (build m)-        rs <- liftResourceT (http s _envManager)-        debug _envLogger (build rs)-        return (Right rs)+-- | Retrieve the specified 'Metadata'.+metadata :: MonadAWS m => EC2.Metadata -> m ByteString+metadata = liftAWS . AWST.metadata -    err ex = return (Left (ex :: HttpException))+-- | Retrieve the user data. Returns 'Nothing' if no user data is assigned+-- to the instance.+userdata :: MonadAWS m => m (Maybe ByteString)+userdata = liftAWS AWST.userdata++{- $usage+The key functions dealing with the request/response lifecycle are:++* 'send'++* 'paginate'++* 'await'++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 determine 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>:++@+import Control.Lens+import Network.AWS+import Network.AWS.S3+import System.IO++example :: IO PutObjectResponse+example = do+    -- To specify configuration preferences, 'newEnv' is used to create a new 'Env'. The 'Region' denotes the AWS region requests will be performed against,+    -- and 'Credentials' is used to specify the desired 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:+    e <- newEnv Frankfurt Discover++    -- A new 'Logger' to replace the default noop logger is created, with the logger set to print debug information and errors to stdout:+    l <- newLogger Debug stdout++    -- The payload (and hash) for the S3 object is retrieved from a FilePath:+    b <- sourceFileIO "local\/path\/to\/object-payload"++    -- We now run the AWS computation with the overriden logger, performing the PutObject request:+    runAWS (e & envLogger .~ l) $+        send (putObject "bucket-name" "object-key" b)+@+-}++{- $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 'Network.AWS.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 (or the supplied 'Service' configuration+when using the @*With@ variants) 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 @Network.AWS.{ServiceName}.Waiters@+namespace for services which support 'await'.+-}++{- $service+When a request is sent, various configuration values such as the endpoint,+retry strategy, timeout and error handlers are taken from the associated 'Service'+configuration.++You can override the default configuration for a series of one or more actions+by using 'within', 'once' and 'timeout', or by using the @*With@ suffixed+functions on an individual request basis below.+-}++{- $streaming+Streaming request bodies (such as 'PutObject') require a precomputed+'SHA256' for signing purposes.+The 'ToBody' typeclass has instances available to construct a 'RqBody',+automatically calculating the hash as needed for types such as 'Text' and 'ByteString'.++For reading files and handles, functions such 'sourceFileIO' or 'sourceHandle'+can 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 <http://hackage.haskell.org/package/lifted-async lifted-async>.++The following example demonstrates retrieving two objects from S3 concurrently:++> import Control.Concurrent.Async.Lifted+> import Control.Lens+> import Control.Monad.Trans.AWS+> import Network.AWS.S3+>+> do x   <- async . send $ getObject "bucket" "prefix/object-foo"+>    y   <- async . send $ getObject "bucket" "prefix/object-bar"+>    foo <- wait x+>    bar <- wait y+>    ...++/See:/ <http://hackage.haskell.org/package/lifted-async Control.Concurrent.Async.Lifted>+-}++{- $errors+Errors are thrown by the library using 'MonadThrow' (unless "Control.Monad.Error.AWS" is used).+Sub-errors of the canonical 'Error' type can be caught using 'trying' or+'catching' and the appropriate 'AsError' 'Prism':++@+trying '_Error'          (send $ ListObjects "bucket-name") :: Either 'Error'          ListObjectsResponse+trying '_TransportError' (send $ ListObjects "bucket-name") :: Either 'HttpException'  ListObjectsResponse+trying '_SerializeError' (send $ ListObjects "bucket-name") :: Either 'SerializeError' ListObjectsResponse+trying '_ServiceError'   (send $ ListObjects "bucket-name") :: Either 'ServiceError'   ListObjectsResponse+@++Many of the individual @amazonka-*@ libraries export compatible '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/tiny-log tiny-log> or+<http://hackage.haskell.org/package/fast-logger fast-logger>.+-}
+ src/Network/AWS/Auth.hs view
@@ -0,0 +1,446 @@+{-# LANGUAGE BangPatterns       #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts   #-}+{-# LANGUAGE LambdaCase         #-}+{-# LANGUAGE OverloadedStrings  #-}++-- |+-- Module      : Network.AWS.Auth+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Explicitly specify your Amazon AWS security credentials, or retrieve them+-- from the underlying OS.+--+-- The format of environment variables and the credentials file follows the official+-- <http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs AWS SDK guidelines>.+module Network.AWS.Auth+    (+    -- * Authentication+    -- ** Retrieving authentication+      getAuth+    , Credentials   (..)+    , Auth++    -- ** Defaults+    -- *** Environment+    , envAccessKey+    , envSecretKey+    , envSessionToken++    -- *** Credentials File+    , credAccessKey+    , credSecretKey+    , credSessionToken+    , credProfile+    , credFile++    -- $credentials++    , fromKeys+    , fromSession+    , fromEnv+    , fromEnvKeys+    , fromFile+    , fromFilePath+    , fromProfile+    , fromProfileName++    -- ** Keys+    , AccessKey    (..)+    , SecretKey    (..)+    , SessionToken (..)++    -- ** Handling Errors+    , AsAuthError (..)+    , AuthError   (..)+    ) where++import           Control.Applicative+import           Control.Concurrent+import           Control.Exception.Lens+import           Control.Monad+import           Control.Monad.Catch+import           Control.Monad.IO.Class+import qualified Data.ByteString.Char8      as BS8+import qualified Data.ByteString.Lazy.Char8 as LBS8+import qualified Data.Ini                   as INI+import           Data.IORef+import           Data.Monoid+import qualified Data.Text                  as Text+import qualified Data.Text.Encoding         as Text+import           Data.Time                  (diffUTCTime, getCurrentTime)+import           Network.AWS.Data.Log+import           Network.AWS.EC2.Metadata+import           Network.AWS.Prelude+import           Network.AWS.Types+import           Network.HTTP.Conduit+import           System.Directory           (doesFileExist, getHomeDirectory)+import           System.Environment+import           System.Mem.Weak++import           Prelude++-- | Default access key environment variable.+envAccessKey :: Text -- ^ AWS_ACCESS_KEY_ID+envAccessKey = "AWS_ACCESS_KEY_ID"++-- | Default secret key environment variable.+envSecretKey :: Text -- ^ AWS_SECRET_ACCESS_KEY+envSecretKey = "AWS_SECRET_ACCESS_KEY"++-- | Default session token environment variable.+envSessionToken :: Text -- ^ AWS_SESSION_TOKEN+envSessionToken = "AWS_SESSION_TOKEN"++-- | Credentials INI file access key variable.+credAccessKey :: Text -- ^ aws_access_key_id+credAccessKey = "aws_access_key_id"++-- | Credentials INI file secret key variable.+credSecretKey :: Text -- ^ aws_secret_access_key+credSecretKey = "aws_secret_access_key"++-- | Credentials INI file session token variable.+credSessionToken :: Text -- ^ aws_session_token+credSessionToken = "aws_session_token"++-- | Credentials INI default profile section variable.+credProfile :: Text -- ^ default+credProfile = "default"++-- | Default path for the credentials file. This looks in in the @HOME@ directory+-- as determined by the <http://hackage.haskell.org/package/directory directory>+-- library.+--+-- * UNIX/OSX: @$HOME/.aws/credentials@+--+-- * Windows: @C:\/Users\//\<user\>\.aws\credentials@+--+-- /Note:/ This does not match the default AWS SDK location of+-- @%USERPROFILE%\.aws\credentials@ on Windows. (Sorry.)+credFile :: MonadIO m => m FilePath+credFile = (<> "/.aws/credentials") `liftM` liftIO getHomeDirectory+-- TODO: probably should be using System.FilePath above.++{- $credentials+'getAuth' is implemented using the following @from*@-styled functions below.+Both 'fromKeys' and 'fromSession' can be used directly to avoid the 'MonadIO'+constraint.+-}++-- | Explicit access and secret keys.+fromKeys :: AccessKey -> SecretKey -> Auth+fromKeys a s = Auth (AuthEnv a s Nothing Nothing)++-- | A session containing the access key, secret key, and a session token.+fromSession :: AccessKey -> SecretKey -> SessionToken -> Auth+fromSession a s t = Auth (AuthEnv a s (Just t) Nothing)++-- | Determines how AuthN/AuthZ information is retrieved.+data Credentials+    = FromKeys AccessKey SecretKey+      -- ^ Explicit access and secret keys. See 'fromKeys'.++    | FromSession AccessKey SecretKey SessionToken+      -- ^ Explicit access key, secret key and a session token. See 'fromSession'.++    | FromEnv Text Text (Maybe Text)+      -- ^ Lookup specific environment variables for access key, secret key, and+      -- an optional session token respectively.++    | FromProfile Text+      -- ^ An IAM Profile name to lookup from the local EC2 instance-data.+      -- ^ Environment variables to lookup for the access key, secret key and+      -- optional session token.++    | FromFile Text FilePath+      -- ^ A credentials profile name (the INI section) and the path to the AWS+      -- <http://blogs.aws.amazon.com/security/post/Tx3D6U6WSFGOK2H/A-New-and-Standardized-Way-to-Manage-Credentials-in-the-AWS-SDKs credentials> file.++    | Discover+      -- ^ Attempt to credentials discovery via the following steps:+      --+      -- * Read the 'envAccessKey' and 'envSecretKey' from the environment if they are set.+      --+      -- * Read the credentials file if 'credFile' exists.+      --+      -- * Retrieve the first available IAM profile if running on EC2.+      --+      -- An attempt is made to resolve <http://instance-data> rather than directly+      -- retrieving <http://169.254.169.254> for IAM profile information.+      -- This assists in ensuring the DNS lookup terminates promptly if not+      -- running on EC2.+      deriving (Eq)++instance ToLog Credentials where+    build = \case+        FromKeys    a _   -> "FromKeys "    <> build a <> " ****"+        FromSession a _ _ -> "FromSession " <> build a <> " **** ****"+        FromEnv     a s t -> "FromEnv "     <> build a <> " " <> build s <> " " <> m t+        FromProfile n     -> "FromProfile " <> build n+        FromFile    n f   -> "FromFile "    <> build n <> " " <> build f+        Discover          -> "Discover"+      where+        m (Just x) = "(Just " <> build x <> ")"+        m Nothing  = "Nothing"++instance Show Credentials where+    show = BS8.unpack . toBS . build++-- | An error thrown when attempting to read AuthN/AuthZ information.+data AuthError+    = RetrievalError   HttpException+    | MissingEnvError  Text+    | MissingFileError FilePath+    | InvalidFileError Text+    | InvalidIAMError  Text+      deriving (Show, Typeable)++instance Exception AuthError++instance ToLog AuthError where+    build = \case+        RetrievalError   e -> build e+        MissingEnvError  e -> "[MissingEnvError]  { message = " <> build e <> "}"+        MissingFileError f -> "[MissingFileError] { path = "    <> build f <> "}"+        InvalidFileError e -> "[InvalidFileError] { message = " <> build e <> "}"+        InvalidIAMError  e -> "[InvalidIAMError]  { message = " <> build e <> "}"++class AsAuthError a where+    -- | A general authentication error.+    _AuthError        :: Prism' a AuthError+    {-# MINIMAL _AuthError #-}++    -- | An error occured while communicating over HTTP with+    -- the local metadata endpoint.+    _RetrievalError   :: Prism' a HttpException++    -- | An error occured looking up a named environment variable.+    _MissingEnvError  :: Prism' a Text++    -- | The specified credentials file could not be found.+    _MissingFileError :: Prism' a FilePath++    -- | An error occured parsing the credentials file.+    _InvalidFileError :: Prism' a Text++    -- | The specified IAM profile could not be found or deserialised.+    _InvalidIAMError  :: Prism' a Text++    _RetrievalError   = _AuthError . _RetrievalError+    _MissingEnvError  = _AuthError . _MissingEnvError+    _MissingFileError = _AuthError . _MissingFileError+    _InvalidFileError = _AuthError . _InvalidFileError+    _InvalidIAMError  = _AuthError . _InvalidIAMError++instance AsAuthError SomeException where+    _AuthError = exception++instance AsAuthError AuthError where+    _AuthError = id++    _RetrievalError = prism RetrievalError $ \case+        RetrievalError   e -> Right e+        x                  -> Left  x++    _MissingEnvError = prism MissingEnvError $ \case+        MissingEnvError  e -> Right e+        x                  -> Left  x++    _MissingFileError = prism MissingFileError $ \case+        MissingFileError f -> Right f+        x                  -> Left  x++    _InvalidFileError = prism InvalidFileError $ \case+        InvalidFileError e -> Right e+        x                  -> Left  x++    _InvalidIAMError = prism InvalidIAMError $ \case+        InvalidIAMError  e -> Right e+        x                  -> Left  x++-- | Retrieve authentication information via the specified 'Credentials' mechanism.+--+-- Throws 'AuthError' when environment variables or IAM profiles cannot be read.+getAuth :: (Applicative m, MonadIO m, MonadCatch m)+        => Manager+        -> Credentials+        -> m Auth+getAuth m = \case+    FromKeys    a s   -> return (fromKeys a s)+    FromSession a s t -> return (fromSession a s t)+    FromEnv     a s t -> fromEnvKeys a s t+    FromProfile n     -> fromProfileName m n+    FromFile    n f   -> fromFilePath n f+    Discover          ->+        -- Don't try and catch InvalidFileError, or InvalidIAMProfile,+        -- let both errors propagate.+        catching_ _MissingEnvError fromEnv $+            -- proceed, missing env keys+            catching _MissingFileError fromFile $ \f -> do+                -- proceed, missing credentials file+                p <- isEC2 m+                unless p $ throwingM _MissingFileError f+                fromProfile m++-- | Retrieve access key, secret key, and a session token from the default+-- environment variables.+--+-- Throws 'MissingEnvError' if either of the default environment variables+-- cannot be read, but not if the session token is absent.+--+-- /See:/ 'envAccessKey', 'envSecretKey', 'envSessionToken'+fromEnv :: (Applicative m, MonadIO m, MonadThrow m) => m Auth+fromEnv = fromEnvKeys envAccessKey envSecretKey (Just envSessionToken)++-- | Retrieve access key, secret key and a session token from specific+-- environment variables.+--+-- Throws 'MissingEnvError' if either of the specified key environment variables+-- cannot be read, but not if the session token is absent.+fromEnvKeys :: (Applicative m, MonadIO m, MonadThrow m)+            => Text       -- ^ Access key environment variable.+            -> Text       -- ^ Secret key environment variable.+            -> Maybe Text -- ^ Session token environment variable.+            -> m Auth+fromEnvKeys a s t = fmap Auth $ AuthEnv+    <$> (req a <&> AccessKey)+    <*> (req s <&> SecretKey)+    <*> (opt t <&> fmap SessionToken)+    <*> pure Nothing+  where+    req k = do+        m <- opt (Just k)+        maybe (throwM . MissingEnvError $ "Unable to read ENV variable: " <> k)+              return+              m++    opt Nothing  = return Nothing+    opt (Just k) = fmap BS8.pack <$> liftIO (lookupEnv (Text.unpack k))++-- | Loads the default @credentials@ INI file using the default profile name.+--+-- Throws 'MissingFileError' if 'credFile' is missing, or 'InvalidFileError'+-- if an error occurs during parsing.+--+-- /See:/ 'credProfile' and 'credFile'+fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m Auth+fromFile = credFile >>= fromFilePath credProfile++-- | Retrieve the access, secret and session token from the specified section+-- (profile) in a valid INI @credentials@ file.+--+-- Throws 'MissingFileError' if the specified file is missing, or 'InvalidFileError'+-- if an error occurs during parsing.+fromFilePath :: (Applicative m, MonadIO m, MonadCatch m)+             => Text+             -> FilePath+             -> m Auth+fromFilePath n f = do+    p <- liftIO (doesFileExist f)+    unless p $ throwM (MissingFileError f)+    i <- either (throwM . invalidErr) return =<< liftIO (INI.readIniFile f)+    fmap Auth $ AuthEnv+        <$> (req credAccessKey i    <&> AccessKey)+        <*> (req credSecretKey i    <&> SecretKey)+        <*> (opt credSessionToken i <&> fmap SessionToken)+        <*> pure Nothing+  where+    req k i =+        either (throwM . invalidErr)+               (return . Text.encodeUtf8)+               (INI.lookupValue n k i)++    opt k i = return $+        either (const Nothing)+               (Just . Text.encodeUtf8)+               (INI.lookupValue n k i)++    invalidErr :: String -> AuthError+    invalidErr = InvalidFileError . Text.pack++-- | Retrieve the default IAM Profile from the local EC2 instance-data.+--+-- The default IAM profile is determined by Amazon as the first profile found+-- in the response from:+-- @http://169.254.169.254/latest/meta-data/iam/security-credentials/@+--+-- Throws 'RetrievalError' if the HTTP call fails, or 'InvalidIAMError' if+-- the default IAM profile cannot be read.+fromProfile :: (MonadIO m, MonadCatch m) => Manager -> m Auth+fromProfile m = do+    ls <- try $ metadata m (IAM (SecurityCredentials Nothing))+    case BS8.lines `liftM` ls of+        Right (x:_) -> fromProfileName m (Text.decodeUtf8 x)+        Left  e     -> throwM (RetrievalError e)+        _           -> throwM $+            InvalidIAMError "Unable to get default IAM Profile from EC2 metadata"++-- | Lookup a specific IAM Profile by name from the local EC2 instance-data.+--+-- The resulting IONewRef wrapper + timer is designed so that multiple concurrent+-- accesses of 'AuthEnv' from the 'AWS' environment are not required to calculate+-- expiry and sequentially queue to update it.+--+-- The forked timer ensures a singular owner and pre-emptive refresh of the+-- temporary session credentials.+--+-- A weak reference is used to ensure that the forked thread will eventually+-- terminate when 'Auth' is no longer referenced.+--+-- Throws 'RetrievalError' if the HTTP call fails, or 'InvalidIAMError' if+-- the specified IAM profile cannot be read.+fromProfileName :: (MonadIO m, MonadCatch m) => Manager -> Text -> m Auth+fromProfileName m name = auth >>= start+  where+    auth :: (MonadIO m, MonadCatch m) => m AuthEnv+    auth = do+        bs <- try $ metadata m (IAM . SecurityCredentials $ Just name)+        case bs of+            Left  e -> throwM (RetrievalError e)+            Right x ->+                either (throwM . invalidErr)+                       return+                       (eitherDecode' (LBS8.fromStrict x))++    invalidErr = InvalidIAMError+        . mappend ("Error parsing IAM profile '" <> name <> "' ")+        . Text.pack++    start :: MonadIO m => AuthEnv -> m Auth+    start !a = liftIO $+        case _authExpiry a of+            Nothing -> return (Auth a)+            Just x  -> do+                r <- newIORef a+                p <- myThreadId+                s <- timer r p x+                return (Ref s r)++    timer :: IORef AuthEnv -> ThreadId -> UTCTime -> IO ThreadId+    timer !r !p !x = forkIO $ do+        s <- myThreadId+        w <- mkWeakIORef r (killThread s)+        loop w p x++    loop :: Weak (IORef AuthEnv) -> ThreadId -> UTCTime -> IO ()+    loop w !p !x = do+        diff x <$> getCurrentTime >>= threadDelay+        ea <- try auth+        case ea of+            Left   e -> throwTo p (RetrievalError e)+            Right !a -> do+                 mr <- deRefWeak w+                 case mr of+                     Nothing -> return ()+                     Just  r -> do+                         atomicWriteIORef r a+                         maybe (return ()) (loop w p) (_authExpiry a)++    diff !x !y = (* 1000000) $ if n > 0 then n else 1+      where+        !n = truncate (diffUTCTime x y) - 60
+ src/Network/AWS/Data.hs view
@@ -0,0 +1,32 @@+-- |+-- Module      : Network.AWS.Data+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Re-exports some of the underlying textual and byte serialisation mechanisms+-- for convenience.+--+-- Many of the AWS identifiers like S3's 'ObjectVersionId' or 'ETag',+-- as well as any nullary sum types such as 'Network.AWS.Region' have 'ToText'+-- and 'ToByteString' instances, making it convenient to use the type classes+-- to convert a value to its textual representation.+module Network.AWS.Data+    (+    -- * Text+      FromText     (..)+    , fromText+    , ToText       (..)++    -- * ByteString+    , ToByteString (..)++    -- * Log Messages+    , ToLog        (..)+    ) where++import           Network.AWS.Data.ByteString+import           Network.AWS.Data.Log+import           Network.AWS.Data.Text
src/Network/AWS/EC2/Metadata.hs view
@@ -1,55 +1,58 @@-{-# LANGUAGE BangPatterns      #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE BangPatterns       #-}+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE FlexibleContexts   #-}+{-# LANGUAGE LambdaCase         #-}+{-# LANGUAGE OverloadedStrings  #-} +-- | -- Module      : Network.AWS.EC2.Metadata--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0. -- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental+-- Stability   : provisional -- Portability : non-portable (GHC extensions)---- | Retrieve an EC2 instance's local metadata.+--+-- This module contains functions for retrieving various EC2 metadata from an+-- instance's local metadata endpoint using 'MonadIO' and not one of the AWS+-- specific transformers.+--+-- It is intended to be used when you need to make metadata calls prior to+-- initialisation of the 'Network.AWS.Env.Env'.+-- If you wish to retrieve instance metadata during normal operations+-- and are using either the 'Network.AWS.AWS' or 'Control.Monad.Trans.AWS.AWST'+-- monads, then prefer one of the 'Control.Monad.Trans.AWS.metadata' related+-- functions available there, as the functions in this module do not use the+-- underlying 'FreeT' 'Network.AWS.Free.Command' DSL. module Network.AWS.EC2.Metadata     (-    -- * Requests-    -- ** Running on EC2+    -- * EC2 Instance Check       isEC2 -    -- ** Dynamic-    , Dynamic   (..)+    -- * Retrieving Instance Data     , dynamic+    , metadata+    , userdata -    -- ** Metadata+    -- ** Path Constructors+    , Dynamic   (..)     , Metadata  (..)     , Mapping   (..)     , Info      (..)     , Interface (..)-    , metadata--    -- ** User data-    , userdata     ) where -import           Control.Applicative-import           Control.Exception import           Control.Monad-import           Control.Monad.Error        (catchError, throwError)+import           Control.Monad.Catch import           Control.Monad.IO.Class-import           Control.Monad.Trans.Except-import           Data.ByteString            (ByteString)-import qualified Data.ByteString.Char8      as BS-import qualified Data.ByteString.Lazy       as LBS-import           Data.Maybe+import qualified Data.ByteString.Char8  as BS8+import qualified Data.ByteString.Lazy   as LBS import           Data.Monoid-import           Data.Text                  (Text)-import qualified Data.Text                  as Text-import           Network.AWS.Data+import qualified Data.Text              as Text+import           Network.AWS.Prelude    hiding (request) import           Network.HTTP.Conduit +import           Prelude+ data Dynamic     = FWS     -- ^ Value showing whether the customer has enabled detailed one-minute@@ -63,13 +66,14 @@     -- ^ Used to verify the document's authenticity and content against the     -- signature.     | Signature+      deriving (Eq, Ord, Show, Typeable) -instance ToPath Dynamic where-    toPath x = case x of-       FWS       -> "fws/instance-monitoring"-       Document  -> "instance-identity/document"-       PKCS7     -> "instance-identity/pkcs7"-       Signature -> "instance-identity/signature"+instance ToText Dynamic where+    toText = \case+       FWS       -> "dynamic/fws/instance-monitoring"+       Document  -> "dynamic/instance-identity/document"+       PKCS7     -> "dynamic/instance-identity/pkcs7"+       Signature -> "dynamic/instance-identity/signature"  data Metadata     = AMIId@@ -138,33 +142,33 @@     -- ^ ID of the reservation.     | SecurityGroups     -- ^ The names of the security groups applied to the instance.-      deriving (Eq, Ord, Show)+      deriving (Eq, Ord, Show, Typeable) -instance ToPath Metadata where-    toPath x = case x of-        AMIId            -> "ami-id"-        AMILaunchIndex   -> "ami-launch-index"-        AMIManifestPath  -> "ami-manifest-path"-        AncestorAMIIds   -> "ancestor-ami-ids"-        BlockDevice m    -> "block-device-mapping/" <> toPath m-        Hostname         -> "hostname"-        IAM m            -> "iam/" <> toPath m-        InstanceAction   -> "instance-action"-        InstanceId       -> "instance-id"-        InstanceType     -> "instance-type"-        KernelId         -> "kernel-id"-        LocalHostname    -> "local-hostname"-        LocalIPV4        -> "local-ipv4"-        MAC              -> "mac"-        Network n m      -> "network/interfaces/macs/" <> n <> "/" <> toPath m-        AvailabilityZone -> "placement/availability-zone"-        ProductCodes     -> "product-codes"-        PublicHostname   -> "public-hostname"-        PublicIPV4       -> "public-ipv4"-        OpenSSHKey       -> "public-keys/0/openssh-key"-        RAMDiskId        -> "ramdisk-id"-        ReservationId    -> "reservation-id"-        SecurityGroups   -> "security-groups"+instance ToText Metadata where+    toText = \case+        AMIId            -> "meta-data/ami-id"+        AMILaunchIndex   -> "meta-data/ami-launch-index"+        AMIManifestPath  -> "meta-data/ami-manifest-path"+        AncestorAMIIds   -> "meta-data/ancestor-ami-ids"+        BlockDevice m    -> "meta-data/block-device-mapping/" <> toText m+        Hostname         -> "meta-data/hostname"+        IAM m            -> "meta-data/iam/" <> toText m+        InstanceAction   -> "meta-data/instance-action"+        InstanceId       -> "meta-data/instance-id"+        InstanceType     -> "meta-data/instance-type"+        KernelId         -> "meta-data/kernel-id"+        LocalHostname    -> "meta-data/local-hostname"+        LocalIPV4        -> "meta-data/local-ipv4"+        MAC              -> "meta-data/mac"+        Network n m      -> "meta-data/network/interfaces/macs/" <> toText n <> "/" <> toText m+        AvailabilityZone -> "meta-data/placement/availability-zone"+        ProductCodes     -> "meta-data/product-codes"+        PublicHostname   -> "meta-data/public-hostname"+        PublicIPV4       -> "meta-data/public-ipv4"+        OpenSSHKey       -> "meta-data/public-keys/0/openssh-key"+        RAMDiskId        -> "meta-data/ramdisk-id"+        ReservationId    -> "meta-data/reservation-id"+        SecurityGroups   -> "meta-data/security-groups"  data Mapping     = AMI@@ -182,10 +186,10 @@     -- is associated with the given instance.     | Swap     -- ^ The virtual devices associated with swap. Not always present.-      deriving (Eq, Ord, Show)+      deriving (Eq, Ord, Show, Typeable) -instance ToPath Mapping where-    toPath x = case x of+instance ToText Mapping where+    toText = \case         AMI         -> "ami"         EBS       n -> "ebs"       <> toText n         Ephemeral n -> "ephemeral" <> toText n@@ -237,12 +241,12 @@     | IVPCIPV4_CIDRBlock     -- ^ The CIDR block of the VPC in which the interface resides. Returned only     -- for instances launched into a VPC.-      deriving (Eq, Ord, Show)+      deriving (Eq, Ord, Show, Typeable) -instance ToPath Interface where-    toPath x = case x of+instance ToText Interface where+    toText = \case         IDeviceNumber         -> "device-number"-        IIPV4Associations ip  -> "ipv4-associations/" <> ip+        IIPV4Associations ip  -> "ipv4-associations/" <> toText ip         ILocalHostname        -> "local-hostname"         ILocalIPV4s           -> "local-ipv4s"         IMAC                  -> "mac"@@ -257,7 +261,7 @@         IVPCIPV4_CIDRBlock    -> "vpc-ipv4-cidr-block"  data Info-    = Info+    = Info'     -- ^ Returns information about the last time the instance profile was updated,     -- including the instance's LastUpdated date, InstanceProfileArn,     -- and InstanceProfileId.@@ -266,15 +270,19 @@     -- Returns the temporary security credentials.     --     -- See: 'Auth' for JSON deserialisation.-      deriving (Eq, Ord, Show)+      deriving (Eq, Ord, Show, Typeable) -instance ToPath Info where-    toPath x = case x of-        Info                  -> "info"-        SecurityCredentials r -> "security-credentials/" <> fromMaybe "" r+instance ToText Info where+    toText = \case+        Info'                 -> "info"+        SecurityCredentials r -> "security-credentials/" <> maybe mempty toText r --- | Test whether the host is running on EC2 by requesting the instance-data.-isEC2 :: Manager -> IO Bool+latest :: Text+latest = "http://169.254.169.254/latest/"++-- | Test whether the underlying host is running on EC2 by+-- making an HTTP request to @http://instance-data/latest@.+isEC2 :: MonadIO m => Manager -> m Bool isEC2 m = liftIO (req `catch` err)   where     req = do@@ -284,43 +292,37 @@     err :: HttpException -> IO Bool     err = const (return False) -dynamic :: MonadIO m-        => Manager-        -> Dynamic-        -> ExceptT HttpException m ByteString-dynamic m = get m . mappend "http://169.254.169.254/latest/dynamic/" . toPath+-- | Retrieve the specified 'Dynamic' data.+--+-- Throws 'HttpException' if HTTP communication fails.+dynamic :: (MonadIO m, MonadThrow m) => Manager -> Dynamic -> m ByteString+dynamic m = get m . mappend latest . toText -metadata :: MonadIO m-         => Manager-         -> Metadata-         -> ExceptT HttpException m ByteString-metadata m = get m . mappend "http://169.254.169.254/latest/meta-data/" . toPath+-- | Retrieve the specified 'Metadata'.+--+-- Throws 'HttpException' if HTTP communication fails.+metadata :: (MonadIO m, MonadThrow m) => Manager -> Metadata -> m ByteString+metadata m = get m . mappend latest . toText -userdata :: MonadIO m-         => Manager-         -> ExceptT HttpException m (Maybe ByteString)-userdata m = Just-    `liftM` get m "http://169.254.169.254/latest/user-data"-    `catchError` err-  where-    err (StatusCodeException s _ _) | fromEnum s == 404-          = return Nothing-    err e = throwError e+-- | Retrieve the user data. Returns 'Nothing' if no user data is assigned+-- to the instance.+--+-- Throws 'HttpException' if HTTP communication fails.+userdata :: (MonadIO m, MonadCatch m) => Manager -> m (Maybe ByteString)+userdata m = do+    x <- try $ get m (latest <> "user-data")+    case x of+        Right b                 -> return (Just b)+        Left (StatusCodeException s _ _)+            | fromEnum s == 404 -> return Nothing+        Left e                  -> throwM e -get :: MonadIO m-    => Manager-    -> Text-    -> ExceptT HttpException m ByteString-get m url = ExceptT . liftIO $ req `catch` err+get :: (MonadIO m, MonadThrow m) => Manager -> Text -> m ByteString+get m url = liftIO (strip `liftM` request m url)   where-    req = Right . strip <$> request m url-     strip bs-        | BS.isSuffixOf "\n" bs = BS.init bs-        | otherwise             = bs--    err :: HttpException -> IO (Either HttpException a)-    err = return . Left+        | BS8.isSuffixOf "\n" bs = BS8.init bs+        | otherwise              = bs  request :: Manager -> Text -> IO ByteString request m url = do
+ src/Network/AWS/Env.hs view
@@ -0,0 +1,168 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RankNTypes        #-}+{-# LANGUAGE RecordWildCards   #-}++-- |+-- Module      : Network.AWS.Env+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Environment and AWS specific configuration for the+-- 'Network.AWS.AWS' and 'Control.Monad.Trans.AWS.AWST' monads.+module Network.AWS.Env+    (+    -- * Creating the Environment+      newEnv+    , newEnvWith++    , Env    (..)+    , HasEnv (..)++    -- * Scoped Actions+    , within+    , once+    , timeout+    ) where++import           Control.Applicative+import           Control.Lens+import           Control.Monad+import           Control.Monad.Catch+import           Control.Monad.IO.Class+import           Control.Monad.Reader+import           Control.Retry+import           Data.IORef+import           Data.Monoid+import           Network.AWS.Auth+import           Network.AWS.Internal.Logger+import           Network.AWS.Types+import           Network.HTTP.Conduit++import           Prelude++-- | The environment containing the parameters required to make AWS requests.+data Env = Env+    { _envRegion      :: !Region+    , _envLogger      :: !Logger+    , _envRetryCheck  :: !(Int -> HttpException -> IO Bool)+    , _envRetryPolicy :: !(Maybe RetryPolicy)+    , _envTimeout     :: !(Maybe Seconds)+    , _envManager     :: !Manager+    , _envEC2         :: !(IORef (Maybe Bool))+    , _envAuth        :: !Auth+    }++-- Note: The strictness annotations aobe are applied to ensure+-- total field initialisation.++class HasEnv a where+    environment    :: Lens' a Env+    {-# MINIMAL environment #-}++    -- | The current region.+    envRegion      :: Lens' a Region++    -- | The function used to output log messages.+    envLogger      :: Lens' a Logger++    -- | The function used to determine if an 'HttpException' should be retried.+    envRetryCheck  :: Lens' a (Int -> HttpException -> IO Bool)++    -- | The 'RetryPolicy' used to determine backoff\/on and retry delay\/growth.+    envRetryPolicy :: Lens' a (Maybe RetryPolicy)++    -- | A HTTP response timeout override to apply. This defaults to 'Nothing',+    -- and the timeout selection is outlined below.+    --+    -- Timeouts are chosen by considering:+    --+    -- * This 'envTimeout', if set.+    --+    -- * The related 'Service' timeout for the sent request if set. (Usually 70s)+    --+    -- * The 'envManager' timeout if set.+    --+    -- * The default 'ClientRequest' timeout. (Approximately 30s)+    --+    envTimeout     :: Lens' a (Maybe Seconds)++    -- | The 'Manager' used to create and manage open HTTP connections.+    envManager     :: Lens' a Manager++    -- | The credentials used to sign requests for authentication with AWS.+    envAuth        :: Lens' a Auth++    -- | A memoised predicate for whether the underlying host is an EC2 instance.+    envEC2         :: Getter a (IORef (Maybe Bool))++    envRegion      = environment . lens _envRegion      (\s a -> s { _envRegion      = a })+    envLogger      = environment . lens _envLogger      (\s a -> s { _envLogger      = a })+    envRetryCheck  = environment . lens _envRetryCheck  (\s a -> s { _envRetryCheck  = a })+    envRetryPolicy = environment . lens _envRetryPolicy (\s a -> s { _envRetryPolicy = a })+    envTimeout     = environment . lens _envTimeout     (\s a -> s { _envTimeout     = a })+    envManager     = environment . lens _envManager     (\s a -> s { _envManager     = a })+    envAuth        = environment . lens _envAuth        (\s a -> s { _envAuth        = a })+    envEC2         = environment . to _envEC2++instance HasEnv Env where+    environment = id++instance ToLog Env where+    build Env{..} = b <> "\n" <> build _envAuth+      where+        b = buildLines+            [ "[Amazonka Env] {"+            , "  region      = " <> build _envRegion+            , "  retry (n=0) = " <> build (join $ ($ 0) . getRetryPolicy <$> _envRetryPolicy)+            , "  timeout     = " <> build _envTimeout+            , "}"+            ]++-- | Scope an action within the specific 'Region'.+within :: (MonadReader r m, HasEnv r) => Region -> m a -> m a+within r = local (envRegion .~ r)++-- | Scope an action such that any retry logic for the 'Service' is+-- ignored and any requests will at most be sent once.+once :: (MonadReader r m, HasEnv r) => m a -> m a+once = local $ \e -> e+    & envRetryPolicy ?~ limitRetries 0+    & envRetryCheck  .~ (\_ _ -> return False)++-- | Scope an action such that any HTTP response will use this timeout value.+timeout :: (MonadReader r m, HasEnv r) => Seconds -> m a -> m a+timeout s = local (envTimeout ?~ s)++-- | Creates a new environment with a new 'Manager' without debug logging+-- and uses 'getAuth' to expand/discover the supplied 'Credentials'.+-- Lenses from 'HasEnv' can be used to further configure the resulting 'Env'.+--+-- Throws 'AuthError' when environment variables or IAM profiles cannot be read.+--+-- /See:/ 'newEnvWith'.+newEnv :: (Applicative m, MonadIO m, MonadCatch m)+       => Region      -- ^ Initial region to operate in.+       -> Credentials -- ^ Credential discovery mechanism.+       -> m Env+newEnv r c = liftIO (newManager conduitManagerSettings)+    >>= newEnvWith r c Nothing++-- | /See:/ 'newEnv'+--+-- Throws 'AuthError' when environment variables or IAM profiles cannot be read.+newEnvWith :: (Applicative m, MonadIO m, MonadCatch m)+           => Region      -- ^ Initial region to operate in.+           -> Credentials -- ^ Credential discovery mechanism.+           -> Maybe Bool  -- ^ Preload memoisation for the underlying EC2 instance check.+           -> Manager+           -> m Env+newEnvWith r c p m = Env r logger check Nothing Nothing m+    <$> liftIO (newIORef p)+    <*> getAuth m c+  where+    logger _ _ = return ()+    -- FIXME: verify the usage of check.+    check  _ _ = return True
+ src/Network/AWS/Free.hs view
@@ -0,0 +1,196 @@+{-# LANGUAGE BangPatterns      #-}+{-# LANGUAGE CPP               #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE GADTs             #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE RankNTypes        #-}++-- |+-- Module      : Network.AWS.Free+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Defines the core AST, logic and interpreters for AWS behaviour.+module Network.AWS.Free where++import           Control.Applicative+import           Control.Monad.Reader+import           Control.Monad.Trans.Free.Church+import           Data.Conduit                    (Source, yield)+import           Network.AWS.EC2.Metadata        (Dynamic, Metadata)+import           Network.AWS.Pager+import           Network.AWS.Prelude+import           Network.AWS.Request             (requestURL)+import           Network.AWS.Types+import           Network.AWS.Waiter++#if MIN_VERSION_free(4,12,0)+#else+import           Control.Monad.Catch+import           Control.Monad.Trans.Free        (FreeT (..))+#endif++import           Prelude++data Command r where+    CheckF ::             (Bool             -> r) -> Command r+    DynF   :: Dynamic  -> (ByteString       -> r) -> Command r+    MetaF  :: Metadata -> (ByteString       -> r) -> Command r+    UserF  ::             (Maybe ByteString -> r) -> Command r++    SignF  :: (AWSPresigner (Sg s), AWSRequest a)+           => Service s+           -> UTCTime+           -> Seconds+           -> a+           -> (ClientRequest -> r)+           -> Command r++    SendF  :: (AWSSigner (Sg s), AWSRequest a)+           => Service s+           -> a+           -> (Rs a -> r)+           -> Command r++    AwaitF :: (AWSSigner (Sg s), AWSRequest a)+           => Service s+           -> Wait a+           -> a+           -> (Rs a -> r)+           -> Command r++instance Functor Command where+    fmap f = \case+        CheckF         k -> CheckF         (fmap f k)+        DynF         x k -> DynF         x (fmap f k)+        MetaF        x k -> MetaF        x (fmap f k)+        UserF          k -> UserF          (fmap f k)+        SignF  s t e x k -> SignF  s t e x (fmap f k)+        SendF  s     x k -> SendF  s     x (fmap f k)+        AwaitF s w   x k -> AwaitF s w   x (fmap f k)++#if MIN_VERSION_free(4,12,0)+#else+instance MonadThrow m => MonadThrow (FreeT Command m) where+    throwM = lift . throwM++instance MonadCatch m => MonadCatch (FreeT Command m) where+    catch (FreeT m) f = FreeT $+        liftM (fmap (`catch` f)) m `catch` (runFreeT . f)+#endif++-- | Send a request, returning the associated response if successful.+--+-- /See:/ 'sendWith'+send :: (MonadFree Command m, AWSRequest a)+     => a+     -> m (Rs a)+send = sendWith id++-- | A variant of 'send' that allows modifying the default 'Service' definition+-- used to configure the request.+sendWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a)+         => (Service (Sv a) -> Service s) -- ^ Modify the default service configuration.+         -> a                             -- ^ Request.+         -> m (Rs a)+sendWith f x = liftF (SendF (f (serviceOf x)) x id)++-- | Repeatedly send a request, automatically setting markers and+-- paginating over multiple responses while available.+--+-- /See:/ 'paginateWith'+paginate :: (MonadFree Command m, AWSPager a)+         => a+         -> Source m (Rs a)+paginate = paginateWith id++-- | A variant of 'paginate' that allows modifying the default 'Service' definition+-- used to configure the request.+paginateWith :: (MonadFree Command m, AWSSigner (Sg s), AWSPager a)+             => (Service (Sv a) -> Service s) -- ^ Modify the default service configuration.+             -> a                             -- ^ Initial request.+             -> Source m (Rs a)+paginateWith f rq = go rq+  where+    go !x = do+        !y <- lift $ liftF (SendF s x id)+        yield y+        maybe (pure ())+              go+              (page x y)++    !s = f (serviceOf rq)++-- | Poll the API with the supplied request until a specific 'Wait' condition+-- is fulfilled.+--+-- /See:/ 'awaitWith'+await :: (MonadFree Command m, AWSRequest a)+      => Wait a+      -> a+      -> m (Rs a)+await = awaitWith id++-- | A variant of 'await' that allows modifying the default 'Service' definition+-- used to configure the request.+awaitWith :: (MonadFree Command m, AWSSigner (Sg s), AWSRequest a)+          => (Service (Sv a) -> Service s) -- ^ Modify the default service configuration.+          -> Wait a                        -- ^ Polling, error and acceptance criteria.+          -> a                             -- ^ Request to poll with.+          -> m (Rs a)+awaitWith f w x = liftF (AwaitF (f (serviceOf x)) w x id)++-- | Presign an URL that is valid from the specified time until the+-- number of seconds expiry has elapsed.+--+-- /See:/ 'presign', 'presignWith'+presignURL :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a)+           => UTCTime     -- ^ Signing time.+           -> Seconds     -- ^ Expiry time.+           -> a           -- ^ Request to presign.+           -> m ByteString+presignURL ts ex = liftM requestURL . presign ts ex++-- | Presign an HTTP request that is valid from the specified time until the+-- number of seconds expiry has elapsed.+--+-- /See:/ 'presignWith'+presign :: (MonadFree Command m, AWSPresigner (Sg (Sv a)), AWSRequest a)+        => UTCTime     -- ^ Signing time.+        -> Seconds     -- ^ Expiry time.+        -> a           -- ^ Request to presign.+        -> m ClientRequest+presign = presignWith id++-- | A variant of 'presign' that allows specifying the 'Service' definition+-- used to configure the request.+presignWith :: (MonadFree Command m, AWSPresigner (Sg s), AWSRequest a)+            => (Service (Sv a) -> Service s) -- ^ Function to modify the service configuration.+            -> UTCTime                       -- ^ Signing time.+            -> Seconds                       -- ^ Expiry time.+            -> a                             -- ^ Request to presign.+            -> m ClientRequest+presignWith f ts ex x = liftF (SignF (f (serviceOf x)) ts ex x id)++-- | Test whether the underlying host is running on EC2.+-- For 'IO' based interpretations of 'FreeT' 'Command', this is memoised and+-- any external check occurs for the first call only.+isEC2 :: MonadFree Command m => m Bool+isEC2 = liftF (CheckF id)++-- | Retrieve the specified 'Dynamic' data.+dynamic :: MonadFree Command m => Dynamic -> m ByteString+dynamic d = liftF (DynF d id)++-- | Retrieve the specified 'Metadata'.+metadata :: MonadFree Command m => Metadata -> m ByteString+metadata m = liftF (MetaF m id)++-- | Retrieve the user data. Returns 'Nothing' if no user data is assigned+-- to the instance.+userdata :: MonadFree Command m => m (Maybe ByteString)+userdata = liftF (UserF id)
− src/Network/AWS/Internal/Auth.hs
@@ -1,194 +0,0 @@-{-# LANGUAGE BangPatterns      #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE ViewPatterns      #-}---- Module      : Network.AWS.Internal.Auth--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental--- Portability : non-portable (GHC extensions)---- | Explicitly specify your Amazon AWS security credentials, or retrieve them--- from the underlying OS.-module Network.AWS.Internal.Auth where--import           Control.Applicative-import           Control.Concurrent-import           Control.Monad-import           Control.Monad.Error        (catchError, throwError)-import           Control.Monad.IO.Class-import           Control.Monad.Trans.Except-import qualified Data.ByteString.Char8      as BS-import qualified Data.ByteString.Lazy.Char8 as LBS-import           Data.IORef-import           Data.Monoid-import           Data.Text                  (Text)-import qualified Data.Text                  as Text-import qualified Data.Text.Encoding         as Text-import           Data.Time                  (diffUTCTime, getCurrentTime)-import           Network.AWS.Data-import           Network.AWS.EC2.Metadata-import           Network.AWS.Types-import           Network.HTTP.Conduit-import           System.Environment-import           System.Mem.Weak---- | Default access key environment variable.-accessKey :: Text -- ^ 'AWS_ACCESS_KEY'-accessKey = "AWS_ACCESS_KEY"---- | Default secret key environment variable.-secretKey :: Text -- ^ 'AWS_SECRET_KEY'-secretKey = "AWS_SECRET_KEY"---- | Explicit access and secret keys.-fromKeys :: AccessKey -> SecretKey -> Auth-fromKeys a s = Auth (AuthEnv a s Nothing Nothing)---- | A session containing the access key, secret key, and a security token.-fromSession :: AccessKey -> SecretKey -> SecurityToken -> Auth-fromSession a s t = Auth (AuthEnv a s (Just t) Nothing)---- | Determines how authentication information is retrieved.-data Credentials-    = FromKeys AccessKey SecretKey-      -- ^ Explicit access and secret keys.-      -- /Note:/ you can achieve the same result purely using 'fromKeys' without-      -- having to use the impure 'getAuth'.-    | FromSession AccessKey SecretKey SecurityToken-      -- ^ A session containing the access key, secret key, and a security token.-      -- /Note:/ you can achieve the same result purely using 'fromSession'-      -- without having to use the impure 'getAuth'.-    | FromProfile Text-      -- ^ An IAM Profile name to lookup from the local EC2 instance-data.-    | FromEnv Text Text-      -- ^ Environment variables to lookup for the access and secret keys.-    | Discover-      -- ^ Attempt to read the default access and secret keys from the environment,-      -- falling back to the first available IAM profile if they are not set.-      ---      -- /Note:/ This attempts to resolve <http://instance-data> rather than directly-      -- retrieving <http://169.254.169.254> for IAM profile information to ensure-      -- the dns lookup terminates promptly if not running on EC2.-      deriving (Eq)--instance ToBuilder Credentials where-    build = \case-        FromKeys    a _   -> "FromKeys "    <> build a <> " ****"-        FromSession a _ _ -> "FromSession " <> build a <> " **** ****"-        FromProfile n     -> "FromProfile " <> build n-        FromEnv     a s   -> "FromEnv "     <> build a <> " " <> build s-        Discover          -> "Discover"--instance Show Credentials where-    show = LBS.unpack . buildBS---- | Retrieve authentication information using the specified 'Credentials' style.-getAuth :: (Functor m, MonadIO m)-        => Manager-        -> Credentials-        -> ExceptT String m Auth-getAuth m = \case-    FromKeys    a s   -> return (fromKeys a s)-    FromSession a s t -> return (fromSession a s t)-    FromProfile n     -> show `withExceptT` fromProfileName m n-    FromEnv     a s   -> fromEnvVars a s-    Discover          -> fromEnv `catchError` const (iam `catchError` const err)-      where-        iam = show `withExceptT` fromProfile m-        err = throwError "Unable to read environment variables or IAM profile."---- | Retrieve access and secret keys from the default environment variables.------ /See:/ 'accessKey' and 'secretKey'-fromEnv :: (Functor m, MonadIO m) => ExceptT String m Auth-fromEnv = fromEnvVars accessKey secretKey---- | Retrieve access and secret keys from specific environment variables.-fromEnvVars :: (Functor m, MonadIO m) => Text -> Text -> ExceptT String m Auth-fromEnvVars a s = fmap Auth $ AuthEnv-    <$> (AccessKey <$> key a)-    <*> (SecretKey <$> key s)-    <*> pure Nothing-    <*> pure Nothing-  where-    key (Text.unpack -> k) = ExceptT $ do-        m <- liftIO (lookupEnv k)-        return $-            maybe (Left $ "Unable to read ENV variable: " ++ k)-                  (Right . BS.pack)-                  m---- | Retrieve the default IAM Profile from the local EC2 instance-data.------ This determined by Amazon as the first IAM profile found in the response from:--- @http://169.254.169.254/latest/meta-data/iam/security-credentials/@-fromProfile :: MonadIO m => Manager -> ExceptT HttpException m Auth-fromProfile m = do-    !ls <- BS.lines `liftM` metadata m (IAM $ SecurityCredentials Nothing)-    case ls of-        (x:_) -> fromProfileName m (Text.decodeUtf8 x)-        _     -> throwError $-           HttpParserException "Unable to get default IAM Profile from EC2 metadata"---- | Lookup a specific IAM Profile by name from the local EC2 instance-data.------ The resulting IONewRef wrapper + timer is designed so that multiple concurrent--- accesses of 'AuthEnv' from the 'AWS' environment are not required to calculate--- expiry and sequentially queue to update it.------ The forked timer ensures a singular owner and pre-emptive refresh of the--- temporary session credentials.------ A weak reference is used to ensure that the forked thread will eventually--- terminate when 'Auth' is no longer referenced.-fromProfileName :: MonadIO m-                => Manager-                -> Text-                -> ExceptT HttpException m Auth-fromProfileName m name = auth >>= start-  where-    auth :: MonadIO m => ExceptT HttpException m AuthEnv-    auth = do-        !lbs <- LBS.fromStrict `liftM` metadata m-            (IAM . SecurityCredentials $ Just name)-        either (throwError . HttpParserException)-               return-               (eitherDecode' lbs)--    start !a = ExceptT . liftM Right . liftIO $-        case _authExpiry a of-            Nothing -> return (Auth a)-            Just x  -> do-                r <- newIORef a-                p <- myThreadId-                s <- timer r p x-                return (Ref s r)--    timer r p x = forkIO $ do-        s <- myThreadId-        w <- mkWeakIORef r (killThread s)-        loop w p x--    loop w p x = do-        diff x <$> getCurrentTime >>= threadDelay-        ea <- runExceptT auth-        case ea of-            Left   e -> throwTo p e-            Right !a -> do-                 mr <- deRefWeak w-                 case mr of-                     Nothing -> return ()-                     Just  r -> do-                         atomicWriteIORef r a-                         maybe (return ()) (loop w p) (_authExpiry a)--    diff x y = (* 1000000) $-        let n = truncate (diffUTCTime x y) - 60-         in if n > 0 then n else 1
src/Network/AWS/Internal/Body.hs view
@@ -1,29 +1,29 @@+-- | -- Module      : Network.AWS.Internal.Body--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0. -- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental+-- Stability   : provisional -- Portability : non-portable (GHC extensions)-+-- module Network.AWS.Internal.Body where  import           Control.Applicative+import           Control.Monad import           Control.Monad.IO.Class+import           Control.Monad.Morph import           Control.Monad.Trans.Resource-import           Crypto.Hash-import qualified Crypto.Hash.Conduit          as Conduit-import           Data.ByteString              (ByteString) import           Data.Conduit import qualified Data.Conduit.Binary          as Conduit import           Data.Int-import           Network.AWS.Data+import           Network.AWS.Prelude+import qualified Network.HTTP.Client          as Client import           Network.HTTP.Conduit import           System.IO --- | Unsafely construct a 'RqBody' from a source, manually specifying the+import           Prelude++-- | Construct a 'RqBody' from a source, manually specifying the -- SHA256 hash and file size. sourceBody :: Digest SHA256            -> Int64@@ -31,24 +31,47 @@            -> RqBody sourceBody h n = RqBody h . requestBodySource n --- | Unsafely construct a 'RqBody' from a 'Handle', manually specifying the+-- | Construct a 'RqBody' from a 'Handle', manually specifying the -- SHA256 hash and file size. sourceHandle :: Digest SHA256 -> Int64 -> Handle -> RqBody sourceHandle h n = sourceBody h n . Conduit.sourceHandle --- | Unsafely construct a 'RqBody' from a 'FilePath', manually specifying the+-- | Construct a 'RqBody' from a 'FilePath', manually specifying the -- SHA256 hash and file size. sourceFile :: Digest SHA256 -> Int64 -> FilePath -> RqBody sourceFile h n = sourceBody h n . Conduit.sourceFile --- | Safely construct a 'RqBody' from a 'FilePath', calculating the SHA256 hash+-- | Construct a 'RqBody' from a 'FilePath', calculating the SHA256 hash -- and file size. ----- /Note:/ While this function will perform in constant space, it will read the+-- /Note:/ While this function will perform in constant space, it will enumerate the -- entirety of the file contents _twice_. Firstly to calculate the SHA256 and -- lastly to stream the contents to the socket during sending. sourceFileIO :: MonadIO m => FilePath -> m RqBody-sourceFileIO f = liftIO $ sourceFile-    <$> runResourceT (Conduit.sourceFile f $$ Conduit.sinkHash)-    <*> fmap fromIntegral (withBinaryFile f ReadMode hFileSize)-    <*> pure f+sourceFileIO f = liftIO $+    RqBody <$> runResourceT (Conduit.sourceFile f $$ sinkSHA256)+           <*> Client.streamFile f++-- | Convenience function for obtaining the size of a file.+getFileSize :: MonadIO m => FilePath -> m Int64+getFileSize f = liftIO $ fromIntegral `liftM` withBinaryFile f ReadMode hFileSize++-- | Connect a 'Sink' to a reponse body.+sinkBody :: MonadResource m => RsBody -> Sink ByteString m a -> m a+sinkBody (RsBody src) sink = hoist liftResourceT src $$+- sink++sinkMD5 :: Monad m => Consumer ByteString m (Digest MD5)+sinkMD5 = sinkHash++sinkSHA256 :: Monad m => Consumer ByteString m (Digest SHA256)+sinkSHA256 = sinkHash++-- | A cryptonite compatible incremental hash sink.+sinkHash :: (Monad m, HashAlgorithm a) => Consumer ByteString m (Digest a)+sinkHash = sink hashInit+  where+    sink ctx = do+        b <- await+        case b of+            Nothing -> return $! hashFinalize ctx+            Just bs -> sink $! hashUpdate ctx bs
− src/Network/AWS/Internal/Env.hs
@@ -1,80 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RankNTypes        #-}-{-# LANGUAGE RecordWildCards   #-}---- Module      : Network.AWS.Internal.Env--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental--- Portability : non-portable (GHC extensions)--module Network.AWS.Internal.Env where--import           Control.Lens-import           Control.Retry-import qualified Data.ByteString.Lazy.Char8 as LBS-import           Data.List                  (intersperse)-import           Data.Monoid-import           Network.AWS.Data           (ToBuilder(..), buildBS)-import           Network.AWS.Types-import           Network.HTTP.Conduit---- | The environment containing the parameters required to make AWS requests.-data Env = Env-    { _envRegion      :: !Region-    , _envLogger      :: Logger-    , _envRetryCheck  :: Int -> HttpException -> IO Bool-    , _envRetryPolicy :: Maybe RetryPolicy-    , _envManager     :: Manager-    , _envAuth        :: Auth-    }---- | The current region.-envRegion :: Lens' Env Region-envRegion = lens _envRegion (\s a -> s { _envRegion = a })-{-# INLINE envRegion #-}---- | The function used to output log messages.-envLogger :: Lens' Env Logger-envLogger = lens _envLogger (\s a -> s { _envLogger = a })-{-# INLINE envLogger #-}---- | The function used to determine if an 'HttpException' should be retried.-envRetryCheck :: Lens' Env (Int -> HttpException -> IO Bool)-envRetryCheck = lens _envRetryCheck (\s a -> s { _envRetryCheck = a })-{-# INLINE envRetryCheck #-}---- | The 'RetryPolicy' used to determine backoff/on and retry delay/growth.-envRetryPolicy :: Lens' Env (Maybe RetryPolicy)-envRetryPolicy = lens _envRetryPolicy (\s a -> s { _envRetryPolicy = a })-{-# INLINE envRetryPolicy #-}---- | The 'Manager' used to create and manage open HTTP connections.-envManager :: Lens' Env Manager-envManager = lens _envManager (\s a -> s { _envManager = a })-{-# INLINE envManager #-}---- | The credentials used to sign requests for authentication with AWS.-envAuth :: Lens' Env Auth-envAuth = lens _envAuth (\s a -> s { _envAuth = a })-{-# INLINE envAuth #-}--instance ToBuilder Env where-    build Env{..} = mconcat $ intersperse "\n"-        [ "[Amazonka Env] {"-        , "  region      = " <> build _envRegion-        , "  retry (n=0) = " <> maybe "Nothing" policy _envRetryPolicy-        , build . indent $ buildBS _envAuth-        , "}"-        ]-      where-        policy (RetryPolicy f) = "Just " <> build (f 0)--        indent = mappend "  "-               . LBS.concat-               . intersperse "\n  "-               . LBS.lines
+ src/Network/AWS/Internal/HTTP.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE BangPatterns      #-}+{-# LANGUAGE FlexibleContexts  #-}+{-# LANGUAGE LambdaCase        #-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards   #-}+{-# LANGUAGE TypeFamilies      #-}++-- |+-- Module      : Network.AWS.Internal.HTTP+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+module Network.AWS.Internal.HTTP+    ( perform+    , retrier+    , waiter+    ) where++import           Control.Monad+import           Control.Monad.Catch+import           Control.Monad.IO.Class+import           Control.Monad.Trans.Resource+import           Control.Retry+import           Data.List                    (intersperse)+import           Data.Monoid+import           Data.Time+import           Network.AWS.Env+import           Network.AWS.Internal.Logger+import           Network.AWS.Prelude+import           Network.AWS.Waiter+import           Network.HTTP.Conduit         hiding (Request, Response)++import           Prelude++perform :: (MonadCatch m, MonadResource m, AWSSigner (Sg s), AWSRequest a)+        => Env+        -> Service s+        -> Request a+        -> m (Either Error (Response a))+perform e@Env{..} svc x = catches go handlers+  where+    go = do+        t          <- liftIO getCurrentTime+        Signed m s <- withAuth _envAuth $ \a ->+            return (signed a _envRegion t svc x)++        let rq = s { responseTimeout = timeoutFor e svc }++        logTrace _envLogger m  -- trace:Signing:Meta+        logDebug _envLogger rq -- debug:ClientRequest++        rs         <- liftResourceT (http rq _envManager)++        logDebug _envLogger rs -- debug:ClientResponse++        Right <$>+            response _envLogger svc x rs++    handlers =+        [ Handler $ return . Left+        , Handler $ \er -> do+            logError _envLogger er+            return (Left (TransportError er))+        ]++retrier :: MonadIO m+        => Env+        -> Service s+        -> Request a+        -> m (Either Error (Response a))+        -> m (Either Error (Response a))+retrier Env{..} Service{..} rq =+    retrying (fromMaybe policy _envRetryPolicy) check+  where+    policy = limitRetries _retryAttempts+       <> RetryPolicy (const $ listToMaybe [0 | not stream])+       <> RetryPolicy delay+      where+        !stream = bodyStream (_rqBody rq)++        delay n+            | n > 0     = Just $ truncate (grow * 1000000)+            | otherwise = Nothing+          where+            grow = _retryBase * (fromIntegral _retryGrowth ^^ (n - 1))++    check n = \case+        Left (TransportError e) -> do+            p <- liftIO (_envRetryCheck n e)+            when p (msg "http_error" n) >> return p++        Left e | Just x <- e ^? _ServiceError . to _retryCheck . _Just ->+            msg x n >> return True++        _ -> return False++    msg x n = logDebug _envLogger+        . mconcat+        . intersperse " "+        $ [ "[Retry " <> build x <> "]"+          , " after "+          , build (n + 1)+          , "attempts."+          ]++    Exponential{..} = _svcRetry++waiter :: MonadIO m+       => Env+       -> Wait a+       -> Request a+       -> m (Either Error (Response a))+       -> m (Either Error (Response a))+waiter Env{..} w@Wait{..} rq = retrying policy check+  where+    policy = limitRetries _waitAttempts+          <> constantDelay (microseconds _waitDelay)++    check n rs = do+        let a = fromMaybe AcceptRetry (accept w rq rs)+        msg n a >> return (retry a)++    retry AcceptSuccess = False+    retry AcceptFailure = False+    retry AcceptRetry   = True++    msg n a = logDebug _envLogger+        . mconcat+        . intersperse " "+        $ [ "[Await " <> build _waitName <> "]"+          , build a+          , " after "+          , build (n + 1)+          , "attempts."+          ]++-- | Returns the possible HTTP response timeout value in microseconds+-- given the timeout configuration sources.+timeoutFor :: HasEnv a => a -> Service s -> Maybe Int+timeoutFor e s = microseconds <$> (e ^. envTimeout <|> _svcTimeout s)
− src/Network/AWS/Internal/Log.hs
@@ -1,46 +0,0 @@-{-# LANGUAGE OverloadedStrings #-}---- Module      : Network.AWS.Internal.Log--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental--- Portability : non-portable (GHC extensions)---- | Types and functions for optional logging machinery used during the--- request, response, and signing life-cycles.-module Network.AWS.Internal.Log where--import Control.Monad-import Control.Monad.IO.Class-import Data.ByteString.Builder-import Data.Monoid-import Network.AWS.Data-import Network.AWS.Types-import System.IO---- | This is a primitive logger which can be used to log messages to a 'Handle'.--- A more sophisticated logging library such as tinylog or FastLogger should be--- used in production code.-newLogger :: MonadIO m => LogLevel -> Handle -> m Logger-newLogger x hd = liftIO $ do-    hSetBinaryMode hd True-    hSetBuffering  hd LineBuffering-    return $ \y b ->-        when (x >= y) $-            hPutBuilder hd (b <> "\n")--info :: (MonadIO m, ToBuilder a) => Logger -> a -> m ()-info f = liftIO . f Info . build-{-# INLINE info #-}--debug :: (MonadIO m, ToBuilder a) => Logger -> a -> m ()-debug f = liftIO . f Debug . build-{-# INLINE debug #-}--trace :: (MonadIO m, ToBuilder a) => Logger -> a -> m ()-trace f = liftIO . f Trace . build-{-# INLINE trace #-}
+ src/Network/AWS/Internal/Logger.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE OverloadedStrings #-}++-- |+-- Module      : Network.AWS.Internal.Logger+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- Types and functions for constructing loggers and emitting log messages.+module Network.AWS.Internal.Logger+    (+    -- * Constructing a Logger+      Logger+    , newLogger++    -- * Levels+    , LogLevel (..)+    , logError+    , logInfo+    , logDebug+    , logTrace++    -- * Building Messages+    , ToLog    (..)+    , buildLines+    ) where++import           Control.Monad+import           Control.Monad.IO.Class+import qualified Data.ByteString.Lazy.Builder as Build+import           Data.Monoid+import           Network.AWS.Data.Log+import           Network.AWS.Types+import           System.IO++import           Prelude++-- | This is a primitive logger which can be used to log builds to a 'Handle'.+--+-- /Note:/ A more sophisticated logging library such as+-- <http://hackage.haskell.org/package/tinylog tinylog> or+-- <http://hackage.haskell.org/package/FastLogger fast-logger>+-- should be used in production code.+newLogger :: MonadIO m => LogLevel -> Handle -> m Logger+newLogger x hd = liftIO $ do+    hSetBinaryMode hd True+    hSetBuffering  hd LineBuffering+    return $ \y b ->+        when (x >= y) $+            Build.hPutBuilder hd (b <> "\n")++logError, logInfo, logDebug, logTrace+ :: (MonadIO m, ToLog a) => Logger -> a -> m ()+logError f = liftIO . f Error . build+logInfo  f = liftIO . f Info  . build+logDebug f = liftIO . f Debug . build+logTrace f = liftIO . f Trace . build
− src/Network/AWS/Internal/Retry.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE BangPatterns      #-}-{-# LANGUAGE FlexibleContexts  #-}-{-# LANGUAGE LambdaCase        #-}-{-# LANGUAGE OverloadedStrings #-}-{-# LANGUAGE RecordWildCards   #-}---- Module      : Network.AWS.Internal.Retry--- Copyright   : (c) 2013-2015 Brendan Hay <brendan.g.hay@gmail.com>--- License     : This Source Code Form is subject to the terms of---               the Mozilla Public License, v. 2.0.---               A copy of the MPL can be found in the LICENSE file or---               you can obtain it at http://mozilla.org/MPL/2.0/.--- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>--- Stability   : experimental--- Portability : non-portable (GHC extensions)--module Network.AWS.Internal.Retry-    ( retrier-    , waiter-    ) where--import Control.Monad-import Control.Monad.IO.Class-import Control.Retry-import Data.List                (intersperse)-import Data.Monoid-import Network.AWS.Internal.Env-import Network.AWS.Internal.Log-import Network.AWS.Prelude-import Network.AWS.Types-import Network.AWS.Waiters--retrier :: (MonadIO m, AWSService (Sv a))-        => Env-        -> Request a-        -> m (Response' a)-        -> m (Response' a)-retrier Env{..} rq = retrying (fromMaybe policy _envRetryPolicy) check-  where-    policy = limitRetries _retryAttempts-       <> RetryPolicy (const $ listToMaybe [0 | not stream])-       <> RetryPolicy delay-      where-        !stream = isStreaming (_rqBody rq)--        delay n-            | n > 0     = Just $ truncate (grow * 1000000)-            | otherwise = Nothing-          where-            grow = _retryBase * (fromIntegral _retryGrowth ^^ (n - 1))--    check n = \case-        Left (ServiceError _ s e)-            | _retryCheck s e -> msg n >> return True-        Left (HttpError e)    -> do-            p <- liftIO (_envRetryCheck n e)-            when p (msg n) >> return p-        _                     -> return False--    msg n = debug _envLogger $-        "[Retrying] after " <> build (n + 1) <> " attempts."--    Exponential{..} = _svcRetry (serviceOf rq)--waiter :: MonadIO m-       => Env-       -> Wait a-       -> Request a-       -> m (Response' a)-       -> m (Response' a)-waiter Env{..} w@Wait{..} rq = retrying policy check-  where-    policy = limitRetries _waitAttempts <> constantDelay (_waitDelay * 1000000)--    check n rs = do-        let a = fromMaybe AcceptRetry (accept w rq rs)-        msg n a >> return (retry a)--    retry AcceptSuccess = False-    retry AcceptFailure = False-    retry AcceptRetry   = True--    msg n a = debug _envLogger-        . mconcat-        . intersperse " "-        $ [ "[Await " <> build _waitName <> "]"-          , build a-          , " after "-          , build (n + 1)-          , "attempts."-          ]
+ src/Network/AWS/Presign.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE FlexibleContexts #-}++-- |+-- Module      : Network.AWS.Presign+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : provisional+-- Portability : non-portable (GHC extensions)+--+-- This module contains functions for presigning requests using 'MonadIO' and+-- not one of the AWS specific transformers.+--+-- It is intended for use directly with "Network.AWS.Auth" when only presigning+-- and no other AWS actions are required.+-- If you wish to presign requests and are using either the 'Network.AWS.AWS'+-- or 'Control.Monad.Trans.AWS.AWST' monads, then prefer one of the relevant+-- 'Control.Monad.Trans.AWS.presign'ing functions available there, as the+-- functions in this module do not use the underlying 'FreeT'+-- 'Network.AWS.Free.Command' DSL.+module Network.AWS.Presign where++import           Control.Lens+import           Control.Monad+import           Control.Monad.IO.Class+import           Network.AWS.Data.Time+import           Network.AWS.Prelude+import           Network.AWS.Request    (requestURL)+import           Network.AWS.Types++import           Prelude++-- | Presign an URL that is valid from the specified time until the+-- number of seconds expiry has elapsed.+--+-- /See:/ 'presign', 'presignWith'+presignURL :: (MonadIO m, AWSPresigner (Sg (Sv a)), AWSRequest a)+           => Auth+           -> Region+           -> UTCTime     -- ^ Signing time.+           -> Seconds     -- ^ Expiry time.+           -> a           -- ^ Request to presign.+           -> m ByteString+presignURL a r e ts = liftM requestURL . presign a r e ts++-- | Presign an HTTP request that is valid from the specified time until the+-- number of seconds expiry has elapsed.+--+-- This requires the 'Service' signer to be an instance of 'AWSPresigner'.+-- Not all signing algorithms support this.+--+-- /See:/ 'presignWith'+presign :: (MonadIO m, AWSPresigner (Sg (Sv a)), AWSRequest a)+        => Auth+        -> Region+        -> UTCTime     -- ^ Signing time.+        -> Seconds     -- ^ Expiry time.+        -> a           -- ^ Request to presign.+        -> m ClientRequest+presign a r ts ex = presignWith id a r ts ex++-- | A variant of 'presign' that allows specifying the 'Service' definition+-- used to configure the request.+presignWith :: (MonadIO m, AWSPresigner (Sg s), AWSRequest a)+            => (Service (Sv a) -> Service s) -- ^ Modify the default service configuration.+            -> Auth+            -> Region+            -> UTCTime                       -- ^ Signing time.+            -> Seconds                       -- ^ Expiry time.+            -> a                             -- ^ Request to presign.+            -> m ClientRequest+presignWith s a r ts ex x =+    withAuth a $ \ae ->+        return . view sgRequest $+            presigned ae r ts ex (s (serviceOf x)) (request x)
+ test/Main.hs view
@@ -0,0 +1,16 @@+-- Module      : Main+-- Copyright   : (c) 2013-2015 Brendan Hay+-- License     : Mozilla Public License, v. 2.0.+-- Maintainer  : Brendan Hay <brendan.g.hay@gmail.com>+-- Stability   : experimental+-- Portability : non-portable (GHC extensions)++module Main (main) where++import           Test.Tasty++main :: IO ()+main = defaultMain $+    testGroup "amazonka"+        [+        ]