Files
linuxkit/src/moby/output.go
Rolf Neugebauer 2b1a611bab output: Extract ucode if present
For now the backends for the different formats do not yet
use the extracted ucode cpio archive, but '// TODO' are
placed for the backends which should eventually handle it.

Signed-off-by: Rolf Neugebauer <rolf.neugebauer@docker.com>
2018-01-15 16:49:42 +00:00

379 lines
10 KiB
Go

package moby
import (
"archive/tar"
"bytes"
"fmt"
"io"
"io/ioutil"
"os"
"runtime"
"github.com/moby/tool/src/initrd"
log "github.com/sirupsen/logrus"
)
const (
isoBios = "linuxkit/mkimage-iso-bios:165b051322578cb0c2a4f16253b20f7d2797a502"
isoEfi = "linuxkit/mkimage-iso-efi:43592c9b316b036338c4db9f90aada37b48415dc"
rawBios = "linuxkit/mkimage-raw-bios:eac4dcb78f837618e7e009060146977c2adabe19"
rawEfi = "linuxkit/mkimage-raw-efi:167441e223c1c131dac349deb67966d400066e04"
gcp = "linuxkit/mkimage-gcp:d1883809d212ce048f60beb0308a4d2b14c256af"
vhd = "linuxkit/mkimage-vhd:2a31f2bc91c1d247160570bd17868075e6c0009a"
vmdk = "linuxkit/mkimage-vmdk:df02a4fabd87a82209fbbacebde58c4440d2daf0"
dynamicvhd = "linuxkit/mkimage-dynamic-vhd:8553167d10c3e8d8603b2566d01bdc0cf5908fa5"
rpi3 = "linuxkit/mkimage-rpi3:0735656fff247ca978135e3aeb62864adc612180"
)
var outFuns = map[string]func(string, io.Reader, int) error{
"kernel+initrd": func(base string, image io.Reader, size int) error {
kernel, initrd, cmdline, _, err := tarToInitrd(image)
if err != nil {
return fmt.Errorf("Error converting to initrd: %v", err)
}
// TODO: Handle ucode
err = outputKernelInitrd(base, kernel, initrd, cmdline)
if err != nil {
return fmt.Errorf("Error writing kernel+initrd output: %v", err)
}
return nil
},
"tar-kernel-initrd": func(base string, image io.Reader, size int) error {
kernel, initrd, cmdline, _, err := tarToInitrd(image)
if err != nil {
return fmt.Errorf("Error converting to initrd: %v", err)
}
// TODO: Handle ucode
if err := outputKernelInitrdTarball(base, kernel, initrd, cmdline); err != nil {
return fmt.Errorf("Error writing kernel+initrd tarball output: %v", err)
}
return nil
},
"iso-bios": func(base string, image io.Reader, size int) error {
err := outputIso(isoBios, base+".iso", image)
if err != nil {
return fmt.Errorf("Error writing iso-bios output: %v", err)
}
return nil
},
"iso-efi": func(base string, image io.Reader, size int) error {
err := outputIso(isoEfi, base+"-efi.iso", image)
if err != nil {
return fmt.Errorf("Error writing iso-efi output: %v", err)
}
return nil
},
"raw-bios": func(base string, image io.Reader, size int) error {
kernel, initrd, cmdline, _, err := tarToInitrd(image)
if err != nil {
return fmt.Errorf("Error converting to initrd: %v", err)
}
// TODO: Handle ucode
err = outputImg(rawBios, base+"-bios.img", kernel, initrd, cmdline)
if err != nil {
return fmt.Errorf("Error writing raw-bios output: %v", err)
}
return nil
},
"raw-efi": func(base string, image io.Reader, size int) error {
kernel, initrd, cmdline, _, err := tarToInitrd(image)
if err != nil {
return fmt.Errorf("Error converting to initrd: %v", err)
}
err = outputImg(rawEfi, base+"-efi.img", kernel, initrd, cmdline)
if err != nil {
return fmt.Errorf("Error writing raw-efi output: %v", err)
}
return nil
},
"aws": func(base string, image io.Reader, size int) error {
filename := base + ".raw"
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)
if err != nil {
return fmt.Errorf("Error writing raw output: %v", err)
}
return nil
},
"gcp": func(base string, image io.Reader, size int) 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 output: %v", err)
}
return nil
},
"qcow2-bios": func(base string, image io.Reader, size int) 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)
}
// TODO: Handle ucode
err = outputLinuxKit("qcow2", filename, kernel, initrd, cmdline, size)
if err != nil {
return fmt.Errorf("Error writing qcow2 output: %v", err)
}
return nil
},
"vhd": func(base string, image io.Reader, size int) 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 writing vhd output: %v", err)
}
return nil
},
"dynamic-vhd": func(base string, image io.Reader, size int) error {
kernel, initrd, cmdline, _, err := tarToInitrd(image)
if err != nil {
return fmt.Errorf("Error converting to initrd: %v", err)
}
err = outputImg(dynamicvhd, base+".vhd", kernel, initrd, cmdline)
if err != nil {
return fmt.Errorf("Error writing vhd output: %v", err)
}
return nil
},
"vmdk": func(base string, image io.Reader, size int) 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
},
"rpi3": func(base string, image io.Reader, size int) error {
if runtime.GOARCH != "arm64" {
return fmt.Errorf("Raspberry Pi output currently only supported on arm64")
}
err := outputRPi3(rpi3, base+".tar", image)
if err != nil {
return fmt.Errorf("Error writing rpi3 output: %v", err)
}
return nil
},
}
var prereq = map[string]string{
"aws": "mkimage",
"qcow2-bios": "mkimage",
}
func ensurePrereq(out string) error {
var err error
p := prereq[out]
if p != "" {
err = ensureLinuxkitImage(p)
}
return err
}
// ValidateFormats checks if the format type is known
func ValidateFormats(formats []string) error {
log.Debugf("validating output: %v", formats)
for _, o := range formats {
f := outFuns[o]
if f == nil {
return fmt.Errorf("Unknown format type %s", o)
}
err := ensurePrereq(o)
if err != nil {
return fmt.Errorf("Failed to set up format type %s: %v", o, err)
}
}
return nil
}
// Formats generates all the specified output formats
func Formats(base string, image string, formats []string, size int) error {
log.Debugf("format: %v %s", formats, base)
err := ValidateFormats(formats)
if err != nil {
return err
}
for _, o := range formats {
ir, err := os.Open(image)
if err != nil {
return err
}
defer ir.Close()
f := outFuns[o]
if err := f(base, ir, size); err != nil {
return err
}
}
return nil
}
func tarToInitrd(r io.Reader) ([]byte, []byte, string, []byte, error) {
w := new(bytes.Buffer)
iw := initrd.NewWriter(w)
tr := tar.NewReader(r)
kernel, cmdline, ucode, err := initrd.CopySplitTar(iw, tr)
if err != nil {
return []byte{}, []byte{}, "", []byte{}, err
}
iw.Close()
return kernel, w.Bytes(), cmdline, ucode, 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
}
return buf, tw.Close()
}
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
}
output, err := os.Create(filename)
if err != nil {
return err
}
defer output.Close()
return dockerRun(buf, output, true, image, cmdline)
}
func outputIso(image, filename string, filesystem io.Reader) error {
log.Debugf("output ISO: %s %s", image, filename)
log.Infof(" %s", filename)
output, err := os.Create(filename)
if err != nil {
return err
}
defer output.Close()
return dockerRun(filesystem, output, true, image)
}
func outputRPi3(image, filename string, filesystem io.Reader) error {
log.Debugf("output RPi3: %s %s", image, filename)
log.Infof(" %s", filename)
output, err := os.Create(filename)
if err != nil {
return err
}
defer output.Close()
return dockerRun(filesystem, output, true, image)
}
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
}
return ioutil.WriteFile(base+"-cmdline", []byte(cmdline), os.FileMode(0644))
}
func outputKernelInitrdTarball(base string, kernel []byte, initrd []byte, cmdline string) error {
log.Debugf("output kernel/initrd tarball: %s %s", base, cmdline)
log.Infof(" %s", base+"-initrd.tar")
f, err := os.Create(base + "-initrd.tar")
if err != nil {
return err
}
defer f.Close()
tw := tar.NewWriter(f)
hdr := &tar.Header{
Name: "kernel",
Mode: 0644,
Size: int64(len(kernel)),
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if _, err := tw.Write(kernel); err != nil {
return err
}
hdr = &tar.Header{
Name: "initrd.img",
Mode: 0644,
Size: int64(len(initrd)),
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if _, err := tw.Write(initrd); err != nil {
return err
}
hdr = &tar.Header{
Name: "cmdline",
Mode: 0644,
Size: int64(len(cmdline)),
}
if err := tw.WriteHeader(hdr); err != nil {
return err
}
if _, err := tw.Write([]byte(cmdline)); err != nil {
return err
}
return tw.Close()
}