Skip to content

Commit 5494cc3

Browse files
committed
remote: add AddToStoreNar client
1 parent d3408a6 commit 5494cc3

File tree

11 files changed

+242
-9
lines changed

11 files changed

+242
-9
lines changed

hnix-store-remote/hnix-store-remote.cabal

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ library
8585
, System.Nix.Store.Remote.Types.GC
8686
, System.Nix.Store.Remote.Types.Handshake
8787
, System.Nix.Store.Remote.Types.Logger
88+
, System.Nix.Store.Remote.Types.NoReply
8889
, System.Nix.Store.Remote.Types.ProtoVersion
8990
, System.Nix.Store.Remote.Types.Query
9091
, System.Nix.Store.Remote.Types.Query.Missing
@@ -196,6 +197,7 @@ test-suite remote-io
196197
ghc-options: -rtsopts -fprof-auto "-with-rtsopts -V0"
197198
other-modules:
198199
NixDaemonSpec
200+
, SampleNar
199201
build-depends:
200202
base >=4.12 && <5
201203
, hnix-store-core
@@ -206,6 +208,8 @@ test-suite remote-io
206208
, concurrency
207209
, containers
208210
, crypton
211+
, data-default-class
212+
, dependent-sum
209213
, directory
210214
, exceptions
211215
, filepath
@@ -216,5 +220,6 @@ test-suite remote-io
216220
, some
217221
, temporary
218222
, text
223+
, time
219224
, unix
220225
, unordered-containers

