remote: heavy lifting

- By layus
  - `RemoteStoreT`, `RemoteStoreState` from #72

- By Ericson2314
  - Reorg, `MonadRemoteStore0`, `MonadRemoteStoreHandshake`,
    `PreStoreConfig`, better `greet`

- By ryantrinkle
  - Correctly detect when other side has hung up, throws
    `RemoteStoreError_Disconnected`

Co-Authored-By: Guillaume Maudoux <layus.on@gmail.com>
Co-Authored-By: John Ericson <John.Ericson@Obsidian.Systems>
Co-Authored-By: Ryan Trinkle <ryan@trinkle.org>
This commit is contained in:
sorki 2023-11-30 13:50:57 +01:00
parent 1bc4d0575d
commit a3c9530198
7 changed files with 423 additions and 179 deletions

View File

@ -193,7 +193,7 @@ tup a b = Serializer
data GetSerializerError customGetError
= SerializerError_GetFail String
| SerializerError_Get customGetError
deriving (Eq, Show)
deriving (Eq, Ord, Show)
-- | Helper for transforming nested Eithers
-- into @GetSerializerError@ wrapper

View File

@ -3,7 +3,9 @@
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Store.Remote
( addToStore
(
-- * Operations
addToStore
, addTextToStore
, addSignatures
, addIndirectRoot
@ -25,51 +27,124 @@ module System.Nix.Store.Remote
, queryPathFromHashPart
, queryMissing
, optimiseStore
, runStore
, syncWithGC
, verifyStore
, module System.Nix.Store.Types
, module System.Nix.Store.Remote.MonadStore
, module System.Nix.Store.Remote.Types
-- * Compat
, MonadStore
-- * Runners
, runStore
, runStoreOpts
, runStoreOptsTCP
) where
import Crypto.Hash (SHA256)
import Data.ByteString (ByteString)
import Data.Default.Class (Default(def))
import Data.Dependent.Sum (DSum((:=>)))
import Data.HashSet (HashSet)
import Data.Map (Map)
import Data.Text (Text)
import Data.Word (Word64)
import Network.Socket (Family, SockAddr(SockAddrUnix))
import System.Nix.Nar (NarSource)
import System.Nix.Derivation (Derivation)
import System.Nix.Store.Types (FileIngestionMethod(..), RepairMode(..))
import System.Nix.Build (BuildMode, BuildResult)
import System.Nix.Hash (NamedAlgo(..), BaseEncoding(Base16), decodeDigestWith)
import System.Nix.StorePath (StorePath, StorePathName, StorePathHashPart, InvalidPathError)
import System.Nix.StorePath (StoreDir(..), StorePath, StorePathName, StorePathHashPart, InvalidPathError)
import System.Nix.StorePath.Metadata (Metadata(..), StorePathTrust(..))
import qualified Data.Text
import qualified Control.Exception
import qualified Control.Monad
import qualified Data.Attoparsec.Text
import qualified Data.Text.Encoding
import qualified Data.Map.Strict
import qualified Data.Serialize.Put
import qualified Data.Set
import qualified Network.Socket
import qualified System.Nix.ContentAddress
import qualified System.Nix.Hash
import qualified System.Nix.Signature
import qualified System.Nix.StorePath
import System.Nix.Store.Remote.MonadStore
import System.Nix.Store.Remote.Protocol
import System.Nix.Store.Remote.MonadStore (MonadRemoteStore, getStoreDir, RemoteStoreError(RemoteStoreError_GetAddrInfoFailed))
import System.Nix.Store.Remote.Protocol (Run, runStoreSocket, runOp, runOpArgs, runOpArgsIO, simpleOp, simpleOpArgs)
import System.Nix.Store.Remote.Socket
import System.Nix.Store.Remote.Types
import Data.Serialize (get)
import System.Nix.Store.Remote.Serialize
import System.Nix.Store.Remote.Serialize (putDerivation)
import System.Nix.Store.Remote.Serialize.Prim
-- * Compat
type MonadStore = MonadRemoteStore
-- * Runners
runStore :: MonadStore a -> Run a
runStore = runStoreOpts defaultSockPath def
where
defaultSockPath :: String
defaultSockPath = "/nix/var/nix/daemon-socket/socket"
runStoreOpts
:: FilePath
-> StoreDir
-> MonadStore a
-> Run a
runStoreOpts socketPath =
runStoreOpts'
Network.Socket.AF_UNIX
(SockAddrUnix socketPath)
runStoreOptsTCP
:: String
-> Int
-> StoreDir
-> MonadStore a
-> Run a
runStoreOptsTCP host port sd code = do
Network.Socket.getAddrInfo
(Just Network.Socket.defaultHints)
(Just host)
(Just $ show port)
>>= \case
(sockAddr:_) ->
runStoreOpts'
(Network.Socket.addrFamily sockAddr)
(Network.Socket.addrAddress sockAddr)
sd
code
_ -> pure (Left RemoteStoreError_GetAddrInfoFailed, [])
runStoreOpts'
:: Family
-> SockAddr
-> StoreDir
-> MonadStore a
-> Run a
runStoreOpts' sockFamily sockAddr storeRootDir code =
Control.Exception.bracket
open
(Network.Socket.close . hasStoreSocket)
(flip runStoreSocket code)
where
open = do
soc <- Network.Socket.socket sockFamily Network.Socket.Stream 0
Network.Socket.connect soc sockAddr
pure PreStoreConfig
{ preStoreConfig_socket = soc
, preStoreConfig_dir = storeRootDir
}
-- * Operations
-- | Pack `Nar` and add it to the store.
addToStore
:: forall a

View File

@ -8,18 +8,22 @@ import Data.Serialize (Result(..))
import System.Nix.Store.Remote.Serialize.Prim (putByteString)
import System.Nix.Store.Remote.Serializer (LoggerSError, logger, runSerialT)
import System.Nix.Store.Remote.Socket (sockGet8, sockPut)
import System.Nix.Store.Remote.MonadStore (MonadStore, clearData)
import System.Nix.Store.Remote.Types (Logger(..), ProtoVersion, hasProtoVersion)
import System.Nix.Store.Remote.MonadStore (MonadRemoteStore0, RemoteStoreError(..), clearData, getData, getProtoVersion)
import System.Nix.Store.Remote.Types.Logger (Logger(..))
import System.Nix.Store.Remote.Types.ProtoVersion (HasProtoVersion(..), ProtoVersion)
import System.Nix.Store.Remote.Types.StoreConfig (HasStoreSocket(..))
import qualified Control.Monad
import qualified Control.Monad.Reader
import qualified Control.Monad.State.Strict
import qualified Data.Serialize.Get
import qualified Data.Serializer
processOutput :: MonadStore [Logger]
processOutput
:: ( HasProtoVersion r
, HasStoreSocket r
)
=> MonadRemoteStore0 r [Logger]
processOutput = do
protoVersion <- Control.Monad.Reader.asks hasProtoVersion
protoVersion <- getProtoVersion
sockGet8 >>= go . (decoder protoVersion)
where
decoder
@ -30,14 +34,19 @@ processOutput = do
Data.Serialize.Get.runGetPartial
(runSerialT protoVersion $ Data.Serializer.getS logger)
go :: Result (Either LoggerSError Logger) -> MonadStore [Logger]
go
:: ( HasProtoVersion r
, HasStoreSocket r
)
=> Result (Either LoggerSError Logger)
-> MonadRemoteStore0 r [Logger]
go (Done ectrl leftover) = do
Control.Monad.unless (leftover == mempty) $
-- TODO: throwError
error $ "Leftovers detected: '" ++ show leftover ++ "'"
protoVersion <- Control.Monad.Reader.asks hasProtoVersion
protoVersion <- getProtoVersion
case ectrl of
-- TODO: tie this with throwError and better error type
Left e -> error $ show e
@ -46,9 +55,9 @@ processOutput = do
e@(Logger_Error _) -> pure [e]
Logger_Last -> pure [Logger_Last]
Logger_Read _n -> do
(mdata, _) <- Control.Monad.State.Strict.get
mdata <- getData
case mdata of
Nothing -> throwError "No data to read provided"
Nothing -> throwError RemoteStoreError_NoDataProvided
Just part -> do
-- XXX: we should check/assert part size against n of (Read n)
sockPut $ putByteString part

View File

@ -1,58 +1,171 @@
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module System.Nix.Store.Remote.MonadStore
( MonadStore
, mapStoreDir
( RemoteStoreState(..)
, RemoteStoreError(..)
, WorkerError(..)
, RemoteStoreT
, runRemoteStoreT
, mapStoreConfig
, MonadRemoteStore0
, MonadRemoteStore
, MonadRemoteStoreHandshake
-- *
, getStoreDir
, getLog
, flushLog
, getStoreSocket
, getProtoVersion
-- *
, appendLogs
, getLogs
, flushLogs
, gotError
, getErrors
-- *
, getData
, setData
, clearData
) where
import Control.Monad.Except (ExceptT)
import Control.Monad.Reader (ReaderT, asks)
import Control.Monad.Reader.Class (MonadReader)
import Control.Monad.State.Strict (StateT, gets, modify)
import Control.Monad.Except (MonadError)
import Control.Monad.IO.Class (MonadIO)
import Control.Monad.Reader (MonadReader, ask)
import Control.Monad.State.Strict (get, modify)
import Control.Monad.Trans (MonadTrans, lift)
import Control.Monad.Trans.State.Strict (StateT, runStateT, mapStateT)
import Control.Monad.Trans.Except (ExceptT, runExceptT, mapExceptT)
import Control.Monad.Trans.Reader (ReaderT, runReaderT, withReaderT)
import Data.ByteString (ByteString)
import Control.Monad.Trans.State.Strict (mapStateT)
import Control.Monad.Trans.Except (mapExceptT)
import Control.Monad.Trans.Reader (withReaderT)
import Data.Word (Word64)
import Network.Socket (Socket)
import System.Nix.StorePath (HasStoreDir(..), StoreDir)
import System.Nix.Store.Remote.Serializer (SError)
import System.Nix.Store.Remote.Types.Logger (Logger, isError)
import System.Nix.Store.Remote.Types.StoreConfig (StoreConfig(..))
import System.Nix.Store.Remote.Types.ProtoVersion (HasProtoVersion(..), ProtoVersion)
import System.Nix.Store.Remote.Types.StoreConfig (HasStoreSocket(..), PreStoreConfig, StoreConfig)
data RemoteStoreState = RemoteStoreState {
remoteStoreState_logs :: [Logger]
, remoteStoreState_mData :: Maybe ByteString
} deriving (Eq, Ord, Show)
data RemoteStoreError
= RemoteStoreError_Fixme String
| RemoteStoreError_BuildFailed
| RemoteStoreError_ClientVersionTooOld
| RemoteStoreError_Disconnected
| RemoteStoreError_GetAddrInfoFailed
| RemoteStoreError_SerializerGet SError
| RemoteStoreError_SerializerPut SError
| RemoteStoreError_NoDataProvided
| RemoteStoreError_ProtocolMismatch
| RemoteStoreError_WorkerMagic2Mismatch
| RemoteStoreError_WorkerError WorkerError
deriving (Eq, Show, Ord)
-- | Non-fatal (to server) errors in worker interaction
data WorkerError
= WorkerError_SendClosed
| WorkerError_InvalidOperation Word64
| WorkerError_NotYetImplemented
deriving (Eq, Ord, Show)
newtype RemoteStoreT r m a = RemoteStoreT
{ _unRemoteStoreT
:: ExceptT RemoteStoreError
(StateT RemoteStoreState
(ReaderT r m)) a
}
deriving
( Functor
, Applicative
, Monad
, MonadReader r
--, MonadState StoreState -- Avoid making the internal state explicit
--, MonadFail
, MonadError RemoteStoreError
, MonadIO
)
instance MonadTrans (RemoteStoreT r) where
lift = RemoteStoreT . lift . lift . lift
-- | Runner for @RemoteStoreT@
runRemoteStoreT
:: ( HasStoreDir r
, HasStoreSocket r
, Monad m
)
=> r
-> RemoteStoreT r m a
-> m (Either RemoteStoreError a, [Logger])
runRemoteStoreT r =
fmap (\(res, RemoteStoreState{..}) -> (res, remoteStoreState_logs))
. (`runReaderT` r)
. (`runStateT` emptyState)
. runExceptT
. _unRemoteStoreT
where
emptyState = RemoteStoreState
{ remoteStoreState_logs = mempty
, remoteStoreState_mData = Nothing
}
type MonadRemoteStore0 r = RemoteStoreT r IO
type MonadRemoteStore = MonadRemoteStore0 StoreConfig
type MonadRemoteStoreHandshake = MonadRemoteStore0 PreStoreConfig
mapStoreConfig
:: (rb -> ra)
-> (MonadRemoteStore0 ra a -> MonadRemoteStore0 rb a)
mapStoreConfig f =
RemoteStoreT
. ( mapExceptT
. mapStateT
. withReaderT
) f
. _unRemoteStoreT
-- | Ask for a @StoreDir@
getStoreDir :: (HasStoreDir r, MonadReader r m) => m StoreDir
getStoreDir = asks hasStoreDir
getStoreDir :: HasStoreDir r => MonadRemoteStore0 r StoreDir
getStoreDir = hasStoreDir <$> RemoteStoreT ask
type MonadStore a
= ExceptT
String
(StateT (Maybe ByteString, [Logger]) (ReaderT StoreConfig IO))
a
-- | Ask for a @StoreDir@
getStoreSocket :: HasStoreSocket r => MonadRemoteStore0 r Socket
getStoreSocket = hasStoreSocket <$> RemoteStoreT ask
-- | For lying about the store dir in tests
mapStoreDir :: (StoreDir -> StoreDir) -> (MonadStore a -> MonadStore a)
mapStoreDir f = mapExceptT . mapStateT . withReaderT
$ \c@StoreConfig { storeConfig_dir = sd } -> c { storeConfig_dir = f sd }
-- | Ask for a @StoreDir@
getProtoVersion :: HasProtoVersion r => MonadRemoteStore0 r ProtoVersion
getProtoVersion = hasProtoVersion <$> RemoteStoreT ask
gotError :: MonadStore Bool
gotError = gets (any isError . snd)
gotError :: MonadRemoteStore0 r Bool
gotError = any isError <$> getLogs
getErrors :: MonadStore [Logger]
getErrors = gets (filter isError . snd)
getErrors :: MonadRemoteStore0 r [Logger]
getErrors = filter isError <$> getLogs
getLog :: MonadStore [Logger]
getLog = gets snd
-- *
flushLog :: MonadStore ()
flushLog = modify (\(a, _b) -> (a, []))
appendLogs :: [Logger] -> MonadRemoteStore0 r ()
appendLogs x = RemoteStoreT
$ modify
$ \s -> s { remoteStoreState_logs = remoteStoreState_logs s <> x }
setData :: ByteString -> MonadStore ()
setData x = modify (\(_, b) -> (Just x, b))
getLogs :: MonadRemoteStore0 r [Logger]
getLogs = remoteStoreState_logs <$> RemoteStoreT get
clearData :: MonadStore ()
clearData = modify (\(_, b) -> (Nothing, b))
flushLogs :: MonadRemoteStore0 r ()
flushLogs = RemoteStoreT $ modify $ \s -> s { remoteStoreState_logs = mempty }
-- *
getData :: MonadRemoteStore0 r (Maybe ByteString)
getData = remoteStoreState_mData <$> RemoteStoreT get
setData :: ByteString -> MonadRemoteStore0 r ()
setData x = RemoteStoreT $ modify $ \s -> s { remoteStoreState_mData = pure x }
clearData :: MonadRemoteStore0 r ()
clearData = RemoteStoreT $ modify $ \s -> s { remoteStoreState_mData = Nothing }

View File

@ -1,42 +1,29 @@
{-# LANGUAGE OverloadedStrings #-}
module System.Nix.Store.Remote.Protocol
( WorkerOp(..)
( Run
, simpleOp
, simpleOpArgs
, runOp
, runOpArgs
, runOpArgsIO
, runStore
, runStoreOpts
, runStoreOptsTCP
, runStoreOpts'
, runStoreSocket
, ourProtoVersion
, GCAction(..)
) where
import qualified Control.Monad
import Control.Exception ( bracket )
import Control.Monad.Except
import Control.Monad.Reader (asks, runReaderT)
import Control.Monad.State.Strict
import Control.Monad (unless, when)
import Control.Monad.Except (throwError)
import Control.Monad.IO.Class (liftIO)
import Data.Serialize.Put (Put, runPut)
import Data.Default.Class (Default(def))
import qualified Data.Bool
import Data.Serialize.Get
import Data.Serialize.Put
import qualified Data.ByteString
import qualified Network.Socket.ByteString
import Network.Socket (SockAddr(SockAddrUnix))
import qualified Network.Socket as S
import Network.Socket.ByteString (recv, sendAll)
import System.Nix.StorePath (StoreDir(..))
import System.Nix.Store.Remote.Serialize.Prim
import System.Nix.Store.Remote.Logger
import System.Nix.Store.Remote.Logger (processOutput)
import System.Nix.Store.Remote.MonadStore
import System.Nix.Store.Remote.Socket
import System.Nix.Store.Remote.Serializer (protoVersion)
import System.Nix.Store.Remote.Socket (sockPutS, sockGetS)
import System.Nix.Store.Remote.Serializer (bool, enum, int, protoVersion, text)
import System.Nix.Store.Remote.Types
ourProtoVersion :: ProtoVersion
@ -50,28 +37,27 @@ workerMagic1 = 0x6e697863
workerMagic2 :: Int
workerMagic2 = 0x6478696f
defaultSockPath :: String
defaultSockPath = "/nix/var/nix/daemon-socket/socket"
type Run a = IO (Either RemoteStoreError a, [Logger])
simpleOp :: WorkerOp -> MonadStore Bool
simpleOp :: WorkerOp -> MonadRemoteStore Bool
simpleOp op = simpleOpArgs op $ pure ()
simpleOpArgs :: WorkerOp -> Put -> MonadStore Bool
simpleOpArgs :: WorkerOp -> Put -> MonadRemoteStore Bool
simpleOpArgs op args = do
runOpArgs op args
err <- gotError
Data.Bool.bool
sockGetBool
(sockGetS bool)
(do
-- TODO: don't use show
getErrors >>= throwError . show
getErrors >>= throwError . RemoteStoreError_Fixme . show
)
err
runOp :: WorkerOp -> MonadStore ()
runOp :: WorkerOp -> MonadRemoteStore ()
runOp op = runOpArgs op $ pure ()
runOpArgs :: WorkerOp -> Put -> MonadStore ()
runOpArgs :: WorkerOp -> Put -> MonadRemoteStore ()
runOpArgs op args =
runOpArgsIO
op
@ -79,76 +65,70 @@ runOpArgs op args =
runOpArgsIO
:: WorkerOp
-> ((Data.ByteString.ByteString -> MonadStore ()) -> MonadStore ())
-> MonadStore ()
-> ((Data.ByteString.ByteString -> MonadRemoteStore ())
-> MonadRemoteStore ()
)
-> MonadRemoteStore ()
runOpArgsIO op encoder = do
sockPutS enum op
sockPut $ putEnum op
soc <- asks storeConfig_socket
encoder (liftIO . sendAll soc)
soc <- getStoreSocket
encoder (liftIO . Network.Socket.ByteString.sendAll soc)
out <- processOutput
modify (\(a, b) -> (a, b <> out))
appendLogs out
err <- gotError
Control.Monad.when err $ do
when err $ do
-- TODO: don't use show
getErrors >>= throwError . show
getErrors >>= throwError . RemoteStoreError_Fixme . show
runStore :: MonadStore a -> IO (Either String a, [Logger])
runStore = runStoreOpts defaultSockPath def
runStoreSocket
:: PreStoreConfig
-> MonadRemoteStore a
-> Run a
runStoreSocket preStoreConfig code =
runRemoteStoreT preStoreConfig $ do
pv <- greet
mapStoreConfig
(\(PreStoreConfig a b) -> StoreConfig a pv b)
code
runStoreOpts
:: FilePath -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
runStoreOpts path = runStoreOpts' S.AF_UNIX (SockAddrUnix path)
where
greet :: MonadRemoteStoreHandshake ProtoVersion
greet = do
sockPutS int workerMagic1
runStoreOptsTCP
:: String -> Int -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
runStoreOptsTCP host port storeRootDir code = do
S.getAddrInfo (Just S.defaultHints) (Just host) (Just $ show port) >>= \case
(sockAddr:_) -> runStoreOpts' (S.addrFamily sockAddr) (S.addrAddress sockAddr) storeRootDir code
_ -> pure (Left "Couldn't resolve host and port with getAddrInfo.", [])
magic <- sockGetS int
unless
(magic == workerMagic2)
$ throwError RemoteStoreError_WorkerMagic2Mismatch
runStoreOpts'
:: S.Family -> S.SockAddr -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
runStoreOpts' sockFamily sockAddr storeRootDir code =
bracket open (S.close . storeConfig_socket) run
daemonVersion <- sockGetS protoVersion
where
open = do
soc <- S.socket sockFamily S.Stream 0
S.connect soc sockAddr
pure StoreConfig
{ storeConfig_dir = storeRootDir
, storeConfig_protoVersion = ourProtoVersion
, storeConfig_socket = soc
}
when (daemonVersion < ProtoVersion 1 10)
$ throwError RemoteStoreError_ClientVersionTooOld
greet = do
sockPut $ putInt workerMagic1
soc <- asks hasStoreSocket
vermagic <- liftIO $ recv soc 16
let
eres =
flip runGet vermagic
$ (,)
<$> (getInt :: Get Int)
<*> (getInt :: Get Int)
sockPutS protoVersion ourProtoVersion
case eres of
Left err -> error $ "Error parsing vermagic " ++ err
Right (magic2, _daemonProtoVersion) -> do
Control.Monad.unless (magic2 == workerMagic2) $ error "Worker magic 2 mismatch"
when (daemonVersion >= ProtoVersion 1 14)
$ sockPutS int (0 :: Int) -- affinity, obsolete
pv <- asks hasProtoVersion
sockPutS @() protoVersion pv -- clientVersion
sockPut $ putInt (0 :: Int) -- affinity
sockPut $ putInt (0 :: Int) -- obsolete reserveSpace
when (daemonVersion >= ProtoVersion 1 11) $ do
sockPutS bool False -- reserveSpace, obsolete
processOutput
-- not quite right, should be min of the two
-- as well as two ^ above
when (ourProtoVersion >= ProtoVersion 1 33) $ do
-- If we were buffering I/O, we would flush the output here.
_daemonNixVersion <- sockGetS text
return ()
run sock =
fmap (\(res, (_data, logs)) -> (res, logs))
$ (`runReaderT` sock)
$ (`runStateT` (Nothing, []))
$ runExceptT (greet >> code)
-- TODO do something with it
-- TODO patter match better
_ <- mapStoreConfig
(\(PreStoreConfig a b) -> StoreConfig a ourProtoVersion b)
processOutput
-- TODO should be minimum of
-- ourProtoVersion vs daemonVersion
pure ourProtoVersion

View File

@ -1,19 +1,21 @@
module System.Nix.Store.Remote.Socket where
import Control.Monad.Except (throwError)
import Control.Monad.Reader (asks)
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Reader (MonadReader, ask, asks)
import Data.ByteString (ByteString)
import Data.HashSet (HashSet)
import Data.Serialize.Get (Get, Result(..))
import Data.Serialize.Put
import Data.Serialize.Put (Put, runPut)
import Network.Socket.ByteString (recv, sendAll)
import System.Nix.StorePath (StorePath)
import System.Nix.Store.Remote.MonadStore
import System.Nix.Store.Remote.Serializer (NixSerializer, runP)
import System.Nix.Store.Remote.Serialize.Prim
import System.Nix.Store.Remote.Types
import System.Nix.StorePath (HasStoreDir, StorePath)
import System.Nix.Store.Remote.MonadStore (MonadRemoteStore0, RemoteStoreError(..), getStoreDir, getStoreSocket)
import System.Nix.Store.Remote.Serializer (NixSerializer, SError, runP, runSerialT)
import System.Nix.Store.Remote.Serialize.Prim (getInt, getByteString, getByteStrings, getPath, getPathsOrFail)
import System.Nix.Store.Remote.Types (HasStoreSocket(..))
import qualified Data.ByteString
import qualified Data.Serializer
import qualified Data.Serialize.Get
genericIncremental
@ -31,57 +33,116 @@ genericIncremental getsome parser = do
go (k chunk)
go (Fail msg _leftover) = error msg
getSocketIncremental :: Get a -> MonadStore a
getSocketIncremental = genericIncremental sockGet8
sockGet8 :: MonadStore ByteString
sockGet8
:: HasStoreSocket r
=> MonadRemoteStore0 r ByteString
sockGet8 = do
soc <- asks hasStoreSocket
soc <- getStoreSocket
liftIO $ recv soc 8
sockPut :: Put -> MonadStore ()
sockPut
:: HasStoreSocket r
=> Put
-> MonadRemoteStore0 r ()
sockPut p = do
soc <- asks hasStoreSocket
soc <- getStoreSocket
liftIO $ sendAll soc $ runPut p
sockPutS
:: Show e
=> NixSerializer ProtoVersion e a
:: ( MonadReader r m
, MonadError RemoteStoreError m
, MonadIO m
, HasStoreSocket r
)
=> NixSerializer r SError a
-> a
-> MonadStore ()
-> m ()
sockPutS s a = do
soc <- asks hasStoreSocket
pv <- asks hasProtoVersion
case runP s pv a of
Right x -> liftIO $ sendAll soc x
-- TODO: errors
Left e -> throwError $ show e
r <- ask
case runP s r a of
Right x -> liftIO $ sendAll (hasStoreSocket r) x
Left e -> throwError $ RemoteStoreError_SerializerPut e
sockGet :: Get a -> MonadStore a
sockGetS
:: forall r m a
. ( HasStoreSocket r
, MonadError RemoteStoreError m
, MonadReader r m
, MonadIO m
)
=> NixSerializer r SError a
-> m a
sockGetS s = do
r <- ask
res <- genericIncremental sockGet8'
$ runSerialT r $ Data.Serializer.getS s
case res of
Right x -> pure x
Left e -> throwError $ RemoteStoreError_SerializerGet e
where
sockGet8' :: MonadError RemoteStoreError m => m ByteString
sockGet8' = do
soc <- asks hasStoreSocket
result <- liftIO $ recv soc 8
if Data.ByteString.length result == 0
then throwError RemoteStoreError_Disconnected
else pure result
-- * Obsolete
getSocketIncremental
:: HasStoreSocket r
=> Get a
-> MonadRemoteStore0 r a
getSocketIncremental = genericIncremental sockGet8
sockGet
:: HasStoreSocket r
=> Get a
-> MonadRemoteStore0 r a
sockGet = getSocketIncremental
sockGetInt :: Integral a => MonadStore a
sockGetInt
:: ( HasStoreSocket r
, Integral a
)
=> MonadRemoteStore0 r a
sockGetInt = getSocketIncremental getInt
sockGetBool :: MonadStore Bool
sockGetBool
:: HasStoreSocket r
=> MonadRemoteStore0 r Bool
sockGetBool = (== (1 :: Int)) <$> sockGetInt
sockGetStr :: MonadStore ByteString
sockGetStr
:: HasStoreSocket r
=> MonadRemoteStore0 r ByteString
sockGetStr = getSocketIncremental getByteString
sockGetStrings :: MonadStore [ByteString]
sockGetStrings
:: HasStoreSocket r
=> MonadRemoteStore0 r [ByteString]
sockGetStrings = getSocketIncremental getByteStrings
sockGetPath :: MonadStore StorePath
sockGetPath
:: ( HasStoreDir r
, HasStoreSocket r
)
=> MonadRemoteStore0 r StorePath
sockGetPath = do
sd <- getStoreDir
pth <- getSocketIncremental (getPath sd)
either
(throwError . show)
(throwError . RemoteStoreError_Fixme . show)
pure
pth
sockGetPathMay :: MonadStore (Maybe StorePath)
sockGetPathMay
:: ( HasStoreDir r
, HasStoreSocket r
)
=> MonadRemoteStore0 r (Maybe StorePath)
sockGetPathMay = do
sd <- getStoreDir
pth <- getSocketIncremental (getPath sd)
@ -91,7 +152,11 @@ sockGetPathMay = do
Just
pth
sockGetPaths :: MonadStore (HashSet StorePath)
sockGetPaths
:: ( HasStoreDir r
, HasStoreSocket r
)
=> MonadRemoteStore0 r (HashSet StorePath)
sockGetPaths = do
sd <- getStoreDir
getSocketIncremental (getPathsOrFail sd)

View File

@ -29,7 +29,6 @@ import System.Nix.Build
import System.Nix.StorePath
import System.Nix.StorePath.Metadata
import System.Nix.Store.Remote
import System.Nix.Store.Remote.Protocol
import Crypto.Hash (SHA256)
import System.Nix.Nar (dumpPath)
@ -89,7 +88,7 @@ error: changing ownership of path '/run/user/1000/test-nix-store-06b0d249e561612
startDaemon
:: FilePath
-> IO (P.ProcessHandle, MonadStore a -> IO (Either String a, [Logger]))
-> IO (P.ProcessHandle, MonadStore a -> IO (Either RemoteStoreError a, [Logger]))
startDaemon fp = do
writeConf (fp </> "etc" </> "nix.conf")
p <- createProcessEnv fp "nix-daemon" []
@ -110,7 +109,7 @@ enterNamespaces = do
writeGroupMappings Nothing [GroupMapping 0 gid 1] True
withNixDaemon
:: ((MonadStore a -> IO (Either String a, [Logger])) -> IO a) -> IO a
:: ((MonadStore a -> IO (Either RemoteStoreError a, [Logger])) -> IO a) -> IO a
withNixDaemon action =
withSystemTempDirectory "test-nix-store" $ \path -> do
@ -213,7 +212,10 @@ spec_protocol = Hspec.around withNixDaemon $
itRights "validates path" $ withPath $ \path -> do
liftIO $ print path
isValidPathUncached path `shouldReturn` True
itLefts "fails on invalid path" $ mapStoreDir (\_ -> StoreDir "/asdf") $ isValidPathUncached invalidPath
itLefts "fails on invalid path"
$ mapStoreConfig
(\sc -> sc { storeConfig_dir = StoreDir "/asdf" })
$ isValidPathUncached invalidPath
context "queryAllValidPaths" $ do
itRights "empty query" queryAllValidPaths