tmp-proc-0.7.2.1: Run 'tmp' processes in integration tests
Copyright(c) 2020-2021 Tim Emiola
LicenseBSD3
MaintainerTim Emiola <adetokunbo@users.noreply.github.com>
Safe HaskellSafe-Inferred
LanguageHaskell2010

System.TmpProc.Docker

Description

Provides the core datatypes and combinators used to launch temporary (tmp) processes (procs) using docker.

tmp-proc helps launch services used by integration tests on docker. It aims to simplify writing those kind of tests, by providing combinators that

  • launch services on docker
  • provide references to the launched services
  • clean up these services after the test completes

It achieves this through its typeclasses, datatypes and combinators:

  • A datatype implements the Proc typeclass to specify a docker image that provides a service
  • startup starts a Proc; using a docker run command generated from metadata encoded in the Proc's implementation
  • Additionally, a Proc datatype may implement the ToRunCmd typeclass to customize the arguments of the docker run command that launches the service.
  • Invoking startup constructs a ProcHandle on successful launch of a service specifed by a Proc

    • It provides combinators for accessing the service, and for eventually shutting it down
  • Multiple services are launched by specifying distinct Procs in an HList

    • startupAll constructs an HList of the corresponding ProcHandle on successful launch of the specified services.

Support for additional features that might prove useful in integration tests is available by implementing additional supporting typeclasses:

Use Connectable when there is a specific Connection datatype used to access a service. It provides a combinator to construct an instance of that datatype that accesses the launched service

Use Preparer to allow customization and cleanup of the docker container used to launch the service

  • Preparer allows resources used by a docker container to be set up before invoking the docker run command that starts a service, and enables these to be cleaned up afterwards
  • It works with ToRunCmd to allow refererences to the resources to be specified in the docker run command
Synopsis

define tmp procs

class (KnownSymbol (Image a), KnownSymbol (Name a)) => Proc a where Source #

Specifies how to launch a temporary process using Docker.

Minimal complete definition

uriOf, reset, ping

Associated Types

type Image a :: Symbol Source #

The image name of the docker image, e.g, postgres:10.6

type Name a = (labelName :: Symbol) | labelName -> a Source #

A label used to refer to the running tmp proc created from this image, e.g, a-postgres-db

Methods

runArgs :: [Text] Source #

Additional arguments to the docker command that launches the tmp proc.

uriOf :: HostIpAddress -> SvcURI Source #

Determines the service URI of the tmp proc, when applicable.

reset :: ProcHandle a -> IO () Source #

Resets some state in a tmp proc service.

ping :: ProcHandle a -> IO Pinged Source #

Checks if the tmp proc started correctly.

pingCount :: Natural Source #

Maximum number of pings to perform during startup.

pingGap :: Natural Source #

Number of milliseconds between pings.

startup :: ProcPlus a prepared => a -> IO (ProcHandle a) Source #

Starts a Proc.

It uses ping to determine if the Proc started up ok, and will fail by throwing an exception if it did not.

Returns the ProcHandle used to access and control the Proc once a ping has succeeded.

nameOf :: forall a. Proc a => a -> Text Source #

Name of a process.

uriOf' :: forall a. Proc a => a -> HostIpAddress -> SvcURI Source #

Simplifies use of uriOf.

runArgs' :: forall a. Proc a => a -> [Text] Source #

Simplifies use of runArgs.

data Pinged Source #

Indicates the result of pinging a Proc.

If the ping succeeds, ping should return OK.

ping should catch any exceptions that are expected when the Procs service is not available and return NotOK.

startupAll uses PingFailed to report any unexpected exceptions that escape ping.

Constructors

OK

The service is running OK.

NotOK

The service is not running.

PingFailed Text

Contact to the service failed unexpectedly.

Instances

Instances details
Show Pinged Source # 
Instance details

Defined in System.TmpProc.Docker

Eq Pinged Source # 
Instance details

Defined in System.TmpProc.Docker

toPinged :: forall e a. Exception e => Proxy e -> IO a -> IO Pinged Source #

Use an action that might throw an exception as a ping.

class AreProcs as Source #

Declares a proof that a list of types only contains Procs.

Minimal complete definition

procProof

Instances

