Skip to content

Commit

Permalink
Refactor HealthCheck/HealthStatus types
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Ospald committed Nov 13, 2020
1 parent d847bd0 commit f9ece70
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 118 deletions.
62 changes: 26 additions & 36 deletions lib/core/src/Cardano/Pool/Metadata.hs
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ module Cardano.Pool.Metadata
, StakePoolMetadataFetchLog (..)
, fetchDelistedPools
, healthCheck
, isHealthy
, HealthCheck (..)
, isHealthyStatus
, HealthStatusSMASH (..)

-- * Construct URLs
, identityUrlBuilder
Expand All @@ -40,7 +40,7 @@ import Cardano.BM.Data.Severity
import Cardano.BM.Data.Tracer
( HasPrivacyAnnotation (..), HasSeverityAnnotation (..) )
import Cardano.Wallet.Api.Types
( HealthCheck (..), defaultRecordTypeOptions )
( HealthStatusSMASH (..), defaultRecordTypeOptions )
import Cardano.Wallet.Primitive.AddressDerivation
( hex )
import Cardano.Wallet.Primitive.Types
Expand All @@ -53,7 +53,7 @@ import Cardano.Wallet.Primitive.Types
import Control.Exception
( IOException, handle )
import Control.Monad
( forM, forM_, when )
( forM, when )
import Control.Monad.IO.Class
( MonadIO (..) )
import Control.Monad.Trans.Except
Expand Down Expand Up @@ -180,24 +180,18 @@ registryUrlBuilder baseUrl pid _ hash =
{ uriPath = "/" <> metadaFetchEp pid hash
}

trMaybe
:: Maybe (Tracer IO StakePoolMetadataFetchLog)
-> StakePoolMetadataFetchLog
-> IO ()
trMaybe mtr m = forM_ mtr $ \tr -> traceWith tr m

-- | A smash GET request that reads the result at once into memory.
smashRequest
:: Maybe (Tracer IO StakePoolMetadataFetchLog)
:: Tracer IO StakePoolMetadataFetchLog
-> URI
-> Manager
-> ExceptT String IO ByteString
smashRequest mtr uri manager = getPayload
smashRequest tr uri manager = getPayload
where
getPayload :: ExceptT String IO ByteString
getPayload = do
req <- withExceptT show $ except $ requestFromURI uri
liftIO $ trMaybe mtr $ MsgFetchSMASH uri
liftIO $ traceWith tr $ MsgFetchSMASH uri
ExceptT
$ handle fromIOException
$ handle fromHttpException
Expand All @@ -216,35 +210,31 @@ smashRequest mtr uri manager = getPayload
fromHttpException :: Monad m => HttpException -> m (Either String a)
fromHttpException = return . Left . ("HTTP exception: " <>) . show

-- | Gets the health status from the SMASH server. Returns
-- @Nothing@ if the server is unreachable.
healthCheck
:: Maybe (Tracer IO StakePoolMetadataFetchLog)
:: Tracer IO StakePoolMetadataFetchLog
-> URI
-> Manager
-> IO (Maybe HealthCheck)
healthCheck mtr uri manager = runExceptTLog $ do
pl <- smashRequest mtr
-> IO (Maybe HealthStatusSMASH)
healthCheck tr uri manager = runExceptTLog $ do
pl <- smashRequest tr
(uri { uriPath = "/" <> healthCheckEP , uriQuery = "", uriFragment = "" })
manager
except . eitherDecodeStrict @HealthCheck $ pl
except . eitherDecodeStrict @HealthStatusSMASH $ pl
where
runExceptTLog
:: ExceptT String IO HealthCheck
-> IO (Maybe HealthCheck)
:: ExceptT String IO HealthStatusSMASH
-> IO (Maybe HealthStatusSMASH)
runExceptTLog action = runExceptT action >>= \case
Left msg ->
Nothing <$ trMaybe mtr (MsgFetchHealthCheckFailure msg)

Right health
| isHealthy health -> do
trMaybe mtr (MsgFetchHealthCheckFailure
("Server reports unhealthy status: " <> T.unpack (status health)))
pure $ Just health
| otherwise -> do
trMaybe mtr (MsgFetchHealthCheckSuccess health)
pure (Just health)

isHealthy :: HealthCheck -> Bool
isHealthy (HealthCheck {..})
Nothing <$ traceWith tr (MsgFetchHealthCheckFailure msg)
Right health -> do
traceWith tr (MsgFetchHealthCheckSuccess health)
pure $ Just health

isHealthyStatus :: HealthStatusSMASH -> Bool
isHealthyStatus (HealthStatusSMASH {..})
| T.toLower status /= "ok" = True
| otherwise = False

Expand All @@ -254,7 +244,7 @@ fetchDelistedPools
-> Manager
-> IO (Maybe [PoolId])
fetchDelistedPools tr uri manager = runExceptTLog $ do
pl <- smashRequest (Just tr) uri manager
pl <- smashRequest tr uri manager
smashPids <- except $ eitherDecodeStrict @[SMASHPoolId] pl
forM smashPids $ except . first getTextDecodingError . toPoolId
where
Expand Down Expand Up @@ -364,7 +354,7 @@ data StakePoolMetadataFetchLog
| MsgFetchDelistedPoolsFailure String
| MsgFetchDelistedPoolsSuccess [PoolId]
| MsgFetchHealthCheckFailure String
| MsgFetchHealthCheckSuccess HealthCheck
| MsgFetchHealthCheckSuccess HealthStatusSMASH
deriving (Show, Eq)

