1
0

Use Docker instead of Vagrant for generate images

Fixes #2
This commit is contained in:
dump_stack() 2018-10-25 23:38:12 +00:00
parent 37c2e05062
commit a93073e0be
10 changed files with 76 additions and 231 deletions

View File

@ -18,9 +18,8 @@ Read [Qemu API](qemu/README.md).
If you already have Go, Qemu, Vagrant and Docker installed, there's cross-platform installation checklist:
$ go get github.com/jollheef/out-of-tree
$ cd $GOPATH/src/github.com/jollheef/out-of-tree
$ cd tools/qemu-debian-img/
$ vagrant up && vagrant destroy -f
$ cd $GOPATH/src/github.com/jollheef/out-of-tree/tools/qemu-debian-img/
$ ./bootstrap.sh
$ cd ../kernel-factory
$ rm -rf {Debian,CentOS,Ubuntu/{14.04,18.04}} # speed up :)
$ ./bootstrap.sh

View File

@ -15,26 +15,23 @@ Features:
First of all we need to generate rootfs for run qemu.
#### GNU/Linux
#### Install qemu and docker
$ sudo apt install -y debootstrap qemu
$ sudo qemu-debian-img generate sid.img
##### GNU/Linux
#### macOS
$ sudo apt install -y qemu docker
##### macOS
Note: qemu on macOS since v2.12 (24 April 2018) supports Hypervisor.framework.
$ brew install qemu
$ brew cask install docker
Because it's a very complicated to debootstrap qemu images from macOS,
preferred way is to use Vagrant with any hypervisor.
#### Generate image
$ brew cask install vagrant
$ vagrant plugin install vagrant-vbguest
$ cd $GOPATH/src/github.com/jollheef/out-of-tree/qemu/tools/qemu-debian-image
$ vagrant up && vagrant destroy -f
bionic.img, vmlinuz-bionic and initrd-bionic will be created in current directory.
$ cd $GOPATH/src/github.com/jollheef/out-of-tree/tools/qemu-debian-img
$ ./bootstrap.sh
### Fill configuration file

View File

@ -4,7 +4,7 @@
package qemukernel
const testConfigVmlinuz = "../tools/qemu-debian-img/vmlinuz-bionic"
const testConfigInitrd = "../tools/qemu-debian-img/initrd-bionic"
const testConfigRootfs = "../tools/qemu-debian-img/bionic.img"
const testConfigSampleKo = "../tools/qemu-debian-img/sample.ko"
const testConfigVmlinuz = "../tools/qemu-debian-img/ubuntu1804.vmlinuz"
const testConfigInitrd = "../tools/qemu-debian-img/ubuntu1804.initrd"
const testConfigRootfs = "../tools/qemu-debian-img/ubuntu1804.img"
const testConfigSampleKo = "../tools/qemu-debian-img/ubuntu1804.ko"

View File

@ -1,6 +1,6 @@
*.img
*.log
vmlinuz*
initrd*
*vmlinuz*
*initrd*
*.ko
.vagrant

View File

