mirror of
https://github.com/linuxkit/linuxkit.git
synced 2025-10-25 00:53:12 +00:00
This sets the base name of the built images which otherwise defaults to the basename of your yaml file. This allows building different versions easily eg adding git sha to the output names. Signed-off-by: Justin Cormack <justin.cormack@docker.com>
266 lines
5.6 KiB
Go
266 lines
5.6 KiB
Go
package main
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"errors"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"log"
|
|
"os/exec"
|
|
"path/filepath"
|
|
|
|
"github.com/docker/moby/pkg/initrd"
|
|
)
|
|
|
|
const (
|
|
docker2tar = "mobylinux/docker2tar:82a3f11f70b2959c7100dd6e184b511ebfc65908@sha256:e4fd36febc108477a2e5316d263ac257527779409891c7ac10d455a162df05c1"
|
|
)
|
|
|
|
func dockerRun(args ...string) ([]byte, error) {
|
|
// TODO switch to using Docker client API not exec - just a quick prototype
|
|
docker, err := exec.LookPath("docker")
|
|
if err != nil {
|
|
return []byte{}, errors.New("Docker does not seem to be installed")
|
|
}
|
|
args = append([]string{"run", "--rm"}, args...)
|
|
cmd := exec.Command(docker, args...)
|
|
|
|
stderrPipe, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stdoutPipe, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stdout, err := ioutil.ReadAll(stdoutPipe)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stderr, err := ioutil.ReadAll(stderrPipe)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
return []byte{}, fmt.Errorf("%s: %s", err, stderr)
|
|
}
|
|
|
|
return stdout, nil
|
|
}
|
|
|
|
func dockerRunInput(input io.Reader, args ...string) ([]byte, error) {
|
|
// TODO switch to using Docker client API not exec - just a quick prototype
|
|
docker, err := exec.LookPath("docker")
|
|
if err != nil {
|
|
return []byte{}, errors.New("Docker does not seem to be installed")
|
|
}
|
|
args = append([]string{"run", "--rm", "-i"}, args...)
|
|
cmd := exec.Command(docker, args...)
|
|
cmd.Stdin = input
|
|
|
|
stderrPipe, err := cmd.StderrPipe()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stdoutPipe, err := cmd.StdoutPipe()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stdout, err := ioutil.ReadAll(stdoutPipe)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
stderr, err := ioutil.ReadAll(stderrPipe)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
err = cmd.Wait()
|
|
if err != nil {
|
|
return []byte{}, fmt.Errorf("%s: %s", err, stderr)
|
|
}
|
|
|
|
return stdout, nil
|
|
}
|
|
|
|
func untarKernel(buf *bytes.Buffer, bzimageName, ktarName string) (*bytes.Buffer, *bytes.Buffer, error) {
|
|
tr := tar.NewReader(buf)
|
|
|
|
var bzimage, ktar *bytes.Buffer
|
|
|
|
for {
|
|
hdr, err := tr.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
log.Fatalln(err)
|
|
}
|
|
switch hdr.Name {
|
|
case bzimageName:
|
|
bzimage = new(bytes.Buffer)
|
|
_, err := io.Copy(bzimage, tr)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
case ktarName:
|
|
ktar = new(bytes.Buffer)
|
|
_, err := io.Copy(bzimage, tr)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
|
|
if ktar == nil || bzimage == nil {
|
|
return nil, nil, errors.New("did not find bzImage and kernel.tar in tarball")
|
|
}
|
|
|
|
return bzimage, ktar, nil
|
|
}
|
|
|
|
func containersInitrd(containers []*bytes.Buffer) (*bytes.Buffer, error) {
|
|
w := new(bytes.Buffer)
|
|
iw := initrd.NewWriter(w)
|
|
defer iw.Close()
|
|
for _, file := range containers {
|
|
_, err := initrd.Copy(iw, file)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return w, nil
|
|
}
|
|
|
|
func build(m *Moby, name string) {
|
|
containers := []*bytes.Buffer{}
|
|
|
|
// get kernel bzImage and initrd tarball from container
|
|
// TODO examine contents to see what names they might have
|
|
const (
|
|
bzimageName = "bzImage"
|
|
ktarName = "kernel.tar"
|
|
)
|
|
out, err := dockerRun(m.Kernel.Image, "tar", "cf", "-", bzimageName, ktarName)
|
|
if err != nil {
|
|
log.Fatalf("Failed to extract kernel image and tarball: %v", err)
|
|
}
|
|
buf := bytes.NewBuffer(out)
|
|
bzimage, ktar, err := untarKernel(buf, bzimageName, ktarName)
|
|
if err != nil {
|
|
log.Fatalf("Could not extract bzImage and kernel filesystem from tarball")
|
|
}
|
|
containers = append(containers, ktar)
|
|
|
|
// convert init image to tarball
|
|
init, err := dockerRun("-v", "/var/run/docker.sock:/var/run/docker.sock", docker2tar, m.Init)
|
|
if err != nil {
|
|
log.Fatalf("Failed to build init tarball: %v", err)
|
|
}
|
|
buffer := bytes.NewBuffer(init)
|
|
containers = append(containers, buffer)
|
|
|
|
for i, image := range m.System {
|
|
args := ConfigToRun(i, "system", &image)
|
|
out, err := dockerRun(args...)
|
|
if err != nil {
|
|
log.Fatalf("Failed to build container tarball: %v", err)
|
|
}
|
|
buffer := bytes.NewBuffer(out)
|
|
containers = append(containers, buffer)
|
|
}
|
|
|
|
for i, image := range m.Daemon {
|
|
args := ConfigToRun(i, "daemon", &image)
|
|
out, err := dockerRun(args...)
|
|
if err != nil {
|
|
log.Fatalf("Failed to build container tarball: %v", err)
|
|
}
|
|
buffer := bytes.NewBuffer(out)
|
|
containers = append(containers, buffer)
|
|
}
|
|
|
|
// add files
|
|
buffer, err = filesystem(m)
|
|
if err != nil {
|
|
log.Fatalf("failed to add filesystem parts: %v", err)
|
|
}
|
|
containers = append(containers, buffer)
|
|
|
|
initrd, err := containersInitrd(containers)
|
|
if err != nil {
|
|
log.Fatalf("Failed to make initrd %v", err)
|
|
}
|
|
|
|
err = outputs(m, name, bzimage.Bytes(), initrd.Bytes())
|
|
if err != nil {
|
|
log.Fatalf("Error writing outputs: %v", err)
|
|
}
|
|
}
|
|
|
|
var (
|
|
conf string
|
|
cmdline bool
|
|
name string
|
|
)
|
|
|
|
func main() {
|
|
flag.BoolVar(&cmdline, "cmdline", false, "Print the kernel command line and exit")
|
|
flag.StringVar(&name, "name", "", "Name to use for output files")
|
|
flag.Parse()
|
|
|
|
conf = "moby.yaml"
|
|
if len(flag.Args()) > 0 {
|
|
conf = flag.Args()[0]
|
|
}
|
|
|
|
if name == "" {
|
|
name = filepath.Base(conf)
|
|
ext := filepath.Ext(conf)
|
|
if ext != "" {
|
|
name = name[:len(name)-len(ext)]
|
|
}
|
|
}
|
|
|
|
config, err := ioutil.ReadFile(conf)
|
|
if err != nil {
|
|
log.Fatalf("Cannot open config file: %v", err)
|
|
}
|
|
|
|
m, err := NewConfig(config)
|
|
if err != nil {
|
|
log.Fatalf("Invalid config: %v", err)
|
|
}
|
|
|
|
if cmdline {
|
|
fmt.Printf("%s\n", m.Kernel.Cmdline)
|
|
return
|
|
}
|
|
|
|
build(m, name)
|
|
}
|