Merge pull request #21 from monstermunchkin/issues/19
Add missing tests for lxc/lxd image generation
This commit is contained in:
commit
4faff0b0df
@ -6,6 +6,10 @@ os:
|
||||
go:
|
||||
- 1.9
|
||||
|
||||
before_install:
|
||||
- "sudo apt-get -qq update"
|
||||
- "sudo apt-get install -y squashfs-tools"
|
||||
|
||||
install:
|
||||
- "mkdir -p $GOPATH/github.com/lxc"
|
||||
- "rsync -az ${TRAVIS_BUILD_DIR}/ $HOME/gopath/src/github.com/lxc/distrobuilder/"
|
||||
|
36
image/lxc.go
36
image/lxc.go
@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
lxd "github.com/lxc/lxd/shared"
|
||||
pongo2 "gopkg.in/flosch/pongo2.v3"
|
||||
|
||||
"github.com/lxc/distrobuilder/shared"
|
||||
@ -102,15 +103,24 @@ func (l *LXCImage) createMetadata() error {
|
||||
|
||||
var excludesUser string
|
||||
|
||||
filepath.Walk(filepath.Join(l.cacheDir, "rootfs", "dev"),
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
if info.Mode()&os.ModeDevice != 0 {
|
||||
excludesUser += fmt.Sprintf("%s\n",
|
||||
strings.TrimPrefix(path, filepath.Join(l.cacheDir, "rootfs")))
|
||||
}
|
||||
if lxd.PathExists(filepath.Join(l.cacheDir, "rootfs", "dev")) {
|
||||
err := filepath.Walk(filepath.Join(l.cacheDir, "rootfs", "dev"),
|
||||
func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if info.Mode()&os.ModeDevice != 0 {
|
||||
excludesUser += fmt.Sprintf("%s\n",
|
||||
strings.TrimPrefix(path, filepath.Join(l.cacheDir, "rootfs")))
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error while walking /dev: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
err = l.writeMetadata(filepath.Join(metaDir, "excludes-user"), excludesUser)
|
||||
if err != nil {
|
||||
@ -121,8 +131,14 @@ func (l *LXCImage) createMetadata() error {
|
||||
}
|
||||
|
||||
func (l *LXCImage) packMetadata() error {
|
||||
err := shared.Pack("meta.tar.xz", filepath.Join(l.cacheDir, "metadata"), "config",
|
||||
"config-user", "create-message", "expiry", "templates", "excludes-user")
|
||||
files := []string{"config", "config-user", "create-message", "expiry",
|
||||
"excludes-user"}
|
||||
|
||||
if lxd.PathExists(filepath.Join(l.cacheDir, "metadata", "templates")) {
|
||||
files = append(files, "templates")
|
||||
}
|
||||
|
||||
err := shared.Pack("meta.tar.xz", filepath.Join(l.cacheDir, "metadata"), files...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to create metadata: %s", err)
|
||||
}
|
||||
|
257
image/lxc_test.go
Normal file
257
image/lxc_test.go
Normal file
@ -0,0 +1,257 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"syscall"
|
||||
"testing"
|
||||
|
||||
"github.com/lxc/distrobuilder/shared"
|
||||
)
|
||||
|
||||
var lxcImageDef = shared.DefinitionImage{
|
||||
Description: "{{ image. Distribution|capfirst }} {{ image.Release }}",
|
||||
Distribution: "ubuntu",
|
||||
Release: "17.10",
|
||||
Arch: "amd64",
|
||||
Expiry: "30d",
|
||||
Name: "{{ image.Distribution|lower }}-{{ image.Release }}-{{ image.Arch }}-{{ creation_date }}",
|
||||
}
|
||||
|
||||
var lxcTarget = shared.DefinitionTargetLXC{
|
||||
CreateMessage: "Welcome to {{ image.Distribution|capfirst}} {{ image.Release }}",
|
||||
Config: `lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf
|
||||
lxc.arch = x86_64`,
|
||||
ConfigUser: `lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf
|
||||
lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.userns.conf
|
||||
lxc.arch = x86_64`,
|
||||
}
|
||||
|
||||
func lxcCacheDir() string {
|
||||
wd, _ := os.Getwd()
|
||||
return filepath.Join(wd, "distrobuilder-test")
|
||||
}
|
||||
|
||||
func setupLXC() *LXCImage {
|
||||
return NewLXCImage(lxcCacheDir(), lxcImageDef, lxcTarget)
|
||||
}
|
||||
|
||||
func teardownLXC() {
|
||||
os.RemoveAll(lxcCacheDir())
|
||||
}
|
||||
|
||||
func TestNewLXCImage(t *testing.T) {
|
||||
image := NewLXCImage(lxcCacheDir(), lxcImageDef, lxcTarget)
|
||||
defer teardownLXC()
|
||||
|
||||
if image.cacheDir != lxcCacheDir() {
|
||||
t.Fatalf("Expected image.cacheDir to be '%s', got '%s'", lxcCacheDir(),
|
||||
image.cacheDir)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(image.definition, lxcImageDef) {
|
||||
t.Fatalf("lxcImageDef and image.definition are not equal")
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(image.target, lxcTarget) {
|
||||
t.Fatalf("lxcTarget and image.target are not equal")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLXCAddTemplate(t *testing.T) {
|
||||
image := setupLXC()
|
||||
defer teardownLXC()
|
||||
|
||||
// Make sure templates file is empty.
|
||||
info, err := os.Stat(filepath.Join(lxcCacheDir(), "metadata", "templates"))
|
||||
if err == nil && info.Size() > 0 {
|
||||
t.Fatalf("Expected file size to be 0, got %d", info.Size())
|
||||
}
|
||||
|
||||
// Add first template entry.
|
||||
image.AddTemplate("/path/file1")
|
||||
file, err := os.Open(filepath.Join(lxcCacheDir(), "metadata", "templates"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// Copy file content to buffer.
|
||||
var buffer bytes.Buffer
|
||||
io.Copy(&buffer, file)
|
||||
file.Close()
|
||||
|
||||
if buffer.String() != "/path/file1\n" {
|
||||
t.Fatalf("Expected templates content to be '%s', got '%s'",
|
||||
"/path/file", buffer.String())
|
||||
}
|
||||
|
||||
// Add second template entry.
|
||||
image.AddTemplate("/path/file2")
|
||||
file, err = os.Open(filepath.Join(lxcCacheDir(), "metadata", "templates"))
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// Copy file content to buffer.
|
||||
buffer.Reset()
|
||||
io.Copy(&buffer, file)
|
||||
file.Close()
|
||||
|
||||
if buffer.String() != "/path/file1\n/path/file2\n" {
|
||||
t.Fatalf("Expected templates content to be '%s', got '%s'",
|
||||
"/path/file1\n/path/file2", buffer.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestLXCBuild(t *testing.T) {
|
||||
image := setupLXC()
|
||||
defer teardownLXC()
|
||||
|
||||
err := os.MkdirAll(filepath.Join(lxcCacheDir(), "rootfs"), 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
err = image.Build()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
os.Remove("meta.tar.xz")
|
||||
os.Remove("rootfs.tar.xz")
|
||||
}()
|
||||
}
|
||||
|
||||
func TestLXCCreateMetadata(t *testing.T) {
|
||||
defaultImage := setupLXC()
|
||||
defer teardownLXC()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
shouldFail bool
|
||||
expectedError string
|
||||
prepareImage func(LXCImage) *LXCImage
|
||||
}{
|
||||
{
|
||||
"valid metadata",
|
||||
false,
|
||||
"",
|
||||
func(l LXCImage) *LXCImage { return &l },
|
||||
},
|
||||
{
|
||||
"invalid config template",
|
||||
true,
|
||||
"Error writing 'config': .+",
|
||||
func(l LXCImage) *LXCImage {
|
||||
l.target.Config = "{{ invalid }"
|
||||
return &l
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid config-user template",
|
||||
true,
|
||||
"Error writing 'config-user': .+",
|
||||
func(l LXCImage) *LXCImage {
|
||||
l.target.ConfigUser = "{{ invalid }"
|
||||
return &l
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid create-message template",
|
||||
true,
|
||||
"Error writing 'create-message': .+",
|
||||
func(l LXCImage) *LXCImage {
|
||||
l.target.CreateMessage = "{{ invalid }"
|
||||
return &l
|
||||
},
|
||||
},
|
||||
{
|
||||
"existing dev directory",
|
||||
false,
|
||||
"",
|
||||
func(l LXCImage) *LXCImage {
|
||||
// Create /dev and device file.
|
||||
os.MkdirAll(filepath.Join(lxcCacheDir(), "rootfs", "dev"), 0755)
|
||||
syscall.Mknod(filepath.Join(lxcCacheDir(), "rootfs", "dev", "null"),
|
||||
syscall.S_IFCHR, 0)
|
||||
return &l
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
log.Printf("Running test #%d: %s", i, tt.name)
|
||||
image := tt.prepareImage(*defaultImage)
|
||||
err := image.createMetadata()
|
||||
if tt.shouldFail {
|
||||
if err == nil {
|
||||
t.Fatal("Expected to fail, but didn't")
|
||||
}
|
||||
|
||||
match, _ := regexp.MatchString(tt.expectedError, err.Error())
|
||||
if !match {
|
||||
t.Fatalf("Expected to fail with '%s', got '%s'", tt.expectedError,
|
||||
err.Error())
|
||||
}
|
||||
}
|
||||
if !tt.shouldFail && err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLXCPackMetadata(t *testing.T) {
|
||||
image := setupLXC()
|
||||
defer func() {
|
||||
teardownLXC()
|
||||
os.Remove("meta.tar.xz")
|
||||
}()
|
||||
|
||||
err := image.createMetadata()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
err = image.packMetadata()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// Include templates directory.
|
||||
image.AddTemplate("/path/file")
|
||||
err = image.packMetadata()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
// Provoke error by removing the metadata directory
|
||||
os.RemoveAll(filepath.Join(lxcCacheDir(), "metadata"))
|
||||
err = image.packMetadata()
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestLXCWriteMetadata(t *testing.T) {
|
||||
image := setupLXC()
|
||||
defer teardownLXC()
|
||||
|
||||
// Should fail due to invalid path
|
||||
err := image.writeMetadata("/path/file", "")
|
||||
if err == nil {
|
||||
t.Fatal("Expected failure")
|
||||
}
|
||||
|
||||
// Should succeed
|
||||
err = image.writeMetadata("test", "metadata")
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected failure: %s", err)
|
||||
}
|
||||
os.Remove("test")
|
||||
}
|
26
image/lxd.go
26
image/lxd.go
@ -57,33 +57,44 @@ func (l *LXDImage) Build(unified bool) error {
|
||||
return fmt.Errorf("Failed to write metadata: %s", err)
|
||||
}
|
||||
|
||||
if unified {
|
||||
var fname string
|
||||
paths := []string{"rootfs", "templates", "metadata.yaml"}
|
||||
paths := []string{"metadata.yaml"}
|
||||
|
||||
// Only include templates directory in the tarball if it's present.
|
||||
info, err := os.Stat(filepath.Join(l.cacheDir, "templates"))
|
||||
if err == nil && info.IsDir() {
|
||||
paths = append(paths, "templates")
|
||||
}
|
||||
|
||||
if unified {
|
||||
ctx := pongo2.Context{
|
||||
"image": l.definition,
|
||||
"creation_date": l.creationDate.Format("20060201_1504"),
|
||||
}
|
||||
|
||||
var fname string
|
||||
if l.definition.Name != "" {
|
||||
// Use a custom name for the unified tarball.
|
||||
fname, _ = renderTemplate(l.definition.Name, ctx)
|
||||
} else {
|
||||
// Default name for the unified tarball.
|
||||
fname = "lxd"
|
||||
}
|
||||
|
||||
paths = append(paths, "rootfs")
|
||||
err = shared.Pack(fmt.Sprintf("%s.tar.xz", fname), l.cacheDir, paths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Create rootfs as squashfs.
|
||||
err = shared.RunCommand("mksquashfs", filepath.Join(l.cacheDir, "rootfs"),
|
||||
"rootfs.squashfs", "-noappend")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = shared.Pack("lxd.tar.xz", l.cacheDir, "templates", "metadata.yaml")
|
||||
// Create metadata tarball.
|
||||
err = shared.Pack("lxd.tar.xz", l.cacheDir, paths...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -107,9 +118,12 @@ func (l *LXDImage) createMetadata() error {
|
||||
return err
|
||||
}
|
||||
|
||||
l.Metadata.Architecture = arch
|
||||
// Use proper architecture name from now on.
|
||||
l.definition.Arch = arch
|
||||
|
||||
l.Metadata.Architecture = l.definition.Arch
|
||||
l.Metadata.CreationDate = l.creationDate.Unix()
|
||||
l.Metadata.Properties["architecture"] = arch
|
||||
l.Metadata.Properties["architecture"] = l.definition.Arch
|
||||
l.Metadata.Properties["os"] = l.definition.Distribution
|
||||
l.Metadata.Properties["release"] = l.definition.Release
|
||||
|
||||
|
179
image/lxd_test.go
Normal file
179
image/lxd_test.go
Normal file
@ -0,0 +1,179 @@
|
||||
package image
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/lxc/distrobuilder/shared"
|
||||
lxd "github.com/lxc/lxd/shared"
|
||||
)
|
||||
|
||||
var lxdImageDef = shared.DefinitionImage{
|
||||
Description: "{{ image. Distribution|capfirst }} {{ image.Release }}",
|
||||
Distribution: "ubuntu",
|
||||
Release: "17.10",
|
||||
Arch: "amd64",
|
||||
Expiry: "30d",
|
||||
Name: "{{ image.Distribution|lower }}-{{ image.Release }}-{{ image.Arch }}-{{ creation_date }}",
|
||||
}
|
||||
|
||||
func setupLXD(t *testing.T) *LXDImage {
|
||||
cacheDir := filepath.Join(os.TempDir(), "distrobuilder-test")
|
||||
|
||||
err := os.MkdirAll(filepath.Join(cacheDir, "rootfs"), 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create rootfs directory: %s", err)
|
||||
}
|
||||
|
||||
err = os.MkdirAll(filepath.Join(cacheDir, "templates"), 0755)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to create templates directory: %s", err)
|
||||
}
|
||||
|
||||
image := NewLXDImage(cacheDir, lxdImageDef)
|
||||
|
||||
// Override creation date
|
||||
image.creationDate = time.Date(2006, 1, 2, 3, 4, 5, 0, time.UTC)
|
||||
|
||||
// Check cache directory
|
||||
if image.cacheDir != cacheDir {
|
||||
teardownLXD(t)
|
||||
t.Fatalf("Expected cacheDir to be '%s', is '%s'", cacheDir, image.cacheDir)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(lxdImageDef, image.definition) {
|
||||
teardownLXD(t)
|
||||
t.Fatal("lxdImageDef and image.definition are not equal")
|
||||
}
|
||||
|
||||
return image
|
||||
}
|
||||
|
||||
func teardownLXD(t *testing.T) {
|
||||
os.RemoveAll(filepath.Join(os.TempDir(), "distrobuilder-test"))
|
||||
}
|
||||
|
||||
func TestLXDBuild(t *testing.T) {
|
||||
image := setupLXD(t)
|
||||
defer teardownLXD(t)
|
||||
|
||||
testLXDBuildSplitImage(t, image)
|
||||
testLXDBuildUnifiedImage(t, image)
|
||||
}
|
||||
|
||||
func testLXDBuildSplitImage(t *testing.T, image *LXDImage) {
|
||||
// Create split tarball and squashfs.
|
||||
err := image.Build(false)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
defer func() {
|
||||
os.Remove("lxd.tar.xz")
|
||||
os.Remove("rootfs.squashfs")
|
||||
}()
|
||||
|
||||
if !lxd.PathExists("lxd.tar.xz") {
|
||||
t.Fatalf("File '%s' does not exist", "lxd.tar.xz")
|
||||
}
|
||||
|
||||
if !lxd.PathExists("rootfs.squashfs") {
|
||||
t.Fatalf("File '%s' does not exist", "rootfs.squashfs")
|
||||
}
|
||||
}
|
||||
|
||||
func testLXDBuildUnifiedImage(t *testing.T, image *LXDImage) {
|
||||
// Create unified tarball with custom name.
|
||||
err := image.Build(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
defer os.Remove("ubuntu-17.10-x86_64-20060201_0304.tar.xz")
|
||||
|
||||
if !lxd.PathExists("ubuntu-17.10-x86_64-20060201_0304.tar.xz") {
|
||||
t.Fatalf("File '%s' does not exist", "ubuntu-17.10-x86_64-20060201_0304.tar.xz")
|
||||
}
|
||||
|
||||
// Create unified tarball with default name.
|
||||
image.definition.Name = ""
|
||||
err = image.Build(true)
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
defer os.Remove("lxd.tar.xz")
|
||||
|
||||
if !lxd.PathExists("lxd.tar.xz") {
|
||||
t.Fatalf("File '%s' does not exist", "lxd.tar.xz")
|
||||
}
|
||||
}
|
||||
|
||||
func TestLXDCreateMetadata(t *testing.T) {
|
||||
image := setupLXD(t)
|
||||
defer teardownLXD(t)
|
||||
|
||||
err := image.createMetadata()
|
||||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %s", err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
have string
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
"Architecture",
|
||||
image.Metadata.Architecture,
|
||||
"x86_64",
|
||||
},
|
||||
{
|
||||
"CreationDate",
|
||||
string(image.Metadata.CreationDate),
|
||||
string(image.creationDate.Unix()),
|
||||
},
|
||||
{
|
||||
"Properties[architecture]",
|
||||
image.Metadata.Properties["architecture"],
|
||||
"x86_64",
|
||||
},
|
||||
{
|
||||
"Properties[os]",
|
||||
image.Metadata.Properties["os"],
|
||||
lxdImageDef.Distribution,
|
||||
},
|
||||
{
|
||||
"Properties[release]",
|
||||
image.Metadata.Properties["release"],
|
||||
lxdImageDef.Release,
|
||||
},
|
||||
{
|
||||
"Properties[description]",
|
||||
image.Metadata.Properties["description"],
|
||||
fmt.Sprintf("%s %s", strings.Title(lxdImageDef.Distribution),
|
||||
lxdImageDef.Release),
|
||||
},
|
||||
{
|
||||
"Properties[name]",
|
||||
image.Metadata.Properties["name"],
|
||||
fmt.Sprintf("%s-%s-%s-%s", strings.ToLower(lxdImageDef.Distribution),
|
||||
lxdImageDef.Release, "x86_64", image.creationDate.Format("20060201_1504")),
|
||||
},
|
||||
{
|
||||
"ExpiryDate",
|
||||
fmt.Sprintf("%d", image.Metadata.ExpiryDate),
|
||||
"1138763045",
|
||||
},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
log.Printf("Running test #%d: %s", i, tt.name)
|
||||
if tt.have != tt.expected {
|
||||
t.Fatalf("Expected '%s', got '%s'", tt.expected, tt.have)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user