Instances details
AreProcs ('[] :: [Type]) Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

procProof :: SomeProcs '[]

(ProcPlus a prepared, AreProcs as, IsAbsent a as) => AreProcs (a ': as) Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

procProof :: SomeProcs (a ': as)

customize proc setup

class Preparer a prepared => ToRunCmd a prepared where Source #

Allow customization of the docker command that launches a Proc

The docker launch command is `docker run -d optional-args --name $(name a) $(imageText a)`

A Proc datatype should declare an instance of ToRunCmd to control optional-args

This module provides an Overlappable fallback instance with default behaviour that matches all Proc, so this typeclass is only needed when a Proc datatypes actually needs additional arguments

Methods

toRunCmd :: a -> prepared -> [Text] Source #

Instances

Instances details
(a ~ a', Proc a) => ToRunCmd a a' Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

toRunCmd :: a -> a' -> [Text] Source #

class Preparer a prepared | a -> prepared where Source #

Prepare resources for use by a Proc

Preparation occurs before the docker container is a launched; once the resources are set up, they are located using the prepared datatype.

Usually, this means it will be used by toRunCmd to provide additional arguments to the docker run command

This module provides an Overlappable fallback instance that matches all Proc, so this typeclass is only needed when a Proc datatype actually requires preparatory setup.

The first argument to prepare is a [SlimHandle] that gives access to other tmp-procs previously launched in the same test, to allow prepare to setup links to them when necessary

Methods

prepare :: [SlimHandle] -> a -> IO prepared Source #

Generate a prepared before the docker container is started

tidy :: a -> prepared -> IO () Source #

Clean up any resources associated with prepared

Instances

Instances details
(a ~ a', Proc a) => Preparer a a' Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

prepare :: [SlimHandle] -> a -> IO a' Source #

tidy :: a -> a' -> IO () Source #

type ProcPlus a prepared = (Proc a, ToRunCmd a prepared, Preparer a prepared) Source #

A Constraint that combines Proc and its supporting typeclasses

start/stop many procs

startupAll :: AreProcs procs => HList procs -> IO (HandlesOf procs) Source #

Start up processes for each Proc type

the processes' are able to communicate via a docker network with a unique generated name

terminateAll :: AreProcs procs => HandlesOf procs -> IO () Source #

Terminate all processes owned by some ProcHandles.

withTmpProcs :: AreProcs procs => HList procs -> (HandlesOf procs -> IO b) -> IO b Source #

Set up some Procs, run an action that uses them, then terminate them.

startupAll' :: AreProcs procs => Maybe Text -> HList procs -> IO (NetworkHandlesOf procs) Source #

Deprecated: since v0.7 this is no longer needed and will be removed, use startupAll instead; it always generates a named docker network

Start up processes for each Proc type.

netwTerminateAll :: AreProcs procs => NetworkHandlesOf procs -> IO () Source #

Deprecated: since v0.7 this is no longer needed and will be removed, use terminateAll instead

Like terminateAll, but also removes the docker network connecting the processes.

netwStartupAll :: AreProcs procs => HList procs -> IO (NetworkHandlesOf procs) Source #

Deprecated: since v0.7 this is no longer needed and will be removed, use startupAll instead

Like startupAll, but reveals the generated network name via the deprecated NetworkHandlesOf

access running procs

data ProcHandle a where Source #

Provides access to a Proc that has been started.

Bundled Patterns

pattern ProcHandle

A pattern constructor the provides selectors for the ProcHandle fields

The selectors are readonly, i.e they only match in a pattern context since ProcHandles cannot be constructed directly; they are constructed by invoking startupAll or startup

Fields

  • :: a

    the Proc that led to this ProcHandle

  • -> String

    the docker process ID corresponding to the started container

  • -> SvcURI

    the URI to the test service instance

  • -> HostIpAddress

    the IP address of the test service instance

  • -> ProcHandle a
     

handleOf :: HandleOf a procs b => Proxy a -> HandlesOf procs -> ProcHandle b Source #

Obtain the handle matching the given type from a HList of ProcHandle.

data SlimHandle Source #

Provides an untyped view of the data in a ProcHandle

Constructors

SlimHandle 

Instances

Instances details
Show SlimHandle Source # 
Instance details

Defined in System.TmpProc.Docker

Eq SlimHandle Source # 
Instance details

Defined in System.TmpProc.Docker

slim :: Proc a => ProcHandle a -> SlimHandle Source #

Obtain the SlimHandle.

type family Proc2Handle (as :: [Type]) = (handleTys :: [Type]) | handleTys -> as where ... Source #

Converts list of types to the corresponding ProcHandle types.

Equations

Proc2Handle '[] = '[] 
Proc2Handle (a ': as) = ProcHandle a ': Proc2Handle as 

type HasHandle aProc procs = (Proc aProc, AreProcs procs, IsInProof (ProcHandle aProc) (Proc2Handle procs)) Source #

Constraint alias used to constrain types where proxy of a Proc type looks up a value in an HList of ProcHandle.

type HasNamedHandle name a procs = (name ~ Name a, Proc a, AreProcs procs, MemberKV name (ProcHandle a) (Handle2KV (Proc2Handle procs))) Source #

Constraint alias used to constrain types where a Name looks up a type in an HList of ProcHandle.

access many started procs

type HandlesOf procs = HList (Proc2Handle procs) Source #

A list of ProcHandle values.

ixReset :: IxReset a procs => Proxy a -> HandlesOf procs -> IO () Source #

Resets the handle whose index is specified by the proxy type.

ixPing :: IxPing a procs => Proxy a -> HandlesOf procs -> IO Pinged Source #

Pings the handle whose index is specified by the proxy type.

ixUriOf :: IxUriOf a procs => Proxy a -> HandlesOf procs -> SvcURI Source #

Obtains the service URI of the handle whose index is specified by the proxy type.

manyNamed :: SomeNamedHandles names namedProcs someProcs sortedProcs => Proxy names -> HandlesOf someProcs -> HandlesOf namedProcs Source #

Select the named ProcHandles from an HList of ProcHandle.

mapSlim :: AreProcs procs => HandlesOf procs -> [SlimHandle] Source #

Obtain the SlimHandle of several Procs

type SomeNamedHandles names procs someProcs sortedProcs = (names ~ Proc2Name procs, ManyMemberKV (SortSymbols names) (SortHandles (Proc2Handle procs)) (Handle2KV (Proc2Handle sortedProcs)), ReorderH (SortHandles (Proc2Handle procs)) (Proc2Handle procs), ReorderH (Proc2Handle someProcs) (Proc2Handle sortedProcs), AreProcs sortedProcs, SortHandles (Proc2Handle someProcs) ~ Proc2Handle sortedProcs) Source #

Constraint alias when several Names are used to find matching types in an HList of ProcHandle.

type NetworkHandlesOf procs = (Text, HandlesOf procs) Source #

Deprecated: since v0.7 this is no longer necessary and will be removed

A list of ProcHandle values of different types with the name of the docker network connecting their processes

access via a known connection type

class Proc a => Connectable a where Source #

Specifies how to construct a connection datatype for accessing the launched service specified by a Proc.

Minimal complete definition

openConn

Associated Types

type Conn a = (conn :: Type) | conn -> a Source #

The connection type.

Methods

openConn :: ProcHandle a -> IO (Conn a) Source #

Get a connection to the Proc via its ProcHandle.

closeConn :: Conn a -> IO () Source #

Close a connection to a Proc.

withTmpConn :: Connectable a => ProcHandle a -> (Conn a -> IO b) -> IO b Source #

Run an action on a Connectable handle as a callback on its Conn

openAll :: Connectables xs => HandlesOf xs -> IO (HList (ConnsOf xs)) Source #

Open all the Connectable types to corresponding Conn types.

closeAll :: Connectables procs => HList (ConnsOf procs) -> IO () Source #

Close some Connectable types.

withConns :: Connectables procs => HandlesOf procs -> (HList (ConnsOf procs) -> IO b) -> IO b Source #

Open some connections, use them in an action; close them.

withConnOf :: (HandleOf idx procs namedConn, Connectable namedConn) => Proxy idx -> HandlesOf procs -> (Conn namedConn -> IO b) -> IO b Source #

Builds on handleOf; gives the Conn of the ProcHandle to a callback.

withKnownConns :: (AreProcs someProcs, Connectables conns, ReorderH (Proc2Handle someProcs) (Proc2Handle conns)) => HandlesOf someProcs -> (HList (ConnsOf conns) -> IO b) -> IO b Source #

Open all known connections; use them in an action; close them.

withNamedConns :: (SomeNamedHandles names namedConns someProcs sortedProcs, Connectables namedConns) => Proxy names -> HandlesOf someProcs -> (HList (ConnsOf namedConns) -> IO b) -> IO b Source #

Open the named connections; use them in an action; close them.

class Connectables as Source #

Declares a proof that a list of types only contains Connectables.

Minimal complete definition

connProof

Instances

Instances details
Connectables ('[] :: [Type]) Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

connProof :: Uniquely Connectable Connectables '[]

(Connectable a, Connectables as, IsAbsent a as) => Connectables (a ': as) Source # 
Instance details

Defined in System.TmpProc.Docker

Methods

connProof :: Uniquely Connectable Connectables (a ': as)

Docker status

hasDocker :: IO Bool Source #

Determine if the docker daemon is accessible.

genNetworkName :: IO Text Source #

Deprecated: since v0.7 this is no longer needs to be exported and will be hidden in later releases

generate a random network name

Aliases

type HostIpAddress = Text Source #

The IP address of the docker host.

type SvcURI = ByteString Source #

A connection string used to access the service once its running.

Re-exports

type family SortSymbols (xs :: [Symbol]) :: [Symbol] where ... Source #

Sort a list of type-level symbols using merge sort.

Examples

Expand
>>> :kind! SortSymbols '["xyz", "def", "abc"]
SortSymbols '["xyz", "def", "abc"] :: [Symbol]
= '["abc", "def", "xyz"]

Equations

SortSymbols '[] = '[] 
SortSymbols '[x] = '[x] 
SortSymbols '[x, y] = MergeSymbols '[x] '[y] 
SortSymbols xs = SortSymbolsStep xs (HalfOf (LengthOf xs)) 

type family HalfOf (n :: Nat) :: Nat where ... Source #

Computes the midpoint of a number.

N.B: maximum value that this works for depends on the reduction limit of the type-checker.

Examples

Expand
>>> :kind! CmpNat 49 (HalfOf 99)
CmpNat 49 (HalfOf 99) :: Ordering
= 'EQ
>>> :kind! CmpNat 50 (HalfOf 100)
CmpNat 50 (HalfOf 100) :: Ordering
= 'EQ

Equations

HalfOf 0 = 0 
HalfOf 1 = 1 
HalfOf 2 = 1 
HalfOf 3 = 1 
HalfOf 4 = 2 
HalfOf 5 = 2 
HalfOf 6 = 3 
HalfOf 7 = 3 
HalfOf n = HalfOfImpl n 0 n 'LT 

type family LengthOf (xs :: [k]) :: Nat where ... Source #

Counts a list, 1 element at a time.

Examples

Expand
>>> :kind! CmpNat 4 (LengthOf '[1, 2, 3, 4])
CmpNat 4 (LengthOf '[1, 2, 3, 4]) :: Ordering
= 'EQ

Equations

LengthOf '[] = 0 
LengthOf (x ': xs) = 1 + LengthOf xs 

type family Drop (xs :: [k]) (n :: Nat) :: [k] where ... Source #

Drops 1 element at a time until the the dropped target is reached.

Examples

Expand
>>> :kind! Drop '["a", "b", "c", "d"] 2
Drop '["a", "b", "c", "d"] 2 :: [Symbol]
= '["c", "d"]
>>> :kind! Drop '["a"] 2
Drop '["a"] 2 :: [Symbol]
= '[]

Equations

Drop '[] n = '[] 
Drop xs 0 = xs 
Drop (x ': xs) n = Drop xs (n - 1) 

type family Take (xs :: [k]) (n :: Nat) :: [k] where ... Source #

Takes 1 element at a time from a list until the desired length is reached.

Examples

Expand
>>> :kind! Take '["a", "b", "c", "d"] 2
Take '["a", "b", "c", "d"] 2 :: [Symbol]
= '["a", "b"]

Equations

Take '[] n = '[] 
Take xs 0 = '[] 
Take (x ': xs) n = x ': Take xs (n - 1) 

class IsInProof t (tys :: [Type]) Source #

Generate proof instances of IsIn.

Minimal complete definition

provedIsIn

Instances

Instances details
IsInProof t tys => IsInProof t (a ': tys) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

provedIsIn :: IsIn t (a ': tys)

IsInProof t (t ': tys) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

provedIsIn :: IsIn t (t ': tys)

class ReorderH xs ys where Source #

Allows reordering of similar HLists.

Examples

Expand
>>> hReorder @_ @'[Bool, Int] ('c' &: (3 :: Int) &: True &: (3.1 :: Double) &: HNil)
True &: 3 &: HNil
>>> hReorder @_ @'[Double, Bool, Int] ('c' &: (3 :: Int) &: True &: (3.1 :: Double) &: HNil)
3.1 &: True &: 3 &: HNil

Methods

hReorder :: HList xs -> HList ys Source #

Instances

Instances details
ReorderH xs ('[] :: [Type]) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

hReorder :: HList xs -> HList '[] Source #

(IsInProof y xs, ReorderH xs ys) => ReorderH xs (y ': ys) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

hReorder :: HList xs -> HList (y ': ys) Source #

class ManyMemberKV (ks :: [Symbol]) (ts :: [Type]) (kvs :: [Type]) Source #

Generate proof instances of LookupMany.

Minimal complete definition

manyProof

Instances

Instances details
ManyMemberKV ks ts kvs => ManyMemberKV ks ts (KV ok ot ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany ks ts (KV ok ot ': kvs) Source #

ManyMemberKV '[k] '[t] (KV k t ': ks) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany '[k] '[t] (KV k t ': ks) Source #

ManyMemberKV ks ts kvs => ManyMemberKV (k ': ks) (t ': ts) (KV k t ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany (k ': ks) (t ': ts) (KV k t ': kvs) Source #

class MemberKV (k :: Symbol) (t :: Type) (xs :: [Type]) Source #

Generate proof instances of LookupKV.

Minimal complete definition

lookupProof

Instances

Instances details
MemberKV k t '[KV k t] Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t '[KV k t] Source #

MemberKV k t (KV k t ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t (KV k t ': kvs) Source #

MemberKV k t kvs => MemberKV k t (KV ok ot ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t (KV ok ot ': kvs) Source #

type family IsAbsent e r :: Constraint where ... Source #

A constraint that confirms that a type is not present in a type-level list.

Equations

IsAbsent e '[] = () 
IsAbsent e (e ': _) = TypeError (NotAbsentErr e) 
IsAbsent e (e' ': tail) = IsAbsent e tail 

data KV :: Symbol -> Type -> Type where Source #

Use a type-level symbol as key type that indexes a value type.

Constructors

V :: a -> KV s a 

Instances

Instances details
ManyMemberKV ks ts kvs => ManyMemberKV ks ts (KV ok ot ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany ks ts (KV ok ot ': kvs) Source #

MemberKV k t '[KV k t] Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t '[KV k t] Source #

MemberKV k t (KV k t ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t (KV k t ': kvs) Source #

MemberKV k t kvs => MemberKV k t (KV ok ot ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

lookupProof :: LookupKV k t (KV ok ot ': kvs) Source #

ManyMemberKV '[k] '[t] (KV k t ': ks) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany '[k] '[t] (KV k t ': ks) Source #

ManyMemberKV ks ts kvs => ManyMemberKV (k ': ks) (t ': ts) (KV k t ': kvs) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

manyProof :: LookupMany (k ': ks) (t ': ts) (KV k t ': kvs) Source #

data HList :: [Type] -> Type where Source #

Defines a Heterogenous list.

Constructors

HNil :: HList '[] 
HCons :: anyTy -> HList manyTys -> HList (anyTy ': manyTys) infixr 5 

Instances

Instances details
(Show x, Show (HList xs)) => Show (HList (x ': xs)) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

showsPrec :: Int -> HList (x ': xs) -> ShowS Source #

show :: HList (x ': xs) -> String Source #

showList :: [HList (x ': xs)] -> ShowS Source #

Show (HList ('[] :: [Type])) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

showsPrec :: Int -> HList '[] -> ShowS Source #

show :: HList '[] -> String Source #

showList :: [HList '[]] -> ShowS Source #

(Eq x, Eq (HList xs)) => Eq (HList (x ': xs)) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

(==) :: HList (x ': xs) -> HList (x ': xs) -> Bool Source #

(/=) :: HList (x ': xs) -> HList (x ': xs) -> Bool Source #

Eq (HList ('[] :: [Type])) Source # 
Instance details

Defined in System.TmpProc.TypeLevel

Methods

(==) :: HList '[] -> HList '[] -> Bool Source #

(/=) :: HList '[] -> HList '[] -> Bool Source #

hHead :: HList (a ': as) -> a Source #

Obtain the first element of a HList.

hOf :: forall y xs. IsInProof y xs => Proxy y -> HList xs -> y Source #

Get an item in an HList given its type.

(&:) :: x -> HList xs -> HList (x ': xs) infixr 5 Source #

An infix alias for HCons.

both :: x -> y -> HList '[x, y] infixr 6 Source #

Construct a two-item HList.

(&:&) :: x -> y -> HList '[x, y] infixr 6 Source #

An infix alias for both.

only :: x -> HList '[x] Source #

Construct a singleton HList

select :: forall k t xs. MemberKV k t xs => HList xs -> t Source #

Select an item from an HList of KVs by key.

N.B Returns the first item. It assumes the keys in the KV HList are unique. TODO: enforce this rule using a constraint.

Examples

Expand
>>> select @"d" @Double  @'[KV "b" Bool, KV "d" Double] (V True &:  V (3.1 :: Double) &: HNil)
3.1

selectMany :: forall ks ts xs. ManyMemberKV ks ts xs => HList xs -> HList ts Source #

Select items with specified keys from an HList of KVs by key.

N.B. this this is an internal function.

The keys must be provided in the same order as they occur in the HList, any other order will likely result in an compiler error.

Examples

Expand
>>> selectMany @'["b"] @'[Bool] @'[KV "b" Bool, KV "d" Double] (V True &:  V (3.1 :: Double) &: HNil)
True &: HNil