{-| Effectful functions to execute and build virtual machine images using an execution environment like e.g. libvirt-lxc. -} module B9.VmBuilder ( buildWithVm ) where import Control.Eff import Control.Monad import Control.Monad.IO.Class import Data.List import System.Directory (canonicalizePath, createDirectoryIfMissing) import Text.Printf (printf) import Text.Show.Pretty (ppShow) import B9.Artifact.Readable import B9.B9Config import B9.B9Logging import B9.B9Monad import B9.BuildInfo import B9.DiskImageBuilder import B9.DiskImages import B9.ExecEnv import qualified B9.LibVirtLXC as LXC import B9.ShellScript import B9.Vm buildWithVm :: IsB9 e => InstanceId -> [ImageTarget] -> FilePath -> VmScript -> Eff e Bool buildWithVm iid imageTargets instanceDir vmScript = do vmBuildSupportedImageTypes <- getVmScriptSupportedImageTypes vmScript buildImages <- createBuildImages imageTargets vmBuildSupportedImageTypes success <- runVmScript iid imageTargets buildImages instanceDir vmScript when success (createDestinationImages buildImages imageTargets) return success getVmScriptSupportedImageTypes :: IsB9 e => VmScript -> Eff e [ImageType] getVmScriptSupportedImageTypes NoVmScript = return [QCow2, Raw, Vmdk] getVmScriptSupportedImageTypes _ = supportedImageTypes <$> getExecEnvType supportedImageTypes :: ExecEnvType -> [ImageType] supportedImageTypes LibVirtLXC = LXC.supportedImageTypes createBuildImages :: IsB9 e => [ImageTarget] -> [ImageType] -> Eff e [Image] createBuildImages imageTargets vmBuildSupportedImageTypes = do dbgL "creating build images" traceL (ppShow imageTargets) buildImages <- mapM createBuildImage imageTargets infoL "CREATED BUILD IMAGES" traceL (ppShow buildImages) return buildImages where createBuildImage (ImageTarget dest imageSource _mnt) = do buildDir <- getBuildDir destTypes <- preferredDestImageTypes imageSource let buildImgType = head (destTypes `intersect` preferredSourceImageTypes dest `intersect` vmBuildSupportedImageTypes) srcImg <- resolveImageSource imageSource let buildImg = changeImageFormat buildImgType (changeImageDirectory buildDir srcImg) buildImgAbsolutePath <- ensureAbsoluteImageDirExists buildImg materializeImageSource imageSource buildImg return buildImgAbsolutePath runVmScript :: forall e. IsB9 e => InstanceId -> [ImageTarget] -> [Image] -> FilePath -> VmScript -> Eff e Bool runVmScript _ _ _ _ NoVmScript = return True runVmScript (IID iid) imageTargets buildImages instanceDir vmScript = do dbgL (printf "starting vm script with instanceDir '%s'" instanceDir) traceL (ppShow vmScript) execEnv <- setUpExecEnv let (VmScript _ _ script) = vmScript success <- runInEnvironment execEnv script if success then infoL "EXECUTED BUILD SCRIPT" else errorL "BUILD SCRIPT FAILED" return success where setUpExecEnv :: IsB9 e => Eff e ExecEnv setUpExecEnv = do let (VmScript cpu shares _) = vmScript let mountedImages = buildImages `zip` (itImageMountPoint <$> imageTargets) sharesAbs <- createSharedDirs instanceDir shares return (ExecEnv iid mountedImages sharesAbs (Resources AutomaticRamSize 8 cpu)) createSharedDirs :: IsB9 e => FilePath -> [SharedDirectory] -> Eff e [SharedDirectory] createSharedDirs instanceDir = mapM createSharedDir where createSharedDir (SharedDirectoryRO d m) = do d' <- createAndCanonicalize d return $ SharedDirectoryRO d' m createSharedDir (SharedDirectory d m) = do d' <- createAndCanonicalize d return $ SharedDirectory d' m createSharedDir (SharedSources mp) = do d' <- createAndCanonicalize instanceDir return $ SharedDirectoryRO d' mp createAndCanonicalize d = liftIO $ do createDirectoryIfMissing True d canonicalizePath d createDestinationImages :: IsB9 e => [Image] -> [ImageTarget] -> Eff e () createDestinationImages buildImages imageTargets = do dbgL "converting build- to output images" let pairsToConvert = buildImages `zip` (itImageDestination `map` imageTargets) traceL (ppShow pairsToConvert) mapM_ (uncurry createDestinationImage) pairsToConvert infoL "CONVERTED BUILD- TO OUTPUT IMAGES" runInEnvironment :: IsB9 e => ExecEnv -> Script -> Eff e Bool runInEnvironment env script = do t <- getExecEnvType case t of LibVirtLXC -> LXC.runInEnvironment env script