b9 0.2.4 → 0.2.5
raw patch · 10 files changed
+198/−36 lines, 10 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
Files
- .travis.yml +2/−0
- TODO.org +4/−0
- b9.cabal +1/−1
- src/lib/B9.hs +5/−1
- src/lib/B9/ArtifactGenerator.hs +106/−7
- src/lib/B9/Content/AST.hs +29/−11
- src/lib/B9/Content/StringTemplate.hs +1/−1
- src/lib/B9/DiskImages.hs +41/−6
- src/lib/B9/LibVirtLXC.hs +4/−3
- src/lib/B9/Repository.hs +5/−6
.travis.yml view
@@ -1,1 +1,3 @@ language: haskell+ghc:+ - 7.8
TODO.org view
@@ -86,4 +86,8 @@ * Make B9 a real library ** Restrict exposed modules ** Add pure library functions for munging Artifacts+** Move SharedImage functions from DiskImageBuilder to a better place+** Refactor the B9Monad.run function to not need a ConfigParser+** Move 'Repository' from B9.RepositoryIO to B9.Repository * Add building of RPMs and ARCHLINUX packages+* Use unique ids for vm image builds
b9.cabal view
@@ -1,5 +1,5 @@ name: b9-version: 0.2.4+version: 0.2.5 synopsis: A tool and library for building virtual machine images.
src/lib/B9.hs view
@@ -5,7 +5,11 @@ This module re-exports the modules needed to build a tool around the library, e.g. see @src\/cli\/Main.hs@ as an example.- -}++ "B9.ArtifactGenerator" is the module containing the basic data structure+ used to describe a B9 build.++-} module B9 ( module B9.Builder
src/lib/B9/ArtifactGenerator.hs view
@@ -30,15 +30,79 @@ import Test.QuickCheck --- | A single config generator specifies howto generate multiple output--- files/directories. It consists of a netsted set of variable bindings that are--- replaced inside the text files+{-| Artifacts represent the things B9 can build. A generator specifies howto+generate parameterized, multiple artifacts. The general structure is:++@+ Let [ ... bindings ... ]+ [ Sources+ [ ... list all input files ... ]+ [ Artifact ...+ , Artifact ...+ , Let [ ... ] [ ... ]+ ]+ ]+@++The reasons why 'Sources' takes a list of 'ArtifactGenerator's is that+1. this makes the value easier to read/write for humans+2. the sources are static files used in all children (e.g. company logo image)+3. the sources are parameterized by variables that bound to different values+ for each artifact, e.g. a template network config file which contains+ the host IP address.++To bind such variables use 'Let', 'Each', 'LetX' or 'EachT'.++String subtitution of these variables is done by "B9.Content.StringTemplate".+These variables can be used as value in nested 'Let's, in most file names/paths+and in source files added with 'B9.Content.StringTemplate.SourceFile'++-} data ArtifactGenerator = Sources [ArtifactSource] [ArtifactGenerator]+ -- ^ Add sources available to 'ArtifactAssembly's in+ -- nested artifact generators. | Let [(String, String)] [ArtifactGenerator]+ -- ^ Bind variables, variables are avaible in nested+ -- generators. | LetX [(String, [String])] [ArtifactGenerator]- | EachT [String] [[String]] [ArtifactGenerator]+ -- ^ A 'Let' where each variable is assigned to each+ -- value; the nested generator is executed for each+ -- permutation.+ --+ -- @+ -- LetX [("x", ["1","2","3"]), ("y", ["a","b"])] [..]+ -- @+ -- Is equal to:+ --+ -- @+ -- Let [] [+ -- Let [("x", "1"), ("y", "a")] [..]+ -- Let [("x", "1"), ("y", "b")] [..]+ -- Let [("x", "2"), ("y", "a")] [..]+ -- Let [("x", "2"), ("y", "b")] [..]+ -- Let [("x", "3"), ("y", "a")] [..]+ -- Let [("x", "3"), ("y", "b")] [..]+ -- ]+ -- @ | Each [(String,[String])] [ArtifactGenerator]+ -- ^ Bind each variable to their first value, then each+ -- variable to the second value, etc ... and execute the+ -- nested generator in every step. 'LetX' represents a+ -- product of all variables, whereas 'Each' represents a+ -- sum of variable bindings - 'Each' is more like a /zip/+ -- whereas 'LetX' is more like a list comprehension.+ | EachT [String] [[String]] [ArtifactGenerator]+ -- ^ The transposed verison of 'Each': Bind the variables+ -- in the first list to each a set of values from the+ -- second argument; execute the nested generators for+ -- each binding | Artifact InstanceId ArtifactAssembly+ -- ^ Generate an artifact defined by an+ -- 'ArtifactAssembly'; the assembly can access the files+ -- created from the 'Sources' and variables bound by+ -- 'Let'ish elements. An artifact has an instance id,+ -- that is a unique, human readable string describing the+ -- artifact to assemble. | EmptyArtifact deriving (Read, Show, Eq) @@ -48,33 +112,68 @@ x `mappend` (Let [] []) = x x `mappend` y = Let [] [x, y] --- | Explicit is better than implicit: Only files that have explicitly been--- listed will be included in any generated configuration. That's right: There's--- no "inlcude *.*". B9 will check that *all* files in the directory specified with 'FromDir' are referred to by nested 'ArtifactSource's.+-- | Describe how input files for artifacts to build are obtained. The general+-- structure of each constructor is __FromXXX__ /destination/ /source/ data ArtifactSource = FromFile FilePath SourceFile+ -- ^ Copy a 'B9.Content.StringTemplate.SourceFile'+ -- potentially replacing variabled defined in 'Let'-like+ -- parent elements. | FromContent FilePath Content+ -- ^ Create a file from some 'Content' | SetPermissions Int Int Int [ArtifactSource]+ -- ^ Set the unix /file permissions/ to all files generated+ -- by the nested list of 'ArtifactSource's. | FromDirectory FilePath [ArtifactSource]+ -- ^ Assume a local directory as starting point for all+ -- relative source files in the nested 'ArtifactSource's. | IntoDirectory FilePath [ArtifactSource]+ -- ^ Specify an output directory for all the files+ -- generated by the nested 'ArtifactSource's | Concatenation FilePath [ArtifactSource]+ -- ^ __Deprecated__ Concatenate the files generated by the+ -- nested 'ArtifactSource's. The nested, generated files+ -- are not written when they are concatenated. deriving (Read, Show, Eq) +-- | Identify an artifact. __Deprecated__ TODO: B9 does not check if all+-- instances IDs are unique. newtype InstanceId = IID String deriving (Read, Show, Typeable, Data, Eq) +-- | The variable containing the instance id. __Deprecated__ instanceIdKey :: String instanceIdKey = "instance_id" +-- | The variable containing the buildId that identifies each execution of+-- B9. For more info about variable substitution in source files see+-- 'B9.Content.StringTemplate' buildIdKey :: String buildIdKey = "build_id" +-- | The variable containing the date and time a build was started. For more+-- info about variable substitution in source files see+-- 'B9.Content.StringTemplate' buildDateKey :: String buildDateKey = "build_date" +-- | Define an __output__ of a build. Assemblies are nested into+-- 'ArtifactGenerator's. They contain all the files defined by the 'Sources'+-- they are nested into. data ArtifactAssembly = CloudInit [CloudInitType] FilePath+ -- ^ Generate a __cloud-init__ compatible directory, ISO-+ -- or VFAT image, as specified by the list of+ -- 'CloudInitType's. Every item will use the second+ -- argument to create an appropriate /file name/,+ -- e.g. for the 'CI_ISO' type the output is @second_param.iso@. | VmImages [ImageTarget] VmScript+ -- ^ a set of VM-images that were created by executing a+ -- build script on them. deriving (Read, Show, Typeable, Data, Eq) +-- | A type representing the targets assembled by+-- 'B9.ArtifactGeneratorImpl.assemble' from an 'ArtifactAssembly'. There is a+-- list of 'ArtifactTarget's because e.g. a single 'CloudInit' can produce upto+-- three output files, a directory, an ISO image and a VFAT image. data AssembledArtifact = AssembledArtifact InstanceId [ArtifactTarget] deriving (Read, Show, Typeable, Data, Eq)
src/lib/B9/Content/AST.hs view
@@ -34,25 +34,43 @@ import Test.QuickCheck import B9.QCUtil +-- | Types of values that can be parsed/rendered from/to 'ByteString's. This+-- class is used as basis for the 'ASTish' class. class (Semigroup a) => ConcatableSyntax a where- decodeSyntax :: FilePath -> B.ByteString -> Either String a+ -- Parse a bytestring into an 'a', and return @Left errorMessage@ or @Right a@+ decodeSyntax :: FilePath -- ^ An arbitrary string for error messages that corresponds+ -- to a possible input file.+ -> B.ByteString -- ^ The raw input to parse+ -> Either String a+ -- Generate a string representation of @a@ encodeSyntax :: a -> B.ByteString instance ConcatableSyntax B.ByteString where decodeSyntax _ = Right encodeSyntax = id --- | A simple AST wrapper for merging embeded ASTs-data AST c a = ASTObj [(String, AST c a)]- | ASTArr [AST c a]- | ASTMerge [AST c a]- | ASTEmbed c- | ASTString String- | ASTParse SourceFile- | AST a+-- | Describe how to create structured content that has a tree-like syntactic+-- structure, e.g. yaml, JSON and erlang-proplists. The first parameter defines+-- a /context/ into which the 'AST' is embeded,+-- e.g. B9.Content.Generator.Content'. The second parameter defines a specifix+-- syntax, e.g 'B9.Content.ErlangPropList' that the 'AST' value generates.+data AST c a = ASTObj [(String, AST c a)] -- ^ Create an object similar to a+ -- Json object.+ | ASTArr [AST c a] -- ^ An array.+ | ASTMerge [AST c a] -- ^ Merge the nested elements, this is a very+ -- powerful tool that allows to combine+ -- several inputs in a smart and safe way,+ -- e.g. by merging the values of the same+ -- fields in yaml objects.+ | ASTEmbed c -- Embed some pure content.+ | ASTString String -- A string literal.+ | ASTParse SourceFile -- An 'AST' obtained from parsing a source+ -- file that contains a string corresponding+ -- to the type parameter @a@, e.g. 'YamlObject's+ | AST a -- Embed a literal @a@. deriving (Read, Show, Typeable, Data, Eq) --- | Structure data into an abstract syntax tree+-- | Types of values that describe content, that can be created from an 'AST'. class (ConcatableSyntax a) => ASTish a where fromAST :: (CanRender c ,Applicative m@@ -62,7 +80,7 @@ => AST c a -> m a --- | Things that produce byte strings.+-- | Types of values that can be /rendered/ into a 'ByteString' class CanRender c where render :: (Functor m ,Applicative m
src/lib/B9/Content/StringTemplate.hs view
@@ -1,5 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}-{-| Utility functions bnased on 'Data.Text.Template' to offer @ $var @ variable+{-| Utility functions based on 'Data.Text.Template' to offer @ $var @ variable expansion in string throughout a B9 artifact. -} module B9.Content.StringTemplate (subst ,substE
src/lib/B9/DiskImages.hs view
@@ -1,12 +1,14 @@ {-| Data types that describe all B9 relevant elements of virtual machine disk- images. -}+images.-} module B9.DiskImages where import Data.Semigroup import Data.Data import System.FilePath --- | Build target for disk images.+-- | Build target for disk images; the destination, format and size of the image+-- to generate, as well as how to create or obtain the image before a+-- 'B9.Vm.VmScript' is executed with the image mounted at a 'MountPoint'. data ImageTarget = ImageTarget ImageDestination ImageSource@@ -19,22 +21,44 @@ -- | The destination of an image. data ImageDestination = Share String ImageType ImageResize+ -- ^ Create the image and some meta data so that other+ -- builds can use them as 'ImageSource's via 'From'. | LiveInstallerImage String FilePath ImageResize+ -- ^ __DEPRECATED__ Export a raw image that can directly+ -- be booted. | LocalFile Image ImageResize+ -- ^ Write an image file to the path in the first+ -- argument., possible resizing it, | Transient+ -- ^ Do not export the image. Usefule if the main+ -- objective of the b9 build is not an image file, but+ -- rather some artifact produced by executing by a+ -- containerize build. deriving (Read, Show, Typeable, Data,Eq) -- | Specification of how the image to build is obtained. data ImageSource = EmptyImage String FileSystem ImageType ImageSize+ -- ^ Create an empty image file having a file system label+ -- (first parameter), a file system type (e.g. 'Ext4') and an+ -- 'ImageSize' | CopyOnWrite Image+ -- ^ __DEPRECATED__ | SourceImage Image Partition ImageResize+ -- ^ Clone an existing image file; if the image file contains+ -- partitions, select the partition to use, b9 will extract+ -- that partition by reading the offset of the partition from+ -- the partition table and extract it using @dd@. | From String ImageResize+ -- ^ Use an image previously shared by via 'Share'. deriving (Show,Read,Typeable,Data,Eq) -data Partition = NoPT | Partition Int- deriving (Eq, Show, Read, Typeable, Data)-+-- | The partition to extract.+data Partition = NoPT -- ^ There is no partition table on the image+ | Partition Int -- ^ Extract partition @n@ @n@ must be in @0..3@+ deriving (Eq, Show, Read, Typeable, Data) +-- | A vm disk image file consisting of a path to the image file, and the type+-- and file system. data Image = Image FilePath ImageType FileSystem deriving (Eq, Show, Read, Typeable, Data) @@ -50,10 +74,20 @@ data SizeUnit = B | KB | MB | GB deriving (Eq, Show, Read, Ord, Typeable, Data) +-- | How to resize an image file. data ImageResize = ResizeImage ImageSize+ -- ^ Resize the image __but not the file system__. Note that+ -- a file system contained in the image file might be+ -- corrupted by this operation. To not only resize the image+ -- file but also the fil system contained in it, use+ -- 'Resize'. | Resize ImageSize+ -- ^ Resize an image and the contained file system. | ShrinkToMinimum+ -- ^ Resize an image and the contained file system to the+ -- smallest size to fit the contents of the file system. | KeepSize+ -- ^ Do not change the image size. deriving (Eq, Show, Read, Typeable, Data) type Mounted a = (a, MountPoint)@@ -87,7 +121,8 @@ changeImageDirectory dir (Image img fmt fs) = Image img' fmt fs where img' = dir </> takeFileName img --- | 'SharedImage' holds all data necessary to identify an image shared.+-- | 'SharedImage' holds all data necessary to identify an image shared. Shared+-- images are stored in 'B9.Repository's. data SharedImage = SharedImage SharedImageName SharedImageDate SharedImageBuildId
src/lib/B9/LibVirtLXC.hs view
@@ -32,22 +32,23 @@ setUp = do cfg <- configureLibVirtLXC buildId <- getBuildId- buildDir <- getBuildDir+ buildBaseDir <- getBuildDir uuid <- randomUUID let scriptDirHost = buildDir </> "init-script" scriptDirGuest = "/" ++ buildId+ domainFile = buildBaseDir </> uuid' <.> domainConfig domain = createDomain cfg env buildId uuid' scriptDirHost scriptDirGuest uuid' = printf "%U" uuid script = Begin [scriptIn, successMarkerCmd scriptDirGuest]- domainFile <- (</> domainConfig) <$> getBuildDir+ buildDir = buildBaseDir </> uuid' liftIO $ do createDirectoryIfMissing True scriptDirHost writeSh (scriptDirHost </> initScript) script writeFile domainFile domain return $ Context scriptDirHost uuid domainFile cfg successMarkerCmd scriptDirGuest =- As "root" [In scriptDirGuest [Run "touch" [successMarkerFile]]]+ In scriptDirGuest [Run "touch" [successMarkerFile]] execute (Context scriptDirHost uuid domainFile cfg) = do let virsh = virshCommand cfg
src/lib/B9/Repository.hs view
@@ -1,9 +1,8 @@-{-| B9 has a concept of shared images. Images can be pulled and pushed to/from- remote locations via rsync+ssh. B9 also maintains a local cache; the whole- thing is supposed to be build-server-safe, that means no two builds shall- interfere with each other. This is accomplished by refraining from- automatic cache updates from/to remote repositories.- -}+{-| B9 has a concept of 'B9.DiskImages.SharedImaged'. Shared images can be pulled and+pushed to/from remote locations via rsync+ssh. B9 also maintains a local cache;+the whole thing is supposed to be build-server-safe, that means no two builds+shall interfere with each other. This is accomplished by refraining from+automatic cache updates from/to remote repositories.-} module B9.Repository (RemoteRepo(..) ,remoteRepoRepoId ,RepoCache(..)