[lxc-devel] [distrobuilder/master] Add support for Alpine edge
stgraber on Github
lxc-bot at linuxcontainers.org
Wed Jun 27 23:29:10 UTC 2018
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 363 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180627/8de2aa1d/attachment.bin>
-------------- next part --------------
From 2e3d2075ed8340bcfb1cc57b1e136796b505589e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 27 Jun 2018 19:25:51 -0400
Subject: [PATCH] Add support for Alpine edge
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
distrobuilder/chroot.go | 273 ---------------------------------------------
distrobuilder/main.go | 2 +-
distrobuilder/main_lxc.go | 2 +-
distrobuilder/main_lxd.go | 2 +-
shared/chroot.go | 279 ++++++++++++++++++++++++++++++++++++++++++++++
sources/alpine-http.go | 50 ++++++++-
6 files changed, 328 insertions(+), 280 deletions(-)
create mode 100644 shared/chroot.go
diff --git a/distrobuilder/chroot.go b/distrobuilder/chroot.go
index 8fae569..de3e0fd 100644
--- a/distrobuilder/chroot.go
+++ b/distrobuilder/chroot.go
@@ -2,284 +2,11 @@ package main
import (
"fmt"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strconv"
- "syscall"
-
- lxd "github.com/lxc/lxd/shared"
"github.com/lxc/distrobuilder/managers"
"github.com/lxc/distrobuilder/shared"
)
-type chrootMount struct {
- source string
- target string
- fstype string
- flags uintptr
- data string
- isDir bool
-}
-
-func setupMounts(rootfs string, mounts []chrootMount) error {
- // Create a temporary mount path
- err := os.MkdirAll(filepath.Join(rootfs, ".distrobuilder"), 0700)
- if err != nil {
- return err
- }
-
- for i, mount := range mounts {
- // Target path
- tmpTarget := filepath.Join(rootfs, ".distrobuilder", fmt.Sprintf("%d", i))
-
- // Create the target mountpoint
- if mount.isDir {
- err := os.Mkdir(tmpTarget, 0755)
- if err != nil {
- return err
- }
- } else {
- _, err = os.Create(tmpTarget)
- if err != nil {
- return err
- }
- }
-
- // Mount to the temporary path
- err := syscall.Mount(mount.source, tmpTarget, mount.fstype, mount.flags, mount.data)
- if err != nil {
- return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
- }
- }
-
- return nil
-}
-
-func moveMounts(mounts []chrootMount) error {
- for i, mount := range mounts {
- // Source path
- tmpSource := filepath.Join("/", ".distrobuilder", fmt.Sprintf("%d", i))
-
- // Resolve symlinks
- target := mount.target
- for {
- // Get information on current target
- fi, err := os.Lstat(target)
- if err != nil {
- break
- }
-
- // If not a symlink, we're done
- if fi.Mode()&os.ModeSymlink == 0 {
- break
- }
-
- // If a symlink, resolve it
- newTarget, err := os.Readlink(target)
- if err != nil {
- break
- }
-
- target = newTarget
- }
-
- // Create parent paths if missing
- err := os.MkdirAll(filepath.Dir(target), 0755)
- if err != nil {
- return err
- }
-
- // Create target path
- if mount.isDir {
- err = os.MkdirAll(target, 0755)
- if err != nil {
- return err
- }
- } else {
- _, err = os.Create(target)
- if err != nil {
- return err
- }
- }
-
- // Move the mount to its destination
- err = syscall.Mount(tmpSource, target, "", syscall.MS_MOVE, "")
- if err != nil {
- return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
- }
- }
-
- // Cleanup our temporary path
- err := os.RemoveAll(filepath.Join("/", ".distrobuilder"))
- if err != nil {
- return err
- }
-
- return nil
-
-}
-
-func killChrootProcesses(rootfs string) error {
- // List all files under /proc
- proc, err := os.Open(filepath.Join(rootfs, "proc"))
- if err != nil {
- return err
- }
-
- dirs, err := proc.Readdirnames(0)
- if err != nil {
- return err
- }
-
- // Get all processes and kill them
- for _, dir := range dirs {
- match, _ := regexp.MatchString(`\d+`, dir)
- if match {
- link, _ := os.Readlink(filepath.Join(rootfs, "proc", dir, "root"))
- if link == rootfs {
- pid, _ := strconv.Atoi(dir)
- syscall.Kill(pid, syscall.SIGKILL)
- }
- }
- }
-
- return nil
-}
-
-func setupChroot(rootfs string) (func() error, error) {
- // Mount the rootfs
- err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, "")
- if err != nil {
- return nil, fmt.Errorf("Failed to mount '%s': %s", rootfs, err)
- }
-
- // Setup all other needed mounts
- mounts := []chrootMount{
- {"none", "/proc", "proc", 0, "", true},
- {"none", "/sys", "sysfs", 0, "", true},
- {"/dev", "/dev", "", syscall.MS_BIND, "", true},
- {"none", "/run", "tmpfs", 0, "", true},
- {"none", "/tmp", "tmpfs", 0, "", true},
- {"/etc/resolv.conf", "/etc/resolv.conf", "", syscall.MS_BIND, "", false},
- }
-
- // Keep a reference to the host rootfs and cwd
- root, err := os.Open("/")
- if err != nil {
- return nil, err
- }
-
- cwd, err := os.Getwd()
- if err != nil {
- return nil, err
- }
-
- // Setup all needed mounts in a temporary location
- err = setupMounts(rootfs, mounts)
- if err != nil {
- return nil, fmt.Errorf("Failed to mount filesystems: %v", err)
- }
-
- // Chroot into the container's rootfs
- err = syscall.Chroot(rootfs)
- if err != nil {
- root.Close()
- return nil, err
- }
-
- err = syscall.Chdir("/")
- if err != nil {
- return nil, err
- }
-
- // Move all the mounts into place
- err = moveMounts(mounts)
- if err != nil {
- return nil, err
- }
-
- // Set environment variables
- oldEnvVariables := shared.SetEnvVariables(
- []shared.EnvVariable{
- {
- Key: "PATH",
- Value: "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
- Set: true,
- },
- {
- Key: "SHELL",
- Value: "/bin/sh",
- Set: true,
- },
- {
- Key: "TERM",
- Value: "xterm",
- Set: true,
- },
- {
- Key: "DEBIAN_FRONTEND",
- Value: "noninteractive",
- Set: true,
- },
- })
-
- // Setup policy-rc.d override
- policyCleanup := false
- if lxd.PathExists("/usr/sbin/") && !lxd.PathExists("/usr/sbin/policy-rc.d") {
- err = ioutil.WriteFile("/usr/sbin/policy-rc.d", []byte(`#!/bin/sh
-exit 101
-`), 0755)
- if err != nil {
- return nil, err
- }
-
- policyCleanup = true
- }
-
- return func() error {
- defer root.Close()
-
- // Cleanup policy-rc.d
- if policyCleanup {
- err = os.Remove("/usr/sbin/policy-rc.d")
- if err != nil {
- return err
- }
- }
-
- // Reset old environment variables
- shared.SetEnvVariables(oldEnvVariables)
-
- // Switch back to the host rootfs
- err = root.Chdir()
- if err != nil {
- return err
- }
-
- err = syscall.Chroot(".")
- if err != nil {
- return err
- }
-
- err = syscall.Chdir(cwd)
- if err != nil {
- return err
- }
-
- // This will kill all processes in the chroot and allow to cleanly
- // unmount everything.
- killChrootProcesses(rootfs)
-
- // And now unmount the entire tree
- syscall.Unmount(rootfs, syscall.MNT_DETACH)
-
- return nil
- }, nil
-}
-
func managePackages(def shared.DefinitionPackages, actions []shared.DefinitionAction) error {
var err error
diff --git a/distrobuilder/main.go b/distrobuilder/main.go
index d376447..ed14548 100644
--- a/distrobuilder/main.go
+++ b/distrobuilder/main.go
@@ -197,7 +197,7 @@ func (c *cmdGlobal) preRunBuild(cmd *cobra.Command, args []string) error {
}
// Setup the mounts and chroot into the rootfs
- exitChroot, err := setupChroot(c.sourceDir)
+ exitChroot, err := shared.SetupChroot(c.sourceDir)
if err != nil {
return fmt.Errorf("Failed to setup chroot: %s", err)
}
diff --git a/distrobuilder/main_lxc.go b/distrobuilder/main_lxc.go
index 3885390..86cb4d2 100644
--- a/distrobuilder/main_lxc.go
+++ b/distrobuilder/main_lxc.go
@@ -66,7 +66,7 @@ func (c *cmdLXC) run(cmd *cobra.Command, args []string) error {
}
}
- exitChroot, err := setupChroot(c.global.sourceDir)
+ exitChroot, err := shared.SetupChroot(c.global.sourceDir)
if err != nil {
return err
}
diff --git a/distrobuilder/main_lxd.go b/distrobuilder/main_lxd.go
index e9d3d7d..3a33696 100644
--- a/distrobuilder/main_lxd.go
+++ b/distrobuilder/main_lxd.go
@@ -85,7 +85,7 @@ func (c *cmdLXD) run(cmd *cobra.Command, args []string) error {
}
}
- exitChroot, err := setupChroot(c.global.sourceDir)
+ exitChroot, err := shared.SetupChroot(c.global.sourceDir)
if err != nil {
return err
}
diff --git a/shared/chroot.go b/shared/chroot.go
new file mode 100644
index 0000000..0b28201
--- /dev/null
+++ b/shared/chroot.go
@@ -0,0 +1,279 @@
+package shared
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "regexp"
+ "strconv"
+ "syscall"
+
+ lxd "github.com/lxc/lxd/shared"
+)
+
+type chrootMount struct {
+ source string
+ target string
+ fstype string
+ flags uintptr
+ data string
+ isDir bool
+}
+
+func setupMounts(rootfs string, mounts []chrootMount) error {
+ // Create a temporary mount path
+ err := os.MkdirAll(filepath.Join(rootfs, ".distrobuilder"), 0700)
+ if err != nil {
+ return err
+ }
+
+ for i, mount := range mounts {
+ // Target path
+ tmpTarget := filepath.Join(rootfs, ".distrobuilder", fmt.Sprintf("%d", i))
+
+ // Create the target mountpoint
+ if mount.isDir {
+ err := os.Mkdir(tmpTarget, 0755)
+ if err != nil {
+ return err
+ }
+ } else {
+ _, err = os.Create(tmpTarget)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Mount to the temporary path
+ err := syscall.Mount(mount.source, tmpTarget, mount.fstype, mount.flags, mount.data)
+ if err != nil {
+ return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
+ }
+ }
+
+ return nil
+}
+
+func moveMounts(mounts []chrootMount) error {
+ for i, mount := range mounts {
+ // Source path
+ tmpSource := filepath.Join("/", ".distrobuilder", fmt.Sprintf("%d", i))
+
+ // Resolve symlinks
+ target := mount.target
+ for {
+ // Get information on current target
+ fi, err := os.Lstat(target)
+ if err != nil {
+ break
+ }
+
+ // If not a symlink, we're done
+ if fi.Mode()&os.ModeSymlink == 0 {
+ break
+ }
+
+ // If a symlink, resolve it
+ newTarget, err := os.Readlink(target)
+ if err != nil {
+ break
+ }
+
+ target = newTarget
+ }
+
+ // Create parent paths if missing
+ err := os.MkdirAll(filepath.Dir(target), 0755)
+ if err != nil {
+ return err
+ }
+
+ // Create target path
+ if mount.isDir {
+ err = os.MkdirAll(target, 0755)
+ if err != nil {
+ return err
+ }
+ } else {
+ _, err = os.Create(target)
+ if err != nil {
+ return err
+ }
+ }
+
+ // Move the mount to its destination
+ err = syscall.Mount(tmpSource, target, "", syscall.MS_MOVE, "")
+ if err != nil {
+ return fmt.Errorf("Failed to mount '%s': %s", mount.source, err)
+ }
+ }
+
+ // Cleanup our temporary path
+ err := os.RemoveAll(filepath.Join("/", ".distrobuilder"))
+ if err != nil {
+ return err
+ }
+
+ return nil
+
+}
+
+func killChrootProcesses(rootfs string) error {
+ // List all files under /proc
+ proc, err := os.Open(filepath.Join(rootfs, "proc"))
+ if err != nil {
+ return err
+ }
+
+ dirs, err := proc.Readdirnames(0)
+ if err != nil {
+ return err
+ }
+
+ // Get all processes and kill them
+ for _, dir := range dirs {
+ match, _ := regexp.MatchString(`\d+`, dir)
+ if match {
+ link, _ := os.Readlink(filepath.Join(rootfs, "proc", dir, "root"))
+ if link == rootfs {
+ pid, _ := strconv.Atoi(dir)
+ syscall.Kill(pid, syscall.SIGKILL)
+ }
+ }
+ }
+
+ return nil
+}
+
+// SetupChroot sets up mount and files, a reverter and then chroots for you
+func SetupChroot(rootfs string) (func() error, error) {
+ // Mount the rootfs
+ err := syscall.Mount(rootfs, rootfs, "", syscall.MS_BIND, "")
+ if err != nil {
+ return nil, fmt.Errorf("Failed to mount '%s': %s", rootfs, err)
+ }
+
+ // Setup all other needed mounts
+ mounts := []chrootMount{
+ {"none", "/proc", "proc", 0, "", true},
+ {"none", "/sys", "sysfs", 0, "", true},
+ {"/dev", "/dev", "", syscall.MS_BIND, "", true},
+ {"none", "/run", "tmpfs", 0, "", true},
+ {"none", "/tmp", "tmpfs", 0, "", true},
+ {"/etc/resolv.conf", "/etc/resolv.conf", "", syscall.MS_BIND, "", false},
+ }
+
+ // Keep a reference to the host rootfs and cwd
+ root, err := os.Open("/")
+ if err != nil {
+ return nil, err
+ }
+
+ cwd, err := os.Getwd()
+ if err != nil {
+ return nil, err
+ }
+
+ // Setup all needed mounts in a temporary location
+ err = setupMounts(rootfs, mounts)
+ if err != nil {
+ return nil, fmt.Errorf("Failed to mount filesystems: %v", err)
+ }
+
+ // Chroot into the container's rootfs
+ err = syscall.Chroot(rootfs)
+ if err != nil {
+ root.Close()
+ return nil, err
+ }
+
+ err = syscall.Chdir("/")
+ if err != nil {
+ return nil, err
+ }
+
+ // Move all the mounts into place
+ err = moveMounts(mounts)
+ if err != nil {
+ return nil, err
+ }
+
+ // Set environment variables
+ oldEnvVariables := SetEnvVariables(
+ []EnvVariable{
+ {
+ Key: "PATH",
+ Value: "/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin",
+ Set: true,
+ },
+ {
+ Key: "SHELL",
+ Value: "/bin/sh",
+ Set: true,
+ },
+ {
+ Key: "TERM",
+ Value: "xterm",
+ Set: true,
+ },
+ {
+ Key: "DEBIAN_FRONTEND",
+ Value: "noninteractive",
+ Set: true,
+ },
+ })
+
+ // Setup policy-rc.d override
+ policyCleanup := false
+ if lxd.PathExists("/usr/sbin/") && !lxd.PathExists("/usr/sbin/policy-rc.d") {
+ err = ioutil.WriteFile("/usr/sbin/policy-rc.d", []byte(`#!/bin/sh
+exit 101
+`), 0755)
+ if err != nil {
+ return nil, err
+ }
+
+ policyCleanup = true
+ }
+
+ return func() error {
+ defer root.Close()
+
+ // Cleanup policy-rc.d
+ if policyCleanup {
+ err = os.Remove("/usr/sbin/policy-rc.d")
+ if err != nil {
+ return err
+ }
+ }
+
+ // Reset old environment variables
+ SetEnvVariables(oldEnvVariables)
+
+ // Switch back to the host rootfs
+ err = root.Chdir()
+ if err != nil {
+ return err
+ }
+
+ err = syscall.Chroot(".")
+ if err != nil {
+ return err
+ }
+
+ err = syscall.Chdir(cwd)
+ if err != nil {
+ return err
+ }
+
+ // This will kill all processes in the chroot and allow to cleanly
+ // unmount everything.
+ killChrootProcesses(rootfs)
+
+ // And now unmount the entire tree
+ syscall.Unmount(rootfs, syscall.MNT_DETACH)
+
+ return nil
+ }, nil
+}
diff --git a/sources/alpine-http.go b/sources/alpine-http.go
index 6435bdc..7fe1736 100644
--- a/sources/alpine-http.go
+++ b/sources/alpine-http.go
@@ -23,11 +23,33 @@ func NewAlpineLinuxHTTP() *AlpineLinuxHTTP {
// Run downloads an Alpine Linux mini root filesystem.
func (s *AlpineLinuxHTTP) Run(definition shared.Definition, rootfsDir string) error {
- fname := fmt.Sprintf("alpine-minirootfs-%s-%s.tar.gz", definition.Image.Release,
+ releaseFull := definition.Image.Release
+ releaseShort := releaseFull
+
+ if definition.Image.Release == "edge" {
+ if definition.Source.SameAs == "" {
+ return fmt.Errorf("You can't use Alpine edge without setting same_as")
+ }
+
+ releaseFull = definition.Source.SameAs
+ releaseShort = releaseFull
+ }
+
+ releaseField := strings.Split(releaseFull, ".")
+ if len(releaseField) == 2 {
+ releaseShort = fmt.Sprintf("v%s", releaseFull)
+ releaseFull = fmt.Sprintf("%s.0", releaseFull)
+ } else if len(releaseField) == 3 {
+ releaseShort = fmt.Sprintf("v%s.%s", releaseField[0], releaseField[1])
+ } else {
+ return fmt.Errorf("Bad Alpine release: %s", releaseFull)
+ }
+
+ fname := fmt.Sprintf("alpine-minirootfs-%s-%s.tar.gz", releaseFull,
definition.Image.ArchitectureMapped)
- tarball := fmt.Sprintf("%s/v%s/releases/%s/%s", definition.Source.URL,
- strings.Join(strings.Split(definition.Image.Release, ".")[0:2], "."),
- definition.Image.ArchitectureMapped, fname)
+
+ tarball := fmt.Sprintf("%s/%s/releases/%s/%s", definition.Source.URL,
+ releaseShort, definition.Image.ArchitectureMapped, fname)
url, err := url.Parse(tarball)
if err != nil {
@@ -65,5 +87,25 @@ func (s *AlpineLinuxHTTP) Run(definition shared.Definition, rootfsDir string) er
return err
}
+ if definition.Image.Release == "edge" {
+ // Upgrade to edge
+ exitChroot, err := shared.SetupChroot(rootfsDir)
+ if err != nil {
+ return err
+ }
+
+ err = shared.RunCommand("sed", "-i", "-e", "s/v[[:digit:]]\\.[[:digit:]]/edge/g", "/etc/apk/repositories")
+ if err != nil {
+ return err
+ }
+
+ err = shared.RunCommand("apk", "upgrade", "--update-cache", "--available")
+ if err != nil {
+ return err
+ }
+
+ exitChroot()
+ }
+
return nil
}
More information about the lxc-devel
mailing list