language-puppet-1.5.1: src/Puppet/Interpreter/Types.hs
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
module Puppet.Interpreter.Types
( -- * Operational state
InterpreterState (InterpreterState),
scopes,
definedResources,
nestedDeclarations,
resModifiers,
extraRelations,
curScope,
curPos,
loadedClasses,
-- * Operational reader
InterpreterReader (InterpreterReader),
readerNativeTypes,
readerGetStatement,
readerGetTemplate,
readerPdbApi,
readerExternalFunc,
readerNodename,
readerHieraQuery,
readerIoMethods,
readerIgnoredModules,
readerExternalModules,
readerIsStrict,
readerPuppetPaths,
readerFacts,
readerRebaseFile,
-- * Interpreter monad
InterpreterMonad,
InterpreterWriter,
InterpreterInstr (..),
Strictness (..),
-- * Io methods
IoMethods (IoMethods),
ioGetCurrentCallStack,
ioReadFile,
ioTraceEvent,
MonadThrowPos (..),
-- * Resource modifier
ResourceModifier (ResourceModifier),
rmResType,
rmDeclaration,
rmSearch,
rmType,
rmMutation,
rmModifierType,
ModifierType (..),
OverrideType (..),
ResourceCollectorType (..),
ClassIncludeType (..),
RSearchExpression (..),
-- * Scope information
ScopeInformation (ScopeInformation),
scopeResDefaults,
scopeVariables,
scopeParent,
scopeOverrides,
scopeContainer,
scopeExtraTags,
CurContainer (CurContainer),
cctype,
cctags,
-- * Resource default
ResDefaults (ResDefaults),
resDefValues,
resDefSrcScope,
resDefPos,
resDefType,
ResRefOverride (..),
ScopeEnteringContext (..),
TopLevelType (..),
-- * Hiera
HieraQueryLayers (..),
globalLayer,
environmentLayer,
moduleLayer,
-- * Template
TemplateSource (..),
-- * Re-export
module Puppet.Language,
)
where
import qualified Control.Monad.Fail as Fail
import Control.Monad.Operational
import Control.Monad.State.Strict
import Control.Monad.Writer.Class
import Data.Aeson as A
import qualified Data.Either.Strict as S
import qualified Data.HashMap.Strict as HM
import qualified Data.List as List
import qualified Data.Maybe.Strict as S
import Facter
import qualified GHC.Show
import qualified GHC.Stack
import Hiera.Server
import Puppet.Language
import Puppet.Parser.Types
import PuppetDB
import qualified System.Log.Logger as Log
import XPrelude.Extra
import XPrelude.PP
-- | The intepreter can run in two modes : a strict mode (recommended), and
-- a permissive mode.
data Strictness
= Strict
| Permissive
deriving (Show, Eq)
instance FromJSON Strictness where
parseJSON (Bool True) = pure Strict
parseJSON (Bool False) = pure Permissive
parseJSON _ = mzero
data RSearchExpression
= REqualitySearch !Text !PValue
| RNonEqualitySearch !Text !PValue
| RAndSearch !RSearchExpression !RSearchExpression
| ROrSearch !RSearchExpression !RSearchExpression
| RAlwaysTrue
deriving (Show, Eq)
-- | Puppet has two main ways to declare classes: include-like and resource-like.
--
-- See <https://docs.puppetlabs.com/puppet/latest/reference/lang_classes.html#include-like-vs-resource-like puppet reference>.
data ClassIncludeType
= -- | Using the include or contain function
ClassIncludeLike
| -- | Resource like declaration
ClassResourceLike
deriving (Eq)
-- | Differentiate the distinct top level types such as node, define or class.
data TopLevelType
= -- | For node entries
TopNode
| -- | For defines
TopDefine
| -- | For classes
TopClass
deriving (Generic, Eq)
instance Hashable TopLevelType
-- | From the evaluation of Resource Default Declaration.
data ResDefaults = ResDefaults
{ _resDefType :: !Text,
_resDefSrcScope :: !Text,
_resDefValues :: !(Container PValue),
_resDefPos :: !PPosition
}
-- | From the evaluation of Resource Override Declaration.
data ResRefOverride = ResRefOverride
{ _rrid :: !RIdentifier,
_rrparams :: !(Container PValue),
_rrpos :: !PPosition
}
deriving (Eq)
data ScopeEnteringContext
= SENormal
| -- | We enter the scope as the child of another class
SEChild !Text
| -- | We enter the scope as the parent of another class
SEParent !Text
-- | The type of the container together with its tags.
data CurContainer = CurContainer
{ _cctype :: !CurContainerDesc,
_cctags :: !(HashSet Text)
}
deriving (Eq)
data ScopeInformation = ScopeInformation
{ _scopeVariables :: !(Container (Pair (Pair PValue PPosition) CurContainerDesc)),
_scopeResDefaults :: !(Container ResDefaults),
_scopeExtraTags :: !(HashSet Text),
_scopeContainer :: !CurContainer,
_scopeOverrides :: !(HashMap RIdentifier ResRefOverride),
_scopeParent :: !(S.Maybe Text)
}
data InterpreterState = InterpreterState
{ _scopes :: !(Container ScopeInformation),
_loadedClasses :: !(Container (Pair ClassIncludeType PPosition)),
_definedResources :: !(HM.HashMap RIdentifier Resource),
_curScope :: ![CurContainerDesc],
_curPos :: !PPosition,
_nestedDeclarations :: !(HashMap (TopLevelType, Text) Statement),
_extraRelations :: ![LinkInformation],
_resModifiers :: ![ResourceModifier]
}
data IoMethods m = IoMethods
{ _ioGetCurrentCallStack :: m [String],
_ioReadFile :: [Text] -> m (Either String Text),
_ioTraceEvent :: String -> m ()
}
-- | All available queries including the global and module layer
-- The environment layer is not implemented.
--
-- The datatype belongs to the "Puppet.Interpreter" module because it serves to implement how Hiera is used within Puppet.
data HieraQueryLayers m = HieraQueryLayers
{ _globalLayer :: HieraQueryFunc m,
_environmentLayer :: HieraQueryFunc m,
_moduleLayer :: Container (HieraQueryFunc m)
}
-- | Whether the template source is specified 'inline' or loaded from a file.
data TemplateSource = Inline Text | Filename FilePath
data InterpreterReader m = InterpreterReader
{ _readerNativeTypes :: !(Container NativeTypeMethods),
-- | Access to parsed statements
_readerGetStatement :: TopLevelType -> Text -> m (S.Either PrettyError Statement),
_readerGetTemplate :: TemplateSource -> InterpreterState -> InterpreterReader m -> m (S.Either PrettyError Text),
_readerPdbApi :: PuppetDBAPI m,
-- | External func such as stdlib or puppetlabs
_readerExternalFunc :: Container ([PValue] -> InterpreterMonad PValue),
_readerNodename :: Text,
_readerHieraQuery :: HieraQueryLayers m,
_readerIoMethods :: IoMethods m,
_readerIgnoredModules :: HashSet Text,
_readerExternalModules :: HashSet Text,
_readerIsStrict :: Bool,
_readerPuppetPaths :: PuppetDirPaths,
_readerRebaseFile :: Maybe FilePath,
-- | Access to the list of facts that were given to the 'Preferences' module
_readerFacts :: Container PValue
}
data InterpreterInstr a where
-- Utility for using what's in 'InterpreterReader'
GetNativeTypes :: InterpreterInstr (Container NativeTypeMethods)
GetStatement :: TopLevelType -> Text -> InterpreterInstr Statement
ComputeTemplate :: TemplateSource -> InterpreterState -> InterpreterInstr Text
ExternalFunction :: Text -> [PValue] -> InterpreterInstr PValue
Facts :: InterpreterInstr (Container PValue)
GetNodeName :: InterpreterInstr Text
HieraQuery :: Container PValue -> Text -> HieraQueryType -> InterpreterInstr (Maybe PValue)
GetCurrentCallStack :: InterpreterInstr [String]
IsIgnoredModule :: Text -> InterpreterInstr Bool
IsExternalModule :: Text -> InterpreterInstr Bool
IsStrict :: InterpreterInstr Bool
PuppetPaths :: InterpreterInstr PuppetDirPaths
RebaseFile :: InterpreterInstr (Maybe FilePath)
-- error
ErrorThrow :: PrettyError -> InterpreterInstr a
ErrorCatch :: InterpreterMonad a -> (PrettyError -> InterpreterMonad a) -> InterpreterInstr a
-- writer
WriterTell :: InterpreterWriter -> InterpreterInstr ()
WriterPass :: InterpreterMonad (a, InterpreterWriter -> InterpreterWriter) -> InterpreterInstr a
WriterListen :: InterpreterMonad a -> InterpreterInstr (a, InterpreterWriter)
-- puppetdb wrappers , see 'PuppetDBAPI' for details
PDBInformation :: InterpreterInstr Doc
PDBReplaceCatalog :: WireCatalog -> InterpreterInstr ()
PDBReplaceFacts :: [(NodeName, Facts)] -> InterpreterInstr ()
PDBDeactivateNode :: NodeName -> InterpreterInstr ()
PDBGetFacts :: Query FactField -> InterpreterInstr [FactInfo]
PDBGetResources :: Query ResourceField -> InterpreterInstr [Resource]
PDBGetNodes :: Query NodeField -> InterpreterInstr [NodeInfo]
PDBCommitDB :: InterpreterInstr ()
PDBGetResourcesOfNode :: NodeName -> Query ResourceField -> InterpreterInstr [Resource]
-- Reading the first file that can be read in a list
ReadFile :: [Text] -> InterpreterInstr Text
-- Tracing events
TraceEvent :: String -> InterpreterInstr ()
-- | The main monad
type InterpreterMonad = ProgramT InterpreterInstr (State InterpreterState)
instance Fail.MonadFail InterpreterMonad where
fail = throwError . PrettyError . ppstring
instance MonadError PrettyError InterpreterMonad where
throwError = singleton . ErrorThrow
catchError a c = singleton (ErrorCatch a c)
-- | Log
type InterpreterWriter = [Pair Log.Priority Doc]
instance MonadWriter InterpreterWriter InterpreterMonad where
tell = singleton . WriterTell
pass = singleton . WriterPass
listen = singleton . WriterListen
data ResourceModifier = ResourceModifier
{ _rmResType :: !Text,
_rmModifierType :: !ModifierType,
_rmType :: !ResourceCollectorType,
_rmSearch :: !RSearchExpression,
_rmMutation :: !(Resource -> InterpreterMonad Resource),
_rmDeclaration :: !PPosition
}
instance Show ResourceModifier where
show (ResourceModifier rt mt ct se _ p) = List.unwords ["ResourceModifier", show rt, show mt, show ct, "(" ++ show se ++ ")", "???", show p]
data ModifierType
= -- | For collectors, optional resources
ModifierCollector
| -- | For stuff like realize
ModifierMustMatch
deriving (Show, Eq)
data OverrideType
= -- | Overriding forbidden, will throw an error
CantOverride
| -- | Can silently replace
Replace
| -- | Silently ignore errors
CantReplace
| -- | Can append values
AppendAttribute
deriving (Show, Eq)
data ResourceCollectorType
= RealizeVirtual
| RealizeCollected
| DontRealize
deriving (Show, Eq)
makeLenses ''ResDefaults
makeLenses ''HieraQueryLayers
makeLenses ''ResourceModifier
makeLenses ''InterpreterReader
makeLenses ''IoMethods
makeLenses ''CurContainer
makeLenses ''ScopeInformation
makeLenses ''InterpreterState
class (Monad m) => MonadThrowPos m where
throwPosError :: Doc -> m a
-- Useful for mocking for instance in a REPL
instance MonadThrowPos (Either Doc) where
throwPosError = Left
class MonadStack m where
getCurrentCallStack :: m [String]
instance MonadStack InterpreterMonad where
getCurrentCallStack = singleton GetCurrentCallStack
instance MonadThrowPos InterpreterMonad where
throwPosError s = do
p <- use (curPos . _1)
stack <- getCurrentCallStack
let dstack =
if null stack
then line
else mempty </> ppstring (GHC.Stack.renderStack stack)
throwError (PrettyError (s <+> "at" <+> showPos p <> dstack))