@ -0,0 +1,35 @@
# Copyright 2018 Mikhail Klementev. All rights reserved.
# Use of this source code is governed by a AGPLv3 license
# (or later) that can be found in the LICENSE file.
#
# Usage:
#
# $ docker build -t gen-ubuntu1804-image .
# $ docker run --privileged -v $(pwd):/shared -t gen-ubuntu1804-image
#
# centos7.img will be created in current directory. You can change $(pwd) to
# different directory to use different destination for image.
#
FROM ubuntu:18.04
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update
RUN apt install -y debootstrap qemu
RUN apt install -y linux-image-generic
ENV TMPDIR=/tmp/ubuntu
ENV IMAGEDIR=/tmp/image
ENV IMAGE=/shared/ubuntu1804.img
ENV REPOSITORY=http://archive.ubuntu.com/ubuntu
ENV RELEASE=bionic
RUN mkdir $IMAGEDIR
# Must be runned with --privileged because of /dev/loop
CMD debootstrap --include=openssh-server $RELEASE $TMPDIR $REPOSITORY && \
/shared/setup.sh $TMPDIR && \
qemu-img create $IMAGE 2G && \
mkfs.ext4 -F $IMAGE && \
mount -o loop $IMAGE $IMAGEDIR && \
cp -a $TMPDIR/* $IMAGEDIR/ && \
umount $IMAGEDIR

View File

@ -1,5 +0,0 @@
# TODO
* Add ability to bootstrap centos image, rename tool to qemu-bootstrap-img
yum --installroot=/chroot --releasever=7 --disablerepo='*' --enablerepo=base install centos-release

View File

@ -1,22 +0,0 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/bionic64"
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y debootstrap qemu golang
mkdir qemu-debian-img && cd qemu-debian-img
cp /vagrant/main.go ./
GOBIN=/usr/bin go get ./
export REPO=http://archive.ubuntu.com/ubuntu
for RELEASE in trusty xenial bionic; do \
qemu-debian-img generate --repository=$REPO --release=$RELEASE $RELEASE.img
cp $RELEASE.img /vagrant/
done
cp /boot/vmlinuz-* /vagrant/vmlinuz-bionic
cp /boot/initrd* /vagrant/initrd-bionic
cp /lib/modules/`uname -r`/kernel/lib/test_static_key_base.ko /vagrant/sample.ko
SHELL
end

View File

@ -0,0 +1,7 @@
#!/bin/sh -eux
docker build -t gen-ubuntu1804-image .
RUN="docker run --privileged -v $(pwd):/shared -t gen-ubuntu1804-image"
$RUN # generate image
$RUN sh -c 'cp /vmlinuz /shared/ubuntu1804.vmlinuz'
$RUN sh -c 'cp /initrd.img /shared/ubuntu1804.initrd'
$RUN sh -c 'cp $(find /lib/modules -name test_static_key_base.ko) /shared/ubuntu1804.ko'

View File

@ -1,183 +0,0 @@
// Copyright 2018 Mikhail Klementev. All rights reserved.
// Use of this source code is governed by a AGPLv3 license
// (or later) that can be found in the LICENSE file.
package main
import (
"errors"
"io/ioutil"
"log"
"net/http"
"os"
"os/user"
"regexp"
"strings"
system "github.com/jollheef/go-system"
kingpin "gopkg.in/alecthomas/kingpin.v2"
)
func matchBody(url, pattern string) bool {
resp, err := http.Get(url)
if err != nil {
return false
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return false
}
matched, err := regexp.MatchString(pattern, string(body))
if err != nil {
return false
}
if !matched {
return false
}
return true
}
func isAptCacherExists(url string) bool {
return matchBody(url, "Apt-Cacher")
}
func runInChroot(chroot, cmd string) (string, string, int, error) {
return system.System("chroot", chroot, "/bin/sh", "-c", cmd)
}
func generateImage(repo, release, path, size string) (err error) {
log.Println("Check current user")
usr, err := user.Current()
if err != nil {
return
}
if usr.Name != "root" {
err = errors.New("Run as root is required")
return
}
log.Println("Create qcow2 image")
stdout, stderr, _, err := system.System("qemu-img", "create", path, size)
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Create ext4 file system")
stdout, stderr, _, err = system.System("mkfs.ext4", "-F", path)
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Create temporary directory")
stdout, stderr, _, err = system.System("mktemp", "-d")
if err != nil {
log.Println(stdout, stderr)
return
}
tmpdir := strings.TrimSpace(stdout)
defer func() {
log.Println("Remove temporary directory")
err := os.RemoveAll(tmpdir)
if err != nil {
log.Println(err)
}
}()
log.Println("Mount qemu image")
stdout, stderr, _, err = system.System("mount", "-o", "loop", path, tmpdir)
if err != nil {
log.Println(stdout, stderr)
return
}
defer func() {
log.Println("Umount qemu image")
stdout, stderr, _, err := system.System("umount", tmpdir)
if err != nil {
log.Println(err, stdout, stderr)
}
}()
log.Println("debootstrap (may be more than 10-15 minutes)")
stdout, stderr, _, err = system.System("debootstrap",
"--include=openssh-server",
release, tmpdir, repo)
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Create new user")
stdout, stderr, _, err = runInChroot(tmpdir, "useradd -m user")
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Login without password")
stdout, stderr, _, err = runInChroot(tmpdir, "passwd -d root")
if err != nil {
log.Println(stdout, stderr)
return
}
stdout, stderr, _, err = runInChroot(tmpdir, "passwd -d user")
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Allow ssh login without password (NOTE: fixed sshd pam.d)")
stdout, stderr, _, err = runInChroot(tmpdir,
"echo auth sufficient pam_permit.so > /etc/pam.d/sshd"+
" && sed -i '/PermitEmptyPasswords/d' /etc/ssh/sshd_config"+
" && echo PermitEmptyPasswords yes >> /etc/ssh/sshd_config"+
" && sed -i '/PermitRootLogin/d' /etc/ssh/sshd_config"+
" && echo PermitRootLogin yes >> /etc/ssh/sshd_config")
if err != nil {
log.Println(stdout, stderr)
return
}
log.Println("Add dhclient to rc.local")
stdout, stderr, _, err = runInChroot(tmpdir,
"echo '#!/bin/sh\ndhclient' > /etc/rc.local"+
" && chmod +x /etc/rc.local")
if err != nil {
log.Println(stdout, stderr)
return
}
return
}
func main() {
generate := kingpin.Command("generate", "Generate qemu image")
repository := generate.Flag("repository", "Debian/ubuntu repository").Default(
"deb.debian.org/debian").String()
aptCacherURL := generate.Flag("apt-cacher", "Local apt cacher url").Default(
"http://localhost:3142/").String()
release := generate.Flag("release", "Debian/ubuntu release name").Default(
"sid").String()
size := generate.Flag("size", "Image size").Default("8G").String()
path := generate.Arg("path", "Generated image path").Required().String()
switch kingpin.Parse() {
case "generate":
repo := *repository
if isAptCacherExists(*aptCacherURL) {
log.Println("Use local apt cache")
repo = *aptCacherURL + "/" + repo
}
log.Println("Repository:", repo)
err := generateImage(repo, *release, *path, *size)
if err != nil {
log.Fatalln(err)
}
}
}

17
tools/qemu-debian-img/setup.sh Executable file
View File

@ -0,0 +1,17 @@
#!/bin/sh -eux
# Copyright 2018 Mikhail Klementev. All rights reserved.
# Use of this source code is governed by a AGPLv3 license
# (or later) that can be found in the LICENSE file.
TMPDIR=$1
chroot $TMPDIR /bin/sh -c 'useradd -m user'
sed -i 's/root:\*:/root::/' $TMPDIR/etc/shadow
sed -i 's/user:!!:/user::/' $TMPDIR/etc/shadow
echo auth sufficient pam_permit.so > $TMPDIR/etc/pam.d/sshd
sed -i '/PermitEmptyPasswords/d' $TMPDIR/etc/ssh/sshd_config
echo PermitEmptyPasswords yes >> $TMPDIR/etc/ssh/sshd_config
sed -i '/PermitRootLogin/d' $TMPDIR/etc/ssh/sshd_config
echo PermitRootLogin yes >> $TMPDIR/etc/ssh/sshd_config
echo '#!/bin/sh' > $TMPDIR/etc/rc.local
echo 'dhclient' >> $TMPDIR/etc/rc.local
chmod +x $TMPDIR/etc/rc.local