[lxc-devel] [distrobuilder/master] Add missing tests for lxc/lxd image generation
monstermunchkin on Github
lxc-bot at linuxcontainers.org
Wed Feb 28 10:09:52 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 322 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180228/9777bbc2/attachment.bin>
-------------- next part --------------
From d1946033d0481a8de624f8c7621c5c34e29a82ec Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Mon, 26 Feb 2018 17:04:29 +0100
Subject: [PATCH 1/2] image: Add LXD tests
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
image/lxd.go | 26 ++++++--
image/lxd_test.go | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 199 insertions(+), 6 deletions(-)
create mode 100644 image/lxd_test.go
diff --git a/image/lxd.go b/image/lxd.go
index ae3607c..f04b6c6 100644
--- a/image/lxd.go
+++ b/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
diff --git a/image/lxd_test.go b/image/lxd_test.go
new file mode 100644
index 0000000..48b2fad
--- /dev/null
+++ b/image/lxd_test.go
@@ -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)
+ }
+ }
+}
From 92bb3540a70a4875bb99555c0fd05246ce35b34d Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Tue, 27 Feb 2018 18:23:05 +0100
Subject: [PATCH 2/2] image: Add LXC tests
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
image/lxc.go | 36 +++++---
image/lxc_test.go | 257 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 283 insertions(+), 10 deletions(-)
create mode 100644 image/lxc_test.go
diff --git a/image/lxc.go b/image/lxc.go
index 449eba4..10b90f3 100644
--- a/image/lxc.go
+++ b/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)
}
diff --git a/image/lxc_test.go b/image/lxc_test.go
new file mode 100644
index 0000000..803d73b
--- /dev/null
+++ b/image/lxc_test.go
@@ -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")
+}
More information about the lxc-devel
mailing list