mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-07-19 09:16:29 +00:00
Merge pull request #49 from justincormack/linuxkit
Use linuxkit to build qcow2 and raw image disks, rather than docker containers with libguestfs
This commit is contained in:
commit
294389aef9
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
@ -50,8 +51,10 @@ func build(args []string) {
|
||||
}
|
||||
buildName := buildCmd.String("name", "", "Name to use for output files")
|
||||
buildDir := buildCmd.String("dir", "", "Directory for output files, default current directory")
|
||||
buildSize := buildCmd.String("size", "1024M", "Size for output image, if supported and fixed size")
|
||||
buildPull := buildCmd.Bool("pull", false, "Always pull images")
|
||||
buildDisableTrust := buildCmd.Bool("disable-content-trust", false, "Skip image trust verification specified in trust section of config (default false)")
|
||||
buildHyperkit := buildCmd.Bool("hyperkit", false, "Use hyperkit for LinuxKit based builds where possible")
|
||||
buildCmd.Var(&buildOut, "output", "Output types to create [ "+strings.Join(outputTypes, " ")+" ]")
|
||||
|
||||
if err := buildCmd.Parse(args); err != nil {
|
||||
@ -59,6 +62,12 @@ func build(args []string) {
|
||||
}
|
||||
remArgs := buildCmd.Args()
|
||||
|
||||
if len(remArgs) == 0 {
|
||||
fmt.Println("Please specify a configuration file")
|
||||
buildCmd.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(buildOut) == 0 {
|
||||
buildOut = outputList{"kernel+initrd"}
|
||||
}
|
||||
@ -72,11 +81,11 @@ func build(args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if len(remArgs) == 0 {
|
||||
fmt.Println("Please specify a configuration file")
|
||||
buildCmd.Usage()
|
||||
os.Exit(1)
|
||||
size, err := getDiskSizeMB(*buildSize)
|
||||
if err != nil {
|
||||
log.Fatalf("Unable to parse disk size: %v", err)
|
||||
}
|
||||
|
||||
name := *buildName
|
||||
var config []byte
|
||||
if conf := remArgs[0]; conf == "-" {
|
||||
@ -115,12 +124,33 @@ func build(args []string) {
|
||||
image := buildInternal(m, *buildPull)
|
||||
|
||||
log.Infof("Create outputs:")
|
||||
err = outputs(filepath.Join(*buildDir, name), image, buildOut)
|
||||
err = outputs(filepath.Join(*buildDir, name), image, buildOut, size, *buildHyperkit)
|
||||
if err != nil {
|
||||
log.Fatalf("Error writing outputs: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Parse a string which is either a number in MB, or a number with
|
||||
// either M (for Megabytes) or G (for GigaBytes) as a suffix and
|
||||
// returns the number in MB. Return 0 if string is empty.
|
||||
func getDiskSizeMB(s string) (int, error) {
|
||||
if s == "" {
|
||||
return 0, nil
|
||||
}
|
||||
sz := len(s)
|
||||
if strings.HasSuffix(s, "G") {
|
||||
i, err := strconv.Atoi(s[:sz-1])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return i * 1024, nil
|
||||
}
|
||||
if strings.HasSuffix(s, "M") {
|
||||
s = s[:sz-1]
|
||||
}
|
||||
return strconv.Atoi(s)
|
||||
}
|
||||
|
||||
func initrdAppend(iw *tar.Writer, r io.Reader) {
|
||||
tr := tar.NewReader(r)
|
||||
for {
|
||||
|
140
cmd/moby/linuxkit.go
Normal file
140
cmd/moby/linuxkit.go
Normal file
@ -0,0 +1,140 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
var linuxkitYaml = map[string]string{"mkimage": `
|
||||
kernel:
|
||||
image: "linuxkit/kernel:4.9.x"
|
||||
cmdline: "console=ttyS0"
|
||||
init:
|
||||
- linuxkit/init:1b8a7e394d2ec2f1fdb4d67645829d1b5bdca037
|
||||
- linuxkit/runc:3a4e6cbf15470f62501b019b55e1caac5ee7689f
|
||||
- linuxkit/containerd:b1766e4c4c09f63ac4925a6e4612852a93f7e73b
|
||||
onboot:
|
||||
- name: mkimage
|
||||
image: "linuxkit/mkimage:f4bf0c24261f7d120c8674892805ab3054eb8ac3"
|
||||
- name: poweroff
|
||||
image: "linuxkit/poweroff:a8f1e4ad8d459f1fdaad9e4b007512cb3b504ae8"
|
||||
trust:
|
||||
org:
|
||||
- linuxkit
|
||||
`}
|
||||
|
||||
func imageFilename(name string) string {
|
||||
yaml := linuxkitYaml[name]
|
||||
hash := sha256.Sum256([]byte(yaml))
|
||||
return filepath.Join(MobyDir, "linuxkit", name+"-"+fmt.Sprintf("%x", hash))
|
||||
}
|
||||
|
||||
func ensureLinuxkitImage(name string) error {
|
||||
filename := imageFilename(name)
|
||||
_, err1 := os.Stat(filename + "-kernel")
|
||||
_, err2 := os.Stat(filename + "-initrd.img")
|
||||
_, err3 := os.Stat(filename + "-cmdline")
|
||||
if err1 == nil && err2 == nil && err3 == nil {
|
||||
return nil
|
||||
}
|
||||
err := os.MkdirAll(filepath.Join(MobyDir, "linuxkit"), 0755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO clean up old files
|
||||
log.Infof("Building LinuxKit image %s to generate output formats", name)
|
||||
|
||||
yaml := linuxkitYaml[name]
|
||||
|
||||
m, err := NewConfig([]byte(yaml))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO pass through --pull to here
|
||||
image := buildInternal(m, false)
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
err = writeKernelInitrd(filename, kernel, initrd, cmdline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeKernelInitrd(filename string, kernel []byte, initrd []byte, cmdline string) error {
|
||||
err := ioutil.WriteFile(filename+"-kernel", kernel, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filename+"-initrd.img", initrd, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = ioutil.WriteFile(filename+"-cmdline", []byte(cmdline), 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputLinuxKit(format string, filename string, kernel []byte, initrd []byte, cmdline string, size int, hyperkit bool) error {
|
||||
log.Debugf("output linuxkit generated img: %s %s size %d", format, filename, size)
|
||||
|
||||
tmp, err := ioutil.TempDir("", "moby")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.RemoveAll(tmp)
|
||||
|
||||
buf, err := tarInitrdKernel(kernel, initrd, cmdline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tardisk := filepath.Join(tmp, "tardisk")
|
||||
f, err := os.Create(tardisk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = io.Copy(f, buf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sizeString := fmt.Sprintf("%dM", size)
|
||||
_ = os.Remove(filename)
|
||||
_, err = os.Stat(filename)
|
||||
if err == nil || !os.IsNotExist(err) {
|
||||
return fmt.Errorf("Cannot remove existing file [%s]", filename)
|
||||
}
|
||||
linuxkit, err := exec.LookPath("linuxkit")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Cannot find linuxkit executable, needed to build %s output type: %v", format, err)
|
||||
}
|
||||
commandLine := []string{"-q", "run", "qemu", "-disk", fmt.Sprintf("%s,size=%s,format=%s", filename, sizeString, format), "-disk", fmt.Sprintf("%s,format=raw", tardisk), "-kernel", imageFilename("mkimage")}
|
||||
// if hyperkit && format == "raw" {
|
||||
// TODO support hyperkit
|
||||
// }
|
||||
log.Debugf("run %s: %v", linuxkit, commandLine)
|
||||
cmd := exec.Command(linuxkit, commandLine...)
|
||||
cmd.Stderr = os.Stderr
|
||||
err = cmd.Run()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
@ -3,9 +3,12 @@ package main
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/linuxkit/linuxkit/src/initrd"
|
||||
@ -14,22 +17,19 @@ import (
|
||||
const (
|
||||
bios = "linuxkit/mkimage-iso-bios:db791abed6f2b5320feb6cec255a635aee3756f6@sha256:e57483075307bcea4a7257f87eee733d3e24e7a964ba15dcc01111df6729ab3b"
|
||||
efi = "linuxkit/mkimage-iso-efi:5c2fc616bde288476a14f4f6dd0d273a66832822@sha256:876ef47ec2b30af40e70f1e98f496206eb430915867c4f9f400e1af47fd58d7c"
|
||||
gcp = "linuxkit/mkimage-gcp:46716b3d3f7aa1a7607a3426fe0ccebc554b14ee@sha256:18d8e0482f65a2481f5b6ba1e7ce77723b246bf13bdb612be5e64df90297940c"
|
||||
img = "linuxkit/mkimage-img-gz:eb85aac97f716ad8b8e7e593de3378e740ef2eeb@sha256:f1fb2368765a8ba6d1edfb073565550ae98486fb4943fbeb7d05357e5ba9969d"
|
||||
qcow = "linuxkit/mkimage-qcow:69890f35b55e4ff8a2c7a714907f988e57056d02@sha256:f89dc09f82bdbf86d7edae89604544f20b99d99c9b5cabcf1f93308095d8c244"
|
||||
vhd = "linuxkit/mkimage-vhd:a04c8480d41ca9cef6b7710bd45a592220c3acb2@sha256:ba373dc8ae5dc72685dbe4b872d8f588bc68b2114abd8bdc6a74d82a2b62cce3"
|
||||
vmdk = "linuxkit/mkimage-vmdk:182b541474ca7965c8e8f987389b651859f760da@sha256:99638c5ddb17614f54c6b8e11bd9d49d1dea9d837f38e0f6c1a5f451085d449b"
|
||||
)
|
||||
|
||||
var outFuns = map[string]func(string, []byte) error{
|
||||
"tar": func(base string, image []byte) error {
|
||||
var outFuns = map[string]func(string, []byte, int, bool) error{
|
||||
"tar": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
err := outputTar(base, image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing tar output: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"kernel+initrd": func(base string, image []byte) error {
|
||||
"kernel+initrd": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
@ -40,7 +40,7 @@ var outFuns = map[string]func(string, []byte) error{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"iso-bios": func(base string, image []byte) error {
|
||||
"iso-bios": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
@ -51,7 +51,7 @@ var outFuns = map[string]func(string, []byte) error{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"iso-efi": func(base string, image []byte) error {
|
||||
"iso-efi": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
@ -62,40 +62,137 @@ var outFuns = map[string]func(string, []byte) error{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"img-gz": func(base string, image []byte) error {
|
||||
"img": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
filename := base + ".img"
|
||||
log.Infof(" %s", filename)
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
err = outputImgSize(img, base+".img.gz", kernel, initrd, cmdline, "1G")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing img-gz output: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"gcp-img": func(base string, image []byte) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
err = outputImg(gcp, base+".img.tar.gz", kernel, initrd, cmdline)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing gcp-img output: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"qcow2": func(base string, image []byte) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
err = outputImg(qcow, base+".qcow2", kernel, initrd, cmdline)
|
||||
err = outputLinuxKit("raw", filename, kernel, initrd, cmdline, size, hyperkit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing qcow2 output: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"vhd": func(base string, image []byte) error {
|
||||
"img-gz": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
filename := base + ".img.gz"
|
||||
log.Infof(" %s", filename)
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
tmp, err := ioutil.TempDir("", "img-gz")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = outputLinuxKit("raw", filepath.Join(tmp, "uncompressed.img"), kernel, initrd, cmdline, size, hyperkit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing img-gz output: %v", err)
|
||||
}
|
||||
out, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in, err := os.Open(filepath.Join(tmp, "uncompressed.img"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zw := gzip.NewWriter(out)
|
||||
io.Copy(zw, in)
|
||||
err = zw.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = in.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = out.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.RemoveAll(tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"gcp-img": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
filename := base + ".img.tar.gz"
|
||||
log.Infof(" %s", filename)
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
tmp, err := ioutil.TempDir("", "gcp-img")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = outputLinuxKit("raw", filepath.Join(tmp, "disk.raw"), kernel, initrd, cmdline, size, hyperkit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing gcp-img output: %v", err)
|
||||
}
|
||||
out, err := os.Create(filename)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
in, err := os.Open(filepath.Join(tmp, "disk.raw"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := in.Stat()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
zw := gzip.NewWriter(out)
|
||||
tw := tar.NewWriter(zw)
|
||||
hdr := &tar.Header{
|
||||
Name: "disk.raw",
|
||||
Mode: 0600,
|
||||
Size: fi.Size(),
|
||||
}
|
||||
err = tw.WriteHeader(hdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(tw, in)
|
||||
err = tw.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = zw.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = in.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = out.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = os.RemoveAll(tmp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"qcow2": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
filename := base + ".qcow2"
|
||||
log.Infof(" %s", filename)
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
}
|
||||
err = outputLinuxKit("qcow2", filename, kernel, initrd, cmdline, size, hyperkit)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error writing qcow2 output: %v", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"vhd": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
@ -106,7 +203,7 @@ var outFuns = map[string]func(string, []byte) error{
|
||||
}
|
||||
return nil
|
||||
},
|
||||
"vmdk": func(base string, image []byte) error {
|
||||
"vmdk": func(base string, image []byte, size int, hyperkit bool) error {
|
||||
kernel, initrd, cmdline, err := tarToInitrd(image)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error converting to initrd: %v", err)
|
||||
@ -119,6 +216,22 @@ var outFuns = map[string]func(string, []byte) error{
|
||||
},
|
||||
}
|
||||
|
||||
var prereq = map[string]string{
|
||||
"img": "mkimage",
|
||||
"img-gz": "mkimage",
|
||||
"gcp-img": "mkimage",
|
||||
"qcow2": "mkimage",
|
||||
}
|
||||
|
||||
func ensurePrereq(out string) error {
|
||||
var err error
|
||||
p := prereq[out]
|
||||
if p != "" {
|
||||
err = ensureLinuxkitImage(p)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func validateOutputs(out outputList) error {
|
||||
log.Debugf("validating output: %v", out)
|
||||
|
||||
@ -127,12 +240,16 @@ func validateOutputs(out outputList) error {
|
||||
if f == nil {
|
||||
return fmt.Errorf("Unknown output type %s", o)
|
||||
}
|
||||
err := ensurePrereq(o)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to set up output type %s: %v", o, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func outputs(base string, image []byte, out outputList) error {
|
||||
func outputs(base string, image []byte, out outputList, size int, hyperkit bool) error {
|
||||
log.Debugf("output: %v %s", out, base)
|
||||
|
||||
err := validateOutputs(out)
|
||||
@ -141,7 +258,7 @@ func outputs(base string, image []byte, out outputList) error {
|
||||
}
|
||||
for _, o := range out {
|
||||
f := outFuns[o]
|
||||
err = f(base, image)
|
||||
err := f(base, image, size, hyperkit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -230,19 +347,19 @@ func outputImg(image, filename string, kernel []byte, initrd []byte, cmdline str
|
||||
return nil
|
||||
}
|
||||
|
||||
// this should replace the other version for types that can specify a size, and get size from CLI in future
|
||||
func outputImgSize(image, filename string, kernel []byte, initrd []byte, cmdline string, size string) error {
|
||||
log.Debugf("output img: %s %s size %s", image, filename, size)
|
||||
// this should replace the other version for types that can specify a size
|
||||
func outputImgSize(image, filename string, kernel []byte, initrd []byte, cmdline string, size int) error {
|
||||
log.Debugf("output img: %s %s size %d", image, filename, size)
|
||||
log.Infof(" %s", filename)
|
||||
buf, err := tarInitrdKernel(kernel, initrd, cmdline)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var img []byte
|
||||
if size == "" {
|
||||
if size == 0 {
|
||||
img, err = dockerRunInput(buf, image)
|
||||
} else {
|
||||
img, err = dockerRunInput(buf, image, size)
|
||||
img, err = dockerRunInput(buf, image, fmt.Sprintf("%dM", size))
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
|
Loading…
Reference in New Issue
Block a user