The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/distrobuilder/pull/16

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
TBD:
- [ ] Tests
- [ ] Template support for LXC metadata (#14)
From b48e4ba2dcc16e441bf52df048ba6693aadca295 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Mon, 12 Feb 2018 16:19:29 +0100
Subject: [PATCH 1/6] shared: Add Pack function

Signed-off-by: Thomas Hipp <[email protected]>
---
 shared/util.go | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/shared/util.go b/shared/util.go
index ddac11c..976e82a 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -79,3 +79,8 @@ func VerifyFile(signedFile, signatureFile string, keys 
[]string, keyserver strin
 
        return true, nil
 }
+
+// Pack creates an xz-compressed tarball.
+func Pack(filename, path string, args ...string) error {
+       return RunCommand("tar", append([]string{"-cJf", filename, "-C", path}, 
args...)...)
+}

From 2be9e89612eb0ac7efc0deeb9f941f01b31e107b Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Fri, 9 Feb 2018 10:17:06 +0100
Subject: [PATCH 2/6] image: Create LXD images

Signed-off-by: Thomas Hipp <[email protected]>
---
 image/lxd.go | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 177 insertions(+)
 create mode 100644 image/lxd.go

diff --git a/image/lxd.go b/image/lxd.go
new file mode 100644
index 0000000..88737e7
--- /dev/null
+++ b/image/lxd.go
@@ -0,0 +1,177 @@
+package image
+
+import (
+       "fmt"
+       "os"
+       "path/filepath"
+       "time"
+
+       "github.com/lxc/distrobuilder/shared"
+       "github.com/lxc/lxd/shared/osarch"
+       pongo2 "gopkg.in/flosch/pongo2.v3"
+       yaml "gopkg.in/yaml.v2"
+)
+
+// A LXDMetadataTemplate represents template information.
+type LXDMetadataTemplate struct {
+       Template   string                       `yaml:"template"`
+       When       []string                     `yaml:"when"`
+       Trigger    string                       `yaml:"trigger,omitempty"`
+       Path       string                       `yaml:"path,omitempty"`
+       Container  map[string]string            `yaml:"container,omitempty"`
+       Config     map[string]string            `yaml:"config,omitempty"`
+       Devices    map[string]map[string]string `yaml:"devices,omitempty"`
+       Properties map[string]string            `yaml:"properties,omitempty"`
+       CreateOnly bool                         `yaml:"create_only,omitempty"`
+}
+
+// A LXDMetadataProperties represents properties of the LXD image.
+type LXDMetadataProperties struct {
+       Architecture string `yaml:"architecture"`
+       Description  string `yaml:"description"`
+       OS           string `yaml:"os"`
+       Release      string `yaml:"release"`
+       Variant      string `yaml:"variant,omitempty"`
+       Name         string `yaml:"name,omitempty"`
+}
+
+// A LXDMetadata represents meta information about the LXD image.
+type LXDMetadata struct {
+       Architecture string                         `yaml:"architecture"`
+       CreationDate int64                          `yaml:"creation_date"`
+       Properties   LXDMetadataProperties          
`yaml:"properties,omitempty"`
+       Templates    map[string]LXDMetadataTemplate `yaml:"templates,omitempty"`
+}
+
+// A LXDImage represents a LXD image.
+type LXDImage struct {
+       cacheDir     string
+       creationDate time.Time
+       Metadata     LXDMetadata
+       definition   shared.DefinitionImage
+}
+
+// NewLXDImage returns a LXDImage.
+func NewLXDImage(cacheDir string, imageDef shared.DefinitionImage) *LXDImage {
+       return &LXDImage{
+               cacheDir,
+               time.Now(),
+               LXDMetadata{
+                       Templates: make(map[string]LXDMetadataTemplate),
+               },
+               imageDef,
+       }
+}
+
+// Build creates a LXD image.
+func (l *LXDImage) Build(unified bool) error {
+       var err error
+
+       err = l.createMetadata()
+       if err != nil {
+               return nil
+       }
+
+       file, err := os.Create(filepath.Join(l.cacheDir, "metadata.yaml"))
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       data, err := yaml.Marshal(l.Metadata)
+       if err != nil {
+               return err
+       }
+
+       file.Write(data)
+
+       if unified {
+               var fname string
+               paths := []string{"rootfs", "templates", "metadata.yaml"}
+
+               if l.definition.Name != "" {
+                       fname, _ = l.renderTemplate(l.definition.Name)
+               } else {
+                       fname = "lxd"
+               }
+
+               err = shared.Pack(fmt.Sprintf("%s.tar.xz", fname), l.cacheDir, 
paths...)
+               if err != nil {
+                       return err
+               }
+       } else {
+               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")
+               if err != nil {
+                       return err
+               }
+       }
+
+       return nil
+}
+
+func (l *LXDImage) createMetadata() error {
+       var err error
+
+       ID, err := osarch.ArchitectureId(l.definition.Arch)
+       if err != nil {
+               return err
+       }
+
+       arch, err := osarch.ArchitectureName(ID)
+       if err != nil {
+               return err
+       }
+
+       l.Metadata.Architecture = arch
+       l.Metadata.CreationDate = l.creationDate.Unix()
+       l.Metadata.Properties = LXDMetadataProperties{
+               Architecture: arch,
+               OS:           l.definition.Distribution,
+               Release:      l.definition.Release,
+       }
+
+       l.Metadata.Properties.Description, err = 
l.renderTemplate(l.definition.Description)
+       if err != err {
+               return nil
+       }
+
+       l.Metadata.Properties.Name, err = l.renderTemplate(l.definition.Name)
+       if err != nil {
+               return err
+       }
+
+       return err
+}
+
+func (l *LXDImage) renderTemplate(template string) (string, error) {
+       var (
+               err error
+               ret string
+       )
+
+       ctx := pongo2.Context{
+               "arch":          l.definition.Arch,
+               "os":            l.definition.Distribution,
+               "release":       l.definition.Release,
+               "variant":       l.definition.Variant,
+               "creation_date": l.creationDate.Format("20060201_1504"),
+       }
+
+       tpl, err := pongo2.FromString(template)
+       if err != nil {
+               return ret, err
+       }
+
+       ret, err = tpl.Execute(ctx)
+       if err != nil {
+               return ret, err
+       }
+
+       return ret, err
+}

From f11d041833556132818b202875dda0d9fcbd6459 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Mon, 12 Feb 2018 21:14:49 +0100
Subject: [PATCH 3/6] image: Create LXC images

Signed-off-by: Thomas Hipp <[email protected]>
---
 image/lxc.go | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 138 insertions(+)
 create mode 100644 image/lxc.go

diff --git a/image/lxc.go b/image/lxc.go
new file mode 100644
index 0000000..9a109c4
--- /dev/null
+++ b/image/lxc.go
@@ -0,0 +1,138 @@
+package image
+
+import (
+       "fmt"
+       "os"
+       "path/filepath"
+       "strings"
+       "time"
+
+       "github.com/lxc/distrobuilder/shared"
+)
+
+// LXCImage represents a LXC image.
+type LXCImage struct {
+       cacheDir   string
+       definition shared.DefinitionImage
+       target     shared.DefinitionTargetLXC
+}
+
+// NewLXCImage returns a LXCImage.
+func NewLXCImage(cacheDir string, definition shared.DefinitionImage,
+       target shared.DefinitionTargetLXC) *LXCImage {
+       img := LXCImage{
+               cacheDir,
+               definition,
+               target,
+       }
+
+       // 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()
+
+       file.WriteString(fmt.Sprintf("%v\n", path))
+
+       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("rootfs.tar.xz", l.cacheDir, "rootfs")
+       if err != nil {
+               return err
+       }
+
+       return nil
+}
+
+func (l *LXCImage) createMetadata() error {
+       metaDir := filepath.Join(l.cacheDir, "metadata")
+
+       err := l.writeMetadata(filepath.Join(metaDir, "config"), 
l.target.Config)
+       if err != nil {
+               return fmt.Errorf("Error writing 'config': %s", err)
+       }
+
+       err = l.writeMetadata(filepath.Join(metaDir, "config-user"), 
l.target.ConfigUser)
+       if err != nil {
+               return fmt.Errorf("Error writing 'config-user': %s", err)
+       }
+
+       err = l.writeMetadata(filepath.Join(metaDir, "create-message"), 
l.target.CreateMessage)
+       if err != nil {
+               return fmt.Errorf("Error writing 'create-message': %s", err)
+       }
+
+       err = l.writeMetadata(filepath.Join(metaDir, "expiry"), 
string(time.Now().Unix()))
+       if err != nil {
+               return fmt.Errorf("Error writing 'expiry': %s", err)
+       }
+       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")))
+                       }
+
+                       return nil
+               })
+
+       err = l.writeMetadata(filepath.Join(metaDir, "excludes-user"), 
excludesUser)
+       if err != nil {
+               return fmt.Errorf("Error writing 'excludes-user': %s", err)
+       }
+
+       return nil
+}
+
+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")
+       if err != nil {
+               return fmt.Errorf("Failed to create metadata: %s", err)
+       }
+
+       return nil
+}
+func (l *LXCImage) writeMetadata(filename, content string) error {
+       file, err := os.Create(filename)
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       _, err = file.WriteString(content)
+       if err != nil {
+               return err
+       }
+
+       return nil
+}

