module Ecluse.Proxy (
runProxy,
runServer,
runWorker,
npmServerConfig,
mountBindingFor,
unconfiguredRegistry,
planCveSync,
CveSyncHandle (..),
cveRuleDepsFor,
cveSyncReady,
cveSyncScheduleFor,
) where
import Amazonka qualified as AWS
import Data.Map.Strict qualified as Map
import Data.Text qualified as T
import Data.Time (getCurrentTime)
import GHC.Conc (getNumCapabilities)
import Katip (SimpleLogPayload, katipAddNamespace, runKatipContextT)
import Network.HTTP.Client (Manager, newManager)
import Network.HTTP.Client.TLS (tlsManagerSettings)
import System.Directory (createDirectoryIfMissing, listDirectory, removeFile)
import System.FilePath (isExtensionOf, (</>))
import UnliftIO (concurrently_, race_, throwIO)
import UnliftIO.Async (mapConcurrently_)
import UnliftIO.Exception (catchAny)
import Ecluse.Boot
import Ecluse.Composition (
PublishTarget (ptCredentials, ptEcosystem, ptMirrorUrl),
connectionPoolSettings,
initCredentialProviders,
planMirrorQueue,
planMounts,
planPublishTargets,
renderBootError,
)
import Ecluse.Composition qualified as Composition
import Ecluse.Config (
AppConfig (cfgCveDbPollInterval, cfgMaxOsvDbBytes, cfgMounts, cfgOsvDataDir, cfgPort, cfgPrivateConnectionsPerHost, cfgPublicConnectionsPerHost, cfgServeMaxInFlight, cfgShutdownDrainTimeout, cfgVulnerabilityDatabaseBucket),
)
import Ecluse.Core.Breaker (BreakerReporter)
import Ecluse.Core.Credential (AuthToken (..), currentToken)
import Ecluse.Core.Credential.Refresh (CredentialReporters (CredentialReporters, crBreakerReporter, crRefreshReporter))
import Ecluse.Core.Cve.Slot (CveSlot, newCveSlot, withSlotLookup)
import Ecluse.Core.Cve.Sync (SyncEnv (..), SyncSchedule (SyncSchedule, schedBootBackoff, schedPollDelay), bootBackoffDelays, runCveSync, s3CveFetch)
import Ecluse.Core.Ecosystem (Ecosystem (Npm), ecosystemName, parseEcosystem, prefixFor)
import Ecluse.Core.Osv.Schema (osvDbFileName)
import Ecluse.Core.Queue (MirrorQueue, newEnqueueBuffer)
import Ecluse.Core.Registry (
ParseError (..),
RegistryClient (..),
)
import Ecluse.Core.Registry.Metadata (fetchVersionDetails)
import Ecluse.Core.Registry.Npm (NpmClientConfig (NpmClientConfig, npmBaseUrl, npmLimits, npmManager, npmToken), newNpmPublishClient)
import Ecluse.Core.Registry.Npm.Route qualified as Npm
import Ecluse.Core.Registry.Npm.Serve (npmRenderer)
import Ecluse.Core.Rules (RuleDeps (..))
import Ecluse.Core.Security (defaultLimits)
import Ecluse.Core.Server.Admission (newServeAdmission)
import Ecluse.Core.Server.Cache (Source (Source), newMetadataCache)
import Ecluse.Core.Server.Context (PackumentDeps, PublishDeps, pdLimits, pdNow, pdPublicBaseUrl, pdRules)
import Ecluse.Core.Server.Metadata (ManifestCaching (Cached), newNpmMetadataClient)
import Ecluse.Core.Telemetry.Metrics (BreakerSource (CredentialMint, EffectfulRule), Provider (CodeArtifact), Upstream (Public))
import Ecluse.Core.Worker (WorkerPolicies, WorkerPolicy (..), runWorkerM, workerLoop)
import Ecluse.Env (Env, envDdContext, envLogEnv, envManager, envMetadataCache, envMetrics, envTelemetry, newWorkerHeartbeat, withEnvWithAdmission, workerRuntimeOf)
import Ecluse.Pilot.Export (buildS3Env)
import Ecluse.Server (MountBinding (..), ServerConfig (scCheckReady, scDrainTimeout, scPort), ShutdownDrainTimeout (ShutdownDrainTimeout), mkServerConfig)
import Ecluse.Server qualified as Server
import Ecluse.Telemetry.Correlation (ddPayloadNow)
import Ecluse.Telemetry.Instruments (metricsPortOf)
import Ecluse.Telemetry.Reporters (
deferredBreakerReporter,
deferredMirrorEnqueueFailure,
deferredRefreshReporter,
installMetrics,
newDeferredMetrics,
)
import Ecluse.Telemetry.Tracing (instrumentDataPlaneManagerSettings, tracingPortOf)
runProxy :: BootEnv -> IO ()
runProxy :: BootEnv -> IO ()
runProxy BootEnv
bootEnv = do
let env :: AppConfig
env = BootEnv -> AppConfig
beConfig BootEnv
bootEnv
let config :: Config
config = BootEnv -> Config
beConfigFull BootEnv
bootEnv
let logEnv :: LogEnv
logEnv = BootEnv -> LogEnv
beLogEnv BootEnv
bootEnv
let telemetry :: Telemetry
telemetry = BootEnv -> Telemetry
beTelemetry BootEnv
bootEnv
deferredMetrics <- IO DeferredMetrics
newDeferredMetrics
let credentialReporters =
CredentialReporters
{ crBreakerReporter :: BreakerReporter
crBreakerReporter = DeferredMetrics -> BreakerSource -> BreakerReporter
deferredBreakerReporter DeferredMetrics
deferredMetrics BreakerSource
CredentialMint
, crRefreshReporter :: RefreshReporter
crRefreshReporter = DeferredMetrics -> Provider -> RefreshReporter
deferredRefreshReporter DeferredMetrics
deferredMetrics Provider
CodeArtifact
}
providers <- initCredentialProviders credentialReporters env >>= orExit (T.unlines . map renderBootError)
cveSyncPlan <- planCveSync env
let ruleDepsFor = Map Ecosystem CveSyncHandle
-> BreakerReporter -> Ecosystem -> RuleDeps
cveRuleDepsFor Map Ecosystem CveSyncHandle
cveSyncPlan (DeferredMetrics -> BreakerSource -> BreakerReporter
deferredBreakerReporter DeferredMetrics
deferredMetrics BreakerSource
EffectfulRule)
bindings <- planMounts mountBindingFor getCurrentTime ruleDepsFor providers config >>= orExit (T.unlines . map renderBootError)
publishTargets <- orExit (T.unlines . map renderBootError) (planPublishTargets providers config)
queuePlan <- orExit (T.unlines . map renderBootError) (planMirrorQueue env)
capabilities <- getNumCapabilities
let (serveMaxInFlight, admissionLine) = Composition.resolveServeAdmission (cfgServeMaxInFlight env) capabilities
logBootInfo logEnv admissionLine
serveAdmission <- newServeAdmission serveMaxInFlight
fdLimit <- Composition.openFileSoftLimit
let (privateConnections, privateConnectionsLine) = Composition.resolvePrivateConnections (cfgPrivateConnectionsPerHost env) fdLimit
logBootInfo logEnv privateConnectionsLine
let (publicConnections, publicConnectionsLine) = Composition.resolvePublicConnections (cfgPublicConnectionsPerHost env) fdLimit
logBootInfo logEnv publicConnectionsLine
let serverConfig =
([MountBinding] -> ServerConfig
mkServerConfig [MountBinding]
bindings)
{ scPort = cfgPort env
, scDrainTimeout = ShutdownDrainTimeout (cfgShutdownDrainTimeout env)
, scCheckReady = cveSyncReady cveSyncPlan
}
logRuleBootOrder logEnv bindings
backendQueue <- buildMirrorQueue logEnv queuePlan
(queue, drainEnqueueBuffer) <-
bufferedMirrorHandOff (logBootWarning logEnv) (deferredMirrorEnqueueFailure deferredMetrics) backendQueue
metadataCache <- newMetadataCache (Composition.cacheConfigFor env)
heartbeat <- newWorkerHeartbeat
publicSettings <- instrumentDataPlaneManagerSettings telemetry tlsManagerSettings
privateSettings <- instrumentDataPlaneManagerSettings telemetry tlsManagerSettings
manager <- newManager (connectionPoolSettings publicConnections publicSettings)
privateManager <- newManager (connectionPoolSettings privateConnections privateSettings)
publishClient <- resolvePublishClient privateManager publishTargets
withEnvWithAdmission serveAdmission publishClient queue manager privateManager metadataCache logEnv telemetry heartbeat $ \Env
builtEnv -> do
DeferredMetrics -> Metrics -> IO ()
installMetrics DeferredMetrics
deferredMetrics (Env -> Metrics
envMetrics Env
builtEnv)
let syncTasks :: [IO ()]
syncTasks = Env -> SyncSchedule -> Map Ecosystem CveSyncHandle -> [IO ()]
cveSyncTasks Env
builtEnv (AppConfig -> SyncSchedule
cveSyncScheduleFor AppConfig
env) Map Ecosystem CveSyncHandle
cveSyncPlan
IO () -> IO () -> IO ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m ()
race_
(ServerConfig -> WorkerPolicies -> Env -> IO ()
runServices ServerConfig
serverConfig (Env -> [MountBinding] -> WorkerPolicies
workerPoliciesFor Env
builtEnv [MountBinding]
bindings) Env
builtEnv)
(IO () -> IO () -> IO ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m ()
concurrently_ IO ()
drainEnqueueBuffer ((IO () -> IO ()) -> [IO ()] -> IO ()
forall (m :: * -> *) (f :: * -> *) a b.
(MonadUnliftIO m, Foldable f) =>
(a -> m b) -> f a -> m ()
mapConcurrently_ IO () -> IO ()
forall a. a -> a
id [IO ()]
syncTasks))
bufferedMirrorHandOff :: (Text -> IO ()) -> IO () -> MirrorQueue -> IO (MirrorQueue, IO ())
bufferedMirrorHandOff :: (Text -> IO ()) -> IO () -> MirrorQueue -> IO (MirrorQueue, IO ())
bufferedMirrorHandOff Text -> IO ()
warn IO ()
countEnqueueFailure =
Int
-> (Int -> IO ())
-> (Int -> Text -> IO ())
-> MirrorQueue
-> IO (MirrorQueue, IO ())
newEnqueueBuffer
Int
Composition.mirrorEnqueueBufferDepth
( \Int
drops -> do
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int -> Bool
enqueueReportWorthy Int
drops) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
Text -> IO ()
warn (Text
"mirror enqueue buffer full: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
drops Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" job(s) dropped so far; each is re-enqueued on the next demand for its artifact")
IO ()
countEnqueueFailure
)
( \Int
failures Text
detail -> do
Bool -> IO () -> IO ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Int -> Bool
enqueueReportWorthy Int
failures) (IO () -> IO ()) -> IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
Text -> IO ()
warn (Text
"mirror enqueue delivery failed (" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Int -> Text
forall b a. (Show a, IsString b) => a -> b
show Int
failures Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
" so far): " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
detail)
IO ()
countEnqueueFailure
)
enqueueReportWorthy :: Int -> Bool
enqueueReportWorthy :: Int -> Bool
enqueueReportWorthy Int
n = Int
n Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
1 Bool -> Bool -> Bool
|| Int
n Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` Int
Composition.mirrorEnqueueReportInterval Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== Int
0
cveSyncTasks :: Env -> SyncSchedule -> Map.Map Ecosystem CveSyncHandle -> [IO ()]
cveSyncTasks :: Env -> SyncSchedule -> Map Ecosystem CveSyncHandle -> [IO ()]
cveSyncTasks Env
builtEnv SyncSchedule
schedule Map Ecosystem CveSyncHandle
plan =
[ LogEnv
-> SimpleLogPayload -> Namespace -> KatipContextT IO () -> IO ()
forall c (m :: * -> *) a.
LogItem c =>
LogEnv -> c -> Namespace -> KatipContextT m a -> m a
runKatipContextT (Env -> LogEnv
envLogEnv Env
builtEnv) (SimpleLogPayload
forall a. Monoid a => a
mempty :: SimpleLogPayload) Namespace
"cve-sync" (KatipContextT IO () -> IO ()) -> KatipContextT IO () -> IO ()
forall a b. (a -> b) -> a -> b
$
SyncEnv -> SyncSchedule -> IO () -> KatipContextT IO ()
forall (m :: * -> *).
(MonadUnliftIO m, KatipContext m) =>
SyncEnv -> SyncSchedule -> IO () -> m ()
runCveSync (CveSyncHandle -> SyncEnv
csEnv CveSyncHandle
handle) SyncSchedule
schedule (STM () -> IO ()
forall (m :: * -> *) a. MonadIO m => STM a -> m a
atomically (TVar Bool -> Bool -> STM ()
forall a. TVar a -> a -> STM ()
writeTVar (CveSyncHandle -> TVar Bool
csReady CveSyncHandle
handle) Bool
True))
| CveSyncHandle
handle <- Map Ecosystem CveSyncHandle -> [CveSyncHandle]
forall k a. Map k a -> [a]
Map.elems Map Ecosystem CveSyncHandle
plan
]
cveRuleDepsFor :: Map.Map Ecosystem CveSyncHandle -> BreakerReporter -> Ecosystem -> RuleDeps
cveRuleDepsFor :: Map Ecosystem CveSyncHandle
-> BreakerReporter -> Ecosystem -> RuleDeps
cveRuleDepsFor Map Ecosystem CveSyncHandle
plan BreakerReporter
reporter Ecosystem
eco =
RuleDeps
{ rdWithCveLookup :: forall a. (Maybe CveLookup -> IO a) -> IO a
rdWithCveLookup = ((Maybe CveLookup -> IO a) -> IO a)
-> (CveSyncHandle -> (Maybe CveLookup -> IO a) -> IO a)
-> Maybe CveSyncHandle
-> (Maybe CveLookup -> IO a)
-> IO a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe (\Maybe CveLookup -> IO a
use -> Maybe CveLookup -> IO a
use Maybe CveLookup
forall a. Maybe a
Nothing) (CveSlot -> (Maybe CveLookup -> IO a) -> IO a
forall a. CveSlot -> (Maybe CveLookup -> IO a) -> IO a
withSlotLookup (CveSlot -> (Maybe CveLookup -> IO a) -> IO a)
-> (CveSyncHandle -> CveSlot)
-> CveSyncHandle
-> (Maybe CveLookup -> IO a)
-> IO a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CveSyncHandle -> CveSlot
csSlot) (Ecosystem -> Map Ecosystem CveSyncHandle -> Maybe CveSyncHandle
forall k a. Ord k => k -> Map k a -> Maybe a
Map.lookup Ecosystem
eco Map Ecosystem CveSyncHandle
plan)
, rdBreakerReporter :: BreakerReporter
rdBreakerReporter = BreakerReporter
reporter
}
cveSyncReady :: Map.Map Ecosystem CveSyncHandle -> IO Bool
cveSyncReady :: Map Ecosystem CveSyncHandle -> IO Bool
cveSyncReady Map Ecosystem CveSyncHandle
plan = (CveSyncHandle -> IO Bool) -> [CveSyncHandle] -> IO Bool
forall (f :: * -> *) (m :: * -> *) a.
(Foldable f, Monad m) =>
(a -> m Bool) -> f a -> m Bool
allM (TVar Bool -> IO Bool
forall (m :: * -> *) a. MonadIO m => TVar a -> m a
readTVarIO (TVar Bool -> IO Bool)
-> (CveSyncHandle -> TVar Bool) -> CveSyncHandle -> IO Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. CveSyncHandle -> TVar Bool
csReady) (Map Ecosystem CveSyncHandle -> [CveSyncHandle]
forall k a. Map k a -> [a]
Map.elems Map Ecosystem CveSyncHandle
plan)
cveSyncScheduleFor :: AppConfig -> SyncSchedule
cveSyncScheduleFor :: AppConfig -> SyncSchedule
cveSyncScheduleFor AppConfig
env =
SyncSchedule
{ schedBootBackoff :: [Int]
schedBootBackoff = [Int]
bootBackoffDelays
, schedPollDelay :: Int
schedPollDelay = NominalDiffTime -> Int
forall b. Integral b => NominalDiffTime -> b
forall a b. (RealFrac a, Integral b) => a -> b
round (AppConfig -> NominalDiffTime
cfgCveDbPollInterval AppConfig
env) Int -> Int -> Int
forall a. Num a => a -> a -> a
* Int
1_000_000
}
data CveSyncHandle = CveSyncHandle
{ CveSyncHandle -> CveSlot
csSlot :: CveSlot
, CveSyncHandle -> TVar Bool
csReady :: TVar Bool
, CveSyncHandle -> SyncEnv
csEnv :: SyncEnv
}
planCveSync :: AppConfig -> IO (Map.Map Ecosystem CveSyncHandle)
planCveSync :: AppConfig -> IO (Map Ecosystem CveSyncHandle)
planCveSync AppConfig
appCfg = case AppConfig -> Maybe Text
cfgVulnerabilityDatabaseBucket AppConfig
appCfg of
Maybe Text
Nothing -> Map Ecosystem CveSyncHandle -> IO (Map Ecosystem CveSyncHandle)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Map Ecosystem CveSyncHandle
forall k a. Map k a
Map.empty
Just Text
bucket -> do
let dataDir :: String
dataDir = AppConfig -> String
cfgOsvDataDir AppConfig
appCfg
Bool -> String -> IO ()
createDirectoryIfMissing Bool
True String
dataDir
String -> IO ()
sweepStaleTemps String
dataDir
awsEnv <- AppConfig -> IO Env
buildS3Env AppConfig
appCfg
Map.fromList <$> traverse (cveSyncHandleFor appCfg awsEnv bucket) (Map.keys (cfgMounts appCfg))
cveSyncHandleFor :: AppConfig -> AWS.Env -> Text -> Ecosystem -> IO (Ecosystem, CveSyncHandle)
cveSyncHandleFor :: AppConfig
-> Env -> Text -> Ecosystem -> IO (Ecosystem, CveSyncHandle)
cveSyncHandleFor AppConfig
appCfg Env
awsEnv Text
bucket Ecosystem
eco = do
slot <- IO CveSlot
newCveSlot
ready <- newTVarIO False
let key = Text -> String
osvDbFileName (Ecosystem -> Text
ecosystemName Ecosystem
eco)
syncEnv =
SyncEnv
{ syncFetch :: CveFetch
syncFetch = Env -> Text -> Text -> Int -> CveFetch
s3CveFetch Env
awsEnv Text
bucket (String -> Text
forall a. ToText a => a -> Text
toText String
key) (AppConfig -> Int
cfgMaxOsvDbBytes AppConfig
appCfg)
, syncEcosystem :: Ecosystem
syncEcosystem = Ecosystem
eco
, syncDbPath :: String
syncDbPath = AppConfig -> String
cfgOsvDataDir AppConfig
appCfg String -> String -> String
</> String
key
, syncSlot :: CveSlot
syncSlot = CveSlot
slot
}
pure (eco, CveSyncHandle{csSlot = slot, csReady = ready, csEnv = syncEnv})
sweepStaleTemps :: FilePath -> IO ()
sweepStaleTemps :: String -> IO ()
sweepStaleTemps String
dataDir =
( do
entries <- String -> IO [String]
listDirectory String
dataDir
forM_ [e | e <- entries, "tmp" `isExtensionOf` e] (\String
e -> String -> IO ()
removeFile (String
dataDir String -> String -> String
</> String
e) IO () -> (SomeException -> IO ()) -> IO ()
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> (SomeException -> m a) -> m a
`catchAny` IO () -> SomeException -> IO ()
forall a b. a -> b -> a
const IO ()
forall (f :: * -> *). Applicative f => f ()
pass)
)
IO () -> (SomeException -> IO ()) -> IO ()
forall (m :: * -> *) a.
MonadUnliftIO m =>
m a -> (SomeException -> m a) -> m a
`catchAny` IO () -> SomeException -> IO ()
forall a b. a -> b -> a
const IO ()
forall (f :: * -> *). Applicative f => f ()
pass
runServices :: ServerConfig -> WorkerPolicies -> Env -> IO ()
runServices :: ServerConfig -> WorkerPolicies -> Env -> IO ()
runServices ServerConfig
serverConfig WorkerPolicies
policies Env
env = IO () -> IO () -> IO ()
forall (m :: * -> *) a b. MonadUnliftIO m => m a -> m b -> m ()
concurrently_ (ServerConfig -> Env -> IO ()
runServer ServerConfig
serverConfig Env
env) (WorkerPolicies -> Env -> IO ()
runWorker WorkerPolicies
policies Env
env)
runServer :: ServerConfig -> Env -> IO ()
runServer :: ServerConfig -> Env -> IO ()
runServer ServerConfig
cfg Env
env = ServerConfig -> IO Application -> IO ()
Server.runWarp ServerConfig
cfg (ServerConfig -> Env -> IO Application
Server.tracedApplication ServerConfig
cfg Env
env)
npmServerConfig :: ServerConfig
npmServerConfig :: ServerConfig
npmServerConfig = [MountBinding] -> ServerConfig
mkServerConfig [Maybe PackumentDeps -> Maybe PublishDeps -> MountBinding
npmMount Maybe PackumentDeps
forall a. Maybe a
Nothing Maybe PublishDeps
forall a. Maybe a
Nothing]
mountBindingFor :: Ecosystem -> Maybe PackumentDeps -> Maybe PublishDeps -> Maybe MountBinding
mountBindingFor :: Ecosystem
-> Maybe PackumentDeps -> Maybe PublishDeps -> Maybe MountBinding
mountBindingFor Ecosystem
eco Maybe PackumentDeps
packumentDeps Maybe PublishDeps
publishDeps = case Ecosystem
eco of
Ecosystem
Npm -> MountBinding -> Maybe MountBinding
forall a. a -> Maybe a
Just (Maybe PackumentDeps -> Maybe PublishDeps -> MountBinding
npmMount Maybe PackumentDeps
packumentDeps Maybe PublishDeps
publishDeps)
Ecosystem
_ -> Maybe MountBinding
forall a. Maybe a
Nothing
npmMount :: Maybe PackumentDeps -> Maybe PublishDeps -> MountBinding
npmMount :: Maybe PackumentDeps -> Maybe PublishDeps -> MountBinding
npmMount Maybe PackumentDeps
packumentDeps Maybe PublishDeps
publishDeps =
MountBinding
{ bindingPrefix :: NonEmpty Text
bindingPrefix = Ecosystem -> NonEmpty Text
prefixFor Ecosystem
Npm
, bindingClassifier :: Classifier
bindingClassifier = Classifier
Npm.classify
, bindingPackumentDeps :: Maybe PackumentDeps
bindingPackumentDeps = Maybe PackumentDeps
packumentDeps
, bindingPublishDeps :: Maybe PublishDeps
bindingPublishDeps = Maybe PublishDeps
publishDeps
, bindingRenderer :: MountRenderer
bindingRenderer = MountRenderer
npmRenderer
}
runWorker :: WorkerPolicies -> Env -> IO ()
runWorker :: WorkerPolicies -> Env -> IO ()
runWorker WorkerPolicies
policies Env
env = do
dd <- DdContext -> IO SimpleLogPayload
forall (m :: * -> *). MonadIO m => DdContext -> m SimpleLogPayload
ddPayloadNow (Env -> DdContext
envDdContext Env
env)
runWorkerM (envLogEnv env) dd (workerRuntimeOf policies env) (katipAddNamespace "worker" workerLoop)
workerPoliciesFor :: Env -> [MountBinding] -> WorkerPolicies
workerPoliciesFor :: Env -> [MountBinding] -> WorkerPolicies
workerPoliciesFor Env
env [MountBinding]
bindings =
[(Ecosystem, WorkerPolicy)] -> WorkerPolicies
forall k a. Ord k => [(k, a)] -> Map k a
Map.fromList
[ (Ecosystem
eco, Env -> PackumentDeps -> WorkerPolicy
workerPolicyFor Env
env PackumentDeps
deps)
| MountBinding
binding <- [MountBinding]
bindings
, let Text
prefixHead :| [Text]
_ = MountBinding -> NonEmpty Text
bindingPrefix MountBinding
binding
, Just Ecosystem
eco <- [Text -> Maybe Ecosystem
parseEcosystem Text
prefixHead]
, Just PackumentDeps
deps <- [MountBinding -> Maybe PackumentDeps
bindingPackumentDeps MountBinding
binding]
]
workerPolicyFor :: Env -> PackumentDeps -> WorkerPolicy
workerPolicyFor :: Env -> PackumentDeps -> WorkerPolicy
workerPolicyFor Env
env PackumentDeps
deps =
WorkerPolicy
{ wpResolveVersion :: PackageName -> Version -> IO VersionEvaluation
wpResolveVersion = MetadataClient -> PackageName -> Version -> IO VersionEvaluation
fetchVersionDetails MetadataClient
client
, wpRules :: [PreparedRule]
wpRules = PackumentDeps -> [PreparedRule]
pdRules PackumentDeps
deps
, wpNow :: IO UTCTime
wpNow = PackumentDeps -> IO UTCTime
pdNow PackumentDeps
deps
}
where
client :: MetadataClient
client =
TracingPort
-> MetricsPort
-> Upstream
-> ManifestCaching
-> (PackageName -> MetadataError -> IO ())
-> (PackageName -> [InvalidEntry] -> IO ())
-> (PackageName -> IO ())
-> NpmClientConfig
-> MetadataClient
newNpmMetadataClient
(Telemetry -> TracingPort
tracingPortOf (Env -> Telemetry
envTelemetry Env
env))
(Metrics -> MetricsPort
metricsPortOf (Env -> Metrics
envMetrics Env
env))
Upstream
Public
(MetadataCache -> Source -> ManifestCaching
Cached (Env -> MetadataCache
envMetadataCache Env
env) (Text -> Source
Source (PackumentDeps -> Text
pdPublicBaseUrl PackumentDeps
deps)))
(\PackageName
_ MetadataError
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
(\PackageName
_ [InvalidEntry]
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
(\PackageName
_ -> () -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ())
NpmClientConfig
{ npmBaseUrl :: Text
npmBaseUrl = PackumentDeps -> Text
pdPublicBaseUrl PackumentDeps
deps
, npmManager :: Manager
npmManager = Env -> Manager
envManager Env
env
, npmToken :: Maybe Secret
npmToken = Maybe Secret
forall a. Maybe a
Nothing
, npmLimits :: Limits
npmLimits = PackumentDeps -> Limits
pdLimits PackumentDeps
deps
}
resolvePublishClient :: Manager -> [PublishTarget] -> IO RegistryClient
resolvePublishClient :: Manager -> [PublishTarget] -> IO RegistryClient
resolvePublishClient Manager
manager [PublishTarget]
targets =
case (PublishTarget -> Bool) -> [PublishTarget] -> Maybe PublishTarget
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find ((Ecosystem -> Ecosystem -> Bool
forall a. Eq a => a -> a -> Bool
== Ecosystem
Npm) (Ecosystem -> Bool)
-> (PublishTarget -> Ecosystem) -> PublishTarget -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. PublishTarget -> Ecosystem
ptEcosystem) [PublishTarget]
targets of
Maybe PublishTarget
Nothing -> RegistryClient -> IO RegistryClient
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure RegistryClient
unconfiguredRegistry
Just PublishTarget
target -> do
let mintToken :: IO (Maybe Secret)
mintToken = Secret -> Maybe Secret
forall a. a -> Maybe a
Just (Secret -> Maybe Secret)
-> (AuthToken -> Secret) -> AuthToken -> Maybe Secret
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AuthToken -> Secret
authSecret (AuthToken -> Maybe Secret) -> IO AuthToken -> IO (Maybe Secret)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> CredentialProvider -> IO AuthToken
currentToken (PublishTarget -> CredentialProvider
ptCredentials PublishTarget
target)
NpmClientConfig -> IO (Maybe Secret) -> IO RegistryClient
newNpmPublishClient
NpmClientConfig
{ npmBaseUrl :: Text
npmBaseUrl = PublishTarget -> Text
ptMirrorUrl PublishTarget
target
, npmManager :: Manager
npmManager = Manager
manager
, npmToken :: Maybe Secret
npmToken = Maybe Secret
forall a. Maybe a
Nothing
, npmLimits :: Limits
npmLimits = Limits
defaultLimits
}
IO (Maybe Secret)
mintToken
data RegistryUnconfigured = RegistryUnconfigured
deriving stock (RegistryUnconfigured -> RegistryUnconfigured -> Bool
(RegistryUnconfigured -> RegistryUnconfigured -> Bool)
-> (RegistryUnconfigured -> RegistryUnconfigured -> Bool)
-> Eq RegistryUnconfigured
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RegistryUnconfigured -> RegistryUnconfigured -> Bool
== :: RegistryUnconfigured -> RegistryUnconfigured -> Bool
$c/= :: RegistryUnconfigured -> RegistryUnconfigured -> Bool
/= :: RegistryUnconfigured -> RegistryUnconfigured -> Bool
Eq, Int -> RegistryUnconfigured -> String -> String
[RegistryUnconfigured] -> String -> String
RegistryUnconfigured -> String
(Int -> RegistryUnconfigured -> String -> String)
-> (RegistryUnconfigured -> String)
-> ([RegistryUnconfigured] -> String -> String)
-> Show RegistryUnconfigured
forall a.
(Int -> a -> String -> String)
-> (a -> String) -> ([a] -> String -> String) -> Show a
$cshowsPrec :: Int -> RegistryUnconfigured -> String -> String
showsPrec :: Int -> RegistryUnconfigured -> String -> String
$cshow :: RegistryUnconfigured -> String
show :: RegistryUnconfigured -> String
$cshowList :: [RegistryUnconfigured] -> String -> String
showList :: [RegistryUnconfigured] -> String -> String
Show)
instance Exception RegistryUnconfigured
unconfiguredRegistry :: RegistryClient
unconfiguredRegistry :: RegistryClient
unconfiguredRegistry =
RegistryClient
{ fetchMetadata :: PackageName -> IO RegistryResponse
fetchMetadata = IO RegistryResponse -> PackageName -> IO RegistryResponse
forall a b. a -> b -> a
const IO RegistryResponse
forall a. IO a
refuse
, fetchArtifact :: PackageName -> Version -> IO RegistryResponse
fetchArtifact = \PackageName
_ Version
_ -> IO RegistryResponse
forall a. IO a
refuse
, publishArtifact :: PackageName
-> Version
-> MirrorArtifact
-> ByteString
-> IO (Either PublishFault ())
publishArtifact = \PackageName
_ Version
_ MirrorArtifact
_ ByteString
_ -> IO (Either PublishFault ())
forall a. IO a
refuse
, parsePackageInfo :: PackageName -> RegistryResponse -> Either ParseError PackageInfo
parsePackageInfo = \PackageName
_ RegistryResponse
_ -> ParseError -> Either ParseError PackageInfo
forall a b. a -> Either a b
Left ParseError
notConfigured
, parseVersionDetails :: RegistryResponse -> Version -> Either ParseError PackageDetails
parseVersionDetails = \RegistryResponse
_ Version
_ -> ParseError -> Either ParseError PackageDetails
forall a b. a -> Either a b
Left ParseError
notConfigured
, parseVersionList :: RegistryResponse -> Either ParseError [Version]
parseVersionList = Either ParseError [Version]
-> RegistryResponse -> Either ParseError [Version]
forall a b. a -> b -> a
const (ParseError -> Either ParseError [Version]
forall a b. a -> Either a b
Left ParseError
notConfigured)
}
where
refuse :: IO a
refuse :: forall a. IO a
refuse = RegistryUnconfigured -> IO a
forall (m :: * -> *) e a. (MonadIO m, Exception e) => e -> m a
throwIO RegistryUnconfigured
RegistryUnconfigured
notConfigured :: ParseError
notConfigured :: ParseError
notConfigured = ParseError{parseErrorMessage :: Text
parseErrorMessage = Text
"no registry backend configured"}