mirror of
https://github.com/haskell-nix/hnix-store.git
synced 2024-09-11 18:05:56 +03:00
Merge pull request #255 from haskell-nix/srk/daemon
cereal remote, server side integration
This commit is contained in:
commit
70eb0d35fb
2
.github/workflows/ci.dhall.frozen
vendored
2
.github/workflows/ci.dhall.frozen
vendored
@ -1,6 +1,6 @@
|
||||
let haskellCi =
|
||||
https://raw.githubusercontent.com/sorki/github-actions-dhall/main/haskell-ci.dhall
|
||||
sha256:5d7058a7684fd5315467b562853bd1c4a43da691c09293d3715ee739dfa26e08
|
||||
sha256:a39801f73d93c6e0f91942755ef8ae4c50947e9a9b180b6724957229470f7b8d
|
||||
|
||||
let defSteps = haskellCi.defaultCabalSteps
|
||||
|
||||
|
1
.github/workflows/ci.yaml
vendored
1
.github/workflows/ci.yaml
vendored
@ -59,7 +59,6 @@ jobs:
|
||||
- "macos-latest"
|
||||
name: Haskell CI
|
||||
'on':
|
||||
pull_request: {}
|
||||
push: {}
|
||||
schedule:
|
||||
- cron: "4 20 10 * *"
|
||||
|
@ -20,4 +20,4 @@ package hnix-store-nar
|
||||
flags: +bounded_memory
|
||||
|
||||
package hnix-store-remote
|
||||
flags: +build-readme +io-testsuite
|
||||
flags: +build-derivation +build-readme +io-testsuite
|
||||
|
@ -6,6 +6,7 @@ module System.Nix.Base
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Text (Text)
|
||||
import GHC.Generics (Generic)
|
||||
|
||||
import qualified Data.Text.Encoding
|
||||
import qualified Data.ByteString.Base16
|
||||
@ -21,6 +22,7 @@ data BaseEncoding
|
||||
-- & NixBase seems be the most widely used in Nix.
|
||||
| Base16
|
||||
| Base64
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
||||
|
||||
-- | Encode @ByteString@ with @Base@ encoding, produce @Text@.
|
||||
encodeWith :: BaseEncoding -> ByteString -> Text
|
||||
|
@ -15,25 +15,28 @@ import GHC.Generics (Generic)
|
||||
|
||||
-- keep the order of these Enums to match enums from reference implementations
|
||||
-- src/libstore/store-api.hh
|
||||
data BuildMode = Normal | Repair | Check
|
||||
data BuildMode
|
||||
= BuildMode_Normal
|
||||
| BuildMode_Repair
|
||||
| BuildMode_Check
|
||||
deriving (Eq, Generic, Ord, Enum, Show)
|
||||
|
||||
data BuildStatus =
|
||||
Built
|
||||
| Substituted
|
||||
| AlreadyValid
|
||||
| PermanentFailure
|
||||
| InputRejected
|
||||
| OutputRejected
|
||||
| TransientFailure -- possibly transient
|
||||
| CachedFailure -- no longer used
|
||||
| TimedOut
|
||||
| MiscFailure
|
||||
| DependencyFailed
|
||||
| LogLimitExceeded
|
||||
| NotDeterministic
|
||||
| ResolvesToAlreadyValid
|
||||
| NoSubstituters
|
||||
BuildStatus_Built
|
||||
| BuildStatus_Substituted
|
||||
| BuildStatus_AlreadyValid
|
||||
| BuildStatus_PermanentFailure
|
||||
| BuildStatus_InputRejected
|
||||
| BuildStatus_OutputRejected
|
||||
| BuildStatus_TransientFailure -- possibly transient
|
||||
| BuildStatus_CachedFailure -- no longer used
|
||||
| BuildStatus_TimedOut
|
||||
| BuildStatus_MiscFailure
|
||||
| BuildStatus_DependencyFailed
|
||||
| BuildStatus_LogLimitExceeded
|
||||
| BuildStatus_NotDeterministic
|
||||
| BuildStatus_ResolvesToAlreadyValid
|
||||
| BuildStatus_NoSubstituters
|
||||
deriving (Eq, Generic, Ord, Enum, Show)
|
||||
|
||||
-- | Result of the build
|
||||
@ -55,4 +58,8 @@ data BuildResult = BuildResult
|
||||
|
||||
buildSuccess :: BuildResult -> Bool
|
||||
buildSuccess BuildResult {..} =
|
||||
status `elem` [Built, Substituted, AlreadyValid]
|
||||
status `elem`
|
||||
[ BuildStatus_Built
|
||||
, BuildStatus_Substituted
|
||||
, BuildStatus_AlreadyValid
|
||||
]
|
||||
|
@ -167,7 +167,7 @@ decodeDigestWith b x =
|
||||
let
|
||||
toEither =
|
||||
maybeToRight
|
||||
("Cryptonite was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.")
|
||||
("Crypton was not able to convert '(ByteString -> Digest a)' for: '" <> show bs <>"'.")
|
||||
(toEither . Crypto.Hash.digestFromByteString) bs
|
||||
where
|
||||
-- To not depend on @extra@
|
||||
|
@ -34,6 +34,7 @@ module System.Nix.StorePath
|
||||
, pathParser
|
||||
-- * Utilities for tests
|
||||
, unsafeMakeStorePath
|
||||
, unsafeMakeStorePathHashPart
|
||||
) where
|
||||
|
||||
import Crypto.Hash (HashAlgorithm)
|
||||
@ -307,3 +308,11 @@ unsafeMakeStorePath
|
||||
-> StorePathName
|
||||
-> StorePath
|
||||
unsafeMakeStorePath = StorePath
|
||||
|
||||
-- | Path hash parts rarely need to be constructed directly.
|
||||
-- Prefer @mkStorePathHashPart@
|
||||
-- Used by remote store in wire protocol
|
||||
unsafeMakeStorePathHashPart
|
||||
:: ByteString
|
||||
-> StorePathHashPart
|
||||
unsafeMakeStorePathHashPart = StorePathHashPart
|
||||
|
41
hnix-store-remote/app/BuildDerivation.hs
Normal file
41
hnix-store-remote/app/BuildDerivation.hs
Normal file
@ -0,0 +1,41 @@
|
||||
{-# LANGUAGE LambdaCase #-}
|
||||
module Main where
|
||||
|
||||
import Data.Default.Class (Default(def))
|
||||
import Data.Text (Text)
|
||||
import System.Nix.Derivation (Derivation)
|
||||
import System.Nix.StorePath (StorePath)
|
||||
|
||||
import qualified Data.Text
|
||||
import qualified Data.Text.IO
|
||||
import qualified Data.Attoparsec.Text
|
||||
import qualified System.Environment
|
||||
import qualified System.Nix.Build
|
||||
import qualified System.Nix.Derivation
|
||||
import qualified System.Nix.StorePath
|
||||
import qualified System.Nix.Store.Remote
|
||||
|
||||
parseDerivation :: FilePath -> IO (Derivation StorePath Text)
|
||||
parseDerivation source = do
|
||||
contents <- Data.Text.IO.readFile source
|
||||
case Data.Attoparsec.Text.parseOnly
|
||||
(System.Nix.Derivation.parseDerivation def) contents of
|
||||
Left e -> error e
|
||||
Right drv -> pure drv
|
||||
|
||||
main :: IO ()
|
||||
main = System.Environment.getArgs >>= \case
|
||||
[filename] -> do
|
||||
case System.Nix.StorePath.parsePathFromText def (Data.Text.pack filename) of
|
||||
Left e -> error $ show e
|
||||
Right p -> do
|
||||
d <- parseDerivation filename
|
||||
out <-
|
||||
System.Nix.Store.Remote.runStore
|
||||
$ System.Nix.Store.Remote.buildDerivation
|
||||
p
|
||||
d
|
||||
System.Nix.Build.BuildMode_Normal
|
||||
print out
|
||||
_ -> error "No input derivation file"
|
||||
|
@ -26,14 +26,17 @@ common commons
|
||||
, DeriveFoldable
|
||||
, DeriveTraversable
|
||||
, DeriveLift
|
||||
, DerivingVia
|
||||
, FlexibleContexts
|
||||
, FlexibleInstances
|
||||
, GADTs
|
||||
, RecordWildCards
|
||||
, ScopedTypeVariables
|
||||
, StandaloneDeriving
|
||||
, TypeApplications
|
||||
, TypeSynonymInstances
|
||||
, InstanceSigs
|
||||
, KindSignatures
|
||||
, MultiParamTypeClasses
|
||||
, TupleSections
|
||||
, LambdaCase
|
||||
@ -53,6 +56,12 @@ flag io-testsuite
|
||||
Enable testsuite, which requires external
|
||||
binaries and Linux namespace support.
|
||||
|
||||
flag build-derivation
|
||||
default:
|
||||
False
|
||||
description:
|
||||
Build build-derivation executable
|
||||
|
||||
flag build-readme
|
||||
default:
|
||||
False
|
||||
@ -62,37 +71,68 @@ flag build-readme
|
||||
library
|
||||
import: commons
|
||||
exposed-modules:
|
||||
System.Nix.Store.Remote
|
||||
, System.Nix.Store.Remote.Binary
|
||||
Data.Serializer
|
||||
, Data.Serializer.Example
|
||||
, System.Nix.Store.Remote
|
||||
, System.Nix.Store.Remote.Arbitrary
|
||||
, System.Nix.Store.Remote.Logger
|
||||
, System.Nix.Store.Remote.MonadStore
|
||||
, System.Nix.Store.Remote.Protocol
|
||||
, System.Nix.Store.Remote.Serialize
|
||||
, System.Nix.Store.Remote.Serialize.Prim
|
||||
, System.Nix.Store.Remote.Logger
|
||||
, System.Nix.Store.Remote.Protocol
|
||||
, System.Nix.Store.Remote.Serializer
|
||||
, System.Nix.Store.Remote.Socket
|
||||
, System.Nix.Store.Remote.Types
|
||||
, System.Nix.Store.Remote.Util
|
||||
, System.Nix.Store.Remote.Types.Activity
|
||||
, System.Nix.Store.Remote.Types.CheckMode
|
||||
, System.Nix.Store.Remote.Types.GC
|
||||
, System.Nix.Store.Remote.Types.Logger
|
||||
, System.Nix.Store.Remote.Types.ProtoVersion
|
||||
, System.Nix.Store.Remote.Types.StoreConfig
|
||||
, System.Nix.Store.Remote.Types.SubstituteMode
|
||||
, System.Nix.Store.Remote.Types.Verbosity
|
||||
, System.Nix.Store.Remote.Types.WorkerOp
|
||||
|
||||
build-depends:
|
||||
base >=4.12 && <5
|
||||
, hnix-store-core >= 0.8 && <0.9
|
||||
, hnix-store-nar >= 0.1
|
||||
, attoparsec
|
||||
, binary
|
||||
, bytestring
|
||||
, cereal
|
||||
, containers
|
||||
, crypton
|
||||
, data-default-class
|
||||
, dependent-sum > 0.7 && < 1
|
||||
, generic-arbitrary < 1.1
|
||||
, hashable
|
||||
, text
|
||||
, time
|
||||
, transformers
|
||||
, network
|
||||
, mtl
|
||||
, QuickCheck
|
||||
, quickcheck-instances
|
||||
, unordered-containers
|
||||
, transformers
|
||||
, vector
|
||||
hs-source-dirs: src
|
||||
ghc-options: -Wall
|
||||
|
||||
executable build-derivation
|
||||
if !flag(build-derivation)
|
||||
buildable: False
|
||||
build-depends:
|
||||
base >=4.12 && <5
|
||||
, attoparsec
|
||||
, hnix-store-core
|
||||
, hnix-store-remote
|
||||
, data-default-class
|
||||
, text
|
||||
default-language: Haskell2010
|
||||
main-is: BuildDerivation.hs
|
||||
hs-source-dirs: app
|
||||
ghc-options: -Wall -threaded -rtsopts "-with-rtsopts -N"
|
||||
|
||||
executable remote-readme
|
||||
if !flag(build-readme)
|
||||
buildable: False
|
||||
@ -110,8 +150,10 @@ test-suite remote
|
||||
type: exitcode-stdio-1.0
|
||||
main-is: Driver.hs
|
||||
hs-source-dirs: tests
|
||||
ghc-options: -Wall
|
||||
ghc-options: -Wall -threaded -rtsopts "-with-rtsopts -N"
|
||||
other-modules:
|
||||
Data.SerializerSpec
|
||||
NixSerializerSpec
|
||||
SerializeSpec
|
||||
build-tool-depends:
|
||||
hspec-discover:hspec-discover
|
||||
@ -121,6 +163,9 @@ test-suite remote
|
||||
, hnix-store-remote
|
||||
, hnix-store-tests
|
||||
, cereal
|
||||
, crypton
|
||||
, dependent-sum > 0.7 && < 1
|
||||
, some > 1.0.5 && < 2
|
||||
, text
|
||||
, time
|
||||
, hspec
|
||||
@ -153,14 +198,14 @@ test-suite remote-io
|
||||
, containers
|
||||
, crypton
|
||||
, directory
|
||||
, process
|
||||
, filepath
|
||||
, hspec-expectations-lifted
|
||||
, text
|
||||
, tasty
|
||||
, hspec
|
||||
, tasty-hspec
|
||||
, hspec-expectations-lifted
|
||||
, linux-namespaces
|
||||
, process
|
||||
, tasty
|
||||
, tasty-hspec
|
||||
, temporary
|
||||
, text
|
||||
, unix
|
||||
, unordered-containers
|
||||
|
212
hnix-store-remote/src/Data/Serializer.hs
Normal file
212
hnix-store-remote/src/Data/Serializer.hs
Normal file
@ -0,0 +1,212 @@
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-|
|
||||
Description : Serializer data type
|
||||
Copyright : (c) John Ericson, 2023
|
||||
Richard Marko, 2023
|
||||
Stability : experimental
|
||||
|
||||
@Serializer@ ties @Get@ and @PutM@ monads
|
||||
into a single datatype and allows
|
||||
transforming both monads with a monad transformer
|
||||
for adding extra layers like @ExceptT@
|
||||
(for example when @putS@ can fail due to unsupported
|
||||
version of a protocol) or @ReaderT@ (when we need
|
||||
to serialize a data type based differently based
|
||||
on a protocol version).
|
||||
|
||||
See "Data.Serializer.Example"
|
||||
-}
|
||||
|
||||
module Data.Serializer
|
||||
(
|
||||
-- * Serializer
|
||||
Serializer(..)
|
||||
-- ** Runners
|
||||
, runGetS
|
||||
, runPutS
|
||||
-- * Simple serializer
|
||||
, SimpleSerializer
|
||||
-- ** Simple runners
|
||||
, runGetSimple
|
||||
, runPutSimple
|
||||
-- * From Get/Put, Serialize
|
||||
, lift2
|
||||
, liftSerialize
|
||||
-- * Combinators
|
||||
, mapIsoSerializer
|
||||
, mapPrismSerializer
|
||||
, tup
|
||||
-- * Utility
|
||||
, GetSerializerError(..)
|
||||
, transformGetError
|
||||
, transformPutError
|
||||
-- * Re-exports
|
||||
, Get
|
||||
, PutM
|
||||
) where
|
||||
|
||||
#if !MIN_VERSION_base(4,18,0)
|
||||
import Control.Applicative (liftA2)
|
||||
#endif
|
||||
import Control.Monad.Except (MonadError, throwError)
|
||||
import Control.Monad.Trans (lift)
|
||||
import Control.Monad.Trans (MonadTrans)
|
||||
import Control.Monad.Trans.Identity (IdentityT, runIdentityT)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Serialize (Serialize)
|
||||
import Data.Serialize.Get (Get, runGet)
|
||||
import Data.Serialize.Put (Putter, PutM, runPutM)
|
||||
|
||||
import qualified Data.Serialize
|
||||
|
||||
-- * Serializer
|
||||
|
||||
-- | @Serializer@ ties @Get@ and @PutM@ monads
|
||||
-- into a single datatype and allows
|
||||
-- transforming the monads with a monad transformer
|
||||
-- for e.g. adding @ExceptT@ or @ReaderT@ layers.
|
||||
data Serializer t a = Serializer
|
||||
{ getS :: t Get a
|
||||
, putS :: a -> t PutM ()
|
||||
}
|
||||
|
||||
-- ** Runners
|
||||
|
||||
-- | Runner for putS of @Serializer@
|
||||
runPutS
|
||||
:: ( Monad (t PutM)
|
||||
, MonadTrans t
|
||||
)
|
||||
=> Serializer t a -- ^ Serializer
|
||||
-> (t PutM () -> PutM b) -- ^ Tranformer runner
|
||||
-> a -- ^ Value to (out)put
|
||||
-> (b, ByteString)
|
||||
runPutS s run a = runPutM $ run $ (putS s) a
|
||||
|
||||
-- | Runner for getS of @Serializer@
|
||||
runGetS
|
||||
:: ( Monad (t Get)
|
||||
, MonadTrans t
|
||||
)
|
||||
=> Serializer t a -- ^ Serializer
|
||||
-> (t Get a -> Get b) -- ^ Tranformer runner
|
||||
-> ByteString -- ^ ByteString to parse
|
||||
-> Either String b
|
||||
runGetS s run b = runGet (run (getS s)) b
|
||||
|
||||
-- * Simple serializer
|
||||
|
||||
-- | Simple @Serializer@
|
||||
type SimpleSerializer a = Serializer IdentityT a
|
||||
|
||||
-- ** Simple runners
|
||||
|
||||
-- | Runner for getS of @SimpleSerializer@
|
||||
runGetSimple
|
||||
:: SimpleSerializer a
|
||||
-> ByteString
|
||||
-> Either String a
|
||||
runGetSimple s b =
|
||||
runGetS s (runIdentityT) b
|
||||
|
||||
-- | Runner for putS of @SimpleSerializer@
|
||||
runPutSimple
|
||||
:: SimpleSerializer a
|
||||
-> a
|
||||
-> ByteString
|
||||
runPutSimple s =
|
||||
snd
|
||||
. runPutS s runIdentityT
|
||||
|
||||
-- * From Get/Put, Serialize
|
||||
|
||||
-- | Lift @Get a@ and @Putter a@ into @Serializer@
|
||||
lift2
|
||||
:: forall a t
|
||||
. MonadTrans t
|
||||
=> Get a
|
||||
-> Putter a
|
||||
-> Serializer t a
|
||||
lift2 f g = Serializer
|
||||
{ getS = lift f
|
||||
, putS = lift . g
|
||||
}
|
||||
|
||||
-- | Lift @Serialize a@ instance into @Serializer@
|
||||
liftSerialize
|
||||
:: ( Serialize a
|
||||
, MonadTrans t
|
||||
)
|
||||
=> Serializer t a
|
||||
liftSerialize =
|
||||
lift2
|
||||
Data.Serialize.get
|
||||
Data.Serialize.put
|
||||
|
||||
-- * Combinators
|
||||
|
||||
-- | Map over @Serializer@
|
||||
mapIsoSerializer
|
||||
:: Functor (t Get)
|
||||
=> (a -> b) -- ^ Map over @getS@
|
||||
-> (b -> a) -- ^ Map over @putS@
|
||||
-> (Serializer t a -> Serializer t b)
|
||||
mapIsoSerializer f g s = Serializer
|
||||
{ getS = f <$> getS s
|
||||
, putS = putS s . g
|
||||
}
|
||||
|
||||
-- | Map over @Serializer@ where @getS@
|
||||
-- can return @Either@
|
||||
mapPrismSerializer
|
||||
:: MonadError eGet (t Get)
|
||||
=> (a -> Either eGet b) -- ^ Map over @getS@
|
||||
-> (b -> a) -- ^ Map over @putS@
|
||||
-> (Serializer t a -> Serializer t b)
|
||||
mapPrismSerializer f g s = Serializer
|
||||
{ getS = either throwError pure . f =<< getS s
|
||||
, putS = putS s . g
|
||||
}
|
||||
|
||||
-- | Tuple combinator
|
||||
tup
|
||||
:: ( Applicative (t Get)
|
||||
, Monad (t PutM)
|
||||
)
|
||||
=> Serializer t a
|
||||
-> Serializer t b
|
||||
-> Serializer t (a, b)
|
||||
tup a b = Serializer
|
||||
{ getS = liftA2 (,) (getS a) (getS b)
|
||||
, putS = \(x, y) -> do
|
||||
putS a x
|
||||
putS b y
|
||||
}
|
||||
|
||||
-- * Utilities
|
||||
|
||||
-- | Wrapper for both GetS errors
|
||||
--
|
||||
-- * the one that occurs when @fail@ is called
|
||||
-- * custom one when @ExceptT@ is used
|
||||
data GetSerializerError customGetError
|
||||
= SerializerError_GetFail String
|
||||
| SerializerError_Get customGetError
|
||||
deriving (Eq, Show)
|
||||
|
||||
-- | Helper for transforming nested Eithers
|
||||
-- into @GetSerializerError@ wrapper
|
||||
transformGetError
|
||||
:: Either String (Either customGetError b)
|
||||
-> Either (GetSerializerError customGetError) b
|
||||
transformGetError = \case
|
||||
Left stringyRunGetError -> Left (SerializerError_GetFail stringyRunGetError)
|
||||
Right (Left myGetError) -> Left (SerializerError_Get myGetError)
|
||||
Right (Right res) -> Right res
|
||||
|
||||
-- | Helper for transforming @runPutM@ result
|
||||
transformPutError
|
||||
:: (Either customPutError (), ByteString)
|
||||
-> Either customPutError ByteString
|
||||
transformPutError (e, r) = either Left (pure $ Right r) e
|
276
hnix-store-remote/src/Data/Serializer/Example.hs
Normal file
276
hnix-store-remote/src/Data/Serializer/Example.hs
Normal file
@ -0,0 +1,276 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
module Data.Serializer.Example
|
||||
(
|
||||
-- * Simple protocol
|
||||
OpCode(..)
|
||||
, Cmd(..)
|
||||
-- * Cmd Serializer
|
||||
, cmdS
|
||||
-- * Runners
|
||||
, runG
|
||||
, runP
|
||||
-- * Custom errors
|
||||
, MyGetError(..)
|
||||
, MyPutError(..)
|
||||
-- ** Erroring variants of cmdS
|
||||
-- *** putS with throwError and MyPutError
|
||||
, cmdSPutError
|
||||
-- *** getS with throwError and MyGetError
|
||||
, cmdSGetError
|
||||
-- *** getS with fail
|
||||
, cmdSGetFail
|
||||
-- *** putS with fail
|
||||
, cmdSPutFail
|
||||
-- * Elaborate
|
||||
, cmdSRest
|
||||
, runGRest
|
||||
, runPRest
|
||||
) where
|
||||
|
||||
import Control.Monad.Except (MonadError, throwError)
|
||||
import Control.Monad.Reader (MonadReader, ask)
|
||||
import Control.Monad.State (MonadState)
|
||||
import Control.Monad.Trans (MonadTrans, lift)
|
||||
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
|
||||
import Control.Monad.Trans.Except (ExceptT, runExceptT)
|
||||
import Control.Monad.Trans.State (StateT, runStateT)
|
||||
import Data.Bifunctor (first, second)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Int (Int8)
|
||||
import Data.GADT.Show (GShow(..), defaultGshowsPrec)
|
||||
import Data.Kind (Type)
|
||||
import Data.Type.Equality
|
||||
import Data.Serialize.Get (getInt8)
|
||||
import Data.Serialize.Put (putInt8)
|
||||
import Data.Serializer
|
||||
import Data.Some (Some(..))
|
||||
import GHC.Generics
|
||||
import System.Nix.Store.Remote.Serialize.Prim (getBool, putBool, getEnum, putEnum)
|
||||
|
||||
import Test.QuickCheck (Arbitrary(..), oneof)
|
||||
|
||||
-- * Simple protocol
|
||||
|
||||
-- | OpCode used to differentiate between operations
|
||||
data OpCode = OpCode_Int | OpCode_Bool
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
||||
|
||||
-- | Protocol operations
|
||||
data Cmd :: Type -> Type where
|
||||
Cmd_Int :: Int8 -> Cmd Int8
|
||||
Cmd_Bool :: Bool -> Cmd Bool
|
||||
|
||||
deriving instance Eq (Cmd a)
|
||||
deriving instance Show (Cmd a)
|
||||
|
||||
instance GShow Cmd where
|
||||
gshowsPrec = defaultGshowsPrec
|
||||
|
||||
instance TestEquality Cmd where
|
||||
testEquality (Cmd_Int _) (Cmd_Int _) = Just Refl
|
||||
testEquality (Cmd_Bool _) (Cmd_Bool _) = Just Refl
|
||||
testEquality _ _ = Nothing
|
||||
|
||||
-- constructors only
|
||||
-- import Data.GADT.Compare
|
||||
-- instance GEq Cmd where
|
||||
-- geq = testEquality
|
||||
|
||||
instance {-# OVERLAPPING #-} Eq (Some Cmd) where
|
||||
Some (Cmd_Int a) == Some (Cmd_Int b) = a == b
|
||||
Some (Cmd_Bool a) == Some (Cmd_Bool b) = a == b
|
||||
_ == _ = False
|
||||
|
||||
instance Arbitrary (Some Cmd) where
|
||||
arbitrary = oneof
|
||||
[ Some . Cmd_Int <$> arbitrary
|
||||
, Some . Cmd_Bool <$> arbitrary
|
||||
]
|
||||
|
||||
-- | @OpCode@ @Serializer@
|
||||
opcode :: MonadTrans t => Serializer t OpCode
|
||||
opcode = Serializer
|
||||
{ getS = lift getEnum
|
||||
, putS = lift . putEnum
|
||||
}
|
||||
|
||||
-- * Cmd Serializer
|
||||
|
||||
-- | @Cmd@ @Serializer@
|
||||
cmdS
|
||||
:: forall t . ( MonadTrans t
|
||||
, Monad (t Get)
|
||||
, Monad (t PutM)
|
||||
)
|
||||
=> Serializer t (Some Cmd)
|
||||
cmdS = Serializer
|
||||
{ getS = getS opcode >>= \case
|
||||
OpCode_Int -> Some . Cmd_Int <$> lift getInt8
|
||||
OpCode_Bool -> Some . Cmd_Bool <$> lift getBool
|
||||
, putS = \case
|
||||
Some (Cmd_Int i) -> putS opcode OpCode_Int >> lift (putInt8 i)
|
||||
Some (Cmd_Bool b) -> putS opcode OpCode_Bool >> lift (putBool b)
|
||||
}
|
||||
|
||||
-- * Runners
|
||||
|
||||
-- | @runGetS@ specialized to @ExceptT e@
|
||||
runG
|
||||
:: Serializer (ExceptT e) a
|
||||
-> ByteString
|
||||
-> Either (GetSerializerError e) a
|
||||
runG s =
|
||||
transformGetError
|
||||
. runGetS s runExceptT
|
||||
|
||||
-- | @runPutS@ specialized to @ExceptT e@
|
||||
runP
|
||||
:: Serializer (ExceptT e) a
|
||||
-> a
|
||||
-> Either e ByteString
|
||||
runP s =
|
||||
(\(e, r) -> either Left (pure $ Right r) e)
|
||||
. runPutS s runExceptT
|
||||
|
||||
-- * Custom errors
|
||||
|
||||
data MyGetError
|
||||
= MyGetError_Example
|
||||
deriving (Eq, Show)
|
||||
|
||||
data MyPutError
|
||||
= MyPutError_NoLongerSupported -- no longer supported protocol version
|
||||
deriving (Eq, Show)
|
||||
|
||||
-- ** Erroring variants of cmdS
|
||||
|
||||
-- *** putS with throwError and MyPutError
|
||||
|
||||
cmdSPutError :: Serializer (ExceptT MyPutError) (Some Cmd)
|
||||
cmdSPutError = Serializer
|
||||
{ getS = getS cmdS
|
||||
, putS = \case
|
||||
Some (Cmd_Int i) -> putS opcode OpCode_Int >> lift (putInt8 i)
|
||||
Some (Cmd_Bool _b) -> throwError MyPutError_NoLongerSupported
|
||||
}
|
||||
|
||||
-- *** getS with throwError and MyGetError
|
||||
|
||||
cmdSGetError :: Serializer (ExceptT MyGetError) (Some Cmd)
|
||||
cmdSGetError = Serializer
|
||||
{ getS = getS opcode >>= \case
|
||||
OpCode_Int -> Some . Cmd_Int <$> lift getInt8
|
||||
OpCode_Bool -> throwError MyGetError_Example
|
||||
, putS = putS cmdS
|
||||
}
|
||||
|
||||
-- *** getS with fail
|
||||
|
||||
cmdSGetFail
|
||||
:: ( MonadTrans t
|
||||
, MonadFail (t Get)
|
||||
, Monad (t PutM)
|
||||
)
|
||||
=> Serializer t (Some Cmd)
|
||||
cmdSGetFail = Serializer
|
||||
{ getS = getS opcode >>= \case
|
||||
OpCode_Int -> Some . Cmd_Int <$> lift getInt8
|
||||
OpCode_Bool -> fail "no parse"
|
||||
, putS = putS cmdS
|
||||
}
|
||||
|
||||
-- *** putS with fail
|
||||
|
||||
-- | Unused as PutM doesn't have @MonadFail@
|
||||
-- >>> serializerPutFail = cmdPutFail @(ExceptT MyGetError)
|
||||
-- No instance for (MonadFail PutM)
|
||||
-- as expected
|
||||
cmdSPutFail
|
||||
:: ( MonadTrans t
|
||||
, MonadFail (t PutM)
|
||||
, Monad (t Get)
|
||||
)
|
||||
=> Serializer t (Some Cmd)
|
||||
cmdSPutFail = Serializer
|
||||
{ getS = getS cmdS
|
||||
, putS = \case
|
||||
Some (Cmd_Int i) -> putS opcode OpCode_Int >> lift (putInt8 i)
|
||||
Some (Cmd_Bool _b) -> fail "can't"
|
||||
}
|
||||
|
||||
-- * Elaborate
|
||||
|
||||
-- | Transformer for @Serializer@
|
||||
newtype REST r e s m a = REST
|
||||
{ _unREST :: ExceptT e (StateT s (ReaderT r m)) a }
|
||||
deriving
|
||||
( Applicative
|
||||
, Functor
|
||||
, Monad
|
||||
, MonadError e
|
||||
, MonadReader r
|
||||
, MonadState s
|
||||
, MonadFail
|
||||
)
|
||||
|
||||
instance MonadTrans (REST r e s) where
|
||||
lift = REST . lift . lift . lift
|
||||
|
||||
-- | Runner for @REST@
|
||||
restRunner
|
||||
:: Monad m
|
||||
=> r
|
||||
-> s
|
||||
-> REST r e s m a
|
||||
-> m ((Either e a), s)
|
||||
restRunner r s =
|
||||
(`runReaderT` r)
|
||||
. (`runStateT` s)
|
||||
. runExceptT
|
||||
. _unREST
|
||||
|
||||
runGRest
|
||||
:: Serializer (REST r e s) a
|
||||
-> r
|
||||
-> s
|
||||
-> ByteString
|
||||
-> Either (GetSerializerError e) a
|
||||
runGRest serializer r s =
|
||||
transformGetError
|
||||
. second fst
|
||||
. runGetS
|
||||
serializer
|
||||
(restRunner r s)
|
||||
|
||||
runPRest
|
||||
:: Serializer (REST r e s) a
|
||||
-> r
|
||||
-> s
|
||||
-> a
|
||||
-> Either e ByteString
|
||||
runPRest serializer r s =
|
||||
transformPutError
|
||||
. first fst
|
||||
. runPutS
|
||||
serializer
|
||||
(restRunner r s)
|
||||
|
||||
cmdSRest
|
||||
:: Serializer (REST Bool e Int) (Some Cmd)
|
||||
cmdSRest = Serializer
|
||||
{ getS = getS opcode >>= \case
|
||||
OpCode_Int -> do
|
||||
isTrue <- ask
|
||||
if isTrue
|
||||
then Some . Cmd_Int . (+1) <$> lift getInt8
|
||||
else Some . Cmd_Int <$> lift getInt8
|
||||
OpCode_Bool -> Some . Cmd_Bool <$> lift getBool
|
||||
, putS = \case
|
||||
Some (Cmd_Int i) -> do
|
||||
putS opcode OpCode_Int
|
||||
isTrue <- ask
|
||||
if isTrue
|
||||
then lift (putInt8 (i - 1))
|
||||
else lift (putInt8 i)
|
||||
Some (Cmd_Bool b) -> putS opcode OpCode_Bool >> lift (putBool b)
|
||||
}
|
@ -10,6 +10,7 @@ module System.Nix.Store.Remote
|
||||
, addTempRoot
|
||||
, buildPaths
|
||||
, buildDerivation
|
||||
, deleteSpecific
|
||||
, ensurePath
|
||||
, findRoots
|
||||
, isValidPathUncached
|
||||
@ -28,54 +29,46 @@ module System.Nix.Store.Remote
|
||||
, syncWithGC
|
||||
, verifyStore
|
||||
, module System.Nix.Store.Types
|
||||
, module System.Nix.Store.Remote.MonadStore
|
||||
, module System.Nix.Store.Remote.Types
|
||||
) where
|
||||
|
||||
import Crypto.Hash (SHA256)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Dependent.Sum (DSum((:=>)))
|
||||
import Data.HashSet (HashSet)
|
||||
import Data.Map (Map)
|
||||
import Data.Text (Text)
|
||||
import Data.Word (Word64)
|
||||
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.Metadata (Metadata(..), StorePathTrust(..))
|
||||
|
||||
import qualified Data.Text
|
||||
import qualified Control.Monad
|
||||
import qualified Data.Attoparsec.Text
|
||||
import qualified Data.Text.Encoding
|
||||
import qualified System.Nix.Hash
|
||||
--
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
|
||||
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.Metadata ( Metadata(..)
|
||||
, StorePathTrust(..)
|
||||
)
|
||||
|
||||
import qualified Data.Binary.Put
|
||||
import qualified Data.Map.Strict
|
||||
import qualified Data.Serialize.Put
|
||||
import qualified Data.Set
|
||||
|
||||
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.Binary
|
||||
import System.Nix.Store.Remote.Types
|
||||
import System.Nix.Store.Remote.Protocol
|
||||
import System.Nix.Store.Remote.Util
|
||||
import qualified System.Nix.Signature
|
||||
import Crypto.Hash ( SHA256 )
|
||||
import System.Nix.Nar ( NarSource )
|
||||
import System.Nix.Store.Remote.MonadStore
|
||||
import System.Nix.Store.Remote.Protocol
|
||||
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.Prim
|
||||
|
||||
-- | Pack `Nar` and add it to the store.
|
||||
addToStore
|
||||
@ -91,7 +84,7 @@ addToStore name source recursive repair = do
|
||||
$ error "repairing is not supported when building through the Nix daemon"
|
||||
|
||||
runOpArgsIO AddToStore $ \yield -> do
|
||||
yield $ BSL.toStrict $ Data.Binary.Put.runPut $ do
|
||||
yield $ Data.Serialize.Put.runPut $ do
|
||||
putText $ System.Nix.StorePath.unStorePathName name
|
||||
putBool
|
||||
$ not
|
||||
@ -124,7 +117,7 @@ addTextToStore name text references' repair = do
|
||||
putPaths storeDir references'
|
||||
sockGetPath
|
||||
|
||||
addSignatures :: StorePath -> [BSL.ByteString] -> MonadStore ()
|
||||
addSignatures :: StorePath -> [ByteString] -> MonadStore ()
|
||||
addSignatures p signatures = do
|
||||
storeDir <- getStoreDir
|
||||
Control.Monad.void $ simpleOpArgs AddSignatures $ do
|
||||
@ -169,9 +162,30 @@ buildDerivation p drv buildMode = do
|
||||
-- but without it protocol just hangs waiting for
|
||||
-- more data. Needs investigation.
|
||||
-- Intentionally the only warning that should pop-up.
|
||||
putInt (0 :: Integer)
|
||||
putInt (0 :: Int)
|
||||
|
||||
getSocketIncremental getBuildResult
|
||||
getSocketIncremental get
|
||||
|
||||
-- | Delete store paths
|
||||
deleteSpecific
|
||||
:: HashSet StorePath -- ^ Paths to delete
|
||||
-> MonadStore GCResult
|
||||
deleteSpecific paths = do
|
||||
storeDir <- getStoreDir
|
||||
runOpArgs CollectGarbage $ do
|
||||
putEnum GCAction_DeleteSpecific
|
||||
putPaths storeDir paths
|
||||
putBool False -- ignoreLiveness
|
||||
putInt (maxBound :: Word64) -- maxFreedBytes
|
||||
putInt (0::Int)
|
||||
putInt (0::Int)
|
||||
putInt (0::Int)
|
||||
getSocketIncremental $ do
|
||||
gcResult_deletedPaths <- getPathsOrFail storeDir
|
||||
gcResult_bytesFreed <- getInt
|
||||
-- TODO: who knows
|
||||
_ :: Int <- getInt
|
||||
pure GCResult{..}
|
||||
|
||||
ensurePath :: StorePath -> MonadStore ()
|
||||
ensurePath pn = do
|
||||
@ -179,7 +193,7 @@ ensurePath pn = do
|
||||
Control.Monad.void $ simpleOpArgs EnsurePath $ putPath storeDir pn
|
||||
|
||||
-- | Find garbage collector roots.
|
||||
findRoots :: MonadStore (Map BSL.ByteString StorePath)
|
||||
findRoots :: MonadStore (Map ByteString StorePath)
|
||||
findRoots = do
|
||||
runOp FindRoots
|
||||
sd <- getStoreDir
|
||||
@ -187,7 +201,7 @@ findRoots = do
|
||||
getSocketIncremental
|
||||
$ getMany
|
||||
$ (,)
|
||||
<$> (BSL.fromStrict <$> getByteStringLen)
|
||||
<$> getByteString
|
||||
<*> getPath sd
|
||||
|
||||
r <- catRights res
|
||||
@ -208,13 +222,13 @@ isValidPathUncached p = do
|
||||
-- | Query valid paths from set, optionally try to use substitutes.
|
||||
queryValidPaths
|
||||
:: HashSet StorePath -- ^ Set of `StorePath`s to query
|
||||
-> SubstituteFlag -- ^ Try substituting missing paths when `True`
|
||||
-> SubstituteMode -- ^ Try substituting missing paths when `True`
|
||||
-> MonadStore (HashSet StorePath)
|
||||
queryValidPaths ps substitute = do
|
||||
storeDir <- getStoreDir
|
||||
runOpArgs QueryValidPaths $ do
|
||||
putPaths storeDir ps
|
||||
putBool (unSubstituteFlag substitute)
|
||||
putBool $ substitute == SubstituteMode_DoSubstitute
|
||||
sockGetPaths
|
||||
|
||||
queryAllValidPaths :: MonadStore (HashSet StorePath)
|
||||
@ -253,8 +267,8 @@ queryPathInfoUncached path = do
|
||||
narBytes <- Just <$> sockGetInt
|
||||
ultimate <- sockGetBool
|
||||
|
||||
sigStrings <- fmap bsToText <$> sockGetStrings
|
||||
caString <- bsToText <$> sockGetStr
|
||||
sigStrings <- fmap Data.Text.Encoding.decodeUtf8 <$> sockGetStrings
|
||||
caString <- Data.Text.Encoding.decodeUtf8 <$> sockGetStr
|
||||
|
||||
let
|
||||
sigs = case
|
||||
@ -335,7 +349,7 @@ syncWithGC :: MonadStore ()
|
||||
syncWithGC = Control.Monad.void $ simpleOp SyncWithGC
|
||||
|
||||
-- returns True on errors
|
||||
verifyStore :: CheckFlag -> RepairMode -> MonadStore Bool
|
||||
verifyStore :: CheckMode -> RepairMode -> MonadStore Bool
|
||||
verifyStore check repair = simpleOpArgs VerifyStore $ do
|
||||
putBool $ unCheckFlag check
|
||||
putBool $ check == CheckMode_DoCheck
|
||||
putBool $ repair == RepairMode_DoRepair
|
||||
|
45
hnix-store-remote/src/System/Nix/Store/Remote/Arbitrary.hs
Normal file
45
hnix-store-remote/src/System/Nix/Store/Remote/Arbitrary.hs
Normal file
@ -0,0 +1,45 @@
|
||||
-- due to recent generic-arbitrary
|
||||
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
module System.Nix.Store.Remote.Arbitrary where
|
||||
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
import Test.QuickCheck (Arbitrary(..))
|
||||
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||
import Test.QuickCheck.Instances ()
|
||||
|
||||
deriving via GenericArbitrary ProtoVersion
|
||||
instance Arbitrary ProtoVersion
|
||||
|
||||
-- * Logger
|
||||
|
||||
deriving via GenericArbitrary Activity
|
||||
instance Arbitrary Activity
|
||||
|
||||
deriving via GenericArbitrary ActivityID
|
||||
instance Arbitrary ActivityID
|
||||
|
||||
deriving via GenericArbitrary ActivityResult
|
||||
instance Arbitrary ActivityResult
|
||||
|
||||
deriving via GenericArbitrary Field
|
||||
instance Arbitrary Field
|
||||
|
||||
deriving via GenericArbitrary Trace
|
||||
instance Arbitrary Trace
|
||||
|
||||
deriving via GenericArbitrary BasicError
|
||||
instance Arbitrary BasicError
|
||||
|
||||
deriving via GenericArbitrary ErrorInfo
|
||||
instance Arbitrary ErrorInfo
|
||||
|
||||
deriving via GenericArbitrary LoggerOpCode
|
||||
instance Arbitrary LoggerOpCode
|
||||
|
||||
deriving via GenericArbitrary Logger
|
||||
instance Arbitrary Logger
|
||||
|
||||
deriving via GenericArbitrary Verbosity
|
||||
instance Arbitrary Verbosity
|
@ -1,54 +0,0 @@
|
||||
{-|
|
||||
Description : Utilities for packing stuff
|
||||
Maintainer : srk <srk@48.io>
|
||||
|-}
|
||||
module System.Nix.Store.Remote.Binary where
|
||||
|
||||
import Control.Monad
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Binary.Get
|
||||
import Data.Binary.Put
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
|
||||
putInt :: Integral a => a -> Put
|
||||
putInt = putWord64le . fromIntegral
|
||||
|
||||
getInt :: Integral a => Get a
|
||||
getInt = fromIntegral <$> getWord64le
|
||||
|
||||
putMany :: Foldable t => (a -> Put) -> t a -> Put
|
||||
putMany printer xs = do
|
||||
putInt (length xs)
|
||||
mapM_ printer xs
|
||||
|
||||
getMany :: Get a -> Get [a]
|
||||
getMany parser = do
|
||||
count <- getInt
|
||||
replicateM count parser
|
||||
|
||||
-- length prefixed string packing with padding to 8 bytes
|
||||
putByteStringLen :: BSL.ByteString -> Put
|
||||
putByteStringLen x = do
|
||||
putInt len
|
||||
putLazyByteString x
|
||||
when (len `mod` 8 /= 0) $ pad $ 8 - (len `mod` 8)
|
||||
where
|
||||
len :: Int
|
||||
len = fromIntegral $ BSL.length x
|
||||
pad count = replicateM_ count (putWord8 0)
|
||||
|
||||
putByteStrings :: Foldable t => t BSL.ByteString -> Put
|
||||
putByteStrings = putMany putByteStringLen
|
||||
|
||||
getByteStringLen :: Get ByteString
|
||||
getByteStringLen = do
|
||||
len <- getInt
|
||||
st <- getLazyByteString len
|
||||
when (len `mod` 8 /= 0) $ do
|
||||
pads <- unpad $ fromIntegral $ 8 - (len `mod` 8)
|
||||
unless (all (== 0) pads) $ fail $ "No zeroes" <> show (st, len, pads)
|
||||
pure $ BSL.toStrict st
|
||||
where unpad x = replicateM x getWord8
|
||||
|
||||
getByteStrings :: Get [ByteString]
|
||||
getByteStrings = getMany getByteStringLen
|
@ -1,87 +1,67 @@
|
||||
|
||||
module System.Nix.Store.Remote.Logger
|
||||
( Logger(..)
|
||||
, Field(..)
|
||||
, processOutput
|
||||
( processOutput
|
||||
) where
|
||||
|
||||
import Control.Monad.Except ( throwError )
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Control.Monad.Reader (asks)
|
||||
import Control.Monad.State.Strict (get)
|
||||
import Data.Binary.Get
|
||||
|
||||
import Network.Socket.ByteString ( recv )
|
||||
|
||||
import System.Nix.Store.Remote.Binary
|
||||
import System.Nix.Store.Remote.Types
|
||||
import System.Nix.Store.Remote.Util
|
||||
|
||||
import Control.Monad.Except (throwError)
|
||||
import Data.ByteString (ByteString)
|
||||
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 qualified Control.Monad
|
||||
|
||||
controlParser :: Get Logger
|
||||
controlParser = do
|
||||
ctrl <- getInt
|
||||
case (ctrl :: Int) of
|
||||
0x6f6c6d67 -> Next <$> getByteStringLen
|
||||
0x64617461 -> Read <$> getInt
|
||||
0x64617416 -> Write <$> getByteStringLen
|
||||
0x616c7473 -> pure Last
|
||||
0x63787470 -> flip Error <$> getByteStringLen
|
||||
<*> getInt
|
||||
0x53545254 -> StartActivity <$> getInt
|
||||
<*> getInt
|
||||
<*> getInt
|
||||
<*> getByteStringLen
|
||||
<*> getFields
|
||||
<*> getInt
|
||||
0x53544f50 -> StopActivity <$> getInt
|
||||
0x52534c54 -> Result <$> getInt
|
||||
<*> getInt
|
||||
<*> getFields
|
||||
x -> fail $ "Invalid control message received:" <> show x
|
||||
import qualified Control.Monad.Reader
|
||||
import qualified Control.Monad.State.Strict
|
||||
import qualified Data.Serialize.Get
|
||||
import qualified Data.Serializer
|
||||
|
||||
processOutput :: MonadStore [Logger]
|
||||
processOutput = go decoder
|
||||
processOutput = do
|
||||
protoVersion <- Control.Monad.Reader.asks hasProtoVersion
|
||||
sockGet8 >>= go . (decoder protoVersion)
|
||||
where
|
||||
decoder = runGetIncremental controlParser
|
||||
go :: Decoder Logger -> MonadStore [Logger]
|
||||
go (Done _leftover _consumed ctrl) = do
|
||||
case ctrl of
|
||||
e@(Error _ _) -> pure [e]
|
||||
Last -> pure [Last]
|
||||
Read _n -> do
|
||||
(mdata, _) <- get
|
||||
case mdata of
|
||||
Nothing -> throwError "No data to read provided"
|
||||
Just part -> do
|
||||
-- XXX: we should check/assert part size against n of (Read n)
|
||||
sockPut $ putByteStringLen part
|
||||
clearData
|
||||
decoder
|
||||
:: ProtoVersion
|
||||
-> ByteString
|
||||
-> Result (Either LoggerSError Logger)
|
||||
decoder protoVersion =
|
||||
Data.Serialize.Get.runGetPartial
|
||||
(runSerialT protoVersion $ Data.Serializer.getS logger)
|
||||
|
||||
go decoder
|
||||
go :: Result (Either LoggerSError Logger) -> MonadStore [Logger]
|
||||
go (Done ectrl leftover) = do
|
||||
|
||||
-- we should probably handle Read here as well
|
||||
x -> do
|
||||
next <- go decoder
|
||||
pure $ x : next
|
||||
Control.Monad.unless (leftover == mempty) $
|
||||
-- TODO: throwError
|
||||
error $ "Leftovers detected: '" ++ show leftover ++ "'"
|
||||
|
||||
protoVersion <- Control.Monad.Reader.asks hasProtoVersion
|
||||
case ectrl of
|
||||
-- TODO: tie this with throwError and better error type
|
||||
Left e -> error $ show e
|
||||
Right ctrl -> do
|
||||
case ctrl of
|
||||
e@(Logger_Error _) -> pure [e]
|
||||
Logger_Last -> pure [Logger_Last]
|
||||
Logger_Read _n -> do
|
||||
(mdata, _) <- Control.Monad.State.Strict.get
|
||||
case mdata of
|
||||
Nothing -> throwError "No data to read provided"
|
||||
Just part -> do
|
||||
-- XXX: we should check/assert part size against n of (Read n)
|
||||
sockPut $ putByteString part
|
||||
clearData
|
||||
|
||||
sockGet8 >>= go . (decoder protoVersion)
|
||||
|
||||
-- we should probably handle Read here as well
|
||||
x -> do
|
||||
next <- sockGet8 >>= go . (decoder protoVersion)
|
||||
pure $ x : next
|
||||
go (Partial k) = do
|
||||
soc <- asks storeSocket
|
||||
chunk <- liftIO (Just <$> recv soc 8)
|
||||
chunk <- sockGet8
|
||||
go (k chunk)
|
||||
|
||||
go (Fail _leftover _consumed msg) = error msg
|
||||
|
||||
getFields :: Get [Field]
|
||||
getFields = do
|
||||
cnt <- getInt
|
||||
Control.Monad.replicateM cnt getField
|
||||
|
||||
getField :: Get Field
|
||||
getField = do
|
||||
typ <- getInt
|
||||
case (typ :: Int) of
|
||||
0 -> LogInt <$> getInt
|
||||
1 -> LogStr <$> getByteStringLen
|
||||
x -> fail $ "Unknown log type: " <> show x
|
||||
go (Fail msg _leftover) = error msg
|
||||
|
58
hnix-store-remote/src/System/Nix/Store/Remote/MonadStore.hs
Normal file
58
hnix-store-remote/src/System/Nix/Store/Remote/MonadStore.hs
Normal file
@ -0,0 +1,58 @@
|
||||
module System.Nix.Store.Remote.MonadStore
|
||||
( MonadStore
|
||||
, mapStoreDir
|
||||
, getStoreDir
|
||||
, getLog
|
||||
, flushLog
|
||||
, gotError
|
||||
, getErrors
|
||||
, 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 Data.ByteString (ByteString)
|
||||
|
||||
import Control.Monad.Trans.State.Strict (mapStateT)
|
||||
import Control.Monad.Trans.Except (mapExceptT)
|
||||
import Control.Monad.Trans.Reader (withReaderT)
|
||||
|
||||
import System.Nix.StorePath (HasStoreDir(..), StoreDir)
|
||||
import System.Nix.Store.Remote.Types.Logger (Logger, isError)
|
||||
import System.Nix.Store.Remote.Types.StoreConfig (StoreConfig(..))
|
||||
|
||||
-- | Ask for a @StoreDir@
|
||||
getStoreDir :: (HasStoreDir r, MonadReader r m) => m StoreDir
|
||||
getStoreDir = asks hasStoreDir
|
||||
|
||||
type MonadStore a
|
||||
= ExceptT
|
||||
String
|
||||
(StateT (Maybe ByteString, [Logger]) (ReaderT StoreConfig IO))
|
||||
a
|
||||
|
||||
-- | 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 }
|
||||
|
||||
gotError :: MonadStore Bool
|
||||
gotError = gets (any isError . snd)
|
||||
|
||||
getErrors :: MonadStore [Logger]
|
||||
getErrors = gets (filter isError . snd)
|
||||
|
||||
getLog :: MonadStore [Logger]
|
||||
getLog = gets snd
|
||||
|
||||
flushLog :: MonadStore ()
|
||||
flushLog = modify (\(a, _b) -> (a, []))
|
||||
|
||||
setData :: ByteString -> MonadStore ()
|
||||
setData x = modify (\(_, b) -> (Just x, b))
|
||||
|
||||
clearData :: MonadStore ()
|
||||
clearData = modify (\(_, b) -> (Nothing, b))
|
@ -11,39 +11,39 @@ module System.Nix.Store.Remote.Protocol
|
||||
, runStoreOpts
|
||||
, runStoreOptsTCP
|
||||
, runStoreOpts'
|
||||
, 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.Exception ( bracket )
|
||||
import Control.Monad.Except
|
||||
import Control.Monad.Reader (asks, runReaderT)
|
||||
import Control.Monad.State.Strict
|
||||
|
||||
import Data.Default.Class (Default(def))
|
||||
import qualified Data.Bool
|
||||
import Data.Binary.Get
|
||||
import Data.Binary.Put
|
||||
import Data.Serialize.Get
|
||||
import Data.Serialize.Put
|
||||
import qualified Data.ByteString
|
||||
import qualified Data.ByteString.Char8
|
||||
import qualified Data.ByteString.Lazy
|
||||
|
||||
import Network.Socket ( SockAddr(SockAddrUnix) )
|
||||
import qualified Network.Socket as S
|
||||
import Network.Socket.ByteString ( recv
|
||||
, sendAll
|
||||
)
|
||||
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.Binary
|
||||
import System.Nix.Store.Remote.Logger
|
||||
import System.Nix.Store.Remote.Types
|
||||
import System.Nix.Store.Remote.Util
|
||||
import System.Nix.StorePath (StoreDir(..))
|
||||
import System.Nix.Store.Remote.Serialize.Prim
|
||||
import System.Nix.Store.Remote.Logger
|
||||
import System.Nix.Store.Remote.MonadStore
|
||||
import System.Nix.Store.Remote.Socket
|
||||
import System.Nix.Store.Remote.Serializer (protoVersion)
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
|
||||
protoVersion :: Int
|
||||
protoVersion = 0x115
|
||||
-- major protoVersion & 0xFF00
|
||||
-- minor .. & 0x00FF
|
||||
ourProtoVersion :: ProtoVersion
|
||||
ourProtoVersion = ProtoVersion
|
||||
{ protoVersion_major = 1
|
||||
, protoVersion_minor = 21
|
||||
}
|
||||
|
||||
workerMagic1 :: Int
|
||||
workerMagic1 = 0x6e697863
|
||||
@ -53,76 +53,6 @@ workerMagic2 = 0x6478696f
|
||||
defaultSockPath :: String
|
||||
defaultSockPath = "/nix/var/nix/daemon-socket/socket"
|
||||
|
||||
data WorkerOp =
|
||||
IsValidPath
|
||||
| HasSubstitutes
|
||||
| QueryReferrers
|
||||
| AddToStore
|
||||
| AddTextToStore
|
||||
| BuildPaths
|
||||
| EnsurePath
|
||||
| AddTempRoot
|
||||
| AddIndirectRoot
|
||||
| SyncWithGC
|
||||
| FindRoots
|
||||
| SetOptions
|
||||
| CollectGarbage
|
||||
| QuerySubstitutablePathInfo
|
||||
| QueryDerivationOutputs
|
||||
| QueryAllValidPaths
|
||||
| QueryFailedPaths
|
||||
| ClearFailedPaths
|
||||
| QueryPathInfo
|
||||
| QueryDerivationOutputNames
|
||||
| QueryPathFromHashPart
|
||||
| QuerySubstitutablePathInfos
|
||||
| QueryValidPaths
|
||||
| QuerySubstitutablePaths
|
||||
| QueryValidDerivers
|
||||
| OptimiseStore
|
||||
| VerifyStore
|
||||
| BuildDerivation
|
||||
| AddSignatures
|
||||
| NarFromPath
|
||||
| AddToStoreNar
|
||||
| QueryMissing
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
opNum :: WorkerOp -> Int
|
||||
opNum IsValidPath = 1
|
||||
opNum HasSubstitutes = 3
|
||||
opNum QueryReferrers = 6
|
||||
opNum AddToStore = 7
|
||||
opNum AddTextToStore = 8
|
||||
opNum BuildPaths = 9
|
||||
opNum EnsurePath = 10
|
||||
opNum AddTempRoot = 11
|
||||
opNum AddIndirectRoot = 12
|
||||
opNum SyncWithGC = 13
|
||||
opNum FindRoots = 14
|
||||
opNum SetOptions = 19
|
||||
opNum CollectGarbage = 20
|
||||
opNum QuerySubstitutablePathInfo = 21
|
||||
opNum QueryDerivationOutputs = 22
|
||||
opNum QueryAllValidPaths = 23
|
||||
opNum QueryFailedPaths = 24
|
||||
opNum ClearFailedPaths = 25
|
||||
opNum QueryPathInfo = 26
|
||||
opNum QueryDerivationOutputNames = 28
|
||||
opNum QueryPathFromHashPart = 29
|
||||
opNum QuerySubstitutablePathInfos = 30
|
||||
opNum QueryValidPaths = 31
|
||||
opNum QuerySubstitutablePaths = 32
|
||||
opNum QueryValidDerivers = 33
|
||||
opNum OptimiseStore = 34
|
||||
opNum VerifyStore = 35
|
||||
opNum BuildDerivation = 36
|
||||
opNum AddSignatures = 37
|
||||
opNum NarFromPath = 38
|
||||
opNum AddToStoreNar = 39
|
||||
opNum QueryMissing = 40
|
||||
|
||||
|
||||
simpleOp :: WorkerOp -> MonadStore Bool
|
||||
simpleOp op = simpleOpArgs op $ pure ()
|
||||
|
||||
@ -133,8 +63,8 @@ simpleOpArgs op args = do
|
||||
Data.Bool.bool
|
||||
sockGetBool
|
||||
(do
|
||||
Error _num msg <- head <$> getError
|
||||
throwError $ Data.ByteString.Char8.unpack msg
|
||||
-- TODO: don't use show
|
||||
getErrors >>= throwError . show
|
||||
)
|
||||
err
|
||||
|
||||
@ -145,7 +75,7 @@ runOpArgs :: WorkerOp -> Put -> MonadStore ()
|
||||
runOpArgs op args =
|
||||
runOpArgsIO
|
||||
op
|
||||
(\encode -> encode $ Data.ByteString.Lazy.toStrict $ runPut args)
|
||||
(\encode -> encode $ runPut args)
|
||||
|
||||
runOpArgsIO
|
||||
:: WorkerOp
|
||||
@ -153,17 +83,17 @@ runOpArgsIO
|
||||
-> MonadStore ()
|
||||
runOpArgsIO op encoder = do
|
||||
|
||||
sockPut $ putInt $ opNum op
|
||||
sockPut $ putEnum op
|
||||
|
||||
soc <- asks storeSocket
|
||||
soc <- asks storeConfig_socket
|
||||
encoder (liftIO . sendAll soc)
|
||||
|
||||
out <- processOutput
|
||||
modify (\(a, b) -> (a, b <> out))
|
||||
err <- gotError
|
||||
Control.Monad.when err $ do
|
||||
Error _num msg <- head <$> getError
|
||||
throwError $ Data.ByteString.Char8.unpack msg
|
||||
-- TODO: don't use show
|
||||
getErrors >>= throwError . show
|
||||
|
||||
runStore :: MonadStore a -> IO (Either String a, [Logger])
|
||||
runStore = runStoreOpts defaultSockPath def
|
||||
@ -182,30 +112,36 @@ runStoreOptsTCP host port storeRootDir code = do
|
||||
runStoreOpts'
|
||||
:: S.Family -> S.SockAddr -> StoreDir -> MonadStore a -> IO (Either String a, [Logger])
|
||||
runStoreOpts' sockFamily sockAddr storeRootDir code =
|
||||
bracket open (S.close . storeSocket) run
|
||||
bracket open (S.close . storeConfig_socket) run
|
||||
|
||||
where
|
||||
open = do
|
||||
soc <- S.socket sockFamily S.Stream 0
|
||||
S.connect soc sockAddr
|
||||
pure StoreConfig
|
||||
{ storeSocket = soc
|
||||
, storeDir = storeRootDir
|
||||
{ storeConfig_dir = storeRootDir
|
||||
, storeConfig_protoVersion = ourProtoVersion
|
||||
, storeConfig_socket = soc
|
||||
}
|
||||
|
||||
greet = do
|
||||
sockPut $ putInt workerMagic1
|
||||
soc <- asks storeSocket
|
||||
soc <- asks hasStoreSocket
|
||||
vermagic <- liftIO $ recv soc 16
|
||||
let
|
||||
(magic2, _daemonProtoVersion) =
|
||||
flip runGet (Data.ByteString.Lazy.fromStrict vermagic)
|
||||
eres =
|
||||
flip runGet vermagic
|
||||
$ (,)
|
||||
<$> (getInt :: Get Int)
|
||||
<*> (getInt :: Get Int)
|
||||
Control.Monad.unless (magic2 == workerMagic2) $ error "Worker magic 2 mismatch"
|
||||
|
||||
sockPut $ putInt protoVersion -- clientVersion
|
||||
case eres of
|
||||
Left err -> error $ "Error parsing vermagic " ++ err
|
||||
Right (magic2, _daemonProtoVersion) -> do
|
||||
Control.Monad.unless (magic2 == workerMagic2) $ error "Worker magic 2 mismatch"
|
||||
|
||||
pv <- asks hasProtoVersion
|
||||
sockPutS @() protoVersion pv -- clientVersion
|
||||
sockPut $ putInt (0 :: Int) -- affinity
|
||||
sockPut $ putInt (0 :: Int) -- obsolete reserveSpace
|
||||
|
||||
|
@ -9,7 +9,10 @@ import Data.Serialize (Serialize(..))
|
||||
import Data.Serialize.Get (Get)
|
||||
import Data.Serialize.Put (Putter)
|
||||
import Data.Text (Text)
|
||||
import Data.Word (Word8, Word32)
|
||||
|
||||
import qualified Control.Monad
|
||||
import qualified Data.Bits
|
||||
import qualified Data.Bool
|
||||
import qualified Data.Map
|
||||
import qualified Data.Set
|
||||
@ -20,11 +23,14 @@ import System.Nix.Derivation (Derivation(..), DerivationOutput(..))
|
||||
import System.Nix.Build (BuildMode(..), BuildStatus(..), BuildResult(..))
|
||||
import System.Nix.StorePath (StoreDir, StorePath)
|
||||
import System.Nix.Store.Remote.Serialize.Prim
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
instance Serialize Text where
|
||||
get = getText
|
||||
put = putText
|
||||
|
||||
-- * BuildResult
|
||||
|
||||
instance Serialize BuildMode where
|
||||
get = getEnum
|
||||
put = putEnum
|
||||
@ -37,7 +43,7 @@ instance Serialize BuildResult where
|
||||
get = do
|
||||
status <- get
|
||||
errorMessage <-
|
||||
(\em -> Data.Bool.bool (Just em) Nothing (Data.Text.null em))
|
||||
(\em -> Data.Bool.bool (Just em) Nothing (Data.Text.null em))
|
||||
<$> get
|
||||
timesBuilt <- getInt
|
||||
isNonDeterministic <- getBool
|
||||
@ -55,6 +61,30 @@ instance Serialize BuildResult where
|
||||
putTime startTime
|
||||
putTime stopTime
|
||||
|
||||
-- * GCAction
|
||||
--
|
||||
instance Serialize GCAction where
|
||||
get = getEnum
|
||||
put = putEnum
|
||||
|
||||
-- * ProtoVersion
|
||||
|
||||
-- protoVersion_major & 0xFF00
|
||||
-- protoVersion_minor & 0x00FF
|
||||
instance Serialize ProtoVersion where
|
||||
get = do
|
||||
v <- getInt @Word32
|
||||
pure ProtoVersion
|
||||
{ protoVersion_major = fromIntegral $ Data.Bits.shiftR v 8
|
||||
, protoVersion_minor = fromIntegral $ v Data.Bits..&. 0x00FF
|
||||
}
|
||||
put p =
|
||||
putInt @Word32
|
||||
$ ((Data.Bits.shiftL (fromIntegral $ protoVersion_major p :: Word32) 8)
|
||||
Data.Bits..|. fromIntegral (protoVersion_minor p))
|
||||
|
||||
-- * Derivation
|
||||
|
||||
getDerivation
|
||||
:: StoreDir
|
||||
-> Get (Derivation StorePath Text)
|
||||
@ -104,3 +134,75 @@ putDerivation storeDir Derivation{..} = do
|
||||
|
||||
flip putMany (Data.Map.toList env)
|
||||
$ \(a1, a2) -> putText a1 *> putText a2
|
||||
|
||||
-- * Logger
|
||||
|
||||
instance Serialize Activity where
|
||||
get =
|
||||
toEnumCheckBounds . (+(-100)) <$> getInt
|
||||
>>= either fail pure
|
||||
put = putInt . (+100) . fromEnum
|
||||
|
||||
instance Serialize ActivityID where
|
||||
get = ActivityID <$> getInt
|
||||
put (ActivityID aid) = putInt aid
|
||||
|
||||
instance Serialize ActivityResult where
|
||||
get =
|
||||
toEnumCheckBounds . (+(-100)) <$> getInt
|
||||
>>= either fail pure
|
||||
put = putInt . (+100) . fromEnum
|
||||
|
||||
instance Serialize Field where
|
||||
get = (getInt :: Get Word8) >>= \case
|
||||
0 -> Field_LogInt <$> getInt
|
||||
1 -> Field_LogStr <$> getText
|
||||
x -> fail $ "Unknown log field type: " <> show x
|
||||
put (Field_LogInt x) = putInt (0 :: Word8) >> putInt x
|
||||
put (Field_LogStr x) = putInt (1 :: Word8) >> putText x
|
||||
|
||||
instance Serialize Trace where
|
||||
get = do
|
||||
tracePosition <- (\case 0 -> Nothing; x -> Just x) <$> getInt @Int
|
||||
traceHint <- get
|
||||
pure Trace{..}
|
||||
put Trace{..} = do
|
||||
maybe (putInt @Int 0) putInt $ tracePosition
|
||||
put traceHint
|
||||
|
||||
instance Serialize BasicError where
|
||||
get = do
|
||||
basicErrorMessage <- get
|
||||
basicErrorExitStatus <- getInt
|
||||
pure BasicError{..}
|
||||
put BasicError{..} = do
|
||||
put basicErrorMessage
|
||||
putInt basicErrorExitStatus
|
||||
|
||||
instance Serialize ErrorInfo where
|
||||
get = do
|
||||
etyp <- get @Text
|
||||
Control.Monad.unless (etyp == Data.Text.pack "Error")
|
||||
$ fail
|
||||
$ "get ErrorInfo: received unknown error type" ++ show etyp
|
||||
errorInfoLevel <- get
|
||||
_name <- get @Text -- removed error name
|
||||
errorInfoMessage <- get
|
||||
errorInfoPosition <- (\case 0 -> Nothing; x -> Just x) <$> getInt @Int
|
||||
errorInfoTraces <- getMany get
|
||||
pure ErrorInfo{..}
|
||||
put ErrorInfo{..} = do
|
||||
put $ Data.Text.pack "Error"
|
||||
put errorInfoLevel
|
||||
put $ Data.Text.pack "Error" -- removed error name
|
||||
put errorInfoMessage
|
||||
maybe (putInt @Int 0) putInt $ errorInfoPosition
|
||||
putMany put errorInfoTraces
|
||||
|
||||
instance Serialize LoggerOpCode where
|
||||
get = getInt @Int >>= either fail pure . intToLoggerOpCode
|
||||
put = putInt @Int . loggerOpCodeToInt
|
||||
|
||||
instance Serialize Verbosity where
|
||||
get = getEnum
|
||||
put = putEnum
|
||||
|
@ -11,9 +11,11 @@ import Data.Serialize.Get (Get)
|
||||
import Data.Serialize.Put (Putter)
|
||||
import Data.Text (Text)
|
||||
import Data.Time (NominalDiffTime, UTCTime)
|
||||
import Data.Word (Word8)
|
||||
import System.Nix.StorePath (StoreDir, StorePath, InvalidPathError)
|
||||
|
||||
import qualified Control.Monad
|
||||
import qualified Data.Either
|
||||
import qualified Data.HashSet
|
||||
import qualified Data.Serialize.Get
|
||||
import qualified Data.Serialize.Put
|
||||
@ -25,18 +27,21 @@ import qualified System.Nix.StorePath
|
||||
-- * Int
|
||||
|
||||
-- | Deserialize Nix like integer
|
||||
getInt :: Get Int
|
||||
getInt :: Integral a => Get a
|
||||
getInt = fromIntegral <$> Data.Serialize.Get.getWord64le
|
||||
|
||||
-- | Serialize Nix like integer
|
||||
putInt :: Putter Int
|
||||
putInt :: Integral a => Putter a
|
||||
putInt = Data.Serialize.Put.putWord64le . fromIntegral
|
||||
|
||||
-- * Bool
|
||||
|
||||
-- | Deserialize @Bool@ from integer
|
||||
getBool :: Get Bool
|
||||
getBool = (== 1) <$> (getInt :: Get Int)
|
||||
getBool = (getInt :: Get Word8) >>= \case
|
||||
0 -> pure False
|
||||
1 -> pure True
|
||||
x -> fail $ "illegal bool value " ++ show x
|
||||
|
||||
-- | Serialize @Bool@ into integer
|
||||
putBool :: Putter Bool
|
||||
@ -45,9 +50,18 @@ putBool False = putInt (0 :: Int)
|
||||
|
||||
-- * Enum
|
||||
|
||||
-- | Utility toEnum version checking bounds using Bounded class
|
||||
toEnumCheckBounds :: Enum a => Int -> Either String a
|
||||
toEnumCheckBounds = \case
|
||||
x | x < minBound -> Left $ "enum out of min bound " ++ show x
|
||||
x | x > maxBound -> Left $ "enum out of max bound " ++ show x
|
||||
x | otherwise -> Right $ toEnum x
|
||||
|
||||
-- | Deserialize @Enum@ to integer
|
||||
getEnum :: Enum a => Get a
|
||||
getEnum = toEnum <$> getInt
|
||||
getEnum =
|
||||
toEnumCheckBounds <$> getInt
|
||||
>>= either fail pure
|
||||
|
||||
-- | Serialize @Enum@ to integer
|
||||
putEnum :: Enum a => Putter a
|
||||
@ -179,6 +193,19 @@ getPaths sd =
|
||||
. fmap (System.Nix.StorePath.parsePath sd)
|
||||
<$> getByteStrings
|
||||
|
||||
-- | Deserialize @StorePath@, checking
|
||||
-- that @StoreDir@ matches expected value
|
||||
getPathsOrFail :: StoreDir -> Get (HashSet StorePath)
|
||||
getPathsOrFail sd = do
|
||||
eps <-
|
||||
fmap (System.Nix.StorePath.parsePath sd)
|
||||
<$> getByteStrings
|
||||
Control.Monad.when (any Data.Either.isLeft eps)
|
||||
$ fail
|
||||
$ show
|
||||
$ Data.Either.lefts eps
|
||||
pure $ Data.HashSet.fromList $ Data.Either.rights eps
|
||||
|
||||
-- | Serialize a @HashSet@ of @StorePath@s
|
||||
putPaths :: StoreDir -> Putter (HashSet StorePath)
|
||||
putPaths storeDir =
|
||||
|
874
hnix-store-remote/src/System/Nix/Store/Remote/Serializer.hs
Normal file
874
hnix-store-remote/src/System/Nix/Store/Remote/Serializer.hs
Normal file
@ -0,0 +1,874 @@
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
{-|
|
||||
Description : NixSerializer
|
||||
Copyright : (c) John Ericson, 2023
|
||||
Richard Marko, 2023
|
||||
|-}
|
||||
|
||||
module System.Nix.Store.Remote.Serializer
|
||||
(
|
||||
-- * NixSerializer
|
||||
NixSerializer
|
||||
-- * Errors
|
||||
, SError(..)
|
||||
-- ** Runners
|
||||
, runSerialT
|
||||
, runG
|
||||
, runP
|
||||
-- * Primitives
|
||||
, int
|
||||
, bool
|
||||
, byteString
|
||||
, enum
|
||||
, text
|
||||
, maybeText
|
||||
-- * UTCTime
|
||||
, time
|
||||
-- * Combinators
|
||||
, list
|
||||
, set
|
||||
, hashSet
|
||||
, mapS
|
||||
-- * ProtoVersion
|
||||
, protoVersion
|
||||
-- * StorePath
|
||||
, storePath
|
||||
, storePathHashPart
|
||||
, storePathName
|
||||
-- * Metadata
|
||||
, pathMetadata
|
||||
-- * Some HashAlgo
|
||||
, someHashAlgo
|
||||
-- * Digest
|
||||
, digest
|
||||
-- * Derivation
|
||||
, derivation
|
||||
-- * Derivation
|
||||
, derivedPath
|
||||
-- * Build
|
||||
, buildMode
|
||||
, buildResult
|
||||
-- * Logger
|
||||
, LoggerSError(..)
|
||||
, activityID
|
||||
, maybeActivity
|
||||
, activityResult
|
||||
, field
|
||||
, trace
|
||||
, basicError
|
||||
, errorInfo
|
||||
, loggerOpCode
|
||||
, logger
|
||||
, verbosity
|
||||
) where
|
||||
|
||||
import Control.Monad.Except (MonadError, throwError, withExceptT)
|
||||
import Control.Monad.Reader (MonadReader)
|
||||
import Control.Monad.Trans (MonadTrans, lift)
|
||||
import Control.Monad.Trans.Reader (ReaderT, runReaderT)
|
||||
import Control.Monad.Trans.Except (ExceptT, runExceptT)
|
||||
import Crypto.Hash (Digest, HashAlgorithm, SHA256)
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Dependent.Sum (DSum((:=>)))
|
||||
import Data.Fixed (Uni)
|
||||
import Data.Hashable (Hashable)
|
||||
import Data.HashSet (HashSet)
|
||||
import Data.Map (Map)
|
||||
import Data.Set (Set)
|
||||
import Data.Some (Some)
|
||||
import Data.Text (Text)
|
||||
import Data.Time (NominalDiffTime, UTCTime)
|
||||
import Data.Vector (Vector)
|
||||
import Data.Word (Word8, Word32, Word64)
|
||||
import GHC.Generics (Generic)
|
||||
|
||||
import qualified Control.Monad
|
||||
import qualified Control.Monad.Reader
|
||||
import qualified Data.Attoparsec.Text
|
||||
import qualified Data.Bits
|
||||
import qualified Data.ByteString
|
||||
import qualified Data.HashSet
|
||||
import qualified Data.Map.Strict
|
||||
import qualified Data.Serialize.Get
|
||||
import qualified Data.Serialize.Put
|
||||
import qualified Data.Set
|
||||
import qualified Data.Text
|
||||
import qualified Data.Text.Encoding
|
||||
import qualified Data.Time.Clock.POSIX
|
||||
import qualified Data.Vector
|
||||
|
||||
import Data.Serializer
|
||||
import System.Nix.Base (BaseEncoding(NixBase32))
|
||||
import System.Nix.Build (BuildMode, BuildResult(..))
|
||||
import System.Nix.ContentAddress (ContentAddress)
|
||||
import System.Nix.Derivation (Derivation(..), DerivationOutput(..))
|
||||
import System.Nix.DerivedPath (DerivedPath, ParseOutputsError)
|
||||
import System.Nix.Hash (HashAlgo)
|
||||
import System.Nix.Signature (NarSignature)
|
||||
import System.Nix.StorePath (HasStoreDir(..), InvalidPathError, StorePath, StorePathHashPart, StorePathName)
|
||||
import System.Nix.StorePath.Metadata (Metadata(..), StorePathTrust(..))
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
import qualified Data.Coerce
|
||||
import qualified Data.Bifunctor
|
||||
import qualified Data.Some
|
||||
import qualified System.Nix.Base
|
||||
import qualified System.Nix.ContentAddress
|
||||
import qualified System.Nix.DerivedPath
|
||||
import qualified System.Nix.Hash
|
||||
import qualified System.Nix.Signature
|
||||
import qualified System.Nix.StorePath
|
||||
|
||||
-- | Transformer for @Serializer@
|
||||
newtype SerialT r e m a = SerialT
|
||||
{ _unSerialT :: ExceptT e (ReaderT r m) a }
|
||||
deriving
|
||||
( Applicative
|
||||
, Functor
|
||||
, Monad
|
||||
, MonadError e
|
||||
, MonadReader r
|
||||
, MonadFail
|
||||
)
|
||||
|
||||
instance MonadTrans (SerialT r e) where
|
||||
lift = SerialT . lift . lift
|
||||
|
||||
-- | Runner for @SerialT@
|
||||
runSerialT
|
||||
:: Monad m
|
||||
=> r
|
||||
-> SerialT r e m a
|
||||
-> m (Either e a)
|
||||
runSerialT r =
|
||||
(`runReaderT` r)
|
||||
. runExceptT
|
||||
. _unSerialT
|
||||
|
||||
mapError
|
||||
:: Functor m
|
||||
=> (e -> e')
|
||||
-> SerialT r e m a
|
||||
-> SerialT r e' m a
|
||||
mapError f =
|
||||
SerialT
|
||||
. withExceptT f
|
||||
. _unSerialT
|
||||
|
||||
-- * NixSerializer
|
||||
|
||||
type NixSerializer r e = Serializer (SerialT r e)
|
||||
|
||||
-- * Errors
|
||||
|
||||
data SError
|
||||
= SError
|
||||
| SError_BadPadding
|
||||
{ badPaddingStr :: ByteString
|
||||
, badPaddingLen :: Int
|
||||
, badPaddingPads :: [Word8]
|
||||
}
|
||||
| SError_ContentAddress String
|
||||
| SError_DerivedPath ParseOutputsError
|
||||
| SError_Digest String
|
||||
| SError_EnumOutOfMinBound Int
|
||||
| SError_EnumOutOfMaxBound Int
|
||||
| SError_HashAlgo String
|
||||
| SError_IllegalBool Word64
|
||||
| SError_InvalidNixBase32
|
||||
| SError_NarHashMustBeSHA256
|
||||
| SError_NotYetImplemented String (ForPV ProtoVersion)
|
||||
| SError_Path InvalidPathError
|
||||
| SError_Signature String
|
||||
deriving (Eq, Ord, Generic, Show)
|
||||
|
||||
data ForPV a
|
||||
= ForPV_Newer a
|
||||
| ForPV_Older a
|
||||
deriving (Eq, Ord, Generic, Show)
|
||||
|
||||
-- ** Runners
|
||||
|
||||
runG
|
||||
:: NixSerializer r e a
|
||||
-> r
|
||||
-> ByteString
|
||||
-> Either (GetSerializerError e) a
|
||||
runG serializer r =
|
||||
transformGetError
|
||||
. runGetS
|
||||
serializer
|
||||
(runSerialT r)
|
||||
|
||||
runP
|
||||
:: NixSerializer r e a
|
||||
-> r
|
||||
-> a
|
||||
-> Either e ByteString
|
||||
runP serializer r =
|
||||
transformPutError
|
||||
. runPutS
|
||||
serializer
|
||||
(runSerialT r)
|
||||
|
||||
-- * Primitives
|
||||
|
||||
int :: Integral a => NixSerializer r e a
|
||||
int = Serializer
|
||||
{ getS = fromIntegral <$> lift Data.Serialize.Get.getWord64le
|
||||
, putS = lift . Data.Serialize.Put.putWord64le . fromIntegral
|
||||
}
|
||||
|
||||
bool :: NixSerializer r SError Bool
|
||||
bool = Serializer
|
||||
{ getS = getS (int @Word64) >>= \case
|
||||
0 -> pure False
|
||||
1 -> pure True
|
||||
x -> throwError $ SError_IllegalBool x
|
||||
, putS = \case
|
||||
False -> putS (int @Word8) 0
|
||||
True -> putS (int @Word8) 1
|
||||
}
|
||||
|
||||
byteString :: NixSerializer r SError ByteString
|
||||
byteString = Serializer
|
||||
{ getS = do
|
||||
len <- getS int
|
||||
st <- lift $ Data.Serialize.Get.getByteString len
|
||||
Control.Monad.when (len `mod` 8 /= 0) $ do
|
||||
pads <- unpad $ fromIntegral $ 8 - (len `mod` 8)
|
||||
Control.Monad.unless
|
||||
(all (== 0) pads)
|
||||
$ throwError
|
||||
$ SError_BadPadding st len pads
|
||||
pure st
|
||||
, putS = \x -> do
|
||||
let len = Data.ByteString.length x
|
||||
putS int len
|
||||
lift $ Data.Serialize.Put.putByteString x
|
||||
Control.Monad.when
|
||||
(len `mod` 8 /= 0)
|
||||
$ pad $ 8 - (len `mod` 8)
|
||||
}
|
||||
where
|
||||
unpad count =
|
||||
Control.Monad.replicateM
|
||||
count
|
||||
(lift Data.Serialize.Get.getWord8)
|
||||
pad count =
|
||||
Control.Monad.replicateM_
|
||||
count
|
||||
(lift $ Data.Serialize.Put.putWord8 0)
|
||||
|
||||
-- | Utility toEnum version checking bounds using Bounded class
|
||||
toEnumCheckBoundsM
|
||||
:: ( Enum a
|
||||
, MonadError SError m
|
||||
)
|
||||
=> Int
|
||||
-> m a
|
||||
toEnumCheckBoundsM = \case
|
||||
x | x < minBound -> throwError $ SError_EnumOutOfMinBound x
|
||||
x | x > maxBound -> throwError $ SError_EnumOutOfMaxBound x
|
||||
x | otherwise -> pure $ toEnum x
|
||||
|
||||
enum :: Enum a => NixSerializer r SError a
|
||||
enum = Serializer
|
||||
{ getS = getS int >>= toEnumCheckBoundsM
|
||||
, putS = putS int . fromEnum
|
||||
}
|
||||
|
||||
text :: NixSerializer r SError Text
|
||||
text = mapIsoSerializer
|
||||
Data.Text.Encoding.decodeUtf8
|
||||
Data.Text.Encoding.encodeUtf8
|
||||
byteString
|
||||
|
||||
maybeText :: NixSerializer r SError (Maybe Text)
|
||||
maybeText = mapIsoSerializer
|
||||
(\case
|
||||
t | Data.Text.null t -> Nothing
|
||||
t | otherwise -> Just t
|
||||
)
|
||||
(maybe mempty id)
|
||||
text
|
||||
|
||||
-- * UTCTime
|
||||
|
||||
time :: NixSerializer r e UTCTime
|
||||
time = Serializer
|
||||
{ getS =
|
||||
Data.Time.Clock.POSIX.posixSecondsToUTCTime
|
||||
. toPicoSeconds
|
||||
<$> getS int
|
||||
, putS =
|
||||
putS int
|
||||
. fromPicoSeconds
|
||||
. Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds
|
||||
}
|
||||
where
|
||||
-- fancy (*10^12), from Int to Uni to Pico(seconds)
|
||||
toPicoSeconds :: Int -> NominalDiffTime
|
||||
toPicoSeconds n = realToFrac (toEnum n :: Uni)
|
||||
|
||||
-- fancy (`div`10^12), from Pico to Uni to Int
|
||||
fromPicoSeconds :: NominalDiffTime -> Int
|
||||
fromPicoSeconds = (fromEnum :: Uni -> Int) . realToFrac
|
||||
|
||||
-- * Combinators
|
||||
|
||||
list
|
||||
:: NixSerializer r e a
|
||||
-> NixSerializer r e [a]
|
||||
list s = Serializer
|
||||
{ getS = do
|
||||
count <- getS int
|
||||
Control.Monad.replicateM count (getS s)
|
||||
, putS = \xs -> do
|
||||
putS int (length xs)
|
||||
mapM_ (putS s) xs
|
||||
}
|
||||
|
||||
set
|
||||
:: Ord a
|
||||
=> NixSerializer r e a
|
||||
-> NixSerializer r e (Set a)
|
||||
set =
|
||||
mapIsoSerializer
|
||||
Data.Set.fromList
|
||||
Data.Set.toList
|
||||
. list
|
||||
|
||||
hashSet
|
||||
:: ( Eq a
|
||||
, Hashable a
|
||||
)
|
||||
=> NixSerializer r e a
|
||||
-> NixSerializer r e (HashSet a)
|
||||
hashSet =
|
||||
mapIsoSerializer
|
||||
Data.HashSet.fromList
|
||||
Data.HashSet.toList
|
||||
. list
|
||||
|
||||
mapS
|
||||
:: Ord k
|
||||
=> NixSerializer r e k
|
||||
-> NixSerializer r e v
|
||||
-> NixSerializer r e (Map k v)
|
||||
mapS k v =
|
||||
mapIsoSerializer
|
||||
Data.Map.Strict.fromList
|
||||
Data.Map.Strict.toList
|
||||
$ list
|
||||
$ tup k v
|
||||
|
||||
vector
|
||||
:: Ord a
|
||||
=> NixSerializer r e a
|
||||
-> NixSerializer r e (Vector a)
|
||||
vector =
|
||||
mapIsoSerializer
|
||||
Data.Vector.fromList
|
||||
Data.Vector.toList
|
||||
. list
|
||||
|
||||
-- * ProtoVersion
|
||||
|
||||
-- protoVersion_major & 0xFF00
|
||||
-- protoVersion_minor & 0x00FF
|
||||
protoVersion :: NixSerializer r e ProtoVersion
|
||||
protoVersion = Serializer
|
||||
{ getS = do
|
||||
v <- getS (int @Word32)
|
||||
pure ProtoVersion
|
||||
{ protoVersion_major = fromIntegral $ Data.Bits.shiftR v 8
|
||||
, protoVersion_minor = fromIntegral $ v Data.Bits..&. 0x00FF
|
||||
}
|
||||
, putS = \p ->
|
||||
putS (int @Word32)
|
||||
$ ((Data.Bits.shiftL (fromIntegral $ protoVersion_major p :: Word32) 8)
|
||||
Data.Bits..|. fromIntegral (protoVersion_minor p))
|
||||
}
|
||||
|
||||
-- * StorePath
|
||||
|
||||
storePath :: HasStoreDir r => NixSerializer r SError StorePath
|
||||
storePath = Serializer
|
||||
{ getS = do
|
||||
sd <- Control.Monad.Reader.asks hasStoreDir
|
||||
System.Nix.StorePath.parsePath sd <$> getS byteString
|
||||
>>=
|
||||
either
|
||||
(throwError . SError_Path)
|
||||
pure
|
||||
, putS = \p -> do
|
||||
sd <- Control.Monad.Reader.asks hasStoreDir
|
||||
putS
|
||||
byteString
|
||||
$ System.Nix.StorePath.storePathToRawFilePath sd p
|
||||
}
|
||||
|
||||
storePathHashPart :: NixSerializer r SError StorePathHashPart
|
||||
storePathHashPart =
|
||||
mapIsoSerializer
|
||||
System.Nix.StorePath.unsafeMakeStorePathHashPart
|
||||
System.Nix.StorePath.unStorePathHashPart
|
||||
$ mapPrismSerializer
|
||||
(Data.Bifunctor.first (pure SError_InvalidNixBase32)
|
||||
. System.Nix.Base.decodeWith NixBase32)
|
||||
(System.Nix.Base.encodeWith NixBase32)
|
||||
text
|
||||
|
||||
storePathName :: NixSerializer r SError StorePathName
|
||||
storePathName =
|
||||
mapPrismSerializer
|
||||
(Data.Bifunctor.first SError_Path
|
||||
. System.Nix.StorePath.makeStorePathName)
|
||||
System.Nix.StorePath.unStorePathName
|
||||
text
|
||||
|
||||
pathMetadata
|
||||
:: HasStoreDir r
|
||||
=> NixSerializer r SError (Metadata StorePath)
|
||||
pathMetadata = Serializer
|
||||
{ getS = do
|
||||
deriverPath <- getS maybePath
|
||||
|
||||
digest' <- getS $ digest NixBase32
|
||||
let narHash = System.Nix.Hash.HashAlgo_SHA256 :=> digest'
|
||||
|
||||
references <- getS $ hashSet storePath
|
||||
registrationTime <- getS time
|
||||
narBytes <- (\case
|
||||
0 -> Nothing
|
||||
size -> Just size) <$> getS int
|
||||
trust <- getS storePathTrust
|
||||
|
||||
sigs <- getS $ set signature
|
||||
contentAddress <- getS maybeContentAddress
|
||||
|
||||
pure $ Metadata{..}
|
||||
|
||||
, putS = \Metadata{..} -> do
|
||||
putS maybePath deriverPath
|
||||
|
||||
let putNarHash
|
||||
:: DSum HashAlgo Digest
|
||||
-> SerialT r SError PutM ()
|
||||
putNarHash = \case
|
||||
System.Nix.Hash.HashAlgo_SHA256 :=> d
|
||||
-> putS (digest @SHA256 NixBase32) d
|
||||
_ -> throwError SError_NarHashMustBeSHA256
|
||||
|
||||
putNarHash narHash
|
||||
|
||||
putS (hashSet storePath) references
|
||||
putS time registrationTime
|
||||
putS int $ Prelude.maybe 0 id $ narBytes
|
||||
putS storePathTrust trust
|
||||
putS (set signature) sigs
|
||||
putS maybeContentAddress contentAddress
|
||||
}
|
||||
where
|
||||
maybeContentAddress
|
||||
:: NixSerializer r SError (Maybe ContentAddress)
|
||||
maybeContentAddress =
|
||||
mapPrismSerializer
|
||||
(maybe
|
||||
(pure Nothing)
|
||||
$ Data.Bifunctor.bimap
|
||||
SError_ContentAddress
|
||||
Just
|
||||
. System.Nix.ContentAddress.parseContentAddress
|
||||
)
|
||||
(fmap System.Nix.ContentAddress.buildContentAddress)
|
||||
maybeText
|
||||
|
||||
maybePath
|
||||
:: HasStoreDir r
|
||||
=> NixSerializer r SError (Maybe StorePath)
|
||||
maybePath = Serializer
|
||||
{ getS = do
|
||||
getS maybeText >>= \case
|
||||
Nothing -> pure Nothing
|
||||
Just t -> do
|
||||
sd <- Control.Monad.Reader.asks hasStoreDir
|
||||
either
|
||||
(throwError . SError_Path)
|
||||
(pure . pure)
|
||||
$ System.Nix.StorePath.parsePathFromText sd t
|
||||
|
||||
, putS = \case
|
||||
Nothing -> putS maybeText Nothing
|
||||
Just p -> do
|
||||
sd <- Control.Monad.Reader.asks hasStoreDir
|
||||
putS text $ System.Nix.StorePath.storePathToText sd p
|
||||
}
|
||||
|
||||
storePathTrust
|
||||
:: NixSerializer r SError StorePathTrust
|
||||
storePathTrust =
|
||||
mapIsoSerializer
|
||||
(\case False -> BuiltElsewhere; True -> BuiltLocally)
|
||||
(\case BuiltElsewhere -> False; BuiltLocally -> True)
|
||||
bool
|
||||
|
||||
signature
|
||||
:: NixSerializer r SError NarSignature
|
||||
signature =
|
||||
mapPrismSerializer
|
||||
(Data.Bifunctor.first SError_Signature
|
||||
. Data.Attoparsec.Text.parseOnly
|
||||
System.Nix.Signature.signatureParser)
|
||||
(System.Nix.Signature.signatureToText)
|
||||
text
|
||||
|
||||
-- * Some HashAlgo
|
||||
|
||||
someHashAlgo :: NixSerializer r SError (Some HashAlgo)
|
||||
someHashAlgo =
|
||||
mapPrismSerializer
|
||||
(Data.Bifunctor.first SError_HashAlgo
|
||||
. System.Nix.Hash.textToAlgo)
|
||||
(Data.Some.foldSome System.Nix.Hash.algoToText)
|
||||
text
|
||||
|
||||
-- * Digest
|
||||
|
||||
digest
|
||||
:: forall a r
|
||||
. HashAlgorithm a
|
||||
=> BaseEncoding
|
||||
-> NixSerializer r SError (Digest a)
|
||||
digest base =
|
||||
mapIsoSerializer
|
||||
Data.Coerce.coerce
|
||||
Data.Coerce.coerce
|
||||
$ mapPrismSerializer
|
||||
(Data.Bifunctor.first SError_Digest
|
||||
. System.Nix.Hash.decodeDigestWith @a base)
|
||||
(System.Nix.Hash.encodeDigestWith base)
|
||||
$ text
|
||||
|
||||
derivationOutput
|
||||
:: HasStoreDir r
|
||||
=> NixSerializer r SError (DerivationOutput StorePath Text)
|
||||
derivationOutput = Serializer
|
||||
{ getS = do
|
||||
path <- getS storePath
|
||||
hashAlgo <- getS text
|
||||
hash <- getS text
|
||||
pure DerivationOutput{..}
|
||||
, putS = \DerivationOutput{..} -> do
|
||||
putS storePath path
|
||||
putS text hashAlgo
|
||||
putS text hash
|
||||
}
|
||||
|
||||
-- * Derivation
|
||||
|
||||
derivation
|
||||
:: HasStoreDir r
|
||||
=> NixSerializer r SError (Derivation StorePath Text)
|
||||
derivation = Serializer
|
||||
{ getS = do
|
||||
outputs <- getS (mapS text derivationOutput)
|
||||
-- Our type is Derivation, but in Nix
|
||||
-- the type sent over the wire is BasicDerivation
|
||||
-- which omits inputDrvs
|
||||
inputDrvs <- pure mempty
|
||||
inputSrcs <- getS (set storePath)
|
||||
|
||||
platform <- getS text
|
||||
builder <- getS text
|
||||
args <- getS (vector text)
|
||||
env <- getS (mapS text text)
|
||||
pure Derivation{..}
|
||||
, putS = \Derivation{..} -> do
|
||||
putS (mapS text derivationOutput) outputs
|
||||
putS (set storePath) inputSrcs
|
||||
putS text platform
|
||||
putS text builder
|
||||
putS (vector text) args
|
||||
putS (mapS text text) env
|
||||
}
|
||||
|
||||
-- * DerivedPath
|
||||
|
||||
derivedPathNew
|
||||
:: HasStoreDir r
|
||||
=> NixSerializer r SError DerivedPath
|
||||
derivedPathNew = Serializer
|
||||
{ getS = do
|
||||
root <- Control.Monad.Reader.asks hasStoreDir
|
||||
p <- getS text
|
||||
case System.Nix.DerivedPath.parseDerivedPath root p of
|
||||
Left err -> throwError $ SError_DerivedPath err
|
||||
Right x -> pure x
|
||||
, putS = \d -> do
|
||||
root <- Control.Monad.Reader.asks hasStoreDir
|
||||
putS text (System.Nix.DerivedPath.derivedPathToText root d)
|
||||
}
|
||||
|
||||
derivedPath
|
||||
:: ( HasProtoVersion r
|
||||
, HasStoreDir r
|
||||
)
|
||||
=> NixSerializer r SError DerivedPath
|
||||
derivedPath = Serializer
|
||||
{ getS = do
|
||||
pv <- Control.Monad.Reader.asks hasProtoVersion
|
||||
if pv < ProtoVersion 1 30
|
||||
then
|
||||
throwError
|
||||
$ SError_NotYetImplemented
|
||||
"DerivedPath"
|
||||
(ForPV_Older pv)
|
||||
else getS derivedPathNew
|
||||
, putS = \d -> do
|
||||
pv <- Control.Monad.Reader.asks hasProtoVersion
|
||||
if pv < ProtoVersion 1 30
|
||||
then
|
||||
throwError
|
||||
$ SError_NotYetImplemented
|
||||
"DerivedPath"
|
||||
(ForPV_Older pv)
|
||||
else putS derivedPathNew d
|
||||
}
|
||||
|
||||
-- * Build
|
||||
|
||||
buildMode :: NixSerializer r SError BuildMode
|
||||
buildMode = enum
|
||||
|
||||
buildResult :: NixSerializer r SError BuildResult
|
||||
buildResult = Serializer
|
||||
{ getS = do
|
||||
status <- getS enum
|
||||
errorMessage <- getS maybeText
|
||||
timesBuilt <- getS int
|
||||
isNonDeterministic <- getS bool
|
||||
startTime <- getS time
|
||||
stopTime <- getS time
|
||||
pure $ BuildResult{..}
|
||||
|
||||
, putS = \BuildResult{..} -> do
|
||||
putS enum status
|
||||
putS maybeText errorMessage
|
||||
putS int timesBuilt
|
||||
putS bool isNonDeterministic
|
||||
putS time startTime
|
||||
putS time stopTime
|
||||
}
|
||||
|
||||
-- * Logger
|
||||
|
||||
data LoggerSError
|
||||
= LoggerSError_Prim SError
|
||||
| LoggerSError_InvalidOpCode Int
|
||||
| LoggerSError_TooOldForErrorInfo
|
||||
| LoggerSError_TooNewForBasicError
|
||||
| LoggerSError_UnknownLogFieldType Word8
|
||||
deriving (Eq, Ord, Generic, Show)
|
||||
|
||||
mapPrimE
|
||||
:: Functor m
|
||||
=> SerialT r SError m a
|
||||
-> SerialT r LoggerSError m a
|
||||
mapPrimE = mapError LoggerSError_Prim
|
||||
|
||||
maybeActivity :: NixSerializer r LoggerSError (Maybe Activity)
|
||||
maybeActivity = Serializer
|
||||
{ getS = getS (int @Int) >>= \case
|
||||
0 -> pure Nothing
|
||||
x -> mapPrimE $ toEnumCheckBoundsM (x - 100) >>= pure . Just
|
||||
, putS = \case
|
||||
Nothing -> putS (int @Int) 0
|
||||
Just act -> putS activity act
|
||||
}
|
||||
where
|
||||
activity :: NixSerializer r LoggerSError Activity
|
||||
activity = Serializer
|
||||
{ getS = mapPrimE $ getS int >>= toEnumCheckBoundsM . (+(-100))
|
||||
, putS = putS int . (+100) . fromEnum
|
||||
}
|
||||
|
||||
activityID :: NixSerializer r LoggerSError ActivityID
|
||||
activityID = mapIsoSerializer ActivityID unActivityID int
|
||||
|
||||
activityResult :: NixSerializer r LoggerSError ActivityResult
|
||||
activityResult = Serializer
|
||||
{ getS = mapPrimE $ getS int >>= toEnumCheckBoundsM . (+(-100))
|
||||
, putS = putS int . (+100) . fromEnum
|
||||
}
|
||||
|
||||
field :: NixSerializer r LoggerSError Field
|
||||
field = Serializer
|
||||
{ getS = getS (int @Word8) >>= \case
|
||||
0 -> Field_LogInt <$> getS int
|
||||
1 -> Field_LogStr <$> mapPrimE (getS text)
|
||||
x -> throwError $ LoggerSError_UnknownLogFieldType x
|
||||
, putS = \case
|
||||
Field_LogInt x -> putS int (0 :: Word8) >> putS int x
|
||||
Field_LogStr x -> putS int (1 :: Word8) >> mapPrimE (putS text x)
|
||||
}
|
||||
|
||||
trace :: NixSerializer r LoggerSError Trace
|
||||
trace = Serializer
|
||||
{ getS = do
|
||||
tracePosition <- (\case 0 -> Nothing; x -> Just x) <$> getS (int @Int)
|
||||
traceHint <- mapPrimE $ getS text
|
||||
pure Trace{..}
|
||||
, putS = \Trace{..} -> do
|
||||
maybe (putS (int @Int) 0) (putS int) $ tracePosition
|
||||
mapPrimE $ putS text traceHint
|
||||
}
|
||||
|
||||
basicError :: NixSerializer r LoggerSError BasicError
|
||||
basicError = Serializer
|
||||
{ getS = do
|
||||
basicErrorMessage <- mapPrimE $ getS text
|
||||
basicErrorExitStatus <- getS int
|
||||
pure BasicError{..}
|
||||
|
||||
, putS = \BasicError{..} -> do
|
||||
mapPrimE $ putS text basicErrorMessage
|
||||
putS int basicErrorExitStatus
|
||||
}
|
||||
|
||||
errorInfo :: NixSerializer r LoggerSError ErrorInfo
|
||||
errorInfo = Serializer
|
||||
{ getS = do
|
||||
etyp <- mapPrimE $ getS text
|
||||
Control.Monad.unless (etyp == Data.Text.pack "Error")
|
||||
$ fail
|
||||
$ "get ErrorInfo: received unknown error type" ++ show etyp
|
||||
errorInfoLevel <- getS verbosity
|
||||
_name <- mapPrimE $ getS text -- removed error name
|
||||
errorInfoMessage <- mapPrimE $ getS text
|
||||
errorInfoPosition <- (\case 0 -> Nothing; x -> Just x) <$> getS int
|
||||
errorInfoTraces <- getS (list trace)
|
||||
pure ErrorInfo{..}
|
||||
|
||||
, putS = \ErrorInfo{..} -> do
|
||||
mapPrimE $ do
|
||||
putS text $ Data.Text.pack "Error"
|
||||
putS verbosity errorInfoLevel
|
||||
mapPrimE $ do
|
||||
putS text $ Data.Text.pack "Error" -- removed error name
|
||||
putS text errorInfoMessage
|
||||
maybe (putS (int @Word8) 0) (putS int) errorInfoPosition
|
||||
putS (list trace) errorInfoTraces
|
||||
}
|
||||
|
||||
loggerOpCode :: NixSerializer r LoggerSError LoggerOpCode
|
||||
loggerOpCode = Serializer
|
||||
{ getS = do
|
||||
c <- getS int
|
||||
either
|
||||
(pure $ throwError (LoggerSError_InvalidOpCode c))
|
||||
pure
|
||||
$ intToLoggerOpCode c
|
||||
, putS = putS int . loggerOpCodeToInt
|
||||
}
|
||||
|
||||
logger
|
||||
:: HasProtoVersion r
|
||||
=> NixSerializer r LoggerSError Logger
|
||||
logger = Serializer
|
||||
{ getS = getS loggerOpCode >>= \case
|
||||
LoggerOpCode_Next ->
|
||||
mapPrimE $
|
||||
Logger_Next <$> getS text
|
||||
|
||||
LoggerOpCode_Read ->
|
||||
Logger_Read <$> getS int
|
||||
|
||||
LoggerOpCode_Write ->
|
||||
mapPrimE $
|
||||
Logger_Write <$> getS byteString
|
||||
|
||||
LoggerOpCode_Last ->
|
||||
pure Logger_Last
|
||||
|
||||
LoggerOpCode_Error -> do
|
||||
pv <- Control.Monad.Reader.asks hasProtoVersion
|
||||
Logger_Error <$>
|
||||
if protoVersion_minor pv >= 26
|
||||
then Right <$> getS errorInfo
|
||||
else Left <$> getS basicError
|
||||
|
||||
LoggerOpCode_StartActivity -> do
|
||||
startActivityID <- getS activityID
|
||||
startActivityVerbosity <- getS verbosity
|
||||
startActivityType <- getS maybeActivity
|
||||
startActivityString <- mapPrimE $ getS byteString
|
||||
startActivityFields <- getS (list field)
|
||||
startActivityParentID <- getS activityID
|
||||
pure Logger_StartActivity{..}
|
||||
|
||||
LoggerOpCode_StopActivity -> do
|
||||
stopActivityID <- getS activityID
|
||||
pure Logger_StopActivity{..}
|
||||
|
||||
LoggerOpCode_Result -> do
|
||||
resultActivityID <- getS activityID
|
||||
resultType <- getS activityResult
|
||||
resultFields <- getS (list field)
|
||||
pure Logger_Result {..}
|
||||
|
||||
, putS = \case
|
||||
Logger_Next s -> do
|
||||
putS loggerOpCode LoggerOpCode_Next
|
||||
mapError LoggerSError_Prim $
|
||||
putS text s
|
||||
|
||||
Logger_Read i -> do
|
||||
putS loggerOpCode LoggerOpCode_Read
|
||||
putS int i
|
||||
|
||||
Logger_Write s -> do
|
||||
putS loggerOpCode LoggerOpCode_Write
|
||||
mapPrimE $ putS byteString s
|
||||
|
||||
Logger_Last ->
|
||||
putS loggerOpCode LoggerOpCode_Last
|
||||
|
||||
Logger_Error basicOrInfo -> do
|
||||
putS loggerOpCode LoggerOpCode_Error
|
||||
|
||||
minor <- protoVersion_minor <$> Control.Monad.Reader.asks hasProtoVersion
|
||||
|
||||
case basicOrInfo of
|
||||
Left _ | minor >= 26 -> throwError $ LoggerSError_TooNewForBasicError
|
||||
Left e | otherwise -> putS basicError e
|
||||
Right _ | minor < 26 -> throwError $ LoggerSError_TooOldForErrorInfo
|
||||
Right e -> putS errorInfo e
|
||||
|
||||
Logger_StartActivity{..} -> do
|
||||
putS loggerOpCode LoggerOpCode_StartActivity
|
||||
putS activityID startActivityID
|
||||
putS verbosity startActivityVerbosity
|
||||
putS maybeActivity startActivityType
|
||||
mapPrimE $
|
||||
putS byteString startActivityString
|
||||
putS (list field) startActivityFields
|
||||
putS activityID startActivityParentID
|
||||
|
||||
Logger_StopActivity{..} -> do
|
||||
putS loggerOpCode LoggerOpCode_StopActivity
|
||||
putS activityID stopActivityID
|
||||
|
||||
Logger_Result{..} -> do
|
||||
putS loggerOpCode LoggerOpCode_Result
|
||||
putS activityID resultActivityID
|
||||
putS activityResult resultType
|
||||
putS (list field) resultFields
|
||||
}
|
||||
|
||||
verbosity :: NixSerializer r LoggerSError Verbosity
|
||||
verbosity = Serializer
|
||||
{ getS = mapPrimE $ getS enum
|
||||
, putS = mapPrimE . putS enum
|
||||
}
|
97
hnix-store-remote/src/System/Nix/Store/Remote/Socket.hs
Normal file
97
hnix-store-remote/src/System/Nix/Store/Remote/Socket.hs
Normal file
@ -0,0 +1,97 @@
|
||||
module System.Nix.Store.Remote.Socket where
|
||||
|
||||
import Control.Monad.Except (throwError)
|
||||
import Control.Monad.Reader (asks)
|
||||
import Control.Monad.IO.Class (MonadIO(..))
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashSet (HashSet)
|
||||
import Data.Serialize.Get (Get, Result(..))
|
||||
import Data.Serialize.Put
|
||||
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 qualified Data.Serialize.Get
|
||||
|
||||
genericIncremental
|
||||
:: MonadIO m
|
||||
=> m ByteString
|
||||
-> Get a
|
||||
-> m a
|
||||
genericIncremental getsome parser = do
|
||||
getsome >>= go . decoder
|
||||
where
|
||||
decoder = Data.Serialize.Get.runGetPartial parser
|
||||
go (Done x _leftover) = pure x
|
||||
go (Partial k) = do
|
||||
chunk <- getsome
|
||||
go (k chunk)
|
||||
go (Fail msg _leftover) = error msg
|
||||
|
||||
getSocketIncremental :: Get a -> MonadStore a
|
||||
getSocketIncremental = genericIncremental sockGet8
|
||||
|
||||
sockGet8 :: MonadStore ByteString
|
||||
sockGet8 = do
|
||||
soc <- asks hasStoreSocket
|
||||
liftIO $ recv soc 8
|
||||
|
||||
sockPut :: Put -> MonadStore ()
|
||||
sockPut p = do
|
||||
soc <- asks hasStoreSocket
|
||||
liftIO $ sendAll soc $ runPut p
|
||||
|
||||
sockPutS
|
||||
:: Show e
|
||||
=> NixSerializer ProtoVersion e a
|
||||
-> a
|
||||
-> MonadStore ()
|
||||
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
|
||||
|
||||
sockGet :: Get a -> MonadStore a
|
||||
sockGet = getSocketIncremental
|
||||
|
||||
sockGetInt :: Integral a => MonadStore a
|
||||
sockGetInt = getSocketIncremental getInt
|
||||
|
||||
sockGetBool :: MonadStore Bool
|
||||
sockGetBool = (== (1 :: Int)) <$> sockGetInt
|
||||
|
||||
sockGetStr :: MonadStore ByteString
|
||||
sockGetStr = getSocketIncremental getByteString
|
||||
|
||||
sockGetStrings :: MonadStore [ByteString]
|
||||
sockGetStrings = getSocketIncremental getByteStrings
|
||||
|
||||
sockGetPath :: MonadStore StorePath
|
||||
sockGetPath = do
|
||||
sd <- getStoreDir
|
||||
pth <- getSocketIncremental (getPath sd)
|
||||
either
|
||||
(throwError . show)
|
||||
pure
|
||||
pth
|
||||
|
||||
sockGetPathMay :: MonadStore (Maybe StorePath)
|
||||
sockGetPathMay = do
|
||||
sd <- getStoreDir
|
||||
pth <- getSocketIncremental (getPath sd)
|
||||
pure $
|
||||
either
|
||||
(const Nothing)
|
||||
Just
|
||||
pth
|
||||
|
||||
sockGetPaths :: MonadStore (HashSet StorePath)
|
||||
sockGetPaths = do
|
||||
sd <- getStoreDir
|
||||
getSocketIncremental (getPathsOrFail sd)
|
@ -1,119 +1,21 @@
|
||||
module System.Nix.Store.Remote.Types
|
||||
( MonadStore
|
||||
, StoreConfig(..)
|
||||
, CheckFlag
|
||||
, doCheck
|
||||
, dontCheck
|
||||
, unCheckFlag
|
||||
, SubstituteFlag
|
||||
, doSubstitute
|
||||
, dontSubstitute
|
||||
, unSubstituteFlag
|
||||
, Logger(..)
|
||||
, Field(..)
|
||||
, mapStoreDir
|
||||
, getStoreDir
|
||||
, getStoreDir'
|
||||
, getLog
|
||||
, flushLog
|
||||
, gotError
|
||||
, getError
|
||||
, setData
|
||||
, clearData
|
||||
( module System.Nix.Store.Remote.Types.Activity
|
||||
, module System.Nix.Store.Remote.Types.GC
|
||||
, module System.Nix.Store.Remote.Types.CheckMode
|
||||
, module System.Nix.Store.Remote.Types.Logger
|
||||
, module System.Nix.Store.Remote.Types.ProtoVersion
|
||||
, module System.Nix.Store.Remote.Types.StoreConfig
|
||||
, module System.Nix.Store.Remote.Types.SubstituteMode
|
||||
, module System.Nix.Store.Remote.Types.Verbosity
|
||||
, module System.Nix.Store.Remote.Types.WorkerOp
|
||||
) 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 Data.ByteString (ByteString)
|
||||
import Network.Socket (Socket)
|
||||
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
|
||||
import Control.Monad.Trans.State.Strict (mapStateT)
|
||||
import Control.Monad.Trans.Except (mapExceptT)
|
||||
import Control.Monad.Trans.Reader (withReaderT)
|
||||
|
||||
import System.Nix.StorePath (HasStoreDir(..), StoreDir)
|
||||
|
||||
|
||||
data StoreConfig = StoreConfig
|
||||
{ storeDir :: StoreDir
|
||||
, storeSocket :: Socket
|
||||
}
|
||||
|
||||
-- | Check flag, used by @verifyStore@
|
||||
newtype CheckFlag = CheckFlag { unCheckFlag :: Bool }
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
doCheck, dontCheck :: CheckFlag
|
||||
doCheck = CheckFlag True
|
||||
dontCheck = CheckFlag False
|
||||
|
||||
-- | Substitute flag, used by @queryValidPaths@
|
||||
newtype SubstituteFlag = SubstituteFlag { unSubstituteFlag :: Bool }
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
doSubstitute, dontSubstitute :: SubstituteFlag
|
||||
doSubstitute = SubstituteFlag True
|
||||
dontSubstitute = SubstituteFlag False
|
||||
|
||||
-- | Ask for a @StoreDir@
|
||||
getStoreDir' :: (HasStoreDir r, MonadReader r m) => m StoreDir
|
||||
getStoreDir' = asks hasStoreDir
|
||||
|
||||
type MonadStore a
|
||||
= ExceptT
|
||||
String
|
||||
(StateT (Maybe BSL.ByteString, [Logger]) (ReaderT StoreConfig IO))
|
||||
a
|
||||
|
||||
-- | For lying about the store dir in tests
|
||||
mapStoreDir :: (StoreDir -> StoreDir) -> (MonadStore a -> MonadStore a)
|
||||
mapStoreDir f = mapExceptT . mapStateT . withReaderT $ \c@StoreConfig { storeDir = sd } -> c { storeDir = f sd }
|
||||
|
||||
type ActivityID = Int
|
||||
type ActivityParentID = Int
|
||||
type ActivityType = Int
|
||||
type Verbosity = Int
|
||||
type ResultType = Int
|
||||
|
||||
data Field = LogStr ByteString | LogInt Int
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
data Logger =
|
||||
Next ByteString
|
||||
| Read Int -- data needed from source
|
||||
| Write ByteString -- data for sink
|
||||
| Last
|
||||
| Error Int ByteString
|
||||
| StartActivity ActivityID Verbosity ActivityType ByteString [Field] ActivityParentID
|
||||
| StopActivity ActivityID
|
||||
| Result ActivityID ResultType [Field]
|
||||
deriving (Eq, Ord, Show)
|
||||
|
||||
isError :: Logger -> Bool
|
||||
isError (Error _ _) = True
|
||||
isError _ = False
|
||||
|
||||
gotError :: MonadStore Bool
|
||||
gotError = gets (any isError . snd)
|
||||
|
||||
getError :: MonadStore [Logger]
|
||||
getError = gets (filter isError . snd)
|
||||
|
||||
getLog :: MonadStore [Logger]
|
||||
getLog = gets snd
|
||||
|
||||
flushLog :: MonadStore ()
|
||||
flushLog = modify (\(a, _b) -> (a, []))
|
||||
|
||||
setData :: BSL.ByteString -> MonadStore ()
|
||||
setData x = modify (\(_, b) -> (Just x, b))
|
||||
|
||||
clearData :: MonadStore ()
|
||||
clearData = modify (\(_, b) -> (Nothing, b))
|
||||
|
||||
getStoreDir :: MonadStore StoreDir
|
||||
getStoreDir = asks storeDir
|
||||
import System.Nix.Store.Remote.Types.Activity
|
||||
import System.Nix.Store.Remote.Types.GC
|
||||
import System.Nix.Store.Remote.Types.CheckMode
|
||||
import System.Nix.Store.Remote.Types.Logger
|
||||
import System.Nix.Store.Remote.Types.ProtoVersion
|
||||
import System.Nix.Store.Remote.Types.StoreConfig
|
||||
import System.Nix.Store.Remote.Types.SubstituteMode
|
||||
import System.Nix.Store.Remote.Types.Verbosity
|
||||
import System.Nix.Store.Remote.Types.WorkerOp
|
||||
|
@ -0,0 +1,55 @@
|
||||
module System.Nix.Store.Remote.Types.Activity
|
||||
( Activity(..)
|
||||
, ActivityID(..)
|
||||
, ActivityResult(..)
|
||||
) where
|
||||
|
||||
import GHC.Generics
|
||||
|
||||
-- | Type of the activity
|
||||
--
|
||||
-- We don't have Activity_Unknown here
|
||||
-- as we can do @Maybe Activity@ and @Nothing@
|
||||
-- corresponding to Unknown (which has 0 value)
|
||||
--
|
||||
-- Rest of the values are offset by @(+100)@
|
||||
-- on the wire, i.e.:
|
||||
--
|
||||
-- * @Activity_CopyPath = 100@
|
||||
-- * @Activity_BuildWaiting = 111@
|
||||
data Activity
|
||||
= Activity_CopyPath
|
||||
| Activity_FileTransfer
|
||||
| Activity_Realise
|
||||
| Activity_CopyPaths
|
||||
| Activity_Builds
|
||||
| Activity_Build
|
||||
| Activity_OptimiseStore
|
||||
| Activity_VerifyPaths
|
||||
| Activity_Substitute
|
||||
| Activity_QueryPathInfo
|
||||
| Activity_PostBuildHook
|
||||
| Activity_BuildWaiting
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
||||
|
||||
-- | Numeric ID of the activity
|
||||
newtype ActivityID = ActivityID { unActivityID :: Int }
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
-- | Result of some activity
|
||||
--
|
||||
-- The values are offset by @(+100)@
|
||||
-- on the wire, i.e.:
|
||||
--
|
||||
-- * @ActivityResult_FileLinked = 100@
|
||||
-- * @ActivityResult_PostBuildLogLine = 107@
|
||||
data ActivityResult
|
||||
= ActivityResult_FileLinked
|
||||
| ActivityResult_BuildLogLine
|
||||
| ActivityResult_UnstrustedPath
|
||||
| ActivityResult_CorruptedPath
|
||||
| ActivityResult_SetPhase
|
||||
| ActivityResult_Progress
|
||||
| ActivityResult_SetExpected
|
||||
| ActivityResult_PostBuildLogLine
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
@ -0,0 +1,11 @@
|
||||
module System.Nix.Store.Remote.Types.CheckMode
|
||||
( CheckMode(..)
|
||||
) where
|
||||
|
||||
import GHC.Generics
|
||||
|
||||
-- | Check mode, used by @verifyStore@
|
||||
data CheckMode
|
||||
= CheckMode_DoCheck
|
||||
| CheckMode_DontCheck
|
||||
deriving (Bounded, Eq, Generic, Enum, Ord, Show)
|
47
hnix-store-remote/src/System/Nix/Store/Remote/Types/GC.hs
Normal file
47
hnix-store-remote/src/System/Nix/Store/Remote/Types/GC.hs
Normal file
@ -0,0 +1,47 @@
|
||||
{-|
|
||||
Description : Garbage collection actions / options
|
||||
Maintainer : srk <srk@48.io>
|
||||
|-}
|
||||
module System.Nix.Store.Remote.Types.GC (
|
||||
GCAction(..)
|
||||
, GCOptions(..)
|
||||
, GCResult(..)
|
||||
) where
|
||||
|
||||
import Data.HashSet (HashSet)
|
||||
import Data.Word (Word64)
|
||||
import GHC.Generics (Generic)
|
||||
import System.Nix.StorePath (StorePath)
|
||||
|
||||
-- | Garbage collection action
|
||||
data GCAction
|
||||
= GCAction_ReturnLive -- ^ Return the set of paths reachable from roots (closure)
|
||||
| GCAction_ReturnDead -- ^ Return unreachable paths
|
||||
| GCAction_DeleteDead -- ^ Delete unreachable paths
|
||||
| GCAction_DeleteSpecific -- ^ Delete specified paths
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
||||
|
||||
-- | Garbage collector operation options
|
||||
data GCOptions = GCOptions
|
||||
{ -- | Operation
|
||||
gcOptions_operation :: GCAction
|
||||
-- | If set, then reachability from the roots is ignored (unused)
|
||||
, gcOptions_ignoreLiveness :: Bool
|
||||
-- | Paths to delete for @GCAction_DeleteSpecific@
|
||||
, gcOptions_pathsToDelete :: HashSet StorePath
|
||||
-- | Stop after `gcOptions_maxFreed` bytes have been freed
|
||||
, gcOptions_maxFreed :: Integer
|
||||
} deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
-- | Result of the garbage collection operation
|
||||
data GCResult = GCResult
|
||||
{ -- | Depending on the action, the GC roots,
|
||||
-- or the paths that would be or have been deleted
|
||||
gcResult_deletedPaths :: HashSet StorePath
|
||||
-- | The number of bytes that would be or was freed for
|
||||
--
|
||||
-- - @GCAction_ReturnDead@
|
||||
-- - @GCAction_DeleteDead@
|
||||
-- - @GCAction_DeleteSpecific@
|
||||
, gcResult_bytesFreed :: Word64
|
||||
} deriving (Eq, Generic, Ord, Show)
|
107
hnix-store-remote/src/System/Nix/Store/Remote/Types/Logger.hs
Normal file
107
hnix-store-remote/src/System/Nix/Store/Remote/Types/Logger.hs
Normal file
@ -0,0 +1,107 @@
|
||||
module System.Nix.Store.Remote.Types.Logger
|
||||
( Field(..)
|
||||
, Trace(..)
|
||||
, BasicError(..)
|
||||
, ErrorInfo(..)
|
||||
, Logger(..)
|
||||
, LoggerOpCode(..)
|
||||
, loggerOpCodeToInt
|
||||
, intToLoggerOpCode
|
||||
, isError
|
||||
) where
|
||||
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.Text (Text)
|
||||
import GHC.Generics
|
||||
import System.Nix.Store.Remote.Types.Activity (Activity, ActivityID, ActivityResult)
|
||||
import System.Nix.Store.Remote.Types.Verbosity (Verbosity)
|
||||
|
||||
data Field
|
||||
= Field_LogStr Text
|
||||
| Field_LogInt Int
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
-- | Error trace
|
||||
data Trace = Trace
|
||||
{ tracePosition :: Maybe Int -- Error position, Nix always writes 0 here
|
||||
, traceHint :: Text
|
||||
}
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
data BasicError = BasicError
|
||||
{ basicErrorExitStatus :: Int
|
||||
, basicErrorMessage :: Text
|
||||
}
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
-- | Extended error info
|
||||
-- available for protoVersion_minor >= 26
|
||||
data ErrorInfo = ErrorInfo
|
||||
{ errorInfoLevel :: Verbosity
|
||||
, errorInfoMessage :: Text
|
||||
, errorInfoPosition :: Maybe Int -- Error position, Nix always writes 0 here
|
||||
, errorInfoTraces :: [Trace]
|
||||
}
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
data LoggerOpCode
|
||||
= LoggerOpCode_Next
|
||||
| LoggerOpCode_Read
|
||||
| LoggerOpCode_Write
|
||||
| LoggerOpCode_Last
|
||||
| LoggerOpCode_Error
|
||||
| LoggerOpCode_StartActivity
|
||||
| LoggerOpCode_StopActivity
|
||||
| LoggerOpCode_Result
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
loggerOpCodeToInt :: LoggerOpCode -> Int
|
||||
loggerOpCodeToInt = \case
|
||||
LoggerOpCode_Next -> 0x6f6c6d67
|
||||
LoggerOpCode_Read -> 0x64617461
|
||||
LoggerOpCode_Write -> 0x64617416
|
||||
LoggerOpCode_Last -> 0x616c7473
|
||||
LoggerOpCode_Error -> 0x63787470
|
||||
LoggerOpCode_StartActivity -> 0x53545254
|
||||
LoggerOpCode_StopActivity -> 0x53544f50
|
||||
LoggerOpCode_Result -> 0x52534c54
|
||||
|
||||
intToLoggerOpCode :: Int -> Either String LoggerOpCode
|
||||
intToLoggerOpCode = \case
|
||||
0x6f6c6d67 -> Right LoggerOpCode_Next
|
||||
0x64617461 -> Right LoggerOpCode_Read
|
||||
0x64617416 -> Right LoggerOpCode_Write
|
||||
0x616c7473 -> Right LoggerOpCode_Last
|
||||
0x63787470 -> Right LoggerOpCode_Error
|
||||
0x53545254 -> Right LoggerOpCode_StartActivity
|
||||
0x53544f50 -> Right LoggerOpCode_StopActivity
|
||||
0x52534c54 -> Right LoggerOpCode_Result
|
||||
x -> Left $ "Invalid LoggerOpCode: " ++ show x
|
||||
|
||||
data Logger
|
||||
= Logger_Next Text
|
||||
| Logger_Read Int -- data needed from source
|
||||
| Logger_Write ByteString -- data for sink
|
||||
| Logger_Last
|
||||
| Logger_Error (Either BasicError ErrorInfo)
|
||||
| Logger_StartActivity
|
||||
{ startActivityID :: ActivityID
|
||||
, startActivityVerbosity :: Verbosity
|
||||
, startActivityType :: Maybe Activity
|
||||
, startActivityString :: ByteString
|
||||
, startActivityFields :: [Field]
|
||||
, startActivityParentID :: ActivityID
|
||||
}
|
||||
| Logger_StopActivity
|
||||
{ stopActivityID :: ActivityID
|
||||
}
|
||||
| Logger_Result
|
||||
{ resultActivityID :: ActivityID
|
||||
, resultType :: ActivityResult
|
||||
, resultFields :: [Field]
|
||||
}
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
isError :: Logger -> Bool
|
||||
isError Logger_Error {} = True
|
||||
isError _ = False
|
@ -0,0 +1,19 @@
|
||||
module System.Nix.Store.Remote.Types.ProtoVersion
|
||||
( ProtoVersion(..)
|
||||
, HasProtoVersion(..)
|
||||
) where
|
||||
|
||||
import Data.Word (Word8, Word16)
|
||||
import GHC.Generics
|
||||
|
||||
data ProtoVersion = ProtoVersion
|
||||
{ protoVersion_major :: Word16
|
||||
, protoVersion_minor :: Word8
|
||||
}
|
||||
deriving (Eq, Generic, Ord, Show)
|
||||
|
||||
class HasProtoVersion r where
|
||||
hasProtoVersion :: r -> ProtoVersion
|
||||
|
||||
instance HasProtoVersion ProtoVersion where
|
||||
hasProtoVersion = id
|
@ -0,0 +1,45 @@
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
module System.Nix.Store.Remote.Types.StoreConfig
|
||||
( PreStoreConfig(..)
|
||||
, StoreConfig(..)
|
||||
, HasStoreSocket(..)
|
||||
) where
|
||||
|
||||
import Network.Socket (Socket)
|
||||
import System.Nix.StorePath (HasStoreDir(..), StoreDir)
|
||||
import System.Nix.Store.Remote.Types.ProtoVersion (HasProtoVersion(..), ProtoVersion)
|
||||
|
||||
data PreStoreConfig = PreStoreConfig
|
||||
{ preStoreConfig_dir :: StoreDir
|
||||
, preStoreConfig_socket :: Socket
|
||||
}
|
||||
|
||||
instance HasStoreDir PreStoreConfig where
|
||||
hasStoreDir = preStoreConfig_dir
|
||||
|
||||
class HasStoreSocket r where
|
||||
hasStoreSocket :: r -> Socket
|
||||
|
||||
instance HasStoreSocket Socket where
|
||||
hasStoreSocket = id
|
||||
|
||||
instance HasStoreSocket PreStoreConfig where
|
||||
hasStoreSocket = preStoreConfig_socket
|
||||
|
||||
data StoreConfig = StoreConfig
|
||||
{ storeConfig_dir :: StoreDir
|
||||
, storeConfig_protoVersion :: ProtoVersion
|
||||
, storeConfig_socket :: Socket
|
||||
}
|
||||
|
||||
instance HasStoreDir StoreDir where
|
||||
hasStoreDir = id
|
||||
|
||||
instance HasStoreDir StoreConfig where
|
||||
hasStoreDir = storeConfig_dir
|
||||
|
||||
instance HasProtoVersion StoreConfig where
|
||||
hasProtoVersion = storeConfig_protoVersion
|
||||
|
||||
instance HasStoreSocket StoreConfig where
|
||||
hasStoreSocket = storeConfig_socket
|
@ -0,0 +1,11 @@
|
||||
module System.Nix.Store.Remote.Types.SubstituteMode
|
||||
( SubstituteMode(..)
|
||||
) where
|
||||
|
||||
import GHC.Generics
|
||||
|
||||
-- | Path substitution mode, used by @queryValidPaths@
|
||||
data SubstituteMode
|
||||
= SubstituteMode_DoSubstitute
|
||||
| SubstituteMode_DontSubstitute
|
||||
deriving (Bounded, Eq, Generic, Enum, Ord, Show)
|
@ -0,0 +1,17 @@
|
||||
module System.Nix.Store.Remote.Types.Verbosity
|
||||
( Verbosity(..)
|
||||
) where
|
||||
|
||||
import GHC.Generics
|
||||
|
||||
-- | Logging verbosity
|
||||
data Verbosity
|
||||
= Verbosity_Error
|
||||
| Verbosity_Warn
|
||||
| Verbosity_Notice
|
||||
| Verbosity_Info
|
||||
| Verbosity_Talkative
|
||||
| Verbosity_Chatty
|
||||
| Verbosity_Debug
|
||||
| Verbosity_Vomit
|
||||
deriving (Bounded, Eq, Enum, Generic, Ord, Show)
|
@ -0,0 +1,57 @@
|
||||
module System.Nix.Store.Remote.Types.WorkerOp
|
||||
( WorkerOp(..)
|
||||
) where
|
||||
|
||||
-- | Worker opcode
|
||||
--
|
||||
-- This type has gaps filled in so that the GHC builtin
|
||||
-- Enum instance lands on the right values.
|
||||
data WorkerOp
|
||||
= Reserved_0__ -- 0
|
||||
| IsValidPath -- 1
|
||||
| Reserved_2__ -- 2
|
||||
| HasSubstitutes -- 3
|
||||
| QueryPathHash -- 4 // obsolete
|
||||
| QueryReferences -- 5 // obsolete
|
||||
| QueryReferrers -- 6
|
||||
| AddToStore -- 7
|
||||
| AddTextToStore -- 8 // obsolete since 1.25, Nix 3.0. Use wopAddToStore
|
||||
| BuildPaths -- 9
|
||||
| EnsurePath -- 10 0xa
|
||||
| AddTempRoot -- 11 0xb
|
||||
| AddIndirectRoot -- 12 0xc
|
||||
| SyncWithGC -- 13 0xd
|
||||
| FindRoots -- 14 0xe
|
||||
| Reserved_15__ -- 15 0xf
|
||||
| ExportPath -- 16 0x10 // obsolete
|
||||
| Reserved_17__ -- 17 0x11
|
||||
| QueryDeriver -- 18 0x12 // obsolete
|
||||
| SetOptions -- 19 0x13
|
||||
| CollectGarbage -- 20 0x14
|
||||
| QuerySubstitutablePathInfo -- 21 0x15
|
||||
| QueryDerivationOutputs -- 22 0x16 // obsolete
|
||||
| QueryAllValidPaths -- 23 0x17
|
||||
| QueryFailedPaths -- 24 0x18
|
||||
| ClearFailedPaths -- 25 0x19
|
||||
| QueryPathInfo -- 26 0x1a
|
||||
| ImportPaths -- 27 0x1b // obsolete
|
||||
| QueryDerivationOutputNames -- 28 0x1c // obsolete
|
||||
| QueryPathFromHashPart -- 29 0x1d
|
||||
| QuerySubstitutablePathInfos -- 30 0x1e
|
||||
| QueryValidPaths -- 31 0x1f
|
||||
| QuerySubstitutablePaths -- 32 0x20
|
||||
| QueryValidDerivers -- 33 0x21
|
||||
| OptimiseStore -- 34 0x22
|
||||
| VerifyStore -- 35 0x23
|
||||
| BuildDerivation -- 36 0x24
|
||||
| AddSignatures -- 37 0x25
|
||||
| NarFromPath -- 38 0x26
|
||||
| AddToStoreNar -- 39 0x27
|
||||
| QueryMissing -- 40 0x28
|
||||
| QueryDerivationOutputMap -- 41 0x29
|
||||
| RegisterDrvOutput -- 42 0x2a
|
||||
| QueryRealisation -- 43 0x2b
|
||||
| AddMultipleToStore -- 44 0x2c
|
||||
| AddBuildLog -- 45 0x2d
|
||||
| BuildPathsWithResults -- 46 0x2e
|
||||
deriving (Bounded, Eq, Enum, Ord, Show, Read)
|
@ -1,172 +0,0 @@
|
||||
module System.Nix.Store.Remote.Util where
|
||||
|
||||
import Control.Monad.Except (throwError)
|
||||
import Control.Monad.Reader (asks)
|
||||
import Control.Monad.IO.Class (MonadIO(..))
|
||||
import Data.ByteString (ByteString)
|
||||
import Data.HashSet (HashSet)
|
||||
import Data.Text (Text)
|
||||
import Data.Either (rights)
|
||||
|
||||
import Data.Binary.Get
|
||||
import Data.Binary.Put
|
||||
import qualified Data.Text.Encoding as T
|
||||
import qualified Data.Text.Lazy as TL
|
||||
import qualified Data.Text.Lazy.Encoding as TL
|
||||
import Data.Time
|
||||
import Data.Time.Clock.POSIX
|
||||
import qualified Data.ByteString.Char8 as BSC
|
||||
import qualified Data.ByteString.Lazy as BSL
|
||||
|
||||
import Network.Socket.ByteString ( recv
|
||||
, sendAll
|
||||
)
|
||||
|
||||
import System.Nix.Build
|
||||
import System.Nix.Derivation
|
||||
import System.Nix.StorePath (StoreDir, StorePath, InvalidPathError, parsePath, storePathToRawFilePath)
|
||||
import System.Nix.Store.Remote.Binary
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
import qualified Data.HashSet
|
||||
import qualified Data.Map
|
||||
|
||||
genericIncremental :: (MonadIO m) => m (Maybe ByteString) -> Get a -> m a
|
||||
genericIncremental getsome parser = go decoder
|
||||
where
|
||||
decoder = runGetIncremental parser
|
||||
go (Done _leftover _consumed x ) = pure x
|
||||
go (Partial k ) = do
|
||||
chunk <- getsome
|
||||
go (k chunk)
|
||||
go (Fail _leftover _consumed msg) = error msg
|
||||
|
||||
getSocketIncremental :: Get a -> MonadStore a
|
||||
getSocketIncremental = genericIncremental sockGet8
|
||||
where
|
||||
sockGet8 :: MonadStore (Maybe BSC.ByteString)
|
||||
sockGet8 = do
|
||||
soc <- asks storeSocket
|
||||
liftIO $ Just <$> recv soc 8
|
||||
|
||||
sockPut :: Put -> MonadStore ()
|
||||
sockPut p = do
|
||||
soc <- asks storeSocket
|
||||
liftIO $ sendAll soc $ BSL.toStrict $ runPut p
|
||||
|
||||
sockGet :: Get a -> MonadStore a
|
||||
sockGet = getSocketIncremental
|
||||
|
||||
sockGetInt :: Integral a => MonadStore a
|
||||
sockGetInt = getSocketIncremental getInt
|
||||
|
||||
sockGetBool :: MonadStore Bool
|
||||
sockGetBool = (== (1 :: Int)) <$> sockGetInt
|
||||
|
||||
sockGetStr :: MonadStore ByteString
|
||||
sockGetStr = getSocketIncremental getByteStringLen
|
||||
|
||||
sockGetStrings :: MonadStore [ByteString]
|
||||
sockGetStrings = getSocketIncremental getByteStrings
|
||||
|
||||
sockGetPath :: MonadStore StorePath
|
||||
sockGetPath = do
|
||||
sd <- getStoreDir
|
||||
pth <- getSocketIncremental (getPath sd)
|
||||
either
|
||||
(throwError . show)
|
||||
pure
|
||||
pth
|
||||
|
||||
sockGetPathMay :: MonadStore (Maybe StorePath)
|
||||
sockGetPathMay = do
|
||||
sd <- getStoreDir
|
||||
pth <- getSocketIncremental (getPath sd)
|
||||
pure $
|
||||
either
|
||||
(const Nothing)
|
||||
Just
|
||||
pth
|
||||
|
||||
sockGetPaths :: MonadStore (HashSet StorePath)
|
||||
sockGetPaths = do
|
||||
sd <- getStoreDir
|
||||
getSocketIncremental (getPaths sd)
|
||||
|
||||
bsToText :: ByteString -> Text
|
||||
bsToText = T.decodeUtf8
|
||||
|
||||
textToBS :: Text -> ByteString
|
||||
textToBS = T.encodeUtf8
|
||||
|
||||
bslToText :: BSL.ByteString -> Text
|
||||
bslToText = TL.toStrict . TL.decodeUtf8
|
||||
|
||||
textToBSL :: Text -> BSL.ByteString
|
||||
textToBSL = TL.encodeUtf8 . TL.fromStrict
|
||||
|
||||
putText :: Text -> Put
|
||||
putText = putByteStringLen . textToBSL
|
||||
|
||||
putTexts :: [Text] -> Put
|
||||
putTexts = putByteStrings . fmap textToBSL
|
||||
|
||||
getPath :: StoreDir -> Get (Either InvalidPathError StorePath)
|
||||
getPath sd = parsePath sd <$> getByteStringLen
|
||||
|
||||
getPaths :: StoreDir -> Get (HashSet StorePath)
|
||||
getPaths sd =
|
||||
Data.HashSet.fromList . rights . fmap (parsePath sd) <$> getByteStrings
|
||||
|
||||
putPath :: StoreDir -> StorePath -> Put
|
||||
putPath storeDir = putByteStringLen . BSL.fromStrict . storePathToRawFilePath storeDir
|
||||
|
||||
putPaths :: StoreDir -> HashSet StorePath -> Put
|
||||
putPaths storeDir = putByteStrings . Data.HashSet.toList . Data.HashSet.map
|
||||
(BSL.fromStrict . storePathToRawFilePath storeDir)
|
||||
|
||||
putBool :: Bool -> Put
|
||||
putBool True = putInt (1 :: Int)
|
||||
putBool False = putInt (0 :: Int)
|
||||
|
||||
getBool :: Get Bool
|
||||
getBool = (== 1) <$> (getInt :: Get Int)
|
||||
|
||||
putEnum :: (Enum a) => a -> Put
|
||||
putEnum = putInt . fromEnum
|
||||
|
||||
getEnum :: (Enum a) => Get a
|
||||
getEnum = toEnum <$> getInt
|
||||
|
||||
putTime :: UTCTime -> Put
|
||||
putTime = (putInt :: Int -> Put) . round . utcTimeToPOSIXSeconds
|
||||
|
||||
getTime :: Get UTCTime
|
||||
getTime = posixSecondsToUTCTime <$> getEnum
|
||||
|
||||
getBuildResult :: Get BuildResult
|
||||
getBuildResult =
|
||||
BuildResult
|
||||
<$> getEnum
|
||||
<*> (Just . bsToText <$> getByteStringLen)
|
||||
<*> getInt
|
||||
<*> getBool
|
||||
<*> getTime
|
||||
<*> getTime
|
||||
|
||||
putDerivation :: StoreDir -> Derivation StorePath Text -> Put
|
||||
putDerivation storeDir Derivation{..} = do
|
||||
flip putMany (Data.Map.toList outputs)
|
||||
$ \(outputName, DerivationOutput{..}) -> do
|
||||
putText outputName
|
||||
putPath storeDir path
|
||||
putText hashAlgo
|
||||
putText hash
|
||||
|
||||
putMany (putPath storeDir) inputSrcs
|
||||
putText platform
|
||||
putText builder
|
||||
putMany putText args
|
||||
|
||||
flip putMany (Data.Map.toList env)
|
||||
$ \(a1, a2) -> putText a1 *> putText a2
|
@ -3,42 +3,36 @@
|
||||
module NixDaemon where
|
||||
|
||||
import Data.Text (Text)
|
||||
import Data.Either ( isRight
|
||||
, isLeft
|
||||
)
|
||||
import Data.Bool ( bool )
|
||||
import Control.Monad ( void )
|
||||
import Control.Monad.IO.Class ( liftIO )
|
||||
|
||||
import Data.Either (isRight, isLeft)
|
||||
import Data.Bool (bool)
|
||||
import Control.Monad (forM_, void)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import qualified System.Environment
|
||||
import Control.Exception ( bracket )
|
||||
import Control.Concurrent ( threadDelay )
|
||||
import qualified Data.ByteString.Char8 as BSC
|
||||
import Control.Exception (bracket)
|
||||
import Control.Concurrent (threadDelay)
|
||||
import qualified Data.ByteString.Char8 as BSC
|
||||
import qualified Data.Either
|
||||
import qualified Data.HashSet as HS
|
||||
import qualified Data.Map.Strict as M
|
||||
import System.Directory
|
||||
import System.IO.Temp
|
||||
import qualified System.Process as P
|
||||
import System.Posix.User as U
|
||||
import System.Linux.Namespaces as NS
|
||||
import Test.Hspec ( Spec
|
||||
, describe
|
||||
, context
|
||||
)
|
||||
import qualified Test.Hspec as Hspec
|
||||
import Test.Hspec.Expectations.Lifted
|
||||
import qualified Data.HashSet as HS
|
||||
import qualified Data.Map.Strict as M
|
||||
import qualified Data.Text
|
||||
import qualified Data.Text.Encoding
|
||||
import System.Directory
|
||||
import System.IO.Temp
|
||||
import qualified System.Process as P
|
||||
import System.Posix.User as U
|
||||
import System.Linux.Namespaces as NS
|
||||
import Test.Hspec (Spec, describe, context)
|
||||
import qualified Test.Hspec as Hspec
|
||||
import Test.Hspec.Expectations.Lifted
|
||||
import System.FilePath
|
||||
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 System.FilePath
|
||||
|
||||
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 )
|
||||
import Crypto.Hash (SHA256)
|
||||
import System.Nix.Nar (dumpPath)
|
||||
|
||||
createProcessEnv :: FilePath -> String -> [String] -> IO P.ProcessHandle
|
||||
createProcessEnv fp proc args = do
|
||||
@ -194,14 +188,23 @@ spec_protocol = Hspec.around withNixDaemon $
|
||||
|
||||
context "verifyStore" $ do
|
||||
itRights "check=False repair=False" $
|
||||
verifyStore dontCheck RepairMode_DontRepair `shouldReturn` False
|
||||
verifyStore
|
||||
CheckMode_DontCheck
|
||||
RepairMode_DontRepair
|
||||
`shouldReturn` False
|
||||
|
||||
itRights "check=True repair=False" $
|
||||
verifyStore doCheck RepairMode_DontRepair `shouldReturn` False
|
||||
verifyStore
|
||||
CheckMode_DoCheck
|
||||
RepairMode_DontRepair
|
||||
`shouldReturn` False
|
||||
|
||||
--privileged
|
||||
itRights "check=True repair=True" $
|
||||
verifyStore doCheck RepairMode_DoRepair `shouldReturn` False
|
||||
verifyStore
|
||||
CheckMode_DoCheck
|
||||
RepairMode_DoRepair
|
||||
`shouldReturn` False
|
||||
|
||||
context "addTextToStore" $
|
||||
itRights "adds text to store" $ withPath pure
|
||||
@ -234,15 +237,15 @@ spec_protocol = Hspec.around withNixDaemon $
|
||||
context "buildPaths" $ do
|
||||
itRights "build Normal" $ withPath $ \path -> do
|
||||
let pathSet = HS.fromList [path]
|
||||
buildPaths pathSet Normal
|
||||
buildPaths pathSet BuildMode_Normal
|
||||
|
||||
itRights "build Check" $ withPath $ \path -> do
|
||||
let pathSet = HS.fromList [path]
|
||||
buildPaths pathSet Check
|
||||
buildPaths pathSet BuildMode_Check
|
||||
|
||||
itLefts "build Repair" $ withPath $ \path -> do
|
||||
let pathSet = HS.fromList [path]
|
||||
buildPaths pathSet Repair
|
||||
buildPaths pathSet BuildMode_Repair
|
||||
|
||||
context "roots" $ context "findRoots" $ do
|
||||
itRights "empty roots" (findRoots `shouldReturn` M.empty)
|
||||
@ -272,3 +275,17 @@ spec_protocol = Hspec.around withNixDaemon $
|
||||
path <- dummy
|
||||
liftIO $ print path
|
||||
isValidPathUncached path `shouldReturn` True
|
||||
|
||||
context "deleteSpecific" $
|
||||
itRights "delete a path from the store" $ withPath $ \path -> do
|
||||
-- clear temp gc roots so the delete works. restarting the nix daemon should also do this...
|
||||
storeDir <- getStoreDir
|
||||
let tempRootsDir = Data.Text.unpack $ mconcat [ Data.Text.Encoding.decodeUtf8 (unStoreDir storeDir), "/../var/nix/temproots/" ]
|
||||
tempRootList <- liftIO $ listDirectory tempRootsDir
|
||||
liftIO $ forM_ tempRootList $ \entry -> do
|
||||
removeFile $ mconcat [ tempRootsDir, "/", entry ]
|
||||
|
||||
GCResult{..} <- deleteSpecific (HS.fromList [path])
|
||||
gcResult_deletedPaths `shouldBe` HS.fromList [path]
|
||||
gcResult_bytesFreed `shouldBe` 4
|
||||
|
||||
|
43
hnix-store-remote/tests/Data/SerializerSpec.hs
Normal file
43
hnix-store-remote/tests/Data/SerializerSpec.hs
Normal file
@ -0,0 +1,43 @@
|
||||
module Data.SerializerSpec (spec) where
|
||||
|
||||
import Data.Some
|
||||
import Data.Serializer
|
||||
import Data.Serializer.Example
|
||||
|
||||
import Test.Hspec (Spec, describe, it, shouldBe)
|
||||
import Test.Hspec.QuickCheck (prop)
|
||||
|
||||
spec :: Spec
|
||||
spec = describe "Serializer" $ do
|
||||
prop "Roundtrips GADT protocol" $ \someCmd ->
|
||||
(runG cmdS
|
||||
<$> (runP cmdS someCmd))
|
||||
`shouldBe`
|
||||
((pure $ pure someCmd) ::
|
||||
Either MyPutError
|
||||
(Either (GetSerializerError MyGetError)
|
||||
(Some Cmd)))
|
||||
|
||||
it "Handles putS error" $
|
||||
runP cmdSPutError (Some (Cmd_Bool True))
|
||||
`shouldBe`
|
||||
Left MyPutError_NoLongerSupported
|
||||
|
||||
it "Handles getS error" $
|
||||
runG cmdSGetError (runPutSimple cmdS (Some (Cmd_Bool True)))
|
||||
`shouldBe`
|
||||
Left (SerializerError_Get MyGetError_Example)
|
||||
|
||||
it "Handles getS fail" $
|
||||
runG cmdSGetFail (runPutSimple cmdS (Some (Cmd_Bool True)))
|
||||
`shouldBe`
|
||||
Left (SerializerError_GetFail @MyGetError "Failed reading: no parse\nEmpty call stack\n")
|
||||
|
||||
prop "Roundtrips elaborate example" $ \someCmd readerBool ->
|
||||
(runGRest cmdSRest readerBool 0
|
||||
<$> (runPRest cmdSRest readerBool 0 someCmd))
|
||||
`shouldBe`
|
||||
((pure $ pure $ someCmd) ::
|
||||
Either MyPutError
|
||||
(Either (GetSerializerError MyGetError)
|
||||
(Some Cmd)))
|
164
hnix-store-remote/tests/NixSerializerSpec.hs
Normal file
164
hnix-store-remote/tests/NixSerializerSpec.hs
Normal file
@ -0,0 +1,164 @@
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module NixSerializerSpec (spec) where
|
||||
|
||||
import Crypto.Hash (MD5, SHA1, SHA256, SHA512)
|
||||
import Data.Dependent.Sum (DSum((:=>)))
|
||||
import Data.Fixed (Uni)
|
||||
import Data.Time (NominalDiffTime)
|
||||
import Test.Hspec (Expectation, Spec, describe, parallel, shouldBe)
|
||||
import Test.Hspec.QuickCheck (prop)
|
||||
import Test.QuickCheck (Gen, arbitrary, forAll, suchThat)
|
||||
import Test.QuickCheck.Instances ()
|
||||
|
||||
import qualified Data.Time.Clock.POSIX
|
||||
import qualified Data.Serializer
|
||||
import qualified System.Nix.Build
|
||||
import qualified System.Nix.Hash
|
||||
|
||||
import System.Nix.Arbitrary ()
|
||||
import System.Nix.Build (BuildResult)
|
||||
import System.Nix.Derivation (Derivation(inputDrvs))
|
||||
import System.Nix.StorePath (StoreDir)
|
||||
import System.Nix.StorePath.Metadata (Metadata(..))
|
||||
import System.Nix.Store.Remote.Arbitrary ()
|
||||
import System.Nix.Store.Remote.Serializer
|
||||
import System.Nix.Store.Remote.Types (ErrorInfo(..), Logger(..), ProtoVersion(..), Trace(..))
|
||||
|
||||
-- | Test for roundtrip using @NixSerializer@
|
||||
roundtripSReader
|
||||
:: forall r e a
|
||||
. ( Eq a
|
||||
, Show a
|
||||
, Eq e
|
||||
, Show e
|
||||
)
|
||||
=> NixSerializer r e a
|
||||
-> r
|
||||
-> a
|
||||
-> Expectation
|
||||
roundtripSReader serializer readerVal a =
|
||||
(runG serializer readerVal
|
||||
<$> runP serializer readerVal a)
|
||||
`shouldBe` (pure $ pure a)
|
||||
|
||||
roundtripS
|
||||
:: ( Eq a
|
||||
, Show a
|
||||
, Eq e
|
||||
, Show e
|
||||
)
|
||||
=> NixSerializer () e a
|
||||
-> a
|
||||
-> Expectation
|
||||
roundtripS serializer = roundtripSReader serializer ()
|
||||
|
||||
spec :: Spec
|
||||
spec = parallel $ do
|
||||
describe "Prim" $ do
|
||||
prop "Int" $ roundtripS @Int @() int
|
||||
prop "Bool" $ roundtripS bool
|
||||
prop "ByteString" $ roundtripS byteString
|
||||
prop "Text" $ roundtripS text
|
||||
prop "Maybe Text"
|
||||
$ forAll (arbitrary `suchThat` (/= Just ""))
|
||||
$ roundtripS maybeText
|
||||
prop "UTCTime" $ do
|
||||
let
|
||||
-- scale to seconds and back
|
||||
toSeconds :: Int -> NominalDiffTime
|
||||
toSeconds n = realToFrac (toEnum n :: Uni)
|
||||
fromSeconds :: NominalDiffTime -> Int
|
||||
fromSeconds = (fromEnum :: Uni -> Int) . realToFrac
|
||||
|
||||
roundtripS @Int @() $
|
||||
Data.Serializer.mapIsoSerializer
|
||||
(fromSeconds . Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds)
|
||||
(Data.Time.Clock.POSIX.posixSecondsToUTCTime . toSeconds)
|
||||
time
|
||||
|
||||
describe "Combinators" $ do
|
||||
prop "list" $ roundtripS @[Int] @() (list int)
|
||||
prop "set" $ roundtripS (set byteString)
|
||||
prop "hashSet" $ roundtripS (hashSet byteString)
|
||||
prop "mapS" $ roundtripS (mapS (int @Int) byteString)
|
||||
|
||||
describe "Complex" $ do
|
||||
prop "BuildResult"
|
||||
$ forAll (arbitrary `suchThat` ((/= Just "") . System.Nix.Build.errorMessage))
|
||||
$ \br ->
|
||||
roundtripS @BuildResult buildResult
|
||||
-- fix time to 0 as we test UTCTime above
|
||||
$ br { System.Nix.Build.startTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||
, System.Nix.Build.stopTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||
}
|
||||
|
||||
prop "StorePath" $
|
||||
roundtripSReader @StoreDir storePath
|
||||
|
||||
prop "StorePathHashPart" $
|
||||
roundtripS storePathHashPart
|
||||
|
||||
prop "StorePathName" $
|
||||
roundtripS storePathName
|
||||
|
||||
let narHashIsSHA256 Metadata{..} =
|
||||
case narHash of
|
||||
(System.Nix.Hash.HashAlgo_SHA256 :=> _) -> True
|
||||
_ -> False
|
||||
|
||||
prop "Metadata (StorePath)"
|
||||
$ \sd -> forAll (arbitrary `suchThat` (\m -> narHashIsSHA256 m && narBytes m /= Just 0))
|
||||
$ roundtripSReader @StoreDir pathMetadata sd
|
||||
. (\m -> m
|
||||
{ registrationTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||
})
|
||||
|
||||
prop "Some HashAlgo" $
|
||||
roundtripS someHashAlgo
|
||||
|
||||
describe "Digest" $ do
|
||||
prop "MD5" $ roundtripS . digest @MD5
|
||||
prop "SHA1" $ roundtripS . digest @SHA1
|
||||
prop "SHA256" $ roundtripS . digest @SHA256
|
||||
prop "SHA512" $ roundtripS . digest @SHA512
|
||||
|
||||
prop "Derivation" $ \sd ->
|
||||
roundtripSReader @StoreDir derivation sd
|
||||
. (\drv -> drv { inputDrvs = mempty })
|
||||
|
||||
prop "ProtoVersion" $ roundtripS @ProtoVersion @() protoVersion
|
||||
|
||||
describe "Logger" $ do
|
||||
prop "ActivityID" $ roundtripS activityID
|
||||
prop "Maybe Activity" $ roundtripS maybeActivity
|
||||
prop "ActivityResult" $ roundtripS activityResult
|
||||
prop "Field" $ roundtripS field
|
||||
prop "Trace"
|
||||
$ forAll (arbitrary `suchThat` ((/= Just 0) . tracePosition))
|
||||
$ roundtripS trace
|
||||
prop "BasicError" $ roundtripS basicError
|
||||
prop "ErrorInfo"
|
||||
$ forAll (arbitrary
|
||||
`suchThat`
|
||||
(\ErrorInfo{..}
|
||||
-> errorInfoPosition /= Just 0
|
||||
&& all ((/= Just 0) . tracePosition) errorInfoTraces
|
||||
)
|
||||
)
|
||||
$ roundtripS errorInfo
|
||||
prop "LoggerOpCode" $ roundtripS loggerOpCode
|
||||
prop "Verbosity" $ roundtripS verbosity
|
||||
prop "Logger"
|
||||
$ forAll (arbitrary :: Gen ProtoVersion)
|
||||
$ \pv ->
|
||||
forAll (arbitrary `suchThat` errorInfoIf (protoVersion_minor pv >= 26))
|
||||
$ roundtripSReader logger pv
|
||||
where
|
||||
errorInfoIf True (Logger_Error (Right x)) = noJust0s x
|
||||
errorInfoIf False (Logger_Error (Left _)) = True
|
||||
errorInfoIf _ (Logger_Error _) = False
|
||||
errorInfoIf _ _ = True
|
||||
noJust0s ErrorInfo{..} =
|
||||
errorInfoPosition /= Just 0
|
||||
&& all ((/= Just 0) . tracePosition) errorInfoTraces
|
@ -1,4 +1,3 @@
|
||||
{-# LANGUAGE NumericUnderscores #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
|
||||
module SerializeSpec (spec) where
|
||||
@ -9,7 +8,7 @@ import Data.Serialize.Get (Get, runGet)
|
||||
import Data.Serialize.Put (Putter, runPut)
|
||||
import Data.Text (Text)
|
||||
import Data.Time (NominalDiffTime)
|
||||
import Test.Hspec (Expectation, Spec, describe, it, shouldBe)
|
||||
import Test.Hspec (Expectation, Spec, describe, it, parallel, shouldBe)
|
||||
import Test.Hspec.QuickCheck (prop)
|
||||
import Test.Hspec.Nix (roundtrips)
|
||||
import Test.QuickCheck (arbitrary, forAll, suchThat)
|
||||
@ -21,10 +20,12 @@ import qualified Data.Time.Clock.POSIX
|
||||
import qualified System.Nix.Build
|
||||
|
||||
import System.Nix.Arbitrary ()
|
||||
import System.Nix.Build (BuildMode, BuildStatus)
|
||||
import System.Nix.Derivation (Derivation(..))
|
||||
import System.Nix.Build (BuildMode(..), BuildStatus(..))
|
||||
import System.Nix.Derivation (Derivation(inputDrvs))
|
||||
import System.Nix.Store.Remote.Arbitrary ()
|
||||
import System.Nix.Store.Remote.Serialize (getDerivation, putDerivation)
|
||||
import System.Nix.Store.Remote.Serialize.Prim
|
||||
import System.Nix.Store.Remote.Types
|
||||
|
||||
-- | Test for roundtrip using @Putter@ and @Get@ functions
|
||||
roundtrips2
|
||||
@ -54,10 +55,11 @@ roundtripS =
|
||||
(runGet get)
|
||||
|
||||
spec :: Spec
|
||||
spec = do
|
||||
spec = parallel $ do
|
||||
describe "Prim" $ do
|
||||
prop "Int" $ roundtrips2 putInt getInt
|
||||
prop "Int" $ roundtrips2 putInt (getInt @Int)
|
||||
prop "Bool" $ roundtrips2 putBool getBool
|
||||
prop "ByteString" $ roundtrips2 putByteString getByteString
|
||||
|
||||
prop "UTCTime" $ do
|
||||
let
|
||||
@ -72,8 +74,7 @@ spec = do
|
||||
(fromSeconds . Data.Time.Clock.POSIX.utcTimeToPOSIXSeconds <$> getTime)
|
||||
|
||||
describe "Combinators" $ do
|
||||
prop "Many" $ roundtrips2 (putMany putInt) (getMany getInt)
|
||||
prop "ByteString" $ roundtrips2 putByteString getByteString
|
||||
prop "Many" $ roundtrips2 (putMany putInt) (getMany (getInt @Int))
|
||||
prop "[ByteString]" $ roundtrips2 putByteStrings getByteStrings
|
||||
prop "Text" $ roundtrips2 putText getText
|
||||
prop "[Text]" $ roundtrips2 putTexts getTexts
|
||||
@ -101,6 +102,8 @@ spec = do
|
||||
, System.Nix.Build.stopTime = Data.Time.Clock.POSIX.posixSecondsToUTCTime 0
|
||||
}
|
||||
|
||||
prop "ProtoVersion" $ roundtripS @ProtoVersion
|
||||
|
||||
prop "Derivation StorePath Text" $ \sd ->
|
||||
roundtrips2
|
||||
(putDerivation sd)
|
||||
@ -108,25 +111,98 @@ spec = do
|
||||
-- inputDrvs is not used in remote protocol serialization
|
||||
. (\drv -> drv { inputDrvs = mempty })
|
||||
|
||||
let it' name constr value = it name $ runPut (put constr) `shouldBe` runPut (putInt value)
|
||||
describe "Build enum order matches Nix" $ do
|
||||
it' "Normal" System.Nix.Build.Normal 0
|
||||
it' "Repair" System.Nix.Build.Repair 1
|
||||
it' "Check" System.Nix.Build.Check 2
|
||||
describe "Logger" $ do
|
||||
prop "Activity" $ roundtripS @Activity
|
||||
prop "ActivityID" $ roundtripS @ActivityID
|
||||
prop "Activity" $ roundtripS @Activity
|
||||
prop "Field" $ roundtripS @Field
|
||||
prop "Trace"
|
||||
$ forAll (arbitrary `suchThat` ((/= Just 0) . tracePosition))
|
||||
$ roundtripS @Trace
|
||||
prop "BasicError" $ roundtripS @BasicError
|
||||
prop "ErrorInfo"
|
||||
$ forAll (arbitrary
|
||||
`suchThat`
|
||||
(\ErrorInfo{..}
|
||||
-> errorInfoPosition /= Just 0
|
||||
&& all ((/= Just 0) . tracePosition) errorInfoTraces
|
||||
)
|
||||
)
|
||||
$ roundtripS @ErrorInfo
|
||||
prop "LoggerOpCode" $ roundtripS @LoggerOpCode
|
||||
prop "Verbosity" $ roundtripS @Verbosity
|
||||
|
||||
describe "BuildStatus enum order matches Nix" $ do
|
||||
it' "Built" System.Nix.Build.Built 0
|
||||
it' "Substituted" System.Nix.Build.Substituted 1
|
||||
it' "AlreadyValid" System.Nix.Build.AlreadyValid 2
|
||||
it' "PermanentFailure" System.Nix.Build.PermanentFailure 3
|
||||
it' "InputRejected" System.Nix.Build.InputRejected 4
|
||||
it' "OutputRejected" System.Nix.Build.OutputRejected 5
|
||||
it' "TransientFailure" System.Nix.Build.TransientFailure 6
|
||||
it' "CachedFailure" System.Nix.Build.CachedFailure 7
|
||||
it' "TimedOut" System.Nix.Build.TimedOut 8
|
||||
it' "MiscFailure" System.Nix.Build.MiscFailure 9
|
||||
it' "DependencyFailed" System.Nix.Build.DependencyFailed 10
|
||||
it' "LogLimitExceeded" System.Nix.Build.LogLimitExceeded 11
|
||||
it' "NotDeterministic" System.Nix.Build.NotDeterministic 12
|
||||
it' "ResolvesToAlreadyValid" System.Nix.Build.ResolvesToAlreadyValid 13
|
||||
it' "NoSubstituters" System.Nix.Build.NoSubstituters 14
|
||||
describe "Enums" $ do
|
||||
let it' name constr value = it name $ runPut (put constr) `shouldBe` runPut (putInt @Int value)
|
||||
describe "BuildMode enum order matches Nix" $ do
|
||||
it' "Normal" BuildMode_Normal 0
|
||||
it' "Repair" BuildMode_Repair 1
|
||||
it' "Check" BuildMode_Check 2
|
||||
|
||||
describe "BuildStatus enum order matches Nix" $ do
|
||||
it' "Built" BuildStatus_Built 0
|
||||
it' "Substituted" BuildStatus_Substituted 1
|
||||
it' "AlreadyValid" BuildStatus_AlreadyValid 2
|
||||
it' "PermanentFailure" BuildStatus_PermanentFailure 3
|
||||
it' "InputRejected" BuildStatus_InputRejected 4
|
||||
it' "OutputRejected" BuildStatus_OutputRejected 5
|
||||
it' "TransientFailure" BuildStatus_TransientFailure 6
|
||||
it' "CachedFailure" BuildStatus_CachedFailure 7
|
||||
it' "TimedOut" BuildStatus_TimedOut 8
|
||||
it' "MiscFailure" BuildStatus_MiscFailure 9
|
||||
it' "DependencyFailed" BuildStatus_DependencyFailed 10
|
||||
it' "LogLimitExceeded" BuildStatus_LogLimitExceeded 11
|
||||
it' "NotDeterministic" BuildStatus_NotDeterministic 12
|
||||
it' "ResolvesToAlreadyValid" BuildStatus_ResolvesToAlreadyValid 13
|
||||
it' "NoSubstituters" BuildStatus_NoSubstituters 14
|
||||
|
||||
describe "GCAction enum order matches Nix" $ do
|
||||
it' "ReturnLive" GCAction_ReturnLive 0
|
||||
it' "ReturnDead" GCAction_ReturnDead 1
|
||||
it' "DeleteDead" GCAction_DeleteDead 2
|
||||
it' "DeleteSpecific" GCAction_DeleteSpecific 3
|
||||
|
||||
describe "Logger" $ do
|
||||
describe "Activity enum order matches Nix" $ do
|
||||
it' "CopyPath" Activity_CopyPath 100
|
||||
it' "FileTransfer" Activity_FileTransfer 101
|
||||
it' "Realise" Activity_Realise 102
|
||||
it' "CopyPaths" Activity_CopyPaths 103
|
||||
it' "Builds" Activity_Builds 104
|
||||
it' "Build" Activity_Build 105
|
||||
it' "OptimiseStore" Activity_OptimiseStore 106
|
||||
it' "VerifyPaths" Activity_VerifyPaths 107
|
||||
it' "Substitute" Activity_Substitute 108
|
||||
it' "QueryPathInfo" Activity_QueryPathInfo 109
|
||||
it' "PostBuildHook" Activity_PostBuildHook 110
|
||||
it' "BuildWaiting" Activity_BuildWaiting 111
|
||||
|
||||
describe "ActivityResult enum order matches Nix" $ do
|
||||
it' "FileLinked" ActivityResult_FileLinked 100
|
||||
it' "BuildLogLine" ActivityResult_BuildLogLine 101
|
||||
it' "UnstrustedPath" ActivityResult_UnstrustedPath 102
|
||||
it' "CorruptedPath" ActivityResult_CorruptedPath 103
|
||||
it' "SetPhase" ActivityResult_SetPhase 104
|
||||
it' "Progress" ActivityResult_Progress 105
|
||||
it' "SetExpected" ActivityResult_SetExpected 106
|
||||
it' "PostBuildLogLine" ActivityResult_PostBuildLogLine 107
|
||||
|
||||
describe "LoggerOpCode matches Nix" $ do
|
||||
it' "Next" LoggerOpCode_Next 0x6f6c6d67
|
||||
it' "Read" LoggerOpCode_Read 0x64617461
|
||||
it' "Write" LoggerOpCode_Write 0x64617416
|
||||
it' "Last" LoggerOpCode_Last 0x616c7473
|
||||
it' "Error" LoggerOpCode_Error 0x63787470
|
||||
it' "StartActivity" LoggerOpCode_StartActivity 0x53545254
|
||||
it' "StopActivity" LoggerOpCode_StopActivity 0x53544f50
|
||||
it' "Result" LoggerOpCode_Result 0x52534c54
|
||||
|
||||
describe "Verbosity enum order matches Nix" $ do
|
||||
it' "Error" Verbosity_Error 0
|
||||
it' "Warn" Verbosity_Warn 1
|
||||
it' "Notice" Verbosity_Notice 2
|
||||
it' "Info" Verbosity_Info 3
|
||||
it' "Talkative" Verbosity_Talkative 4
|
||||
it' "Chatty" Verbosity_Chatty 5
|
||||
it' "Debug" Verbosity_Debug 6
|
||||
it' "Vomit" Verbosity_Vomit 7
|
||||
|
@ -36,6 +36,7 @@ library
|
||||
import: commons
|
||||
exposed-modules:
|
||||
System.Nix.Arbitrary
|
||||
, System.Nix.Arbitrary.Base
|
||||
, System.Nix.Arbitrary.Build
|
||||
, System.Nix.Arbitrary.ContentAddress
|
||||
, System.Nix.Arbitrary.Derivation
|
||||
@ -44,6 +45,7 @@ library
|
||||
, System.Nix.Arbitrary.Signature
|
||||
, System.Nix.Arbitrary.Store.Types
|
||||
, System.Nix.Arbitrary.StorePath
|
||||
, System.Nix.Arbitrary.StorePath.Metadata
|
||||
, Test.Hspec.Nix
|
||||
build-depends:
|
||||
base >=4.12 && <5
|
||||
|
@ -1,5 +1,6 @@
|
||||
module System.Nix.Arbitrary where
|
||||
|
||||
import System.Nix.Arbitrary.Base ()
|
||||
import System.Nix.Arbitrary.Build ()
|
||||
import System.Nix.Arbitrary.ContentAddress ()
|
||||
import System.Nix.Arbitrary.Derivation ()
|
||||
@ -8,3 +9,4 @@ import System.Nix.Arbitrary.Hash ()
|
||||
import System.Nix.Arbitrary.Signature ()
|
||||
import System.Nix.Arbitrary.Store.Types ()
|
||||
import System.Nix.Arbitrary.StorePath ()
|
||||
import System.Nix.Arbitrary.StorePath.Metadata ()
|
||||
|
13
hnix-store-tests/src/System/Nix/Arbitrary/Base.hs
Normal file
13
hnix-store-tests/src/System/Nix/Arbitrary/Base.hs
Normal file
@ -0,0 +1,13 @@
|
||||
-- due to recent generic-arbitrary
|
||||
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
module System.Nix.Arbitrary.Base where
|
||||
|
||||
import System.Nix.Base
|
||||
|
||||
import Test.QuickCheck (Arbitrary(..))
|
||||
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||
import Test.QuickCheck.Instances ()
|
||||
|
||||
deriving via GenericArbitrary BaseEncoding
|
||||
instance Arbitrary BaseEncoding
|
@ -6,6 +6,7 @@ module System.Nix.Arbitrary.Hash where
|
||||
import Data.ByteString (ByteString)
|
||||
import Crypto.Hash (Digest, MD5(..), SHA1(..), SHA256(..), SHA512(..))
|
||||
import Data.Dependent.Sum (DSum((:=>)))
|
||||
import Data.Some (Some(Some))
|
||||
import System.Nix.Hash (HashAlgo(..))
|
||||
|
||||
import Test.QuickCheck (Arbitrary(arbitrary), oneof)
|
||||
@ -36,3 +37,14 @@ instance Arbitrary (DSum HashAlgo Digest) where
|
||||
, (HashAlgo_SHA256 :=>) <$> arbitrary
|
||||
, (HashAlgo_SHA512 :=>) <$> arbitrary
|
||||
]
|
||||
|
||||
instance Arbitrary (Some HashAlgo) where
|
||||
arbitrary =
|
||||
oneof
|
||||
$ pure
|
||||
<$> [
|
||||
Some HashAlgo_MD5
|
||||
, Some HashAlgo_SHA1
|
||||
, Some HashAlgo_SHA256
|
||||
, Some HashAlgo_SHA512
|
||||
]
|
||||
|
@ -1,13 +1,11 @@
|
||||
-- due to recent generic-arbitrary
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
module System.Nix.Arbitrary.StorePath where
|
||||
|
||||
#if !MIN_VERSION_base(4,18,0)
|
||||
import Control.Applicative (liftA2)
|
||||
#endif
|
||||
import Crypto.Hash (SHA256)
|
||||
import Crypto.Hash (MD5, SHA1, SHA256, SHA512)
|
||||
import qualified Data.ByteString.Char8
|
||||
import qualified Data.Text
|
||||
import System.Nix.StorePath (StoreDir(..)
|
||||
@ -17,7 +15,7 @@ import System.Nix.StorePath (StoreDir(..)
|
||||
)
|
||||
import qualified System.Nix.StorePath
|
||||
|
||||
import Test.QuickCheck (Arbitrary(arbitrary), listOf, elements)
|
||||
import Test.QuickCheck (Arbitrary(arbitrary), elements, listOf, oneof)
|
||||
|
||||
instance Arbitrary StoreDir where
|
||||
arbitrary =
|
||||
@ -43,6 +41,13 @@ instance Arbitrary StorePathName where
|
||||
|
||||
instance Arbitrary StorePathHashPart where
|
||||
arbitrary =
|
||||
-- TODO(srk): other hashes
|
||||
System.Nix.StorePath.mkStorePathHashPart @SHA256
|
||||
. Data.ByteString.Char8.pack <$> arbitrary
|
||||
oneof
|
||||
[ System.Nix.StorePath.mkStorePathHashPart @MD5
|
||||
. Data.ByteString.Char8.pack <$> arbitrary
|
||||
, System.Nix.StorePath.mkStorePathHashPart @SHA1
|
||||
. Data.ByteString.Char8.pack <$> arbitrary
|
||||
, System.Nix.StorePath.mkStorePathHashPart @SHA256
|
||||
. Data.ByteString.Char8.pack <$> arbitrary
|
||||
, System.Nix.StorePath.mkStorePathHashPart @SHA512
|
||||
. Data.ByteString.Char8.pack <$> arbitrary
|
||||
]
|
||||
|
@ -0,0 +1,23 @@
|
||||
-- due to recent generic-arbitrary
|
||||
{-# LANGUAGE CPP #-}
|
||||
{-# OPTIONS_GHC -fconstraint-solver-iterations=0 #-}
|
||||
{-# OPTIONS_GHC -Wno-orphans #-}
|
||||
module System.Nix.Arbitrary.StorePath.Metadata where
|
||||
|
||||
import System.Nix.Arbitrary.ContentAddress ()
|
||||
import System.Nix.Arbitrary.Hash ()
|
||||
import System.Nix.Arbitrary.Signature ()
|
||||
import System.Nix.Arbitrary.StorePath ()
|
||||
import System.Nix.StorePath (StorePath)
|
||||
import System.Nix.StorePath.Metadata (Metadata, StorePathTrust)
|
||||
|
||||
import Test.QuickCheck (Arbitrary(..))
|
||||
import Test.QuickCheck.Arbitrary.Generic (GenericArbitrary(..))
|
||||
import Test.QuickCheck.Instances ()
|
||||
|
||||
deriving via GenericArbitrary StorePathTrust
|
||||
instance Arbitrary StorePathTrust
|
||||
|
||||
deriving via GenericArbitrary (Metadata StorePath)
|
||||
instance Arbitrary (Metadata StorePath)
|
||||
|
Loading…
Reference in New Issue
Block a user