mirror of
https://github.com/linuxkit/linuxkit.git
synced 2026-04-02 22:19:24 +00:00
This is a little ugly in terms of the validation now, but it is a move towards splitting "build" and "package". The "tar" output (and soon others) can output direct to a file or to stdout. Obviously you can only build a single output format like this. The LinuxKit output formats that build disk images cannot stream as they have to build whole images. These allow multiple outputs. In future we will probably change to ``` moby build | moby package ``` or similar, but that is a bit ugly, so currently have a compromise where there are essentially two output types. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
332 lines
8.7 KiB
Go
332 lines
8.7 KiB
Go
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"
|
|
)
|
|
|
|
const (
|
|
bios = "linuxkit/mkimage-iso-bios:db791abed6f2b5320feb6cec255a635aee3756f6@sha256:e57483075307bcea4a7257f87eee733d3e24e7a964ba15dcc01111df6729ab3b"
|
|
efi = "linuxkit/mkimage-iso-efi:5c2fc616bde288476a14f4f6dd0d273a66832822@sha256:876ef47ec2b30af40e70f1e98f496206eb430915867c4f9f400e1af47fd58d7c"
|
|
gcp = "linuxkit/mkimage-gcp:46716b3d3f7aa1a7607a3426fe0ccebc554b14ee@sha256:18d8e0482f65a2481f5b6ba1e7ce77723b246bf13bdb612be5e64df90297940c"
|
|
vhd = "linuxkit/mkimage-vhd:a04c8480d41ca9cef6b7710bd45a592220c3acb2@sha256:ba373dc8ae5dc72685dbe4b872d8f588bc68b2114abd8bdc6a74d82a2b62cce3"
|
|
vmdk = "linuxkit/mkimage-vmdk:182b541474ca7965c8e8f987389b651859f760da@sha256:99638c5ddb17614f54c6b8e11bd9d49d1dea9d837f38e0f6c1a5f451085d449b"
|
|
)
|
|
|
|
var outFuns = map[string]func(string, []byte, int, bool) 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)
|
|
}
|
|
err = outputKernelInitrd(base, kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing kernel+initrd output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
"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)
|
|
}
|
|
err = outputImg(bios, base+".iso", kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing iso-bios output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
"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)
|
|
}
|
|
err = outputImg(efi, base+"-efi.iso", kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing iso-efi output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
"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 = outputLinuxKit("raw", filename, kernel, initrd, cmdline, size, hyperkit)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing qcow2 output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
"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(filepath.Join(MobyDir, "tmp"), "img-gz")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer os.RemoveAll(tmp)
|
|
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
|
|
}
|
|
return nil
|
|
},
|
|
"gcp-img": 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)
|
|
}
|
|
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, 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)
|
|
}
|
|
err = outputImg(vhd, base+".vhd", kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writingvhd output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
"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)
|
|
}
|
|
err = outputImg(vmdk, base+".vmdk", kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return fmt.Errorf("Error writing vmdk output: %v", err)
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
|
|
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)
|
|
|
|
for _, o := range out {
|
|
f := outFuns[o]
|
|
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, size int, hyperkit bool) error {
|
|
log.Debugf("output: %v %s", out, base)
|
|
|
|
err := validateOutputs(out)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
for _, o := range out {
|
|
f := outFuns[o]
|
|
err := f(base, image, size, hyperkit)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func tarToInitrd(image []byte) ([]byte, []byte, string, error) {
|
|
w := new(bytes.Buffer)
|
|
iw := initrd.NewWriter(w)
|
|
r := bytes.NewReader(image)
|
|
tr := tar.NewReader(r)
|
|
kernel, cmdline, err := initrd.CopySplitTar(iw, tr)
|
|
if err != nil {
|
|
return []byte{}, []byte{}, "", err
|
|
}
|
|
iw.Close()
|
|
return kernel, w.Bytes(), cmdline, nil
|
|
}
|
|
|
|
func tarInitrdKernel(kernel, initrd []byte, cmdline string) (*bytes.Buffer, error) {
|
|
buf := new(bytes.Buffer)
|
|
tw := tar.NewWriter(buf)
|
|
hdr := &tar.Header{
|
|
Name: "kernel",
|
|
Mode: 0600,
|
|
Size: int64(len(kernel)),
|
|
}
|
|
err := tw.WriteHeader(hdr)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
_, err = tw.Write(kernel)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
hdr = &tar.Header{
|
|
Name: "initrd.img",
|
|
Mode: 0600,
|
|
Size: int64(len(initrd)),
|
|
}
|
|
err = tw.WriteHeader(hdr)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
_, err = tw.Write(initrd)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
hdr = &tar.Header{
|
|
Name: "cmdline",
|
|
Mode: 0600,
|
|
Size: int64(len(cmdline)),
|
|
}
|
|
err = tw.WriteHeader(hdr)
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
_, err = tw.Write([]byte(cmdline))
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
err = tw.Close()
|
|
if err != nil {
|
|
return buf, err
|
|
}
|
|
return buf, nil
|
|
}
|
|
|
|
func outputImg(image, filename string, kernel []byte, initrd []byte, cmdline string) error {
|
|
log.Debugf("output img: %s %s", image, filename)
|
|
log.Infof(" %s", filename)
|
|
buf, err := tarInitrdKernel(kernel, initrd, cmdline)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
img, err := dockerRunInput(buf, image, cmdline)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(filename, img, os.FileMode(0644))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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 == 0 {
|
|
img, err = dockerRunInput(buf, image)
|
|
} else {
|
|
img, err = dockerRunInput(buf, image, fmt.Sprintf("%dM", size))
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(filename, img, os.FileMode(0644))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func outputKernelInitrd(base string, kernel []byte, initrd []byte, cmdline string) error {
|
|
log.Debugf("output kernel/initrd: %s %s", base, cmdline)
|
|
log.Infof(" %s %s %s", base+"-kernel", base+"-initrd.img", base+"-cmdline")
|
|
err := ioutil.WriteFile(base+"-initrd.img", initrd, os.FileMode(0644))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(base+"-kernel", kernel, os.FileMode(0644))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = ioutil.WriteFile(base+"-cmdline", []byte(cmdline), os.FileMode(0644))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|