hnix-store-remote/src/System/Nix/Store/Remote/Client.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
module System.Nix.Store.Remote.Client
22
( addToStore
3+
, addToStoreNar
34
, addTextToStore
45
, addSignatures
56
, addTempRoot
@@ -28,10 +29,12 @@ module System.Nix.Store.Remote.Client
2829

2930
import Control.Monad (void, when)
3031
import Control.Monad.Except (throwError)
32+
import Data.ByteString (ByteString)
3133
import Data.HashSet (HashSet)
3234
import Data.Map (Map)
3335
import Data.Set (Set)
3436
import Data.Some (Some)
37+
import Data.Word (Word64)
3538

3639
import System.Nix.Build (BuildMode, BuildResult)
3740
import System.Nix.DerivedPath (DerivedPath)
@@ -73,6 +76,19 @@ addToStore name source method hashAlgo repair = do
7376
setNarSource source
7477
doReq (AddToStore name method hashAlgo repair)
7578

79+
addToStoreNar
80+
:: MonadRemoteStore m
81+
=> StorePath
82+
-> Metadata StorePath
83+
-> RepairMode
84+
-> CheckMode
85+
-> (Word64 -> IO(Maybe ByteString))
86+
-> m ()
87+
addToStoreNar path metadata repair checkSigs source = do
88+
setDataSource source
89+
void $ doReq (AddToStoreNar path metadata repair checkSigs)
90+
pure ()
91+
7692
-- | Add @StoreText@ to the store
7793
-- Reference accepts repair but only uses it
7894
-- to throw error in case of remote talking to nix-daemon.

hnix-store-remote/src/System/Nix/Store/Remote/Client/Core.hs

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,13 @@ module System.Nix.Store.Remote.Client.Core
77
import Control.Monad (unless, when)
88
import Control.Monad.Except (throwError)
99
import Control.Monad.IO.Class (MonadIO, liftIO)
10+
import Data.ByteString (ByteString)
1011
import Data.DList (DList)
1112
import Data.Some (Some(Some))
13+
import Data.Word (Word64)
14+
import Network.Socket (Socket)
1215
import System.Nix.Nar (NarSource)
16+
import System.Nix.StorePath.Metadata (Metadata(..))
1317
import System.Nix.Store.Remote.Logger (processOutput)
1418
import System.Nix.Store.Remote.MonadStore
1519
( MonadRemoteStore(..)
@@ -28,11 +32,13 @@ import System.Nix.Store.Remote.Serializer
2832
)
2933
import System.Nix.Store.Remote.Types.Handshake (ClientHandshakeOutput(..))
3034
import System.Nix.Store.Remote.Types.Logger (Logger)
35+
import System.Nix.Store.Remote.Types.NoReply (NoReply(..))
3136
import System.Nix.Store.Remote.Types.ProtoVersion (ProtoVersion(..))
3237
import System.Nix.Store.Remote.Types.StoreRequest (StoreRequest(..))
3338
import System.Nix.Store.Remote.Types.StoreReply (StoreReply(..))
3439
import System.Nix.Store.Remote.Types.WorkerMagic (WorkerMagic(..))
3540

41+
import qualified Data.ByteString
3642
import qualified Network.Socket.ByteString
3743

3844
type Run m a = m (Either RemoteStoreError a, DList Logger)
@@ -69,14 +75,58 @@ doReq = \case
6975
Nothing ->
7076
throwError
7177
RemoteStoreError_NoNarSourceProvided
72-
73-
_ -> pure ()
74-
75-
processOutput
76-
sockGetS
77-
(mapErrorS RemoteStoreError_SerializerReply
78-
$ getReplyS @a
79-
)
78+
processOutput
79+
processReply
80+
81+
AddToStoreNar _ meta _ _ -> do
82+
let narBytes = maybe 0 id $ metadataNarBytes meta
83+
maybeDataSource <- takeDataSource
84+
soc <- getStoreSocket
85+
case maybeDataSource of
86+
Nothing ->
87+
if narBytes == 0 then writeFramedSource (const (pure Nothing)) soc 0
88+
else throwError RemoteStoreError_NoDataSourceProvided
89+
Just dataSource -> do
90+
writeFramedSource dataSource soc narBytes
91+
processOutput
92+
pure NoReply
93+
94+
_ -> do
95+
processOutput
96+
processReply
97+
98+
where
99+
processReply = sockGetS
100+
(mapErrorS RemoteStoreError_SerializerReply
101+
$ getReplyS @a
102+
)
103+
104+
writeFramedSource
105+
:: forall m
106+
. ( MonadIO m
107+
, MonadRemoteStore m
108+
)
109+
=> (Word64 -> IO(Maybe ByteString))
110+
-> Socket
111+
-> Word64
112+
-> m ()
113+
writeFramedSource dataSource soc remainingBytes = do
114+
let chunkSize = 16384
115+
maybeBytes <- liftIO $ dataSource chunkSize
116+
case maybeBytes of
117+
Nothing -> do
118+
unless (remainingBytes == 0) $ throwError RemoteStoreError_DataSourceExhausted
119+
let eof :: Word64 = 0
120+
sockPutS int eof
121+
Just bytes -> do
122+
let bytesInChunk = fromIntegral $ Data.ByteString.length bytes
123+
when (bytesInChunk > chunkSize || bytesInChunk > remainingBytes) $ throwError RemoteStoreError_DataSourceReadTooLarge
124+
when (bytesInChunk == 0) $ throwError RemoteStoreError_DataSourceZeroLengthRead
125+
sockPutS int bytesInChunk
126+
liftIO
127+
$ Network.Socket.ByteString.sendAll soc bytes
128+
let nextRemainingBytes = remainingBytes - bytesInChunk
129+
writeFramedSource dataSource soc nextRemainingBytes
80130

81131
greetServer
82132
:: MonadRemoteStore m

hnix-store-remote/src/System/Nix/Store/Remote/MonadStore.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@ data RemoteStoreError
7777
| RemoteStoreError_LoggerParserFail String ByteString -- when incremental parser returns ((Fail msg leftover) :: Result)
7878
| RemoteStoreError_NoDataSourceProvided -- remoteStoreStateMDataSource is required but it is Nothing
7979
| RemoteStoreError_DataSourceExhausted -- remoteStoreStateMDataSource returned Nothing but more data was requested
80+
| RemoteStoreError_DataSourceZeroLengthRead -- remoteStoreStateMDataSource returned a zero length ByteString
81+
| RemoteStoreError_DataSourceReadTooLarge -- remoteStoreStateMDataSource returned a ByteString larger than the chunk size requested or the remaining bytes
8082
| RemoteStoreError_NoDataSinkProvided -- remoteStoreStateMDataSink is required but it is Nothing
8183
| RemoteStoreError_NoNarSourceProvided
8284
| RemoteStoreError_OperationFailed
@@ -250,6 +252,15 @@ class ( MonadIO m
250252
-> m ()
251253
setDataSource x = lift (setDataSource x)
252254

255+
takeDataSource :: m (Maybe (Word64 -> IO (Maybe ByteString)))
256+
default takeDataSource
257+
:: ( MonadTrans t
258+
, MonadRemoteStore m'
259+
, m ~ t m'
260+
)
261+
=> m (Maybe (Word64 -> IO (Maybe ByteString)))
262+
takeDataSource = lift takeDataSource
263+
253264
getDataSource :: m (Maybe (Word64 -> IO (Maybe ByteString)))
254265
default getDataSource
255266
:: ( MonadTrans t
@@ -327,6 +338,11 @@ instance MonadIO m => MonadRemoteStore (RemoteStoreT m) where
327338
getDataSource = RemoteStoreT (gets remoteStoreStateMDataSource)
328339
clearDataSource = RemoteStoreT $ modify $ \s -> s { remoteStoreStateMDataSource = Nothing }
329340

341+
takeDataSource = RemoteStoreT $ do
342+
x <- remoteStoreStateMDataSource <$> get
343+
modify $ \s -> s { remoteStoreStateMDataSource = Nothing }
344+
pure x
345+
330346
setDataSink x = RemoteStoreT $ modify $ \s -> s { remoteStoreStateMDataSink = pure x }
331347
getDataSink = RemoteStoreT (gets remoteStoreStateMDataSink)
332348
clearDataSink = RemoteStoreT $ modify $ \s -> s { remoteStoreStateMDataSink = Nothing }

hnix-store-remote/src/System/Nix/Store/Remote/Serializer.hs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ module System.Nix.Store.Remote.Serializer
8585
-- ** Reply
8686
, ReplySError(..)
8787
, opSuccess
88+
, noop
8889
-- *** Realisation
8990
, derivationOutputTyped
9091
, realisation
@@ -1077,6 +1078,16 @@ storeRequest = Serializer
10771078

10781079
pure $ Some (AddToStore pathName recursive hashAlgo repair)
10791080

1081+
WorkerOp_AddToStoreNar -> mapGetE $ do
1082+
storePath' <- getS storePath
1083+
metadata <- getS pathMetadata
1084+
repair <- getS bool
1085+
let repairMode = if repair then RepairMode_DoRepair else RepairMode_DontRepair
1086+
dontCheckSigs <- getS bool
1087+
let checkSigs = if dontCheckSigs then CheckMode_DontCheck else CheckMode_DoCheck
1088+
1089+
pure $ Some (AddToStoreNar storePath' metadata repairMode checkSigs)
1090+
10801091
WorkerOp_AddTextToStore -> mapGetE $ do
10811092
txt <- getS storeText
10821093
paths <- getS (hashSet storePath)
@@ -1175,7 +1186,6 @@ storeRequest = Serializer
11751186

11761187
w@WorkerOp_AddBuildLog -> notYet w
11771188
w@WorkerOp_AddMultipleToStore -> notYet w
1178-
w@WorkerOp_AddToStoreNar -> notYet w
11791189
w@WorkerOp_BuildPathsWithResults -> notYet w
11801190
w@WorkerOp_ClearFailedPaths -> notYet w
11811191
w@WorkerOp_ExportPath -> notYet w
@@ -1207,6 +1217,14 @@ storeRequest = Serializer
12071217
putS bool (recursive == FileIngestionMethod_FileRecursive)
12081218
putS someHashAlgo hashAlgo
12091219

1220+
Some (AddToStoreNar storePath' metadata repair checkSigs) -> mapPutE $ do
1221+
putS workerOp WorkerOp_AddToStoreNar
1222+
1223+
putS storePath storePath'
1224+
putS pathMetadata metadata
1225+
putS bool $ repair == RepairMode_DoRepair
1226+
putS bool $ checkSigs == CheckMode_DontCheck
1227+
12101228
Some (AddTextToStore txt paths _repair) -> mapPutE $ do
12111229
putS workerOp WorkerOp_AddTextToStore
12121230

@@ -1379,6 +1397,12 @@ opSuccess = Serializer
13791397
, putS = \_ -> mapPutER $ putS bool True
13801398
}
13811399

1400+
noop :: a -> NixSerializer r ReplySError a
1401+
noop ret = Serializer
1402+
{ getS = pure ret
1403+
, putS = \_ -> pure ()
1404+
}
1405+
13821406
-- *** Realisation
13831407

13841408
derivationOutputTyped :: NixSerializer r ReplySError (System.Nix.Realisation.DerivationOutput OutputName)

hnix-store-remote/src/System/Nix/Store/Remote/Server.hs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ processConnection workerHelper postGreet sock = do
170170
-- out of thin air
171171
() <- Data.Some.withSome someReq $ \case
172172
r@AddToStore {} -> perform r
173+
r@AddToStoreNar {} -> perform r
173174
r@AddTextToStore {} -> perform r
174175
r@AddSignatures {} -> perform r
175176
r@AddTempRoot {} -> perform r
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module System.Nix.Store.Remote.Types.NoReply
2+
( NoReply(..)
3+
) where
4+
5+
-- | Reply type for the case where the server does not reply
6+
data NoReply = NoReply
7+
deriving (Show)
8+

hnix-store-remote/src/System/Nix/Store/Remote/Types/StoreReply.hs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import System.Nix.Build (BuildResult)
88
import System.Nix.StorePath (StorePath, StorePathName)
99
import System.Nix.StorePath.Metadata (Metadata)
1010
import System.Nix.Store.Remote.Serializer
11+
import System.Nix.Store.Remote.Types.NoReply (NoReply(..))
1112
import System.Nix.Store.Remote.Types.SuccessCodeReply (SuccessCodeReply)
1213
import System.Nix.Store.Remote.Types.GC (GCResult, GCRoot)
1314
import System.Nix.Store.Remote.Types.Query.Missing (Missing)
@@ -24,6 +25,9 @@ class StoreReply a where
2425
instance StoreReply SuccessCodeReply where
2526
getReplyS = opSuccess
2627

28+
instance StoreReply NoReply where
29+
getReplyS = noop NoReply
30+
2731
instance StoreReply Bool where
2832
getReplyS = mapPrimE bool
2933

hnix-store-remote/src/System/Nix/Store/Remote/Types/StoreRequest.hs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import System.Nix.StorePath (StorePath, StorePathName, StorePathHashPart)
2525
import System.Nix.StorePath.Metadata (Metadata)
2626
import System.Nix.Store.Remote.Types.GC (GCOptions, GCResult, GCRoot)
2727
import System.Nix.Store.Remote.Types.CheckMode (CheckMode)
28+
import System.Nix.Store.Remote.Types.NoReply (NoReply)
2829
import System.Nix.Store.Remote.Types.Query.Missing (Missing)
2930
import System.Nix.Store.Remote.Types.StoreText (StoreText)
3031
import System.Nix.Store.Remote.Types.SubstituteMode (SubstituteMode)
@@ -39,6 +40,14 @@ data StoreRequest :: Type -> Type where
3940
-> RepairMode -- ^ Only used by local store backend
4041
-> StoreRequest StorePath
4142

43+
-- | Add a NAR with Metadata to the store.
44+
AddToStoreNar
45+
:: StorePath
46+
-> Metadata StorePath
47+
-> RepairMode
48+
-> CheckMode -- ^ Whether to check signatures
49+
-> StoreRequest NoReply
50+
4251
-- | Add text to store.
4352
--
4453
-- Reference accepts repair but only uses it
@@ -159,6 +168,7 @@ deriveGShow ''StoreRequest
159168

160169
instance {-# OVERLAPPING #-} Eq (Some StoreRequest) where
161170
Some (AddToStore a b c d) == Some (AddToStore a' b' c' d') = (a, b, c, d) == (a', b', c', d')
171+
Some (AddToStoreNar a b c d) == Some (AddToStoreNar a' b' c' d') = (a, b, c, d) == (a', b', c', d')
162172
Some (AddTextToStore a b c) == Some (AddTextToStore a' b' c') = (a, b, c) == (a', b', c')
163173
Some (AddSignatures a b) == Some (AddSignatures a' b') = (a, b) == (a', b')
164174
Some (AddIndirectRoot a) == Some (AddIndirectRoot a') = a == a'

hnix-store-remote/tests-io/NixDaemonSpec.hs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import qualified Data.Map
3535
import qualified Data.Set
3636
import qualified Data.Text
3737
import qualified Data.Text.Encoding
38+
import qualified SampleNar
3839
import qualified System.Directory
3940
import qualified System.Environment
4041
import qualified System.IO.Temp
@@ -488,3 +489,13 @@ makeProtoSpec f flavor = around f $ do
488489
}
489490
gcResultDeletedPaths `shouldBe` Data.HashSet.fromList [path]
490491
gcResultBytesFreed `shouldBe` 4
492+
493+
context "addToStoreNar" $ do
494+
itRights "adds nar file" $ do
495+
unless (flavor == SpecFlavor_MITM) $ do
496+
sampleNar@SampleNar.SampleNar{..} <- liftIO SampleNar.sampleNar0
497+
dataSource <- liftIO $ SampleNar.buildDataSource sampleNar
498+
addToStoreNar sampleNar_storePath sampleNar_metadata RepairMode_DontRepair CheckMode_DontCheck dataSource
499+
500+
meta <- queryPathInfo sampleNar_storePath
501+
(metadataDeriverPath =<< meta) `shouldBe` metadataDeriverPath sampleNar_metadata

0 commit comments

Comments
 (0)