instance HasPrivacyAnnotation StakePoolMetadataFetchLog
Expand All @@ -377,7 +367,7 @@ instance HasSeverityAnnotation StakePoolMetadataFetchLog where
MsgFetchSMASH{} -> Debug
MsgFetchDelistedPoolsFailure{} -> Warning
MsgFetchDelistedPoolsSuccess{} -> Info
MsgFetchHealthCheckFailure{} -> Error
MsgFetchHealthCheckFailure{} -> Warning
MsgFetchHealthCheckSuccess{} -> Info

instance ToText StakePoolMetadataFetchLog where
Expand Down
6 changes: 3 additions & 3 deletions lib/core/src/Cardano/Wallet/Api.hs
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ import Cardano.Wallet.Api.Types
, ApiByronWallet
, ApiCoinSelectionT
, ApiFee
, ApiHealthCheck
, ApiMaintenanceAction
, ApiMaintenanceActionPostData
, ApiNetworkClock
Expand All @@ -148,6 +147,7 @@ import Cardano.Wallet.Api.Types
, ApiWalletPassphrase
, ApiWalletSignData
, ByronWalletPutPassphraseData
, HealthCheckSMASH
, Iso8601Time
, MinWithdrawal
, PostExternalTransactionData
Expand Down Expand Up @@ -725,12 +725,12 @@ type SMASH = GetCurrentSMASHHealth

type GetCurrentSMASHHealth = "smash"
:> "health"
:> Get '[JSON] ApiHealthCheck
:> Get '[JSON] (ApiT HealthCheckSMASH)

type GetURISmashHealth = "smash"
:> "health"
:> ReqBody '[JSON] (ApiT SmashServer)
:> Post '[JSON] ApiHealthCheck
:> Post '[JSON] (ApiT HealthCheckSMASH)

{-------------------------------------------------------------------------------
Proxy_
Expand Down
45 changes: 19 additions & 26 deletions lib/core/src/Cardano/Wallet/Api/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,8 @@ module Cardano.Wallet.Api.Types

-- * Others
, defaultRecordTypeOptions
, HealthCheck (..)
, ApiHealthCheck (..)
, HealthStatusSMASH (..)
, HealthCheckSMASH (..)
) where

import Prelude
Expand Down Expand Up @@ -2165,37 +2165,30 @@ type instance ApiWalletMigrationPostDataT (n :: NetworkDiscriminant) (s :: Symbo
-------------------------------------------------------------------------------}

-- | Parses the SMASH HealthCheck type from the SMASH API.
data HealthCheck = HealthCheck
{ status :: T.Text
, version :: T.Text
data HealthStatusSMASH = HealthStatusSMASH
{ status :: Text
, version :: Text
} deriving (Generic, Show, Eq, Ord)

instance FromJSON HealthCheck where
instance FromJSON HealthStatusSMASH where
parseJSON = genericParseJSON defaultRecordTypeOptions
instance ToJSON HealthCheck where
instance ToJSON HealthStatusSMASH where
toJSON = genericToJSON defaultRecordTypeOptions

data ApiHealthCheck =
Available -- server available
| Unavailable -- server reachable, but unavailable
| Unreachable -- could not get a response from the SMASH server
| NoSMASH -- no SMASH server has been configured
-- | Dscribes the health status of the SMASH server.
data HealthCheckSMASH =
Available -- server available
| Unavailable -- server reachable, but unavailable
| Unreachable -- could not get a response from the SMASH server
| NoSmashConfigured -- no SMASH server has been configured
deriving (Generic, Show, Eq, Ord)

instance FromJSON ApiHealthCheck where
parseJSON = withText "ApiHealthCheck" $ \txt -> do
case T.unpack txt of
"available" -> pure Available
"unavailable" -> pure Unavailable
"unreachable" -> pure Unreachable
"no_smash_configured" -> pure NoSMASH
e -> fail ("Unexpeced value: " <> e)

instance ToJSON ApiHealthCheck where
toJSON Available = String "available"
toJSON Unavailable = String "unavailable"
toJSON Unreachable = String "unreachable"
toJSON NoSMASH = String "no_smash_configured"
instance FromJSON (ApiT HealthCheckSMASH) where
parseJSON = fmap ApiT . genericParseJSON defaultSumTypeOptions
{ sumEncoding = UntaggedValue }

instance ToJSON (ApiT HealthCheckSMASH) where
toJSON = genericToJSON defaultSumTypeOptions . getApiT

instance FromJSON (ApiT SmashServer) where
parseJSON = parseJSON >=> either (fail . show . ShowFmt) (pure . ApiT) . fromText
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
{
"seed": 5135632696367642844,
"seed": -5187213199879941418,
"samples": [
"available",
"no_smash_configured",
"unreachable",
"no_smash_configured",
"available",
"unavailable",
"available",
"available",
"unavailable",
"no_smash_configured",
"unavailable",
"no_smash_configured",
"unreachable"
"unavailable",
"unavailable"
]
}
8 changes: 4 additions & 4 deletions lib/core/test/unit/Cardano/Wallet/Api/TypesSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import Cardano.Wallet.Api.Types
, ApiEpochInfo (..)
, ApiErrorCode (..)
, ApiFee (..)
, ApiHealthCheck (..)
, ApiMaintenanceAction (..)
, ApiMaintenanceActionPostData (..)
, ApiMnemonicT (..)
Expand Down Expand Up @@ -107,6 +106,7 @@ import Cardano.Wallet.Api.Types
, DecodeStakeAddress (..)
, EncodeAddress (..)
, EncodeStakeAddress (..)
, HealthCheckSMASH (..)
, Iso8601Time (..)
, NtpSyncingStatus (..)
, PostExternalTransactionData (..)
Expand Down Expand Up @@ -348,7 +348,7 @@ spec = do
jsonRoundtripAndGolden $ Proxy @ApiNetworkParameters
jsonRoundtripAndGolden $ Proxy @ApiNetworkClock
jsonRoundtripAndGolden $ Proxy @ApiWalletDelegation
jsonRoundtripAndGolden $ Proxy @ApiHealthCheck
jsonRoundtripAndGolden $ Proxy @(ApiT HealthCheckSMASH)
jsonRoundtripAndGolden $ Proxy @ApiWalletDelegationStatus
jsonRoundtripAndGolden $ Proxy @ApiWalletDelegationNext
jsonRoundtripAndGolden $ Proxy @(ApiT (Hash "Genesis"))
Expand Down Expand Up @@ -1719,7 +1719,7 @@ instance Arbitrary ApiAddressInspect where
, "stake_reference" .= Aeson.String stake
]

