[lxc-devel] [distrobuilder/master] Support Ubuntu Core
monstermunchkin on Github
lxc-bot at linuxcontainers.org
Mon Jun 24 14:25:54 UTC 2019
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 385 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190624/d85bd5c4/attachment.bin>
-------------- next part --------------
From 5444d51af33c4488d8c21ac2d5303df928a93a0e Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 21 Jun 2019 20:16:16 +0200
Subject: [PATCH 1/2] sources: Add Ubuntu Core support
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
sources/ubuntu-http.go | 353 ++++++++++++++++++++++++++++++++++++-----
1 file changed, 311 insertions(+), 42 deletions(-)
diff --git a/sources/ubuntu-http.go b/sources/ubuntu-http.go
index 9d81316..58bc1ef 100644
--- a/sources/ubuntu-http.go
+++ b/sources/ubuntu-http.go
@@ -4,16 +4,17 @@ import (
"crypto/sha256"
"errors"
"fmt"
+ "io"
"io/ioutil"
"net/http"
"net/url"
"os"
- "path"
"path/filepath"
"regexp"
"strings"
lxd "github.com/lxc/lxd/shared"
+ "golang.org/x/sys/unix"
"github.com/lxc/distrobuilder/shared"
)
@@ -21,6 +22,7 @@ import (
// UbuntuHTTP represents the Ubuntu HTTP downloader.
type UbuntuHTTP struct {
fname string
+ fpath string
}
// NewUbuntuHTTP creates a new UbuntuHTTP instance.
@@ -30,19 +32,263 @@ func NewUbuntuHTTP() *UbuntuHTTP {
// Run downloads the tarball and unpacks it.
func (s *UbuntuHTTP) Run(definition shared.Definition, rootfsDir string) error {
- baseURL := fmt.Sprintf("%s/releases/%s/release/", definition.Source.URL,
- definition.Image.Release)
+ err := s.downloadImage(definition)
+ if err != nil {
+ return err
+ }
+
+ switch strings.ToLower(definition.Image.Variant) {
+ case "default":
+ return s.runDefaultVariant(definition, rootfsDir)
+ case "core":
+ return s.runCoreVariant(definition, rootfsDir)
+
+ }
+
+ return fmt.Errorf("Unknown Ubuntu variant: %s", definition.Image.Variant)
+}
+
+func (s *UbuntuHTTP) runDefaultVariant(definition shared.Definition, rootfsDir string) error {
+ err := s.unpack(filepath.Join(s.fpath, s.fname), rootfsDir)
+ if err != nil {
+ return err
+ }
+
+ if definition.Source.AptSources != "" {
+ // Run the template
+ out, err := shared.RenderTemplate(definition.Source.AptSources, definition)
+ if err != nil {
+ return err
+ }
+
+ // Append final new line if missing
+ if !strings.HasSuffix(out, "\n") {
+ out += "\n"
+ }
+
+ // Replace content of sources.list with the templated content.
+ file, err := os.Create(filepath.Join(rootfsDir, "etc", "apt", "sources.list"))
+ if err != nil {
+ return err
+ }
+ defer file.Close()
+
+ file.WriteString(out)
+ }
+
+ return nil
+}
+
+func (s *UbuntuHTTP) runCoreVariant(definition shared.Definition, rootfsDir string) error {
+ f := filepath.Join(s.fpath, s.fname)
+
+ if !lxd.PathExists(filepath.Join(s.fpath, strings.TrimSuffix(s.fname, ".xz"))) {
+ err := shared.RunCommand("unxz", "-k", filepath.Join(s.fpath, s.fname))
+ if err != nil {
+ return err
+ }
+ }
+
+ s.fname = strings.TrimSuffix(s.fname, ".xz")
+ f = filepath.Join(s.fpath, s.fname)
+
+ output, err := lxd.RunCommand("kpartx", "-a", "-v", f)
+ if err != nil {
+ return err
+ }
+ defer lxd.RunCommand("kpartx", "-d", f)
+
+ lines := strings.Split(output, "\n")
+
+ if len(lines) < 3 {
+ return fmt.Errorf("Failed to mount core image")
+ }
+
+ rootPartition := filepath.Join("/dev", "mapper", strings.Split(lines[2], " ")[2])
+
+ imageDir := filepath.Join(os.TempDir(), "distrobuilder", "image")
+ snapsDir := filepath.Join(os.TempDir(), "distrobuilder", "snaps")
+
+ os.MkdirAll(imageDir, 0755)
+ os.MkdirAll(snapsDir, 0755)
+ defer os.RemoveAll(filepath.Join(os.TempDir(), "distrobuilder"))
+
+ err = shared.RunCommand("mount", rootPartition, imageDir)
+ if err != nil {
+ return err
+ }
+ defer unix.Unmount(imageDir, 0)
+
+ err = shared.RunCommand("rsync", "-qa", filepath.Join(imageDir, "system-data"), rootfsDir)
+ if err != nil {
+ return err
+ }
+
+ // Create all the needed paths and links
+
+ dirs := []string{"bin", "dev", "initrd", "lib", "mnt", "proc", "root", "sbin", "sys"}
+
+ for _, d := range dirs {
+ err := os.Mkdir(filepath.Join(rootfsDir, d), 0755)
+ if err != nil {
+ return err
+ }
+ }
+
+ links := []struct {
+ target string
+ link string
+ }{
+ {
+ "lib",
+ filepath.Join(rootfsDir, "lib64"),
+ },
+ {
+ "/bin/busybox",
+ filepath.Join(rootfsDir, "bin", "sh"),
+ },
+ {
+ "/bin/init",
+ filepath.Join(rootfsDir, "sbin", "init"),
+ },
+ }
+
+ for _, l := range links {
+ err = os.Symlink(l.target, l.link)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy system binaries
+
+ binaries := []struct {
+ source string
+ target string
+ }{
+ {
+ getFullPath("busybox"),
+ filepath.Join(rootfsDir, "bin", "busybox"),
+ },
+ {
+ getFullPath("cpio"),
+ filepath.Join(rootfsDir, "bin", "cpio"),
+ },
+ {
+ getFullPath("mount.fuse"),
+ filepath.Join(rootfsDir, "bin", "mount.fuse"),
+ },
+ {
+ getFullPath("pivot_root"),
+ filepath.Join(rootfsDir, "bin", "pivot_root"),
+ },
+ {
+ getFullPath("squashfuse"),
+ filepath.Join(rootfsDir, "bin", "squashfuse"),
+ },
+ }
+
+ for _, b := range binaries {
+ err := lxd.FileCopy(b.source, b.target)
+ if err != nil {
+ return err
+ }
+
+ err = os.Chmod(b.target, 0755)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Copy needed libraries
- if strings.ContainsAny(definition.Image.Release, "0123456789") {
- s.fname = fmt.Sprintf("ubuntu-base-%s-base-%s.tar.gz",
- definition.Image.Release, definition.Image.ArchitectureMapped)
- } else {
- // if release is non-numerical, find the latest release
- s.fname = getLatestRelease(definition.Source.URL,
- definition.Image.Release, definition.Image.ArchitectureMapped)
- if s.fname == "" {
- return fmt.Errorf("Couldn't find latest release")
+ patterns := []string{
+ "/lib/*-linux-gnu/ld-linux*.so.2",
+ "/lib/*-linux-gnu/libc.so.6",
+ "/lib/*-linux-gnu/libdl.so.2",
+ "/lib/*-linux-gnu/libfuse.so.2",
+ "/usr/lib/*-linux-gnu/liblz4.so.1",
+ "/lib/*-linux-gnu/liblzma.so.5",
+ "/lib/*-linux-gnu/liblzo2.so.2",
+ "/lib/*-linux-gnu/libpthread.so.0",
+ "/lib/*-linux-gnu/libz.so.1",
+ "/lib/*-linux-gnu/libsquashfuse.so.0",
+ "/lib/*-linux-gnu/libfuseprivate.so.0",
+ }
+
+ for _, p := range patterns {
+ matches, err := filepath.Glob(p)
+ if err != nil {
+ return err
+ }
+
+ if len(matches) != 1 {
+ continue
}
+
+ target := filepath.Join(rootfsDir, "lib", filepath.Base(matches[0]))
+
+ err = lxd.FileCopy(matches[0], target)
+ if err != nil {
+ return err
+ }
+
+ err = os.Chmod(target, 0755)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Download init script
+ res, err := http.Get("https://raw.githubusercontent.com/lxc/lxc-ci/master/images/ubuntu-core/init")
+ if err != nil {
+ return err
+ }
+ defer res.Body.Close()
+
+ initFile, err := os.Create(filepath.Join(rootfsDir, "bin", "init"))
+ if err != nil {
+ return err
+ }
+ defer initFile.Close()
+
+ _, err = io.Copy(initFile, res.Body)
+ if err != nil {
+ return err
+ }
+
+ err = initFile.Chmod(0755)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func (s *UbuntuHTTP) downloadImage(definition shared.Definition) error {
+ var baseURL string
+
+ switch strings.ToLower(definition.Image.Variant) {
+ case "default":
+ baseURL = fmt.Sprintf("%s/releases/%s/release/", definition.Source.URL,
+ definition.Image.Release)
+
+ if strings.ContainsAny(definition.Image.Release, "0123456789") {
+ s.fname = fmt.Sprintf("ubuntu-base-%s-base-%s.tar.gz",
+ definition.Image.Release, definition.Image.ArchitectureMapped)
+ } else {
+ // if release is non-numerical, find the latest release
+ s.fname = getLatestRelease(baseURL,
+ definition.Image.Release, definition.Image.ArchitectureMapped)
+ if s.fname == "" {
+ return fmt.Errorf("Couldn't find latest release")
+ }
+ }
+ case "core":
+ baseURL = fmt.Sprintf("%s/%s/stable/current/", definition.Source.URL, definition.Image.Release)
+ s.fname = fmt.Sprintf("ubuntu-core-%s-%s.img.xz", definition.Image.Release, definition.Image.ArchitectureMapped)
+ default:
+ return fmt.Errorf("Unknown Ubuntu variant: %s", definition.Image.Variant)
}
url, err := url.Parse(baseURL)
@@ -80,38 +326,11 @@ func (s *UbuntuHTTP) Run(definition shared.Definition, rootfsDir string) error {
}
}
- fpath, err = shared.DownloadHash(definition.Image, baseURL+s.fname, checksumFile, sha256.New())
+ s.fpath, err = shared.DownloadHash(definition.Image, baseURL+s.fname, checksumFile, sha256.New())
if err != nil {
return fmt.Errorf("Error downloading Ubuntu image: %s", err)
}
- err = s.unpack(filepath.Join(fpath, s.fname), rootfsDir)
- if err != nil {
- return err
- }
-
- if definition.Source.AptSources != "" {
- // Run the template
- out, err := shared.RenderTemplate(definition.Source.AptSources, definition)
- if err != nil {
- return err
- }
-
- // Append final new line if missing
- if !strings.HasSuffix(out, "\n") {
- out += "\n"
- }
-
- // Replace content of sources.list with the templated content.
- file, err := os.Create(filepath.Join(rootfsDir, "etc", "apt", "sources.list"))
- if err != nil {
- return err
- }
- defer file.Close()
-
- file.WriteString(out)
- }
-
return nil
}
@@ -127,8 +346,8 @@ func (s UbuntuHTTP) unpack(filePath, rootDir string) error {
return nil
}
-func getLatestRelease(URL, release, arch string) string {
- resp, err := http.Get(URL + path.Join("/", "releases", release, "release"))
+func getLatestRelease(baseURL, release, arch string) string {
+ resp, err := http.Get(baseURL)
if err != nil {
fmt.Fprintln(os.Stderr, err)
return ""
@@ -145,3 +364,53 @@ func getLatestRelease(URL, release, arch string) string {
return ""
}
+
+func getFullPath(name string) string {
+ var out string
+
+ paths := strings.Split(os.Getenv("PATH"), ":")
+ f := func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+
+ if info.IsDir() {
+ return nil
+ }
+
+ if filepath.Base(path) != name {
+ // This is not the file we're looking for.
+ return nil
+ }
+
+ target := path
+
+ if info.Mode() == os.ModeSymlink {
+ // Check the symlink target
+ target, err = os.Readlink(path)
+ if err != nil {
+ return err
+ }
+
+ if filepath.Base(target) != name {
+ return nil
+ }
+ }
+
+ if out == "" {
+ out = target
+ }
+
+ return nil
+ }
+
+ for _, p := range paths {
+ filepath.Walk(p, f)
+
+ if out != "" {
+ return out
+ }
+ }
+
+ return ""
+}
From 212f1391de98f64f37f29a8c8bd9ef5c6de5d0b3 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Fri, 21 Jun 2019 20:16:44 +0200
Subject: [PATCH 2/2] doc: Add Ubuntu Core example
Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
doc/examples/ubuntu-core | 100 +++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 doc/examples/ubuntu-core
diff --git a/doc/examples/ubuntu-core b/doc/examples/ubuntu-core
new file mode 100644
index 0000000..e2803e5
--- /dev/null
+++ b/doc/examples/ubuntu-core
@@ -0,0 +1,100 @@
+image:
+ distribution: ubuntu
+ release: 16
+ variant: core
+ description: Ubuntu Core {{ image.release }}
+ expiry: 30d
+ architecture: amd64
+
+source:
+ downloader: ubuntu-http
+ url: http://cdimage.ubuntu.com/ubuntu-core
+ keys:
+ - 0x46181433FBB75451
+ - 0xD94AA3F0EFE21092
+
+targets:
+ lxc:
+ create-message: |-
+ You just created an {{ image.description }} container.
+
+ To enable SSH, run: apt install openssh-server
+ No default root or user password are set by LXC.
+
+ config:
+ - type: all
+ before: 5
+ content: |-
+ lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.common.conf
+
+ - type: user
+ before: 5
+ content: |-
+ lxc.include = LXC_TEMPLATE_CONFIG/ubuntu.userns.conf
+
+ - type: all
+ after: 4
+ content: |-
+ lxc.include = LXC_TEMPLATE_CONFIG/common.conf
+
+ # For Ubuntu 14.04
+ lxc.mount.entry = /sys/kernel/debug sys/kernel/debug none bind,optional 0 0
+ lxc.mount.entry = /sys/kernel/security sys/kernel/security none bind,optional 0 0
+ lxc.mount.entry = /sys/fs/pstore sys/fs/pstore none bind,optional 0 0
+ lxc.mount.entry = mqueue dev/mqueue mqueue rw,relatime,create=dir,optional 0 0
+
+ - type: user
+ after: 4
+ content: |-
+ lxc.include = LXC_TEMPLATE_CONFIG/userns.conf
+
+ # For Ubuntu 14.04
+ lxc.mount.entry = /sys/firmware/efi/efivars sys/firmware/efi/efivars none bind,optional 0 0
+ lxc.mount.entry = /proc/sys/fs/binfmt_misc proc/sys/fs/binfmt_misc none bind,optional 0 0
+
+ - type: all
+ content: |-
+ lxc.arch = {{ image.architecture_personality }}
+
+packages:
+ custom-manager:
+ install:
+ cmd: true
+ remove:
+ cmd: true
+ update:
+ cmd: true
+ clean:
+ cmd: true
+ refresh:
+ cmd: true
+
+files:
+ - name: cloud-init-meta
+ path: /lxd/meta-data
+ generator: template
+ - name: cloud-init-network
+ path: /lxd/network-config
+ generator: template
+ - name: cloud-init-user
+ path: /lxd/user-data
+ generator: template
+ template:
+ properties:
+ default: |
+ #cloud-config
+ {}
+ - name: cloud-init-vendor
+ path: /lxd/vendor-data
+ generator: template
+ template:
+ properties:
+ default: |
+ #cloud-config
+ {}
+ - name: hostname
+ path: /lxd/hostname
+ generator: template
+
+mappings:
+ architecture_map: debian
\ No newline at end of file
More information about the lxc-devel
mailing list