amazonka 1.4.3 → 1.4.4
raw patch · 7 files changed
+321/−117 lines, 7 filesdep ~amazonka-coredep ~http-conduitPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: amazonka-core, http-conduit
API changes (from Hackage documentation)
+ Control.Monad.Trans.AWS: Bombay :: Region
+ Control.Monad.Trans.AWS: _InvalidEnvError :: AsAuthError a => Prism' a Text
+ Network.AWS: Bombay :: Region
+ Network.AWS: _InvalidEnvError :: AsAuthError a => Prism' a Text
+ Network.AWS.Auth: InvalidEnvError :: Text -> AuthError
+ Network.AWS.Auth: _InvalidEnvError :: AsAuthError a => Prism' a Text
+ Network.AWS.EC2.Metadata: IdentityDocument :: Maybe Text -> Maybe Text -> Text -> Text -> Text -> !Region -> Text -> Text -> Text -> Text -> Text -> Maybe Text -> Text -> IdentityDocument
+ Network.AWS.EC2.Metadata: [_accountID] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_architecture] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_availabilityZone] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_billingProducts] :: IdentityDocument -> Maybe Text
+ Network.AWS.EC2.Metadata: [_devpayProductCodes] :: IdentityDocument -> Maybe Text
+ Network.AWS.EC2.Metadata: [_imageID] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_instanceID] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_instanceType] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_kernelID] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_privateIP] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: [_ramdiskID] :: IdentityDocument -> Maybe Text
+ Network.AWS.EC2.Metadata: [_region] :: IdentityDocument -> !Region
+ Network.AWS.EC2.Metadata: [_version] :: IdentityDocument -> Text
+ Network.AWS.EC2.Metadata: accountID :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: architecture :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: availabilityZone :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: billingProducts :: Lens' IdentityDocument (Maybe Text)
+ Network.AWS.EC2.Metadata: data IdentityDocument
+ Network.AWS.EC2.Metadata: devpayProductCodes :: Lens' IdentityDocument (Maybe Text)
+ Network.AWS.EC2.Metadata: identity :: (MonadIO m, MonadThrow m) => Manager -> m (Either String IdentityDocument)
+ Network.AWS.EC2.Metadata: imageID :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: instance Data.Aeson.Types.Class.FromJSON Network.AWS.EC2.Metadata.IdentityDocument
+ Network.AWS.EC2.Metadata: instance Data.Aeson.Types.Class.ToJSON Network.AWS.EC2.Metadata.IdentityDocument
+ Network.AWS.EC2.Metadata: instance GHC.Classes.Eq Network.AWS.EC2.Metadata.IdentityDocument
+ Network.AWS.EC2.Metadata: instance GHC.Show.Show Network.AWS.EC2.Metadata.IdentityDocument
+ Network.AWS.EC2.Metadata: instanceID :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: instanceType :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: kernelID :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: privateIP :: Lens' IdentityDocument Text
+ Network.AWS.EC2.Metadata: ramdiskID :: Lens' IdentityDocument (Maybe Text)
+ Network.AWS.EC2.Metadata: region :: Lens' IdentityDocument Region
+ Network.AWS.EC2.Metadata: version :: Lens' IdentityDocument Text
- Control.Monad.Trans.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
+ Control.Monad.Trans.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> (Maybe Text) -> Credentials
- Control.Monad.Trans.AWS: _Error :: AsError a => Prism' a Error
+ Control.Monad.Trans.AWS: _Error :: Prism' a Error
- Control.Monad.Trans.AWS: _SerializeError :: AsError a => Prism' a SerializeError
+ Control.Monad.Trans.AWS: _SerializeError :: Prism' a SerializeError
- Control.Monad.Trans.AWS: _ServiceError :: AsError a => Prism' a ServiceError
+ Control.Monad.Trans.AWS: _ServiceError :: Prism' a ServiceError
- Control.Monad.Trans.AWS: _TransportError :: AsError a => Prism' a HttpException
+ Control.Monad.Trans.AWS: _TransportError :: Prism' a HttpException
- 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 AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _InvalidEnvError = _AuthError . _InvalidEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
- Control.Monad.Trans.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
+ Control.Monad.Trans.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Credentials -> m Env
- Control.Monad.Trans.AWS: toBody :: ToBody a => a -> RqBody
+ Control.Monad.Trans.AWS: toBody :: a -> RqBody
- Control.Monad.Trans.AWS: toHashed :: ToHashedBody a => a -> HashedBody
+ Control.Monad.Trans.AWS: toHashed :: a -> HashedBody
- Network.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
+ Network.AWS: FromEnv :: Text -> Text -> (Maybe Text) -> (Maybe Text) -> Credentials
- Network.AWS: _Error :: AsError a => Prism' a Error
+ Network.AWS: _Error :: Prism' a Error
- Network.AWS: _SerializeError :: AsError a => Prism' a SerializeError
+ Network.AWS: _SerializeError :: Prism' a SerializeError
- Network.AWS: _ServiceError :: AsError a => Prism' a ServiceError
+ Network.AWS: _ServiceError :: Prism' a ServiceError
- Network.AWS: _TransportError :: AsError a => Prism' a HttpException
+ Network.AWS: _TransportError :: Prism' a HttpException
- Network.AWS: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
+ Network.AWS: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _InvalidEnvError = _AuthError . _InvalidEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
- Network.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
+ Network.AWS: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Credentials -> m Env
- Network.AWS: toBody :: ToBody a => a -> RqBody
+ Network.AWS: toBody :: a -> RqBody
- Network.AWS: toHashed :: ToHashedBody a => a -> HashedBody
+ Network.AWS: toHashed :: a -> HashedBody
- Network.AWS.Auth: FromEnv :: Text -> Text -> (Maybe Text) -> Credentials
+ Network.AWS.Auth: FromEnv :: Text -> Text -> (Maybe Text) -> (Maybe Text) -> Credentials
- 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: class AsAuthError a where _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError _InvalidEnvError = _AuthError . _InvalidEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError
- Network.AWS.Auth: fromEnv :: (Applicative m, MonadIO m, MonadThrow m) => m Auth
+ Network.AWS.Auth: fromEnv :: (Applicative m, MonadIO m, MonadThrow m) => m (Auth, Maybe Region)
- Network.AWS.Auth: fromEnvKeys :: (Applicative m, MonadIO m, MonadThrow m) => Text -> Text -> Maybe Text -> m Auth
+ Network.AWS.Auth: fromEnvKeys :: (Applicative m, MonadIO m, MonadThrow m) => Text -> Text -> Maybe Text -> Maybe Text -> m (Auth, Maybe Region)
- Network.AWS.Auth: fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m Auth
+ Network.AWS.Auth: fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m (Auth, Maybe Region)
- Network.AWS.Auth: fromFilePath :: (Applicative m, MonadIO m, MonadCatch m) => Text -> FilePath -> m Auth
+ Network.AWS.Auth: fromFilePath :: (Applicative m, MonadIO m, MonadCatch m) => Text -> FilePath -> m (Auth, Maybe Region)
- Network.AWS.Auth: fromProfile :: (MonadIO m, MonadCatch m) => Manager -> m Auth
+ Network.AWS.Auth: fromProfile :: (MonadIO m, MonadCatch m) => Manager -> m (Auth, Maybe Region)
- Network.AWS.Auth: fromProfileName :: (MonadIO m, MonadCatch m) => Manager -> Text -> m Auth
+ Network.AWS.Auth: fromProfileName :: (MonadIO m, MonadCatch m) => Manager -> Text -> m (Auth, Maybe Region)
- Network.AWS.Auth: getAuth :: (Applicative m, MonadIO m, MonadCatch m) => Manager -> Credentials -> m Auth
+ Network.AWS.Auth: getAuth :: (Applicative m, MonadIO m, MonadCatch m) => Manager -> Credentials -> m (Auth, Maybe Region)
- Network.AWS.Data: build :: ToLog a => a -> Builder
+ Network.AWS.Data: build :: a -> Builder
- Network.AWS.Data: parser :: FromText a => Parser a
+ Network.AWS.Data: parser :: Parser a
- Network.AWS.Data: toBS :: ToByteString a => a -> ByteString
+ Network.AWS.Data: toBS :: a -> ByteString
- Network.AWS.Data: toText :: ToText a => a -> Text
+ Network.AWS.Data: toText :: a -> Text
- Network.AWS.Env: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> m Env
+ Network.AWS.Env: newEnv :: (Applicative m, MonadIO m, MonadCatch m) => Credentials -> m Env
- Network.AWS.Env: newEnvWith :: (Applicative m, MonadIO m, MonadCatch m) => Region -> Credentials -> Maybe Bool -> Manager -> m Env
+ Network.AWS.Env: newEnvWith :: (Applicative m, MonadIO m, MonadCatch m) => Credentials -> Maybe Bool -> Manager -> m Env
Files
- CHANGELOG.md +14/−14
- amazonka.cabal +4/−4
- src/Control/Monad/Trans/AWS.hs +2/−2
- src/Network/AWS.hs +23/−14
- src/Network/AWS/Auth.hs +110/−57
- src/Network/AWS/EC2/Metadata.hs +137/−10
- src/Network/AWS/Env.hs +31/−16
CHANGELOG.md view
@@ -1,7 +1,7 @@ # Change Log ## [1.4.3](https://github.com/brendanhay/amazonka/tree/1.4.3)-Released: **09 June, 2016**, Compare: [1.4.3](https://github.com/brendanhay/amazonka/compare/1.4.2...1.4.3)+Released: **09 June, 2016**, Compare: [1.4.2](https://github.com/brendanhay/amazonka/compare/1.4.2...1.4.3) ### Fixed @@ -9,14 +9,14 @@ - CloudWatchLogs `FilterLogEvents` pagination now correctly returns all results. [\#296](https://github.com/brendanhay/amazonka/issues/296) - Documentation code samples for IoT, Lambda, and Discovery are now correctly Haddock formatted. -### Changes+### Changed - Documentation is now formatted more consistently at the expense of longer line columns. - `POSIX` timestamps no longer have unecessary (and misleading) Text/XML instances. ## [1.4.2](https://github.com/brendanhay/amazonka/tree/1.4.2)-Released: **03 June, 2016**, Compare: [1.4.2](https://github.com/brendanhay/amazonka/compare/1.4.1...1.4.2)+Released: **03 June, 2016**, Compare: [1.4.1](https://github.com/brendanhay/amazonka/compare/1.4.1...1.4.2) ### Fixed @@ -24,7 +24,7 @@ - APIGateway now correctly parses and encodes `ISO8601` formatted timestamps. [\#291](https://github.com/brendanhay/amazonka/issues/291) - `~/.aws/credentials` now correctly parses with leading newlines. [\#290](https://github.com/brendanhay/amazonka/issues/290) -### Changes+### Changed - `SerializeError` now contains the unparsed response body. [\#293](https://github.com/brendanhay/amazonka/pull/293) @@ -44,14 +44,14 @@ ## [1.4.1](https://github.com/brendanhay/amazonka/tree/1.4.1)-Released: **09 May, 2016**, Compare: [1.4.1](https://github.com/brendanhay/amazonka/compare/1.4.0...1.4.1)+Released: **09 May, 2016**, Compare: [1.4.0](https://github.com/brendanhay/amazonka/compare/1.4.0...1.4.1) ### Fixed - AutoScaling `DescribeAutoScalingInstances` response field `LaunchConfigurationName` is now optional. [\#281](https://github.com/brendanhay/amazonka/issues/281) - SWF `PollForDecisionTask` and `PollForActivityTask` response fields are now optional. [\#285](https://github.com/brendanhay/amazonka/issues/285) -### Changes+### Changed - `NFData` instances generated for all eligible types. [\#283](https://github.com/brendanhay/amazonka/issues/283) - Additional retry cases for HTTP `5XX` response codes. [c5e494e](https://github.com/brendanhay/amazonka/commit/c5e494e2a97fcf2e9210527ed5e8547f1be898de)@@ -99,7 +99,7 @@ ## [1.4.0](https://github.com/brendanhay/amazonka/tree/1.4.0)-Released: **21 March, 2016**, Compare: [1.4.0](https://github.com/brendanhay/amazonka/compare/1.3.7...1.4.0)+Released: **21 March, 2016**, Compare: [1.3.7](https://github.com/brendanhay/amazonka/compare/1.3.7...1.4.0) ### Fixed @@ -158,13 +158,13 @@ ## [1.3.7](https://github.com/brendanhay/amazonka/tree/1.3.7)-Released: **18 December, 2015**, Compare: [1.3.7](https://github.com/brendanhay/amazonka/compare/1.3.6...1.3.7)+Released: **18 December, 2015**, Compare: [1.3.6](https://github.com/brendanhay/amazonka/compare/1.3.6...1.3.7) ### Fixed - Fix SWF `PollFor{Activity,Decision}Task` response deserialisation. [\#257](https://github.com/brendanhay/amazonka/issues/257) -### Changes+### Changed - The `ErrorCode` type constructor is now exported. [\#258](https://github.com/brendanhay/amazonka/issues/258) - Add `Bounded` instances for all enumerations with stable values. [\#255](https://github.com/brendanhay/amazonka/issues/255)@@ -189,7 +189,7 @@ ## [1.3.6](https://github.com/brendanhay/amazonka/tree/1.3.6)-Released: **18 November, 2015**, Compare: [1.3.6](https://github.com/brendanhay/amazonka/compare/1.3.5.1...1.3.6)+Released: **18 November, 2015**, Compare: [1.3.5.1](https://github.com/brendanhay/amazonka/compare/1.3.5.1...1.3.6) ### Fixed @@ -197,7 +197,7 @@ - Fix S3 `BucketLocationConstraint` type de/serialisation. [\#249](https://github.com/brendanhay/amazonka/issues/249) - Fix S3 `PutBucketACL` header vs request body serialisation. [\#241](https://github.com/brendanhay/amazonka/issues/241) -### Changes+### Changed - `await` responses now indicate request fulfillment. [\#245](https://github.com/brendanhay/amazonka/issues/245) @@ -216,7 +216,7 @@ ## [1.3.5.1](https://github.com/brendanhay/amazonka/tree/1.3.5.1)-Released: **18 November, 2015**, Compare: [1.3.5.1](https://github.com/brendanhay/amazonka/compare/1.3.5...1.3.5.1)+Released: **18 November, 2015**, Compare: [1.3.5](https://github.com/brendanhay/amazonka/compare/1.3.5...1.3.5.1) ### Fixed @@ -225,7 +225,7 @@ ## [1.3.5](https://github.com/brendanhay/amazonka/tree/1.3.5)-Released: **27 October, 2015**, Compare: [1.3.5](https://github.com/brendanhay/amazonka/compare/1.3.4...1.3.5)+Released: **27 October, 2015**, Compare: [1.3.4](https://github.com/brendanhay/amazonka/compare/1.3.4...1.3.5) ### Updated Services Definitions @@ -239,7 +239,7 @@ ## [1.3.4](https://github.com/brendanhay/amazonka/tree/1.3.4)-Released: **25 October, 2015**, Compare: [1.3.4](https://github.com/brendanhay/amazonka/compare/1.3.3...1.3.4)+Released: **25 October, 2015**, Compare: [1.3.3](https://github.com/brendanhay/amazonka/compare/1.3.3...1.3.4) ### Updated Services Definitions
amazonka.cabal view
@@ -1,5 +1,5 @@ name: amazonka-version: 1.4.3+version: 1.4.4 synopsis: Comprehensive Amazon Web Services SDK. homepage: https://github.com/brendanhay/amazonka bug-reports: https://github.com/brendanhay/amazonka/issues@@ -55,14 +55,14 @@ , Network.AWS.Internal.Logger build-depends:- amazonka-core == 1.4.3.*- , base >= 4.7 && < 5+ amazonka-core == 1.4.4.*+ , base >= 4.7 && < 5 , bytestring >= 0.9 , conduit >= 1.1 , conduit-extra >= 1.1 , directory >= 1.2 , exceptions >= 0.6- , http-conduit >= 2.1.4+ , http-conduit >= 2.2 && < 3 , ini >= 0.3.5 , mmorph >= 1 , monad-control >= 1
src/Control/Monad/Trans/AWS.hs view
@@ -430,7 +430,7 @@ The updated configuration is then passed to the 'Env' during setup: -> e <- newEnv Frankfurt Discover <&> configure dynamo+> e <- newEnv Discover <&> configure dynamo > runResourceT . runAWS e $ do > -- This S3 operation will communicate with remote AWS APIs. > x <- send listBuckets@@ -444,7 +444,7 @@ You can also scope the 'Endpoint' modifications (or any other 'Service' configuration) to specific actions: -> e <- newEnv Ireland Discover+> e <- newEnv Discover > runResourceT . runAWS e $ do > -- Service operations here will communicate with AWS, even DynamoDB. > x <- send listTables
src/Network/AWS.hs view
@@ -299,17 +299,20 @@ * 'await' -To utilise these, you will need to specify what 'Region' you wish to operate in-and your Amazon credentials for AuthN/AuthZ purposes.+These functions have constraints that types from the @amazonka-*@ libraries+satisfy. To utilise these, you will need to specify what 'Region' you wish to+operate in and your Amazon credentials for AuthN/AuthZ purposes. 'Credentials' can be supplied in a number of ways. Either via explicit keys,-via session profiles, or have Amazonka determine the credentials from an+via session profiles, or have Amazonka retrieve the credentials from an underlying IAM Role/Profile. As a basic example, you might wish to store an object in an S3 bucket using <http://hackage.haskell.org/package/amazonka-s3 amazonka-s3>: @+{-# LANGUAGE OverloadedStrings #-}+ import Control.Lens import Network.AWS import Network.AWS.S3@@ -317,20 +320,26 @@ 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:+ lgr <- newLogger Debug stdout - -- 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+ -- To specify configuration preferences, 'newEnv' is used to create a new+ -- configuration environment. The 'Credentials' parameter is used to specify+ -- mechanism for supplying or retrieving AuthN/AuthZ information.+ -- In this case 'Discover' will cause the library to try a number of options such+ -- as default environment variables, or an instance's IAM Profile and identity document:+ env <- newEnv Discover - -- The payload (and hash) for the S3 object is retrieved from a FilePath:- b <- sourceFileIO "local\/path\/to\/object-payload"+ -- The payload (and hash) for the S3 object is retrieved from a 'FilePath':+ body <- sourceFileIO "local\/path\/to\/object-payload" - -- We now run the AWS computation with the overriden logger, performing the PutObject request:- runResourceT . runAWS (e & envLogger .~ l) $- send (putObject "bucket-name" "object-key" b)+ -- We now run the 'AWS' computation with the overriden logger, performing the+ -- 'PutObject' request. 'envRegion' or 'within' can be used to set the+ -- remote AWS 'Region':+ runResourceT $ runAWS (env & envLogger .~ lgr) $+ within Frankfurt $+ send (putObject "bucket-name" "object-key" body) @ -}
src/Network/AWS/Auth.hs view
@@ -65,6 +65,7 @@ import Control.Monad import Control.Monad.Catch import Control.Monad.IO.Class+import Control.Monad.Trans.Maybe (MaybeT (..)) import qualified Data.ByteString.Char8 as BS8 import qualified Data.ByteString.Lazy.Char8 as LBS8 import Data.Char (isSpace)@@ -78,7 +79,7 @@ import Network.AWS.EC2.Metadata import Network.AWS.Lens (catching, catching_, exception, throwingM, _IOException)-import Network.AWS.Lens (Prism', prism)+import Network.AWS.Lens (Prism', prism, (<&>)) import Network.AWS.Prelude import Network.AWS.Types import Network.HTTP.Conduit@@ -98,10 +99,14 @@ envSessionToken :: Text -- ^ AWS_SESSION_TOKEN envSessionToken = "AWS_SESSION_TOKEN" --- | Credentials profile environment variable.+-- | Default credentials profile environment variable. envProfile :: Text -- ^ AWS_PROFILE envProfile = "AWS_PROFILE" +-- | Default region environment variable+envRegion :: Text -- ^ AWS_REGION+envRegion = "AWS_REGION"+ -- | Credentials INI file access key variable. credAccessKey :: Text -- ^ aws_access_key_id credAccessKey = "aws_access_key_id"@@ -159,9 +164,9 @@ | 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.+ | FromEnv Text Text (Maybe Text) (Maybe Text)+ -- ^ Lookup specific environment variables for access key, secret key,+ -- an optional session token, and an optional region, respectively. | FromProfile Text -- ^ An IAM Profile name to lookup from the local EC2 instance-data.@@ -175,11 +180,12 @@ | Discover -- ^ Attempt credentials discovery via the following steps: --- -- * Read the 'envAccessKey' and 'envSecretKey' from the environment if they are set.+ -- * Read the 'envAccessKey', 'envSecretKey', and 'envRegion' from the environment if they are set. -- -- * Read the credentials file if 'credFile' exists. --- -- * Retrieve the first available IAM profile if running on EC2.+ -- * Retrieve the first available IAM profile and read+ -- the 'Region' from the instance identity document, 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.@@ -189,12 +195,18 @@ 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"+ FromKeys a _ ->+ "FromKeys " <> build a <> " ****"+ FromSession a _ _ ->+ "FromSession " <> build a <> " **** ****"+ FromEnv a s t r ->+ "FromEnv " <> build a <> " " <> build s <> " " <> m t <> " " <> m r+ 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"@@ -206,6 +218,7 @@ data AuthError = RetrievalError HttpException | MissingEnvError Text+ | InvalidEnvError Text | MissingFileError FilePath | InvalidFileError Text | InvalidIAMError Text@@ -217,6 +230,7 @@ build = \case RetrievalError e -> build e MissingEnvError e -> "[MissingEnvError] { message = " <> build e <> "}"+ InvalidEnvError e -> "[InvalidEnvError] { message = " <> build e <> "}" MissingFileError f -> "[MissingFileError] { path = " <> build f <> "}" InvalidFileError e -> "[InvalidFileError] { message = " <> build e <> "}" InvalidIAMError e -> "[InvalidIAMError] { message = " <> build e <> "}"@@ -230,9 +244,12 @@ -- the local metadata endpoint. _RetrievalError :: Prism' a HttpException - -- | An error occured looking up a named environment variable.+ -- | The named environment variable was not found. _MissingEnvError :: Prism' a Text + -- | An error occured parsing named environment variable's value.+ _InvalidEnvError :: Prism' a Text+ -- | The specified credentials file could not be found. _MissingFileError :: Prism' a FilePath @@ -244,6 +261,7 @@ _RetrievalError = _AuthError . _RetrievalError _MissingEnvError = _AuthError . _MissingEnvError+ _InvalidEnvError = _AuthError . _InvalidEnvError _MissingFileError = _AuthError . _MissingFileError _InvalidFileError = _AuthError . _InvalidFileError _InvalidIAMError = _AuthError . _InvalidIAMError@@ -262,6 +280,10 @@ MissingEnvError e -> Right e x -> Left x + _InvalidEnvError = prism InvalidEnvError $ \case+ InvalidEnvError e -> Right e+ x -> Left x+ _MissingFileError = prism MissingFileError $ \case MissingFileError f -> Right f x -> Left x@@ -281,14 +303,14 @@ getAuth :: (Applicative m, MonadIO m, MonadCatch m) => Manager -> Credentials- -> m Auth+ -> m (Auth, Maybe Region) 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 ->+ FromKeys a s -> return (fromKeys a s, Nothing)+ FromSession a s t -> return (fromSession a s t, Nothing)+ FromEnv a s t r -> fromEnvKeys a s t r+ 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 $@@ -309,8 +331,13 @@ -- 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)+fromEnv :: (Applicative m, MonadIO m, MonadThrow m) => m (Auth, Maybe Region)+fromEnv =+ fromEnvKeys+ envAccessKey+ envSecretKey+ (Just envSessionToken)+ (Just envRegion) -- | Retrieve access key, secret key and a session token from specific -- environment variables.@@ -321,13 +348,26 @@ => 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- <$> (AccessKey <$> req a)- <*> (SecretKey <$> req s)- <*> (fmap SessionToken <$> opt t)- <*> pure Nothing+ -> Maybe Text -- ^ Region environment variable.+ -> m (Auth, Maybe Region)+fromEnvKeys access secret session region =+ (,) <$> fmap Auth lookupKeys <*> lookupRegion where+ lookupKeys = AuthEnv+ <$> (req access <&> AccessKey . BS8.pack)+ <*> (req secret <&> SecretKey . BS8.pack)+ <*> (opt session <&> fmap (SessionToken . BS8.pack))+ <*> return Nothing++ lookupRegion :: (MonadIO m, MonadThrow m) => m (Maybe Region)+ lookupRegion = runMaybeT $ do+ k <- MaybeT (return region)+ r <- MaybeT (opt region)+ case fromText (Text.pack r) of+ Right x -> return x+ Left e -> throwM . InvalidEnvError $+ "Unable to parse ENV variable: " <> k <> ", " <> Text.pack e+ req k = do m <- opt (Just k) maybe (throwM . MissingEnvError $ "Unable to read ENV variable: " <> k)@@ -335,7 +375,7 @@ m opt Nothing = return Nothing- opt (Just k) = fmap BS8.pack <$> liftIO (lookupEnv (Text.unpack k))+ opt (Just k) = liftIO (lookupEnv (Text.unpack k)) -- | Loads the default @credentials@ INI file using the default profile name. --@@ -343,12 +383,11 @@ -- if an error occurs during parsing. -- -- /See:/ 'credProfile', 'credFile', and 'envProfile'-fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m Auth+fromFile :: (Applicative m, MonadIO m, MonadCatch m) => m (Auth, Maybe Region) fromFile = do- f <- credFile- ep <- liftIO (lookupEnv (Text.unpack envProfile))- let p = Text.pack (fromMaybe (Text.unpack credProfile) ep)- fromFilePath p f+ p <- liftIO (lookupEnv (Text.unpack envProfile))+ fromFilePath (maybe credProfile Text.pack p)+ =<< credFile -- | Retrieve the access, secret and session token from the specified section -- (profile) in a valid INI @credentials@ file.@@ -358,17 +397,18 @@ fromFilePath :: (Applicative m, MonadIO m, MonadCatch m) => Text -> FilePath- -> m Auth+ -> m (Auth, Maybe Region) fromFilePath n f = do p <- liftIO (doesFileExist f) unless p $ throwM (MissingFileError f)- i <- liftIO (INI.readIniFile f) >>= either (invalidErr Nothing) return- fmap Auth $ AuthEnv- <$> (AccessKey <$> req credAccessKey i)- <*> (SecretKey <$> req credSecretKey i)- <*> (fmap SessionToken <$> opt credSessionToken i)- <*> pure Nothing+ ini <- either (invalidErr Nothing) return =<< liftIO (INI.readIniFile f)+ env <- AuthEnv+ <$> (req credAccessKey ini <&> AccessKey)+ <*> (req credSecretKey ini <&> SecretKey)+ <*> (opt credSessionToken ini <&> fmap SessionToken)+ <*> return Nothing+ return (Auth env, Nothing) where req k i = case INI.lookupValue n k i of@@ -396,7 +436,7 @@ -- -- 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 :: (MonadIO m, MonadCatch m) => Manager -> m (Auth, Maybe Region) fromProfile m = do ls <- try $ metadata m (IAM (SecurityCredentials Nothing)) case BS8.lines `liftM` ls of@@ -419,23 +459,36 @@ -- -- 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+fromProfileName :: (MonadIO m, MonadCatch m)+ => Manager+ -> Text+ -> m (Auth, Maybe Region)+fromProfileName m name = do+ auth <- getCredentials >>= start+ reg <- getRegion+ return (auth, Just reg) 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))+ getCredentials :: (MonadIO m, MonadCatch m) => m AuthEnv+ getCredentials =+ try (metadata m (IAM . SecurityCredentials $ Just name)) >>=+ handleErr (eitherDecode' . LBS8.fromStrict) invalidIAMErr - invalidErr = InvalidIAMError+ getRegion :: (MonadIO m, MonadCatch m) => m Region+ getRegion =+ try (identity m) >>=+ handleErr (fmap _region) invalidIdentityErr++ handleErr _ _ (Left e) = throwM (RetrievalError e)+ handleErr f g (Right x) = either (throwM . g) return (f x)++ invalidIAMErr = InvalidIAMError . mappend ("Error parsing IAM profile '" <> name <> "' ") . Text.pack + invalidIdentityErr = InvalidIAMError+ . mappend "Error parsing Instance Identity Document "+ . Text.pack+ start :: MonadIO m => AuthEnv -> m Auth start !a = liftIO $ case _authExpiry a of@@ -455,8 +508,8 @@ loop :: Weak (IORef AuthEnv) -> ThreadId -> UTCTime -> IO () loop w !p !x = do diff x <$> getCurrentTime >>= threadDelay- ea <- try auth- case ea of+ env <- try getCredentials+ case env of Left e -> throwTo p (RetrievalError e) Right !a -> do mr <- deRefWeak w
src/Network/AWS/EC2/Metadata.hs view
@@ -3,6 +3,7 @@ {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE RecordWildCards #-} -- | -- Module : Network.AWS.EC2.Metadata@@ -27,13 +28,32 @@ , dynamic , metadata , userdata+ , identity -- ** Path Constructors- , Dynamic (..)- , Metadata (..)- , Mapping (..)- , Info (..)- , Interface (..)+ , Dynamic (..)+ , Metadata (..)+ , Mapping (..)+ , Info (..)+ , Interface (..)++ -- ** Identity Document+ , IdentityDocument (..)++ -- *** Lenses+ , devpayProductCodes+ , billingProducts+ , version+ , privateIP+ , availabilityZone+ , region+ , instanceID+ , instanceType+ , accountID+ , imageID+ , kernelID+ , ramdiskID+ , architecture ) where import Control.Monad@@ -43,6 +63,8 @@ import qualified Data.ByteString.Lazy as LBS import Data.Monoid import qualified Data.Text as Text+import Network.AWS.Data.JSON+import Network.AWS.Lens (Lens', lens) import Network.AWS.Prelude hiding (request) import Network.HTTP.Conduit @@ -55,6 +77,7 @@ | Document -- ^ JSON containing instance attributes, such as instance-id, -- private IP address, etc.+ -- /See:/ 'identity', 'InstanceDocument'. | PKCS7 -- ^ Used to verify the document's authenticity and content against the -- signature.@@ -305,11 +328,115 @@ 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+ Left (HttpExceptionRequest _ (StatusCodeException rs _))+ | fromEnum (responseStatus rs) == 404+ -> return Nothing+ Left e -> throwM e+ Right b -> return (Just b) +-- | Represents an instance's identity document.+--+-- /Note:/ Fields such as '_instanceType' are represented as unparsed 'Text' and+-- will need to be manually parsed using 'fromText' when the relevant types+-- from a library such as "Network.AWS.EC2" are brought into scope.+data IdentityDocument = IdentityDocument+ { _devpayProductCodes :: Maybe Text+ , _billingProducts :: Maybe Text+ , _version :: Text+ , _privateIP :: Text+ , _availabilityZone :: Text+ , _region :: !Region+ , _instanceID :: Text+ , _instanceType :: Text+ , _accountID :: Text+ , _imageID :: Text+ , _kernelID :: Text+ , _ramdiskID :: Maybe Text+ , _architecture :: Text+ } deriving (Eq, Show)++devpayProductCodes :: Lens' IdentityDocument (Maybe Text)+devpayProductCodes = lens _devpayProductCodes (\s a -> s { _devpayProductCodes = a })++billingProducts :: Lens' IdentityDocument (Maybe Text)+billingProducts = lens _billingProducts (\s a -> s { _billingProducts = a })++version :: Lens' IdentityDocument Text+version = lens _version (\s a -> s { _version = a })++privateIP :: Lens' IdentityDocument Text+privateIP = lens _privateIP (\s a -> s { _privateIP = a })++availabilityZone :: Lens' IdentityDocument Text+availabilityZone = lens _availabilityZone (\s a -> s { _availabilityZone = a })++region :: Lens' IdentityDocument Region+region = lens _region (\s a -> s { _region = a })++instanceID :: Lens' IdentityDocument Text+instanceID = lens _instanceID (\s a -> s { _instanceID = a })++instanceType :: Lens' IdentityDocument Text+instanceType = lens _instanceType (\s a -> s { _instanceType = a })++accountID :: Lens' IdentityDocument Text+accountID = lens _accountID (\s a -> s { _accountID = a })++imageID :: Lens' IdentityDocument Text+imageID = lens _imageID (\s a -> s { _imageID = a })++kernelID :: Lens' IdentityDocument Text+kernelID = lens _kernelID (\s a -> s { _kernelID = a })++ramdiskID :: Lens' IdentityDocument (Maybe Text)+ramdiskID = lens _ramdiskID (\s a -> s { _ramdiskID = a })++architecture :: Lens' IdentityDocument Text+architecture = lens _architecture (\s a -> s { _architecture = a })++instance FromJSON IdentityDocument where+ parseJSON = withObject "dynamic/instance-identity/document" $ \o ->+ IdentityDocument+ <$> o .:? "devpayProductCodes"+ <*> o .:? "availabilityZone"+ <*> o .: "privateIp"+ <*> o .: "version"+ <*> o .: "region"+ <*> o .: "instanceId"+ <*> o .: "billingProducts"+ <*> o .: "instanceType"+ <*> o .: "accountId"+ <*> o .: "imageId"+ <*> o .: "kernelId"+ <*> o .:? "ramdiskId"+ <*> o .: "architecture"++instance ToJSON IdentityDocument where+ toJSON IdentityDocument{..} =+ object+ [ "devpayProductCodes" .= _devpayProductCodes+ , "availabilityZone" .= _availabilityZone+ , "privateIp" .= _privateIP+ , "version" .= _version+ , "region" .= _region+ , "instanceId" .= _instanceID+ , "billingProducts" .= _billingProducts+ , "instanceType" .= _instanceType+ , "accountId" .= _accountID+ , "imageId" .= _imageID+ , "kernelId" .= _kernelID+ , "ramdiskId" .= _ramdiskID+ , "architecture" .= _architecture+ ]++-- | Retrieve the instance's identity document, detailing various EC2 metadata.+--+-- /See:/ <http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-identity-documents.html AWS Instance Identity Documents>.+identity :: (MonadIO m, MonadThrow m)+ => Manager+ -> m (Either String IdentityDocument)+identity m = (eitherDecode . LBS.fromStrict) `liftM` dynamic m Document+ get :: (MonadIO m, MonadThrow m) => Manager -> Text -> m ByteString get m url = liftIO (strip `liftM` request m url) where@@ -319,6 +446,6 @@ request :: Manager -> Text -> IO ByteString request m url = do- rq <- parseUrl (Text.unpack url)+ rq <- parseUrlThrow (Text.unpack url) rs <- httpLbs rq m return . LBS.toStrict $ responseBody rs
src/Network/AWS/Env.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE ViewPatterns #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-}@@ -37,6 +38,7 @@ , retryConnectionFailure ) where +import Data.Maybe (fromMaybe) import Control.Applicative import Control.Monad.Catch import Control.Monad.IO.Class@@ -170,6 +172,11 @@ -- and uses 'getAuth' to expand/discover the supplied 'Credentials'. -- Lenses from 'HasEnv' can be used to further configure the resulting 'Env'. --+-- /Since:/ @1.5.0@ - The region is now retrieved from the @AWS_REGION@ environment+-- variable (identical to official SDKs), or defaults to @us-east-1@.+-- You can override the 'Env' region by using 'envRegion', or the current operation's+-- region by using 'within'.+-- -- /Since:/ @1.3.6@ - The default logic for retrying 'HttpException's now uses -- 'retryConnectionFailure' to retry specific connection failure conditions up to 3 times. -- Previously only service specific errors were automatically retried.@@ -180,33 +187,41 @@ -- -- /See:/ 'newEnvWith'. newEnv :: (Applicative m, MonadIO m, MonadCatch m)- => Region -- ^ Initial region to operate in.- -> Credentials -- ^ Credential discovery mechanism.+ => Credentials -- ^ Credential discovery mechanism. -> m Env-newEnv r c = liftIO (newManager conduitManagerSettings)- >>= newEnvWith r c Nothing+newEnv c =+ liftIO (newManager conduitManagerSettings)+ >>= newEnvWith c Nothing -- | /See:/ 'newEnv' --+-- The 'Maybe' 'Bool' parameter is used by the EC2 instance check. By passing a+-- value of 'Nothing', the check will be performed. 'Just' 'True' would cause+-- the check to be skipped and the host treated as an EC2 instance.+-- -- 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 -- ^ Dictate if the instance is running on EC2. (Preload memoisation.)+ => Credentials -- ^ Credential discovery mechanism.+ -> Maybe Bool -- ^ Preload the EC2 instance check. -> Manager -> m Env-newEnvWith r c p m =+newEnvWith c p m = do+ (a, fromMaybe NorthVirginia -> r) <- getAuth m c Env r (\_ _ -> pure ()) (retryConnectionFailure 3) mempty m <$> liftIO (newIORef p)- <*> getAuth m c+ <*> pure a -- | Retry the subset of transport specific errors encompassing connection -- failure up to the specific number of times. retryConnectionFailure :: Int -> Int -> HttpException -> Bool-retryConnectionFailure limit n = \case- _ | n >= limit -> False- NoResponseDataReceived -> True- FailedConnectionException {} -> True- FailedConnectionException2 {} -> True- TlsException {} -> True- _ -> False+retryConnectionFailure _ _ InvalidUrlException {} = False+retryConnectionFailure limit n (HttpExceptionRequest _ ex)+ | n >= limit = False+ | otherwise =+ case ex of+ NoResponseDataReceived -> True+ ConnectionTimeout -> True+ ConnectionClosed -> True+ ConnectionFailure {} -> True+ InternalException {} -> True+ _ -> False