2018-02-09 10:17:06 +01:00
package image
import (
"fmt"
"os"
"path/filepath"
"time"
"github.com/lxc/distrobuilder/shared"
2018-02-22 16:32:43 +01:00
"github.com/lxc/lxd/shared/api"
2018-02-09 10:17:06 +01:00
"github.com/lxc/lxd/shared/osarch"
pongo2 "gopkg.in/flosch/pongo2.v3"
yaml "gopkg.in/yaml.v2"
)
// A LXDImage represents a LXD image.
type LXDImage struct {
2018-03-02 22:08:58 +01:00
sourceDir string
targetDir string
2018-02-09 10:17:06 +01:00
cacheDir string
creationDate time . Time
2018-02-22 16:32:43 +01:00
Metadata api . ImageMetadata
2018-02-09 10:17:06 +01:00
definition shared . DefinitionImage
}
// NewLXDImage returns a LXDImage.
2018-03-02 22:08:58 +01:00
func NewLXDImage ( sourceDir , targetDir , cacheDir string ,
imageDef shared . DefinitionImage ) * LXDImage {
2018-02-09 10:17:06 +01:00
return & LXDImage {
2018-03-02 22:08:58 +01:00
sourceDir ,
targetDir ,
2018-02-09 10:17:06 +01:00
cacheDir ,
time . Now ( ) ,
2018-02-22 16:32:43 +01:00
api . ImageMetadata {
Properties : make ( map [ string ] string ) ,
Templates : make ( map [ string ] * api . ImageMetadataTemplate ) ,
2018-02-09 10:17:06 +01:00
} ,
imageDef ,
}
}
// Build creates a LXD image.
func ( l * LXDImage ) Build ( unified bool ) error {
2018-02-22 16:28:26 +01:00
err := l . createMetadata ( )
2018-02-09 10:17:06 +01:00
if err != nil {
return nil
}
2018-03-02 22:08:58 +01:00
file , err := os . Create ( filepath . Join ( l . sourceDir , "metadata.yaml" ) )
2018-02-09 10:17:06 +01:00
if err != nil {
return err
}
defer file . Close ( )
data , err := yaml . Marshal ( l . Metadata )
if err != nil {
return err
}
2018-02-22 16:28:26 +01:00
_ , err = file . Write ( data )
if err != nil {
return fmt . Errorf ( "Failed to write metadata: %s" , err )
}
2018-02-09 10:17:06 +01:00
2018-02-26 17:04:29 +01:00
paths := [ ] string { "metadata.yaml" }
// Only include templates directory in the tarball if it's present.
2018-03-02 22:08:58 +01:00
info , err := os . Stat ( filepath . Join ( l . sourceDir , "templates" ) )
2018-02-26 17:04:29 +01:00
if err == nil && info . IsDir ( ) {
paths = append ( paths , "templates" )
}
2018-02-09 10:17:06 +01:00
2018-02-26 17:04:29 +01:00
if unified {
2018-02-26 14:20:25 +01:00
ctx := pongo2 . Context {
"image" : l . definition ,
2018-02-26 14:40:03 +01:00
"creation_date" : l . creationDate . Format ( "20060201_1504" ) ,
2018-02-26 14:20:25 +01:00
}
2018-02-26 17:04:29 +01:00
var fname string
2018-02-09 10:17:06 +01:00
if l . definition . Name != "" {
2018-02-26 17:04:29 +01:00
// Use a custom name for the unified tarball.
2018-02-26 14:20:25 +01:00
fname , _ = renderTemplate ( l . definition . Name , ctx )
2018-02-09 10:17:06 +01:00
} else {
2018-02-26 17:04:29 +01:00
// Default name for the unified tarball.
2018-02-09 10:17:06 +01:00
fname = "lxd"
}
2018-02-26 17:04:29 +01:00
paths = append ( paths , "rootfs" )
2018-03-02 22:08:58 +01:00
err = shared . Pack ( filepath . Join ( l . targetDir , fmt . Sprintf ( "%s.tar.xz" , fname ) ) ,
l . sourceDir , paths ... )
2018-02-09 10:17:06 +01:00
if err != nil {
return err
}
} else {
2018-02-26 17:04:29 +01:00
// Create rootfs as squashfs.
2018-03-02 22:08:58 +01:00
err = shared . RunCommand ( "mksquashfs" , filepath . Join ( l . sourceDir , "rootfs" ) ,
filepath . Join ( l . targetDir , "rootfs.squashfs" ) , "-noappend" )
2018-02-09 10:17:06 +01:00
if err != nil {
return err
}
2018-02-26 17:04:29 +01:00
// Create metadata tarball.
2018-03-02 22:08:58 +01:00
err = shared . Pack ( filepath . Join ( l . targetDir , "lxd.tar.xz" ) , l . sourceDir , paths ... )
2018-02-09 10:17:06 +01:00
if err != nil {
return err
}
}
return nil
}
func ( l * LXDImage ) createMetadata ( ) error {
var err error
2018-02-22 16:28:26 +01:00
// Get the arch ID of the provided architecture.
2018-02-09 10:17:06 +01:00
ID , err := osarch . ArchitectureId ( l . definition . Arch )
if err != nil {
return err
}
2018-02-22 16:28:26 +01:00
// Get the "proper" name of the architecture.
2018-02-09 10:17:06 +01:00
arch , err := osarch . ArchitectureName ( ID )
if err != nil {
return err
}
2018-02-26 17:04:29 +01:00
// Use proper architecture name from now on.
l . definition . Arch = arch
l . Metadata . Architecture = l . definition . Arch
2018-02-09 10:17:06 +01:00
l . Metadata . CreationDate = l . creationDate . Unix ( )
2018-02-26 17:04:29 +01:00
l . Metadata . Properties [ "architecture" ] = l . definition . Arch
2018-02-22 16:32:43 +01:00
l . Metadata . Properties [ "os" ] = l . definition . Distribution
l . Metadata . Properties [ "release" ] = l . definition . Release
2018-02-09 10:17:06 +01:00
2018-02-26 14:20:25 +01:00
ctx := pongo2 . Context {
"image" : l . definition ,
2018-02-26 14:40:03 +01:00
"creation_date" : l . creationDate . Format ( "20060201_1504" ) ,
2018-02-26 14:20:25 +01:00
}
l . Metadata . Properties [ "description" ] , err = renderTemplate ( l . definition . Description , ctx )
2018-02-09 10:17:06 +01:00
if err != err {
return nil
}
2018-02-26 14:20:25 +01:00
l . Metadata . Properties [ "name" ] , err = renderTemplate ( l . definition . Name , ctx )
2018-02-09 10:17:06 +01:00
if err != nil {
return err
}
2018-02-22 16:34:47 +01:00
l . Metadata . ExpiryDate = shared . GetExpiryDate ( l . creationDate , l . definition . Expiry ) . Unix ( )
2018-02-09 10:17:06 +01:00
return err
}