{-# LANGUAGE TupleSections #-}
module Devops.Actions (
concurrentTurnup , concurrentTurndown , concurrentUpkeep , checkStatuses
, sequentialTurnup, sequentialTurnDown
, display , defaultDotify , dotifyWithStatuses
, listUniqNodes
) where
import Control.Concurrent.Async (waitCatch)
import Control.Concurrent.STM (STM, atomically)
import Control.Concurrent.STM.TVar (TVar, readTVar)
import Control.Lens (view)
import Data.Graph (edges, transposeG, vertices)
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as Map
import Data.Monoid ((<>))
import qualified Data.Text as Text
import Data.Tree (Tree (..), drawForest, flatten)
import Text.Dot (Dot, edge, showDot, userNode,
userNodeId)
import Devops.Base (CheckResult (..), Op (..),
OpDescription (..), OpUniqueId,
PreOp, opUniqueId, preOpUniqueId,
runPreOp)
import Devops.Graph
concurrentTurnup :: OpGraph -> IO ()
concurrentTurnup graph = do
let s = snapshot TurnedUp graph emptyIntents
sm <- atomically $ makeStatusesMap s
asyncTurnupGraph noBroadcast sm s graph
concurrentUpkeep :: OpGraph -> IO ()
concurrentUpkeep graph = do
let s = snapshot TurnedUp graph emptyIntents
sm <- atomically $ makeStatusesMap s
upkeepGraph noBroadcast sm s graph defaultUpKeepFSM defaultDownKeepFSM
concurrentTurndown :: OpGraph -> IO ()
concurrentTurndown (g,f1,f2) = do
let graph = (transposeG g, f1, f2)
let s = snapshot TurnedDown graph emptyIntents
sm <- atomically $ makeStatusesMap s
asyncTurndownGraph noBroadcast sm s graph
checkStatuses :: OpGraph -> IO (Map OpUniqueId CheckResult)
checkStatuses graph = do
let s = snapshot TurnedUp graph emptyIntents
statuses <- atomically $ makeStatusesMap s
asyncs <- checkWholeGraph noBroadcast statuses s graph
_ <- traverse waitCatch asyncs
atomically $ extractStatuses statuses
where
extractStatuses :: OpStatusesMap -> STM (Map OpUniqueId CheckResult)
extractStatuses statuses =
Map.fromList <$> traverse extractOne (Map.toList statuses)
extractOne :: (OpUniqueId, TVar OpStatus) -> STM (OpUniqueId, CheckResult)
extractOne (opId, tvar) = do
status <- readTVar tvar
return $ (opId, view opCheckResult status)
sequentialTurnup :: OpGraph -> IO ()
sequentialTurnup (g,f1,f2) = syncTurnupGraph noBroadcast (transposeG g, f1, f2)
sequentialTurnDown :: OpGraph -> IO ()
sequentialTurnDown = syncTurnDownGraph noBroadcast
display :: [Tree PreOp] -> IO ()
display = putStrLn . drawForest . (fmap . fmap) (show . opDescription . runPreOp)
listUniqNodes :: [Tree PreOp] -> IO ()
listUniqNodes forest =
let uniq f xs = Map.toList . Map.fromList $ zip (map f xs) xs
in putStrLn . unlines . map (\(k,v) -> show (k, opName $ opDescription v)) . uniq opUniqueId . map runPreOp . concatMap flatten $ forest
dotifyWith :: (PreOp -> [(String,String)]) -> OpGraph -> String
dotifyWith attributes (g,lookupF,_) =
showDot dotted
where
dotted :: Dot ()
dotted = do
let node v = y where (y,_,_) = lookupF v
let vs = vertices g
let es = filter (uncurry (/=)) $ edges g
mapM_ (\i -> userNode (userNodeId i) (attributes (node i))) vs
mapM_ (\(i,j) -> edge (userNodeId i) (userNodeId j) []) es
defaultDotify :: OpGraph -> String
defaultDotify = dotifyWith nameAttributes
dotifyWithStatuses :: OpGraph -> Map OpUniqueId CheckResult -> String
dotifyWithStatuses graph x =
let allAttributes = nameAttributes <> colorFromStatusAttributes x
in dotifyWith allAttributes graph
nameAttributes :: PreOp -> [(String, String)]
nameAttributes preOp =
let o = runPreOp preOp in
[("label", Text.unpack $ opName $ opDescription o)]
colorFromStatusAttributes :: Map OpUniqueId CheckResult -> PreOp -> [(String, String)]
colorFromStatusAttributes c op =
let status = Map.lookup (preOpUniqueId op) c
in maybe unknownStatusLabels labelsFromStatus status
where
labelsFromStatus :: CheckResult -> [(String, String)]
labelsFromStatus Success = [("color", "green")]
labelsFromStatus Skipped = [("color", "yellow")]
labelsFromStatus Unknown = [("color", "blue")]
labelsFromStatus _ = [("color", "red")]
unknownStatusLabels :: [(String, String)]
unknownStatusLabels = [("shape", "egg")]