From 45a73e5c58f39904249ec489cd662cface17b5af Mon Sep 17 00:00:00 2001 From: patrick brisbin Date: Fri, 17 Oct 2025 14:31:36 -0400 Subject: [PATCH] fix: catch AuthError to an informative message Before, ```console % AWS_PROFILE=x stack exec -- stackctl cat CredentialChainExhausted ``` Now, ```console % AWS_PROFILE=x stack exec -- stackctl cat 2025-10-17 18:31:44 [error ] No AWS credentials were found in your environment. For details of where stackctl looks for credentials, see: https://hackage.haskell.org/package/amazonka-2.0/docs/Amazonka-Auth.html#v:discover exception=CredentialChainExhausted ``` --- src/Stackctl/AWS/Core.hs | 19 +++++++++++++++++++ src/Stackctl/CLI.hs | 10 +++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/Stackctl/AWS/Core.hs b/src/Stackctl/AWS/Core.hs index eff7c88..1cd8657 100644 --- a/src/Stackctl/AWS/Core.hs +++ b/src/Stackctl/AWS/Core.hs @@ -12,6 +12,7 @@ module Stackctl.AWS.Core , withAssumedRole -- * Error-handling + , handlingAuthError , handlingServiceError , formatServiceError @@ -38,6 +39,7 @@ import Amazonka , serviceError_code , serviceError_message , serviceError_requestId + , _AuthError , _Sensitive , _ServiceError ) @@ -49,6 +51,7 @@ import qualified Amazonka.Env as Amazonka import Amazonka.STS.AssumeRole import Control.Monad.AWS import Control.Monad.Logger (defaultLoc, toLogStr) +import qualified Data.Text as T import Data.Typeable (typeRep) import Stackctl.AWS.Orphans () import UnliftIO.Exception.Lens (handling) @@ -149,6 +152,22 @@ newtype AccountId = AccountId } deriving newtype (Eq, Ord, Show, ToJSON) +-- | Handle 'AuthError', log it and 'exitFailure' +handlingAuthError :: (MonadUnliftIO m, MonadLogger m) => m a -> m a +handlingAuthError = + handling _AuthError $ \e -> do + logError $ msg :# ["exception" .= displayException e] + exitFailure + where + msg = + "No AWS credentials were found in your environment." + <> continuation "For details of where stackctl looks for credentials, see:" + <> continuation + "https://hackage.haskell.org/package/amazonka-2.0/docs/Amazonka-Auth.html#v:discover" + + -- Shift the continuation lines to line up with the first message line + continuation x = "\n" <> T.replicate 32 " " <> x + -- | Handle 'ServiceError', log it and 'exitFailure' -- -- This is useful at the top-level of the app, where we'd be crashing anyway. It diff --git a/src/Stackctl/CLI.hs b/src/Stackctl/CLI.hs index 475f246..e12671e 100644 --- a/src/Stackctl/CLI.hs +++ b/src/Stackctl/CLI.hs @@ -13,6 +13,7 @@ import Control.Monad.AWS as AWS import Control.Monad.AWS.ViaReader as AWS import Control.Monad.Catch (MonadCatch) import Control.Monad.Trans.Resource (MonadResource, ResourceT, runResourceT) +import Stackctl.AWS.Core (handlingAuthError) import qualified Stackctl.AWS.Core as AWS import Stackctl.AWS.Scope import Stackctl.AutoSSO @@ -103,9 +104,12 @@ runAppT options f = do envLogSettings withLogger logSettings $ \appLogger -> do - appAwsEnv <- runWithLogger appLogger $ handleAutoSSO options $ do - logDebug "Discovering AWS credentials" - AWS.discover + appAwsEnv <- runWithLogger appLogger + $ handleAutoSSO options + $ handlingAuthError + $ do + logDebug "Discovering AWS credentials" + AWS.discover appConfig <- runWithLogger appLogger loadConfigOrExit appAwsScope <- AWS.runEnvT fetchAwsScope appAwsEnv