instance Arbitrary ApiHealthCheck where
instance Arbitrary HealthCheckSMASH where
arbitrary = genericArbitrary
shrink = genericShrink

Expand Down Expand Up @@ -1790,7 +1790,7 @@ instance ToSchema (ApiSelectCoinsData n) where
instance ToSchema (ApiT SmashServer) where
declareNamedSchema _ = declareSchemaForDefinition "ApiSmashServer"

instance ToSchema ApiHealthCheck where
instance ToSchema (ApiT HealthCheckSMASH) where
declareNamedSchema _ = declareSchemaForDefinition "ApiHealthCheck"

instance ToSchema (ApiSelectCoinsPayments n) where
Expand Down
2 changes: 1 addition & 1 deletion lib/shelley/src/Cardano/Wallet/Shelley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,7 @@ instance HasSeverityAnnotation ApplicationLog where
MsgStarting _ -> Info
MsgNetworkName _ -> Info
MsgServerStartupError _ -> Alert
MsgFailedConnectSMASH _ -> Error
MsgFailedConnectSMASH _ -> Warning

{-------------------------------------------------------------------------------
Tracers
Expand Down
12 changes: 6 additions & 6 deletions lib/shelley/src/Cardano/Wallet/Shelley/Api/Server.hs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import Prelude
import Cardano.Address
( unAddress )
import Cardano.Pool.Metadata
( defaultManagerSettings, healthCheck, isHealthy, newManager )
( defaultManagerSettings, healthCheck, isHealthyStatus, newManager )
import Cardano.Wallet
( ErrCreateRandomAddress (..)
, ErrNotASequentialWallet (..)
Expand Down Expand Up @@ -109,13 +109,13 @@ import Cardano.Wallet.Api.Types
, ApiAddressInspectData (..)
, ApiCredential (..)
, ApiErrorCode (..)
, ApiHealthCheck (..)
, ApiMaintenanceAction (..)
, ApiMaintenanceActionPostData (..)
, ApiSelectCoinsAction (..)
, ApiSelectCoinsData (..)
, ApiStakePool
, ApiT (..)
, HealthCheckSMASH (..)
, MaintenanceAction (..)
, SettingsPutData (..)
, SomeByronWalletPostData (..)
Expand Down Expand Up @@ -439,17 +439,17 @@ server byron icarus shelley spl ntp =
where
getHealth smashServer = liftIO $ do
manager <- newManager defaultManagerSettings
health <- healthCheck Nothing (unSmashServer smashServer) manager
pure $ maybe Unreachable
(\h -> if isHealthy h then Available else Unavailable) health
health <- healthCheck mempty (unSmashServer smashServer) manager
pure $ ApiT $ maybe Unreachable
(\h -> if isHealthyStatus h then Available else Unavailable) health

getUriSmashHealth (ApiT smashServer) = Handler $ getHealth smashServer

getCurrentSmashHealth = Handler $ do
settings' <- liftIO $ getSettings spl
case poolMetadataSource settings' of
FetchSMASH smashServer -> getHealth smashServer
_ -> pure NoSMASH
_ -> pure (ApiT NoSmashConfigured)

postAnyAddress
:: NetworkId
Expand Down
Loading

0 comments on commit f9ece70

Please sign in to comment.