diff --git a/Makefile b/Makefile index f035d080ccb..dab64d021ea 100644 --- a/Makefile +++ b/Makefile @@ -266,8 +266,11 @@ ifeq ($(package), all) ./dist/galley-schema --keyspace galley_test --replication-factor 1 ./dist/gundeck-schema --keyspace gundeck_test --replication-factor 1 ./dist/spar-schema --keyspace spar_test --replication-factor 1 -else +# How this check works: https://stackoverflow.com/a/9802777 +else ifeq ($(package), $(filter $(package),brig galley gundeck spar)) $(EXE_SCHEMA) --keyspace $(package)_test --replication-factor 1 +else + @echo No schema migrations for $(package) endif diff --git a/changelog.d/2-features/vhost-addressing-for-s3 b/changelog.d/2-features/vhost-addressing-for-s3 new file mode 100644 index 00000000000..aca06463e27 --- /dev/null +++ b/changelog.d/2-features/vhost-addressing-for-s3 @@ -0,0 +1,3 @@ +Allow vhost style addressing for S3 as path style is not supported for newer buckets. + +More info: https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ \ No newline at end of file diff --git a/charts/cargohold/templates/configmap.yaml b/charts/cargohold/templates/configmap.yaml index 5ceadf367c8..942185bda33 100644 --- a/charts/cargohold/templates/configmap.yaml +++ b/charts/cargohold/templates/configmap.yaml @@ -28,6 +28,9 @@ data: {{- if .s3Compatibility }} s3Compatibility: {{ .s3Compatibility }} {{- end }} + {{- if .s3AddressingStyle }} + s3AddressingStyle: {{ .s3AddressingStyle }} + {{- end }} {{ if .cloudFront }} cloudFront: domain: {{ .cloudFront.domain }} diff --git a/docs/src/how-to/install/configuration-options.rst b/docs/src/how-to/install/configuration-options.rst index 94a6136cc64..869dc6c6039 100644 --- a/docs/src/how-to/install/configuration-options.rst +++ b/docs/src/how-to/install/configuration-options.rst @@ -1058,3 +1058,49 @@ The table assumes the following: * When backend level config says that this feature is disabled, the list of domains is ignored. * When team level feature is disabled, the accompanying domains are ignored. +S3 Addressing Style +------------------- + +S3 can either by addressed in path style, i.e. +`https:////`, or vhost style, i.e. +`https://./`. AWS's S3 offering has deprecated +path style addressing for S3 and completely disabled it for buckets created +after 30 Sep 2020: +https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ + +However other object storage providers (specially self-deployed ones like MinIO) +may not support vhost style addressing yet (or ever?). Users of such buckets +should configure this option to "path": + +.. code:: yaml + + cargohold: + aws: + s3AddressingStyle: path + +Installations using S3 service provided by AWS, should use "auto", this option +will ensure that vhost style is only used when it is possible to construct a +valid hostname from the bucket name and the bucket name doesn't contain a '.'. +Having a '.' in the bucket name causes TLS validation to fail, hence it is not +used by default: + +.. code:: yaml + + cargohold: + aws: + s3AddressingStyle: auto + + +Using "virtual" as an option is only useful in situations where vhost style +addressing must be used even if it is not possible to construct a valid hostname +from the bucket name or the S3 service provider can ensure correct certificate +is issued for bucket which contain one or more '.'s in the name: + +.. code:: yaml + + cargohold: + aws: + s3AddressingStyle: virtual + +When this option is unspecified, wire-server defaults to path style addressing +to ensure smooth transition for older deployments. diff --git a/libs/types-common-aws/default.nix b/libs/types-common-aws/default.nix index 647dd6884dc..a296d9c9d87 100644 --- a/libs/types-common-aws/default.nix +++ b/libs/types-common-aws/default.nix @@ -4,6 +4,7 @@ # dependencies are added or removed. { mkDerivation , amazonka +, amazonka-core , amazonka-sqs , base , base64-bytestring @@ -27,6 +28,7 @@ mkDerivation { src = gitignoreSource ./.; libraryHaskellDepends = [ amazonka + amazonka-core amazonka-sqs base base64-bytestring diff --git a/libs/types-common-aws/src/AWS/Util.hs b/libs/types-common-aws/src/AWS/Util.hs index 1eff3fe67e3..a2a2a0055ce 100644 --- a/libs/types-common-aws/src/AWS/Util.hs +++ b/libs/types-common-aws/src/AWS/Util.hs @@ -18,15 +18,16 @@ module AWS.Util where import qualified Amazonka as AWS +import qualified Amazonka.Data.Time as AWS import Data.Time import Imports readAuthExpiration :: AWS.Env -> IO (Maybe NominalDiffTime) readAuthExpiration env = do authEnv <- - case runIdentity (AWS.envAuth env) of + case runIdentity (AWS.auth env) of AWS.Auth authEnv -> pure authEnv AWS.Ref _ ref -> do readIORef ref now <- getCurrentTime - pure $ (`diffUTCTime` now) . AWS.fromTime <$> AWS._authExpiration authEnv + pure $ (`diffUTCTime` now) . AWS.fromTime <$> AWS.expiration authEnv diff --git a/libs/types-common-aws/types-common-aws.cabal b/libs/types-common-aws/types-common-aws.cabal index 7d8813a2b75..120d78603fb 100644 --- a/libs/types-common-aws/types-common-aws.cabal +++ b/libs/types-common-aws/types-common-aws.cabal @@ -75,6 +75,7 @@ library ghc-prof-options: -fprof-auto-exported build-depends: amazonka + , amazonka-core , amazonka-sqs , base >=4 && <5 , base64-bytestring >=1.0 diff --git a/nix/haskell-pins.nix b/nix/haskell-pins.nix index b228bb16882..f097910d74e 100644 --- a/nix/haskell-pins.nix +++ b/nix/haskell-pins.nix @@ -93,9 +93,9 @@ let }; amazonka = { src = fetchgit { - url = "https://github.com/wireapp/amazonka"; - rev = "7ced54b0396296307b9871d293cc0ac161e5743d"; - sha256 = "0md658m32zrvzc8nljn58r8iw4rqxpihgdnqrhl8vnmkq6i9np51"; + url = "https://github.com/brendanhay/amazonka"; + rev = "cfe2584aef0b03c86650372d362c74f237925d8c"; + sha256 = "sha256-ss8IuIN0BbS6LMjlaFmUdxUqQu+IHsA8ucsjxXJwbyg="; }; packages = { amazonka = "lib/amazonka"; diff --git a/services/brig/brig.cabal b/services/brig/brig.cabal index 09e7f1aa147..4377d9f20c4 100644 --- a/services/brig/brig.cabal +++ b/services/brig/brig.cabal @@ -183,10 +183,11 @@ library build-depends: aeson >=2.0.1.0 - , amazonka >=1.3.7 - , amazonka-dynamodb >=1.3.7 - , amazonka-ses >=1.3.7 - , amazonka-sqs >=1.3.7 + , amazonka >=2 + , amazonka-core >=2 + , amazonka-dynamodb >=2 + , amazonka-ses >=2 + , amazonka-sqs >=2 , async >=2.1 , attoparsec >=0.12 , auto-update >=0.1 diff --git a/services/brig/default.nix b/services/brig/default.nix index b67ec42e530..6c98c83546d 100644 --- a/services/brig/default.nix +++ b/services/brig/default.nix @@ -5,6 +5,7 @@ { mkDerivation , aeson , amazonka +, amazonka-core , amazonka-dynamodb , amazonka-ses , amazonka-sqs @@ -168,6 +169,7 @@ mkDerivation { libraryHaskellDepends = [ aeson amazonka + amazonka-core amazonka-dynamodb amazonka-ses amazonka-sqs diff --git a/services/brig/src/Brig/AWS.hs b/services/brig/src/Brig/AWS.hs index 1f9a88dbe16..1faee375648 100644 --- a/services/brig/src/Brig/AWS.hs +++ b/services/brig/src/Brig/AWS.hs @@ -47,6 +47,7 @@ where import Amazonka (AWSRequest, AWSResponse) import qualified Amazonka as AWS +import qualified Amazonka.Data.Text as AWS import qualified Amazonka.DynamoDB as DDB import qualified Amazonka.SES as SES import qualified Amazonka.SES.Lens as SES @@ -122,13 +123,13 @@ mkEnv lgr opts emailOpts mgr = do mkAwsEnv g ses dyn sqs = do baseEnv <- AWS.newEnv AWS.discover - <&> maybe id AWS.configure ses - <&> maybe id AWS.configure dyn - <&> AWS.configure sqs + <&> maybe id AWS.configureService ses + <&> maybe id AWS.configureService dyn + <&> AWS.configureService sqs pure $ baseEnv - { AWS.envLogger = awsLogger g, - AWS.envManager = mgr + { AWS.logger = awsLogger g, + AWS.manager = mgr } awsLogger g l = Logger.log g (mapLevel l) . Logger.msg . toLazyByteString mapLevel AWS.Info = Logger.Info @@ -226,10 +227,10 @@ sendMail m = do -- after the fact. AWS.ServiceError se | se - ^. AWS.serviceStatus + ^. AWS.serviceError_status == status400 && "Invalid domain name" - `Text.isPrefixOf` AWS.toText (se ^. AWS.serviceCode) -> + `Text.isPrefixOf` AWS.toText (se ^. AWS.serviceError_code) -> throwM SESInvalidDomain _ -> throwM (GeneralError x) @@ -268,7 +269,7 @@ canRetry :: MonadIO m => Either AWS.Error a -> m Bool canRetry (Right _) = pure False canRetry (Left e) = case e of AWS.TransportError (HttpExceptionRequest _ ResponseTimeout) -> pure True - AWS.ServiceError se | se ^. AWS.serviceCode == AWS.ErrorCode "RequestThrottled" -> pure True + AWS.ServiceError se | se ^. AWS.serviceError_code == AWS.ErrorCode "RequestThrottled" -> pure True _ -> pure False retry5x :: (Monad m) => RetryPolicyM m diff --git a/services/brig/src/Brig/Data/Client.hs b/services/brig/src/Brig/Data/Client.hs index 7d4587a1646..5e532d974ef 100644 --- a/services/brig/src/Brig/Data/Client.hs +++ b/services/brig/src/Brig/Data/Client.hs @@ -49,6 +49,7 @@ module Brig.Data.Client where import qualified Amazonka as AWS +import qualified Amazonka.Data.Text as AWS import qualified Amazonka.DynamoDB as AWS import qualified Amazonka.DynamoDB.Lens as AWS import Bilge.Retry (httpHandlers) @@ -567,7 +568,7 @@ withOptLock u c ma = go (10 :: Int) run = execCatch e cmd >>= either handleErr (pure . conv) handlers = httpHandlers ++ [const $ EL.handler_ AWS._ConditionalCheckFailedException (pure True)] policy = limitRetries 3 <> exponentialBackoff 100000 - handleErr (AWS.ServiceError se) | se ^. AWS.serviceCode == AWS.ErrorCode "ProvisionedThroughputExceeded" = do + handleErr (AWS.ServiceError se) | se ^. AWS.serviceError_code == AWS.ErrorCode "ProvisionedThroughputExceeded" = do Metrics.counterIncr (Metrics.path "client.opt_lock.provisioned_throughput_exceeded") m pure Nothing handleErr _ = pure Nothing diff --git a/services/cargohold/src/CargoHold/AWS.hs b/services/cargohold/src/CargoHold/AWS.hs index f7d0fd2df40..e00063457af 100644 --- a/services/cargohold/src/CargoHold/AWS.hs +++ b/services/cargohold/src/CargoHold/AWS.hs @@ -70,9 +70,10 @@ data Env = Env makeLenses ''Env -- | Override the endpoint in the '_amazonkaEnv' with '_amazonkaDownloadEndpoint'. +-- TODO: Choose the correct s3 addressing style amazonkaEnvWithDownloadEndpoint :: Env -> AWS.Env amazonkaEnvWithDownloadEndpoint e = - AWS.override (setAWSEndpoint (e ^. amazonkaDownloadEndpoint)) (e ^. amazonkaEnv) + AWS.overrideService (setAWSEndpoint (e ^. amazonkaDownloadEndpoint)) (e ^. amazonkaEnv) setAWSEndpoint :: AWSEndpoint -> AWS.Service -> AWS.Service setAWSEndpoint e = AWS.setEndpoint (_awsSecure e) (_awsHost e) (_awsPort e) @@ -100,6 +101,7 @@ mkEnv :: Logger -> -- | S3 endpoint AWSEndpoint -> + AWS.S3AddressingStyle -> -- | Endpoint for downloading assets (for the external world) AWSEndpoint -> -- | Bucket @@ -107,9 +109,9 @@ mkEnv :: Maybe CloudFrontOpts -> Manager -> IO Env -mkEnv lgr s3End s3Download bucket cfOpts mgr = do +mkEnv lgr s3End s3AddrStyle s3Download bucket cfOpts mgr = do let g = Logger.clone (Just "aws.cargohold") lgr - e <- mkAwsEnv g (setAWSEndpoint s3End S3.defaultService) + e <- mkAwsEnv g (setAWSEndpoint s3End (S3.defaultService & AWS.service_s3AddressingStyle .~ s3AddrStyle)) cf <- mkCfEnv cfOpts pure (Env g bucket e s3Download cf) where @@ -118,11 +120,11 @@ mkEnv lgr s3End s3Download bucket cfOpts mgr = do mkAwsEnv g s3 = do baseEnv <- AWS.newEnv AWS.discover - <&> AWS.configure s3 + <&> AWS.configureService s3 pure $ baseEnv - { AWS.envLogger = awsLogger g, - AWS.envManager = mgr + { AWS.logger = awsLogger g, + AWS.manager = mgr } awsLogger g l = Logger.log g (mapLevel l) . Log.msg . toLazyByteString mapLevel AWS.Info = Logger.Info @@ -222,7 +224,7 @@ canRetry :: MonadIO m => Either AWS.Error a -> m Bool canRetry (Right _) = pure False canRetry (Left e) = case e of AWS.TransportError (HttpExceptionRequest _ ResponseTimeout) -> pure True - AWS.ServiceError se | se ^. AWS.serviceCode == AWS.ErrorCode "RequestThrottled" -> pure True + AWS.ServiceError se | se ^. AWS.serviceError_code == AWS.ErrorCode "RequestThrottled" -> pure True _ -> pure False retry5x :: (Monad m) => RetryPolicyM m diff --git a/services/cargohold/src/CargoHold/App.hs b/services/cargohold/src/CargoHold/App.hs index 83226c45cf4..b123ed739fc 100644 --- a/services/cargohold/src/CargoHold/App.hs +++ b/services/cargohold/src/CargoHold/App.hs @@ -46,6 +46,7 @@ module CargoHold.App ) where +import Amazonka (S3AddressingStyle (S3AddressingStylePath)) import Bilge (Manager, MonadHttp, RequestId (..), newManager, withResponse) import qualified Bilge import Bilge.RPC (HasRequestId (..)) @@ -97,9 +98,10 @@ newEnv o = do pure $ Env ama met lgr mgr def o loc initAws :: AWSOpts -> Logger -> Manager -> IO AWS.Env -initAws o l = AWS.mkEnv l (o ^. awsS3Endpoint) downloadEndpoint (o ^. awsS3Bucket) (o ^. awsCloudFront) +initAws o l = AWS.mkEnv l (o ^. awsS3Endpoint) addrStyle downloadEndpoint (o ^. awsS3Bucket) (o ^. awsCloudFront) where downloadEndpoint = fromMaybe (o ^. awsS3Endpoint) (o ^. awsS3DownloadEndpoint) + addrStyle = maybe S3AddressingStylePath unwrapS3AddressingStyle (o ^. awsS3AddressingStyle) initHttpManager :: Maybe S3Compatibility -> IO Manager initHttpManager s3Compat = diff --git a/services/cargohold/src/CargoHold/Options.hs b/services/cargohold/src/CargoHold/Options.hs index 3f709c1454a..7a96fdef700 100644 --- a/services/cargohold/src/CargoHold/Options.hs +++ b/services/cargohold/src/CargoHold/Options.hs @@ -20,6 +20,7 @@ module CargoHold.Options where +import Amazonka (S3AddressingStyle (..)) import qualified CargoHold.CloudFront as CF import Control.Lens hiding (Level) import Data.Aeson (FromJSON (..), withText) @@ -45,8 +46,48 @@ deriveFromJSON toOptionFieldName ''CloudFrontOpts makeLenses ''CloudFrontOpts +newtype OptS3AddressingStyle = OptS3AddressingStyle + { unwrapS3AddressingStyle :: S3AddressingStyle + } + deriving (Show) + +instance FromJSON OptS3AddressingStyle where + parseJSON = + withText "S3AddressingStyle" $ + fmap OptS3AddressingStyle . \case + "auto" -> pure S3AddressingStyleAuto + "path" -> pure S3AddressingStylePath + "virtual" -> pure S3AddressingStyleVirtual + other -> fail $ "invalid S3AddressingStyle: " <> show other + data AWSOpts = AWSOpts { _awsS3Endpoint :: !AWSEndpoint, + -- | S3 can either by addressed in path style, i.e. + -- https:////, or vhost style, i.e. + -- https://./. AWS's S3 offering has + -- deprecated path style addressing for S3 and completely disabled it for + -- buckets created after 30 Sep 2020: + -- https://aws.amazon.com/blogs/aws/amazon-s3-path-deprecation-plan-the-rest-of-the-story/ + -- + -- However other object storage providers (specially self-deployed ones like + -- MinIO) may not support vhost style addressing yet (or ever?). Users of + -- such buckets should configure this option to "path". + -- + -- Installations using S3 service provided by AWS, should use "auto", this + -- option will ensure that vhost style is only used when it is possible to + -- construct a valid hostname from the bucket name and the bucket name + -- doesn't contain a '.'. Having a '.' in the bucket name causes TLS + -- validation to fail, hence it is not used by default. + -- + -- Using "virtual" as an option is only useful in situations where vhost + -- style addressing must be used even if it is not possible to construct a + -- valid hostname from the bucket name or the S3 service provider can ensure + -- correct certificate is issued for bucket which contain one or more '.'s + -- in the name. + -- + -- When this option is unspecified, we default to path style addressing to + -- ensure smooth transition for older deployments. + _awsS3AddressingStyle :: !(Maybe OptS3AddressingStyle), -- | S3 endpoint for generating download links. Useful if Cargohold is configured to use -- an S3 replacement running inside the internal network (in which case internally we -- would use one hostname for S3, and when generating an asset link for a client app, we diff --git a/services/cargohold/src/CargoHold/S3.hs b/services/cargohold/src/CargoHold/S3.hs index 404abb79e8a..29137efe3a1 100644 --- a/services/cargohold/src/CargoHold/S3.hs +++ b/services/cargohold/src/CargoHold/S3.hs @@ -36,7 +36,7 @@ module CargoHold.S3 ) where -import Amazonka hiding (Error, ToByteString, (.=)) +import Amazonka hiding (Error) import Amazonka.S3 import Amazonka.S3.Lens import CargoHold.API.Error @@ -145,7 +145,7 @@ downloadV3 :: ExceptT Error App (ConduitM () ByteString (ResourceT IO) ()) downloadV3 (s3Key . mkKey -> key) = do env <- view aws - pure . flattenResourceT $ _streamBody . view getObjectResponse_body <$> AWS.execStream env req + pure . flattenResourceT $ view (getObjectResponse_body . _ResponseBody) <$> AWS.execStream env req where req :: Text -> GetObject req b = diff --git a/services/galley/src/Galley/Aws.hs b/services/galley/src/Galley/Aws.hs index 1ef921a7052..c1ee608b6fb 100644 --- a/services/galley/src/Galley/Aws.hs +++ b/services/galley/src/Galley/Aws.hs @@ -109,12 +109,12 @@ mkEnv lgr mgr opts = do mkAwsEnv g = do baseEnv <- AWS.newEnv AWS.discover - <&> AWS.configure (sqs (opts ^. awsEndpoint)) + <&> AWS.configureService (sqs (opts ^. awsEndpoint)) pure $ baseEnv - { AWS.envLogger = awsLogger g, - AWS.envRetryCheck = retryCheck, - AWS.envManager = mgr + { AWS.logger = awsLogger g, + AWS.retryCheck = retryCheck, + AWS.manager = mgr } awsLogger g l = Logger.log g (mapLevel l) . Logger.msg . toLazyByteString mapLevel AWS.Info = Logger.Info @@ -183,5 +183,5 @@ canRetry :: MonadIO m => Either AWS.Error a -> m Bool canRetry (Right _) = pure False canRetry (Left e) = case e of AWS.TransportError (HttpExceptionRequest _ ResponseTimeout) -> pure True - AWS.ServiceError se | se ^. AWS.serviceCode == AWS.ErrorCode "RequestThrottled" -> pure True + AWS.ServiceError se | se ^. AWS.serviceError_code == AWS.ErrorCode "RequestThrottled" -> pure True _ -> pure False diff --git a/services/gundeck/default.nix b/services/gundeck/default.nix index 3b4c5dc779b..917012e8feb 100644 --- a/services/gundeck/default.nix +++ b/services/gundeck/default.nix @@ -6,6 +6,7 @@ , aeson , aeson-pretty , amazonka +, amazonka-core , amazonka-sns , amazonka-sqs , async @@ -99,6 +100,7 @@ mkDerivation { libraryHaskellDepends = [ aeson amazonka + amazonka-core amazonka-sns amazonka-sqs async diff --git a/services/gundeck/gundeck.cabal b/services/gundeck/gundeck.cabal index 82f74e893e5..1a079e716f1 100644 --- a/services/gundeck/gundeck.cabal +++ b/services/gundeck/gundeck.cabal @@ -98,9 +98,10 @@ library build-depends: aeson >=2.0.1.0 - , amazonka >=1.3.7 - , amazonka-sns >=1.3.7 - , amazonka-sqs >=1.3.7 + , amazonka >=2 + , amazonka-core >=2 + , amazonka-sns >=2 + , amazonka-sqs >=2 , async >=2.0 , attoparsec >=0.10 , auto-update >=0.1 diff --git a/services/gundeck/src/Gundeck/Aws.hs b/services/gundeck/src/Gundeck/Aws.hs index 54c944dc880..ab9f7e839d6 100644 --- a/services/gundeck/src/Gundeck/Aws.hs +++ b/services/gundeck/src/Gundeck/Aws.hs @@ -54,8 +54,9 @@ module Gundeck.Aws ) where -import Amazonka (AWSRequest, AWSResponse, serviceAbbrev, serviceCode, serviceMessage, serviceStatus) +import Amazonka (AWSRequest, AWSResponse, serviceError_abbrev, serviceError_code, serviceError_message, serviceError_status) import qualified Amazonka as AWS +import qualified Amazonka.Data.Text as AWS import qualified Amazonka.SNS as SNS import qualified Amazonka.SNS.Lens as SNS import qualified Amazonka.SQS as SQS @@ -160,14 +161,14 @@ mkEnv lgr opts mgr = do mkAwsEnv g sqs sns = do baseEnv <- AWS.newEnv AWS.discover - <&> AWS.configure sqs - <&> AWS.configure (sns & set AWS.serviceTimeout (Just (AWS.Seconds 5))) + <&> AWS.configureService sqs + <&> AWS.configureService (sns & set AWS.service_timeout (Just (AWS.Seconds 5))) pure $ baseEnv - { AWS.envLogger = awsLogger g, - AWS.envRegion = opts ^. optAws . awsRegion, - AWS.envRetryCheck = retryCheck, - AWS.envManager = mgr + { AWS.logger = awsLogger g, + AWS.region = opts ^. optAws . awsRegion, + AWS.retryCheck = retryCheck, + AWS.manager = mgr } awsLogger g l = Logger.log g (mapLevel l) . Logger.msg . toLazyByteString @@ -240,8 +241,8 @@ updateEndpoint us tk arn = do Right _ -> pure () Left x@(AWS.ServiceError e) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isMetadataLengthError (e ^. serviceMessage) -> + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isMetadataLengthError (e ^. serviceError_message) -> throwM $ InvalidCustomData arn Left x -> throwM $ @@ -303,16 +304,16 @@ createEndpoint u tr arnEnv app token = do Nothing -> throwM NoEndpointArn Just s -> Right <$> readArn s Left x@(AWS.ServiceError e) - | is "SNS" 400 x && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode, - Just ep <- parseExistsError (e ^. serviceMessage) -> + | is "SNS" 400 x && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code, + Just ep <- parseExistsError (e ^. serviceError_message) -> pure (Left (EndpointInUse ep)) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isLengthError (e ^. serviceMessage) -> + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isLengthError (e ^. serviceError_message) -> pure (Left (TokenTooLong $ tokenLength token)) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isTokenError (e ^. serviceMessage) -> do + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isTokenError (e ^. serviceError_message) -> do debug $ msg @Text "InvalidParameter: InvalidToken" . field "response" (show x) @@ -409,19 +410,19 @@ publish arn txt attrs = do case res of Right _ -> pure (Right ()) Left x@(AWS.ServiceError e) - | is "SNS" 400 x && AWS.newErrorCode "EndpointDisabled" == e ^. serviceCode -> + | is "SNS" 400 x && AWS.newErrorCode "EndpointDisabled" == e ^. serviceError_code -> pure (Left (EndpointDisabled arn)) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isProtocolSizeError (e ^. serviceMessage) -> + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isProtocolSizeError (e ^. serviceError_message) -> pure (Left (PayloadTooLarge arn)) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isSnsSizeError (e ^. serviceMessage) -> + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isSnsSizeError (e ^. serviceError_message) -> pure (Left (PayloadTooLarge arn)) | is "SNS" 400 x - && AWS.newErrorCode "InvalidParameter" == e ^. serviceCode - && isArnError (e ^. serviceMessage) -> + && AWS.newErrorCode "InvalidParameter" == e ^. serviceError_code + && isArnError (e ^. serviceError_message) -> pure (Left (InvalidEndpoint arn)) Left x -> throwM (GeneralError x) where @@ -488,7 +489,7 @@ send :: AWSRequest r => AWS.Env -> r -> Amazon (AWSResponse r) send env r = either (throwM . GeneralError) pure =<< sendCatch env r is :: AWS.Abbrev -> Int -> AWS.Error -> Bool -is srv s (AWS.ServiceError e) = srv == e ^. serviceAbbrev && s == statusCode (e ^. serviceStatus) +is srv s (AWS.ServiceError e) = srv == e ^. serviceError_abbrev && s == statusCode (e ^. serviceError_status) is _ _ _ = False isTimeout :: MonadIO m => Either AWS.Error a -> m Bool