diff --git a/CHANGELOG.markdown b/CHANGELOG.markdown
--- a/CHANGELOG.markdown
+++ b/CHANGELOG.markdown
@@ -1,3 +1,23 @@
+# v1.1.1 (2015/04/20)
+
+## New features
+* Add 'notify' native type
+* Ability to provide defaults via a yaml file for some options
+* Added the 'ensure_packages' and 'ensure_resources' functions
+
+## Bugs fixed
+* Enable 'package' native type (issue #102)
+* Builds against GHC 7.10
+
+## Changes
+* Even in Permissive mode, don't resolve unknown variable (see #103)
+* Add priority to the logger permissive output (see #106)
+* New hruby version
+* Rename option `--ignoremodules` into `--ignoredmodules`
+
+## Various
+* Hiera config interpolation logs decrease from NOTICE to INFO
+
 # v1.1.0 (2015/03/11)
 
 Critical bugs have been fixed, upgrade recommended.
diff --git a/Erb/Compute.hs b/Erb/Compute.hs
--- a/Erb/Compute.hs
+++ b/Erb/Compute.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE CPP                      #-}
-{-# LANGUAGE ForeignFunctionInterface #-}
 {-# LANGUAGE LambdaCase               #-}
 {-# LANGUAGE NamedFieldPuns           #-}
 module Erb.Compute(computeTemplate, initTemplateDaemon) where
@@ -11,10 +10,11 @@
 import           Puppet.Utils
 
 import           Control.Concurrent
-import           Control.Monad.Error
+import           Control.Monad.Except
 import qualified Data.Either.Strict           as S
 import           Data.FileCache
 import qualified Data.Text                    as T
+import           Data.String
 import           Debug.Trace
 import           Erb.Evaluate
 import           Erb.Parser
@@ -29,24 +29,24 @@
 
 import           Control.Lens
 import           Data.Tuple.Strict
-import qualified Foreign.Ruby                 as FR
-import           Foreign.Ruby.Safe
+import qualified Foreign.Ruby.Helpers         as FR
+import qualified Foreign.Ruby.Bindings        as FR
+import           Foreign.Ruby
 
+instance IsString TemplateParseError where
+  fromString s = TemplateParseError $ newErrorMessage (Message s) (initialPos "dummy")
 
 newtype TemplateParseError = TemplateParseError { tgetError :: ParseError }
 
-instance Error TemplateParseError where
-    noMsg = TemplateParseError $ newErrorUnknown (initialPos "dummy")
-    strMsg s = TemplateParseError $ newErrorMessage (Message s) (initialPos "dummy")
-
 type TemplateQuery = (Chan TemplateAnswer, Either T.Text T.Text, T.Text, Container ScopeInformation)
 type TemplateAnswer = S.Either PrettyError T.Text
 
 showRubyError :: RubyError -> PrettyError
 showRubyError (Stack msg stk) = PrettyError $ dullred (string msg) </> dullyellow (string stk)
 showRubyError (WithOutput str _) = PrettyError $ dullred (string str)
+showRubyError (OtherError rr) = PrettyError (dullred (text rr))
 
-initTemplateDaemon :: RubyInterpreter -> (Preferences IO) -> MStats -> IO (Either T.Text T.Text -> T.Text -> Container ScopeInformation -> IO (S.Either PrettyError T.Text))
+initTemplateDaemon :: RubyInterpreter -> Preferences IO -> MStats -> IO (Either T.Text T.Text -> T.Text -> Container ScopeInformation -> IO (S.Either PrettyError T.Text))
 initTemplateDaemon intr prefs mvstats = do
     controlchan <- newChan
     templatecache <- newFileCache
@@ -115,7 +115,7 @@
 
 getRubyScriptPath :: String -> IO String
 getRubyScriptPath rubybin = do
-    let checkpath :: FilePath -> (IO FilePath) -> IO FilePath
+    let checkpath :: FilePath -> IO FilePath -> IO FilePath
         checkpath fp nxt = do
             e <- fileExist fp
             if e
@@ -137,14 +137,14 @@
     variables <- FR.extractHaskellValue rvariables
     toresolve <- FR.fromRuby rtoresolve
     let answer = case toresolve of
-                     Just "~g~e~t_h~a~s~h~" ->
+                     Right "~g~e~t_h~a~s~h~" ->
                         let getvars ctx = (variables ^. ix ctx . scopeVariables) & traverse %~ view (_1 . _1)
                             vars = getvars "::" <> getvars scope
                         in  Right (PHash vars)
-                     Just t -> getVariable variables scope t
-                     _ -> Left "The variable name is not a string"
+                     Right t -> getVariable variables scope t
+                     Left rr -> Left ("The variable name is not a string" <+> text rr)
     case answer of
-        Left _  -> FR.getSymbol "undef"
+        Left _  -> getSymbol "undef"
         Right r -> FR.toRuby r
 
 computeTemplateWRuby :: Either T.Text T.Text -> T.Text -> Container ScopeInformation -> IO TemplateAnswer
@@ -158,7 +158,7 @@
             case erbBinding of
                 Left x -> return (Left x)
                 Right v -> do
-                     forM_ (itoList varlist) $ \(varname, (varval :!: _ :!: _)) -> FR.toRuby varval >>= FR.rb_iv_set v (T.unpack varname)
+                     forM_ (itoList varlist) $ \(varname, varval :!: _ :!: _) -> FR.toRuby varval >>= FR.rb_iv_set v (T.unpack varname)
                      f v
     o <- case fileinfo of
              Right fname  -> do
@@ -174,5 +174,5 @@
                             Left _  -> "inline_template"
             in  return (S.Left $ PrettyError (dullred (text rr) <+> "in" <+> dullgreen (text fname)))
         Right r -> FR.fromRuby r >>= \case
-            Just result -> return (S.Right result)
-            Nothing -> return (S.Left "Could not deserialiaze ruby output")
+            Right result -> return (S.Right result)
+            Left rr -> return (S.Left $ PrettyError ("Could not deserialiaze ruby output" <+> text rr))
diff --git a/Facter.hs b/Facter.hs
--- a/Facter.hs
+++ b/Facter.hs
@@ -8,7 +8,6 @@
 import Puppet.Interpreter.Types
 import qualified Data.Text as T
 import Control.Arrow
-import qualified Data.Either.Strict as S
 import Control.Lens
 import System.Posix.User
 import System.Posix.Unistd (getSystemID, SystemID(..))
@@ -17,6 +16,7 @@
 import System.Environment
 import System.Directory (doesFileExist)
 import Data.Maybe (mapMaybe)
+import Control.Monad.Trans.Either
 
 storageunits :: [(String, Int)]
 storageunits = [ ("", 0), ("K", 1), ("M", 2), ("G", 3), ("T", 4) ]
@@ -107,7 +107,7 @@
         lrelease = getval "DISTRIB_RELEASE"
         distid  = getval "DISTRIB_ID"
         maj     | lrelease == "?" = "?"
-                | otherwise = fst $ break (== '.') lrelease
+                | otherwise = takeWhile (/= '.') lrelease
         osfam   | distid == "Ubuntu" = "Debian"
                 | otherwise = distid
     return  [ ("lsbdistid"              , distid)
@@ -164,13 +164,13 @@
 factProcessor = do
     cpuinfo <- readFile "/proc/cpuinfo"
     let cpuinfos = zip [ "processor" ++ show (n :: Int) | n <- [0..]] modelnames
-        modelnames = mapMaybe (fmap (dropWhile (`elem` "\t :")) . stripPrefix "model name") (lines cpuinfo)
+        modelnames = mapMaybe (fmap (dropWhile (`elem` ("\t :" :: String))) . stripPrefix "model name") (lines cpuinfo)
     return $ ("processorcount", show (length cpuinfos)) : cpuinfos
 
 puppetDBFacts :: T.Text -> PuppetDBAPI IO -> IO (Container PValue)
 puppetDBFacts ndename pdbapi =
-    getFacts pdbapi (QEqual FCertname ndename) >>= \case
-        S.Right facts@(_:_) -> return (HM.fromList (map (\f -> (f ^. factname, f ^. factval)) facts))
+    runEitherT (getFacts pdbapi (QEqual FCertname ndename)) >>= \case
+        Right facts@(_:_) -> return (HM.fromList (map (\f -> (f ^. factname, f ^. factval)) facts))
         _ -> do
             rawFacts <- fmap concat (sequence [factNET, factRAM, factOS, fversion, factMountPoints, factOS, factUser, factUName, fenv, factProcessor])
             let ofacts = genFacts $ map (T.pack *** T.pack) rawFacts
diff --git a/HLint.hs b/HLint.hs
new file mode 100644
--- /dev/null
+++ b/HLint.hs
@@ -0,0 +1,4 @@
+import "hint" HLint.HLint
+
+
+ignore "Redundant lambda"
diff --git a/Hiera/Server.hs b/Hiera/Server.hs
--- a/Hiera/Server.hs
+++ b/Hiera/Server.hs
@@ -38,7 +38,7 @@
 import qualified System.Log.Logger           as LOG
 
 import           Puppet.Interpreter.Types
-import           Puppet.PP                   hiding ((<$>))
+import           Puppet.PP
 import           Puppet.Utils                (strictifyEither)
 
 loggerName :: String
@@ -159,7 +159,7 @@
             case resolveInterpolable vars strs of
                 Just s  -> queryCombinator qtype (map (query'' s) _backends)
                 Nothing -> do
-                    LOG.noticeM loggerName (show $ "Hiera lookup: skipping using hierarchy level" <+> pretty strs
+                    LOG.infoM loggerName (show $ "Hiera lookup: skipping using hierarchy level" <+> pretty strs
                                             <$$> "It couldn't be interpolated with known variables:" <+> varlist)
                     return Nothing
         query'' :: T.Text -> Backend -> IO (Maybe PValue)
diff --git a/Puppet/Daemon.hs b/Puppet/Daemon.hs
--- a/Puppet/Daemon.hs
+++ b/Puppet/Daemon.hs
@@ -1,24 +1,35 @@
 {-# LANGUAGE CPP        #-}
+{-# LANGUAGE GADTs      #-}
 {-# LANGUAGE LambdaCase #-}
 module Puppet.Daemon (initDaemon) where
 
+import           Control.Applicative
 import           Control.Exception
+import           Control.Exception.Lens
 import           Control.Lens
-import           Control.Monad            (when)
-import qualified Data.Either.Strict       as S
+import qualified Data.Either.Strict        as S
 import           Data.FileCache
-import qualified Data.HashMap.Strict      as HM
-import qualified Data.Text                as T
-import qualified Data.Text.IO             as T
+import qualified Data.HashMap.Strict       as HM
+import qualified Data.Text                 as T
+import qualified Data.Text.IO              as T
+import           Data.Traversable          (for)
 import           Data.Tuple.Strict
-import qualified Data.Vector              as V
+import qualified Data.Vector               as V
 import           Debug.Trace
 import           Erb.Compute
 import           Foreign.Ruby.Safe
+import           Prelude
+import           System.IO                 (stdout)
+import           System.Log.Formatter      as LOG
+import           System.Log.Handler        as LOG
+import           System.Log.Handler.Simple as LOG
+import qualified System.Log.Logger         as LOG
+
 import           Hiera.Server
 import           Puppet.Interpreter
 import           Puppet.Interpreter.IO
 import           Puppet.Interpreter.Types
+import           Puppet.Lens               (_PrettyError)
 import           Puppet.Manifests
 import           Puppet.OptionalTests
 import           Puppet.Parser
@@ -28,7 +39,6 @@
 import           Puppet.Preferences
 import           Puppet.Stats
 import           Puppet.Utils
-import qualified System.Log.Logger        as LOG
 
 {-| This is a high level function, that will initialize the parsing and
 interpretation infrastructure from the 'Preferences', and will return 'DaemonMethods'.
@@ -52,12 +62,13 @@
 * It might be buggy when top level statements that are not class\/define\/nodes
 are altered, or when files loaded with require are changed.
 
-* The catalog is not computed exactly the same way Puppet does. Some good practices are enforced.
-As an example querying a dictionary with a non existent key returns undef in puppet, whereas it throws an error with language-puppet.
+* The catalog is not computed exactly the same way Puppet does. Some good practices are enforced, particularly in strict mode.
+For instance, unknown variables are always an error. Querying a dictionary with a non existent key returns undef in puppet, whereas it would throw an error in strict mode.
 
 -}
 initDaemon :: Preferences IO -> IO DaemonMethods
 initDaemon prefs = do
+    setupConsoleLogger
     logDebug "initDaemon"
     traceEventIO "initDaemon"
     templateStats <- newStats
@@ -68,7 +79,7 @@
     intr          <- startRubyInterpreter
     getTemplate   <- initTemplateDaemon intr prefs templateStats
     hquery        <- case prefs ^. hieraPath of
-                         Just p  -> fmap (either error id) $ startHiera p
+                         Just p  -> either error id <$> startHiera p
                          Nothing -> return dummyHiera
     luacontainer <- initLuaMaster (T.pack (prefs ^. puppetPaths.modulesPath))
     let myprefs = prefs & prefExtFuncs %~ HM.union luacontainer
@@ -95,18 +106,22 @@
                                             hquery
                                             defaultImpureMethods
                                             (prefs ^. ignoredmodules)
+                                            (prefs ^. externalmodules)
                                             (prefs ^. strictness == Strict))
                                         ndename
                                         facts
     (stmts :!: warnings) <- measure stats ndename catalogComputation
     mapM_ (\(p :!: m) -> LOG.logM loggerName p (displayS (renderCompact (ttext ndename <> ":" <+> m)) "")) warnings
     traceEventIO ("STOP gCatalog " <> T.unpack ndename)
-    when (prefs ^. extraTests) $ runOptionalTests stmts
-    return stmts
+    if prefs ^. extraTests
+       then runOptionalTests stmts
+       else return stmts
     where
-      runOptionalTests r = case r^?S._Right._1 of
-        Nothing -> return ()
-        Just c  -> testCatalog (prefs^.puppetPaths.baseDir) c
+      runOptionalTests stm = case stm^?S._Right._1 of
+        Nothing -> return stm
+        (Just c)  -> catching _PrettyError
+                              (do {testCatalog prefs c; return stm})
+                              (return . S.Left)
 
 parseFunction :: Preferences IO -> FileCache (V.Vector Statement) -> MStats -> TopLevelType -> T.Text -> IO (S.Either PrettyError Statement)
 parseFunction prefs filecache stats topleveltype toplevelname =
@@ -150,3 +165,12 @@
 
 logDebug :: T.Text -> IO ()
 logDebug   = LOG.debugM   loggerName . T.unpack
+
+setupConsoleLogger :: IO ()
+setupConsoleLogger = do
+   hs <- for [LOG.DEBUG, LOG.INFO, LOG.NOTICE, LOG.WARNING] consoleLogHandler
+   LOG.updateGlobalLogger LOG.rootLoggerName $ LOG.setHandlers hs
+   where
+     consoleLogHandler p = LOG.setFormatter
+                          <$> LOG.streamHandler stdout p
+                          <*> pure (LOG.simpleLogFormatter "$prio: $msg")
diff --git a/Puppet/Interpreter.hs b/Puppet/Interpreter.hs
--- a/Puppet/Interpreter.hs
+++ b/Puppet/Interpreter.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE GADTs        #-}
 {-# LANGUAGE LambdaCase   #-}
 {-# LANGUAGE RankNTypes   #-}
 module Puppet.Interpreter
@@ -12,11 +13,11 @@
 import           Puppet.NativeTypes
 import           Puppet.Parser.PrettyPrinter
 import           Puppet.Parser.Types
-import           Puppet.PP                        hiding ((<$>))
+import           Puppet.PP
 
 import           Control.Applicative
 import           Control.Lens
-import           Control.Monad.Error              hiding (forM, mapM)
+import           Control.Monad.Except
 import           Control.Monad.Operational        hiding (view)
 import qualified Data.Either.Strict               as S
 import           Data.Foldable                    (Foldable, foldl', foldlM,
@@ -30,13 +31,14 @@
 import qualified Data.Maybe.Strict                as S
 import           Data.Ord                         (comparing)
 import qualified Data.Text                        as T
-import           Data.Traversable                 (mapM)
+import           Data.Traversable                 (mapM, for)
 import qualified Data.Tree                        as T
 import           Data.Tuple.Strict                (Pair (..))
 import qualified Data.Tuple.Strict                as S
-import           Prelude                          hiding (mapM)
+import qualified Data.Vector                      as V
 import           Puppet.Utils
 import           System.Log.Logger
+import           Prelude                          hiding (mapM)
 
 -- helpers
 vmapM :: (Monad m, Foldable t) => (a -> m b) -> t a -> m [b]
@@ -120,7 +122,7 @@
             n <- isNativeType (r ^. rid . itype)
             -- if we have a native type, or a virtual/exported resource it
             -- should not be expanded !
-            if (n || r ^. rvirtuality /= Normal)
+            if n || r ^. rvirtuality /= Normal
                 then return [r]
                 else expandDefine r
     concat <$> mapM expandableDefine withDefaults
@@ -672,7 +674,6 @@
 addAttribute :: OverrideType -> T.Text -> Resource -> PValue -> InterpreterMonad Resource
 addAttribute _ "alias"     r v = (\rv -> r & ralias . contains rv .~ True) <$> resolvePValueString v
 addAttribute _ "audit"     r _ = use curPos >>= \p -> warn ("Metaparameter audit ignored at" <+> showPPos p) >> return r
-addAttribute _ "noop"      r _ = use curPos >>= \p -> warn ("Metaparameter noop ignored at" <+> showPPos p) >> return r
 addAttribute _ "loglevel"  r _ = use curPos >>= \p -> warn ("Metaparameter loglevel ignored at" <+> showPPos p) >> return r
 addAttribute _ "schedule"  r _ = use curPos >>= \p -> warn ("Metaparameter schedule ignored at" <+> showPPos p) >> return r
 addAttribute _ "stage"     r _ = use curPos >>= \p -> warn ("Metaparameter stage ignored at" <+> showPPos p) >> return r
@@ -768,6 +769,8 @@
         genRes rname x = throwPosError ("create_resource(): the value corresponding to key" <+> ttext rname <+> "should be a hash, not" <+> pretty x)
     concat . HM.elems <$> itraverse genRes hs
 mainFunctionCall "create_resources" args = throwPosError ("create_resource(): expects between two and three arguments, of type [string,hash,hash], and not:" <+> pretty args)
+mainFunctionCall "ensure_packages" args = ensurePackages args
+mainFunctionCall "ensure_resource" args = ensureResource args
 mainFunctionCall "realize" args = do
     p <- use curPos
     let realiz (PResourceReference rt rn) = resMod %= (ResourceModifier rt ModifierMustMatch RealizeVirtual (REqualitySearch "title" (PString rn)) return p : )
@@ -807,6 +810,35 @@
     rs <- singleton (ExternalFunction fname args)
     unless (rs == PUndef) $ throwPosError ("This function call should return" <+> pretty PUndef <+> "and not" <+> pretty rs </> pretty representation)
     return []
+
+ensurePackages :: [PValue] -> InterpreterMonad [Resource]
+ensurePackages [packages] = ensurePackages [packages, PHash mempty]
+ensurePackages [PString p, x] = ensurePackages [ PArray (V.singleton (PString p)), x ]
+ensurePackages [PArray packages, PHash defaults] = do
+    checkStrict "The use of the 'ensure_packages' function is a code smell."
+                "The 'ensure_packages' function is not allowed in strict mode."
+    concat <$> for packages (resolvePValueString >=> ensureResource' "package" (HM.singleton "ensure" "present" <> defaults))
+ensurePackages [PArray _,_] = throwPosError "ensure_packages(): the second argument must be a hash."
+ensurePackages [_,_] = throwPosError "ensure_packages(): the first argument must be a string or an array of strings."
+ensurePackages _ = throwPosError "ensure_packages(): requires one or two arguments."
+
+ensureResource :: [PValue] -> InterpreterMonad [Resource]
+ensureResource [PString tp, PString ttl, PHash params] = do
+    checkStrict "The use of the 'ensure_resource' function is a code smell."
+                "The 'ensure_resource' function is not allowed in strict mode."
+    ensureResource' tp params ttl
+ensureResource [tp,ttl] = ensureResource [tp,ttl,PHash mempty]
+ensureResource [_, PString _, PHash _] = throwPosError "ensureResource(): The first argument must be a string."
+ensureResource [PString _, _, PHash _] = throwPosError "ensureResource(): The second argument must be a string."
+ensureResource [PString _, PString _, _] = throwPosError "ensureResource(): The thrid argument must be a hash."
+ensureResource _ = throwPosError "ensureResource(): expects 2 or 3 arguments."
+
+ensureResource' :: T.Text -> HM.HashMap T.Text PValue -> T.Text -> InterpreterMonad [Resource]
+ensureResource' tp params ttl = do
+    def <- has (ix (RIdentifier tp ttl)) <$> use definedResources
+    if def
+       then return []
+       else use curPos >>= registerResource tp ttl params Normal
 
 -- Method stuff
 evaluateHFC :: HFunctionCall -> InterpreterMonad [Resource]
diff --git a/Puppet/Interpreter/IO.hs b/Puppet/Interpreter/IO.hs
--- a/Puppet/Interpreter/IO.hs
+++ b/Puppet/Interpreter/IO.hs
@@ -1,30 +1,35 @@
-{-# LANGUAGE GADTs #-}
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE BangPatterns      #-}
+{-# LANGUAGE FlexibleContexts  #-}
 {-# LANGUAGE FlexibleInstances #-}
--- | This is an internal module.
-module Puppet.Interpreter.IO (defaultImpureMethods, interpretMonad)  where
-
-import Puppet.PP hiding ((<$>))
-import Puppet.Interpreter.Types
-import Puppet.Interpreter.PrettyPrinter()
-import Puppet.Plugins()
+{-# LANGUAGE GADTs             #-}
+{-# LANGUAGE LambdaCase        #-}
 
-import Control.Monad.Operational
-import Control.Monad.State.Strict
-import Control.Lens
-import Control.Applicative
+-- | This is an internal module.
+module Puppet.Interpreter.IO (
+    defaultImpureMethods
+  , interpretMonad
+  ) where
 
-import qualified Data.Either.Strict as S
+import           Puppet.Interpreter.PrettyPrinter ()
+import           Puppet.Interpreter.Types
+import           Puppet.Plugins                   ()
+import           Puppet.PP
 
-import Data.Monoid
-import GHC.Stack
-import Debug.Trace (traceEventIO)
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
-import qualified Scripting.Lua as Lua
-import Control.Exception
-import Control.Concurrent.MVar
+import           Control.Applicative
+import           Control.Concurrent.MVar
+import           Control.Exception
+import           Control.Lens
+import           Control.Monad.Operational
+import           Control.Monad.State.Strict
+import           Control.Monad.Trans.Either
+import qualified Data.Either.Strict               as S
+import           Data.Monoid
+import qualified Data.Text                        as T
+import qualified Data.Text.IO                     as T
+import           Debug.Trace                      (traceEventIO)
+import           GHC.Stack
+import qualified Scripting.Lua                    as Lua
+import           Prelude
 
 defaultImpureMethods :: (Functor m, MonadIO m) => ImpureMethods m
 defaultImpureMethods = ImpureMethods (liftIO currentCallStack)
@@ -64,6 +69,9 @@
         canFail iof = iof >>= \case
             S.Left err -> thpe err
             S.Right x -> runInstr x
+        canFailE iof = runEitherT iof >>= \case
+            Left err -> thpe err
+            Right x -> runInstr x
         logStuff x c = (_3 %~ (x <>)) <$> c
     in  case a of
             IsStrict                     -> runInstr (r ^. isStrict)
@@ -82,18 +90,19 @@
             GetNodeName                  -> runInstr (r ^. thisNodename)
             HieraQuery scps q t          -> canFail ((r ^. hieraQuery) scps q t)
             PDBInformation               -> pdbInformation pdb >>= runInstr
-            PDBReplaceCatalog w          -> canFail (replaceCatalog pdb w)
-            PDBReplaceFacts fcts         -> canFail (replaceFacts pdb fcts)
-            PDBDeactivateNode nn         -> canFail (deactivateNode pdb nn)
-            PDBGetFacts q                -> canFail (getFacts pdb q)
-            PDBGetResources q            -> canFail (getResources pdb q)
-            PDBGetNodes q                -> canFail (getNodes pdb q)
-            PDBCommitDB                  -> canFail (commitDB pdb)
-            PDBGetResourcesOfNode nn q   -> canFail (getResourcesOfNode pdb nn q)
+            PDBReplaceCatalog w          -> canFailE (replaceCatalog pdb w)
+            PDBReplaceFacts fcts         -> canFailE (replaceFacts pdb fcts)
+            PDBDeactivateNode nn         -> canFailE (deactivateNode pdb nn)
+            PDBGetFacts q                -> canFailE (getFacts pdb q)
+            PDBGetResources q            -> canFailE (getResources pdb q)
+            PDBGetNodes q                -> canFailE (getNodes pdb q)
+            PDBCommitDB                  -> canFailE (commitDB pdb)
+            PDBGetResourcesOfNode nn q   -> canFailE (getResourcesOfNode pdb nn q)
             GetCurrentCallStack          -> (r ^. ioMethods . imGetCurrentCallStack) >>= runInstr
             ReadFile fls                 -> strFail ((r ^. ioMethods . imReadFile) fls) (const $ PrettyError ("No file found in " <> list (map ttext fls)))
             TraceEvent e                 -> (r ^. ioMethods . imTraceEvent) e >>= runInstr
             IsIgnoredModule m            -> runInstr (r ^. ignoredModules . contains m)
+            IsExternalModule m           -> runInstr (r ^. externalModules . contains m)
             CallLua c fname args         -> (r ^. ioMethods . imCallLua) c fname args >>= \case
                                                 Right x -> runInstr x
                                                 Left rr -> thpe (PrettyError (string rr))
diff --git a/Puppet/Interpreter/PrettyPrinter.hs b/Puppet/Interpreter/PrettyPrinter.hs
--- a/Puppet/Interpreter/PrettyPrinter.hs
+++ b/Puppet/Interpreter/PrettyPrinter.hs
@@ -8,17 +8,19 @@
 import           Puppet.PP
 import           Puppet.Utils
 
-import           Control.Arrow               (first, second)
+import           Control.Arrow                (first, second)
 import           Control.Lens
-import qualified Data.ByteString.Lazy.Char8  as BSL
-import qualified Data.HashMap.Strict         as HM
-import qualified Data.HashSet                as HS
+import qualified Data.ByteString.Lazy.Char8   as BSL
+import qualified Data.HashMap.Strict          as HM
+import qualified Data.HashSet                 as HS
 import           Data.List
-import qualified Data.Text                   as T
-import qualified Data.Vector                 as V
+import qualified Data.Text                    as T
+import qualified Data.Vector                  as V
 import           GHC.Exts
 
-import           Data.Aeson                  (ToJSON, encode)
+import           Data.Aeson                   (ToJSON, encode)
+import           Text.PrettyPrint.ANSI.Leijen ((<$>))
+import           Prelude                      hiding ((<$>))
 
 containerComma'' :: Pretty a => [(Doc, a)] -> Doc
 containerComma'' x = indent 2 ins
@@ -55,7 +57,7 @@
     pretty (RIdentifier t n) = pretty (PResourceReference t n)
 
 meta :: Resource -> Doc
-meta r = showPPos (r ^. rpos) <+> (green (node <+> brackets scp) )
+meta r = showPPos (r ^. rpos) <+> green (node <+> brackets scp)
     where
         node = red (ttext (r ^. rnode))
         scp = "Scope" <+> pretty (r ^.. rscope . folded . filtered (/=ContRoot) . to pretty)
@@ -148,6 +150,7 @@
     pretty (ReadFile f)                = pf "ReadFile" (map ttext f)
     pretty (TraceEvent e)              = pf "TraceEvent" [string e]
     pretty (IsIgnoredModule m)         = pf "IsIgnoredModule" [ttext m]
+    pretty (IsExternalModule m)        = pf "IsExternalModule" [ttext m]
 
 instance Pretty LinkInformation where
     pretty (LinkInformation lsrc ldst ltype lpos) = pretty lsrc <+> pretty ltype <+> pretty ldst <+> showPPos lpos
diff --git a/Puppet/Interpreter/Pure.hs b/Puppet/Interpreter/Pure.hs
--- a/Puppet/Interpreter/Pure.hs
+++ b/Puppet/Interpreter/Pure.hs
@@ -21,6 +21,7 @@
 import qualified Data.HashMap.Strict      as HM
 import           Data.Monoid
 import qualified Data.Text                as T
+import           Prelude
 
 -- | Worst name ever, this is a set of pure stub for the 'ImpureMethods'
 -- type.
@@ -31,7 +32,7 @@
 -- templates, and that can include only the supplied top level statements.
 pureReader :: HM.HashMap (TopLevelType, T.Text) Statement -- ^ A top-level statement map
            -> InterpreterReader Identity
-pureReader sttmap = InterpreterReader baseNativeTypes getstatementdummy templatedummy dummyPuppetDB mempty "dummy" hieradummy impurePure mempty True
+pureReader sttmap = InterpreterReader baseNativeTypes getstatementdummy templatedummy dummyPuppetDB mempty "dummy" hieradummy impurePure mempty mempty True
     where
         templatedummy (Right _) _ _ = return (S.Left "Can't interpret files")
         templatedummy (Left cnt) ctx scope =
@@ -57,7 +58,7 @@
 
 -- | A bunch of facts that can be used for pure evaluation.
 dummyFacts :: Facts
-dummyFacts = HM.fromList $
+dummyFacts = HM.fromList
         [ ("architecture", "amd64")
         , ("augeasversion", "0.10.0")
         , ("bios_release_date", "07/06/2010")
diff --git a/Puppet/Interpreter/Resolve.hs b/Puppet/Interpreter/Resolve.hs
--- a/Puppet/Interpreter/Resolve.hs
+++ b/Puppet/Interpreter/Resolve.hs
@@ -35,10 +35,7 @@
 import           Puppet.PP
 import           Puppet.Utils
 
-import           Data.Version                     (parseVersion)
-import           Text.ParserCombinators.ReadP     (readP_to_S)
-
-import           Control.Applicative              hiding ((<$>))
+import           Control.Applicative
 import           Control.Lens
 import           Control.Monad
 import           Control.Monad.Operational        (singleton)
@@ -59,7 +56,11 @@
 import qualified Data.Text.Encoding               as T
 import           Data.Tuple.Strict                as S
 import qualified Data.Vector                      as V
+import           Data.Version                     (parseVersion)
+import           Text.ParserCombinators.ReadP     (readP_to_S)
+import qualified Text.PrettyPrint.ANSI.Leijen     as PP
 import           Text.Regex.PCRE.ByteString.Utils
+import           Prelude
 
 -- | A useful type that is used when trying to perform arithmetic on Puppet
 -- numbers.
@@ -137,9 +138,7 @@
     scps <- use scopes
     scp <- getScopeName
     case getVariable scps scp fullvar of
-        Left rr -> ifStrict
-                        (throwPosError rr)
-                        (warn ("The variable" <+> pretty (UVariableReference fullvar) <+> "was not resolved" <+> pretty PUndef <+> "returned") >> return PUndef)
+        Left rr -> throwPosError rr
         Right x -> return x
 
 -- | A simple helper that checks if a given type is native or a define.
@@ -300,7 +299,7 @@
     unless (S.isNothing (hf ^. hfexpr)) (throwPosError ("You can't combine chains of higher order functions (with .) and giving them parameters, in:" <+> pretty a))
     resolveValue (UHFunctionCall (hf & hfexpr .~ S.Just e))
 resolveExpression (FunctionApplication _ x) = throwPosError ("Expected function application here, not" <+> pretty x)
-resolveExpression x = throwPosError ("Don't know how to resolve this expression:" <$> pretty x)
+resolveExpression x = throwPosError ("Don't know how to resolve this expression:" PP.<$> pretty x)
 
 -- | Resolves an 'UValue' (terminal for the 'Expression' data type) into
 -- a 'PValue'
@@ -335,13 +334,13 @@
        "Resolving the keyword `undef` to the string \"undef\""
        "Strict mode won't convert the keyword `undef` to the string \"undef\""
      return "undef"
-resolvePValueString x = throwPosError ("Don't know how to convert this to a string:" <$> pretty x)
+resolvePValueString x = throwPosError ("Don't know how to convert this to a string:" PP.<$> pretty x)
 
 -- | Turns everything it can into a number, or throws an error
 resolvePValueNumber :: PValue -> InterpreterMonad Scientific
 resolvePValueNumber x = case x ^? _Number of
                             Just n -> return n
-                            Nothing -> throwPosError ("Don't know how to convert this to a number:" <$> pretty x)
+                            Nothing -> throwPosError ("Don't know how to convert this to a number:" PP.<$> pretty x)
 
 -- | > resolveExpressionString = resolveExpression >=> resolvePValueString
 resolveExpressionString :: Expression -> InterpreterMonad T.Text
@@ -395,13 +394,16 @@
 
 resolveFunction' :: T.Text -> [PValue] -> InterpreterMonad PValue
 resolveFunction' "defined" [PResourceReference "class" cn] = do
-    checkStrict "The 'defined' function should not be used." "The 'defined' function is not allowed in strict mode."
+    checkStrict "The use of the 'defined' function is a code smell"
+                "The 'defined' function is not allowed in strict mode."
     fmap (PBoolean . has (ix cn)) (use loadedClasses)
 resolveFunction' "defined" [PResourceReference rt rn] = do
-    checkStrict "The 'defined' function should not be used." "The 'defined' function is not allowed in strict mode."
+    checkStrict "The use of the 'defined' function is a code smell"
+                "The 'defined' function is not allowed in strict mode."
     fmap (PBoolean . has (ix (RIdentifier rt rn))) (use definedResources)
 resolveFunction' "defined" [ut] = do
-    checkStrict "The 'defined' function should not be used." "The 'defined' function is not allowed in strict mode."
+    checkStrict "The use of the 'defined' function is a code smell."
+                "The 'defined' function is not allowed in strict mode."
     t <- resolvePValueString ut
     -- case 1, netsted thingie
     nestedStuff <- use nestedDeclarations
diff --git a/Puppet/Interpreter/Types.hs b/Puppet/Interpreter/Types.hs
--- a/Puppet/Interpreter/Types.hs
+++ b/Puppet/Interpreter/Types.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE AutoDeriveTypeable     #-}
 {-# LANGUAGE DeriveGeneric          #-}
 {-# LANGUAGE FlexibleContexts       #-}
 {-# LANGUAGE FlexibleInstances      #-}
@@ -82,7 +83,6 @@
  , getCurContainer
  , text2Scientific
  , safeDecodeUtf8
- , ifStrict
  , getScope
  , getScopeName
  , scopeName
@@ -104,17 +104,14 @@
  , eitherDocIO
 ) where
 
-import           Puppet.Parser.PrettyPrinter
-import           Puppet.Parser.Types
-import           Puppet.Stats
-
 import           Control.Applicative          hiding (empty)
 import           Control.Concurrent.MVar      (MVar)
 import           Control.Exception
 import           Control.Lens
-import           Control.Monad.Error
+import           Control.Monad.Except
 import           Control.Monad.Operational
 import           Control.Monad.State.Strict
+import           Control.Monad.Trans.Either
 import           Control.Monad.Writer.Class
 import           Data.Aeson                   as A
 import           Data.Aeson.Lens
@@ -136,14 +133,20 @@
 import qualified Data.Traversable             as TR
 import           Data.Tuple.Strict
 import qualified Data.Vector                  as V
-import           Foreign.Ruby
+import           Foreign.Ruby.Helpers
 import           GHC.Generics                 hiding (to)
 import           GHC.Stack
 import qualified Scripting.Lua                as Lua
-import           System.Log.Logger
+import           Servant.Common.Text
+import qualified System.Log.Logger            as LOG
 import           Text.Parsec.Pos
 import           Text.PrettyPrint.ANSI.Leijen hiding (rational, (<$>))
+import           Prelude
 
+import           Puppet.Parser.PrettyPrinter
+import           Puppet.Parser.Types
+import           Puppet.Stats
+
 metaparameters :: HS.HashSet T.Text
 metaparameters = HS.fromList ["tag","stage","name","title","alias","audit","check","loglevel","noop","schedule", "EXPORTEDSOURCE", "require", "before", "register", "notify"]
 
@@ -163,13 +166,20 @@
 instance IsString PrettyError where
     fromString = PrettyError . string
 
+instance Exception PrettyError
+
 -- | The intepreter can run in two modes : a strict mode (recommended), and
 -- a permissive mode. The permissive mode let known antipatterns work with
 -- the interpreter.
 data Strictness = Strict | Permissive
                 deriving (Show, Eq)
 
+instance FromJSON Strictness where
+  parseJSON (Bool True) = pure Strict
+  parseJSON (Bool False) = pure Permissive
+  parseJSON _ = mzero
 
+
 data PValue = PBoolean !Bool
             | PUndef
             | PString !T.Text -- integers and doubles are internally serialized as strings by puppet
@@ -237,7 +247,7 @@
                       | ContDefine !T.Text !T.Text !PPosition -- ^ Contained in a define, along with the position where this define was ... defined
                       | ContImported !CurContainerDesc -- ^ Dummy container for imported resources, so that we know we must update the nodename
                       | ContImport !Nodename !CurContainerDesc -- ^ This one is used when finalizing imported resources, and contains the current node name
-                      deriving (Eq, Generic, Ord)
+                      deriving (Eq, Generic, Ord, Show)
 
 data CurContainer = CurContainer
     { _cctype :: !CurContainerDesc
@@ -280,6 +290,7 @@
     , _hieraQuery              :: HieraQueryFunc m
     , _ioMethods               :: ImpureMethods m
     , _ignoredModules          :: HS.HashSet T.Text
+    , _externalModules         :: HS.HashSet T.Text
     , _isStrict                :: Bool
     }
 
@@ -300,6 +311,7 @@
     HieraQuery          :: Container T.Text -> T.Text -> HieraQueryType -> InterpreterInstr (Maybe PValue)
     GetCurrentCallStack :: InterpreterInstr [String]
     IsIgnoredModule     :: T.Text -> InterpreterInstr Bool
+    IsExternalModule    :: T.Text -> InterpreterInstr Bool
     IsStrict            :: InterpreterInstr Bool
     -- error
     ErrorThrow          :: PrettyError -> InterpreterInstr a
@@ -326,16 +338,16 @@
     CallLua             :: MVar Lua.LuaState -> T.Text -> [PValue] -> InterpreterInstr PValue
 
 
-type InterpreterLog = Pair Priority Doc
+type InterpreterLog = Pair LOG.Priority Doc
 type InterpreterWriter = [InterpreterLog]
 
 warn :: (Monad m, MonadWriter InterpreterWriter m) => Doc -> m ()
-warn d = tell [WARNING :!: d]
+warn d = tell [LOG.WARNING :!: d]
 
 debug :: (Monad m, MonadWriter InterpreterWriter m) => Doc -> m ()
-debug d = tell [DEBUG :!: d]
+debug d = tell [LOG.DEBUG :!: d]
 
-logWriter :: (Monad m, MonadWriter InterpreterWriter m) => Priority -> Doc -> m ()
+logWriter :: (Monad m, MonadWriter InterpreterWriter m) => LOG.Priority -> Doc -> m ()
 logWriter prio d = tell [prio :!: d]
 
 -- | The main monad
@@ -350,10 +362,6 @@
     pass = singleton . WriterPass
     listen = singleton . WriterListen
 
-instance Error PrettyError where
-    noMsg = PrettyError empty
-    strMsg = PrettyError . text
-
 data RIdentifier = RIdentifier
     { _itype :: !T.Text
     , _iname :: !T.Text
@@ -453,14 +461,14 @@
 
 data PuppetDBAPI m = PuppetDBAPI
     { pdbInformation     :: m Doc
-    , replaceCatalog     :: WireCatalog         -> m (S.Either PrettyError ()) -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#replace-catalog-version-3>
-    , replaceFacts       :: [(Nodename, Facts)] -> m (S.Either PrettyError ()) -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#replace-facts-version-1>
-    , deactivateNode     :: Nodename            -> m (S.Either PrettyError ()) -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#deactivate-node-version-1>
-    , getFacts           :: Query FactField     -> m (S.Either PrettyError [PFactInfo]) -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/query/v3/facts.html#get-v3facts>
-    , getResources       :: Query ResourceField -> m (S.Either PrettyError [Resource]) -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/query/v3/resources.html#get-v3resources>
-    , getNodes           :: Query NodeField     -> m (S.Either PrettyError [PNodeInfo])
-    , commitDB           ::                        m (S.Either PrettyError ()) -- ^ This is only here to tell the test PuppetDB to save its content to disk.
-    , getResourcesOfNode :: Nodename -> Query ResourceField -> m (S.Either PrettyError [Resource])
+    , replaceCatalog     :: WireCatalog         -> EitherT PrettyError m () -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#replace-catalog-version-3>
+    , replaceFacts       :: [(Nodename, Facts)] -> EitherT PrettyError m () -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#replace-facts-version-1>
+    , deactivateNode     :: Nodename            -> EitherT PrettyError m () -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/commands.html#deactivate-node-version-1>
+    , getFacts           :: Query FactField     -> EitherT PrettyError m [PFactInfo] -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/query/v3/facts.html#get-v3facts>
+    , getResources       :: Query ResourceField -> EitherT PrettyError m [Resource] -- ^ <http://docs.puppetlabs.com/puppetdb/1.5/api/query/v3/resources.html#get-v3resources>
+    , getNodes           :: Query NodeField     -> EitherT PrettyError m [PNodeInfo]
+    , commitDB           ::                        EitherT PrettyError m () -- ^ This is only here to tell the test PuppetDB to save its content to disk.
+    , getResourcesOfNode :: Nodename -> Query ResourceField -> EitherT PrettyError m [Resource]
     }
 
 -- | Pretty straightforward way to define the various PuppetDB queries
@@ -575,11 +583,12 @@
 instance ToRuby PValue where
     toRuby = toRuby . toJSON
 instance FromRuby PValue where
-    fromRuby v = fromRuby v >>= \case
-            Nothing -> return Nothing
-            Just x  -> case fromJSON x of
-                           Error _ -> return Nothing
-                           Success suc -> return (Just suc)
+    fromRuby = fmap chk . fromRuby
+        where
+            chk (Left x) = Left x
+            chk (Right x) = case fromJSON x of
+                                Error rr -> Left rr
+                                Success suc -> Right suc
 
 eitherDocIO :: IO (S.Either PrettyError a) -> IO (S.Either PrettyError a)
 eitherDocIO computation = (computation >>= check) `catch` (\e -> return $ S.Left $ PrettyError $ dullred $ text $ show (e :: SomeException))
@@ -641,7 +650,7 @@
                       , ("aliases", toJSON $ r ^. ralias)
                       , ("exported", Bool $ r ^. rvirtuality == Exported)
                       , ("tags", toJSON $ r ^. rtags)
-                      , ("parameters", Object ( (HM.map toJSON $ r ^. rattributes) `HM.union` relations ))
+                      , ("parameters", Object ( HM.map toJSON (r ^. rattributes) `HM.union` relations ))
                       , ("sourceline", r ^. rpos . _1 . to sourceLine . to toJSON)
                       , ("sourcefile", r ^. rpos . _1 . to sourceName . to toJSON)
                       ]
@@ -703,6 +712,9 @@
     toJSON (QGE    flds val) = toJSON [ ">=", toJSON flds, toJSON val ]
     toJSON (QEmpty)          = Null
 
+instance ToJSON a => ToText (Query a) where
+    toText = T.decodeUtf8 . Control.Lens.view strict . encode
+
 instance FromJSON a => FromJSON (Query a) where
     parseJSON Null = pure QEmpty
     parseJSON (Array elems) = case V.toList elems of
@@ -849,19 +861,25 @@
 dummypos = initialPPos "dummy"
 
 -- | Throws an error if we are in strict mode
+-- A warning in permissive mode
 checkStrict :: Doc -- ^ The warning message.
             -> Doc -- ^ The error message.
             -> InterpreterMonad ()
 checkStrict wrn err = do
+    extMod <- isExternalModule
+    let priority = if extMod then LOG.NOTICE else LOG.WARNING
     str <- singleton IsStrict
-    if str
+    if str && not extMod
         then throwPosError err
-        else warn wrn
+        else do
+          srcname <- use (curPos._1.lSourceName)
+          logWriter priority (wrn <+> "at" <+> string srcname)
 
--- | Runs operations depending on the strict flag.
-ifStrict :: InterpreterMonad a -- ^ This operation will be run in strict mode.
-         -> InterpreterMonad a -- ^ This operation will be run in permissive mode.
-         -> InterpreterMonad a
-ifStrict yes no = do
-    str <- singleton IsStrict
-    if str then yes else no
+isExternalModule :: InterpreterMonad Bool
+isExternalModule =
+    getScope >>= \case
+      ContClass n      -> isExternal n
+      ContDefine n _ _ -> isExternal n
+      _                -> return False
+    where
+      isExternal = singleton . IsExternalModule . head . T.splitOn "::"
diff --git a/Puppet/Lens.hs b/Puppet/Lens.hs
--- a/Puppet/Lens.hs
+++ b/Puppet/Lens.hs
@@ -66,6 +66,8 @@
  , _ConditionalValue
  , _FunctionApplication
  , _Terminal
+ -- * Prisms for exceptions
+ , _PrettyError
  ) where
 
 import Control.Lens
@@ -85,6 +87,8 @@
 import qualified Data.Maybe.Strict as S
 import Data.Tuple.Strict hiding (uncurry)
 import Text.Parser.Combinators (eof)
+import Control.Exception (SomeException, toException, fromException)
+import Prelude
 
 -- Prisms
 makePrisms ''PValue
@@ -103,6 +107,10 @@
 makePrisms ''SFC
 makePrisms ''RColl
 makePrisms ''Dep
+
+
+_PrettyError :: Prism' SomeException PrettyError
+_PrettyError = prism' toException fromException
 
 _ResourceDeclaration' :: Prism' Statement ResDec
 _ResourceDeclaration' = prism ResourceDeclaration $ \x -> case x of
diff --git a/Puppet/Manifests.hs b/Puppet/Manifests.hs
--- a/Puppet/Manifests.hs
+++ b/Puppet/Manifests.hs
@@ -1,4 +1,6 @@
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE GADTs #-}
 module Puppet.Manifests (filterStatements) where
 
 import Puppet.PP
@@ -8,7 +10,7 @@
 import Text.Regex.PCRE.ByteString.Utils
 import Control.Lens
 import Control.Applicative
-import Control.Monad.Error
+import Control.Monad.Except
 import qualified Data.Vector as V
 import qualified Data.Text as T
 import qualified Data.Text.Encoding as T
@@ -27,7 +29,7 @@
         triage curstuff n@(Node (Nd  NodeDefault _  _ _)) = curstuff & _4 ?~ n
         triage curstuff x = curstuff & _1 %~ (|> x)
         bsnodename = T.encodeUtf8 ndename
-        checkRegexp :: [Pair Regex Statement] -> ErrorT PrettyError IO (Maybe Statement)
+        checkRegexp :: [Pair Regex Statement] -> ExceptT PrettyError IO (Maybe Statement)
         checkRegexp [] = return Nothing
         checkRegexp ((regexp  :!: s):xs) =
             case execute' regexp bsnodename of
@@ -38,7 +40,7 @@
         strictEither (Right x) = S.Right x
     in case directnodes ^. at ndename of -- check if there is a node specifically called after my name
            Just r  -> return (S.Right (TopContainer spurious r))
-           Nothing -> fmap strictEither $ runErrorT $ do
+           Nothing -> fmap strictEither $ runExceptT $ do
                 regexpMatchM <- checkRegexp (V.toList regexpmatches) -- match regexps
                 case regexpMatchM <|> defaultnode of -- check for regexp matches or use the default node
                     Just r -> return (TopContainer spurious r)
diff --git a/Puppet/NativeTypes.hs b/Puppet/NativeTypes.hs
--- a/Puppet/NativeTypes.hs
+++ b/Puppet/NativeTypes.hs
@@ -6,7 +6,7 @@
 
 import           Control.Lens
 import           Control.Monad.Operational
-import qualified Data.HashMap.Strict as HM
+import qualified Data.HashMap.Strict           as HM
 
 import           Puppet.Interpreter.Types
 import           Puppet.NativeTypes.Concat
@@ -17,6 +17,7 @@
 import           Puppet.NativeTypes.Helpers
 import           Puppet.NativeTypes.Host
 import           Puppet.NativeTypes.Mount
+import           Puppet.NativeTypes.Notify
 import           Puppet.NativeTypes.Package
 import           Puppet.NativeTypes.SshSecure
 import           Puppet.NativeTypes.User
@@ -26,23 +27,24 @@
 fakeTypes = map faketype ["class"]
 
 defaultTypes :: [(NativeTypeName, NativeTypeMethods)]
-defaultTypes = map defaulttype ["augeas","computer","filebucket","interface","k5login","macauthorization","mailalias","maillist","mcx","nagios_command","nagios_contact","nagios_contactgroup","nagios_host","nagios_hostdependency","nagios_hostescalation","nagios_hostextinfo","nagios_hostgroup","nagios_service","nagios_servicedependency","nagios_serviceescalation","nagios_serviceextinfo","nagios_servicegroup","nagios_timeperiod","notify","package","resources","router","schedule","scheduledtask","selboolean","selmodule","service","ssh_authorized_key","sshkey","stage","tidy","vlan","yumrepo","zfs","zone","zpool"]
+defaultTypes = map defaulttype ["augeas","computer","filebucket","interface","k5login","macauthorization","mailalias","maillist","mcx","nagios_command","nagios_contact","nagios_contactgroup","nagios_host","nagios_hostdependency","nagios_hostescalation","nagios_hostextinfo","nagios_hostgroup","nagios_service","nagios_servicedependency","nagios_serviceescalation","nagios_serviceextinfo","nagios_servicegroup","nagios_timeperiod","resources","router","schedule","scheduledtask","selboolean","selmodule","service","ssh_authorized_key","sshkey","stage","tidy","vlan","yumrepo","zfs","zone","zpool"]
 
 -- | The map of native types. They are described in "Puppet.NativeTypes.Helpers".
 baseNativeTypes :: Container NativeTypeMethods
 baseNativeTypes = HM.fromList
-    ( nativeHost
-    : nativeMount
-    : nativeGroup
-    : nativeFile
-    : nativeZoneRecord
+    ( nativeConcat
+    : nativeConcatFragment
     : nativeCron
     : nativeExec
+    : nativeFile
+    : nativeGroup
+    : nativeHost
+    : nativeMount
+    : nativeNotify
     : nativePackage
-    : nativeUser
     : nativeSshSecure
-    : nativeConcat
-    : nativeConcatFragment
+    : nativeUser
+    : nativeZoneRecord
     : fakeTypes ++ defaultTypes)
 
 -- | Contrary to the previous iteration, this will let non native types pass.
diff --git a/Puppet/NativeTypes/File.hs b/Puppet/NativeTypes/File.hs
--- a/Puppet/NativeTypes/File.hs
+++ b/Puppet/NativeTypes/File.hs
@@ -1,17 +1,19 @@
 module Puppet.NativeTypes.File (nativeFile) where
 
-import Puppet.NativeTypes.Helpers hiding ((<$>))
-import Control.Monad.Error
-import Puppet.Interpreter.Types
-import Data.Char (isDigit)
-import qualified Data.Text as T
-import Control.Lens
-import Control.Applicative
-import qualified Data.Map.Strict as M
-import qualified Data.Set as S
-import Data.Monoid
-import qualified Data.Attoparsec.Text as AT
+import           Puppet.Interpreter.Types
+import           Puppet.NativeTypes.Helpers
 
+import           Control.Applicative
+import           Control.Lens
+import           Control.Monad.Except
+import           Control.Monad.Trans.Except
+import qualified Data.Attoparsec.Text       as AT
+import           Data.Char                  (isDigit)
+import qualified Data.Map.Strict            as M
+import           Data.Monoid
+import qualified Data.Set                   as S
+import qualified Data.Text                  as T
+
 nativeFile :: (NativeTypeName, NativeTypeMethods)
 nativeFile = ("file", nativetypemethods parameterfunctions (validateSourceOrContent >=> validateMode))
 
@@ -48,9 +50,9 @@
                   Just (PString s) -> return s
                   Just x -> throwError $ PrettyError ("Invalide mode type, should be a string " <+> pretty x)
                   Nothing -> throwError "Could not find mode!"
-    (numeric modestr <|> ugo modestr) & _Right %~ ($ res)
+    (numeric modestr <|> except (ugo modestr)) & runExcept & _Right %~ ($ res)
 
-numeric :: T.Text -> Either PrettyError (Resource -> Resource)
+numeric :: T.Text -> Except PrettyError (Resource -> Resource)
 numeric modestr = do
     when ((T.length modestr /= 3) && (T.length modestr /= 4)) (throwError "Invalid mode size")
     unless (T.all isDigit modestr) (throwError "The mode should only be made of digits")
diff --git a/Puppet/NativeTypes/Helpers.hs b/Puppet/NativeTypes/Helpers.hs
--- a/Puppet/NativeTypes/Helpers.hs
+++ b/Puppet/NativeTypes/Helpers.hs
@@ -32,20 +32,20 @@
     , validateSourceOrContent
     ) where
 
-import Puppet.PP hiding (string,integer)
-import Puppet.Utils
-import qualified Text.PrettyPrint.ANSI.Leijen as P
-import Puppet.Interpreter.Types
-import Puppet.Interpreter.PrettyPrinter()
-import qualified Data.HashMap.Strict as HM
-import qualified Data.HashSet as HS
-import Data.Char (isDigit)
-import Control.Monad
-import qualified Data.Text as T
-import Control.Lens
-import qualified Data.Vector as V
-import Data.Aeson.Lens (_Number,_Integer)
-import Data.Maybe (fromMaybe)
+import           Control.Lens
+import           Control.Monad
+import           Data.Aeson.Lens                  (_Integer, _Number)
+import           Data.Char                        (isDigit)
+import qualified Data.HashMap.Strict              as HM
+import qualified Data.HashSet                     as HS
+import           Data.Maybe                       (fromMaybe)
+import qualified Data.Text                        as T
+import qualified Data.Vector                      as V
+import           Puppet.Interpreter.PrettyPrinter ()
+import           Puppet.Interpreter.Types
+import           Puppet.PP                        hiding (integer, string)
+import           Puppet.Utils
+import qualified Text.PrettyPrint.ANSI.Leijen     as P
 
 type NativeTypeName = T.Text
 
diff --git a/Puppet/NativeTypes/Notify.hs b/Puppet/NativeTypes/Notify.hs
new file mode 100644
--- /dev/null
+++ b/Puppet/NativeTypes/Notify.hs
@@ -0,0 +1,15 @@
+module Puppet.NativeTypes.Notify (nativeNotify) where
+
+import           Puppet.Interpreter.Types
+import           Puppet.NativeTypes.Helpers
+
+import qualified Data.Text                  as T
+
+nativeNotify :: (NativeTypeName, NativeTypeMethods)
+nativeNotify = ("notify", nativetypemethods parameterfunctions return)
+
+parameterfunctions :: [(T.Text, [T.Text -> NativeTypeValidate])]
+parameterfunctions =
+    [("message"   , [string])
+    ,("withpath"  , [string, defaultvalue "false", values ["true","false"]])
+    ]
diff --git a/Puppet/NativeTypes/Package.hs b/Puppet/NativeTypes/Package.hs
--- a/Puppet/NativeTypes/Package.hs
+++ b/Puppet/NativeTypes/Package.hs
@@ -3,7 +3,7 @@
 
 import Puppet.NativeTypes.Helpers
 import Puppet.Interpreter.Types
-import Control.Monad.Error
+import Control.Monad.Except
 import qualified Data.HashSet as HS
 import qualified Data.HashMap.Strict as HM
 import qualified Data.Text as T
@@ -60,7 +60,8 @@
     [("adminfile"        , [string, fullyQualified])
     ,("allowcdrom"       , [string, values ["true","false"]])
     ,("configfiles"      , [string, values ["keep","replace"]])
-    ,("ensure"           , [defaultvalue "present", string, values ["present","absent","latest","held","purged","installed"]])
+    --,("ensure"           , [defaultvalue "present", string, values ["present","absent","latest","held","purged","installed"]])
+    ,("ensure"           , [defaultvalue "present", string])
     ,("flavor"           , [])
     ,("install_options"  , [rarray])
     ,("name"             , [nameval])
diff --git a/Puppet/NativeTypes/SshSecure.hs b/Puppet/NativeTypes/SshSecure.hs
--- a/Puppet/NativeTypes/SshSecure.hs
+++ b/Puppet/NativeTypes/SshSecure.hs
@@ -2,7 +2,7 @@
 
 import Puppet.NativeTypes.Helpers
 import Puppet.Interpreter.Types
-import Control.Monad.Error
+import Control.Monad.Except
 import qualified Data.Text as T
 import Control.Lens
 
diff --git a/Puppet/NativeTypes/ZoneRecord.hs b/Puppet/NativeTypes/ZoneRecord.hs
--- a/Puppet/NativeTypes/ZoneRecord.hs
+++ b/Puppet/NativeTypes/ZoneRecord.hs
@@ -2,7 +2,7 @@
 
 import Puppet.NativeTypes.Helpers
 import Puppet.Interpreter.Types
-import Control.Monad.Error
+import Control.Monad.Except
 import qualified Data.Text as T
 import Control.Lens
 
diff --git a/Puppet/OptionalTests.hs b/Puppet/OptionalTests.hs
--- a/Puppet/OptionalTests.hs
+++ b/Puppet/OptionalTests.hs
@@ -1,30 +1,65 @@
-{-# LANGUAGE LambdaCase      #-}
--- | The module works in IO and exits on failure.
--- It is meant to be use by a `Daemon`.
--- PS: if more flexibility is needed, we can `throwM`
--- on failure and let the caller choose what to do.
+{-# LANGUAGE LambdaCase #-}
+-- | The module works in IO and throws a 'PrettyError' exception at each failure.
+-- These exceptions can be caught (see the exceptions package).
 module Puppet.OptionalTests (testCatalog) where
 
 import           Control.Applicative
 import           Control.Lens
 import           Control.Monad                    (unless)
+import           Control.Monad.Catch
 import           Control.Monad.Trans              (liftIO)
 import           Control.Monad.Trans.Except
-import           Data.Foldable                    (asum, toList, elem, traverse_)
+import           Data.Foldable                    (asum, elem, toList,
+                                                   traverse_)
+import qualified Data.HashSet                     as HS
 import           Data.Maybe                       (mapMaybe)
 import           Data.Monoid                      ((<>))
 import qualified Data.Text                        as T
 import           Prelude                          hiding (all, elem)
+
 import           Puppet.Interpreter.PrettyPrinter ()
 import           Puppet.Interpreter.Types
-import           Puppet.PP                        hiding ((<$>))
-import           System.Exit                      (exitFailure)
+import           Puppet.Lens                      (_PString)
+import           Puppet.PP
+import           Puppet.Preferences
 import           System.Posix.Files
 
+
 -- | Entry point for all optional tests
-testCatalog :: FilePath -> FinalCatalog -> IO ()
-testCatalog = testFileSources
+testCatalog :: Preferences IO -> FinalCatalog -> IO ()
+testCatalog prefs c = testFileSources (prefs^.puppetPaths.baseDir) c >> testUsersGroups (prefs^.knownusers) (prefs^.knowngroups) c
 
+-- | Tests that all users and groups are defined
+testUsersGroups :: [T.Text] -> [T.Text] -> FinalCatalog -> IO ()
+testUsersGroups kusers kgroups c = do
+    let users = HS.fromList $ "" : map (view (rid . iname)) (getResourceFrom "user") ++ kusers
+        groups = HS.fromList $ "" : map (view (rid . iname)) (getResourceFrom "group") ++ kgroups
+        checkResource lu lg = mapM_ (checkResource' lu lg)
+        checkResource' lu lg res = do
+            let msg att name = align (vsep [ "Resource" <+> ttext (res^.rid.itype)
+                                             <+> ttext (res^.rid.iname) <+> showPos (res^.rpos._1)
+                                           , "reference the unknown" <+> string att <+> squotes (ttext name)])
+                               <> line
+            case lu of
+                Just lu' -> do
+                    let u = res ^. rattributes . lu' . _PString
+                    unless (HS.member u users) $ throwM $ PrettyError (msg "user" u)
+                Nothing -> pure ()
+            case lg of
+                Just lg' -> do
+                    let g = res ^. rattributes . lg' . _PString
+                    unless (HS.member g groups) $ throwM $ PrettyError (msg "group" g)
+                Nothing -> pure ()
+    do
+        checkResource (Just $ ix "owner") (Just $ ix "group") (getResourceFrom "file")
+        checkResource (Just $ ix "user")  (Just $ ix "group") (getResourceFrom "exec")
+        checkResource (Just $ ix "user")  Nothing             (getResourceFrom "cron")
+        checkResource (Just $ ix "user")  Nothing             (getResourceFrom "ssh_authorized_key")
+        checkResource (Just $ ix "user")  Nothing             (getResourceFrom "ssh_authorized_key_secure")
+        checkResource Nothing             (Just $ ix "gid")   (getResourceFrom "users")
+    where
+      getResourceFrom t = c ^.. traverse . filtered (\r -> r ^. rid . itype == t && r ^. rattributes . at "ensure" /= Just "absent")
+
 -- | Test source for every file resources in the catalog.
 testFileSources :: FilePath -> FinalCatalog -> IO ()
 testFileSources basedir c = do
@@ -45,9 +80,7 @@
         Left err -> go xs ((PrettyError $ "Could not find " <+> pretty filesource <> semi
                            <+> align (vsep [getError err, showPos (res^.rpos^._1)])):es)
     go [] [] = pure ()
-    go [] es = do
-      traverse_ (\e -> putDoc $ getError e <> line) es
-      exitFailure
+    go [] es = traverse_ throwM es
 
 testFile :: FilePath -> ExceptT PrettyError IO ()
 testFile fp = do
diff --git a/Puppet/PP.hs b/Puppet/PP.hs
--- a/Puppet/PP.hs
+++ b/Puppet/PP.hs
@@ -7,8 +7,8 @@
     , module Text.PrettyPrint.ANSI.Leijen
     ) where
 
-import Text.PrettyPrint.ANSI.Leijen hiding ((<>))
-import qualified Data.Text as T
+import qualified Data.Text                    as T
+import           Text.PrettyPrint.ANSI.Leijen hiding ((<$>), (<>))
 
 ttext :: T.Text -> Doc
 ttext = text . T.unpack
diff --git a/Puppet/Parser/PrettyPrinter.hs b/Puppet/Parser/PrettyPrinter.hs
--- a/Puppet/Parser/PrettyPrinter.hs
+++ b/Puppet/Parser/PrettyPrinter.hs
@@ -1,14 +1,16 @@
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 module Puppet.Parser.PrettyPrinter where
 
-import Puppet.PP
-import Puppet.Parser.Types
-import Puppet.Utils
-import qualified Data.Vector as V
-import qualified Data.Text as T
-import Data.Tuple.Strict (Pair ( (:!:) ))
-import qualified Data.Tuple.Strict as S
-import qualified Data.Maybe.Strict as S
+import qualified Data.Maybe.Strict            as S
+import qualified Data.Text                    as T
+import           Data.Tuple.Strict            (Pair ((:!:)))
+import qualified Data.Tuple.Strict            as S
+import qualified Data.Vector                  as V
+import           Puppet.Parser.Types
+import           Puppet.PP
+import           Puppet.Utils
+import           Text.PrettyPrint.ANSI.Leijen ((<$>))
+import           Prelude                      hiding ((<$>))
 
 capitalize :: T.Text -> Doc
 capitalize = dullyellow . text . T.unpack . capitalizeRT
diff --git a/Puppet/Plugins.hs b/Puppet/Plugins.hs
--- a/Puppet/Plugins.hs
+++ b/Puppet/Plugins.hs
@@ -38,7 +38,7 @@
 import qualified Data.Text.IO as T
 import qualified Data.Vector as V
 import Control.Concurrent
-import Control.Monad.Error
+import Control.Monad.Except
 import Control.Monad.Operational (singleton)
 import Data.Scientific
 
@@ -55,8 +55,7 @@
         push l (PUndef)                  = Lua.push l ("undefined" :: T.Text)
         push l (PNumber b)               = Lua.push l (fromRational (toRational b) :: Double)
 
-        peek l n = do
-            Lua.ltype l n >>= \case
+        peek l n = Lua.ltype l n >>= \case
                 Lua.TBOOLEAN -> fmap (fmap PBoolean) (Lua.peek l n)
                 Lua.TSTRING  -> fmap (fmap PString) (Lua.peek l n)
                 Lua.TNUMBER  -> fmap (fmap (PNumber . fromFloatDigits)) (Lua.peek l n :: IO (Maybe Double))
diff --git a/Puppet/Preferences.hs b/Puppet/Preferences.hs
--- a/Puppet/Preferences.hs
+++ b/Puppet/Preferences.hs
@@ -1,14 +1,28 @@
 {-# LANGUAGE FlexibleInstances      #-}
 {-# LANGUAGE FunctionalDependencies #-}
+{-# LANGUAGE LambdaCase             #-}
 {-# LANGUAGE MultiParamTypeClasses  #-}
 {-# LANGUAGE TemplateHaskell        #-}
 module Puppet.Preferences (
-    setupPreferences
+    dfPreferences
   , HasPreferences(..)
   , Preferences(Preferences)
+  , PuppetDirPaths
   , HasPuppetDirPaths(..)
 ) where
 
+import           Control.Applicative
+import           Control.Lens
+import           Control.Monad              (mzero)
+import           Data.Aeson
+import qualified Data.HashMap.Strict        as HM
+import qualified Data.HashSet               as HS
+import           Data.Maybe                 (fromMaybe)
+import           Data.Text                  (Text)
+import qualified Data.Text                  as T
+import           System.Posix               (fileExist)
+import           Prelude
+
 import           Puppet.Interpreter.Types
 import           Puppet.NativeTypes
 import           Puppet.NativeTypes.Helpers
@@ -17,49 +31,104 @@
 import           Puppet.Utils
 import           PuppetDB.Dummy
 
-import           Control.Lens
-import qualified Data.HashMap.Strict        as HM
-import qualified Data.HashSet               as HS
-import qualified Data.Text                  as T
-
 data PuppetDirPaths = PuppetDirPaths
     { _baseDir       :: FilePath -- ^ Puppet base working directory
     , _manifestPath  :: FilePath -- ^ The path to the manifests.
     , _modulesPath   :: FilePath -- ^ The path to the modules.
     , _templatesPath :: FilePath -- ^ The path to the template.
+    , _testPath      :: FilePath -- ^ The path to a tests folders to hold tests files such as the pdbfiles.
     }
 
 makeClassy ''PuppetDirPaths
 
 data Preferences m = Preferences
-    { _puppetPaths    :: PuppetDirPaths
-    , _prefPDB        :: PuppetDBAPI m
-    , _natTypes       :: Container NativeTypeMethods -- ^ The list of native types.
-    , _prefExtFuncs   :: Container ( [PValue] -> InterpreterMonad PValue )
-    , _hieraPath      :: Maybe FilePath
-    , _ignoredmodules :: HS.HashSet T.Text -- ^ The set of ignored modules
-    , _strictness     :: Strictness
-    , _extraTests     :: Bool
+    { _puppetPaths     :: PuppetDirPaths
+    , _prefPDB         :: PuppetDBAPI m
+    , _natTypes        :: Container NativeTypeMethods -- ^ The list of native types.
+    , _prefExtFuncs    :: Container ( [PValue] -> InterpreterMonad PValue )
+    , _hieraPath       :: Maybe FilePath
+    , _ignoredmodules  :: HS.HashSet Text
+    , _strictness      :: Strictness
+    , _extraTests      :: Bool
+    , _knownusers      :: [Text]
+    , _knowngroups     :: [Text]
+    , _externalmodules :: HS.HashSet Text
     }
 
+data Defaults = Defaults
+    { _dfKnownusers      :: Maybe [Text]
+    , _dfKnowngroups     :: Maybe [Text]
+    , _dfIgnoredmodules  :: Maybe [Text]
+    , _dfStrictness      :: Maybe Strictness
+    , _dfExtratests      :: Maybe Bool
+    , _dfExternalmodules :: Maybe [Text]
+    } deriving Show
+
+
 makeClassy ''Preferences
 
-genPreferences :: FilePath
+instance FromJSON Defaults where
+    parseJSON (Object v) = Defaults
+                           <$> v .:? "knownusers"
+                           <*> v .:? "knowngroups"
+                           <*> v .:? "ignoredmodules"
+                           <*> v .:? "strict"
+                           <*> v .:? "extratests"
+                           <*> v .:? "externalmodules"
+    parseJSON _ = mzero
+
+-- | generate default preferences
+dfPreferences :: FilePath
                -> IO (Preferences IO)
-genPreferences basedir = do
+dfPreferences basedir = do
     let manifestdir = basedir <> "/manifests"
         modulesdir  = basedir <> "/modules"
         templatedir = basedir <> "/templates"
+        testdir     = basedir <> "/tests"
     typenames <- fmap (map takeBaseName) (getFiles (T.pack modulesdir) "lib/puppet/type" ".rb")
+    defaults <- loadDefaults (testdir ++ "/defaults.yaml")
     let loadedTypes = HM.fromList (map defaulttype typenames)
-    return $ Preferences (PuppetDirPaths basedir manifestdir modulesdir templatedir) dummyPuppetDB (baseNativeTypes `HM.union` loadedTypes) (stdlibFunctions) (Just (basedir <> "/hiera.yaml")) mempty Strict True
+    return $ Preferences (PuppetDirPaths basedir manifestdir modulesdir templatedir testdir)
+                         dummyPuppetDB (baseNativeTypes `HM.union` loadedTypes)
+                         stdlibFunctions
+                         (Just (basedir <> "/hiera.yaml"))
+                         (getIgnoredmodules defaults)
+                         (getStrictness defaults)
+                         (getExtraTests defaults)
+                         (getKnownusers defaults)
+                         (getKnowngroups defaults)
+                         (getExternalmodules defaults)
 
-{-| Use lens with the set operator and composition to set external/custom params.
 
-Ex.: @ setupPreferences workingDir ((hieraPath.~mypath) . (prefPDB.~pdbapi)) @
--}
-setupPreferences :: FilePath -- ^ The base working directory
-                 -> (Preferences IO -> Preferences IO) -- ^ Preference setting
-                 -> IO (Preferences IO)
-setupPreferences basedir k =
-  fmap k (genPreferences basedir)
+loadDefaults :: FilePath -> IO (Maybe Defaults)
+loadDefaults fp = do
+  p <- fileExist fp
+  if p then loadYamlFile fp else return Nothing
+
+-- Utilities for getting default values from the yaml file
+-- It provides (the same) static defaults (see the 'Nothing' case) when
+--     no default yaml file or
+--     not key/value for the option has been provided
+getKnownusers :: Maybe Defaults -> [Text]
+getKnownusers (Just df) = fromMaybe (getKnownusers Nothing) (_dfKnownusers df)
+getKnownusers Nothing = ["mysql", "vagrant","nginx", "nagios", "postgres", "puppet", "root", "syslog", "www-data"]
+
+getKnowngroups :: Maybe Defaults -> [Text]
+getKnowngroups (Just df) = fromMaybe (getKnowngroups Nothing) (_dfKnowngroups df)
+getKnowngroups Nothing = ["adm", "syslog", "mysql", "nagios","postgres", "puppet", "root", "www-data"]
+
+getStrictness :: Maybe Defaults -> Strictness
+getStrictness (Just df) = fromMaybe (getStrictness Nothing) (_dfStrictness df)
+getStrictness Nothing = Permissive
+
+getIgnoredmodules :: Maybe Defaults -> HS.HashSet Text
+getIgnoredmodules (Just df) = maybe (getIgnoredmodules Nothing) HS.fromList (_dfIgnoredmodules df)
+getIgnoredmodules Nothing = mempty
+
+getExtraTests :: Maybe Defaults -> Bool
+getExtraTests (Just df) = fromMaybe (getExtraTests Nothing) (_dfExtratests df)
+getExtraTests Nothing = True
+
+getExternalmodules :: Maybe Defaults -> HS.HashSet Text
+getExternalmodules (Just df) = maybe (getExternalmodules Nothing) HS.fromList (_dfExternalmodules df)
+getExternalmodules Nothing = mempty
diff --git a/Puppet/Stdlib.hs b/Puppet/Stdlib.hs
--- a/Puppet/Stdlib.hs
+++ b/Puppet/Stdlib.hs
@@ -1,24 +1,25 @@
 {-# LANGUAGE LambdaCase #-}
 module Puppet.Stdlib (stdlibFunctions) where
 
-import Puppet.PP
-import Puppet.Interpreter.Resolve
-import Puppet.Interpreter.Types
+import           Puppet.Interpreter.Resolve
+import           Puppet.Interpreter.Types
+import           Puppet.PP
 
-import Control.Lens
-import Data.Aeson.Lens
-import Puppet.Lens
-import Data.Char
-import Data.Monoid
-import Control.Monad
-import Data.Vector.Lens (toVectorOf)
-import Text.Regex.PCRE.ByteString.Utils
-import Data.Traversable (for)
-import qualified Data.Vector as V
-import qualified Data.HashMap.Strict as HM
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
-import qualified Data.ByteString.Base16 as B16
+import           Control.Lens
+import           Control.Monad
+import           Data.Aeson.Lens
+import qualified Data.ByteString.Base16           as B16
+import           Data.Char
+import qualified Data.HashMap.Strict              as HM
+import           Data.Monoid
+import qualified Data.Text                        as T
+import qualified Data.Text.Encoding               as T
+import           Data.Traversable                 (for)
+import qualified Data.Vector                      as V
+import           Data.Vector.Lens                 (toVectorOf)
+import           Puppet.Lens
+import qualified Text.PrettyPrint.ANSI.Leijen     as PP
+import           Text.Regex.PCRE.ByteString.Utils
 
 -- | Contains the implementation of the StdLib functions.
 stdlibFunctions :: Container ( [PValue] -> InterpreterMonad PValue )
@@ -324,7 +325,7 @@
     rest <- mapM (resolvePValueString >=> compileRE >=> flip matchRE rstr) (V.toList v)
     if or rest
         then return PUndef
-        else throwPosError (pretty msg <$> "Source string:" <+> pretty str <> comma <+> "regexps:" <+> pretty (V.toList v))
+        else throwPosError (pretty msg PP.<$> "Source string:" <+> pretty str <> comma <+> "regexps:" <+> pretty (V.toList v))
 validateRe [_, r, _] = throwPosError ("validate_re(): expected a regexp or an array of regexps, but not" <+> pretty r)
 validateRe _ = throwPosError "validate_re(): wrong number of arguments (#{args.length}; must be 2 or 3)"
 
diff --git a/Puppet/Utils.hs b/Puppet/Utils.hs
--- a/Puppet/Utils.hs
+++ b/Puppet/Utils.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE LambdaCase      #-}
 -- | Those are utility functions, most of them being pretty much self
 -- explanatory.
 module Puppet.Utils
@@ -8,6 +9,7 @@
     , takeDirectory
     , strictifyEither
     , nameThread
+    , loadYamlFile
     , scientific2text
     ) where
 
@@ -22,6 +24,7 @@
 import Data.Scientific
 import Control.Lens
 import Data.Aeson.Lens
+import qualified Data.Yaml as Y
 
 scientific2text :: Scientific -> T.Text
 scientific2text n = T.pack $ case n ^? _Integer of
@@ -96,3 +99,8 @@
             where
                 (a,b) = T.break (=='/') $ T.reverse y
 
+-- | Read a yaml file and throw a runtime error if the parsing fails
+loadYamlFile :: Y.FromJSON a =>  FilePath -> IO a
+loadYamlFile fp = Y.decodeFileEither fp >>= \case
+    Left rr -> error ("Error when parsing " ++ fp ++ ": " ++ show rr)
+    Right x -> return x
diff --git a/PuppetDB/Common.hs b/PuppetDB/Common.hs
--- a/PuppetDB/Common.hs
+++ b/PuppetDB/Common.hs
@@ -13,6 +13,7 @@
 import System.Environment
 import qualified Data.Either.Strict as S
 import Data.Vector.Lens
+import Servant.Common.BaseUrl
 
 -- | The supported PuppetDB implementations.
 data PDBType = PDBRemote -- ^ Your standard PuppetDB, queried through the HTTP interface.
@@ -39,7 +40,8 @@
 -- | Given a 'PDBType', will try return a sane default implementation.
 getDefaultDB :: PDBType -> IO (S.Either PrettyError (PuppetDBAPI IO))
 getDefaultDB PDBDummy  = return (S.Right dummyPuppetDB)
-getDefaultDB PDBRemote = pdbConnect "http://localhost:8080"
+getDefaultDB PDBRemote = let Right url = parseBaseUrl "http://localhost:8080"
+                         in  pdbConnect url
 getDefaultDB PDBTest   = lookupEnv "HOME" >>= \case
                                 Just h -> loadTestDB (h ++ "/.testdb")
                                 Nothing -> fmap S.Right initTestDB
diff --git a/PuppetDB/Dummy.hs b/PuppetDB/Dummy.hs
--- a/PuppetDB/Dummy.hs
+++ b/PuppetDB/Dummy.hs
@@ -3,17 +3,17 @@
 module PuppetDB.Dummy where
 
 import Puppet.Interpreter.Types
-import qualified Data.Either.Strict as S
+import Control.Monad.Trans.Either
 
 dummyPuppetDB :: Monad m => PuppetDBAPI m
 dummyPuppetDB = PuppetDBAPI
                     (return "dummy")
-                    (const (return (S.Right () )))
-                    (const (return (S.Right () )))
-                    (const (return (S.Right () )))
-                    (const (return (S.Left "not implemented")))
-                    (const (return (S.Right [] )))
-                    (const (return (S.Right [] )))
-                    (return (S.Left "not implemented"))
-                    (\_ _ -> return (S.Right [] ))
+                    (const (return ()))
+                    (const (return ()))
+                    (const (return ()))
+                    (const (left "not implemented"))
+                    (const (return [] ))
+                    (const (return [] ))
+                    (left "not implemented")
+                    (\_ _ -> return [] )
 
diff --git a/PuppetDB/Remote.hs b/PuppetDB/Remote.hs
--- a/PuppetDB/Remote.hs
+++ b/PuppetDB/Remote.hs
@@ -1,60 +1,57 @@
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE PolyKinds #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE TypeOperators #-}
+{-# LANGUAGE OverloadedStrings #-}
 module PuppetDB.Remote (pdbConnect) where
 
-import Puppet.Utils
 import Puppet.PP
 
 import Puppet.Interpreter.Types
-
-import Network.HTTP.Conduit
-import qualified Network.HTTP.Types as W
-import qualified Data.ByteString.Lazy.Char8 as L
-import qualified Data.ByteString.Char8 as BC
-import Data.Aeson
-import qualified Codec.Text.IConv as IConv
-import qualified Control.Exception as X
-import Control.Monad.Error
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
+import Data.Text (Text)
+import Control.Monad.Trans.Either
 import qualified Data.Either.Strict as S
+import Servant.API
+import Servant.Client
+import Servant.Common.Text
+import Data.Aeson
+import Data.Proxy
 
-runRequest :: (Monad m, MonadIO m, FromJSON b, MonadError PrettyError m) => Request -> m b
-runRequest req = do
-    let doRequest = withManager (fmap responseBody . httpLbs req) :: IO L.ByteString
-        eHandler :: X.SomeException -> IO (Either PrettyError L.ByteString)
-        eHandler e = return $ Left $ PrettyError $ string (show e) <> ", with queryString " <+> string (BC.unpack (queryString req))
-    liftIO (fmap Right doRequest `X.catch` eHandler) >>= \case
-        Right o -> do
-            let utf8 = IConv.convert "LATIN1" "UTF-8" o
-            case decode' utf8 of
-                Just x                   -> return x
-                Nothing                  -> throwError (PrettyError ("Json decoding has failed " <> string (show utf8)))
-        Left err -> throwError err
+type PDBAPIv3 =    "nodes"     :> QueryParam "query" (Query NodeField)     :> Get [PNodeInfo]
+              :<|> "nodes"     :> Capture "resourcename" Text :> "resources" :> QueryParam "query" (Query ResourceField) :> Get [Resource]
+              :<|> "facts"     :> QueryParam "query" (Query FactField)     :> Get [PFactInfo]
+              :<|> "resources" :> QueryParam "query" (Query ResourceField) :> Get [Resource]
 
-pdbRequest :: (FromJSON a, ToJSON b) => T.Text -> T.Text -> b -> IO (S.Either PrettyError a)
-pdbRequest url querytype query = fmap strictifyEither $ runErrorT $ do
-    let jsonquery = L.toStrict (encode query)
-        q = case toJSON query of
-                Null -> ""
-                _ -> T.decodeUtf8 $ "?" <> W.renderSimpleQuery False [("query", jsonquery)]
-    let fullurl = url <> "/v3/" <> querytype <> q
-    initReq <- case parseUrl (T.unpack fullurl) of
-            Right r -> return (r :: Request)
-            Left rr -> throwError (PrettyError ("Something failed when parsing the PuppetDB URL" <+> string (show rr)))
-    let req = initReq { requestHeaders = [("Accept", "application/json")] }
-    runRequest req
+type PDBAPI = "v3" :> PDBAPIv3
 
+spdbAPI :: Proxy PDBAPI
+spdbAPI = Proxy
+
+sgetNodes :: Maybe (Query NodeField) -> BaseUrl -> EitherT String IO [PNodeInfo]
+sgetNodeResources :: Text -> Maybe (Query ResourceField) -> BaseUrl -> EitherT String IO [Resource]
+sgetFacts :: Maybe (Query FactField) -> BaseUrl -> EitherT String IO [PFactInfo]
+sgetResources :: Maybe (Query ResourceField) -> BaseUrl -> EitherT String IO [Resource]
+(     sgetNodes
+ :<|> sgetNodeResources
+ :<|> sgetFacts
+ :<|> sgetResources
+ ) = client spdbAPI
+
 -- | Given an URL (ie. @http://localhost:8080@), will return an incomplete 'PuppetDBAPI'.
-pdbConnect :: T.Text -> IO (S.Either PrettyError (PuppetDBAPI IO))
+pdbConnect :: BaseUrl -> IO (S.Either PrettyError (PuppetDBAPI IO))
 pdbConnect url = return $ S.Right $ PuppetDBAPI
-    (return (ttext url))
-    (const (return (S.Left "operation not supported")))
-    (const (return (S.Left "operation not supported")))
-    (const (return (S.Left "operation not supported")))
-    (pdbRequest url "facts")
-    (pdbRequest url "resources")
-    (pdbRequest url "nodes")
-    (return (S.Left "operation not supported"))
-    (\ndename -> pdbRequest url ("nodes/" <> ndename <> "/resources"))
+    (return (string $ show url))
+    (const (left "operation not supported"))
+    (const (left "operation not supported"))
+    (const (left "operation not supported"))
+    (q1 sgetFacts)
+    (q1 sgetResources)
+    (q1 sgetNodes)
+    (left "operation not supported")
+    (\ndename q -> prettyError $ sgetNodeResources ndename (Just q) url)
+    where
+        prettyError :: EitherT String IO b -> EitherT PrettyError IO b
+        prettyError = bimapEitherT (PrettyError . string) id
+        q1 :: (ToText a, FromJSON b) => (Maybe a -> BaseUrl -> EitherT String IO b) -> a -> EitherT PrettyError IO b
+        q1 f x = prettyError $ f (Just x) url
 
diff --git a/PuppetDB/TestDB.hs b/PuppetDB/TestDB.hs
--- a/PuppetDB/TestDB.hs
+++ b/PuppetDB/TestDB.hs
@@ -3,29 +3,37 @@
 {-# LANGUAGE LambdaCase             #-}
 {-# LANGUAGE MultiParamTypeClasses  #-}
 {-# LANGUAGE TemplateHaskell        #-}
+
 -- | A stub implementation of PuppetDB, backed by a YAML file.
-module PuppetDB.TestDB (loadTestDB,initTestDB) where
+module PuppetDB.TestDB
+       ( loadTestDB
+       , initTestDB
+) where
 
 import           Control.Applicative
 import           Control.Concurrent.STM
 import           Control.Exception
 import           Control.Lens
+import           Control.Monad.IO.Class
+import           Control.Monad.Trans.Either
 import           Data.Aeson.Lens
 import           Data.CaseInsensitive
 import qualified Data.Either.Strict       as S
 import qualified Data.HashMap.Strict      as HM
 import qualified Data.HashSet             as HS
 import           Data.List                (foldl')
+import qualified Data.Maybe.Strict        as S
 import           Data.Monoid
 import qualified Data.Text                as T
 import qualified Data.Vector              as V
 import           Data.Yaml
 import           Text.Parsec.Pos
+import           Prelude
 
 import           Puppet.Interpreter.Types
 import           Puppet.Lens
 import           Puppet.Parser.Types
-import           Puppet.PP                hiding ((<$>))
+import           Puppet.PP
 
 data DBContent = DBContent
     { _dbcontentResources   :: Container WireCatalog
@@ -85,7 +93,7 @@
                | ESet (HS.HashSet T.Text)
                | ENil
 
-resolveQuery :: (a -> b -> Extracted) -> Query a -> (b -> Bool)
+resolveQuery :: (a -> b -> Extracted) -> Query a -> b -> Bool
 resolveQuery _ QEmpty = const True
 resolveQuery f (QEqual a t) = \v -> case f a v of
                                         EText tt -> mk tt == mk t
@@ -107,29 +115,26 @@
         Nothing -> return "TestDB"
         Just v -> return ("TestDB" <+> string v)
 
-ncompare :: (Integer -> Integer -> Bool) ->  (a -> b -> Extracted) -> a -> Integer -> (b -> Bool)
+ncompare :: (Integer -> Integer -> Bool) ->  (a -> b -> Extracted) -> a -> Integer -> b -> Bool
 ncompare operation f a i v = case f a v of
                                  EText tt -> case PString tt ^? _Integer of
                                                  Just ii -> operation i ii
                                                  _ -> False
                                  _ -> False
 
-mustWork :: IO () -> IO (S.Either PrettyError ())
-mustWork a = a >> return (S.Right ())
-
-replCat :: DB -> WireCatalog -> IO (S.Either PrettyError ())
-replCat db wc = mustWork $ atomically $ modifyTVar db (resources . at (wc ^. nodename) ?~ wc)
+replCat :: DB -> WireCatalog -> EitherT PrettyError IO ()
+replCat db wc = liftIO $ atomically $ modifyTVar db (resources . at (wc ^. nodename) ?~ wc)
 
-replFacts :: DB -> [(Nodename, Facts)] -> IO (S.Either PrettyError ())
-replFacts db lst = mustWork $ atomically $ modifyTVar db $
+replFacts :: DB -> [(Nodename, Facts)] -> EitherT PrettyError IO ()
+replFacts db lst = liftIO $ atomically $ modifyTVar db $
                     facts %~ (\r -> foldl' (\curr (n,f) -> curr & at n ?~ f) r lst)
 
-deactivate :: DB -> Nodename -> IO (S.Either PrettyError ())
-deactivate db n = mustWork $ atomically $ modifyTVar db $
+deactivate :: DB -> Nodename -> EitherT PrettyError IO ()
+deactivate db n = liftIO $ atomically $ modifyTVar db $
                     (resources . at n .~ Nothing) . (facts . at n .~ Nothing)
 
-getFcts :: DB -> Query FactField -> IO (S.Either PrettyError [PFactInfo])
-getFcts db f = fmap (S.Right . filter (resolveQuery factQuery f) . toFactInfo) (readTVarIO db)
+getFcts :: DB -> Query FactField -> EitherT PrettyError IO [PFactInfo]
+getFcts db f = fmap (filter (resolveQuery factQuery f) . toFactInfo) (liftIO $ readTVarIO db)
     where
         toFactInfo :: DBContent -> [PFactInfo]
         toFactInfo = concatMap gf .  HM.toList . _dbcontentFacts
@@ -159,25 +164,33 @@
 resourceQuery RFile r = r ^. rpos . _1 . to sourceName . to T.pack . to EText
 resourceQuery RLine r = r ^. rpos . _1 . to sourceLine . to show . to T.pack . to EText
 
-getRes :: DB -> Query ResourceField -> IO (S.Either PrettyError [Resource])
-getRes db f = fmap (S.Right . filter (resolveQuery resourceQuery f) . toResources) (readTVarIO db)
+getRes :: DB -> Query ResourceField -> EitherT PrettyError IO [Resource]
+getRes db f = fmap (filter (resolveQuery resourceQuery f) . toResources) (liftIO $ readTVarIO db)
     where
         toResources :: DBContent -> [Resource]
         toResources = concatMap (V.toList . view wResources) .  HM.elems . view resources
 
-getResNode :: DB -> Nodename -> Query ResourceField -> IO (S.Either PrettyError [Resource])
+getResNode :: DB -> Nodename -> Query ResourceField -> EitherT PrettyError IO [Resource]
 getResNode db nn f = do
-    c <- readTVarIO db
-    return $ case c ^. resources . at nn of
-                 Just cnt -> S.Right $ filter (resolveQuery resourceQuery f) $ V.toList $ cnt ^. wResources
-                 Nothing -> S.Left "Unknown node"
+    c <- liftIO $ readTVarIO db
+    case c ^. resources . at nn of
+        Just cnt -> return $ filter (resolveQuery resourceQuery f) $ V.toList $ cnt ^. wResources
+        Nothing -> left "Unknown node"
 
-commit :: DB -> IO (S.Either PrettyError ())
+commit :: DB -> EitherT PrettyError IO ()
 commit db = do
-    dbc <- atomically $ readTVar db
+    dbc <- liftIO $ atomically $ readTVar db
     case dbc ^. backingFile of
-        Nothing -> return (S.Left "No backing file defined")
-        Just bf -> fmap S.Right (encodeFile bf dbc) `catches` [ ]
+        Nothing -> left "No backing file defined"
+        Just bf -> liftIO (encodeFile bf dbc `catches` [ ])
 
-getNds :: DB -> Query NodeField -> IO (S.Either PrettyError [PNodeInfo])
-getNds _ _ = return (S.Left "getNds not implemented")
+getNds :: DB -> Query NodeField -> EitherT PrettyError IO [PNodeInfo]
+getNds db QEmpty = fmap toNodeInfo (liftIO $ readTVarIO db)
+    where
+        toNodeInfo :: DBContent -> [PNodeInfo]
+        toNodeInfo = fmap g . HM.keys . _dbcontentFacts
+             where
+                g :: Nodename -> PNodeInfo
+                g = \n -> PNodeInfo n False S.Nothing S.Nothing S.Nothing
+
+getNds _ _ = left "getNds with query not implemented"
diff --git a/README.adoc b/README.adoc
--- a/README.adoc
+++ b/README.adoc
@@ -15,7 +15,7 @@
 cabal install -j -p
 ```
 
-There are also http://lpuppet.banquise.net/download/[binary packages available] and https://registry.hub.docker.com/u/pierrer/puppetresources[a 400M docker images].
+There are also http://lpuppet.banquise.net/download/[binary packages available] and https://registry.hub.docker.com/u/pierrer/puppetresources[a docker images].
 
 == Puppetresources
 
@@ -64,7 +64,7 @@
 
 `--loglevel` or `-v`::
 
-Possible values are : DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY.
+Possible values are : DEBUG, INFO, NOTICE, WARNING, ERROR
 
 `--pdburl`::
 
@@ -78,7 +78,7 @@
 
 Expects the path to the `hiera.yaml` file.
 
-`--ignoremodules`::
+`--ignoredmodules`::
 
 Expects a list of comma-separated modules. The interpreter will not try to evaluate the defined types and classes from this module. This is useful for using modules that use bad
 practices forbidden by `puppetresources`.
@@ -116,6 +116,12 @@
 `--parse`::
 
 Enable `parse mode`. Specify the puppet file to be parsed. Variables are not resolved. No interpretation.
+
+=== Settings defaults using a yaml file
+
+Defaults for some of these options can be set using a `/yourworkingdirectory/tests/defaults.yaml` file. For instance `OptionalTests` is checking that all users and groups are known. Because some of these users and groups might be defined outside puppet, a list of known ones is used internally. This can be overridden in that file using the key `knownusers` and `knowngroups`.
+
+Please look at https://github.com/bartavelle/language-puppet/blob/master/tests/defaults.yaml[the template file] for a list of possible defaults.
 
 == pdbQuery
 
diff --git a/language-puppet.cabal b/language-puppet.cabal
--- a/language-puppet.cabal
+++ b/language-puppet.cabal
@@ -2,7 +2,7 @@
 -- documentation, see http://haskell.org/cabal/users-guide/
 
 name:                language-puppet
-version:             1.1.0
+version:             1.1.1
 synopsis:            Tools to parse and evaluate the Puppet DSL.
 description:         This is a set of tools that is supposed to fill all your Puppet needs : syntax checks, catalog compilation, PuppetDB queries, simulationg of complex interactions between nodes, Puppet master replacement, and more !
 homepage:            http://lpuppet.banquise.net/
@@ -18,6 +18,7 @@
 extra-source-files:
     CHANGELOG.markdown
     README.adoc
+    HLint.hs
 
 Data-Files:
   ruby/hrubyerb.rb
@@ -54,6 +55,7 @@
                        , Puppet.Stats
                        , Puppet.Stdlib
                        , Puppet.OptionalTests
+                       , Puppet.Utils
                        , SafeProcess
   other-modules:         Erb.Compute
                        , Paths_language_puppet
@@ -66,16 +68,16 @@
                        , Puppet.NativeTypes.Group
                        , Puppet.NativeTypes.Host
                        , Puppet.NativeTypes.Mount
+                       , Puppet.NativeTypes.Notify
                        , Puppet.NativeTypes.Package
                        , Puppet.NativeTypes.SshSecure
                        , Puppet.NativeTypes.User
                        , Puppet.NativeTypes.ZoneRecord
                        , Puppet.Plugins
-                       , Puppet.Utils
   extensions:          OverloadedStrings, BangPatterns
   ghc-options:         -Wall -funbox-strict-fields
   ghc-prof-options:    -auto-all -caf-all
-  build-depends:       base >=4.6 && < 4.8
+  build-depends:       base >=4.6 && < 4.9
                         , aeson                >= 0.7     && < 0.9
                         , ansi-wl-pprint       == 0.6.*
                         , attoparsec           >= 0.11    && < 0.13
@@ -85,31 +87,32 @@
                         , containers           == 0.5.*
                         , cryptohash           >= 0.10    && < 0.12
                         , directory            == 1.2.*
-                        , filecache            >= 0.2.5   && < 0.3
+                        , either               == 4.3.*
+                        , exceptions           >= 0.8     && < 0.9
+                        , filecache            >= 0.2.8   && < 0.3
                         , hashable             == 1.2.*
-                        , hruby                >= 0.2.5 && <0.3
+                        , hruby                >= 0.3 && <0.4
                         , hslogger             == 1.2.*
                         , hslua                >= 0.3.10  && < 0.4
-                        , http-conduit         >= 2.1     && < 2.2
-                        , http-types           == 0.8.*
-                        , iconv                == 0.4.*
-                        , lens                 >= 4.6     && < 4.9
+                        , lens                 >= 4.9     && < 5
                         , lens-aeson           >= 1.0
                         , luautils             >= 0.1.3   && < 0.1.4
-                        , mtl                  == 2.1.*
-                        , operational
+                        , mtl                  >= 2.2     && < 2.3
+                        , operational          >= 0.2.3   && < 0.3
                         , parsec               == 3.1.*
                         , parsers              >= 0.11    && < 0.13
                         , pcre-utils           >= 0.1.4   && < 0.2
                         , process              >= 1.1     && < 1.3
                         , regex-pcre-builtin   >= 0.94.4
                         , scientific           >= 0.2   && < 0.4
+                        , servant              == 0.2.*
+                        , servant-client       == 0.2.*
                         , split                == 0.2.*
                         , stm                  == 2.4.*
                         , strict-base-types    >= 0.2.2
                         , text                 >= 0.11
-                        , time                 == 1.4.*
-                        , transformers-compat  == 0.4.*
+                        , time                 >= 1.4  && < 2
+                        , transformers         == 0.4.*
                         , unix                 >= 2.6     && < 2.8
                         , unordered-containers == 0.2.*
                         , vector               == 0.10.*
@@ -162,7 +165,7 @@
   extensions:          BangPatterns, OverloadedStrings
   ghc-options:         -Wall -rtsopts -threaded -with-rtsopts "-A2M" -eventlog
   ghc-prof-options:    -auto-all -caf-all -fprof-auto
-  build-depends:       language-puppet,base,text,parsec,vector,ansi-wl-pprint,bytestring,mtl,hslogger,Diff,unordered-containers,strict-base-types,optparse-applicative >=0.11,regex-pcre-builtin,lens,aeson,yaml,parallel-io,containers,Glob,hspec >= 1.9
+  build-depends:       language-puppet,base,text,parsec,vector,ansi-wl-pprint,bytestring,mtl,hslogger,Diff,unordered-containers,strict-base-types,optparse-applicative >=0.11,regex-pcre-builtin,lens,aeson,yaml,parallel-io,containers,Glob,hspec >= 1.9, either, servant-client
   main-is:             PuppetResources.hs
 
 executable pdbquery
@@ -170,5 +173,5 @@
   extensions:          BangPatterns, OverloadedStrings
   ghc-options:         -Wall -rtsopts -threaded
   ghc-prof-options:    -auto-all -caf-all -fprof-auto
-  build-depends:       language-puppet,base,optparse-applicative >= 0.11,text,yaml,bytestring,strict-base-types,lens,unordered-containers,vector
+  build-depends:       language-puppet,base,optparse-applicative >= 0.11,text,yaml,bytestring,strict-base-types,lens,unordered-containers,vector,either,servant-client
   main-is:             pdbQuery.hs
diff --git a/progs/PuppetResources.hs b/progs/PuppetResources.hs
--- a/progs/PuppetResources.hs
+++ b/progs/PuppetResources.hs
@@ -1,10 +1,12 @@
-{-# LANGUAGE LambdaCase     #-}
-{-# LANGUAGE NamedFieldPuns #-}
+{-# LANGUAGE LambdaCase      #-}
+{-# LANGUAGE NamedFieldPuns  #-}
+{-# LANGUAGE RecordWildCards #-}
 module Main where
 
 import           Control.Concurrent.ParallelIO    (parallel)
 import           Control.Lens
 import           Control.Monad
+import           Control.Monad.Trans.Either
 import           Data.Aeson                       (encode)
 import qualified Data.ByteString.Lazy.Char8       as BSL
 import           Data.Either                      (partitionEithers)
@@ -13,25 +15,26 @@
 import qualified Data.HashMap.Strict              as HM
 import qualified Data.HashSet                     as HS
 import           Data.List                        (isInfixOf)
-import           Data.Maybe                       (isNothing, mapMaybe)
+import           Data.Maybe                       (fromMaybe, isNothing, mapMaybe)
 import           Data.Monoid                      hiding (First)
 import qualified Data.Set                         as Set
+import           Data.String                      (fromString)
 import qualified Data.Text                        as T
 import qualified Data.Text.IO                     as T
 import           Data.Text.Strict.Lens
 import           Data.Tuple                       (swap)
 import qualified Data.Vector                      as V
-import           Data.Yaml                        (decodeFileEither)
 import           Options.Applicative
+import           Servant.Common.BaseUrl
 import           System.Exit                      (exitFailure, exitSuccess)
 import qualified System.FilePath.Glob             as G
 import           System.IO
 import qualified System.Log.Logger                as LOG
 import qualified Text.Parsec                      as P
 import           Text.Regex.PCRE.String
+import           Prelude
 
 import           Facter
-
 import           Puppet.Daemon
 import           Puppet.Interpreter.PrettyPrinter ()
 import           Puppet.Interpreter.Types
@@ -39,9 +42,10 @@
 import           Puppet.Parser
 import           Puppet.Parser.PrettyPrinter      (ppStatements)
 import           Puppet.Parser.Types
-import           Puppet.PP                        hiding ((<$>))
+import           Puppet.PP
 import           Puppet.Preferences
 import           Puppet.Stats
+import           Puppet.Utils
 import           PuppetDB.Common
 import           PuppetDB.Dummy
 import           PuppetDB.Remote
@@ -57,34 +61,31 @@
                     in [(MultNodes os, "")]
 
 data Options = Options
-    { _pdb          :: Maybe String
-    , _showjson     :: Bool
-    , _showContent  :: Bool
-    , _resourceType :: Maybe T.Text
-    , _resourceName :: Maybe T.Text
-    , _puppetdir    :: Maybe FilePath
-    , _nodename     :: Maybe Nodename
-    , _multnodes    :: Maybe MultNodes
-    , _deadcode     :: Bool
-    , _pdbfile      :: Maybe FilePath
-    , _loglevel     :: LOG.Priority
-    , _hieraFile    :: Maybe FilePath
-    , _factsOverr   :: Maybe FilePath
-    , _factsDefault :: Maybe FilePath
-    , _commitDB     :: Bool
-    , _checkExport  :: Bool
-    , _ignoredMods  :: HS.HashSet T.Text
-    , _parse        :: Maybe FilePath
-    , _strictMode   :: Strictness
-    , _noExtraTests :: Bool
+    { _optShowjson     :: Bool
+    , _optShowContent  :: Bool
+    , _optResourceType :: Maybe T.Text
+    , _optResourceName :: Maybe T.Text
+    , _optPuppetdir    :: Maybe FilePath
+    , _optNodename     :: Maybe Nodename
+    , _optMultnodes    :: Maybe MultNodes
+    , _optDeadcode     :: Bool
+    , _optPdburl       :: Maybe String
+    , _optPdbfile      :: Maybe FilePath
+    , _optLoglevel     :: LOG.Priority
+    , _optHieraFile    :: Maybe FilePath
+    , _optFactsOverr   :: Maybe FilePath
+    , _optFactsDefault :: Maybe FilePath
+    , _optCommitDB     :: Bool
+    , _optCheckExport  :: Bool
+    , _optIgnoredMods  :: Maybe (HS.HashSet T.Text)
+    , _optParse        :: Maybe FilePath
+    , _optStrictMode   :: Bool
+    , _optNoExtraTests :: Bool
     } deriving (Show)
 
 options :: Parser Options
 options = Options
-    <$> optional (strOption
-       (  long "pdburl"
-       <> help "URL of the puppetdb (ie. http://localhost:8080/)."))
-   <*> switch
+    <$> switch
        (  long "JSON"
        <> short 'j'
        <> help "Shows the output as a JSON document (useful for full catalog views)")
@@ -116,12 +117,15 @@
        (  long "deadcode"
        <> help "Show deadcode when the --all option is used")
    <*> optional (strOption
+       (  long "pdburl"
+       <> help "URL of the puppetdb (ie. http://localhost:8080/)."))
+   <*> optional (strOption
        (  long "pdbfile"
        <> help "Path to the testing PuppetDB file."))
    <*> option auto
        (  long "loglevel"
        <> short 'v'
-       <> help "Values are : DEBUG, INFO, NOTICE, WARNING, ERROR, CRITICAL, ALERT, EMERGENCY"
+       <> help "Values are : DEBUG, INFO, NOTICE, WARNING, ERROR"
        <> value LOG.WARNING)
    <*> optional (strOption
        (  long "hiera"
@@ -139,15 +143,14 @@
    <*> switch
        (  long "checkExported"
        <> help "Save exported resources in the puppetDB")
-   <*> (HS.fromList . T.splitOn "," . T.pack <$>
+   <*> optional (HS.fromList . T.splitOn "," . T.pack <$>
        strOption
-       (  long "ignoremodules"
-       <> help "Specify a comma-separated list of modules to ignore"
-       <> value ""))
+       (  long "ignoredmodules"
+       <> help "Specify a comma-separated list of modules to ignore"))
    <*> optional (strOption
        (  long "parse"
        <> help "Parse a single file"))
-   <*> flag Permissive Strict
+   <*> switch
        (  long "strict"
        <> help "Strict mode diverges from vanillia Puppet and enforces good practices")
    <*> flag False True
@@ -169,26 +172,34 @@
 Be aware it uses the locale computer to generate a set of `facts` (this is a bit hackish).
 These facts can be overriden at the command line (see 'Options').
 -}
-initializedaemonWithPuppet :: FilePath -> Options
-                              -> IO (QueryFunc, PuppetDBAPI IO, MStats, MStats, MStats)
-initializedaemonWithPuppet workingdir (Options {_pdb, _pdbfile, _loglevel, _hieraFile, _factsOverr, _factsDefault, _ignoredMods, _strictMode, _noExtraTests}) = do
-    pdbapi <- case (_pdb, _pdbfile) of
+initializedaemonWithPuppet :: FilePath
+                           -> Options
+                           -> IO (QueryFunc, PuppetDBAPI IO, MStats, MStats, MStats)
+initializedaemonWithPuppet workingdir (Options {..}) = do
+    LOG.updateGlobalLogger "Puppet.Daemon" (LOG.setLevel _optLoglevel)
+    LOG.updateGlobalLogger "Hiera.Server" (LOG.setLevel _optLoglevel)
+    pdbapi <- case (_optPdburl, _optPdbfile) of
                   (Nothing, Nothing) -> return dummyPuppetDB
                   (Just _, Just _)   -> error "You must choose between a testing PuppetDB and a remote one"
-                  (Just url, _)      -> pdbConnect (T.pack url) >>= checkError "Error when connecting to the remote PuppetDB"
+                  (Just url, _)      -> checkError "Error when parsing url" (parseBaseUrl url & either (S.Left . fromString) S.Right)
+                                            >>= pdbConnect
+                                            >>= checkError "Error when connecting to the remote PuppetDB"
                   (_, Just file)     -> loadTestDB file >>= checkError "Error when initializing the PuppetDB API"
-    !factsOverrides <- case (_factsOverr, _factsDefault) of
+    !factsOverrides <- case (_optFactsOverr, _optFactsDefault) of
                            (Just _, Just _) -> error "You can't use --facts-override and --facts-defaults at the same time"
-                           (Just p, Nothing) -> HM.union `fmap` loadFactsOverrides p
-                           (Nothing, Just p) -> flip HM.union `fmap` loadFactsOverrides p
+                           (Just p, Nothing) -> HM.union `fmap` loadYamlFile p
+                           (Nothing, Just p) -> flip HM.union `fmap` loadYamlFile p
                            (Nothing, Nothing) -> return id
-    LOG.updateGlobalLogger "Puppet.Daemon" (LOG.setLevel _loglevel)
-    LOG.updateGlobalLogger "Hiera.Server" (LOG.setLevel _loglevel)
-    q <- initDaemon =<< setupPreferences
-         workingdir ((prefPDB.~ pdbapi) . (hieraPath.~ _hieraFile) . (ignoredmodules.~ _ignoredMods) . (strictness.~ _strictMode) . (extraTests.~ not _noExtraTests))
+    prf <- dfPreferences workingdir <&> prefPDB .~ pdbapi
+                                    <&> hieraPath .~ _optHieraFile
+                                    <&> ignoredmodules %~ (`fromMaybe` _optIgnoredMods)
+                                    <&> (if _optStrictMode then strictness .~ Strict else id)
+                                    <&> (if _optNoExtraTests then extraTests .~ False else id)
+    q <- initDaemon prf
     let queryfunc = \node -> fmap factsOverrides (puppetDBFacts node pdbapi) >>= _dGetCatalog q node
     return (queryfunc, pdbapi, _dParserStats q, _dCatalogStats q, _dTemplateStats q)
 
+
 parseFile :: FilePath -> IO (Either P.ParseError (V.Vector Statement))
 parseFile = fmap . runPParser puppetParser <*> T.readFile
 
@@ -233,11 +244,6 @@
        $ w
 
 
-loadFactsOverrides :: FilePath -> IO Facts
-loadFactsOverrides fp = decodeFileEither fp >>= \case
-    Left rr -> error ("Error when parsing " ++ fp ++ ": " ++ show rr)
-    Right x -> return x
-
 -- | Finds the dead code
 findDeadCode :: String -> [Resource] -> Set.Set FilePath -> IO ()
 findDeadCode puppetdir catalogs allfiles = do
@@ -284,9 +290,9 @@
 
 -- | For each node, queryfunc the catalog and return stats
 computeStats :: FilePath -> Options -> QueryFunc -> (MStats, MStats, MStats) -> [Nodename] -> IO ()
-computeStats workingdir (Options {_loglevel, _deadcode})
-              queryfunc (parsingStats, catalogStats, templateStats)
-              topnodes = do
+computeStats workingdir (Options {..})
+             queryfunc (parsingStats, catalogStats, templateStats)
+             topnodes = do
     -- the parsing statistics, so that we known which files
     (cats, Sum failures) <- catMaybesCount <$> parallel (map (computeCatalog queryfunc) topnodes)
     pStats <- getStats parsingStats
@@ -294,7 +300,7 @@
     tStats <- getStats templateStats
     let allres = (cats ^.. folded . _1 . folded) ++ (cats ^.. folded . _2 . folded)
         allfiles = Set.fromList $ map T.unpack $ HM.keys pStats
-    when _deadcode $ findDeadCode workingdir allres allfiles
+    when _optDeadcode $ findDeadCode workingdir allres allfiles
     -- compute statistics
     let (parsing,    Just (wPName, wPMean)) = worstAndSum pStats
         (cataloging, Just (wCName, wCMean)) = worstAndSum cStats
@@ -306,10 +312,10 @@
         worstAndSum = (_1 %~ getSum)
                             . (_2 %~ fmap swap . getMaximum)
                             . ifoldMap (\k (StatsPoint cnt total _ _) -> (Sum total, Maximum $ Just (total / fromIntegral cnt, k)))
-    putStr ("Tested " ++ show nbnodes ++ " nodes. ")
+    putStr ("\nTested " ++ show nbnodes ++ " nodes. ")
     unless (nbnodes == 0) $ do
         putStrLn (formatDouble parserShare <> "% of total CPU time spent parsing, " <> formatDouble templateShare <> "% spent computing templates")
-        when (_loglevel <= LOG.INFO) $ do
+        when (_optLoglevel <= LOG.INFO) $ do
             putStrLn ("Slowest template:           " <> T.unpack wTName <> ", taking " <> formatDouble wTMean <> "s on average")
             putStrLn ("Slowest file to parse:      " <> T.unpack wPName <> ", taking " <> formatDouble wPMean <> "s on average")
             putStrLn ("Slowest catalog to compute: " <> T.unpack wCName <> ", taking " <> formatDouble wCMean <> "s on average")
@@ -322,34 +328,33 @@
         computeCatalog :: QueryFunc -> Nodename -> IO (Maybe (FinalCatalog, [Resource]))
         computeCatalog func node =
             func node >>= \case
-              S.Left err -> putDoc ("Problem with" <+> ttext node <+> ":" <+> getError err </> mempty) >> return Nothing
+              S.Left err -> putDoc (line <> red "ERROR:" <+> parens (ttext node) <+> ":" <+> getError err) >> return Nothing
               S.Right (rawcatalog, _ , rawexported, knownRes) -> return (Just (rawcatalog <> rawexported, knownRes))
 
 -- | Queryfunc the catalog for the node and PP the result
 computeNodeCatalog :: Options -> QueryFunc -> PuppetDBAPI IO -> Nodename -> IO ()
-computeNodeCatalog (Options {_showjson, _showContent, _resourceType, _resourceName, _checkExport })
-                   queryfunc pdbapi node =
+computeNodeCatalog (Options {..}) queryfunc pdbapi node =
     queryfunc node >>= \case
       S.Left rr -> do
-          putDoc ("Problem with" <+> ttext node <+> ":" <+> getError rr </> mempty)
+          putDoc (line <> red "ERROR:" <+> parens (ttext node) <+> getError rr)
           exitFailure
       S.Right (rawcatalog, edgemap, rawexported, _) -> do
           printFunc <- hIsTerminalDevice stdout >>= \isterm -> return $ \x ->
             if isterm
                 then putDoc x >> putStrLn ""
                 else displayIO stdout (renderCompact x) >> putStrLn ""
-          catalog  <- filterCatalog _resourceType _resourceName rawcatalog
-          exported <- filterCatalog _resourceType _resourceName rawexported
+          catalog  <- filterCatalog _optResourceType _optResourceName rawcatalog
+          exported <- filterCatalog _optResourceType _optResourceName rawexported
           let wireCatalog    = generateWireCatalog node (catalog    <> exported   ) edgemap
               rawWireCatalog = generateWireCatalog node (rawcatalog <> rawexported) edgemap
-          when _checkExport $ void $ replaceCatalog pdbapi rawWireCatalog
-          case (_showContent, _showjson) of
+          when _optCheckExport $ void $ runEitherT $ replaceCatalog pdbapi rawWireCatalog
+          case (_optShowContent, _optShowjson) of
               (_, True) -> BSL.putStrLn (encode (prepareForPuppetApply wireCatalog))
               (True, _) -> do
-                  unless (_resourceType == Just "file" || isNothing _resourceType) $ do
+                  unless (_optResourceType == Just "file" || isNothing _optResourceType) $ do
                       putDoc "Show content only works with resource of type file. It is an error to provide another filter type"
                       exitFailure
-                  case _resourceName of
+                  case _optResourceName of
                       Just f  -> printContent f catalog
                       Nothing -> putDoc "You should supply a resource name when using showcontent" >> exitFailure
               _         -> do
@@ -376,27 +381,27 @@
 
 run :: Options -> IO ()
 -- | Parse mode
-run (Options {_parse = Just fp}) = parseFile fp >>= \case
+run (Options {_optParse = Just fp}) = parseFile fp >>= \case
             Left rr -> error ("parse error:" ++ show rr)
             Right s -> putDoc $ ppStatements s
 
-run (Options {_puppetdir = Nothing, _parse = Nothing }) =
+run (Options {_optPuppetdir = Nothing, _optParse = Nothing }) =
     error "Without a puppet dir, only the `--parse` option can be supported"
-run (Options {_puppetdir = Just _, _nodename = Nothing, _multnodes = Nothing}) =
+run (Options {_optPuppetdir = Just _, _optNodename = Nothing, _optMultnodes = Nothing}) =
     error "You need to choose between single or multiple node"
 
 -- | Single node mode (`--node` option)
-run cmd@(Options {_nodename = Just node, _commitDB, _puppetdir = Just workingdir}) = do
+run cmd@(Options {_optNodename = Just node, _optPuppetdir = Just workingdir, ..}) = do
     (queryfunc, pdbapi, _, _, _ ) <- initializedaemonWithPuppet workingdir cmd
     computeNodeCatalog cmd queryfunc pdbapi node
-    when _commitDB $ void $ commitDB pdbapi
+    when _optCommitDB $ void $ runEitherT $ commitDB pdbapi
 
 -- | Multiple nodes mode (`--all`) option
-run cmd@(Options {_nodename = Nothing , _multnodes = Just nodes, _puppetdir = Just workingdir}) = do
+run cmd@(Options {_optNodename = Nothing , _optMultnodes = Just nodes, _optPuppetdir = Just workingdir}) = do
     -- it would be really noisy to run this mode with loglevel < LOG.ERROR;
     -- even the default LOG.WARNING would clutter the output.
     -- That's why we force LOG.ERROR for the puppet daemon.
-    (queryfunc, _, mPStats,mCStats,mTStats) <- initializedaemonWithPuppet workingdir (cmd {_loglevel = LOG.ERROR})
+    (queryfunc, _, mPStats,mCStats,mTStats) <- initializedaemonWithPuppet workingdir (cmd {_optLoglevel = LOG.ERROR})
     computeStats workingdir cmd queryfunc (mPStats, mCStats, mTStats) =<< retrieveNodes nodes
 
   where
diff --git a/progs/pdbQuery.hs b/progs/pdbQuery.hs
--- a/progs/pdbQuery.hs
+++ b/progs/pdbQuery.hs
@@ -1,4 +1,5 @@
 {-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE GADTs      #-}
 module Main where
 
 import           Puppet.Interpreter.Types
@@ -8,7 +9,8 @@
 import           Facter
 
 import           Control.Lens
-import           Control.Monad (forM_,unless)
+import           Control.Monad (forM_,unless,(>=>))
+import           Control.Monad.Trans.Either
 import qualified Data.ByteString.Char8 as BS
 import qualified Data.Either.Strict as S
 import qualified Data.HashMap.Strict as HM
@@ -18,6 +20,7 @@
 import qualified Data.Vector as V
 import           Data.Yaml hiding (Parser)
 import           Options.Applicative as O
+import           Servant.Common.BaseUrl
 
 data Options = Options { _pdbloc :: Maybe FilePath
                        , _pdbtype :: PDBType
@@ -71,54 +74,59 @@
 addfacts = AddFacts <$> O.argument auto mempty
 
 
-display :: (Show r, ToJSON a) => String -> S.Either r a -> IO ()
-display s (S.Left rr) = error (s <> " " <> show rr)
-display _ (S.Right a) = BS.putStrLn (encode a)
+display :: (Show r, ToJSON a) => String -> Either r a -> IO ()
+display s (Left rr) = error (s <> " " <> show rr)
+display _ (Right a) = BS.putStrLn (encode a)
 
-checkError :: (Show r) => String -> S.Either r a -> IO a
-checkError s (S.Left rr) = error (s <> " " <> show rr)
-checkError _ (S.Right a) = return a
+checkErrorS :: (Show r) => String -> S.Either r a -> IO a
+checkErrorS s (S.Left rr) = error (s <> " " <> show rr)
+checkErrorS _ (S.Right a) = return a
 
+checkError :: (Show r) => String -> Either r a -> IO a
+checkError s (Left rr) = error (s <> " " <> show rr)
+checkError _ (Right a) = return a
+
+runCheck :: Show r => String -> EitherT r IO a -> IO a
+runCheck s = runEitherT >=> checkError s
+
 run :: Options -> IO ()
 run cmdl = do
     epdbapi <- case (_pdbloc cmdl, _pdbtype cmdl) of
-                   (Just l, PDBRemote) -> pdbConnect (T.pack l)
+                   (Just l, PDBRemote) -> pdbConnect $ either error id $ parseBaseUrl l
                    (Just l, PDBTest)   -> loadTestDB l
                    (_, x)              -> getDefaultDB x
     pdbapi <- case epdbapi of
                   S.Left r -> error (show r)
                   S.Right x -> return x
-    let getOrError s (S.Left rr) = error (s <> " " <> show rr)
-        getOrError _ (S.Right x) = return x
     case _pdbcmd cmdl of
         DumpFacts -> if _pdbtype cmdl == PDBDummy
                          then puppetDBFacts "dummy"  pdbapi >>= mapM_ print . HM.toList
                          else do
-                             allfacts <- getFacts pdbapi QEmpty >>= checkError "get facts"
-                             tmpdb <- loadTestDB "/tmp/allfacts.yaml" >>= checkError "load test db"
+                             allfacts <- runCheck "get facts" (getFacts pdbapi QEmpty)
+                             tmpdb <- loadTestDB "/tmp/allfacts.yaml" >>= checkErrorS "load test db"
                              let groupfacts = foldl' groupfact HM.empty allfacts
                                  groupfact curmap (PFactInfo ndname fctname fctval) =
                                      curmap & at ndname . non HM.empty %~ (at fctname ?~ fctval)
-                             replaceFacts tmpdb (HM.toList groupfacts) >>= checkError "replace facts in dummy db"
-                             commitDB tmpdb >>= checkError "commit db"
-        DumpNodes -> getNodes pdbapi QEmpty >>= display "dump nodes"
+                             runCheck "replace facts in dummy db" (replaceFacts tmpdb (HM.toList groupfacts))
+                             runCheck "commit db" (commitDB tmpdb)
+        DumpNodes -> runEitherT (getNodes pdbapi QEmpty) >>= display "dump nodes"
         AddFacts n -> do
             unless (_pdbtype cmdl == PDBTest) (error "This option only works with the test puppetdb")
             fcts <- puppetDBFacts n pdbapi
-            replaceFacts pdbapi [(n, fcts)] >>= getOrError "replace facts"
-            commitDB pdbapi >>= getOrError "commit db"
+            runCheck "replace facts" (replaceFacts pdbapi [(n, fcts)])
+            runCheck "commit db" (commitDB pdbapi)
         CreateTestDB destfile -> do
-            ndb <- loadTestDB destfile >>= getOrError "puppetdb load"
-            allnodes <- getNodes pdbapi QEmpty >>= getOrError "get nodes"
-            allfacts <- getFacts pdbapi QEmpty >>= getOrError "get facts"
+            ndb <- loadTestDB destfile >>= checkErrorS "puppetdb load"
+            allnodes <- runCheck "get nodes" (getNodes pdbapi QEmpty)
+            allfacts <- runCheck "get facts" (getFacts pdbapi QEmpty)
             let factsGrouped = HM.toList $ HM.fromListWith (<>) $ map (\x -> (x ^. nodename, HM.singleton (x ^. factname) (x ^. factval))) allfacts
-            replaceFacts ndb factsGrouped >>= getOrError "replace facts"
+            runCheck "replace facts" (replaceFacts ndb factsGrouped)
             forM_ allnodes $ \pnodename -> do
                 let ndename = pnodename ^. nodename
-                res <- getResourcesOfNode pdbapi ndename QEmpty >>= getOrError ("get resources for " ++ show ndename)
+                res <- runCheck ("get resources for " ++ show ndename) (getResourcesOfNode pdbapi ndename QEmpty)
                 let wirecatalog = WireCatalog ndename "version" V.empty (V.fromList res) ndename
-                replaceCatalog ndb wirecatalog
-            commitDB ndb >>= getOrError "commit db"
+                runCheck "replace catalog" (replaceCatalog ndb wirecatalog)
+            runCheck "commit db" (commitDB ndb)
         _ -> error "Not yet implemented"
 
 main :: IO ()
