Thomas Hipp 405598076a
*: Use yaml tags in templates
Signed-off-by: Thomas Hipp <thomas.hipp@canonical.com>
2018-03-14 17:19:21 +01:00

235 lines
5.1 KiB
Go

package image
import (
"fmt"
"os"
"path/filepath"
"strings"
"time"
lxd "github.com/lxc/lxd/shared"
"github.com/lxc/distrobuilder/shared"
)
const maxLXCCompatLevel = 5
// LXCImage represents a LXC image.
type LXCImage struct {
sourceDir string
targetDir string
cacheDir string
definition shared.Definition
}
// NewLXCImage returns a LXCImage.
func NewLXCImage(sourceDir, targetDir, cacheDir string, definition shared.Definition) *LXCImage {
img := LXCImage{
sourceDir,
targetDir,
cacheDir,
definition,
}
// create metadata directory
err := os.MkdirAll(filepath.Join(cacheDir, "metadata"), 0755)
if err != nil {
return nil
}
return &img
}
// AddTemplate adds an entry to the templates file.
func (l *LXCImage) AddTemplate(path string) error {
metaDir := filepath.Join(l.cacheDir, "metadata")
file, err := os.OpenFile(filepath.Join(metaDir, "templates"),
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer file.Close()
_, err = file.WriteString(fmt.Sprintf("%v\n", path))
if err != nil {
return fmt.Errorf("Failed to write to template file: %s", err)
}
return nil
}
// Build creates a LXC image.
func (l *LXCImage) Build() error {
err := l.createMetadata()
if err != nil {
return err
}
err = l.packMetadata()
if err != nil {
return err
}
err = shared.Pack(filepath.Join(l.targetDir, "rootfs.tar"), "xz", l.sourceDir, ".")
if err != nil {
return err
}
return nil
}
func (l *LXCImage) createMetadata() error {
metaDir := filepath.Join(l.cacheDir, "metadata")
for _, c := range l.definition.Targets.LXC.Config {
// If not specified, create files up to ${maxLXCCompatLevel}
if c.Before == 0 {
c.Before = maxLXCCompatLevel + 1
}
for i := uint(1); i < maxLXCCompatLevel+1; i++ {
// Bound checking
if c.After < c.Before {
if i <= c.After || i >= c.Before {
continue
}
} else if c.After >= c.Before {
if i <= c.After && i >= c.Before {
continue
}
}
switch c.Type {
case "all":
err := l.writeConfig(i, filepath.Join(metaDir, "config"), c.Content)
if err != nil {
return err
}
err = l.writeConfig(i, filepath.Join(metaDir, "config.user"), c.Content)
if err != nil {
return err
}
case "system":
err := l.writeConfig(i, filepath.Join(metaDir, "config"), c.Content)
if err != nil {
return err
}
case "user":
err := l.writeConfig(i, filepath.Join(metaDir, "config.user"), c.Content)
if err != nil {
return err
}
}
}
}
err := l.writeMetadata(filepath.Join(metaDir, "create-message"),
l.definition.Targets.LXC.CreateMessage, false)
if err != nil {
return fmt.Errorf("Error writing 'create-message': %s", err)
}
err = l.writeMetadata(filepath.Join(metaDir, "expiry"),
fmt.Sprint(shared.GetExpiryDate(time.Now(), l.definition.Image.Expiry).Unix()),
false)
if err != nil {
return fmt.Errorf("Error writing 'expiry': %s", err)
}
var excludesUser string
if lxd.PathExists(filepath.Join(l.sourceDir, "dev")) {
err := filepath.Walk(filepath.Join(l.sourceDir, "dev"),
func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.Mode()&os.ModeDevice != 0 {
excludesUser += fmt.Sprintf("%s\n",
strings.TrimPrefix(path, l.sourceDir))
}
return nil
})
if err != nil {
return fmt.Errorf("Error while walking /dev: %s", err)
}
}
err = l.writeMetadata(filepath.Join(metaDir, "excludes-user"), strings.TrimSuffix(excludesUser, "\n"),
false)
if err != nil {
return fmt.Errorf("Error writing 'excludes-user': %s", err)
}
return nil
}
func (l *LXCImage) packMetadata() error {
files := []string{"create-message", "expiry", "excludes-user"}
// Get all config and config.user files
configs, err := filepath.Glob(filepath.Join(l.cacheDir, "metadata", "config*"))
if err != nil {
return err
}
for _, c := range configs {
files = append(files, filepath.Base(c))
}
if lxd.PathExists(filepath.Join(l.cacheDir, "metadata", "templates")) {
files = append(files, "templates")
}
err = shared.Pack(filepath.Join(l.targetDir, "meta.tar"), "xz",
filepath.Join(l.cacheDir, "metadata"), files...)
if err != nil {
return fmt.Errorf("Failed to create metadata: %s", err)
}
return nil
}
func (l *LXCImage) writeMetadata(filename, content string, append bool) error {
var file *os.File
var err error
if append {
file, err = os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
} else {
file, err = os.Create(filename)
}
if err != nil {
return err
}
defer file.Close()
out, err := shared.RenderTemplate(content, l.definition)
if err != nil {
return err
}
_, err = file.WriteString(out + "\n")
if err != nil {
return err
}
return nil
}
func (l *LXCImage) writeConfig(compatLevel uint, filename, content string) error {
// Only add suffix if it's not the latest compatLevel
if compatLevel != maxLXCCompatLevel {
filename = fmt.Sprintf("%s.%d", filename, compatLevel)
}
err := l.writeMetadata(filename, content, true)
if err != nil {
return fmt.Errorf("Error writing '%s': %s", filepath.Base(filename), err)
}
return nil
}