From 283ba50ae57f7ab091fd588ed787cf2c9b428ba6 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Wed, 21 Feb 2018 11:29:18 +0100
Subject: [PATCH 4/6] generators: Add generators

Signed-off-by: Thomas Hipp <[email protected]>
---
 generators/generators.go | 57 +++++++++++++++++++++++++++++++++++++++
 generators/hostname.go   | 60 +++++++++++++++++++++++++++++++++++++++++
 generators/hosts.go      | 69 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 186 insertions(+)
 create mode 100644 generators/generators.go
 create mode 100644 generators/hostname.go
 create mode 100644 generators/hosts.go

diff --git a/generators/generators.go b/generators/generators.go
new file mode 100644
index 0000000..cbbcfe7
--- /dev/null
+++ b/generators/generators.go
@@ -0,0 +1,57 @@
+package generators
+
+import (
+       "os"
+       "path/filepath"
+       "strings"
+
+       p "path"
+
+       "github.com/lxc/distrobuilder/image"
+)
+
+// Generator interface.
+type Generator interface {
+       CreateLXCData(string, string, *image.LXCImage) error
+       CreateLXDData(string, string, *image.LXDImage) error
+}
+
+// Get returns a Generator.
+func Get(generator string) Generator {
+       switch generator {
+       case "hostname":
+               return HostnameGenerator{}
+       case "hosts":
+               return HostsGenerator{}
+       }
+
+       return nil
+}
+
+// StoreFile caches a file which can be restored with the RestoreFiles 
function.
+func StoreFile(cacheDir, path string) error {
+       // create temporary directory containing old files
+       err := os.MkdirAll(filepath.Join(cacheDir, "tmp", p.Dir(path)), 0755)
+       if err != nil {
+               return err
+       }
+
+       return os.Rename(filepath.Join(cacheDir, "rootfs", path),
+               filepath.Join(cacheDir, "tmp", path))
+}
+
+// RestoreFiles restores original files which were cached by StoreFile.
+func RestoreFiles(cacheDir string) error {
+       f := func(path string, info os.FileInfo, err error) error {
+               if info.IsDir() {
+                       // We don't care about directories. They should be 
present so there's
+                       // no need to create them.
+                       return nil
+               }
+
+               return os.Rename(path,
+                       filepath.Join(cacheDir, "rootfs", 
strings.TrimPrefix(path, cacheDir)))
+       }
+
+       return filepath.Walk(filepath.Join(cacheDir, "tmp"), f)
+}
diff --git a/generators/hostname.go b/generators/hostname.go
new file mode 100644
index 0000000..91f9118
--- /dev/null
+++ b/generators/hostname.go
@@ -0,0 +1,60 @@
+package generators
+
+import (
+       "os"
+       "path/filepath"
+
+       "github.com/lxc/distrobuilder/image"
+)
+
+// HostnameGenerator represents the Hostname generator.
+type HostnameGenerator struct{}
+
+// CreateLXCData creates a hostname template.
+func (g HostnameGenerator) CreateLXCData(cacheDir, path string, img 
*image.LXCImage) error {
+       rootfs := filepath.Join(cacheDir, "rootfs")
+
+       // store original file
+       err := StoreFile(cacheDir, path)
+       if err != nil {
+               return err
+       }
+
+       file, err := os.Create(filepath.Join(rootfs, path))
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       file.WriteString("LXC_NAME\n")
+
+       return img.AddTemplate(path)
+}
+
+// CreateLXDData creates a hostname template.
+func (g HostnameGenerator) CreateLXDData(cacheDir, path string, img 
*image.LXDImage) error {
+       templateDir := filepath.Join(cacheDir, "templates")
+
+       err := os.MkdirAll(templateDir, 0755)
+       if err != nil {
+               return err
+       }
+
+       file, err := os.Create(filepath.Join(templateDir, "hostname.tpl"))
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       file.WriteString("{{ container.name }}\n")
+
+       img.Metadata.Templates[path] = image.LXDMetadataTemplate{
+               Template: "hostname.tpl",
+               When: []string{
+                       "create",
+                       "copy",
+               },
+       }
+
+       return err
+}
diff --git a/generators/hosts.go b/generators/hosts.go
new file mode 100644
index 0000000..72f0c23
--- /dev/null
+++ b/generators/hosts.go
@@ -0,0 +1,69 @@
+package generators
+
+import (
+       "io"
+       "os"
+       "path/filepath"
+
+       "github.com/lxc/distrobuilder/image"
+)
+
+// HostsGenerator represents the hosts generator.
+type HostsGenerator struct{}
+
+// CreateLXCData creates a LXC specific entry in the hosts file.
+func (g HostsGenerator) CreateLXCData(cacheDir, path string, img 
*image.LXCImage) error {
+       rootfs := filepath.Join(cacheDir, "rootfs")
+
+       // store original file
+       err := StoreFile(cacheDir, path)
+       if err != nil {
+               return err
+       }
+
+       file, err := os.OpenFile(filepath.Join(rootfs, path),
+               os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       file.WriteString("127.0.0.1\tLXC_NAME\n")
+
+       return img.AddTemplate(path)
+}
+
+// CreateLXDData creates a hosts template.
+func (g HostsGenerator) CreateLXDData(cacheDir, path string, img 
*image.LXDImage) error {
+       templateDir := filepath.Join(cacheDir, "templates")
+
+       err := os.MkdirAll(templateDir, 0755)
+       if err != nil {
+               return err
+       }
+
+       file, err := os.Create(filepath.Join(templateDir, "hosts.tpl"))
+       if err != nil {
+               return err
+       }
+       defer file.Close()
+
+       hostsFile, err := os.Open(filepath.Join(cacheDir, "rootfs", path))
+       if err != nil {
+               return err
+       }
+       defer hostsFile.Close()
+
+       io.Copy(file, hostsFile)
+       file.WriteString("127.0.0.1\t{{ container.name }}\n")
+
+       img.Metadata.Templates[path] = image.LXDMetadataTemplate{
+               Template: "hostname.tpl",
+               When: []string{
+                       "create",
+                       "copy",
+               },
+       }
+
+       return err
+}

From 1d01544098d06528cc58b97fd5d2be02a84a9142 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Wed, 21 Feb 2018 15:31:02 +0100
Subject: [PATCH 5/6] chroot: Fix unmounting

Signed-off-by: Thomas Hipp <[email protected]>
---
 distrobuilder/chroot.go | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index f055d73..780d42e 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -125,6 +125,9 @@ func setupChroot(rootfs string) (func() error, error) {
 
        err = setupMounts(rootfs, mounts)
        if err != nil {
+               for _, mount := range mounts {
+                       syscall.Unmount(filepath.Join(rootfs, mount.target), 
syscall.MNT_DETACH)
+               }
                return nil, fmt.Errorf("Failed to mount filesystems: %v", err)
        }
 
@@ -170,7 +173,9 @@ func setupChroot(rootfs string) (func() error, error) {
                // This will kill all processes in the chroot and allow to 
cleanly
                // unmount everything.
                killChrootProcesses(rootfs)
-               syscall.Unmount(rootfs, syscall.MNT_DETACH)
+               for _, mount := range mounts {
+                       syscall.Unmount(filepath.Join(rootfs, mount.target), 
syscall.MNT_DETACH)
+               }
 
                return nil
        }, nil

From 684e34e82052f8dd34830ba5166e6a999ca88a20 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <[email protected]>
Date: Wed, 21 Feb 2018 18:29:58 +0100
Subject: [PATCH 6/6] main: Build images

Signed-off-by: Thomas Hipp <[email protected]>
---
 distrobuilder/main.go | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/distrobuilder/main.go b/distrobuilder/main.go
index 48e90ec..a38f232 100644
--- a/distrobuilder/main.go
+++ b/distrobuilder/main.go
@@ -53,9 +53,12 @@ import (
        "os"
        "path/filepath"
 
+       "github.com/lxc/distrobuilder/generators"
+       "github.com/lxc/distrobuilder/image"
        "github.com/lxc/distrobuilder/shared"
        "github.com/lxc/distrobuilder/sources"
 
+       lxd "github.com/lxc/lxd/shared"
        cli "gopkg.in/urfave/cli.v1"
        yaml "gopkg.in/yaml.v2"
 )
@@ -190,6 +193,54 @@ func run(c *cli.Context) error {
        // Unmount everything and exit the chroot
        exitChroot()
 
+       if c.GlobalBool("lxc") {
+               img := image.NewLXCImage(c.GlobalString("cache-dir"), 
def.Image, def.Targets.LXC)
+
+               for _, file := range def.Files {
+                       generator := generators.Get(file.Generator)
+                       if generator == nil {
+                               continue
+                       }
+
+                       if len(file.Releases) > 0 && 
!lxd.StringInSlice(def.Image.Release, file.Releases) {
+                               continue
+                       }
+
+                       err := 
generator.CreateLXCData(c.GlobalString("cache-dir"), file.Path, img)
+                       if err != nil {
+                               continue
+                       }
+               }
+
+               img.Build()
+
+               // Clean up the chroot by restoring the orginal files.
+               generators.RestoreFiles(c.GlobalString("cache-dir"))
+       }
+
+       if c.GlobalBool("lxd") {
+               img := image.NewLXDImage(c.GlobalString("cache-dir"), def.Image)
+
+               for _, file := range def.Files {
+                       if len(file.Releases) > 0 && 
!lxd.StringInSlice(def.Image.Release, file.Releases) {
+                               continue
+                       }
+
+                       generator := generators.Get(file.Generator)
+                       if generator == nil {
+                               continue
+                       }
+
+                       generator.CreateLXDData(c.GlobalString("cache-dir"), 
file.Path, img)
+               }
+
+               img.Build(c.GlobalBool("unified"))
+       }
+
+       if c.GlobalBool("plain") {
+               shared.Pack("plain.tar.xz", 
filepath.Join(c.GlobalString("cache-dir"), "rootfs"), ".")
+       }
+
        return nil
 }
 
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to